flexi-bench 0.0.0-alpha.3 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/README.md +373 -0
  3. package/dist/api-types.d.ts +28 -7
  4. package/dist/api-types.js +35 -0
  5. package/dist/benchmark-runner.d.ts +79 -0
  6. package/dist/benchmark-runner.js +172 -0
  7. package/dist/benchmark.d.ts +13 -12
  8. package/dist/benchmark.js +188 -120
  9. package/dist/index.d.ts +6 -1
  10. package/dist/index.js +6 -1
  11. package/dist/performance-observer.d.ts +12 -0
  12. package/dist/performance-observer.js +47 -0
  13. package/dist/{benchmark-console-reporter.d.ts → reporters/benchmark-console-reporter.d.ts} +5 -3
  14. package/dist/reporters/benchmark-console-reporter.js +29 -0
  15. package/dist/reporters/markdown-benchmark-reporter.d.ts +12 -0
  16. package/dist/reporters/markdown-benchmark-reporter.js +29 -0
  17. package/dist/reporters/noop-reporter.d.ts +4 -0
  18. package/dist/reporters/noop-reporter.js +7 -0
  19. package/dist/{suite-console-reporter.d.ts → reporters/suite-console-reporter.d.ts} +2 -1
  20. package/dist/reporters/suite-console-reporter.js +16 -0
  21. package/dist/results.d.ts +42 -0
  22. package/dist/results.js +26 -0
  23. package/dist/shared-api.d.ts +15 -0
  24. package/dist/shared-api.js +36 -0
  25. package/dist/suite.d.ts +18 -4
  26. package/dist/suite.js +51 -23
  27. package/dist/utils.d.ts +1 -0
  28. package/dist/utils.js +14 -0
  29. package/dist/variation.d.ts +22 -8
  30. package/dist/variation.js +39 -15
  31. package/package.json +27 -10
  32. package/dist/benchmark-console-reporter.js +0 -31
  33. package/dist/console-reporter.d.ts +0 -8
  34. package/dist/console-reporter.js +0 -31
  35. package/dist/suite-console-reporter.js +0 -15
@@ -0,0 +1,172 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.xvariation = exports.xit = exports.xtest = exports.xbenchmark = exports.xdescribe = exports.xsuite = exports.it = exports.test = exports.describe = exports.afterEach = exports.afterAll = exports.beforeEach = exports.beforeAll = void 0;
4
+ exports.suite = suite;
5
+ exports.benchmark = benchmark;
6
+ exports.variation = variation;
7
+ exports.setup = setup;
8
+ exports.setupEach = setupEach;
9
+ exports.teardown = teardown;
10
+ exports.teardownEach = teardownEach;
11
+ const benchmark_1 = require("./benchmark");
12
+ const suite_1 = require("./suite");
13
+ const variation_1 = require("./variation");
14
+ let activeSuite = null;
15
+ /**
16
+ * Registers a new suite to run.
17
+ * @param name The name of the suite.
18
+ * @param fn Callback to register benchmarks and update the suite.
19
+ * @returns The results of the suite. `Record<string, Result[]>`
20
+ */
21
+ function suite(name, fn) {
22
+ const suite = new suite_1.Suite(name);
23
+ activeSuite = suite;
24
+ const transformed = fn(suite);
25
+ activeSuite = null;
26
+ return (transformed ?? suite).run();
27
+ }
28
+ let activeBenchmark = null;
29
+ /**
30
+ * Registers a new benchmark to run. If inside a {@link suite} callback, it will be added to the suite. Otherwise, it will run immediately.
31
+ * @param name The name of the benchmark.
32
+ * @param fn Callback to register variations and update the benchmark.
33
+ * @returns If not inside a suite, the results of the benchmark. `Result[]`. Else, `void`.
34
+ */
35
+ function benchmark(name, fn) {
36
+ const benchmark = new benchmark_1.Benchmark(name);
37
+ activeBenchmark = benchmark;
38
+ const transformed = fn(benchmark);
39
+ activeBenchmark = null;
40
+ if (activeSuite) {
41
+ activeSuite.addBenchmark(transformed ?? benchmark);
42
+ }
43
+ else {
44
+ return (transformed ?? benchmark).run();
45
+ }
46
+ }
47
+ let activeVariation = null;
48
+ /**
49
+ * Registers a new variation to run. Must be inside a {@link benchmark} or {@link suite} callback.
50
+ * @param name The name of the variation.
51
+ * @param fn A callback to update the variation.
52
+ * @returns `void`
53
+ */
54
+ function variation(name, fn) {
55
+ const variation = new variation_1.Variation(name);
56
+ activeVariation = variation;
57
+ const transformed = fn(variation);
58
+ activeVariation = null;
59
+ if (activeBenchmark) {
60
+ activeBenchmark.withVariation(transformed ?? variation);
61
+ }
62
+ else if (activeSuite) {
63
+ activeSuite.withVariation(transformed ?? variation);
64
+ }
65
+ else {
66
+ throw new Error('`variation` must be called within a benchmark or suite');
67
+ }
68
+ }
69
+ /**
70
+ * Registers a setup method to run before the benchmark or variation. Ran once per benchmark or variation.
71
+ * @param fn The setup method.
72
+ */
73
+ function setup(fn) {
74
+ if (activeVariation) {
75
+ activeVariation.withSetup(fn);
76
+ }
77
+ else if (activeBenchmark) {
78
+ activeBenchmark.withSetup(fn);
79
+ }
80
+ else {
81
+ throw new Error('`beforeAll` must be called within a variation or benchmark');
82
+ }
83
+ }
84
+ /**
85
+ * Registers a setup method to run before each iteration of the benchmark or variation.
86
+ * @param fn The setup method.
87
+ */
88
+ function setupEach(fn) {
89
+ if (activeVariation) {
90
+ activeVariation.withSetupEach(fn);
91
+ }
92
+ else if (activeBenchmark) {
93
+ activeBenchmark.withSetupEach(fn);
94
+ }
95
+ else {
96
+ throw new Error('`beforeEach` must be called within a variation or benchmark');
97
+ }
98
+ }
99
+ /**
100
+ * Registers a teardown method to run after the benchmark or variation. Ran once per benchmark or variation.
101
+ * @param fn The teardown method.
102
+ */
103
+ function teardown(fn) {
104
+ if (activeVariation) {
105
+ activeVariation.withTeardown(fn);
106
+ }
107
+ else if (activeBenchmark) {
108
+ activeBenchmark.withTeardown(fn);
109
+ }
110
+ else {
111
+ throw new Error('`afterAll` must be called within a variation or benchmark');
112
+ }
113
+ }
114
+ /**
115
+ * Registers a teardown method to run after each iteration of the benchmark or variation.
116
+ * @param fn The teardown method.
117
+ */
118
+ function teardownEach(fn) {
119
+ if (activeVariation) {
120
+ activeVariation.withTeardownEach(fn);
121
+ }
122
+ else if (activeBenchmark) {
123
+ activeBenchmark.withTeardownEach(fn);
124
+ }
125
+ else {
126
+ throw new Error('`afterEach` must be called within a variation or benchmark');
127
+ }
128
+ }
129
+ // ALIASES
130
+ // We provide some aliases for comfort when users are familiar with other testing tools.
131
+ /**
132
+ * Alias for `setup`.
133
+ */
134
+ exports.beforeAll = setup;
135
+ /**
136
+ * Alias for `setupEach`.
137
+ */
138
+ exports.beforeEach = setupEach;
139
+ /**
140
+ * Alias for `teardown`.
141
+ */
142
+ exports.afterAll = teardown;
143
+ /**
144
+ * Alias for `teardownEach`.
145
+ */
146
+ exports.afterEach = teardownEach;
147
+ /**
148
+ * Alias for `suite`.
149
+ */
150
+ exports.describe = suite;
151
+ /**
152
+ * Alias for `benchmark`.
153
+ */
154
+ exports.test = benchmark;
155
+ /**
156
+ * Alias for `benchmark`.
157
+ */
158
+ exports.it = benchmark;
159
+ // DISABLED FUNCTIONS
160
+ const xsuite = () => {
161
+ return Promise.resolve({});
162
+ };
163
+ exports.xsuite = xsuite;
164
+ exports.xdescribe = exports.xsuite;
165
+ const xbenchmark = () => {
166
+ return Promise.resolve([]);
167
+ };
168
+ exports.xbenchmark = xbenchmark;
169
+ exports.xtest = exports.xbenchmark;
170
+ exports.xit = exports.xbenchmark;
171
+ const xvariation = () => { };
172
+ exports.xvariation = xvariation;
@@ -1,33 +1,34 @@
1
1
  import { Variation } from './variation';
2
- import { BenchmarkReporter, Result } from './api-types';
3
- import { ActionMethod } from './api-types';
4
- import { TeardownMethod } from './api-types';
5
- import { SetupMethod } from './api-types';
6
- export declare class Benchmark {
2
+ import { TeardownMethod, SetupMethod, Action, BenchmarkReporter, ErrorStrategy } from './api-types';
3
+ import { Result } from './results';
4
+ import { PerformanceObserverOptions } from './performance-observer';
5
+ import { BenchmarkBase } from './shared-api';
6
+ export declare class Benchmark extends BenchmarkBase {
7
7
  name: string;
8
- private setupMethods;
9
- private teardownMethods;
10
- private action?;
11
8
  variations: Variation[];
12
9
  private iterations?;
13
10
  private timeout?;
14
11
  private reporter;
12
+ private watcher?;
13
+ errorStrategy: ErrorStrategy;
15
14
  constructor(name: string, options?: {
16
15
  setup?: SetupMethod;
17
16
  teardown?: TeardownMethod;
18
- action?: ActionMethod;
17
+ action?: Action;
19
18
  iterations?: number;
20
19
  timeout?: number;
21
20
  reporter?: BenchmarkReporter;
22
21
  });
23
22
  withVariation(name: string, builder: (variation: Variation) => Variation): this;
23
+ withVariation(variation: Variation): this;
24
24
  withVariations(variations: Variation[]): this;
25
- withSetup(setup: () => void): this;
26
- withTeardown(teardown: () => void): this;
27
- withAction(action: () => void): this;
25
+ withSetup(setup: SetupMethod): this;
26
+ withTeardown(teardown: TeardownMethod): this;
27
+ withAction(action: Action): this;
28
28
  withIterations(iterations: number): this;
29
29
  withTimeout(timeout: number): this;
30
30
  withReporter(reporter: BenchmarkReporter): this;
31
+ withPerformanceObserver(options?: PerformanceObserverOptions): this;
31
32
  run(): Promise<Result[]>;
32
33
  private validate;
33
34
  }
package/dist/benchmark.js CHANGED
@@ -1,42 +1,52 @@
1
1
  "use strict";
2
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
- return new (P || (P = Promise))(function (resolve, reject) {
5
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
- step((generator = generator.apply(thisArg, _arguments || [])).next());
9
- });
10
- };
11
2
  Object.defineProperty(exports, "__esModule", { value: true });
12
3
  exports.Benchmark = void 0;
13
- const benchmark_console_reporter_1 = require("./benchmark-console-reporter");
4
+ const benchmark_console_reporter_1 = require("./reporters/benchmark-console-reporter");
14
5
  const variation_1 = require("./variation");
15
- class Benchmark {
6
+ const api_types_1 = require("./api-types");
7
+ const child_process_1 = require("child_process");
8
+ const results_1 = require("./results");
9
+ const performance_observer_1 = require("./performance-observer");
10
+ const shared_api_1 = require("./shared-api");
11
+ class Benchmark extends shared_api_1.BenchmarkBase {
12
+ name;
13
+ variations = [];
14
+ iterations;
15
+ timeout;
16
+ reporter;
17
+ watcher;
18
+ errorStrategy = api_types_1.ErrorStrategy.Continue;
16
19
  constructor(name, options) {
20
+ super();
17
21
  this.name = name;
18
- this.setupMethods = [];
19
- this.teardownMethods = [];
20
- this.variations = [];
21
- if (options === null || options === void 0 ? void 0 : options.action) {
22
+ if (options?.action) {
22
23
  this.action = options.action;
23
24
  }
24
- if (options === null || options === void 0 ? void 0 : options.setup) {
25
+ if (options?.setup) {
25
26
  this.setupMethods.push(options.setup);
26
27
  }
27
- if (options === null || options === void 0 ? void 0 : options.teardown) {
28
+ if (options?.teardown) {
28
29
  this.teardownMethods.push(options.teardown);
29
30
  }
30
- if (options === null || options === void 0 ? void 0 : options.iterations) {
31
+ if (options?.iterations) {
31
32
  this.iterations = options.iterations;
32
33
  }
33
- if (options === null || options === void 0 ? void 0 : options.timeout) {
34
+ if (options?.timeout) {
34
35
  this.timeout = options.timeout;
35
36
  }
36
- this.reporter = (options === null || options === void 0 ? void 0 : options.reporter) || new benchmark_console_reporter_1.BenchmarkConsoleReporter();
37
+ this.reporter = options?.reporter || new benchmark_console_reporter_1.BenchmarkConsoleReporter();
37
38
  }
38
- withVariation(name, builder) {
39
- this.variations.push(builder(new variation_1.Variation(name)));
39
+ withVariation(nameOrVariation, builder) {
40
+ if (nameOrVariation instanceof variation_1.Variation) {
41
+ this.variations.push(nameOrVariation);
42
+ }
43
+ else if (builder) {
44
+ this.variations.push(builder(new variation_1.Variation(nameOrVariation)));
45
+ return this;
46
+ }
47
+ else {
48
+ throw new Error('`withVariation` must be called with either a Variation or a name and a builder function.');
49
+ }
40
50
  return this;
41
51
  }
42
52
  withVariations(variations) {
@@ -67,113 +77,139 @@ class Benchmark {
67
77
  this.reporter = reporter;
68
78
  return this;
69
79
  }
70
- run() {
71
- return __awaiter(this, void 0, void 0, function* () {
72
- this.validate();
73
- let results = [];
74
- if (this.variations.length === 0) {
75
- this.variations.push(new variation_1.Variation('default'));
80
+ withPerformanceObserver(options) {
81
+ this.watcher = new performance_observer_1.PerformanceWatcher(options);
82
+ return this;
83
+ }
84
+ async run() {
85
+ this.validate();
86
+ let results = [];
87
+ if (this.variations.length === 0) {
88
+ this.variations.push(new variation_1.Variation('default'));
89
+ }
90
+ const totalIterations = this.iterations
91
+ ? this.variations.length * this.iterations
92
+ : undefined;
93
+ const startTime = performance.now();
94
+ let totalCompletedIterations = 0;
95
+ for (let variationIndex = 0; variationIndex < this.variations.length; variationIndex++) {
96
+ const variation = this.variations[variationIndex];
97
+ const iterationResults = [];
98
+ // SETUP
99
+ const oldEnv = { ...process.env };
100
+ process.env = {
101
+ ...process.env,
102
+ ...variation.environment,
103
+ };
104
+ for (const setup of this.setupMethods) {
105
+ await setup(variation);
76
106
  }
77
- const totalIterations = this.iterations
78
- ? this.variations.length * this.iterations
79
- : undefined;
80
- const startTime = performance.now();
81
- let totalCompletedIterations = 0;
82
- for (let variationIndex = 0; variationIndex < this.variations.length; variationIndex++) {
83
- const variation = this.variations[variationIndex];
84
- const timings = [];
85
- // SETUP
86
- const oldEnv = Object.assign({}, process.env);
87
- process.env = Object.assign(Object.assign({}, process.env), variation.environment);
88
- for (const setup of this.setupMethods) {
89
- yield setup(variation);
90
- }
91
- for (const setup of variation.setupMethods) {
92
- yield setup(variation);
93
- }
94
- // ACT
95
- const benchmarkThis = this;
96
- yield new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
97
- let completedIterations = 0;
98
- let timeout = benchmarkThis.timeout
99
- ? setTimeout(() => {
100
- running = false;
101
- if ((benchmarkThis === null || benchmarkThis === void 0 ? void 0 : benchmarkThis.iterations) &&
102
- completedIterations < benchmarkThis.iterations) {
103
- reject('Timeout');
104
- }
105
- resolve();
106
- }, benchmarkThis.timeout)
107
- : null;
108
- let running = true;
109
- while (running) {
110
- const a = performance.now();
111
- if (variation.action) {
112
- yield variation.action(variation);
113
- }
114
- else if (this.action) {
115
- yield this.action(variation);
116
- }
117
- const b = performance.now();
118
- const duration = b - a;
119
- completedIterations++;
120
- totalCompletedIterations++;
121
- timings.push(duration);
122
- if (this.reporter.progress &&
123
- totalIterations &&
124
- benchmarkThis.iterations) {
125
- this.reporter.progress(variation.name, totalCompletedIterations / totalIterations, {
126
- timeElapsed: performance.now() - startTime,
127
- totalIterations,
128
- completedIterations: totalCompletedIterations,
129
- timeout: benchmarkThis.timeout,
130
- });
107
+ for (const setup of variation.setupMethods) {
108
+ await setup(variation);
109
+ }
110
+ // ACT
111
+ const benchmarkThis = this;
112
+ await new Promise(async (resolve, reject) => {
113
+ let completedIterations = 0;
114
+ let timeout = benchmarkThis.timeout
115
+ ? setTimeout(() => {
116
+ running = false;
117
+ if (benchmarkThis?.iterations &&
118
+ completedIterations < benchmarkThis.iterations) {
119
+ reject('Timeout');
131
120
  }
132
- if ((benchmarkThis === null || benchmarkThis === void 0 ? void 0 : benchmarkThis.iterations) &&
133
- completedIterations >= benchmarkThis.iterations) {
134
- running = false;
135
- if (timeout) {
136
- clearTimeout(timeout);
137
- }
138
- resolve();
121
+ resolve();
122
+ }, benchmarkThis.timeout)
123
+ : null;
124
+ let running = true;
125
+ while (running) {
126
+ for (const setup of this.setupEachMethods.concat(variation.setupEachMethods)) {
127
+ await setup(variation);
128
+ }
129
+ const result = await runAndMeasureAction(benchmarkThis, variation);
130
+ const errorStrategy = variation.errorStrategy ?? benchmarkThis.errorStrategy;
131
+ if (errorStrategy === api_types_1.ErrorStrategy.Abort &&
132
+ result instanceof Error) {
133
+ reject(result);
134
+ return;
135
+ }
136
+ for (const teardown of this.teardownEachMethods.concat(variation.teardownEachMethods)) {
137
+ await teardown(variation);
138
+ }
139
+ completedIterations++;
140
+ totalCompletedIterations++;
141
+ iterationResults.push(result);
142
+ if (this.reporter.progress &&
143
+ totalIterations &&
144
+ benchmarkThis.iterations) {
145
+ this.reporter.progress(variation.name, totalCompletedIterations / totalIterations, {
146
+ timeElapsed: performance.now() - startTime,
147
+ totalIterations,
148
+ completedIterations: totalCompletedIterations,
149
+ timeout: benchmarkThis.timeout,
150
+ });
151
+ }
152
+ if (benchmarkThis?.iterations &&
153
+ completedIterations >= benchmarkThis.iterations) {
154
+ running = false;
155
+ if (timeout) {
156
+ clearTimeout(timeout);
139
157
  }
158
+ resolve();
140
159
  }
141
- }));
142
- if (this.reporter.progress && !totalIterations) {
143
- this.reporter.progress(variation.name, variationIndex / this.variations.length, {
144
- timeElapsed: performance.now() - startTime,
145
- timeout: this.timeout,
146
- completedIterations: totalCompletedIterations,
147
- });
148
160
  }
149
- // TEARDOWN
150
- for (const teardown of variation.teardownMethods) {
151
- yield teardown(variation);
152
- }
153
- for (const teardown of this.teardownMethods) {
154
- yield teardown(variation);
155
- }
156
- process.env = oldEnv;
157
- // REPORT
158
- const min = Math.min(...timings);
159
- const max = Math.max(...timings);
160
- const sum = timings.reduce((a, b) => a + b, 0);
161
- const average = sum / timings.length;
162
- const sortedTimings = [...timings].sort((a, b) => a - b);
163
- const p95 = sortedTimings[Math.floor(sortedTimings.length * 0.95)];
164
- results.push({
165
- label: variation.name,
166
- min,
167
- max,
168
- average,
169
- p95,
161
+ });
162
+ if (this.reporter.progress && !totalIterations) {
163
+ this.reporter.progress(variation.name, variationIndex / this.variations.length, {
164
+ timeElapsed: performance.now() - startTime,
165
+ timeout: this.timeout,
166
+ completedIterations: totalCompletedIterations,
170
167
  });
171
168
  }
172
- this.reporter.report(this, results);
173
- return results;
174
- });
169
+ // TEARDOWN
170
+ for (const teardown of variation.teardownMethods) {
171
+ await teardown(variation);
172
+ }
173
+ for (const teardown of this.teardownMethods) {
174
+ await teardown(variation);
175
+ }
176
+ process.env = oldEnv;
177
+ // REPORT
178
+ const result = (0, results_1.calculateResultsFromDurations)(variation.name, iterationResults);
179
+ // PerformanceObserver needs a chance to flush
180
+ if (this.watcher) {
181
+ const measures = await this.watcher.getMeasures();
182
+ for (const key in measures) {
183
+ result.subresults ??= [];
184
+ result.subresults.push((0, results_1.calculateResultsFromDurations)(key, measures[key]));
185
+ }
186
+ this.watcher.clearMeasures();
187
+ }
188
+ results.push(result);
189
+ }
190
+ this.watcher?.disconnect();
191
+ this.reporter.report(this, results);
192
+ if (this.errorStrategy === api_types_1.ErrorStrategy.DelayedThrow &&
193
+ results.some((r) => r.failed)) {
194
+ throw new api_types_1.AggregateBenchmarkError(results);
195
+ }
196
+ return results;
175
197
  }
176
198
  validate() {
199
+ if (!this.timeout && !this.iterations) {
200
+ this.iterations = 5;
201
+ }
202
+ const missingActions = [];
203
+ for (const variation of this.variations) {
204
+ let action = variation.action || this.action;
205
+ if (!action) {
206
+ missingActions.push(variation.name);
207
+ continue;
208
+ }
209
+ if (typeof action !== 'string' && variation.cliArgs.length > 0) {
210
+ throw new Error(`Cannot use CLI args with a non-command action for ${this.name}:${variation.name}`);
211
+ }
212
+ }
177
213
  if (!this.action) {
178
214
  const missingActions = this.variations.filter((v) => !v.action);
179
215
  if (missingActions.length > 0) {
@@ -188,3 +224,35 @@ class Benchmark {
188
224
  }
189
225
  }
190
226
  exports.Benchmark = Benchmark;
227
+ async function runAndMeasureAction(benchmark, variation) {
228
+ try {
229
+ const a = performance.now();
230
+ await runAction((variation.action ?? benchmark.action), variation);
231
+ const b = performance.now();
232
+ return b - a;
233
+ }
234
+ catch (e) {
235
+ return e instanceof Error ? e : new Error('Unknown error during action.');
236
+ }
237
+ }
238
+ async function runAction(action, variation) {
239
+ if (typeof action === 'string') {
240
+ return new Promise((resolve, reject) => {
241
+ const child = (0, child_process_1.spawn)(action, variation.cliArgs, {
242
+ shell: true,
243
+ windowsHide: true,
244
+ });
245
+ child.on('exit', (code) => {
246
+ if (code === 0) {
247
+ resolve();
248
+ }
249
+ else {
250
+ reject(`Action failed with code ${code}`);
251
+ }
252
+ });
253
+ });
254
+ }
255
+ else {
256
+ return action(variation);
257
+ }
258
+ }
package/dist/index.d.ts CHANGED
@@ -1,5 +1,10 @@
1
+ export * from './reporters/benchmark-console-reporter';
2
+ export * from './reporters/markdown-benchmark-reporter';
3
+ export * from './reporters/suite-console-reporter';
4
+ export * from './reporters/noop-reporter';
1
5
  export * from './api-types';
2
6
  export * from './benchmark';
3
7
  export * from './variation';
4
- export * from './benchmark-console-reporter';
5
8
  export * from './suite';
9
+ export * from './performance-observer';
10
+ export * from './benchmark-runner';
package/dist/index.js CHANGED
@@ -14,8 +14,13 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
14
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./reporters/benchmark-console-reporter"), exports);
18
+ __exportStar(require("./reporters/markdown-benchmark-reporter"), exports);
19
+ __exportStar(require("./reporters/suite-console-reporter"), exports);
20
+ __exportStar(require("./reporters/noop-reporter"), exports);
17
21
  __exportStar(require("./api-types"), exports);
18
22
  __exportStar(require("./benchmark"), exports);
19
23
  __exportStar(require("./variation"), exports);
20
- __exportStar(require("./benchmark-console-reporter"), exports);
21
24
  __exportStar(require("./suite"), exports);
25
+ __exportStar(require("./performance-observer"), exports);
26
+ __exportStar(require("./benchmark-runner"), exports);
@@ -0,0 +1,12 @@
1
+ export declare class PerformanceWatcher {
2
+ private observer?;
3
+ private measures;
4
+ constructor(opts?: PerformanceObserverOptions);
5
+ getMeasures(): Promise<Record<string, number[]>>;
6
+ clearMeasures(): void;
7
+ disconnect(): void;
8
+ }
9
+ export type PerformanceObserverOptions = {
10
+ measureFilter?: (entry: PerformanceEntry) => boolean;
11
+ label?: Record<string, string> | ((label: string) => string);
12
+ };
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PerformanceWatcher = void 0;
4
+ const node_perf_hooks_1 = require("node:perf_hooks");
5
+ class PerformanceWatcher {
6
+ observer;
7
+ measures = {};
8
+ constructor(opts = {}) {
9
+ this.observer = new node_perf_hooks_1.PerformanceObserver((list) => {
10
+ for (const entry of list.getEntries()) {
11
+ if (entry.entryType === 'measure' &&
12
+ (!opts.measureFilter || opts.measureFilter(entry))) {
13
+ const label = normalizeLabel(entry.name, opts?.label);
14
+ this.measures[label] ??= [];
15
+ this.measures[label].push(entry.duration);
16
+ }
17
+ }
18
+ });
19
+ this.observer.observe({ entryTypes: ['measure'] });
20
+ }
21
+ // This method is async to give the observer time to collect the measures...
22
+ // If the action being ran by the benchmark is synchronous, the observer will
23
+ // not fire until the next tick so we need that to happen before the measures are
24
+ // retrieved. Since this method is async, it must be awaited, which actually gives
25
+ // the observer time to collect the measures.
26
+ async getMeasures() {
27
+ return this.measures;
28
+ }
29
+ clearMeasures() {
30
+ for (const key in this.measures) {
31
+ delete this.measures[key];
32
+ }
33
+ }
34
+ disconnect() {
35
+ this.observer?.disconnect();
36
+ delete this.observer;
37
+ }
38
+ }
39
+ exports.PerformanceWatcher = PerformanceWatcher;
40
+ function normalizeLabel(label, transform) {
41
+ if (transform) {
42
+ return typeof transform === 'function'
43
+ ? transform(label)
44
+ : transform[label];
45
+ }
46
+ return label;
47
+ }
@@ -1,7 +1,9 @@
1
- import { Result, BenchmarkReporter, ProgressContext } from './api-types';
2
- import { Benchmark } from './benchmark';
1
+ import { BenchmarkReporter, ProgressContext } from '../api-types';
2
+ import { Benchmark } from '../benchmark';
3
+ import { SingleBar } from 'cli-progress';
4
+ import { Result } from '../results';
3
5
  export declare class BenchmarkConsoleReporter implements BenchmarkReporter {
4
- private bar;
6
+ bar: SingleBar;
5
7
  constructor();
6
8
  progress(name: string, percent: number, context: ProgressContext): void;
7
9
  report(benchmark: Benchmark, results: Result[]): void;