From 6fa779b1fec65dfb63c9e56f2fa833f472a4c413 Mon Sep 17 00:00:00 2001 From: mspiess <30656914+mspiess@users.noreply.github.com> Date: Sun, 2 Aug 2026 19:27:25 +0200 Subject: [PATCH 1/5] chore(deps-dev): update vitest to v5 --- package-lock.json | 243 +++++++++++++++++----------------------------- package.json | 9 +- 2 files changed, 97 insertions(+), 155 deletions(-) diff --git a/package-lock.json b/package-lock.json index d03c50b..96b783c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,7 +16,7 @@ "@microsoft/api-extractor": "7.58.12", "@stylistic/eslint-plugin": "5.10.0", "@types/node": "26.2.0", - "@vitest/coverage-v8": "4.1.10", + "@vitest/coverage-v8": "5.0.0-rc.2", "@vitest/eslint-plugin": "1.6.27", "eslint": "10.8.1", "globals": "17.9.0", @@ -25,7 +25,7 @@ "unplugin-dts": "1.0.3", "vite": "8.2.1", "vite-plugin-doctest": "3.0.0", - "vitest": "4.1.10" + "vitest": "5.0.0-rc.2" } }, "node_modules/@babel/helper-string-parser": { @@ -1220,6 +1220,7 @@ "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -1360,13 +1361,6 @@ "sprintf-js": "~1.0.2" } }, - "node_modules/@standard-schema/spec": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", - "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", - "dev": true, - "license": "MIT" - }, "node_modules/@stylistic/eslint-plugin": { "version": "5.10.0", "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin/-/eslint-plugin-5.10.0.tgz", @@ -1484,6 +1478,7 @@ "integrity": "sha512-5IviulTZeRNp2vAJ514cc/HUlY5nZ9fCbq9DMyC52BrhFZACo3nI0R7qBxhQmo/d27NFe96ur/b7Wwxklda+kg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "undici-types": "~8.3.0" } @@ -1501,6 +1496,7 @@ "integrity": "sha512-Un7Heoyj65NREbKAyIrFxeM143NZpExWmy1Nep4DLeQOeLlTeumPjoNKnBrU5D5moWXbPJgRa5Uwcdu0faVNGQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/regexpp": "^4.12.2", "@typescript-eslint/scope-manager": "8.67.0", @@ -1540,6 +1536,7 @@ "integrity": "sha512-fUBfTuuEulWqX6V8+O3PtScV01tzYYRUDTAirHFKoRAt7nOzoGiPt0M/bB47wWNy0coOOcgEwAMUtBpykMxl6w==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.67.0", "@typescript-eslint/types": "8.67.0", @@ -1739,29 +1736,29 @@ } }, "node_modules/@vitest/coverage-v8": { - "version": "4.1.10", - "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-4.1.10.tgz", - "integrity": "sha512-IM49HmthevbgAO4anp1hwtoT9wYe59w0LR00gr+eagHE+ZJ5lK4sLPeO0ubgoJcwLk6dehU3R24N+FbEEKDc8g==", + "version": "5.0.0-rc.2", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-5.0.0-rc.2.tgz", + "integrity": "sha512-ZwYwczFui6PCGwxUmT9kUSmquuF1YUB9QMnARqjdVYOegE5+wqEL1NgoEu3jmjyoplHuC6gwktMTp8Bif27f/w==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@bcoe/v8-coverage": "^1.0.2", - "@vitest/utils": "4.1.10", - "ast-v8-to-istanbul": "^1.0.0", + "ast-v8-to-istanbul": "^1.0.5", "istanbul-lib-coverage": "^3.2.2", "istanbul-lib-report": "^3.0.1", "istanbul-reports": "^3.2.0", - "magicast": "^0.5.2", - "obug": "^2.1.1", - "std-env": "^4.0.0-rc.1", - "tinyrainbow": "^3.1.0" + "magicast": "^0.5.4", + "obug": "^2.1.4", + "std-env": "^4.2.0", + "tinyrainbow": "^3.1.1" }, "funding": { "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "@vitest/browser": "4.1.10", - "vitest": "4.1.10" + "@vitest/browser": "5.0.0-rc.2", + "vitest": "5.0.0-rc.2" }, "peerDependenciesMeta": { "@vitest/browser": { @@ -1800,34 +1797,17 @@ } } }, - "node_modules/@vitest/expect": { - "version": "4.1.10", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.10.tgz", - "integrity": "sha512-YsCn+qAk1GWjQOWFEsEcL2gNQ0zmVmQu3T03qP6UyjhtmdtwtbuI+DASn/7iQB3HGTXkdBwGddzxPlmiql5vlA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@standard-schema/spec": "^1.1.0", - "@types/chai": "^5.2.2", - "@vitest/spy": "4.1.10", - "@vitest/utils": "4.1.10", - "chai": "^6.2.2", - "tinyrainbow": "^3.1.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, "node_modules/@vitest/mocker": { - "version": "4.1.10", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.10.tgz", - "integrity": "sha512-v0xaezt+DKEmKfaxg133ldzADrwLGd7Ze1MfQQTYfvs8OqZIwbxyxaYURivwV7sWy5fqn3rH5uOrSp07bp44Ow==", + "version": "5.0.0-rc.2", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-5.0.0-rc.2.tgz", + "integrity": "sha512-nbUdBsadb3otfazI0v99+PQuSWiBzz5obMM5Pb1mpLzdPSEG6gVHYiVLuj0PC2dcoqPjd6dgsB5o0t70RXbs2A==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "4.1.10", + "@jridgewell/trace-mapping": "0.3.31", + "@vitest/spy": "5.0.0-rc.2", "estree-walker": "^3.0.3", - "magic-string": "^0.30.21" + "magic-string": "^1.2.0" }, "funding": { "url": "https://opencollective.com/vitest" @@ -1845,70 +1825,22 @@ } } }, - "node_modules/@vitest/pretty-format": { - "version": "4.1.10", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.10.tgz", - "integrity": "sha512-W1HsjSH4MXQ9YfmmhLAoIYf1HRfekQCGngeIgcei6MP5QQGWUe0gkopdZQaVCFO+JDJMrAJGwa5pRpNpvy4P8Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "tinyrainbow": "^3.1.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/runner": { - "version": "4.1.10", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.10.tgz", - "integrity": "sha512-IKI6kpIH+LmpROplyLwBBaCfMgOZOMsygVa6BARD6ahA04VRuJSa6OaVG7kRvSEMD870Vd91rSSw0eegtWyLGg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/utils": "4.1.10", - "pathe": "^2.0.3" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/snapshot": { - "version": "4.1.10", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.10.tgz", - "integrity": "sha512-xRkfOT1qpTAi/Ti4Y1LtfRc3kEuqxGw59eN2jN9pRWMtS/XDevekhcFSqvQqjUNGksfjMJu3Y+oJ+4Ypn2OaJw==", + "node_modules/@vitest/mocker/node_modules/magic-string": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-1.2.0.tgz", + "integrity": "sha512-ptco+HFxTLgjafSLim2LojBSwfg5feBjd+SqyiwdGkzC38UPdZy3zgrHMI2CoTf5fJL38tbHMYWVzIH8BxGqJw==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "4.1.10", - "@vitest/utils": "4.1.10", - "magic-string": "^0.30.21", - "pathe": "^2.0.3" - }, - "funding": { - "url": "https://opencollective.com/vitest" + "@jridgewell/sourcemap-codec": "^1.5.5" } }, "node_modules/@vitest/spy": { - "version": "4.1.10", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.10.tgz", - "integrity": "sha512-PLf/Ugvoq5wO/b4rwYCR1h2PSIdXz7wnkQFMiUpLdtM7l6pqVFcQIBEHyT1+l+cj7mNwAfZHzqXqDyjvOuwbDw==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/utils": { - "version": "4.1.10", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.10.tgz", - "integrity": "sha512-fy9am/HWxbaGt/Sawrp90vt6Y6jQwf1RX77cz3uwoJwJVMli/e1IEwRPnMNJ7vKfPTwo0diXifkpPvwH9v7nGA==", + "version": "5.0.0-rc.2", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-5.0.0-rc.2.tgz", + "integrity": "sha512-6/p30efajxGxC3KzLae67vRazEKB8Z8s4l0DwbVJLobXyCHDAQPoVMXeS7nKP8aWHOi8Ec9XBUxFvFPF/NRnYA==", "dev": true, "license": "MIT", - "dependencies": { - "@vitest/pretty-format": "4.1.10", - "convert-source-map": "^2.0.0", - "tinyrainbow": "^3.1.0" - }, "funding": { "url": "https://opencollective.com/vitest" } @@ -1948,6 +1880,7 @@ "integrity": "sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==", "dev": true, "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -2195,13 +2128,6 @@ "dev": true, "license": "MIT" }, - "node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true, - "license": "MIT" - }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -2390,6 +2316,7 @@ "integrity": "sha512-wqA7W2jbsC/BnV9Iv1UZpKVFkO1AdNoSmYW8NWG4HNOBbkAMvIqDZ27pI2f07dqn583NcIC44ckjAcOXDL1QbQ==", "dev": true, "license": "MIT", + "peer": true, "workspaces": [ "packages/*" ], @@ -2595,9 +2522,9 @@ } }, "node_modules/expect-type": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz", - "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.4.0.tgz", + "integrity": "sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==", "dev": true, "license": "Apache-2.0", "engines": { @@ -5049,6 +4976,7 @@ "integrity": "sha512-rn9wpmxplLf7NLNyCk9FyWh3FM43DbY8jOzCdEPzH7uflhTftRbCEpqi6Ly2osgoU8OwObtmavMbWLaWy4LX7A==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@oxc-project/types": "=0.143.0", "@rolldown/pluginutils": "^1.0.0" @@ -5301,16 +5229,19 @@ } }, "node_modules/tinybench": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", - "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-6.1.3.tgz", + "integrity": "sha512-8k25iNSZHnzkjp3nrpEmx6YkrTNs41PzOHYc28DR5Z2l/CId/Nzw+Ume/41jCeDDmCUMPuK5GkQ/VxJaJ2P6Qg==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">=20.0.0" + } }, "node_modules/tinyexec": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.2.tgz", - "integrity": "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.3.0.tgz", + "integrity": "sha512-QKAl9m8gWWGHV8jZcPeym6j+XULi6tOf1mT83WYJ4Lk2ytW/uwAWkrP0uFsdoYMdueVJ0qs26wZ+23xeB4ibNQ==", "dev": true, "license": "MIT", "engines": { @@ -5335,9 +5266,9 @@ } }, "node_modules/tinyrainbow": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.0.tgz", - "integrity": "sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.1.tgz", + "integrity": "sha512-yau8yJdTt989Mm0Bd/236QnzEiPf2xLLTqUZRUJOo/3CB078LSwzei343DgtJVmfJKJE3TMINY1u42SQsP6mXw==", "dev": true, "license": "MIT", "engines": { @@ -5396,6 +5327,7 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -5612,6 +5544,7 @@ "integrity": "sha512-EU/eS7BH3XROHh2YnBefjM6DBKA6ZeMZEYQbj7NLWg5wHYlhB8B/Mayd5XsgWq+NFYccDOTemRpdETWR6Ka/lw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "lightningcss": "^1.33.0", "picomatch": "^4.0.5", @@ -5697,38 +5630,32 @@ } }, "node_modules/vitest": { - "version": "4.1.10", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.10.tgz", - "integrity": "sha512-R9jUTe5S4Qb0HCd4TNqpC7oGcrMssMRGXLW80ubjWsW9VH5GF8y1Y0SFLY9AbqSk6nt0PnOx4H4WNJYZ13GUPw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/expect": "4.1.10", - "@vitest/mocker": "4.1.10", - "@vitest/pretty-format": "4.1.10", - "@vitest/runner": "4.1.10", - "@vitest/snapshot": "4.1.10", - "@vitest/spy": "4.1.10", - "@vitest/utils": "4.1.10", - "es-module-lexer": "^2.0.0", - "expect-type": "^1.3.0", - "magic-string": "^0.30.21", - "obug": "^2.1.1", - "pathe": "^2.0.3", - "picomatch": "^4.0.3", - "std-env": "^4.0.0-rc.1", - "tinybench": "^2.9.0", - "tinyexec": "^1.0.2", - "tinyglobby": "^0.2.15", - "tinyrainbow": "^3.1.0", - "vite": "^6.0.0 || ^7.0.0 || ^8.0.0", + "version": "5.0.0-rc.2", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-5.0.0-rc.2.tgz", + "integrity": "sha512-tSdrRylWxqWHYhyHWUdFd3ASHGXxqGmRcPfjRvir+bcFzJ5se9DLzhvzMPS7F4yLWOPsaaILSMPBbWzMzgC1VA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@types/chai": "^5.2.2", + "@vitest/mocker": "5.0.0-rc.2", + "chai": "^6.2.2", + "es-module-lexer": "^2.3.1", + "expect-type": "^1.4.0", + "magic-string": "^1.2.0", + "obug": "^2.1.4", + "picomatch": "^4.0.5", + "std-env": "^4.2.0", + "tinybench": "6.1.3", + "tinyexec": "1.3.0", + "tinyglobby": "^0.2.17", "why-is-node-running": "^2.3.0" }, "bin": { "vitest": "vitest.mjs" }, "engines": { - "node": "^20.0.0 || ^22.0.0 || >=24.0.0" + "node": "^22.12.0 || ^24.0.0 || >=26.0.0" }, "funding": { "url": "https://opencollective.com/vitest" @@ -5736,16 +5663,16 @@ "peerDependencies": { "@edge-runtime/vm": "*", "@opentelemetry/api": "^1.9.0", - "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", - "@vitest/browser-playwright": "4.1.10", - "@vitest/browser-preview": "4.1.10", - "@vitest/browser-webdriverio": "4.1.10", - "@vitest/coverage-istanbul": "4.1.10", - "@vitest/coverage-v8": "4.1.10", - "@vitest/ui": "4.1.10", + "@types/node": "^22.0.0 || >=24.0.0", + "@vitest/browser-playwright": "5.0.0-rc.2", + "@vitest/browser-preview": "5.0.0-rc.2", + "@vitest/browser-webdriverio": "^5.0.0-beta.5 || >=5.0.0", + "@vitest/coverage-istanbul": "5.0.0-rc.2", + "@vitest/coverage-v8": "5.0.0-rc.2", + "@vitest/ui": "5.0.0-rc.2", "happy-dom": "*", "jsdom": "*", - "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" + "vite": "^6.4.0 || ^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { "@edge-runtime/vm": { @@ -5786,6 +5713,16 @@ } } }, + "node_modules/vitest/node_modules/magic-string": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-1.2.0.tgz", + "integrity": "sha512-ptco+HFxTLgjafSLim2LojBSwfg5feBjd+SqyiwdGkzC38UPdZy3zgrHMI2CoTf5fJL38tbHMYWVzIH8BxGqJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, "node_modules/vscode-uri": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.1.0.tgz", diff --git a/package.json b/package.json index 11fbd6f..05f0d0e 100644 --- a/package.json +++ b/package.json @@ -47,7 +47,7 @@ "@microsoft/api-extractor": "7.58.12", "@stylistic/eslint-plugin": "5.10.0", "@types/node": "26.2.0", - "@vitest/coverage-v8": "4.1.10", + "@vitest/coverage-v8": "5.0.0-rc.2", "@vitest/eslint-plugin": "1.6.27", "eslint": "10.8.1", "globals": "17.9.0", @@ -56,6 +56,11 @@ "unplugin-dts": "1.0.3", "vite": "8.2.1", "vite-plugin-doctest": "3.0.0", - "vitest": "4.1.10" + "vitest": "5.0.0-rc.2" + }, + "overrides": { + "vite-plugin-doctest": { + "vitest": "5.0.0-rc.2" + } } } From 6b109addb78835fee4d5b9991b310ab1896e457e Mon Sep 17 00:00:00 2001 From: mspiess <30656914+mspiess@users.noreply.github.com> Date: Sun, 2 Aug 2026 20:36:35 +0200 Subject: [PATCH 2/5] test: add benchmarks --- .github/workflows/node.js.yml | 2 + package.json | 1 + src/main.benchmark.ts | 102 ++++++++++++++++++++++++++++++++++ 3 files changed, 105 insertions(+) create mode 100644 src/main.benchmark.ts diff --git a/.github/workflows/node.js.yml b/.github/workflows/node.js.yml index 830e28c..4ca570c 100644 --- a/.github/workflows/node.js.yml +++ b/.github/workflows/node.js.yml @@ -30,3 +30,5 @@ jobs: run: npm test -- --coverage - if: ${{ matrix.node-version == '26.x' }} uses: coverallsapp/github-action@v2 + - if: ${{ matrix.node-version == '26.x' }} + run: npm run benchmark diff --git a/package.json b/package.json index 05f0d0e..c1c2b3d 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ "build": "tsc && vite build", "lint": "eslint src/", "test": "vitest --typecheck --run", + "benchmark": "vitest bench", "changeset": "changeset", "release": "npm run build && changeset tag && npm publish --provenance --access public" }, diff --git a/src/main.benchmark.ts b/src/main.benchmark.ts new file mode 100644 index 0000000..cbf0334 --- /dev/null +++ b/src/main.benchmark.ts @@ -0,0 +1,102 @@ +import { describe, expect, test } from 'vitest'; +import type { IntegerLessThan32 } from './IntegerLessThan32.ts'; +import { EnumSet } from './main.ts'; + +const allValues = [...Array(32).keys()] as IntegerLessThan32[]; + +describe('has', () => { + test('empty set', async ({ bench }) => { + const nativeSet = new Set(); + const enumSet = new EnumSet(); + + await bench.compare( + bench('native set', () => { + allValues.forEach((value) => { + nativeSet.has(value); + }); + }), bench('enum set', () => { + allValues.forEach((value) => { + enumSet.has(value); + }); + }), + ); + }); + + test('full set', async ({ bench }) => { + const nativeSet = new Set(allValues); + const enumSet = new EnumSet(allValues); + + await bench.compare( + bench('native set', () => { + allValues.forEach((value) => { + nativeSet.has(value); + }); + }), bench('enum set', () => { + allValues.forEach((value) => { + enumSet.has(value); + }); + }), + ); + }); +}); + +test('add', async ({ bench }) => { + const nativeSet = new Set(); + const enumSet = new EnumSet(); + + const result = await bench.compare( + bench('native set', () => { + allValues.forEach((value) => { + nativeSet.add(value); + }); + }), bench('enum set', () => { + allValues.forEach((value) => { + enumSet.add(value); + }); + }), + ); + + expect([...nativeSet.values()]).toStrictEqual(allValues); + expect([...enumSet.values()]).toStrictEqual(allValues); + expect(result.get('enum set')).toBeFasterThan(result.get('native set')); +}); + +test('delete', async ({ bench }) => { + const nativeSet = new Set(allValues); + const enumSet = new EnumSet(allValues); + + const result = await bench.compare( + bench('native set', () => { + allValues.forEach((value) => { + nativeSet.delete(value); + }); + }), bench('enum set', () => { + allValues.forEach((value) => { + enumSet.delete(value); + }); + }), + ); + + expect(nativeSet.size).toBe(0); + expect(enumSet.size).toBe(0); + expect(result.get('enum set')).toBeFasterThan(result.get('native set')); +}); + +test.for([ + { method: 'keys' as const }, + { method: 'values' as const }, + // workaround for https://github.com/microsoft/TypeScript/issues/54100 + // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion + { method: Symbol.iterator } as { method: typeof Symbol.iterator }, +])('$method', async ({ method }, { bench }) => { + const nativeSet = new Set(allValues); + const enumSet = new EnumSet(allValues); + + await bench.compare( + bench('native set', () => { + nativeSet[method](); + }), bench('enum set', () => { + enumSet[method](); + }), + ); +}); From 3d14966c1ca6667ed236a7003adf2f2eb11e01c0 Mon Sep 17 00:00:00 2001 From: mspiess <30656914+mspiess@users.noreply.github.com> Date: Sun, 16 Aug 2026 13:13:24 +0200 Subject: [PATCH 3/5] test: add benchmarks for composition methods --- src/main.benchmark.ts | 103 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) diff --git a/src/main.benchmark.ts b/src/main.benchmark.ts index cbf0334..4a9f4e1 100644 --- a/src/main.benchmark.ts +++ b/src/main.benchmark.ts @@ -3,6 +3,8 @@ import type { IntegerLessThan32 } from './IntegerLessThan32.ts'; import { EnumSet } from './main.ts'; const allValues = [...Array(32).keys()] as IntegerLessThan32[]; +const lowerHalfValues = [...Array(16).keys()] as IntegerLessThan32[]; +const upperHalfValues = [...Array(16).keys()].map(value => value + 16) as IntegerLessThan32[]; describe('has', () => { test('empty set', async ({ bench }) => { @@ -100,3 +102,104 @@ test.for([ }), ); }); + +test.for<{ + operation: 'union' | 'intersection' | 'difference' | 'symmetricDifference'; + leftValues: IntegerLessThan32[]; + rightValues: IntegerLessThan32[]; + expectedValues: IntegerLessThan32[]; +}>([ + { + operation: 'union', + leftValues: lowerHalfValues, + rightValues: upperHalfValues, + expectedValues: allValues, + }, + { + operation: 'intersection', + leftValues: lowerHalfValues, + rightValues: allValues, + expectedValues: lowerHalfValues, + }, + { + operation: 'difference', + leftValues: allValues, + rightValues: upperHalfValues, + expectedValues: lowerHalfValues, + }, + { + operation: 'symmetricDifference', + leftValues: [0, 1, 2, 3], + rightValues: [2, 3, 4, 5], + expectedValues: [0, 1, 4, 5], + }, +])('$operation', async ({ operation, leftValues, rightValues, expectedValues }, { bench }) => { + const nativeSets = [new Set(leftValues), new Set(rightValues)]; + const enumSets = [new EnumSet(leftValues), new EnumSet(rightValues)]; + type CompositionMethod = typeof nativeSets[number][typeof operation]; + + let nativeResult: Set | undefined; + let enumSetResult: Set | undefined; + + const result = await bench.compare( + bench('native set', () => { + const [left, right] = nativeSets; + + nativeResult = left[operation](right); + }), + bench('enum set', () => { + const [left, right] = enumSets; + + enumSetResult = (left[operation] as CompositionMethod)(right) as EnumSet; + }), + ); + + expect([...nativeResult!.values()]).toEqual(expectedValues); + expect([...enumSetResult!.values()]).toEqual(expectedValues); + expect(result.get('enum set')).toBeFasterThan(result.get('native set')); +}); + +test.for<{ + operation: 'isDisjointFrom' | 'isSubsetOf' | 'isSupersetOf'; + leftValues: IntegerLessThan32[]; + rightValues: IntegerLessThan32[]; +}>([ + { + operation: 'isSubsetOf', + leftValues: lowerHalfValues, + rightValues: allValues, + }, + { + operation: 'isSupersetOf', + leftValues: allValues, + rightValues: lowerHalfValues, + }, + { + operation: 'isDisjointFrom', + leftValues: lowerHalfValues, + rightValues: upperHalfValues, + }, +])('$operation', async ({ operation, leftValues, rightValues }, { bench }) => { + const nativeSets = [new Set(leftValues), new Set(rightValues)]; + const enumSets = [new EnumSet(leftValues), new EnumSet(rightValues)]; + + let nativeResult = false; + let enumSetResult = false; + + const result = await bench.compare( + bench('native set', () => { + const [left, right] = nativeSets; + + nativeResult = left[operation](right); + }), + bench('enum set', () => { + const [left, right] = enumSets; + + enumSetResult = left[operation](right); + }), + ); + + expect(nativeResult).toBe(true); + expect(enumSetResult).toBe(true); + expect(result.get('enum set')).toBeFasterThan(result.get('native set')); +}); From 46e1840fd2087e5f296a24b7e09043f0eec072d3 Mon Sep 17 00:00:00 2001 From: mspiess <30656914+mspiess@users.noreply.github.com> Date: Mon, 17 Aug 2026 21:57:42 +0200 Subject: [PATCH 4/5] docs: add benchmark section --- README.md | 79 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 78 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 41a9501..a6a59ac 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ All state is packed into a single 32-bit integer bitfield. ## Installation -``` +```shell npm install @marco.spiess/enumset ``` @@ -43,3 +43,80 @@ expect(enumSet.has(Directions.Left)).toBe(false); - Iteration order breaks the [Set](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set) contract. The methods `forEach`, `keys`, `values` and `[Symbol.iterator]` iterate in the ascending order of the enum values and *not* in insertion order. + +## Benchmark + +Compared to the native `Set` `EnumSets` perform better for `add` and `delete`. +For `has` and iteration `EnumSets` are slightly slower. +For all set composition methods `EnumSets` vastly outperform their native counterparts. + +
+Result of a benchmark run in CI + +``` + ✓ bench src/main.benchmark.ts (14 tests) 209391ms + ✓ has (2) + ✓ empty set 19811ms + name hz min max mean p75 p99 p995 p999 rme samples + native set 21,206,116.26 0.0000 0.1372 0.0000 0.0001 0.0001 0.0001 0.0001 ±0.09% 20636388 fastest + enum set 13,198,874.44 0.0001 0.0909 0.0001 0.0001 0.0001 0.0001 0.0001 ±0.08% 12924792 + ✓ full set 20078ms + name hz min max mean p75 p99 p995 p999 rme samples + native set 21,291,907.56 0.0000 0.0978 0.0000 0.0001 0.0001 0.0001 0.0001 ±0.10% 20736684 fastest + enum set 13,237,134.16 0.0001 0.0427 0.0001 0.0001 0.0001 0.0001 0.0001 ±0.07% 13005323 + ✓ add 11337ms + name hz min max mean p75 p99 p995 p999 rme samples + enum set 14,437,209.78 0.0001 0.0320 0.0001 0.0001 0.0001 0.0001 0.0001 ±0.07% 14219315 fastest + native set 4,048,108.96 0.0002 0.3157 0.0003 0.0003 0.0003 0.0003 0.0007 ±0.25% 3965984 + ✓ delete 11266ms + name hz min max mean p75 p99 p995 p999 rme samples + enum set 14,224,467.32 0.0001 0.0729 0.0001 0.0001 0.0001 0.0001 0.0001 ±0.08% 14015339 fastest + native set 4,287,069.25 0.0002 0.3568 0.0002 0.0002 0.0003 0.0004 0.0007 ±0.30% 4186946 + ✓ keys 22782ms + name hz min max mean p75 p99 p995 p999 rme samples + native set 22,845,624.54 0.0000 0.0415 0.0000 0.0001 0.0001 0.0001 0.0001 ±0.07% 22276472 fastest + enum set 22,849,172.68 0.0000 0.0951 0.0000 0.0001 0.0001 0.0001 0.0001 ±0.08% 22254698 + ✓ values 17108ms + name hz min max mean p75 p99 p995 p999 rme samples + native set 19,279,170.57 0.0000 0.0413 0.0001 0.0001 0.0001 0.0001 0.0001 ±0.08% 18898286 fastest + enum set 12,656,450.07 0.0001 18.8421 0.0001 0.0001 0.0001 0.0001 0.0005 ±4.09% 11694214 + ✓ Symbol(Symbol.iterator) 19436ms + name hz min max mean p75 p99 p995 p999 rme samples + native set 19,442,343.10 0.0000 0.0798 0.0001 0.0001 0.0001 0.0001 0.0001 ±0.08% 19006739 fastest + enum set 17,544,892.40 0.0000 66.8953 0.0001 0.0001 0.0001 0.0001 0.0004 ±13.17% 15254944 + ✓ union 12892ms + name hz min max mean p75 p99 p995 p999 rme samples + enum set 20,008,001.47 0.0000 0.1361 0.0001 0.0001 0.0001 0.0001 0.0001 ±0.08% 19682920 fastest + native set 2,583,085.76 0.0003 0.3434 0.0004 0.0004 0.0006 0.0007 0.0011 ±0.48% 2473422 + ✓ intersection 10870ms + name hz min max mean p75 p99 p995 p999 rme samples + enum set 14,711,000.12 0.0001 0.0299 0.0001 0.0001 0.0001 0.0001 0.0001 ±0.08% 14419920 fastest + native set 2,186,253.34 0.0004 0.4180 0.0005 0.0005 0.0006 0.0008 0.0011 ±0.37% 2127560 + ✓ difference 11318ms + name hz min max mean p75 p99 p995 p999 rme samples + enum set 13,911,659.27 0.0001 0.0480 0.0001 0.0001 0.0001 0.0001 0.0001 ±0.08% 13645951 fastest + native set 3,541,035.40 0.0002 0.0290 0.0003 0.0003 0.0004 0.0005 0.0009 ±0.08% 3463004 + ✓ symmetricDifference 11600ms + name hz min max mean p75 p99 p995 p999 rme samples + enum set 13,746,639.49 0.0001 0.0841 0.0001 0.0001 0.0001 0.0001 0.0001 ±0.11% 13438668 fastest + native set 5,153,365.76 0.0002 0.3619 0.0002 0.0002 0.0002 0.0003 0.0006 ±0.40% 4995671 + ✓ isSubsetOf 15365ms + name hz min max mean p75 p99 p995 p999 rme samples + enum set 21,584,121.56 0.0000 0.1773 0.0000 0.0001 0.0001 0.0001 0.0001 ±0.12% 20949499 fastest + native set 5,478,922.03 0.0002 0.0427 0.0002 0.0002 0.0002 0.0002 0.0004 ±0.07% 5405264 + ✓ isSupersetOf 12293ms + name hz min max mean p75 p99 p995 p999 rme samples + enum set 15,724,180.49 0.0000 0.0490 0.0001 0.0001 0.0001 0.0001 0.0001 ±0.07% 15426052 fastest + native set 4,863,504.30 0.0002 0.0346 0.0002 0.0002 0.0004 0.0004 0.0005 ±0.08% 4700775 + ✓ isDisjointFrom 13232ms + name hz min max mean p75 p99 p995 p999 rme samples + enum set 16,888,742.09 0.0000 0.0478 0.0001 0.0001 0.0001 0.0001 0.0001 ±0.08% 16538521 fastest + native set 4,836,225.74 0.0002 0.0411 0.0002 0.0002 0.0002 0.0003 0.0004 ±0.07% 4765987 +``` + +
+ +To run the benchmark: +```shell +npm run benchmark +``` \ No newline at end of file From a9a02e0fc4530c12834d8b2f8d799467dfc4625a Mon Sep 17 00:00:00 2001 From: mspiess <30656914+mspiess@users.noreply.github.com> Date: Mon, 17 Aug 2026 22:07:01 +0200 Subject: [PATCH 5/5] chore: add changeset --- .changeset/famous-islands-invite.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/famous-islands-invite.md diff --git a/.changeset/famous-islands-invite.md b/.changeset/famous-islands-invite.md new file mode 100644 index 0000000..0033bad --- /dev/null +++ b/.changeset/famous-islands-invite.md @@ -0,0 +1,5 @@ +--- +"@marco.spiess/enumset": patch +--- + +Add benchmark