@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.
@@ -0,0 +1,415 @@
1
+ /**
2
+ * Feature Lifecycle Profiling Benchmark
3
+ *
4
+ * Realistic profiling of the A_Feature lifecycle using the actual production path:
5
+ * new A_Feature({ name, component, scope }) → feature.process(scope)
6
+ *
7
+ * Uses 25 extension components (≈26 stages total) to simulate a real-world
8
+ * scenario where many components extend the same feature.
9
+ *
10
+ * Two profiling modes:
11
+ * 1. Black-box: times `new A_Feature(...)` and `.process()` as the user sees them
12
+ * 2. Internals breakdown: manually instruments fromComponent() sub-steps
13
+ */
14
+ import { A_Context } from '@adaas/a-concept/a-context';
15
+ import { A_Component } from '@adaas/a-concept/a-component';
16
+ import { A_Container } from '@adaas/a-concept/a-container';
17
+ import { A_Feature } from '@adaas/a-concept/a-feature';
18
+ import { A_Scope } from '@adaas/a-concept/a-scope';
19
+ import { A_Inject } from '@adaas/a-concept/a-inject';
20
+ import { A_Caller } from '@adaas/a-concept/a-caller';
21
+ import { A_StepsManager } from '@adaas/a-concept/a-step-manager';
22
+ import { A_TypeGuards } from '@adaas/a-concept/helpers/A_TypeGuards.helper';
23
+
24
+ // ── helpers ──────────────────────────────────────────────────────
25
+ function hrMs(start: [number, number]): number {
26
+ const diff = process.hrtime(start);
27
+ return diff[0] * 1000 + diff[1] / 1e6;
28
+ }
29
+
30
+ function median(arr: number[]): number {
31
+ const s = [...arr].sort((a, b) => a - b);
32
+ const mid = Math.floor(s.length / 2);
33
+ return s.length % 2 ? s[mid] : (s[mid - 1] + s[mid]) / 2;
34
+ }
35
+
36
+ function p99(arr: number[]): number {
37
+ const s = [...arr].sort((a, b) => a - b);
38
+ return s[Math.floor(s.length * 0.99)];
39
+ }
40
+
41
+ function avg(arr: number[]): number {
42
+ return arr.reduce((a, b) => a + b, 0) / arr.length;
43
+ }
44
+
45
+ // ── dynamic component generation ─────────────────────────────────
46
+ const NUM_EXTENSIONS = 25;
47
+
48
+ /**
49
+ * Dynamically generates N extension component classes that extend the same feature.
50
+ * Each component has a single handler with @A_Inject(A_Caller).
51
+ */
52
+ function generateExtensionComponents(count: number): Array<typeof A_Component> {
53
+ const components: Array<typeof A_Component> = [];
54
+
55
+ for (let i = 0; i < count; i++) {
56
+ // Create a unique class per extension
57
+ const ExtComp = class extends A_Component {
58
+ handler(caller: any) {
59
+ return;
60
+ }
61
+ };
62
+
63
+ // Give it a unique name for debugging
64
+ Object.defineProperty(ExtComp, 'name', { value: `Ext_${i}` });
65
+
66
+ // Apply decorators programmatically (same as @A_Feature.Extend + @A_Inject)
67
+ A_Feature.Extend({ name: 'profileFeature' })(
68
+ ExtComp.prototype,
69
+ 'handler',
70
+ Object.getOwnPropertyDescriptor(ExtComp.prototype, 'handler')!
71
+ );
72
+
73
+ A_Inject(A_Caller)(ExtComp.prototype, 'handler', 0);
74
+
75
+ components.push(ExtComp);
76
+ }
77
+
78
+ return components;
79
+ }
80
+
81
+ // ── setup ────────────────────────────────────────────────────────
82
+ function setupEnvironment() {
83
+ A_Context.reset();
84
+
85
+ class ProfilingComponent extends A_Component {
86
+ @A_Feature.Define({ name: 'profileFeature', invoke: true })
87
+ profileHandler(
88
+ @A_Inject(A_Caller) caller: any
89
+ ) {
90
+ return;
91
+ }
92
+ }
93
+
94
+ const extensionComponents = generateExtensionComponents(NUM_EXTENSIONS);
95
+
96
+ const container = new A_Container({
97
+ name: 'ProfilingContainer',
98
+ components: [ProfilingComponent, ...extensionComponents]
99
+ });
100
+
101
+ const component = container.scope.resolve(ProfilingComponent)!;
102
+ const stageCount = 1 + NUM_EXTENSIONS; // 1 define + N extensions
103
+
104
+ return { container, component, ProfilingComponent, extensionComponents, stageCount };
105
+ }
106
+
107
+ // ── profiling run ────────────────────────────────────────────────
108
+ export async function runFeatureProfilingBenchmarks() {
109
+ const ITERATIONS = 2000;
110
+ const WARMUP = 200;
111
+
112
+ const { container, component, stageCount } = setupEnvironment();
113
+
114
+ // ═══════════════════════════════════════════════════════════════
115
+ // PART 1: Black-box profiling (realistic production path)
116
+ // ═══════════════════════════════════════════════════════════════
117
+ const blackBox = {
118
+ construct: [] as number[],
119
+ process: [] as number[],
120
+ total: [] as number[],
121
+ };
122
+
123
+ for (let i = 0; i < ITERATIONS + WARMUP; i++) {
124
+ const isWarmup = i < WARMUP;
125
+
126
+ const tTotal = process.hrtime();
127
+
128
+ // This is exactly how features are called in production:
129
+ // const newFeature = new A_Feature({ name, component, scope });
130
+ // return await newFeature.process(scope);
131
+ const tConstruct = process.hrtime();
132
+ const feature = new A_Feature({
133
+ name: 'profileFeature',
134
+ component: component,
135
+ });
136
+ const constructTime = hrMs(tConstruct);
137
+
138
+ const tProcess = process.hrtime();
139
+ feature.process();
140
+ const processTime = hrMs(tProcess);
141
+
142
+ const totalTime = hrMs(tTotal);
143
+
144
+ if (!isWarmup) {
145
+ blackBox.construct.push(constructTime);
146
+ blackBox.process.push(processTime);
147
+ blackBox.total.push(totalTime);
148
+ }
149
+ }
150
+
151
+ // ═══════════════════════════════════════════════════════════════
152
+ // PART 2: Internals breakdown (mirrors fromComponent sub-steps)
153
+ // ═══════════════════════════════════════════════════════════════
154
+ const internals = {
155
+ scopeGet: [] as number[],
156
+ callerCreate: [] as number[],
157
+ scopeAllocate: [] as number[],
158
+ scopeInherit: [] as number[],
159
+ featureTemplate: [] as number[],
160
+ stepsManagerCreate: [] as number[],
161
+ toStages: [] as number[],
162
+ totalConstruct: [] as number[],
163
+ // per-stage breakdown (aggregated across all stages in each iteration)
164
+ stageProcess: [] as number[],
165
+ stageGetComponent: [] as number[],
166
+ stageGetArgs: [] as number[],
167
+ stageCallHandler: [] as number[],
168
+ totalProcess: [] as number[],
169
+ scopeDestroy: [] as number[],
170
+ totalLifecycle: [] as number[],
171
+ };
172
+
173
+ for (let i = 0; i < ITERATIONS + WARMUP; i++) {
174
+ const isWarmup = i < WARMUP;
175
+
176
+ // ─── CONSTRUCTION PHASE (mirrors fromComponent) ──────────
177
+ const tTotalStart = process.hrtime();
178
+
179
+ // 1) Get component scope
180
+ const t1 = process.hrtime();
181
+ const componentScope = A_Context.scope(component);
182
+ const scopeGetTime = hrMs(t1);
183
+
184
+ // 2) Create caller
185
+ const t2 = process.hrtime();
186
+ const caller = new A_Caller(component);
187
+ const callerCreateTime = hrMs(t2);
188
+
189
+ // 3) Allocate scope
190
+ const t3 = process.hrtime();
191
+ const featurePlaceholder = Object.create(A_Feature.prototype);
192
+ featurePlaceholder._name = 'profileFeature';
193
+ featurePlaceholder._caller = caller;
194
+ featurePlaceholder._state = 0;
195
+ featurePlaceholder._stages = [];
196
+ featurePlaceholder._index = 0;
197
+ const scope = A_Context.allocate(featurePlaceholder);
198
+ const scopeAllocateTime = hrMs(t3);
199
+
200
+ // 4) Inherit scope
201
+ const t4 = process.hrtime();
202
+ scope.inherit(componentScope);
203
+ const scopeInheritTime = hrMs(t4);
204
+
205
+ // 5) Get feature template (this is the key step — same as fromComponent step 6)
206
+ const t5 = process.hrtime();
207
+ const template = A_Context.featureTemplate('profileFeature', caller.component, scope);
208
+ const featureTemplateTime = hrMs(t5);
209
+
210
+ // 6) Create StepsManager
211
+ const t6 = process.hrtime();
212
+ const sm = new A_StepsManager(template as any[]);
213
+ const stepsManagerCreateTime = hrMs(t6);
214
+
215
+ // 7) Create stages
216
+ const t7 = process.hrtime();
217
+ const stages = sm.toStages(featurePlaceholder);
218
+ const toStagesTime = hrMs(t7);
219
+
220
+ const constructTime = hrMs(tTotalStart);
221
+
222
+ // ─── PROCESSING PHASE (mirrors stage.process internals) ──
223
+ const tProcessStart = process.hrtime();
224
+
225
+ for (const stage of stages) {
226
+ const tStage = process.hrtime();
227
+
228
+ // a) getStepComponent — resolve component instance
229
+ const tComp = process.hrtime();
230
+ const dep = stage.definition.dependency;
231
+ const instance = scope.resolve(dep) || scope.resolve(dep);
232
+ const getComponentTime = hrMs(tComp);
233
+
234
+ // b) getStepArgs — DI resolution
235
+ const tArgs = process.hrtime();
236
+ let resolverConstructor =
237
+ (stage.definition.dependency.target as any)
238
+ || scope.resolveConstructor(stage.definition.dependency.name);
239
+ const injections = A_Context
240
+ .meta(resolverConstructor)
241
+ .injections(stage.definition.handler);
242
+ const resolvedArgs = injections.map((dependency: any) => {
243
+ if (A_TypeGuards.isCallerConstructor(dependency.target)) {
244
+ return caller.component;
245
+ }
246
+ if (A_TypeGuards.isFeatureConstructor(dependency.target)) {
247
+ return featurePlaceholder;
248
+ }
249
+ return scope.resolve(dependency);
250
+ });
251
+ const getArgsTime = hrMs(tArgs);
252
+
253
+ // c) actual handler call
254
+ const tCall = process.hrtime();
255
+ if (instance && (instance as any)[stage.definition.handler]) {
256
+ (instance as any)[stage.definition.handler].call(instance, ...resolvedArgs);
257
+ }
258
+ const callHandlerTime = hrMs(tCall);
259
+
260
+ const stageTime = hrMs(tStage);
261
+
262
+ if (!isWarmup) {
263
+ internals.stageProcess.push(stageTime);
264
+ internals.stageGetComponent.push(getComponentTime);
265
+ internals.stageGetArgs.push(getArgsTime);
266
+ internals.stageCallHandler.push(callHandlerTime);
267
+ }
268
+ }
269
+
270
+ const processTime = hrMs(tProcessStart);
271
+
272
+ // CLEANUP
273
+ const tDestroy = process.hrtime();
274
+ scope.destroy();
275
+ const destroyTime = hrMs(tDestroy);
276
+
277
+ try { A_Context.deallocate(featurePlaceholder); } catch (e) { }
278
+
279
+ const totalTime = hrMs(tTotalStart);
280
+
281
+ if (!isWarmup) {
282
+ internals.scopeGet.push(scopeGetTime);
283
+ internals.callerCreate.push(callerCreateTime);
284
+ internals.scopeAllocate.push(scopeAllocateTime);
285
+ internals.scopeInherit.push(scopeInheritTime);
286
+ internals.featureTemplate.push(featureTemplateTime);
287
+ internals.stepsManagerCreate.push(stepsManagerCreateTime);
288
+ internals.toStages.push(toStagesTime);
289
+ internals.scopeDestroy.push(destroyTime);
290
+ internals.totalConstruct.push(constructTime);
291
+ internals.totalProcess.push(processTime);
292
+ internals.totalLifecycle.push(totalTime);
293
+ }
294
+ }
295
+
296
+ // ── Print results ────────────────────────────────────────
297
+ console.log('\n══════════════════════════════════════════════════════════════════════════════');
298
+ console.log(` 📊 A_Feature Lifecycle — Realistic Profiling (${stageCount} stages: 1 define + ${NUM_EXTENSIONS} extensions)`);
299
+ console.log('══════════════════════════════════════════════════════════════════════════════\n');
300
+
301
+ // ── Part 1: Black-box results ────────────────────────────
302
+ console.log(' ┌─────────────────────────────────────────────────────────────────────────┐');
303
+ console.log(' │ PART 1: Production Path (new A_Feature + .process()) │');
304
+ console.log(' └─────────────────────────────────────────────────────────────────────────┘\n');
305
+
306
+ const bbPhases: [string, keyof typeof blackBox][] = [
307
+ [' new A_Feature({ component })', 'construct'],
308
+ [' feature.process()', 'process'],
309
+ [' ═══ TOTAL', 'total'],
310
+ ];
311
+
312
+ console.log(
313
+ ' Phase'.padEnd(44) +
314
+ 'median'.padStart(10) +
315
+ 'avg'.padStart(10) +
316
+ 'p99'.padStart(10) +
317
+ 'min'.padStart(10) +
318
+ 'max'.padStart(10)
319
+ );
320
+ console.log(' ' + '─'.repeat(90));
321
+
322
+ for (const [label, key] of bbPhases) {
323
+ const arr = blackBox[key];
324
+ const med = median(arr);
325
+ const a = avg(arr);
326
+ const p = p99(arr);
327
+ const mn = Math.min(...arr);
328
+ const mx = Math.max(...arr);
329
+ const pct = (a / avg(blackBox.total) * 100).toFixed(1);
330
+
331
+ console.log(
332
+ ` ${label.padEnd(42)}` +
333
+ `${med.toFixed(4).padStart(10)}` +
334
+ `${a.toFixed(4).padStart(10)}` +
335
+ `${p.toFixed(4).padStart(10)}` +
336
+ `${mn.toFixed(4).padStart(10)}` +
337
+ `${mx.toFixed(4).padStart(10)}` +
338
+ ` (${pct}%)`
339
+ );
340
+ }
341
+
342
+ // ── Part 2: Internals breakdown ──────────────────────────
343
+ console.log('\n\n ┌─────────────────────────────────────────────────────────────────────────┐');
344
+ console.log(' │ PART 2: Internals Breakdown (fromComponent sub-steps) │');
345
+ console.log(' └─────────────────────────────────────────────────────────────────────────┘\n');
346
+
347
+ const phases: [string, string][] = [
348
+ ['CONSTRUCTION PHASE', ''],
349
+ [' A_Context.scope(component)', 'scopeGet'],
350
+ [' new A_Caller()', 'callerCreate'],
351
+ [' A_Context.allocate()', 'scopeAllocate'],
352
+ [' scope.inherit()', 'scopeInherit'],
353
+ [' A_Context.featureTemplate()', 'featureTemplate'],
354
+ [' new A_StepsManager()', 'stepsManagerCreate'],
355
+ [' SM.toStages()', 'toStages'],
356
+ [' ─── Construction Total', 'totalConstruct'],
357
+ ['', ''],
358
+ [`PROCESSING PHASE (per-stage avg, ${stageCount} stages)`, ''],
359
+ [' getStepComponent (resolve)', 'stageGetComponent'],
360
+ [' getStepArgs (DI injection)', 'stageGetArgs'],
361
+ [' handler call', 'stageCallHandler'],
362
+ [' ─── Stage Total (per-stage)', 'stageProcess'],
363
+ [' ─── All Stages Total', 'totalProcess'],
364
+ ['', ''],
365
+ ['CLEANUP PHASE', ''],
366
+ [' scope.destroy()', 'scopeDestroy'],
367
+ ['', ''],
368
+ ['═══ TOTAL LIFECYCLE', 'totalLifecycle'],
369
+ ];
370
+
371
+ console.log(
372
+ ' Phase'.padEnd(44) +
373
+ 'median'.padStart(10) +
374
+ 'avg'.padStart(10) +
375
+ 'p99'.padStart(10) +
376
+ 'min'.padStart(10) +
377
+ 'max'.padStart(10)
378
+ );
379
+ console.log(' ' + '─'.repeat(90));
380
+
381
+ for (const [label, key] of phases) {
382
+ if (!key) {
383
+ if (label) console.log(`\n ${label}`);
384
+ continue;
385
+ }
386
+ const arr = (internals as any)[key] as number[];
387
+ const med = median(arr);
388
+ const a = avg(arr);
389
+ const p = p99(arr);
390
+ const mn = Math.min(...arr);
391
+ const mx = Math.max(...arr);
392
+
393
+ const pctOfTotal = (a / avg(internals.totalLifecycle) * 100).toFixed(1);
394
+
395
+ console.log(
396
+ ` ${label.padEnd(42)}` +
397
+ `${med.toFixed(4).padStart(10)}` +
398
+ `${a.toFixed(4).padStart(10)}` +
399
+ `${p.toFixed(4).padStart(10)}` +
400
+ `${mn.toFixed(4).padStart(10)}` +
401
+ `${mx.toFixed(4).padStart(10)}` +
402
+ ` (${pctOfTotal}%)`
403
+ );
404
+ }
405
+
406
+ console.log('\n ' + '─'.repeat(90));
407
+ console.log(` Iterations: ${ITERATIONS} (after ${WARMUP} warmup)`);
408
+ console.log(` Stages per feature: ${stageCount} (1 define + ${NUM_EXTENSIONS} extensions)`);
409
+ console.log(` Production path: new A_Feature({ name, component }) → feature.process()\n`);
410
+ }
411
+
412
+ // Run standalone
413
+ if (require.main === module) {
414
+ runFeatureProfilingBenchmarks().catch(console.error);
415
+ }
@@ -0,0 +1,211 @@
1
+ /**
2
+ * ============================================================
3
+ * A_Context.featureTemplate Performance Benchmarks
4
+ * ============================================================
5
+ *
6
+ * Measures:
7
+ * - featureTemplate() — full template assembly
8
+ * - featureDefinition() — base definition lookup
9
+ * - featureExtensions() — extension collection from scope
10
+ * - Scaling with number of components in scope
11
+ */
12
+ import { A_Component } from "@adaas/a-concept/a-component";
13
+ import { A_Feature } from "@adaas/a-concept/a-feature";
14
+ import { A_Scope } from "@adaas/a-concept/a-scope";
15
+ import { A_Context } from "@adaas/a-concept/a-context";
16
+ import { A_Inject } from "@adaas/a-concept/a-inject";
17
+ import { A_Caller } from "@adaas/a-concept/a-caller";
18
+ import { createSuite, BenchResult } from './helpers';
19
+
20
+
21
+ // ──────────────────────────────────────────────────────────────
22
+ // Fixture: components with feature definitions & extensions
23
+ // ──────────────────────────────────────────────────────────────
24
+
25
+ class CoreComponent extends A_Component {
26
+ @A_Feature.Define({ invoke: true })
27
+ @A_Feature.Extend({ name: 'coreFeature' })
28
+ async coreMethod() { }
29
+ }
30
+
31
+ class ExtensionComponent_A extends A_Component {
32
+ @A_Feature.Extend({ name: 'coreFeature' })
33
+ async extensionA() { }
34
+ }
35
+
36
+ class ExtensionComponent_B extends A_Component {
37
+ @A_Feature.Extend({ name: 'coreFeature' })
38
+ async extensionB() { }
39
+ }
40
+
41
+ class ExtensionComponent_C extends A_Component {
42
+ @A_Feature.Extend({ name: 'coreFeature' })
43
+ async extensionC() { }
44
+ }
45
+
46
+ class ExtensionComponent_D extends A_Component {
47
+ @A_Feature.Extend({ name: 'coreFeature' })
48
+ async extensionD() { }
49
+ }
50
+
51
+ class ExtensionComponent_E extends A_Component {
52
+ @A_Feature.Extend({ name: 'coreFeature' })
53
+ async extensionE() { }
54
+ }
55
+
56
+
57
+ // --- For inheritance tests ---
58
+
59
+ class ParentComponent extends A_Component {
60
+ @A_Feature.Define({ invoke: false })
61
+ async parentFeature() { }
62
+
63
+ @A_Feature.Extend({ name: 'parentFeature' })
64
+ async parentStep() { }
65
+ }
66
+
67
+ class ChildComponent extends ParentComponent {
68
+ @A_Feature.Extend({ name: 'parentFeature' })
69
+ async childStep() { }
70
+ }
71
+
72
+ class GrandchildComponent extends ChildComponent { }
73
+
74
+
75
+ // --- For multi-feature tests ---
76
+
77
+ class MultiFeatureComponent extends A_Component {
78
+ @A_Feature.Define({ invoke: true })
79
+ @A_Feature.Extend({ name: 'feature1' })
80
+ async feature1() { }
81
+
82
+ @A_Feature.Define({ invoke: true })
83
+ @A_Feature.Extend({ name: 'feature2' })
84
+ async feature2() { }
85
+
86
+ @A_Feature.Define({ invoke: true })
87
+ @A_Feature.Extend({ name: 'feature3' })
88
+ async feature3() { }
89
+ }
90
+
91
+
92
+ // ──────────────────────────────────────────────────────────────
93
+ // Benchmark Suites
94
+ // ──────────────────────────────────────────────────────────────
95
+
96
+ export async function runFeatureTemplateBenchmarks(): Promise<BenchResult[]> {
97
+ const allResults: BenchResult[] = [];
98
+
99
+ // Suite 1: featureTemplate with varying scope sizes
100
+ const templateResults = await createSuite('A_Context.featureTemplate — Scope Size', (suite) => {
101
+ // Small scope: 2 components
102
+ const scopeSmall = new A_Scope({
103
+ name: 'SmallScope',
104
+ components: [CoreComponent, ExtensionComponent_A]
105
+ });
106
+ const compSmall = scopeSmall.resolve(CoreComponent)!;
107
+
108
+ // Medium scope: 4 components
109
+ const scopeMedium = new A_Scope({
110
+ name: 'MediumScope',
111
+ components: [CoreComponent, ExtensionComponent_A, ExtensionComponent_B, ExtensionComponent_C]
112
+ });
113
+ const compMedium = scopeMedium.resolve(CoreComponent)!;
114
+
115
+ // Large scope: 6 components
116
+ const scopeLarge = new A_Scope({
117
+ name: 'LargeScope',
118
+ components: [
119
+ CoreComponent,
120
+ ExtensionComponent_A,
121
+ ExtensionComponent_B,
122
+ ExtensionComponent_C,
123
+ ExtensionComponent_D,
124
+ ExtensionComponent_E
125
+ ]
126
+ });
127
+ const compLarge = scopeLarge.resolve(CoreComponent)!;
128
+
129
+ suite
130
+ .add('featureTemplate (2 components)', () => {
131
+ A_Context.featureTemplate('coreFeature', compSmall, scopeSmall);
132
+ })
133
+ .add('featureTemplate (4 components)', () => {
134
+ A_Context.featureTemplate('coreFeature', compMedium, scopeMedium);
135
+ })
136
+ .add('featureTemplate (6 components)', () => {
137
+ A_Context.featureTemplate('coreFeature', compLarge, scopeLarge);
138
+ });
139
+ });
140
+ allResults.push(...templateResults);
141
+
142
+ // Suite 2: featureDefinition vs featureExtensions breakdown
143
+ const breakdownResults = await createSuite('A_Context — Definition vs Extensions breakdown', (suite) => {
144
+ const scope = new A_Scope({
145
+ name: 'BreakdownScope',
146
+ components: [
147
+ CoreComponent,
148
+ ExtensionComponent_A,
149
+ ExtensionComponent_B,
150
+ ExtensionComponent_C,
151
+ ]
152
+ });
153
+ const comp = scope.resolve(CoreComponent)!;
154
+
155
+ suite
156
+ .add('featureDefinition only', () => {
157
+ A_Context.featureDefinition('coreFeature', comp);
158
+ })
159
+ .add('featureExtensions only', () => {
160
+ A_Context.featureExtensions('coreFeature', comp, scope);
161
+ })
162
+ .add('featureTemplate (combined)', () => {
163
+ A_Context.featureTemplate('coreFeature', comp, scope);
164
+ });
165
+ });
166
+ allResults.push(...breakdownResults);
167
+
168
+ // Suite 3: Inheritance depth impact
169
+ const inheritanceResults = await createSuite('A_Context.featureTemplate — Inheritance Depth', (suite) => {
170
+ // Direct component
171
+ const scopeDirect = new A_Scope({
172
+ name: 'DirectScope',
173
+ components: [ParentComponent]
174
+ });
175
+ const compDirect = scopeDirect.resolve(ParentComponent)!;
176
+
177
+ // 1-level inheritance
178
+ const scopeChild = new A_Scope({
179
+ name: 'ChildScope',
180
+ components: [ChildComponent]
181
+ });
182
+ const compChild = scopeChild.resolve(ChildComponent)!;
183
+
184
+ // 2-level inheritance
185
+ const scopeGrandchild = new A_Scope({
186
+ name: 'GrandchildScope',
187
+ components: [GrandchildComponent]
188
+ });
189
+ const compGrandchild = scopeGrandchild.resolve(GrandchildComponent)!;
190
+
191
+ suite
192
+ .add('featureTemplate (no inheritance)', () => {
193
+ A_Context.featureTemplate('parentFeature', compDirect, scopeDirect);
194
+ })
195
+ .add('featureTemplate (1-level inherit)', () => {
196
+ A_Context.featureTemplate('parentFeature', compChild, scopeChild);
197
+ })
198
+ .add('featureTemplate (2-level inherit)', () => {
199
+ A_Context.featureTemplate('parentFeature', compGrandchild, scopeGrandchild);
200
+ });
201
+ });
202
+ allResults.push(...inheritanceResults);
203
+
204
+ return allResults;
205
+ }
206
+
207
+
208
+ // Run standalone
209
+ if (require.main === module) {
210
+ runFeatureTemplateBenchmarks().catch(console.error);
211
+ }