modestbench 0.0.3 → 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 +15 -0
- 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 +14 -4
- 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 +14 -4
- package/dist/cli/commands/run.js.map +1 -1
- package/dist/cli/index.cjs +29 -12
- 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 +24 -7
- package/dist/cli/index.js.map +1 -1
- package/dist/config/manager.cjs +8 -2
- 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 +8 -2
- 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 +15 -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 +15 -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 +1 -1
- package/dist/reporters/human.js +1 -1
- 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 +7 -6
- 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 +20 -4
- package/src/cli/index.ts +32 -13
- package/src/config/manager.ts +12 -2
- package/src/constants.ts +60 -0
- package/src/core/engine.ts +31 -47
- 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 +1 -1
- 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/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
|
/**
|
|
@@ -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>,
|
|
@@ -125,30 +122,18 @@ export abstract class ModestBenchEngine implements BenchmarkEngine {
|
|
|
125
122
|
if (mergedConfig.exclude?.length) {
|
|
126
123
|
msg += ` and excluding "${mergedConfig.exclude.join(', ')}"`;
|
|
127
124
|
}
|
|
128
|
-
|
|
129
|
-
this.errorManager.handleError(error, {
|
|
130
|
-
phase: currentPhase,
|
|
131
|
-
timestamp: new Date(),
|
|
132
|
-
});
|
|
133
|
-
throw error;
|
|
125
|
+
throw new FileDiscoveryError(msg);
|
|
134
126
|
}
|
|
135
127
|
|
|
136
128
|
// 3. Validate files
|
|
137
|
-
currentPhase = 'validation';
|
|
138
129
|
const validationResult = await this.validate(files);
|
|
139
130
|
if (!validationResult.valid) {
|
|
140
|
-
|
|
131
|
+
throw new SchemaValidationError(
|
|
141
132
|
`Validation failed: ${validationResult.errors.map((e) => e.message).join(', ')}`,
|
|
142
133
|
);
|
|
143
|
-
this.errorManager.handleError(error, {
|
|
144
|
-
phase: currentPhase,
|
|
145
|
-
timestamp: new Date(),
|
|
146
|
-
});
|
|
147
|
-
throw error;
|
|
148
134
|
}
|
|
149
135
|
|
|
150
136
|
// 4. Initialize progress tracking
|
|
151
|
-
currentPhase = 'setup';
|
|
152
137
|
const runId = this.generateRunId();
|
|
153
138
|
|
|
154
139
|
// Pre-calculate total tasks for progress tracking
|
|
@@ -236,7 +221,6 @@ export abstract class ModestBenchEngine implements BenchmarkEngine {
|
|
|
236
221
|
await this.callReporters(reporters, 'onStart', initialRun);
|
|
237
222
|
|
|
238
223
|
// 6. Execute benchmark files
|
|
239
|
-
currentPhase = 'execution';
|
|
240
224
|
const fileResults: FileResult[] = [];
|
|
241
225
|
|
|
242
226
|
for (const filePath of files) {
|
|
@@ -263,11 +247,6 @@ export abstract class ModestBenchEngine implements BenchmarkEngine {
|
|
|
263
247
|
} catch (error) {
|
|
264
248
|
const fileError =
|
|
265
249
|
error instanceof Error ? error : new Error(String(error));
|
|
266
|
-
this.errorManager.handleError(fileError, {
|
|
267
|
-
file: filePath,
|
|
268
|
-
phase: currentPhase,
|
|
269
|
-
timestamp: new Date(),
|
|
270
|
-
});
|
|
271
250
|
|
|
272
251
|
// Call reporter onError
|
|
273
252
|
await this.callReporters(reporters, 'onError', fileError);
|
|
@@ -350,15 +329,23 @@ export abstract class ModestBenchEngine implements BenchmarkEngine {
|
|
|
350
329
|
// 9. Return completed run
|
|
351
330
|
return finalRun;
|
|
352
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
|
+
|
|
353
341
|
const executionError =
|
|
354
342
|
error instanceof Error ? error : new Error(String(error));
|
|
355
|
-
const handledError = this.errorManager.handleError(executionError, {
|
|
356
|
-
phase: currentPhase,
|
|
357
|
-
timestamp: new Date(),
|
|
358
|
-
});
|
|
359
343
|
|
|
360
344
|
// Re-throw the original error with more context
|
|
361
|
-
throw new
|
|
345
|
+
throw new BenchmarkExecutionError(
|
|
346
|
+
`Benchmark execution failed: ${executionError.message}`,
|
|
347
|
+
{ cause: executionError },
|
|
348
|
+
);
|
|
362
349
|
}
|
|
363
350
|
}
|
|
364
351
|
|
|
@@ -423,11 +410,6 @@ export abstract class ModestBenchEngine implements BenchmarkEngine {
|
|
|
423
410
|
} catch (error) {
|
|
424
411
|
const validationError =
|
|
425
412
|
error instanceof Error ? error : new Error(String(error));
|
|
426
|
-
this.errorManager.handleError(validationError, {
|
|
427
|
-
file,
|
|
428
|
-
phase: 'validation',
|
|
429
|
-
timestamp: new Date(),
|
|
430
|
-
});
|
|
431
413
|
|
|
432
414
|
errors.push({
|
|
433
415
|
code: 'FILE_VALIDATION_ERROR',
|
|
@@ -516,7 +498,7 @@ export abstract class ModestBenchEngine implements BenchmarkEngine {
|
|
|
516
498
|
const benchmarkDef = benchmarkFile.exports as BenchmarkDefinition;
|
|
517
499
|
|
|
518
500
|
if (!benchmarkDef || typeof benchmarkDef !== 'object') {
|
|
519
|
-
throw new
|
|
501
|
+
throw new StructureValidationError(
|
|
520
502
|
'Benchmark file must export a default object with suites',
|
|
521
503
|
);
|
|
522
504
|
}
|
|
@@ -634,7 +616,9 @@ export abstract class ModestBenchEngine implements BenchmarkEngine {
|
|
|
634
616
|
error instanceof Error
|
|
635
617
|
? error
|
|
636
618
|
: new Error(`Setup failed: ${String(error)}`);
|
|
637
|
-
throw new
|
|
619
|
+
throw new SetupError(`Suite setup failed: ${setupError.message}`, {
|
|
620
|
+
cause: setupError,
|
|
621
|
+
});
|
|
638
622
|
}
|
|
639
623
|
}
|
|
640
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
|
}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ModestBench Custom Error System
|
|
3
|
+
*
|
|
4
|
+
* Base error classes providing structured error handling with error codes,
|
|
5
|
+
* documentation URLs, and consistent error display.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Base URL for error documentation
|
|
10
|
+
*/
|
|
11
|
+
const ERROR_DOC_BASE_URL =
|
|
12
|
+
'https://boneskull.github.io/modestbench/reference/errors';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Abstract base class for ModestBench aggregate errors
|
|
16
|
+
*
|
|
17
|
+
* Extends AggregateError to support multiple errors with ModestBench error
|
|
18
|
+
* system features.
|
|
19
|
+
*/
|
|
20
|
+
export abstract class ModestBenchAggregateError extends AggregateError {
|
|
21
|
+
/**
|
|
22
|
+
* Unique error code for this error type Must be in format:
|
|
23
|
+
* ERR_MB_CATEGORY_DESCRIPTION
|
|
24
|
+
*/
|
|
25
|
+
abstract readonly code: string;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Error name (matches class name)
|
|
29
|
+
*/
|
|
30
|
+
public override readonly name: string;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Create a new ModestBench aggregate error
|
|
34
|
+
*
|
|
35
|
+
* @param errors - Array of errors that occurred
|
|
36
|
+
* @param message - Human-readable error message
|
|
37
|
+
* @param options - Optional Error options (e.g., cause)
|
|
38
|
+
*/
|
|
39
|
+
constructor(errors: unknown[], message: string, options?: ErrorOptions) {
|
|
40
|
+
super(errors, message, options);
|
|
41
|
+
this.name = this.constructor.name;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Get the documentation URL for this error
|
|
46
|
+
*
|
|
47
|
+
* Returns a URL to the error reference page with an anchor to the specific
|
|
48
|
+
* error.
|
|
49
|
+
*
|
|
50
|
+
* @returns Documentation URL
|
|
51
|
+
*/
|
|
52
|
+
getDocUrl(): string {
|
|
53
|
+
// Use the error class name as the anchor (e.g., ConfigValidationError -> #configvalidationerror)
|
|
54
|
+
const anchor = this.name.toLowerCase();
|
|
55
|
+
return `${ERROR_DOC_BASE_URL}#${anchor}`;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Convert error to string with code, documentation URL, and nested errors
|
|
60
|
+
*
|
|
61
|
+
* @returns Formatted error string
|
|
62
|
+
*/
|
|
63
|
+
override toString(): string {
|
|
64
|
+
let result = `${this.name} [${this.code}]: ${this.message}\n`;
|
|
65
|
+
result += `See: ${this.getDocUrl()}\n`;
|
|
66
|
+
|
|
67
|
+
if (this.errors.length > 0) {
|
|
68
|
+
result += `\nContains ${this.errors.length} error(s):\n`;
|
|
69
|
+
this.errors.forEach((err, index) => {
|
|
70
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
71
|
+
result += ` ${index + 1}. ${errMsg}\n`;
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return result;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Abstract base class for all ModestBench errors
|
|
81
|
+
*
|
|
82
|
+
* Provides:
|
|
83
|
+
*
|
|
84
|
+
* - Unique error codes with ERR_MB_ prefix
|
|
85
|
+
* - Documentation URL generation
|
|
86
|
+
* - Consistent error display format
|
|
87
|
+
* - TypeScript type safety
|
|
88
|
+
*/
|
|
89
|
+
export abstract class ModestBenchError extends Error {
|
|
90
|
+
/**
|
|
91
|
+
* Unique error code for this error type Must be in format:
|
|
92
|
+
* ERR_MB_CATEGORY_DESCRIPTION
|
|
93
|
+
*/
|
|
94
|
+
abstract readonly code: string;
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Error name (matches class name)
|
|
98
|
+
*/
|
|
99
|
+
public override readonly name: string;
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Create a new ModestBench error
|
|
103
|
+
*
|
|
104
|
+
* @param message - Human-readable error message
|
|
105
|
+
* @param options - Optional Error options (e.g., cause)
|
|
106
|
+
*/
|
|
107
|
+
constructor(message: string, options?: ErrorOptions) {
|
|
108
|
+
super(message, options);
|
|
109
|
+
this.name = this.constructor.name;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Get the documentation URL for this error
|
|
114
|
+
*
|
|
115
|
+
* Returns a URL to the error reference page with an anchor to the specific
|
|
116
|
+
* error.
|
|
117
|
+
*
|
|
118
|
+
* @returns Documentation URL
|
|
119
|
+
*/
|
|
120
|
+
getDocUrl(): string {
|
|
121
|
+
// Use the error class name as the anchor (e.g., ConfigValidationError -> #configvalidationerror)
|
|
122
|
+
const anchor = this.name.toLowerCase();
|
|
123
|
+
return `${ERROR_DOC_BASE_URL}#${anchor}`;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Convert error to string with code and documentation URL
|
|
128
|
+
*
|
|
129
|
+
* @returns Formatted error string
|
|
130
|
+
*/
|
|
131
|
+
override toString(): string {
|
|
132
|
+
return `${this.name} [${this.code}]: ${this.message}\nSee: ${this.getDocUrl()}`;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Type guard to check if an error is a ModestBench error
|
|
138
|
+
*
|
|
139
|
+
* @param error - The error to check
|
|
140
|
+
* @returns True if the error is a ModestBench error
|
|
141
|
+
*/
|
|
142
|
+
export const isModestBenchError = (
|
|
143
|
+
error: unknown,
|
|
144
|
+
): error is ModestBenchError => {
|
|
145
|
+
return (
|
|
146
|
+
typeof error === 'object' &&
|
|
147
|
+
error !== null &&
|
|
148
|
+
'code' in error &&
|
|
149
|
+
typeof (error as { code: unknown }).code === 'string' &&
|
|
150
|
+
(error as { code: string }).code.startsWith('ERR_MB_')
|
|
151
|
+
);
|
|
152
|
+
};
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI-related errors
|
|
3
|
+
*
|
|
4
|
+
* Errors that occur during command-line interface operations.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { ModestBenchError } from './base.js';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Invalid CLI argument
|
|
11
|
+
*
|
|
12
|
+
* Thrown when a CLI argument is invalid or cannot be parsed.
|
|
13
|
+
*/
|
|
14
|
+
export class InvalidArgumentError extends ModestBenchError {
|
|
15
|
+
readonly code = 'ERR_MB_CLI_INVALID_ARGUMENT';
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Invalid date format
|
|
20
|
+
*
|
|
21
|
+
* Thrown when a date string cannot be parsed into a valid date.
|
|
22
|
+
*/
|
|
23
|
+
export class InvalidDateFormatError extends ModestBenchError {
|
|
24
|
+
readonly code = 'ERR_MB_CLI_INVALID_DATE_FORMAT';
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Unknown error
|
|
29
|
+
*
|
|
30
|
+
* Thrown at the CLI boundary to wrap unexpected errors that are not ModestBench
|
|
31
|
+
* errors. This ensures all errors have proper structure and documentation
|
|
32
|
+
* links.
|
|
33
|
+
*/
|
|
34
|
+
export class UnknownError extends ModestBenchError {
|
|
35
|
+
readonly code = 'ERR_MB_UNKNOWN';
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Create a new UnknownError wrapping an unexpected error
|
|
39
|
+
*
|
|
40
|
+
* @param message - The error message (typically from the original error)
|
|
41
|
+
* @param options - Error options with the original error as the cause
|
|
42
|
+
*/
|
|
43
|
+
constructor(message: string, options: ErrorOptions) {
|
|
44
|
+
super(message, options);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Override toString to show full details of the wrapped error
|
|
49
|
+
*/
|
|
50
|
+
override toString(): string {
|
|
51
|
+
let result = super.toString();
|
|
52
|
+
|
|
53
|
+
if (this.cause instanceof Error && this.cause.stack) {
|
|
54
|
+
result += '\n\nOriginal error:\n' + this.cause.stack;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return result;
|
|
58
|
+
}
|
|
59
|
+
}
|