@adaas/a-concept 0.3.7 → 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/benchmarks/feature-chaining.bench.ts +465 -0
- package/benchmarks/feature-optimize.bench.ts +205 -0
- package/dist/browser/index.d.mts +2 -1
- package/dist/browser/index.mjs +2 -2
- package/dist/browser/index.mjs.map +1 -1
- package/dist/node/index.cjs +35 -34
- package/dist/node/index.cjs.map +1 -1
- package/dist/node/index.d.mts +2 -1
- package/dist/node/index.d.ts +2 -1
- package/dist/node/index.mjs +35 -34
- package/dist/node/index.mjs.map +1 -1
- package/package.json +3 -1
- package/src/lib/A-Entity/A-Entity.class.ts +3 -3
- package/src/lib/A-Feature/A-Feature.class.ts +46 -41
- package/tests/A-Feature.test.ts +101 -0
|
@@ -0,0 +1,465 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ============================================================
|
|
3
|
+
* A_Feature Chaining Performance Benchmarks
|
|
4
|
+
* ============================================================
|
|
5
|
+
*
|
|
6
|
+
* Measures realistic end-to-end performance of feature chaining:
|
|
7
|
+
* - Entity creation + scope allocation
|
|
8
|
+
* - Scope registration overhead
|
|
9
|
+
* - Feature invocation through entity.call()
|
|
10
|
+
* - Feature chaining between components (chain())
|
|
11
|
+
* - Dependency injection overhead (@A_Inject)
|
|
12
|
+
* - Scaling with chain depth and component count
|
|
13
|
+
* - Full realistic pipeline: entity → scope → feature → chain → DI
|
|
14
|
+
*/
|
|
15
|
+
import { A_Component } from "@adaas/a-concept/a-component";
|
|
16
|
+
import { A_Feature } from "@adaas/a-concept/a-feature";
|
|
17
|
+
import { A_Scope } from "@adaas/a-concept/a-scope";
|
|
18
|
+
import { A_Inject } from "@adaas/a-concept/a-inject";
|
|
19
|
+
import { A_Caller } from "@adaas/a-concept/a-caller";
|
|
20
|
+
import { A_Entity } from "@adaas/a-concept/a-entity";
|
|
21
|
+
import { A_Context } from "@adaas/a-concept/a-context";
|
|
22
|
+
import { createSuite, BenchResult, printSummary } from './helpers';
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
// ──────────────────────────────────────────────────────────────
|
|
26
|
+
// Fixture: Entities
|
|
27
|
+
// ──────────────────────────────────────────────────────────────
|
|
28
|
+
|
|
29
|
+
class BenchEntity extends A_Entity {
|
|
30
|
+
protected _scope!: A_Scope;
|
|
31
|
+
|
|
32
|
+
get scope(): A_Scope {
|
|
33
|
+
if (!this._scope) {
|
|
34
|
+
this._scope = A_Context.allocate(this, new A_Scope({ name: `${this.aseid.id}-scope` }));
|
|
35
|
+
}
|
|
36
|
+
return this._scope;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
get targetComponent() {
|
|
40
|
+
return this.scope.resolve(TargetComponent)!;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
invokeFeature() {
|
|
44
|
+
return this.call('entityFeature');
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
// ──────────────────────────────────────────────────────────────
|
|
50
|
+
// Fixture: Components — Simple feature (no chaining baseline)
|
|
51
|
+
// ──────────────────────────────────────────────────────────────
|
|
52
|
+
|
|
53
|
+
class SimpleComponent extends A_Component {
|
|
54
|
+
counter: number = 0;
|
|
55
|
+
|
|
56
|
+
@A_Feature.Define({ invoke: true })
|
|
57
|
+
@A_Feature.Extend({ name: 'simpleFeature' })
|
|
58
|
+
simpleMethod() {
|
|
59
|
+
this.counter++;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
// ──────────────────────────────────────────────────────────────
|
|
65
|
+
// Fixture: Components — Single chain (A → B)
|
|
66
|
+
// ──────────────────────────────────────────────────────────────
|
|
67
|
+
|
|
68
|
+
class TargetComponent extends A_Component {
|
|
69
|
+
executed: boolean = false;
|
|
70
|
+
|
|
71
|
+
@A_Feature.Define({ invoke: true })
|
|
72
|
+
@A_Feature.Extend({ name: 'targetFeature' })
|
|
73
|
+
targetStep() {
|
|
74
|
+
this.executed = true;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
class ChainingComponent extends A_Component {
|
|
79
|
+
@A_Feature.Define({ invoke: false })
|
|
80
|
+
@A_Feature.Extend({ name: 'chainedFeature' })
|
|
81
|
+
chainStep(
|
|
82
|
+
@A_Inject(A_Caller) caller: A_Component,
|
|
83
|
+
@A_Inject(A_Scope) scope: A_Scope,
|
|
84
|
+
@A_Inject(A_Feature) feature: A_Feature
|
|
85
|
+
) {
|
|
86
|
+
const target = scope.resolve(TargetComponent)!;
|
|
87
|
+
feature.chain(target, 'targetFeature', scope);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
// ──────────────────────────────────────────────────────────────
|
|
93
|
+
// Fixture: Components — Multi-chain (A → B → C)
|
|
94
|
+
// ──────────────────────────────────────────────────────────────
|
|
95
|
+
|
|
96
|
+
class FinalComponent extends A_Component {
|
|
97
|
+
@A_Feature.Define({ invoke: true })
|
|
98
|
+
@A_Feature.Extend({ name: 'finalFeature' })
|
|
99
|
+
finalStep() { }
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
class MiddleComponent extends A_Component {
|
|
103
|
+
@A_Feature.Define({ invoke: false })
|
|
104
|
+
@A_Feature.Extend({ name: 'middleFeature' })
|
|
105
|
+
middleStep(
|
|
106
|
+
@A_Inject(A_Scope) scope: A_Scope,
|
|
107
|
+
@A_Inject(A_Feature) feature: A_Feature
|
|
108
|
+
) {
|
|
109
|
+
const final = scope.resolve(FinalComponent)!;
|
|
110
|
+
feature.chain(final, 'finalFeature', scope);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
class EntryComponent extends A_Component {
|
|
115
|
+
@A_Feature.Define({ invoke: false })
|
|
116
|
+
@A_Feature.Extend({ name: 'entryFeature' })
|
|
117
|
+
entryStep(
|
|
118
|
+
@A_Inject(A_Scope) scope: A_Scope,
|
|
119
|
+
@A_Inject(A_Feature) feature: A_Feature
|
|
120
|
+
) {
|
|
121
|
+
const middle = scope.resolve(MiddleComponent)!;
|
|
122
|
+
feature.chain(middle, 'middleFeature', scope);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
// ──────────────────────────────────────────────────────────────
|
|
128
|
+
// Fixture: Components — Feature with multiple DI parameters
|
|
129
|
+
// ──────────────────────────────────────────────────────────────
|
|
130
|
+
|
|
131
|
+
class ServiceComponentA extends A_Component {
|
|
132
|
+
getValue() { return 42; }
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
class ServiceComponentB extends A_Component {
|
|
136
|
+
getLabel() { return 'bench'; }
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
class HeavyDIComponent extends A_Component {
|
|
140
|
+
@A_Feature.Define({ invoke: true })
|
|
141
|
+
@A_Feature.Extend({ name: 'heavyDIFeature' })
|
|
142
|
+
heavyStep(
|
|
143
|
+
@A_Inject(A_Caller) caller: A_Component,
|
|
144
|
+
@A_Inject(A_Scope) scope: A_Scope,
|
|
145
|
+
@A_Inject(A_Feature) feature: A_Feature,
|
|
146
|
+
@A_Inject(ServiceComponentA) svcA: ServiceComponentA,
|
|
147
|
+
@A_Inject(ServiceComponentB) svcB: ServiceComponentB,
|
|
148
|
+
) {
|
|
149
|
+
svcA.getValue();
|
|
150
|
+
svcB.getLabel();
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
// ──────────────────────────────────────────────────────────────
|
|
156
|
+
// Fixture: Components — Multiple extensions converging + chain
|
|
157
|
+
// ──────────────────────────────────────────────────────────────
|
|
158
|
+
|
|
159
|
+
class ExtStepA extends A_Component {
|
|
160
|
+
@A_Feature.Extend({ name: 'convergentFeature' })
|
|
161
|
+
stepA() { }
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
class ExtStepB extends A_Component {
|
|
165
|
+
@A_Feature.Extend({ name: 'convergentFeature' })
|
|
166
|
+
stepB() { }
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
class ExtStepC extends A_Component {
|
|
170
|
+
@A_Feature.Extend({ name: 'convergentFeature' })
|
|
171
|
+
stepC() { }
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
class ConvergentComponent extends A_Component {
|
|
175
|
+
@A_Feature.Define({ invoke: false })
|
|
176
|
+
@A_Feature.Extend({ name: 'convergentFeature' })
|
|
177
|
+
convergentStep(
|
|
178
|
+
@A_Inject(A_Scope) scope: A_Scope,
|
|
179
|
+
@A_Inject(A_Feature) feature: A_Feature
|
|
180
|
+
) {
|
|
181
|
+
const target = scope.resolve(TargetComponent)!;
|
|
182
|
+
feature.chain(target, 'targetFeature', scope);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
// ──────────────────────────────────────────────────────────────
|
|
188
|
+
// Fixture: Entity-driven feature chaining
|
|
189
|
+
// ──────────────────────────────────────────────────────────────
|
|
190
|
+
|
|
191
|
+
class EntityFeatureComponent extends A_Component {
|
|
192
|
+
@A_Feature.Extend({
|
|
193
|
+
name: 'entityFeature',
|
|
194
|
+
scope: [BenchEntity]
|
|
195
|
+
})
|
|
196
|
+
entityStep(
|
|
197
|
+
@A_Inject(A_Caller) entity: BenchEntity,
|
|
198
|
+
@A_Inject(A_Scope) scope: A_Scope,
|
|
199
|
+
@A_Inject(A_Feature) feature: A_Feature
|
|
200
|
+
) {
|
|
201
|
+
const target = entity.scope.resolve(TargetComponent);
|
|
202
|
+
if (target) {
|
|
203
|
+
feature.chain(target, 'targetFeature', scope);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
// ──────────────────────────────────────────────────────────────
|
|
210
|
+
// Benchmark Suites
|
|
211
|
+
// ──────────────────────────────────────────────────────────────
|
|
212
|
+
|
|
213
|
+
export async function runFeatureChainingBenchmarks(): Promise<BenchResult[]> {
|
|
214
|
+
const allResults: BenchResult[] = [];
|
|
215
|
+
|
|
216
|
+
// ── Suite 1: Entity + Scope Allocation ──────────────────
|
|
217
|
+
const entityResults = await createSuite('Entity & Scope — Allocation', (suite) => {
|
|
218
|
+
suite
|
|
219
|
+
.add('new A_Entity()', () => {
|
|
220
|
+
new BenchEntity();
|
|
221
|
+
})
|
|
222
|
+
.add('new A_Scope (empty)', () => {
|
|
223
|
+
new A_Scope({ name: 'empty' });
|
|
224
|
+
})
|
|
225
|
+
.add('new A_Scope (3 components)', () => {
|
|
226
|
+
new A_Scope({
|
|
227
|
+
name: 'with-components',
|
|
228
|
+
components: [SimpleComponent, TargetComponent, ChainingComponent]
|
|
229
|
+
});
|
|
230
|
+
})
|
|
231
|
+
.add('new A_Scope (5 components)', () => {
|
|
232
|
+
new A_Scope({
|
|
233
|
+
name: 'with-5-components',
|
|
234
|
+
components: [SimpleComponent, TargetComponent, ChainingComponent, ServiceComponentA, ServiceComponentB]
|
|
235
|
+
});
|
|
236
|
+
})
|
|
237
|
+
.add('A_Context.allocate + deallocate', () => {
|
|
238
|
+
const entity = new BenchEntity();
|
|
239
|
+
A_Context.allocate(entity, new A_Scope({ name: 'alloc-bench' }));
|
|
240
|
+
A_Context.deallocate(entity);
|
|
241
|
+
});
|
|
242
|
+
});
|
|
243
|
+
allResults.push(...entityResults);
|
|
244
|
+
|
|
245
|
+
// ── Suite 2: Scope Registration & Resolution ────────────
|
|
246
|
+
const regResults = await createSuite('Scope — Registration & Resolution', (suite) => {
|
|
247
|
+
suite
|
|
248
|
+
.add('register entity into scope', () => {
|
|
249
|
+
const scope = new A_Scope({ name: 'reg-bench', components: [TargetComponent] });
|
|
250
|
+
const entity = new BenchEntity();
|
|
251
|
+
scope.register(entity);
|
|
252
|
+
})
|
|
253
|
+
.add('resolve component — flat scope', () => {
|
|
254
|
+
const scope = new A_Scope({
|
|
255
|
+
name: 'resolve-bench',
|
|
256
|
+
components: [SimpleComponent, TargetComponent, ChainingComponent]
|
|
257
|
+
});
|
|
258
|
+
scope.resolve(TargetComponent);
|
|
259
|
+
})
|
|
260
|
+
.add('resolve component — nested 2 levels', () => {
|
|
261
|
+
const parent = new A_Scope({ name: 'parent', components: [TargetComponent] });
|
|
262
|
+
const child = new A_Scope({ name: 'child', components: [SimpleComponent] });
|
|
263
|
+
child.inherit(parent);
|
|
264
|
+
child.resolve(TargetComponent);
|
|
265
|
+
})
|
|
266
|
+
.add('resolve component — nested 3 levels', () => {
|
|
267
|
+
const grandparent = new A_Scope({ name: 'gp', components: [TargetComponent] });
|
|
268
|
+
const parent = new A_Scope({ name: 'p', components: [ChainingComponent] });
|
|
269
|
+
const child = new A_Scope({ name: 'c', components: [SimpleComponent] });
|
|
270
|
+
parent.inherit(grandparent);
|
|
271
|
+
child.inherit(parent);
|
|
272
|
+
child.resolve(TargetComponent);
|
|
273
|
+
});
|
|
274
|
+
});
|
|
275
|
+
allResults.push(...regResults);
|
|
276
|
+
|
|
277
|
+
// ── Suite 3: Feature Call — Baseline (no chain) ─────────
|
|
278
|
+
const baselineResults = await createSuite('Feature Call — Baseline (no chain)', (suite) => {
|
|
279
|
+
const scope1 = new A_Scope({ name: 'base-1', components: [SimpleComponent] });
|
|
280
|
+
const comp1 = scope1.resolve(SimpleComponent)!;
|
|
281
|
+
|
|
282
|
+
const scope3 = new A_Scope({
|
|
283
|
+
name: 'base-3',
|
|
284
|
+
components: [SimpleComponent, TargetComponent, ChainingComponent]
|
|
285
|
+
});
|
|
286
|
+
const comp3 = scope3.resolve(SimpleComponent)!;
|
|
287
|
+
|
|
288
|
+
suite
|
|
289
|
+
.add('component.call (1 component in scope)', () => {
|
|
290
|
+
comp1.call('simpleFeature');
|
|
291
|
+
})
|
|
292
|
+
.add('component.call (3 components in scope)', () => {
|
|
293
|
+
comp3.call('simpleFeature');
|
|
294
|
+
});
|
|
295
|
+
});
|
|
296
|
+
allResults.push(...baselineResults);
|
|
297
|
+
|
|
298
|
+
// ── Suite 4: Feature Chaining — Single Chain (A → B) ────
|
|
299
|
+
const singleChainResults = await createSuite('Feature Chaining — Single (A → B)', (suite) => {
|
|
300
|
+
const scope = new A_Scope({
|
|
301
|
+
name: 'single-chain',
|
|
302
|
+
components: [ChainingComponent, TargetComponent]
|
|
303
|
+
});
|
|
304
|
+
const chaining = scope.resolve(ChainingComponent)!;
|
|
305
|
+
|
|
306
|
+
suite
|
|
307
|
+
.add('chain: A → B (resolve + chain + execute)', () => {
|
|
308
|
+
chaining.call('chainedFeature');
|
|
309
|
+
});
|
|
310
|
+
});
|
|
311
|
+
allResults.push(...singleChainResults);
|
|
312
|
+
|
|
313
|
+
// ── Suite 5: Feature Chaining — Double Chain (A → B → C)
|
|
314
|
+
const doubleChainResults = await createSuite('Feature Chaining — Double (A → B → C)', (suite) => {
|
|
315
|
+
const scope = new A_Scope({
|
|
316
|
+
name: 'double-chain',
|
|
317
|
+
components: [EntryComponent, MiddleComponent, FinalComponent]
|
|
318
|
+
});
|
|
319
|
+
const entry = scope.resolve(EntryComponent)!;
|
|
320
|
+
|
|
321
|
+
suite
|
|
322
|
+
.add('chain: A → B → C (2-hop)', () => {
|
|
323
|
+
entry.call('entryFeature');
|
|
324
|
+
});
|
|
325
|
+
});
|
|
326
|
+
allResults.push(...doubleChainResults);
|
|
327
|
+
|
|
328
|
+
// ── Suite 6: Dependency Injection Overhead ──────────────
|
|
329
|
+
const diResults = await createSuite('Dependency Injection — @A_Inject Cost', (suite) => {
|
|
330
|
+
// 0 DI params (baseline)
|
|
331
|
+
const scopeNoDI = new A_Scope({ name: 'no-di', components: [SimpleComponent] });
|
|
332
|
+
const compNoDI = scopeNoDI.resolve(SimpleComponent)!;
|
|
333
|
+
|
|
334
|
+
// 3 DI params (Caller, Scope, Feature) + chain
|
|
335
|
+
const scopeDI3 = new A_Scope({
|
|
336
|
+
name: 'di-3',
|
|
337
|
+
components: [ChainingComponent, TargetComponent]
|
|
338
|
+
});
|
|
339
|
+
const compDI3 = scopeDI3.resolve(ChainingComponent)!;
|
|
340
|
+
|
|
341
|
+
// 5 DI params (Caller, Scope, Feature, ServiceA, ServiceB)
|
|
342
|
+
const scopeDI5 = new A_Scope({
|
|
343
|
+
name: 'di-5',
|
|
344
|
+
components: [HeavyDIComponent, ServiceComponentA, ServiceComponentB]
|
|
345
|
+
});
|
|
346
|
+
const compDI5 = scopeDI5.resolve(HeavyDIComponent)!;
|
|
347
|
+
|
|
348
|
+
suite
|
|
349
|
+
.add('@A_Inject: 0 params (no DI)', () => {
|
|
350
|
+
compNoDI.call('simpleFeature');
|
|
351
|
+
})
|
|
352
|
+
.add('@A_Inject: 3 params (Caller+Scope+Feature)', () => {
|
|
353
|
+
compDI3.call('chainedFeature');
|
|
354
|
+
})
|
|
355
|
+
.add('@A_Inject: 5 params (Caller+Scope+Feature+2 svc)', () => {
|
|
356
|
+
compDI5.call('heavyDIFeature');
|
|
357
|
+
});
|
|
358
|
+
});
|
|
359
|
+
allResults.push(...diResults);
|
|
360
|
+
|
|
361
|
+
// ── Suite 7: Chain + Multiple Extensions ────────────────
|
|
362
|
+
const convergentResults = await createSuite('Chain + Extensions — Convergent Pipeline', (suite) => {
|
|
363
|
+
// Chain with 1 extension
|
|
364
|
+
const scope1 = new A_Scope({
|
|
365
|
+
name: 'conv-1',
|
|
366
|
+
components: [ConvergentComponent, TargetComponent, ExtStepA]
|
|
367
|
+
});
|
|
368
|
+
const conv1 = scope1.resolve(ConvergentComponent)!;
|
|
369
|
+
|
|
370
|
+
// Chain with 3 extensions
|
|
371
|
+
const scope3 = new A_Scope({
|
|
372
|
+
name: 'conv-3',
|
|
373
|
+
components: [ConvergentComponent, TargetComponent, ExtStepA, ExtStepB, ExtStepC]
|
|
374
|
+
});
|
|
375
|
+
const conv3 = scope3.resolve(ConvergentComponent)!;
|
|
376
|
+
|
|
377
|
+
suite
|
|
378
|
+
.add('chain + 1 extension step', () => {
|
|
379
|
+
conv1.call('convergentFeature');
|
|
380
|
+
})
|
|
381
|
+
.add('chain + 3 extension steps', () => {
|
|
382
|
+
conv3.call('convergentFeature');
|
|
383
|
+
});
|
|
384
|
+
});
|
|
385
|
+
allResults.push(...convergentResults);
|
|
386
|
+
|
|
387
|
+
// ── Suite 8: Entity-driven Full Pipeline ────────────────
|
|
388
|
+
const entityPipelineResults = await createSuite('Entity Pipeline — Full Realistic Flow', (suite) => {
|
|
389
|
+
// Warm entity (scope and components already allocated)
|
|
390
|
+
const rootScope = new A_Scope({
|
|
391
|
+
name: 'entity-root',
|
|
392
|
+
components: [EntityFeatureComponent, TargetComponent]
|
|
393
|
+
});
|
|
394
|
+
const warmEntity = new BenchEntity();
|
|
395
|
+
rootScope.register(warmEntity);
|
|
396
|
+
|
|
397
|
+
suite
|
|
398
|
+
.add('entity.call → DI → chain (warm)', () => {
|
|
399
|
+
warmEntity.invokeFeature();
|
|
400
|
+
})
|
|
401
|
+
.add('new entity + register + call + chain (cold)', () => {
|
|
402
|
+
const e = new BenchEntity();
|
|
403
|
+
const s = new A_Scope({
|
|
404
|
+
name: 'cold-scope',
|
|
405
|
+
components: [EntityFeatureComponent, TargetComponent]
|
|
406
|
+
});
|
|
407
|
+
s.register(e);
|
|
408
|
+
e.invokeFeature();
|
|
409
|
+
});
|
|
410
|
+
});
|
|
411
|
+
allResults.push(...entityPipelineResults);
|
|
412
|
+
|
|
413
|
+
// ── Suite 9: Scope Depth Impact on Chain Resolution ─────
|
|
414
|
+
const depthResults = await createSuite('Scope Depth — Impact on Chain Resolution', (suite) => {
|
|
415
|
+
// Flat: all in one scope
|
|
416
|
+
const flatScope = new A_Scope({
|
|
417
|
+
name: 'flat-all',
|
|
418
|
+
components: [ChainingComponent, TargetComponent]
|
|
419
|
+
});
|
|
420
|
+
const flatComp = flatScope.resolve(ChainingComponent)!;
|
|
421
|
+
|
|
422
|
+
// 2-level: target in parent
|
|
423
|
+
const parentScope2 = new A_Scope({ name: 'depth2-parent', components: [TargetComponent] });
|
|
424
|
+
const childScope2 = new A_Scope({ name: 'depth2-child', components: [ChainingComponent] });
|
|
425
|
+
childScope2.inherit(parentScope2);
|
|
426
|
+
const depth2Comp = childScope2.resolve(ChainingComponent)!;
|
|
427
|
+
|
|
428
|
+
// 3-level: target in grandparent
|
|
429
|
+
const gpScope = new A_Scope({ name: 'depth3-gp', components: [TargetComponent] });
|
|
430
|
+
const pScope = new A_Scope({ name: 'depth3-p', components: [] });
|
|
431
|
+
const cScope = new A_Scope({ name: 'depth3-c', components: [ChainingComponent] });
|
|
432
|
+
pScope.inherit(gpScope);
|
|
433
|
+
cScope.inherit(pScope);
|
|
434
|
+
const depth3Comp = cScope.resolve(ChainingComponent)!;
|
|
435
|
+
|
|
436
|
+
suite
|
|
437
|
+
.add('chain — flat scope (depth 0)', () => {
|
|
438
|
+
flatComp.call('chainedFeature');
|
|
439
|
+
})
|
|
440
|
+
.add('chain — nested scope (depth 2)', () => {
|
|
441
|
+
depth2Comp.call('chainedFeature');
|
|
442
|
+
})
|
|
443
|
+
.add('chain — nested scope (depth 3)', () => {
|
|
444
|
+
depth3Comp.call('chainedFeature');
|
|
445
|
+
});
|
|
446
|
+
});
|
|
447
|
+
allResults.push(...depthResults);
|
|
448
|
+
|
|
449
|
+
return allResults;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
|
|
453
|
+
// ──────────────────────────────────────────────────────────────
|
|
454
|
+
// Standalone runner
|
|
455
|
+
// ──────────────────────────────────────────────────────────────
|
|
456
|
+
if (require.main === module) {
|
|
457
|
+
runFeatureChainingBenchmarks()
|
|
458
|
+
.then((results) => {
|
|
459
|
+
const summary = new Map<string, BenchResult[]>();
|
|
460
|
+
summary.set('FeatureChaining', results);
|
|
461
|
+
printSummary(summary);
|
|
462
|
+
console.log('\n✅ Feature chaining benchmarks completed.\n');
|
|
463
|
+
})
|
|
464
|
+
.catch(console.error);
|
|
465
|
+
}
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ============================================================
|
|
3
|
+
* A_Feature Chaining Performance Benchmarks
|
|
4
|
+
* ============================================================
|
|
5
|
+
*
|
|
6
|
+
* Measures realistic end-to-end performance of feature chaining:
|
|
7
|
+
* - Entity creation + scope allocation
|
|
8
|
+
* - Scope registration overhead
|
|
9
|
+
* - Feature invocation through entity.call()
|
|
10
|
+
* - Feature chaining between components (chain())
|
|
11
|
+
* - Dependency injection overhead (@A_Inject)
|
|
12
|
+
* - Scaling with chain depth and component count
|
|
13
|
+
* - Full realistic pipeline: entity → scope → feature → chain → DI
|
|
14
|
+
*/
|
|
15
|
+
import { A_Component } from "@adaas/a-concept/a-component";
|
|
16
|
+
import { A_Feature } from "@adaas/a-concept/a-feature";
|
|
17
|
+
import { A_Scope } from "@adaas/a-concept/a-scope";
|
|
18
|
+
import { A_Inject } from "@adaas/a-concept/a-inject";
|
|
19
|
+
import { A_Caller } from "@adaas/a-concept/a-caller";
|
|
20
|
+
import { A_Entity } from "@adaas/a-concept/a-entity";
|
|
21
|
+
import { A_Context } from "@adaas/a-concept/a-context";
|
|
22
|
+
import { createSuite, BenchResult, printSummary } from './helpers';
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
// ──────────────────────────────────────────────────────────────
|
|
27
|
+
// Fixture: Components — Multiple extensions converging + chain
|
|
28
|
+
// ──────────────────────────────────────────────────────────────
|
|
29
|
+
|
|
30
|
+
export class ExtStepA extends A_Component {
|
|
31
|
+
@A_Feature.Extend({ name: 'componentFeature', scope: [ExtStepA] })
|
|
32
|
+
stepA() {
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
// ──────────────────────────────────────────────────────────────
|
|
38
|
+
// Fixture: Entities
|
|
39
|
+
// ──────────────────────────────────────────────────────────────
|
|
40
|
+
|
|
41
|
+
export class BenchEntity extends A_Entity {
|
|
42
|
+
|
|
43
|
+
protected _scope!: A_Scope;
|
|
44
|
+
|
|
45
|
+
get scope(): A_Scope {
|
|
46
|
+
if (!this._scope) {
|
|
47
|
+
this._scope = A_Context.allocate(this, new A_Scope({ name: `${this.aseid.id}-scope` }));
|
|
48
|
+
}
|
|
49
|
+
return this._scope;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
get targetComponent() {
|
|
53
|
+
return this.scope.resolve(ExtStepA)!;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
entityFeature() {
|
|
57
|
+
return this.call('entityFeature', this.scope);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export class ExtStepB extends A_Component {
|
|
62
|
+
@A_Feature.Extend({ name: 'entityFeature', })
|
|
63
|
+
stepB() {
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export class ExtStepC extends A_Component {
|
|
68
|
+
@A_Feature.Extend({ name: 'entityFeature', })
|
|
69
|
+
stepC() {
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export class ChainingComponent extends A_Component {
|
|
74
|
+
|
|
75
|
+
@A_Feature.Extend({
|
|
76
|
+
name: 'entityFeature',
|
|
77
|
+
after: /.*/,
|
|
78
|
+
})
|
|
79
|
+
convergentStep(
|
|
80
|
+
@A_Inject(A_Caller) entity: BenchEntity,
|
|
81
|
+
@A_Inject(A_Scope) scope: A_Scope,
|
|
82
|
+
@A_Inject(A_Feature) feature: A_Feature
|
|
83
|
+
) {
|
|
84
|
+
feature.chain(entity.targetComponent, 'componentFeature', scope);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
// ──────────────────────────────────────────────────────────────
|
|
91
|
+
// Benchmark Suites
|
|
92
|
+
// ──────────────────────────────────────────────────────────────
|
|
93
|
+
|
|
94
|
+
export async function runFeatureChainingBenchmarks(): Promise<any> {
|
|
95
|
+
const allResults: BenchResult[] = [];
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
// ── Suite 3: Feature Call — Baseline (no chain) ─────────
|
|
100
|
+
const baselineResults = await createSuite('Feature Call — Baseline (no chain)', (suite) => {
|
|
101
|
+
const parentScope = new A_Scope({ name: 'parent-scope', components: [ExtStepA] });
|
|
102
|
+
|
|
103
|
+
const childScope = new A_Scope({
|
|
104
|
+
name: 'base-3',
|
|
105
|
+
components: [ChainingComponent, ExtStepB, ExtStepC]
|
|
106
|
+
}).inherit(parentScope);
|
|
107
|
+
|
|
108
|
+
const entity = new BenchEntity();
|
|
109
|
+
|
|
110
|
+
childScope.register(entity);
|
|
111
|
+
|
|
112
|
+
entity.scope.inherit(childScope);
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
suite
|
|
118
|
+
.add('new Feature instantiation (A_Context.featureTemplate)', () => {
|
|
119
|
+
const definition = A_Context.featureTemplate('entityFeature', entity);
|
|
120
|
+
|
|
121
|
+
})
|
|
122
|
+
.add('new Feature instantiation (new A_Feature)', () => {
|
|
123
|
+
const feature = new A_Feature({
|
|
124
|
+
name: 'entityFeature',
|
|
125
|
+
component: entity,
|
|
126
|
+
scope: entity.scope
|
|
127
|
+
});
|
|
128
|
+
})
|
|
129
|
+
.add('new Feature execution (direct)', () => {
|
|
130
|
+
const feature = new A_Feature({
|
|
131
|
+
name: 'entityFeature',
|
|
132
|
+
component: entity,
|
|
133
|
+
scope: entity.scope
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
for (const stage of feature) {
|
|
137
|
+
stage.process(entity.scope)
|
|
138
|
+
}
|
|
139
|
+
})
|
|
140
|
+
.add('new Feature execution (direct entity.call)', () => {
|
|
141
|
+
|
|
142
|
+
const res = entity.call('entityFeature', entity.scope);
|
|
143
|
+
|
|
144
|
+
if (res instanceof Promise)
|
|
145
|
+
throw new Error('Expected synchronous execution for baseline feature call');
|
|
146
|
+
})
|
|
147
|
+
.add('new Feature execution (wrapped entity.call)', () => {
|
|
148
|
+
|
|
149
|
+
const res = entity.entityFeature();
|
|
150
|
+
|
|
151
|
+
if (res instanceof Promise)
|
|
152
|
+
throw new Error('Expected synchronous execution for baseline feature call');
|
|
153
|
+
|
|
154
|
+
})
|
|
155
|
+
.add('[duplicated 1] new Feature execution (wrapped entity.call)', () => {
|
|
156
|
+
|
|
157
|
+
const res = entity.entityFeature();
|
|
158
|
+
|
|
159
|
+
if (res instanceof Promise)
|
|
160
|
+
throw new Error('Expected synchronous execution for baseline feature call');
|
|
161
|
+
|
|
162
|
+
})
|
|
163
|
+
.add('[duplicated 2] new Feature execution (wrapped entity.call)', () => {
|
|
164
|
+
|
|
165
|
+
const res = entity.entityFeature();
|
|
166
|
+
|
|
167
|
+
if (res instanceof Promise)
|
|
168
|
+
throw new Error('Expected synchronous execution for baseline feature call');
|
|
169
|
+
|
|
170
|
+
})
|
|
171
|
+
.add('[duplicated 3] new Feature execution (wrapped entity.call)', () => {
|
|
172
|
+
|
|
173
|
+
const res = entity.entityFeature();
|
|
174
|
+
if (res instanceof Promise)
|
|
175
|
+
throw new Error('Expected synchronous execution for baseline feature call');
|
|
176
|
+
|
|
177
|
+
})
|
|
178
|
+
.add('[duplicated 4] new Feature execution (wrapped entity.call)', () => {
|
|
179
|
+
|
|
180
|
+
const res = entity.entityFeature();
|
|
181
|
+
|
|
182
|
+
if (res instanceof Promise)
|
|
183
|
+
throw new Error('Expected synchronous execution for baseline feature call');
|
|
184
|
+
|
|
185
|
+
})
|
|
186
|
+
});
|
|
187
|
+
allResults.push(...baselineResults);
|
|
188
|
+
|
|
189
|
+
return allResults;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
// ──────────────────────────────────────────────────────────────
|
|
194
|
+
// Standalone runner
|
|
195
|
+
// ──────────────────────────────────────────────────────────────
|
|
196
|
+
if (require.main === module) {
|
|
197
|
+
runFeatureChainingBenchmarks()
|
|
198
|
+
.then((results) => {
|
|
199
|
+
const summary = new Map<string, BenchResult[]>();
|
|
200
|
+
summary.set('FeatureChaining', results);
|
|
201
|
+
printSummary(summary);
|
|
202
|
+
console.log('\n✅ Feature chaining benchmarks completed.\n');
|
|
203
|
+
})
|
|
204
|
+
.catch(console.error);
|
|
205
|
+
}
|
package/dist/browser/index.d.mts
CHANGED
|
@@ -1084,7 +1084,7 @@ declare class A_Entity<_ConstructorType extends A_TYPES__Entity_Init = A_TYPES__
|
|
|
1084
1084
|
* @param lifecycleMethod
|
|
1085
1085
|
* @param args
|
|
1086
1086
|
*/
|
|
1087
|
-
call(feature: string, scope?: A_Scope): any;
|
|
1087
|
+
call(feature: string, scope?: A_Scope): Promise<any> | void;
|
|
1088
1088
|
/**
|
|
1089
1089
|
* The default method that can be called and extended to load entity data.
|
|
1090
1090
|
*/
|
|
@@ -2013,6 +2013,7 @@ declare class A_Feature<T extends A_TYPES__FeatureAvailableComponents = A_TYPES_
|
|
|
2013
2013
|
* Process stages one by one, ensuring each stage completes before starting the next
|
|
2014
2014
|
*/
|
|
2015
2015
|
private processStagesSequentially;
|
|
2016
|
+
private createStageError;
|
|
2016
2017
|
/**
|
|
2017
2018
|
* This method moves the feature to the next stage
|
|
2018
2019
|
*
|