modestbench 0.0.2 → 0.1.0
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 +25 -0
- package/README.md +21 -19
- package/dist/bootstrap.cjs +0 -2
- package/dist/bootstrap.cjs.map +1 -1
- package/dist/bootstrap.d.cts.map +1 -1
- package/dist/bootstrap.d.ts.map +1 -1
- package/dist/bootstrap.js +0 -2
- package/dist/bootstrap.js.map +1 -1
- package/dist/cli/commands/history.cjs +2 -1
- package/dist/cli/commands/history.cjs.map +1 -1
- package/dist/cli/commands/history.d.cts.map +1 -1
- package/dist/cli/commands/history.d.ts.map +1 -1
- package/dist/cli/commands/history.js +2 -1
- package/dist/cli/commands/history.js.map +1 -1
- package/dist/cli/commands/init.cjs +5 -4
- package/dist/cli/commands/init.cjs.map +1 -1
- package/dist/cli/commands/init.d.cts.map +1 -1
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +5 -4
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/run.cjs +28 -3
- package/dist/cli/commands/run.cjs.map +1 -1
- package/dist/cli/commands/run.d.cts.map +1 -1
- package/dist/cli/commands/run.d.ts.map +1 -1
- package/dist/cli/commands/run.js +28 -3
- package/dist/cli/commands/run.js.map +1 -1
- package/dist/cli/index.cjs +38 -14
- package/dist/cli/index.cjs.map +1 -1
- package/dist/cli/index.d.cts +1 -2
- package/dist/cli/index.d.cts.map +1 -1
- package/dist/cli/index.d.ts +1 -2
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +33 -9
- package/dist/cli/index.js.map +1 -1
- package/dist/config/manager.cjs +9 -3
- package/dist/config/manager.cjs.map +1 -1
- package/dist/config/manager.d.cts.map +1 -1
- package/dist/config/manager.d.ts.map +1 -1
- package/dist/config/manager.js +9 -3
- package/dist/config/manager.js.map +1 -1
- package/dist/constants.cjs +53 -1
- package/dist/constants.cjs.map +1 -1
- package/dist/constants.d.cts +36 -0
- package/dist/constants.d.cts.map +1 -1
- package/dist/constants.d.ts +36 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +52 -0
- package/dist/constants.js.map +1 -1
- package/dist/core/engine.cjs +19 -42
- package/dist/core/engine.cjs.map +1 -1
- package/dist/core/engine.d.cts +1 -3
- package/dist/core/engine.d.cts.map +1 -1
- package/dist/core/engine.d.ts +1 -3
- package/dist/core/engine.d.ts.map +1 -1
- package/dist/core/engine.js +19 -42
- package/dist/core/engine.js.map +1 -1
- package/dist/core/engines/accurate-engine.cjs +2 -1
- package/dist/core/engines/accurate-engine.cjs.map +1 -1
- package/dist/core/engines/accurate-engine.d.cts.map +1 -1
- package/dist/core/engines/accurate-engine.d.ts.map +1 -1
- package/dist/core/engines/accurate-engine.js +2 -1
- package/dist/core/engines/accurate-engine.js.map +1 -1
- package/dist/core/engines/tinybench-engine.cjs +6 -5
- package/dist/core/engines/tinybench-engine.cjs.map +1 -1
- package/dist/core/engines/tinybench-engine.d.cts.map +1 -1
- package/dist/core/engines/tinybench-engine.d.ts.map +1 -1
- package/dist/core/engines/tinybench-engine.js +6 -5
- package/dist/core/engines/tinybench-engine.js.map +1 -1
- package/dist/core/loader.cjs +16 -5
- package/dist/core/loader.cjs.map +1 -1
- package/dist/core/loader.d.cts.map +1 -1
- package/dist/core/loader.d.ts.map +1 -1
- package/dist/core/loader.js +16 -5
- package/dist/core/loader.js.map +1 -1
- package/dist/errors/base.cjs +130 -0
- package/dist/errors/base.cjs.map +1 -0
- package/dist/errors/base.d.cts +97 -0
- package/dist/errors/base.d.cts.map +1 -0
- package/dist/errors/base.d.ts +97 -0
- package/dist/errors/base.d.ts.map +1 -0
- package/dist/errors/base.js +124 -0
- package/dist/errors/base.js.map +1 -0
- package/dist/errors/cli.cjs +58 -0
- package/dist/errors/cli.cjs.map +1 -0
- package/dist/errors/cli.d.cts +44 -0
- package/dist/errors/cli.d.cts.map +1 -0
- package/dist/errors/cli.d.ts +44 -0
- package/dist/errors/cli.d.ts.map +1 -0
- package/dist/errors/cli.js +52 -0
- package/dist/errors/cli.js.map +1 -0
- package/dist/errors/configuration.cjs +48 -0
- package/dist/errors/configuration.cjs.map +1 -0
- package/dist/errors/configuration.d.cts +41 -0
- package/dist/errors/configuration.d.cts.map +1 -0
- package/dist/errors/configuration.d.ts +41 -0
- package/dist/errors/configuration.d.ts.map +1 -0
- package/dist/errors/configuration.js +41 -0
- package/dist/errors/configuration.js.map +1 -0
- package/dist/errors/execution.cjs +65 -0
- package/dist/errors/execution.cjs.map +1 -0
- package/dist/errors/execution.d.cts +56 -0
- package/dist/errors/execution.d.cts.map +1 -0
- package/dist/errors/execution.d.ts +56 -0
- package/dist/errors/execution.d.ts.map +1 -0
- package/dist/errors/execution.js +56 -0
- package/dist/errors/execution.js.map +1 -0
- package/dist/errors/file.cjs +56 -0
- package/dist/errors/file.cjs.map +1 -0
- package/dist/errors/file.d.cts +48 -0
- package/dist/errors/file.d.cts.map +1 -0
- package/dist/errors/file.d.ts +48 -0
- package/dist/errors/file.d.ts.map +1 -0
- package/dist/errors/file.js +48 -0
- package/dist/errors/file.js.map +1 -0
- package/dist/errors/index.cjs +59 -0
- package/dist/errors/index.cjs.map +1 -0
- package/dist/errors/index.d.cts +16 -0
- package/dist/errors/index.d.cts.map +1 -0
- package/dist/errors/index.d.ts +16 -0
- package/dist/errors/index.d.ts.map +1 -0
- package/dist/errors/index.js +24 -0
- package/dist/errors/index.js.map +1 -0
- package/dist/errors/reporter.cjs +38 -0
- package/dist/errors/reporter.cjs.map +1 -0
- package/dist/errors/reporter.d.cts +32 -0
- package/dist/errors/reporter.d.cts.map +1 -0
- package/dist/errors/reporter.d.ts +32 -0
- package/dist/errors/reporter.d.ts.map +1 -0
- package/dist/errors/reporter.js +32 -0
- package/dist/errors/reporter.js.map +1 -0
- package/dist/errors/storage.cjs +55 -0
- package/dist/errors/storage.cjs.map +1 -0
- package/dist/errors/storage.d.cts +47 -0
- package/dist/errors/storage.d.cts.map +1 -0
- package/dist/errors/storage.d.ts +47 -0
- package/dist/errors/storage.d.ts.map +1 -0
- package/dist/errors/storage.js +47 -0
- package/dist/errors/storage.js.map +1 -0
- package/dist/errors/validation.cjs +38 -0
- package/dist/errors/validation.cjs.map +1 -0
- package/dist/errors/validation.d.cts +32 -0
- package/dist/errors/validation.d.cts.map +1 -0
- package/dist/errors/validation.d.ts +32 -0
- package/dist/errors/validation.d.ts.map +1 -0
- package/dist/errors/validation.js +32 -0
- package/dist/errors/validation.js.map +1 -0
- package/dist/index.cjs +3 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/reporters/csv.cjs +3 -2
- package/dist/reporters/csv.cjs.map +1 -1
- package/dist/reporters/csv.d.cts.map +1 -1
- package/dist/reporters/csv.d.ts.map +1 -1
- package/dist/reporters/csv.js +3 -2
- package/dist/reporters/csv.js.map +1 -1
- package/dist/reporters/human.cjs +2 -2
- package/dist/reporters/human.js +2 -2
- package/dist/reporters/json.cjs +3 -2
- package/dist/reporters/json.cjs.map +1 -1
- package/dist/reporters/json.d.cts.map +1 -1
- package/dist/reporters/json.d.ts.map +1 -1
- package/dist/reporters/json.js +3 -2
- package/dist/reporters/json.js.map +1 -1
- package/dist/reporters/registry.cjs +3 -2
- package/dist/reporters/registry.cjs.map +1 -1
- package/dist/reporters/registry.d.cts.map +1 -1
- package/dist/reporters/registry.d.ts.map +1 -1
- package/dist/reporters/registry.js +3 -2
- package/dist/reporters/registry.js.map +1 -1
- package/dist/reporters/simple.cjs +1 -1
- package/dist/reporters/simple.js +1 -1
- package/dist/storage/history.cjs +32 -11
- package/dist/storage/history.cjs.map +1 -1
- package/dist/storage/history.d.cts.map +1 -1
- package/dist/storage/history.d.ts.map +1 -1
- package/dist/storage/history.js +32 -11
- package/dist/storage/history.js.map +1 -1
- package/dist/types/interfaces.d.cts +1 -34
- package/dist/types/interfaces.d.cts.map +1 -1
- package/dist/types/interfaces.d.ts +1 -34
- package/dist/types/interfaces.d.ts.map +1 -1
- package/package.json +12 -8
- package/src/bootstrap.ts +0 -2
- package/src/cli/commands/history.ts +3 -1
- package/src/cli/commands/init.ts +14 -4
- package/src/cli/commands/run.ts +36 -3
- package/src/cli/index.ts +41 -15
- package/src/config/manager.ts +13 -3
- package/src/constants.ts +60 -0
- package/src/core/engine.ts +35 -49
- package/src/core/engines/accurate-engine.ts +4 -1
- package/src/core/engines/tinybench-engine.ts +12 -5
- package/src/core/loader.ts +27 -5
- package/src/errors/base.ts +152 -0
- package/src/errors/cli.ts +59 -0
- package/src/errors/configuration.ts +45 -0
- package/src/errors/execution.ts +62 -0
- package/src/errors/file.ts +53 -0
- package/src/errors/index.ts +71 -0
- package/src/errors/reporter.ts +35 -0
- package/src/errors/storage.ts +52 -0
- package/src/errors/validation.ts +35 -0
- package/src/index.ts +3 -3
- package/src/reporters/csv.ts +4 -2
- package/src/reporters/human.ts +2 -2
- package/src/reporters/json.ts +4 -2
- package/src/reporters/registry.ts +9 -2
- package/src/reporters/simple.ts +1 -1
- package/src/storage/history.ts +58 -11
- package/src/types/interfaces.ts +0 -43
- package/dist/core/error-manager.cjs +0 -303
- package/dist/core/error-manager.cjs.map +0 -1
- package/dist/core/error-manager.d.cts +0 -77
- package/dist/core/error-manager.d.cts.map +0 -1
- package/dist/core/error-manager.d.ts +0 -77
- package/dist/core/error-manager.d.ts.map +0 -1
- package/dist/core/error-manager.js +0 -299
- package/dist/core/error-manager.js.map +0 -1
- package/src/core/error-manager.ts +0 -372
package/src/cli/commands/run.ts
CHANGED
|
@@ -10,6 +10,12 @@ import { resolve } from 'node:path';
|
|
|
10
10
|
import type { BenchmarkRun } from '../../types/index.js';
|
|
11
11
|
import type { CliContext } from '../index.js';
|
|
12
12
|
|
|
13
|
+
import { ErrorCodes } from '../../constants.js';
|
|
14
|
+
import {
|
|
15
|
+
InvalidArgumentError,
|
|
16
|
+
type ModestBenchError,
|
|
17
|
+
UnknownReporterError,
|
|
18
|
+
} from '../../errors/index.js';
|
|
13
19
|
import { ExitCodes } from '../../types/cli.js';
|
|
14
20
|
|
|
15
21
|
/**
|
|
@@ -89,6 +95,18 @@ export const handleRunCommand = async (
|
|
|
89
95
|
console.error(`Found ${discoveredFiles.length} benchmark file(s)`);
|
|
90
96
|
}
|
|
91
97
|
|
|
98
|
+
// Check if no files found and throw to trigger help display
|
|
99
|
+
if (discoveredFiles.length === 0) {
|
|
100
|
+
let msg = `No benchmark files found matching pattern "${config.pattern}"`;
|
|
101
|
+
if (config.exclude?.length) {
|
|
102
|
+
msg += ` (excluding: ${config.exclude.join(', ')})`;
|
|
103
|
+
}
|
|
104
|
+
// Throw error to trigger yargs fail handler which shows help
|
|
105
|
+
const error = new Error(msg);
|
|
106
|
+
error.name = 'FileDiscoveryError';
|
|
107
|
+
throw error;
|
|
108
|
+
}
|
|
109
|
+
|
|
92
110
|
// Step 4: Validation phase
|
|
93
111
|
if (showCliMessages) {
|
|
94
112
|
console.error('Validating benchmark files...');
|
|
@@ -143,6 +161,11 @@ export const handleRunCommand = async (
|
|
|
143
161
|
|
|
144
162
|
return handleResults(executionResult, options, shouldBeQuiet);
|
|
145
163
|
} catch (error) {
|
|
164
|
+
// Re-throw FileDiscoveryError so yargs fail handler can show help
|
|
165
|
+
if ((error as ModestBenchError).code === ErrorCodes.FILE_DISCOVERY_FAILED) {
|
|
166
|
+
throw error;
|
|
167
|
+
}
|
|
168
|
+
|
|
146
169
|
if (!shouldBeQuiet) {
|
|
147
170
|
console.error(
|
|
148
171
|
`Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
@@ -253,8 +276,13 @@ const loadConfiguration = async (context: CliContext, options: RunOptions) => {
|
|
|
253
276
|
|
|
254
277
|
return config;
|
|
255
278
|
} catch (error) {
|
|
256
|
-
throw
|
|
279
|
+
// Re-throw our custom errors
|
|
280
|
+
if ((error as ModestBenchError).code === ErrorCodes.CONFIG_LOAD_FAILED) {
|
|
281
|
+
throw error;
|
|
282
|
+
}
|
|
283
|
+
throw new InvalidArgumentError(
|
|
257
284
|
`Configuration error: ${error instanceof Error ? error.message : String(error)}`,
|
|
285
|
+
{ cause: error },
|
|
258
286
|
);
|
|
259
287
|
}
|
|
260
288
|
};
|
|
@@ -324,7 +352,7 @@ const setupReporters = async (
|
|
|
324
352
|
reporter = context.reporterRegistry.get(reporterName);
|
|
325
353
|
if (!reporter) {
|
|
326
354
|
const availableReporters = ['human', 'json', 'csv', 'simple'];
|
|
327
|
-
throw new
|
|
355
|
+
throw new UnknownReporterError(
|
|
328
356
|
`Unknown reporter: ${reporterName}. Available: ${availableReporters.join(', ')}`,
|
|
329
357
|
);
|
|
330
358
|
}
|
|
@@ -339,8 +367,13 @@ const setupReporters = async (
|
|
|
339
367
|
|
|
340
368
|
return reporters;
|
|
341
369
|
} catch (error) {
|
|
342
|
-
throw
|
|
370
|
+
// Re-throw our custom errors
|
|
371
|
+
if ((error as ModestBenchError).code === ErrorCodes.REPORTER_UNKNOWN) {
|
|
372
|
+
throw error;
|
|
373
|
+
}
|
|
374
|
+
throw new InvalidArgumentError(
|
|
343
375
|
`Reporter setup error: ${error instanceof Error ? error.message : String(error)}`,
|
|
376
|
+
{ cause: error },
|
|
344
377
|
);
|
|
345
378
|
}
|
|
346
379
|
};
|
package/src/cli/index.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
|
|
2
3
|
/**
|
|
3
4
|
* ModestBench CLI Entry Point
|
|
4
5
|
*
|
|
@@ -16,7 +17,6 @@ import { hideBin } from 'yargs/helpers';
|
|
|
16
17
|
import type {
|
|
17
18
|
BenchmarkEngine,
|
|
18
19
|
ConfigurationManager,
|
|
19
|
-
ErrorManager,
|
|
20
20
|
HistoryStorage,
|
|
21
21
|
ProgressManager,
|
|
22
22
|
ReporterRegistry,
|
|
@@ -24,6 +24,7 @@ import type {
|
|
|
24
24
|
|
|
25
25
|
import { bootstrap } from '../bootstrap.js';
|
|
26
26
|
import { AccurateEngine, TinybenchEngine } from '../core/engines/index.js';
|
|
27
|
+
import { isModestBenchError, UnknownError } from '../errors/index.js';
|
|
27
28
|
import {
|
|
28
29
|
CsvReporter,
|
|
29
30
|
HumanReporter,
|
|
@@ -42,7 +43,6 @@ export interface CliContext {
|
|
|
42
43
|
readonly abortController: AbortController;
|
|
43
44
|
readonly configManager: ConfigurationManager;
|
|
44
45
|
readonly engine: BenchmarkEngine;
|
|
45
|
-
readonly errorManager: ErrorManager;
|
|
46
46
|
readonly historyStorage: HistoryStorage;
|
|
47
47
|
readonly options: GlobalOptions;
|
|
48
48
|
readonly progressManager: ProgressManager;
|
|
@@ -151,13 +151,13 @@ export const main = async (
|
|
|
151
151
|
.completion()
|
|
152
152
|
.wrap(Math.min(120, cli.terminalWidth()))
|
|
153
153
|
.command(
|
|
154
|
-
'run [pattern..]',
|
|
154
|
+
['$0 [pattern..]', 'run [pattern..]'],
|
|
155
155
|
'Run benchmark files',
|
|
156
156
|
(yargs) => {
|
|
157
157
|
return yargs
|
|
158
158
|
.positional('pattern', {
|
|
159
159
|
array: true,
|
|
160
|
-
default: [],
|
|
160
|
+
default: ['./bench/**/*.bench.{js,mjs,cjs,ts}'],
|
|
161
161
|
describe:
|
|
162
162
|
'File paths, directory paths, or glob patterns for benchmark files',
|
|
163
163
|
type: 'string',
|
|
@@ -491,8 +491,15 @@ export const main = async (
|
|
|
491
491
|
if (process.env.DEBUG) {
|
|
492
492
|
console.error(err.stack);
|
|
493
493
|
}
|
|
494
|
+
// Show help for file discovery errors (similar to usage errors)
|
|
495
|
+
if (err.name === 'FileDiscoveryError') {
|
|
496
|
+
console.error();
|
|
497
|
+
yargsInstance.showHelp();
|
|
498
|
+
process.exit(ExitCodes.DISCOVERY_ERROR);
|
|
499
|
+
}
|
|
494
500
|
process.exit(ExitCodes.RUNTIME_ERROR);
|
|
495
501
|
} else {
|
|
502
|
+
// Show help for usage errors (unknown arguments, etc.)
|
|
496
503
|
console.error(msg);
|
|
497
504
|
console.error();
|
|
498
505
|
yargsInstance.showHelp();
|
|
@@ -564,7 +571,6 @@ const createCliContext = async (
|
|
|
564
571
|
abortController,
|
|
565
572
|
configManager: engine.configManager,
|
|
566
573
|
engine,
|
|
567
|
-
errorManager: engine.errorManager,
|
|
568
574
|
historyStorage: engine.historyStorage,
|
|
569
575
|
options,
|
|
570
576
|
progressManager: engine.progressManager,
|
|
@@ -589,9 +595,7 @@ const setupSignalHandlers = (abortController: AbortController): void => {
|
|
|
589
595
|
if (abortRequested) {
|
|
590
596
|
// Second signal, force exit
|
|
591
597
|
console.log(`\nReceived ${signal} again, forcing exit...`);
|
|
592
|
-
process.exit(
|
|
593
|
-
128 + (signal === 'SIGINT' ? 2 : signal === 'SIGQUIT' ? 3 : 15),
|
|
594
|
-
);
|
|
598
|
+
process.exit(computeExitCode(signal));
|
|
595
599
|
}
|
|
596
600
|
|
|
597
601
|
console.log(`\nReceived ${signal}, aborting benchmarks...`);
|
|
@@ -601,9 +605,7 @@ const setupSignalHandlers = (abortController: AbortController): void => {
|
|
|
601
605
|
// Give a short grace period for cleanup, then exit
|
|
602
606
|
setTimeout(() => {
|
|
603
607
|
console.log('\nBenchmark aborted.');
|
|
604
|
-
process.exit(
|
|
605
|
-
128 + (signal === 'SIGINT' ? 2 : signal === 'SIGQUIT' ? 3 : 15),
|
|
606
|
-
);
|
|
608
|
+
process.exit(computeExitCode(signal));
|
|
607
609
|
}, 100);
|
|
608
610
|
};
|
|
609
611
|
|
|
@@ -611,13 +613,27 @@ const setupSignalHandlers = (abortController: AbortController): void => {
|
|
|
611
613
|
process.on('SIGQUIT', () => handleSignal('SIGQUIT'));
|
|
612
614
|
process.on('SIGTERM', () => handleSignal('SIGTERM'));
|
|
613
615
|
|
|
614
|
-
process.
|
|
615
|
-
|
|
616
|
+
process.once('uncaughtException', (error) => {
|
|
617
|
+
// Wrap non-ModestBench errors with UnknownError
|
|
618
|
+
const wrappedError: Error = isModestBenchError(error)
|
|
619
|
+
? error
|
|
620
|
+
: new UnknownError(
|
|
621
|
+
error instanceof Error ? error.message : String(error),
|
|
622
|
+
{ cause: error },
|
|
623
|
+
);
|
|
624
|
+
console.error(wrappedError.toString());
|
|
616
625
|
process.exit(ExitCodes.RUNTIME_ERROR);
|
|
617
626
|
});
|
|
618
627
|
|
|
619
|
-
process.
|
|
620
|
-
|
|
628
|
+
process.once('unhandledRejection', (reason) => {
|
|
629
|
+
// Wrap non-ModestBench errors with UnknownError
|
|
630
|
+
const wrappedError: Error = isModestBenchError(reason)
|
|
631
|
+
? (reason as Error)
|
|
632
|
+
: new UnknownError(
|
|
633
|
+
reason instanceof Error ? reason.message : String(reason),
|
|
634
|
+
{ cause: reason },
|
|
635
|
+
);
|
|
636
|
+
console.error(wrappedError.toString());
|
|
621
637
|
process.exit(ExitCodes.RUNTIME_ERROR);
|
|
622
638
|
});
|
|
623
639
|
};
|
|
@@ -640,3 +656,13 @@ try {
|
|
|
640
656
|
cli();
|
|
641
657
|
}
|
|
642
658
|
}
|
|
659
|
+
|
|
660
|
+
/**
|
|
661
|
+
* Compute the exit code based on the signal
|
|
662
|
+
*
|
|
663
|
+
* @param signal - The signal that caused the exit
|
|
664
|
+
* @returns The exit code
|
|
665
|
+
*/
|
|
666
|
+
const computeExitCode = (signal: string): number => {
|
|
667
|
+
return 128 + (signal === 'SIGINT' ? 2 : signal === 'SIGQUIT' ? 3 : 15);
|
|
668
|
+
};
|
package/src/config/manager.ts
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
import { cosmiconfig } from 'cosmiconfig';
|
|
10
10
|
import { resolve } from 'node:path';
|
|
11
11
|
|
|
12
|
+
import type { ModestBenchError } from '../errors/base.js';
|
|
12
13
|
import type {
|
|
13
14
|
ConfigurationManager,
|
|
14
15
|
ModestBenchConfig,
|
|
@@ -17,6 +18,8 @@ import type {
|
|
|
17
18
|
ValidationWarning,
|
|
18
19
|
} from '../types/index.js';
|
|
19
20
|
|
|
21
|
+
import { ErrorCodes } from '../constants.js';
|
|
22
|
+
import { ConfigLoadError, ConfigValidationError } from '../errors/index.js';
|
|
20
23
|
import { safeParseConfig } from './schema.js';
|
|
21
24
|
|
|
22
25
|
/**
|
|
@@ -62,7 +65,7 @@ const DEFAULT_CONFIG: ModestBenchConfig = {
|
|
|
62
65
|
time: 1000, // 1 second minimum for tinybench to gather samples
|
|
63
66
|
timeout: 30000, // 30 seconds
|
|
64
67
|
verbose: false, // No verbose output by default
|
|
65
|
-
warmup:
|
|
68
|
+
warmup: 30, // Light warmup by default - enough for basic JIT optimization
|
|
66
69
|
};
|
|
67
70
|
|
|
68
71
|
/**
|
|
@@ -175,15 +178,22 @@ export class ModestBenchConfigurationManager implements ConfigurationManager {
|
|
|
175
178
|
// 3. Validate final configuration
|
|
176
179
|
const validation = this.validate(finalConfig);
|
|
177
180
|
if (!validation.valid) {
|
|
178
|
-
throw new
|
|
181
|
+
throw new ConfigValidationError(
|
|
179
182
|
`Configuration validation failed: ${validation.errors.map((e) => e.message).join(', ')}`,
|
|
180
183
|
);
|
|
181
184
|
}
|
|
182
185
|
|
|
183
186
|
return finalConfig;
|
|
184
187
|
} catch (error) {
|
|
185
|
-
throw
|
|
188
|
+
// Re-throw our custom errors
|
|
189
|
+
if (
|
|
190
|
+
(error as ModestBenchError).code === ErrorCodes.CONFIG_VALIDATION_FAILED
|
|
191
|
+
) {
|
|
192
|
+
throw error;
|
|
193
|
+
}
|
|
194
|
+
throw new ConfigLoadError(
|
|
186
195
|
`Failed to load configuration: ${error instanceof Error ? error.message : String(error)}`,
|
|
196
|
+
{ cause: error },
|
|
187
197
|
);
|
|
188
198
|
}
|
|
189
199
|
}
|
package/src/constants.ts
CHANGED
|
@@ -19,3 +19,63 @@ export const BENCHMARK_FILE_PATTERN = `.bench.{${Array.from(
|
|
|
19
19
|
)
|
|
20
20
|
.map((ext) => ext.slice(1))
|
|
21
21
|
.join(',')}}`;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Error codes for all ModestBench errors
|
|
25
|
+
*
|
|
26
|
+
* Use these constants to check error types instead of instanceof checks.
|
|
27
|
+
*/
|
|
28
|
+
export const ErrorCodes = {
|
|
29
|
+
//#region cli-errors
|
|
30
|
+
CLI_INVALID_ARGUMENT: 'ERR_MB_CLI_INVALID_ARGUMENT',
|
|
31
|
+
CLI_INVALID_DATE_FORMAT: 'ERR_MB_CLI_INVALID_DATE_FORMAT',
|
|
32
|
+
//#endregion
|
|
33
|
+
|
|
34
|
+
//#region config-errors
|
|
35
|
+
CONFIG_LOAD_FAILED: 'ERR_MB_CONFIG_LOAD_FAILED',
|
|
36
|
+
CONFIG_NOT_FOUND: 'ERR_MB_CONFIG_NOT_FOUND',
|
|
37
|
+
CONFIG_UNSUPPORTED_FORMAT: 'ERR_MB_CONFIG_UNSUPPORTED_FORMAT',
|
|
38
|
+
CONFIG_VALIDATION_FAILED: 'ERR_MB_CONFIG_VALIDATION_FAILED',
|
|
39
|
+
//#endregion
|
|
40
|
+
|
|
41
|
+
//#region execution-errors
|
|
42
|
+
EXECUTION_BENCHMARK_FAILED: 'ERR_MB_EXECUTION_BENCHMARK_FAILED',
|
|
43
|
+
EXECUTION_SETUP_FAILED: 'ERR_MB_EXECUTION_SETUP_FAILED',
|
|
44
|
+
EXECUTION_TASK_FAILED: 'ERR_MB_EXECUTION_TASK_FAILED',
|
|
45
|
+
EXECUTION_TEARDOWN_FAILED: 'ERR_MB_EXECUTION_TEARDOWN_FAILED',
|
|
46
|
+
EXECUTION_TIMEOUT: 'ERR_MB_EXECUTION_TIMEOUT',
|
|
47
|
+
EXECUTION_TOO_FAST: 'ERR_MB_EXECUTION_TOO_FAST',
|
|
48
|
+
//#endregion
|
|
49
|
+
|
|
50
|
+
//#region file-errors
|
|
51
|
+
FILE_DISCOVERY_FAILED: 'ERR_MB_FILE_DISCOVERY_FAILED',
|
|
52
|
+
FILE_LOAD_FAILED: 'ERR_MB_FILE_LOAD_FAILED',
|
|
53
|
+
FILE_NOT_FOUND: 'ERR_MB_FILE_NOT_FOUND',
|
|
54
|
+
FILE_PERMISSION_DENIED: 'ERR_MB_FILE_PERMISSION_DENIED',
|
|
55
|
+
FILE_UNSUPPORTED_EXTENSION: 'ERR_MB_FILE_UNSUPPORTED_EXTENSION',
|
|
56
|
+
//#endregion
|
|
57
|
+
|
|
58
|
+
//#region reporter-errors
|
|
59
|
+
REPORTER_ALREADY_REGISTERED: 'ERR_MB_REPORTER_ALREADY_REGISTERED',
|
|
60
|
+
REPORTER_OUTPUT_FAILED: 'ERR_MB_REPORTER_OUTPUT_FAILED',
|
|
61
|
+
REPORTER_UNKNOWN: 'ERR_MB_REPORTER_UNKNOWN',
|
|
62
|
+
//#endregion
|
|
63
|
+
|
|
64
|
+
//#region storage-errors
|
|
65
|
+
STORAGE_CORRUPTION: 'ERR_MB_STORAGE_CORRUPTION',
|
|
66
|
+
STORAGE_EXPORT_UNSUPPORTED: 'ERR_MB_STORAGE_EXPORT_UNSUPPORTED',
|
|
67
|
+
STORAGE_FAILED: 'ERR_MB_STORAGE_FAILED',
|
|
68
|
+
STORAGE_INDEX_CORRUPTION: 'ERR_MB_STORAGE_INDEX_CORRUPTION',
|
|
69
|
+
STORAGE_INSUFFICIENT_SPACE: 'ERR_MB_STORAGE_INSUFFICIENT_SPACE',
|
|
70
|
+
//#endregion
|
|
71
|
+
|
|
72
|
+
//#region misc-errors
|
|
73
|
+
UNKNOWN: 'ERR_MB_UNKNOWN',
|
|
74
|
+
//#endregion
|
|
75
|
+
|
|
76
|
+
//#region validation-errors
|
|
77
|
+
VALIDATION_SCHEMA_FAILED: 'ERR_MB_VALIDATION_SCHEMA_FAILED',
|
|
78
|
+
VALIDATION_STRUCTURE_INVALID: 'ERR_MB_VALIDATION_STRUCTURE_INVALID',
|
|
79
|
+
VALIDATION_TYPE_FAILED: 'ERR_MB_VALIDATION_TYPE_FAILED',
|
|
80
|
+
//#endregion
|
|
81
|
+
} as const;
|
package/src/core/engine.ts
CHANGED
|
@@ -15,8 +15,6 @@ import type {
|
|
|
15
15
|
CiInfo,
|
|
16
16
|
ConfigurationManager,
|
|
17
17
|
EnvironmentInfo,
|
|
18
|
-
ErrorManager,
|
|
19
|
-
ExecutionPhase,
|
|
20
18
|
FileLoader,
|
|
21
19
|
FileResult,
|
|
22
20
|
GitInfo,
|
|
@@ -33,12 +31,19 @@ import type {
|
|
|
33
31
|
ValidationWarning,
|
|
34
32
|
} from '../types/index.js';
|
|
35
33
|
|
|
34
|
+
import {
|
|
35
|
+
BenchmarkExecutionError,
|
|
36
|
+
FileDiscoveryError,
|
|
37
|
+
SchemaValidationError,
|
|
38
|
+
SetupError,
|
|
39
|
+
StructureValidationError,
|
|
40
|
+
} from '../errors/index.js';
|
|
41
|
+
|
|
36
42
|
/**
|
|
37
43
|
* Dependencies required by the BenchmarkEngine
|
|
38
44
|
*/
|
|
39
45
|
interface EngineDependencies {
|
|
40
46
|
readonly configManager: ConfigurationManager;
|
|
41
|
-
readonly errorManager: ErrorManager;
|
|
42
47
|
readonly fileLoader: FileLoader;
|
|
43
48
|
readonly historyStorage: HistoryStorage;
|
|
44
49
|
readonly progressManager: ProgressManager;
|
|
@@ -55,8 +60,6 @@ interface EngineDependencies {
|
|
|
55
60
|
export abstract class ModestBenchEngine implements BenchmarkEngine {
|
|
56
61
|
public readonly configManager: ConfigurationManager;
|
|
57
62
|
|
|
58
|
-
public readonly errorManager: ErrorManager;
|
|
59
|
-
|
|
60
63
|
public readonly fileLoader: FileLoader;
|
|
61
64
|
|
|
62
65
|
public readonly historyStorage: HistoryStorage;
|
|
@@ -71,7 +74,6 @@ export abstract class ModestBenchEngine implements BenchmarkEngine {
|
|
|
71
74
|
this.reporterRegistry = dependencies.reporterRegistry;
|
|
72
75
|
this.historyStorage = dependencies.historyStorage;
|
|
73
76
|
this.progressManager = dependencies.progressManager;
|
|
74
|
-
this.errorManager = dependencies.errorManager;
|
|
75
77
|
}
|
|
76
78
|
|
|
77
79
|
/**
|
|
@@ -86,13 +88,10 @@ export abstract class ModestBenchEngine implements BenchmarkEngine {
|
|
|
86
88
|
} catch (error) {
|
|
87
89
|
const discoveryError =
|
|
88
90
|
error instanceof Error ? error : new Error(String(error));
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
throw new Error(`File discovery failed: ${discoveryError.message}`);
|
|
91
|
+
throw new FileDiscoveryError(
|
|
92
|
+
`File discovery failed: ${discoveryError.message}`,
|
|
93
|
+
{ cause: discoveryError },
|
|
94
|
+
);
|
|
96
95
|
}
|
|
97
96
|
}
|
|
98
97
|
|
|
@@ -105,11 +104,9 @@ export abstract class ModestBenchEngine implements BenchmarkEngine {
|
|
|
105
104
|
signal?: AbortSignal,
|
|
106
105
|
): Promise<BenchmarkRun> {
|
|
107
106
|
const startTime = new Date();
|
|
108
|
-
let currentPhase: ExecutionPhase = 'discovery';
|
|
109
107
|
|
|
110
108
|
try {
|
|
111
109
|
// 1. Merge configuration with defaults
|
|
112
|
-
currentPhase = 'discovery';
|
|
113
110
|
const mergedConfig = await this.configManager.load(
|
|
114
111
|
undefined, // No specific config path for now
|
|
115
112
|
config as Record<string, unknown>,
|
|
@@ -121,32 +118,22 @@ export abstract class ModestBenchEngine implements BenchmarkEngine {
|
|
|
121
118
|
(await this.discover(mergedConfig.pattern, mergedConfig.exclude));
|
|
122
119
|
|
|
123
120
|
if (files.length === 0) {
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
timestamp: new Date(),
|
|
130
|
-
});
|
|
131
|
-
throw error;
|
|
121
|
+
let msg = `No benchmark files found matching the pattern "${mergedConfig.pattern}`;
|
|
122
|
+
if (mergedConfig.exclude?.length) {
|
|
123
|
+
msg += ` and excluding "${mergedConfig.exclude.join(', ')}"`;
|
|
124
|
+
}
|
|
125
|
+
throw new FileDiscoveryError(msg);
|
|
132
126
|
}
|
|
133
127
|
|
|
134
128
|
// 3. Validate files
|
|
135
|
-
currentPhase = 'validation';
|
|
136
129
|
const validationResult = await this.validate(files);
|
|
137
130
|
if (!validationResult.valid) {
|
|
138
|
-
|
|
131
|
+
throw new SchemaValidationError(
|
|
139
132
|
`Validation failed: ${validationResult.errors.map((e) => e.message).join(', ')}`,
|
|
140
133
|
);
|
|
141
|
-
this.errorManager.handleError(error, {
|
|
142
|
-
phase: currentPhase,
|
|
143
|
-
timestamp: new Date(),
|
|
144
|
-
});
|
|
145
|
-
throw error;
|
|
146
134
|
}
|
|
147
135
|
|
|
148
136
|
// 4. Initialize progress tracking
|
|
149
|
-
currentPhase = 'setup';
|
|
150
137
|
const runId = this.generateRunId();
|
|
151
138
|
|
|
152
139
|
// Pre-calculate total tasks for progress tracking
|
|
@@ -234,7 +221,6 @@ export abstract class ModestBenchEngine implements BenchmarkEngine {
|
|
|
234
221
|
await this.callReporters(reporters, 'onStart', initialRun);
|
|
235
222
|
|
|
236
223
|
// 6. Execute benchmark files
|
|
237
|
-
currentPhase = 'execution';
|
|
238
224
|
const fileResults: FileResult[] = [];
|
|
239
225
|
|
|
240
226
|
for (const filePath of files) {
|
|
@@ -261,11 +247,6 @@ export abstract class ModestBenchEngine implements BenchmarkEngine {
|
|
|
261
247
|
} catch (error) {
|
|
262
248
|
const fileError =
|
|
263
249
|
error instanceof Error ? error : new Error(String(error));
|
|
264
|
-
this.errorManager.handleError(fileError, {
|
|
265
|
-
file: filePath,
|
|
266
|
-
phase: currentPhase,
|
|
267
|
-
timestamp: new Date(),
|
|
268
|
-
});
|
|
269
250
|
|
|
270
251
|
// Call reporter onError
|
|
271
252
|
await this.callReporters(reporters, 'onError', fileError);
|
|
@@ -348,15 +329,23 @@ export abstract class ModestBenchEngine implements BenchmarkEngine {
|
|
|
348
329
|
// 9. Return completed run
|
|
349
330
|
return finalRun;
|
|
350
331
|
} catch (error) {
|
|
332
|
+
// Re-throw our custom errors
|
|
333
|
+
if (
|
|
334
|
+
error instanceof FileDiscoveryError ||
|
|
335
|
+
error instanceof SchemaValidationError ||
|
|
336
|
+
error instanceof BenchmarkExecutionError
|
|
337
|
+
) {
|
|
338
|
+
throw error;
|
|
339
|
+
}
|
|
340
|
+
|
|
351
341
|
const executionError =
|
|
352
342
|
error instanceof Error ? error : new Error(String(error));
|
|
353
|
-
const handledError = this.errorManager.handleError(executionError, {
|
|
354
|
-
phase: currentPhase,
|
|
355
|
-
timestamp: new Date(),
|
|
356
|
-
});
|
|
357
343
|
|
|
358
344
|
// Re-throw the original error with more context
|
|
359
|
-
throw new
|
|
345
|
+
throw new BenchmarkExecutionError(
|
|
346
|
+
`Benchmark execution failed: ${executionError.message}`,
|
|
347
|
+
{ cause: executionError },
|
|
348
|
+
);
|
|
360
349
|
}
|
|
361
350
|
}
|
|
362
351
|
|
|
@@ -421,11 +410,6 @@ export abstract class ModestBenchEngine implements BenchmarkEngine {
|
|
|
421
410
|
} catch (error) {
|
|
422
411
|
const validationError =
|
|
423
412
|
error instanceof Error ? error : new Error(String(error));
|
|
424
|
-
this.errorManager.handleError(validationError, {
|
|
425
|
-
file,
|
|
426
|
-
phase: 'validation',
|
|
427
|
-
timestamp: new Date(),
|
|
428
|
-
});
|
|
429
413
|
|
|
430
414
|
errors.push({
|
|
431
415
|
code: 'FILE_VALIDATION_ERROR',
|
|
@@ -514,7 +498,7 @@ export abstract class ModestBenchEngine implements BenchmarkEngine {
|
|
|
514
498
|
const benchmarkDef = benchmarkFile.exports as BenchmarkDefinition;
|
|
515
499
|
|
|
516
500
|
if (!benchmarkDef || typeof benchmarkDef !== 'object') {
|
|
517
|
-
throw new
|
|
501
|
+
throw new StructureValidationError(
|
|
518
502
|
'Benchmark file must export a default object with suites',
|
|
519
503
|
);
|
|
520
504
|
}
|
|
@@ -632,7 +616,9 @@ export abstract class ModestBenchEngine implements BenchmarkEngine {
|
|
|
632
616
|
error instanceof Error
|
|
633
617
|
? error
|
|
634
618
|
: new Error(`Setup failed: ${String(error)}`);
|
|
635
|
-
throw new
|
|
619
|
+
throw new SetupError(`Suite setup failed: ${setupError.message}`, {
|
|
620
|
+
cause: setupError,
|
|
621
|
+
});
|
|
636
622
|
}
|
|
637
623
|
}
|
|
638
624
|
|
|
@@ -26,6 +26,7 @@ import type {
|
|
|
26
26
|
TaskResult,
|
|
27
27
|
} from '../../types/index.js';
|
|
28
28
|
|
|
29
|
+
import { StructureValidationError } from '../../errors/index.js';
|
|
29
30
|
import { ModestBenchEngine } from '../engine.js';
|
|
30
31
|
import { calculateStatistics, removeOutliersIQR } from '../stats-utils.js';
|
|
31
32
|
|
|
@@ -59,7 +60,9 @@ export class AccurateEngine extends ModestBenchEngine {
|
|
|
59
60
|
): Promise<TaskResult> {
|
|
60
61
|
try {
|
|
61
62
|
if (!taskData.fn || typeof taskData.fn !== 'function') {
|
|
62
|
-
throw new
|
|
63
|
+
throw new StructureValidationError(
|
|
64
|
+
'Benchmark task must have a "fn" function property',
|
|
65
|
+
);
|
|
63
66
|
}
|
|
64
67
|
|
|
65
68
|
// Check for V8 native syntax support
|
|
@@ -14,6 +14,11 @@ import type {
|
|
|
14
14
|
TaskResult,
|
|
15
15
|
} from '../../types/index.js';
|
|
16
16
|
|
|
17
|
+
import {
|
|
18
|
+
BenchmarkExecutionError,
|
|
19
|
+
OperationTooFastError,
|
|
20
|
+
StructureValidationError,
|
|
21
|
+
} from '../../errors/index.js';
|
|
17
22
|
import { ModestBenchEngine } from '../engine.js';
|
|
18
23
|
import { calculateStatistics, removeOutliersIQR } from '../stats-utils.js';
|
|
19
24
|
|
|
@@ -33,7 +38,9 @@ export class TinybenchEngine extends ModestBenchEngine {
|
|
|
33
38
|
): Promise<TaskResult> {
|
|
34
39
|
try {
|
|
35
40
|
if (!taskData.fn || typeof taskData.fn !== 'function') {
|
|
36
|
-
throw new
|
|
41
|
+
throw new StructureValidationError(
|
|
42
|
+
'Benchmark task must have a "fn" function property',
|
|
43
|
+
);
|
|
37
44
|
}
|
|
38
45
|
|
|
39
46
|
// Determine effective time and iterations based on limitBy mode
|
|
@@ -137,13 +144,13 @@ export class TinybenchEngine extends ModestBenchEngine {
|
|
|
137
144
|
await minimalBench.run();
|
|
138
145
|
} catch {
|
|
139
146
|
// If still failing, the operation is too fast even for tinybench
|
|
140
|
-
throw new
|
|
147
|
+
throw new OperationTooFastError(
|
|
141
148
|
`Benchmark operation is too fast to measure reliably (execution time < 1ns)`,
|
|
142
149
|
);
|
|
143
150
|
}
|
|
144
151
|
const minimalResults = minimalBench.results[0];
|
|
145
152
|
if (!minimalResults || minimalResults.error) {
|
|
146
|
-
throw new
|
|
153
|
+
throw new OperationTooFastError(
|
|
147
154
|
`Benchmark too fast to measure reliably: ${minimalResults?.error?.message || 'unknown error'}`,
|
|
148
155
|
);
|
|
149
156
|
}
|
|
@@ -180,7 +187,7 @@ export class TinybenchEngine extends ModestBenchEngine {
|
|
|
180
187
|
// Get results
|
|
181
188
|
const results = bench.results[0];
|
|
182
189
|
if (!results) {
|
|
183
|
-
throw new
|
|
190
|
+
throw new BenchmarkExecutionError('No benchmark results returned');
|
|
184
191
|
}
|
|
185
192
|
|
|
186
193
|
// Check if the task was aborted
|
|
@@ -251,7 +258,7 @@ export class TinybenchEngine extends ModestBenchEngine {
|
|
|
251
258
|
|
|
252
259
|
if (!minimalResults || minimalResults.error) {
|
|
253
260
|
// If retry also fails, just accept it failed
|
|
254
|
-
throw new
|
|
261
|
+
throw new OperationTooFastError(
|
|
255
262
|
`Benchmark operation is too fast to measure reliably`,
|
|
256
263
|
);
|
|
257
264
|
}
|
package/src/core/loader.ts
CHANGED
|
@@ -22,6 +22,11 @@ import {
|
|
|
22
22
|
BENCHMARK_FILE_EXTENSIONS,
|
|
23
23
|
BENCHMARK_FILE_PATTERN,
|
|
24
24
|
} from '../constants.js';
|
|
25
|
+
import {
|
|
26
|
+
FileDiscoveryError,
|
|
27
|
+
FileLoadError,
|
|
28
|
+
StructureValidationError,
|
|
29
|
+
} from '../errors/index.js';
|
|
25
30
|
import { benchmarkFileSchema } from './benchmark-schema.js';
|
|
26
31
|
|
|
27
32
|
/**
|
|
@@ -106,8 +111,9 @@ export class BenchmarkFileLoader implements FileLoader {
|
|
|
106
111
|
|
|
107
112
|
return supportedFiles.sort();
|
|
108
113
|
} catch (error) {
|
|
109
|
-
throw new
|
|
114
|
+
throw new FileDiscoveryError(
|
|
110
115
|
`File discovery failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
116
|
+
{ cause: error },
|
|
111
117
|
);
|
|
112
118
|
}
|
|
113
119
|
}
|
|
@@ -120,7 +126,7 @@ export class BenchmarkFileLoader implements FileLoader {
|
|
|
120
126
|
// Basic file checks (existence, extension)
|
|
121
127
|
const basicValidation = await this.validate(filePath);
|
|
122
128
|
if (!basicValidation.valid) {
|
|
123
|
-
throw new
|
|
129
|
+
throw new FileLoadError(
|
|
124
130
|
`Invalid benchmark file: ${basicValidation.errors.map((e) => e.message).join(', ')}`,
|
|
125
131
|
);
|
|
126
132
|
}
|
|
@@ -160,7 +166,7 @@ export class BenchmarkFileLoader implements FileLoader {
|
|
|
160
166
|
// Validate the loaded exports structure with Zod
|
|
161
167
|
const structureValidation = this.validateExports(filePath, exports);
|
|
162
168
|
if (!structureValidation.valid || !structureValidation.data) {
|
|
163
|
-
throw new
|
|
169
|
+
throw new StructureValidationError(
|
|
164
170
|
`Invalid benchmark structure: ${structureValidation.errors.map((e) => e.message).join(', ')}`,
|
|
165
171
|
);
|
|
166
172
|
}
|
|
@@ -185,8 +191,16 @@ export class BenchmarkFileLoader implements FileLoader {
|
|
|
185
191
|
},
|
|
186
192
|
};
|
|
187
193
|
} catch (error) {
|
|
188
|
-
throw
|
|
194
|
+
// Re-throw our custom errors
|
|
195
|
+
if (
|
|
196
|
+
error instanceof FileLoadError ||
|
|
197
|
+
error instanceof StructureValidationError
|
|
198
|
+
) {
|
|
199
|
+
throw error;
|
|
200
|
+
}
|
|
201
|
+
throw new FileLoadError(
|
|
189
202
|
`Failed to load file ${filePath}: ${error instanceof Error ? error.message : String(error)}`,
|
|
203
|
+
{ cause: error },
|
|
190
204
|
);
|
|
191
205
|
}
|
|
192
206
|
}
|
|
@@ -199,8 +213,16 @@ export class BenchmarkFileLoader implements FileLoader {
|
|
|
199
213
|
const loadPromises = filePaths.map((filePath) => this.load(filePath));
|
|
200
214
|
return await Promise.all(loadPromises);
|
|
201
215
|
} catch (error) {
|
|
202
|
-
throw
|
|
216
|
+
// Re-throw our custom errors (from individual load calls)
|
|
217
|
+
if (
|
|
218
|
+
error instanceof FileLoadError ||
|
|
219
|
+
error instanceof StructureValidationError
|
|
220
|
+
) {
|
|
221
|
+
throw error;
|
|
222
|
+
}
|
|
223
|
+
throw new FileLoadError(
|
|
203
224
|
`Failed to load files: ${error instanceof Error ? error.message : String(error)}`,
|
|
225
|
+
{ cause: error },
|
|
204
226
|
);
|
|
205
227
|
}
|
|
206
228
|
}
|