modestbench 0.0.1
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/CHANGELOG.md +45 -0
- package/LICENSE.md +55 -0
- package/README.md +699 -0
- package/dist/bootstrap.cjs +37 -0
- package/dist/bootstrap.cjs.map +1 -0
- package/dist/bootstrap.d.cts +17 -0
- package/dist/bootstrap.d.cts.map +1 -0
- package/dist/bootstrap.d.ts +17 -0
- package/dist/bootstrap.d.ts.map +1 -0
- package/dist/bootstrap.js +33 -0
- package/dist/bootstrap.js.map +1 -0
- package/dist/cli/commands/history.cjs +459 -0
- package/dist/cli/commands/history.cjs.map +1 -0
- package/dist/cli/commands/history.d.cts +34 -0
- package/dist/cli/commands/history.d.cts.map +1 -0
- package/dist/cli/commands/history.d.ts +34 -0
- package/dist/cli/commands/history.d.ts.map +1 -0
- package/dist/cli/commands/history.js +422 -0
- package/dist/cli/commands/history.js.map +1 -0
- package/dist/cli/commands/init.cjs +566 -0
- package/dist/cli/commands/init.cjs.map +1 -0
- package/dist/cli/commands/init.d.cts +26 -0
- package/dist/cli/commands/init.d.cts.map +1 -0
- package/dist/cli/commands/init.d.ts +26 -0
- package/dist/cli/commands/init.d.ts.map +1 -0
- package/dist/cli/commands/init.js +562 -0
- package/dist/cli/commands/init.js.map +1 -0
- package/dist/cli/commands/run.cjs +285 -0
- package/dist/cli/commands/run.cjs.map +1 -0
- package/dist/cli/commands/run.d.cts +37 -0
- package/dist/cli/commands/run.d.cts.map +1 -0
- package/dist/cli/commands/run.d.ts +37 -0
- package/dist/cli/commands/run.d.ts.map +1 -0
- package/dist/cli/commands/run.js +248 -0
- package/dist/cli/commands/run.js.map +1 -0
- package/dist/cli/index.cjs +523 -0
- package/dist/cli/index.cjs.map +1 -0
- package/dist/cli/index.d.cts +58 -0
- package/dist/cli/index.d.cts.map +1 -0
- package/dist/cli/index.d.ts +58 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +515 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/config/manager.cjs +370 -0
- package/dist/config/manager.cjs.map +1 -0
- package/dist/config/manager.d.cts +46 -0
- package/dist/config/manager.d.cts.map +1 -0
- package/dist/config/manager.d.ts +46 -0
- package/dist/config/manager.d.ts.map +1 -0
- package/dist/config/manager.js +333 -0
- package/dist/config/manager.js.map +1 -0
- package/dist/config/schema.cjs +182 -0
- package/dist/config/schema.cjs.map +1 -0
- package/dist/config/schema.d.cts +51 -0
- package/dist/config/schema.d.cts.map +1 -0
- package/dist/config/schema.d.ts +51 -0
- package/dist/config/schema.d.ts.map +1 -0
- package/dist/config/schema.js +145 -0
- package/dist/config/schema.js.map +1 -0
- package/dist/constants.cjs +22 -0
- package/dist/constants.cjs.map +1 -0
- package/dist/constants.d.cts +10 -0
- package/dist/constants.d.cts.map +1 -0
- package/dist/constants.d.ts +10 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +19 -0
- package/dist/constants.js.map +1 -0
- package/dist/core/benchmark-schema.cjs +135 -0
- package/dist/core/benchmark-schema.cjs.map +1 -0
- package/dist/core/benchmark-schema.d.cts +139 -0
- package/dist/core/benchmark-schema.d.cts.map +1 -0
- package/dist/core/benchmark-schema.d.ts +139 -0
- package/dist/core/benchmark-schema.d.ts.map +1 -0
- package/dist/core/benchmark-schema.js +132 -0
- package/dist/core/benchmark-schema.js.map +1 -0
- package/dist/core/engine.cjs +669 -0
- package/dist/core/engine.cjs.map +1 -0
- package/dist/core/engine.d.cts +128 -0
- package/dist/core/engine.d.cts.map +1 -0
- package/dist/core/engine.d.ts +128 -0
- package/dist/core/engine.d.ts.map +1 -0
- package/dist/core/engine.js +632 -0
- package/dist/core/engine.js.map +1 -0
- package/dist/core/engines/accurate-engine.cjs +292 -0
- package/dist/core/engines/accurate-engine.cjs.map +1 -0
- package/dist/core/engines/accurate-engine.d.cts +63 -0
- package/dist/core/engines/accurate-engine.d.cts.map +1 -0
- package/dist/core/engines/accurate-engine.d.ts +63 -0
- package/dist/core/engines/accurate-engine.d.ts.map +1 -0
- package/dist/core/engines/accurate-engine.js +288 -0
- package/dist/core/engines/accurate-engine.js.map +1 -0
- package/dist/core/engines/index.cjs +21 -0
- package/dist/core/engines/index.cjs.map +1 -0
- package/dist/core/engines/index.d.cts +16 -0
- package/dist/core/engines/index.d.cts.map +1 -0
- package/dist/core/engines/index.d.ts +16 -0
- package/dist/core/engines/index.d.ts.map +1 -0
- package/dist/core/engines/index.js +16 -0
- package/dist/core/engines/index.js.map +1 -0
- package/dist/core/engines/tinybench-engine.cjs +286 -0
- package/dist/core/engines/tinybench-engine.cjs.map +1 -0
- package/dist/core/engines/tinybench-engine.d.cts +18 -0
- package/dist/core/engines/tinybench-engine.d.cts.map +1 -0
- package/dist/core/engines/tinybench-engine.d.ts +18 -0
- package/dist/core/engines/tinybench-engine.d.ts.map +1 -0
- package/dist/core/engines/tinybench-engine.js +282 -0
- package/dist/core/engines/tinybench-engine.js.map +1 -0
- package/dist/core/error-manager.cjs +303 -0
- package/dist/core/error-manager.cjs.map +1 -0
- package/dist/core/error-manager.d.cts +77 -0
- package/dist/core/error-manager.d.cts.map +1 -0
- package/dist/core/error-manager.d.ts +77 -0
- package/dist/core/error-manager.d.ts.map +1 -0
- package/dist/core/error-manager.js +299 -0
- package/dist/core/error-manager.js.map +1 -0
- package/dist/core/loader.cjs +287 -0
- package/dist/core/loader.cjs.map +1 -0
- package/dist/core/loader.d.cts +55 -0
- package/dist/core/loader.d.cts.map +1 -0
- package/dist/core/loader.d.ts +55 -0
- package/dist/core/loader.d.ts.map +1 -0
- package/dist/core/loader.js +250 -0
- package/dist/core/loader.js.map +1 -0
- package/dist/core/stats-utils.cjs +99 -0
- package/dist/core/stats-utils.cjs.map +1 -0
- package/dist/core/stats-utils.d.cts +50 -0
- package/dist/core/stats-utils.d.cts.map +1 -0
- package/dist/core/stats-utils.d.ts +50 -0
- package/dist/core/stats-utils.d.ts.map +1 -0
- package/dist/core/stats-utils.js +94 -0
- package/dist/core/stats-utils.js.map +1 -0
- package/dist/index.cjs +64 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +22 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +30 -0
- package/dist/index.js.map +1 -0
- package/dist/progress/manager.cjs +325 -0
- package/dist/progress/manager.cjs.map +1 -0
- package/dist/progress/manager.d.cts +125 -0
- package/dist/progress/manager.d.cts.map +1 -0
- package/dist/progress/manager.d.ts +125 -0
- package/dist/progress/manager.d.ts.map +1 -0
- package/dist/progress/manager.js +321 -0
- package/dist/progress/manager.js.map +1 -0
- package/dist/reporters/csv.cjs +250 -0
- package/dist/reporters/csv.cjs.map +1 -0
- package/dist/reporters/csv.d.cts +92 -0
- package/dist/reporters/csv.d.cts.map +1 -0
- package/dist/reporters/csv.d.ts +92 -0
- package/dist/reporters/csv.d.ts.map +1 -0
- package/dist/reporters/csv.js +246 -0
- package/dist/reporters/csv.js.map +1 -0
- package/dist/reporters/human.cjs +516 -0
- package/dist/reporters/human.cjs.map +1 -0
- package/dist/reporters/human.d.cts +86 -0
- package/dist/reporters/human.d.cts.map +1 -0
- package/dist/reporters/human.d.ts +86 -0
- package/dist/reporters/human.d.ts.map +1 -0
- package/dist/reporters/human.js +509 -0
- package/dist/reporters/human.js.map +1 -0
- package/dist/reporters/index.cjs +17 -0
- package/dist/reporters/index.cjs.map +1 -0
- package/dist/reporters/index.d.cts +10 -0
- package/dist/reporters/index.d.cts.map +1 -0
- package/dist/reporters/index.d.ts +10 -0
- package/dist/reporters/index.d.ts.map +1 -0
- package/dist/reporters/index.js +10 -0
- package/dist/reporters/index.js.map +1 -0
- package/dist/reporters/json.cjs +215 -0
- package/dist/reporters/json.cjs.map +1 -0
- package/dist/reporters/json.d.cts +79 -0
- package/dist/reporters/json.d.cts.map +1 -0
- package/dist/reporters/json.d.ts +79 -0
- package/dist/reporters/json.d.ts.map +1 -0
- package/dist/reporters/json.js +211 -0
- package/dist/reporters/json.js.map +1 -0
- package/dist/reporters/registry.cjs +255 -0
- package/dist/reporters/registry.cjs.map +1 -0
- package/dist/reporters/registry.d.cts +155 -0
- package/dist/reporters/registry.d.cts.map +1 -0
- package/dist/reporters/registry.d.ts +155 -0
- package/dist/reporters/registry.d.ts.map +1 -0
- package/dist/reporters/registry.js +249 -0
- package/dist/reporters/registry.js.map +1 -0
- package/dist/reporters/simple.cjs +328 -0
- package/dist/reporters/simple.cjs.map +1 -0
- package/dist/reporters/simple.d.cts +51 -0
- package/dist/reporters/simple.d.cts.map +1 -0
- package/dist/reporters/simple.d.ts +51 -0
- package/dist/reporters/simple.d.ts.map +1 -0
- package/dist/reporters/simple.js +321 -0
- package/dist/reporters/simple.js.map +1 -0
- package/dist/schema/modestbench-config.schema.json +162 -0
- package/dist/storage/history.cjs +456 -0
- package/dist/storage/history.cjs.map +1 -0
- package/dist/storage/history.d.cts +99 -0
- package/dist/storage/history.d.cts.map +1 -0
- package/dist/storage/history.d.ts +99 -0
- package/dist/storage/history.d.ts.map +1 -0
- package/dist/storage/history.js +452 -0
- package/dist/storage/history.js.map +1 -0
- package/dist/types/cli.cjs +21 -0
- package/dist/types/cli.cjs.map +1 -0
- package/dist/types/cli.d.cts +296 -0
- package/dist/types/cli.d.cts.map +1 -0
- package/dist/types/cli.d.ts +296 -0
- package/dist/types/cli.d.ts.map +1 -0
- package/dist/types/cli.js +18 -0
- package/dist/types/cli.js.map +1 -0
- package/dist/types/core.cjs +14 -0
- package/dist/types/core.cjs.map +1 -0
- package/dist/types/core.d.cts +380 -0
- package/dist/types/core.d.cts.map +1 -0
- package/dist/types/core.d.ts +380 -0
- package/dist/types/core.d.ts.map +1 -0
- package/dist/types/core.js +13 -0
- package/dist/types/core.js.map +1 -0
- package/dist/types/index.cjs +27 -0
- package/dist/types/index.cjs.map +1 -0
- package/dist/types/index.d.cts +11 -0
- package/dist/types/index.d.cts.map +1 -0
- package/dist/types/index.d.ts +11 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +11 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/interfaces.cjs +10 -0
- package/dist/types/interfaces.cjs.map +1 -0
- package/dist/types/interfaces.d.cts +381 -0
- package/dist/types/interfaces.d.cts.map +1 -0
- package/dist/types/interfaces.d.ts +381 -0
- package/dist/types/interfaces.d.ts.map +1 -0
- package/dist/types/interfaces.js +9 -0
- package/dist/types/interfaces.js.map +1 -0
- package/dist/types/utility.cjs +92 -0
- package/dist/types/utility.cjs.map +1 -0
- package/dist/types/utility.d.cts +330 -0
- package/dist/types/utility.d.cts.map +1 -0
- package/dist/types/utility.d.ts +330 -0
- package/dist/types/utility.d.ts.map +1 -0
- package/dist/types/utility.js +78 -0
- package/dist/types/utility.js.map +1 -0
- package/package.json +211 -0
- package/src/bootstrap.ts +35 -0
- package/src/cli/commands/history.ts +569 -0
- package/src/cli/commands/init.ts +658 -0
- package/src/cli/commands/run.ts +346 -0
- package/src/cli/index.ts +642 -0
- package/src/config/manager.ts +387 -0
- package/src/config/schema.ts +188 -0
- package/src/constants.ts +21 -0
- package/src/core/benchmark-schema.ts +185 -0
- package/src/core/engine.ts +888 -0
- package/src/core/engines/accurate-engine.ts +408 -0
- package/src/core/engines/index.ts +16 -0
- package/src/core/engines/tinybench-engine.ts +335 -0
- package/src/core/error-manager.ts +372 -0
- package/src/core/loader.ts +324 -0
- package/src/core/stats-utils.ts +135 -0
- package/src/index.ts +46 -0
- package/src/progress/manager.ts +415 -0
- package/src/reporters/csv.ts +368 -0
- package/src/reporters/human.ts +707 -0
- package/src/reporters/index.ts +10 -0
- package/src/reporters/json.ts +302 -0
- package/src/reporters/registry.ts +349 -0
- package/src/reporters/simple.ts +459 -0
- package/src/storage/history.ts +600 -0
- package/src/types/cli.ts +312 -0
- package/src/types/core.ts +414 -0
- package/src/types/index.ts +18 -0
- package/src/types/interfaces.ts +451 -0
- package/src/types/utility.ts +446 -0
|
@@ -0,0 +1,669 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* ModestBench Core Engine
|
|
4
|
+
*
|
|
5
|
+
* Main orchestrator for benchmark discovery, validation, and execution.
|
|
6
|
+
* Implements the BenchmarkEngine interface with dependency injection
|
|
7
|
+
* architecture.
|
|
8
|
+
*/
|
|
9
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
12
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
13
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
14
|
+
}
|
|
15
|
+
Object.defineProperty(o, k2, desc);
|
|
16
|
+
}) : (function(o, m, k, k2) {
|
|
17
|
+
if (k2 === undefined) k2 = k;
|
|
18
|
+
o[k2] = m[k];
|
|
19
|
+
}));
|
|
20
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
21
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
22
|
+
}) : function(o, v) {
|
|
23
|
+
o["default"] = v;
|
|
24
|
+
});
|
|
25
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
26
|
+
var ownKeys = function(o) {
|
|
27
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
28
|
+
var ar = [];
|
|
29
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
30
|
+
return ar;
|
|
31
|
+
};
|
|
32
|
+
return ownKeys(o);
|
|
33
|
+
};
|
|
34
|
+
return function (mod) {
|
|
35
|
+
if (mod && mod.__esModule) return mod;
|
|
36
|
+
var result = {};
|
|
37
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
38
|
+
__setModuleDefault(result, mod);
|
|
39
|
+
return result;
|
|
40
|
+
};
|
|
41
|
+
})();
|
|
42
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
43
|
+
exports.ModestBenchEngine = void 0;
|
|
44
|
+
/**
|
|
45
|
+
* Abstract benchmark execution engine with dependency injection
|
|
46
|
+
*
|
|
47
|
+
* Provides generic orchestration logic for benchmark discovery, validation, and
|
|
48
|
+
* execution. Concrete implementations must provide the task execution logic via
|
|
49
|
+
* the executeBenchmarkTask method.
|
|
50
|
+
*/
|
|
51
|
+
class ModestBenchEngine {
|
|
52
|
+
configManager;
|
|
53
|
+
errorManager;
|
|
54
|
+
fileLoader;
|
|
55
|
+
historyStorage;
|
|
56
|
+
progressManager;
|
|
57
|
+
reporterRegistry;
|
|
58
|
+
constructor(dependencies) {
|
|
59
|
+
this.configManager = dependencies.configManager;
|
|
60
|
+
this.fileLoader = dependencies.fileLoader;
|
|
61
|
+
this.reporterRegistry = dependencies.reporterRegistry;
|
|
62
|
+
this.historyStorage = dependencies.historyStorage;
|
|
63
|
+
this.progressManager = dependencies.progressManager;
|
|
64
|
+
this.errorManager = dependencies.errorManager;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Discover benchmark files matching the pattern(s)
|
|
68
|
+
*/
|
|
69
|
+
async discover(pattern, exclude) {
|
|
70
|
+
try {
|
|
71
|
+
return await this.fileLoader.discover(pattern, exclude);
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
const discoveryError = error instanceof Error ? error : new Error(String(error));
|
|
75
|
+
this.errorManager.handleError(discoveryError, {
|
|
76
|
+
metadata: { exclude, pattern },
|
|
77
|
+
phase: 'discovery',
|
|
78
|
+
timestamp: new Date(),
|
|
79
|
+
});
|
|
80
|
+
throw new Error(`File discovery failed: ${discoveryError.message}`);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Execute benchmarks with the given configuration
|
|
85
|
+
*/
|
|
86
|
+
async execute(config, reporters = [], signal) {
|
|
87
|
+
const startTime = new Date();
|
|
88
|
+
let currentPhase = 'discovery';
|
|
89
|
+
try {
|
|
90
|
+
// 1. Merge configuration with defaults
|
|
91
|
+
currentPhase = 'discovery';
|
|
92
|
+
const mergedConfig = await this.configManager.load(undefined, // No specific config path for now
|
|
93
|
+
config);
|
|
94
|
+
// 2. Discover files if not explicitly provided
|
|
95
|
+
const files = config.files ||
|
|
96
|
+
(await this.discover(mergedConfig.pattern, mergedConfig.exclude));
|
|
97
|
+
if (files.length === 0) {
|
|
98
|
+
const error = new Error('No benchmark files found matching the pattern');
|
|
99
|
+
this.errorManager.handleError(error, {
|
|
100
|
+
phase: currentPhase,
|
|
101
|
+
timestamp: new Date(),
|
|
102
|
+
});
|
|
103
|
+
throw error;
|
|
104
|
+
}
|
|
105
|
+
// 3. Validate files
|
|
106
|
+
currentPhase = 'validation';
|
|
107
|
+
const validationResult = await this.validate(files);
|
|
108
|
+
if (!validationResult.valid) {
|
|
109
|
+
const error = new Error(`Validation failed: ${validationResult.errors.map((e) => e.message).join(', ')}`);
|
|
110
|
+
this.errorManager.handleError(error, {
|
|
111
|
+
phase: currentPhase,
|
|
112
|
+
timestamp: new Date(),
|
|
113
|
+
});
|
|
114
|
+
throw error;
|
|
115
|
+
}
|
|
116
|
+
// 4. Initialize progress tracking
|
|
117
|
+
currentPhase = 'setup';
|
|
118
|
+
const runId = this.generateRunId();
|
|
119
|
+
// Pre-calculate total tasks for progress tracking
|
|
120
|
+
let totalTasks = 0;
|
|
121
|
+
let totalSuites = 0;
|
|
122
|
+
for (const filePath of files) {
|
|
123
|
+
try {
|
|
124
|
+
const benchmarkFile = await this.fileLoader.load(filePath);
|
|
125
|
+
const benchmarkDef = benchmarkFile.exports;
|
|
126
|
+
if (benchmarkDef?.suites && typeof benchmarkDef.suites === 'object') {
|
|
127
|
+
const fileTags = benchmarkDef.tags;
|
|
128
|
+
for (const [_suiteName, suiteData] of Object.entries(benchmarkDef.suites)) {
|
|
129
|
+
// Use shared filtering logic
|
|
130
|
+
const { anyTaskMatches, suiteMatches, tasksToRun } = this.getFilteredTasksForSuite(suiteData, fileTags, mergedConfig.tags, mergedConfig.excludeTags);
|
|
131
|
+
// Count suite only if it or any of its tasks match
|
|
132
|
+
if (suiteMatches || anyTaskMatches) {
|
|
133
|
+
totalSuites++;
|
|
134
|
+
totalTasks += tasksToRun.length;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
catch (error) {
|
|
140
|
+
// If we can't load a file for counting, we'll handle it during execution
|
|
141
|
+
// Only show warning if not in quiet mode
|
|
142
|
+
if (!mergedConfig.quiet) {
|
|
143
|
+
console.warn(`Warning: Could not pre-load ${filePath} for task counting:`, error);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
// Create initial run structure for progress tracking
|
|
148
|
+
const gitInfo = await this.getGitInfo();
|
|
149
|
+
const ciInfo = await this.getCiInfo();
|
|
150
|
+
const initialRun = {
|
|
151
|
+
config: mergedConfig,
|
|
152
|
+
duration: 0,
|
|
153
|
+
endTime: startTime,
|
|
154
|
+
environment: await this.getEnvironmentInfo(),
|
|
155
|
+
files: [],
|
|
156
|
+
id: runId,
|
|
157
|
+
startTime,
|
|
158
|
+
...(gitInfo && { git: gitInfo }),
|
|
159
|
+
...(ciInfo && { ci: ciInfo }),
|
|
160
|
+
summary: {
|
|
161
|
+
failedTasks: 0,
|
|
162
|
+
fastest: null,
|
|
163
|
+
overallMean: 0,
|
|
164
|
+
passedTasks: 0,
|
|
165
|
+
slowest: null,
|
|
166
|
+
totalFiles: files.length,
|
|
167
|
+
totalOperations: 0,
|
|
168
|
+
totalSuites,
|
|
169
|
+
totalTasks,
|
|
170
|
+
},
|
|
171
|
+
};
|
|
172
|
+
this.progressManager.initialize(initialRun);
|
|
173
|
+
// Register progress callbacks with reporters that support them
|
|
174
|
+
for (const reporter of reporters) {
|
|
175
|
+
if (typeof reporter.onProgress === 'function') {
|
|
176
|
+
this.progressManager.onProgress((state) => {
|
|
177
|
+
void reporter.onProgress(state);
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
// 5. Call reporter onStart lifecycle method
|
|
182
|
+
await this.callReporters(reporters, 'onStart', initialRun);
|
|
183
|
+
// 6. Execute benchmark files
|
|
184
|
+
currentPhase = 'execution';
|
|
185
|
+
const fileResults = [];
|
|
186
|
+
for (const filePath of files) {
|
|
187
|
+
try {
|
|
188
|
+
// Call reporter onFileStart
|
|
189
|
+
await this.callReporters(reporters, 'onFileStart', filePath);
|
|
190
|
+
const fileResult = await this.executeBenchmarkFile(filePath, mergedConfig, reporters, signal);
|
|
191
|
+
fileResults.push(fileResult);
|
|
192
|
+
// Call reporter onFileEnd
|
|
193
|
+
await this.callReporters(reporters, 'onFileEnd', fileResult);
|
|
194
|
+
// Update progress
|
|
195
|
+
this.progressManager.update({
|
|
196
|
+
currentFile: filePath,
|
|
197
|
+
filesCompleted: fileResults.length,
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
catch (error) {
|
|
201
|
+
const fileError = error instanceof Error ? error : new Error(String(error));
|
|
202
|
+
this.errorManager.handleError(fileError, {
|
|
203
|
+
file: filePath,
|
|
204
|
+
phase: currentPhase,
|
|
205
|
+
timestamp: new Date(),
|
|
206
|
+
});
|
|
207
|
+
// Call reporter onError
|
|
208
|
+
await this.callReporters(reporters, 'onError', fileError);
|
|
209
|
+
// Create error result for this file
|
|
210
|
+
const now = new Date();
|
|
211
|
+
const errorResult = {
|
|
212
|
+
duration: 0,
|
|
213
|
+
endTime: now,
|
|
214
|
+
error: fileError,
|
|
215
|
+
filePath,
|
|
216
|
+
startTime: now,
|
|
217
|
+
suites: [],
|
|
218
|
+
};
|
|
219
|
+
fileResults.push(errorResult);
|
|
220
|
+
// Call reporter onFileEnd for error case
|
|
221
|
+
await this.callReporters(reporters, 'onFileEnd', errorResult);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
// Calculate summary statistics
|
|
225
|
+
const finalTotalSuites = fileResults.reduce((sum, file) => sum + file.suites.length, 0);
|
|
226
|
+
const allTasks = fileResults.flatMap((file) => file.suites.flatMap((suite) => suite.tasks));
|
|
227
|
+
const finalTotalTasks = allTasks.length;
|
|
228
|
+
const failedTasks = allTasks.filter((task) => task.error).length;
|
|
229
|
+
const passedTasks = finalTotalTasks - failedTasks;
|
|
230
|
+
let fastest = null;
|
|
231
|
+
let slowest = null;
|
|
232
|
+
let totalOperations = 0;
|
|
233
|
+
let totalTime = 0;
|
|
234
|
+
for (const task of allTasks) {
|
|
235
|
+
if (!task.error) {
|
|
236
|
+
totalOperations += task.iterations;
|
|
237
|
+
totalTime += task.mean * task.iterations;
|
|
238
|
+
if (!fastest || task.mean < fastest.mean) {
|
|
239
|
+
fastest = task;
|
|
240
|
+
}
|
|
241
|
+
if (!slowest || task.mean > slowest.mean) {
|
|
242
|
+
slowest = task;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
const overallMean = totalOperations > 0 ? totalTime / totalOperations : 0;
|
|
247
|
+
const endTime = new Date();
|
|
248
|
+
const finalRun = {
|
|
249
|
+
...initialRun,
|
|
250
|
+
duration: endTime.getTime() - startTime.getTime(),
|
|
251
|
+
endTime,
|
|
252
|
+
files: fileResults,
|
|
253
|
+
summary: {
|
|
254
|
+
failedTasks,
|
|
255
|
+
fastest,
|
|
256
|
+
overallMean,
|
|
257
|
+
passedTasks,
|
|
258
|
+
slowest,
|
|
259
|
+
totalFiles: files.length,
|
|
260
|
+
totalOperations,
|
|
261
|
+
totalSuites: finalTotalSuites,
|
|
262
|
+
totalTasks: finalTotalTasks,
|
|
263
|
+
},
|
|
264
|
+
};
|
|
265
|
+
// 7. Save to history
|
|
266
|
+
await this.historyStorage.saveRun(finalRun);
|
|
267
|
+
// 8. Call reporter onEnd lifecycle method
|
|
268
|
+
await this.callReporters(reporters, 'onEnd', finalRun);
|
|
269
|
+
// 9. Return completed run
|
|
270
|
+
return finalRun;
|
|
271
|
+
}
|
|
272
|
+
catch (error) {
|
|
273
|
+
const executionError = error instanceof Error ? error : new Error(String(error));
|
|
274
|
+
const handledError = this.errorManager.handleError(executionError, {
|
|
275
|
+
phase: currentPhase,
|
|
276
|
+
timestamp: new Date(),
|
|
277
|
+
});
|
|
278
|
+
// Re-throw the original error with more context
|
|
279
|
+
throw new Error(`Benchmark execution failed: ${handledError.message}`);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* Get the configuration manager
|
|
284
|
+
*/
|
|
285
|
+
getConfigManager() {
|
|
286
|
+
return this.configManager;
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* Get the file loader
|
|
290
|
+
*/
|
|
291
|
+
getFileLoader() {
|
|
292
|
+
return this.fileLoader;
|
|
293
|
+
}
|
|
294
|
+
/**
|
|
295
|
+
* Get the history storage
|
|
296
|
+
*/
|
|
297
|
+
getHistoryStorage() {
|
|
298
|
+
return this.historyStorage;
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
* Get the progress manager
|
|
302
|
+
*/
|
|
303
|
+
getProgressManager() {
|
|
304
|
+
return this.progressManager;
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* Get all available reporters
|
|
308
|
+
*/
|
|
309
|
+
getReporters() {
|
|
310
|
+
return this.reporterRegistry.getAll();
|
|
311
|
+
}
|
|
312
|
+
/**
|
|
313
|
+
* Register a custom reporter
|
|
314
|
+
*/
|
|
315
|
+
registerReporter(name, reporter) {
|
|
316
|
+
this.reporterRegistry.register(name, reporter);
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* Validate benchmark files without executing them
|
|
320
|
+
*/
|
|
321
|
+
async validate(files) {
|
|
322
|
+
try {
|
|
323
|
+
const errors = [];
|
|
324
|
+
const warnings = [];
|
|
325
|
+
const validatedFiles = [];
|
|
326
|
+
// Validate each file
|
|
327
|
+
for (const file of files) {
|
|
328
|
+
try {
|
|
329
|
+
const result = await this.fileLoader.validate(file);
|
|
330
|
+
validatedFiles.push(file);
|
|
331
|
+
errors.push(...result.errors);
|
|
332
|
+
warnings.push(...result.warnings);
|
|
333
|
+
}
|
|
334
|
+
catch (error) {
|
|
335
|
+
const validationError = error instanceof Error ? error : new Error(String(error));
|
|
336
|
+
this.errorManager.handleError(validationError, {
|
|
337
|
+
file,
|
|
338
|
+
phase: 'validation',
|
|
339
|
+
timestamp: new Date(),
|
|
340
|
+
});
|
|
341
|
+
errors.push({
|
|
342
|
+
code: 'FILE_VALIDATION_ERROR',
|
|
343
|
+
file,
|
|
344
|
+
message: `Failed to validate file: ${validationError.message}`,
|
|
345
|
+
severity: 'error',
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
return {
|
|
350
|
+
errors,
|
|
351
|
+
files: validatedFiles,
|
|
352
|
+
valid: errors.length === 0,
|
|
353
|
+
warnings,
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
catch (error) {
|
|
357
|
+
throw new Error(`Validation failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
/**
|
|
361
|
+
* Helper method to call a lifecycle method on all reporters
|
|
362
|
+
*/
|
|
363
|
+
async callReporters(reporters, method, ...args) {
|
|
364
|
+
for (const reporter of reporters) {
|
|
365
|
+
try {
|
|
366
|
+
const reporterMethod = reporter[method];
|
|
367
|
+
if (typeof reporterMethod === 'function') {
|
|
368
|
+
const result = reporterMethod.call(reporter, ...args);
|
|
369
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
370
|
+
if (result && typeof result.then === 'function') {
|
|
371
|
+
await result;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
catch (error) {
|
|
376
|
+
// Log reporter errors but don't fail the benchmark run
|
|
377
|
+
console.error(`Reporter error in ${method}:`, error);
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
/**
|
|
382
|
+
* Execute a single benchmark file and return its results
|
|
383
|
+
*/
|
|
384
|
+
async executeBenchmarkFile(filePath, config, reporters = [], signal) {
|
|
385
|
+
const startTime = new Date();
|
|
386
|
+
try {
|
|
387
|
+
// Load the benchmark file using the file loader
|
|
388
|
+
const benchmarkFile = await this.fileLoader.load(filePath);
|
|
389
|
+
const benchmarkDef = benchmarkFile.exports;
|
|
390
|
+
if (!benchmarkDef || typeof benchmarkDef !== 'object') {
|
|
391
|
+
throw new Error('Benchmark file must export a default object with suites');
|
|
392
|
+
}
|
|
393
|
+
const suiteResults = [];
|
|
394
|
+
const fileTags = benchmarkDef.tags;
|
|
395
|
+
// Process each suite in the file
|
|
396
|
+
if (benchmarkDef.suites && typeof benchmarkDef.suites === 'object') {
|
|
397
|
+
for (const [suiteName, suiteData] of Object.entries(benchmarkDef.suites)) {
|
|
398
|
+
// Use shared filtering logic
|
|
399
|
+
const { anyTaskMatches, suiteMatches } = this.getFilteredTasksForSuite(suiteData, fileTags, config.tags, config.excludeTags);
|
|
400
|
+
// Skip suite only if neither the suite nor any of its tasks match
|
|
401
|
+
if (!suiteMatches && !anyTaskMatches) {
|
|
402
|
+
continue;
|
|
403
|
+
}
|
|
404
|
+
await this.callReporters(reporters, 'onSuiteStart', suiteName);
|
|
405
|
+
const suiteResult = await this.executeBenchmarkSuite(suiteName, suiteData, config, reporters, signal, fileTags);
|
|
406
|
+
await this.callReporters(reporters, 'onSuiteEnd', suiteResult);
|
|
407
|
+
suiteResults.push(suiteResult);
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
const endTime = new Date();
|
|
411
|
+
return {
|
|
412
|
+
config: benchmarkDef.config,
|
|
413
|
+
duration: endTime.getTime() - startTime.getTime(),
|
|
414
|
+
endTime,
|
|
415
|
+
filePath,
|
|
416
|
+
startTime,
|
|
417
|
+
suites: suiteResults,
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
catch (error) {
|
|
421
|
+
const endTime = new Date();
|
|
422
|
+
const executionError = error instanceof Error ? error : new Error(String(error));
|
|
423
|
+
return {
|
|
424
|
+
duration: endTime.getTime() - startTime.getTime(),
|
|
425
|
+
endTime,
|
|
426
|
+
error: executionError,
|
|
427
|
+
filePath,
|
|
428
|
+
startTime,
|
|
429
|
+
suites: [],
|
|
430
|
+
};
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
/**
|
|
434
|
+
* Execute a single benchmark suite and return its results
|
|
435
|
+
*/
|
|
436
|
+
async executeBenchmarkSuite(suiteName, suiteData, config, reporters = [], signal, fileTags) {
|
|
437
|
+
const startTime = new Date();
|
|
438
|
+
try {
|
|
439
|
+
const taskResults = [];
|
|
440
|
+
// Use shared filtering logic to determine which tasks will run
|
|
441
|
+
const { tasksToRun } = this.getFilteredTasksForSuite(suiteData, fileTags, config.tags, config.excludeTags);
|
|
442
|
+
// Only run setup/teardown if there are tasks to execute
|
|
443
|
+
if (tasksToRun.length === 0) {
|
|
444
|
+
// No tasks match the filters, return empty suite result
|
|
445
|
+
const endTime = new Date();
|
|
446
|
+
return {
|
|
447
|
+
duration: endTime.getTime() - startTime.getTime(),
|
|
448
|
+
endTime,
|
|
449
|
+
name: suiteName,
|
|
450
|
+
startTime,
|
|
451
|
+
tasks: [],
|
|
452
|
+
...(suiteData.config !== undefined && { config: suiteData.config }),
|
|
453
|
+
...(suiteData.metadata !== undefined && {
|
|
454
|
+
metadata: suiteData.metadata,
|
|
455
|
+
}),
|
|
456
|
+
...(suiteData.tags !== undefined && { tags: suiteData.tags }),
|
|
457
|
+
};
|
|
458
|
+
}
|
|
459
|
+
// Run suite setup if provided
|
|
460
|
+
if (suiteData.setup && typeof suiteData.setup === 'function') {
|
|
461
|
+
try {
|
|
462
|
+
await suiteData.setup();
|
|
463
|
+
}
|
|
464
|
+
catch (error) {
|
|
465
|
+
const setupError = error instanceof Error
|
|
466
|
+
? error
|
|
467
|
+
: new Error(`Setup failed: ${String(error)}`);
|
|
468
|
+
throw new Error(`Suite setup failed: ${setupError.message}`);
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
try {
|
|
472
|
+
// Process each task that passed filtering
|
|
473
|
+
for (const [taskName, taskData] of tasksToRun) {
|
|
474
|
+
await this.callReporters(reporters, 'onTaskStart', taskName);
|
|
475
|
+
// Mark task as in-progress (shows as 0.5 progress for current task)
|
|
476
|
+
const currentState = this.progressManager.getState();
|
|
477
|
+
this.progressManager.update({
|
|
478
|
+
tasksCompleted: currentState.tasksCompleted + 0.5,
|
|
479
|
+
});
|
|
480
|
+
const taskResult = await this.executeBenchmarkTask(taskName, taskData, config, reporters, signal);
|
|
481
|
+
await this.callReporters(reporters, 'onTaskResult', taskResult);
|
|
482
|
+
taskResults.push(taskResult);
|
|
483
|
+
// Update task-level progress - task is now complete (remove the 0.5 and add 1)
|
|
484
|
+
this.progressManager.update({
|
|
485
|
+
tasksCompleted: currentState.tasksCompleted + 1,
|
|
486
|
+
});
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
finally {
|
|
490
|
+
// Run suite teardown if provided (always runs, even if benchmarks fail)
|
|
491
|
+
if (suiteData.teardown && typeof suiteData.teardown === 'function') {
|
|
492
|
+
try {
|
|
493
|
+
await suiteData.teardown();
|
|
494
|
+
}
|
|
495
|
+
catch (error) {
|
|
496
|
+
// Log teardown errors but don't fail the suite
|
|
497
|
+
const teardownError = error instanceof Error ? error : new Error(String(error));
|
|
498
|
+
console.error(`Warning: Suite teardown failed for "${suiteName}":`, teardownError.message);
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
const endTime = new Date();
|
|
503
|
+
return {
|
|
504
|
+
duration: endTime.getTime() - startTime.getTime(),
|
|
505
|
+
endTime,
|
|
506
|
+
name: suiteName,
|
|
507
|
+
startTime,
|
|
508
|
+
tasks: taskResults,
|
|
509
|
+
...(suiteData.config !== undefined && { config: suiteData.config }),
|
|
510
|
+
...(suiteData.metadata !== undefined && {
|
|
511
|
+
metadata: suiteData.metadata,
|
|
512
|
+
}),
|
|
513
|
+
...(suiteData.tags !== undefined && { tags: suiteData.tags }),
|
|
514
|
+
};
|
|
515
|
+
}
|
|
516
|
+
catch (error) {
|
|
517
|
+
const endTime = new Date();
|
|
518
|
+
const executionError = error instanceof Error ? error : new Error(String(error));
|
|
519
|
+
return {
|
|
520
|
+
duration: endTime.getTime() - startTime.getTime(),
|
|
521
|
+
endTime,
|
|
522
|
+
error: executionError,
|
|
523
|
+
name: suiteName,
|
|
524
|
+
startTime,
|
|
525
|
+
tasks: [],
|
|
526
|
+
};
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
/**
|
|
530
|
+
* Generate a unique run ID
|
|
531
|
+
*/
|
|
532
|
+
generateRunId() {
|
|
533
|
+
return `run-${Date.now()}-${Math.random().toString(36).substring(2, 15)}`;
|
|
534
|
+
}
|
|
535
|
+
/**
|
|
536
|
+
* Get CI/CD information if available
|
|
537
|
+
*/
|
|
538
|
+
async getCiInfo() {
|
|
539
|
+
const process = await Promise.resolve().then(() => __importStar(require('node:process')));
|
|
540
|
+
if (!process.env.CI) {
|
|
541
|
+
return undefined;
|
|
542
|
+
}
|
|
543
|
+
// Detect common CI providers
|
|
544
|
+
if (process.env.GITHUB_ACTIONS) {
|
|
545
|
+
return {
|
|
546
|
+
provider: 'GitHub Actions',
|
|
547
|
+
...(process.env.GITHUB_RUN_NUMBER && {
|
|
548
|
+
buildNumber: process.env.GITHUB_RUN_NUMBER,
|
|
549
|
+
}),
|
|
550
|
+
...(process.env.GITHUB_REPOSITORY &&
|
|
551
|
+
process.env.GITHUB_RUN_ID && {
|
|
552
|
+
buildUrl: `https://github.com/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}`,
|
|
553
|
+
}),
|
|
554
|
+
...(process.env.GITHUB_EVENT_NAME === 'pull_request' &&
|
|
555
|
+
process.env.GITHUB_REF_NAME && {
|
|
556
|
+
pullRequest: process.env.GITHUB_REF_NAME,
|
|
557
|
+
}),
|
|
558
|
+
...(process.env.GITHUB_REF_NAME && {
|
|
559
|
+
branch: process.env.GITHUB_REF_NAME,
|
|
560
|
+
}),
|
|
561
|
+
...(process.env.GITHUB_SHA && { commit: process.env.GITHUB_SHA }),
|
|
562
|
+
};
|
|
563
|
+
}
|
|
564
|
+
// Default CI info
|
|
565
|
+
return {
|
|
566
|
+
provider: 'Unknown CI',
|
|
567
|
+
...(process.env.BRANCH && { branch: process.env.BRANCH }),
|
|
568
|
+
...(process.env.COMMIT && { commit: process.env.COMMIT }),
|
|
569
|
+
};
|
|
570
|
+
}
|
|
571
|
+
/**
|
|
572
|
+
* Get environment information
|
|
573
|
+
*/
|
|
574
|
+
async getEnvironmentInfo() {
|
|
575
|
+
const os = await Promise.resolve().then(() => __importStar(require('node:os')));
|
|
576
|
+
const process = await Promise.resolve().then(() => __importStar(require('node:process')));
|
|
577
|
+
return {
|
|
578
|
+
arch: process.arch,
|
|
579
|
+
availableMemory: os.freemem(),
|
|
580
|
+
cpu: {
|
|
581
|
+
cores: os.cpus().length,
|
|
582
|
+
model: os.cpus()[0]?.model || 'Unknown',
|
|
583
|
+
speed: os.cpus()[0]?.speed || 0,
|
|
584
|
+
},
|
|
585
|
+
env: {
|
|
586
|
+
CI: process.env.CI || 'false',
|
|
587
|
+
NODE_ENV: process.env.NODE_ENV || 'development',
|
|
588
|
+
},
|
|
589
|
+
hostname: os.hostname(),
|
|
590
|
+
memory: {
|
|
591
|
+
free: os.freemem(),
|
|
592
|
+
total: os.totalmem(),
|
|
593
|
+
used: os.totalmem() - os.freemem(),
|
|
594
|
+
},
|
|
595
|
+
nodeVersion: process.version,
|
|
596
|
+
platform: process.platform,
|
|
597
|
+
};
|
|
598
|
+
}
|
|
599
|
+
/**
|
|
600
|
+
* Get filtered tasks for a suite based on tag filtering Returns suite match
|
|
601
|
+
* status and list of tasks to run
|
|
602
|
+
*/
|
|
603
|
+
getFilteredTasksForSuite(suiteData, fileTags, includeTags, excludeTags) {
|
|
604
|
+
// Check if suite itself matches filters
|
|
605
|
+
const mergedSuiteTags = this.mergeTags(fileTags, suiteData.tags);
|
|
606
|
+
const suiteMatches = this.matchesTags(mergedSuiteTags, includeTags, excludeTags);
|
|
607
|
+
// Check which tasks match filters
|
|
608
|
+
const tasksToRun = [];
|
|
609
|
+
if (suiteData.benchmarks && typeof suiteData.benchmarks === 'object') {
|
|
610
|
+
for (const [taskName, taskData] of Object.entries(suiteData.benchmarks)) {
|
|
611
|
+
// Merge task tags with suite and file tags (cascading)
|
|
612
|
+
const mergedTaskTags = this.mergeTags(mergedSuiteTags, taskData.tags);
|
|
613
|
+
// Check if task matches tag filters
|
|
614
|
+
if (this.matchesTags(mergedTaskTags, includeTags, excludeTags)) {
|
|
615
|
+
tasksToRun.push([taskName, taskData]);
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
return {
|
|
620
|
+
anyTaskMatches: tasksToRun.length > 0,
|
|
621
|
+
suiteMatches,
|
|
622
|
+
tasksToRun,
|
|
623
|
+
};
|
|
624
|
+
}
|
|
625
|
+
/**
|
|
626
|
+
* Get Git information if available
|
|
627
|
+
*/
|
|
628
|
+
async getGitInfo() {
|
|
629
|
+
// TODO: Implement Git information extraction
|
|
630
|
+
// This would use child_process to run git commands
|
|
631
|
+
return undefined;
|
|
632
|
+
}
|
|
633
|
+
/**
|
|
634
|
+
* Check if item tags match the filter criteria (OR logic)
|
|
635
|
+
*/
|
|
636
|
+
matchesTags(itemTags, includeTags, excludeTags) {
|
|
637
|
+
const tags = itemTags || [];
|
|
638
|
+
// If exclude tags specified and any match, exclude this item
|
|
639
|
+
if (excludeTags.length > 0 &&
|
|
640
|
+
excludeTags.some((tag) => tags.includes(tag))) {
|
|
641
|
+
return false;
|
|
642
|
+
}
|
|
643
|
+
// If include tags specified, at least one must match
|
|
644
|
+
if (includeTags.length > 0) {
|
|
645
|
+
return includeTags.some((tag) => tags.includes(tag));
|
|
646
|
+
}
|
|
647
|
+
// No filters = include everything
|
|
648
|
+
return true;
|
|
649
|
+
}
|
|
650
|
+
/**
|
|
651
|
+
* Merge tags from parent to child (cascading)
|
|
652
|
+
*/
|
|
653
|
+
mergeTags(parentTags, childTags) {
|
|
654
|
+
const merged = new Set();
|
|
655
|
+
if (parentTags) {
|
|
656
|
+
for (const tag of parentTags) {
|
|
657
|
+
merged.add(tag);
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
if (childTags) {
|
|
661
|
+
for (const tag of childTags) {
|
|
662
|
+
merged.add(tag);
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
return merged.size > 0 ? Array.from(merged) : undefined;
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
exports.ModestBenchEngine = ModestBenchEngine;
|
|
669
|
+
//# sourceMappingURL=engine.js.map
|