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,204 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Profile Human Reporter
|
|
3
|
+
*
|
|
4
|
+
* Human-readable reporter for profile command. Uses modestbench's synthwave
|
|
5
|
+
* ANSI theme to display profiled functions in an attractive, color-coded
|
|
6
|
+
* format.
|
|
7
|
+
*
|
|
8
|
+
* @packageDocumentation
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import path from 'node:path';
|
|
12
|
+
|
|
13
|
+
import type { FilteredProfileData } from '../types/profiler.js';
|
|
14
|
+
|
|
15
|
+
import { ansiChars, colors } from '../utils/ansi.js';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Reporter options
|
|
19
|
+
*/
|
|
20
|
+
interface ProfileReporterOptions {
|
|
21
|
+
/** Enable color output */
|
|
22
|
+
color?: boolean;
|
|
23
|
+
|
|
24
|
+
/** Group by file */
|
|
25
|
+
groupByFile?: boolean;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Human-readable profile reporter
|
|
30
|
+
*/
|
|
31
|
+
export class ProfileHumanReporter {
|
|
32
|
+
private readonly groupByFile: boolean;
|
|
33
|
+
|
|
34
|
+
private readonly useColor: boolean;
|
|
35
|
+
|
|
36
|
+
constructor(options: ProfileReporterOptions = {}) {
|
|
37
|
+
this.useColor =
|
|
38
|
+
options.color ??
|
|
39
|
+
(process.stdout.isTTY &&
|
|
40
|
+
process.env.FORCE_COLOR !== '0' &&
|
|
41
|
+
process.env.NO_COLOR == null);
|
|
42
|
+
|
|
43
|
+
this.groupByFile = options.groupByFile ?? false;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Generate and print profile report
|
|
48
|
+
*/
|
|
49
|
+
report(data: FilteredProfileData): void {
|
|
50
|
+
this.printHeader(data);
|
|
51
|
+
this.printLine();
|
|
52
|
+
|
|
53
|
+
if (this.groupByFile && data.groupedByFile) {
|
|
54
|
+
this.printGroupedResults(data);
|
|
55
|
+
} else {
|
|
56
|
+
this.printFlatResults(data);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
private colorize(color: keyof typeof colors, text: string): string {
|
|
61
|
+
if (!this.useColor) {
|
|
62
|
+
return text;
|
|
63
|
+
}
|
|
64
|
+
return `${colors[color]}${text}${colors.reset}`;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Format file path - show relative path if within CWD, otherwise absolute
|
|
69
|
+
*/
|
|
70
|
+
private formatPath(filePath: string): string {
|
|
71
|
+
const cwd = process.cwd();
|
|
72
|
+
const absolutePath = path.resolve(filePath);
|
|
73
|
+
|
|
74
|
+
// Check if the file is within the current working directory
|
|
75
|
+
if (absolutePath.startsWith(cwd + path.sep) || absolutePath === cwd) {
|
|
76
|
+
return path.relative(cwd, absolutePath);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return absolutePath;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
private getPercentColor(percent: number): keyof typeof colors {
|
|
83
|
+
if (percent >= 10) {
|
|
84
|
+
return 'brightRed';
|
|
85
|
+
}
|
|
86
|
+
if (percent >= 5) {
|
|
87
|
+
return 'brightYellow';
|
|
88
|
+
}
|
|
89
|
+
if (percent >= 2) {
|
|
90
|
+
return 'brightCyan';
|
|
91
|
+
}
|
|
92
|
+
return 'white';
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
private printFlatResults(data: FilteredProfileData): void {
|
|
96
|
+
const header = `${this.colorize('magenta', ansiChars.block.full.repeat(2))} ${this.colorize('brightWhite', this.colorize('bold', 'Benchmark Candidates'))}`;
|
|
97
|
+
this.printLine(header);
|
|
98
|
+
this.printLine();
|
|
99
|
+
this.printLine('Top functions by execution time:');
|
|
100
|
+
this.printLine();
|
|
101
|
+
|
|
102
|
+
for (const fn of data.functions) {
|
|
103
|
+
// Function name and percentage
|
|
104
|
+
const percentColor = this.getPercentColor(fn.percentage);
|
|
105
|
+
const percent = `${fn.percentage.toFixed(1)}%`;
|
|
106
|
+
const ticks = `(${fn.ticks.toLocaleString()} ticks)`;
|
|
107
|
+
|
|
108
|
+
this.printLine(
|
|
109
|
+
` ${this.colorize('brightWhite', fn.name).padEnd(60)} ${this.colorize(percentColor, percent.padStart(6))} ${this.colorize('dim', ticks)}`,
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
// File and line
|
|
113
|
+
const displayPath = this.formatPath(fn.file);
|
|
114
|
+
const lineInfo = fn.line ? `:${fn.line}` : '';
|
|
115
|
+
this.printLine(
|
|
116
|
+
` ${this.colorize('brightMagenta', this.colorize('bold', displayPath + lineInfo))}`,
|
|
117
|
+
);
|
|
118
|
+
this.printLine();
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
this.printSummary(data);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
private printGroupedResults(data: FilteredProfileData): void {
|
|
125
|
+
if (!data.groupedByFile) {
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const header = `${this.colorize('magenta', ansiChars.block.full.repeat(2))} ${this.colorize('brightWhite', this.colorize('bold', 'Grouped by File'))}`;
|
|
130
|
+
this.printLine(header);
|
|
131
|
+
this.printLine();
|
|
132
|
+
|
|
133
|
+
// Sort files by total percentage
|
|
134
|
+
const sortedFiles = Array.from(data.groupedByFile.entries()).sort(
|
|
135
|
+
(a, b) => {
|
|
136
|
+
const aTotal = a[1].reduce((sum, fn) => sum + fn.percentage, 0);
|
|
137
|
+
const bTotal = b[1].reduce((sum, fn) => sum + fn.percentage, 0);
|
|
138
|
+
return bTotal - aTotal;
|
|
139
|
+
},
|
|
140
|
+
);
|
|
141
|
+
|
|
142
|
+
for (const [file, functions] of sortedFiles) {
|
|
143
|
+
const totalPercent = functions.reduce(
|
|
144
|
+
(sum, fn) => sum + fn.percentage,
|
|
145
|
+
0,
|
|
146
|
+
);
|
|
147
|
+
const totalTicks = functions.reduce((sum, fn) => sum + fn.ticks, 0);
|
|
148
|
+
|
|
149
|
+
const percentColor = this.getPercentColor(totalPercent);
|
|
150
|
+
const percent = `${totalPercent.toFixed(1)}%`;
|
|
151
|
+
const ticks = `(${totalTicks.toLocaleString()} ticks)`;
|
|
152
|
+
|
|
153
|
+
// File header
|
|
154
|
+
const displayPath = this.formatPath(file);
|
|
155
|
+
this.printLine(
|
|
156
|
+
`${this.colorize('magenta', ansiChars.block.dark)} ${this.colorize('brightMagenta', this.colorize('bold', displayPath)).padEnd(60)} ${this.colorize(percentColor, percent.padStart(6))} ${this.colorize('dim', ticks)}`,
|
|
157
|
+
);
|
|
158
|
+
|
|
159
|
+
// Functions in this file
|
|
160
|
+
for (const fn of functions) {
|
|
161
|
+
const fnPercent = `${fn.percentage.toFixed(1)}%`;
|
|
162
|
+
const fnTicks = `(${fn.ticks.toLocaleString()} ticks)`;
|
|
163
|
+
const lineInfo = fn.line ? `:${fn.line}` : '';
|
|
164
|
+
|
|
165
|
+
this.printLine(
|
|
166
|
+
` ${this.colorize('magenta', ansiChars.smallSquare)} ${this.colorize('brightWhite', fn.name).padEnd(58)} ${this.colorize(this.getPercentColor(fn.percentage), fnPercent.padStart(6))} ${this.colorize('dim', fnTicks.padEnd(15))} ${this.colorize('dim', lineInfo)}`,
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
this.printLine();
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
this.printSummary(data);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
private printHeader(data: FilteredProfileData): void {
|
|
177
|
+
const header = `${this.colorize('magenta', ansiChars.block.full.repeat(2))} ${this.colorize('brightWhite', this.colorize('bold', 'Profile Analysis'))}`;
|
|
178
|
+
this.printLine(header);
|
|
179
|
+
this.printLine();
|
|
180
|
+
|
|
181
|
+
if (data.command) {
|
|
182
|
+
this.printLine(`Command: ${this.colorize('cyan', data.command)}`);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if (data.duration) {
|
|
186
|
+
const durationSec = (data.duration / 1000).toFixed(1);
|
|
187
|
+
this.printLine(`Duration: ${this.colorize('cyan', `${durationSec}s`)}`);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
this.printLine(
|
|
191
|
+
`Total Ticks: ${this.colorize('cyan', data.totalTicks.toLocaleString())}`,
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
private printLine(text = ''): void {
|
|
196
|
+
console.log(text);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
private printSummary(data: FilteredProfileData): void {
|
|
200
|
+
this.printLine(
|
|
201
|
+
`${this.colorize('dim', `... (showing top ${data.totalShown} of ${data.totalFiltered} user functions)`)}`,
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
}
|
package/src/reporters/simple.ts
CHANGED
|
@@ -9,13 +9,13 @@ import path from 'node:path';
|
|
|
9
9
|
|
|
10
10
|
import type {
|
|
11
11
|
BenchmarkRun,
|
|
12
|
+
BudgetSummary,
|
|
12
13
|
FileResult,
|
|
13
|
-
ProgressState,
|
|
14
14
|
SuiteResult,
|
|
15
15
|
TaskResult,
|
|
16
16
|
} from '../types/index.js';
|
|
17
17
|
|
|
18
|
-
import { BaseReporter } from '
|
|
18
|
+
import { BaseReporter } from '../services/reporter-registry.js';
|
|
19
19
|
|
|
20
20
|
/**
|
|
21
21
|
* Basic symbols for plain text output
|
|
@@ -62,6 +62,78 @@ export class SimpleReporter extends BaseReporter {
|
|
|
62
62
|
this.quiet = options.quiet ?? false;
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
+
/**
|
|
66
|
+
* Format bytes in human-readable format
|
|
67
|
+
*/
|
|
68
|
+
private static formatBytes(this: void, bytes: number): string {
|
|
69
|
+
const units = ['B', 'KB', 'MB', 'GB', 'TB'];
|
|
70
|
+
let size = bytes;
|
|
71
|
+
let unitIndex = 0;
|
|
72
|
+
|
|
73
|
+
while (size >= 1024 && unitIndex < units.length - 1) {
|
|
74
|
+
size /= 1024;
|
|
75
|
+
unitIndex++;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return `${size.toFixed(1)} ${units[unitIndex]}`;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Format file path - show relative path if within CWD, otherwise absolute
|
|
83
|
+
*/
|
|
84
|
+
private static formatPath(this: void, filePath: string): string {
|
|
85
|
+
const cwd = process.cwd();
|
|
86
|
+
const absolutePath = path.resolve(filePath);
|
|
87
|
+
|
|
88
|
+
// Check if the file is within the current working directory
|
|
89
|
+
if (absolutePath.startsWith(cwd + path.sep) || absolutePath === cwd) {
|
|
90
|
+
return path.relative(cwd, absolutePath);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return absolutePath;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Simple pluralization helper
|
|
98
|
+
*/
|
|
99
|
+
private static pluralize(this: void, str: string, count: number): string {
|
|
100
|
+
return count === 1 ? str : `${str}s`;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
onBudgetResult(summary: BudgetSummary): void {
|
|
104
|
+
if (summary.total === 0 || this.quiet) {
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
console.log('== Performance Budgets');
|
|
109
|
+
console.log();
|
|
110
|
+
|
|
111
|
+
for (const result of summary.results) {
|
|
112
|
+
const icon = result.passed ? symbols.checkmark : symbols.cross;
|
|
113
|
+
console.log(` ${icon} ${result.taskId}`);
|
|
114
|
+
|
|
115
|
+
if (!result.passed && result.violations.length > 0) {
|
|
116
|
+
for (const violation of result.violations) {
|
|
117
|
+
console.log(` ${violation.message}`);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
console.log();
|
|
123
|
+
|
|
124
|
+
if (summary.failed === 0) {
|
|
125
|
+
console.log(
|
|
126
|
+
` ${symbols.checkmark} All ${summary.total} budget(s) passed`,
|
|
127
|
+
);
|
|
128
|
+
} else {
|
|
129
|
+
console.log(
|
|
130
|
+
` ${symbols.cross} ${summary.failed} of ${summary.total} budget(s) failed`,
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
console.log();
|
|
135
|
+
}
|
|
136
|
+
|
|
65
137
|
onEnd(run: BenchmarkRun): void {
|
|
66
138
|
if (this.quiet) {
|
|
67
139
|
return;
|
|
@@ -97,7 +169,7 @@ export class SimpleReporter extends BaseReporter {
|
|
|
97
169
|
console.log(`- Files: ${totalFiles}`);
|
|
98
170
|
console.log(`- Suites: ${totalSuites}`);
|
|
99
171
|
console.log(
|
|
100
|
-
`${symbols.approx} Duration: ${
|
|
172
|
+
`${symbols.approx} Duration: ${BaseReporter.formatDuration(duration * 1e6)}`,
|
|
101
173
|
);
|
|
102
174
|
console.log();
|
|
103
175
|
|
|
@@ -111,7 +183,7 @@ export class SimpleReporter extends BaseReporter {
|
|
|
111
183
|
console.log();
|
|
112
184
|
|
|
113
185
|
for (const failure of this.failures) {
|
|
114
|
-
const displayPath =
|
|
186
|
+
const displayPath = SimpleReporter.formatPath(failure.file);
|
|
115
187
|
console.log(` ${displayPath} > ${failure.suite} > ${failure.task}`);
|
|
116
188
|
console.log(` ${failure.error}`);
|
|
117
189
|
console.log();
|
|
@@ -155,7 +227,7 @@ export class SimpleReporter extends BaseReporter {
|
|
|
155
227
|
);
|
|
156
228
|
} else {
|
|
157
229
|
console.log(
|
|
158
|
-
` ${symbols.checkmark} ${totalPassed > 1 ? 'All ' : ''}${totalPassed} ${
|
|
230
|
+
` ${symbols.checkmark} ${totalPassed > 1 ? 'All ' : ''}${totalPassed} ${SimpleReporter.pluralize('task', totalPassed)} passed`,
|
|
159
231
|
);
|
|
160
232
|
}
|
|
161
233
|
|
|
@@ -169,15 +241,10 @@ export class SimpleReporter extends BaseReporter {
|
|
|
169
241
|
return;
|
|
170
242
|
}
|
|
171
243
|
|
|
172
|
-
const displayPath =
|
|
244
|
+
const displayPath = SimpleReporter.formatPath(file);
|
|
173
245
|
console.log(`-- ${displayPath}`);
|
|
174
246
|
}
|
|
175
247
|
|
|
176
|
-
onProgress(_state: ProgressState): void {
|
|
177
|
-
// Simple reporter does not display progress bars
|
|
178
|
-
return;
|
|
179
|
-
}
|
|
180
|
-
|
|
181
248
|
onStart(run: BenchmarkRun): void {
|
|
182
249
|
this.startTime = Date.now();
|
|
183
250
|
this.failures = []; // Reset failures for new run
|
|
@@ -197,7 +264,9 @@ export class SimpleReporter extends BaseReporter {
|
|
|
197
264
|
console.log(
|
|
198
265
|
` cpu: ${run.environment.cpu.model} (${run.environment.cpu.cores} cores)`,
|
|
199
266
|
);
|
|
200
|
-
console.log(
|
|
267
|
+
console.log(
|
|
268
|
+
` mem: ${SimpleReporter.formatBytes(run.environment.memory.total)}`,
|
|
269
|
+
);
|
|
201
270
|
console.log();
|
|
202
271
|
}
|
|
203
272
|
|
|
@@ -231,7 +300,7 @@ export class SimpleReporter extends BaseReporter {
|
|
|
231
300
|
console.log(` ${symbols.cross} ${failed} failed, ${passed} passed`);
|
|
232
301
|
} else {
|
|
233
302
|
console.log(
|
|
234
|
-
` ${symbols.checkmark} ${passed} ${
|
|
303
|
+
` ${symbols.checkmark} ${passed} ${SimpleReporter.pluralize('task', passed)} passed`,
|
|
235
304
|
);
|
|
236
305
|
}
|
|
237
306
|
console.log();
|
|
@@ -275,44 +344,6 @@ export class SimpleReporter extends BaseReporter {
|
|
|
275
344
|
}
|
|
276
345
|
}
|
|
277
346
|
|
|
278
|
-
/**
|
|
279
|
-
* Format bytes in human-readable format
|
|
280
|
-
*/
|
|
281
|
-
private formatBytes(bytes: number): string {
|
|
282
|
-
const units = ['B', 'KB', 'MB', 'GB', 'TB'];
|
|
283
|
-
let size = bytes;
|
|
284
|
-
let unitIndex = 0;
|
|
285
|
-
|
|
286
|
-
while (size >= 1024 && unitIndex < units.length - 1) {
|
|
287
|
-
size /= 1024;
|
|
288
|
-
unitIndex++;
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
return `${size.toFixed(1)} ${units[unitIndex]}`;
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
/**
|
|
295
|
-
* Format file path - show relative path if within CWD, otherwise absolute
|
|
296
|
-
*/
|
|
297
|
-
private formatPath(filePath: string): string {
|
|
298
|
-
const cwd = process.cwd();
|
|
299
|
-
const absolutePath = path.resolve(filePath);
|
|
300
|
-
|
|
301
|
-
// Check if the file is within the current working directory
|
|
302
|
-
if (absolutePath.startsWith(cwd + path.sep) || absolutePath === cwd) {
|
|
303
|
-
return path.relative(cwd, absolutePath);
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
return absolutePath;
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
/**
|
|
310
|
-
* Simple pluralization helper
|
|
311
|
-
*/
|
|
312
|
-
private pluralize(str: string, count: number): string {
|
|
313
|
-
return count === 1 ? str : `${str}s`;
|
|
314
|
-
}
|
|
315
|
-
|
|
316
347
|
/**
|
|
317
348
|
* Print all task results in a suite with aligned columns
|
|
318
349
|
*/
|
|
@@ -364,9 +395,9 @@ export class SimpleReporter extends BaseReporter {
|
|
|
364
395
|
};
|
|
365
396
|
}
|
|
366
397
|
|
|
367
|
-
const duration =
|
|
368
|
-
const opsPerSec =
|
|
369
|
-
const rme =
|
|
398
|
+
const duration = BaseReporter.formatDuration(result.mean * 1e9);
|
|
399
|
+
const opsPerSec = BaseReporter.formatOpsPerSecond(result.opsPerSecond);
|
|
400
|
+
const rme = BaseReporter.formatPercentage(result.marginOfError * 100);
|
|
370
401
|
|
|
371
402
|
return {
|
|
372
403
|
durationLen: duration.length,
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import { mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
|
|
5
|
+
import type {
|
|
6
|
+
BaselineReference,
|
|
7
|
+
BaselineStorage,
|
|
8
|
+
BaselineSummaryData,
|
|
9
|
+
BenchmarkRun,
|
|
10
|
+
TaskId,
|
|
11
|
+
} from '../types/core.js';
|
|
12
|
+
|
|
13
|
+
import { validateBaselineStorage } from '../config/budget-schema.js';
|
|
14
|
+
import { StorageError } from '../errors/storage.js';
|
|
15
|
+
import { createTaskId } from '../types/core.js';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Service for managing named baselines
|
|
19
|
+
*
|
|
20
|
+
* @packageDocumentation
|
|
21
|
+
*/
|
|
22
|
+
export class BaselineStorageService {
|
|
23
|
+
private readonly storageDir: string;
|
|
24
|
+
|
|
25
|
+
private readonly storageFile: string;
|
|
26
|
+
|
|
27
|
+
constructor(storageDir: string = '.') {
|
|
28
|
+
this.storageDir = storageDir;
|
|
29
|
+
this.storageFile = join(storageDir, '.modestbench.baselines.json');
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Delete a baseline
|
|
34
|
+
*/
|
|
35
|
+
async deleteBaseline(name: string): Promise<void> {
|
|
36
|
+
let storage = await this.loadStorage();
|
|
37
|
+
|
|
38
|
+
if (storage.baselines[name]) {
|
|
39
|
+
delete storage.baselines[name];
|
|
40
|
+
|
|
41
|
+
// Clear default if it was the deleted baseline
|
|
42
|
+
if (storage.default === name) {
|
|
43
|
+
storage = { ...storage, default: undefined };
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
await this.saveStorage(storage);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Get a baseline by name
|
|
52
|
+
*/
|
|
53
|
+
async getBaseline(name: string): Promise<BaselineReference | null> {
|
|
54
|
+
const storage = await this.loadStorage();
|
|
55
|
+
return storage.baselines[name] ?? null;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Get default baseline name
|
|
60
|
+
*/
|
|
61
|
+
async getDefault(): Promise<null | string> {
|
|
62
|
+
const storage = await this.loadStorage();
|
|
63
|
+
return storage.default ?? null;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* List all baselines
|
|
68
|
+
*/
|
|
69
|
+
async listBaselines(): Promise<BaselineReference[]> {
|
|
70
|
+
const storage = await this.loadStorage();
|
|
71
|
+
return Object.values(storage.baselines).sort(
|
|
72
|
+
(a, b) => b.date.getTime() - a.date.getTime(),
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Resolve baseline name (use provided or fall back to default)
|
|
78
|
+
*/
|
|
79
|
+
async resolveBaselineName(name?: string): Promise<null | string> {
|
|
80
|
+
if (name) {
|
|
81
|
+
return name;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return await this.getDefault();
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Save a benchmark run as a named baseline
|
|
89
|
+
*/
|
|
90
|
+
async saveBaseline(
|
|
91
|
+
name: string,
|
|
92
|
+
run: BenchmarkRun,
|
|
93
|
+
metadata?: {
|
|
94
|
+
branch?: string;
|
|
95
|
+
commit?: string;
|
|
96
|
+
},
|
|
97
|
+
): Promise<void> {
|
|
98
|
+
const storage = await this.loadStorage();
|
|
99
|
+
|
|
100
|
+
const baseline: BaselineReference = {
|
|
101
|
+
branch: metadata?.branch,
|
|
102
|
+
commit: metadata?.commit,
|
|
103
|
+
date:
|
|
104
|
+
run.startTime instanceof Date ? run.startTime : new Date(run.startTime),
|
|
105
|
+
name,
|
|
106
|
+
runId: run.id,
|
|
107
|
+
summary: this.extractSummary(run),
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
storage.baselines[name] = baseline;
|
|
111
|
+
|
|
112
|
+
await this.saveStorage(storage);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Set default baseline
|
|
117
|
+
*/
|
|
118
|
+
async setDefault(name: string): Promise<void> {
|
|
119
|
+
let storage = await this.loadStorage();
|
|
120
|
+
|
|
121
|
+
if (!storage.baselines[name]) {
|
|
122
|
+
throw new StorageError(
|
|
123
|
+
`Baseline "${name}" does not exist. Cannot set as default.`,
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
storage = { ...storage, default: name };
|
|
128
|
+
await this.saveStorage(storage);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Extract task summary from benchmark run
|
|
133
|
+
*/
|
|
134
|
+
private extractSummary(
|
|
135
|
+
run: BenchmarkRun,
|
|
136
|
+
): Record<TaskId, BaselineSummaryData> {
|
|
137
|
+
const summary: Record<TaskId, BaselineSummaryData> = {};
|
|
138
|
+
|
|
139
|
+
for (const file of run.files) {
|
|
140
|
+
for (const suite of file.suites) {
|
|
141
|
+
for (const task of suite.tasks) {
|
|
142
|
+
if (!task.error) {
|
|
143
|
+
const taskId = createTaskId(file.filePath, suite.name, task.name);
|
|
144
|
+
summary[taskId] = {
|
|
145
|
+
mean: task.mean,
|
|
146
|
+
opsPerSecond: task.opsPerSecond,
|
|
147
|
+
p99: task.p99,
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return summary;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Load baseline storage from disk
|
|
159
|
+
*/
|
|
160
|
+
private async loadStorage(): Promise<BaselineStorage> {
|
|
161
|
+
if (!existsSync(this.storageFile)) {
|
|
162
|
+
return {
|
|
163
|
+
baselines: {},
|
|
164
|
+
version: '1.0.0',
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
try {
|
|
169
|
+
const content = await readFile(this.storageFile, 'utf-8');
|
|
170
|
+
const data = JSON.parse(content) as unknown;
|
|
171
|
+
return validateBaselineStorage(data);
|
|
172
|
+
} catch (error) {
|
|
173
|
+
throw new StorageError(
|
|
174
|
+
`Failed to load baseline storage from ${this.storageFile}`,
|
|
175
|
+
{ cause: error },
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Save baseline storage to disk
|
|
182
|
+
*/
|
|
183
|
+
private async saveStorage(storage: BaselineStorage): Promise<void> {
|
|
184
|
+
try {
|
|
185
|
+
// Ensure directory exists
|
|
186
|
+
if (!existsSync(this.storageDir)) {
|
|
187
|
+
await mkdir(this.storageDir, { recursive: true });
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const content = JSON.stringify(storage, null, 2);
|
|
191
|
+
await writeFile(this.storageFile, content, 'utf-8');
|
|
192
|
+
} catch (error) {
|
|
193
|
+
throw new StorageError(
|
|
194
|
+
`Failed to save baseline storage to ${this.storageFile}`,
|
|
195
|
+
{ cause: error },
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|