[
  {
    "path": ".devcontainer/devcontainer.json",
    "content": "{\n\t\"name\": \"Bench-node Container\",\n\t\"build\": {\n\t\t\"dockerfile\": \"../Dockerfile\"\n\t}\n}\n"
  },
  {
    "path": ".github/FUNDING.yml",
    "content": "# These are supported funding model platforms\n\ngithub: [RafaelGSS] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]\npatreon: # Replace with a single Patreon username\nopen_collective: # Replace with a single Open Collective username\nko_fi: # Replace with a single Ko-fi username\ntidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel\ncommunity_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry\nliberapay: # Replace with a single Liberapay username\nissuehunt: # Replace with a single IssueHunt username\nlfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry\npolar: # Replace with a single Polar username\nbuy_me_a_coffee: # Replace with a single Buy Me a Coffee username\nthanks_dev: # Replace with a single thanks.dev username\ncustom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']\n"
  },
  {
    "path": ".github/workflows/benchmark-comparison.yml",
    "content": "name: Benchmark Comparison\n\non:\n  push:\n    branches:\n      - main\n  workflow_dispatch:\n\npermissions:\n  issues: write\n  contents: read\n  id-token: write\n\njobs:\n  runner-start:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Configure AWS Credentials\n        uses: aws-actions/configure-aws-credentials@v5\n        with:\n          aws-region: us-west-2\n          role-to-assume: arn:aws:iam::800406105498:role/RafaelGSS-nodejs-bench-operations\n      - name: Checkout\n        uses: actions/checkout@v5\n\n      - name: Start Runner\n        uses: nodesource/aws-eco-runner@v1.0.0-beta.3\n        with:\n          instances_id: '[\"i-065f0f848eb1615ae\"]'\n          action: 'start'\n          aws_default_region: 'us-west-2'\n\n  benchmark:\n    name: Run Benchmark Comparisons\n    runs-on: self-hosted\n    continue-on-error: true\n\n    strategy:\n      matrix:\n        node-version: [20, 22, 24]\n\n    steps:\n      - name: Checkout code\n        uses: actions/checkout@v5\n\n      - name: Setup Node.js\n        uses: actions/setup-node@v6\n        with:\n          node-version: ${{ matrix.node-version }}\n\n      - name: Install dependencies\n        run: npm install --dev\n\n      - name: Run basic comparison\n        run: node --allow-natives-syntax examples/benchmark-comparison/comparison.js\n"
  },
  {
    "path": ".github/workflows/commit-message-validation.yml",
    "content": "name: Commit Message Validation\n\non:\n  pull_request:\n\njobs:\n  validate:\n    name: Validate\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v5\n        with:\n          fetch-depth: 0\n\n      - uses: webiny/action-conventional-commits@v1.3.0\n        with:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n"
  },
  {
    "path": ".github/workflows/release.yml",
    "content": "name: Release\n\non:\n  push:\n    branches:\n      - main\n\npermissions:\n  contents: write\n  pull-requests: write\n  id-token: write\n\njobs:\n  release-please:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout Code\n        uses: actions/checkout@v5\n\n      - name: Release Please\n        uses: google-github-actions/release-please-action@v4\n        id: release\n\n      - name: Use Node 22.x\n        uses: actions/setup-node@v6\n        if: ${{ steps.release.outputs.release_created }}\n        with:\n          node-version: 22\n          registry-url: 'https://registry.npmjs.org'\n\n      - name: Install Deps\n        run: npm install\n        if: ${{ steps.release.outputs.release_created }}\n\n      - name: NPM Publish\n        run: npm publish --provenance --access public\n        if: ${{ steps.release.outputs.release_created }}\n        env:\n          NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}\n"
  },
  {
    "path": ".github/workflows/runner_warmer.yml",
    "content": "name: Cron - Keep Runners Active\n\non:\n  schedule:\n    # Runs this workflow every Sunday at 12:00 UTC. You can adjust the schedule according to your needs.\n    - cron: '0 12 * * 0'\n  workflow_dispatch: # Allows manual triggering of the workflow\n\npermissions:\n  issues: write\n  contents: read\n  id-token: write\n\njobs:\n  start_runner:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Configure AWS Credentials\n        uses: aws-actions/configure-aws-credentials@v5\n        with:\n          aws-region: us-west-2\n          role-to-assume: arn:aws:iam::800406105498:role/RafaelGSS-nodejs-bench-operations\n\n      - name: Start Runner\n        uses: nodesource/aws-eco-runner@v1.0.0-beta.3\n        with:\n          instances_id: '[\"i-065f0f848eb1615ae\"]'\n          action: 'start'\n          aws_default_region: 'us-west-2'\n\n  keep-runner-active:\n    runs-on: [self-hosted]\n    steps:\n      - name: Run task to keep the runner active\n        run: echo \"Keep the runner active\"\n"
  },
  {
    "path": ".github/workflows/test.yml",
    "content": "name: Test CI\n\non:\n  push:\n    branches:\n      - main\n  pull_request:\n\npermissions:\n  issues: write\n  contents: read\n  id-token: write\n\njobs:\n  runner-start:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Configure AWS Credentials\n        uses: aws-actions/configure-aws-credentials@v5\n        with:\n          aws-region: us-west-2\n          role-to-assume: arn:aws:iam::800406105498:role/RafaelGSS-nodejs-bench-operations\n      - name: Checkout\n        uses: actions/checkout@v5\n\n      - name: Start Runner\n        uses: nodesource/aws-eco-runner@v1.0.0-beta.3\n        with:\n          instances_id: '[\"i-065f0f848eb1615ae\"]'\n          action: 'start'\n          aws_default_region: 'us-west-2'\n\n  build:\n    runs-on: self-hosted\n\n    strategy:\n      matrix:\n        node-version: [18, 20, 22, 24, 25]\n\n    env:\n      CI: true\n\n    steps:\n      - name: Checkout code\n        uses: actions/checkout@v5\n\n      - name: Set up Node.js\n        uses: actions/setup-node@v6\n        with:\n          node-version: ${{ matrix.node-version }}\n\n      - name: Install dependencies\n        run: npm install\n\n      - name: Run tests\n        run: npm test\n"
  },
  {
    "path": ".gitignore",
    "content": "tags\n.idea\nnode_modules/\npackage-lock.json\n\ncoverage\n"
  },
  {
    "path": ".npmignore",
    "content": "# We don't want to publish the logo and others\nassets\n\ncoverage\n"
  },
  {
    "path": ".release-please-manifest.json",
    "content": "{\n\t\".\": \"0.15.0\"\n}\n"
  },
  {
    "path": "BENCHMARK_COMPARISSON.md",
    "content": "# Benchmark Library Comparison\n\nThis document provides a comprehensive comparison between `bench-node` and other popular Node.js benchmarking libraries based on real benchmark results across different Node.js versions. This comparison helps identify potential errors in our benchmarking approach and provides insights for specific algorithm comparisons.\n\n## Overview\n\nThe following benchmarking libraries are compared in this document:\n\n- **bench-node** - Our library with V8 optimization control\n- **benchmark.js** - The most popular JavaScript benchmarking library\n- **mitata** - Modern, fast benchmarking library\n- **tinybench** - Lightweight benchmarking tool\n\n## Test Environment\n\nAll benchmarks were executed on:\n- **CPU**: Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz\n- **Platform**: Linux x64\n- **Node.js versions**: 20.19.3, 22.17.0, 24.3.0\n\n\n\n## Detailed Benchmark Results\n\n### Simple Operations - JIT Optimizable\n\nThis test measures a simple operation that V8 can heavily optimize:\n\n#### Node.js 24.3.0\n| Library | ops/sec | Margin | Relative Performance |\n|---------|---------|--------|---------------------|\n| benchmark.js | 141,211,777 | ±5.18% | **1.00x** (baseline) |\n| bench-node | 125,902,288 | N/A | 0.89x |\n| mitata | 37,131,930 | ±5.00% | 0.26x |\n| tinybench | 28,207,575 | ±0.10% | 0.20x |\n\n#### Node.js 22.17.0\n| Library | ops/sec | Margin | Relative Performance |\n|---------|---------|--------|---------------------|\n| benchmark.js | 142,037,530 | ±3.88% | **1.00x** (baseline) |\n| bench-node | 141,504,337 | N/A | 1.00x |\n| tinybench | 17,040,169 | ±1.40% | 0.12x |\n| mitata | 12,382,061 | ±5.00% | 0.09x |\n\n#### Node.js 20.19.3\n| Library | ops/sec | Margin | Relative Performance |\n|---------|---------|--------|---------------------|\n| **benchmark.js** | **843,084,082** | ±0.25% | **1.00x** (baseline) |\n| bench-node | 136,801,267 | N/A | 0.16x |\n| tinybench | 24,275,731 | ±0.11% | 0.03x |\n| mitata | 18,411,459 | ±5.00% | 0.02x |\n\n\n\n### String Processing - Regex Test\n\nThis test performs regex matching on strings:\n\n#### Cross-Version Comparison\n| Node.js Version | bench-node | benchmark.js | mitata | tinybench |\n|-----------------|------------|--------------|--------|-----------|\n| **24.3.0** | 4,352,775 | 4,299,609 (±0.58%) | 3,543,737 (±5.00%) | 3,654,837 (±0.40%) |\n| **22.17.0** | 4,349,482 | 4,382,198 (±0.49%) | 3,933,431 (±5.00%) | 3,526,797 (±0.83%) |\n| **20.19.3** | 4,419,500 | 4,320,038 (±0.40%) | 3,475,360 (±5.00%) | 3,698,242 (±1.85%) |\n\n\n\n### CPU Intensive - Fibonacci Calculations\n\n#### Fibonacci(10) - Light CPU Load\n\n| Node.js Version | benchmark.js | bench-node | mitata | tinybench |\n|-----------------|--------------|------------|--------|-----------|\n| **24.3.0** | 77,510,123 (±2.38%) | 69,120,722 | 42,308,343 (±5.00%) | 26,206,646 (±0.09%) |\n| **22.17.0** | 56,097,219 (±1.68%) | 54,623,064 | 51,268,905 (±5.00%) | 15,451,469 (±0.09%) |\n| **20.19.3** | 73,590,107 (±0.23%) | 57,284,808 | 10,910,003 (±5.00%) | 22,108,774 (±0.11%) |\n\n#### Fibonacci(30) - Medium CPU Load\n\n| Node.js Version | benchmark.js | bench-node | mitata | tinybench |\n|-----------------|--------------|------------|--------|-----------|\n| **24.3.0** | 43,276,919 (±1.26%) | 40,936,562 | 30,824,240 (±5.00%) | 18,752,734 (±0.11%) |\n| **22.17.0** | 22,187,269 (±0.53%) | 21,316,196 | 14,024,655 (±5.00%) | 11,373,272 (±1.98%) |\n| **20.19.3** | 22,078,755 (±0.27%) | 21,783,805 | 21,708,455 (±5.00%) | 14,162,915 (±0.11%) |\n\n#### Fibonacci(40) - Heavy CPU Load\n\n| Node.js Version | benchmark.js | bench-node | mitata | tinybench |\n|-----------------|--------------|------------|--------|-----------|\n| **24.3.0** | 35,115,900 (±1.30%) | 33,956,245 | 26,752,990 (±5.00%) | 18,719,206 (±0.10%) |\n| **22.17.0** | 16,511,904 (±0.91%) | 16,112,632 | 15,599,164 (±5.00%) | 9,496,167 (±4.91%) |\n| **20.19.3** | 16,726,334 (±0.31%) | 16,504,271 | 16,264,129 (±5.00%) | 11,755,080 (±3.32%) |\n\n### Fibonacci Recursive(10) - Algorithm Comparison\n\n| Node.js Version | bench-node | mitata | benchmark.js | tinybench |\n|-----------------|------------|--------|--------------|-----------|\n| **24.3.0** | **999,165** | 992,635 (±5.00%) | 986,251 (±0.31%) | 977,228 (±0.09%) |\n| **22.17.0** | **978,652** | 972,507 (±5.00%) | 969,517 (±0.30%) | 938,449 (±0.07%) |\n| **20.19.3** | **989,935** | 939,466 (±5.00%) | 973,359 (±0.19%) | 965,775 (±0.07%) |\n\n\n---\n\n*Last updated: August 2025 | Based on bench-node v2.x, benchmark.js v2.1.4, mitata v1.x, tinybench v2.x*\n*Benchmark workflow: [benchmark-comparison.yml](https://github.com/RafaelGSS/bench-node/actions/workflows/benchmark-comparison.yml)*\n*Test results from Node.js versions 20.19.3, 22.17.0, and 24.3.0*"
  },
  {
    "path": "CHANGELOG.md",
    "content": "# Changelog\n\n## [0.15.0](https://github.com/RafaelGSS/bench-node/compare/v0.14.0...v0.15.0) (2026-05-08)\n\n\n### Features\n\n* Add fixed-unit time format to JSON min/max output for use in ([#157](https://github.com/RafaelGSS/bench-node/issues/157)) ([38d10d1](https://github.com/RafaelGSS/bench-node/commit/38d10d1ad5bb56d66fc60e764b7eb54eaa1f5d1e))\n* Expose bar width flag. ([#152](https://github.com/RafaelGSS/bench-node/issues/152)) ([83ddc1d](https://github.com/RafaelGSS/bench-node/commit/83ddc1d720feb7746598dbde9a887e96766531a2))\n* Support benchmark warmup with useWorkers=true ([#154](https://github.com/RafaelGSS/bench-node/issues/154)) ([f73ed33](https://github.com/RafaelGSS/bench-node/commit/f73ed3366300993aa7189fef306df417c3da173e))\n\n\n### Bug Fixes\n\n* **plugin:** make V8NeverOptimizePlugin target benchmark functions ([#163](https://github.com/RafaelGSS/bench-node/issues/163)) ([c0975c8](https://github.com/RafaelGSS/bench-node/commit/c0975c80d35edafa8820a375e744339de772fe30))\n\n\n### Miscellaneous Chores\n\n* --test flag propagation appears to be fixed. ([#162](https://github.com/RafaelGSS/bench-node/issues/162)) ([8750bfc](https://github.com/RafaelGSS/bench-node/commit/8750bfc7b7580cd2199f8c62e75fe96462aea6e5))\n* add node v25 to test matrix ([#144](https://github.com/RafaelGSS/bench-node/issues/144)) ([08526ea](https://github.com/RafaelGSS/bench-node/commit/08526ea0e561328a72862d2c0cd42717884cd6a2))\n* add node v25 to test matrix ([#159](https://github.com/RafaelGSS/bench-node/issues/159)) ([946bbdc](https://github.com/RafaelGSS/bench-node/commit/946bbdc068a6f955e631470acea608648e868665))\n\n\n### Code Refactoring\n\n* Clarify the purpose and units of various numbers in lifecycle.js ([#151](https://github.com/RafaelGSS/bench-node/issues/151)) ([28e872e](https://github.com/RafaelGSS/bench-node/commit/28e872eab08a92a8c364f1885a76f1a57f7de141))\n\n## [0.14.0](https://github.com/RafaelGSS/bench-node/compare/v0.13.0...v0.14.0) (2025-12-17)\n\n\n### Features\n\n* add dce detection plugin ([#131](https://github.com/RafaelGSS/bench-node/issues/131)) ([2e2a6be](https://github.com/RafaelGSS/bench-node/commit/2e2a6be87e7dc7562952dd53b9e34017e0822b8e))\n* add t-test mode for statistical significance testing ([#133](https://github.com/RafaelGSS/bench-node/issues/133)) ([53e20aa](https://github.com/RafaelGSS/bench-node/commit/53e20aae67fbd747a05465b6ad824f56483e231a))\n* Narrow the bar display by another couple of characters by using ANSI line drawing. ([#134](https://github.com/RafaelGSS/bench-node/issues/134)) ([814e88e](https://github.com/RafaelGSS/bench-node/commit/814e88e905da60d00615a2b95cfeed23b6034c7d))\n\n\n### Bug Fixes\n\n* Clock inaccuracy in libuv is causing flaky tests. ([cad6bfa](https://github.com/RafaelGSS/bench-node/commit/cad6bfac1b03f270da38c81f3d1703db95a7b0f4))\n* implement support to timers to workers ([35eb144](https://github.com/RafaelGSS/bench-node/commit/35eb144908e76b73667ef6de9131c4d37425974b))\n\n\n### Miscellaneous Chores\n\n* Localize op/sec in text reporter ([#146](https://github.com/RafaelGSS/bench-node/issues/146)) ([18302e3](https://github.com/RafaelGSS/bench-node/commit/18302e369b3f430d35fe856177d0fd0a55592eaf))\n\n## [0.13.0](https://github.com/RafaelGSS/bench-node/compare/v0.12.0...v0.13.0) (2025-11-27)\n\n\n### Features\n\n* export to&lt;format&gt; functions and rework some of the tests. ([960cb87](https://github.com/RafaelGSS/bench-node/commit/960cb87caf3e73c01550cf93127679991c928ef8))\n\n\n### Miscellaneous Chores\n\n* change printTree to formatTree ([d6ff418](https://github.com/RafaelGSS/bench-node/commit/d6ff418dcc50fbe256b89e9bd562521efc8b7095))\n* Convert json reporter to allow json generation to string. ([cfc464c](https://github.com/RafaelGSS/bench-node/commit/cfc464c4709c1ed4415ff86d486a29dc39556708))\n* Convert printResult to generate a line of output. ([4199a57](https://github.com/RafaelGSS/bench-node/commit/4199a578f29eec173b5c22a21affb80c9b415f07))\n* extract toCSV from csv reporter. ([033dd4b](https://github.com/RafaelGSS/bench-node/commit/033dd4bdd8cd874d0fe985a0f6dae34666452e68))\n* Extract toPretty() method ([c2e54a3](https://github.com/RafaelGSS/bench-node/commit/c2e54a36387ff812c461961724e66aa2389e4358))\n* extract toText from text reporter ([db850ca](https://github.com/RafaelGSS/bench-node/commit/db850ca95b124fd8f4a1ce8dd140868be2f2de5d))\n* Move lint after unit tests. ([e0c019d](https://github.com/RafaelGSS/bench-node/commit/e0c019d48c9bdd77dc934e2c5c15231fd1a3b836))\n* Move short branch to top of conditional block. ([e7254b3](https://github.com/RafaelGSS/bench-node/commit/e7254b35286f58a18622c30aebea7d2e127542b7))\n* Remove duplicate time code in pretty and text printers ([a977b97](https://github.com/RafaelGSS/bench-node/commit/a977b972adab5b947b290bb347c774d93ff011fd))\n* Split out text only chart creation function. ([80538fa](https://github.com/RafaelGSS/bench-node/commit/80538fa6447fe542284a6f58d1d8f08c0d362696))\n* Use report for tests. ([b14db92](https://github.com/RafaelGSS/bench-node/commit/b14db929b058f2a25717a0ff83f5f911c015fb66))\n\n## [0.12.0](https://github.com/RafaelGSS/bench-node/compare/v0.11.0...v0.12.0) (2025-11-03)\n\n\n### Features\n\n* add several code refactor/fixes ([#123](https://github.com/RafaelGSS/bench-node/issues/123)) ([cf96c3d](https://github.com/RafaelGSS/bench-node/commit/cf96c3deaace1086df64dfa0424aeb5bbe90ca5f))\n* Deduplicate the data summarization code. ([#120](https://github.com/RafaelGSS/bench-node/issues/120)) ([85bfd3e](https://github.com/RafaelGSS/bench-node/commit/85bfd3e8b4a7da047859c70a1ec5e43e81161c10))\n* make minSamples option available per Suite creation ([#126](https://github.com/RafaelGSS/bench-node/issues/126)) ([152b945](https://github.com/RafaelGSS/bench-node/commit/152b94571e870246db75311e80b962451e2af9ca))\n\n\n### Bug Fixes\n\n* **plugins:** export plugin memory ([52bb536](https://github.com/RafaelGSS/bench-node/commit/52bb536117460cff49518881a5680b96ef06426c))\n\n\n### Miscellaneous Chores\n\n* **index.d.ts:** update exported types for plugins ([abc828f](https://github.com/RafaelGSS/bench-node/commit/abc828fd7f77fe6bb48aa239155e1ad4ca9f9e63))\n* typo ([#125](https://github.com/RafaelGSS/bench-node/issues/125)) ([88aff41](https://github.com/RafaelGSS/bench-node/commit/88aff41f3b190a005fd33160b41a888f9227b3b4))\n\n\n### Code Refactoring\n\n* **plugins:** enable support for memory & fixes on types ([cd4f96e](https://github.com/RafaelGSS/bench-node/commit/cd4f96e5a48a3381242047c18fc082e293ef7723))\n\n\n### Continuous Integration\n\n* bump ci ([#124](https://github.com/RafaelGSS/bench-node/issues/124)) ([2d7ba17](https://github.com/RafaelGSS/bench-node/commit/2d7ba17eac29328a7d09393efe2692960c84c869))\n\n## [0.11.0](https://github.com/RafaelGSS/bench-node/compare/v0.10.0...v0.11.0) (2025-09-22)\n\n\n### Features\n\n* add comparisson with other benchmark libs ([#106](https://github.com/RafaelGSS/bench-node/issues/106)) ([19de73f](https://github.com/RafaelGSS/bench-node/commit/19de73f3a3fb4f11e8ea1d746154c8f3391d0192))\n* Allow for configurable column width for the chart output. ([#104](https://github.com/RafaelGSS/bench-node/issues/104)) ([315d551](https://github.com/RafaelGSS/bench-node/commit/315d551209e25827c844c0f4a301afa6e2bc276b))\n* reduce NPM package size by adding files field to package.json ([#111](https://github.com/RafaelGSS/bench-node/issues/111)) ([a584269](https://github.com/RafaelGSS/bench-node/commit/a584269c8267a30a3fb9b0bb77fccf201f77381c))\n\n\n### Bug Fixes\n\n* optional iterations count for end(), add type tests ([a4dc145](https://github.com/RafaelGSS/bench-node/commit/a4dc145dee4dd53d331cd294771f83333efba7f6))\n\n\n### Documentation\n\n* add comprehensive library comparison document ([#109](https://github.com/RafaelGSS/bench-node/issues/109)) ([caa18a9](https://github.com/RafaelGSS/bench-node/commit/caa18a914f416be9f53357d2a2c8e2ca8a092c66))\n\n\n### Miscellaneous Chores\n\n* **main:** release 0.11.0.beta-1 ([b234ce7](https://github.com/RafaelGSS/bench-node/commit/b234ce76b8f03db0b4668eaddf531291819bd922))\n* use active versions only for bench comparisson ([#107](https://github.com/RafaelGSS/bench-node/issues/107)) ([bf119c7](https://github.com/RafaelGSS/bench-node/commit/bf119c783df026ad40b7e26209fc7b2368b6a292))\n\n## [0.10.0](https://github.com/RafaelGSS/bench-node/compare/v0.9.0...v0.10.0) (2025-07-31)\n\n\n### Features\n\n* Add code coverage to the test code. ([#96](https://github.com/RafaelGSS/bench-node/issues/96)) ([daff350](https://github.com/RafaelGSS/bench-node/commit/daff3509be6731e7415eaa2a8ba0b7464d1b7408))\n* add fastest/slowest value for feature parity with benchmark.js ([9ad4c94](https://github.com/RafaelGSS/bench-node/commit/9ad4c9461f105f7681a77f80a2c33eea521ddba6))\n* Show bar chart with 2% resolution by using partial width box characters. ([#97](https://github.com/RafaelGSS/bench-node/issues/97)) ([4c65557](https://github.com/RafaelGSS/bench-node/commit/4c65557b6472c8437eba98d4f91b52da8628d614))\n\n\n### Bug Fixes\n\n* cpus().length is broken under docker. ([#100](https://github.com/RafaelGSS/bench-node/issues/100)) ([c423cdd](https://github.com/RafaelGSS/bench-node/commit/c423cdd0220bc7317861fa387c99a230a34076ee))\n\n\n### Styles\n\n* Blue works better on light terminals and still looks good on dark. ([#95](https://github.com/RafaelGSS/bench-node/issues/95)) ([5ec0319](https://github.com/RafaelGSS/bench-node/commit/5ec0319787c08ff69ecd54c8003c715e96eafdc7))\n\n\n### Miscellaneous Chores\n\n* do not stop machine on runner_warmer ([6e0e71d](https://github.com/RafaelGSS/bench-node/commit/6e0e71dd30e4eb3bb5a1902a9eb9e6050bbccef7))\n* run lint:ci on test ([#103](https://github.com/RafaelGSS/bench-node/issues/103)) ([a85e15c](https://github.com/RafaelGSS/bench-node/commit/a85e15cfb6b8bbf90a56c28aea49a3ff2e913567))\n\n## [0.9.0](https://github.com/RafaelGSS/bench-node/compare/v0.8.0...v0.9.0) (2025-07-17)\n\n\n### Features\n\n* add reporterOptions support with printHeader opt ([#92](https://github.com/RafaelGSS/bench-node/issues/92)) ([20d34e9](https://github.com/RafaelGSS/bench-node/commit/20d34e90340d179ea20958f760f732f7e1573551))\n\n## [0.8.0](https://github.com/RafaelGSS/bench-node/compare/v0.7.0...v0.8.0) (2025-07-16)\n\n\n### Features\n\n* add baseline and summary to pretty and text reporter ([#83](https://github.com/RafaelGSS/bench-node/issues/83)) ([3aa57cb](https://github.com/RafaelGSS/bench-node/commit/3aa57cb1e487274b141ab79a99e7ed0718cdbf63))\n* add pretty-reporter and shorthand pretty: true ([#82](https://github.com/RafaelGSS/bench-node/issues/82)) ([45efe9c](https://github.com/RafaelGSS/bench-node/commit/45efe9c833cc39e7d8abfd203fbb05a4e84e9daa))\n* export bench-node ts types ([#77](https://github.com/RafaelGSS/bench-node/issues/77)) ([d93f111](https://github.com/RafaelGSS/bench-node/commit/d93f111472515ff19b0fbe57bce588fdc51cfc9d))\n\n\n### Bug Fixes\n\n* fix imports in type test file ([#87](https://github.com/RafaelGSS/bench-node/issues/87)) ([defa158](https://github.com/RafaelGSS/bench-node/commit/defa158ffadceb6b8c660a802445eafe33933db5))\n* use self-hosted runner for test CI ([#81](https://github.com/RafaelGSS/bench-node/issues/81)) ([a65af10](https://github.com/RafaelGSS/bench-node/commit/a65af107859946808ac8458d7b15bb9c887b12da))\n\n\n### Miscellaneous Chores\n\n* drop Node.js v23 and add v24 ([#78](https://github.com/RafaelGSS/bench-node/issues/78)) ([c2dae5f](https://github.com/RafaelGSS/bench-node/commit/c2dae5f038e3ec613aaac753091fbff5ed927b3f))\n\n## [0.7.0](https://github.com/RafaelGSS/bench-node/compare/v0.6.0...v0.7.0) (2025-05-15)\n\n\n### Features\n\n* add opsSecPerRun result ([#74](https://github.com/RafaelGSS/bench-node/issues/74)) ([34ad9e9](https://github.com/RafaelGSS/bench-node/commit/34ad9e95b23d92d80442955569f04efdafc97655))\n\n## [0.6.0](https://github.com/RafaelGSS/bench-node/compare/v0.5.4...v0.6.0) (2025-04-30)\n\n\n### Features\n\n* add time mode benchmark ([#71](https://github.com/RafaelGSS/bench-node/issues/71)) ([9d72044](https://github.com/RafaelGSS/bench-node/commit/9d72044a13a9fd93ae5f1b97fc26ff4a7db7d5f1))\n\n\n### Documentation\n\n* add JSDoc to functions ([#69](https://github.com/RafaelGSS/bench-node/issues/69)) ([e5c6695](https://github.com/RafaelGSS/bench-node/commit/e5c66957ec737f1a8f5ecc4f204e8032daf50aca))\n\n\n### Miscellaneous Chores\n\n* start issuing semver-minor ([3a261ae](https://github.com/RafaelGSS/bench-node/commit/3a261ae94ea029e8aed68153968570eb7c38a2c1))\n\n## [0.5.4](https://github.com/RafaelGSS/bench-node/compare/v0.5.3...v0.5.4) (2025-03-13)\n\n\n### Features\n\n* expose histogram sample data to reporters via sampleData property ([#67](https://github.com/RafaelGSS/bench-node/issues/67)) ([833bec1](https://github.com/RafaelGSS/bench-node/commit/833bec16ae8167785e148ec939fc6211a0662822))\n\n## [0.5.3](https://github.com/RafaelGSS/bench-node/compare/v0.5.2...v0.5.3) (2025-02-24)\n\n\n### Features\n\n* add min samples as param  ([#65](https://github.com/RafaelGSS/bench-node/issues/65)) ([9c6812e](https://github.com/RafaelGSS/bench-node/commit/9c6812e1124f44d95f8d086cba01b5302ec5187e))\n\n\n### Miscellaneous Chores\n\n* **readme:** clean + update ([#61](https://github.com/RafaelGSS/bench-node/issues/61)) ([b5e1e8b](https://github.com/RafaelGSS/bench-node/commit/b5e1e8bb507b9f8b017a91e8c814fb1046908042))\n\n## [0.5.2](https://github.com/RafaelGSS/bench-node/compare/v0.5.1...v0.5.2) (2025-01-27)\n\n\n### Features\n\n* **chartReport:** include node-v ([#53](https://github.com/RafaelGSS/bench-node/issues/53)) ([695842e](https://github.com/RafaelGSS/bench-node/commit/695842eb58c8b6106598a4cef26fe44a552065c6))\n* improve htmlReport UX ([#60](https://github.com/RafaelGSS/bench-node/issues/60)) ([79ce533](https://github.com/RafaelGSS/bench-node/commit/79ce5335e95d531b90c4e26fde983ec1c6de9502))\n\n\n### Documentation\n\n* **readme:** add badges and links ([f85f809](https://github.com/RafaelGSS/bench-node/commit/f85f809aa0f85987d3b01ef9737ed0a855e46741))\n\n\n### Miscellaneous Chores\n\n* **\"branding\":** add logo ([#58](https://github.com/RafaelGSS/bench-node/issues/58)) ([3eedd06](https://github.com/RafaelGSS/bench-node/commit/3eedd064791f5804810545db247a509aea618234))\n\n## [0.5.1](https://github.com/RafaelGSS/bench-node/compare/v0.5.0...v0.5.1) (2025-01-19)\n\n\n### Features\n\n* create csv reporter ([#38](https://github.com/RafaelGSS/bench-node/issues/38)) ([11be8e5](https://github.com/RafaelGSS/bench-node/commit/11be8e52e8cbb5e17f378a8462dd1c4bf7b35351))\n* **reporter:** polish chart output ([#40](https://github.com/RafaelGSS/bench-node/issues/40)) ([91082b6](https://github.com/RafaelGSS/bench-node/commit/91082b6441cfa6ba7b195d7386d493d689e29454))\n* use biome linter ([#34](https://github.com/RafaelGSS/bench-node/issues/34)) ([11c1108](https://github.com/RafaelGSS/bench-node/commit/11c11088e12d2b77547389eb0a5055ad3ff11427))\n\n\n### Bug Fixes\n\n* ignore package.json due to release-please ([7c95b0a](https://github.com/RafaelGSS/bench-node/commit/7c95b0a4fd41aa81576503ac9444a775ed498eda))\n* lint package.json ([549f6ca](https://github.com/RafaelGSS/bench-node/commit/549f6ca574f4a30915e86dff9cd073b3d90def1e))\n\n\n### Miscellaneous Chores\n\n* **main:** release 0.5.1 ([#44](https://github.com/RafaelGSS/bench-node/issues/44)) ([4e51324](https://github.com/RafaelGSS/bench-node/commit/4e51324ea129c3607229aaec3b8d22ef221d0e7d))\n* **main:** release 0.5.2 ([#45](https://github.com/RafaelGSS/bench-node/issues/45)) ([baf2014](https://github.com/RafaelGSS/bench-node/commit/baf20147c1f09f3e50491845e536c590db0d8aa5))\n* **main:** release 0.5.3 ([4757182](https://github.com/RafaelGSS/bench-node/commit/4757182c015cfbd769ebf3969c8269120271e5b3))\n\n\n### Tests\n\n* add managed basic tests ([#36](https://github.com/RafaelGSS/bench-node/issues/36)) ([c491a32](https://github.com/RafaelGSS/bench-node/commit/c491a328329bc79b2ef8124856b162c8df0e8cfb))\n\n\n### Continuous Integration\n\n* **release:** add support to release via release-please ([#42](https://github.com/RafaelGSS/bench-node/issues/42)) ([5263cc6](https://github.com/RafaelGSS/bench-node/commit/5263cc68a5c854a260b68e1f5b930496153ac7fb))\n\n## [0.5.3](https://github.com/RafaelGSS/bench-node/compare/v0.5.2...v0.5.3) (2025-01-16)\n\n\n### Features\n\n* add benchmark repetition ([#27](https://github.com/RafaelGSS/bench-node/issues/27)) ([d65e8aa](https://github.com/RafaelGSS/bench-node/commit/d65e8aab609b882a32331a48bb60fb81ee2db24a))\n* add enough context to plugin methods to figure out bench task name ([c16cf34](https://github.com/RafaelGSS/bench-node/commit/c16cf340699cf198ca10146f30c158697afff908))\n* add html reporter ([a69fdfb](https://github.com/RafaelGSS/bench-node/commit/a69fdfb7415eeb4645e7116be125ccf876d00ebc))\n* add JSONReporter ([7b51c16](https://github.com/RafaelGSS/bench-node/commit/7b51c16db1446b4a2c921c2548e14462197f4779))\n* code and examples ([#1](https://github.com/RafaelGSS/bench-node/issues/1)) ([503b573](https://github.com/RafaelGSS/bench-node/commit/503b573a67cf9245383da949274b30412c366084))\n* create csv reporter ([#38](https://github.com/RafaelGSS/bench-node/issues/38)) ([11be8e5](https://github.com/RafaelGSS/bench-node/commit/11be8e52e8cbb5e17f378a8462dd1c4bf7b35351))\n* **enrichers:** added V8NeverOptimizeEnricher and V8OptimizeOnNextCallEnricher ([16e842e](https://github.com/RafaelGSS/bench-node/commit/16e842eb5dad9703fd009979f68b4f71c98436b2))\n* initial commit ([ee2d46f](https://github.com/RafaelGSS/bench-node/commit/ee2d46fc446a481c7bca731639759e4b7529c405))\n* **memory-enricher:** added support to report memory heap statistics ([441b3ad](https://github.com/RafaelGSS/bench-node/commit/441b3adfee5d92cdd32cb0d4bfd5e7b49d14c2af))\n* **reporter:** polish chart output ([#40](https://github.com/RafaelGSS/bench-node/issues/40)) ([91082b6](https://github.com/RafaelGSS/bench-node/commit/91082b6441cfa6ba7b195d7386d493d689e29454))\n* use biome linter ([#34](https://github.com/RafaelGSS/bench-node/issues/34)) ([11c1108](https://github.com/RafaelGSS/bench-node/commit/11c11088e12d2b77547389eb0a5055ad3ff11427))\n\n\n### Bug Fixes\n\n* handle htmlReport when bench suite is uppercase ([1685144](https://github.com/RafaelGSS/bench-node/commit/16851442e3fe3a97f8e6fc5c98993c77162dc4bc))\n* **lifecycle:** missing imports ([08c6064](https://github.com/RafaelGSS/bench-node/commit/08c60646736ee1236cb371143594e337b1d5f502))\n* typo ([d2a36ae](https://github.com/RafaelGSS/bench-node/commit/d2a36aec5dae24b9a4e95e4f055109a73d3b6bbc))\n\n\n### Miscellaneous Chores\n\n* add exec permission to run.sh ([5d0f4ef](https://github.com/RafaelGSS/bench-node/commit/5d0f4ef72849189472b6700ddf7e56376eea61a2))\n* add node_modules to ignore ([478f24c](https://github.com/RafaelGSS/bench-node/commit/478f24c3fb8cd896e28e1c87e3212269fe9e31eb))\n* **examples:** added example benchmarks ([b4b50b2](https://github.com/RafaelGSS/bench-node/commit/b4b50b23def45698c854bf3bbe434d3f3a92567d))\n* **gitignore:** ignore .idea folder ([e9a13ae](https://github.com/RafaelGSS/bench-node/commit/e9a13ae640fd2ec2d7e714c0c6c9240f4ab1c628))\n* **main:** release 0.5.1 ([#44](https://github.com/RafaelGSS/bench-node/issues/44)) ([4e51324](https://github.com/RafaelGSS/bench-node/commit/4e51324ea129c3607229aaec3b8d22ef221d0e7d))\n* **main:** release 0.5.2 ([#45](https://github.com/RafaelGSS/bench-node/issues/45)) ([baf2014](https://github.com/RafaelGSS/bench-node/commit/baf20147c1f09f3e50491845e536c590db0d8aa5))\n* rename to bench-node ([2f15705](https://github.com/RafaelGSS/bench-node/commit/2f157051e3b1988ac3a8094e0fc7e4daee267a48))\n* rename to nodebenchmark ([9806a31](https://github.com/RafaelGSS/bench-node/commit/9806a31c819073d705bd59c29adc35e808e61d6c))\n* **run:** added script to run all examples ([6733730](https://github.com/RafaelGSS/bench-node/commit/6733730de9fa83a0b6ee7f243b1c3c0576f6f4ad))\n* update rafaelgss email ([a5ec544](https://github.com/RafaelGSS/bench-node/commit/a5ec5445a777c9db12181cae70cd47def0ac56c2))\n\n\n### Code Refactoring\n\n* **lib:** from esm to commonjs ([f25d0e4](https://github.com/RafaelGSS/bench-node/commit/f25d0e40c293a07fe865f09f9bd6693b3152e5b0))\n* **lib:** make the code usable outside/inside node core ([c60c80e](https://github.com/RafaelGSS/bench-node/commit/c60c80e8fd6cad52f5275419252e313e03767893))\n* **validators:** added missing validators on clock ([478fc7e](https://github.com/RafaelGSS/bench-node/commit/478fc7e3456c84797cd718b2c7eeb7e876bad2bc))\n\n\n### Tests\n\n* add a test documenting the plugin signature and lifecycle ([fd379d6](https://github.com/RafaelGSS/bench-node/commit/fd379d6ed51317504255eb78a24e33db21e0b3a7))\n* add basic test suite ([8349ee0](https://github.com/RafaelGSS/bench-node/commit/8349ee0f96236646776fd12843c01d1d9c806b42))\n* add managed basic tests ([#36](https://github.com/RafaelGSS/bench-node/issues/36)) ([c491a32](https://github.com/RafaelGSS/bench-node/commit/c491a328329bc79b2ef8124856b162c8df0e8cfb))\n* add scenario for optimized managed benchmark ([74c4db1](https://github.com/RafaelGSS/bench-node/commit/74c4db1046857f9af57c0c54cc5bf801d0195339))\n* add test case for copy function ([ddf6dc7](https://github.com/RafaelGSS/bench-node/commit/ddf6dc7b4e7a695f6bff5766788b4b0d5beec527))\n* fix the plugin api test ([be8ec69](https://github.com/RafaelGSS/bench-node/commit/be8ec69ff9481ce55b8e49f5732e01a468f6b5de))\n* include TODO test for managed and async ([15ff469](https://github.com/RafaelGSS/bench-node/commit/15ff46924bb969d724d1f92f5611a3f4385f0d47))\n* increase percentage diff on CI ([fa57188](https://github.com/RafaelGSS/bench-node/commit/fa571883f30fd7033a12e05f291fe12bf4816152))\n\n\n### Build System\n\n* move run.sh to examples folder ([08ac769](https://github.com/RafaelGSS/bench-node/commit/08ac7699032a32f3a04a252cc48ee1514fd734bd))\n\n\n### Continuous Integration\n\n* **release:** add support to release via release-please ([#42](https://github.com/RafaelGSS/bench-node/issues/42)) ([5263cc6](https://github.com/RafaelGSS/bench-node/commit/5263cc68a5c854a260b68e1f5b930496153ac7fb))\n\n## [0.5.2](https://github.com/RafaelGSS/bench-node/compare/v0.5.1...v0.5.2) (2025-01-16)\n\n\n### Features\n\n* add benchmark repetition ([#27](https://github.com/RafaelGSS/bench-node/issues/27)) ([d65e8aa](https://github.com/RafaelGSS/bench-node/commit/d65e8aab609b882a32331a48bb60fb81ee2db24a))\n* add enough context to plugin methods to figure out bench task name ([c16cf34](https://github.com/RafaelGSS/bench-node/commit/c16cf340699cf198ca10146f30c158697afff908))\n* add html reporter ([a69fdfb](https://github.com/RafaelGSS/bench-node/commit/a69fdfb7415eeb4645e7116be125ccf876d00ebc))\n* add JSONReporter ([7b51c16](https://github.com/RafaelGSS/bench-node/commit/7b51c16db1446b4a2c921c2548e14462197f4779))\n* code and examples ([#1](https://github.com/RafaelGSS/bench-node/issues/1)) ([503b573](https://github.com/RafaelGSS/bench-node/commit/503b573a67cf9245383da949274b30412c366084))\n* create csv reporter ([#38](https://github.com/RafaelGSS/bench-node/issues/38)) ([11be8e5](https://github.com/RafaelGSS/bench-node/commit/11be8e52e8cbb5e17f378a8462dd1c4bf7b35351))\n* **enrichers:** added V8NeverOptimizeEnricher and V8OptimizeOnNextCallEnricher ([16e842e](https://github.com/RafaelGSS/bench-node/commit/16e842eb5dad9703fd009979f68b4f71c98436b2))\n* initial commit ([ee2d46f](https://github.com/RafaelGSS/bench-node/commit/ee2d46fc446a481c7bca731639759e4b7529c405))\n* **memory-enricher:** added support to report memory heap statistics ([441b3ad](https://github.com/RafaelGSS/bench-node/commit/441b3adfee5d92cdd32cb0d4bfd5e7b49d14c2af))\n* **reporter:** polish chart output ([#40](https://github.com/RafaelGSS/bench-node/issues/40)) ([91082b6](https://github.com/RafaelGSS/bench-node/commit/91082b6441cfa6ba7b195d7386d493d689e29454))\n* use biome linter ([#34](https://github.com/RafaelGSS/bench-node/issues/34)) ([11c1108](https://github.com/RafaelGSS/bench-node/commit/11c11088e12d2b77547389eb0a5055ad3ff11427))\n\n\n### Bug Fixes\n\n* handle htmlReport when bench suite is uppercase ([1685144](https://github.com/RafaelGSS/bench-node/commit/16851442e3fe3a97f8e6fc5c98993c77162dc4bc))\n* **lifecycle:** missing imports ([08c6064](https://github.com/RafaelGSS/bench-node/commit/08c60646736ee1236cb371143594e337b1d5f502))\n* typo ([d2a36ae](https://github.com/RafaelGSS/bench-node/commit/d2a36aec5dae24b9a4e95e4f055109a73d3b6bbc))\n\n\n### Miscellaneous Chores\n\n* add exec permission to run.sh ([5d0f4ef](https://github.com/RafaelGSS/bench-node/commit/5d0f4ef72849189472b6700ddf7e56376eea61a2))\n* add node_modules to ignore ([478f24c](https://github.com/RafaelGSS/bench-node/commit/478f24c3fb8cd896e28e1c87e3212269fe9e31eb))\n* **examples:** added example benchmarks ([b4b50b2](https://github.com/RafaelGSS/bench-node/commit/b4b50b23def45698c854bf3bbe434d3f3a92567d))\n* **gitignore:** ignore .idea folder ([e9a13ae](https://github.com/RafaelGSS/bench-node/commit/e9a13ae640fd2ec2d7e714c0c6c9240f4ab1c628))\n* **main:** release 0.5.1 ([#44](https://github.com/RafaelGSS/bench-node/issues/44)) ([4e51324](https://github.com/RafaelGSS/bench-node/commit/4e51324ea129c3607229aaec3b8d22ef221d0e7d))\n* rename to bench-node ([2f15705](https://github.com/RafaelGSS/bench-node/commit/2f157051e3b1988ac3a8094e0fc7e4daee267a48))\n* rename to nodebenchmark ([9806a31](https://github.com/RafaelGSS/bench-node/commit/9806a31c819073d705bd59c29adc35e808e61d6c))\n* **run:** added script to run all examples ([6733730](https://github.com/RafaelGSS/bench-node/commit/6733730de9fa83a0b6ee7f243b1c3c0576f6f4ad))\n* update rafaelgss email ([a5ec544](https://github.com/RafaelGSS/bench-node/commit/a5ec5445a777c9db12181cae70cd47def0ac56c2))\n\n\n### Code Refactoring\n\n* **lib:** from esm to commonjs ([f25d0e4](https://github.com/RafaelGSS/bench-node/commit/f25d0e40c293a07fe865f09f9bd6693b3152e5b0))\n* **lib:** make the code usable outside/inside node core ([c60c80e](https://github.com/RafaelGSS/bench-node/commit/c60c80e8fd6cad52f5275419252e313e03767893))\n* **validators:** added missing validators on clock ([478fc7e](https://github.com/RafaelGSS/bench-node/commit/478fc7e3456c84797cd718b2c7eeb7e876bad2bc))\n\n\n### Tests\n\n* add a test documenting the plugin signature and lifecycle ([fd379d6](https://github.com/RafaelGSS/bench-node/commit/fd379d6ed51317504255eb78a24e33db21e0b3a7))\n* add basic test suite ([8349ee0](https://github.com/RafaelGSS/bench-node/commit/8349ee0f96236646776fd12843c01d1d9c806b42))\n* add managed basic tests ([#36](https://github.com/RafaelGSS/bench-node/issues/36)) ([c491a32](https://github.com/RafaelGSS/bench-node/commit/c491a328329bc79b2ef8124856b162c8df0e8cfb))\n* add scenario for optimized managed benchmark ([74c4db1](https://github.com/RafaelGSS/bench-node/commit/74c4db1046857f9af57c0c54cc5bf801d0195339))\n* add test case for copy function ([ddf6dc7](https://github.com/RafaelGSS/bench-node/commit/ddf6dc7b4e7a695f6bff5766788b4b0d5beec527))\n* fix the plugin api test ([be8ec69](https://github.com/RafaelGSS/bench-node/commit/be8ec69ff9481ce55b8e49f5732e01a468f6b5de))\n* include TODO test for managed and async ([15ff469](https://github.com/RafaelGSS/bench-node/commit/15ff46924bb969d724d1f92f5611a3f4385f0d47))\n* increase percentage diff on CI ([fa57188](https://github.com/RafaelGSS/bench-node/commit/fa571883f30fd7033a12e05f291fe12bf4816152))\n\n\n### Build System\n\n* move run.sh to examples folder ([08ac769](https://github.com/RafaelGSS/bench-node/commit/08ac7699032a32f3a04a252cc48ee1514fd734bd))\n\n\n### Continuous Integration\n\n* **release:** add support to release via release-please ([#42](https://github.com/RafaelGSS/bench-node/issues/42)) ([5263cc6](https://github.com/RafaelGSS/bench-node/commit/5263cc68a5c854a260b68e1f5b930496153ac7fb))\n\n## [0.5.1](https://github.com/RafaelGSS/bench-node/compare/v0.5.0...v0.5.1) (2025-01-14)\n\n\n### Features\n\n* create csv reporter ([#38](https://github.com/RafaelGSS/bench-node/issues/38)) ([11be8e5](https://github.com/RafaelGSS/bench-node/commit/11be8e52e8cbb5e17f378a8462dd1c4bf7b35351))\n* **reporter:** polish chart output ([#40](https://github.com/RafaelGSS/bench-node/issues/40)) ([91082b6](https://github.com/RafaelGSS/bench-node/commit/91082b6441cfa6ba7b195d7386d493d689e29454))\n* use biome linter ([#34](https://github.com/RafaelGSS/bench-node/issues/34)) ([11c1108](https://github.com/RafaelGSS/bench-node/commit/11c11088e12d2b77547389eb0a5055ad3ff11427))\n\n\n### Tests\n\n* add managed basic tests ([#36](https://github.com/RafaelGSS/bench-node/issues/36)) ([c491a32](https://github.com/RafaelGSS/bench-node/commit/c491a328329bc79b2ef8124856b162c8df0e8cfb))\n\n\n### Continuous Integration\n\n* **release:** add support to release via release-please ([#42](https://github.com/RafaelGSS/bench-node/issues/42)) ([5263cc6](https://github.com/RafaelGSS/bench-node/commit/5263cc68a5c854a260b68e1f5b930496153ac7fb))\n"
  },
  {
    "path": "Dockerfile",
    "content": "FROM node:24-trixie-slim\n\nRUN apt update && apt install git -y\n"
  },
  {
    "path": "README.md",
    "content": "<h1 align=\"center\">\n  <img\n    src=\"https://raw.githubusercontent.com/RafaelGSS/bench-node/refs/heads/main/assets/logo.svg\"\n    alt=\"Bench Node logo\"\n  />\n  Bench Node\n</h1>\n\n<p align=\"center\">\n  <a href=\"#install\">Install</a>&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;\n  <a href=\"#usage\">Usage</a>&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;\n  <a href=\"#class-suite\">Suite</a>&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;\n  <a href=\"#plugins\">Plugins</a>&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;\n  <a href=\"#using-reporter\">Using Reporter</a>&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;\n  <a href=\"#setup-and-teardown\">Setup and Teardown</a>&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;\n  <a href=\"#benchmark-modes\">Benchmark Modes</a>&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;\n  <a href=\"#writing-javascript-mistakes\">Writing JavaScript Mistakes</a>\n</p>\n\n[![npm package][npm-img]][npm-url]\n[![Build Status][build-img]][build-url]\n[![Downloads][downloads-img]][downloads-url]\n[![Issues][issues-img]][issues-url]\n\nThe `bench-node` module allows you to measure operations per second of Node.js code blocks.\n\n## Install\n\n```bash\n$ npm install bench-node\n```\n\n## Usage\n\n```cjs\nconst { Suite } = require('bench-node');\n\nconst suite = new Suite();\n\nsuite.add('Using delete property', () => {\n  const data = { x: 1, y: 2, z: 3 };\n  delete data.y;\n\n  data.x;\n  data.y;\n  data.z;\n});\n\nsuite.run()\n```\n\n```bash\n$ node --allow-natives-syntax my-benchmark.js\nUsing delete property x 3,326,913 ops/sec (11 runs sampled) v8-never-optimize=true min..max=(0ns ... 0ns) p75=0ns p99=0ns\n```\n\nThis module uses V8 deoptimization to help ensure that the code block is not optimized away, producing accurate benchmarks -- but not realistic.\nSee the [Writing JavaScript Microbenchmark Mistakes](#writing-javascript-mistakes) section for more details.\n\nThe [`bench-node-cli`](https://github.com/RafaelGSS/bench-node-cli) tool allows you to execute a `bench-node` benchmark\nfrom any location, eliminating the need to install the `bench-node` package locally.\nSimply use the following command to run your benchmark:\n\n```bash\nnpx bench-node-cli my-benchmark.js\n```\n\nSee the [examples folder](./examples/) for more common usage examples.\n\n## Table of Contents\n\n- [Install](#install)\n- [Usage](#usage)\n- [Table of Contents](#table-of-contents)\n- [Sponsors](#sponsors)\n- [Class: `Suite`](#class-suite)\n  - [`new Suite([options])`](#new-suiteoptions)\n  - [`suite.add(name[, options], fn)`](#suiteaddname-options-fn)\n  - [`suite.run()`](#suiterun)\n- [Dead Code Elimination Detection](#dead-code-elimination-detection)\n  - [How It Works](#how-it-works)\n  - [Configuration](#configuration)\n  - [When DCE Warnings Appear](#when-dce-warnings-appear)\n- [Plugins](#plugins)\n  - [Plugin Methods](#plugin-methods)\n  - [Example Plugin](#example-plugin)\n- [Using Reporter](#using-reporter)\n  - [`textReport` (Default)](#textreport-default)\n  - [`chartReport`](#chartreport)\n  - [`htmlReport`](#htmlreport)\n  - [`jsonReport`](#jsonreport)\n  - [CSV Reporter](#csv-reporter)\n  - [Pretty Reporter](#pretty-reporter)\n  - [Custom Reporter](#custom-reporter)\n- [Setup and Teardown](#setup-and-teardown)\n  - [Managed Benchmarks](#managed-benchmarks)\n  - [Worker Threads](#worker-threads)\n- [Benchmark Modes](#benchmark-modes)\n  - [Operations Mode](#operations-mode)\n  - [Time Mode](#time-mode)\n- [Baseline Comparisons](#baseline-comparisons)\n- [Statistical Significance Testing](#statistical-significance-testing-t-test)\n  - [Direct API Usage](#direct-api-usage)\n  - [Fixing Inconclusive Tests](#fixing-inconclusive-tests)\n- [Writing JavaScript Mistakes](#writing-javascript-mistakes)\n\n## Sponsors\n\nTest machines are generously sponsored by [NodeSource](https://nodesource.com/).  \n<img src=\"https://github.com/user-attachments/assets/c30ddaf6-145b-465e-a81f-c9942cb93175\" alt=\"NodeSource logo\" width=\"200\"/>\n\n## Class: `Suite`\n\n> Stability: 1.1 Active Development\n\nA `Suite` manages and executes benchmark functions. It provides two methods: `add()` and `run()`.\n\n### `new Suite([options])`\n\n* `options` {Object} Configuration options for the suite.\n  * `reporter` {Function} Callback function for reporting results. Receives two arguments:\n    * `results` {Object[]} Array of benchmark results:\n      * `name` {string} Benchmark name.\n      * `opsSec` {string} Operations per second.\n      * `iterations` {Number} Number of iterations.\n      * `histogram` {Histogram} Histogram instance.\n  * `ttest` {boolean} Enable Welch's t-test for statistical significance testing. Automatically sets `repeatSuite=30`. **Default:** `false`.\n  * `reporterOptions` {Object} Reporter-specific options.\n    * `printHeader` {boolean} Whether to print system information header. **Default:** `true`.\n    * `labelWidth` {number} Width for benchmark labels in output. **Default:** `45`.\n    * `alpha` {number} Significance level for t-test (e.g., 0.05 for 95% confidence). **Default:** `0.05`.\n  * `benchmarkMode` {string} Benchmark mode to use. Can be 'ops' or 'time'. **Default:** `'ops'`.\n    * `'ops'` - Measures operations per second (traditional benchmarking).\n    * `'time'` - Measures actual execution time for a single run.\n  * `useWorkers` {boolean} Whether to run benchmarks in worker threads. **Default:** `false`.\n  * `plugins` {Array} Array of plugin instances to use.\n  * `repeatSuite` {number} Number of times to repeat each benchmark. Automatically set to `30` when `ttest: true`. **Default:** `1`.\n  * `plugins` {Array} Array of plugin instances to use. **Default:** `[V8NeverOptimizePlugin]`.\n  * `minSamples` {number} Minimum number of samples per round for all benchmarks in the suite. Can be overridden per benchmark. **Default:** `10` samples.\n  * `detectDeadCodeElimination` {boolean} Enable dead code elimination detection. When enabled, default plugins are disabled to allow V8 optimizations. **Default:** `false`.\n  * `dceThreshold` {number} Threshold multiplier for DCE detection. Benchmarks faster than baseline × threshold will trigger warnings. **Default:** `10`.\n\nIf no `reporter` is provided, results are printed to the console.\n\n```js\nconst { Suite } = require('bench-node');\nconst suite = new Suite();\n```\n\nIf you don't want results to be printed to the console, `false` and `null` can be used\n\n```js\nconst { Suite } = require('bench-node');\nconst suite = new Suite({ reporter: false });\n```\n\n### `suite.add(name[, options], fn)`\n\n* `name` {string} The name of the benchmark, displayed when reporting results.\n* `options` {Object} Configuration options for the benchmark. Supported properties:\n  * `minTime` {number} The minimum duration of each sampling interval. **Default:** `0.05` seconds.\n  * `maxTime` {number} Maximum duration for the benchmark to run. **Default:** `0.5` seconds.\n  * `repeatSuite` {number} Number of times to repeat benchmark to run. **Default:** `1` times.\n  * `minSamples` {number} Number minimum of samples the each round. **Default:** `10` samples.\n  * `baseline` {boolean} Mark this benchmark as the baseline for comparison. Only one benchmark per suite can be baseline. **Default:** `false`.\n* `fn` {Function|AsyncFunction} The benchmark function. Can be synchronous or asynchronous. \n* Returns: {Suite}\n\nAdds a benchmark function to the suite.\n\n```bash\n$ node --allow-natives-syntax my-benchmark.js\nUsing delete property x 5,853,505 ops/sec (10 runs sampled) min..max=(169ns ... 171ns)\n```\n\n### `suite.run()`\n\n* Returns: `{Promise<Array<Object>>}` An array of benchmark results, each containing:\n  * `opsSec` {number} Operations per second (Only in 'ops' mode).\n  * `opsSecPerRun` {Array} Array of operations per second (useful when repeatSuite > 1).\n  * `totalTime` {number} Total execution time in seconds (Only in 'time' mode).\n  * `iterations` {number} Number of executions of `fn`.\n  * `histogram` {Histogram} Histogram of benchmark iterations.\n  * `name` {string} Benchmark name.\n  * `plugins` {Object} Object with plugin results if any plugins are active.\n\nRuns all added benchmarks and returns the results.\n\n## Dead Code Elimination Detection\n\n**bench-node** includes optional detection of dead code elimination (DCE) to help identify benchmarks that may be producing inaccurate results. When the JIT compiler optimizes away your benchmark code, it can run nearly as fast as an empty function, leading to misleading performance measurements.\n\n**Important:** DCE detection is **opt-in**. When enabled, the `V8NeverOptimizePlugin` is automatically disabled to allow V8 optimizations to occur naturally. This helps catch benchmarks that would be optimized away in real-world scenarios.\n\n### How It Works\n\nWhen enabled, bench-node measures a baseline (empty function) performance before running your benchmarks. After each benchmark completes, it compares the timing against this baseline. If a benchmark runs suspiciously fast (less than 10× slower than the baseline by default), a warning is emitted.\n\n### Example Warning Output\n\n```\n⚠️  Dead Code Elimination Warnings:\nThe following benchmarks may have been optimized away by the JIT compiler:\n\n  • array creation\n    Benchmark: 3.98ns/iter\n    Baseline:  0.77ns/iter  \n    Ratio:     5.18x of baseline\n    Suggestion: Ensure the result is used or assign to a variable\n\nℹ️  These benchmarks are running nearly as fast as an empty function,\n   which suggests the JIT may have eliminated the actual work.\n```\n\n### Configuration\n\n```js\nconst { Suite, V8NeverOptimizePlugin } = require('bench-node');\n\n// Enable DCE detection (disables V8NeverOptimizePlugin automatically)\nconst suite = new Suite({\n  detectDeadCodeElimination: true\n});\n\n// Enable DCE detection with custom threshold (default is 10x)\nconst strictSuite = new Suite({\n  detectDeadCodeElimination: true,\n  dceThreshold: 20 // Only warn if < 20x slower than baseline\n});\n\n// Use both DCE detection AND prevent optimization\n// (helpful for educational purposes to see warnings even when using %NeverOptimizeFunction)\nconst educationalSuite = new Suite({\n  plugins: [new V8NeverOptimizePlugin()],\n  detectDeadCodeElimination: true\n});\n```\n\n### When DCE Warnings Appear\n\nCommon scenarios that trigger warnings:\n\n```js\n// ❌ Result not used - will be optimized away\nsuite.add('computation', () => {\n  const result = Math.sqrt(144);\n  // result is never used\n});\n\n// ✅ Result is used - less likely to be optimized\nsuite.add('computation', () => {\n  const result = Math.sqrt(144);\n  if (result !== 12) throw new Error('Unexpected');\n});\n```\n\n**Note:** DCE detection only works in `'ops'` benchmark mode and when not using worker threads. It is automatically disabled for `'time'` mode and worker-based benchmarks.\n\nSee [examples/dce-detection/](./examples/dce-detection/) for more examples.\n\n## Plugins\n\nPlugins extend the functionality of the benchmark module. \n\nSee [Plugins](./doc/Plugins.md) for details.\n\n### Plugin Methods\n\n- **`isSupported()`**: Checks if the plugin can run in the current environment.\n- **`beforeClockTemplate(varNames)`**: Injects code before the benchmark starts. Returns an array with:\n  * `Code` {string} JavaScript code to execute.\n  * `Wrapper` {string} (optional) Function to wrap the benchmark function.\n- **`afterClockTemplate(varNames)`**: Injects code after the benchmark finishes. Returns an array with:\n  * `Code` {string} JavaScript code to execute.\n- **`onCompleteBenchmark(result, bench)`**: Called when the benchmark completes, allowing plugins to process results.\n- **`toString()`**: Returns a string identifier for the plugin.\n- **`getReport(benchmarkName)`**: Returns a string to be displayed in the benchmark result line.\n- **`getResult(benchmarkName)`**: Returns the data that can be used by the reporter.\n- **`reset()`**: Resets the plugin state, to avoid carrying over data between benchmarks.\n\n### Example Plugin\n\n```js\nclass V8OptimizeOnNextCallPlugin {\n  isSupported() {\n    try {\n      new Function(`%OptimizeFunctionOnNextCall(() => {})`)();\n      return true;\n    } catch (e) {\n      return false;\n    }\n  }\n\n  beforeClockTemplate({ awaitOrEmpty, bench }) {\n    let code = '';\n    code += `%OptimizeFunctionOnNextCall(${bench}.fn);\\n`;\n    code += `${awaitOrEmpty}${bench}.fn();\\n`;\n    code += `${awaitOrEmpty}${bench}.fn();\\n`;\n    return [code];\n  }\n\n  toString() {\n    return 'V8OptimizeOnNextCallPlugin';\n  }\n}\n```\n\n## Using Reporter\n\nThis module exports two reporters that control how benchmark results are displayed:\na detailed `textReport` for statistical analysis, and a visual `chartReport` that\ndisplays a bar graph in the terminal.\n\n### `textReport` (Default)\n\nThe `textReport` is the default reporter, which provides simple statistical information\nabout each benchmark result. It includes the number of operations per second, the number\nof runs sampled, min...max, and enabled plugins.\n\n**Example Output**:\n\n```\nsingle with matcher                           x 710,248 ops/sec (11 runs sampled) v8-never-optimize=true min..max=(1.40us...1.42us)\nmultiple replaces                             x 604,713 ops/sec (11 runs sampled) v8-never-optimize=true min..max=(1.64us...1.70us)\n```\n\nHere’s how you can explicitly pass it as a reporter:\n\n```cjs\nconst { Suite, textReport } = require('bench-node');\n\nconst suite = new Suite({\n  reporter: textReport, // Optional, since this is the default\n});\n```\n\n### `chartReport`\n\nThe `chartReport` reporter provides a graphical representation of benchmark\nresults in the form of a bar chart, making it easier to visualize the relative\nperformance of each benchmark. It scales the bars based on the highest operations\nper second (ops/sec) value, and displays the results incrementally as they are collected.\n\nExample output:\n\n```\nNode.js version: v23.6.1\nPlatform: darwin arm64\nCPU Cores: 8 vCPUs | 16.0GB Mem\n\nsingle with matcher                           | ██████████████████████████████ | 709,321 ops/sec | 10 samples\nmultiple replaces                             | ██████████████████████████---- | 606,401 ops/sec | 11 samples\n```\n\nUsage:\n\n```cjs\nconst { Suite, chartReport } = require('bench-node');\n\nconst suite = new Suite({\n  reporter: chartReport,\n  // Optionally control header display\n  reporterOptions: {\n    printHeader: true // Set to false to hide system info header\n  }\n});\n```\n\nThe `reporterOptions.printHeader` setting controls whether the system information (Node.js version, platform, and CPU cores) appears at the top of the output. This is useful when running multiple suites in sequence and you want to avoid duplicate headers.\n\n### `htmlReport`\n\nThe `htmlReport` generates an interactive HTML visualization of benchmark results.\nIt transforms benchmark data into a visual format, such as speed circle animations,\nmaking it easier to interpret and share performance insights.\n\nExample output:\n\nhttps://github.com/user-attachments/assets/b2b98175-6648-4af4-8319-63f3ebbc729e\n\nUsage:\n\n```cjs\nconst { Suite, htmlReport } = require('bench-node');\n\nconst suite = new Suite({\n  reporter: htmlReport,\n});\n```\n\n### `jsonReport`\n\nThe `jsonReport` plugin provides benchmark results in **JSON format**.  \nIt includes key performance metrics—such as `opsSec`, `runsSampled`, `min`\nand `max` times, and any reporter data from your **plugins**—so you can easily\nstore, parse, or share the information.\n\nExample output:\n\n```json\n[\n  {\n    \"name\": \"single with matcher\",\n    \"opsSec\": 180000,\n    \"runsSampled\": 50,\n    \"min\": \"13.20μs\",\n    \"max\": \"82.57μs\",\n    \"plugins\": []\n  },\n  {\n    \"name\": \"Multiple replaces\",\n    \"opsSec\": 170000,\n    \"runsSampled\": 50,\n    \"min\": \"15.31μs\",\n    \"max\": \"77.49μs\",\n    \"plugins\": []\n  }\n]\n```\n\n**Usage:**\n\n```cjs\nconst { Suite, jsonReport } = require('bench-node');\n\nconst suite = new Suite({\n  reporter: jsonReport,\n});\n```\n\n### CSV Reporter\n\nThe `csvReport` plugin generates benchmark results in CSV format.\nIt includes columns for key performance metrics like `ops/sec`, `samples`, `min` and `max` times,\nas well as any reporter data provided by your plugins.\n\nExample output:\n\n```csv\nname,ops/sec,samples,plugins,min,max\nUsing delete property,\"7,732,517\",10,v8-never-optimize=true,127.91ns,129.95ns\nUsing delete property (proto: null),\"24,636,631\",10,v8-never-optimize=true,39.57ns,40.91ns\nUsing delete property (cached proto: null),\"7,497,893\",11,v8-never-optimize=true,132.25ns,134.89ns\nUsing undefined assignment,\"132,093,600\",11,v8-never-optimize=true,7.53ns,7.64ns\nUsing undefined assignment (proto: null),\"28,231,374\",9,v8-never-optimize=true,35.27ns,35.42ns\nUsing undefined property (cached proto: null),\"60,843,193\",10,v8-never-optimize=true,16.24ns,16.65ns\n[Managed] Using undefined property (cached proto: null),\"35,394,060\",10,v8-never-optimize=true,27.90ns,28.54ns\n```\n\n**Usage:**\n\n```cjs\nconst { Suite, csvReport } = require('bench-node');\n\nconst suite = new Suite({\n  reporter: csvReport,\n});\n```\n\n### Pretty Reporter\n\nThe `prettyReport` provides a beautiful, hierarchical view of your benchmark results.\n\n**Usage:**\n\n```javascript\nconst { Suite } = require('bench-node');\n\n// You can either pass the reporter function directly...\nconst suiteWithReporter = new Suite({\n  reporter: prettyReport,\n});\n\n// ...or use the `pretty` option for convenience.\nconst suiteWithPrettyOption = new Suite({\n  pretty: true,\n});\n\nsuite\n  .add('my-group/my-benchmark', () => {\n    //...\n  })\n  .add('my-group/my-benchmark-2', () => {\n    //...\n  })\n  .add('second-group/baseline', () => {\n    //...\n  })\n  .run();\n```\n\n**Sample Output:**\n\n```\nSystem Information:\n  Node.js: v22.15.0\n  OS: darwin 24.5.0\n  CPU: Apple M2\n\nBenchmark results (3 total):\nPlugins enabled: V8NeverOptimizePlugin\n├─ my-group\n│ ├─ my-benchmark                                      1,461,380 ops/sec (10 runs sampled) min..max=(682.30ns...685.79ns)\n│ └─ my-benchmark-2                                    2,270,973 ops/sec (10 runs sampled) min..max=(437.22ns...444.12ns)\n└─ second-group\n  └─ baseline                                          637,787 ops/sec (11 runs sampled) min..max=(1.54us...1.59us)\n```\n\n### Custom Reporter\n\nCustomize data reporting by providing a `reporter` function when creating the `Suite`:\n\n```js\nconst { Suite } = require('bench-node');\n\nfunction reporter(results) {\n  for (const result of results) {\n    console.log(`Benchmark: ${result.name}`);\n    console.log(`Operations per second: ${result.opsSec}`);\n    console.log(`Iterations: ${result.iterations}`);\n    console.log(`Histogram: ${result.histogram}`);\n  }\n}\n\nconst suite = new Suite({ reporter });\n\nsuite.add('Using delete to remove property from object', () => {\n  const data = { x: 1, y: 2, z: 3 };\n  delete data.y;\n\n  data.x;\n  data.y;\n  data.z;\n});\n\nsuite.run();\n```\n\n```bash\n$ node --allow-natives-syntax my-benchmark.js\nBenchmark: Using delete to remove property from object - 6,032,212 ops/sec\n```\n\n## Setup and Teardown\n\nControl the benchmark function's setup and teardown using the timer argument:\n\n```js\nconst { Suite } = require('bench-node');\nconst { readFileSync, writeFileSync, rmSync } = require('node:fs');\n\nconst suite = new Suite();\n\nsuite.add('readFileSync', (timer) => {\n  const randomFile = Date.now();\n  const filePath = `./${randomFile}.txt`;\n  writeFileSync(filePath, Math.random().toString());\n\n  timer.start();\n  readFileSync(filePath, 'utf8');\n  timer.end();\n\n  rmSync(filePath);\n}).run();\n```\n\nFor advanced setups, use the timer argument to start and end timing explicitly:\n\n```js\nconst { Suite } = require('bench-node');\nconst { readFileSync, writeFileSync, rmSync } = require('node:fs');\n\nconst suite = new Suite();\n\nsuite.add('readFileSync', (timer) => {\n  const randomFile = Date.now();\n  const filePath = `./${randomFile}.txt`;\n  writeFileSync(filePath, Math.random().toString());\n\n  timer.start();\n  for (let i = 0; i < timer.count; i++) {\n    readFileSync(filePath, 'utf8');\n  }\n  timer.end(timer.count);\n\n  rmSync(filePath);\n});\n\nsuite.run();\n```\n\n> [!WARNING]\n> When using the `timer`, the setup will also be deoptimized.\n> As a result, if you compare this approach with one that uses functions outside\n> the benchmark function, the results may not match.\n> See: [Deleting Properties Example](./examples/deleting-properties/node.js).\n\nEnsure you call `.start()` and `.end()` methods when using the timer argument, or an `ERR_BENCHMARK_MISSING_OPERATION` error will be thrown.\n\n### Managed Benchmarks\n\nIn regular benchmarks (when `timer` is not used), you run the benchmarked function in a loop,\nand the timing is managed implicitly.\nThis means each iteration of the benchmarked function is measured directly.\nThe downside is that optimizations like inlining or caching might affect the timing, especially for fast operations.\n\nExample:\n\n```cjs\nsuite.add('Using includes', () => {\n  const text = 'text/html,...';\n  const r = text.includes('application/json');\n});\n```\n\nHere, `DoNotOptimize` is called inside the generated loop for regular benchmarks\n(assuming `V8NeverOptimizePlugin` is being used), and the benchmark function is\nalso marked with `%NeverOptimizeFunction`.\nTogether, these prevent V8 from optimizing the benchmark function and from\ntreating the returned value as irrelevant.\n\nManaged benchmarks explicitly handle timing through `start()` and `end()` calls around the benchmarked code.\nThis encapsulates the entire set of iterations in one timed block,\nwhich can result in tighter measurement with less overhead.\nHowever, it can lead to over-optimistic results, especially if the timer’s start and stop calls are placed outside of the loop,\nallowing V8 to over-optimize the entire block.\n\nExample:\n\n```cjs\nsuite.add('[Managed] Using includes', (timer) => {\n  timer.start();\n  for (let i = 0; i < timer.count; i++) {\n    const text = 'text/html,...';\n    const r = text.includes('application/json');\n    assert.ok(r);  // Ensure the result is used so it doesn't get V8 optimized away\n  }\n  timer.end(timer.count);\n});\n```\n\nIn this case, `DoNotOptimize` is applied to the benchmark function's return value,\noutside the user-managed loop. It does not consume the local `r` value from each\niteration. That's why `assert.ok(r)` has been used: it makes the per-iteration\nresult observable inside the timed block.\n\n> [!NOTE]\n> V8 assumptions can change any time soon. Therefore, it's crucial to investigate\n> results between versions of V8/Node.js.\n\n### Worker Threads\n\n> Stability: 1.0 (Experimental)\n\n`bench-node` provides experimental support for **Worker Threads**. When you set `useWorkers: true`,\nthe library runs each benchmark in a separate worker thread, ensuring that one benchmark\ndoes not affect another. Usage is straightforward:\n\n```cjs\nconst suite = new Suite({\n  useWorkers: true,\n});\n```\n\n## Benchmark Modes\n\n`bench-node` supports multiple benchmarking modes to measure code performance in different ways.\n\n### Operations Mode\n\nOperations mode (default) measures how many operations can be performed in a given timeframe. \nThis is the traditional benchmarking approach that reports results in operations per second (ops/sec).\n\nThis mode is best for:\n- Comparing relative performance between different implementations\n- Measuring throughput of small, fast operations\n- Traditional microbenchmarking\n\nExample output:\n```\nString concatenation x 12,345,678 ops/sec (11 runs sampled) v8-never-optimize=true min..max=(81ns...85ns)\n```\n\n### Time Mode\n\nTime mode measures the actual time taken to execute a function exactly once. \nThis mode is useful when you want to measure the real execution time for operations that have a known, fixed duration.\n\nThis mode is best for:\n- Costly operations where multiple instructions are executed in a single run \n- Benchmarking operations with predictable timing\n- Verifying performance guarantees for time-sensitive functions\n\nTo use time mode, set the `benchmarkMode` option to `'time'` when creating a Suite:\n\n```js\nconst { Suite } = require('bench-node');\n\nconst timeSuite = new Suite({\n    benchmarkMode: 'time' // Enable time mode\n});\n\n// Create a function that takes a predictable amount of time\nconst delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));\n\ntimeSuite.add('Async Delay 100ms', async () => {\n    await delay(100);\n});\n\ntimeSuite.add('Sync Busy Wait 50ms', () => {\n    const start = Date.now();\n    while (Date.now() - start < 50);\n});\n\n// Optional: Run the benchmark multiple times with repeatSuite\ntimeSuite.add('Quick Operation with 5 repeats', { repeatSuite: 5 }, () => {\n    // This will run exactly once per repeat (5 times total)\n    // and report the average time\n    let x = 1 + 1;\n});\n\n(async () => {\n    await timeSuite.run();\n})();\n```\n\nIn time mode, results include `totalTime` (in seconds) instead of `opsSec`.\n\nExample output:\n```\nAsync Delay 100ms x 0.1003s (1 sample) v8-never-optimize=true\nSync Busy Wait 50ms x 0.0502s (1 sample) v8-never-optimize=true\nQuick Operation with 5 repeats x 0.0000s (5 samples) v8-never-optimize=true\n```\n\nSee [examples/time-mode.js](./examples/time-mode.js) for a complete example.\n\n## Baseline Comparisons\n\nYou can mark one benchmark as a baseline to compare all other benchmarks against it:\n\n```js\nconst { Suite } = require('bench-node');\n\nconst suite = new Suite();\n\nsuite\n  .add('baseline', { baseline: true }, () => {\n    // baseline implementation\n    const arr = [1, 2, 3];\n    arr.includes(2);\n  })\n  .add('alternative', () => {\n    // alternative implementation\n    const arr = [1, 2, 3];\n    arr.indexOf(2) !== -1;\n  });\n\nsuite.run();\n```\n\nExample output with baseline:\n```\nbaseline     x 52,832,865 ops/sec (10 runs sampled) min..max=(18.50ns...19.22ns)\nalternative  x 53,550,219 ops/sec (11 runs sampled) min..max=(18.26ns...18.89ns)\n\nSummary (vs. baseline):\n  baseline     (baseline)\n  alternative  (1.01x faster)\n```\n\n## Statistical Significance Testing (T-Test)\n\n> Stability: 1.0 (Experimental)\n\nWhen comparing benchmarks, especially on machines with high variance (cloud VMs, shared environments),\nraw ops/sec differences may not be meaningful. `bench-node` provides **Welch's t-test** to determine\nif performance differences are statistically significant.\n\nWelch's t-test is preferred over Student's t-test because it doesn't assume equal variances between\nthe two samples, which is common in benchmark scenarios.\n\n### Enabling T-Test Mode\n\nEnable t-test mode with `ttest: true`. This automatically sets `repeatSuite=30` to collect enough\nindependent samples for reliable statistical analysis (per the Central Limit Theorem):\n\n```js\nconst { Suite } = require('bench-node');\n\nconst suite = new Suite({\n  ttest: true,  // Enables t-test and auto-sets repeatSuite=30\n});\n\nsuite\n  .add('baseline', { baseline: true }, () => {\n    let sum = 0;\n    for (let i = 0; i < 100; i++) sum += i;\n  })\n  .add('optimized', () => {\n    let sum = (99 * 100) / 2; // Gauss formula\n  });\n\nsuite.run();\n```\n\nExample output:\n```\nT-Test Mode: Enabled (repeatSuite=30)\n\nbaseline   x 1,234,567 ops/sec (300 runs sampled) min..max=(810.05ns...812.45ns)\noptimized  x 9,876,543 ops/sec (305 runs sampled) min..max=(101.23ns...102.87ns)\n\nSummary (vs. baseline):\n  baseline   (baseline)\n  optimized  (8.00x faster) ***\n\n  Significance: * p<0.05, ** p<0.01, *** p<0.001\n```\n\nThe asterisks indicate significance level:\n- `***` = p < 0.001 (0.1% risk of false positive)\n- `**` = p < 0.01 (1% risk of false positive)\n- `*` = p < 0.05 (5% risk of false positive)\n- (no stars) = not statistically significant\n\nThis helps identify when a benchmark shows a difference due to random variance vs. a real performance improvement.\n\n**How it works**: With `ttest: true`, each benchmark runs 30 times independently (via `repeatSuite=30`). The t-test compares the 30 ops/sec values from the baseline against the 30 ops/sec values from each test benchmark. This accounts for run-to-run variance within that benchmark session.\n\n**Note**: Running the entire benchmark suite multiple times may still show variance in absolute numbers due to system-level factors (CPU frequency scaling, thermal throttling, background processes). The t-test helps determine if differences are statistically significant within each benchmark session, but results can vary between separate benchmark runs due to changing system conditions.\n\nSee also: [Fixing Inconclusive Tests](doc/Inconclusive.md).\n\n### Direct API Usage\n\nYou can also use the t-test utilities directly for custom analysis:\n\n```js\nconst { welchTTest, compareBenchmarks } = require('bench-node');\n\n// Raw sample data from two benchmarks (e.g., timing samples in nanoseconds)\nconst baseline = [100, 102, 99, 101, 100, 98, 103, 99, 100, 101];\nconst optimized = [50, 51, 49, 52, 50, 48, 51, 49, 50, 51];\n\n// High-level comparison\nconst result = compareBenchmarks(optimized, baseline, 0.05);\nconsole.log(result);\n// {\n//   significant: true,\n//   pValue: 0.00001,\n//   confidence: '99.99%',\n//   stars: '***',\n//   difference: 'faster',\n//   tStatistic: 45.2,\n//   degreesOfFreedom: 17.8\n// }\n\n// Low-level Welch's t-test\nconst ttest = welchTTest(optimized, baseline);\nconsole.log(ttest);\n// {\n//   tStatistic: 45.2,\n//   degreesOfFreedom: 17.8,\n//   pValue: 0.00001,\n//   significant: true,\n//   mean1: 50.1,\n//   mean2: 100.3,\n//   variance1: 1.43,\n//   variance2: 2.23\n// }\n```\n\n#### Interpreting Results\n\n- **`significant: true`** - The performance difference is statistically significant at the given alpha level\n- **`pValue`** - Probability that the observed difference occurred by chance (lower = more confident)\n- **`confidence`** - Confidence level (e.g., \"99.95%\" means 99.95% confident the difference is real)\n- **`stars`** - Visual indicator of significance: `'***'` (p<0.001), `'**'` (p<0.01), `'*'` (p<0.05), or `''` (not significant)\n- **`difference`** - Whether the first sample is `'faster'`, `'slower'`, or `'same'` as the second\n\nA common threshold is `alpha = 0.05` (95% confidence). If `pValue < alpha`, the difference is significant.\n\n## Writing JavaScript Mistakes\n\nWhen working on JavaScript micro-benchmarks, it’s easy to forget that modern engines use\nmultiple tiers of Just-In-Time (JIT) compilation and sometimes even entirely different\noptimizations. The results you get from a simple timing loop often aren’t representative\nof how your code will behave under real-world conditions, especially once the browser or\nruntime has adjusted for frequent function calls. Caching, tail call optimizations,\nand hidden class transformations can all distort your measurements, leading to overblown\nclaims about performance improvements that might never materialize in production.\n\nThat’s why **bench-node** was created—to provide a stable and consistent way to compare\nsmall snippets of code. By default, it tells V8 to never optimize the benchmark\nfunction with `%NeverOptimizeFunction(bench.fn)` and consumes the returned value\nwith a non-optimized `DoNotOptimize` helper so the JIT compiler doesn’t remove\ndead code. However, even this approach can’t fully replicate real-world scenarios\nin which V8 optimizations and unpredictable workloads impact performance. Think of\nbench-node as a helpful tool for quick comparisons rather than a guarantee of what you’ll\nsee in production.\n\n\n[build-img]:https://github.com/RafaelGSS/bench-node/actions/workflows/release.yml/badge.svg\n\n[build-url]:https://github.com/RafaelGSS/bench-node/actions/workflows/release.yml\n\n[downloads-img]:https://img.shields.io/npm/dt/bench-node\n\n[downloads-url]:https://www.npmtrends.com/bench-node\n\n[npm-img]:https://img.shields.io/npm/v/bench-node\n\n[npm-url]:https://www.npmjs.com/package/bench-node\n\n[issues-img]:https://img.shields.io/github/issues/RafaelGSS/bench-node\n\n[issues-url]:https://github.com/RafaelGSS/bench-node/issues\n"
  },
  {
    "path": "assets/README.md",
    "content": "# \"Branding\"\n\n## Logo\n\n| Logo | Description |\n| --- | --- |\n| ![Logo](./logo.svg) | simple logo for readme |\n| ![Logo](./logo-text-light.svg) | logo with text for light backgrounds |\n| ![Logo](./logo-text-dark.svg) | logo with text for dark backgrounds |\n\n## Other\n\n- **Font**: [Geist](https://fonts.google.com/specimen/Geist)\n- **Light Green**: `#84BA64`\n- **Dark Green**: `#2C682C`\n\n> The design of the assets was created by [@AugustinMauroy](https://github.com/AugustinMauroy).\n"
  },
  {
    "path": "biome.json",
    "content": "{\n\t\"$schema\": \"https://biomejs.dev/schemas/1.8.3/schema.json\",\n\t\"organizeImports\": {\n\t\t\"enabled\": true\n\t},\n\t\"linter\": {\n\t\t\"enabled\": true,\n\t\t\"rules\": {\n\t\t\t\"recommended\": true,\n\t\t\t\"suspicious\": {\n\t\t\t\t\"noExplicitAny\": \"off\"\n\t\t\t},\n\t\t\t\"style\": {\n\t\t\t\t\"noParameterAssign\": \"off\",\n\t\t\t\t\"noUselessElse\": \"off\"\n\t\t\t}\n\t\t}\n\t},\n\t\"formatter\": {\n\t\t\"enabled\": true\n\t},\n\t\"files\": {\n\t\t\"ignore\": [\n\t\t\t\"examples/*\",\n\t\t\t\"coverage/*\",\n\t\t\t\"test/plugin-api-doc.js\",\n\t\t\t\"package.json\"\n\t\t]\n\t}\n}\n"
  },
  {
    "path": "doc/Inconclusive.md",
    "content": "# Fixing Inconclusive Tests\n\nt-tests are looking at the distribution of both sets of results and trying to determine if they overlap in a way that\nmakes the average value significant or just noise in the results. A run with a bimodal distribution for instance, caused\nby problems with the machine the tests are running on or the NodeJS runtime doing things in the background. Here are a\nfew causes.\n\n## Random Input Data\n\nVariability in the inputs between runs can lead to big changes in the runtime of an algorithm. Particularly with code\nthat sorts, filters, or conditionally operates on input data, feeding them certain combinations of data will result in\nwildly different run times from one loop to the next or occasionally from one sample to the next. The Central Limit\nTheorem (that over a long enough time a situation will revert to the mean), does not invalidate the existence of the\nGambler's Paradox (that it will revert to the mean before I become bankrupt).\n\nIt is better to do your fuzzing in fuzz tests and pick representative data for your benchmarks. Partially informed by\nthe results of your fuzz tests, and other bug reports.\n\n## Underprovisioned VM, Oversubscribed hardware\n\nFor a clean micro benchmark, we generally want to be the only one using the machine at the time. There are a number of\nknown issues running benchmarks on machines that are thermally throttling, or on cheap VMs that use best-effort to\nallocate CPU time to the running processes. In particular, docker images with `cpu-shares` are especially poor targets\nfor running benchmarks because the quota might expire for one timeslice in the middle of one test or between benchmarks\nin a single Suite. This creates an unfair advantage for the first test, and/or lots of noise in the results. We are\ncurrently investigating ways to detect this sort of noise, and analyzing if the t-tests are sufficient to do so.\n\n## Epicycles in GC or JIT compilation\n\nIf the warmup time is insufficient to get V8 to optimize the code, it may kick in during the middle of a sample, which\nwill introduce a bimodal distribution of answers (before, and after). There is currently not a way to adjust the warmup\ntime of `bench-node`, but should be added as a feature.\n\nOne of the nastiest performance issues to detect in garbage collected code is allocation epicycles. This happens when\nearly parts of a calculation create lots of temporary data but not sufficient to cross the incremental or full GC\nthreshold, so that the next function in a call sequence routinely gets hit with exceeding the threshold. This is\nespecially common in code that generates a JSON or HTML response to a series of calculations - it is the single biggest\nallocation in the sequence, but it gets blamed in the performance report for the lion's share of the CPU time.\n\nIf you change the `minTime` up or down, that will alter the number of iterations per sample which may smooth out the\nresults. You can also try increasing `minSamples` to get more samples. But also take this as a suggestion that your code\nmay have a performance bug that is worth prioritizing.\n\nForcing GC in the teardown or setup methods between subsequent tests in a single Suite may help with some situations\nwhen reordering the tests results in differences in runtime, but for the more general case, you may need to review the\ncode under test to make it 'play well' both with benchmarking and in production systems.\n\nIn production code, particularly where p9# values are used as a fitness test, it is sometimes better to chose the\nalgorithm with more consistent runtime over the one with supposedly better average runtime. This can also be true where\nDDOS scenarios are possible - the attacker will always chose the worst, most assymetric request to send to your machine,\nand mean response time will not matter one whit. If `bench-node` is complaining, the problem may not be `bench-node`.\n"
  },
  {
    "path": "doc/Plugins.md",
    "content": "# Plugins\n\nThe benchmark module supports a flexible plugin system that\nallows you to extend its functionality by adding custom plugins.\nThis documentation explains how to create, validate, and use\nplugins within the benchmarking framework.\n\n[V8NeverOptimizePlugin](#class-v8neveroptimizeplugin) is enabled by default.\n\nTo observe how a plugin is used, see the `plugin-api-doc.js` file in tests and explore its results.\n\n## Structure\n\nEach plugin is expected to follow a specific structure with required methods\nfor integration into the benchmark module. The plugins are required to define\nthe following methods:\n\n* `isSupported()`: This method checks if the plugin can run in the\n  current environment. If the plugin uses features specific to certain\n  environments (e.g., V8 engine features), it should return `true` if those\n  features are available and `false` otherwise.\n\n* `toString()`: This method should return a string representation of the plugin.\n  It’s used for logging and error messages.\n\nIn addition to these required methods, plugins can optionally define other\nmethods based on their functionality, such as `beforeClockTemplate()`,\n`afterClockTemplate()`, `onCompleteBenchmark()`, and more.\n\n## Plugin Methods\n\n### `isSupported()` (required)\n\nThis method checks if the plugin's functionality is available in the\ncurrent environment. For instance, if a plugin uses specific V8 engine commands,\nthis method ensures the environment supports them.\n\n### `beforeClockTemplate(varNames)`\n\n* `varNames` {Object}\n  * `bench` {string}  - Name for the benchmark variable.\n  * `context` {string} - Name for the context variable.\n  * `timer` {string} - Name for the timer variable.\n  * `awaitOrEmpty` {string} - A string with `await` or empty string (`''`).\n\nSome plugins need to modify or prepare the code before the benchmark starts.\nThe `beforeClockTemplate()` method allows you to inject code before the timing\nprocess begins.\n\nThis method must return an array where:\n\n* The first element is a string representing the JavaScript code to be executed\nbefore the benchmark function.\n\n* The second element (optional) is a string representing a function that will\nwrap the benchmark function. This wrapper is used to customize how the\nbenchmark function is called during execution.\n\nThe wrapped function provides a powerful way to manipulate how the benchmark\nis run without directly modifying the benchmark logic.\n\n```js\nbeforeClockTemplate({ bench }) {\n  let code = '';\n\n  code += `\nfunction DoNotOptimize(x) {}\n// Prevent the benchmark function and result consumer from optimizing or being inlined.\n%NeverOptimizeFunction(${bench}.fn);\n%NeverOptimizeFunction(DoNotOptimize);\n`\n  return [code, 'DoNotOptimize'];\n}\n```\n\nIn this example, the plugin injects the `DoNotOptimize` function and also\nprovides it as a wrapper for the benchmark function result. The benchmark\nfunction itself is marked with `%NeverOptimizeFunction(${bench}.fn)`, while the\n`DoNotOptimize` wrapper consumes the returned value so the benchmark expression\ndoes not become observationally irrelevant.\n\nThese two protections address different parts of the generated code:\n`%NeverOptimizeFunction(${bench}.fn)` targets the function under test, and\n`DoNotOptimize(bench.fn())` targets the value returned by each call.\n\n### `afterClockTemplate(varNames)`\n\n* `varNames` {Object}\n  * `bench` {string}  - Name for the benchmark variable.\n  * `context` {string} - Name for the context variable.\n  * `timer` {string} - Name for the timer variable.\n  * `awaitOrEmpty` {string} - A string with `await` or empty string (`''`).\n\nAfter the benchmark runs, this method can inject code to gather performance data\nor reset configurations. It must return an array where:\n\n* The first element is a string containing the JavaScript code to be executed\nafter the benchmark finishes.\n\nUnlike `beforeClockTemplate`, `afterClockTemplate` does not support a second\nelement in the returned array, as it only runs cleanup or data collection code\nafter the benchmark is executed.\n\n### `onCompleteBenchmark(result)`\n\n* `result` {Object}\n  * `duration` {number}  - Benchmark duration\n  * `count` {number} - Number of iterations\n  * `context` {Object} - A object used to store results after the benchmark clock\n\nThis method is called when the benchmark completes. Plugins can collect and\nprocess data from the benchmark results in this step.\n\n### `toString()` (required)\n\nThis method returns a string identifier for the plugin, typically the plugin’s\nname. It is used in error messages and logging.\n\n## Example Plugins\n\nHere are examples of plugins that follow the required structure and functionality.\n\n```js\nclass V8OptimizeOnNextCallPlugin {\n  isSupported() {\n    try {\n      new Function(`%OptimizeFunctionOnNextCall(() => {})`)();\n      return true;\n    } catch (e) {\n      return false;\n    }\n  }\n\n  beforeClockTemplate({ awaitOrEmpty, bench }) {\n    let code = '';\n\n    code += `%OptimizeFunctionOnNextCall(${ bench }.fn);\\n`;\n    code += `${ awaitOrEmpty }${ bench }.fn();\\n`;\n    code += `${ awaitOrEmpty }${ bench }.fn();\\n`;\n\n    return [code];\n  }\n\n  toString() {\n    return 'V8OptimizeOnNextCallPlugin';\n  }\n}\n```\n\n## Official Plugins\n\nThis is a list of official plugins that can be fetched when requiring\n`bench-node` module.\n\n```js\nconst { V8OptimizeOnNextCallPlugin, Suite } = require('bench-node');\nconst suite = new Suite({\n  plugins: [new V8OptimizeOnNextCallPlugin()],\n})\n```\n\n### Class: `V8OptimizeOnNextCallPlugin`\n\nThe `V8OptimizeOnNextCallPlugin` triggers the V8 engine to optimize the\nfunction before it is called. This can improve performance in repeated\nbenchmarks.\n\n### Class: `V8NeverOptimizePlugin`\n\nThe `V8NeverOptimizePlugin` prevents the V8 engine from optimizing or inlining\nthe benchmark function. It also wraps the benchmark result in a non-optimized\n`DoNotOptimize` helper so V8 cannot treat an unused return value as irrelevant.\n\n### Class: `V8GetOptimizationStatus`\n\nThe `V8GetOptimizationStatus` plugin collects the V8 engine's optimization\nstatus for a given function after it has been benchmarked.\n"
  },
  {
    "path": "examples/.gitignore",
    "content": ""
  },
  {
    "path": "examples/benchmark-comparison/README.md",
    "content": "# Benchmark Comparison Framework\n\nThis directory contains a framework for comparing `bench-node` against other popular benchmark libraries:\n\n- [Benchmark.js](https://benchmarkjs.com/)\n- [Mitata](https://github.com/evanwashere/mitata)\n- [Tinybench](https://github.com/tinylibs/tinybench)\n\n## Test Cases\n\nThe comparison focuses on four key test scenarios:\n\n1. **JIT Optimizable** - Simple operations that the JavaScript JIT compiler might optimize away\n2. **Regex Test** - Regular expression pattern matching operations\n3. **Fibonacci** - CPU-bound recursive calculation\n\n## Running the Comparisons\n\nTo run any of the comparison scripts, use:\n\n```bash\nnode --allow-natives-syntax examples/benchmark-comparison/comparison.js\n```\n\n> **Note:** All bench-node scripts must be run with the `--allow-natives-syntax` flag.\n"
  },
  {
    "path": "examples/benchmark-comparison/comparison.js",
    "content": "\nconst { Suite } = require('../../lib');\nconst Benchmark = require('benchmark');\nconst { bench, run: mitataRun } = require('mitata');\nconst { Bench } = require('tinybench');\n\nconst testSuites = {\n  'simple-operations': {\n    name: 'Simple Operations',\n    tests: {\n      'jit-optimizable': {\n        name: 'JIT Optimizable',\n        fn: function() {\n          const x = 1;\n          const y = 2;\n          return x + y;\n        }\n      }\n    }\n  },\n  \n  'string-processing': {\n    name: 'String Processing',\n    tests: {\n      'regex-test': {\n        name: 'Regex Test',\n        fn: function() {\n          const pattern = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$/;\n          const emails = [\n            'test@example.com',\n            'invalid-email',\n            'another.test@example.co.uk',\n            'john.doe123@sub.domain.com'\n          ];\n          return emails.map(email => pattern.test(email));\n        }\n      }\n    }\n  },\n  \n  'cpu-intensive': {\n    name: 'CPU Intensive',\n    tests: {\n      'fibonacci-10': {\n        name: 'Fibonacci(10)',\n        fn: function() {\n          const n = 10;\n          let a = 0, b = 1;\n          for (let i = 2; i <= n; i++) {\n            [a, b] = [b, a + b];\n          }\n          return b;\n        }\n      },\n      \n      'fibonacci-30': {\n        name: 'Fibonacci(30)',\n        fn: function() {\n          const n = 30;\n          let a = 0, b = 1;\n          for (let i = 2; i <= n; i++) {\n            [a, b] = [b, a + b];\n          }\n          return b;\n        }\n      },\n      \n      'fibonacci-40': {\n        name: 'Fibonacci(40)',\n        fn: function() {\n          const n = 40;\n          let a = 0, b = 1;\n          for (let i = 2; i <= n; i++) {\n            [a, b] = [b, a + b];\n          }\n          return b;\n        }\n      },\n      \n      'fibonacci-recursive-10': {\n        name: 'Fibonacci Recursive(10)',\n        fn: function() {\n          function fibRecursive(n) {\n            if (n <= 1) return n;\n            return fibRecursive(n - 1) + fibRecursive(n - 2);\n          }\n          return fibRecursive(10);\n        }\n      }\n    }\n  }\n};\n\nfunction formatNumber(num) {\n  return new Intl.NumberFormat().format(Math.round(num));\n}\n\nasync function runBenchNode() {\n  console.log('\\n🔍 Running bench-node tests... (detailed output hidden)');\n  \n  // Save original console.log\n  const originalConsoleLog = console.log;\n  // Replace with no-op function\n  console.log = () => {};\n  \n  const results = [];\n  \n  try {\n    for (const [suiteKey, suite] of Object.entries(testSuites)) {\n      const benchSuite = new Suite({ name: suite.name });\n      \n      Object.entries(suite.tests).forEach(([testKey, test]) => {\n        benchSuite.add(test.name, test.fn);\n      });\n      \n      try {\n        const suiteResults = await benchSuite.run();\n        \n        // Collect results\n        suiteResults.forEach(result => {\n          // Extract ops/sec from the bench-node result object\n          // The opsSec field contains the operations per second value\n          let opsPerSec = 0;\n          \n          if (typeof result.opsSec === 'number') {\n            // Direct extraction from the opsSec field\n            opsPerSec = result.opsSec;\n          } else if (typeof result.hz === 'number') {\n            // Fallback to hz if available\n            opsPerSec = result.hz;\n          } else if (result.stats && typeof result.stats.mean === 'number') {\n            // Mean is in nanoseconds, convert to ops/sec\n            opsPerSec = 1_000_000_000 / result.stats.mean;\n          } else if (typeof result.avg === 'number') {\n            // Avg is in seconds, convert to ops/sec\n            opsPerSec = 1 / result.avg;\n          }\n          \n          results.push({\n            tool: 'bench-node',\n            suite: suite.name,\n            test: result.name,\n            opsPerSec: opsPerSec,\n            margin: result.rme || 0\n          });\n        });\n      } catch (error) {\n        // Temporarily restore console.log for error reporting\n        const tempConsole = console.log;\n        console.log = originalConsoleLog;\n        console.log(`Error running benchmark suite ${suite.name}: ${error.message}`);\n        console.log = tempConsole;\n        \n        // Add placeholder results for this suite so we don't lose the comparison\n        Object.entries(suite.tests).forEach(([testKey, test]) => {\n          results.push({\n            tool: 'bench-node',\n            suite: suite.name,\n            test: test.name,\n            opsPerSec: 0,\n            margin: 0\n          });\n        });\n      }\n    }\n  } catch (error) {\n    // Temporarily restore console.log for error reporting\n    const tempConsole = console.log;\n    console.log = originalConsoleLog;\n    console.log(`Error in bench-node benchmarks: ${error.message}`);\n    console.log = tempConsole;\n  }\n  \n  // Restore original console.log\n  console.log = originalConsoleLog;\n  return results;\n}\n\n\nasync function runBenchmarkJS() {\n  console.log('\\n🔍 Running benchmark.js tests... (detailed output hidden)');\n  \n  // Save original console.log\n  const originalConsoleLog = console.log;\n  // Replace with no-op function\n  console.log = () => {};\n  \n  const results = [];\n  \n  try {\n    for (const [suiteKey, suite] of Object.entries(testSuites)) {\n      await new Promise((resolve) => {\n        const benchSuite = new Benchmark.Suite(suite.name);\n        \n        Object.entries(suite.tests).forEach(([testKey, test]) => {\n          benchSuite.add(test.name, test.fn);\n        });\n        \n        benchSuite\n          .on('cycle', (event) => {\n            // Collect result for each test\n            results.push({\n              tool: 'benchmark.js',\n              suite: suite.name,\n              test: event.target.name,\n              opsPerSec: event.target.hz,\n              margin: event.target.stats.rme\n            });\n          })\n          .on('complete', function() {\n            resolve();\n          })\n          .run();\n      });\n    }\n  } catch (error) {\n    // Temporarily restore console.log for error reporting\n    const tempConsole = console.log;\n    console.log = originalConsoleLog;\n    console.log(`Error in benchmark.js benchmarks: ${error.message}`);\n    console.log = tempConsole;\n  }\n  \n  // Restore original console.log\n  console.log = originalConsoleLog;\n  return results;\n}\n\nasync function runMitata() {\n  console.log('\\n🔍 Running mitata tests... (detailed output hidden)');\n  \n  // Save original console.log\n  const originalConsoleLog = console.log;\n  // Replace with no-op function\n  console.log = () => {};\n\n  // Create a results collection\n  const results = [];\n  \n  try {\n    // Store test to name mappings for result collection\n    const testMap = new Map();\n    \n    for (const [suiteKey, suite] of Object.entries(testSuites)) {\n      // Estimate performance based on test function execution time\n      Object.entries(suite.tests).forEach(([testKey, test]) => {\n        const fullName = `${suite.name}: ${test.name}`;\n        bench(fullName, test.fn);\n        \n        // Create a test mapping\n        testMap.set(fullName, { suite: suite.name, test: test.name });\n        \n        // Do a simple performance estimate (not as accurate as real benchmark)\n        // This is a fallback since we can't easily extract mitata results\n        const start = process.hrtime.bigint();\n        const iterations = 1000;\n        for (let i = 0; i < iterations; i++) {\n          test.fn();\n        }\n        const end = process.hrtime.bigint();\n        const avgNs = Number(end - start) / iterations;\n        const estOpsPerSec = 1_000_000_000 / avgNs;\n        \n        // Add estimated result\n        results.push({\n          tool: 'mitata',\n          suite: suite.name,\n          test: test.name,\n          opsPerSec: estOpsPerSec,\n          margin: 5 // Rough estimate of margin\n        });\n      });\n    }\n    \n    // Run mitata benchmarks silently\n    await mitataRun();\n  } catch (error) {\n    // Temporarily restore console.log for error reporting\n    const tempConsole = console.log;\n    console.log = originalConsoleLog;\n    console.log(`Error in mitata benchmarks: ${error.message}`);\n    console.log = tempConsole;\n  }\n  \n  // Restore original console.log\n  console.log = originalConsoleLog;\n  return results;\n}\n\n\nasync function runTinyBench() {\n  console.log('\\n🔍 Running tinybench tests... (detailed output hidden)');\n  \n  // Save original console.log\n  const originalConsoleLog = console.log;\n  // Replace with no-op function\n  console.log = () => {};\n  \n  const allResults = [];\n  \n  try {\n    // Create benchmarks for each suite\n    for (const [suiteKey, suite] of Object.entries(testSuites)) {\n      const benchmark = new Bench();\n      \n      // Add tests to suite\n      Object.entries(suite.tests).forEach(([testKey, test]) => {\n        benchmark.add(test.name, test.fn);\n      });\n      \n      // Run benchmarks\n      await benchmark.run();\n      \n      // Format and collect results\n      const suiteResults = benchmark.tasks.map(({ name, result }) => ({\n        name,\n        ops: result?.hz || 0,\n        margin: result?.rme ? `±${result?.rme.toFixed(2)}%` : 'N/A'\n      }));\n      suiteResults.forEach(result => {\n        // Extract numeric margin value from the margin string (e.g., '±1.20%' -> 1.20)\n        let marginValue = 0;\n        if (result.margin && result.margin !== 'N/A') {\n          const match = result.margin.match(/[0-9.]+/);\n          if (match) {\n            marginValue = parseFloat(match[0]);\n          }\n        }\n        \n        allResults.push({\n          tool: 'tinybench',\n          suite: suite.name,\n          test: result.name,\n          opsPerSec: result.ops,\n          margin: marginValue\n        });\n      });\n    }\n  } catch (error) {\n    // Temporarily restore console.log for error reporting\n    const tempConsole = console.log;\n    console.log = originalConsoleLog;\n    console.log(`Error in tinybench benchmarks: ${error.message}`);\n    console.log = tempConsole;\n  }\n  \n  // Restore original console.log\n  console.log = originalConsoleLog;\n  return allResults;\n}\n\n\nfunction compareResults(results) {\n  console.log('\\n📈 BENCHMARK COMPARISON SUMMARY');\n  console.log('============================');\n  \n  // Group results by suite and test\n  const resultsBySuiteAndTest = {};\n  \n  results.forEach(result => {\n    // Ensure we have valid ops/sec values for comparison\n    if (isNaN(result.opsPerSec) || result.opsPerSec === undefined || result.opsPerSec === null) {\n      result.opsPerSec = 0;\n    }\n    \n    const key = `${result.suite} - ${result.test}`;\n    if (!resultsBySuiteAndTest[key]) {\n      resultsBySuiteAndTest[key] = [];\n    }\n    resultsBySuiteAndTest[key].push(result);\n  });\n  \n  // For each test case, compare across tools\n  Object.entries(resultsBySuiteAndTest).forEach(([testCase, testResults]) => {\n    console.log(`\\n📏 Test Case: ${testCase}`);\n    \n    const comparisonTable = testResults.map(result => ({\n      'Tool': result.tool,\n      'ops/sec': formatNumber(result.opsPerSec || 0),\n      'margin': result.margin ? `±${result.margin.toFixed(2)}%` : 'N/A',\n      'relative': '1x'\n    }));\n    \n    // Find the fastest tool for this test (with valid non-zero result)\n    const validResults = testResults.filter(r => r.opsPerSec > 0);\n    const fastest = validResults.length > 0 ?\n      validResults.reduce((max, result) => result.opsPerSec > max.opsPerSec ? result : max, validResults[0]) :\n      { opsPerSec: 1 };\n    \n    // Calculate relative performance\n    comparisonTable.forEach(row => {\n      const result = testResults.find(r => r.tool === row['Tool']);\n      if (result && result.opsPerSec > 0 && fastest.opsPerSec > 0) {\n        const relative = result.opsPerSec / fastest.opsPerSec;\n        row['relative'] = `${relative.toFixed(2)}x`;\n      } else {\n        row['relative'] = 'N/A';\n      }\n    });\n    \n    // Sort by performance (descending)\n    comparisonTable.sort((a, b) => {\n      const aResult = testResults.find(r => r.tool === a['Tool']);\n      const bResult = testResults.find(r => r.tool === b['Tool']);\n      const aSpeed = aResult && aResult.opsPerSec > 0 ? aResult.opsPerSec : 0;\n      const bSpeed = bResult && bResult.opsPerSec > 0 ? bResult.opsPerSec : 0;\n      return bSpeed - aSpeed;\n    });\n    \n    console.table(comparisonTable);\n  });\n}\n\nasync function main() {\n  console.log('🚀 BENCHMARK COMPARISON');\n  console.log('======================');\n  console.log('Comparing: bench-node, benchmark.js, mitata, tinybench\\n');\n\n  try {\n    const results = [];\n    \n    // Run all benchmarks and collect results\n    const benchNodeResults = await runBenchNode();\n    const benchmarkJsResults = await runBenchmarkJS();\n    const mitataResults = await runMitata();\n    const tinyBenchResults = await runTinyBench();\n    \n    // Combine all results\n    results.push(...benchNodeResults);\n    results.push(...benchmarkJsResults);\n    results.push(...mitataResults);\n    results.push(...tinyBenchResults);\n    \n    // Compare results across all tools\n    compareResults(results);\n    \n    console.log('\\n✅ All benchmarks completed successfully!');\n  } catch (error) {\n    console.error('❌ Error running benchmarks:', error);\n  }\n}\n\nmain();\n"
  },
  {
    "path": "examples/chart-report/node.js",
    "content": "const { Suite, chartReport } = require('../../lib');\nconst assert = require('node:assert');\n\nasync function runSuiteOne() {\n  const suite = new Suite({\n    reporter: chartReport,\n  });\n\n  await suite\n    .add('test 1', function () {\n      const pattern = /[123]/g\n    })\n    .add('test 2', function () {\n      const subject = '123123123123123123123123123123123123123123123123'\n      const r = subject.replace(/1/g, 'a').replace(/2/g, 'b').replace(/3/g, 'c')\n    })\n    .run();\n}\n\nasync function runSuiteTwo() {\n  const suite = new Suite({\n    reporter: chartReport,\n    reporterOptions: {\n      printHeader: false\n    }\n  });\n\n  await suite\n    .add('test 3', function () {\n      const pattern = /[123]/g\n    })\n    .add('test 4', function () {\n      const subject = '123123123123123123123123123123123123123123123123'\n      const r = subject.replace(/1/g, 'a').replace(/2/g, 'b').replace(/3/g, 'c')\n      assert.ok(r)\n    })\n    .run();\n}\n\nasync function main() {\n  await runSuiteOne()\n  process.stdout.write('\\n\\n')\n  await runSuiteTwo()\n}\n\nmain()\n"
  },
  {
    "path": "examples/create-uint32array/node.js",
    "content": "const { Suite } = require('../../lib');\n\nconst suite = new Suite();\n\nsuite\n  .add(`new Uint32Array(1024)`, { baseline: true }, function () {\n    return new Uint32Array(1024);\n  })\n  .add(`new Uint32Array(1024) with 10 repetitions`,  {repeatSuite: 10}, function () {\n    return new Uint32Array(1024);\n  })\n  .add(`new Uint32Array(1024) with min samples of 50`, {minSamples: 50}, function() {\n    return new Uint32Array(1024);\n  })\n  .add(`[Managed] new Uint32Array(1024)`, function (timer) {\n    const assert = require('node:assert');\n\n    let r;\n\n    timer.start();\n    for (let i = 0; i < timer.count; i++) {\n      r = new Uint32Array(1024);\n    }\n    timer.end(timer.count);\n\n    assert.ok(r);\n  })\n  .run();\n"
  },
  {
    "path": "examples/create-uint32array/node.js.log",
    "content": "new Uint32Array(1024) x 2,930,763 ops/sec (11 runs sampled) v8-never-optimize=true min..max=(332.08ns ... 355.52ns) p75=344.36ns p99=355.52ns\n[Managed] new Uint32Array(1024) x 2,934,651 ops/sec (10 runs sampled) v8-never-optimize=true min..max=(335.94ns ... 357.76ns) p75=340.16ns p99=357.76ns\n----------------------------------------------------------------------------\nnew Uint32Array(1024) x 2,967,727 ops/sec (9 runs sampled) v8-never-optimize=true min..max=(330.75ns ... 337.55ns) p75=334.98ns p99=337.55ns\n[Managed] new Uint32Array(1024) x 3,029,150 ops/sec (10 runs sampled) v8-never-optimize=true min..max=(326.51ns ... 341.28ns) p75=331.01ns p99=341.28ns\n----------------------------------------------------------------------------\nnew Uint32Array(1024) x 2,920,851 ops/sec (12 runs sampled) v8-never-optimize=true min..max=(332.88ns ... 363.08ns) p75=348.19ns p99=363.08ns\n[Managed] new Uint32Array(1024) x 3,032,256 ops/sec (12 runs sampled) v8-never-optimize=true min..max=(327.13ns ... 343.75ns) p75=329.19ns p99=343.75ns\n----------------------------------------------------------------------------\n"
  },
  {
    "path": "examples/crypto-verify/node.js",
    "content": "const crypto = require('node:crypto');\nconst { readFileSync } = require('fs');\nconst { Suite } = require('../../lib');\n\nconst rsaPrivateKey = readFileSync(`${__dirname}/private-key.pem`, 'utf-8');\nconst rsaPublicKey = readFileSync(`${__dirname}/public-key.pem`, 'utf-8');\n\nconst thing = 'hello world';\nconst algorithm = 'RSA-SHA256';\nconst signature = crypto.createSign(algorithm).update(thing).sign(rsaPrivateKey, 'base64');\n\nconst suite = new Suite();\n\nsuite\n  .add(`crypto.createVerify('${algorithm}')`, function () {\n    var verifier = crypto.createVerify(algorithm);\n    verifier.update(thing);\n    verifier.verify(rsaPublicKey, signature, 'base64');\n  })\n  .add(`crypto.verify('${algorithm}')`, function () {\n    crypto.verify(algorithm, thing, rsaPublicKey, Buffer.from(signature, 'base64'));\n  })\n  .add(`[Managed] crypto.createVerify('RSA-SHA256')`, function (timer) {\n    const crypto = require('node:crypto');\n    const { readFileSync } =require('node:fs');\n    const assert = require('node:assert');\n\n    const rsaPrivateKey = readFileSync(`${__dirname}/private-key.pem`, 'utf-8');\n    const rsaPublicKey = readFileSync(`${__dirname}/public-key.pem`, 'utf-8');\n\n    const thing = 'hello world'\n    const signature = crypto.createSign('RSA-SHA256').update(thing).sign(rsaPrivateKey, 'base64')\n\n    let verifier;\n\n    timer.start();\n    for (let i = 0; i < timer.count; i++) {\n      verifier = crypto.createVerify('RSA-SHA256')\n      verifier.update(thing)\n      verifier.verify(rsaPublicKey, signature, 'base64')\n    }\n    timer.end(timer.count);\n\n    assert.ok(verifier);\n  })\n  .add(`[Managed] crypto.verify('RSA-SHA256')`, function (timer) {\n    const crypto = require('node:crypto');\n    const { readFileSync } = require('node:fs');\n    const assert = require('node:assert');\n\n    const rsaPrivateKey = readFileSync(`${__dirname}/private-key.pem`, 'utf-8');\n    const rsaPublicKey = readFileSync(`${__dirname}/public-key.pem`, 'utf-8');\n\n    const thing = 'hello world'\n    const signature = crypto.createSign('RSA-SHA256').update(thing).sign(rsaPrivateKey, 'base64')\n\n    let r;\n\n    timer.start();\n    for (let i = 0; i < timer.count; i++) {\n      r = crypto.verify('RSA-SHA256', thing, rsaPublicKey, Buffer.from(signature, 'base64'));\n    }\n    timer.end(timer.count);\n\n    assert.ok(r);\n  })\n  .run();\n"
  },
  {
    "path": "examples/crypto-verify/node.js.log",
    "content": "crypto.createVerify('RSA-SHA256') x 10,925 ops/sec (12 runs sampled) v8-never-optimize=true min..max=(88.50us ... 93.51us) p75=91.54us p99=93.51us\ncrypto.verify('RSA-SHA256') x 10,988 ops/sec (9 runs sampled) v8-never-optimize=true min..max=(90.93us ... 91.84us) p75=91.58us p99=91.84us\n[Managed] crypto.createVerify('RSA-SHA256') x 10,520 ops/sec (9 runs sampled) v8-never-optimize=true min..max=(88.44us ... 92.23us) p75=91.47us p99=92.23us\n[Managed] crypto.verify('RSA-SHA256') x 10,948 ops/sec (9 runs sampled) v8-never-optimize=true min..max=(89.98us ... 92.36us) p75=91.49us p99=92.36us\n----------------------------------------------------------------------------\ncrypto.createVerify('RSA-SHA256') x 10,807 ops/sec (8 runs sampled) v8-never-optimize=true min..max=(91.66us ... 92.58us) p75=92.23us p99=92.58us\ncrypto.verify('RSA-SHA256') x 10,964 ops/sec (11 runs sampled) v8-never-optimize=true min..max=(88.95us ... 92.46us) p75=91.83us p99=92.46us\n[Managed] crypto.createVerify('RSA-SHA256') x 10,954 ops/sec (10 runs sampled) v8-never-optimize=true min..max=(90.08us ... 92.88us) p75=91.72us p99=92.88us\n[Managed] crypto.verify('RSA-SHA256') x 10,990 ops/sec (9 runs sampled) v8-never-optimize=true min..max=(90.31us ... 92.43us) p75=91.43us p99=92.43us\n----------------------------------------------------------------------------\ncrypto.createVerify('RSA-SHA256') x 10,890 ops/sec (11 runs sampled) v8-never-optimize=true min..max=(87.88us ... 94.08us) p75=92.96us p99=94.08us\ncrypto.verify('RSA-SHA256') x 10,906 ops/sec (9 runs sampled) v8-never-optimize=true min..max=(91.03us ... 92.45us) p75=91.66us p99=92.45us\n[Managed] crypto.createVerify('RSA-SHA256') x 11,050 ops/sec (11 runs sampled) v8-never-optimize=true min..max=(87.94us ... 92.09us) p75=91.88us p99=92.09us\n[Managed] crypto.verify('RSA-SHA256') x 10,990 ops/sec (11 runs sampled) v8-never-optimize=true min..max=(90.29us ... 92.13us) p75=91.92us p99=92.13us\n----------------------------------------------------------------------------\n"
  },
  {
    "path": "examples/crypto-verify/private-key.pem",
    "content": "-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEAz7QweYwr7puNtc8/6jf4pADXfY2/HVq7LgI7JXkAwW5HTEjU\nRvu02SYC7n/ylr86Da4hcAEAFBC2baVh7J0EnLo6D4FA3yV4s5zj7tqL+p2CdniY\nMDEny+uxS/9eRuLf1RIYHjSVB6wOoU4lg9FtDOQA8W09ZDLvc3/4ZSwEf9O2J5Vm\nqcSGaPt35eIEBS0iIMiOUCGqbUcVMZkp4JO7I65HOK/g0Scvb1LLY6f4qDsThqyi\nID/NXM5LtjnztRP+9dCLT9DdwC+LeU2te6vcEwC2bPYs3nUwkZL4qqIWO6GHaavn\ngFKSmbmc9muVAPmYW/ffMtznKZaWCp9Li0JGuQIDAQABAoIBAQC7OI7hYSpQgEKy\neUgBlcY3/tI/SD/W8+v5QuWRl4rI0ODPsG44NbcEbbECzq4al/B6WFWnoh8x9waZ\nuxOTts1rgKnJRBb3jc1JCcijirfWhZgNthJojkZzF9bOzDds6iAc7Zxzza3wJnVh\njRFfyqzji7oV5QQLh6YzlEyQ1aaQmOKTkCDV1UDetI8iItUvq3i8EwsFmtfvXjTl\naymL/xSd3R6w3WDEd8PS5ZPDoFmkt2h/4IhOo6bVXLwWBWqcopRAvzGQrc806c2l\njjePwk9fddIG+vIHc8DHqaC/WzMR6iBt3K7Q9Eyu4k97qeOxB5TIEVlhxg2DJON9\nDlGtInXpAoGBAPje3L0xL441i7kh7GqiWtXiDgxVwdPmN5p7n755MddSxvu8894h\nT9/LxwAVcpolXANgyRtrl5bu/gXgznKefmVr976BzG2DY33PESf5FnHZAmixWSfz\nVOdfVlz+Bk/BI+nVwmeT0+8NSHJ6TJ/9NOSzI9ctUceszIHG5/Gfc2wHAoGBANWn\nbHNT7aphFqyUMfWyc6+h8Radq/RVMrOGeD2wRRqUTeb2Ydbx2OwB+gcwnlbsQjfS\nBYAa3kn1z5K5kOigB9qu6x7zuM9SMReKIlMg3NpYVyKKj+JaNF3YArFa5TWy0B26\nwNJ0FiiZyPq4eP5hPwM+Bed4ytIRWa54P5GeRYc/AoGAWT8ijb4rvaW6G4Ps0jiy\ntmzAeO/v+FtgqUeX+6helUccEH6sPYZYrHrZPFB0ro6jNprow6qLzBacheMeZcAs\nt5ZGW80UUFmDvkQZdOpAgEdAM+cVf9wlIGvx/psiDEvI4zxC4P4ETH/I8TSmceFN\nrI4JVkrsPtza4ddAqkdyDtUCgYAaoMs7dHJikccpqy6u2JbihORvVSdhRF0VUuUZ\niyaRsXokFwEKsQnAIF7xFnYljzyRiHN3C+I4hZJhTw9obsmLz9EuAmI+NJg5vtWY\nVrgv3mK9w1c7dtKf/5QWVqXKk4asreHqWN2KIeCSnvs1eRlJZimGN9/PXqo2vHXv\nyDISMQKBgE22EhVB99rEsLXDCXOUHIFGpW46ZavQc4DLXg+uIQm3y0WWyc0oyNF8\n9kr1luu5QI5PV9mWpSKa3SKmc74biyxhlUp/DYuMyuSXpdDegMAKheAHVXP9ToU/\n5qhq4HBcISN3CwSAYcFcbqC3cB07T+IwX6NTEz4zel4gty8t/tsM\n-----END RSA PRIVATE KEY-----"
  },
  {
    "path": "examples/crypto-verify/public-key.pem",
    "content": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAz7QweYwr7puNtc8/6jf4\npADXfY2/HVq7LgI7JXkAwW5HTEjURvu02SYC7n/ylr86Da4hcAEAFBC2baVh7J0E\nnLo6D4FA3yV4s5zj7tqL+p2CdniYMDEny+uxS/9eRuLf1RIYHjSVB6wOoU4lg9Ft\nDOQA8W09ZDLvc3/4ZSwEf9O2J5VmqcSGaPt35eIEBS0iIMiOUCGqbUcVMZkp4JO7\nI65HOK/g0Scvb1LLY6f4qDsThqyiID/NXM5LtjnztRP+9dCLT9DdwC+LeU2te6vc\nEwC2bPYs3nUwkZL4qqIWO6GHaavngFKSmbmc9muVAPmYW/ffMtznKZaWCp9Li0JG\nuQIDAQAB\n-----END PUBLIC KEY-----"
  },
  {
    "path": "examples/csv-report/node.js",
    "content": "const { Suite } = require('../../lib');\nconst assert = require('node:assert');\n\nconst suite = new Suite();\n\nsuite\n  .add('single with matcher', function () {\n    const pattern = /[123]/g\n    const replacements = { 1: 'a', 2: 'b', 3: 'c' }\n    const subject = '123123123123123123123123123123123123123123123123'\n    const r = subject.replace(pattern, m => replacements[m])\n    assert.ok(r);\n  })\n  .add('multiple replaces', function () {\n    const subject = '123123123123123123123123123123123123123123123123'\n    const r = subject.replace(/1/g, 'a').replace(/2/g, 'b').replace(/3/g, 'c')\n    assert.ok(r);\n  })\n  .run();\n"
  },
  {
    "path": "examples/dce-detection/example.js",
    "content": "const { Suite } = require(\"../../lib/index\");\n\n// Enable DCE detection to catch benchmarks that may be optimized away\nconst suite = new Suite({\n\tdetectDeadCodeElimination: true,\n});\n\n// Example 1: Likely to trigger DCE warning - result not used\nsuite.add(\"simple addition (likely DCE)\", () => {\n\tconst result = 1 + 1;\n\t// result is never used - JIT might optimize this away\n});\n\n// Example 2: Result is used - should not trigger warning\nsuite.add(\"simple addition (used)\", () => {\n\tconst result = 1 + 1;\n\tif (result !== 2) throw new Error(\"Unexpected result\");\n});\n\n// Example 3: Array creation without use - likely DCE\nsuite.add(\"array creation (likely DCE)\", () => {\n\tconst arr = new Array(100);\n\t// arr is never accessed - might be optimized away\n});\n\n// Example 4: Array creation with access - should not trigger warning\nsuite.add(\"array creation (accessed)\", () => {\n\tconst arr = new Array(100);\n\tarr[0] = 1; // Using the array\n});\n\n// Example 5: Object creation without use - likely DCE\nsuite.add(\"object creation (likely DCE)\", () => {\n\tconst obj = { x: 1, y: 2, z: 3 };\n\t// obj is never accessed\n});\n\n// Example 6: More realistic computation\nsuite.add(\"string operations\", () => {\n\tconst str1 = \"hello\";\n\tconst str2 = \"world\";\n\tconst result = str1 + \" \" + str2;\n\tif (!result.includes(\"hello\")) throw new Error(\"Missing hello\");\n});\n\nsuite.run();\n"
  },
  {
    "path": "examples/dce-detection/with-dce-disabled.js",
    "content": "const { Suite } = require(\"../../lib/index\");\n\n// Default behavior - DCE detection is disabled, V8NeverOptimizePlugin is used\n// You don't need to set detectDeadCodeElimination: false explicitly\nconst suite = new Suite();\n\n// These benchmarks will run with V8NeverOptimizePlugin, so they'll be slower\n// but more deterministic and won't be optimized away\nsuite.add(\"simple addition\", () => {\n\tconst result = 1 + 1;\n});\n\nsuite.add(\"array creation\", () => {\n\tconst arr = new Array(100);\n});\n\nsuite.run();\n"
  },
  {
    "path": "examples/dce-detection/without-never-optimize.js",
    "content": "const { Suite } = require(\"../../lib/index\");\n\n// Enable DCE detection - this automatically disables V8NeverOptimizePlugin\n// In this mode, V8 optimizations occur naturally and DCE warnings help identify issues\nconst suite = new Suite({\n\tdetectDeadCodeElimination: true,\n});\n\n// Example 1: Likely to trigger DCE warning - result not used\nsuite.add(\"simple addition (likely DCE)\", () => {\n\tconst result = 1 + 1;\n\t// result is never used - JIT will optimize this away\n});\n\n// Example 2: Result is used - should not trigger warning\nsuite.add(\"simple addition (used)\", () => {\n\tconst result = 1 + 1;\n\tif (result !== 2) throw new Error(\"Unexpected result\");\n});\n\n// Example 3: Array creation without use - likely DCE\nsuite.add(\"array creation (likely DCE)\", () => {\n\tconst arr = new Array(100);\n\t// arr is never accessed - will be optimized away\n});\n\n// Example 4: Array creation with access - should not trigger warning\nsuite.add(\"array creation (accessed)\", () => {\n\tconst arr = new Array(100);\n\tarr[0] = 1; // Using the array\n});\n\n// Example 5: More realistic computation that takes time\nsuite.add(\"actual work\", () => {\n\tlet sum = 0;\n\tfor (let i = 0; i < 100; i++) {\n\t\tsum += Math.sqrt(i);\n\t}\n\tif (sum < 0) throw new Error(\"Impossible\");\n});\n\nsuite.run();\n"
  },
  {
    "path": "examples/deleting-properties/node.js",
    "content": "const { Suite } = require('../../lib');\n\nconst suite = new Suite();\n\nconst NullObject = function NullObject() { }\nNullObject.prototype = Object.create(null);\n%NeverOptimizeFunction(NullObject);\n\nsuite\n  .add('Using delete property', function () {\n    const data = { x: 1, y: 2, z: 3 }\n    delete data.y\n\n    data.x\n    data.y\n    data.z\n  })\n  .add('Using delete property (proto: null)', function () {\n    const data = { __proto__: null, x: 1, y: 2, z: 3 }\n    delete data.y\n\n    data.x\n    data.y\n    data.z\n  })\n  .add('Using delete property (cached proto: null)', function () {\n    const data = new NullObject()\n\n    data.x = 1\n    data.y = 2\n    data.z = 3\n\n    delete data.y\n\n    data.x\n    data.y\n    data.z\n  })\n  .add('Using undefined assignment', function () {\n    const data = { x: 1, y: 2, z: 3 }\n    data.y = undefined\n\n    data.x\n    data.y\n    data.z\n  })\n  .add('Using undefined assignment (proto: null)', function () {\n    const data = { __proto__: null, x: 1, y: 2, z: 3 }\n    data.y = undefined\n\n    data.x\n    data.y\n    data.z\n  })\n  .add('Using undefined property (cached proto: null)', function () {\n    const data = new NullObject()\n\n    data.x = 1\n    data.y = 2\n    data.z = 3\n\n    data.y = undefined\n\n    data.x\n    data.y\n    data.z\n  })\n  .add('[Managed] Using undefined property (cached proto: null)', function (t) {\n    const NullObject = function () { }\n    NullObject.prototype = Object.create(null)\n\n    t.start();\n    for (let i = 0; i < t.count; i++) {\n      const data = new NullObject()\n\n      data.x = 1\n      data.y = 2\n      data.z = 3\n\n      data.y = undefined\n\n      data.x\n      data.y\n      data.z\n    }\n    t.end(t.count);\n  })\n  .run();\n"
  },
  {
    "path": "examples/deleting-properties/node.js.log",
    "content": "Using delete property x 7,763,866 ops/sec (11 runs sampled) v8-never-optimize=true min..max=(128.11ns ... 129.99ns) p75=129.03ns p99=129.99ns\nUsing delete property (proto: null) x 22,946,068 ops/sec (10 runs sampled) v8-never-optimize=true min..max=(43.13ns ... 44.11ns) p75=43.74ns p99=44.11ns\nUsing delete property (cached proto: null) x 7,331,951 ops/sec (11 runs sampled) v8-never-optimize=true min..max=(135.64ns ... 137.39ns) p75=136.52ns p99=137.39ns\nUsing undefined assignment x 133,449,849 ops/sec (11 runs sampled) v8-never-optimize=true min..max=(7.45ns ... 7.56ns) p75=7.53ns p99=7.56ns\nUsing undefined assignment (proto: null) x 25,432,632 ops/sec (11 runs sampled) v8-never-optimize=true min..max=(38.72ns ... 40.06ns) p75=39.60ns p99=40.06ns\nUsing undefined property (cached proto: null) x 58,871,059 ops/sec (11 runs sampled) v8-never-optimize=true min..max=(16.92ns ... 17.12ns) p75=17.01ns p99=17.12ns\n[Managed] Using undefined property (cached proto: null) x 35,463,712 ops/sec (10 runs sampled) v8-never-optimize=true min..max=(28.05ns ... 28.43ns) p75=28.25ns p99=28.43ns\n----------------------------------------------------------------------------\nUsing delete property x 6,669,856 ops/sec (10 runs sampled) v8-never-optimize=true min..max=(149.14ns ... 151.02ns) p75=150.12ns p99=151.02ns\nUsing delete property (proto: null) x 15,242,131 ops/sec (11 runs sampled) v8-never-optimize=true min..max=(64.77ns ... 66.38ns) p75=66.25ns p99=66.38ns\nUsing delete property (cached proto: null) x 5,920,843 ops/sec (11 runs sampled) v8-never-optimize=true min..max=(167.87ns ... 170.33ns) p75=169.07ns p99=170.33ns\nUsing undefined assignment x 78,647,245 ops/sec (12 runs sampled) v8-never-optimize=true min..max=(12.62ns ... 12.83ns) p75=12.70ns p99=12.83ns\nUsing undefined assignment (proto: null) x 16,278,910 ops/sec (11 runs sampled) v8-never-optimize=true min..max=(60.79ns ... 62.25ns) p75=61.57ns p99=62.25ns\nUsing undefined property (cached proto: null) x 27,262,884 ops/sec (10 runs sampled) v8-never-optimize=true min..max=(36.54ns ... 36.65ns) p75=36.62ns p99=36.65ns\n[Managed] Using undefined property (cached proto: null) x 28,068,785 ops/sec (12 runs sampled) v8-never-optimize=true min..max=(35.07ns ... 35.79ns) p75=35.67ns p99=35.79ns\n----------------------------------------------------------------------------\nUsing delete property x 7,632,095 ops/sec (11 runs sampled) v8-never-optimize=true min..max=(129.63ns ... 132.85ns) p75=131.19ns p99=132.85ns\nUsing delete property (proto: null) x 23,040,357 ops/sec (11 runs sampled) v8-never-optimize=true min..max=(43.06ns ... 44.20ns) p75=43.56ns p99=44.20ns\nUsing delete property (cached proto: null) x 7,222,103 ops/sec (11 runs sampled) v8-never-optimize=true min..max=(137.05ns ... 139.88ns) p75=139.18ns p99=139.88ns\nUsing undefined assignment x 133,133,677 ops/sec (10 runs sampled) v8-never-optimize=true min..max=(7.49ns ... 7.53ns) p75=7.52ns p99=7.53ns\nUsing undefined assignment (proto: null) x 25,217,884 ops/sec (11 runs sampled) v8-never-optimize=true min..max=(39.24ns ... 40.48ns) p75=39.89ns p99=40.48ns\nUsing undefined property (cached proto: null) x 58,899,972 ops/sec (11 runs sampled) v8-never-optimize=true min..max=(16.94ns ... 17.03ns) p75=16.99ns p99=17.03ns\n[Managed] Using undefined property (cached proto: null) x 34,904,120 ops/sec (11 runs sampled) v8-never-optimize=true min..max=(28.34ns ... 29.89ns) p75=28.84ns p99=29.89ns\n----------------------------------------------------------------------------\n"
  },
  {
    "path": "examples/empty/node.js",
    "content": "const { Suite } = require('../../lib');\n\nconst suite = new Suite();\n\nsuite\n  .add(`empty`, function () {})\n  .add(`empty async`, async function () {})\n  .run();\n"
  },
  {
    "path": "examples/empty/node.js.log",
    "content": "empty x 218,984,985 ops/sec (12 runs sampled) v8-never-optimize=true min..max=(4.53ns ... 4.66ns) p75=4.58ns p99=4.66ns\nempty async x 20,898,403 ops/sec (9 runs sampled) v8-never-optimize=true min..max=(47.46ns ... 48.57ns) p75=48.04ns p99=48.57ns\n----------------------------------------------------------------------------\nempty x 220,571,877 ops/sec (12 runs sampled) v8-never-optimize=true min..max=(4.49ns ... 4.61ns) p75=4.58ns p99=4.61ns\nempty async x 20,925,022 ops/sec (14 runs sampled) v8-never-optimize=true min..max=(37.43ns ... 48.60ns) p75=48.03ns p99=48.60ns\n----------------------------------------------------------------------------\nempty x 218,923,690 ops/sec (11 runs sampled) v8-never-optimize=true min..max=(4.52ns ... 4.64ns) p75=4.59ns p99=4.64ns\nempty async x 20,792,931 ops/sec (12 runs sampled) v8-never-optimize=true min..max=(42.94ns ... 49.45ns) p75=48.54ns p99=49.45ns\n----------------------------------------------------------------------------\n"
  },
  {
    "path": "examples/fs-read-async/node.js",
    "content": "const { Suite } = require('../../lib');\nconst { readFile } = require('node:fs/promises');\nconst { resolve } = require('node:path');\n\nconst suite = new Suite();\n\nconst sampleFile = resolve(__dirname, 'sample-file.txt');\n\nsuite\n  .add('readFile', async function () {\n    const r = await readFile(sampleFile);\n  })\n  .add('readFile utf-8', async function () {\n    const r = await readFile(sampleFile, 'utf-8');\n  })\n  .add('[managed] readFile', async function (timer) {\n    const { readFile } = require('node:fs/promises');\n    const { resolve } = require('node:path');\n    const assert = require('node:assert');\n\n    const sampleFile = resolve(__dirname, 'sample-file.txt');\n    let r;\n\n    timer.start();\n    for (let i = 0; i < timer.count; i++) {\n      r = await readFile(sampleFile);\n    }\n    timer.end(timer.count);\n\n    assert.ok(r);\n  })\n  .add('[managed] readFile utf-8', async function (timer) {\n    const { readFile } = require('node:fs/promises');\n    const { resolve } = require('node:path');\n    const assert = require('node:assert');\n\n    const sampleFile = resolve(__dirname, 'sample-file.txt');\n    let r;\n\n    timer.start();\n    for (let i = 0; i < timer.count; i++) {\n      r = await readFile(sampleFile, 'utf-8');\n    }\n    timer.end(timer.count);\n\n    assert.ok(r);\n  })\n  .run();\n"
  },
  {
    "path": "examples/fs-read-async/node.js.log",
    "content": "readFile x 23,167 ops/sec (11 runs sampled) v8-never-optimize=true min..max=(42.59us ... 43.59us) p75=43.42us p99=43.59us\nreadFile utf-8 x 23,021 ops/sec (11 runs sampled) v8-never-optimize=true min..max=(43.14us ... 43.92us) p75=43.64us p99=43.92us\n[managed] readFile x 23,386 ops/sec (11 runs sampled) v8-never-optimize=true min..max=(42.63us ... 43.04us) p75=42.89us p99=43.04us\n[managed] readFile utf-8 x 23,302 ops/sec (10 runs sampled) v8-never-optimize=true min..max=(42.76us ... 43.30us) p75=43.01us p99=43.30us\n----------------------------------------------------------------------------\nreadFile x 23,177 ops/sec (10 runs sampled) v8-never-optimize=true min..max=(42.70us ... 43.54us) p75=43.31us p99=43.54us\nreadFile utf-8 x 23,155 ops/sec (10 runs sampled) v8-never-optimize=true min..max=(42.34us ... 43.53us) p75=43.15us p99=43.53us\n[managed] readFile x 23,538 ops/sec (11 runs sampled) v8-never-optimize=true min..max=(42.22us ... 42.82us) p75=42.59us p99=42.82us\n[managed] readFile utf-8 x 23,458 ops/sec (11 runs sampled) v8-never-optimize=true min..max=(42.11us ... 43.20us) p75=42.86us p99=43.20us\n----------------------------------------------------------------------------\nreadFile x 23,235 ops/sec (11 runs sampled) v8-never-optimize=true min..max=(42.73us ... 43.41us) p75=43.17us p99=43.41us\nreadFile utf-8 x 23,130 ops/sec (11 runs sampled) v8-never-optimize=true min..max=(42.90us ... 44.40us) p75=43.62us p99=44.40us\n[managed] readFile x 23,490 ops/sec (11 runs sampled) v8-never-optimize=true min..max=(42.40us ... 42.89us) p75=42.66us p99=42.89us\n[managed] readFile utf-8 x 23,553 ops/sec (11 runs sampled) v8-never-optimize=true min..max=(41.99us ... 42.81us) p75=42.58us p99=42.81us\n----------------------------------------------------------------------------\n"
  },
  {
    "path": "examples/fs-read-async/node.managed.js",
    "content": "const { Suite } = require('../../lib');\n\nconst suite = new Suite();\n\nsuite\n  .add('readFile', async function (timer) {\n    const { readFile } = require('node:fs/promises');\n    const { resolve } = require('node:path');\n    const assert = require('node:assert');\n\n    const sampleFile = resolve(__dirname, 'sample-file.txt');\n    let r;\n\n    timer.start();\n    for (let i = 0; i < timer.count; i++) {\n      r = await readFile(sampleFile);\n    }\n    timer.end(timer.count);\n\n    assert.ok(r);\n  })\n  .add('readFile utf-8', async function (timer) {\n    const { readFile } = require('node:fs/promises');\n    const { resolve } = require('node:path');\n    const assert = require('node:assert');\n\n    const sampleFile = resolve(__dirname, 'sample-file.txt');\n    let r;\n\n    timer.start();\n    for (let i = 0; i < timer.count; i++) {\n      r = await readFile(sampleFile, 'utf-8');\n    }\n    timer.end(timer.count);\n\n    assert.ok(r);\n  })\n  .run();\n"
  },
  {
    "path": "examples/fs-read-async/node.managed.js.log",
    "content": "readFile x 23,066 ops/sec (10 runs sampled) v8-never-optimize=true min..max=(42.59us ... 43.63us) p75=43.20us p99=43.63us\nreadFile utf-8 x 23,334 ops/sec (11 runs sampled) v8-never-optimize=true min..max=(42.21us ... 43.70us) p75=42.96us p99=43.70us\n----------------------------------------------------------------------------\nreadFile x 23,145 ops/sec (10 runs sampled) v8-never-optimize=true min..max=(42.64us ... 43.18us) p75=43.04us p99=43.18us\nreadFile utf-8 x 23,324 ops/sec (10 runs sampled) v8-never-optimize=true min..max=(42.54us ... 43.80us) p75=42.93us p99=43.80us\n----------------------------------------------------------------------------\nreadFile x 23,124 ops/sec (10 runs sampled) v8-never-optimize=true min..max=(42.36us ... 43.30us) p75=42.99us p99=43.30us\nreadFile utf-8 x 23,277 ops/sec (12 runs sampled) v8-never-optimize=true min..max=(41.50us ... 43.88us) p75=42.98us p99=43.88us\n----------------------------------------------------------------------------\n"
  },
  {
    "path": "examples/fs-read-async/sample-file.txt",
    "content": "hello"
  },
  {
    "path": "examples/fs-read-sync/node.js",
    "content": "const { Suite } = require('../../lib');\nconst { readFileSync } = require('node:fs');\nconst { resolve } = require('node:path');\n\nconst suite = new Suite();\n\nconst sampleFile = resolve(__dirname, 'sample-file.txt');\n\nsuite\n  .add('readFileSync', function () {\n    const r = readFileSync(sampleFile);\n  })\n  .add('readFileSync utf-8', function () {\n    const r = readFileSync(sampleFile, 'utf-8');\n  })\n  .add('[Managed] readFileSync', function (timer) {\n    const { readFileSync } = require('node:fs');\n    const { resolve } = require('node:path');\n    const assert = require('node:assert');\n\n    const sampleFile = resolve(__dirname, 'sample-file.txt');\n    let r;\n\n    timer.start();\n    for (let i = 0; i < timer.count; i++) {\n      r = readFileSync(sampleFile);\n    }\n    timer.end(timer.count);\n\n    assert.ok(r);\n  })\n  .add('[Managed] readFileSync utf-8', function (timer) {\n    const { readFileSync } = require('node:fs');\n    const { resolve } = require('node:path');\n    const assert = require('node:assert');\n\n    const sampleFile = resolve(__dirname, 'sample-file.txt');\n    let r;\n\n    timer.start();\n    for (let i = 0; i < timer.count; i++) {\n      r = readFileSync(sampleFile, 'utf-8');\n    }\n    timer.end(timer.count);\n\n    assert.ok(r);\n  })\n\n  .run();\n"
  },
  {
    "path": "examples/fs-read-sync/node.js.log",
    "content": "readFileSync x 198,828 ops/sec (11 runs sampled) v8-never-optimize=true min..max=(5.01us ... 5.06us) p75=5.04us p99=5.06us\nreadFileSync utf-8 x 203,828 ops/sec (11 runs sampled) v8-never-optimize=true min..max=(4.88us ... 4.94us) p75=4.92us p99=4.94us\n[Managed] readFileSync x 196,632 ops/sec (10 runs sampled) v8-never-optimize=true min..max=(5.05us ... 5.08us) p75=5.07us p99=5.08us\n[Managed] readFileSync utf-8 x 203,265 ops/sec (9 runs sampled) v8-never-optimize=true min..max=(4.89us ... 4.92us) p75=4.91us p99=4.92us\n----------------------------------------------------------------------------\nreadFileSync x 198,461 ops/sec (9 runs sampled) v8-never-optimize=true min..max=(5.00us ... 5.05us) p75=5.02us p99=5.05us\nreadFileSync utf-8 x 205,171 ops/sec (10 runs sampled) v8-never-optimize=true min..max=(4.82us ... 4.86us) p75=4.86us p99=4.86us\n[Managed] readFileSync x 193,061 ops/sec (11 runs sampled) v8-never-optimize=true min..max=(5.05us ... 5.48us) p75=5.30us p99=5.48us\n[Managed] readFileSync utf-8 x 204,046 ops/sec (10 runs sampled) v8-never-optimize=true min..max=(4.86us ... 4.92us) p75=4.91us p99=4.92us\n----------------------------------------------------------------------------\nreadFileSync x 196,533 ops/sec (11 runs sampled) v8-never-optimize=true min..max=(5.02us ... 5.20us) p75=5.10us p99=5.20us\nreadFileSync utf-8 x 205,213 ops/sec (10 runs sampled) v8-never-optimize=true min..max=(4.83us ... 4.89us) p75=4.86us p99=4.89us\n[Managed] readFileSync x 183,333 ops/sec (12 runs sampled) v8-never-optimize=true min..max=(5.06us ... 6.46us) p75=5.28us p99=6.46us\n[Managed] readFileSync utf-8 x 201,593 ops/sec (11 runs sampled) v8-never-optimize=true min..max=(4.90us ... 5.07us) p75=5.06us p99=5.07us\n----------------------------------------------------------------------------\n"
  },
  {
    "path": "examples/fs-read-sync/sample-file.txt",
    "content": "hello"
  },
  {
    "path": "examples/html-report/node.js",
    "content": "const { Suite, htmlReport } = require('../../lib');\nconst assert = require('node:assert');\n\nconst suite = new Suite({\n  reporter: htmlReport,\n});\n\nsuite\n  .add('single with matcher', function () {\n    const pattern = /[123]/g\n    const replacements = { 1: 'a', 2: 'b', 3: 'c' }\n    const subject = '123123123123123123123123123123123123123123123123'\n    const r = subject.replace(pattern, m => replacements[m])\n    assert.ok(r);\n  })\n  .add('Multiple replaces', function () {\n    const subject = '123123123123123123123123123123123123123123123123'\n    const r = subject.replace(/1/g, 'a').replace(/2/g, 'b').replace(/3/g, 'c')\n    assert.ok(r);\n  })\n  .run();\n"
  },
  {
    "path": "examples/html-report/result.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n  <meta charset=\"UTF-8\">\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n  <title>Benchmark Visualizer</title>\n  <style>\n    body {\n      display: flex;\n      flex-direction: column;\n      justify-content: center;\n      align-items: center;\n      height: 100vh;\n      margin: 0;\n      background: #f4f4f4;\n      font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;\n    }\n    .env {\n      width: 400px;\n    }\n\n    .container {\n      position: relative;\n      width: 400px; /* Reduced width */\n      height: 200px;\n      border: 2px solid #000;\n      background: #fff;\n    }\n\n    .label {\n      position: absolute;\n      left: -130px; /* Place labels outside the box */\n      width: 100px;\n      text-align: right;\n      font-size: 14px;\n    }\n\n    .circle {\n      width: 40px;\n      height: 40px;\n      border-radius: 50%;\n      position: absolute;\n    }\n\n    .number {\n      font-family: 'Times New Roman', Times, serif;\n    }\n\n    .made-with {\n      position: fixed;\n      bottom: 10px;\n      right: 10px;\n      padding: 10px;\n      font-size: 12px;\n    }\n    .made-with a {\n      color: #2C682C;\n    }\n    .made-with a:hover {\n      text-decoration: none;\n    }\n\n    \n      #label-single-with-matcher {\n        top: 20px;\n      }\n\n      #circle-single-with-matcher {\n        background-color: blue;\n        top: 20px;\n      }\n    \n      #label-Multiple-replaces {\n        top: 100px;\n      }\n\n      #circle-Multiple-replaces {\n        background-color: orange;\n        top: 100px;\n      }\n    \n\n    /* Dark theme */\n    @media (prefers-color-scheme: dark) {\n      body {\n        background: #333;\n        color: #fff;\n      }\n\n      .container {\n        border-color: #fff;\n        background: #444;\n      }\n\n      .made-with a {\n        color: #84BA64;\n      }\n    }\n  </style>\n</head>\n<body>\n  <div class=\"env\">\n    <p>Node.js version: <span class=\"number\">v20.18.1</span></p>\n\t<p>Platform: <span class=\"number\">darwin arm64</span></p>\n\t<p>CPU Cores: <span class=\"number\">8</span> vCPUs | <span class=\"number\">16.0GB Mem</span></p>\n  </div>  \n\n  <div class=\"container\">\n    \n      <div id=\"circle-single-with-matcher\" class=\"circle\"></div>\n    \n      <div id=\"circle-Multiple-replaces\" class=\"circle\"></div>\n    \n\n    \n      <div id=\"label-single-with-matcher\" class=\"label\">\n\t  single-with-matcher(<span class=\"number\">660,788.4</span> ops/sec)\n\t  </div>\n    \n      <div id=\"label-Multiple-replaces\" class=\"label\">\n\t  Multiple-replaces(<span class=\"number\">578,527</span> ops/sec)\n\t  </div>\n    \n  </div>\n\n  <div class=\"made-with\">\n    <p>Benchmark done with <a href=\"https://github.com/RafaelGSS/bench-node\">bench-node</a></p>\n  </div>\n\n  </div>\n\n  <script>\n    const durations = [{\"name\":\"single-with-matcher\",\"duration\":10,\"opsSecFormatted\":\"660,788.4\"},{\"name\":\"Multiple-replaces\",\"duration\":11.421911109342416,\"opsSecFormatted\":\"578,527\"}];\n\n    const animateCircles = () => {\n      const boxWidth = 400; // Width of the container box\n      const circles = durations.map((d) => ({\n        id: \"circle-\" + d.name,\n        duration: d.duration,\n        position: 0,\n        direction: 1,\n      }));\n\n      const update = () => {\n        circles.forEach(circle => {\n          const element = document.getElementById(circle.id);\n\n          circle.position += circle.direction * (boxWidth / circle.duration) * 0.5;\n\n          if (circle.position >= boxWidth - 20 || circle.position <= 0) {\n            circle.direction *= -1; // Reverse direction on collision\n          }\n\n          element.style.transform = `translateX(${circle.position}px)`;\n        });\n\n        setTimeout(() => {\n          requestAnimationFrame(update);\n        }, 1000 / 120); // \"60 FPS\"\n      };\n\n      update();\n    };\n\n    animateCircles();\n  </script>\n</body>\n</html>\n"
  },
  {
    "path": "examples/json-report/node.js",
    "content": "const { Suite, jsonReport } = require('../../lib');\nconst assert = require('node:assert');\n\nconst suite = new Suite({\n  reporter: jsonReport,\n});\n\nsuite\n  .add('single with matcher', function () {\n    const pattern = /[123]/g\n    const replacements = { 1: 'a', 2: 'b', 3: 'c' }\n    const subject = '123123123123123123123123123123123123123123123123'\n    const r = subject.replace(pattern, m => replacements[m])\n    assert.ok(r);\n  })\n  .add('Multiple replaces', function () {\n    const subject = '123123123123123123123123123123123123123123123123'\n    const r = subject.replace(/1/g, 'a').replace(/2/g, 'b').replace(/3/g, 'c')\n    assert.ok(r);\n  })\n  .run();\n"
  },
  {
    "path": "examples/plugins/all.js",
    "content": "const {\n  Suite,\n  V8GetOptimizationStatus,\n  V8NeverOptimizePlugin,\n  V8OptimizeOnNextCallPlugin,\n  MemoryPlugin,\n} = require('../../lib');\n\nconst suite = new Suite({\n  plugins: [\n    new V8GetOptimizationStatus(),\n    new V8NeverOptimizePlugin(),\n    new MemoryPlugin(),\n    new V8OptimizeOnNextCallPlugin(),\n  ],\n});\n\nsuite\n  .add(`new Uint32Array(1024)`, function () {\n    return new Uint32Array(1024);\n  })\n  .add(`[Managed] new Uint32Array(1024)`, function (timer) {\n    const assert = require('node:assert');\n\n    let r;\n\n    timer.start();\n    for (let i = 0; i < timer.count; i++) {\n      r = new Uint32Array(1024);\n    }\n    timer.end(timer.count);\n\n    assert.ok(r);\n  })\n  .run();\n"
  },
  {
    "path": "examples/plugins/all.js.log",
    "content": "new Uint32Array(1024) x 2,395,395 ops/sec (14 runs sampled) v8-opt-status=\"Optimized, Interpreted, Marked for Optimization, Concurrently Optimizing\" v8-never-optimize=true min..max=(302.80ns ... 435.20ns) p75=420.50ns p99=435.20ns\n[Managed] new Uint32Array(1024) x 1,878,469 ops/sec (12 runs sampled) v8-opt-status=\"Optimized, Interpreted, Marked for Optimization, Concurrently Optimizing\" v8-never-optimize=true min..max=(217.82ns ... 577.91ns) p75=430.46ns p99=577.91ns\n----------------------------------------------------------------------------\nnew Uint32Array(1024) x 2,282,799 ops/sec (13 runs sampled) v8-opt-status=\"Optimized, Interpreted, Marked for Optimization, Concurrently Optimizing\" v8-never-optimize=true min..max=(339.06ns ... 479.78ns) p75=449.29ns p99=479.78ns\n[Managed] new Uint32Array(1024) x 2,219,660 ops/sec (10 runs sampled) v8-opt-status=\"Optimized, Interpreted, Marked for Optimization, Concurrently Optimizing\" v8-never-optimize=true min..max=(383.40ns ... 548.65ns) p75=461.81ns p99=548.65ns\n----------------------------------------------------------------------------\nnew Uint32Array(1024) x 2,362,479 ops/sec (11 runs sampled) v8-opt-status=\"Optimized, Interpreted, Marked for Optimization, Concurrently Optimizing\" v8-never-optimize=true min..max=(384.64ns ... 456.35ns) p75=440.57ns p99=456.35ns\n[Managed] new Uint32Array(1024) x 2,272,352 ops/sec (10 runs sampled) v8-opt-status=\"Optimized, Interpreted, Marked for Optimization, Concurrently Optimizing\" v8-never-optimize=true min..max=(421.95ns ... 451.89ns) p75=443.17ns p99=451.89ns\n----------------------------------------------------------------------------\n"
  },
  {
    "path": "examples/plugins/memory.js",
    "content": "const { Suite, MemoryPlugin} = require('../../lib');\n\nconst suite = new Suite({\n    plugins: [new MemoryPlugin()],\n});\n\nsuite\n    .add(`new Uint32Array(1024)`, function () {\n        return new Uint32Array(1024);\n    })\n    .add(`[Managed] new Uint32Array(1024)`, function (timer) {\n        const assert = require('node:assert');\n\n        let r;\n\n        timer.start();\n        for (let i = 0; i < timer.count; i++) {\n            r = new Uint32Array(1024);\n        }\n        timer.end(timer.count);\n\n        assert.ok(r);\n    })\n    .run();\n"
  },
  {
    "path": "examples/plugins/v8-get-opt-status.js",
    "content": "const { Suite, V8GetOptimizationStatus } = require('../../lib');\n\nconst suite = new Suite({\n  plugins: [new V8GetOptimizationStatus()],\n});\n\nsuite\n  .add(`new Uint32Array(1024)`, function () {\n    return new Uint32Array(1024);\n  })\n  .add(`[Managed] new Uint32Array(1024)`, function (timer) {\n    const assert = require('node:assert');\n\n    let r;\n\n    timer.start();\n    for (let i = 0; i < timer.count; i++) {\n      r = new Uint32Array(1024);\n    }\n    timer.end(timer.count);\n\n    assert.ok(r);\n  })\n  .run();\n"
  },
  {
    "path": "examples/plugins/v8-get-opt-status.js.log",
    "content": "new Uint32Array(1024) x 2,353,498 ops/sec (10 runs sampled) v8-opt-status=\"Optimized, Interpreted, Marked for Optimization, Concurrently Optimizing\" min..max=(387.20ns ... 435.11ns) p75=426.25ns p99=435.11ns\n[Managed] new Uint32Array(1024) x 2,295,396 ops/sec (12 runs sampled) v8-opt-status=\"Optimized, Interpreted, Marked for Optimization, Concurrently Optimizing\" min..max=(346.78ns ... 471.27ns) p75=450.57ns p99=471.27ns\n----------------------------------------------------------------------------\nnew Uint32Array(1024) x 2,206,492 ops/sec (11 runs sampled) v8-opt-status=\"Optimized, Interpreted, Marked for Optimization, Concurrently Optimizing\" min..max=(413.34ns ... 498.95ns) p75=457.27ns p99=498.95ns\n[Managed] new Uint32Array(1024) x 2,308,235 ops/sec (10 runs sampled) v8-opt-status=\"Optimized, Interpreted, Marked for Optimization, Concurrently Optimizing\" min..max=(410.95ns ... 472.38ns) p75=444.99ns p99=472.38ns\n----------------------------------------------------------------------------\nnew Uint32Array(1024) x 2,765,470 ops/sec (11 runs sampled) v8-opt-status=\"Optimized, Interpreted, Marked for Optimization, Concurrently Optimizing\" min..max=(343.99ns ... 396.13ns) p75=386.55ns p99=396.13ns\n[Managed] new Uint32Array(1024) x 2,773,144 ops/sec (11 runs sampled) v8-opt-status=\"Optimized, Interpreted, Marked for Optimization, Concurrently Optimizing\" min..max=(351.75ns ... 377.92ns) p75=369.54ns p99=377.92ns\n----------------------------------------------------------------------------\n"
  },
  {
    "path": "examples/plugins/v8-never-optimize.js",
    "content": "const { Suite, V8NeverOptimizePlugin } = require('../../lib');\n\nconst suite = new Suite({\n  plugins: [new V8NeverOptimizePlugin()],\n});\n\nsuite\n  .add(`new Uint32Array(1024)`, function () {\n    return new Uint32Array(1024);\n  })\n  .add(`[Managed] new Uint32Array(1024)`, function (timer) {\n    const assert = require('node:assert');\n\n    let r;\n\n    timer.start();\n    for (let i = 0; i < timer.count; i++) {\n      r = new Uint32Array(1024);\n    }\n    timer.end(timer.count);\n\n    assert.ok(r);\n  })\n  .run();\n"
  },
  {
    "path": "examples/plugins/v8-never-optimize.js.log",
    "content": "new Uint32Array(1024) x 2,535,349 ops/sec (11 runs sampled) v8-never-optimize=true min..max=(357.30ns ... 442.55ns) p75=416.14ns p99=442.55ns\n[Managed] new Uint32Array(1024) x 2,646,454 ops/sec (12 runs sampled) v8-never-optimize=true min..max=(356.81ns ... 405.88ns) p75=382.20ns p99=405.88ns\n----------------------------------------------------------------------------\nnew Uint32Array(1024) x 2,785,937 ops/sec (12 runs sampled) v8-never-optimize=true min..max=(350.61ns ... 368.86ns) p75=360.60ns p99=368.86ns\n[Managed] new Uint32Array(1024) x 2,535,974 ops/sec (10 runs sampled) v8-never-optimize=true min..max=(353.44ns ... 462.38ns) p75=447.16ns p99=462.38ns\n----------------------------------------------------------------------------\nnew Uint32Array(1024) x 2,336,195 ops/sec (9 runs sampled) v8-never-optimize=true min..max=(380.49ns ... 453.11ns) p75=431.40ns p99=453.11ns\n[Managed] new Uint32Array(1024) x 2,624,328 ops/sec (10 runs sampled) v8-never-optimize=true min..max=(341.92ns ... 432.82ns) p75=411.22ns p99=432.82ns\n----------------------------------------------------------------------------\n"
  },
  {
    "path": "examples/plugins/v8-optimize-next-call.js",
    "content": "const { Suite, V8OptimizeOnNextCallPlugin } = require('../../lib');\n\nconst suite = new Suite({\n  plugins: [new V8OptimizeOnNextCallPlugin()],\n});\n\nsuite\n  .add(`new Uint32Array(1024)`, function () {\n    return new Uint32Array(1024);\n  })\n  .add(`[Managed] new Uint32Array(1024)`, function (timer) {\n    const assert = require('node:assert');\n\n    let r;\n\n    timer.start();\n    for (let i = 0; i < timer.count; i++) {\n      r = new Uint32Array(1024);\n    }\n    timer.end(timer.count);\n\n    assert.ok(r);\n  })\n  .run();\n"
  },
  {
    "path": "examples/plugins/v8-optimize-next-call.js.log",
    "content": "new Uint32Array(1024) x 2,812,339 ops/sec (13 runs sampled) v8-optimize-next-call=enabled min..max=(314.34ns ... 373.62ns) p75=359.22ns p99=373.62ns\n[Managed] new Uint32Array(1024) x 2,763,965 ops/sec (11 runs sampled) v8-optimize-next-call=enabled min..max=(348.80ns ... 381.77ns) p75=373.99ns p99=381.77ns\n----------------------------------------------------------------------------\nnew Uint32Array(1024) x 2,827,020 ops/sec (11 runs sampled) v8-optimize-next-call=enabled min..max=(298.24ns ... 365.66ns) p75=354.33ns p99=365.66ns\n[Managed] new Uint32Array(1024) x 2,775,090 ops/sec (12 runs sampled) v8-optimize-next-call=enabled min..max=(349.78ns ... 378.17ns) p75=360.17ns p99=378.17ns\n----------------------------------------------------------------------------\nnew Uint32Array(1024) x 2,793,090 ops/sec (9 runs sampled) v8-optimize-next-call=enabled min..max=(342.17ns ... 352.58ns) p75=350.66ns p99=352.58ns\n[Managed] new Uint32Array(1024) x 2,751,982 ops/sec (10 runs sampled) v8-optimize-next-call=enabled min..max=(352.72ns ... 385.30ns) p75=367.30ns p99=385.30ns\n----------------------------------------------------------------------------\n"
  },
  {
    "path": "examples/pretty-report/node.js",
    "content": "const { Suite, prettyReport } = require('../../lib');\nconst assert = require('node:assert');\n\nconst suite = new Suite({\n  reporter: prettyReport,\n});\n\nsuite\n  .add('my-group/my-benchmark', () => {\n    // A slower benchmark\n    for (let i = 0; i < 10000; i++) {}\n  })\n  .add('my-group/my-benchmark-2', { baseline: true }, function () {\n    // The baseline\n    for (let i = 0; i < 1000; i++) {}\n  })\n  .add('second-group/another-benchmark', function () {\n    // A faster benchmark\n    for (let i = 0; i < 100; i++) {}\n  })\n  .run();\n"
  },
  {
    "path": "examples/run.sh",
    "content": "#!/bin/bash\n\n# clean previous logs\nrm -f ./**/*.log\n\nfor filename in ./**/*.*js; do\n    echo \"[1] Running $filename\"\n    node --allow-natives-syntax \"./$filename\" | sed \"s,\\x1B\\[[0-9;]*m,,g\" >>\"$filename.log\"\n    echo -e \"----------------------------------------------------------------------------\" >>\"$filename.log\"\n\n    echo \"[2] Running $filename\"\n    node --allow-natives-syntax \"./$filename\" | sed \"s,\\x1B\\[[0-9;]*m,,g\" >>\"$filename.log\"\n    echo -e \"----------------------------------------------------------------------------\" >>\"$filename.log\"\n\n    echo \"[3] Running $filename\"\n    node --allow-natives-syntax \"./$filename\" | sed \"s,\\x1B\\[[0-9;]*m,,g\" >>\"$filename.log\"\n    echo -e \"----------------------------------------------------------------------------\" >>\"$filename.log\"\ndone\n"
  },
  {
    "path": "examples/statistical-significance/README.md",
    "content": "# Statistical Significance Testing (T-Test)\n\nThis example demonstrates how to use Welch's t-test to determine if benchmark differences are statistically significant.\n\n## The Problem\n\nWhen running benchmarks on shared or cloud environments, results can vary due to:\n- CPU throttling\n- Background processes\n- Memory pressure\n- Cache effects\n\nA benchmark might show one implementation as \"1.05x faster\", but is that a real improvement or just noise?\n\n## The Solution\n\nEnable t-test mode with `ttest: true`:\n\n```js\nconst { Suite } = require('bench-node');\n\nconst suite = new Suite({\n  ttest: true,  // Automatically sets repeatSuite=30\n});\n\nsuite.add('baseline', { baseline: true }, () => {\n  // ...\n});\n\nsuite.add('alternative', () => {\n  // ...\n});\n```\n\nWhen `ttest: true` is set, the suite automatically:\n1. Sets `repeatSuite=30` for all benchmarks (can be overridden)\n2. Runs Welch's t-test to compare results against baseline\n3. Displays significance stars in the output\n\n## Understanding the Output\n\nThe output will show significance stars next to comparisons:\n\n```\nSummary (vs. baseline):\n  baseline/for-loop  (baseline)\n  forEach            (1.80x slower) ***\n  for-of-loop        (1.09x slower) ***\n  reduce             (1.06x faster) **\n\n  Significance: * p<0.05, ** p<0.01, *** p<0.001\n```\n\n- `***` = p < 0.001 - Very high confidence (99.9%) the difference is real\n- `**` = p < 0.01 - High confidence (99%) the difference is real  \n- `*` = p < 0.05 - Moderate confidence (95%) the difference is real\n- (no stars) = Not statistically significant - difference may be noise\n\n## When to Use\n\n1. **Comparing similar implementations** - Is the \"optimization\" actually faster?\n2. **CI/CD pipelines** - Detect real regressions vs. flaky results\n3. **Cloud/shared environments** - High variance requires statistical validation\n4. **Small differences** - 5% faster could be noise or real\n\n## Run the Example\n\n```bash\nnode --allow-natives-syntax node.js\n```\n\n## Sample Output\n\n```\nbaseline/for-loop   x 85,009,221 ops/sec (311 runs sampled)\nreduce              x 89,853,937 ops/sec (321 runs sampled)\nfor-of-loop         x 78,268,434 ops/sec (302 runs sampled)\nforEach             x 47,249,597 ops/sec (334 runs sampled)\n\nSummary (vs. baseline):\n  baseline/for-loop  (baseline)\n  forEach            (1.80x slower) ***\n  for-of-loop        (1.09x slower) ***\n  reduce             (1.06x faster) **\n\n  Significance: * p<0.05, ** p<0.01, *** p<0.001\n```\n"
  },
  {
    "path": "examples/statistical-significance/node.js",
    "content": "/**\n * Statistical Significance Example\n *\n * This example demonstrates how to use Welch's t-test to determine\n * if benchmark differences are statistically significant.\n *\n * When running benchmarks, especially on shared/cloud environments,\n * small performance differences may just be random noise. The t-test\n * helps identify when a difference is real vs. just variance.\n *\n * Run with: node --allow-natives-syntax node.js\n */\n\nconst { Suite } = require('../../lib');\n\n// Enable t-test mode - this automatically sets repeatSuite=30 for all benchmarks\nconst suite = new Suite({\n  ttest: true,\n});\n\n// Baseline: Simple array sum using for loop\nsuite.add('baseline/for-loop', { baseline: true }, () => {\n  const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];\n  let sum = 0;\n  for (let i = 0; i < arr.length; i++) {\n    sum += arr[i];\n  }\n  return sum;\n});\n\n// Alternative 1: Using reduce (typically slower due to function call overhead)\nsuite.add('reduce', () => {\n  const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];\n  return arr.reduce((acc, val) => acc + val, 0);\n});\n\n// Alternative 2: for-of loop (similar performance to for loop)\nsuite.add('for-of-loop', () => {\n  const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];\n  let sum = 0;\n  for (const val of arr) {\n    sum += val;\n  }\n  return sum;\n});\n\n// Alternative 3: forEach (slower due to function call per element)\nsuite.add('forEach', () => {\n  const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];\n  let sum = 0;\n  arr.forEach((val) => {\n    sum += val;\n  });\n  return sum;\n});\n\nsuite.run();\n"
  },
  {
    "path": "examples/string-replace/node.js",
    "content": "const { Suite } = require('../../lib');\n\nconst suite = new Suite();\n\nconst pattern = /[123]/g\nconst replacements = { 1: 'a', 2: 'b', 3: 'c' }\n\nconst subject = '123123123123123123123123123123123123123123123123'\n\nsuite\n  .add('single with matcher', function () {\n    const r = subject.replace(pattern, m => replacements[m])\n  })\n  .add('multiple replaces', function () {\n    const r = subject.replace(/1/g, 'a').replace(/2/g, 'b').replace(/3/g, 'c')\n  })\n  .add('[Managed] single with matcher', function (timer) {\n    const assert = require('node:assert');\n\n    const pattern = /[123]/g\n    const replacements = { 1: 'a', 2: 'b', 3: 'c' }\n\n    const subject = '123123123123123123123123123123123123123123123123'\n\n    let r;\n\n    timer.start();\n    for (let i = 0; i < timer.count; i++) {\n      r = subject.replace(pattern, m => replacements[m]);\n    }\n    timer.end(timer.count);\n\n    assert.ok(r);\n  })\n  .add('[Managed] multiple replaces', function (timer) {\n    const assert = require('node:assert');\n\n    const subject = '123123123123123123123123123123123123123123123123'\n\n    let r;\n\n    timer.start();\n    for (let i = 0; i < timer.count; i++) {\n      r = subject.replace(/1/g, 'a').replace(/2/g, 'b').replace(/3/g, 'c');\n    }\n    timer.end(timer.count);\n    assert.ok(r);\n  })\n  .run()\n"
  },
  {
    "path": "examples/string-replace/node.js.log",
    "content": "single with matcher x 752,020 ops/sec (10 runs sampled) v8-never-optimize=true min..max=(1.32us ... 1.34us) p75=1.33us p99=1.34us\nmultiple replaces x 642,602 ops/sec (10 runs sampled) v8-never-optimize=true min..max=(1.54us ... 1.55us) p75=1.55us p99=1.55us\n[Managed] single with matcher x 774,049 ops/sec (12 runs sampled) v8-never-optimize=true min..max=(1.28us ... 1.30us) p75=1.29us p99=1.30us\n[Managed] multiple replaces x 645,958 ops/sec (12 runs sampled) v8-never-optimize=true min..max=(1.54us ... 1.58us) p75=1.55us p99=1.58us\n----------------------------------------------------------------------------\nsingle with matcher x 748,221 ops/sec (11 runs sampled) v8-never-optimize=true min..max=(1.33us ... 1.35us) p75=1.35us p99=1.35us\nmultiple replaces x 639,390 ops/sec (10 runs sampled) v8-never-optimize=true min..max=(1.55us ... 1.57us) p75=1.56us p99=1.57us\n[Managed] single with matcher x 765,396 ops/sec (12 runs sampled) v8-never-optimize=true min..max=(1.30us ... 1.32us) p75=1.31us p99=1.32us\n[Managed] multiple replaces x 644,492 ops/sec (9 runs sampled) v8-never-optimize=true min..max=(1.55us ... 1.55us) p75=1.55us p99=1.55us\n----------------------------------------------------------------------------\nsingle with matcher x 742,699 ops/sec (9 runs sampled) v8-never-optimize=true min..max=(1.32us ... 1.34us) p75=1.33us p99=1.34us\nmultiple replaces x 640,209 ops/sec (11 runs sampled) v8-never-optimize=true min..max=(1.54us ... 1.57us) p75=1.56us p99=1.57us\n[Managed] single with matcher x 774,824 ops/sec (11 runs sampled) v8-never-optimize=true min..max=(1.29us ... 1.30us) p75=1.29us p99=1.30us\n[Managed] multiple replaces x 642,062 ops/sec (11 runs sampled) v8-never-optimize=true min..max=(1.54us ... 1.60us) p75=1.57us p99=1.60us\n----------------------------------------------------------------------------\n"
  },
  {
    "path": "examples/string-searching/node.js",
    "content": "const { Suite } = require('../../lib');\n\nconst suite = new Suite();\n\nconst text = 'text/html,application/xhtml+xml,application/xml;application/json;q=0.9,image/avif,image/webp,*/*;q=0.8'\nconst regex = /application\\/json/\n\nsuite\n  .add('Using includes', function () {\n    const r = text.includes('application/json')\n  })\n  .add('Using indexof', function () {\n    const r = text.indexOf('application/json') !== -1\n  })\n  .add('Using cached RegExp.test', function () {\n    const r = regex.test(text)\n  })\n  .add('[Managed] Using includes', function (timer) {\n    const assert = require('node:assert');\n\n    const text = 'text/html,application/xhtml+xml,application/xml;application/json;q=0.9,image/avif,image/webp,*/*;q=0.8';\n\n    let r;\n\n    timer.start();\n    for (let i = 0; i < timer.count; i++) {\n      r = text.includes('application/json');\n    }\n    timer.end(timer.count);\n\n    assert.ok(r);\n  })\n  .add('[Managed] Using indexof', function (timer) {\n    const assert = require('node:assert');\n\n    const text = 'text/html,application/xhtml+xml,application/xml;application/json;q=0.9,image/avif,image/webp,*/*;q=0.8';\n\n    let r;\n\n    timer.start();\n    for (let i = 0; i < timer.count; i++) {\n      r = text.indexOf('application/json') !== -1;\n    }\n    timer.end(timer.count);\n\n    assert.ok(r);\n  })\n  .add('[Managed] Using cached RegExp.test', function (timer) {\n    const assert = require('node:assert');\n\n    const regex = /application\\/json/;\n    const text = 'text/html,application/xhtml+xml,application/xml;application/json;q=0.9,image/avif,image/webp,*/*;q=0.8';\n\n    let r;\n\n    timer.start();\n    for (let i = 0; i < timer.count; i++) {\n      r = regex.test(text);\n    }\n    timer.end(timer.count);\n\n    assert.ok(r);\n  })\n  .run();\n"
  },
  {
    "path": "examples/string-searching/node.js.log",
    "content": "Using includes x 131,465,887 ops/sec (11 runs sampled) v8-never-optimize=true min..max=(7.53ns ... 7.61ns) p75=7.59ns p99=7.61ns\nUsing indexof x 130,628,503 ops/sec (10 runs sampled) v8-never-optimize=true min..max=(7.62ns ... 7.67ns) p75=7.65ns p99=7.67ns\nUsing cached RegExp.test x 19,855,444 ops/sec (9 runs sampled) v8-never-optimize=true min..max=(50.32ns ... 50.38ns) p75=50.37ns p99=50.38ns\n[Managed] Using includes x 2,259,413,999 ops/sec (17 runs sampled) v8-never-optimize=true min..max=(0.50ns ... 0.50ns) p75=0.50ns p99=0.50ns\n[Managed] Using indexof x 2,262,452,858 ops/sec (17 runs sampled) v8-never-optimize=true min..max=(0.50ns ... 0.50ns) p75=0.50ns p99=0.50ns\n[Managed] Using cached RegExp.test x 24,771,962 ops/sec (11 runs sampled) v8-never-optimize=true min..max=(40.11ns ... 40.43ns) p75=40.37ns p99=40.43ns\n----------------------------------------------------------------------------\nUsing includes x 131,872,369 ops/sec (12 runs sampled) v8-never-optimize=true min..max=(7.53ns ... 7.70ns) p75=7.63ns p99=7.70ns\nUsing indexof x 131,276,601 ops/sec (10 runs sampled) v8-never-optimize=true min..max=(7.56ns ... 7.67ns) p75=7.59ns p99=7.67ns\nUsing cached RegExp.test x 19,684,556 ops/sec (12 runs sampled) v8-never-optimize=true min..max=(50.51ns ... 51.39ns) p75=50.75ns p99=51.39ns\n[Managed] Using includes x 2,279,136,862 ops/sec (17 runs sampled) v8-never-optimize=true min..max=(0.50ns ... 0.50ns) p75=0.50ns p99=0.50ns\n[Managed] Using indexof x 2,259,877,221 ops/sec (17 runs sampled) v8-never-optimize=true min..max=(0.50ns ... 0.50ns) p75=0.50ns p99=0.50ns\n[Managed] Using cached RegExp.test x 24,313,713 ops/sec (11 runs sampled) v8-never-optimize=true min..max=(40.92ns ... 41.19ns) p75=41.19ns p99=41.19ns\n----------------------------------------------------------------------------\nUsing includes x 132,464,212 ops/sec (10 runs sampled) v8-never-optimize=true min..max=(7.52ns ... 7.56ns) p75=7.56ns p99=7.56ns\nUsing indexof x 132,177,669 ops/sec (11 runs sampled) v8-never-optimize=true min..max=(7.52ns ... 7.67ns) p75=7.60ns p99=7.67ns\nUsing cached RegExp.test x 19,854,899 ops/sec (8 runs sampled) v8-never-optimize=true min..max=(50.28ns ... 50.36ns) p75=50.32ns p99=50.36ns\n[Managed] Using includes x 2,259,179,846 ops/sec (17 runs sampled) v8-never-optimize=true min..max=(0.50ns ... 0.50ns) p75=0.50ns p99=0.50ns\n[Managed] Using indexof x 2,181,069,609 ops/sec (11 runs sampled) v8-never-optimize=true min..max=(0.50ns ... 0.50ns) p75=0.50ns p99=0.50ns\n[Managed] Using cached RegExp.test x 23,308,043 ops/sec (10 runs sampled) v8-never-optimize=true min..max=(40.23ns ... 41.24ns) p75=40.69ns p99=41.24ns\n----------------------------------------------------------------------------\n"
  },
  {
    "path": "examples/time-mode.js",
    "content": "const { Suite } = require('../lib');\n\nconst timeSuite = new Suite({\n    benchmarkMode: 'time' // Set mode for the entire suite\n});\n\nconst delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));\n\ntimeSuite.add('Async Delay 100ms (time)', async () => {\n    await delay(100);\n});\n\ntimeSuite.add('Sync Busy Wait 50ms (time)', () => {\n    const start = Date.now();\n    while (Date.now() - start < 50);\n});\n\ntimeSuite.add('Quick Sync Op with 5 repeats (time)', { repeatSuite: 5 }, () => {\n    // This will run exactly once per repeat (5 times total)\n    // and report the average time\n    let x = 1 + 1;\n});\n\n\n(async () => {\n    console.log('\\nRunning benchmark suite in TIME mode...');\n    await timeSuite.run();\n})();\n"
  },
  {
    "path": "examples/worker-threads/node.js",
    "content": "const { Suite } = require('../../lib');\n\nconst suite = new Suite({\n  useWorkers: true,\n});\n\nsuite\n  .add('Using import without node: prefix', function () {\n    return import('fs');\n  })\n  .add('Using import with node: prefix', function () {\n    return import('node:fs');\n  })\n\t.add('async test', async function (timer) {\n\t\ttimer.start();\n\t\tlet i = 0;\n\t\twhile (i++ < timer.count) {\n\t\t\tawait import(\"node:fs\");\n\t\t}\n\t\ttimer.end(timer.count);\n\t})\n  .run();\n"
  },
  {
    "path": "index.d.ts",
    "content": "// Type definitions for bench-node\n\n/// <reference types=\"node\" />\nimport type { Histogram } from \"node:perf_hooks\";\n\nexport declare namespace BenchNode {\n\tclass Benchmark {\n\t\tname: string;\n\t\tfn: any;\n\t\tminTime: number;\n\t\tmaxTime: number;\n\t\tplugins: Plugin[];\n\t\trepeatSuite: number;\n\t\tminSamples: number;\n\t\tbaseline: boolean;\n\n\t\tconstructor(\n\t\t\tname: string,\n\t\t\tfn: any,\n\t\t\tminTime: number,\n\t\t\tmaxTime: number,\n\t\t\tplugins: Plugin[],\n\t\t\trepeatSuite: number,\n\t\t\tminSamples: number,\n\t\t\tbaseline?: boolean,\n\t\t);\n\n\t\tserializeBenchmark(): void;\n\t}\n\n\tinterface PluginHookVarNames {\n\t\tawaitOrEmpty: string;\n\t\tbench: string;\n\t\tcontext: string;\n\t\ttimer: string;\n\t\tmanaged: boolean;\n\t}\n\n\tinterface BenchmarkResult {\n\t\tname: string;\n\t\topsSec?: number; // Only in 'ops' mode\n\t\topsSecPerRun?: number[]; // Useful when repeatSuite > 1\n\t\ttotalTime?: number; // Total execution time in seconds (Only in 'time' mode)\n\t\titerations: number;\n\t\thistogram: Histogram;\n\t\tplugins?: Record<string, any>; // Object with plugin results\n\t}\n\n\ttype ReporterFunction = (results: BenchmarkResult[]) => void;\n\n\tinterface ReporterOptions {\n\t\tprintHeader?: boolean;\n\t\tlabelWidth?: number;\n\t\tttest?: boolean; // Passed automatically when Suite ttest option is enabled\n\t\talpha?: number; // Significance level for t-test (default: 0.05)\n\t}\n\n\tinterface SuiteOptions {\n\t\treporter?: ReporterFunction | false | null;\n\t\tbenchmarkMode?: \"ops\" | \"time\";\n\t\tuseWorkers?: boolean;\n\t\tplugins?: Plugin[];\n\t\tminSamples?: number; // Minimum number of samples per round for all benchmarks\n\t\trepeatSuite?: number; // Number of times to repeat each benchmark (default: 1, or 30 when ttest is enabled)\n\t\tttest?: boolean; // Enable t-test mode for statistical significance (auto-sets repeatSuite=30)\n\t\treporterOptions?: ReporterOptions;\n\t\tdetectDeadCodeElimination?: boolean; // Enable DCE detection, default: false\n\t\tdceThreshold?: number; // DCE detection threshold multiplier, default: 10\n\t}\n\n\tinterface BenchmarkOptions {\n\t\tminTime?: number; // Minimum duration in seconds\n\t\tmaxTime?: number; // Maximum duration in seconds\n\t\trepeatSuite?: number; // Number of times to repeat benchmark\n\t\tminSamples?: number; // Minimum number of samples per round\n\t}\n\n\ttype BenchmarkFunction = (timer?: {\n\t\tstart: () => void;\n\t\tend: (iterations?: number) => void;\n\t\tcount: number;\n\t}) => void | Promise<void>;\n\n\ttype OnCompleteBenchmarkResult = [\n\t\tduration: number,\n\t\tcount: number,\n\t\tcontext: Record<string, any>,\n\t];\n\ttype PluginResult = {\n\t\ttype: string;\n\t\t[key: string]: any;\n\t};\n\n\tinterface Plugin {\n\t\tisSupported?(): boolean;\n\t\tbeforeClockTemplate?(varNames: PluginHookVarNames): string[];\n\t\tafterClockTemplate?(varNames: PluginHookVarNames): string[];\n\t\tonCompleteBenchmark?(\n\t\t\tresult: OnCompleteBenchmarkResult,\n\t\t\tbench: Benchmark,\n\t\t): void;\n\t\ttoString?(): string;\n\t\tgetReport?(benchmarkName: string): string;\n\t\tgetResult?(benchmarkName: string): PluginResult;\n\t\treset?(): void;\n\t}\n\n\tclass Suite {\n\t\tconstructor(options?: SuiteOptions);\n\t\tadd(name: string, fn: BenchmarkFunction): this;\n\t\tadd(name: string, options: BenchmarkOptions, fn: BenchmarkFunction): this;\n\t\trun(): Promise<BenchmarkResult[]>;\n\t}\n\n\tclass V8NeverOptimizePlugin implements Plugin {\n\t\tisSupported(): boolean;\n\t\tbeforeClockTemplate(varNames: PluginHookVarNames): string[];\n\t\ttoString(): string;\n\t\tgetReport(benchmarkName: string): string;\n\t}\n\n\tclass V8GetOptimizationStatus implements Plugin {\n\t\tisSupported(): boolean;\n\t\tafterClockTemplate(varNames: PluginHookVarNames): string[];\n\t\tonCompleteBenchmark(result: OnCompleteBenchmarkResult): void;\n\t\ttoString(): string;\n\t\tgetReport(benchmarkName: string): string;\n\t\tgetResult?(benchmarkName: string): PluginResult;\n\t}\n\n\tclass V8OptimizeOnNextCallPlugin implements Plugin {\n\t\tisSupported(): boolean;\n\t\tbeforeClockTemplate(varNames: PluginHookVarNames): string[];\n\t\ttoString(): string;\n\t\tgetReport(): string;\n\t}\n\n\tclass MemoryPlugin implements Plugin {\n\t\tisSupported(): boolean;\n\t\tbeforeClockTemplate(varNames: PluginHookVarNames): string[];\n\t\tafterClockTemplate(varNames: PluginHookVarNames): string[];\n\t\tonCompleteBenchmark(result: OnCompleteBenchmarkResult): void;\n\t\tgetReport(benchmarkName: string): string;\n\t\tgetResult(benchmarkName: string): PluginResult;\n\t\ttoString(): string;\n\t}\n\n\tclass DeadCodeEliminationDetectionPlugin implements Plugin {\n\t\tconstructor(options?: { threshold?: number });\n\t\tisSupported(): boolean;\n\t\tsetBaseline(timePerOp: number): void;\n\t\tonCompleteBenchmark(\n\t\t\tresult: OnCompleteBenchmarkResult,\n\t\t\tbench: Benchmark,\n\t\t): void;\n\t\tgetWarning(\n\t\t\tbenchmarkName: string,\n\t\t): { timePerOp: number; baselineTime: number; ratio: number } | undefined;\n\t\tgetAllWarnings(): Array<{\n\t\t\tname: string;\n\t\t\ttimePerOp: number;\n\t\t\tbaselineTime: number;\n\t\t\tratio: number;\n\t\t}>;\n\t\thasWarning(benchmarkName: string): boolean;\n\t\temitWarnings(): void;\n\t\ttoString(): string;\n\t\treset(): void;\n\t}\n}\n\nexport declare const textReport: BenchNode.ReporterFunction;\nexport declare const chartReport: BenchNode.ReporterFunction;\nexport declare const prettyReport: BenchNode.ReporterFunction;\nexport declare const htmlReport: BenchNode.ReporterFunction;\nexport declare const jsonReport: BenchNode.ReporterFunction;\nexport declare const csvReport: BenchNode.ReporterFunction;\n\nexport declare class Suite extends BenchNode.Suite {}\nexport declare class V8NeverOptimizePlugin extends BenchNode.V8NeverOptimizePlugin {}\nexport declare class V8GetOptimizationStatus extends BenchNode.V8GetOptimizationStatus {}\nexport declare class V8OptimizeOnNextCallPlugin extends BenchNode.V8OptimizeOnNextCallPlugin {}\nexport declare class MemoryPlugin extends BenchNode.MemoryPlugin {}\n\n// Statistical T-Test utilities\nexport declare namespace TTest {\n\tinterface WelchTTestResult {\n\t\ttStatistic: number;\n\t\tdegreesOfFreedom: number;\n\t\tpValue: number;\n\t\tsignificant: boolean;\n\t\tmean1: number;\n\t\tmean2: number;\n\t\tvariance1: number;\n\t\tvariance2: number;\n\t}\n\n\tinterface CompareBenchmarksResult {\n\t\tsignificant: boolean;\n\t\tpValue: number;\n\t\tconfidence: string;\n\t\tstars: \"***\" | \"**\" | \"*\" | \"\";\n\t\tdifference: \"faster\" | \"slower\" | \"same\";\n\t\ttStatistic: number;\n\t\tdegreesOfFreedom: number;\n\t}\n}\n\n/**\n * Returns significance stars based on p-value thresholds.\n * @param pValue - The p-value from statistical test\n * @returns Stars indicating significance level ('***', '**', '*', or '')\n */\nexport declare function getSignificanceStars(\n\tpValue: number,\n): \"***\" | \"**\" | \"*\" | \"\";\n\n/**\n * Performs Welch's t-test for two independent samples.\n * Does not assume equal variances between the samples.\n * @param sample1 - First sample array\n * @param sample2 - Second sample array\n * @returns Test results including t-statistic, degrees of freedom, p-value, and significance\n */\nexport declare function welchTTest(\n\tsample1: number[],\n\tsample2: number[],\n): TTest.WelchTTestResult;\n\n/**\n * Determines if two benchmark results are statistically different\n * using Welch's t-test at a given significance level.\n * @param sample1 - Sample data from first benchmark\n * @param sample2 - Sample data from second benchmark\n * @param alpha - Significance level (default 0.05 for 95% confidence)\n * @returns Comparison result with significance info\n */\nexport declare function compareBenchmarks(\n\tsample1: number[],\n\tsample2: number[],\n\talpha?: number,\n): TTest.CompareBenchmarksResult;\n\nexport declare class DeadCodeEliminationDetectionPlugin extends BenchNode.DeadCodeEliminationDetectionPlugin {}\n"
  },
  {
    "path": "lib/clock.js",
    "content": "const { debuglog } = require(\"node:util\");\nconst { validateNumber } = require(\"./validators\");\n\nconst debugBench = debuglog(\"benchmark\");\n\nconst kUnmanagedTimerResult = Symbol(\"kUnmanagedTimerResult\");\n\n// If the smallest time measurement is 1ns\n// the minimum resolution of this timer is 0.5\nconst MIN_RESOLUTION = 0.5;\n\nclass Timer {\n\tconstructor() {\n\t\tthis.now = process.hrtime.bigint;\n\t}\n\n\tget scale() {\n\t\treturn 1e9;\n\t}\n\n\tget resolution() {\n\t\treturn 1 / 1e9;\n\t}\n\n\t/**\n\t * @param {number} timeInNs\n\t * @returns {string}\n\t */\n\tformat(timeInNs) {\n\t\tvalidateNumber(timeInNs, \"timeInNs\", 0);\n\n\t\tif (timeInNs > 1e9) {\n\t\t\treturn `${(timeInNs / 1e9).toFixed(2)}s`;\n\t\t}\n\n\t\tif (timeInNs > 1e6) {\n\t\t\treturn `${(timeInNs / 1e6).toFixed(2)}ms`;\n\t\t}\n\n\t\tif (timeInNs > 1e3) {\n\t\t\treturn `${(timeInNs / 1e3).toFixed(2)}us`;\n\t\t}\n\n\t\treturn `${(timeInNs).toFixed(2)}ns`;\n\t}\n}\n\nconst timer = new Timer();\n\nclass ManagedTimer {\n\tstartTime;\n\tendTime;\n\titerations;\n\trecommendedCount;\n\n\t/**\n\t * @param {number} recommendedCount\n\t */\n\tconstructor(recommendedCount) {\n\t\tthis.recommendedCount = recommendedCount;\n\t}\n\n\t/**\n\t * Returns the recommended value to be used to benchmark your code\n\t * @returns {number}\n\t */\n\tget count() {\n\t\treturn this.recommendedCount;\n\t}\n\n\t/**\n\t * Starts the timer\n\t */\n\tstart() {\n\t\tthis.startTime = timer.now();\n\t}\n\n\t/**\n\t * Stops the timer\n\t * @param {number} [iterations=1] The amount of iterations that run\n\t */\n\tend(iterations = 1) {\n\t\tthis.endTime = timer.now();\n\t\tvalidateNumber(iterations, \"iterations\", 1);\n\t\tthis.iterations = iterations;\n\t}\n\n\t[kUnmanagedTimerResult](context) {\n\t\tif (this.startTime === undefined)\n\t\t\tthrow new Error(\"You forgot to call .start()\");\n\n\t\tif (this.endTime === undefined)\n\t\t\tthrow new Error(\"You forgot to call .end(count)\");\n\n\t\treturn [Number(this.endTime - this.startTime), this.iterations, context];\n\t}\n}\n\nfunction createRunUnmanagedBenchmark(bench, awaitOrEmpty) {\n\tconst varNames = {\n\t\tawaitOrEmpty,\n\t\ttimer: \"timer\",\n\t\tcontext: \"context\",\n\t\tbench: \"bench\",\n\t\tmanaged: false,\n\t};\n\n\tlet code = `\nlet i = 0;\nlet ${varNames.context} = {};\n`;\n\n\tlet benchFnCall = `${awaitOrEmpty}${varNames.bench}.fn()`;\n\tconst wrapFunctions = [];\n\tfor (const p of bench.plugins) {\n\t\tif (typeof p.beforeClockTemplate === \"function\") {\n\t\t\tconst [newCode, functionToCall] = p.beforeClockTemplate(varNames);\n\t\t\tcode += newCode;\n\t\t\tif (functionToCall) {\n\t\t\t\twrapFunctions.push(functionToCall);\n\t\t\t}\n\t\t}\n\t}\n\tbenchFnCall = wrapFunctions.reduce((prev, n) => {\n\t\treturn `${n}(${prev})`;\n\t}, benchFnCall);\n\n\tcode += `\nconst startedAt = ${varNames.timer}.now();\n\nfor (; i < count; i++)\n  ${benchFnCall};\n\nconst duration = Number(${varNames.timer}.now() - startedAt);\n`;\n\n\tfor (const p of bench.plugins) {\n\t\tif (typeof p.afterClockTemplate === \"function\") {\n\t\t\tconst [newCode] = p.afterClockTemplate(varNames);\n\t\t\tcode += newCode;\n\t\t}\n\t}\n\n\tcode += `return [duration, count, ${varNames.context}];`;\n\treturn code;\n}\n\nfunction createRunManagedBenchmark(bench, awaitOrEmpty) {\n\tconst varNames = {\n\t\tawaitOrEmpty,\n\t\ttimer: \"timer\",\n\t\tcontext: \"context\",\n\t\tbench: \"bench\",\n\t\tmanaged: true,\n\t};\n\n\tlet code = `\nlet i = 0;\nlet ${varNames.context} = {};\n`;\n\n\tlet benchFnCall = `${awaitOrEmpty}${varNames.bench}.fn(${varNames.timer})`;\n\tconst wrapFunctions = [];\n\tfor (const p of bench.plugins) {\n\t\tif (typeof p.beforeClockTemplate === \"function\") {\n\t\t\tconst [newCode, functionToCall] = p.beforeClockTemplate(varNames);\n\t\t\tcode += newCode;\n\t\t\tif (functionToCall) {\n\t\t\t\twrapFunctions.push(functionToCall);\n\t\t\t}\n\t\t}\n\t}\n\tbenchFnCall = wrapFunctions.reduce((prev, n) => {\n\t\treturn `${n}(${prev})`;\n\t}, benchFnCall);\n\n\tcode += `\n${benchFnCall};\nconst result = ${varNames.timer}[kUnmanagedTimerResult](${varNames.context});\n`;\n\tfor (const p of bench.plugins) {\n\t\tif (typeof p.afterClockTemplate === \"function\") {\n\t\t\tconst [newCode] = p.afterClockTemplate(varNames);\n\t\t\tcode += newCode;\n\t\t}\n\t}\n\n\tcode += \"return result;\";\n\treturn code;\n}\n\nconst AsyncFunction = (async () => {}).constructor;\nconst SyncFunction = (() => {}).constructor;\n\nfunction createFnString(bench) {\n\tconst { isAsync, hasArg } = bench;\n\n\tconst compiledFnStringFactory = hasArg\n\t\t? createRunManagedBenchmark\n\t\t: createRunUnmanagedBenchmark;\n\tconst compiledFnString = compiledFnStringFactory(\n\t\tbench,\n\t\tisAsync ? \"await \" : \"\",\n\t);\n\treturn compiledFnString;\n}\n\nfunction createRunner(bench, recommendedCount) {\n\tconst { isAsync, hasArg } = bench;\n\tconst compiledFnString = bench.fnStr;\n\n\tconst createFnPrototype = isAsync ? AsyncFunction : SyncFunction;\n\tconst compiledFn = createFnPrototype(\n\t\t\"bench\",\n\t\t\"timer\",\n\t\t\"count\",\n\t\t\"kUnmanagedTimerResult\",\n\t\tcompiledFnString,\n\t);\n\tconst selectedTimer = hasArg ? new ManagedTimer(recommendedCount) : timer;\n\tconst runner = compiledFn.bind(\n\t\tglobalThis,\n\t\tbench,\n\t\tselectedTimer,\n\t\trecommendedCount,\n\t\tkUnmanagedTimerResult,\n\t);\n\tdebugBench(`Compiled Code: ${compiledFnString}`);\n\tdebugBench(\n\t\t`Created compiled benchmark, hasArg=${hasArg}, isAsync=${isAsync}, recommendedCount=${recommendedCount}`,\n\t);\n\n\treturn runner;\n}\n\n/**\n * Executes a benchmark and returns the time taken and number of iterations\n * @param {import('./index').Benchmark} bench - The benchmark to execute\n * @param {number} recommendedCount - The recommended number of iterations\n * @param {Object} [options] - Additional options\n * @param {boolean} [options.timeMode=false] - If true, runs the benchmark exactly once\n * @returns {Promise<[number, number]>} - Returns [duration, iterations]\n */\nasync function clockBenchmark(bench, recommendedCount, options = {}) {\n\tconst runner = createRunner(bench, recommendedCount);\n\tconst result = await runner();\n\n\t// Just to avoid issues with empty fn\n\tresult[0] = Math.max(MIN_RESOLUTION, result[0]);\n\n\tfor (const p of bench.plugins) {\n\t\tif (typeof p.onCompleteBenchmark === \"function\") {\n\t\t\t// TODO: this won't work when useWorkers=true\n\t\t\tp.onCompleteBenchmark(result, bench);\n\t\t}\n\t}\n\n\tdebugBench(\n\t\t`Took ${timer.format(result[0])} to execute ${result[1]} iterations${options.timeMode ? \" (time mode)\" : \"\"}`,\n\t);\n\treturn result;\n}\n\nmodule.exports = {\n\tclockBenchmark,\n\tcreateFnString,\n\ttimer,\n\tMIN_RESOLUTION,\n\tdebugBench,\n};\n"
  },
  {
    "path": "lib/histogram.js",
    "content": "const { validateNumber } = require(\"./validators\");\n\n/**\n * A class that calculates and maintains statistical measurements for a set of numeric samples.\n * Handles outlier removal, and calculates various statistical measures like mean, standard deviation,\n * coefficient of variation, and percentiles.\n */\nclass StatisticalHistogram {\n\tall = [];\n\tmin;\n\tmax;\n\tmean;\n\tcv;\n\tstddev;\n\tfinished = false;\n\n\t/**\n\t * @returns {number[]}\n\t */\n\tget samples() {\n\t\treturn this.all.slice();\n\t}\n\n\t/**\n\t * @param {number} percentile\n\t * @returns {number}\n\t */\n\tpercentile(percentile) {\n\t\tvalidateNumber(percentile, \"percentile\");\n\n\t\tif (Number.isNaN(percentile) || percentile < 0 || percentile > 100)\n\t\t\tthrow new Error(\n\t\t\t\t\"Invalid percentile value. Must be a number between 0 and 100.\",\n\t\t\t);\n\n\t\tif (this.all.length === 0) return 0;\n\n\t\tif (percentile === 0) return this.all[0];\n\n\t\treturn this.all[Math.ceil(this.all.length * (percentile / 100)) - 1];\n\t}\n\n\t/**\n\t * @param {number} value\n\t */\n\trecord(value) {\n\t\tvalidateNumber(value, \"value\", 0);\n\n\t\tthis.all.push(value);\n\t}\n\n\tfinish() {\n\t\tif (this.finished) return;\n\n\t\tthis.finished = true;\n\t\tthis.removeOutliers();\n\n\t\tthis.calculateMinMax();\n\t\tthis.calculateMean();\n\t\tthis.calculateStd();\n\t\tthis.calculateCv();\n\t}\n\n\t/**\n\t * References:\n\t * - https://gist.github.com/rmeissn/f5b42fb3e1386a46f60304a57b6d215a\n\t * - https://en.wikipedia.org/wiki/Interquartile_range\n\t */\n\tremoveOutliers() {\n\t\tthis.all.sort((a, b) => a - b);\n\n\t\tconst size = this.all.length;\n\n\t\tif (size < 4) return;\n\n\t\tlet q1;\n\t\tlet q3;\n\n\t\tif (((size - 1) / 4) % 1 === 0 || (size / 4) % 1 === 0) {\n\t\t\tq1 =\n\t\t\t\t(1 / 2) *\n\t\t\t\t(this.all[Math.floor(size / 4) - 1] + this.all[Math.floor(size / 4)]);\n\t\t\tq3 =\n\t\t\t\t(1 / 2) *\n\t\t\t\t(this.all[Math.ceil((size * 3) / 4) - 1] +\n\t\t\t\t\tthis.all[Math.ceil((size * 3) / 4)]);\n\t\t} else {\n\t\t\tq1 = this.all[Math.floor(size / 4)];\n\t\t\tq3 = this.all[Math.floor((size * 3) / 4)];\n\t\t}\n\n\t\tconst iqr = q3 - q1;\n\t\tconst minValue = q1 - iqr * 1.5;\n\t\tconst maxValue = q3 + iqr * 1.5;\n\n\t\tthis.all = this.all.filter(\n\t\t\t(value) => value <= maxValue && value >= minValue,\n\t\t);\n\t}\n\n\tcalculateMinMax() {\n\t\tthis.min = Number.POSITIVE_INFINITY;\n\t\tthis.max = Number.NEGATIVE_INFINITY;\n\n\t\tfor (let i = 0; i < this.all.length; i++) {\n\t\t\tthis.min = Math.min(this.all[i], this.min);\n\t\t\tthis.max = Math.max(this.all[i], this.max);\n\t\t}\n\t}\n\n\tcalculateMean() {\n\t\tif (this.all.length === 0) {\n\t\t\tthis.mean = 0;\n\t\t\treturn;\n\t\t}\n\n\t\tif (this.all.length === 1) {\n\t\t\tthis.mean = this.all[0];\n\t\t\treturn;\n\t\t}\n\n\t\tthis.mean =\n\t\t\tthis.all.reduce(\n\t\t\t\t(acc, value) => Math.min(Number.MAX_SAFE_INTEGER, acc + value),\n\t\t\t\t0,\n\t\t\t) / this.all.length;\n\t}\n\n\tcalculateStd() {\n\t\tif (this.all.length < 2) {\n\t\t\tthis.stddev = 0;\n\t\t\treturn;\n\t\t}\n\t\tconst variance =\n\t\t\tthis.all.reduce((acc, value) => {\n\t\t\t\treturn acc + (value - this.mean) ** 2;\n\t\t\t}, 0) /\n\t\t\t(this.all.length - 1);\n\t\tthis.stddev = Math.sqrt(variance);\n\t}\n\n\t/**\n\t * References:\n\t * - https://en.wikipedia.org/wiki/Coefficient_of_variation\n\t * - https://github.com/google/benchmark/blob/159eb2d0ffb85b86e00ec1f983d72e72009ec387/src/statistics.ccL81-L88\n\t */\n\tcalculateCv() {\n\t\tif (this.all.length < 2 || this.mean === 0) {\n\t\t\tthis.cv = 0;\n\t\t\treturn;\n\t\t}\n\n\t\tthis.cv = (this.stddev / this.mean) * 100;\n\t}\n}\n\nmodule.exports = {\n\tStatisticalHistogram,\n};\n"
  },
  {
    "path": "lib/index.js",
    "content": "const { Worker } = require(\"node:worker_threads\");\nconst { types } = require(\"node:util\");\nconst path = require(\"node:path\");\n\nconst {\n\ttextReport,\n\tchartReport,\n\thtmlReport,\n\tjsonReport,\n\tcsvReport,\n\tprettyReport,\n} = require(\"./report\");\nconst {\n\tgetInitialIterations,\n\trunBenchmark,\n\trunWarmup,\n} = require(\"./lifecycle\");\nconst { debugBench, timer, createFnString } = require(\"./clock\");\nconst {\n\tvalidatePlugins,\n\tV8NeverOptimizePlugin,\n\tV8GetOptimizationStatus,\n\tV8OptimizeOnNextCallPlugin,\n\tMemoryPlugin,\n\tDeadCodeEliminationDetectionPlugin,\n} = require(\"./plugins\");\nconst {\n\tvalidateFunction,\n\tvalidateNumber,\n\tvalidateObject,\n\tvalidateString,\n\tvalidateArray,\n\tvalidateBenchmarkMode,\n\tvalidateBoolean,\n} = require(\"./validators\");\nconst {\n\twelchTTest,\n\tcompareBenchmarks,\n\tgetSignificanceStars,\n} = require(\"./utils/ttest\");\n\nconst getFunctionBody = (string) =>\n\tstring.substring(string.indexOf(\"{\") + 1, string.lastIndexOf(\"}\"));\n\nclass Benchmark {\n\tname = \"Benchmark\";\n\tfn;\n\tminTime;\n\tmaxTime;\n\tplugins;\n\trepeatSuite;\n\tminSamples;\n\tbaseline = false;\n\n\tconstructor(\n\t\tname,\n\t\tfn,\n\t\tminTime,\n\t\tmaxTime,\n\t\tplugins,\n\t\trepeatSuite,\n\t\tminSamples,\n\t\tbaseline = false,\n\t) {\n\t\tthis.name = name;\n\t\tthis.fn = fn;\n\t\tthis.minTime = minTime;\n\t\tthis.maxTime = maxTime;\n\t\tthis.plugins = plugins;\n\t\tthis.repeatSuite = repeatSuite;\n\t\tthis.minSamples = minSamples;\n\t\tthis.baseline = baseline;\n\n\t\tthis.hasArg = this.fn.length >= 1;\n\t\tif (this.fn.length > 1) {\n\t\t\tprocess.emitWarning(\n\t\t\t\t`The benchmark \"${this.name}\" function should not have more than 1 argument.`,\n\t\t\t);\n\t\t}\n\n\t\tthis.isAsync = types.isAsyncFunction(this.fn);\n\n\t\tthis.fnStr = createFnString(this);\n\t}\n\n\tserializeBenchmark() {\n\t\t// Regular functions can't be passed to worker.postMessage\n\t\t// So we pass the string and deserialize fnStr into a new Function\n\t\t// on worker\n\t\tthis.fn = getFunctionBody(this.fn.toString());\n\t}\n}\n\nconst defaultBenchOptions = {\n\t// 0.05s - Arbitrary number used in some benchmark tools\n\tminTime: 0.05,\n\t// 0.5s - Arbitrary number used in some benchmark tools\n\tmaxTime: 0.5,\n\t// Number of times the benchmark will be repeated\n\trepeatSuite: 1,\n\t// Number minimum of samples the each round\n\tminSamples: 10,\n};\n\n// Minimum repeatSuite runs required for reliable t-test results\nconst MIN_REPEAT_FOR_TTEST = 30;\n\nfunction throwIfNoNativesSyntax() {\n\tif (process.execArgv.includes(\"--allow-natives-syntax\") === false) {\n\t\tthrow new Error(\n\t\t\t\"bench-node module must be run with --allow-natives-syntax argument\",\n\t\t);\n\t}\n}\n\nclass Suite {\n\t#benchmarks;\n\t#reporter;\n\t#plugins;\n\t#useWorkers;\n\t#benchmarkMode;\n\t#reporterOptions;\n\t#minSamples;\n\t#repeatSuite;\n\t#ttest;\n\t#dceDetector;\n\n\tconstructor(options = {}) {\n\t\tthis.#benchmarks = [];\n\t\tvalidateObject(options, \"options\");\n\n\t\tif (options?.reporter !== undefined) {\n\t\t\tif (options?.reporter !== false && options?.reporter !== null) {\n\t\t\t\tvalidateFunction(options.reporter, \"reporter\");\n\t\t\t}\n\t\t\tthis.#reporter = options.reporter;\n\t\t} else if (options?.pretty === true) {\n\t\t\tthis.#reporter = prettyReport;\n\t\t} else {\n\t\t\tthis.#reporter = textReport;\n\t\t}\n\n\t\tthis.#useWorkers = options.useWorkers || false;\n\n\t\t// DCE detection is opt-in to avoid breaking changes\n\t\tconst dceEnabled = options.detectDeadCodeElimination === true;\n\t\tif (dceEnabled) {\n\t\t\tthis.#dceDetector = new DeadCodeEliminationDetectionPlugin(\n\t\t\t\toptions.dceThreshold ? { threshold: options.dceThreshold } : {},\n\t\t\t);\n\t\t}\n\n\t\t// Plugin setup: If DCE detection is enabled, default to no plugins (allow optimization)\n\t\t// Otherwise, use V8NeverOptimizePlugin as the default\n\t\tif (options?.plugins) {\n\t\t\tvalidateArray(options.plugins, \"plugin\");\n\t\t\tvalidatePlugins(options.plugins);\n\t\t\tthis.#plugins = options.plugins;\n\t\t} else if (dceEnabled) {\n\t\t\t// DCE detection requires optimization to be enabled, so no default plugins\n\t\t\tthis.#plugins = [];\n\t\t} else {\n\t\t\t// Default behavior - use V8NeverOptimizePlugin\n\t\t\tthis.#plugins = [new V8NeverOptimizePlugin()];\n\t\t}\n\n\t\tthis.#benchmarkMode = options.benchmarkMode || \"ops\";\n\t\tvalidateBenchmarkMode(this.#benchmarkMode, \"options.benchmarkMode\");\n\n\t\tthis.#reporterOptions = options.reporterOptions || {\n\t\t\tprintHeader: true,\n\t\t};\n\n\t\tif (options.ttest !== undefined) {\n\t\t\tvalidateBoolean(options.ttest, \"options.ttest\");\n\t\t}\n\t\tthis.#ttest = options.ttest ?? false;\n\n\t\tlet repeatSuite = defaultBenchOptions.repeatSuite;\n\t\tif (options.repeatSuite !== undefined) {\n\t\t\tvalidateNumber(options.repeatSuite, \"options.repeatSuite\", 1);\n\t\t\trepeatSuite = options.repeatSuite;\n\t\t} else if (this.#ttest) {\n\t\t\trepeatSuite = MIN_REPEAT_FOR_TTEST;\n\t\t}\n\t\tthis.#repeatSuite = repeatSuite;\n\n\t\tif (this.#ttest) {\n\t\t\tthis.#reporterOptions.ttest = true;\n\t\t}\n\t\tlet minSamples = defaultBenchOptions.minSamples;\n\t\tif (options.minSamples !== undefined) {\n\t\t\tvalidateNumber(options.minSamples, \"options.minSamples\", 1);\n\t\t\tminSamples = options.minSamples;\n\t\t}\n\n\t\tthis.#minSamples = minSamples;\n\t}\n\n\tadd(name, options, fn) {\n\t\tvalidateString(name, \"name\");\n\t\tif (typeof options === \"function\") {\n\t\t\tfn = options;\n\t\t\toptions = {\n\t\t\t\t...defaultBenchOptions,\n\t\t\t\tminSamples: this.#minSamples,\n\t\t\t\trepeatSuite: this.#repeatSuite,\n\t\t\t};\n\t\t} else {\n\t\t\tvalidateObject(options, \"options\");\n\t\t\toptions = {\n\t\t\t\t...defaultBenchOptions,\n\t\t\t\tminSamples: this.#minSamples,\n\t\t\t\trepeatSuite: this.#repeatSuite,\n\t\t\t\t...options,\n\t\t\t};\n\t\t\t// Enforce strict minimum (> 1e-6s). Using EPSILON to make boundary exclusive.\n\t\t\tvalidateNumber(\n\t\t\t\toptions.minTime,\n\t\t\t\t\"options.minTime\",\n\t\t\t\ttimer.resolution * 1e3 + Number.EPSILON,\n\t\t\t);\n\t\t\tvalidateNumber(options.maxTime, \"options.maxTime\", options.minTime);\n\t\t\tvalidateNumber(options.repeatSuite, \"options.repeatSuite\", 1);\n\t\t\tvalidateNumber(options.minSamples, \"options.minSamples\", 1);\n\t\t}\n\t\tvalidateFunction(fn, \"fn\");\n\n\t\tconst { baseline = false } = options || {};\n\t\tif (baseline && this.#benchmarks.some((b) => b.baseline)) {\n\t\t\tthrow new Error(\"There is already a baseline benchmark\");\n\t\t}\n\n\t\tconst benchmark = new Benchmark(\n\t\t\tname,\n\t\t\tfn,\n\t\t\toptions.minTime,\n\t\t\toptions.maxTime,\n\t\t\tthis.#plugins,\n\t\t\toptions.repeatSuite,\n\t\t\toptions.minSamples,\n\t\t\tbaseline,\n\t\t);\n\t\tthis.#benchmarks.push(benchmark);\n\t\treturn this;\n\t}\n\n\tasync run() {\n\t\tthrowIfNoNativesSyntax();\n\t\tconst results = new Array(this.#benchmarks.length);\n\n\t\t// Measure baseline for DCE detection (only in ops mode, not in worker mode)\n\t\tif (\n\t\t\tthis.#dceDetector &&\n\t\t\t!this.#useWorkers &&\n\t\t\tthis.#benchmarkMode === \"ops\"\n\t\t) {\n\t\t\tawait this.#measureBaseline();\n\t\t}\n\n\t\t// It doesn't make sense to warmup a fresh new instance of Worker.\n\t\t// TODO: Should this be folded into the main loop?\n\t\tif (!this.#useWorkers) {\n\t\t\t// This is required to avoid variance on first benchmark run\n\t\t\tfor (let i = 0; i < this.#benchmarks.length; ++i) {\n\t\t\t\tconst benchmark = this.#benchmarks[i];\n\t\t\t\tdebugBench(\n\t\t\t\t\t`Warmup ${benchmark.name} with minTime=${benchmark.minTime}, maxTime=${benchmark.maxTime}`,\n\t\t\t\t);\n\t\t\t\tconst initialIteration = await getInitialIterations(benchmark);\n\t\t\t\tawait runWarmup(benchmark, initialIteration, {\n\t\t\t\t\tminTime: 0.005,\n\t\t\t\t\tmaxTime: 0.05,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\tfor (let i = 0; i < this.#benchmarks.length; ++i) {\n\t\t\tconst benchmark = this.#benchmarks[i];\n\n\t\t\t// Add DCE detector to benchmark plugins if enabled\n\t\t\tif (this.#dceDetector && this.#benchmarkMode === \"ops\") {\n\t\t\t\tconst originalPlugins = benchmark.plugins;\n\t\t\t\tbenchmark.plugins = [...benchmark.plugins, this.#dceDetector];\n\t\t\t\t// Regenerate function string with new plugins\n\t\t\t\tbenchmark.fnStr = createFnString(benchmark);\n\t\t\t}\n\n\t\t\t// Warmup is calculated to reduce noise/bias on the results\n\t\t\tconst initialIterations = await getInitialIterations(benchmark);\n\t\t\tdebugBench(\n\t\t\t\t`Starting ${benchmark.name} with mode=${this.#benchmarkMode}, minTime=${benchmark.minTime}, maxTime=${benchmark.maxTime}, repeatSuite=${benchmark.repeatSuite}, minSamples=${benchmark.minSamples}`,\n\t\t\t);\n\n\t\t\tlet result;\n\t\t\tif (this.#useWorkers) {\n\t\t\t\tif (this.#benchmarkMode === \"time\") {\n\t\t\t\t\tconsole.warn(\n\t\t\t\t\t\t\"Warning: Worker mode currently doesn't fully support 'time' benchmarkMode.\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tresult = await this.runWorkerBenchmark(benchmark, initialIterations);\n\t\t\t} else {\n\t\t\t\tresult = await runBenchmark(\n\t\t\t\t\tbenchmark,\n\t\t\t\t\tinitialIterations,\n\t\t\t\t\tthis.#benchmarkMode,\n\t\t\t\t\tbenchmark.repeatSuite,\n\t\t\t\t\tbenchmark.minSamples,\n\t\t\t\t);\n\t\t\t}\n\t\t\tresults[i] = result;\n\t\t}\n\n\t\tif (this.#reporter) {\n\t\t\tthis.#reporter(results, this.#reporterOptions);\n\t\t}\n\n\t\t// Emit DCE warnings after reporting\n\t\tif (this.#dceDetector) {\n\t\t\tthis.#dceDetector.emitWarnings();\n\t\t}\n\n\t\treturn results;\n\t}\n\n\tasync #measureBaseline() {\n\t\tdebugBench(\"Measuring baseline for DCE detection...\");\n\n\t\t// Create a minimal baseline benchmark (empty function)\n\t\tconst baselineBench = new Benchmark(\n\t\t\t\"__baseline__\",\n\t\t\t() => {},\n\t\t\t0.01, // minTime\n\t\t\t0.05, // maxTime\n\t\t\tthis.#plugins,\n\t\t\t1, // repeatSuite\n\t\t\t10, // minSamples\n\t\t);\n\n\t\tconst initialIterations = await getInitialIterations(baselineBench);\n\t\tconst result = await runBenchmark(\n\t\t\tbaselineBench,\n\t\t\tinitialIterations,\n\t\t\t\"ops\",\n\t\t\t1,\n\t\t\t10,\n\t\t);\n\n\t\tconst baselineTimePerOp = (1 / result.opsSec) * 1e9; // Convert to ns\n\t\tdebugBench(`DCE baseline: ${timer.format(baselineTimePerOp)}/iter`);\n\n\t\tthis.#dceDetector.setBaseline(baselineTimePerOp);\n\t}\n\n\tasync runWorkerBenchmark(benchmark, initialIterations) {\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tconst workerPath = path.resolve(__dirname, \"./worker-runner.js\");\n\t\t\tconst worker = new Worker(workerPath);\n\n\t\t\tbenchmark.serializeBenchmark();\n\t\t\tworker.postMessage({\n\t\t\t\tbenchmark,\n\t\t\t\tinitialIterations,\n\t\t\t\tbenchmarkMode: this.#benchmarkMode, // Pass suite mode\n\t\t\t\trepeatSuite: benchmark.repeatSuite,\n\t\t\t\tminSamples: benchmark.minSamples,\n\t\t\t});\n\n\t\t\tworker.on(\"message\", (result) => {\n\t\t\t\tresolve(result);\n\t\t\t\tworker.terminate();\n\t\t\t});\n\t\t\tworker.on(\"error\", (error) => {\n\t\t\t\treject(error);\n\t\t\t\tworker.terminate();\n\t\t\t});\n\t\t\tworker.on(\"exit\", (code) => {\n\t\t\t\tif (code !== 0)\n\t\t\t\t\treject(new Error(`Worker stopped with exit code ${code}`));\n\t\t\t});\n\t\t});\n\t}\n}\n\nmodule.exports = {\n\tSuite,\n\tV8NeverOptimizePlugin,\n\tV8GetOptimizationStatus,\n\tV8OptimizeOnNextCallPlugin,\n\tMemoryPlugin,\n\tDeadCodeEliminationDetectionPlugin,\n\tchartReport,\n\ttextReport,\n\tprettyReport,\n\thtmlReport,\n\tjsonReport,\n\tcsvReport,\n\t// Statistical utilities\n\twelchTTest,\n\tcompareBenchmarks,\n\tgetSignificanceStars,\n};\n"
  },
  {
    "path": "lib/lifecycle.js",
    "content": "const {\n\tclockBenchmark,\n\tdebugBench,\n\tMIN_RESOLUTION,\n\ttimer,\n} = require(\"./clock\");\nconst { StatisticalHistogram } = require(\"./histogram\");\n\n/**\n * @param {number} durationPerOp The amount of time each operation takes, in timer.scale\n * @param {number} targetTime The amount of time we want the benchmark to execute, in seconds\n * @return {number} - a suggested iteration count >= 1\n */\nfunction getItersForOpDuration(durationPerOp, targetTime) {\n\tconst secondsPerOp = durationPerOp / timer.scale;\n\tconst opsForTargetTime = Math.round(targetTime / secondsPerOp);\n\n\treturn Math.min(Number.MAX_SAFE_INTEGER, Math.max(1, opsForTargetTime));\n}\n\nfunction parsePluginsResult(plugins, name) {\n\tconst result = [];\n\tfor (const p of plugins) {\n\t\tresult.push({\n\t\t\tname: p.toString(),\n\t\t\tresult: p.getResult?.(name) ?? \"enabled\",\n\t\t\treport: p.getReport?.(name) ?? \"\",\n\t\t});\n\t}\n\treturn result;\n}\n\n/**\n * Calculates and returns the initial number of iterations for a benchmark\n * @param {import('./index').Benchmark} bench - The benchmark object to be executed\n * @returns {Promise<number>} The calculated number of initial iterations\n */\nasync function getInitialIterations(bench) {\n\tconst { 0: duration, 1: realIterations } = await clockBenchmark(bench, 30);\n\n\t// Just to avoid issues with empty fn\n\tconst durationPerOp = Math.max(MIN_RESOLUTION, duration / realIterations);\n\tdebugBench(\n\t\t`Duration per operation on initial count: ${timer.format(durationPerOp)}`,\n\t);\n\n\t// TODO: is this a correct assumpion?\n\tif (durationPerOp > bench.maxTime * timer.scale)\n\t\tprocess.emitWarning(\n\t\t\t`The benchmark \"${bench.name}\" has a duration per operation greater than the maxTime.`,\n\t\t);\n\n\treturn getItersForOpDuration(durationPerOp, bench.minTime);\n}\n\n/**\n * Executes the warmup phase of a benchmark\n * @param {import('./index').Benchmark} bench - The benchmark object to be executed\n * @param {number} initialIterations - The initial number of iterations to run\n * @param {Object} options - Warmup options\n * @param {number} [options.minTime] - Minimum time for warmup, in seconds. Defaults to bench.minTime\n * @param {number} [options.maxTime] - Maximum time for warmup, in seconds. Defaults to bench.minTime\n * @returns {Promise<void>}\n */\nasync function runWarmup(bench, initialIterations, { minTime, maxTime }) {\n\tminTime = minTime ?? bench.minTime;\n\tmaxTime = maxTime ?? bench.minTime;\n\n\tconst maxDuration = maxTime * timer.scale;\n\tconst minSamples = 10;\n\n\tlet iterations = 0;\n\tlet timeSpent = 0;\n\tlet samples = 0;\n\n\twhile (timeSpent < maxDuration || samples <= minSamples) {\n\t\tconst { 0: duration, 1: realIterations } = await clockBenchmark(\n\t\t\tbench,\n\t\t\tinitialIterations,\n\t\t);\n\t\ttimeSpent += duration;\n\n\t\titerations += realIterations;\n\t\titerations = Math.min(Number.MAX_SAFE_INTEGER, iterations);\n\n\t\t// Just to avoid issues with empty fn\n\t\tconst durationPerOp = Math.max(MIN_RESOLUTION, duration / realIterations);\n\t\tconst remainingTime = Math.max(0, (maxDuration - timeSpent) / timer.scale);\n\t\tconst targetTime = Math.min(remainingTime, minTime);\n\n\t\tinitialIterations = getItersForOpDuration(durationPerOp, targetTime);\n\t\tsamples++;\n\t}\n}\n\nasync function runBenchmarkOnce(\n\tbench,\n\thistogram,\n\t{ initialIterations, maxDuration, minSamples },\n\tbenchmarkMode = \"ops\",\n) {\n\tlet iterations = 0;\n\tlet timeSpent = 0;\n\n\t// For time mode, we want to run the benchmark exactly once\n\tif (benchmarkMode === \"time\") {\n\t\tconst { 0: duration, 1: realIterations } = await clockBenchmark(bench, 1);\n\t\ttimeSpent = duration;\n\t\titerations = realIterations;\n\n\t\t// Record the duration in the histogram\n\t\thistogram.record(duration);\n\n\t\treturn { iterations, timeSpent };\n\t}\n\n\t// Ops mode - run the sampling loop\n\twhile (timeSpent < maxDuration || histogram.samples.length <= minSamples) {\n\t\tconst { 0: duration, 1: realIterations } = await clockBenchmark(\n\t\t\tbench,\n\t\t\tinitialIterations,\n\t\t);\n\t\ttimeSpent += duration;\n\n\t\titerations = Math.min(Number.MAX_SAFE_INTEGER, iterations + realIterations);\n\n\t\t// Just to avoid issues with empty fn\n\t\tconst durationPerOp = Math.max(MIN_RESOLUTION, duration / realIterations);\n\n\t\thistogram.record(durationPerOp);\n\n\t\tconst remainingTime = Math.max(0, (maxDuration - timeSpent) / timer.scale);\n\t\tconst targetTime = Math.min(remainingTime, bench.minTime);\n\t\tinitialIterations = getItersForOpDuration(durationPerOp, targetTime);\n\t}\n\n\treturn { iterations, timeSpent };\n}\n\n/**\n * Executes a benchmark with the specified parameters\n * @param {import('./index').Benchmark} bench - The benchmark object to be executed\n * @param {number} initialIterations - The initial number of iterations to run\n * @param {string} benchmarkMode - The benchmark mode ('ops' or 'time')\n * @param {number} repeatSuite - Number of times to repeat the benchmark suite\n * @param {number} minSamples - Minimum number of samples to collect\n * @returns {Promise<Object>} The benchmark results containing operations per second or total time, iterations, histogram data and plugin results\n */\nasync function runBenchmark(\n\tbench,\n\tinitialIterations,\n\tbenchmarkMode,\n\trepeatSuite,\n\tminSamples,\n) {\n\tconst histogram = new StatisticalHistogram();\n\tconst maxDuration = bench.maxTime * timer.scale;\n\n\tlet totalIterations = 0;\n\tlet totalTimeSpent = 0;\n\tconst opsSecPerRun = [];\n\n\tfor (let i = 0; i < repeatSuite; ++i) {\n\t\tconst { iterations, timeSpent } = await runBenchmarkOnce(\n\t\t\tbench,\n\t\t\thistogram,\n\t\t\t{\n\t\t\t\tinitialIterations,\n\t\t\t\tmaxDuration,\n\t\t\t\tminSamples,\n\t\t\t},\n\t\t\tbenchmarkMode,\n\t\t);\n\n\t\tconst runOpsSec = iterations / (timeSpent / timer.scale);\n\t\topsSecPerRun.push(runOpsSec);\n\n\t\ttotalTimeSpent += timeSpent;\n\t\ttotalIterations += iterations;\n\t}\n\thistogram.finish();\n\n\tconst totalTime = totalTimeSpent / timer.scale; // Convert ns to seconds\n\tconst opsSec = totalIterations / totalTime;\n\n\tconst sampleData = histogram.samples;\n\n\tconst result = {\n\t\titerations: totalIterations,\n\t\t// StatisticalHistogram is not a serializable object, keep raw ns for min/max\n\t\thistogram: {\n\t\t\tsamples: sampleData.length,\n\t\t\tmin: histogram.min,\n\t\t\tmax: histogram.max,\n\t\t\tsampleData,\n\t\t},\n\t\tname: bench.name,\n\t\tplugins: parsePluginsResult(bench.plugins, bench.name),\n\t\tbaseline: bench.baseline,\n\t};\n\n\t// Add the appropriate metric based on the benchmark mode\n\tif (benchmarkMode === \"time\") {\n\t\tresult.totalTime = totalTime / repeatSuite; // Average time per repeat\n\t\tdebugBench(\n\t\t\t`${bench.name} completed ${repeatSuite} repeats with average time ${result.totalTime.toFixed(6)} seconds`,\n\t\t);\n\t} else {\n\t\tresult.opsSec = opsSec;\n\t\tresult.opsSecPerRun = opsSecPerRun;\n\t\tdebugBench(\n\t\t\t`${bench.name} completed ${sampleData.length} samples with ${opsSec.toFixed(2)} ops/sec`,\n\t\t);\n\t}\n\n\t// since the instance is shared across benchmarks, reset it after use\n\tfor (const plugin of bench.plugins) {\n\t\tplugin.reset?.();\n\t}\n\n\treturn result;\n}\n\nmodule.exports = {\n\tgetInitialIterations,\n\trunBenchmark,\n\trunWarmup,\n};\n"
  },
  {
    "path": "lib/plugins/dce-detection.js",
    "content": "const { timer } = require(\"../clock\");\n\n/**\n * Plugin that detects potential dead code elimination (DCE) in benchmarks.\n * Compares benchmark timings against a baseline (empty function) to identify\n * suspiciously fast benchmarks that may have been optimized away by the JIT.\n *\n * This helps educate users about benchmark quality and potential issues.\n */\nclass DeadCodeEliminationDetectionPlugin {\n\t#baselineTimePerOp = null;\n\t#warnings = new Map();\n\t#threshold = 10; // Warn if benchmark is less than 10x slower than baseline\n\n\tconstructor(options = {}) {\n\t\t// Allow customizing the threshold\n\t\tif (options.threshold !== undefined) {\n\t\t\tthis.#threshold = options.threshold;\n\t\t}\n\t}\n\n\tisSupported() {\n\t\treturn true; // Works everywhere\n\t}\n\n\t/**\n\t * Stores baseline measurement for comparison\n\t * @param {number} timePerOp - Time per operation in nanoseconds\n\t */\n\tsetBaseline(timePerOp) {\n\t\tthis.#baselineTimePerOp = timePerOp;\n\t}\n\n\t/**\n\t * Called after each benchmark completes to check for DCE\n\t */\n\tonCompleteBenchmark(result, bench) {\n\t\tif (this.#baselineTimePerOp === null) {\n\t\t\t// No baseline yet, skip detection\n\t\t\treturn;\n\t\t}\n\n\t\tconst [duration, iterations] = result;\n\t\tconst timePerOp = duration / iterations;\n\n\t\t// Check if this benchmark is suspiciously fast compared to baseline\n\t\tif (timePerOp < this.#baselineTimePerOp * this.#threshold) {\n\t\t\tthis.#warnings.set(bench.name, {\n\t\t\t\ttimePerOp,\n\t\t\t\tbaselineTime: this.#baselineTimePerOp,\n\t\t\t\tratio: timePerOp / this.#baselineTimePerOp,\n\t\t\t});\n\t\t}\n\t}\n\n\t/**\n\t * Get warning for a specific benchmark\n\t */\n\tgetWarning(benchmarkName) {\n\t\treturn this.#warnings.get(benchmarkName);\n\t}\n\n\t/**\n\t * Get all warnings\n\t */\n\tgetAllWarnings() {\n\t\treturn Array.from(this.#warnings.entries()).map(([name, data]) => ({\n\t\t\tname,\n\t\t\t...data,\n\t\t}));\n\t}\n\n\t/**\n\t * Check if a benchmark has a warning\n\t */\n\thasWarning(benchmarkName) {\n\t\treturn this.#warnings.has(benchmarkName);\n\t}\n\n\t/**\n\t * Emit warnings to console\n\t */\n\temitWarnings() {\n\t\tif (this.#warnings.size === 0) {\n\t\t\treturn;\n\t\t}\n\n\t\tconsole.log(\"\\n⚠️  Dead Code Elimination Warnings:\");\n\t\tconsole.log(\n\t\t\t\"The following benchmarks may have been optimized away by the JIT compiler:\\n\",\n\t\t);\n\n\t\tfor (const [name, data] of this.#warnings.entries()) {\n\t\t\tconsole.log(`  • ${name}`);\n\t\t\tconsole.log(`    Benchmark: ${timer.format(data.timePerOp)}/iter`);\n\t\t\tconsole.log(`    Baseline:  ${timer.format(data.baselineTime)}/iter`);\n\t\t\tconsole.log(`    Ratio:     ${data.ratio.toFixed(2)}x of baseline`);\n\t\t\tconsole.log(\n\t\t\t\t\"    Suggestion: Ensure the result is used or assign to a variable\\n\",\n\t\t\t);\n\t\t}\n\n\t\tconsole.log(\n\t\t\t\"ℹ️  These benchmarks are running nearly as fast as an empty function,\",\n\t\t);\n\t\tconsole.log(\n\t\t\t\"   which suggests the JIT may have eliminated the actual work.\\n\",\n\t\t);\n\t}\n\n\ttoString() {\n\t\treturn \"DeadCodeEliminationDetectionPlugin\";\n\t}\n\n\treset() {\n\t\t// Don't reset - we want to accumulate warnings across all benchmarks\n\t\t// They will be emitted once at the end of the suite run\n\t}\n}\n\nmodule.exports = {\n\tDeadCodeEliminationDetectionPlugin,\n};\n"
  },
  {
    "path": "lib/plugins/memory.js",
    "content": "const { StatisticalHistogram } = require(\"../histogram\");\n\n/**\n * Formats a byte value into a human-readable string with appropriate units (B, Kb, MB, GB)\n * @param {number} bytes - The number of bytes to format\n * @returns {string} Formatted string with appropriate unit suffix\n */\nfunction formatBytes(bytes) {\n\tif (bytes < 1024) return `${Math.round(bytes)}B`;\n\n\tconst kbytes = bytes / 1024;\n\tif (kbytes < 1024) return `${kbytes.toFixed(2)}Kb`;\n\n\tconst mbytes = kbytes / 1024;\n\tif (mbytes < 1024) return `${mbytes.toFixed(2)}MB`;\n\n\tconst gbytes = mbytes / 1024;\n\treturn `${gbytes.toFixed(2)}GB`;\n}\n\n/**\n * Plugin that measures memory usage during benchmark execution\n * Collects heap usage statistics and provides reporting capabilities\n */\nclass MemoryPlugin {\n\tstatic MEMORY_BEFORE_RUN = \"memoryBeforeRun\";\n\tstatic MEMORY_AFTER_RUN = \"memoryAfterRun\";\n\tstatic #WARNING_REPORTED = false;\n\n\t/**\n\t * @type {StatisticalHistogram}\n\t */\n\t#heapUsedHistogram = new StatisticalHistogram();\n\n\tisSupported() {\n\t\treturn typeof globalThis.gc === \"function\";\n\t}\n\n\tbeforeClockTemplate({ managed, context }) {\n\t\tif (managed && !MemoryPlugin.#WARNING_REPORTED) {\n\t\t\tMemoryPlugin.#WARNING_REPORTED = true;\n\t\t\tprocess.emitWarning(\n\t\t\t\t\"The memory statistics can be inaccurate since it will include the tear-up and teardown of your benchmark.\",\n\t\t\t);\n\t\t}\n\n\t\tlet code = \"\";\n\n\t\tcode += `${context}.${MemoryPlugin.MEMORY_BEFORE_RUN} = 0;\\n`;\n\t\tcode += `${context}.${MemoryPlugin.MEMORY_AFTER_RUN} = 0;\\n`;\n\t\tcode += \"globalThis.gc();\\n\";\n\t\tcode += `${context}.${MemoryPlugin.MEMORY_BEFORE_RUN} = globalThis.process.memoryUsage();\\n`;\n\n\t\treturn [code];\n\t}\n\n\tafterClockTemplate({ context }) {\n\t\treturn [\n\t\t\t`${context}.${MemoryPlugin.MEMORY_AFTER_RUN} = globalThis.process.memoryUsage();\\n`,\n\t\t];\n\t}\n\n\tonCompleteBenchmark([, realIterations, context]) {\n\t\tconst heapUsed =\n\t\t\tcontext[MemoryPlugin.MEMORY_AFTER_RUN].heapUsed -\n\t\t\tcontext[MemoryPlugin.MEMORY_BEFORE_RUN].heapUsed;\n\t\tconst externalUsed =\n\t\t\tcontext[MemoryPlugin.MEMORY_AFTER_RUN].external -\n\t\t\tcontext[MemoryPlugin.MEMORY_BEFORE_RUN].external;\n\n\t\tconst memoryAllocated = (heapUsed + externalUsed) / realIterations;\n\n\t\t// below 0, we just coerce to be zero\n\t\tthis.#heapUsedHistogram.record(Math.max(0, memoryAllocated));\n\t}\n\n\ttoString() {\n\t\treturn \"MemoryPlugin\";\n\t}\n\n\tgetReport() {\n\t\tthis.#heapUsedHistogram.finish();\n\n\t\treturn `heap usage=${formatBytes(this.#heapUsedHistogram.mean)} (${formatBytes(this.#heapUsedHistogram.min)} ... ${formatBytes(this.#heapUsedHistogram.max)})`;\n\t}\n\n\tgetResult() {\n\t\treturn {\n\t\t\tproto: null,\n\t\t\ttype: this.toString(),\n\t\t\thistogram: this.#heapUsedHistogram,\n\t\t};\n\t}\n\n\treset() {\n\t\tthis.#heapUsedHistogram = new StatisticalHistogram();\n\t}\n}\n\nmodule.exports = {\n\tMemoryPlugin,\n};\n"
  },
  {
    "path": "lib/plugins/v8-never-opt.js",
    "content": "class V8NeverOptimizePlugin {\n\tisSupported() {\n\t\ttry {\n\t\t\tnew Function(\"%NeverOptimizeFunction(() => {})\")();\n\n\t\t\treturn true;\n\t\t} catch (e) {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tbeforeClockTemplate({ bench }) {\n\t\tlet code = \"\";\n\n\t\tcode += `\nfunction DoNotOptimize(x) {}\n// Prevent the benchmark function and result consumer from optimizing or being inlined.\n%NeverOptimizeFunction(${bench}.fn);\n%NeverOptimizeFunction(DoNotOptimize);\n`;\n\t\treturn [code, \"DoNotOptimize\"];\n\t}\n\n\ttoString() {\n\t\treturn \"V8NeverOptimizePlugin\";\n\t}\n\n\tgetReport() {\n\t\treturn \"v8-never-optimize=true\";\n\t}\n}\n\nmodule.exports = {\n\tV8NeverOptimizePlugin,\n};\n"
  },
  {
    "path": "lib/plugins/v8-opt.js",
    "content": "class V8OptimizeOnNextCallPlugin {\n\tisSupported() {\n\t\ttry {\n\t\t\tnew Function(\"%OptimizeFunctionOnNextCall(() => {})\")();\n\n\t\t\treturn true;\n\t\t} catch (e) {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tbeforeClockTemplate({ awaitOrEmpty, bench, timer }) {\n\t\tlet code = \"\";\n\n\t\tcode += `%OptimizeFunctionOnNextCall(${bench}.fn);\\n`;\n\t\tcode += `${awaitOrEmpty}${bench}.fn(${timer});\\n`;\n\t\tcode += `${awaitOrEmpty}${bench}.fn(${timer});\\n`;\n\n\t\treturn [code];\n\t}\n\n\tgetReport() {\n\t\treturn \"v8-optimize-next-call=enabled\";\n\t}\n\n\ttoString() {\n\t\treturn \"V8OptimizeOnNextCallPlugin\";\n\t}\n}\n\nmodule.exports = {\n\tV8OptimizeOnNextCallPlugin,\n};\n"
  },
  {
    "path": "lib/plugins/v8-print-status.js",
    "content": "function checkBitmap(value, bit) {\n\treturn (value & bit) === bit;\n}\n\nfunction translateStatus(optStatus) {\n\tif (optStatus === -1) {\n\t\treturn \"unknown\";\n\t}\n\n\tconst optStat = [];\n\tif (checkBitmap(optStatus, 2)) {\n\t\toptStat.push(\"Never Optimized\");\n\t}\n\tif (checkBitmap(optStatus, 4)) {\n\t\toptStat.push(\"Always Optimized\");\n\t}\n\tif (checkBitmap(optStatus, 8)) {\n\t\toptStat.push(\"Maybe Deopted\");\n\t}\n\tif (checkBitmap(optStatus, 16)) {\n\t\toptStat.push(\"Optimized\");\n\t}\n\tif (checkBitmap(optStatus, 32)) {\n\t\toptStat.push(\"TurboFanned\");\n\t}\n\tif (checkBitmap(optStatus, 64)) {\n\t\toptStat.push(\"Interpreted\");\n\t}\n\tif (checkBitmap(optStatus, 128)) {\n\t\toptStat.push(\"Marked for Optimization\");\n\t}\n\tif (checkBitmap(optStatus, 256)) {\n\t\toptStat.push(\"Marked for Concurrent Optimization\");\n\t}\n\tif (checkBitmap(optStatus, 512)) {\n\t\toptStat.push(\"Concurrently Optimizing\");\n\t}\n\tif (checkBitmap(optStatus, 1024)) {\n\t\toptStat.push(\"Is Executing\");\n\t}\n\tif (checkBitmap(optStatus, 2048)) {\n\t\toptStat.push(\"Topmost frame is Turbo Fanned\");\n\t}\n\tif (checkBitmap(optStatus, 4096)) {\n\t\toptStat.push(\"Lite Mode\");\n\t}\n\tif (checkBitmap(optStatus, 8192)) {\n\t\toptStat.push(\"Marked for de-optimization\");\n\t}\n\n\treturn optStat.join(\", \");\n}\n\nclass V8GetOptimizationStatus {\n\t#optimizationStatuses = [];\n\n\tisSupported() {\n\t\ttry {\n\t\t\tnew Function(\"%GetOptimizationStatus(() => {})\")();\n\n\t\t\treturn true;\n\t\t} catch (e) {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tafterClockTemplate({ bench, context }) {\n\t\tlet code = \"\";\n\t\tcode += `${context}.v8OptimizationStatus = %GetOptimizationStatus(${bench}.fn);\\n`;\n\t\treturn [code];\n\t}\n\n\tonCompleteBenchmark(result) {\n\t\tconst context = result[2];\n\t\tthis.#optimizationStatuses.push(context.v8OptimizationStatus);\n\t}\n\n\ttoString() {\n\t\treturn \"V8GetOptimizationStatus\";\n\t}\n\n\tgetReport() {\n\t\tconst allAvailableStatus = this.#optimizationStatuses.reduce(\n\t\t\t(acc, v) => acc | v,\n\t\t\t0,\n\t\t);\n\t\treturn `v8-opt-status=\"${translateStatus(allAvailableStatus)}\"`;\n\t}\n\n\tgetResult() {\n\t\tconst allAvailableStatus = this.#optimizationStatuses.reduce(\n\t\t\t(acc, v) => acc | v,\n\t\t\t0,\n\t\t);\n\t\treturn {\n\t\t\ttype: this.toString(),\n\t\t\toptimizationStatuses: translateStatus(allAvailableStatus),\n\t\t};\n\t}\n\n\treset() {\n\t\tthis.#optimizationStatuses = [];\n\t}\n}\n\nmodule.exports = {\n\tV8GetOptimizationStatus,\n};\n"
  },
  {
    "path": "lib/plugins.js",
    "content": "const { V8OptimizeOnNextCallPlugin } = require(\"./plugins/v8-opt\");\nconst { V8NeverOptimizePlugin } = require(\"./plugins/v8-never-opt\");\n\nconst { V8GetOptimizationStatus } = require(\"./plugins/v8-print-status\");\nconst { MemoryPlugin } = require(\"./plugins/memory\");\nconst {\n\tDeadCodeEliminationDetectionPlugin,\n} = require(\"./plugins/dce-detection\");\n\nconst { validateFunction, validateArray } = require(\"./validators\");\n\n/**\n * Validates that all plugins in the array implement the required plugin interface\n * and that they are supported in the current environment\n * @param {Array<Object>} plugins - Array of plugin objects to validate\n * @throws {Error} If any plugin doesn't meet the requirements or isn't supported\n */\nfunction validatePlugins(plugins) {\n\tfor (const p of plugins) {\n\t\tvalidateFunction(p.isSupported, \"Plugins must have a isSupported method.\");\n\t\tvalidateFunction(p.toString, \"Plugins must have a toString() method.\");\n\n\t\tif (!p.isSupported()) {\n\t\t\tthrow new Error(`Plugin: ${p.toString()} is not supported.`);\n\t\t}\n\n\t\tif (typeof p.beforeClockTemplate === \"function\") {\n\t\t\tconst result = p.beforeClockTemplate({\n\t\t\t\tbench: \"\",\n\t\t\t\tawaitOrEmpty: \"\",\n\t\t\t\tcontext: \"\",\n\t\t\t\ttimer: \"\",\n\t\t\t});\n\t\t\tvalidateArray(result, `${p.toString()}.beforeClockTemplate()`);\n\t\t}\n\n\t\tif (typeof p.afterClockTemplate === \"function\") {\n\t\t\tconst result = p.afterClockTemplate({\n\t\t\t\tbench: \"\",\n\t\t\t\tawaitOrEmpty: \"\",\n\t\t\t\tcontext: \"\",\n\t\t\t\ttimer: \"\",\n\t\t\t});\n\t\t\tvalidateArray(result, `${p.toString()}.afterClockTemplate()`);\n\t\t}\n\t}\n}\n\nmodule.exports = {\n\tMemoryPlugin,\n\tV8NeverOptimizePlugin,\n\tV8GetOptimizationStatus,\n\tV8OptimizeOnNextCallPlugin,\n\tDeadCodeEliminationDetectionPlugin,\n\tvalidatePlugins,\n};\n"
  },
  {
    "path": "lib/report.js",
    "content": "const textReport = require(\"./reporter/text\");\nconst prettyReport = require(\"./reporter/pretty\");\nconst chartReport = require(\"./reporter/chart\");\nconst htmlReport = require(\"./reporter/html\");\nconst jsonReport = require(\"./reporter/json\");\nconst csvReport = require(\"./reporter/csv\");\n\n/**\n * @typedef {Object} BenchmarkResult\n * @property {string} name - The name of the benchmark\n * @property {number} opsSec - Operations per second\n * @property {number} iterations - Total number of iterations run\n * @property {Object} histogram - Statistical data about the benchmark runs\n * @property {Array} plugins - Results from plugins used during benchmarking\n */\n\n/**\n * Exports various report generators for benchmark results\n * @type {Object}\n * @property {function(BenchmarkResult[]): void} chartReport - Generates a chart visualization of benchmark results\n * @property {function(BenchmarkResult[]): void} textReport - Generates a text report of benchmark results\n * @property {function(BenchmarkResult[]): void} htmlReport - Generates an HTML report of benchmark results\n * @property {function(BenchmarkResult[]): string} jsonReport - Generates a JSON report of benchmark results\n * @property {function(BenchmarkResult[]): string} csvReport - Generates a CSV report of benchmark results\n */\nmodule.exports = {\n\t...chartReport,\n\t...textReport,\n\t...prettyReport,\n\t...htmlReport,\n\t...jsonReport,\n\t...csvReport,\n};\n"
  },
  {
    "path": "lib/reporter/chart.js",
    "content": "const { platform, arch, availableParallelism, totalmem } = require(\"node:os\");\nconst { styleText } = require(\"../utils/styleText\");\nconst { analyze } = require(\"../utils/analyze.js\");\n\n/**\n * Draws a bar chart representation of a benchmark result\n * @param {string} label - The label for the bar (benchmark name)\n * @param {number} value - The value to display (operations per second or time per operation)\n * @param {number} total - The maximum value in the dataset (for scaling)\n * @param {number} samples - Number of samples collected\n * @param {string} metric - The metric being displayed (opsSec or totalTime)\n * @param {number} width - Length of the bar in characters\n * @param {string} [comment=\"\"] - optional additional comment\n */\nfunction drawBar(label, value, total, samples, metric, width, comment = \"\") {\n\tlet percentage;\n\tlet displayedValue;\n\tlet displayedMetric;\n\n\tif (metric === \"opsSec\") {\n\t\tpercentage = value / total; // Higher ops/sec is better\n\t\tconst valueReported =\n\t\t\tvalue < 100 ? Number(value.toFixed(2)) : Math.round(value);\n\t\tdisplayedValue = styleText([\"yellow\"], formatter.format(valueReported));\n\t\tdisplayedMetric = \"ops/sec\";\n\t} else {\n\t\t// metric === 'totalTime'\n\t\tpercentage = 1 - value / total; // Lower totalTime is better, invert percentage\n\t\tlet timeFormatted;\n\t\tif (value < 0.000001) {\n\t\t\t// Less than 1 microsecond, show in nanoseconds\n\t\t\ttimeFormatted = `${(value * 1000000000).toFixed(2)} ns`;\n\t\t} else if (value < 0.001) {\n\t\t\t// Less than 1 millisecond, show in microseconds\n\t\t\ttimeFormatted = `${(value * 1000000).toFixed(2)} µs`;\n\t\t} else if (value < 1) {\n\t\t\t// Less than 1 second, show in milliseconds\n\t\t\ttimeFormatted = `${(value * 1000).toFixed(2)} ms`;\n\t\t} else {\n\t\t\t// 1 second or more, show in seconds\n\t\t\ttimeFormatted = `${value.toFixed(2)} s`;\n\t\t}\n\t\tdisplayedValue = styleText([\"yellow\"], timeFormatted);\n\t\tdisplayedMetric = \"total time\";\n\t}\n\n\tconst ratio = width * percentage;\n\tconst filledLength = Math.floor(ratio);\n\tconst fraction = ratio % 1;\n\tconst partial = fraction >= 0.5 ? \"▌\" : \"\";\n\n\tconst bar =\n\t\t\"█\".repeat(filledLength) +\n\t\tpartial +\n\t\t\"─\".repeat(width - filledLength - partial.length);\n\n\tconst displayedSamples = `${styleText([\"yellow\"], samples.toString().padStart(2))} samples`;\n\n\treturn `${label} ▏${bar}▕ ${displayedValue} ${displayedMetric} | ${displayedSamples} ${comment}\\n`;\n}\n\nconst formatter = Intl.NumberFormat(undefined, {\n\tnotation: \"standard\",\n\tmaximumFractionDigits: 2,\n});\n\nconst timer = Intl.NumberFormat(undefined, {\n\tminimumFractionDigits: 3,\n\tmaximumFractionDigits: 3,\n});\n\nconst environment = {\n\tnodeVersion: `Node.js version: ${process.version}`,\n\tplatform: `${platform()} ${arch()}`,\n\thardware: `${availableParallelism()} vCPUs | ${(totalmem() / 1024 ** 3).toFixed(1)}GB Mem`,\n};\n\n/**\n * Outputs a chart visualization of benchmark results in the console\n * Displays system information and a bar chart of operations per second or time per operation\n * @param {BenchmarkResult[]} results - Array of benchmark results\n * @param options {object} layout options\n */\nfunction chartReport(results, options = { labelWidth: 45, printHeader: true }) {\n\tprocess.stdout.write(toChart(results, options));\n}\n\n/**\n * Generates a chart visualization of benchmark results in the console\n * Displays system information and a bar chart of operations per second or time per operation\n * @param {BenchmarkResult[]} results - Array of benchmark results\n * @param options {object} layout options\n */\nfunction toChart(\n\tresults,\n\toptions = { labelWidth: 45, printHeader: true, barWidth: 25 },\n) {\n\t// Determine the primary metric and calculate max value for scaling\n\tconst primaryMetric =\n\t\tresults[0]?.opsSec !== undefined ? \"opsSec\" : \"totalTime\";\n\tconst maxValue = Math.max(...results.map((b) => b[primaryMetric]));\n\n\tlet text = \"\";\n\n\tif (options.printHeader) {\n\t\ttext += `${environment.nodeVersion}\\n`;\n\t\ttext += `Platform: ${environment.platform}\\n`;\n\t\ttext += `CPU Cores: ${environment.hardware}\\n\\n`;\n\t}\n\n\tconst hasBaseline = results.find((result) => result.baseline) !== undefined;\n\n\tresults = analyze(results, false);\n\n\tif (hasBaseline) {\n\t\ttext += styleText(\"bold\", \"\\nSummary (vs. baseline):\\n\");\n\t}\n\n\tconst maxNameLength = Math.max(...results.map((r) => r.name.length));\n\tconst columnWidth = Math.max(maxNameLength, options.labelWidth ?? 45);\n\n\tfor (const result of results) {\n\t\tlet comment = \"\";\n\n\t\tif (hasBaseline) {\n\t\t\tif (result.baseline) {\n\t\t\t\tcomment = styleText(\"magenta\", \"(baseline)\");\n\t\t\t} else {\n\t\t\t\tconst isSignificant = result.significanceTest?.significant ?? true;\n\t\t\t\tconst isFaster = !result.comparison.startsWith(\"-\");\n\t\t\t\tconst comparisonText = isFaster\n\t\t\t\t\t? `(${result.comparison}x faster)`\n\t\t\t\t\t: `(${result.comparison.slice(1)}x slower)`;\n\n\t\t\t\tif (isSignificant) {\n\t\t\t\t\tcomment = styleText(isFaster ? \"green\" : \"red\", comparisonText);\n\t\t\t\t} else {\n\t\t\t\t\tcomment = styleText(\"dim\", comparisonText);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\ttext += drawBar(\n\t\t\tresult.name.padEnd(columnWidth),\n\t\t\tresult[primaryMetric],\n\t\t\tmaxValue,\n\t\t\tresult.histogram.samples,\n\t\t\tprimaryMetric,\n\t\t\toptions.barWidth ?? 25,\n\t\t\tcomment,\n\t\t);\n\t}\n\n\treturn text;\n}\n\nmodule.exports = {\n\tchartReport,\n\ttoChart,\n};\n"
  },
  {
    "path": "lib/reporter/csv.js",
    "content": "const { summarize } = require(\"../utils/analyze\");\n\nconst formatter = Intl.NumberFormat(undefined, {\n\tnotation: \"standard\",\n\tmaximumFractionDigits: 2,\n});\n\n/**\n * Outputs JSON formatted results the console\n * @param {BenchmarkResult[]} results - Array of benchmark results\n */\nfunction csvReport(results) {\n\tprocess.stdout.write(toCSV(results));\n}\n\n/**\n * Outputs CSV formatted results as a string\n * @param {BenchmarkResult[]} results - Array of benchmark results\n * @returns {string}\n */\nfunction toCSV(results) {\n\tconst primaryMetric =\n\t\tresults[0]?.opsSec !== undefined ? \"opsSec\" : \"totalTime\";\n\tlet text = `name,${primaryMetric === \"opsSec\" ? \"ops/sec\" : \"total time\"},samples,plugins,min,max\\n`;\n\n\tconst output = summarize(results);\n\n\tfor (const entry of output) {\n\t\ttext += `${entry.name},`;\n\n\t\tif (primaryMetric === \"opsSec\") {\n\t\t\ttext += `\"${formatter.format(entry.opsSec)}\",`;\n\t\t} else {\n\t\t\ttext += `${entry.totalTimeFormatted},`;\n\t\t}\n\n\t\ttext += `${entry.runsSampled},`;\n\t\ttext += `\"${entry.plugins.join(\",\")}\",`;\n\n\t\t// For test compatibility, format min/max in microseconds\n\t\tconst minInUs = entry.min / 1000;\n\t\tconst maxInUs = entry.max / 1000;\n\t\ttext += `${minInUs.toFixed(2)}us,`;\n\t\ttext += `${maxInUs.toFixed(2)}us\\n`;\n\t}\n\n\treturn text;\n}\n\nmodule.exports = {\n\tcsvReport,\n\ttoCSV,\n};\n"
  },
  {
    "path": "lib/reporter/html.js",
    "content": "const { platform, arch, availableParallelism, totalmem } = require(\"node:os\");\nconst fs = require(\"node:fs\");\nconst path = require(\"node:path\");\nconst { summarize } = require(\"../utils/analyze\");\nconst { timer } = require(\"../clock\");\n\nconst formatter = Intl.NumberFormat(undefined, {\n\tnotation: \"standard\",\n\tmaximumFractionDigits: 3,\n});\n\nconst timeNumberFormat = Intl.NumberFormat(undefined, {\n\tminimumFractionDigits: 3,\n\tmaximumFractionDigits: 3,\n});\n\nconst valueToDuration = (maxValue, value, isTimeBased, scalingFactor = 10) => {\n\tconst normalizedValue = isTimeBased ? maxValue / value : value / maxValue;\n\tconst baseSpeed = (1 / normalizedValue) * scalingFactor;\n\treturn Math.max(baseSpeed, 2); // Normalize speed with a minimum of 2 seconds\n};\n\nconst generateHTML = (template, durations) => {\n\tlet css = \"\";\n\tlet labelDiv = \"\";\n\tlet circleDiv = \"\";\n\tlet position = 20;\n\tconst colors = [\n\t\t\"blue\",\n\t\t\"orange\",\n\t\t\"yellow\",\n\t\t\"purple\",\n\t\t\"black\",\n\t\t\"grey\",\n\t\t\"red\",\n\t\t\"green\",\n\t\t\"pink\",\n\t\t\"cyan\",\n\t];\n\tfor (const d of durations) {\n\t\tcss += `\n      #label-${d.name} {\n        top: ${position}px;\n      }\n\n      #circle-${d.name} {\n        background-color: ${colors.shift()};\n        top: ${position}px;\n      }\n    `;\n\t\tcircleDiv += `\n      <div id=\"label-${d.name}\" class=\"label\">\n\t  ${d.name}(<span class=\"number\">${d.metricValueFormatted}</span> ${d.metricUnit})\n\t  <br><span class=\"details\">min: ${d.minFormatted}, max: ${d.maxFormatted}</span>\n\t  </div>\n    `;\n\t\tlabelDiv += `\n      <div id=\"circle-${d.name}\" class=\"circle\"></div>\n    `;\n\n\t\tposition += 80;\n\t}\n\n\tconst environmentDiv = `<p>Node.js version: <span class=\"number\">${process.version}</span></p>\n\t<p>Platform: <span class=\"number\">${platform()} ${arch()}</span></p>\n\t<p>CPU Cores: <span class=\"number\">${availableParallelism()}</span> vCPUs | <span class=\"number\">${(totalmem() / 1024 ** 3).toFixed(1)}GB Mem</span></p>`;\n\n\treturn template\n\t\t.replaceAll(\"{{CONTAINER_HEIGHT}}\", `${durations.length * 100}px;`)\n\t\t.replaceAll(\"{{CSS}}\", css)\n\t\t.replaceAll(\"{{ENVIRONMENT_DIV}}\", environmentDiv)\n\t\t.replaceAll(\"{{LABEL_DIV}}\", labelDiv)\n\t\t.replaceAll(\"{{CIRCLE_DIV}}\", circleDiv)\n\t\t.replaceAll(\"{{DURATIONS}}\", JSON.stringify(durations));\n};\n\nconst templatePath = path.join(__dirname, \"template.html\");\nconst template = fs.readFileSync(templatePath, \"utf8\");\n\nfunction htmlReport(results) {\n\tconst summary = summarize(results);\n\tconst primaryMetric =\n\t\tresults[0]?.opsSec !== undefined ? \"opsSec\" : \"totalTime\";\n\tlet durations;\n\n\tif (primaryMetric === \"opsSec\") {\n\t\tconst maxOpsSec = Math.max(...summary.map((b) => b.opsSec));\n\t\tdurations = summary.map((r) => ({\n\t\t\tname: r.name.replaceAll(\" \", \"-\"),\n\t\t\tduration: valueToDuration(maxOpsSec, r.opsSec, false),\n\t\t\tmetricValueFormatted: formatter.format(r.opsSec),\n\t\t\tmetricUnit: \"ops/sec\",\n\t\t\tminFormatted: timer.format(r.min), // Use timer for ns format\n\t\t\tmaxFormatted: timer.format(r.max),\n\t\t}));\n\t} else {\n\t\t// metric === 'totalTime'\n\t\tconst maxTotalTime = Math.max(...summary.map((b) => b.totalTime));\n\t\tdurations = summary.map((r) => ({\n\t\t\tname: r.name.replaceAll(\" \", \"-\"),\n\t\t\tduration: valueToDuration(maxTotalTime, r.totalTime, true),\n\t\t\tmetricValueFormatted: r.totalTimeFormatted,\n\t\t\tmetricUnit: \"total time\",\n\t\t\tminFormatted: timer.format(r.min), // Use timer for ns format\n\t\t\tmaxFormatted: timer.format(r.max),\n\t\t}));\n\t}\n\n\tconst htmlContent = generateHTML(template, durations);\n\tfs.writeFileSync(\"result.html\", htmlContent, \"utf8\");\n\tprocess.stdout.write(\"HTML file has been generated: result.html\\n\");\n}\n\nmodule.exports = {\n\thtmlReport,\n};\n"
  },
  {
    "path": "lib/reporter/json.js",
    "content": "const { summarize } = require(\"../utils/analyze\");\n\n/**\n * Outputs JSON formatted results the console\n * @param {BenchmarkResult[]} results - Array of benchmark results\n */\n\nfunction jsonReport(results) {\n\tprocess.stdout.write(toJSON(results));\n}\n\n/**\n * Outputs JSON formatted results as a string\n * @param {BenchmarkResult[]} results - Array of benchmark results\n * @returns {string}\n */\nfunction toJSON(results) {\n\tconst output = summarize(results).map((result) => ({\n\t\tname: result.name,\n\t\trunsSampled: result.runsSampled,\n\t\tmin: result.minFormatted,\n\t\tminNS: result.min,\n\t\tmax: result.maxFormatted,\n\t\tmaxNS: result.max,\n\t\tplugins: result.plugins.map((p) => p.report).filter(Boolean),\n\t\topsSec: result.opsSec,\n\t\ttotalTime: result.totalTime,\n\t\ttotalTimeFormatted: result.totalTimeFormatted,\n\t}));\n\n\treturn JSON.stringify(output, null, 2);\n}\n\nmodule.exports = {\n\tjsonReport,\n\ttoJSON,\n};\n"
  },
  {
    "path": "lib/reporter/pretty.js",
    "content": "const { styleText } = require(\"../utils/styleText\");\nconst os = require(\"node:os\");\nconst { analyze, summarize } = require(\"../utils/analyze.js\");\n\nconst formatter = Intl.NumberFormat(undefined, {\n\tnotation: \"standard\",\n\tmaximumFractionDigits: 2,\n});\n\nconst BOX_VERTICAL = \"│\";\nconst BOX_HORIZONTAL = \"─\";\nconst BOX_CORNER_BOTTOM_LEFT = \"└\";\nconst BOX_TEE_RIGHT = \"├\";\n\n/**\n * Pretty print format a report.\n * @param {BenchmarkResult[]} results\n */\nfunction prettyReport(results, options = {}) {\n\tprocess.stdout.write(toPretty(results, options));\n}\n\n/**\n * Pretty print format a report.\n * @param {BenchmarkResult[]} results\n * @returns {string} the formatted report\n */\nfunction toPretty(results, options = {}) {\n\tif (results.length === 0) {\n\t\treturn \"\";\n\t}\n\n\tconst cpu = os.cpus()[0];\n\tconst ttest = options?.ttest ?? false;\n\n\tlet text = styleText(\"bold\", \"\\nSystem Information:\\n\");\n\ttext += `  Node.js: ${process.version}\\n`;\n\ttext += `  OS: ${os.platform()} ${os.release()}\\n`;\n\ttext += `  CPU: ${cpu.model}\\n`;\n\n\tif (ttest) {\n\t\ttext += styleText(\"cyan\", \"\\nT-Test Mode: Enabled (repeatSuite=30)\\n\");\n\t}\n\n\tconst hasBaseline = results.find((result) => result.baseline) !== undefined;\n\n\tif (hasBaseline) {\n\t\ttext += `\\nLegend: ${styleText(\"magenta\", \"■\")} Baseline\\n\\n`;\n\t}\n\n\ttext += styleText(\"bold\", `Benchmark results (${results.length} total):\\n`);\n\n\tconst firstResult = results[0];\n\tif (firstResult && firstResult.plugins.length > 0) {\n\t\tconst pluginNames = firstResult.plugins.map((p) => p.name).join(\", \");\n\t\ttext += styleText(\"dim\", `Plugins enabled: ${pluginNames}\\n`);\n\t}\n\n\tconst tree = buildTree(results);\n\ttext += formatTree(tree);\n\n\tif (hasBaseline) {\n\t\ttext += styleText(\"bold\", \"\\n\\nSummary (vs. baseline):\\n\");\n\n\t\tconst sortedResults = analyze(results, true, {\n\t\t\tttest,\n\t\t\talpha: options?.alpha ?? 0.05,\n\t\t});\n\t\tconst maxNameLength = Math.max(...sortedResults.map((r) => r.name.length));\n\n\t\tfor (const result of sortedResults) {\n\t\t\tconst namePart = `  ${result.name.padEnd(maxNameLength)}  `;\n\t\t\ttext += namePart;\n\n\t\t\tif (result.baseline) {\n\t\t\t\ttext += styleText(\"magenta\", \"(baseline)\");\n\t\t\t} else {\n\t\t\t\tconst isSignificant = result.significanceTest?.significant ?? true;\n\t\t\t\tconst isFaster = !result.comparison.startsWith(\"-\");\n\t\t\t\tconst comparisonText = isFaster\n\t\t\t\t\t? `(${result.comparison}x faster)`\n\t\t\t\t\t: `(${result.comparison.slice(1)}x slower)`;\n\n\t\t\t\tif (isSignificant) {\n\t\t\t\t\ttext += styleText(isFaster ? \"green\" : \"red\", comparisonText);\n\t\t\t\t} else {\n\t\t\t\t\ttext += styleText(\"dim\", comparisonText);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (result.significanceTest) {\n\t\t\t\tconst sig = result.significanceTest;\n\t\t\t\tif (sig.stars) {\n\t\t\t\t\ttext += styleText(\"cyan\", ` ${sig.stars}`);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\ttext += \"\\n\";\n\t\t}\n\n\t\tif (ttest) {\n\t\t\ttext += styleText(\n\t\t\t\t\"dim\",\n\t\t\t\t\"\\n  Significance: * p<0.05, ** p<0.01, *** p<0.001\\n\",\n\t\t\t);\n\t\t}\n\t}\n\n\ttext += \"\\n\";\n\n\treturn text;\n}\n\nfunction buildTree(results) {\n\tconst tree = { children: new Map() };\n\tconst summary = summarize(results);\n\n\tfor (const result of summary) {\n\t\tconst parts = result.name.split(\"/\");\n\t\tlet currentNode = tree;\n\n\t\tfor (let i = 0; i < parts.length; i++) {\n\t\t\tconst part = parts[i];\n\t\t\tif (!currentNode.children.has(part)) {\n\t\t\t\tcurrentNode.children.set(part, { children: new Map() });\n\t\t\t}\n\t\t\tcurrentNode = currentNode.children.get(part);\n\n\t\t\tif (i === parts.length - 1) {\n\t\t\t\tcurrentNode.result = result;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn tree;\n}\n\nfunction formatTree(node, prefix = \"\", isLast = true) {\n\tconst children = Array.from(node.children.entries());\n\n\tlet tree = \"\";\n\n\tfor (let i = 0; i < children.length; i++) {\n\t\tconst [name, childNode] = children[i];\n\t\tconst isCurrentLast = i === children.length - 1;\n\t\tconst connector = isCurrentLast\n\t\t\t? `${BOX_CORNER_BOTTOM_LEFT}${BOX_HORIZONTAL}`\n\t\t\t: `${BOX_TEE_RIGHT}${BOX_HORIZONTAL}`;\n\n\t\tconst prefixStyled = styleText(\"dim\", `${prefix}${connector}`);\n\n\t\ttree += prefixStyled;\n\n\t\tif (!childNode.result) {\n\t\t\t// This is a group\n\t\t\ttree += ` ${styleText([\"bold\", \"yellow\"], name)}`;\n\t\t} else {\n\t\t\tconst style = [\"bold\"];\n\t\t\tif (childNode.result.baseline) {\n\t\t\t\tstyle.push(\"magenta\");\n\t\t\t}\n\n\t\t\tconst nameStyled = ` ${styleText(style, name)}`;\n\t\t\ttree += nameStyled;\n\t\t\tconst prefixUnstyled = `${prefix}${connector}`;\n\t\t\tconst nameUnstyled = ` ${name}`;\n\t\t\tconst unstyledLength = prefixUnstyled.length + nameUnstyled.length;\n\t\t\ttree += resultLine(childNode.result, unstyledLength);\n\t\t}\n\n\t\ttree += \"\\n\";\n\n\t\tconst newPrefix = `${prefix}${isCurrentLast ? \"  \" : `${BOX_VERTICAL} `}`;\n\t\tif (childNode.children.size > 0) {\n\t\t\ttree += formatTree(childNode, newPrefix, isCurrentLast);\n\t\t}\n\t}\n\n\treturn tree;\n}\n\nfunction resultLine(result, prefixLength) {\n\tconst PADDING = 55;\n\tconst padding = PADDING - prefixLength > 0 ? PADDING - prefixLength : 0;\n\n\tlet line = \" \".repeat(padding);\n\n\tconst color = \"blue\";\n\n\tif (result.opsSec !== undefined) {\n\t\tline += styleText([\"bold\"], `${formatter.format(result.opsSec)} ops/sec`);\n\t} else if (result.totalTime !== undefined) {\n\t\tline += styleText([color, \"bold\"], `${result.timeFormatted} total time`);\n\t}\n\n\tif (result.runsSampled) {\n\t\tline += ` (${result.runsSampled} runs sampled) `;\n\t\tline += \"min..max=(\";\n\t\tline += styleText(\"green\", result.minFormatted);\n\t\tline += styleText(\"dim\", \"...\");\n\t\tline += styleText(\"red\", result.maxFormatted);\n\t\tline += \")\";\n\t}\n\n\treturn line;\n}\n\nmodule.exports = {\n\tprettyReport,\n\ttoPretty,\n};\n"
  },
  {
    "path": "lib/reporter/template.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n  <meta charset=\"UTF-8\">\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n  <title>Benchmark Visualizer</title>\n  <style>\n    body {\n      display: flex;\n      flex-direction: column;\n      justify-content: center;\n      align-items: center;\n      height: 100vh;\n      margin: 0;\n      background: #f4f4f4;\n      font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;\n    }\n    .env {\n      width: 400px;\n    }\n\n    .container {\n      position: relative;\n      width: 400px; /* Reduced width */\n      height: {{CONTAINER_HEIGHT}}\n      border: 2px solid #000;\n      background: #fff;\n    }\n\n    .label {\n      position: absolute;\n      left: -130px; /* Place labels outside the box */\n      width: 100px;\n      text-align: right;\n      font-size: 14px;\n    }\n\n    .circle {\n      width: 40px;\n      height: 40px;\n      border-radius: 50%;\n      position: absolute;\n    }\n\n    .number {\n      font-family: 'Times New Roman', Times, serif;\n    }\n\n    .made-with {\n      position: fixed;\n      bottom: 10px;\n      right: 10px;\n      padding: 10px;\n      font-size: 12px;\n    }\n    .made-with a {\n      color: #2C682C;\n    }\n    .made-with a:hover {\n      text-decoration: none;\n    }\n\n    {{CSS}}\n\n    /* Dark theme */\n    @media (prefers-color-scheme: dark) {\n      body {\n        background: #333;\n        color: #fff;\n      }\n\n      .container {\n        border-color: #fff;\n        background: #444;\n      }\n\n      .made-with a {\n        color: #84BA64;\n      }\n    }\n  </style>\n</head>\n<body>\n  <div class=\"env\">\n    {{ENVIRONMENT_DIV}}\n  </div>  \n\n  <div class=\"container\">\n    {{LABEL_DIV}}\n\n    {{CIRCLE_DIV}}\n  </div>\n\n  <div class=\"made-with\">\n    <p>Benchmark done with <a href=\"https://github.com/RafaelGSS/bench-node\">bench-node</a></p>\n  </div>\n\n  </div>\n\n  <script>\n    const durations = {{DURATIONS}};\n\n    const animateCircles = () => {\n      const boxWidth = 400; // Width of the container box\n      const circles = durations.map((d) => ({\n        id: \"circle-\" + d.name,\n        duration: d.duration,\n        position: 0,\n        direction: 1,\n      }));\n\n      const update = () => {\n        circles.forEach(circle => {\n          const element = document.getElementById(circle.id);\n\n          circle.position += circle.direction * (boxWidth / circle.duration) * 0.5;\n\n          if (circle.position >= boxWidth - 20 || circle.position <= 0) {\n            circle.direction *= -1; // Reverse direction on collision\n          }\n\n          element.style.transform = `translateX(${circle.position}px)`;\n        });\n\n        setTimeout(() => {\n          requestAnimationFrame(update);\n        }, 1000 / 120); // \"60 FPS\"\n      };\n\n      update();\n    };\n\n    animateCircles();\n  </script>\n</body>\n</html>\n"
  },
  {
    "path": "lib/reporter/text.js",
    "content": "const { styleText } = require(\"../utils/styleText\");\nconst { analyze, summarize } = require(\"../utils/analyze.js\");\n\nconst formatter = Intl.NumberFormat(undefined, {\n\tnotation: \"standard\",\n\tmaximumFractionDigits: 2,\n});\n\n/**\n * Generates a text report of benchmark results, displaying each benchmark's name,\n * operations per second, number of samples, plugin results, and min/max timings\n * @param {import('../report').BenchmarkResult[]} results - Array of benchmark results\n * @param options {object} layout options\n */\nfunction textReport(results, options = {}) {\n\tprocess.stdout.write(toText(results, options));\n}\n\nfunction toText(results, options = {}) {\n\tconst summary = summarize(results);\n\tconst ttest = options?.ttest ?? false;\n\n\tlet text = \"\";\n\n\tif (ttest) {\n\t\ttext += styleText(\"cyan\", \"T-Test Mode: Enabled (repeatSuite=30)\\n\\n\");\n\t}\n\n\tfor (const result of summary) {\n\t\ttext += result.name.padEnd(options.labelWidth ?? 45);\n\t\ttext += \" x \";\n\n\t\tif (result.opsSec !== undefined) {\n\t\t\ttext += styleText([\"blue\", \"bold\"], `${localize(result.opsSec)} ops/sec`);\n\t\t} else if (result.totalTime !== undefined) {\n\t\t\ttext += styleText([\"blue\", \"bold\"], `${result.timeFormatted} total time`);\n\t\t}\n\n\t\t// TODO: produce confidence on stddev\n\t\ttext += ` (${result.runsSampled} runs sampled) `;\n\n\t\tfor (const p of result.plugins) {\n\t\t\tif (p.report) {\n\t\t\t\ttext += styleText(\"dim\", `${p.report} `);\n\t\t\t}\n\t\t}\n\n\t\ttext += \"min..max=(\";\n\t\ttext += styleText(\"green\", result.minFormatted);\n\t\ttext += styleText(\"dim\", \"...\");\n\t\ttext += styleText(\"red\", result.maxFormatted);\n\t\ttext += \")\\n\";\n\t}\n\n\tconst baselineResult = results.find((result) => result.baseline);\n\n\tif (baselineResult) {\n\t\ttext += styleText(\"bold\", \"\\nSummary (vs. baseline):\\n\");\n\n\t\tconst sortedResults = analyze(results, true, {\n\t\t\tttest,\n\t\t\talpha: options?.alpha ?? 0.05,\n\t\t});\n\t\tconst maxNameLength = Math.max(...sortedResults.map((r) => r.name.length));\n\n\t\tfor (const result of sortedResults) {\n\t\t\tconst namePart = `  ${result.name.padEnd(maxNameLength)}  `;\n\t\t\ttext += namePart;\n\n\t\t\tif (result.baseline) {\n\t\t\t\ttext += styleText(\"magenta\", \"(baseline)\");\n\t\t\t} else if (result.comparison !== undefined) {\n\t\t\t\tconst isSignificant = result.significanceTest?.significant ?? true;\n\t\t\t\tconst isFaster = !result.comparison.startsWith(\"-\");\n\t\t\t\tconst comparisonText = isFaster\n\t\t\t\t\t? `(${result.comparison}x faster)`\n\t\t\t\t\t: `(${result.comparison.slice(1)}x slower)`;\n\n\t\t\t\tif (isSignificant) {\n\t\t\t\t\ttext += styleText(isFaster ? \"green\" : \"red\", comparisonText);\n\t\t\t\t} else {\n\t\t\t\t\ttext += styleText(\"dim\", comparisonText);\n\t\t\t\t}\n\n\t\t\t\tif (result.significanceTest) {\n\t\t\t\t\tconst sig = result.significanceTest;\n\t\t\t\t\tif (sig.stars) {\n\t\t\t\t\t\ttext += styleText(\"cyan\", ` ${sig.stars}`);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\ttext += \"\\n\";\n\t\t}\n\n\t\tif (ttest) {\n\t\t\ttext += styleText(\n\t\t\t\t\"dim\",\n\t\t\t\t\"\\n  Significance: * p<0.05, ** p<0.01, *** p<0.001\\n\",\n\t\t\t);\n\t\t}\n\t}\n\n\treturn text;\n}\n\nconst numberFormat = new Intl.NumberFormat(\n\tprocess.env.BENCH_NODE_LOCALE_ID || \"en-US\",\n\t{\n\t\tstyle: \"decimal\",\n\t\tuseGrouping: true,\n\t},\n);\nfunction localize(number) {\n\treturn numberFormat.format(number);\n}\n\nmodule.exports = {\n\ttextReport,\n\ttoText,\n};\n"
  },
  {
    "path": "lib/utils/analyze.js",
    "content": "const { timer } = require(\"../clock\");\nconst { compareBenchmarks } = require(\"./ttest\");\n\nfunction analyze(results, sorted = true, options = {}) {\n\tconst { ttest = false, alpha = 0.05 } = options;\n\tconst baselineResult = results.find((result) => result.baseline);\n\n\tconst output = [...results];\n\n\tif (baselineResult?.opsSec === undefined) {\n\t\treturn output;\n\t}\n\n\tconst baselineHz = baselineResult.opsSec;\n\tconst baselineSamples = baselineResult.histogram?.sampleData;\n\tlet min = Number.MAX_SAFE_INTEGER;\n\tlet max = Number.MIN_SAFE_INTEGER;\n\n\tlet fastest;\n\tlet slowest;\n\n\tfor (const result of output) {\n\t\tconst benchmarkHz = result.opsSec;\n\n\t\tif (benchmarkHz === undefined) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (benchmarkHz > max) {\n\t\t\tmax = benchmarkHz;\n\t\t\tfastest = result;\n\t\t}\n\n\t\tif (benchmarkHz < min) {\n\t\t\tmin = benchmarkHz;\n\t\t\tslowest = result;\n\t\t}\n\n\t\tif (!result.baseline) {\n\t\t\tif (benchmarkHz > baselineHz) {\n\t\t\t\tresult.comparison = (benchmarkHz / baselineHz).toFixed(2);\n\t\t\t} else {\n\t\t\t\tresult.comparison = `-${(baselineHz / benchmarkHz).toFixed(2)}`;\n\t\t\t}\n\n\t\t\tif (ttest) {\n\t\t\t\tconst resultSamples = result.opsSecPerRun;\n\t\t\t\tconst baselineSamplesForTest = baselineResult.opsSecPerRun;\n\n\t\t\t\tif (\n\t\t\t\t\tbaselineSamplesForTest?.length >= 30 &&\n\t\t\t\t\tresultSamples?.length >= 30\n\t\t\t\t) {\n\t\t\t\t\tconst ttestResult = compareBenchmarks(\n\t\t\t\t\t\tresultSamples,\n\t\t\t\t\t\tbaselineSamplesForTest,\n\t\t\t\t\t\talpha,\n\t\t\t\t\t);\n\t\t\t\t\tresult.significanceTest = {\n\t\t\t\t\t\tsignificant: ttestResult.significant,\n\t\t\t\t\t\tpValue: ttestResult.pValue,\n\t\t\t\t\t\tconfidence: ttestResult.confidence,\n\t\t\t\t\t\tstars: ttestResult.stars,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tif (fastest !== undefined) {\n\t\tfastest.fastest = true;\n\t}\n\n\tif (slowest !== undefined) {\n\t\tslowest.slowest = true;\n\t}\n\n\tif (sorted) {\n\t\toutput.sort((a, b) => {\n\t\t\tif (a.baseline) return -1;\n\t\t\tif (b.baseline) return 1;\n\t\t\treturn (a.opsSec || 0) - (b.opsSec || 0);\n\t\t});\n\t}\n\n\treturn output;\n}\n\nconst formatter = Intl.NumberFormat(undefined, {\n\tnotation: \"standard\",\n\tmaximumFractionDigits: 2,\n});\n\n// Helper function to format time in appropriate units\nconst formatTime = (time) => {\n\tif (time < 0.000001) {\n\t\t// Less than 1 microsecond, show in nanoseconds\n\t\treturn `${(time * 1000000000).toFixed(2)} ns`;\n\t} else if (time < 0.001) {\n\t\t// Less than 1 millisecond, show in microseconds\n\t\treturn `${(time * 1000000).toFixed(2)} µs`;\n\t} else if (time < 1) {\n\t\t// Less than 1 second, show in milliseconds\n\t\treturn `${(time * 1000).toFixed(2)} ms`;\n\t} else {\n\t\t// 1 second or more, show in seconds\n\t\treturn `${time.toFixed(2)} s`;\n\t}\n};\n\nfunction summarize(results) {\n\treturn results.map((result) => {\n\t\tconst baseResult = {\n\t\t\tname: result.name,\n\t\t\trunsSampled: result.histogram.samples,\n\t\t\tmin: result.histogram.min,\n\t\t\tmax: result.histogram.max,\n\t\t\tminFormatted: timer.format(result.histogram.min), // Use timer for ns format\n\t\t\tmaxFormatted: timer.format(result.histogram.max),\n\t\t\t// Report anything the plugins returned\n\t\t\tplugins: result.plugins.map((p) => p.report).filter(Boolean),\n\t\t};\n\n\t\tif (result.opsSec !== undefined) {\n\t\t\tconst opsSecReported =\n\t\t\t\tresult.opsSec < 100\n\t\t\t\t\t? result.opsSec.toFixed(2)\n\t\t\t\t\t: result.opsSec.toFixed(0);\n\t\t\tbaseResult.opsSec = Number(opsSecReported);\n\t\t} else if (result.totalTime !== undefined) {\n\t\t\tbaseResult.totalTime = result.totalTime; // Total time in seconds\n\t\t\tbaseResult.totalTimeFormatted = formatTime(result.totalTime);\n\t\t}\n\n\t\treturn baseResult;\n\t});\n}\n\nmodule.exports = {\n\tanalyze,\n\tsummarize,\n};\n"
  },
  {
    "path": "lib/utils/styleText.js",
    "content": "const util = require(\"node:util\");\n\nmodule.exports.styleText =\n\ttypeof util.styleText === \"function\"\n\t\t? util.styleText\n\t\t: (_style, text) => text;\n"
  },
  {
    "path": "lib/utils/ttest.js",
    "content": "// Welch's t-test implementation for benchmark comparison\n\nfunction mean(arr) {\n\tif (arr.length === 0) return 0;\n\treturn arr.reduce((sum, val) => sum + val, 0) / arr.length;\n}\n\n// Sample variance with Bessel's correction\nfunction variance(arr, arrMean) {\n\tif (arr.length < 2) return 0;\n\tconst m = arrMean !== undefined ? arrMean : mean(arr);\n\treturn arr.reduce((sum, val) => sum + (val - m) ** 2, 0) / (arr.length - 1);\n}\n\n// Abramowitz and Stegun approximation\nfunction erf(x) {\n\tconst sign = x >= 0 ? 1 : -1;\n\tx = Math.abs(x);\n\n\tconst a1 = 0.254829592;\n\tconst a2 = -0.284496736;\n\tconst a3 = 1.421413741;\n\tconst a4 = -1.453152027;\n\tconst a5 = 1.061405429;\n\tconst p = 0.3275911;\n\n\tconst t = 1.0 / (1.0 + p * x);\n\tconst y =\n\t\t1.0 - ((((a5 * t + a4) * t + a3) * t + a2) * t + a1) * t * Math.exp(-x * x);\n\n\treturn sign * y;\n}\n\n// Lanczos approximation\nfunction lnGamma(z) {\n\tconst g = 7;\n\tconst c = [\n\t\t0.99999999999980993, 676.5203681218851, -1259.1392167224028,\n\t\t771.32342877765313, -176.61502916214059, 12.507343278686905,\n\t\t-0.13857109526572012, 9.9843695780195716e-6, 1.5056327351493116e-7,\n\t];\n\n\tif (z < 0.5) {\n\t\treturn Math.log(Math.PI / Math.sin(Math.PI * z)) - lnGamma(1 - z);\n\t}\n\n\tz -= 1;\n\tlet x = c[0];\n\tfor (let i = 1; i < g + 2; i++) {\n\t\tx += c[i] / (z + i);\n\t}\n\tconst t = z + g + 0.5;\n\treturn (\n\t\t0.5 * Math.log(2 * Math.PI) + (z + 0.5) * Math.log(t) - t + Math.log(x)\n\t);\n}\n\n// Incomplete beta function for t-distribution CDF\nfunction incompleteBeta(a, b, x) {\n\tif (x === 0) return 0;\n\tif (x === 1) return 1;\n\n\t// Symmetry relation for stability\n\tif (x > (a + 1) / (a + b + 2)) {\n\t\treturn 1 - incompleteBeta(b, a, 1 - x);\n\t}\n\n\tconst lnBeta = lnGamma(a) + lnGamma(b) - lnGamma(a + b);\n\tconst front = Math.exp(Math.log(x) * a + Math.log(1 - x) * b - lnBeta) / a;\n\n\t// Lentz's algorithm\n\tconst maxIterations = 200;\n\tconst epsilon = 1e-14;\n\n\tlet f = 1;\n\tlet c = 1;\n\tlet d = 0;\n\n\tfor (let m = 0; m <= maxIterations; m++) {\n\t\tlet numerator;\n\n\t\tif (m === 0) {\n\t\t\tnumerator = 1;\n\t\t} else if (m % 2 === 0) {\n\t\t\tconst k = m / 2;\n\t\t\tnumerator = (k * (b - k) * x) / ((a + 2 * k - 1) * (a + 2 * k));\n\t\t} else {\n\t\t\tconst k = (m - 1) / 2;\n\t\t\tnumerator =\n\t\t\t\t-((a + k) * (a + b + k) * x) / ((a + 2 * k) * (a + 2 * k + 1));\n\t\t}\n\n\t\td = 1 + numerator * d;\n\t\tif (Math.abs(d) < epsilon) d = epsilon;\n\t\td = 1 / d;\n\n\t\tc = 1 + numerator / c;\n\t\tif (Math.abs(c) < epsilon) c = epsilon;\n\n\t\tconst delta = c * d;\n\t\tf *= delta;\n\n\t\tif (Math.abs(delta - 1) < epsilon) {\n\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn front * (f - 1);\n}\n\nfunction tDistCdf(t, df) {\n\tconst x = df / (df + t * t);\n\tconst prob = 0.5 * incompleteBeta(df / 2, 0.5, x);\n\n\treturn t >= 0 ? 1 - prob : prob;\n}\n\nfunction welchTTest(sample1, sample2) {\n\tconst n1 = sample1.length;\n\tconst n2 = sample2.length;\n\n\tif (n1 < 2 || n2 < 2) {\n\t\treturn {\n\t\t\ttStatistic: 0,\n\t\t\tdegreesOfFreedom: 0,\n\t\t\tpValue: 1,\n\t\t\tsignificant: false,\n\t\t\tmean1: n1 > 0 ? mean(sample1) : 0,\n\t\t\tmean2: n2 > 0 ? mean(sample2) : 0,\n\t\t\tvariance1: 0,\n\t\t\tvariance2: 0,\n\t\t};\n\t}\n\n\tconst mean1 = mean(sample1);\n\tconst mean2 = mean(sample2);\n\n\tconst var1 = variance(sample1, mean1);\n\tconst var2 = variance(sample2, mean2);\n\n\t// Standard error of the difference\n\tconst se1 = var1 / n1;\n\tconst se2 = var2 / n2;\n\tconst seTotal = se1 + se2;\n\n\t// Handle edge case where both variances are 0\n\tif (seTotal === 0) {\n\t\treturn {\n\t\t\ttStatistic: 0,\n\t\t\tdegreesOfFreedom: n1 + n2 - 2,\n\t\t\tpValue: mean1 === mean2 ? 1 : 0,\n\t\t\tsignificant: mean1 !== mean2,\n\t\t\tmean1,\n\t\t\tmean2,\n\t\t\tvariance1: var1,\n\t\t\tvariance2: var2,\n\t\t};\n\t}\n\n\t// Welch's t-statistic\n\tconst t = (mean1 - mean2) / Math.sqrt(seTotal);\n\n\t// Welch-Satterthwaite degrees of freedom approximation\n\tconst df =\n\t\t(seTotal * seTotal) / ((se1 * se1) / (n1 - 1) + (se2 * se2) / (n2 - 1));\n\n\t// Two-tailed p-value\n\tconst pValue = 2 * (1 - tDistCdf(Math.abs(t), df));\n\n\treturn {\n\t\ttStatistic: t,\n\t\tdegreesOfFreedom: df,\n\t\tpValue,\n\t\tsignificant: pValue < 0.05, // 95% confidence level\n\t\tmean1,\n\t\tmean2,\n\t\tvariance1: var1,\n\t\tvariance2: var2,\n\t};\n}\n\nfunction getSignificanceStars(pValue) {\n\tif (pValue < 0.001) return \"***\";\n\tif (pValue < 0.01) return \"**\";\n\tif (pValue < 0.05) return \"*\";\n\treturn \"\";\n}\n\nfunction compareBenchmarks(sample1, sample2, alpha = 0.05) {\n\tconst result = welchTTest(sample1, sample2);\n\n\tlet difference = \"same\";\n\tif (result.significant) {\n\t\tdifference = result.mean1 > result.mean2 ? \"faster\" : \"slower\";\n\t}\n\n\tconst confidence = ((1 - result.pValue) * 100).toFixed(2);\n\tconst stars = getSignificanceStars(result.pValue);\n\n\treturn {\n\t\tsignificant: result.pValue < alpha,\n\t\tpValue: result.pValue,\n\t\tconfidence: `${confidence}%`,\n\t\tstars,\n\t\tdifference,\n\t\ttStatistic: result.tStatistic,\n\t\tdegreesOfFreedom: result.degreesOfFreedom,\n\t};\n}\n\nmodule.exports = {\n\tmean,\n\tvariance,\n\twelchTTest,\n\tcompareBenchmarks,\n\ttDistCdf,\n\tgetSignificanceStars,\n};\n"
  },
  {
    "path": "lib/validators.js",
    "content": "/**\n * Creates an error object with ERR_INVALID_ARG_TYPE code\n * @param {string} message - The error message\n * @returns {Error} Error with code ERR_INVALID_ARG_TYPE\n */\nfunction ERR_INVALID_ARG_TYPE(message) {\n\tconst err = new Error(message);\n\terr.code = \"ERR_INVALID_ARG_TYPE\";\n\treturn err;\n}\n\n/**\n * Creates an error object with ERR_INVALID_ARG_VALUE code\n * @param {string} message - The error message\n * @returns {Error} Error with code ERR_INVALID_ARG_VALUE\n */\nfunction ERR_INVALID_ARG_VALUE(message) {\n\tconst err = new Error(message);\n\terr.code = \"ERR_INVALID_ARG_VALUE\";\n\treturn err;\n}\n\n/**\n * Validates that a value is a number within an optional range\n * @param {any} value - The value to validate\n * @param {string} name - Name of the parameter being validated\n * @param {number} [min] - Optional minimum value (inclusive)\n * @param {number} [max] - Optional maximum value (inclusive)\n * @throws {Error} If validation fails\n */\nfunction validateNumber(value, name, min, max) {\n\tif (typeof value !== \"number\")\n\t\tthrow ERR_INVALID_ARG_TYPE(\n\t\t\t`value must be a number, name: ${name}, value: ${value}`,\n\t\t);\n\n\tif (\n\t\t(min != null && value < min) ||\n\t\t(max != null && value > max) ||\n\t\t((min != null || max != null) && Number.isNaN(value))\n\t) {\n\t\tthrow ERR_INVALID_ARG_VALUE(\n\t\t\t`value must be ${min != null ? `>= ${min}` : \"\"}${min != null && max != null ? \" && \" : \"\"}${max != null ? `<= ${max}` : \"\"}, name: ${name}, value: ${value}`,\n\t\t);\n\t}\n}\n\n/**\n * Validates that a value is an object (not null and not an array)\n * @param {any} value - The value to validate\n * @param {string} name - Name of the parameter being validated\n * @throws {Error} If validation fails\n */\nfunction validateObject(value, name) {\n\tif (value === null || Array.isArray(value)) {\n\t\tthrow ERR_INVALID_ARG_TYPE(\n\t\t\t`value must be an object, name: ${name}, value: ${value}`,\n\t\t);\n\t}\n\n\tif (typeof value !== \"object\") {\n\t\tthrow ERR_INVALID_ARG_TYPE(\n\t\t\t`value must be an object, name: ${name}, value: ${value}`,\n\t\t);\n\t}\n}\n\n/**\n * Validates that a value is a function\n * @param {any} value - The value to validate\n * @param {string} name - Name of the parameter being validated\n * @throws {Error} If validation fails\n */\nfunction validateFunction(value, name) {\n\tif (typeof value !== \"function\")\n\t\tthrow ERR_INVALID_ARG_TYPE(\n\t\t\t`value must be a function, name: ${name}, value: ${value}`,\n\t\t);\n}\n\n/**\n * Validates that a value is a string\n * @param {any} value - The value to validate\n * @param {string} name - Name of the parameter being validated\n * @throws {Error} If validation fails\n */\nfunction validateString(value, name) {\n\tif (typeof value !== \"string\")\n\t\tthrow ERR_INVALID_ARG_TYPE(\n\t\t\t`value must be a string, name: ${name}, value: ${value}`,\n\t\t);\n}\n\n/**\n * Validates that a value is one of the allowed benchmark modes\n * @param {any} value - The value to validate\n * @param {string} name - Name of the parameter being validated\n * @throws {Error} If validation fails\n */\nfunction validateBenchmarkMode(value, name) {\n\tvalidateString(value, name);\n\n\tconst validModes = [\"ops\", \"time\"];\n\tif (!validModes.includes(value)) {\n\t\tthrow ERR_INVALID_ARG_VALUE(\n\t\t\t`value must be one of ${validModes.join(\", \")}, name: ${name}, value: ${value}`,\n\t\t);\n\t}\n}\n\n/**\n * Validates that a value is an array\n * @param {any} value - The value to validate\n * @param {string} name - Name of the parameter being validated\n * @throws {Error} If validation fails\n */\nfunction validateArray(value, name) {\n\tif (!Array.isArray(value))\n\t\tthrow ERR_INVALID_ARG_TYPE(\n\t\t\t`value must be a array, name: ${name}, value: ${value}`,\n\t\t);\n}\n\n/**\n * Validates that a value is a boolean\n * @param {any} value - The value to validate\n * @param {string} name - Name of the parameter being validated\n * @throws {Error} If validation fails\n */\nfunction validateBoolean(value, name) {\n\tif (typeof value !== \"boolean\")\n\t\tthrow ERR_INVALID_ARG_TYPE(\n\t\t\t`value must be a boolean, name: ${name}, value: ${value}`,\n\t\t);\n}\n\nmodule.exports = {\n\tvalidateFunction,\n\tvalidateNumber,\n\tvalidateObject,\n\tvalidateString,\n\tvalidateArray,\n\tvalidateBenchmarkMode,\n\tvalidateBoolean,\n};\n"
  },
  {
    "path": "lib/worker-runner.js",
    "content": "const { parentPort } = require(\"node:worker_threads\");\nconst {\n\trunBenchmark,\n\tgetInitialIterations,\n\trunWarmup,\n} = require(\"./lifecycle\");\nconst { debugBench } = require(\"./clock\");\nconst AsyncFunction = Object.getPrototypeOf(async () => {}).constructor;\n\n// Deserialize the benchmark function\nfunction deserializeBenchmark(benchmark) {\n\tconst { isAsync, hasArg } = benchmark;\n\tconst fnPrototype = isAsync ? AsyncFunction : Function;\n\n\tif (hasArg) {\n\t\tbenchmark.fn = new fnPrototype(\"timer\", benchmark.fn);\n\t} else {\n\t\tbenchmark.fn = new fnPrototype(benchmark.fn);\n\t}\n}\n\nparentPort.on(\n\t\"message\",\n\tasync ({\n\t\tbenchmark,\n\t\tinitialIterations,\n\t\tbenchmarkMode,\n\t\trepeatSuite,\n\t\tminSamples,\n\t}) => {\n\t\tdeserializeBenchmark(benchmark);\n\n\t\tdebugBench(\n\t\t\t`Warmup ${benchmark.name} with minTime=${benchmark.minTime}, maxTime=${benchmark.maxTime}`,\n\t\t);\n\t\tconst initialIteration = await getInitialIterations(benchmark);\n\t\tawait runWarmup(benchmark, initialIteration, {\n\t\t\tminTime: 0.005,\n\t\t\tmaxTime: 0.05,\n\t\t});\n\n\t\tconst result = await runBenchmark(\n\t\t\tbenchmark,\n\t\t\tinitialIterations,\n\t\t\tbenchmarkMode,\n\t\t\trepeatSuite,\n\t\t\tminSamples,\n\t\t);\n\t\tparentPort.postMessage(result);\n\t},\n);\n"
  },
  {
    "path": "package.json",
    "content": "{\n\t\"name\": \"bench-node\",\n\t\"version\": \"0.15.0\",\n\t\"description\": \"\",\n\t\"main\": \"lib/index.js\",\n\t\"types\": \"index.d.ts\",\n\t\"scripts\": {\n\t\t\"test\": \"c8 node --test --allow-natives-syntax --expose-gc && npm run lint:ci\",\n\t\t\"test:types\": \"tsd -f types/types.test-d.ts\",\n\t\t\"lint\": \"biome lint .\",\n\t\t\"lint:ci\": \"biome ci .\",\n\t\t\"lint:fix\": \"biome lint --write .\",\n\t\t\"lint:force-fix\": \"biome lint --write --unsafe .\",\n\t\t\"format\": \"biome format --write .\"\n\t},\n\t\"repository\": {\n\t\t\"type\": \"git\",\n\t\t\"url\": \"git+https://github.com/RafaelGSS/bench-node.git\"\n\t},\n\t\"keywords\": [\n\t\t\"benchmark\",\n\t\t\"nodejs\"\n\t],\n\t\"author\": \"RafaelGSS <rafael.nunu@hotmail.com>\",\n\t\"contributors\": [\n\t\t{\n\t\t\t\"name\": \"H4ad\",\n\t\t\t\"author\": true\n\t\t}\n\t],\n\t\"license\": \"MIT\",\n\t\"bugs\": {\n\t\t\"url\": \"https://github.com/RafaelGSS/bench-node/issues\"\n\t},\n\t\"homepage\": \"https://github.com/RafaelGSS/bench-node#readme\",\n\t\"dependencies\": {\n\t\t\"piscina\": \"^4.8.0\"\n\t},\n\t\"devDependencies\": {\n\t\t\"@biomejs/biome\": \"1.9.4\",\n\t\t\"@types/node\": \"^20\",\n\t\t\"benchmark\": \"^2.1.4\",\n\t\t\"c8\": \"^10.1.3\",\n\t\t\"mitata\": \"^1.0.34\",\n\t\t\"tinybench\": \"^4.1.0\",\n\t\t\"tsd\": \"^0.31.0\"\n\t},\n\t\"c8\": {\n\t\t\"reporter\": \"lcov\"\n\t},\n\t\"files\": [\n\t\t\"lib/\",\n\t\t\"index.d.ts\"\n\t]\n}\n"
  },
  {
    "path": "release-please-config.json",
    "content": "{\n\t\"packages\": {\n\t\t\".\": {\n\t\t\t\"release-type\": \"node\",\n\t\t\t\"bump-minor-pre-major\": true,\n\t\t\t\"bump-patch-for-minor-pre-major\": false,\n\t\t\t\"draft\": false,\n\t\t\t\"prerelease\": false,\n\t\t\t\"draft-pull-request\": true,\n\t\t\t\"include-component-in-tag\": false,\n\t\t\t\"include-v-in-tag\": true,\n\t\t\t\"separate-pull-requests\": false,\n\t\t\t\"skip-github-release\": false,\n\t\t\t\"versioning\": \"default\",\n\t\t\t\"pull-request-header\": \":robot: I have created a release *beep* *boop*\",\n\t\t\t\"pull-request-title-pattern\": \"chore${scope}: release${component} ${version}\",\n\t\t\t\"changelog-path\": \"CHANGELOG.md\",\n\t\t\t\"changelog-host\": \"https://github.com\",\n\t\t\t\"changelog-type\": \"default\",\n\t\t\t\"changelog-sections\": [\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"feat\",\n\t\t\t\t\t\"section\": \"Features\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"feature\",\n\t\t\t\t\t\"section\": \"Features\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"fix\",\n\t\t\t\t\t\"section\": \"Bug Fixes\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"perf\",\n\t\t\t\t\t\"section\": \"Performance Improvements\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"revert\",\n\t\t\t\t\t\"section\": \"Reverts\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"docs\",\n\t\t\t\t\t\"section\": \"Documentation\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"style\",\n\t\t\t\t\t\"section\": \"Styles\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"chore\",\n\t\t\t\t\t\"section\": \"Miscellaneous Chores\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"refactor\",\n\t\t\t\t\t\"section\": \"Code Refactoring\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"test\",\n\t\t\t\t\t\"section\": \"Tests\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"build\",\n\t\t\t\t\t\"section\": \"Build System\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"ci\",\n\t\t\t\t\t\"section\": \"Continuous Integration\"\n\t\t\t\t}\n\t\t\t]\n\t\t}\n\t},\n\t\"$schema\": \"https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json\"\n}\n"
  },
  {
    "path": "test/async.js",
    "content": "const { todo } = require(\"node:test\");\n\ntodo(\"async tasks should behave similar to sync tasks\", async () => {});\n"
  },
  {
    "path": "test/basic.js",
    "content": "const { Suite } = require(\"../lib/index\");\nconst { describe, it, todo } = require(\"node:test\");\nconst assert = require(\"node:assert\");\nconst { spawnSync } = require(\"node:child_process\");\nconst path = require(\"node:path\");\n\nfunction noop() {}\n\ndescribe(\"API Interface\", () => {\n\tit(\"options should be an object\", () => {\n\t\tfor (const r of [1, \"ds\", null]) {\n\t\t\tassert.throws(\n\t\t\t\t() => {\n\t\t\t\t\tnew Suite(r);\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tcode: \"ERR_INVALID_ARG_TYPE\",\n\t\t\t\t},\n\t\t\t);\n\t\t}\n\t\t// doesNotThrow\n\t\tnew Suite({});\n\t});\n\n\tit(\"reporter should be a function\", () => {\n\t\tfor (const r of [1, \"ds\", {}]) {\n\t\t\tassert.throws(\n\t\t\t\t() => {\n\t\t\t\t\tnew Suite({ reporter: r });\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tcode: \"ERR_INVALID_ARG_TYPE\",\n\t\t\t\t},\n\t\t\t);\n\t\t}\n\t\t// doesNotThrow\n\t\tnew Suite({ reporter: () => {} });\n\t});\n\n\tit(\"reporter can be false or null\", () => {\n\t\tfor (const r of [false, null]) {\n\t\t\t// doesNotThrow\n\t\t\tnew Suite({ reporter: r });\n\t\t}\n\t});\n\n\tit(\"suite-level minSamples should be a valid number\", () => {\n\t\tfor (const r of [\"ds\", {}, () => {}]) {\n\t\t\tassert.throws(\n\t\t\t\t() => {\n\t\t\t\t\tnew Suite({ minSamples: r });\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tcode: \"ERR_INVALID_ARG_TYPE\",\n\t\t\t\t},\n\t\t\t);\n\t\t}\n\t\t// doesNotThrow\n\t\tnew Suite({ minSamples: 20 });\n\t});\n\n\tdescribe(\"suite.add\", () => {\n\t\tconst bench = new Suite({ reporter: noop });\n\t\tit(\"name should be an string\", () => {\n\t\t\tfor (const r of [1, undefined, null, {}]) {\n\t\t\t\tassert.throws(() => {\n\t\t\t\t\tbench.add(r);\n\t\t\t\t});\n\t\t\t}\n\t\t\t// doesNotThrow\n\t\t\tbench.add(\"example\", noop);\n\t\t});\n\n\t\tit(\"options should be an valid object\", () => {\n\t\t\tfor (const r of [1, \"ds\", null]) {\n\t\t\t\tassert.throws(\n\t\t\t\t\t() => {\n\t\t\t\t\t\tbench.add(\"name\", r, noop);\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tcode: \"ERR_INVALID_ARG_TYPE\",\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t}\n\t\t});\n\n\t\tit(\"minTime should be a valid number\", () => {\n\t\t\tfor (const r of [\"ds\", {}, () => {}]) {\n\t\t\t\tassert.throws(\n\t\t\t\t\t() => {\n\t\t\t\t\t\tbench.add(\"name\", { minTime: r }, noop);\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tcode: \"ERR_INVALID_ARG_TYPE\",\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t}\n\t\t\tassert.throws(\n\t\t\t\t() => {\n\t\t\t\t\tbench.add(\"name\", { minTime: 0 }, noop);\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tcode: \"ERR_INVALID_ARG_VALUE\",\n\t\t\t\t},\n\t\t\t);\n\t\t\tassert.throws(\n\t\t\t\t() => {\n\t\t\t\t\tbench.add(\"name\", { minTime: 0.000001 }, noop);\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tcode: \"ERR_INVALID_ARG_VALUE\",\n\t\t\t\t},\n\t\t\t);\n\t\t\t// doesNotThrow\n\t\t\tbench.add(\"name\", { minTime: 0.5 }, noop);\n\t\t});\n\n\t\tit(\"maxTime should be a valid number\", () => {\n\t\t\tfor (const r of [\"ds\", {}, () => {}]) {\n\t\t\t\tassert.throws(\n\t\t\t\t\t() => {\n\t\t\t\t\t\tbench.add(\"name\", { minTime: r }, noop);\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tcode: \"ERR_INVALID_ARG_TYPE\",\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t}\n\t\t});\n\t\tit(\"maxTime should be greater than minTime\", () => {\n\t\t\tassert.throws(\n\t\t\t\t() => {\n\t\t\t\t\tbench.add(\"name\", { maxTime: 0 }, noop);\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tcode: \"ERR_INVALID_ARG_VALUE\",\n\t\t\t\t},\n\t\t\t);\n\t\t\tassert.throws(\n\t\t\t\t() => {\n\t\t\t\t\tbench.add(\"name\", { maxTime: 0.1, minTime: 0.2 }, noop);\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tcode: \"ERR_INVALID_ARG_VALUE\",\n\t\t\t\t},\n\t\t\t);\n\t\t\t// doesNotThrow\n\t\t\tbench.add(\"name\", { minTime: 0.01, maxTime: 0.02 }, noop);\n\t\t});\n\n\t\tit(\"fn should be a function\", () => {\n\t\t\tfor (const r of [\"ds\", {}, 42]) {\n\t\t\t\tassert.throws(\n\t\t\t\t\t() => {\n\t\t\t\t\t\tbench.add(\"name\", {}, r);\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tcode: \"ERR_INVALID_ARG_TYPE\",\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t}\n\t\t\t// doesNotThrow\n\t\t\tbench.add(\"name\", noop);\n\t\t});\n\n\t\tit(\"repeatSuite should be a valid number\", () => {\n\t\t\tfor (const r of [\"ds\", {}, () => {}]) {\n\t\t\t\tassert.throws(\n\t\t\t\t\t() => {\n\t\t\t\t\t\tbench.add(\"name\", { repeatSuite: r }, noop);\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tcode: \"ERR_INVALID_ARG_TYPE\",\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t}\n\t\t});\n\n\t\tit(\"minSamples should be a valid number\", () => {\n\t\t\tfor (const r of [\"ds\", {}, () => {}]) {\n\t\t\t\tassert.throws(\n\t\t\t\t\t() => {\n\t\t\t\t\t\tbench.add(\"name\", { minSamples: r }, noop);\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tcode: \"ERR_INVALID_ARG_TYPE\",\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t}\n\t\t});\n\t});\n});\n\ndescribe(\"simple usage\", async () => {\n\tconst bench = new Suite({ reporter: noop });\n\tbench\n\t\t.add(\"foo\", async () => {\n\t\t\tawait new Promise((resolve) => setTimeout(resolve, 50));\n\t\t})\n\t\t.add(\"bar\", async () => {\n\t\t\tawait new Promise((resolve) => setTimeout(resolve, 100));\n\t\t});\n\n\tconst [bench1, bench2] = await bench.run();\n\n\tit(\"benchmark name should be returned in results\", () => {\n\t\tassert.strictEqual(bench1.name, \"foo\");\n\t\tassert.strictEqual(bench2.name, \"bar\");\n\t});\n\n\tit(\"ops/sec should match the expected duration\", () => {\n\t\t// 1000(ms)/50 = 20 + cost of creating promises\n\t\tassert.ok(bench1.opsSec > 18 && bench1.opsSec <= 20);\n\t\t// 1000(ms)/100 = 100 + cost of creating promises\n\t\tassert.ok(bench2.opsSec > 8 && bench2.opsSec <= 10);\n\t});\n\n\tit(\"tasks should have at least 10 samples\", () => {\n\t\tassert.ok(bench1.iterations >= 10);\n\t\tassert.ok(bench2.iterations >= 10);\n\t});\n\n\tit(\"opsSecPerRun should exist when repeatSuite is 1\", async () => {\n\t\tassert.ok(\n\t\t\tArray.isArray(bench1.opsSecPerRun),\n\t\t\t\"opsSecPerRun should be an array\",\n\t\t);\n\t\tassert.ok(\n\t\t\tArray.isArray(bench2.opsSecPerRun),\n\t\t\t\"opsSecPerRun should be an array\",\n\t\t);\n\t});\n});\n\ndescribe(\"repeat suite\", async () => {\n\tconst repeatCount = 3;\n\tconst bench = new Suite({ reporter: noop });\n\tbench.add(\"Repeat ops test\", { repeatSuite: repeatCount }, () => {\n\t\t// Simple operation\n\t\tconst x = 1 + 1;\n\t});\n\n\tconst results = await bench.run();\n\n\tit(\"should include opsSecPerRun when repeatSuite is greater than 1\", async () => {\n\t\tassert.strictEqual(results.length, 1);\n\t\tassert.ok(results[0].opsSec !== undefined, \"opsSec should be defined\");\n\t\tassert.ok(\n\t\t\tArray.isArray(results[0].opsSecPerRun),\n\t\t\t\"opsSecPerRun should be an array\",\n\t\t);\n\t\tassert.strictEqual(\n\t\t\tresults[0].opsSecPerRun.length,\n\t\t\trepeatCount,\n\t\t\t`opsSecPerRun should have ${repeatCount} elements`,\n\t\t);\n\t\tfor (const opsSec of results[0].opsSecPerRun) {\n\t\t\tassert.ok(\n\t\t\t\ttypeof opsSec === \"number\" && !Number.isNaN(opsSec),\n\t\t\t\t\"each element should be a valid number\",\n\t\t\t);\n\t\t}\n\t});\n});\n\ndescribe(\"throws when a benchmark task throw\", async () => {\n\tconst bench = new Suite();\n\tconst err = new Error();\n\n\tbench.add(\"error\", () => {\n\t\tthrow err;\n\t});\n\tassert.rejects(() => bench.run());\n});\n\ndescribe(\"when no --allow-natives-syntax\", async () => {\n\tit(\"should throw\", () => {\n\t\tconst file = path.join(__dirname, \"fixtures\", \"bench.js\");\n\t\tconst { status, stderr } = spawnSync(process.execPath, [file]);\n\t\tassert.strictEqual(status, 1);\n\t\tassert.match(\n\t\t\tstderr.toString(),\n\t\t\t/bench-node module must be run with --allow-natives-syntax/,\n\t\t);\n\t});\n});\n\ntodo(\"histogram values\", async () => {});\n"
  },
  {
    "path": "test/env.js",
    "content": "const { describe, it, before } = require(\"node:test\");\nconst assert = require(\"node:assert\");\nconst { Suite } = require(\"../lib\");\nconst copyBench = require(\"./fixtures/copy\");\nconst { managedBench, managedOptBench } = require(\"./fixtures/opt-managed\");\n\nfunction assertMinBenchmarkDifference(\n\tresults,\n\t{ percentageLimit, ciPercentageLimit },\n) {\n\tassertBenchmarkDifference(results, {\n\t\tpercentageLimit,\n\t\tciPercentageLimit,\n\t\tgreaterThan: true,\n\t});\n}\n\nfunction assertMaxBenchmarkDifference(\n\tresults,\n\t{ percentageLimit, ciPercentageLimit },\n) {\n\tassertBenchmarkDifference(results, {\n\t\tpercentageLimit,\n\t\tciPercentageLimit,\n\t\tgreaterThan: false,\n\t});\n}\n\nfunction assertBenchmarkDifference(\n\tresults,\n\t{ percentageLimit, ciPercentageLimit, greaterThan },\n) {\n\tfor (let i = 0; i < results.length; i++) {\n\t\tfor (let j = 0; j < results.length; j++) {\n\t\t\tif (i !== j) {\n\t\t\t\tconst opsSec1 = results[i].opsSec;\n\t\t\t\tconst opsSec2 = results[j].opsSec;\n\n\t\t\t\t// Calculate the percentage difference\n\t\t\t\tconst difference = Math.abs(opsSec1 - opsSec2);\n\t\t\t\tconst percentageDifference =\n\t\t\t\t\t(difference / Math.min(opsSec1, opsSec2)) * 100;\n\n\t\t\t\t// Check if the percentage difference is less than or equal to 10%\n\t\t\t\tif (process.env.CI) {\n\t\t\t\t\t// CI runs in a shared-env so the percentage of difference\n\t\t\t\t\t// must be greather there due to high variance of hardware\n\t\t\t\t\tassert.ok(\n\t\t\t\t\t\tgreaterThan\n\t\t\t\t\t\t\t? percentageDifference >= ciPercentageLimit\n\t\t\t\t\t\t\t: percentageDifference <= ciPercentageLimit,\n\t\t\t\t\t\t`\"${results[i].name}\" too different from \"${results[j].name}\" - ${percentageDifference} != ${ciPercentageLimit} - ${opsSec1} x ${opsSec2}`,\n\t\t\t\t\t);\n\t\t\t\t} else {\n\t\t\t\t\tassert.ok(\n\t\t\t\t\t\tgreaterThan\n\t\t\t\t\t\t\t? percentageDifference >= percentageLimit\n\t\t\t\t\t\t\t: percentageDifference <= percentageLimit,\n\t\t\t\t\t\t`${results[i].name} too different from ${results[j].name} - ${percentageDifference} != ${percentageLimit}`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n// TODO: on small machines the results are discrepant. Fix it.\nif (!process.env.CI) {\n\tdescribe(\"Same benchmark function\", () => {\n\t\tlet results;\n\n\t\tbefore(async () => {\n\t\t\tresults = await copyBench.run();\n\t\t});\n\n\t\tit(\"must have a similar benchmark result\", () => {\n\t\t\tassertMaxBenchmarkDifference(results, {\n\t\t\t\tpercentageLimit: 10,\n\t\t\t\tciPercentageLimit: 30,\n\t\t\t});\n\t\t});\n\t});\n}\ndescribe(\"Managed can be V8 optimized\", () => {\n\tlet optResults;\n\tlet results;\n\n\tbefore(async () => {\n\t\toptResults = await managedOptBench.run();\n\t\tresults = await managedBench.run();\n\t});\n\n\tit(\"should be more than 50% different from unmanaged\", () => {\n\t\tassertMinBenchmarkDifference(optResults, {\n\t\t\tpercentageLimit: 50,\n\t\t\tciPercentageLimit: 30,\n\t\t});\n\t});\n\n\t// it('should be similar when avoiding V8 optimizatio', () => {\n\t//   assertBenchmarkDifference(results, 50, 30);\n\t// });\n});\n\ndescribe(\"Workers should have parallel context\", () => {\n\tlet results;\n\tbefore(async () => {\n\t\tconst bench = new Suite({\n\t\t\treporter: () => {},\n\t\t\tuseWorkers: true,\n\t\t\tbenchmarkMode: \"ops\",\n\t\t});\n\n\t\tbench\n\t\t\t.add(\"Import with node: prefix\", () => {\n\t\t\t\treturn import(\"node:fs\");\n\t\t\t})\n\t\t\t.add(\"Import without node: prefix\", () => {\n\t\t\t\treturn import(\"node:fs\");\n\t\t\t});\n\t\tresults = await bench.run();\n\t});\n\n\tit(\"should have a similar result as they will not share import.meta.cache\", () => {\n\t\tassertMaxBenchmarkDifference(results, {\n\t\t\tpercentageLimit: 10,\n\t\t\tciPercentageLimit: 30,\n\t\t});\n\t});\n});\n"
  },
  {
    "path": "test/fixtures/bench.js",
    "content": "const { Suite } = require(\"../../lib\");\n\nconst suite = new Suite();\n\nsuite\n\t.add(\"empty\", () => {})\n\t.add(\"empty async\", async () => {})\n\t.run();\n"
  },
  {
    "path": "test/fixtures/copy.js",
    "content": "const { Suite } = require(\"../../lib\");\n\nconst suite = new Suite({ reporter: false });\n\nsuite\n\t.add(\"Using includes\", { minSamples: 20 }, () => {\n\t\tconst text =\n\t\t\t\"text/html,application/xhtml+xml,application/xml;application/json;q=0.9,image/avif,image/webp,*/*;q=0.8\";\n\t\tconst r = text.includes(\"application/json\");\n\t})\n\t.add(\"Using includes 2\", { minSamples: 20 }, () => {\n\t\tconst text =\n\t\t\t\"text/html,application/xhtml+xml,application/xml;application/json;q=0.9,image/avif,image/webp,*/*;q=0.8\";\n\t\tconst r = text.includes(\"application/json\");\n\t})\n\t.add(\"Using includes 3\", { minSamples: 20 }, () => {\n\t\tconst text =\n\t\t\t\"text/html,application/xhtml+xml,application/xml;application/json;q=0.9,image/avif,image/webp,*/*;q=0.8\";\n\t\tconst r = text.includes(\"application/json\");\n\t});\n\nmodule.exports = suite;\n"
  },
  {
    "path": "test/fixtures/opt-managed.js",
    "content": "const { Suite } = require(\"../../lib\");\nconst assert = require(\"node:assert\");\n\nconst suite = new Suite({ reporter: false });\n\nsuite\n\t.add(\"Using includes\", () => {\n\t\tconst text =\n\t\t\t\"text/html,application/xhtml+xml,application/xml;application/json;q=0.9,image/avif,image/webp,*/*;q=0.8\";\n\t\tconst r = text.includes(\"application/json\");\n\t\tassert.ok(r);\n\t})\n\t.add(\"[Managed] Using includes\", (timer) => {\n\t\ttimer.start();\n\t\tfor (let i = 0; i < timer.count; i++) {\n\t\t\tconst text =\n\t\t\t\t\"text/html,application/xhtml+xml,application/xml;application/json;q=0.9,image/avif,image/webp,*/*;q=0.8\";\n\t\t\tconst r = text.includes(\"application/json\");\n\t\t\tassert.ok(r);\n\t\t}\n\t\ttimer.end(timer.count);\n\t});\n\nsuite.run();\n\nconst optSuite = new Suite({ reporter: false });\n\noptSuite\n\t.add(\"Using includes\", () => {\n\t\tconst text =\n\t\t\t\"text/html,application/xhtml+xml,application/xml;application/json;q=0.9,image/avif,image/webp,*/*;q=0.8\";\n\t\tconst r = text.includes(\"application/json\");\n\t\t// assert.ok(r)\n\t})\n\t.add(\"[Managed] Using includes\", (timer) => {\n\t\ttimer.start();\n\t\tfor (let i = 0; i < timer.count; i++) {\n\t\t\tconst text =\n\t\t\t\t\"text/html,application/xhtml+xml,application/xml;application/json;q=0.9,image/avif,image/webp,*/*;q=0.8\";\n\t\t\tconst r = text.includes(\"application/json\");\n\t\t\t// assert.ok(r)\n\t\t}\n\t\ttimer.end(timer.count);\n\t});\n\nmodule.exports = {\n\tmanagedBench: suite,\n\tmanagedOptBench: optSuite,\n};\n"
  },
  {
    "path": "test/managed.js",
    "content": "const { describe, it } = require(\"node:test\");\nconst assert = require(\"node:assert\");\nconst { Suite } = require(\"../lib/index\");\n\ndescribe(\"managed benchmarks\", async () => {\n\tit(\"should throw when timer.start isn't called\", () => {\n\t\tconst suite = new Suite({ reporter: () => {} });\n\t\tassert.rejects(\n\t\t\tasync () => {\n\t\t\t\tsuite.add(\"managed\", (timer) => {});\n\t\t\t\tawait suite.run();\n\t\t\t},\n\t\t\t{\n\t\t\t\tmessage: \"You forgot to call .start()\",\n\t\t\t},\n\t\t);\n\t});\n\n\tit(\"should throw when timer.end isn't called\", () => {\n\t\tconst suite = new Suite({ reporter: () => {} });\n\t\tassert.rejects(\n\t\t\tasync () => {\n\t\t\t\tsuite.add(\"managed\", (timer) => {\n\t\t\t\t\ttimer.start();\n\t\t\t\t});\n\t\t\t\tawait suite.run();\n\t\t\t},\n\t\t\t{\n\t\t\t\tmessage: \"You forgot to call .end(count)\",\n\t\t\t},\n\t\t);\n\t});\n});\n"
  },
  {
    "path": "test/plugin-api-doc.js",
    "content": "// @ts-check\nconst { Suite } = require(\"../lib/index.js\");\nconst { describe, it } = require(\"node:test\");\nconst assert = require(\"node:assert\");\n\nclass ExamplePlugin {\n  #aggregation = {};\n  constructor() { }\n\n  isSupported() {\n    return true;\n  }\n\n  beforeClockTemplate() {\n    return [`record(\"- evaluated beforeClockCode\");`];\n  }\n\n  afterClockTemplate({ context }) {\n    return [\n      `\n    ${context}.example=1;\n    record(\"- evaluated afterClockCode\");\n    `,\n    ];\n  }\n\n  onCompleteBenchmark([time, iterations, results], { name }) {\n    if (undefined === this.#aggregation[name]) {\n      this.#aggregation[name] = 0;\n    }\n    this.#aggregation[name] += results.example;\n  }\n\n  toString() {\n    return \"ExamplePlugin\";\n  }\n\n  getReport(name) {\n    return `examplePlugin report for ${name}`;\n  }\n\n  getResult(name) {\n    return {\n      examplePluginAggregation: this.#aggregation[name],\n    };\n  }\n}\n\ndescribe(\"plugin API\", async () => {\n  const bench = new Suite({\n    reporter: () => { },\n    plugins: [captureAll(new ExamplePlugin())],\n  });\n  bench.add(\"task1\", async () => {\n    record(\"- task1\");\n  });\n  bench.add(\"task2\", async () => {\n    record(\"- task2\");\n  });\n  const [bench1] = await bench.run();\n\n  it(\"matches method signatures\", async () => {\n    const recordedMethodSignatures = getSignatures();\n    assert.deepStrictEqual(recordedMethodSignatures, [\n      \"afterClockTemplate({awaitOrEmpty, bench, context, managed, timer})\",\n      \"beforeClockTemplate({awaitOrEmpty, bench, context, managed, timer})\",\n      \"getReport(string)\",\n      \"getResult(string)\",\n      \"isSupported()\",\n      \"onCompleteBenchmark([number, number, object], {baseline, fn, fnStr, hasArg, isAsync, maxTime, minSamples, minTime, name, plugins, repeatSuite})\",\n      \"reset()\",\n      \"toJSON(string)\",\n      \"toString()\",\n    ]);\n  });\n  it(\"produces history\", async () => {\n    printExcerptFromHistory();\n  });\n  it(\"aggregates results\", async () => {\n    console.log(\"Benchmark results plugins field:\", bench1.plugins);\n    assert(bench1.plugins[0].result.examplePluginAggregation > 1);\n    assert.strictEqual(\n      bench1.plugins[0].report,\n      \"examplePlugin report for task1\",\n    );\n  });\n});\n\n// ============================================\n// Utilities to capture the methods and history.\n// Moved down the file to keep the test code clean. Hoisting is how they're available.\n// No need to look at them, stop reading now, look at the test output instead.\n\nfunction record(name, args) {\n  if (args && args.length) {\n    history.push([name, 1, JSON.stringify(Array.from(args))]);\n  } else {\n    const last = history[history.length - 1];\n    if (last && last[0] === name) {\n      last[1]++;\n    } else {\n      history.push([name, 1]);\n    }\n  }\n}\n\nfunction printExcerptFromHistory(n = 25) {\n  const excerpt = [\n    ...history.slice(0, n),\n    [\"(... redacted for brevity ...)\", 1],\n    ...history.slice(-n),\n  ]\n    .map(\n      ([name, count, args]) =>\n        `${name} ${count > 1 ? \"x\" + count : \"\"}${args ? \" with args: \" + args : \"\"\n        }`,\n    )\n    .join(\"\\n| \");\n  console.log(\"+----------------------------------\");\n  console.log(\"| Plugin lifecycle log:\");\n  console.log(\"+----------------------------------\");\n  console.log(\"|\", excerpt);\n  console.log(\"+----------------------------------\");\n}\nfunction getSignatures() {\n  return Object.entries(API)\n    .map(\n      ([name, args]) =>\n        `${name}(${Array.from(args)\n          .map((a) => {\n            if (!a) return \"\";\n            if (Array.isArray(a))\n              return \"[\" + a.map((a) => typeof a).join(\", \") + \"]\";\n            if (typeof a === \"object\")\n              return \"{\" + Object.keys(a).sort().join(\", \") + \"}\";\n            return typeof a;\n          })\n          .join(\", \")})`,\n    )\n    .sort();\n}\nvar history, API;\nfunction captureAll(pluginInstance) {\n  history = [];\n  API = {};\n  globalThis.record = record; // make record available in the tasks\n\n  return new Proxy(pluginInstance, {\n    get(target, prop, receiver) {\n      return function (...args) {\n        record(prop, args);\n        API[prop] = args;\n        if (typeof target[prop] === \"function\") {\n          return target[prop].apply(target, args);\n        }\n      };\n    },\n    has(target, prop) {\n      return true;\n    },\n  });\n}\n"
  },
  {
    "path": "test/plugins.js",
    "content": "const {\n\tSuite,\n\tV8NeverOptimizePlugin,\n\tV8GetOptimizationStatus,\n\tV8OptimizeOnNextCallPlugin,\n\tMemoryPlugin,\n\tDeadCodeEliminationDetectionPlugin,\n} = require(\"../lib/index\");\nconst { describe, it } = require(\"node:test\");\nconst assert = require(\"node:assert\");\n\nclass InvalidPlugin {}\n\nclass ValidPlugin {\n\ttoString() {\n\t\treturn \"\";\n\t}\n\tisSupported() {\n\t\treturn true;\n\t}\n}\n\ndescribe(\"Plugins validation\", () => {\n\tit(\"should be an object with expected methods\", () => {\n\t\tfor (const r of [1, \"ds\", {}]) {\n\t\t\tassert.throws(\n\t\t\t\t() => {\n\t\t\t\t\tnew Suite({ plugins: r });\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tcode: \"ERR_INVALID_ARG_TYPE\",\n\t\t\t\t},\n\t\t\t);\n\t\t}\n\t\tassert.throws(\n\t\t\t() => {\n\t\t\t\tnew Suite({ plugins: [new InvalidPlugin()] });\n\t\t\t},\n\t\t\t{\n\t\t\t\tcode: \"ERR_INVALID_ARG_TYPE\",\n\t\t\t},\n\t\t);\n\t\t// doesNotThrow\n\t\tnew Suite({ plugins: [new ValidPlugin()] });\n\t});\n\n\tit(\"beforeClockTemplate should return an array\", () => {\n\t\tclass InvalidPlugin2 {\n\t\t\tbeforeClockTemplate() {\n\t\t\t\treturn \"error\";\n\t\t\t}\n\t\t\ttoString() {\n\t\t\t\treturn \"\";\n\t\t\t}\n\t\t\tisSupported() {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\n\t\tassert.throws(\n\t\t\t() => {\n\t\t\t\tnew Suite({ plugins: [new InvalidPlugin2()] });\n\t\t\t},\n\t\t\t{\n\t\t\t\tcode: \"ERR_INVALID_ARG_TYPE\",\n\t\t\t},\n\t\t);\n\t});\n\n\tit(\"afterClockTemplate should return an array\", () => {\n\t\tclass InvalidPlugin2 {\n\t\t\tbeforeClockTemplate() {\n\t\t\t\treturn [\"\"];\n\t\t\t}\n\t\t\tafterClockTemplate() {\n\t\t\t\treturn \"error\";\n\t\t\t}\n\t\t\ttoString() {\n\t\t\t\treturn \"\";\n\t\t\t}\n\t\t\tisSupported() {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\n\t\tassert.throws(\n\t\t\t() => {\n\t\t\t\tnew Suite({ plugins: [new InvalidPlugin2()] });\n\t\t\t},\n\t\t\t{\n\t\t\t\tcode: \"ERR_INVALID_ARG_TYPE\",\n\t\t\t},\n\t\t);\n\t});\n});\n\ndescribe(\"Official plugins validation\", () => {\n\tit(\"V8NeverOptimizePlugin validation\", () => {\n\t\tconst bench = new Suite({\n\t\t\tplugins: [new V8NeverOptimizePlugin()],\n\t\t});\n\t\tassert.ok(bench);\n\t});\n\n\tit(\"V8NeverOptimizePlugin prevents benchmark function optimization\", () => {\n\t\tconst plugin = new V8NeverOptimizePlugin();\n\t\tconst [code, wrapper] = plugin.beforeClockTemplate({\n\t\t\tbench: \"bench\",\n\t\t\tawaitOrEmpty: \"\",\n\t\t\tcontext: \"context\",\n\t\t\ttimer: \"timer\",\n\t\t});\n\n\t\tassert.strictEqual(wrapper, \"DoNotOptimize\");\n\t\tassert.match(code, /%NeverOptimizeFunction\\(bench\\.fn\\);/);\n\t\tassert.match(code, /%NeverOptimizeFunction\\(DoNotOptimize\\);/);\n\t});\n\n\tit(\"V8GetOptimizationStatus validation\", () => {\n\t\tconst bench = new Suite({\n\t\t\tplugins: [new V8GetOptimizationStatus()],\n\t\t});\n\t\tassert.ok(bench);\n\t});\n\tit(\"V8OptimizeOnNextCallPlugin validation\", () => {\n\t\tconst bench = new Suite({\n\t\t\tplugins: [new V8OptimizeOnNextCallPlugin()],\n\t\t});\n\t\tassert.ok(bench);\n\t});\n\n\tit(\"MemoryPlugin validation\", () => {\n\t\tconst bench = new Suite({\n\t\t\tplugins: [new MemoryPlugin()],\n\t\t});\n\t\tassert.ok(bench);\n\t});\n\n\tit(\"DeadCodeEliminationDetectionPlugin validation\", () => {\n\t\tconst bench = new Suite({\n\t\t\tplugins: [new DeadCodeEliminationDetectionPlugin()],\n\t\t});\n\t\tassert.ok(bench);\n\t});\n});\n\ndescribe(\"DeadCodeEliminationDetectionPlugin\", () => {\n\tit(\"should initialize with default threshold\", () => {\n\t\tconst plugin = new DeadCodeEliminationDetectionPlugin();\n\t\tassert.ok(plugin);\n\t\tassert.strictEqual(plugin.isSupported(), true);\n\t});\n\n\tit(\"should accept custom threshold\", () => {\n\t\tconst plugin = new DeadCodeEliminationDetectionPlugin({ threshold: 20 });\n\t\tassert.ok(plugin);\n\t});\n\n\tit(\"should set and use baseline\", () => {\n\t\tconst plugin = new DeadCodeEliminationDetectionPlugin();\n\t\tplugin.setBaseline(100); // 100ns baseline\n\t\tassert.strictEqual(plugin.hasWarning(\"test\"), false);\n\t});\n\n\tit(\"should detect suspiciously fast benchmarks\", () => {\n\t\tconst plugin = new DeadCodeEliminationDetectionPlugin({ threshold: 10 });\n\t\tplugin.setBaseline(100); // 100ns baseline\n\n\t\t// Simulate a benchmark that's only 5x slower than baseline (should warn)\n\t\tconst fastResult = [500, 1]; // 500ns total, 1 iteration = 500ns/iter\n\t\tplugin.onCompleteBenchmark(fastResult, { name: \"fast-bench\" });\n\n\t\tassert.strictEqual(plugin.hasWarning(\"fast-bench\"), true);\n\t\tconst warning = plugin.getWarning(\"fast-bench\");\n\t\tassert.ok(warning);\n\t\tassert.strictEqual(warning.timePerOp, 500);\n\t\tassert.strictEqual(warning.baselineTime, 100);\n\t\tassert.strictEqual(warning.ratio, 5);\n\t});\n\n\tit(\"should not warn for legitimate benchmarks\", () => {\n\t\tconst plugin = new DeadCodeEliminationDetectionPlugin({ threshold: 10 });\n\t\tplugin.setBaseline(100); // 100ns baseline\n\n\t\t// Simulate a benchmark that's 50x slower than baseline (should not warn)\n\t\tconst slowResult = [5000, 1]; // 5000ns total, 1 iteration = 5000ns/iter\n\t\tplugin.onCompleteBenchmark(slowResult, { name: \"slow-bench\" });\n\n\t\tassert.strictEqual(plugin.hasWarning(\"slow-bench\"), false);\n\t});\n\n\tit(\"should track multiple warnings\", () => {\n\t\tconst plugin = new DeadCodeEliminationDetectionPlugin({ threshold: 10 });\n\t\tplugin.setBaseline(100);\n\n\t\tplugin.onCompleteBenchmark([200, 1], { name: \"bench1\" });\n\t\tplugin.onCompleteBenchmark([300, 1], { name: \"bench2\" });\n\n\t\tconst allWarnings = plugin.getAllWarnings();\n\t\tassert.strictEqual(allWarnings.length, 2);\n\t\tassert.strictEqual(allWarnings[0].name, \"bench1\");\n\t\tassert.strictEqual(allWarnings[1].name, \"bench2\");\n\t});\n\n\tit(\"should create Suite with DCE detection enabled\", () => {\n\t\t// Just verify Suite accepts the option\n\t\tconst suite = new Suite({\n\t\t\tdetectDeadCodeElimination: true,\n\t\t\tdceThreshold: 15,\n\t\t\treporter: false,\n\t\t});\n\t\tassert.ok(suite);\n\t});\n});\n"
  },
  {
    "path": "test/reporter.js",
    "content": "const { describe, it, before } = require(\"node:test\");\nconst assert = require(\"node:assert\");\nconst fs = require(\"node:fs\");\nconst { Suite } = require(\"../lib\");\n\nconst {\n\tchartReport,\n\ttoChart,\n\thtmlReport,\n\tjsonReport,\n\ttoJSON,\n\tcsvReport,\n\ttoCSV,\n\tprettyReport,\n\ttoPretty,\n\ttextReport,\n\ttoText,\n} = require(\"../lib/report\");\n\nconst { analyze, summarize } = require(\"../lib/utils/analyze.js\");\n\ndescribe(\"chartReport\", () => {\n\tdescribe(\"outputs benchmark results as a bar chart\", async (t) => {\n\t\tlet output = \"\";\n\n\t\tbefore(async () => {\n\t\t\tconst originalStdoutWrite = process.stdout.write;\n\t\t\tprocess.stdout.write = (data) => {\n\t\t\t\toutput += data;\n\t\t\t};\n\n\t\t\tconst suite = new Suite({\n\t\t\t\treporter: chartReport,\n\t\t\t});\n\n\t\t\tsuite\n\t\t\t\t.add(\"single with matcher\", () => {\n\t\t\t\t\tconst pattern = /[123]/g;\n\t\t\t\t\tconst replacements = { 1: \"a\", 2: \"b\", 3: \"c\" };\n\t\t\t\t\tconst subject = \"123123123123123123123123123123123123123123123123\";\n\t\t\t\t\tconst r = subject.replace(pattern, (m) => replacements[m]);\n\t\t\t\t\tassert.ok(r);\n\t\t\t\t})\n\t\t\t\t.add(\"multiple replaces\", () => {\n\t\t\t\t\tconst subject = \"123123123123123123123123123123123123123123123123\";\n\t\t\t\t\tconst r = subject\n\t\t\t\t\t\t.replace(/1/g, \"a\")\n\t\t\t\t\t\t.replace(/2/g, \"b\")\n\t\t\t\t\t\t.replace(/3/g, \"c\");\n\t\t\t\t\tassert.ok(r);\n\t\t\t\t});\n\t\t\tawait suite.run();\n\n\t\t\tprocess.stdout.write = originalStdoutWrite;\n\t\t});\n\n\t\tit(\"should include bar chart chars\", () => {\n\t\t\tassert.ok(output.includes(\"█\"));\n\t\t});\n\n\t\tit(\"should include ops/sec\", () => {\n\t\t\tassert.ok(output.includes(\"ops/sec\"));\n\t\t});\n\n\t\tit(\"should include benchmark names\", () => {\n\t\t\tassert.ok(output.includes(\"single with matcher\"));\n\t\t\tassert.ok(output.includes(\"multiple replaces\"));\n\t\t});\n\n\t\tit(\"should include sample count\", () => {\n\t\t\tassert.ok(output.includes(\"samples\"));\n\t\t});\n\n\t\tit(\"should include Node.js version\", () => {\n\t\t\tconst regex = /Node\\.js version: v\\d+\\.\\d+\\.\\d+/;\n\n\t\t\tassert.ok(output.match(regex));\n\t\t});\n\t});\n\n\tdescribe(\"with long names\", async (t) => {\n\t\tlet output = \"\";\n\n\t\tbefore(async () => {\n\t\t\tconst originalStdoutWrite = process.stdout.write;\n\t\t\tprocess.stdout.write = (data) => {\n\t\t\t\toutput += data;\n\t\t\t};\n\n\t\t\tconst suite = new Suite({\n\t\t\t\treporter: chartReport,\n\t\t\t});\n\n\t\t\tsuite\n\t\t\t\t.add(\"single with matcher looooooooooooooooooooooooooong\", () => {\n\t\t\t\t\tconst pattern = /[123]/g;\n\t\t\t\t\tconst replacements = { 1: \"a\", 2: \"b\", 3: \"c\" };\n\t\t\t\t\tconst subject = \"123123123123123123123123123123123123123123123123\";\n\t\t\t\t\tconst r = subject.replace(pattern, (m) => replacements[m]);\n\t\t\t\t\tassert.ok(r);\n\t\t\t\t})\n\t\t\t\t.add(\"multiple replaces\", () => {\n\t\t\t\t\tconst subject = \"123123123123123123123123123123123123123123123123\";\n\t\t\t\t\tconst r = subject\n\t\t\t\t\t\t.replace(/1/g, \"a\")\n\t\t\t\t\t\t.replace(/2/g, \"b\")\n\t\t\t\t\t\t.replace(/3/g, \"c\");\n\t\t\t\t\tassert.ok(r);\n\t\t\t\t});\n\t\t\tawait suite.run();\n\n\t\t\tprocess.stdout.write = originalStdoutWrite;\n\t\t});\n\n\t\tit(\"should pad out benchmark names\", () => {\n\t\t\tassert.ok(output.includes(\"oong ▏█\"));\n\t\t\tassert.ok(output.includes(\"multiple replaces\".padEnd(51)));\n\t\t});\n\t});\n\n\tdescribe(\"respects reporterOptions.printHeader\", async (t) => {\n\t\tlet outputWithHeader = \"\";\n\t\tlet outputWithoutHeader = \"\";\n\n\t\tbefore(async () => {\n\t\t\tconst originalStdoutWrite = process.stdout.write;\n\n\t\t\t// Test with default settings (printHeader: true)\n\t\t\tprocess.stdout.write = (data) => {\n\t\t\t\toutputWithHeader += data;\n\t\t\t};\n\n\t\t\tconst suiteWithHeader = new Suite({\n\t\t\t\treporter: chartReport,\n\t\t\t\treporterOptions: {\n\t\t\t\t\tprintHeader: true,\n\t\t\t\t},\n\t\t\t});\n\n\t\t\tsuiteWithHeader.add(\"test benchmark\", () => {\n\t\t\t\tconst a = 1 + 1;\n\t\t\t\tassert.strictEqual(a, 2);\n\t\t\t});\n\t\t\tawait suiteWithHeader.run();\n\n\t\t\t// Test with printHeader: false\n\t\t\toutputWithoutHeader = \"\";\n\t\t\tprocess.stdout.write = (data) => {\n\t\t\t\toutputWithoutHeader += data;\n\t\t\t};\n\n\t\t\tconst suiteWithoutHeader = new Suite({\n\t\t\t\treporter: chartReport,\n\t\t\t\treporterOptions: {\n\t\t\t\t\tprintHeader: false,\n\t\t\t\t},\n\t\t\t});\n\n\t\t\tsuiteWithoutHeader.add(\"test benchmark\", () => {\n\t\t\t\tconst a = 1 + 1;\n\t\t\t\tassert.strictEqual(a, 2);\n\t\t\t});\n\t\t\tawait suiteWithoutHeader.run();\n\n\t\t\tprocess.stdout.write = originalStdoutWrite;\n\t\t});\n\n\t\tit(\"should include Node.js version when printHeader is true\", () => {\n\t\t\tconst regex = /Node\\.js version: v\\d+\\.\\d+\\.\\d+/;\n\t\t\tassert.ok(outputWithHeader.match(regex));\n\t\t});\n\n\t\tit(\"should include Platform when printHeader is true\", () => {\n\t\t\tassert.ok(outputWithHeader.includes(\"Platform:\"));\n\t\t});\n\n\t\tit(\"should include CPU Cores when printHeader is true\", () => {\n\t\t\tassert.ok(outputWithHeader.includes(\"CPU Cores:\"));\n\t\t});\n\n\t\tit(\"should NOT include Node.js version when printHeader is false\", () => {\n\t\t\tconst regex = /Node\\.js version: v\\d+\\.\\d+\\.\\d+/;\n\t\t\tassert.ok(!outputWithoutHeader.match(regex));\n\t\t});\n\n\t\tit(\"should NOT include Platform when printHeader is false\", () => {\n\t\t\tassert.ok(!outputWithoutHeader.includes(\"Platform:\"));\n\t\t});\n\n\t\tit(\"should NOT include CPU Cores when printHeader is false\", () => {\n\t\t\tassert.ok(!outputWithoutHeader.includes(\"CPU Cores:\"));\n\t\t});\n\n\t\tit(\"should still include benchmark data with or without header\", () => {\n\t\t\t// Both outputs should still have benchmark bars and results\n\t\t\tassert.ok(outputWithHeader.includes(\"█\"));\n\t\t\tassert.ok(outputWithoutHeader.includes(\"█\"));\n\t\t\tassert.ok(outputWithHeader.includes(\"test benchmark\"));\n\t\t\tassert.ok(outputWithoutHeader.includes(\"test benchmark\"));\n\t\t});\n\t});\n});\n\ndescribe(\"htmlReport should create a file\", async (t) => {\n\tlet output = \"\";\n\tlet htmlName = \"\";\n\tlet htmlContent = \"\";\n\n\tbefore(async () => {\n\t\tconst originalStdoutWrite = process.stdout.write;\n\t\tconst originalWriteFileSync = fs.writeFileSync;\n\n\t\tfs.writeFileSync = (name, content) => {\n\t\t\thtmlName = name;\n\t\t\thtmlContent = content;\n\t\t};\n\n\t\tprocess.stdout.write = (data) => {\n\t\t\toutput += data;\n\t\t};\n\n\t\tconst suite = new Suite({\n\t\t\treporter: htmlReport,\n\t\t});\n\n\t\tsuite\n\t\t\t.add(\"single with matcher\", () => {\n\t\t\t\tconst pattern = /[123]/g;\n\t\t\t\tconst replacements = { 1: \"a\", 2: \"b\", 3: \"c\" };\n\t\t\t\tconst subject = \"123123123123123123123123123123123123123123123123\";\n\t\t\t\tconst r = subject.replace(pattern, (m) => replacements[m]);\n\t\t\t\tassert.ok(r);\n\t\t\t})\n\t\t\t.add(\"Multiple replaces\", () => {\n\t\t\t\tconst subject = \"123123123123123123123123123123123123123123123123\";\n\t\t\t\tconst r = subject\n\t\t\t\t\t.replace(/1/g, \"a\")\n\t\t\t\t\t.replace(/2/g, \"b\")\n\t\t\t\t\t.replace(/3/g, \"c\");\n\t\t\t\tassert.ok(r);\n\t\t\t});\n\t\tawait suite.run();\n\n\t\tfs.writeFileSync = originalWriteFileSync;\n\t\tprocess.stdout.write = originalStdoutWrite;\n\t});\n\n\tit(\"should print that a HTML file has been generated\", () => {\n\t\tassert.ok(output.includes(\"HTML file has been generated\"));\n\t});\n\n\tit(\"htmlName should be result.html\", () => {\n\t\tassert.strictEqual(htmlName, \"result.html\");\n\t});\n\n\tit(\"htmlContent should not be empty\", () => {\n\t\tassert.ok(htmlContent.length > 100);\n\t});\n\n\tit(\"htmlContent bench suite should be used as class name\", () => {\n\t\tassert.ok(htmlContent.includes(\"circle-Multiple-replaces\"));\n\t\tassert.ok(htmlContent.includes(\"circle-single-with-matcher\"));\n\t});\n\n\tit(\"htmlContent should not contain replace tags {{}}\", () => {\n\t\tassert.ok(htmlContent.includes(\"{{\") === false);\n\t\tassert.ok(htmlContent.includes(\"}}\") === false);\n\t});\n});\n\ndescribe(\"prettyReport outputs a beautiful report\", async (t) => {\n\tlet output = \"\";\n\t// biome-ignore lint/suspicious/noControlCharactersInRegex: This is required to strip ANSI color codes from the output for testing.\n\tconst stripAnsi = (str) => str.replace(/\\x1b\\[[0-9;]*m/g, \"\");\n\n\tbefore(async () => {\n\t\tconst originalStdoutWrite = process.stdout.write;\n\t\tprocess.stdout.write = (data) => {\n\t\t\toutput += data;\n\t\t};\n\n\t\tconst suite = new Suite({\n\t\t\treporter: prettyReport,\n\t\t});\n\n\t\tsuite\n\t\t\t.add(\"suite/single with matcher\", () => {\n\t\t\t\tconst pattern = /[123]/g;\n\t\t\t\tconst replacements = { 1: \"a\", 2: \"b\", 3: \"c\" };\n\t\t\t\tconst subject = \"123123123123123123123123123123123123123123123123\";\n\t\t\t\tconst r = subject.replace(pattern, (m) => replacements[m]);\n\t\t\t\tassert.ok(r);\n\t\t\t})\n\t\t\t.add(\"suite/multiple replaces\", () => {\n\t\t\t\tconst subject = \"123123123123123123123123123123123123123123123123\";\n\t\t\t\tconst r = subject\n\t\t\t\t\t.replace(/1/g, \"a\")\n\t\t\t\t\t.replace(/2/g, \"b\")\n\t\t\t\t\t.replace(/3/g, \"c\");\n\t\t\t\tassert.ok(r);\n\t\t\t})\n\t\t\t.add(\"suite/nested/deeper/test\", () => {\n\t\t\t\tassert.ok(true);\n\t\t\t});\n\t\tawait suite.run();\n\n\t\tprocess.stdout.write = originalStdoutWrite;\n\t});\n\n\tit(\"should include system information\", () => {\n\t\tassert.ok(output.includes(\"System Information:\"));\n\t\tassert.ok(output.includes(\"Node.js:\"));\n\t\tassert.ok(output.includes(\"OS:\"));\n\t\tassert.ok(output.includes(\"CPU:\"));\n\t});\n\n\tit(\"should include benchmark results header\", () => {\n\t\tassert.ok(output.includes(\"Benchmark results\"));\n\t});\n\n\tit(\"should include tree structure characters\", () => {\n\t\tassert.ok(output.includes(\"└─\"));\n\t\tassert.ok(output.includes(\"├─\"));\n\t});\n\n\tit(\"should correctly group tests with slashes\", () => {\n\t\tconst stripped = stripAnsi(output);\n\t\tassert.ok(stripped.includes(\"suite\"));\n\t\tassert.ok(stripped.includes(\"single with matcher\"));\n\t\tassert.ok(stripped.includes(\"multiple replaces\"));\n\t\tassert.ok(stripped.includes(\"nested\"));\n\t\tassert.ok(stripped.includes(\"deeper\"));\n\t\tassert.ok(stripped.includes(\"test\"));\n\t});\n});\n\ndescribe(\"analyze\", async (t) => {\n\tlet analysis;\n\n\tbefore(async () => {\n\t\t// Create a new Suite with the pretty reporter\n\t\tconst suite = new Suite({});\n\n\t\t// Add benchmarks with one being the baseline\n\t\tsuite\n\t\t\t.add(\"baseline-test\", { baseline: true }, () => {\n\t\t\t\t// Medium-speed operation\n\t\t\t\tfor (let i = 0; i < 10000; i++) {}\n\t\t\t})\n\t\t\t.add(\"other-test\", () => {\n\t\t\t\t// Faster operation\n\t\t\t\tfor (let i = 0; i < 1000; i++) {}\n\t\t\t})\n\t\t\t.add(\"faster-test\", () => {\n\t\t\t\t// Slower operation\n\t\t\t\tfor (let i = 0; i < 100; i++) {}\n\t\t\t});\n\n\t\t// Run the suite\n\t\tconst results = await suite.run();\n\t\tanalysis = analyze(results, false);\n\t});\n\n\tit(\"annotates the fastest result\", () => {\n\t\tassert.equal(analysis[2].fastest, true, \"fastest result\");\n\t});\n\n\tit(\"annotates the slowest result\", () => {\n\t\tassert.equal(analysis[0].slowest, true, \"slowest result\");\n\t});\n});\n\ndescribe(\"summarize\", async (t) => {\n\tlet results;\n\n\tbefore(async () => {\n\t\t// Create a new Suite with the pretty reporter\n\t\tconst suite = new Suite({});\n\n\t\t// Add benchmarks with one being the baseline\n\t\tsuite\n\t\t\t.add(\"baseline-test\", { baseline: true }, () => {\n\t\t\t\t// Medium-speed operation\n\t\t\t\tfor (let i = 0; i < 10000; i++) {}\n\t\t\t})\n\t\t\t.add(\"other-test\", () => {\n\t\t\t\t// Faster operation\n\t\t\t\tfor (let i = 0; i < 1000; i++) {}\n\t\t\t})\n\t\t\t.add(\"faster-test\", () => {\n\t\t\t\t// Slower operation\n\t\t\t\tfor (let i = 0; i < 100; i++) {}\n\t\t\t});\n\n\t\t// Run the suite\n\t\tresults = await suite.run();\n\t});\n\n\tit(\"should contain the required benchmark fields\", () => {\n\t\tconst data = summarize(results);\n\n\t\t// We expect the two benchmarks we added: 'single with matcher' and 'Multiple replaces'\n\t\tassert.strictEqual(data.length, 3, \"Should have results for 3 benchmarks\");\n\n\t\tfor (const entry of data) {\n\t\t\t// Ensure each entry has expected keys\n\t\t\tassert.ok(typeof entry.name === \"string\", \"name should be a string\");\n\t\t\tassert.ok(typeof entry.opsSec === \"number\", \"opsSec should be a number\");\n\t\t\tassert.ok(\n\t\t\t\ttypeof entry.runsSampled === \"number\",\n\t\t\t\t\"runsSampled should be a number\",\n\t\t\t);\n\t\t\tassert.ok(typeof entry.min === \"number\", \"min should be a number\");\n\t\t\tassert.ok(typeof entry.max === \"number\", \"max should be a number\");\n\t\t\tassert.ok(\n\t\t\t\ttypeof entry.minFormatted === \"string\",\n\t\t\t\t\"minFormatted should be a string (formatted time)\",\n\t\t\t);\n\t\t\tassert.ok(\n\t\t\t\ttypeof entry.maxFormatted === \"string\",\n\t\t\t\t\"maxFormatted should be a string (formatted time)\",\n\t\t\t);\n\t\t\tassert.ok(Array.isArray(entry.plugins), \"plugins should be an array\");\n\t\t}\n\t});\n});\n\ndescribe(\"baseline comparisons\", async (t) => {\n\tlet results;\n\n\tbefore(async () => {\n\t\t// Create a new Suite with the pretty reporter\n\t\tconst suite = new Suite({});\n\n\t\t// Add benchmarks with one being the baseline\n\t\tsuite\n\t\t\t.add(\"baseline-test\", { baseline: true }, () => {\n\t\t\t\t// Medium-speed operation\n\t\t\t\tfor (let i = 0; i < 1000; i++) {}\n\t\t\t})\n\t\t\t.add(\"faster-test\", () => {\n\t\t\t\t// Faster operation\n\t\t\t\tfor (let i = 0; i < 100; i++) {}\n\t\t\t})\n\t\t\t.add(\"slower-test\", () => {\n\t\t\t\t// Slower operation\n\t\t\t\tfor (let i = 0; i < 10000; i++) {}\n\t\t\t});\n\n\t\t// Run the suite\n\t\tresults = await suite.run();\n\t});\n\n\tdescribe(\"analyze\", () => {\n\t\tlet analysis;\n\n\t\tbefore(() => {\n\t\t\tanalysis = analyze(results);\n\t\t});\n\n\t\tit(\"annotates the fastest result\", () => {\n\t\t\tassert.equal(results[1].fastest, true, \"fastest result\");\n\t\t});\n\n\t\tit(\"annotates the slowest result\", () => {\n\t\t\tassert.equal(results[2].slowest, true, \"slowest result\");\n\t\t});\n\t});\n\n\tdescribe(\"for pretty report\", async (t) => {\n\t\tlet output = \"\";\n\n\t\tbefore(async () => {\n\t\t\toutput = toPretty(results);\n\t\t});\n\n\t\tit(\"should include a summary section\", () => {\n\t\t\tassert.ok(output.includes(\"Summary (vs. baseline):\"));\n\t\t});\n\n\t\tit(\"should show 'faster' comparison in summary\", () => {\n\t\t\tconst summary = output.split(\"Summary (vs. baseline):\")[1];\n\t\t\tassert.ok(summary.includes(\"faster\"));\n\t\t});\n\n\t\tit(\"should show 'slower' comparison in summary\", () => {\n\t\t\tconst summary = output.split(\"Summary (vs. baseline):\")[1];\n\t\t\tassert.ok(summary.includes(\"slower\"));\n\t\t});\n\t});\n\n\tdescribe(\"for text Report\", async (t) => {\n\t\tlet output = \"\";\n\n\t\tbefore(async () => {\n\t\t\toutput = toText(results);\n\t\t});\n\n\t\tit(\"should include a summary section\", () => {\n\t\t\tassert.ok(output.includes(\"Summary (vs. baseline):\"));\n\t\t});\n\n\t\tit(\"should show 'faster' comparison in summary\", () => {\n\t\t\tconst summary = output.split(\"Summary (vs. baseline):\")[1];\n\t\t\tassert.ok(summary.includes(\"faster\"));\n\t\t});\n\n\t\tit(\"should show 'slower' comparison in summary\", () => {\n\t\t\tconst summary = output.split(\"Summary (vs. baseline):\")[1];\n\t\t\tassert.ok(summary.includes(\"slower\"));\n\t\t});\n\t});\n\n\tdescribe(\"for chart Report\", async (t) => {\n\t\tlet output = \"\";\n\n\t\tbefore(async () => {\n\t\t\toutput = toChart(results, {});\n\t\t});\n\n\t\tit(\"should include a summary section\", () => {\n\t\t\tassert.ok(output.includes(\"Summary (vs. baseline):\"));\n\t\t});\n\n\t\tit(\"should show 'faster' comparison in summary\", () => {\n\t\t\tconst summary = output.split(\"Summary (vs. baseline):\")[1];\n\t\t\tassert.ok(summary.includes(\"faster\"));\n\t\t});\n\n\t\tit(\"should show 'slower' comparison in summary\", () => {\n\t\t\tconst summary = output.split(\"Summary (vs. baseline):\")[1];\n\t\t\tassert.ok(summary.includes(\"slower\"));\n\t\t});\n\n\t\tit(\"uses the default column width for the name\", () => {\n\t\t\tconst summary = output.split(\"Summary (vs. baseline):\")[1];\n\t\t\tassert.ok(\n\t\t\t\t//                123456789012345678901234567890123456789012345 ▏\n\t\t\t\tsummary.includes(\"baseline-test                                 ▏\"),\n\t\t\t);\n\t\t});\n\n\t\tit(\"can adjust the display widths to suite\", () => {\n\t\t\tconst inputs = [\n\t\t\t\t{\n\t\t\t\t\titerations: 1635480,\n\t\t\t\t\thistogram: {\n\t\t\t\t\t\tsamples: 11,\n\t\t\t\t\t\tmin: 301.5006542361793,\n\t\t\t\t\t\tmax: 313.07250487172166,\n\t\t\t\t\t\tsampleData: [\n\t\t\t\t\t\t\t301.5006542361793, 301.5593469278305, 302.8870084803949,\n\t\t\t\t\t\t\t304.6617804001423, 304.71883159667146, 305.0352153017722,\n\t\t\t\t\t\t\t306.6694294422758, 306.9953128406366, 309.27860394347147,\n\t\t\t\t\t\t\t310.3016037436935, 313.07250487172166,\n\t\t\t\t\t\t],\n\t\t\t\t\t},\n\t\t\t\t\tname: \"baseline-test\",\n\t\t\t\t\tbaseline: true,\n\t\t\t\t\topsSec: 3268352.671656186,\n\t\t\t\t\topsSecPerRun: [3268352.671656186],\n\t\t\t\t},\n\t\t\t];\n\n\t\t\toutput = toChart(inputs, { labelWidth: 20, barWidth: 10 });\n\n\t\t\tconst summary = output.split(\"Summary (vs. baseline):\")[1];\n\n\t\t\t//                          12345678901234567890 ▏1234567890 ▏\n\t\t\tassert.ok(summary.includes(\"baseline-test        ▏██████████▕ \"));\n\t\t});\n\t});\n});\n\ndescribe(\"Suite with pretty: true option\", async (t) => {\n\tlet output = \"\";\n\n\tbefore(async () => {\n\t\tconst originalStdoutWrite = process.stdout.write;\n\t\tprocess.stdout.write = (data) => {\n\t\t\toutput += data;\n\t\t};\n\n\t\tconst suite = new Suite({\n\t\t\tpretty: true,\n\t\t});\n\n\t\tsuite.add(\"test\", () => {});\n\t\tawait suite.run();\n\n\t\tprocess.stdout.write = originalStdoutWrite;\n\t});\n\n\tit(\"should use the pretty reporter\", () => {\n\t\tassert.ok(output.includes(\"System Information:\"));\n\t});\n});\n\ndescribe(\"custom reporter should have access to histogram data\", async () => {\n\tlet output = \"\";\n\n\tbefore(async () => {\n\t\tconst originalStdoutWrite = process.stdout.write;\n\t\tprocess.stdout.write = (data) => {\n\t\t\toutput += data;\n\t\t};\n\n\t\tfunction customReporter(results) {\n\t\t\tconst json = [];\n\t\t\tfor (const result of results) {\n\t\t\t\t// Calculate the median of result.histogram.sampleData:\n\t\t\t\tconst sorted = [...result.histogram.sampleData].sort((a, b) => a - b);\n\t\t\t\tconst median =\n\t\t\t\t\tsorted.length % 2 === 0\n\t\t\t\t\t\t? (sorted[sorted.length / 2 - 1] + sorted[sorted.length / 2]) / 2\n\t\t\t\t\t\t: sorted[Math.floor(sorted.length / 2)];\n\n\t\t\t\tjson.push({\n\t\t\t\t\tname: result.name,\n\t\t\t\t\tlow: result.histogram.sampleData.filter((v) => v <= median),\n\t\t\t\t\thigh: result.histogram.sampleData.filter((v) => v >= median),\n\t\t\t\t\tmedian,\n\t\t\t\t});\n\t\t\t}\n\t\t\tconsole.log(JSON.stringify(json, null, 2));\n\t\t}\n\n\t\t// Create a new Suite with the custom reporter\n\t\tconst suite = new Suite({\n\t\t\treporter: customReporter,\n\t\t});\n\n\t\tsuite\n\t\t\t.add(\"single with matcher\", () => {\n\t\t\t\tconst pattern = /[123]/g;\n\t\t\t\tconst replacements = { 1: \"a\", 2: \"b\", 3: \"c\" };\n\t\t\t\tconst subject = \"123123123123123123123123123123123123123123123123\";\n\t\t\t\tconst r = subject.replace(pattern, (m) => replacements[m]);\n\t\t\t\tassert.ok(r);\n\t\t\t})\n\t\t\t.add(\"Multiple replaces\", () => {\n\t\t\t\tconst subject = \"123123123123123123123123123123123123123123123123\";\n\t\t\t\tconst r = subject\n\t\t\t\t\t.replace(/1/g, \"a\")\n\t\t\t\t\t.replace(/2/g, \"b\")\n\t\t\t\t\t.replace(/3/g, \"c\");\n\t\t\t\tassert.ok(r);\n\t\t\t});\n\n\t\t// Run the suite\n\t\tawait suite.run();\n\n\t\t// Restore stdout\n\t\tprocess.stdout.write = originalStdoutWrite;\n\t});\n\n\tit(\"should print valid JSON\", () => {\n\t\t// Verify if the output can be parsed as JSON\n\t\tlet data;\n\t\ttry {\n\t\t\tdata = JSON.parse(output);\n\t\t} catch (err) {\n\t\t\tassert.fail(`Output is not valid JSON: ${err.message}`);\n\t\t}\n\n\t\tassert.ok(Array.isArray(data), \"Output should be an array of results\");\n\t});\n\n\tit(\"should calculate median correctly\", () => {\n\t\tconst data = JSON.parse(output);\n\t\tfor (const result of data) {\n\t\t\tassert.strictEqual(\n\t\t\t\tresult.low.length,\n\t\t\t\tresult.high.length,\n\t\t\t\t\"Same number of samples above and below median\",\n\t\t\t);\n\t\t}\n\t});\n});\n\ndescribe(\"toJSON\", () => {\n\tlet results;\n\n\tbefore(async () => {\n\t\tconst suite = new Suite({});\n\n\t\tsuite\n\t\t\t.add(\"single with matcher\", () => {\n\t\t\t\tconst pattern = /[123]/g;\n\t\t\t\tconst replacements = { 1: \"a\", 2: \"b\", 3: \"c\" };\n\t\t\t\tconst subject = \"123123123123123123123123123123123123123123123123\";\n\t\t\t\tconst r = subject.replace(pattern, (m) => replacements[m]);\n\t\t\t\tassert.ok(r);\n\t\t\t})\n\t\t\t.add(\"Multiple replaces\", () => {\n\t\t\t\tconst subject = \"123123123123123123123123123123123123123123123123\";\n\t\t\t\tconst r = subject\n\t\t\t\t\t.replace(/1/g, \"a\")\n\t\t\t\t\t.replace(/2/g, \"b\")\n\t\t\t\t\t.replace(/3/g, \"c\");\n\t\t\t\tassert.ok(r);\n\t\t\t});\n\n\t\t// Run the suite\n\t\tresults = await suite.run();\n\t});\n\n\tit(\"should run\", () => {\n\t\ttoJSON(results);\n\t});\n\n\tit(\"should print valid JSON\", () => {\n\t\tconst output = toJSON(results);\n\t\tconst data = JSON.parse(output);\n\n\t\tassert.ok(Array.isArray(data), \"Output should be an array of results\");\n\t});\n\n\tit(\"should contain the required benchmark fields\", () => {\n\t\tconst output = toJSON(results);\n\t\tconst data = JSON.parse(output);\n\n\t\t// We expect the two benchmarks we added: 'single with matcher' and 'Multiple replaces'\n\t\tassert.strictEqual(data.length, 2, \"Should have results for 2 benchmarks\");\n\n\t\tfor (const entry of data) {\n\t\t\t// Ensure each entry has expected keys\n\t\t\tassert.ok(typeof entry.name === \"string\", \"name should be a string\");\n\t\t\tassert.ok(typeof entry.opsSec === \"number\", \"opsSec should be a number\");\n\t\t\tassert.ok(\n\t\t\t\ttypeof entry.runsSampled === \"number\",\n\t\t\t\t\"runsSampled should be a number\",\n\t\t\t);\n\t\t\tassert.ok(\n\t\t\t\ttypeof entry.min === \"string\",\n\t\t\t\t\"min should be a string (formatted time)\",\n\t\t\t);\n\t\t\tassert.ok(\n\t\t\t\ttypeof entry.minNS === \"number\",\n\t\t\t\t\"minNS should be a number (time in NS)\",\n\t\t\t);\n\t\t\tassert.ok(\n\t\t\t\ttypeof entry.max === \"string\",\n\t\t\t\t\"max should be a string (formatted time)\",\n\t\t\t);\n\t\t\tassert.ok(\n\t\t\t\ttypeof entry.maxNS === \"number\",\n\t\t\t\t\"maxNS should be a number (time in NS)\",\n\t\t\t);\n\t\t\tassert.ok(Array.isArray(entry.plugins), \"plugins should be an array\");\n\t\t}\n\t});\n});\n\ndescribe(\"jsonReport\", () => {\n\tlet output = \"\";\n\n\tbefore(async () => {\n\t\tconst originalStdoutWrite = process.stdout.write;\n\t\tprocess.stdout.write = (data) => {\n\t\t\toutput += data;\n\t\t};\n\n\t\t// Create a new Suite with the JSON reporter\n\t\tconst suite = new Suite({\n\t\t\treporter: jsonReport,\n\t\t});\n\n\t\tsuite\n\t\t\t.add(\"single with matcher\", () => {\n\t\t\t\tconst pattern = /[123]/g;\n\t\t\t\tconst replacements = { 1: \"a\", 2: \"b\", 3: \"c\" };\n\t\t\t\tconst subject = \"123123123123123123123123123123123123123123123123\";\n\t\t\t\tconst r = subject.replace(pattern, (m) => replacements[m]);\n\t\t\t\tassert.ok(r);\n\t\t\t})\n\t\t\t.add(\"Multiple replaces\", () => {\n\t\t\t\tconst subject = \"123123123123123123123123123123123123123123123123\";\n\t\t\t\tconst r = subject\n\t\t\t\t\t.replace(/1/g, \"a\")\n\t\t\t\t\t.replace(/2/g, \"b\")\n\t\t\t\t\t.replace(/3/g, \"c\");\n\t\t\t\tassert.ok(r);\n\t\t\t});\n\n\t\t// Run the suite\n\t\tawait suite.run();\n\n\t\t// Restore stdout\n\t\tprocess.stdout.write = originalStdoutWrite;\n\t});\n\n\tit(\"should print valid JSON\", () => {\n\t\t// Verify if the output can be parsed as JSON\n\t\tlet data;\n\t\ttry {\n\t\t\tdata = JSON.parse(output);\n\t\t} catch (err) {\n\t\t\tassert.fail(`Output is not valid JSON: ${err.message}`);\n\t\t}\n\n\t\tassert.ok(Array.isArray(data), \"Output should be an array of results\");\n\t});\n});\n\ndescribe(\"toCSV\", () => {\n\tit(\"should generate valid CSV output\", async (t) => {\n\t\tconst result = toCSV([\n\t\t\t{\n\t\t\t\topsSec: 749625.5652171721,\n\t\t\t\titerations: 374813,\n\t\t\t\thistogram: {\n\t\t\t\t\tsamples: 10,\n\t\t\t\t\tmin: 1322.2615873857162,\n\t\t\t\t\tmax: 1345.4275821344213,\n\t\t\t\t},\n\t\t\t\tname: \"single with matcher\",\n\t\t\t\tplugins: [\n\t\t\t\t\t{\n\t\t\t\t\t\tname: \"V8NeverOptimizePlugin\",\n\t\t\t\t\t\tresult: \"enabled\",\n\t\t\t\t\t\treport: \"v8-never-optimize=true\",\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t},\n\t\t\t{\n\t\t\t\topsSec: 634284.7401772924,\n\t\t\t\titerations: 317148,\n\t\t\t\thistogram: {\n\t\t\t\t\tsamples: 11,\n\t\t\t\t\tmin: 1552.562466504839,\n\t\t\t\t\tmax: 1612.7852084972462,\n\t\t\t\t},\n\t\t\t\tname: \"Multiple replaces\",\n\t\t\t\tplugins: [\n\t\t\t\t\t{\n\t\t\t\t\t\tname: \"V8NeverOptimizePlugin\",\n\t\t\t\t\t\tresult: \"enabled\",\n\t\t\t\t\t\treport: \"v8-never-optimize=true\",\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t},\n\t\t]);\n\n\t\tassert.deepStrictEqual(\n\t\t\tresult,\n\t\t\t\"name,ops/sec,samples,plugins,min,max\\n\" +\n\t\t\t\t'single with matcher,\"749,626\",10,\"v8-never-optimize=true\",1.32us,1.35us\\n' +\n\t\t\t\t'Multiple replaces,\"634,285\",11,\"v8-never-optimize=true\",1.55us,1.61us\\n',\n\t\t);\n\t});\n});\n\ndescribe(\"csvReport\", () => {\n\tit(\"should generate valid CSV output\", async (t) => {\n\t\tconst fn = t.mock.method(process.stdout, \"write\");\n\n\t\t// noop\n\t\tfn.mock.mockImplementation(() => {});\n\n\t\tcsvReport([\n\t\t\t{\n\t\t\t\topsSec: 749625.5652171721,\n\t\t\t\titerations: 374813,\n\t\t\t\thistogram: {\n\t\t\t\t\tsamples: 10,\n\t\t\t\t\tmin: 1322.2615873857162,\n\t\t\t\t\tmax: 1345.4275821344213,\n\t\t\t\t},\n\t\t\t\tname: \"single with matcher\",\n\t\t\t\tplugins: [\n\t\t\t\t\t{\n\t\t\t\t\t\tname: \"V8NeverOptimizePlugin\",\n\t\t\t\t\t\tresult: \"enabled\",\n\t\t\t\t\t\treport: \"v8-never-optimize=true\",\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t},\n\t\t\t{\n\t\t\t\topsSec: 634284.7401772924,\n\t\t\t\titerations: 317148,\n\t\t\t\thistogram: {\n\t\t\t\t\tsamples: 11,\n\t\t\t\t\tmin: 1552.562466504839,\n\t\t\t\t\tmax: 1612.7852084972462,\n\t\t\t\t},\n\t\t\t\tname: \"Multiple replaces\",\n\t\t\t\tplugins: [\n\t\t\t\t\t{\n\t\t\t\t\t\tname: \"V8NeverOptimizePlugin\",\n\t\t\t\t\t\tresult: \"enabled\",\n\t\t\t\t\t\treport: \"v8-never-optimize=true\",\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t},\n\t\t]);\n\n\t\tconst callArgs = process.stdout.write.mock.calls.map(\n\t\t\t(call) => call.arguments[0],\n\t\t);\n\n\t\tassert.strictEqual(process.stdout.write.mock.callCount(), 1);\n\t\tassert.deepStrictEqual(callArgs, [\n\t\t\t\"name,ops/sec,samples,plugins,min,max\\n\" +\n\t\t\t\t'single with matcher,\"749,626\",10,\"v8-never-optimize=true\",1.32us,1.35us\\n' +\n\t\t\t\t'Multiple replaces,\"634,285\",11,\"v8-never-optimize=true\",1.55us,1.61us\\n',\n\t\t]);\n\t});\n});\n"
  },
  {
    "path": "test/time-mode.js",
    "content": "const { Suite } = require(\"../lib/index\");\nconst { describe, it } = require(\"node:test\");\nconst assert = require(\"node:assert\");\n\n// Helper function to create a controlled delay\nconst delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));\n\ndescribe(\"Time-based Benchmarking\", () => {\n\tit(\"should run in 'ops' mode by default\", async () => {\n\t\tconst suite = new Suite({ reporter: false });\n\n\t\tsuite.add(\"Default mode test\", () => {\n\t\t\t// Simple operation\n\t\t\tMath.sqrt(Math.random());\n\t\t});\n\n\t\tconst results = await suite.run();\n\n\t\tassert.strictEqual(results.length, 1);\n\t\tassert.ok(results[0].opsSec !== undefined, \"opsSec should be defined\");\n\t\tassert.ok(\n\t\t\tresults[0].totalTime === undefined,\n\t\t\t\"totalTime should not be defined\",\n\t\t);\n\t\tassert.ok(\n\t\t\tresults[0].histogram.samples > 1,\n\t\t\t\"Should have multiple samples in ops mode\",\n\t\t);\n\t});\n\n\tit(\"should run in 'time' mode when specified at suite level\", async () => {\n\t\tconst suite = new Suite({\n\t\t\treporter: false,\n\t\t\tbenchmarkMode: \"time\",\n\t\t});\n\n\t\tconst delayTime = 50; // 50ms delay\n\n\t\tsuite.add(\"Time mode test\", async () => {\n\t\t\tawait delay(delayTime);\n\t\t});\n\n\t\tconst results = await suite.run();\n\n\t\tassert.strictEqual(results.length, 1);\n\t\tassert.ok(\n\t\t\tresults[0].totalTime !== undefined,\n\t\t\t\"totalTime should be defined\",\n\t\t);\n\t\tassert.ok(results[0].opsSec === undefined, \"opsSec should not be defined\");\n\n\t\t// Verify the time is approximately correct (allow for some overhead)\n\t\tconst measuredTime = results[0].totalTime * 1000; // Convert to ms\n\t\tassert.ok(\n\t\t\tmeasuredTime >= delayTime - 0.999 && measuredTime < delayTime + 20,\n\t\t\t`Measured time (${measuredTime}ms) should be close to expected delay (${delayTime}ms)`,\n\t\t);\n\n\t\t// Verify there's exactly 1 sample in time mode\n\t\tassert.strictEqual(\n\t\t\tresults[0].histogram.samples,\n\t\t\t1,\n\t\t\t\"Should have exactly 1 sample in time mode\",\n\t\t);\n\t});\n\n\tit(\"should average results across repeatSuite in time mode\", async () => {\n\t\tconst suite = new Suite({\n\t\t\treporter: false,\n\t\t\tbenchmarkMode: \"time\",\n\t\t});\n\n\t\tconst repeatCount = 5;\n\n\t\t// A very fast operation that should be consistent\n\t\tsuite.add(\"Repeat time test\", { repeatSuite: repeatCount }, () => {\n\t\t\t// Simple operation\n\t\t\tconst x = 1 + 1;\n\t\t});\n\n\t\tconst results = await suite.run();\n\n\t\tassert.strictEqual(results.length, 1);\n\t\tassert.ok(\n\t\t\tresults[0].totalTime !== undefined,\n\t\t\t\"totalTime should be defined\",\n\t\t);\n\n\t\t// Verify the number of samples matches repeatSuite\n\t\tassert.strictEqual(\n\t\t\tresults[0].histogram.samples,\n\t\t\trepeatCount,\n\t\t\t`Should have exactly ${repeatCount} samples with repeatSuite=${repeatCount}`,\n\t\t);\n\t});\n\n\tit(\"should not mix modes within the same suite\", async () => {\n\t\t// This test verifies that benchmarkMode is a suite-level setting\n\t\t// and cannot be overridden at the benchmark level\n\n\t\tconst opsSuite = new Suite({ reporter: false });\n\t\tconst timeSuite = new Suite({ reporter: false, benchmarkMode: \"time\" });\n\n\t\t// Add benchmarks to both suites\n\t\topsSuite.add(\"Ops benchmark\", () => Math.random());\n\t\ttimeSuite.add(\"Time benchmark\", () => Math.random());\n\n\t\t// Run both suites\n\t\tconst opsResults = await opsSuite.run();\n\t\tconst timeResults = await timeSuite.run();\n\n\t\t// Verify ops suite reports ops/sec\n\t\tassert.ok(\n\t\t\topsResults[0].opsSec !== undefined,\n\t\t\t\"opsSec should be defined for ops suite\",\n\t\t);\n\t\tassert.ok(\n\t\t\topsResults[0].totalTime === undefined,\n\t\t\t\"totalTime should not be defined for ops suite\",\n\t\t);\n\n\t\t// Verify time suite reports totalTime\n\t\tassert.ok(\n\t\t\ttimeResults[0].totalTime !== undefined,\n\t\t\t\"totalTime should be defined for time suite\",\n\t\t);\n\t\tassert.ok(\n\t\t\ttimeResults[0].opsSec === undefined,\n\t\t\t\"opsSec should not be defined for time suite\",\n\t\t);\n\t});\n\n\tit(\"should validate benchmarkMode option\", () => {\n\t\t// Valid modes\n\t\tassert.doesNotThrow(() => {\n\t\t\tnew Suite({ benchmarkMode: \"ops\" });\n\t\t\tnew Suite({ benchmarkMode: \"time\" });\n\t\t});\n\n\t\t// Invalid modes\n\t\tassert.throws(() => new Suite({ benchmarkMode: \"invalid\" }), {\n\t\t\tcode: \"ERR_INVALID_ARG_VALUE\",\n\t\t});\n\n\t\tassert.throws(() => new Suite({ benchmarkMode: 123 }), {\n\t\t\tcode: \"ERR_INVALID_ARG_TYPE\",\n\t\t});\n\t});\n});\n"
  },
  {
    "path": "test/ttest.js",
    "content": "const { describe, it } = require(\"node:test\");\nconst assert = require(\"node:assert\");\nconst {\n\tmean,\n\tvariance,\n\twelchTTest,\n\tcompareBenchmarks,\n\ttDistCdf,\n\tgetSignificanceStars,\n} = require(\"../lib/utils/ttest\");\n\ndescribe(\"T-Test Utilities\", () => {\n\tdescribe(\"mean\", () => {\n\t\tit(\"should return 0 for empty array\", () => {\n\t\t\tassert.strictEqual(mean([]), 0);\n\t\t});\n\n\t\tit(\"should calculate mean correctly\", () => {\n\t\t\tassert.strictEqual(mean([1, 2, 3, 4, 5]), 3);\n\t\t\tassert.strictEqual(mean([10, 20, 30]), 20);\n\t\t\tassert.strictEqual(mean([5]), 5);\n\t\t});\n\n\t\tit(\"should handle negative numbers\", () => {\n\t\t\tassert.strictEqual(mean([-1, 0, 1]), 0);\n\t\t\tassert.strictEqual(mean([-10, -5, 0, 5, 10]), 0);\n\t\t});\n\t});\n\n\tdescribe(\"variance\", () => {\n\t\tit(\"should return 0 for arrays with less than 2 elements\", () => {\n\t\t\tassert.strictEqual(variance([]), 0);\n\t\t\tassert.strictEqual(variance([5]), 0);\n\t\t});\n\n\t\tit(\"should calculate sample variance correctly (with Bessel's correction)\", () => {\n\t\t\t// Sample variance with n-1 denominator\n\t\t\t// Mean = (2+4+4+4+5+5+7+9) / 8 = 5\n\t\t\t// Variance = [(2-5)² + (4-5)² + (4-5)² + (4-5)² + (5-5)² + (5-5)² + (7-5)² + (9-5)²] / (8-1)\n\t\t\t// = [9 + 1 + 1 + 1 + 0 + 0 + 4 + 16] / 7 = 32/7 ≈ 4.571\n\t\t\tconst arr = [2, 4, 4, 4, 5, 5, 7, 9];\n\t\t\tconst m = mean(arr);\n\t\t\tconst expectedVariance = 32 / 7; // ≈ 4.571\n\t\t\tassert.ok(Math.abs(variance(arr, m) - expectedVariance) < 0.01);\n\t\t});\n\n\t\tit(\"should work without pre-calculated mean\", () => {\n\t\t\tconst arr = [2, 4, 4, 4, 5, 5, 7, 9];\n\t\t\tassert.ok(Math.abs(variance(arr) - 32 / 7) < 0.01);\n\t\t});\n\t});\n\n\tdescribe(\"tDistCdf\", () => {\n\t\tit(\"should return 0.5 for t=0\", () => {\n\t\t\tconst result = tDistCdf(0, 10);\n\t\t\tassert.ok(Math.abs(result - 0.5) < 0.001);\n\t\t});\n\n\t\tit(\"should approach 1 for large positive t\", () => {\n\t\t\tconst result = tDistCdf(10, 30);\n\t\t\tassert.ok(result > 0.999);\n\t\t});\n\n\t\tit(\"should approach 0 for large negative t\", () => {\n\t\t\tconst result = tDistCdf(-10, 30);\n\t\t\tassert.ok(result < 0.001);\n\t\t});\n\n\t\tit(\"should return approximately correct values for known t-values\", () => {\n\t\t\t// For t=1.96 with large df, should be approximately 0.975 (two-tailed 95%)\n\t\t\tconst result = tDistCdf(1.96, 100);\n\t\t\tassert.ok(Math.abs(result - 0.975) < 0.01);\n\t\t});\n\t});\n\n\tdescribe(\"welchTTest\", () => {\n\t\tit(\"should return non-significant for identical samples\", () => {\n\t\t\tconst sample1 = [10, 10, 10, 10, 10];\n\t\t\tconst sample2 = [10, 10, 10, 10, 10];\n\t\t\tconst result = welchTTest(sample1, sample2);\n\n\t\t\tassert.strictEqual(result.tStatistic, 0);\n\t\t\tassert.strictEqual(result.significant, false);\n\t\t\tassert.strictEqual(result.pValue, 1);\n\t\t});\n\n\t\tit(\"should return significant for clearly different samples\", () => {\n\t\t\t// Two clearly different distributions\n\t\t\tconst sample1 = [100, 101, 102, 99, 100, 101, 100, 99, 102, 100];\n\t\t\tconst sample2 = [50, 51, 49, 50, 52, 48, 51, 49, 50, 51];\n\t\t\tconst result = welchTTest(sample1, sample2);\n\n\t\t\tassert.ok(result.significant);\n\t\t\tassert.ok(result.pValue < 0.05);\n\t\t\tassert.ok(result.tStatistic > 0); // sample1 > sample2\n\t\t});\n\n\t\tit(\"should handle samples with different sizes\", () => {\n\t\t\tconst sample1 = [100, 101, 102, 99, 100];\n\t\t\tconst sample2 = [50, 51, 49, 50, 52, 48, 51, 49, 50, 51, 50, 49];\n\t\t\tconst result = welchTTest(sample1, sample2);\n\n\t\t\tassert.ok(result.significant);\n\t\t\tassert.ok(result.pValue < 0.05);\n\t\t});\n\n\t\tit(\"should return safe values for insufficient samples\", () => {\n\t\t\tconst result = welchTTest([1], [2]);\n\n\t\t\tassert.strictEqual(result.tStatistic, 0);\n\t\t\tassert.strictEqual(result.pValue, 1);\n\t\t\tassert.strictEqual(result.significant, false);\n\t\t});\n\n\t\tit(\"should handle edge case with zero variance\", () => {\n\t\t\tconst sample1 = [5, 5, 5, 5, 5];\n\t\t\tconst sample2 = [3, 3, 3, 3, 3];\n\t\t\tconst result = welchTTest(sample1, sample2);\n\n\t\t\t// Different means, zero variance - should be significant\n\t\t\tassert.strictEqual(result.significant, true);\n\t\t\tassert.strictEqual(result.pValue, 0);\n\t\t});\n\t});\n\n\tdescribe(\"compareBenchmarks\", () => {\n\t\tit(\"should indicate no significant difference for similar samples\", () => {\n\t\t\t// Samples with overlapping distributions\n\t\t\tconst sample1 = [100, 102, 98, 101, 99, 100, 101, 99, 100, 102];\n\t\t\tconst sample2 = [99, 101, 100, 98, 102, 100, 99, 101, 100, 98];\n\t\t\tconst result = compareBenchmarks(sample1, sample2);\n\n\t\t\tassert.strictEqual(result.significant, false);\n\t\t\tassert.strictEqual(result.difference, \"same\");\n\t\t});\n\n\t\tit(\"should indicate faster when first sample is significantly higher\", () => {\n\t\t\tconst sample1 = [200, 201, 199, 202, 198, 200, 201, 199, 200, 202];\n\t\t\tconst sample2 = [100, 101, 99, 100, 102, 98, 101, 99, 100, 101];\n\t\t\tconst result = compareBenchmarks(sample1, sample2);\n\n\t\t\tassert.strictEqual(result.significant, true);\n\t\t\tassert.strictEqual(result.difference, \"faster\");\n\t\t\tassert.ok(result.pValue < 0.05);\n\t\t});\n\n\t\tit(\"should indicate slower when first sample is significantly lower\", () => {\n\t\t\tconst sample1 = [50, 51, 49, 50, 52, 48, 51, 49, 50, 51];\n\t\t\tconst sample2 = [100, 101, 99, 100, 102, 98, 101, 99, 100, 101];\n\t\t\tconst result = compareBenchmarks(sample1, sample2);\n\n\t\t\tassert.strictEqual(result.significant, true);\n\t\t\tassert.strictEqual(result.difference, \"slower\");\n\t\t\tassert.ok(result.pValue < 0.05);\n\t\t});\n\n\t\tit(\"should respect custom alpha level\", () => {\n\t\t\t// Create samples where p-value is between 0.01 and 0.05\n\t\t\tconst sample1 = [100, 102, 104, 101, 103, 100, 102, 101, 103, 100];\n\t\t\tconst sample2 = [98, 99, 97, 98, 100, 96, 99, 97, 98, 99];\n\t\t\tconst result005 = compareBenchmarks(sample1, sample2, 0.05);\n\t\t\tconst result001 = compareBenchmarks(sample1, sample2, 0.01);\n\n\t\t\t// With alpha=0.05, might be significant; with alpha=0.01, might not be\n\t\t\t// The exact behavior depends on the p-value\n\t\t\tassert.ok(result005.pValue === result001.pValue);\n\t\t});\n\n\t\tit(\"should include confidence percentage in result\", () => {\n\t\t\tconst sample1 = [100, 101, 102, 99, 100];\n\t\t\tconst sample2 = [50, 51, 49, 50, 52];\n\t\t\tconst result = compareBenchmarks(sample1, sample2);\n\n\t\t\tassert.ok(result.confidence.endsWith(\"%\"));\n\t\t\tassert.ok(result.tStatistic !== undefined);\n\t\t\tassert.ok(result.stars !== undefined);\n\t\t});\n\n\t\tit(\"should include significance stars based on p-value\", () => {\n\t\t\t// Highly significant difference (p < 0.001)\n\t\t\tconst sample1 = [200, 201, 199, 202, 198, 200, 201, 199, 200, 202];\n\t\t\tconst sample2 = [100, 101, 99, 100, 102, 98, 101, 99, 100, 101];\n\t\t\tconst result = compareBenchmarks(sample1, sample2);\n\n\t\t\tassert.strictEqual(result.stars, \"***\");\n\t\t\tassert.ok(result.degreesOfFreedom !== undefined);\n\t\t});\n\t});\n\n\tdescribe(\"getSignificanceStars\", () => {\n\t\tit(\"should return *** for p < 0.001\", () => {\n\t\t\tassert.strictEqual(getSignificanceStars(0.0001), \"***\");\n\t\t\tassert.strictEqual(getSignificanceStars(0.0009), \"***\");\n\t\t});\n\n\t\tit(\"should return ** for 0.001 <= p < 0.01\", () => {\n\t\t\tassert.strictEqual(getSignificanceStars(0.001), \"**\");\n\t\t\tassert.strictEqual(getSignificanceStars(0.005), \"**\");\n\t\t\tassert.strictEqual(getSignificanceStars(0.009), \"**\");\n\t\t});\n\n\t\tit(\"should return * for 0.01 <= p < 0.05\", () => {\n\t\t\tassert.strictEqual(getSignificanceStars(0.01), \"*\");\n\t\t\tassert.strictEqual(getSignificanceStars(0.03), \"*\");\n\t\t\tassert.strictEqual(getSignificanceStars(0.049), \"*\");\n\t\t});\n\n\t\tit(\"should return empty string for p >= 0.05\", () => {\n\t\t\tassert.strictEqual(getSignificanceStars(0.05), \"\");\n\t\t\tassert.strictEqual(getSignificanceStars(0.1), \"\");\n\t\t\tassert.strictEqual(getSignificanceStars(0.5), \"\");\n\t\t\tassert.strictEqual(getSignificanceStars(1), \"\");\n\t\t});\n\t});\n});\n\ndescribe(\"T-Test Integration with analyze\", () => {\n\tconst { analyze } = require(\"../lib/utils/analyze\");\n\n\tit(\"should not include significanceTest when ttest is false\", () => {\n\t\tconst results = [\n\t\t\t{\n\t\t\t\tname: \"baseline\",\n\t\t\t\topsSec: 100,\n\t\t\t\tbaseline: true,\n\t\t\t\thistogram: { sampleData: [100, 101, 99, 100, 102] },\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"test\",\n\t\t\t\topsSec: 200,\n\t\t\t\thistogram: { sampleData: [200, 201, 199, 200, 202] },\n\t\t\t},\n\t\t];\n\n\t\tconst analyzed = analyze(results, true, { ttest: false });\n\t\tconst testResult = analyzed.find((r) => r.name === \"test\");\n\n\t\tassert.strictEqual(testResult.significanceTest, undefined);\n\t});\n\n\tit(\"should include significanceTest when ttest is true and opsSecPerRun >= 30\", () => {\n\t\t// Generate 30+ opsSecPerRun samples (from repeatSuite)\n\t\tconst baselineOpsSecPerRun = Array.from(\n\t\t\t{ length: 30 },\n\t\t\t(_, i) => 100 + (i % 3) - 1,\n\t\t);\n\t\tconst testOpsSecPerRun = Array.from(\n\t\t\t{ length: 30 },\n\t\t\t(_, i) => 200 + (i % 3) - 1,\n\t\t);\n\n\t\tconst results = [\n\t\t\t{\n\t\t\t\tname: \"baseline\",\n\t\t\t\topsSec: 100,\n\t\t\t\tbaseline: true,\n\t\t\t\topsSecPerRun: baselineOpsSecPerRun,\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"test\",\n\t\t\t\topsSec: 200,\n\t\t\t\topsSecPerRun: testOpsSecPerRun,\n\t\t\t},\n\t\t];\n\n\t\tconst analyzed = analyze(results, true, { ttest: true });\n\t\tconst testResult = analyzed.find((r) => r.name === \"test\");\n\n\t\tassert.ok(testResult.significanceTest !== undefined);\n\t\tassert.ok(typeof testResult.significanceTest.significant === \"boolean\");\n\t\tassert.ok(typeof testResult.significanceTest.pValue === \"number\");\n\t\tassert.ok(typeof testResult.significanceTest.confidence === \"string\");\n\t});\n\n\tit(\"should not include significanceTest without opsSecPerRun\", () => {\n\t\tconst results = [\n\t\t\t{\n\t\t\t\tname: \"baseline\",\n\t\t\t\topsSec: 100,\n\t\t\t\tbaseline: true,\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"test\",\n\t\t\t\topsSec: 200,\n\t\t\t},\n\t\t];\n\n\t\tconst analyzed = analyze(results, true, { ttest: true });\n\t\tconst testResult = analyzed.find((r) => r.name === \"test\");\n\n\t\t// Should not throw, and significanceTest should not be set (no opsSecPerRun)\n\t\tassert.strictEqual(testResult.significanceTest, undefined);\n\t});\n\n\tit(\"should not include significanceTest when opsSecPerRun < 30\", () => {\n\t\tconst results = [\n\t\t\t{\n\t\t\t\tname: \"baseline\",\n\t\t\t\topsSec: 100,\n\t\t\t\tbaseline: true,\n\t\t\t\topsSecPerRun: Array.from({ length: 10 }, () => 100),\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"test\",\n\t\t\t\topsSec: 200,\n\t\t\t\topsSecPerRun: Array.from({ length: 10 }, () => 200),\n\t\t\t},\n\t\t];\n\n\t\tconst analyzed = analyze(results, true, { ttest: true });\n\t\tconst testResult = analyzed.find((r) => r.name === \"test\");\n\n\t\t// Should not throw, and significanceTest should not be set (not enough samples)\n\t\tassert.strictEqual(testResult.significanceTest, undefined);\n\t});\n\n\tit(\"should detect significant difference between clearly different benchmarks\", () => {\n\t\t// Generate 30+ opsSecPerRun with clearly different means\n\t\tconst baselineOpsSecPerRun = Array.from(\n\t\t\t{ length: 30 },\n\t\t\t(_, i) => 100 + (i % 5) - 2,\n\t\t);\n\t\tconst fastOpsSecPerRun = Array.from(\n\t\t\t{ length: 30 },\n\t\t\t(_, i) => 200 + (i % 5) - 2,\n\t\t);\n\n\t\tconst results = [\n\t\t\t{\n\t\t\t\tname: \"baseline\",\n\t\t\t\topsSec: 100,\n\t\t\t\tbaseline: true,\n\t\t\t\topsSecPerRun: baselineOpsSecPerRun,\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"fast\",\n\t\t\t\topsSec: 200,\n\t\t\t\topsSecPerRun: fastOpsSecPerRun,\n\t\t\t},\n\t\t];\n\n\t\tconst analyzed = analyze(results, true, { ttest: true });\n\t\tconst fastResult = analyzed.find((r) => r.name === \"fast\");\n\n\t\tassert.ok(fastResult.significanceTest.significant);\n\t\tassert.ok(fastResult.significanceTest.pValue < 0.05);\n\t});\n\n\tit(\"should not mark as significant when differences are within noise\", () => {\n\t\t// Same benchmark run twice - should have similar results with high variance overlap\n\t\t// Generate 30+ opsSecPerRun with overlapping distributions\n\t\tconst baselineOpsSecPerRun = Array.from(\n\t\t\t{ length: 30 },\n\t\t\t(_, i) => 100 + ((i % 5) - 2) * 2,\n\t\t);\n\t\tconst similarOpsSecPerRun = Array.from(\n\t\t\t{ length: 30 },\n\t\t\t(_, i) => 101 + ((i % 5) - 2) * 2,\n\t\t);\n\n\t\tconst results = [\n\t\t\t{\n\t\t\t\tname: \"baseline\",\n\t\t\t\topsSec: 100,\n\t\t\t\tbaseline: true,\n\t\t\t\topsSecPerRun: baselineOpsSecPerRun,\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"similar\",\n\t\t\t\topsSec: 101, // Very close to baseline\n\t\t\t\topsSecPerRun: similarOpsSecPerRun,\n\t\t\t},\n\t\t];\n\n\t\tconst analyzed = analyze(results, true, { ttest: true });\n\t\tconst similarResult = analyzed.find((r) => r.name === \"similar\");\n\n\t\t// With overlapping distributions, should not be significant\n\t\tassert.strictEqual(similarResult.significanceTest.significant, false);\n\t});\n});\n\ndescribe(\"Statistical significance requires repeatSuite >= 30\", () => {\n\tconst { analyze } = require(\"../lib/utils/analyze\");\n\n\tit(\"should only compute significance when repeatSuite provides 30+ samples\", () => {\n\t\t// With 30+ opsSecPerRun, significance should be computed\n\t\tconst results = [\n\t\t\t{\n\t\t\t\tname: \"baseline\",\n\t\t\t\topsSec: 100,\n\t\t\t\tbaseline: true,\n\t\t\t\topsSecPerRun: Array.from({ length: 30 }, () => 100),\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"test\",\n\t\t\t\topsSec: 200,\n\t\t\t\topsSecPerRun: Array.from({ length: 30 }, () => 200),\n\t\t\t},\n\t\t];\n\n\t\tconst analyzed = analyze(results, true, { ttest: true });\n\t\tconst testResult = analyzed.find((r) => r.name === \"test\");\n\n\t\tassert.ok(testResult.significanceTest !== undefined);\n\t});\n\n\tit(\"should not compute significance when repeatSuite < 30\", () => {\n\t\t// With fewer than 30 opsSecPerRun, significance should not be computed\n\t\tconst results = [\n\t\t\t{\n\t\t\t\tname: \"baseline\",\n\t\t\t\topsSec: 100,\n\t\t\t\tbaseline: true,\n\t\t\t\topsSecPerRun: Array.from({ length: 10 }, () => 100),\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"test\",\n\t\t\t\topsSec: 200,\n\t\t\t\topsSecPerRun: Array.from({ length: 10 }, () => 200),\n\t\t\t},\n\t\t];\n\n\t\tconst analyzed = analyze(results, true, { ttest: true });\n\t\tconst testResult = analyzed.find((r) => r.name === \"test\");\n\n\t\tassert.strictEqual(testResult.significanceTest, undefined);\n\t});\n});\n"
  },
  {
    "path": "test/worker.js",
    "content": "const workerThreads = require(\"node:worker_threads\");\nconst { describe, it, before, after, mock } = require(\"node:test\");\nconst assert = require(\"node:assert\");\n\nfunction noop() {}\n\ndescribe(\"Using worker_threads\", () => {\n\tbefore(async () => {\n\t\tmock.method(workerThreads, \"Worker\");\n\n\t\tconst { Suite } = require(\"../lib/index\");\n\n\t\tconst bench = new Suite({\n\t\t\treporter: noop,\n\t\t\tuseWorkers: true,\n\t\t});\n\n\t\tbench\n\t\t\t.add(\"Import with node: prefix\", () => {\n\t\t\t\treturn import(\"node:fs\");\n\t\t\t})\n\t\t\t.add(\"Import without node: prefix\", () => {\n\t\t\t\treturn import(\"node:fs\");\n\t\t\t})\n\t\t\t.add(\"async test\", async () => {\n\t\t\t\treturn import(\"node:fs\");\n\t\t\t})\n\t\t\t.add(\"async with timer\", async (timer) => {\n\t\t\t\ttimer.start();\n\t\t\t\tlet i = 0;\n\t\t\t\twhile (i++ < timer.count) {\n\t\t\t\t\tawait import(\"node:fs\");\n\t\t\t\t}\n\t\t\t\ttimer.end(timer.count);\n\t\t\t});\n\n\t\tawait bench.run();\n\t});\n\n\tafter(() => {\n\t\tmock.restoreAll();\n\t});\n\n\tit(\"should create a new Worker 4 times\", () => {\n\t\tassert.strictEqual(workerThreads.Worker.mock.calls.length, 4);\n\t});\n});\n"
  },
  {
    "path": "types/types.test-d.ts",
    "content": "import type { Histogram } from \"node:perf_hooks\";\nimport { expectAssignable, expectNotAssignable, expectType } from \"tsd\";\n\nimport {\n\tMemoryPlugin,\n\tSuite,\n\tV8GetOptimizationStatus,\n\tV8NeverOptimizePlugin,\n\tV8OptimizeOnNextCallPlugin,\n\tchartReport,\n\tcsvReport,\n\thtmlReport,\n\tjsonReport,\n\tprettyReport,\n\ttextReport,\n} from \"..\";\n\nimport type { BenchNode } from \"..\";\n\nexpectType<BenchNode.Suite>(new Suite());\nexpectType<BenchNode.Suite>(\n\tnew Suite({\n\t\treporter: textReport,\n\t\tbenchmarkMode: \"ops\",\n\t\tuseWorkers: true,\n\t\tplugins: [new V8NeverOptimizePlugin()],\n\t}),\n);\nexpectType<BenchNode.Suite>(new Suite({ reporter: false }));\nexpectType<BenchNode.Suite>(new Suite({ reporter: null }));\nexpectType<BenchNode.Suite>(new Suite({ minSamples: 20 }));\n\nexpectAssignable<BenchNode.SuiteOptions>({});\nexpectAssignable<BenchNode.SuiteOptions>({ reporter: chartReport });\nexpectAssignable<BenchNode.SuiteOptions>({ benchmarkMode: \"time\" });\nexpectAssignable<BenchNode.SuiteOptions>({ useWorkers: false });\nexpectAssignable<BenchNode.SuiteOptions>({\n\tplugins: [new V8GetOptimizationStatus()],\n});\nexpectAssignable<BenchNode.SuiteOptions>({ minSamples: 20 });\nexpectNotAssignable<BenchNode.SuiteOptions>({ unknownOption: \"test\" });\nexpectNotAssignable<BenchNode.SuiteOptions>({ reporter: \"not-a-function\" });\nexpectNotAssignable<BenchNode.SuiteOptions>({ minSamples: \"not-a-number\" });\n\nexpectAssignable<BenchNode.BenchmarkOptions>({});\nexpectAssignable<BenchNode.BenchmarkOptions>({ minTime: 0.1 });\nexpectAssignable<BenchNode.BenchmarkOptions>({ maxTime: 1 });\nexpectAssignable<BenchNode.BenchmarkOptions>({ repeatSuite: 2 });\nexpectAssignable<BenchNode.BenchmarkOptions>({ minSamples: 5 });\nexpectNotAssignable<BenchNode.BenchmarkOptions>({ minTime: \"not-a-number\" });\n\n// Test Suite.add method\nconst suite = new Suite();\nexpectType<BenchNode.Suite>(\n\tsuite.add(\"sync benchmark\", () => {\n\t\tconst arr = [];\n\t\tfor (let i = 0; i < 1000; i++) {\n\t\t\tarr.push(i);\n\t\t}\n\t}),\n);\nexpectType<BenchNode.Suite>(\n\tsuite.add(\"async benchmark\", async () => {\n\t\tawait new Promise((resolve) => setTimeout(resolve, 10));\n\t}),\n);\nexpectType<BenchNode.Suite>(\n\tsuite.add(\n\t\t\"benchmark with options\",\n\t\t{ minTime: 0.1, maxTime: 1, repeatSuite: 2, minSamples: 5 },\n\t\t() => {\n\t\t\t/* ... */\n\t\t},\n\t),\n);\n\nconst managedBenchFn: BenchNode.BenchmarkFunction = (timer) => {\n\tif (timer) {\n\t\texpectType<() => void>(timer.start);\n\t\texpectType<(iterations?: number) => void>(timer.end);\n\t\texpectType<number>(timer.count);\n\t\ttimer.start();\n\t\tfor (let i = 0; i < timer.count; i++) {\n\t\t\t// some operation\n\t\t}\n\t}\n};\nexpectType<BenchNode.Suite>(suite.add(\"managed benchmark\", managedBenchFn));\n\n// Test Suite.run method\nexpectType<Promise<BenchNode.BenchmarkResult[]>>(suite.run());\n\nsuite.run().then((results) => {\n\texpectType<BenchNode.BenchmarkResult[]>(results);\n\tif (results.length > 0) {\n\t\tconst result = results[0];\n\t\texpectType<string>(result.name);\n\t\texpectType<number | undefined>(result.opsSec);\n\t\texpectType<number[] | undefined>(result.opsSecPerRun);\n\t\texpectType<number | undefined>(result.totalTime);\n\t\texpectType<number>(result.iterations);\n\t\texpectType<Histogram>(result.histogram);\n\t\texpectType<Record<string, any> | undefined>(result.plugins);\n\n\t\tif (result.plugins?.V8GetOptimizationStatus) {\n\t\t\texpectType<any>(\n\t\t\t\tresult.plugins.V8GetOptimizationStatus.optimizationStatuses,\n\t\t\t);\n\t\t}\n\t}\n});\n\n// Test ReporterFunction\nconst sampleResults: BenchNode.BenchmarkResult[] = [\n\t{\n\t\tname: \"sample\",\n\t\titerations: 100,\n\t\thistogram: {} as Histogram, // Cast for simplicity in type test\n\t\topsSec: 10000,\n\t\topsSecPerRun: [10000],\n\t\ttotalTime: 0.1,\n\t\tplugins: { MyPlugin: { data: \"value\" } },\n\t},\n];\nexpectType<void>(textReport(sampleResults));\nexpectType<void>(chartReport(sampleResults));\nexpectType<void>(htmlReport(sampleResults));\nexpectType<void>(jsonReport(sampleResults));\nexpectType<void>(csvReport(sampleResults));\nexpectType<void>(prettyReport(sampleResults));\n\n// Test Plugins\nconst plugin1 = new V8NeverOptimizePlugin();\nexpectAssignable<BenchNode.Plugin>(plugin1);\nif (plugin1.isSupported?.()) {\n\texpectType<boolean>(plugin1.isSupported());\n\tconst varNames: BenchNode.PluginHookVarNames = {\n\t\tawaitOrEmpty: \"\",\n\t\tbench: \"fn\",\n\t\tcontext: \"context\",\n\t\ttimer: \"timer\",\n\t\tmanaged: false,\n\t};\n\texpectType<string[]>(plugin1.beforeClockTemplate(varNames));\n\texpectType<string>(plugin1.toString());\n}\n\nconst plugin2 = new V8GetOptimizationStatus();\nexpectAssignable<BenchNode.Plugin>(plugin2);\nif (plugin2.isSupported?.()) {\n\texpectType<boolean>(plugin2.isSupported());\n\tconst varNames: BenchNode.PluginHookVarNames = {\n\t\tawaitOrEmpty: \"\",\n\t\tbench: \"fn\",\n\t\tcontext: \"context\",\n\t\ttimer: \"timer\",\n\t\tmanaged: false,\n\t};\n\tconst benchmarkResult: BenchNode.OnCompleteBenchmarkResult = [0, 0, {}];\n\texpectType<string[]>(plugin2.afterClockTemplate(varNames));\n\texpectType<void>(plugin2.onCompleteBenchmark(benchmarkResult));\n\texpectType<string>(plugin2.toString());\n}\n\nconst plugin3 = new V8OptimizeOnNextCallPlugin();\nexpectAssignable<BenchNode.Plugin>(plugin3);\nif (plugin3.isSupported?.()) {\n\texpectType<boolean>(plugin3.isSupported());\n\tconst varNames: BenchNode.PluginHookVarNames = {\n\t\tawaitOrEmpty: \"\",\n\t\tbench: \"fn\",\n\t\tcontext: \"context\",\n\t\ttimer: \"timer\",\n\t\tmanaged: false,\n\t};\n\texpectType<string[]>(plugin3.beforeClockTemplate(varNames));\n\texpectType<string>(plugin3.toString());\n}\n\nconst plugin4 = new MemoryPlugin();\nexpectAssignable<BenchNode.Plugin>(plugin4);\nif (plugin4.isSupported?.()) {\n\texpectType<boolean>(plugin3.isSupported());\n\tconst varNames: BenchNode.PluginHookVarNames = {\n\t\tawaitOrEmpty: \"\",\n\t\tbench: \"fn\",\n\t\tcontext: \"context\",\n\t\ttimer: \"timer\",\n\t\tmanaged: false,\n\t};\n\tconst benchmarkResult: BenchNode.OnCompleteBenchmarkResult = [0, 0, {}];\n\texpectType<string[]>(plugin4.beforeClockTemplate(varNames));\n\texpectType<string[]>(plugin4.afterClockTemplate(varNames));\n\texpectType<void>(plugin4.onCompleteBenchmark(benchmarkResult));\n\texpectType<string>(plugin4.toString());\n\texpectType<string>(plugin4.getReport(\"test\"));\n\texpectType<BenchNode.PluginResult>(plugin4.getResult(\"test\"));\n}\n\nexpectAssignable<BenchNode.Suite>(new Suite());\nexpectAssignable<BenchNode.V8NeverOptimizePlugin>(new V8NeverOptimizePlugin());\nexpectAssignable<BenchNode.V8GetOptimizationStatus>(\n\tnew V8GetOptimizationStatus(),\n);\nexpectAssignable<BenchNode.V8OptimizeOnNextCallPlugin>(\n\tnew V8OptimizeOnNextCallPlugin(),\n);\nexpectAssignable<BenchNode.MemoryPlugin>(new MemoryPlugin());\n"
  }
]