@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
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ============================================================
|
|
3
|
+
* A_Feature Lifecycle Performance Benchmarks
|
|
4
|
+
* ============================================================
|
|
5
|
+
*
|
|
6
|
+
* Measures end-to-end performance of feature creation and execution:
|
|
7
|
+
* - Feature initialization from component
|
|
8
|
+
* - Feature initialization from template
|
|
9
|
+
* - Feature process() execution (sync steps)
|
|
10
|
+
* - Full decorator → resolve → template → process pipeline
|
|
11
|
+
* - Scaling with number of extensions
|
|
12
|
+
*/
|
|
13
|
+
import { A_Component } from "@adaas/a-concept/a-component";
|
|
14
|
+
import { A_Feature } from "@adaas/a-concept/a-feature";
|
|
15
|
+
import { A_Scope } from "@adaas/a-concept/a-scope";
|
|
16
|
+
import { A_Dependency } from "@adaas/a-concept/a-dependency";
|
|
17
|
+
import { A_Inject } from "@adaas/a-concept/a-inject";
|
|
18
|
+
import { A_Caller } from "@adaas/a-concept/a-caller";
|
|
19
|
+
import { createSuite, BenchResult } from './helpers';
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
// ──────────────────────────────────────────────────────────────
|
|
23
|
+
// Fixture Components
|
|
24
|
+
// ──────────────────────────────────────────────────────────────
|
|
25
|
+
|
|
26
|
+
// Simple sync component for pure overhead measurement
|
|
27
|
+
class SimpleComponent extends A_Component {
|
|
28
|
+
counter: number = 0;
|
|
29
|
+
|
|
30
|
+
@A_Feature.Define({ invoke: true })
|
|
31
|
+
@A_Feature.Extend({ name: 'simpleFeature' })
|
|
32
|
+
simpleMethod() {
|
|
33
|
+
this.counter++;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Component with multiple sync extensions
|
|
38
|
+
class ExtA extends A_Component {
|
|
39
|
+
@A_Feature.Extend({ name: 'multiFeature' })
|
|
40
|
+
stepA() { }
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
class ExtB extends A_Component {
|
|
44
|
+
@A_Feature.Extend({ name: 'multiFeature' })
|
|
45
|
+
stepB() { }
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
class ExtC extends A_Component {
|
|
49
|
+
@A_Feature.Extend({ name: 'multiFeature' })
|
|
50
|
+
stepC() { }
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
class ExtD extends A_Component {
|
|
54
|
+
@A_Feature.Extend({ name: 'multiFeature' })
|
|
55
|
+
stepD() { }
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
class ExtE extends A_Component {
|
|
59
|
+
@A_Feature.Extend({ name: 'multiFeature' })
|
|
60
|
+
stepE() { }
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Component that defines the multi-feature
|
|
64
|
+
class MultiComponent extends A_Component {
|
|
65
|
+
@A_Feature.Define({ invoke: false })
|
|
66
|
+
async multiFeature() {
|
|
67
|
+
await this.call('multiFeature');
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Inheritance chain for testing feature inheritance overhead
|
|
72
|
+
class BaseComp extends A_Component {
|
|
73
|
+
@A_Feature.Define({ invoke: false })
|
|
74
|
+
async inheritedFeature() {
|
|
75
|
+
await this.call('inheritedFeature');
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
@A_Feature.Extend({ name: 'inheritedFeature' })
|
|
79
|
+
baseStep() { }
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
class ChildComp extends BaseComp {
|
|
83
|
+
@A_Feature.Extend({ name: 'inheritedFeature' })
|
|
84
|
+
childStep() { }
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
class GrandchildComp extends ChildComp { }
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
// ──────────────────────────────────────────────────────────────
|
|
91
|
+
// Benchmark Suites
|
|
92
|
+
// ──────────────────────────────────────────────────────────────
|
|
93
|
+
|
|
94
|
+
export async function runFeatureLifecycleBenchmarks(): Promise<BenchResult[]> {
|
|
95
|
+
const allResults: BenchResult[] = [];
|
|
96
|
+
|
|
97
|
+
// Suite 1: Feature Construction
|
|
98
|
+
const constructionResults = await createSuite('A_Feature — Construction', (suite) => {
|
|
99
|
+
const scope = new A_Scope({ name: 'ConstructScope', components: [SimpleComponent] });
|
|
100
|
+
const comp = scope.resolve(SimpleComponent)!;
|
|
101
|
+
|
|
102
|
+
const templateSteps = [
|
|
103
|
+
{
|
|
104
|
+
name: 'SimpleComponent.simpleMethod',
|
|
105
|
+
dependency: new A_Dependency(SimpleComponent),
|
|
106
|
+
handler: 'simpleMethod',
|
|
107
|
+
}
|
|
108
|
+
];
|
|
109
|
+
|
|
110
|
+
suite
|
|
111
|
+
.add('new A_Feature (from component)', () => {
|
|
112
|
+
new A_Feature({
|
|
113
|
+
name: 'simpleFeature',
|
|
114
|
+
component: comp,
|
|
115
|
+
});
|
|
116
|
+
})
|
|
117
|
+
.add('new A_Feature (from template, 1 step)', () => {
|
|
118
|
+
new A_Feature({
|
|
119
|
+
name: 'benchFeature',
|
|
120
|
+
scope: new A_Scope(),
|
|
121
|
+
template: templateSteps,
|
|
122
|
+
});
|
|
123
|
+
})
|
|
124
|
+
.add('new A_Feature (from template, 5 steps)', () => {
|
|
125
|
+
new A_Feature({
|
|
126
|
+
name: 'benchFeature',
|
|
127
|
+
scope: new A_Scope(),
|
|
128
|
+
template: [
|
|
129
|
+
...templateSteps,
|
|
130
|
+
{ name: 's2', dependency: new A_Dependency(SimpleComponent), handler: 's2' },
|
|
131
|
+
{ name: 's3', dependency: new A_Dependency(SimpleComponent), handler: 's3' },
|
|
132
|
+
{ name: 's4', dependency: new A_Dependency(SimpleComponent), handler: 's4' },
|
|
133
|
+
{ name: 's5', dependency: new A_Dependency(SimpleComponent), handler: 's5' },
|
|
134
|
+
],
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
allResults.push(...constructionResults);
|
|
139
|
+
|
|
140
|
+
// Suite 2: Feature Execution (sync)
|
|
141
|
+
const execResults = await createSuite('A_Feature — Sync Execution', (suite) => {
|
|
142
|
+
// 1-step feature
|
|
143
|
+
const scope1 = new A_Scope({ name: 'ExecScope1', components: [SimpleComponent] });
|
|
144
|
+
const comp1 = scope1.resolve(SimpleComponent)!;
|
|
145
|
+
|
|
146
|
+
// Multi-step feature (3 extensions)
|
|
147
|
+
const scope3 = new A_Scope({
|
|
148
|
+
name: 'ExecScope3',
|
|
149
|
+
components: [MultiComponent, ExtA, ExtB, ExtC]
|
|
150
|
+
});
|
|
151
|
+
const comp3 = scope3.resolve(MultiComponent)!;
|
|
152
|
+
|
|
153
|
+
// Multi-step feature (5 extensions)
|
|
154
|
+
const scope5 = new A_Scope({
|
|
155
|
+
name: 'ExecScope5',
|
|
156
|
+
components: [MultiComponent, ExtA, ExtB, ExtC, ExtD, ExtE]
|
|
157
|
+
});
|
|
158
|
+
const comp5 = scope5.resolve(MultiComponent)!;
|
|
159
|
+
|
|
160
|
+
suite
|
|
161
|
+
.add('call feature (1 sync step)', () => {
|
|
162
|
+
comp1.call('simpleFeature');
|
|
163
|
+
})
|
|
164
|
+
.add('call feature (3 sync extensions)', () => {
|
|
165
|
+
comp3.call('multiFeature');
|
|
166
|
+
})
|
|
167
|
+
.add('call feature (5 sync extensions)', () => {
|
|
168
|
+
comp5.call('multiFeature');
|
|
169
|
+
});
|
|
170
|
+
});
|
|
171
|
+
allResults.push(...execResults);
|
|
172
|
+
|
|
173
|
+
// Suite 3: Full lifecycle — construction + execution
|
|
174
|
+
const fullResults = await createSuite('A_Feature — Full Lifecycle (construct + execute)', (suite) => {
|
|
175
|
+
const scope = new A_Scope({ name: 'FullScope', components: [SimpleComponent] });
|
|
176
|
+
const comp = scope.resolve(SimpleComponent)!;
|
|
177
|
+
|
|
178
|
+
const scope3 = new A_Scope({
|
|
179
|
+
name: 'FullScope3',
|
|
180
|
+
components: [MultiComponent, ExtA, ExtB, ExtC]
|
|
181
|
+
});
|
|
182
|
+
const comp3 = scope3.resolve(MultiComponent)!;
|
|
183
|
+
|
|
184
|
+
suite
|
|
185
|
+
.add('construct + process (1 step)', () => {
|
|
186
|
+
const feature = new A_Feature({
|
|
187
|
+
name: 'simpleFeature',
|
|
188
|
+
component: comp,
|
|
189
|
+
});
|
|
190
|
+
feature.process();
|
|
191
|
+
})
|
|
192
|
+
.add('construct + process (3 extensions)', () => {
|
|
193
|
+
const feature = new A_Feature({
|
|
194
|
+
name: 'multiFeature',
|
|
195
|
+
component: comp3,
|
|
196
|
+
});
|
|
197
|
+
feature.process();
|
|
198
|
+
});
|
|
199
|
+
});
|
|
200
|
+
allResults.push(...fullResults);
|
|
201
|
+
|
|
202
|
+
// Suite 4: Inheritance depth impact on feature lifecycle
|
|
203
|
+
const inheritResults = await createSuite('A_Feature — Inheritance Impact', (suite) => {
|
|
204
|
+
const scopeBase = new A_Scope({ name: 'BaseScope', components: [BaseComp] });
|
|
205
|
+
const compBase = scopeBase.resolve(BaseComp)!;
|
|
206
|
+
|
|
207
|
+
const scopeChild = new A_Scope({ name: 'ChildScope', components: [ChildComp] });
|
|
208
|
+
const compChild = scopeChild.resolve(ChildComp)!;
|
|
209
|
+
|
|
210
|
+
const scopeGrand = new A_Scope({ name: 'GrandScope', components: [GrandchildComp] });
|
|
211
|
+
const compGrand = scopeGrand.resolve(GrandchildComp)!;
|
|
212
|
+
|
|
213
|
+
suite
|
|
214
|
+
.add('construct + process (base class)', () => {
|
|
215
|
+
const feature = new A_Feature({
|
|
216
|
+
name: 'inheritedFeature',
|
|
217
|
+
component: compBase,
|
|
218
|
+
});
|
|
219
|
+
feature.process();
|
|
220
|
+
})
|
|
221
|
+
.add('construct + process (1-level child)', () => {
|
|
222
|
+
const feature = new A_Feature({
|
|
223
|
+
name: 'inheritedFeature',
|
|
224
|
+
component: compChild,
|
|
225
|
+
});
|
|
226
|
+
feature.process();
|
|
227
|
+
})
|
|
228
|
+
.add('construct + process (2-level grandchild)', () => {
|
|
229
|
+
const feature = new A_Feature({
|
|
230
|
+
name: 'inheritedFeature',
|
|
231
|
+
component: compGrand,
|
|
232
|
+
});
|
|
233
|
+
feature.process();
|
|
234
|
+
});
|
|
235
|
+
});
|
|
236
|
+
allResults.push(...inheritResults);
|
|
237
|
+
|
|
238
|
+
return allResults;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
// Run standalone
|
|
243
|
+
if (require.main === module) {
|
|
244
|
+
runFeatureLifecycleBenchmarks().catch(console.error);
|
|
245
|
+
}
|
|
@@ -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
|
+
}
|