modestbench 0.1.0 → 0.3.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 +31 -0
- package/README.md +162 -57
- package/dist/bootstrap.cjs +10 -10
- 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 +5 -5
- package/dist/bootstrap.js.map +1 -1
- package/dist/cli/commands/analyze.cjs +60 -0
- package/dist/cli/commands/analyze.cjs.map +1 -0
- package/dist/cli/commands/analyze.d.cts +35 -0
- package/dist/cli/commands/analyze.d.cts.map +1 -0
- package/dist/cli/commands/analyze.d.ts +35 -0
- package/dist/cli/commands/analyze.d.ts.map +1 -0
- package/dist/cli/commands/analyze.js +56 -0
- package/dist/cli/commands/analyze.js.map +1 -0
- package/dist/cli/commands/baseline.cjs +404 -0
- package/dist/cli/commands/baseline.cjs.map +1 -0
- package/dist/cli/commands/baseline.d.cts +72 -0
- package/dist/cli/commands/baseline.d.cts.map +1 -0
- package/dist/cli/commands/baseline.d.ts +72 -0
- package/dist/cli/commands/baseline.d.ts.map +1 -0
- package/dist/cli/commands/baseline.js +396 -0
- package/dist/cli/commands/baseline.js.map +1 -0
- package/dist/cli/commands/history.cjs +108 -266
- package/dist/cli/commands/history.cjs.map +1 -1
- package/dist/cli/commands/history.d.cts +75 -12
- package/dist/cli/commands/history.d.cts.map +1 -1
- package/dist/cli/commands/history.d.ts +75 -12
- package/dist/cli/commands/history.d.ts.map +1 -1
- package/dist/cli/commands/history.js +105 -268
- package/dist/cli/commands/history.js.map +1 -1
- package/dist/cli/commands/init.cjs +88 -155
- package/dist/cli/commands/init.cjs.map +1 -1
- package/dist/cli/commands/init.d.cts +4 -4
- package/dist/cli/commands/init.d.cts.map +1 -1
- package/dist/cli/commands/init.d.ts +4 -4
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +88 -155
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/run.cjs +143 -112
- package/dist/cli/commands/run.cjs.map +1 -1
- package/dist/cli/commands/run.d.cts +17 -3
- package/dist/cli/commands/run.d.cts.map +1 -1
- package/dist/cli/commands/run.d.ts +17 -3
- package/dist/cli/commands/run.d.ts.map +1 -1
- package/dist/cli/commands/run.js +142 -78
- package/dist/cli/commands/run.js.map +1 -1
- package/dist/cli/index.cjs +671 -266
- package/dist/cli/index.cjs.map +1 -1
- package/dist/cli/index.d.cts +4 -16
- package/dist/cli/index.d.cts.map +1 -1
- package/dist/cli/index.d.ts +4 -16
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +664 -259
- package/dist/cli/index.js.map +1 -1
- package/dist/config/budget-schema.cjs +172 -0
- package/dist/config/budget-schema.cjs.map +1 -0
- package/dist/config/budget-schema.d.cts +59 -0
- package/dist/config/budget-schema.d.cts.map +1 -0
- package/dist/config/budget-schema.d.ts +59 -0
- package/dist/config/budget-schema.d.ts.map +1 -0
- package/dist/config/budget-schema.js +166 -0
- package/dist/config/budget-schema.js.map +1 -0
- package/dist/config/schema.cjs +182 -2
- package/dist/config/schema.cjs.map +1 -1
- package/dist/config/schema.d.cts +122 -3
- package/dist/config/schema.d.cts.map +1 -1
- package/dist/config/schema.d.ts +122 -3
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/config/schema.js +180 -1
- package/dist/config/schema.js.map +1 -1
- package/dist/constants.cjs +45 -2
- package/dist/constants.cjs.map +1 -1
- package/dist/constants.d.cts +41 -0
- package/dist/constants.d.cts.map +1 -1
- package/dist/constants.d.ts +41 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +44 -1
- package/dist/constants.js.map +1 -1
- package/dist/core/engine.cjs +104 -15
- package/dist/core/engine.cjs.map +1 -1
- package/dist/core/engine.d.cts +7 -4
- package/dist/core/engine.d.cts.map +1 -1
- package/dist/core/engine.d.ts +7 -4
- package/dist/core/engine.d.ts.map +1 -1
- package/dist/core/engine.js +105 -16
- package/dist/core/engine.js.map +1 -1
- package/dist/core/output-path-resolver.cjs +41 -0
- package/dist/core/output-path-resolver.cjs.map +1 -0
- package/dist/core/output-path-resolver.d.cts +10 -0
- package/dist/core/output-path-resolver.d.cts.map +1 -0
- package/dist/core/output-path-resolver.d.ts +10 -0
- package/dist/core/output-path-resolver.d.ts.map +1 -0
- package/dist/core/output-path-resolver.js +37 -0
- package/dist/core/output-path-resolver.js.map +1 -0
- package/dist/errors/base.cjs +12 -3
- package/dist/errors/base.cjs.map +1 -1
- package/dist/errors/base.d.cts +7 -0
- package/dist/errors/base.d.cts.map +1 -1
- package/dist/errors/base.d.ts +7 -0
- package/dist/errors/base.d.ts.map +1 -1
- package/dist/errors/base.js +10 -2
- package/dist/errors/base.js.map +1 -1
- package/dist/errors/budget.cjs +37 -0
- package/dist/errors/budget.cjs.map +1 -0
- package/dist/errors/budget.d.cts +31 -0
- package/dist/errors/budget.d.cts.map +1 -0
- package/dist/errors/budget.d.ts +31 -0
- package/dist/errors/budget.d.ts.map +1 -0
- package/dist/errors/budget.js +33 -0
- package/dist/errors/budget.js.map +1 -0
- package/dist/errors/index.cjs +4 -1
- package/dist/errors/index.cjs.map +1 -1
- package/dist/errors/index.d.cts +1 -0
- package/dist/errors/index.d.cts.map +1 -1
- package/dist/errors/index.d.ts +1 -0
- package/dist/errors/index.d.ts.map +1 -1
- package/dist/errors/index.js +2 -0
- package/dist/errors/index.js.map +1 -1
- package/dist/formatters/history/base.cjs +9 -0
- package/dist/formatters/history/base.cjs.map +1 -0
- package/dist/formatters/history/base.d.cts +26 -0
- package/dist/formatters/history/base.d.cts.map +1 -0
- package/dist/formatters/history/base.d.ts +26 -0
- package/dist/formatters/history/base.d.ts.map +1 -0
- package/dist/formatters/history/base.js +8 -0
- package/dist/formatters/history/base.js.map +1 -0
- package/dist/formatters/history/compare.cjs +127 -0
- package/dist/formatters/history/compare.cjs.map +1 -0
- package/dist/formatters/history/compare.d.cts +21 -0
- package/dist/formatters/history/compare.d.cts.map +1 -0
- package/dist/formatters/history/compare.d.ts +21 -0
- package/dist/formatters/history/compare.d.ts.map +1 -0
- package/dist/formatters/history/compare.js +123 -0
- package/dist/formatters/history/compare.js.map +1 -0
- package/dist/formatters/history/list.cjs +74 -0
- package/dist/formatters/history/list.cjs.map +1 -0
- package/dist/formatters/history/list.d.cts +25 -0
- package/dist/formatters/history/list.d.cts.map +1 -0
- package/dist/formatters/history/list.d.ts +25 -0
- package/dist/formatters/history/list.d.ts.map +1 -0
- package/dist/formatters/history/list.js +70 -0
- package/dist/formatters/history/list.js.map +1 -0
- package/dist/formatters/history/show.cjs +98 -0
- package/dist/formatters/history/show.cjs.map +1 -0
- package/dist/formatters/history/show.d.cts +21 -0
- package/dist/formatters/history/show.d.cts.map +1 -0
- package/dist/formatters/history/show.d.ts +21 -0
- package/dist/formatters/history/show.d.ts.map +1 -0
- package/dist/formatters/history/show.js +94 -0
- package/dist/formatters/history/show.js.map +1 -0
- package/dist/formatters/history/trends.cjs +194 -0
- package/dist/formatters/history/trends.cjs.map +1 -0
- package/dist/formatters/history/trends.d.cts +22 -0
- package/dist/formatters/history/trends.d.cts.map +1 -0
- package/dist/formatters/history/trends.d.ts +22 -0
- package/dist/formatters/history/trends.d.ts.map +1 -0
- package/dist/formatters/history/trends.js +190 -0
- package/dist/formatters/history/trends.js.map +1 -0
- package/dist/formatters/history/visualization.cjs +79 -0
- package/dist/formatters/history/visualization.cjs.map +1 -0
- package/dist/formatters/history/visualization.d.cts +24 -0
- package/dist/formatters/history/visualization.d.cts.map +1 -0
- package/dist/formatters/history/visualization.d.ts +24 -0
- package/dist/formatters/history/visualization.d.ts.map +1 -0
- package/dist/formatters/history/visualization.js +74 -0
- package/dist/formatters/history/visualization.js.map +1 -0
- package/dist/index.cjs +27 -17
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +10 -5
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +10 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +14 -9
- package/dist/index.js.map +1 -1
- package/dist/reporters/csv.cjs +39 -19
- package/dist/reporters/csv.cjs.map +1 -1
- package/dist/reporters/csv.d.cts +4 -7
- package/dist/reporters/csv.d.cts.map +1 -1
- package/dist/reporters/csv.d.ts +4 -7
- package/dist/reporters/csv.d.ts.map +1 -1
- package/dist/reporters/csv.js +38 -18
- package/dist/reporters/csv.js.map +1 -1
- package/dist/reporters/human.cjs +87 -99
- package/dist/reporters/human.cjs.map +1 -1
- package/dist/reporters/human.d.cts +15 -14
- package/dist/reporters/human.d.cts.map +1 -1
- package/dist/reporters/human.d.ts +15 -14
- package/dist/reporters/human.d.ts.map +1 -1
- package/dist/reporters/human.js +68 -80
- package/dist/reporters/human.js.map +1 -1
- package/dist/reporters/json.cjs +25 -50
- package/dist/reporters/json.cjs.map +1 -1
- package/dist/reporters/json.d.cts +3 -29
- package/dist/reporters/json.d.cts.map +1 -1
- package/dist/reporters/json.d.ts +3 -29
- package/dist/reporters/json.d.ts.map +1 -1
- package/dist/reporters/json.js +26 -51
- package/dist/reporters/json.js.map +1 -1
- package/dist/reporters/profile-human.cjs +149 -0
- package/dist/reporters/profile-human.cjs.map +1 -0
- package/dist/reporters/profile-human.d.cts +44 -0
- package/dist/reporters/profile-human.d.cts.map +1 -0
- package/dist/reporters/profile-human.d.ts +44 -0
- package/dist/reporters/profile-human.d.ts.map +1 -0
- package/dist/reporters/profile-human.js +142 -0
- package/dist/reporters/profile-human.js.map +1 -0
- package/dist/reporters/simple.cjs +66 -46
- package/dist/reporters/simple.cjs.map +1 -1
- package/dist/reporters/simple.d.cts +15 -15
- package/dist/reporters/simple.d.cts.map +1 -1
- package/dist/reporters/simple.d.ts +15 -15
- package/dist/reporters/simple.d.ts.map +1 -1
- package/dist/reporters/simple.js +65 -45
- package/dist/reporters/simple.js.map +1 -1
- package/dist/schema/modestbench-config.schema.json +153 -0
- package/dist/services/baseline-storage.cjs +151 -0
- package/dist/services/baseline-storage.cjs.map +1 -0
- package/dist/services/baseline-storage.d.cts +55 -0
- package/dist/services/baseline-storage.d.cts.map +1 -0
- package/dist/services/baseline-storage.d.ts +55 -0
- package/dist/services/baseline-storage.d.ts.map +1 -0
- package/dist/services/baseline-storage.js +147 -0
- package/dist/services/baseline-storage.js.map +1 -0
- package/dist/services/budget-evaluator.cjs +146 -0
- package/dist/services/budget-evaluator.cjs.map +1 -0
- package/dist/services/budget-evaluator.d.cts +29 -0
- package/dist/services/budget-evaluator.d.cts.map +1 -0
- package/dist/services/budget-evaluator.d.ts +29 -0
- package/dist/services/budget-evaluator.d.ts.map +1 -0
- package/dist/services/budget-evaluator.js +142 -0
- package/dist/services/budget-evaluator.js.map +1 -0
- package/dist/{config/manager.cjs → services/config-manager.cjs} +25 -11
- package/dist/services/config-manager.cjs.map +1 -0
- package/dist/{config/manager.d.cts → services/config-manager.d.cts} +7 -2
- package/dist/services/config-manager.d.cts.map +1 -0
- package/dist/{config/manager.d.ts → services/config-manager.d.ts} +7 -2
- package/dist/services/config-manager.d.ts.map +1 -0
- package/dist/{config/manager.js → services/config-manager.js} +25 -11
- package/dist/services/config-manager.js.map +1 -0
- package/dist/{core/loader.cjs → services/file-loader.cjs} +5 -8
- package/dist/services/file-loader.cjs.map +1 -0
- package/dist/{core/loader.d.cts → services/file-loader.d.cts} +1 -1
- package/dist/services/file-loader.d.cts.map +1 -0
- package/dist/{core/loader.d.ts → services/file-loader.d.ts} +1 -1
- package/dist/services/file-loader.d.ts.map +1 -0
- package/dist/{core/loader.js → services/file-loader.js} +5 -8
- package/dist/services/file-loader.js.map +1 -0
- package/dist/services/history/comparison.cjs +124 -0
- package/dist/services/history/comparison.cjs.map +1 -0
- package/dist/services/history/comparison.d.cts +18 -0
- package/dist/services/history/comparison.d.cts.map +1 -0
- package/dist/services/history/comparison.d.ts +18 -0
- package/dist/services/history/comparison.d.ts.map +1 -0
- package/dist/services/history/comparison.js +120 -0
- package/dist/services/history/comparison.js.map +1 -0
- package/dist/services/history/models.cjs +9 -0
- package/dist/services/history/models.cjs.map +1 -0
- package/dist/services/history/models.d.cts +139 -0
- package/dist/services/history/models.d.cts.map +1 -0
- package/dist/services/history/models.d.ts +139 -0
- package/dist/services/history/models.d.ts.map +1 -0
- package/dist/services/history/models.js +8 -0
- package/dist/services/history/models.js.map +1 -0
- package/dist/services/history/query.cjs +97 -0
- package/dist/services/history/query.cjs.map +1 -0
- package/dist/services/history/query.d.cts +38 -0
- package/dist/services/history/query.d.cts.map +1 -0
- package/dist/services/history/query.d.ts +38 -0
- package/dist/services/history/query.d.ts.map +1 -0
- package/dist/services/history/query.js +92 -0
- package/dist/services/history/query.js.map +1 -0
- package/dist/services/history/trend-analysis.cjs +187 -0
- package/dist/services/history/trend-analysis.cjs.map +1 -0
- package/dist/services/history/trend-analysis.d.cts +34 -0
- package/dist/services/history/trend-analysis.d.cts.map +1 -0
- package/dist/services/history/trend-analysis.d.ts +34 -0
- package/dist/services/history/trend-analysis.d.ts.map +1 -0
- package/dist/services/history/trend-analysis.js +179 -0
- package/dist/services/history/trend-analysis.js.map +1 -0
- package/dist/{storage/history.cjs → services/history-storage.cjs} +1 -1
- package/dist/services/history-storage.cjs.map +1 -0
- package/dist/{storage/history.d.cts → services/history-storage.d.cts} +1 -1
- package/dist/services/history-storage.d.cts.map +1 -0
- package/dist/{storage/history.d.ts → services/history-storage.d.ts} +1 -1
- package/dist/services/history-storage.d.ts.map +1 -0
- package/dist/{storage/history.js → services/history-storage.js} +1 -1
- package/dist/services/history-storage.js.map +1 -0
- package/dist/services/profiler/profile-filter.cjs +113 -0
- package/dist/services/profiler/profile-filter.cjs.map +1 -0
- package/dist/services/profiler/profile-filter.d.cts +20 -0
- package/dist/services/profiler/profile-filter.d.cts.map +1 -0
- package/dist/services/profiler/profile-filter.d.ts +20 -0
- package/dist/services/profiler/profile-filter.d.ts.map +1 -0
- package/dist/services/profiler/profile-filter.js +109 -0
- package/dist/services/profiler/profile-filter.js.map +1 -0
- package/dist/services/profiler/profile-parser.cjs +139 -0
- package/dist/services/profiler/profile-parser.cjs.map +1 -0
- package/dist/services/profiler/profile-parser.d.cts +18 -0
- package/dist/services/profiler/profile-parser.d.cts.map +1 -0
- package/dist/services/profiler/profile-parser.d.ts +18 -0
- package/dist/services/profiler/profile-parser.d.ts.map +1 -0
- package/dist/services/profiler/profile-parser.js +132 -0
- package/dist/services/profiler/profile-parser.js.map +1 -0
- package/dist/services/profiler/profile-runner.cjs +90 -0
- package/dist/services/profiler/profile-runner.cjs.map +1 -0
- package/dist/services/profiler/profile-runner.d.cts +29 -0
- package/dist/services/profiler/profile-runner.d.cts.map +1 -0
- package/dist/services/profiler/profile-runner.d.ts +29 -0
- package/dist/services/profiler/profile-runner.d.ts.map +1 -0
- package/dist/services/profiler/profile-runner.js +86 -0
- package/dist/services/profiler/profile-runner.js.map +1 -0
- package/dist/{progress/manager.cjs → services/progress-manager.cjs} +1 -1
- package/dist/services/progress-manager.cjs.map +1 -0
- package/dist/{progress/manager.d.cts → services/progress-manager.d.cts} +1 -1
- package/dist/services/progress-manager.d.cts.map +1 -0
- package/dist/{progress/manager.d.ts → services/progress-manager.d.ts} +1 -1
- package/dist/services/progress-manager.d.ts.map +1 -0
- package/dist/{progress/manager.js → services/progress-manager.js} +1 -1
- package/dist/services/progress-manager.js.map +1 -0
- package/dist/{reporters/registry.cjs → services/reporter-registry.cjs} +19 -25
- package/dist/services/reporter-registry.cjs.map +1 -0
- package/dist/{reporters/registry.d.cts → services/reporter-registry.d.cts} +19 -41
- package/dist/services/reporter-registry.d.cts.map +1 -0
- package/dist/{reporters/registry.d.ts → services/reporter-registry.d.ts} +19 -41
- package/dist/services/reporter-registry.d.ts.map +1 -0
- package/dist/{reporters/registry.js → services/reporter-registry.js} +19 -25
- package/dist/services/reporter-registry.js.map +1 -0
- package/dist/types/budgets.cjs +8 -0
- package/dist/types/budgets.cjs.map +1 -0
- package/dist/types/budgets.d.cts +149 -0
- package/dist/types/budgets.d.cts.map +1 -0
- package/dist/types/budgets.d.ts +149 -0
- package/dist/types/budgets.d.ts.map +1 -0
- package/dist/types/budgets.js +7 -0
- package/dist/types/budgets.js.map +1 -0
- package/dist/types/cli.cjs +2 -11
- package/dist/types/cli.cjs.map +1 -1
- package/dist/types/cli.d.cts +3 -224
- package/dist/types/cli.d.cts.map +1 -1
- package/dist/types/cli.d.ts +3 -224
- package/dist/types/cli.d.ts.map +1 -1
- package/dist/types/cli.js +2 -11
- package/dist/types/cli.js.map +1 -1
- package/dist/types/core.cjs +6 -1
- package/dist/types/core.cjs.map +1 -1
- package/dist/types/core.d.cts +13 -2
- package/dist/types/core.d.cts.map +1 -1
- package/dist/types/core.d.ts +13 -2
- package/dist/types/core.d.ts.map +1 -1
- package/dist/types/core.js +2 -1
- package/dist/types/core.js.map +1 -1
- package/dist/types/index.cjs +5 -0
- package/dist/types/index.cjs.map +1 -1
- package/dist/types/index.d.cts +2 -0
- package/dist/types/index.d.cts.map +1 -1
- package/dist/types/index.d.ts +2 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +2 -0
- package/dist/types/index.js.map +1 -1
- package/dist/types/interfaces.d.cts +15 -8
- package/dist/types/interfaces.d.cts.map +1 -1
- package/dist/types/interfaces.d.ts +15 -8
- package/dist/types/interfaces.d.ts.map +1 -1
- package/dist/types/profiler.cjs +11 -0
- package/dist/types/profiler.cjs.map +1 -0
- package/dist/types/profiler.d.cts +100 -0
- package/dist/types/profiler.d.cts.map +1 -0
- package/dist/types/profiler.d.ts +100 -0
- package/dist/types/profiler.d.ts.map +1 -0
- package/dist/types/profiler.js +10 -0
- package/dist/types/profiler.js.map +1 -0
- package/dist/types/utility.cjs.map +1 -1
- package/dist/types/utility.d.cts +0 -8
- package/dist/types/utility.d.cts.map +1 -1
- package/dist/types/utility.d.ts +0 -8
- package/dist/types/utility.d.ts.map +1 -1
- package/dist/types/utility.js.map +1 -1
- package/dist/utils/ansi.cjs +61 -0
- package/dist/utils/ansi.cjs.map +1 -0
- package/dist/utils/ansi.d.cts +53 -0
- package/dist/utils/ansi.d.cts.map +1 -0
- package/dist/utils/ansi.d.ts +53 -0
- package/dist/utils/ansi.d.ts.map +1 -0
- package/dist/utils/ansi.js +57 -0
- package/dist/utils/ansi.js.map +1 -0
- package/dist/utils/identifiers.cjs +32 -0
- package/dist/utils/identifiers.cjs.map +1 -0
- package/dist/utils/identifiers.d.cts +32 -0
- package/dist/utils/identifiers.d.cts.map +1 -0
- package/dist/utils/identifiers.d.ts +32 -0
- package/dist/utils/identifiers.d.ts.map +1 -0
- package/dist/utils/identifiers.js +27 -0
- package/dist/utils/identifiers.js.map +1 -0
- package/dist/utils/package.cjs +40 -0
- package/dist/utils/package.cjs.map +1 -0
- package/dist/utils/package.d.cts +15 -0
- package/dist/utils/package.d.cts.map +1 -0
- package/dist/utils/package.d.ts +15 -0
- package/dist/utils/package.d.ts.map +1 -0
- package/dist/utils/package.js +33 -0
- package/dist/utils/package.js.map +1 -0
- package/dist/utils/type-guards.cjs +48 -0
- package/dist/utils/type-guards.cjs.map +1 -0
- package/dist/utils/type-guards.d.cts +22 -0
- package/dist/utils/type-guards.d.cts.map +1 -0
- package/dist/utils/type-guards.d.ts +22 -0
- package/dist/utils/type-guards.d.ts.map +1 -0
- package/dist/utils/type-guards.js +43 -0
- package/dist/utils/type-guards.js.map +1 -0
- package/package.json +14 -13
- package/src/bootstrap.ts +5 -5
- package/src/cli/commands/analyze.ts +101 -0
- package/src/cli/commands/baseline.ts +577 -0
- package/src/cli/commands/history.ts +194 -342
- package/src/cli/commands/init.ts +105 -183
- package/src/cli/commands/run.ts +186 -88
- package/src/cli/index.ts +731 -234
- package/src/config/budget-schema.ts +189 -0
- package/src/config/schema.ts +260 -1
- package/src/constants.ts +53 -1
- package/src/core/engine.ts +153 -14
- package/src/core/output-path-resolver.ts +46 -0
- package/src/errors/base.ts +11 -2
- package/src/errors/budget.ts +38 -0
- package/src/errors/index.ts +3 -0
- package/src/formatters/history/base.ts +28 -0
- package/src/formatters/history/compare.ts +186 -0
- package/src/formatters/history/list.ts +101 -0
- package/src/formatters/history/show.ts +155 -0
- package/src/formatters/history/trends.ts +281 -0
- package/src/formatters/history/visualization.ts +93 -0
- package/src/index.ts +17 -12
- package/src/reporters/csv.ts +55 -26
- package/src/reporters/human.ts +90 -89
- package/src/reporters/json.ts +27 -72
- package/src/reporters/profile-human.ts +204 -0
- package/src/reporters/simple.ts +85 -54
- package/src/services/baseline-storage.ts +199 -0
- package/src/services/budget-evaluator.ts +182 -0
- package/src/{config/manager.ts → services/config-manager.ts} +24 -9
- package/src/{core/loader.ts → services/file-loader.ts} +4 -7
- package/src/services/history/comparison.ts +130 -0
- package/src/services/history/models.ts +148 -0
- package/src/services/history/query.ts +116 -0
- package/src/services/history/trend-analysis.ts +238 -0
- package/src/services/profiler/profile-filter.ts +143 -0
- package/src/services/profiler/profile-parser.ts +194 -0
- package/src/services/profiler/profile-runner.ts +121 -0
- package/src/{reporters/registry.ts → services/reporter-registry.ts} +46 -81
- package/src/types/budgets.ts +180 -0
- package/src/types/cli.ts +5 -235
- package/src/types/core.ts +50 -10
- package/src/types/index.ts +5 -0
- package/src/types/interfaces.ts +16 -6
- package/src/types/profiler.ts +132 -0
- package/src/types/utility.ts +0 -10
- package/src/utils/ansi.ts +59 -0
- package/src/utils/identifiers.ts +58 -0
- package/src/utils/package.ts +35 -0
- package/src/utils/type-guards.ts +51 -0
- package/dist/config/manager.cjs.map +0 -1
- package/dist/config/manager.d.cts.map +0 -1
- package/dist/config/manager.d.ts.map +0 -1
- package/dist/config/manager.js.map +0 -1
- package/dist/core/loader.cjs.map +0 -1
- package/dist/core/loader.d.cts.map +0 -1
- package/dist/core/loader.d.ts.map +0 -1
- package/dist/core/loader.js.map +0 -1
- package/dist/progress/manager.cjs.map +0 -1
- package/dist/progress/manager.d.cts.map +0 -1
- package/dist/progress/manager.d.ts.map +0 -1
- package/dist/progress/manager.js.map +0 -1
- package/dist/reporters/registry.cjs.map +0 -1
- package/dist/reporters/registry.d.cts.map +0 -1
- package/dist/reporters/registry.d.ts.map +0 -1
- package/dist/reporters/registry.js.map +0 -1
- package/dist/storage/history.cjs.map +0 -1
- package/dist/storage/history.d.cts.map +0 -1
- package/dist/storage/history.d.ts.map +0 -1
- package/dist/storage/history.js.map +0 -1
- /package/src/{storage/history.ts → services/history-storage.ts} +0 -0
- /package/src/{progress/manager.ts → services/progress-manager.ts} +0 -0
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
BaselineSummaryData,
|
|
3
|
+
Budget,
|
|
4
|
+
BudgetResult,
|
|
5
|
+
BudgetSummary,
|
|
6
|
+
BudgetViolation,
|
|
7
|
+
TaskId,
|
|
8
|
+
TaskResult,
|
|
9
|
+
} from '../types/core.js';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Service for evaluating performance budgets
|
|
13
|
+
*
|
|
14
|
+
* @packageDocumentation
|
|
15
|
+
*/
|
|
16
|
+
export class BudgetEvaluator {
|
|
17
|
+
/**
|
|
18
|
+
* Format number with thousands separators
|
|
19
|
+
*/
|
|
20
|
+
private static formatNumber(this: void, value: number): string {
|
|
21
|
+
return value.toLocaleString('en-US', {
|
|
22
|
+
maximumFractionDigits: 0,
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Format decimal as percentage
|
|
28
|
+
*/
|
|
29
|
+
private static formatPercentage(this: void, value: number): string {
|
|
30
|
+
return `${(value * 100).toFixed(1)}%`;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Format time in nanoseconds to human-readable string
|
|
35
|
+
*/
|
|
36
|
+
private static formatTime(this: void, nanoseconds: number): string {
|
|
37
|
+
if (nanoseconds < 1_000) {
|
|
38
|
+
return `${nanoseconds.toFixed(0)}ns`;
|
|
39
|
+
} else if (nanoseconds < 1_000_000) {
|
|
40
|
+
return `${(nanoseconds / 1_000).toFixed(2)}μs`;
|
|
41
|
+
} else if (nanoseconds < 1_000_000_000) {
|
|
42
|
+
return `${(nanoseconds / 1_000_000).toFixed(2)}ms`;
|
|
43
|
+
} else {
|
|
44
|
+
return `${(nanoseconds / 1_000_000_000).toFixed(2)}s`;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Evaluate budgets for an entire benchmark run
|
|
50
|
+
*/
|
|
51
|
+
evaluateRun(
|
|
52
|
+
budgets: Record<string, Budget>,
|
|
53
|
+
taskResults: Map<TaskId, TaskResult>,
|
|
54
|
+
baselineData?: Map<TaskId, BaselineSummaryData>,
|
|
55
|
+
): BudgetSummary {
|
|
56
|
+
const results: BudgetResult[] = [];
|
|
57
|
+
|
|
58
|
+
for (const [taskId, budget] of Object.entries(budgets)) {
|
|
59
|
+
const taskResult = taskResults.get(taskId as TaskId);
|
|
60
|
+
|
|
61
|
+
// Skip if no result for this task
|
|
62
|
+
if (!taskResult) {
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Skip relative budgets if no baseline data
|
|
67
|
+
if (budget.relative && !baselineData) {
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const budgetResult = this.evaluateTask(
|
|
72
|
+
taskId as TaskId,
|
|
73
|
+
budget,
|
|
74
|
+
taskResult,
|
|
75
|
+
baselineData?.get(taskId as TaskId),
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
results.push(budgetResult);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const passed = results.filter((r) => r.passed).length;
|
|
82
|
+
const failed = results.filter((r) => !r.passed).length;
|
|
83
|
+
|
|
84
|
+
return {
|
|
85
|
+
failed,
|
|
86
|
+
passed,
|
|
87
|
+
results,
|
|
88
|
+
total: results.length,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Evaluate budgets for a single task
|
|
94
|
+
*/
|
|
95
|
+
private evaluateTask(
|
|
96
|
+
taskId: TaskId,
|
|
97
|
+
budget: Budget,
|
|
98
|
+
actual: TaskResult,
|
|
99
|
+
baseline?: BaselineSummaryData,
|
|
100
|
+
): BudgetResult {
|
|
101
|
+
const violations: BudgetViolation[] = [];
|
|
102
|
+
|
|
103
|
+
// Evaluate absolute budgets
|
|
104
|
+
if (budget.absolute) {
|
|
105
|
+
if (budget.absolute.maxTime !== undefined) {
|
|
106
|
+
if (actual.mean > budget.absolute.maxTime) {
|
|
107
|
+
violations.push({
|
|
108
|
+
actual: actual.mean,
|
|
109
|
+
delta:
|
|
110
|
+
(actual.mean - budget.absolute.maxTime) / budget.absolute.maxTime,
|
|
111
|
+
message: `Mean execution time ${BudgetEvaluator.formatTime(actual.mean)} exceeded budget of ${BudgetEvaluator.formatTime(budget.absolute.maxTime)} by ${BudgetEvaluator.formatPercentage((actual.mean - budget.absolute.maxTime) / budget.absolute.maxTime)}`,
|
|
112
|
+
threshold: budget.absolute.maxTime,
|
|
113
|
+
type: 'maxTime',
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (budget.absolute.minOpsPerSec !== undefined) {
|
|
119
|
+
if (actual.opsPerSecond < budget.absolute.minOpsPerSec) {
|
|
120
|
+
violations.push({
|
|
121
|
+
actual: actual.opsPerSecond,
|
|
122
|
+
delta:
|
|
123
|
+
(budget.absolute.minOpsPerSec - actual.opsPerSecond) /
|
|
124
|
+
budget.absolute.minOpsPerSec,
|
|
125
|
+
message: `Operations per second ${BudgetEvaluator.formatNumber(actual.opsPerSecond)} is below minimum of ${BudgetEvaluator.formatNumber(budget.absolute.minOpsPerSec)} by ${BudgetEvaluator.formatPercentage((budget.absolute.minOpsPerSec - actual.opsPerSecond) / budget.absolute.minOpsPerSec)}`,
|
|
126
|
+
threshold: budget.absolute.minOpsPerSec,
|
|
127
|
+
type: 'minOpsPerSec',
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (budget.absolute.maxP99 !== undefined && actual.p99 !== undefined) {
|
|
133
|
+
if (actual.p99 > budget.absolute.maxP99) {
|
|
134
|
+
violations.push({
|
|
135
|
+
actual: actual.p99,
|
|
136
|
+
delta:
|
|
137
|
+
(actual.p99 - budget.absolute.maxP99) / budget.absolute.maxP99,
|
|
138
|
+
message: `P99 latency ${BudgetEvaluator.formatTime(actual.p99)} exceeded budget of ${BudgetEvaluator.formatTime(budget.absolute.maxP99)} by ${BudgetEvaluator.formatPercentage((actual.p99 - budget.absolute.maxP99) / budget.absolute.maxP99)}`,
|
|
139
|
+
threshold: budget.absolute.maxP99,
|
|
140
|
+
type: 'maxP99',
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Evaluate relative budgets
|
|
147
|
+
if (budget.relative && baseline) {
|
|
148
|
+
if (budget.relative.maxRegression !== undefined) {
|
|
149
|
+
const regression = (actual.mean - baseline.mean) / baseline.mean;
|
|
150
|
+
|
|
151
|
+
if (regression > budget.relative.maxRegression) {
|
|
152
|
+
violations.push({
|
|
153
|
+
actual: regression,
|
|
154
|
+
delta: regression - budget.relative.maxRegression,
|
|
155
|
+
message: `Performance regressed by ${BudgetEvaluator.formatPercentage(regression)} exceeding maximum allowed regression of ${BudgetEvaluator.formatPercentage(budget.relative.maxRegression)}`,
|
|
156
|
+
threshold: budget.relative.maxRegression,
|
|
157
|
+
type: 'maxRegression',
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return {
|
|
164
|
+
actual: {
|
|
165
|
+
mean: actual.mean,
|
|
166
|
+
opsPerSecond: actual.opsPerSecond,
|
|
167
|
+
p99: actual.p99,
|
|
168
|
+
},
|
|
169
|
+
baseline: baseline
|
|
170
|
+
? {
|
|
171
|
+
mean: baseline.mean,
|
|
172
|
+
opsPerSecond: baseline.opsPerSecond,
|
|
173
|
+
p99: baseline.p99,
|
|
174
|
+
}
|
|
175
|
+
: undefined,
|
|
176
|
+
budget,
|
|
177
|
+
passed: violations.length === 0,
|
|
178
|
+
taskId,
|
|
179
|
+
violations,
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
}
|
|
@@ -18,9 +18,9 @@ import type {
|
|
|
18
18
|
ValidationWarning,
|
|
19
19
|
} from '../types/index.js';
|
|
20
20
|
|
|
21
|
+
import { safeParseConfig } from '../config/schema.js';
|
|
21
22
|
import { ErrorCodes } from '../constants.js';
|
|
22
23
|
import { ConfigLoadError, ConfigValidationError } from '../errors/index.js';
|
|
23
|
-
import { safeParseConfig } from './schema.js';
|
|
24
24
|
|
|
25
25
|
/**
|
|
26
26
|
* Get the default reporter based on TTY status and environment
|
|
@@ -56,7 +56,7 @@ const DEFAULT_CONFIG: ModestBenchConfig = {
|
|
|
56
56
|
limitBy: 'iterations', // Default to limiting by iteration count
|
|
57
57
|
metadata: {},
|
|
58
58
|
outputDir: './benchmark-results',
|
|
59
|
-
pattern: '
|
|
59
|
+
pattern: 'bench/**/*.bench.{js,ts,mjs,cjs,mts,cts}', // Search bench/ directory recursively
|
|
60
60
|
quiet: false,
|
|
61
61
|
reporterConfig: {},
|
|
62
62
|
reporters: [getDefaultReporter()],
|
|
@@ -121,10 +121,16 @@ export class ModestBenchConfigurationManager implements ConfigurationManager {
|
|
|
121
121
|
|
|
122
122
|
/**
|
|
123
123
|
* Load configuration from various sources with precedence
|
|
124
|
+
*
|
|
125
|
+
* @param configPath - Optional path to configuration file
|
|
126
|
+
* @param cliArgs - Optional CLI arguments to merge
|
|
127
|
+
* @param commandDefaults - Command-specific defaults (fallback to
|
|
128
|
+
* DEFAULT_CONFIG)
|
|
124
129
|
*/
|
|
125
130
|
async load(
|
|
126
131
|
configPath?: string,
|
|
127
132
|
cliArgs?: Record<string, unknown>,
|
|
133
|
+
commandDefaults?: Partial<ModestBenchConfig>,
|
|
128
134
|
): Promise<ModestBenchConfig> {
|
|
129
135
|
try {
|
|
130
136
|
// Create a fresh explorer for each load to avoid module caching issues
|
|
@@ -164,9 +170,13 @@ export class ModestBenchConfigurationManager implements ConfigurationManager {
|
|
|
164
170
|
|
|
165
171
|
const fileConfig = (result?.config || {}) as Partial<ModestBenchConfig>;
|
|
166
172
|
|
|
167
|
-
// 2. Merge: defaults <- file <- CLI args
|
|
173
|
+
// 2. Merge: command defaults <- file <- CLI args
|
|
174
|
+
// Use command-specific defaults if provided, otherwise use DEFAULT_CONFIG
|
|
175
|
+
const baseDefaults = commandDefaults
|
|
176
|
+
? this.merge(DEFAULT_CONFIG, commandDefaults)
|
|
177
|
+
: DEFAULT_CONFIG;
|
|
168
178
|
const normalizedCliArgs = cliArgs ? this.normalizeCliArgs(cliArgs) : {};
|
|
169
|
-
const merged = this.merge(
|
|
179
|
+
const merged = this.merge(baseDefaults, fileConfig, normalizedCliArgs);
|
|
170
180
|
|
|
171
181
|
// 2.5. Apply smart defaults for limitBy if not explicitly provided
|
|
172
182
|
const finalConfig = ModestBenchConfigurationManager.applySmartDefaults(
|
|
@@ -175,15 +185,20 @@ export class ModestBenchConfigurationManager implements ConfigurationManager {
|
|
|
175
185
|
fileConfig,
|
|
176
186
|
);
|
|
177
187
|
|
|
178
|
-
// 3. Validate final configuration
|
|
179
|
-
|
|
180
|
-
|
|
188
|
+
// 3. Validate final configuration and get transformed config
|
|
189
|
+
// The validation also transforms budgets from nested to flat format
|
|
190
|
+
const validation = safeParseConfig(finalConfig);
|
|
191
|
+
if (!validation.success) {
|
|
192
|
+
const errors = validation.error.issues.map((issue) => {
|
|
193
|
+
const path = issue.path.join('.');
|
|
194
|
+
return `${path ? `${path}: ` : ''}${issue.message}`;
|
|
195
|
+
});
|
|
181
196
|
throw new ConfigValidationError(
|
|
182
|
-
`Configuration validation failed: ${
|
|
197
|
+
`Configuration validation failed: ${errors.join(', ')}`,
|
|
183
198
|
);
|
|
184
199
|
}
|
|
185
200
|
|
|
186
|
-
return
|
|
201
|
+
return validation.data;
|
|
187
202
|
} catch (error) {
|
|
188
203
|
// Re-throw our custom errors
|
|
189
204
|
if (
|
|
@@ -22,12 +22,12 @@ import {
|
|
|
22
22
|
BENCHMARK_FILE_EXTENSIONS,
|
|
23
23
|
BENCHMARK_FILE_PATTERN,
|
|
24
24
|
} from '../constants.js';
|
|
25
|
+
import { benchmarkFileSchema } from '../core/benchmark-schema.js';
|
|
25
26
|
import {
|
|
26
27
|
FileDiscoveryError,
|
|
27
28
|
FileLoadError,
|
|
28
29
|
StructureValidationError,
|
|
29
30
|
} from '../errors/index.js';
|
|
30
|
-
import { benchmarkFileSchema } from './benchmark-schema.js';
|
|
31
31
|
|
|
32
32
|
/**
|
|
33
33
|
* File change notification for watch functionality
|
|
@@ -64,8 +64,7 @@ export class BenchmarkFileLoader implements FileLoader {
|
|
|
64
64
|
// Handle empty patterns - use sensible defaults
|
|
65
65
|
if (patterns.length === 0) {
|
|
66
66
|
patterns = [
|
|
67
|
-
|
|
68
|
-
`bench/*${BENCHMARK_FILE_PATTERN}`, // top-level bench/ directory
|
|
67
|
+
`bench/**/*${BENCHMARK_FILE_PATTERN}`, // bench/ directory (recursive)
|
|
69
68
|
];
|
|
70
69
|
}
|
|
71
70
|
|
|
@@ -152,10 +151,8 @@ export class BenchmarkFileLoader implements FileLoader {
|
|
|
152
151
|
default?: unknown;
|
|
153
152
|
};
|
|
154
153
|
} else {
|
|
155
|
-
// Use native dynamic import for JavaScript files
|
|
156
|
-
|
|
157
|
-
const timestamp = Date.now();
|
|
158
|
-
module = (await import(`${filePath}?t=${timestamp}`)) as {
|
|
154
|
+
// Use native dynamic import for JavaScript files
|
|
155
|
+
module = (await import(filePath)) as {
|
|
159
156
|
[key: string]: unknown;
|
|
160
157
|
default?: unknown;
|
|
161
158
|
};
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Benchmark Comparison Service
|
|
3
|
+
*
|
|
4
|
+
* Handles comparison logic between two benchmark runs, calculating performance
|
|
5
|
+
* differences and categorizing tasks.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { BenchmarkRun } from '../../types/index.js';
|
|
9
|
+
import type { CompareResult, TaskComparison } from './models.js';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Service for comparing benchmark runs
|
|
13
|
+
*/
|
|
14
|
+
export class ComparisonService {
|
|
15
|
+
/**
|
|
16
|
+
* Compare two benchmark runs and produce detailed comparison result
|
|
17
|
+
*/
|
|
18
|
+
compareRuns(run1: BenchmarkRun, run2: BenchmarkRun): CompareResult {
|
|
19
|
+
// Build task maps for comparison
|
|
20
|
+
const tasksMap1 = new Map<string, TaskComparison>();
|
|
21
|
+
const tasksMap2 = new Map<string, TaskComparison>();
|
|
22
|
+
|
|
23
|
+
// Extract tasks from run1
|
|
24
|
+
for (const file of run1.files) {
|
|
25
|
+
for (const suite of file.suites) {
|
|
26
|
+
for (const task of suite.tasks) {
|
|
27
|
+
if (!task.error) {
|
|
28
|
+
const key = `${file.filePath}::${suite.name}::${task.name}`;
|
|
29
|
+
tasksMap1.set(key, {
|
|
30
|
+
file: file.filePath,
|
|
31
|
+
inBoth: false,
|
|
32
|
+
percentChange: 0,
|
|
33
|
+
run1: {
|
|
34
|
+
cv: task.cv,
|
|
35
|
+
iterations: task.iterations,
|
|
36
|
+
max: task.max,
|
|
37
|
+
mean: task.mean,
|
|
38
|
+
min: task.min,
|
|
39
|
+
},
|
|
40
|
+
suite: suite.name,
|
|
41
|
+
task: task.name,
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Extract tasks from run2 and merge
|
|
49
|
+
for (const file of run2.files) {
|
|
50
|
+
for (const suite of file.suites) {
|
|
51
|
+
for (const task of suite.tasks) {
|
|
52
|
+
if (!task.error) {
|
|
53
|
+
const key = `${file.filePath}::${suite.name}::${task.name}`;
|
|
54
|
+
const existing = tasksMap1.get(key);
|
|
55
|
+
|
|
56
|
+
if (existing && existing.run1) {
|
|
57
|
+
// Task exists in both runs - calculate comparison
|
|
58
|
+
const percentChange =
|
|
59
|
+
((task.mean - existing.run1.mean) / existing.run1.mean) * 100;
|
|
60
|
+
|
|
61
|
+
tasksMap1.set(key, {
|
|
62
|
+
...existing,
|
|
63
|
+
inBoth: true,
|
|
64
|
+
percentChange,
|
|
65
|
+
run2: {
|
|
66
|
+
cv: task.cv,
|
|
67
|
+
iterations: task.iterations,
|
|
68
|
+
max: task.max,
|
|
69
|
+
mean: task.mean,
|
|
70
|
+
min: task.min,
|
|
71
|
+
},
|
|
72
|
+
});
|
|
73
|
+
} else {
|
|
74
|
+
// Task only in run2
|
|
75
|
+
tasksMap2.set(key, {
|
|
76
|
+
file: file.filePath,
|
|
77
|
+
inBoth: false,
|
|
78
|
+
percentChange: 0,
|
|
79
|
+
run2: {
|
|
80
|
+
cv: task.cv,
|
|
81
|
+
iterations: task.iterations,
|
|
82
|
+
max: task.max,
|
|
83
|
+
mean: task.mean,
|
|
84
|
+
min: task.min,
|
|
85
|
+
},
|
|
86
|
+
suite: suite.name,
|
|
87
|
+
task: task.name,
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Separate tasks into categories
|
|
96
|
+
const tasksInBoth: TaskComparison[] = [];
|
|
97
|
+
const tasksOnlyIn1: TaskComparison[] = [];
|
|
98
|
+
const tasksOnlyIn2: TaskComparison[] = [];
|
|
99
|
+
|
|
100
|
+
for (const task of tasksMap1.values()) {
|
|
101
|
+
if (task.inBoth) {
|
|
102
|
+
tasksInBoth.push(task);
|
|
103
|
+
} else {
|
|
104
|
+
tasksOnlyIn1.push(task);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
for (const task of tasksMap2.values()) {
|
|
109
|
+
tasksOnlyIn2.push(task);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return {
|
|
113
|
+
run1: {
|
|
114
|
+
endTime: run1.endTime,
|
|
115
|
+
id: run1.id,
|
|
116
|
+
startTime: run1.startTime,
|
|
117
|
+
summary: run1.summary,
|
|
118
|
+
},
|
|
119
|
+
run2: {
|
|
120
|
+
endTime: run2.endTime,
|
|
121
|
+
id: run2.id,
|
|
122
|
+
startTime: run2.startTime,
|
|
123
|
+
summary: run2.summary,
|
|
124
|
+
},
|
|
125
|
+
tasksInBoth,
|
|
126
|
+
tasksOnlyInRun1: tasksOnlyIn1,
|
|
127
|
+
tasksOnlyInRun2: tasksOnlyIn2,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Data Models for History Services
|
|
3
|
+
*
|
|
4
|
+
* Type definitions for history command data structures, shared between
|
|
5
|
+
* services, formatters, and CLI handlers.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { BenchmarkRun } from '../../types/index.js';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Result from comparing two benchmark runs
|
|
12
|
+
*/
|
|
13
|
+
export interface CompareResult {
|
|
14
|
+
run1: {
|
|
15
|
+
endTime: Date;
|
|
16
|
+
id: string;
|
|
17
|
+
startTime: Date;
|
|
18
|
+
summary: {
|
|
19
|
+
failedTasks: number;
|
|
20
|
+
passedTasks: number;
|
|
21
|
+
totalFiles: number;
|
|
22
|
+
totalTasks: number;
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
run2: {
|
|
26
|
+
endTime: Date;
|
|
27
|
+
id: string;
|
|
28
|
+
startTime: Date;
|
|
29
|
+
summary: {
|
|
30
|
+
failedTasks: number;
|
|
31
|
+
passedTasks: number;
|
|
32
|
+
totalFiles: number;
|
|
33
|
+
totalTasks: number;
|
|
34
|
+
};
|
|
35
|
+
};
|
|
36
|
+
tasksInBoth: TaskComparison[];
|
|
37
|
+
tasksOnlyInRun1: TaskComparison[];
|
|
38
|
+
tasksOnlyInRun2: TaskComparison[];
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Distribution bucket for histogram visualization
|
|
43
|
+
*/
|
|
44
|
+
export interface DistributionBucket {
|
|
45
|
+
count: number;
|
|
46
|
+
label: string;
|
|
47
|
+
max: number;
|
|
48
|
+
min: number;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Result from listing historical runs
|
|
53
|
+
*/
|
|
54
|
+
export interface HistoryListResult {
|
|
55
|
+
runs: Array<{
|
|
56
|
+
duration: number;
|
|
57
|
+
id: string;
|
|
58
|
+
startTime: Date;
|
|
59
|
+
summary: {
|
|
60
|
+
failedTasks: number;
|
|
61
|
+
passedTasks: number;
|
|
62
|
+
totalFiles: number;
|
|
63
|
+
totalTasks: number;
|
|
64
|
+
};
|
|
65
|
+
}>;
|
|
66
|
+
totalCount: number;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Show command result (just wraps BenchmarkRun for consistency)
|
|
71
|
+
*/
|
|
72
|
+
export type ShowResult = BenchmarkRun;
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Task comparison result between two runs
|
|
76
|
+
*/
|
|
77
|
+
export interface TaskComparison {
|
|
78
|
+
file: string;
|
|
79
|
+
inBoth: boolean;
|
|
80
|
+
percentChange: number;
|
|
81
|
+
run1?: {
|
|
82
|
+
cv: number;
|
|
83
|
+
iterations: number;
|
|
84
|
+
max: number;
|
|
85
|
+
mean: number;
|
|
86
|
+
min: number;
|
|
87
|
+
};
|
|
88
|
+
run2?: {
|
|
89
|
+
cv: number;
|
|
90
|
+
iterations: number;
|
|
91
|
+
max: number;
|
|
92
|
+
mean: number;
|
|
93
|
+
min: number;
|
|
94
|
+
};
|
|
95
|
+
suite: string;
|
|
96
|
+
task: string;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Complete trend analysis for a single task
|
|
101
|
+
*/
|
|
102
|
+
export interface TrendData {
|
|
103
|
+
confidence: number;
|
|
104
|
+
dataPoints: TrendDataPoint[];
|
|
105
|
+
percentChange: number;
|
|
106
|
+
runs: number;
|
|
107
|
+
statistics: TrendStatistics;
|
|
108
|
+
task: string;
|
|
109
|
+
trend: 'degrading' | 'improving' | 'stable';
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Single data point in a trend series
|
|
114
|
+
*/
|
|
115
|
+
export interface TrendDataPoint {
|
|
116
|
+
date: Date;
|
|
117
|
+
mean: number;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Result from analyzing performance trends
|
|
122
|
+
*/
|
|
123
|
+
export interface TrendsResult {
|
|
124
|
+
lowConfidenceRegressions: TrendData[];
|
|
125
|
+
regressions: TrendData[];
|
|
126
|
+
runs: number;
|
|
127
|
+
summary: {
|
|
128
|
+
degradingTasks: number;
|
|
129
|
+
improvingTasks: number;
|
|
130
|
+
stableTasks: number;
|
|
131
|
+
totalTasks: number;
|
|
132
|
+
};
|
|
133
|
+
timespan: {
|
|
134
|
+
end: Date;
|
|
135
|
+
start: Date;
|
|
136
|
+
};
|
|
137
|
+
trends: TrendData[];
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Statistical metrics for trend analysis
|
|
142
|
+
*/
|
|
143
|
+
export interface TrendStatistics {
|
|
144
|
+
mean: number;
|
|
145
|
+
median: number;
|
|
146
|
+
stdDeviation: number;
|
|
147
|
+
variance: number;
|
|
148
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* History Query Service
|
|
3
|
+
*
|
|
4
|
+
* Handles querying benchmark run history with date parsing and filtering.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type {
|
|
8
|
+
BenchmarkRun,
|
|
9
|
+
HistoryQuery,
|
|
10
|
+
HistoryStorage,
|
|
11
|
+
} from '../../types/index.js';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Service for querying historical benchmark runs
|
|
15
|
+
*/
|
|
16
|
+
export class HistoryQueryService {
|
|
17
|
+
constructor(private readonly storage: HistoryStorage) {}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Query runs with automatic date string parsing
|
|
21
|
+
*/
|
|
22
|
+
async queryWithDateParsing(options: {
|
|
23
|
+
limit?: number;
|
|
24
|
+
pattern?: string;
|
|
25
|
+
since?: string;
|
|
26
|
+
tags?: string[];
|
|
27
|
+
until?: string;
|
|
28
|
+
}): Promise<BenchmarkRun[]> {
|
|
29
|
+
// Build query object all at once
|
|
30
|
+
const query: Partial<HistoryQuery> = {
|
|
31
|
+
...(options.since && { since: parseDate(options.since) }),
|
|
32
|
+
...(options.until && { until: parseDate(options.until) }),
|
|
33
|
+
...(options.pattern && { pattern: options.pattern }),
|
|
34
|
+
...(options.tags && options.tags.length > 0 && { tags: options.tags }),
|
|
35
|
+
...(options.limit && { limit: options.limit }),
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
return await this.storage.queryRuns(query);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Parse date string (ISO 8601 or relative)
|
|
44
|
+
*
|
|
45
|
+
* Supports:
|
|
46
|
+
*
|
|
47
|
+
* - ISO 8601: "2025-10-24T12:00:00Z", "2025-10-24"
|
|
48
|
+
* - Relative: "1 day ago", "3 weeks ago", "2 hours ago"
|
|
49
|
+
* - Shorthand: "1d", "2w", "3m", "6h"
|
|
50
|
+
*
|
|
51
|
+
* @param dateStr - Date string to parse
|
|
52
|
+
* @returns Parsed Date object
|
|
53
|
+
* @throws Error if date format is invalid
|
|
54
|
+
*/
|
|
55
|
+
export const parseDate = (dateStr: string): Date => {
|
|
56
|
+
if (!dateStr || dateStr.trim() === '') {
|
|
57
|
+
throw new Error(`Invalid date format: "${dateStr}"`);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Try parsing as ISO 8601 first
|
|
61
|
+
const isoDate = new Date(dateStr);
|
|
62
|
+
if (!isNaN(isoDate.getTime())) {
|
|
63
|
+
return isoDate;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Parse relative dates like "1 week ago", "3 days ago"
|
|
67
|
+
const relativePattern = /^(\d+)\s+(hour|day|week|month)s?\s+ago$/i;
|
|
68
|
+
const relativeMatch = dateStr.trim().match(relativePattern);
|
|
69
|
+
|
|
70
|
+
if (relativeMatch && relativeMatch[1] && relativeMatch[2]) {
|
|
71
|
+
const amount = parseInt(relativeMatch[1], 10);
|
|
72
|
+
const unit = relativeMatch[2].toLowerCase();
|
|
73
|
+
|
|
74
|
+
if (amount <= 0) {
|
|
75
|
+
throw new Error(`Invalid date format: "${dateStr}"`);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const now = new Date();
|
|
79
|
+
const msPerUnit: Record<string, number> = {
|
|
80
|
+
day: 24 * 60 * 60 * 1000,
|
|
81
|
+
hour: 60 * 60 * 1000,
|
|
82
|
+
month: 30 * 24 * 60 * 60 * 1000, // Approximate
|
|
83
|
+
week: 7 * 24 * 60 * 60 * 1000,
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
const offset = amount * (msPerUnit[unit] || 0);
|
|
87
|
+
return new Date(now.getTime() - offset);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Parse shorthand formats like "1d", "2w", "3m", "6h"
|
|
91
|
+
// cspell:ignore hdwm
|
|
92
|
+
const shorthandPattern = /^(\d+)([hdwm])$/i;
|
|
93
|
+
const shorthandMatch = dateStr.trim().match(shorthandPattern);
|
|
94
|
+
|
|
95
|
+
if (shorthandMatch && shorthandMatch[1] && shorthandMatch[2]) {
|
|
96
|
+
const amount = parseInt(shorthandMatch[1], 10);
|
|
97
|
+
const unit = shorthandMatch[2].toLowerCase();
|
|
98
|
+
|
|
99
|
+
if (amount <= 0) {
|
|
100
|
+
throw new Error(`Invalid date format: "${dateStr}"`);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const now = new Date();
|
|
104
|
+
const msPerUnit: Record<string, number> = {
|
|
105
|
+
d: 24 * 60 * 60 * 1000,
|
|
106
|
+
h: 60 * 60 * 1000,
|
|
107
|
+
m: 30 * 24 * 60 * 60 * 1000, // Approximate month
|
|
108
|
+
w: 7 * 24 * 60 * 60 * 1000,
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
const offset = amount * (msPerUnit[unit] || 0);
|
|
112
|
+
return new Date(now.getTime() - offset);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
throw new Error(`Invalid date format: "${dateStr}"`);
|
|
116
|
+
};
|