diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml
index 19440b0..d8e5e7d 100644
--- a/.github/workflows/check.yml
+++ b/.github/workflows/check.yml
@@ -7,7 +7,7 @@ on:
push:
branches: [main]
pull_request:
- branches: ['**']
+ branches: ["**"]
# Allow manually re-running the check from the Actions tab.
workflow_dispatch:
@@ -28,7 +28,7 @@ jobs:
strategy:
matrix:
- node-version: ['24.14']
+ node-version: ["24.14"]
steps:
# persist-credentials: false keeps the GITHUB_TOKEN out of .git/config, so a
diff --git a/CLAUDE.md b/CLAUDE.md
index 46c2c85..e1005e9 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -23,19 +23,21 @@ IMPORTANT for AI agents: Do NOT run `gro dev` - the developer will manage the de
- Svelte 5 - component framework with runes
- SvelteKit - application framework with static adapter
-- fuz_css (`@fuzdev/fuz_css`) - CSS framework and design system
+- fuz_css (`@fuzdev/fuz_css`) - semantic-first CSS framework and design system
- fuz_ui (`@fuzdev/fuz_ui`) - UI components, theming, docs system
- fuz_util (`@fuzdev/fuz_util`) - utility functions
- fuz_code (`@fuzdev/fuz_code`) - syntax highlighting
- Gro (`@fuzdev/gro`) - build system and task runner
-- prettier + prettier-plugin-svelte - code formatting
+- tsv (via Gro) - code formatting
- mdz (`@fuzdev/mdz`) - markdown preprocessor wired into `svelte.config.js`
- `@fuzdev/tsv_wasm` - tsv's formatter + parser as WASM; powers the playground, loaded lazily in the browser
-Note: `@fuzdev/tsv_wasm` is loaded only on `/docs/playground` via a browser-only dynamic `import()`, so the ~900KB WASM never weighs down `/docs` or the prerendered pages.
+Note: `@fuzdev/tsv_wasm` is loaded only on `/docs/playground` via a browser-only dynamic `import()`, so the ~900KB-gzipped WASM (~2.9MB decoded) never weighs down `/docs` or the prerendered pages.
Note: several devDependencies — `@webref/css` (CSS spec data), `zimmerframe` (AST traversal), `@sveltejs/acorn-typescript`, `zod`, and `@fuzdev/blake3_wasm` — are *optional peer dependencies* of `@fuzdev/fuz_css`'s `vite_plugin_fuz_css`, declared here so its build-time CSS generation resolves them (e.g. `css_literal.ts` imports `@webref/css`, `css_class_extractor.ts` walks ASTs with `zimmerframe`). They aren't imported by this app's own source, so don't mistake them for dead deps.
+Note: `vite` is deliberately held at 7.x (with `@sveltejs/vite-plugin-svelte` 6.x) — vite 8 + plugin-svelte 7 was buggy with this app or SvelteKit's integration. Don't upgrade to vite 8 without deliberately re-verifying the site works.
+
## Scope
tsv.fuz.dev is the public face of the tsv tool:
@@ -56,8 +58,6 @@ tsv.fuz.dev is the public face of the tsv tool:
```
src/
-├── lib/
-│ └── sample.ts # placeholder constant (not yet wired up)
├── routes/
│ ├── +page.svelte # Home: hero, links to docs and benchmarks
│ ├── +layout.svelte # Root layout: fuz_css/fuz_code CSS, ThemeRoot, SiteState
@@ -81,23 +81,33 @@ src/
Benchmark data comes from `tsv`. Full workflow to update:
```bash
-# 1. In ~/dev/tsv — run benchmarks (builds ffi + wasm automatically)
+# 1. In ~/dev/tsv — run benchmarks across deno/node/bun (builds artifacts automatically)
deno task bench
-# 2. In ~/dev/tsv.fuz.dev — copy the latest result
+# 2. In ~/dev/tsv.fuz.dev — copy the latest results
npm run update-benchmarks
```
-Step 1 writes `benches/deno/results/report.json` (committed to tsv).
-Step 2 copies it to `src/routes/docs/benchmarks/benchmarks.json`.
-The JSON format matches `BenchmarkBaseline` in `benchmark_data.ts`.
+Step 1 writes the per-runtime `benches/js/results/report..{json,md}`
+siblings, the composed cross-runtime `report.{json,md}`, and the conformance
+coverage report `report.conformance.node.json` (committed to tsv).
+Step 2 copies three of them — `report.node.json` → `benchmarks.json`, the
+composed `report.json` → `benchmarks_cross_runtime.json`, and
+`report.conformance.node.json` → `benchmarks_conformance.json` — then runs
+`gro format` over the copies. Note the script's source paths are hardcoded to
+`../tsv` — if the reports were generated in a different worktree, copy them
+into `~/dev/tsv` (or copy manually) first.
+The JSON formats match the types in `benchmark_data.ts`.
Key files in `src/routes/docs/benchmarks/`:
-- `benchmarks.json` — raw benchmark data (copied from tsv)
+- `benchmarks.json` — per-runtime Node report (copied from tsv)
+- `benchmarks_cross_runtime.json` — composed cross-runtime report (copied from tsv)
+- `benchmarks_conformance.json` — conformance parse-coverage report (copied from tsv)
- `benchmark_data.ts` — TypeScript types matching the JSON format
-- `benchmarks.ts` — re-exports the JSON with types
+- `benchmarks.ts`, `benchmarks_cross_runtime.ts`, `benchmarks_conformance.ts` — re-export the JSON with types
- `BenchmarksBar.svelte`, `BenchmarksGroup.svelte`, etc. — visualization components
+- `BenchmarksBaselineGroup.svelte` — shared interactive column behind the format, parse, and binary-size groups: hovering a row re-baselines that group's ratios (each of the three groups per section is independent), restoring the default anchor (the canonical reference — Prettier for format, the JS baseline for parse — and the smallest build for size) on mouseleave. `benchmark_data.ts`'s `compute_baseline_ratio`/`format_baseline_ratio`/`baseline_ratio_color` carry the per-`BaselineDirection` (`speed`/`size`) formulas it and the derivations share
## Architecture
@@ -117,7 +127,7 @@ Deploys to `https://tsv.fuz.dev/` via `gro deploy` (builds and pushes to deploy
- TypeScript strict mode
- Svelte 5 with runes API
-- Prettier with tabs, 100 char width
+- tsv with tabs, 100 char width
- Node >= 24.14
- Private package (not published to npm)
diff --git a/package-lock.json b/package-lock.json
index ee9152a..f1da569 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -14,31 +14,28 @@
"@fuzdev/fuz_css": "^0.63.2",
"@fuzdev/fuz_ui": "^0.206.5",
"@fuzdev/fuz_util": "^0.65.2",
- "@fuzdev/gro": "^0.205.1",
+ "@fuzdev/gro": "^0.206.0",
"@fuzdev/mdz": "^0.2.0",
"@fuzdev/tsv_wasm": "^0.1.0",
"@ryanatkn/eslint-config": "^0.12.1",
"@sveltejs/acorn-typescript": "^1.0.9",
"@sveltejs/adapter-static": "^3.0.10",
- "@sveltejs/kit": "^2.61.1",
- "@sveltejs/vite-plugin-svelte": "^7.1.2",
+ "@sveltejs/kit": "^2.69.1",
+ "@sveltejs/vite-plugin-svelte": "^6.2.4",
"@types/estree": "^1.0.8",
"@types/node": "^24.12.4",
"@webref/css": "^8.2.0",
"eslint": "^9.39.1",
"eslint-plugin-svelte": "^3.13.1",
"esm-env": "^1.2.2",
- "magic-string": "^0.30.21",
- "prettier": "^3.7.4",
- "prettier-plugin-svelte": "^3.4.1",
- "svelte": "^5.56.0",
- "svelte-check": "^4.3.6",
+ "svelte": "^5.56.4",
+ "svelte-check": "^4.7.2",
"svelte-docinfo": "^0.5.4",
"svelte2tsx": "^0.7.47",
"tslib": "^2.8.1",
"typescript": "^5.9.3",
"typescript-eslint": "^8.48.1",
- "vite": "^8.0.16",
+ "vite": "^7.3.6",
"vitest": "^4.1.9",
"zimmerframe": "^1.1.4",
"zod": "^4.3.6"
@@ -54,6 +51,7 @@
"dev": true,
"license": "MIT",
"optional": true,
+ "peer": true,
"dependencies": {
"@emnapi/wasi-threads": "1.2.1",
"tslib": "^2.4.0"
@@ -66,6 +64,7 @@
"dev": true,
"license": "MIT",
"optional": true,
+ "peer": true,
"dependencies": {
"tslib": "^2.4.0"
}
@@ -77,6 +76,7 @@
"dev": true,
"license": "MIT",
"optional": true,
+ "peer": true,
"dependencies": {
"tslib": "^2.4.0"
}
@@ -94,7 +94,6 @@
"os": [
"aix"
],
- "peer": true,
"engines": {
"node": ">=18"
}
@@ -112,7 +111,6 @@
"os": [
"android"
],
- "peer": true,
"engines": {
"node": ">=18"
}
@@ -130,7 +128,6 @@
"os": [
"android"
],
- "peer": true,
"engines": {
"node": ">=18"
}
@@ -148,7 +145,6 @@
"os": [
"android"
],
- "peer": true,
"engines": {
"node": ">=18"
}
@@ -166,7 +162,6 @@
"os": [
"darwin"
],
- "peer": true,
"engines": {
"node": ">=18"
}
@@ -184,7 +179,6 @@
"os": [
"darwin"
],
- "peer": true,
"engines": {
"node": ">=18"
}
@@ -202,7 +196,6 @@
"os": [
"freebsd"
],
- "peer": true,
"engines": {
"node": ">=18"
}
@@ -220,7 +213,6 @@
"os": [
"freebsd"
],
- "peer": true,
"engines": {
"node": ">=18"
}
@@ -238,7 +230,6 @@
"os": [
"linux"
],
- "peer": true,
"engines": {
"node": ">=18"
}
@@ -256,7 +247,6 @@
"os": [
"linux"
],
- "peer": true,
"engines": {
"node": ">=18"
}
@@ -274,7 +264,6 @@
"os": [
"linux"
],
- "peer": true,
"engines": {
"node": ">=18"
}
@@ -292,7 +281,6 @@
"os": [
"linux"
],
- "peer": true,
"engines": {
"node": ">=18"
}
@@ -310,7 +298,6 @@
"os": [
"linux"
],
- "peer": true,
"engines": {
"node": ">=18"
}
@@ -328,7 +315,6 @@
"os": [
"linux"
],
- "peer": true,
"engines": {
"node": ">=18"
}
@@ -346,7 +332,6 @@
"os": [
"linux"
],
- "peer": true,
"engines": {
"node": ">=18"
}
@@ -364,7 +349,6 @@
"os": [
"linux"
],
- "peer": true,
"engines": {
"node": ">=18"
}
@@ -382,7 +366,6 @@
"os": [
"linux"
],
- "peer": true,
"engines": {
"node": ">=18"
}
@@ -400,7 +383,6 @@
"os": [
"netbsd"
],
- "peer": true,
"engines": {
"node": ">=18"
}
@@ -418,7 +400,6 @@
"os": [
"netbsd"
],
- "peer": true,
"engines": {
"node": ">=18"
}
@@ -436,7 +417,6 @@
"os": [
"openbsd"
],
- "peer": true,
"engines": {
"node": ">=18"
}
@@ -454,7 +434,6 @@
"os": [
"openbsd"
],
- "peer": true,
"engines": {
"node": ">=18"
}
@@ -472,7 +451,6 @@
"os": [
"openharmony"
],
- "peer": true,
"engines": {
"node": ">=18"
}
@@ -490,7 +468,6 @@
"os": [
"sunos"
],
- "peer": true,
"engines": {
"node": ">=18"
}
@@ -508,7 +485,6 @@
"os": [
"win32"
],
- "peer": true,
"engines": {
"node": ">=18"
}
@@ -526,7 +502,6 @@
"os": [
"win32"
],
- "peer": true,
"engines": {
"node": ">=18"
}
@@ -544,7 +519,6 @@
"os": [
"win32"
],
- "peer": true,
"engines": {
"node": ">=18"
}
@@ -884,18 +858,17 @@
}
},
"node_modules/@fuzdev/gro": {
- "version": "0.205.1",
- "resolved": "https://registry.npmjs.org/@fuzdev/gro/-/gro-0.205.1.tgz",
- "integrity": "sha512-o5WBXWSH/UlN1BsDnpBNIi0WHR7lj7UFGltH/JctSP7oSB2ojvmoXD5OLCEeVdRVcDaSQKl48xcXKUwHKqVSmQ==",
+ "version": "0.206.0",
+ "resolved": "https://registry.npmjs.org/@fuzdev/gro/-/gro-0.206.0.tgz",
+ "integrity": "sha512-11nkTAEWDOvkgzynRChQS+bj3tc7R0NduB+eHLXzXZJqz66nc/oop8Ttvz0UkjTBPX/zzgtU8QuDDNyA7buf5A==",
"dev": true,
"license": "MIT",
"dependencies": {
+ "@fuzdev/tsv_wasm": "^0.1.0",
"chokidar": "^5.0.0",
"dotenv": "^17.2.3",
"esm-env": "^1.2.2",
"oxc-parser": "^0.99.0",
- "prettier": "^3.7.4",
- "prettier-plugin-svelte": "^3.5.1",
"ts-blank-space": "^0.6.2",
"tslib": "^2.8.1"
},
@@ -1393,27 +1366,38 @@
"dev": true,
"license": "MIT"
},
- "node_modules/@rolldown/binding-android-arm64": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.3.tgz",
- "integrity": "sha512-454rs7jHngixp/NMxd5srYD57OnzSlZ/eFTETjORQHLwJG1lRtmNOJcBerZlfu4GjKqeq8aCCIQrMdHyhI51Hw==",
+ "node_modules/@rollup/rollup-android-arm-eabi": {
+ "version": "4.62.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.62.2.tgz",
+ "integrity": "sha512-6o7ZLZK+BeenkZCFNDXqpbjw9bD6nuWonvS/lwQJp7NoVVxm6p3qE7qQ5jGuBjiFsgvqjD8mZAU5oWxTmbOeOg==",
"cpu": [
- "arm64"
+ "arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
+ ]
+ },
+ "node_modules/@rollup/rollup-android-arm64": {
+ "version": "4.62.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.62.2.tgz",
+ "integrity": "sha512-BaH7BllCACHoH1LguOU56UItGfUWjujlO65kS9LAodViaN4bwIKd7oeW/ZHJ/4ljr/7MIiENnNy3HJ0zXv8Zkw==",
+ "cpu": [
+ "arm64"
],
- "engines": {
- "node": "^20.19.0 || >=22.12.0"
- }
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ]
},
- "node_modules/@rolldown/binding-darwin-arm64": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.3.tgz",
- "integrity": "sha512-PcAhP+ynjURNyy8SKGl5DQP94aGuB/7JrXJb/t7P+hanXvQVMWzUvRRhBAcg/lNRadBhoUPqSoP4xw5tR/KBEA==",
+ "node_modules/@rollup/rollup-darwin-arm64": {
+ "version": "4.62.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.62.2.tgz",
+ "integrity": "sha512-v39RCCvj4He82I9sFmk+M1VZ0PLM9sfsLVikjfx2hYBNALhrrOR2D3JjQA6AhlaSOgcR+RzrKY7e1+bT6SUO/A==",
"cpu": [
"arm64"
],
@@ -1422,15 +1406,12 @@
"optional": true,
"os": [
"darwin"
- ],
- "engines": {
- "node": "^20.19.0 || >=22.12.0"
- }
+ ]
},
- "node_modules/@rolldown/binding-darwin-x64": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.3.tgz",
- "integrity": "sha512-9YpfeUvSE2RS7wysJ81uOZkXJz7f7Q55H2Gvp3VEw/EsahqDtrphrZ0EwDLK5vvKOzaCrBsjF8JmnMLcUt78Gg==",
+ "node_modules/@rollup/rollup-darwin-x64": {
+ "version": "4.62.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.62.2.tgz",
+ "integrity": "sha512-yl0y2vq3S3lHeuXhEdss6TWfKW8vkujImO12tn4ZkG/4oghr09LvdYm2RElVjokTQiUvDUGXLGsYeLqUMCKpGA==",
"cpu": [
"x64"
],
@@ -1439,15 +1420,26 @@
"optional": true,
"os": [
"darwin"
+ ]
+ },
+ "node_modules/@rollup/rollup-freebsd-arm64": {
+ "version": "4.62.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.62.2.tgz",
+ "integrity": "sha512-tT4pvt4qXD+vEoezupCWi+a1F0vvDiksiHc+PxRlYTOH1I6/X4id9jPxTP+Fg+545euaFT1jJVs4CEdHZAU1vw==",
+ "cpu": [
+ "arm64"
],
- "engines": {
- "node": "^20.19.0 || >=22.12.0"
- }
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ]
},
- "node_modules/@rolldown/binding-freebsd-x64": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.3.tgz",
- "integrity": "sha512-yB1IlAsSNHncV6SCTL27/MVGR5htvQsoGxIv5KMGXALp+Ll1wYsn+x98M9MW7qa+NdSbvrrY7ANI4wLJ0n1e6g==",
+ "node_modules/@rollup/rollup-freebsd-x64": {
+ "version": "4.62.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.62.2.tgz",
+ "integrity": "sha512-6nU5F2wCW+qvCBhTn1pdIU3bzsIoF7EUwsCDRxilWGprQR6yd508YnH9+OKFCwpfS8pjZqDUmnCAr7exax0XCg==",
"cpu": [
"x64"
],
@@ -1456,32 +1448,46 @@
"optional": true,
"os": [
"freebsd"
- ],
- "engines": {
- "node": "^20.19.0 || >=22.12.0"
- }
+ ]
},
- "node_modules/@rolldown/binding-linux-arm-gnueabihf": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.3.tgz",
- "integrity": "sha512-Yi30IVAAfLUCy2MseFjbB1jAMDl1VMCAas5StnYp8da9+CKvMd2H2cbEjWcw5NPaPqzvYkVIaF1nNUG+b7u/sw==",
+ "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
+ "version": "4.62.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.62.2.tgz",
+ "integrity": "sha512-n1GJHPOvpIfhi3TmrCeh6S6URt9BFCt0KQE3qvexyGCTAKpR4Lg+eWvNZEqu7epxwus/8ElT3hacYEucm49SZg==",
"cpu": [
"arm"
],
"dev": true,
+ "libc": [
+ "glibc"
+ ],
"license": "MIT",
"optional": true,
"os": [
"linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm-musleabihf": {
+ "version": "4.62.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.62.2.tgz",
+ "integrity": "sha512-JqgflS8wEB+UXV/vS1RpRbifGBeN4D5lz8D8oOFbFZw4vedvdOgCFAjfBmIMdW3yL10XpQQ0Ambepw6MXrhOnA==",
+ "cpu": [
+ "arm"
],
- "engines": {
- "node": "^20.19.0 || >=22.12.0"
- }
+ "dev": true,
+ "libc": [
+ "musl"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
},
- "node_modules/@rolldown/binding-linux-arm64-gnu": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.3.tgz",
- "integrity": "sha512-jsO7R8To+AdlYgUmN5sHSCZbfhtMBkO0WUx8iORQnPcMMdgr7qM2DQmMwgabs3GhNztdmoKkMKQFHD6DTMCIQw==",
+ "node_modules/@rollup/rollup-linux-arm64-gnu": {
+ "version": "4.62.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.62.2.tgz",
+ "integrity": "sha512-wnFJkogWvN4jm/hQRF2UBaeUmk20j5+DmHvoyWii2b8HJDyvz1MF2OU/6ynXt2KR63rbZLWkFpoytpdc/yBuSA==",
"cpu": [
"arm64"
],
@@ -1493,15 +1499,12 @@
"optional": true,
"os": [
"linux"
- ],
- "engines": {
- "node": "^20.19.0 || >=22.12.0"
- }
+ ]
},
- "node_modules/@rolldown/binding-linux-arm64-musl": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.3.tgz",
- "integrity": "sha512-VWkUHwWriDciit80wleYwKILoR/KMvxh/IdwS/paX+ZgpuRpCrKLUdadJbc0NpBEiyhpYawsJ73j9aCvOH+f7Q==",
+ "node_modules/@rollup/rollup-linux-arm64-musl": {
+ "version": "4.62.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.62.2.tgz",
+ "integrity": "sha512-HVu2bp0zhvJ8xHEV9+UUs7S90VadmBSY3LcIMvozbPo4AuMGDWlz3ymHLHZPX4hR67TKTt8Qp5PJ5RBg/i+RMQ==",
"cpu": [
"arm64"
],
@@ -1513,15 +1516,46 @@
"optional": true,
"os": [
"linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-loong64-gnu": {
+ "version": "4.62.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.62.2.tgz",
+ "integrity": "sha512-mQqqAV8QaoSgr9I2fKDLY2BAVvmKjWoGiu/cSYQonsLvtqwEn1E4QYfnCOcp5zoEqNhsDYin1s6jx/VJmrxlZg==",
+ "cpu": [
+ "loong64"
],
- "engines": {
- "node": "^20.19.0 || >=22.12.0"
- }
+ "dev": true,
+ "libc": [
+ "glibc"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-loong64-musl": {
+ "version": "4.62.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.62.2.tgz",
+ "integrity": "sha512-IxKLoxCQ2IWi6bT2akyDUBGsOImDKB+sPp4EsTmwFQ/fMwpCKm8uLSSgP/Kx/QYUgKis6SEZ5/Nlhup0DIA0PQ==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "libc": [
+ "musl"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
},
- "node_modules/@rolldown/binding-linux-ppc64-gnu": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.3.tgz",
- "integrity": "sha512-5f1laC0SlIR0yDbFCd8acUhvJIag6N3zC5P7oUPN6wX0aOma+uKJ0wBDH5aq7I1PVI2ttTlhJwzwRIBnLiSGEg==",
+ "node_modules/@rollup/rollup-linux-ppc64-gnu": {
+ "version": "4.62.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.62.2.tgz",
+ "integrity": "sha512-Mk5ha2RQSgyFfmYYLkBpPnUk8D8FriBxesO1u9O75X0mHgXL1UQcH5Itl2lurWL2tj0RxV9b9tJgipac0hRY9A==",
"cpu": [
"ppc64"
],
@@ -1533,17 +1567,31 @@
"optional": true,
"os": [
"linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-ppc64-musl": {
+ "version": "4.62.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.62.2.tgz",
+ "integrity": "sha512-CjvEnqJL/0/TQ3TXX3OPIJ/kmBellrWd4heXUmHeJlTnmwjKpSJzoehLaL6Xk0ZnMHBu9dZuFADNOrtjF4v+2w==",
+ "cpu": [
+ "ppc64"
],
- "engines": {
- "node": "^20.19.0 || >=22.12.0"
- }
+ "dev": true,
+ "libc": [
+ "musl"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
},
- "node_modules/@rolldown/binding-linux-s390x-gnu": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.3.tgz",
- "integrity": "sha512-Iq4ko0r4XsgbrF/LunNgHtAGLRRVE2kXonAXQ/MV0mC6jQpMOhW1SvtZja2EhC/kd05++bP78dsqBeIQyYJ6Yg==",
+ "node_modules/@rollup/rollup-linux-riscv64-gnu": {
+ "version": "4.62.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.62.2.tgz",
+ "integrity": "sha512-1SiZbzwdkaDURsew/tSOrooKiYy7EQGT6m8ufavAi9NEyQb/6VuIxFXAL1fqa4iZe3g4NbNk4P7J32z2tw5Mgg==",
"cpu": [
- "s390x"
+ "riscv64"
],
"dev": true,
"libc": [
@@ -1553,17 +1601,31 @@
"optional": true,
"os": [
"linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-riscv64-musl": {
+ "version": "4.62.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.62.2.tgz",
+ "integrity": "sha512-nQts12zJ3NQRoE6uYljOH89v7szzLDvG2JD/vsX+vGXU8w/At1GowTZ5/7qeFQ8m7L55rpR8Okugnuo5bgjy2Q==",
+ "cpu": [
+ "riscv64"
],
- "engines": {
- "node": "^20.19.0 || >=22.12.0"
- }
+ "dev": true,
+ "libc": [
+ "musl"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
},
- "node_modules/@rolldown/binding-linux-x64-gnu": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.3.tgz",
- "integrity": "sha512-B8m6tD5+/N5FeNQFbKlLA/2yVq9ycQP1SeedyEYYKWBNR3ZQbkvIUcNnDNM03lO1l5F2roiiFJGgvoLLyZXtSg==",
+ "node_modules/@rollup/rollup-linux-s390x-gnu": {
+ "version": "4.62.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.62.2.tgz",
+ "integrity": "sha512-E9/ll019jhPIJgpzfZoIkBGhcz+kKNgVWYRY0zr9srBdPPFVpvOKW8VaJKUbeK+eZXyQF9ltME+Kk6affeaPgg==",
"cpu": [
- "x64"
+ "s390x"
],
"dev": true,
"libc": [
@@ -1573,15 +1635,29 @@
"optional": true,
"os": [
"linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-x64-gnu": {
+ "version": "4.62.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.62.2.tgz",
+ "integrity": "sha512-5BqxR/pshjey51iliyzTD5Xi3EN0aLmQ2lZ3lvefVV9c82BvrLo2/6OT55iifpWBufs6kdwWbuOKS841DrmK9A==",
+ "cpu": [
+ "x64"
],
- "engines": {
- "node": "^20.19.0 || >=22.12.0"
- }
+ "dev": true,
+ "libc": [
+ "glibc"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
},
- "node_modules/@rolldown/binding-linux-x64-musl": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.3.tgz",
- "integrity": "sha512-pSdpdUJHkuCxun9LE7jvgUB9qsRgaiyNNCX7m/AvHTcq67AiT/Yhoxvw5zPfhrM8k/BfP8ce/hMOpthKDpEUow==",
+ "node_modules/@rollup/rollup-linux-x64-musl": {
+ "version": "4.62.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.62.2.tgz",
+ "integrity": "sha512-uNN83XxQrRAh/w0/pmAfibcwyb6YWt4gP+dpnQKPVJshAloQ785ii8CT8ZCIxkGg9opVsvAlGhFitSm6D1Jjpg==",
"cpu": [
"x64"
],
@@ -1593,15 +1669,26 @@
"optional": true,
"os": [
"linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-openbsd-x64": {
+ "version": "4.62.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.62.2.tgz",
+ "integrity": "sha512-srjEIxSH3LRnJN6THczDHWQplqEMFiAJrTab0msUryh9kwNpkICf3Ea6q6MN/2cZwRFUNx5w+h6Hpi4QuHS6Zg==",
+ "cpu": [
+ "x64"
],
- "engines": {
- "node": "^20.19.0 || >=22.12.0"
- }
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ]
},
- "node_modules/@rolldown/binding-openharmony-arm64": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.3.tgz",
- "integrity": "sha512-OXXS3RKJgX2uLwM+gYyuH5omcH8fL1LJs96pZGgtetVCahON57+d4SJHzTgZiOjxgGkSnpXpOsWuPDGAKAigEg==",
+ "node_modules/@rollup/rollup-openharmony-arm64": {
+ "version": "4.62.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.62.2.tgz",
+ "integrity": "sha512-8hOJnxgbyObnCm5AlRA3A931xX19xq80RjVTKgJOvEKWqJruP/Uf12IbAOaDjjEXYRewwHLfmF0YRIdK3OwKWA==",
"cpu": [
"arm64"
],
@@ -1610,51 +1697,40 @@
"optional": true,
"os": [
"openharmony"
- ],
- "engines": {
- "node": "^20.19.0 || >=22.12.0"
- }
+ ]
},
- "node_modules/@rolldown/binding-wasm32-wasi": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.3.tgz",
- "integrity": "sha512-JTtb8BWFynicNSoPrehsCzBtOKjZ6jhMiPFEmOiuXg1Fl8dn2KHQob+GuPSGR0dryQa1PQJbzjF3dqO/whhjLg==",
+ "node_modules/@rollup/rollup-win32-arm64-msvc": {
+ "version": "4.62.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.62.2.tgz",
+ "integrity": "sha512-mmF4AY1i0hG/bLWUctUq59gtmgaSIRa3cu/A3JFRp/sCNEme2bgDEiDS22P9FbnJB8NJNF4jPJiSP5RHQpUTDg==",
"cpu": [
- "wasm32"
+ "arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
- "dependencies": {
- "@emnapi/core": "1.10.0",
- "@emnapi/runtime": "1.10.0",
- "@napi-rs/wasm-runtime": "^1.1.4"
- },
- "engines": {
- "node": "^20.19.0 || >=22.12.0"
- }
+ "os": [
+ "win32"
+ ]
},
- "node_modules/@rolldown/binding-win32-arm64-msvc": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.3.tgz",
- "integrity": "sha512-gEdFFEN70A/jxb2svrWsN3aDL7OUtmvlOy+6fa2jxG8K0wQ1ZbdeLGnidov6Yu5/733dI5ySfzFlQ/cb0bSz1g==",
+ "node_modules/@rollup/rollup-win32-ia32-msvc": {
+ "version": "4.62.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.62.2.tgz",
+ "integrity": "sha512-DZgkknc6jhHrk46V25vbAM0zZkyP0nSDkJB8/dRkLTxv470dOmWDqGoEJl/9A0dFfS7yE3REOwNDxpHwSLSt0Q==",
"cpu": [
- "arm64"
+ "ia32"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
- ],
- "engines": {
- "node": "^20.19.0 || >=22.12.0"
- }
+ ]
},
- "node_modules/@rolldown/binding-win32-x64-msvc": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.3.tgz",
- "integrity": "sha512-eXB7CHuaQdqmJcc3koCNtNPmT/bj2gc999kUFgBxG8Ac0NdgXc4rkCHhqrgrhN3zddvvvrgzj1e90SuSfmyIXA==",
+ "node_modules/@rollup/rollup-win32-x64-gnu": {
+ "version": "4.62.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.62.2.tgz",
+ "integrity": "sha512-T6xr6ucWSFto+VGajA8YH26LdpHRuP4YLHEKAtCWvJDOlnmWcDZVCI2Jmjr+IFHDlt2zRaTAKE4tfjTaWLgJBg==",
"cpu": [
"x64"
],
@@ -1663,17 +1739,21 @@
"optional": true,
"os": [
"win32"
- ],
- "engines": {
- "node": "^20.19.0 || >=22.12.0"
- }
+ ]
},
- "node_modules/@rolldown/pluginutils": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz",
- "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==",
+ "node_modules/@rollup/rollup-win32-x64-msvc": {
+ "version": "4.62.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.62.2.tgz",
+ "integrity": "sha512-BfzEnDJOt9T8M989/lA37EcJgat01wLRnoi5dQf3QzOH7jzpqTAzdDbVfRljVr5r+jzKqpbHeyOfAaXxAd0PAA==",
+ "cpu": [
+ "x64"
+ ],
"dev": true,
- "license": "MIT"
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
},
"node_modules/@ryanatkn/eslint-config": {
"version": "0.12.1",
@@ -1724,9 +1804,9 @@
}
},
"node_modules/@sveltejs/kit": {
- "version": "2.61.1",
- "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.61.1.tgz",
- "integrity": "sha512-Ny8s1SR1TyQS2hD2Rvw0XKzU2Nw1eUF52dTb6T2bdcgz7wSC+Nyb5IwjWYlR4b2dvbbR5NJDiQwHg3rnNseghg==",
+ "version": "2.69.1",
+ "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.69.1.tgz",
+ "integrity": "sha512-+nz8Fx/cElzb2ZPHC+6Ll3y3NEVIe4Na75PeplLlyTmd1dBXAjz2KR14y1ZgNjb2ThfAYzulu+PFy1UE3RCUzA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1765,24 +1845,53 @@
}
}
},
+ "node_modules/@sveltejs/load-config": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/@sveltejs/load-config/-/load-config-0.2.0.tgz",
+ "integrity": "sha512-1LgZ/qUqSoq+QorD83lk2hka79Px0wXNW2q5V1nZlxGhQgw1jrsIbVz5YiCeucVLo4XvFLjXukUaQjIiqowkcg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 18.0.0"
+ }
+ },
"node_modules/@sveltejs/vite-plugin-svelte": {
- "version": "7.1.2",
- "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-7.1.2.tgz",
- "integrity": "sha512-DrUBA2UXRfDmUX/ZTiEopd3X40yavsJF1FX2RygcuIScHL7o5YX1fMvoYnDhjeJQC4weCOklirpNWlcb2NiSeA==",
+ "version": "6.2.4",
+ "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-6.2.4.tgz",
+ "integrity": "sha512-ou/d51QSdTyN26D7h6dSpusAKaZkAiGM55/AKYi+9AGZw7q85hElbjK3kEyzXHhLSnRISHOYzVge6x0jRZ7DXA==",
"dev": true,
"license": "MIT",
"dependencies": {
+ "@sveltejs/vite-plugin-svelte-inspector": "^5.0.0",
"deepmerge": "^4.3.1",
"magic-string": "^0.30.21",
"obug": "^2.1.0",
- "vitefu": "^1.1.2"
+ "vitefu": "^1.1.1"
},
"engines": {
"node": "^20.19 || ^22.12 || >=24"
},
"peerDependencies": {
- "svelte": "^5.46.4",
- "vite": "^8.0.0-beta.7 || ^8.0.0"
+ "svelte": "^5.0.0",
+ "vite": "^6.3.0 || ^7.0.0"
+ }
+ },
+ "node_modules/@sveltejs/vite-plugin-svelte-inspector": {
+ "version": "5.0.2",
+ "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte-inspector/-/vite-plugin-svelte-inspector-5.0.2.tgz",
+ "integrity": "sha512-TZzRTcEtZffICSAoZGkPSl6Etsj2torOVrx6Uw0KpXxrec9Gg6jFWQ60Q3+LmNGfZSxHRCZL7vXVZIWmuV50Ig==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "obug": "^2.1.0"
+ },
+ "engines": {
+ "node": "^20.19 || ^22.12 || >=24"
+ },
+ "peerDependencies": {
+ "@sveltejs/vite-plugin-svelte": "^6.0.0-next.0",
+ "svelte": "^5.0.0",
+ "vite": "^6.3.0 || ^7.0.0"
}
},
"node_modules/@tybys/wasm-util": {
@@ -1822,9 +1931,9 @@
"license": "MIT"
},
"node_modules/@types/estree": {
- "version": "1.0.8",
- "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
- "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz",
+ "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==",
"dev": true,
"license": "MIT"
},
@@ -2545,16 +2654,6 @@
"node": ">=0.10.0"
}
},
- "node_modules/detect-libc": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
- "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
- "dev": true,
- "license": "Apache-2.0",
- "engines": {
- "node": ">=8"
- }
- },
"node_modules/devalue": {
"version": "5.8.1",
"resolved": "https://registry.npmjs.org/devalue/-/devalue-5.8.1.tgz",
@@ -2589,7 +2688,6 @@
"dev": true,
"hasInstallScript": true,
"license": "MIT",
- "peer": true,
"bin": {
"esbuild": "bin/esbuild"
},
@@ -2801,9 +2899,9 @@
}
},
"node_modules/esrap": {
- "version": "2.2.9",
- "resolved": "https://registry.npmjs.org/esrap/-/esrap-2.2.9.tgz",
- "integrity": "sha512-4KijP+NxCWthMCUC3qHbE6n4vCjqgJS1uAYKhuT/GWfFTf1Qyive2TgOjep+gzbSzRfnNyaN/UU9YmdOt8Eg0A==",
+ "version": "2.2.13",
+ "resolved": "https://registry.npmjs.org/esrap/-/esrap-2.2.13.tgz",
+ "integrity": "sha512-m8jH5hZgJE2RRUK/jjkGPcJEDAV+dYnZYFkosQaPTcE+Yw4xynXHOo6FUdwaWBtdR3b1MMa7wEDTSHeR2VWsGA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3181,279 +3279,6 @@
"node": ">= 0.8.0"
}
},
- "node_modules/lightningcss": {
- "version": "1.32.0",
- "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz",
- "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==",
- "dev": true,
- "license": "MPL-2.0",
- "dependencies": {
- "detect-libc": "^2.0.3"
- },
- "engines": {
- "node": ">= 12.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/parcel"
- },
- "optionalDependencies": {
- "lightningcss-android-arm64": "1.32.0",
- "lightningcss-darwin-arm64": "1.32.0",
- "lightningcss-darwin-x64": "1.32.0",
- "lightningcss-freebsd-x64": "1.32.0",
- "lightningcss-linux-arm-gnueabihf": "1.32.0",
- "lightningcss-linux-arm64-gnu": "1.32.0",
- "lightningcss-linux-arm64-musl": "1.32.0",
- "lightningcss-linux-x64-gnu": "1.32.0",
- "lightningcss-linux-x64-musl": "1.32.0",
- "lightningcss-win32-arm64-msvc": "1.32.0",
- "lightningcss-win32-x64-msvc": "1.32.0"
- }
- },
- "node_modules/lightningcss-android-arm64": {
- "version": "1.32.0",
- "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz",
- "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MPL-2.0",
- "optional": true,
- "os": [
- "android"
- ],
- "engines": {
- "node": ">= 12.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/parcel"
- }
- },
- "node_modules/lightningcss-darwin-arm64": {
- "version": "1.32.0",
- "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz",
- "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MPL-2.0",
- "optional": true,
- "os": [
- "darwin"
- ],
- "engines": {
- "node": ">= 12.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/parcel"
- }
- },
- "node_modules/lightningcss-darwin-x64": {
- "version": "1.32.0",
- "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz",
- "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MPL-2.0",
- "optional": true,
- "os": [
- "darwin"
- ],
- "engines": {
- "node": ">= 12.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/parcel"
- }
- },
- "node_modules/lightningcss-freebsd-x64": {
- "version": "1.32.0",
- "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz",
- "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MPL-2.0",
- "optional": true,
- "os": [
- "freebsd"
- ],
- "engines": {
- "node": ">= 12.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/parcel"
- }
- },
- "node_modules/lightningcss-linux-arm-gnueabihf": {
- "version": "1.32.0",
- "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz",
- "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==",
- "cpu": [
- "arm"
- ],
- "dev": true,
- "license": "MPL-2.0",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">= 12.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/parcel"
- }
- },
- "node_modules/lightningcss-linux-arm64-gnu": {
- "version": "1.32.0",
- "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz",
- "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "libc": [
- "glibc"
- ],
- "license": "MPL-2.0",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">= 12.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/parcel"
- }
- },
- "node_modules/lightningcss-linux-arm64-musl": {
- "version": "1.32.0",
- "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz",
- "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "libc": [
- "musl"
- ],
- "license": "MPL-2.0",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">= 12.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/parcel"
- }
- },
- "node_modules/lightningcss-linux-x64-gnu": {
- "version": "1.32.0",
- "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz",
- "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "libc": [
- "glibc"
- ],
- "license": "MPL-2.0",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">= 12.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/parcel"
- }
- },
- "node_modules/lightningcss-linux-x64-musl": {
- "version": "1.32.0",
- "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz",
- "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "libc": [
- "musl"
- ],
- "license": "MPL-2.0",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">= 12.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/parcel"
- }
- },
- "node_modules/lightningcss-win32-arm64-msvc": {
- "version": "1.32.0",
- "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz",
- "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MPL-2.0",
- "optional": true,
- "os": [
- "win32"
- ],
- "engines": {
- "node": ">= 12.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/parcel"
- }
- },
- "node_modules/lightningcss-win32-x64-msvc": {
- "version": "1.32.0",
- "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz",
- "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MPL-2.0",
- "optional": true,
- "os": [
- "win32"
- ],
- "engines": {
- "node": ">= 12.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/parcel"
- }
- },
"node_modules/lilconfig": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz",
@@ -3879,33 +3704,6 @@
"node": ">= 0.8.0"
}
},
- "node_modules/prettier": {
- "version": "3.7.4",
- "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.7.4.tgz",
- "integrity": "sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==",
- "dev": true,
- "license": "MIT",
- "bin": {
- "prettier": "bin/prettier.cjs"
- },
- "engines": {
- "node": ">=14"
- },
- "funding": {
- "url": "https://github.com/prettier/prettier?sponsor=1"
- }
- },
- "node_modules/prettier-plugin-svelte": {
- "version": "3.5.1",
- "resolved": "https://registry.npmjs.org/prettier-plugin-svelte/-/prettier-plugin-svelte-3.5.1.tgz",
- "integrity": "sha512-65+fr5+cgIKWKiqM1Doum4uX6bY8iFCdztvvp2RcF+AJoieaw9kJOFMNcJo/bkmKYsxFaM9OsVZK/gWauG/5mg==",
- "dev": true,
- "license": "MIT",
- "peerDependencies": {
- "prettier": "^3.0.0",
- "svelte": "^3.2.0 || ^4.0.0-next.0 || ^5.0.0-next.0"
- }
- },
"node_modules/punycode": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
@@ -3940,48 +3738,49 @@
"node": ">=4"
}
},
- "node_modules/rolldown": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.3.tgz",
- "integrity": "sha512-i00lAJ2ks1BYr7rjNjKC7BcqAS7nVfiT3QX1SI5aY+AFHblCmaUf9OE9dbdzDvW6dJxbi2ZCZiy9v3CcwOiX3g==",
+ "node_modules/rollup": {
+ "version": "4.62.2",
+ "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.62.2.tgz",
+ "integrity": "sha512-RFnrW4lhXA3s3eqHDZvN654g8OTjzRfqpIRJYczCGB6HzphckVAi/Qh4tbPUbRuDi7s1Llv8g/NspLkttY3gTA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@oxc-project/types": "=0.133.0",
- "@rolldown/pluginutils": "^1.0.0"
+ "@types/estree": "1.0.9"
},
"bin": {
- "rolldown": "bin/cli.mjs"
+ "rollup": "dist/bin/rollup"
},
"engines": {
- "node": "^20.19.0 || >=22.12.0"
+ "node": ">=18.0.0",
+ "npm": ">=8.0.0"
},
"optionalDependencies": {
- "@rolldown/binding-android-arm64": "1.0.3",
- "@rolldown/binding-darwin-arm64": "1.0.3",
- "@rolldown/binding-darwin-x64": "1.0.3",
- "@rolldown/binding-freebsd-x64": "1.0.3",
- "@rolldown/binding-linux-arm-gnueabihf": "1.0.3",
- "@rolldown/binding-linux-arm64-gnu": "1.0.3",
- "@rolldown/binding-linux-arm64-musl": "1.0.3",
- "@rolldown/binding-linux-ppc64-gnu": "1.0.3",
- "@rolldown/binding-linux-s390x-gnu": "1.0.3",
- "@rolldown/binding-linux-x64-gnu": "1.0.3",
- "@rolldown/binding-linux-x64-musl": "1.0.3",
- "@rolldown/binding-openharmony-arm64": "1.0.3",
- "@rolldown/binding-wasm32-wasi": "1.0.3",
- "@rolldown/binding-win32-arm64-msvc": "1.0.3",
- "@rolldown/binding-win32-x64-msvc": "1.0.3"
- }
- },
- "node_modules/rolldown/node_modules/@oxc-project/types": {
- "version": "0.133.0",
- "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.133.0.tgz",
- "integrity": "sha512-KzkdCd6Uxqnf6l3HOw1xfatAlUURA0g14cvBYFyJ5SaNOQbOUvBr9PKArcPcrNIeRsBdgcUzOGrhKveVpvOIGA==",
- "dev": true,
- "license": "MIT",
- "funding": {
- "url": "https://github.com/sponsors/Boshen"
+ "@rollup/rollup-android-arm-eabi": "4.62.2",
+ "@rollup/rollup-android-arm64": "4.62.2",
+ "@rollup/rollup-darwin-arm64": "4.62.2",
+ "@rollup/rollup-darwin-x64": "4.62.2",
+ "@rollup/rollup-freebsd-arm64": "4.62.2",
+ "@rollup/rollup-freebsd-x64": "4.62.2",
+ "@rollup/rollup-linux-arm-gnueabihf": "4.62.2",
+ "@rollup/rollup-linux-arm-musleabihf": "4.62.2",
+ "@rollup/rollup-linux-arm64-gnu": "4.62.2",
+ "@rollup/rollup-linux-arm64-musl": "4.62.2",
+ "@rollup/rollup-linux-loong64-gnu": "4.62.2",
+ "@rollup/rollup-linux-loong64-musl": "4.62.2",
+ "@rollup/rollup-linux-ppc64-gnu": "4.62.2",
+ "@rollup/rollup-linux-ppc64-musl": "4.62.2",
+ "@rollup/rollup-linux-riscv64-gnu": "4.62.2",
+ "@rollup/rollup-linux-riscv64-musl": "4.62.2",
+ "@rollup/rollup-linux-s390x-gnu": "4.62.2",
+ "@rollup/rollup-linux-x64-gnu": "4.62.2",
+ "@rollup/rollup-linux-x64-musl": "4.62.2",
+ "@rollup/rollup-openbsd-x64": "4.62.2",
+ "@rollup/rollup-openharmony-arm64": "4.62.2",
+ "@rollup/rollup-win32-arm64-msvc": "4.62.2",
+ "@rollup/rollup-win32-ia32-msvc": "4.62.2",
+ "@rollup/rollup-win32-x64-gnu": "4.62.2",
+ "@rollup/rollup-win32-x64-msvc": "4.62.2",
+ "fsevents": "~2.3.2"
}
},
"node_modules/sade": {
@@ -4120,9 +3919,9 @@
}
},
"node_modules/svelte": {
- "version": "5.56.0",
- "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.56.0.tgz",
- "integrity": "sha512-kTXr26t1bchFp28ROrb957LtbujpBmBDibmqMGziVpUs7awBi96TGgX6SovrA8BNoEUDVRK2Fb9FkeYlGspoVg==",
+ "version": "5.56.4",
+ "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.56.4.tgz",
+ "integrity": "sha512-/d0QHehmRuJW8gVz395MTkPcPozxzdjBMBE8oEYGz8O3b9KTMzzQ9ZHJQLuFKOHOPQbU6kx/X4iid/EBBzH7iw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -4137,7 +3936,7 @@
"clsx": "^2.1.1",
"devalue": "^5.8.1",
"esm-env": "^1.2.1",
- "esrap": "^2.2.9",
+ "esrap": "^2.2.12",
"is-reference": "^3.0.3",
"locate-character": "^3.0.0",
"magic-string": "^0.30.11",
@@ -4148,13 +3947,14 @@
}
},
"node_modules/svelte-check": {
- "version": "4.3.6",
- "resolved": "https://registry.npmjs.org/svelte-check/-/svelte-check-4.3.6.tgz",
- "integrity": "sha512-uBkz96ElE3G4pt9E1Tw0xvBfIUQkeH794kDQZdAUk795UVMr+NJZpuFSS62vcmO/DuSalK83LyOwhgWq8YGU1Q==",
+ "version": "4.7.2",
+ "resolved": "https://registry.npmjs.org/svelte-check/-/svelte-check-4.7.2.tgz",
+ "integrity": "sha512-GoS4XJdGswlq0rIT1vtFLzJY1bvHtY37McY9H9Gkm1Ggw/ICdZYn8J/Z8Yi0BEL0i3R4+jtaWVePjyppMlij/A==",
"dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/trace-mapping": "^0.3.25",
+ "@sveltejs/load-config": "^0.2.0",
"chokidar": "^4.0.1",
"fdir": "^6.2.0",
"picocolors": "^1.0.0",
@@ -4432,17 +4232,18 @@
"license": "MIT"
},
"node_modules/vite": {
- "version": "8.0.16",
- "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.16.tgz",
- "integrity": "sha512-h9bXPmJichP5fLmVQo3PyaGSDE2n3aPuomeAlVRm0JLmt4rY6zmPKd59HYI4LNW8oTK7tlTsuC7l/m7awx9Jcw==",
+ "version": "7.3.6",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.6.tgz",
+ "integrity": "sha512-4XP60spRGjSZFf1qYH+dJIkK2znL3zQfl9KkOV9MkkRR/3Dls0dxaBsQPTloEc5BLXWPL9vsOxopxyKoMmDueg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "lightningcss": "^1.32.0",
- "picomatch": "^4.0.4",
- "postcss": "^8.5.15",
- "rolldown": "1.0.3",
- "tinyglobby": "^0.2.17"
+ "esbuild": "^0.27.0 || ^0.28.0",
+ "fdir": "^6.5.0",
+ "picomatch": "^4.0.3",
+ "postcss": "^8.5.6",
+ "rollup": "^4.43.0",
+ "tinyglobby": "^0.2.15"
},
"bin": {
"vite": "bin/vite.js"
@@ -4458,10 +4259,9 @@
},
"peerDependencies": {
"@types/node": "^20.19.0 || >=22.12.0",
- "@vitejs/devtools": "^0.1.18",
- "esbuild": "^0.27.0 || ^0.28.0",
"jiti": ">=1.21.0",
"less": "^4.0.0",
+ "lightningcss": "^1.21.0",
"sass": "^1.70.0",
"sass-embedded": "^1.70.0",
"stylus": ">=0.54.8",
@@ -4474,18 +4274,15 @@
"@types/node": {
"optional": true
},
- "@vitejs/devtools": {
- "optional": true
- },
- "esbuild": {
- "optional": true
- },
"jiti": {
"optional": true
},
"less": {
"optional": true
},
+ "lightningcss": {
+ "optional": true
+ },
"sass": {
"optional": true
},
diff --git a/package.json b/package.json
index e1c6a86..5bf4516 100644
--- a/package.json
+++ b/package.json
@@ -16,7 +16,7 @@
"test": "gro test",
"preview": "vite preview",
"deploy": "gro deploy",
- "update-benchmarks": "cp ../tsv/benches/deno/results/report.json src/routes/docs/benchmarks/benchmarks.json"
+ "update-benchmarks": "cp ../tsv/benches/js/results/report.node.json src/routes/docs/benchmarks/benchmarks.json && cp ../tsv/benches/js/results/report.json src/routes/docs/benchmarks/benchmarks_cross_runtime.json && cp ../tsv/benches/js/results/report.conformance.node.json src/routes/docs/benchmarks/benchmarks_conformance.json && gro format"
},
"type": "module",
"engines": {
@@ -28,52 +28,32 @@
"@fuzdev/fuz_css": "^0.63.2",
"@fuzdev/fuz_ui": "^0.206.5",
"@fuzdev/fuz_util": "^0.65.2",
- "@fuzdev/gro": "^0.205.1",
+ "@fuzdev/gro": "^0.206.0",
"@fuzdev/mdz": "^0.2.0",
"@fuzdev/tsv_wasm": "^0.1.0",
"@ryanatkn/eslint-config": "^0.12.1",
"@sveltejs/acorn-typescript": "^1.0.9",
"@sveltejs/adapter-static": "^3.0.10",
- "@sveltejs/kit": "^2.61.1",
- "@sveltejs/vite-plugin-svelte": "^7.1.2",
+ "@sveltejs/kit": "^2.69.1",
+ "@sveltejs/vite-plugin-svelte": "^6.2.4",
"@types/estree": "^1.0.8",
"@types/node": "^24.12.4",
"@webref/css": "^8.2.0",
"eslint": "^9.39.1",
"eslint-plugin-svelte": "^3.13.1",
"esm-env": "^1.2.2",
- "magic-string": "^0.30.21",
- "prettier": "^3.7.4",
- "prettier-plugin-svelte": "^3.4.1",
- "svelte": "^5.56.0",
- "svelte-check": "^4.3.6",
+ "svelte": "^5.56.4",
+ "svelte-check": "^4.7.2",
"svelte-docinfo": "^0.5.4",
"svelte2tsx": "^0.7.47",
"tslib": "^2.8.1",
"typescript": "^5.9.3",
"typescript-eslint": "^8.48.1",
- "vite": "^8.0.16",
+ "vite": "^7.3.6",
"vitest": "^4.1.9",
"zimmerframe": "^1.1.4",
"zod": "^4.3.6"
},
- "prettier": {
- "plugins": [
- "prettier-plugin-svelte"
- ],
- "useTabs": true,
- "printWidth": 100,
- "singleQuote": true,
- "bracketSpacing": false,
- "overrides": [
- {
- "files": "package.json",
- "options": {
- "useTabs": false
- }
- }
- ]
- },
"sideEffects": [
"**/*.css"
],
diff --git a/src/app.html b/src/app.html
index e3061bc..a1c58fd 100644
--- a/src/app.html
+++ b/src/app.html
@@ -1,23 +1,29 @@
-
-
-
-
-
+
+
+
+
+
-
+
- %sveltekit.head%
-
-
- %sveltekit.body%
-
+ %sveltekit.head%
+
+
+ %sveltekit.body%
+
diff --git a/src/lib/sample.ts b/src/lib/sample.ts
deleted file mode 100644
index 429c922..0000000
--- a/src/lib/sample.ts
+++ /dev/null
@@ -1 +0,0 @@
-export const sample = 'TODO - publish tsv!';
diff --git a/src/routes/docs/benchmarks/+page.svelte b/src/routes/docs/benchmarks/+page.svelte
index ef80d88..a761b5a 100644
--- a/src/routes/docs/benchmarks/+page.svelte
+++ b/src/routes/docs/benchmarks/+page.svelte
@@ -5,15 +5,23 @@
import {tome_get_by_slug} from '@fuzdev/fuz_ui/tome.ts';
import {benchmarks_json} from './benchmarks.ts';
+ import {benchmarks_conformance_json} from './benchmarks_conformance.ts';
+ import {benchmarks_cross_runtime_json} from './benchmarks_cross_runtime.ts';
+ import {benchmarks_cli} from './benchmarks_cli.ts';
import {
- category_color,
derive_benchmark_groups,
+ derive_conformance_groups,
derive_speedup_summary,
+ format_corpus_source_files,
+ corpus_source_url,
} from './benchmark_data.ts';
import BenchmarksSummary from './BenchmarksSummary.svelte';
import BenchmarksGroup from './BenchmarksGroup.svelte';
+ import BenchmarksConformance from './BenchmarksConformance.svelte';
import BenchmarksSizes from './BenchmarksSizes.svelte';
import BenchmarksMeta from './BenchmarksMeta.svelte';
+ import BenchmarksCrossRuntime from './BenchmarksCrossRuntime.svelte';
+ import BenchmarksCli from './BenchmarksCli.svelte';
const LIBRARY_ITEM_NAME = 'benchmarks';
@@ -21,8 +29,12 @@
const groups = derive_benchmark_groups(benchmarks_json);
const speedup_rows = derive_speedup_summary(groups);
+ const conformance_groups = derive_conformance_groups(benchmarks_conformance_json);
const corpus = $derived(benchmarks_json.corpus);
+ // Derived from the report so the "What's measured" figure can't drift from the
+ // copied data (the report carries per-language file counts, not bytes).
+ const corpus_file_count = $derived(Object.values(corpus).reduce((sum, n) => sum + n, 0));
const format_groups = groups.filter((g) => g.operation === 'format');
const parse_groups = groups.filter((g) => g.operation === 'parse');
@@ -30,120 +42,291 @@
- tsv is a toolchain for TypeScript/JS, CSS, and Svelte in Rust. Performance is its priority
- after correctness, and the numbers compare favorably to Oxc and Biome for its supported
- languages.
+ tsv is a toolchain for TypeScript/JS, CSS, and Svelte in Rust. After correctness, performance
+ is tsv's next priority. This page shows how it measures up against Prettier, which tsv closely
+ follows, and against Oxc and Biome, which are similar tools with wider language support (tsv
+ doesn't support JSX/TSX/SCSS/etc).
-
-
-
tsv native
-
tsv native json
-
tsv wasm
-
tsv wasm json
+
+
+ Compared to Oxc and Biome, tsv is faster, smaller, and uses less memory to parse and format
+ its supported languages. This section has a prose summary; skip ahead for charts.
+
+
+ The measurements on this page are single-threaded and in-process, where each tool parses or
+ formats one file at a time (isolating engine speed from multi-core parallelism), measured over
+ a large collection of real-world repos: {corpus_file_count.toLocaleString('en-US')} files
+ including Svelte's official repos (svelte, kit, svelte.dev) and the fuz.dev repos . On that basis:
+
+
+
+ Formatting TypeScript, tsv is ~1.66x faster than Oxfmt (native-vs-native), ~26x faster than
+ Prettier (native Rust vs Prettier's JS), and ~6.5x faster than Biome (wasm-vs-wasm).
+
+
+ Formatting Svelte, tsv is is ~63x faster than Prettier (which Oxfmt's Svelte path delegates
+ to internally) and 7.3x faster than Biome.
+
+ Formatting CSS, it's ~2.6x faster than Oxfmt and 12.4x faster than Biome.
+
+ Parsing TypeScript, tsv is ~1.3x faster than Oxc (with the payload-matched span-only AST
+ both emit) and Biome doesn't expose its parser to JS. tsv's default AST adds a per-node
+ line/column loc for drop-in Svelte compatibility, with a fast path to
+ reconstruct locs in JS.
+
+
+ A fork of Oxc's official bench-formatter is an end-to-end CLI benchmark with its own corpus. On a real TypeScript repo, tsv formats
+ ~3.1x faster than Oxfmt and ~6.5x faster than Biome using 3–10x less memory. Wall-clock
+ measurements are warped by each tool's multi-file parallelism, so they scale with core count
+ and are machine-dependent, and could change with tuning.
+
+
+
+ Each section below has notes that attempt to fairly contextualize its numbers. The charts show
+ numbers using Node v24, and at the end of the page is a cross-runtime comparison .
+
+
+
+
+
+
+
+
+
+
+
+ tsv's formatter is similar to Oxfmt
- biome
- oxc
- canonical
-
-
+ and Biome . It formats Svelte, TypeScript, and
+ CSS, plus JS as strict-mode TypeScript:
+
+ {#each format_groups as group (group.language)}
+
+ {/each}
+
+ Notes:
+
+ wasm-to-wasm and native-to-native (N-API here) are the fair comparisons
+
+ Oxfmt formats TypeScript, JS, and CSS with its own native engine, and for Svelte it
+ delegates to Prettier internally (via prettier-plugin-svelte).
+
+
+ There's no native Biome entry: its native engine ships only as the biome CLI
+ binary, a separate process rather than an embeddable library, so it can't be fairly timed
+ in-process like the others.
+
+
+
+
-
-
- Rather than supporting many languages, tsv focuses on Svelte/HTML, TypeScript/JS, and CSS.
- This lets it be smaller when it's all you need, a quality that's more relevant when used in
- the browser via wasm:
+
+
+ The parse entries that build a full JS AST are comparable in mechanism: tsv and oxc-parser
+ both serialize the AST to JSON in Rust and deserialize it in JS, native and wasm alike. But
+ the deliverables differ — tsv's default wire (tsv-json / tsv_wasm-json) carries a per-node loc (line/column) object that oxc-parser's default
+ span-only AST omits, and that loc is roughly half the wire bytes and most of its
+ JSON.parse cost. The tsv-json-no-locations / tsv_wasm-json-no-locations entries drop it, emitting the same span-only shape oxc does, so those are the
+ payload-matched, apples-to-apples comparison with oxc-parser (line/column stays derivable from
+ the offsets plus source, so nothing is lost). The tsv-internal and tsv_wasm-internal entries
+ build the native AST but skip JS-side materialization, so they show raw in-engine speed rather
+ than a cross-tool comparison.
-
+ {#each parse_groups as group (group.language)}
+
+ {/each}
- Important notes:
+ Some notes:
- apples-to-apples comparisons are difficult here because of differing scope
- Biome includes a parser, formatter, and linter supporting many languages
- tsv and tsv_wasm include a parser and formatter for Svelte/HTML, TypeScript/JS, and CSS
+ JS parsers skip the Rust-to-JS serialization step that tsv and oxc pay for, which keeps
+ them competitive.
- oxc-parser only parses TypeScript and JS, not CSS or HTML; oxfmt is its separate formatter
+ Biome is shown grayed-out across all three parse groups because its @biomejs/js-api package doesn't expose a parser to JS (only formatting and linting). Biome parses
+ internally but never surfaces the AST across the JS boundary, so it can't be measured.
- oxfmt has no wasm build as of June 2026
- tsv doesn't publish native artifacts yet, but it builds them for benchmarking over FFI
- with Deno
+ oxc-parser only parses TypeScript and JS (and JSX, not measured here). oxc-parser doesn't
+ expose a CSS parser, and it doesn't parse Svelte.
+
+
+ tsv-json-no-locations / tsv_wasm-json-no-locations are the fair,
+ payload-matched comparison with oxc-parser: they emit tsv's span-only wire (offsets, no
+ per-node loc), matching oxc's default AST shape. The plain
+ tsv-json entries carry the richer loc-bearing drop-in AST that
+ Svelte's compiler consumes for sourcemaps — a strictly larger deliverable, so a slower
+ number that isn't an engine-speed difference.
+
+
+ tsv internal (node napi) and tsv_wasm internal aren't a fair
+ comparison to the other entries — they skip JS-side AST materialization entirely, so
+ they're included only to show tsv's own JSON-serialization overhead against its
+ non-internal entry.
-
-
+
+
+ Separate from the speed numbers above that use a real-world corpus of code, this section
+ measures parse coverage : how much of a much larger, deliberately hard corpus each
+ parser accepts — Prettier's format-test suites, Svelte's compiler test suite, CSS extracted
+ from
+ web-platform-tests , and test262 's expected-valid strict-mode tests.
+
+
+
-
-
- tsv's formatter is similar to Oxfmt
- and Biome . Today it can format Svelte, TypeScript, and CSS,
- plus HTML and JS (as strict-mode TypeScript):
+
+
+ Rather than supporting many languages, tsv focuses on Svelte/HTML, TypeScript/JS, and CSS.
+ This lets it be smaller when it's all you need, a quality that's more relevant when used in
+ the browser via wasm.
- {#each format_groups as group (group.language)}
-
- {/each}
+
-
+
- The parse rows that build a full JS AST are directly comparable: tsv and oxc-parser both
- serialize the AST to JSON in Rust and deserialize it in JS, native and wasm alike. The
- tsv-internal and tsv_wasm-internal rows are tsv's parse-only numbers - they build the native
- AST but skip JS-side materialization, so they show raw in-engine speed rather than a
- cross-tool comparison.
+ The numbers above measure tsv's engine in-process, one file at a time, using tsv's original
+ benchmarks. This section describes the results from a fork of Oxc's official
+ bench-formatter
+ that
+ adds tsv , timing
+ the whole CLI end-to-end — process spawn, file discovery, I/O, and each tool's default
+ multi-file parallelism — plus peak memory. It's the "what you experience typing the command"
+ measure, run on real repositories. tsv appears in the two JSX-free scenarios (it has no
+ JSX/TSX parser).
- {#each parse_groups as group (group.language)}
-
- {/each}
+
@@ -152,39 +335,54 @@
All numbers are single-threaded: every library formats or parses one file at a time, measured
- sequentially with no cross-file parallelism. These are per-file, single-core latency and
- throughput numbers - not the multi-core batch throughput a CLI gets when it formats many files
- at once, which most of these tools (tsv included) can do.
+ sequentially with no cross-file parallelism. Each row is the total time to process the whole
+ corpus once on a single core — not the multi-core batch throughput a CLI gets when it formats
+ many files at once, which most of these tools (tsv included) can do.
- What's measured: around 5,500 files (~15 MB) of
- .svelte/.html, .ts/.js, and
- .css, from three sources: the fuz.dev libraries and apps, upstream framework
- source (Svelte, SvelteKit, and the svelte.dev site), and formatter conformance fixtures
- (Prettier's and prettier-plugin-svelte's own test suites - deliberately tricky edge cases, not
- typical code, so they skew the corpus toward hard cases).
+ What's measured: {corpus_file_count.toLocaleString('en-US')} files of .svelte,
+ .ts/.js, and .css — real-world code only, from two
+ sources: the author's libraries, apps, and sites (the fuz.dev ecosystem plus personal
+ SvelteKit sites), and upstream framework source (Svelte, SvelteKit, and the svelte.dev site).
+ The CSS set also includes real-authored CSS extracted from those components'
+ <style> blocks, concatenated per repo — standalone CSS files are rare in
+ this ecosystem, and the same bytes appear in the Svelte rows (rows are never summed). Test
+ files count as real code and stay in; fixture files (the formatter test suites that used to be
+ part of this corpus, and fixture subtrees inside the measured repos) are excluded —
+ deliberately tricky edge cases measure conformance, not typical throughput, and are covered by
+ the parse-conformance section above.
-
-
+
+
+
+ The same benchmark harness runs under three JS runtimes — Node, Deno, and Bun. The headline
+ numbers above are the Node run. The native entry differs by runtime: Node and Bun load tsv's
+ N-API addon, while Deno loads its C-FFI library. They share code but cross a different binding
+ boundary, so a per-runtime delta on the same row is a runtime effect — the JS engine or the
+ binding boundary — not a difference in tsv's own engine, which is identical across all three.
+
+
+ Reading the tables:
+
+
+ tsv's native binding boundaries are at parity across runtimes — the
+ tsv-internal rows, which cross the boundary but hand nothing back to
+ materialize, sit within a few percent Node vs Deno. The visible spread is largely confined
+ to the JSON-materializing parse rows, where it reflects each JS engine's
+ JSON.parse cost rather than the N-API-vs-FFI boundary: Deno and Bun edge out
+ Node there. Node is the headline as the default N-API path, not because it's the fastest
+ native number.
+
+
+ tsv's own paths — native (N-API/FFI) and wasm — run on all three runtimes. Bun currently
+ fails to load two third-party wasm implementations (biome's wasm-bundler and oxc-parser's
+ wasm32-wasi binding), so they show fail in the Bun column.
+
+
+
+
+
+
diff --git a/src/routes/docs/benchmarks/BenchmarksBar.svelte b/src/routes/docs/benchmarks/BenchmarksBar.svelte
index 128c7b5..23540b6 100644
--- a/src/routes/docs/benchmarks/BenchmarksBar.svelte
+++ b/src/routes/docs/benchmarks/BenchmarksBar.svelte
@@ -14,6 +14,9 @@
ratio_text,
ratio_color,
annotation,
+ disabled = false,
+ on_enter,
+ on_leave,
}: {
label: string;
bar_fraction: number;
@@ -24,48 +27,95 @@
// optional extra context shown between value and ratio (corpus coverage in
// benchmark groups, gzipped size in binary-size groups); omitted when absent
annotation?: string | undefined;
+ // a grayed-out, inert placeholder (a tool that doesn't run in this group) —
+ // no bar, no value, no ratio, just the label held in its shared slot
+ disabled?: boolean;
+ // hover-to-rebaseline: fired when the pointer enters/leaves the row so the
+ // group can adopt this row as its ratio anchor; omitted on inert placeholders
+ on_enter?: (() => void) | undefined;
+ on_leave?: (() => void) | undefined;
} = $props();
+
+ // the parenthesized binding suffix (`(wasm)`/`(node napi)`) describes how the
+ // tool actually ran in this (Node) report - meaningless for biome's disabled
+ // placeholder (its only variant is wasm, so there's no ambiguity to lose).
+ // oxc-parser keeps its suffix even when disabled since it has two placeholder
+ // rows (napi and wasm) that would otherwise become indistinguishable.
+ const display_label = $derived(
+ disabled && category === 'biome'
+ ? format_label(label).replace(/ \([^)]*\)$/, '')
+ : format_label(label),
+ );
-
-
{format_label(label)}
+
+
+
+
{display_label}
-
+ {#if !disabled}
+
+ {/if}
-
{value.value} {value.unit}
+
+ {#if disabled}
+ n/a
+ {:else}
+ {value.value} {value.unit}
+ {/if}
+
{#if annotation != null}
{annotation}
{/if}
-
{ratio_text}
+
{#if !disabled}{ratio_text}{/if}
diff --git a/src/routes/docs/benchmarks/BenchmarksConformance.svelte b/src/routes/docs/benchmarks/BenchmarksConformance.svelte
new file mode 100644
index 0000000..050dceb
--- /dev/null
+++ b/src/routes/docs/benchmarks/BenchmarksConformance.svelte
@@ -0,0 +1,76 @@
+
+
+{#each groups as group (group.language)}
+
+
+ Parsing {group.files_total.toLocaleString('en-US')}
+ {LANGUAGE_LABELS[group.language] ?? group.language} files
+
+
+
+
+
+ files accepted
+ coverage
+
+
+
+ {#each group.rows as row (row.name)}
+
+ {row.name}
+ {row.files_processed.toLocaleString('en-US')} / {row.files_total.toLocaleString(
+ 'en-US',
+ )}
+ {format_coverage_percent(row.coverage_fraction)}
+
+ {/each}
+
+
+
+{/each}
+
+
diff --git a/src/routes/docs/benchmarks/BenchmarksCrossRuntime.svelte b/src/routes/docs/benchmarks/BenchmarksCrossRuntime.svelte
new file mode 100644
index 0000000..16de023
--- /dev/null
+++ b/src/routes/docs/benchmarks/BenchmarksCrossRuntime.svelte
@@ -0,0 +1,146 @@
+
+
+
+ {#if report.mixed_vintage}
+
+ ⚠ The per-runtime reports backing these tables come from different commits/versions, so the
+ ratios are unreliable until every runtime is re-run.
+
+ {/if}
+ {#if report.mixed_machine}
+
+ ⚠ The per-runtime reports backing these tables were produced on different hardware, so the
+ ratios are not comparable until every runtime is re-run on one machine.
+
+ {/if}
+ {#if runtime_versions.length}
+
+ {#each runtime_versions as { runtime, version } (runtime)}
+ {runtime} {version}
+ {/each}
+
+ {/if}
+ {#each groups as group (group.group)}
+
+
{group_label(group.operation, group.language)}
+
+
+
+
+ {#each runtimes as runtime (runtime)}
+ {runtime}
+ {/each}
+ {#each others as runtime (runtime)}
+ {runtime}/{base}
+ {/each}
+
+
+
+ {#each group.rows as row (row.name)}
+
+
+
+ {format_cross_runtime_label(row.name)}
+
+ {#each runtimes as runtime (runtime)}
+ {format_ops(row.ops_per_second[runtime])}
+ {/each}
+ {#each others as runtime (runtime)}
+ {@const ratio = row.ratio_vs_base[runtime]}
+
+ {ratio != null ? format_speedup(ratio) : 'fail'}
+
+ {/each}
+
+ {/each}
+
+
+
+ {/each}
+
+ sweeps/sec — one sweep is a full pass over the group's timed file set (higher is faster); ratios
+ are vs {base} (> 1 = faster than {base}). A
+ fail is an implementation that runtime can't load (see notes above). The
+ native rows load each runtime's idiomatic binding of the same engine — the N-API
+ addon under node and bun (tsv (node napi)), the C-FFI
+ library under deno (tsv (deno ffi)) — so the deno column
+ is a first-class FFI-vs-N-API comparison, not a re-run of the same binding.
+
+
+
+
diff --git a/src/routes/docs/benchmarks/BenchmarksGroup.svelte b/src/routes/docs/benchmarks/BenchmarksGroup.svelte
index d3e2cb8..4ad6e1a 100644
--- a/src/routes/docs/benchmarks/BenchmarksGroup.svelte
+++ b/src/routes/docs/benchmarks/BenchmarksGroup.svelte
@@ -1,12 +1,6 @@
@@ -35,25 +38,7 @@
{count_label}
{group.language} files
- {#if has_coverage}
files handled / total · speed {/if}
+
total time · speed
-
- {#each group.entries as entry (entry.name)}
-
- {/each}
-
+
diff --git a/src/routes/docs/benchmarks/BenchmarksMeta.svelte b/src/routes/docs/benchmarks/BenchmarksMeta.svelte
index 596ee77..ddea0ef 100644
--- a/src/routes/docs/benchmarks/BenchmarksMeta.svelte
+++ b/src/routes/docs/benchmarks/BenchmarksMeta.svelte
@@ -1,7 +1,7 @@
-{#if wasm_display.length > 0}
-
-
wasm
-
- {#each wasm_display as s (s.label)}
-
- {/each}
-
-
-{/if}
-
-{#if native_display.length > 0}
+{#each size_groups as group (group.capability)}
-
native
-
- {#each native_display as s (s.label)}
-
- {/each}
-
+
{group.heading}
+
-{/if}
+{/each}
diff --git a/src/routes/docs/benchmarks/benchmark_data.ts b/src/routes/docs/benchmarks/benchmark_data.ts
index 9bf0958..4cda956 100644
--- a/src/routes/docs/benchmarks/benchmark_data.ts
+++ b/src/routes/docs/benchmarks/benchmark_data.ts
@@ -1,7 +1,12 @@
-// Raw types matching the tsv bench.ts `Baseline` format
+// Raw types matching the tsv bench's per-runtime report format
+// (`benches/js/results/report..json` — the site's flagship detailed
+// view is the Node report, the N-API native path)
export interface BenchmarkBaseline {
version: number;
+ // The runtime that produced this report (`node` for the flagship view).
+ // Present from report `version` 5 on.
+ runtime?: string;
timestamp: string;
git_commit: string;
corpus: Record;
@@ -11,23 +16,91 @@ export interface BenchmarkBaseline {
// Counts of silenced third-party stderr noise, keyed by message pattern.
// Present from baseline `version` 4 on; not rendered, kept for parity.
suppressed_noise?: Record;
+ // Which corpus/surface produced the report: `perf` (real-world corpus,
+ // format + parse) or `conformance` (the deliberately-hard fixture suites,
+ // disjoint from the perf corpus, parse only). Present from `version` 6 on.
+ corpus_kind?: 'perf' | 'conformance';
+ // Per-entry corpus composition (path + loaded file count) — discloses which
+ // sources were present on the machine that produced the report. Present
+ // from `version` 6 on.
+ corpus_sources?: Array;
+ // The machine that produced the report — CPU model, OS/arch, runtime version.
+ // The throughput numbers are machine-relative, so this is the environment the
+ // meta panel discloses. Present from `version` 7 on (absent on older reports).
+ machine?: Machine;
}
+// The hardware/runtime a report was measured on. Excludes hostname (the reports
+// are published) and volatile fields (free memory, load) that would churn.
+export interface Machine {
+ cpu_model: string;
+ os: string;
+ arch: string;
+ // The producing runtime's own version (`node`/`deno`/`bun` version string).
+ runtime_version: string;
+}
+
+export interface CorpusSource {
+ path: string;
+ files: number;
+ // Per-language split of `files` (svelte/typescript/css counts summing to
+ // `files`). Present on reports whose loader emitted it; older reports carry
+ // only the `files` total, so treat it as optional.
+ by_language?: Partial>;
+ // The source's GitHub origin, git-detected by the bench at report-build time
+ // (URL + commit + subpath). Present from `version` 8 on; absent on older
+ // reports and on sources with no GitHub remote — treat as optional.
+ repo?: CorpusRepoRef;
+}
+
+// A corpus source's GitHub origin — see `CorpusSource.repo` and `corpus_source_url`.
+export interface CorpusRepoRef {
+ // Canonical https GitHub URL, e.g. `https://github.com/sveltejs/svelte`.
+ url: string;
+ // `owner/name` (e.g. `sveltejs/svelte`) — a compact label.
+ slug: string;
+ // The commit the corpus was loaded at (full SHA); `''` for a harvested cache
+ // linked at its canonical upstream root (no pin).
+ commit: string;
+ // Path within the repo to this source (`''` = repo root).
+ subpath: string;
+}
+
+/**
+ * The GitHub URL for a corpus source, pinned to the measured commit + subpath
+ * (`…/tree//`) when detected, or the repo root for a
+ * canonical-upstream cache (empty `commit`). `undefined` when the source has no
+ * detected origin (older reports, or the local `svelte_styles` cache).
+ */
+export const corpus_source_url = (source: CorpusSource): string | undefined => {
+ const repo = source.repo;
+ if (!repo) return undefined;
+ if (!repo.commit) return repo.url;
+ return repo.subpath
+ ? `${repo.url}/tree/${repo.commit}/${repo.subpath}`
+ : `${repo.url}/tree/${repo.commit}`;
+};
+
export interface BaselineEntry {
name: string;
group: string;
- mean_ns: number;
- p50_ns: number;
- p75_ns: number;
- p90_ns: number;
- p95_ns: number;
- p99_ns: number;
- min_ns: number;
- max_ns: number;
- std_dev_ns: number;
- cv: number;
- ops_per_second: number;
- sample_size: number;
+ // Timing stats are `null` on a coverage-only report (the conformance surface
+ // the site refreshes from — parse coverage measured, timed phase skipped). A
+ // perf report always carries real numbers, and only the perf path
+ // (`derive_benchmark_groups`) reads these, so the nulls are unreachable there
+ // but must be expressed for the shared cast to stay sound.
+ mean_ns: number | null;
+ p50_ns: number | null;
+ p75_ns: number | null;
+ p90_ns: number | null;
+ p95_ns: number | null;
+ p99_ns: number | null;
+ min_ns: number | null;
+ max_ns: number | null;
+ std_dev_ns: number | null;
+ cv: number | null;
+ ops_per_second: number | null;
+ sample_size: number | null;
// Per-implementation preflight coverage: files this impl processed / the
// language's total discovered files. Present from baseline `version` 3 on;
// absent (or `null`) in older baselines.
@@ -36,6 +109,9 @@ export interface BaselineEntry {
// Files this impl was actually timed on (the per-group intersection in
// default mode). Present from baseline `version` 4 on.
files_iterated?: number | null;
+ // Present from report `version` 5 on (matches the report's top-level);
+ // not rendered, kept for parity.
+ runtime?: string;
}
export interface BaselineVersions {
@@ -84,10 +160,14 @@ export interface BenchmarkDisplayEntry {
name: string;
mean_ns: number;
bar_fraction: number;
- speedup_vs_canonical: number | undefined;
category: ImplementationCategory;
files_processed: number | null;
files_total: number | null;
+ // A placeholder entry mirrored from another language's group for a tool that
+ // doesn't run in this one (e.g. `oxc-parser` under svelte/css parse) — rendered
+ // grayed-out and inert so the parse groups share one entry order. Absent on real,
+ // measured entries.
+ disabled?: boolean;
}
export interface SpeedupRow {
@@ -105,9 +185,11 @@ const CATEGORY_BY_NAME: Record = {
'acorn-typescript': 'canonical',
tsv: 'tsv_native',
'tsv-json': 'tsv_native_json',
+ 'tsv-json-no-locations': 'tsv_native_json',
'tsv-internal': 'tsv_native',
tsv_wasm: 'tsv_wasm',
'tsv_wasm-json': 'tsv_wasm_json',
+ 'tsv_wasm-json-no-locations': 'tsv_wasm_json',
'tsv_wasm-internal': 'tsv_wasm',
'biome-wasm': 'biome',
'oxc-parser': 'oxc',
@@ -115,25 +197,15 @@ const CATEGORY_BY_NAME: Record = {
oxfmt: 'oxc',
};
-const categorize_name = (name: string): ImplementationCategory => CATEGORY_BY_NAME[name] ?? 'oxc';
+export const categorize_name = (name: string): ImplementationCategory =>
+ CATEGORY_BY_NAME[name] ?? 'oxc';
export const categorize_size = (label: string): ImplementationCategory => {
// covers `tsv_wasm` plus the `tsv_format_wasm`/`tsv_parse_wasm` subsets
if (label.startsWith('tsv') && label.includes('wasm')) return 'tsv_wasm';
if (label.startsWith('tsv')) return 'tsv_native';
if (label.startsWith('biome')) return 'biome';
- if (label.includes('oxc') || label.includes('oxfmt')) return 'oxc';
- return 'oxc';
-};
-
-// Canonical entry names per group
-const CANONICAL_BY_GROUP: Record = {
- 'parse/svelte': 'svelte/compiler',
- 'parse/typescript': 'acorn-typescript',
- 'parse/css': 'svelte/compiler',
- 'format/svelte': 'prettier',
- 'format/typescript': 'prettier',
- 'format/css': 'prettier',
+ return 'oxc'; // oxc-parser / oxfmt, and any unrecognized label
};
// Primary tsv entry names for speedup summary (fair comparisons)
@@ -142,6 +214,46 @@ const PRIMARY_WASM_FORMAT = 'tsv_wasm';
// Derivation functions
+// Shared display order for benchmark groups: format before parse, then by
+// language — used by the detailed, conformance, and cross-runtime views alike.
+const OPERATION_ORDER: Record = {format: 0, parse: 1};
+const LANGUAGE_ORDER: Record = {
+ svelte: 0,
+ typescript: 1,
+ css: 2,
+};
+
+/**
+ * Fixed slot for a format/parse row, applied in place of a size-ordered sort so the
+ * rows read in a stable, meaningful sequence across every group: the canonical
+ * reference first (the default 1.0x anchor), then the cross-tool comparisons (biome,
+ * then oxc), then tsv's JSON-materializing wires (the span-only `no-locations` wire
+ * before the default `loc`-carrying one), then tsv's raw internal engine.
+ */
+const speed_entry_rank = (entry: BenchmarkDisplayEntry): number => {
+ if (entry.category === 'canonical') return 0;
+ if (entry.category === 'biome') return 1;
+ if (entry.category === 'oxc') return 2;
+ if (entry.name.endsWith('-no-locations')) return 3; // tsv json, span-only wire
+ if (entry.name.endsWith('-json')) return 4; // tsv json, loc-carrying wire
+ return 5; // tsv-internal / tsv_wasm-internal — raw in-engine, no JS materialization
+};
+
+/**
+ * Orders format/parse rows by their fixed `speed_entry_rank` slot, then wasm before
+ * native within a tier (the browser-relevant build leads each pairing), then by name
+ * — shared by the initial sort and the re-sort after disabled placeholders are mixed
+ * in, so every group scans identically.
+ */
+const compare_speed_entries = (a: BenchmarkDisplayEntry, b: BenchmarkDisplayEntry): number => {
+ const rank = speed_entry_rank(a) - speed_entry_rank(b);
+ if (rank !== 0) return rank;
+ // wasm before native within a tier
+ const kind = (a.name.includes('wasm') ? 0 : 1) - (b.name.includes('wasm') ? 0 : 1);
+ if (kind !== 0) return kind;
+ return a.name.localeCompare(b.name);
+};
+
export const derive_benchmark_groups = (baseline: BenchmarkBaseline): Array => {
const grouped: Map> = new Map();
for (const entry of baseline.entries) {
@@ -159,29 +271,27 @@ export const derive_benchmark_groups = (baseline: BenchmarkBaseline): Array e.name === canonical_name);
- const slowest = Math.max(...entries.map((e) => e.mean_ns));
+ // The sort below leads each group with its canonical reference (Prettier for
+ // format, the JS baseline for parse), so the first row is the default 1.0x
+ // anchor; the shared component reads that default off the first row and
+ // recomputes every ratio, re-baselining onto whichever row is hovered. (Size
+ // groups lead with their smallest build; see `derive_size_groups`.)
+ // `?? 0` coerces the coverage-only null (unreachable on a perf report, the
+ // only kind this runs on) so the display entry's `mean_ns` stays a number.
+ const slowest = Math.max(...entries.map((e) => e.mean_ns ?? 0));
const display_entries: Array = entries.map((e) => ({
name: e.name,
- mean_ns: e.mean_ns,
- bar_fraction: slowest > 0 ? e.mean_ns / slowest : 0,
- speedup_vs_canonical:
- canonical_entry_raw && e.name !== canonical_name
- ? canonical_entry_raw.mean_ns / e.mean_ns
- : undefined,
+ mean_ns: e.mean_ns ?? 0,
+ bar_fraction: slowest > 0 ? (e.mean_ns ?? 0) / slowest : 0,
category: categorize_name(e.name),
files_processed: e.files_processed ?? null,
files_total: e.files_total ?? null,
}));
- // Sort: canonical first, then by mean_ns descending (slowest first for visual)
- display_entries.sort((a, b) => {
- if (a.category === 'canonical' && b.category !== 'canonical') return -1;
- if (b.category === 'canonical' && a.category !== 'canonical') return 1;
- return b.mean_ns - a.mean_ns;
- });
+ // Fixed order (see `compare_speed_entries`): canonical leads as the default 1.0x
+ // anchor, then biome, oxc, tsv's json wires, then tsv's internal engine
+ display_entries.sort(compare_speed_entries);
const iterated_counts = entries
.map((e) => e.files_iterated)
@@ -195,18 +305,131 @@ export const derive_benchmark_groups = (baseline: BenchmarkBaseline): Array = {svelte: 0, typescript: 1, css: 2};
- const OP_ORDER: Record = {format: 0, parse: 1};
result.sort(
(a, b) =>
- (OP_ORDER[a.operation] ?? 9) - (OP_ORDER[b.operation] ?? 9) ||
- (LANG_ORDER[a.language] ?? 9) - (LANG_ORDER[b.language] ?? 9),
+ (OPERATION_ORDER[a.operation] ?? 9) - (OPERATION_ORDER[b.operation] ?? 9) ||
+ (LANGUAGE_ORDER[a.language] ?? 9) - (LANGUAGE_ORDER[b.language] ?? 9),
);
+ // Neither `biome` nor (for svelte/css) `oxc-parser` has a real entry in every
+ // parse group. `biome`'s `@biomejs/js-api` never exposes a parser to JS at all
+ // (only formatting and linting), so no parse group has a real biome entry;
+ // `oxc-parser` only parses TypeScript/JS, so the svelte and css parse groups
+ // lack it. Mirror both in as disabled placeholders — biome always, oxc-parser
+ // only where it's missing — then re-sort so they fall into their fixed slots
+ // (biome then oxc, right after the canonical row), giving all three parse groups
+ // one shared entry order.
+ const ts_parse = result.find((g) => g.operation === 'parse' && g.language === 'typescript');
+ const oxc_templates = ts_parse?.entries.filter((e) => e.category === 'oxc') ?? [];
+ for (const group of result) {
+ if (group.operation !== 'parse') continue;
+ const biome_placeholder: BenchmarkDisplayEntry = {
+ name: 'biome-wasm',
+ mean_ns: 0,
+ bar_fraction: 0,
+ category: 'biome',
+ files_processed: null,
+ files_total: null,
+ disabled: true,
+ };
+ const needs_oxc =
+ group.language !== 'typescript' && !group.entries.some((e) => e.category === 'oxc');
+ const oxc_placeholders: Array = needs_oxc
+ ? oxc_templates.map((e) => ({
+ ...e,
+ bar_fraction: 0,
+ files_processed: null,
+ files_total: null,
+ disabled: true,
+ }))
+ : [];
+ group.entries.push(biome_placeholder, ...oxc_placeholders);
+ group.entries.sort(compare_speed_entries);
+ }
+
return result;
};
+// Parse-conformance coverage (the conformance report's headline metric)
+
+export interface ConformanceRow {
+ name: string;
+ files_processed: number;
+ files_total: number;
+ // files_processed / files_total, rendered as the coverage percentage
+ coverage_fraction: number;
+}
+
+export interface ConformanceGroup {
+ language: string;
+ // the language's total discovered files (every row shares it)
+ files_total: number;
+ rows: Array;
+}
+
+/**
+ * One coverage row per ENGINE, not per binding: the conformance headline is
+ * "which files does this parser accept," which is identical across a tool's
+ * native/wasm/internal variants — so the `-wasm` and `-internal` duplicates
+ * are dropped and `tsv-json` stands in for tsv (relabeled plainly, since the
+ * JSON-materialization qualifier is a speed concern, not a coverage one).
+ */
+const CONFORMANCE_ENGINE_NAMES: Record = {
+ 'svelte/compiler': 'svelte/compiler',
+ 'acorn-typescript': 'acorn-typescript',
+ 'tsv-json': 'tsv',
+ 'oxc-parser': 'oxc-parser',
+};
+
+/**
+ * Derives per-language parse-coverage groups from a conformance report
+ * (`corpus_kind: 'conformance'` — parse groups only). Rows are ordered by coverage,
+ * highest first; entries without coverage data are dropped.
+ */
+export const derive_conformance_groups = (baseline: BenchmarkBaseline): Array => {
+ const by_language: Map> = new Map();
+ for (const entry of baseline.entries) {
+ const [operation, language] = entry.group.split('/');
+ if (operation !== 'parse' || !language) continue;
+ const display_name = CONFORMANCE_ENGINE_NAMES[entry.name];
+ if (!display_name) continue;
+ if (entry.files_processed == null || entry.files_total == null) continue;
+ let rows = by_language.get(language);
+ if (!rows) {
+ rows = [];
+ by_language.set(language, rows);
+ }
+ rows.push({
+ name: display_name,
+ files_processed: entry.files_processed,
+ files_total: entry.files_total,
+ coverage_fraction: entry.files_total > 0 ? entry.files_processed / entry.files_total : 0,
+ });
+ }
+
+ const result: Array = [];
+ for (const [language, rows] of by_language) {
+ // coverage descending (highest acceptance first), name as a stable tiebreak
+ rows.sort((a, b) => b.coverage_fraction - a.coverage_fraction || a.name.localeCompare(b.name));
+ result.push({
+ language,
+ files_total: Math.max(0, ...rows.map((r) => r.files_total)),
+ rows,
+ });
+ }
+ result.sort((a, b) => (LANGUAGE_ORDER[a.language] ?? 9) - (LANGUAGE_ORDER[b.language] ?? 9));
+ return result;
+};
+
+/**
+ * Formats a coverage fraction as a percentage with two decimals (`99.85%`),
+ * FLOORED rather than rounded — rounding would render e.g. 44219/44220 as
+ * `100.00%` next to a visibly non-total count. Only exact totality reads 100%
+ * (matching the harness's own `coverage_pct` convention in tsv's report.ts).
+ */
+export const format_coverage_percent = (fraction: number): string =>
+ `${(Math.floor(fraction * 10_000) / 100).toFixed(2)}%`;
+
export const derive_speedup_summary = (groups: Array): Array => {
const find_speedup = (
operation: string,
@@ -236,6 +459,266 @@ export const derive_speedup_summary = (groups: Array): Array {
+ const has_parse = label.includes('parse');
+ const has_format = label.includes('format') || label.includes('fmt');
+ // a build that does both is a full toolchain (e.g. the combined oxc-parser + oxfmt entry)
+ if (has_parse && has_format) return 'full';
+ if (has_parse) return 'parser'; // tsv parse (ffi), tsv_parse_wasm, oxc-parser
+ if (has_format) return 'formatter'; // tsv format, oxfmt
+ return 'full'; // tsv (napi/ffi), tsv_wasm, biome
+};
+
+export interface SizeDisplayEntry extends BinarySize {
+ bar_fraction: number;
+ category: ImplementationCategory;
+ // a grayed-out, inert placeholder for a build that doesn't exist (e.g. oxfmt's
+ // absent wasm build) — no bar, no size, just the label held in its slot. Absent
+ // on real, measured entries.
+ disabled?: boolean;
+}
+
+export interface SizeCapabilityGroup {
+ capability: SizeCapability;
+ heading: string;
+ // sorted smallest-first, so the leading entry is the default ratio anchor (1.0x)
+ entries: Array;
+}
+
+const SIZE_CAPABILITY_ORDER: ReadonlyArray<{
+ capability: SizeCapability;
+ heading: string;
+}> = [
+ {capability: 'full', heading: 'Full toolchain (parse + format)'},
+ {capability: 'formatter', heading: 'Formatter'},
+ {capability: 'parser', heading: 'Parser'},
+];
+
+/** Display label for the synthesized combined oxc full-toolchain build. */
+export const OXC_FULL_LABEL = 'oxc-parser + oxfmt (napi)';
+
+/** Display label for oxfmt's absent wasm build placeholder — see `derive_size_groups`. */
+export const OXFMT_WASM_LABEL = 'oxfmt (wasm)';
+
+/**
+ * Synthesizes oxc's full-toolchain native build by summing its separately-shipped
+ * parser (`oxc-parser (napi)`) and formatter (`oxfmt (napi)`) packages — together
+ * they're the closest equivalent to tsv's single parse+format build, so the entry
+ * stands beside `tsv (napi)` in the full-toolchain group. Returns `undefined` when
+ * either half is missing (older baselines), and sums gzip only when both carry it.
+ */
+const synthesize_oxc_full = (sizes: Array): BinarySize | undefined => {
+ const parser = sizes.find((s) => s.label === 'oxc-parser (napi)');
+ const formatter = sizes.find((s) => s.label === 'oxfmt (napi)');
+ if (!parser || !formatter) return undefined;
+ return {
+ label: OXC_FULL_LABEL,
+ bytes: parser.bytes + formatter.bytes,
+ kind: 'native',
+ gzip_bytes:
+ parser.gzip_bytes != null && formatter.gzip_bytes != null
+ ? parser.gzip_bytes + formatter.gzip_bytes
+ : null,
+ };
+};
+
+/**
+ * Groups the binary sizes by capability (full / formatter / parser), each group
+ * mixing wasm and native builds sorted smallest-first. Bars scale to the group's
+ * largest build; the `vs` ratio anchors on the group's single smallest build, so
+ * exactly one entry reads 1.0x and every other is a multiple of it. (In the current
+ * data that smallest build is always one of tsv's, so tsv reads 1.0x and the heavier
+ * competitors read >1.0x.) A combined `oxc-parser + oxfmt` entry is
+ * synthesized into the full-toolchain group, since oxc ships parse and format apart.
+ * oxfmt has no wasm build, so the formatter group gets a disabled `oxfmt (wasm)`
+ * placeholder slotted just above its real `oxfmt (napi)` entry, holding the slot
+ * rather than omitting it.
+ */
+export const derive_size_groups = (sizes: Array): Array => {
+ const oxc_full = synthesize_oxc_full(sizes);
+ const all_sizes = oxc_full ? [...sizes, oxc_full] : sizes;
+ const groups: Array = [];
+ for (const {capability, heading} of SIZE_CAPABILITY_ORDER) {
+ const items = all_sizes.filter((s) => categorize_size_capability(s.label) === capability);
+ if (items.length === 0) continue;
+ const sorted = items.toSorted((a, b) => a.bytes - b.bytes);
+ const max = Math.max(0, ...items.map((s) => s.bytes));
+ // `sorted` is ascending, so the smallest build leads the group — its single
+ // default ratio anchor (1.0x), one baseline whether or not the group mixes wasm
+ // and native (rather than the confusing pair a per-kind anchor produced). The
+ // shared component reads every ratio from that leading row.
+ const entries: Array = sorted.map((s) => ({
+ ...s,
+ bar_fraction: max > 0 ? s.bytes / max : 0,
+ category: categorize_size(s.label),
+ }));
+ if (capability === 'formatter' && !entries.some((e) => e.label === OXFMT_WASM_LABEL)) {
+ const native_index = entries.findIndex((e) => e.label === 'oxfmt (napi)');
+ const placeholder: SizeDisplayEntry = {
+ label: OXFMT_WASM_LABEL,
+ bytes: 0,
+ kind: 'wasm',
+ gzip_bytes: null,
+ bar_fraction: 0,
+ category: categorize_size(OXFMT_WASM_LABEL),
+ disabled: true,
+ };
+ entries.splice(native_index === -1 ? entries.length : native_index, 0, placeholder);
+ }
+ groups.push({capability, heading, entries});
+ }
+ return groups;
+};
+
+// Cross-runtime combined report (the bench composer's `report.json`, `kind: 'combined'`)
+
+export type BenchmarkRuntime = 'deno' | 'node' | 'bun';
+
+export interface CrossRuntimeRow {
+ group: string;
+ name: string;
+ ops_per_second: Partial>;
+ mean_ns: Partial>;
+ files_iterated: Partial>;
+}
+
+export interface CrossRuntimeReport {
+ version: number;
+ kind: 'combined';
+ generated: string;
+ runtimes: Array;
+ // The sibling reports came from different commits/versions — ratios are
+ // unreliable until every runtime is re-run. Present from combined
+ // `version` 6 on.
+ mixed_vintage?: boolean;
+ // The sibling reports were produced on different hardware — ratios are not
+ // comparable. Present from combined `version` 7 on.
+ mixed_machine?: boolean;
+ sources: Array<{
+ runtime: BenchmarkRuntime;
+ timestamp: string;
+ git_commit: string | null;
+ tsv: string | null;
+ // The producing box's machine block; present from combined `version` 7 on.
+ machine?: Machine | null;
+ }>;
+ rows: Array;
+}
+
+export interface CrossRuntimeDisplayRow {
+ name: string;
+ category: ImplementationCategory;
+ ops_per_second: Partial>;
+ // ratio of each runtime vs the base (first present) runtime; `> 1` = faster
+ ratio_vs_base: Partial>;
+}
+
+export interface CrossRuntimeGroup {
+ group: string;
+ operation: string;
+ language: string;
+ rows: Array;
+}
+
+// The combined report stores runtimes deno-first (matching the bench's
+// `report.md`); the site presents them node-first (the flagship N-API runtime),
+// then deno, then bun.
+const CROSS_RUNTIME_DISPLAY_ORDER: Array = ['node', 'deno', 'bun'];
+
+/**
+ * The report's runtimes in the site's display order — node (the flagship, the
+ * ratio anchor) first, then deno, then bun. Shared by `derive_cross_runtime_groups`
+ * and the table headers so the anchor can't drift between them.
+ */
+export const order_cross_runtime_runtimes = (
+ runtimes: Array,
+): Array => CROSS_RUNTIME_DISPLAY_ORDER.filter((r) => runtimes.includes(r));
+
+/** One runtime's own version string (`node 24.14.1`), for the cross-runtime section. */
+export interface RuntimeVersion {
+ runtime: BenchmarkRuntime;
+ version: string;
+}
+
+/**
+ * The per-runtime version strings from a cross-runtime report, in the site's
+ * display order (node first). Drops any runtime whose source carries no machine
+ * block (reports predating combined `version` 7). The cross-runtime section
+ * renders these so the three-runtime tables disclose which node/deno/bun version
+ * each column was measured under; the environment panel above stays scoped to the
+ * flagship Node baseline. The shared hardware identity (CPU/OS/arch) isn't
+ * repeated here — it lives in the environment panel, and a per-runtime hardware
+ * mismatch is the report's `mixed_machine` flag's concern.
+ */
+export const derive_runtime_versions = (report: CrossRuntimeReport): Array => {
+ const by_runtime = new Map(report.sources.map((source) => [source.runtime, source]));
+ const result: Array = [];
+ for (const runtime of order_cross_runtime_runtimes(report.runtimes)) {
+ const version = by_runtime.get(runtime)?.machine?.runtime_version;
+ if (version) result.push({runtime, version});
+ }
+ return result;
+};
+
+/**
+ * Groups the combined report's rows by benchmark group, in the same display
+ * order as `derive_benchmark_groups` (format before parse, then svelte /
+ * typescript / css). The ratio base is the first runtime in display order
+ * (node when present — the flagship N-API path), regardless of the report's
+ * own deno-first storage order.
+ */
+export const derive_cross_runtime_groups = (
+ report: CrossRuntimeReport,
+): Array => {
+ const runtimes = order_cross_runtime_runtimes(report.runtimes);
+ const base = runtimes[0];
+ const grouped: Map> = new Map();
+ for (const row of report.rows) {
+ let rows = grouped.get(row.group);
+ if (!rows) {
+ rows = [];
+ grouped.set(row.group, rows);
+ }
+ const base_ops = base ? row.ops_per_second[base] : undefined;
+ const ratio_vs_base: Partial> = {};
+ for (const runtime of runtimes) {
+ const ops = row.ops_per_second[runtime];
+ if (ops != null && base_ops != null && base_ops > 0) {
+ ratio_vs_base[runtime] = ops / base_ops;
+ }
+ }
+ rows.push({
+ name: row.name,
+ category: categorize_name(row.name),
+ ops_per_second: row.ops_per_second,
+ ratio_vs_base,
+ });
+ }
+
+ const result: Array = [];
+ for (const [group, rows] of grouped) {
+ const [operation, language] = group.split('/');
+ result.push({group, operation: operation!, language: language!, rows});
+ }
+ result.sort(
+ (a, b) =>
+ (OPERATION_ORDER[a.operation] ?? 9) - (OPERATION_ORDER[b.operation] ?? 9) ||
+ (LANGUAGE_ORDER[a.language] ?? 9) - (LANGUAGE_ORDER[b.language] ?? 9),
+ );
+ return result;
+};
+
// Formatting utilities
export interface FormattedUnit {
@@ -261,15 +744,92 @@ export const format_bytes = (bytes: number): FormattedUnit => {
};
/**
- * Formats per-implementation corpus coverage as `processed/total`, or
- * `undefined` when either value is missing (older baselines without coverage).
+ * Formats a corpus source's file count as a per-language breakdown
+ * (`124 typescript, 15 svelte, 31 css`), largest language first and dropping
+ * zero-count languages. Falls back to the plain `N files` total when the report
+ * predates the per-language split (or lists no recognized language).
+ */
+export const format_corpus_source_files = (source: CorpusSource): string => {
+ const total = `${source.files.toLocaleString('en-US')} files`;
+ if (!source.by_language) return total;
+ const parts = Object.entries(source.by_language)
+ .filter((entry): entry is [string, number] => (entry[1] ?? 0) > 0)
+ .sort((a, b) => b[1] - a[1])
+ .map(([language, count]) => `${count.toLocaleString('en-US')} ${language}`);
+ return parts.length > 0 ? parts.join(', ') : total;
+};
+
+// Corpus source repos (site-owned path → URL mapping)
+
+export interface CorpusRepo {
+ // public repo URL the entry links to
+ url: string;
+ // `org/name`, derived from the URL — the linkified display label
+ label: string;
+}
+
+/**
+ * Maps a corpus source's on-disk path to its public repo URL. The bench records
+ * only local paths (`../zzz/src`), so the site owns this mapping — the links then
+ * survive an `update-benchmarks` refresh without touching the copied JSON. Keyed by
+ * the repo's directory prefix, each ending in a slash so siblings like `../fuz_css/`
+ * and `../fuz_code/`, and `../svelte/` vs `../svelte.dev/`, stay distinct. A source
+ * matching no prefix — the derived `svelte_styles` CSS cache, which isn't a single
+ * repo — is dropped from the repos list rather than shown unlinked.
*/
-export const format_coverage = (
- processed: number | null | undefined,
- total: number | null | undefined,
-): string | undefined => {
- if (processed == null || total == null) return undefined;
- return `${processed}/${total}`;
+const CORPUS_REPO_URL_BY_PREFIX: ReadonlyArray = [
+ ['../zzz/', 'https://github.com/fuzdev/zzz'],
+ ['../fuz_app/', 'https://github.com/fuzdev/fuz_app'],
+ ['../fuz_blog/', 'https://github.com/fuzdev/fuz_blog'],
+ ['../fuz_code/', 'https://github.com/fuzdev/fuz_code'],
+ ['../fuz_css/', 'https://github.com/fuzdev/fuz_css'],
+ ['../fuz_docs/', 'https://github.com/fuzdev/fuz_docs'],
+ ['../fuz_gitops/', 'https://github.com/fuzdev/fuz_gitops'],
+ ['../fuz_mastodon/', 'https://github.com/fuzdev/fuz_mastodon'],
+ ['../fuz_template/', 'https://github.com/fuzdev/fuz_template'],
+ ['../fuz_ui/', 'https://github.com/fuzdev/fuz_ui'],
+ ['../fuz_util/', 'https://github.com/fuzdev/fuz_util'],
+ ['../mdz/', 'https://github.com/fuzdev/mdz'],
+ ['../gro/', 'https://github.com/fuzdev/gro'],
+ ['../svelte-docinfo/', 'https://github.com/fuzdev/svelte-docinfo'],
+ ['../tsv.fuz.dev/', 'https://github.com/fuzdev/tsv.fuz.dev'],
+ ['../ryanatkn.com/', 'https://github.com/ryanatkn/ryanatkn.com'],
+ ['../webdevladder.net/', 'https://github.com/ryanatkn/webdevladder.net'],
+ ['../kit/', 'https://github.com/sveltejs/kit'],
+ ['../svelte.dev/', 'https://github.com/sveltejs/svelte.dev'],
+ ['../svelte/', 'https://github.com/sveltejs/svelte'],
+];
+
+/** The repo URL for a corpus source path, or `undefined` when it maps to no repo. */
+const corpus_repo_url = (path: string): string | undefined =>
+ CORPUS_REPO_URL_BY_PREFIX.find(([prefix]) => path.startsWith(prefix))?.[1];
+
+/** `org/name` from a `https://github.com/org/name` URL — the linkified label. */
+const corpus_repo_label = (url: string): string => new URL(url).pathname.slice(1);
+
+/**
+ * The distinct source repos behind a report's corpus, one entry per URL in
+ * first-seen order (the author's ecosystem leads, the upstream framework repos
+ * trail, matching the source order). Sources sharing a repo (svelte.dev's several
+ * packages) collapse to one entry; sources with no mapped repo (the `svelte_styles`
+ * CSS cache) are dropped. URLs come from the site-owned `CORPUS_REPO_URL_BY_PREFIX`,
+ * not the report, so the links survive a refresh.
+ */
+export const derive_corpus_repos = (
+ sources: Array | undefined,
+): Array => {
+ const by_url: Map = new Map();
+ for (const source of sources ?? []) {
+ // Prefer the report's git-detected repo; fall back to the legacy prefix
+ // map for reports predating `version` 8 (which lack `source.repo`).
+ const url = source.repo?.url ?? corpus_repo_url(source.path);
+ if (!url || by_url.has(url)) continue;
+ by_url.set(url, {
+ url,
+ label: source.repo?.slug ?? corpus_repo_label(url),
+ });
+ }
+ return [...by_url.values()];
};
/**
@@ -287,10 +847,55 @@ export const format_gzip_size = (gzip_bytes: number | null | undefined): string
export const format_speedup = (ratio: number): string =>
ratio >= 10 ? `${ratio.toFixed(1)}x` : `${ratio.toFixed(2)}x`;
+/**
+ * Signed speedup: entries at or above the anchor read as a plain multiple
+ * (`2.50x`), while slower entries show the reciprocal negated (`0.15x` → `-6.67x`)
+ * so "how many times slower" is directly legible instead of a fraction the reader
+ * has to invert. The minus is a convention for "times slower", not a literal
+ * negative rate. Used for the format/parse speed bars, where entries span widely
+ * on both sides of the anchor; the near-parity cross-runtime table and the
+ * bigger-is-worse size ratios stay on the plain fractional `format_speedup`.
+ */
+export const format_speedup_signed = (ratio: number): string => {
+ const magnitude = ratio >= 1 ? ratio : 1 / ratio;
+ const digits = magnitude >= 10 ? 1 : 2;
+ return `${ratio < 1 ? '-' : ''}${magnitude.toFixed(digits)}x`;
+};
+
/** Hyphenated tool names that should preserve their hyphens in display labels. */
const HYPHENATED_NAMES = ['acorn-typescript', 'oxc-parser'];
+/**
+ * Display labels for the raw benchmark entry names in the main (Node) tables,
+ * annotating each with the runtime **and** binding it runs under — the native
+ * builds load the N-API addon under Node, so they read `(node napi)` to
+ * distinguish them from the Deno FFI numbers the cross-runtime table surfaces
+ * (`tsv (deno ffi)`); the third-party wasm builds are marked `(wasm)`. Mirrors
+ * the parenthesized suffixes the binary-size section's labels already carry.
+ * tsv's own wasm entries keep their `tsv_wasm` package-name style, as in the size
+ * groups, so they aren't listed here. The already-parenthesized size labels
+ * aren't keys, so they fall through to the generic formatting below unchanged.
+ * The cross-runtime table neutralizes the `(node napi)` suffix per row (its
+ * columns span runtimes) via `format_cross_runtime_label`.
+ */
+const LABEL_OVERRIDES: Record = {
+ tsv: 'tsv (node napi)',
+ 'tsv-json': 'tsv json (node napi)',
+ // `no-locs` (not `no-locations`) — the full word eats too much column width.
+ 'tsv-json-no-locations': 'tsv json no-locs (node napi)',
+ // the one tsv_wasm entry listed here: the generic formatting below would
+ // break the `no-locs` hyphen its native sibling deliberately keeps
+ 'tsv_wasm-json-no-locations': 'tsv_wasm json no-locs',
+ 'tsv-internal': 'tsv internal (node napi)',
+ 'oxc-parser': 'oxc-parser (node napi)',
+ oxfmt: 'oxfmt (node napi)',
+ 'biome-wasm': 'biome (wasm)',
+ 'oxc-parser-wasm': 'oxc-parser (wasm)',
+};
+
export const format_label = (name: string): string => {
+ const override = LABEL_OVERRIDES[name];
+ if (override) return override;
for (const tool of HYPHENATED_NAMES) {
if (name.startsWith(tool)) {
return tool + name.slice(tool.length).replaceAll('-', ' ');
@@ -299,6 +904,16 @@ export const format_label = (name: string): string => {
return name.replaceAll('-', ' ');
};
+/**
+ * The label for a cross-runtime row. Each row spans node/deno/bun columns, and a
+ * native build's binding is runtime-specific (N-API on node & bun, C-FFI on
+ * deno), so the row can't pin one binding — neutralize `format_label`'s
+ * node-centric `(node napi)` suffix to `(native)`. The per-runtime binding is
+ * disclosed once in the table caption instead (see `BenchmarksCrossRuntime`).
+ */
+export const format_cross_runtime_label = (name: string): string =>
+ format_label(name).replace(' (node napi)', ' (native)');
+
/** Returns a CSS background color variable for a category. */
export const category_color = (category: ImplementationCategory): string => {
switch (category) {
@@ -319,11 +934,17 @@ export const category_color = (category: ImplementationCategory): string => {
}
};
-/** Returns a CSS color variable for a size ratio (inverted — bigger is worse). */
+/**
+ * Returns a CSS color variable for a size ratio (inverted — bigger is worse).
+ * Green is reserved for builds smaller than the baseline (ratio < 1, only reachable
+ * once a hover re-baselines onto a larger build); any build at or above the baseline
+ * reads yellow at the floor, ramping through orange to red as it grows.
+ */
export const size_ratio_color = (ratio: number): string => {
- if (ratio < 2) return 'var(--color_b_50)'; // green — similar
- if (ratio < 5) return 'var(--color_e_50)'; // yellow — notably larger
- return 'var(--color_c_50)'; // red — much larger
+ if (ratio < 1) return 'var(--color_b_50)'; // green — smaller than baseline
+ if (ratio < 3) return 'var(--color_e_50)'; // yellow — larger
+ if (ratio < 10) return 'var(--color_h_50)'; // orange — much larger
+ return 'var(--color_c_50)'; // red — enormous
};
/** Returns a CSS color variable for the speedup ratio. */
@@ -334,3 +955,73 @@ export const speedup_color = (ratio: number): string => {
if (ratio < 5) return 'var(--color_b_50)'; // green — fast
return 'var(--color_j_50)'; // teal — exceptional
};
+
+/**
+ * Cross-runtime ratio cell background: a stable fuz_css red (`color_c`) / green
+ * (`color_b`) whose ALPHA varies with distance from parity. Fully transparent at ratio
+ * `1.0` (parity recedes), ramping to `0.3` alpha at ratio `0.8` and below (red) or `1.2`
+ * and above (green). The hue stays constant — only opacity moves — so it reads
+ * consistently in light and dark themes while the cell's text keeps the default color.
+ * Deliberately its own scale — NOT the shared `speedup_color` — because cross-runtime
+ * deltas cluster tightly near 1.0 and this must not bleed into the other displays.
+ */
+export const cross_runtime_ratio_background = (ratio: number): string => {
+ // 0 alpha at ratio 1.0, up to 0.3 at ratio ≤ 0.8 (red) or ≥ 1.2 (green)
+ const alpha = Math.min(1, Math.abs(ratio - 1) / 0.2) * 0.3;
+ const color = ratio < 1 ? 'var(--color_c_50)' : 'var(--color_b_50)';
+ return `color-mix(in srgb, ${color} ${(alpha * 100).toFixed(1)}%, transparent)`;
+};
+
+// Interactive baseline (hover-to-rebaseline)
+
+/**
+ * Which way a group's ratio runs, so re-anchoring on hover stays consistent. A
+ * `speed` group (format/parse) reads its anchor as a reference speed — faster
+ * entries are positive multiples, slower ones negative (`format_speedup_signed` /
+ * `speedup_color`). A `size` group reads its anchor as a reference size — bigger
+ * builds are multiples ≥ 1 (`format_size_ratio` / `size_ratio_color`). The two
+ * ratios are reciprocals, which is exactly why one direction flag suffices.
+ */
+export type BaselineDirection = 'speed' | 'size';
+
+/** An entry's ratio against its group's anchor, in the group's direction. */
+export const compute_baseline_ratio = (
+ direction: BaselineDirection,
+ entry_raw: number,
+ anchor_raw: number,
+): number => (direction === 'speed' ? anchor_raw / entry_raw : entry_raw / anchor_raw);
+
+/** Plain size ratio (`2.3x`) — its own formatter since sizes never take the signed treatment. */
+export const format_size_ratio = (ratio: number): string => `${ratio.toFixed(1)}x`;
+
+/** Formats a baseline ratio for display in the given direction. */
+export const format_baseline_ratio = (direction: BaselineDirection, ratio: number): string =>
+ direction === 'speed' ? format_speedup_signed(ratio) : format_size_ratio(ratio);
+
+/** Color for a baseline ratio in the given direction. */
+export const baseline_ratio_color = (direction: BaselineDirection, ratio: number): string =>
+ direction === 'speed' ? speedup_color(ratio) : size_ratio_color(ratio);
+
+/**
+ * A normalized row for `BenchmarksBaselineGroup` — the shared hover-to-rebaseline
+ * column behind the format, parse, and binary-size groups. `raw` is the number the
+ * ratio derives from (sweep mean ns for speed, bytes for size); `value` is the
+ * row's displayed measurement (the whole-sweep mean — total time over the group's
+ * iterated corpus — for speed, bytes for size). Flattening the group-specific
+ * display entries to this one shape lets a single component own the anchor state
+ * for all three sections.
+ */
+export interface BaselineRow {
+ // stable identity for `#each` and anchor matching (the entry name / size label)
+ key: string;
+ // raw name/label; `BenchmarksBar` formats it for display
+ label: string;
+ category: ImplementationCategory;
+ bar_fraction: number;
+ value: FormattedUnit;
+ // the number the ratio derives from — mean ns (speed) or bytes (size)
+ raw: number;
+ annotation: string | undefined;
+ // grayed-out, inert placeholder — never an anchor, no hover highlight
+ disabled: boolean;
+}
diff --git a/src/routes/docs/benchmarks/benchmarks.json b/src/routes/docs/benchmarks/benchmarks.json
index bf56417..eefd677 100644
--- a/src/routes/docs/benchmarks/benchmarks.json
+++ b/src/routes/docs/benchmarks/benchmarks.json
@@ -1,685 +1,1162 @@
{
- "version": 4,
- "timestamp": "2026-06-12T09:22:00.588Z",
- "git_commit": "98918f9",
+ "version": 7,
+ "runtime": "node",
+ "corpus_kind": "perf",
+ "timestamp": "2026-07-16T11:43:29.034Z",
+ "git_commit": "135b7b93",
+ "machine": {
+ "cpu_model": "AMD Ryzen 5 PRO 7530U with Radeon Graphics",
+ "os": "linux",
+ "arch": "x86_64",
+ "runtime_version": "24.14.1"
+ },
"corpus": {
- "svelte": 1240,
- "typescript": 4025,
- "css": 193
+ "svelte": 763,
+ "typescript": 2437,
+ "css": 49
},
+ "corpus_sources": [
+ {
+ "path": "../zzz/src",
+ "files": 326,
+ "by_language": {
+ "svelte": 173,
+ "typescript": 152,
+ "css": 1
+ },
+ "repo": {
+ "url": "https://github.com/fuzdev/zzz",
+ "slug": "fuzdev/zzz",
+ "commit": "838d499c7cc746ebe1f5da8470e7679c8895aee0",
+ "subpath": "src"
+ }
+ },
+ {
+ "path": "../fuz_app/src",
+ "files": 665,
+ "by_language": {
+ "svelte": 33,
+ "typescript": 631,
+ "css": 1
+ },
+ "repo": {
+ "url": "https://github.com/fuzdev/fuz_app",
+ "slug": "fuzdev/fuz_app",
+ "commit": "a0e63a732b979df8dd7ae6730548227cca4bc79c",
+ "subpath": "src"
+ }
+ },
+ {
+ "path": "../fuz_blog/src",
+ "files": 37,
+ "by_language": {
+ "svelte": 20,
+ "typescript": 16,
+ "css": 1
+ },
+ "repo": {
+ "url": "https://github.com/fuzdev/fuz_blog",
+ "slug": "fuzdev/fuz_blog",
+ "commit": "7147f354fac7dcb535f70f241db1dd3543c0a370",
+ "subpath": "src"
+ }
+ },
+ {
+ "path": "../fuz_code/src",
+ "files": 66,
+ "by_language": {
+ "svelte": 17,
+ "typescript": 46,
+ "css": 3
+ },
+ "repo": {
+ "url": "https://github.com/fuzdev/fuz_code",
+ "slug": "fuzdev/fuz_code",
+ "commit": "7b3664851737def05ac8d4b646b27643cfb4a39f",
+ "subpath": "src"
+ }
+ },
+ {
+ "path": "../fuz_css/src",
+ "files": 146,
+ "by_language": {
+ "svelte": 39,
+ "typescript": 105,
+ "css": 2
+ },
+ "repo": {
+ "url": "https://github.com/fuzdev/fuz_css",
+ "slug": "fuzdev/fuz_css",
+ "commit": "b0b02e804d3db586c37851754401e3f57b449345",
+ "subpath": "src"
+ }
+ },
+ {
+ "path": "../fuz_docs/src",
+ "files": 65,
+ "by_language": {
+ "svelte": 45,
+ "typescript": 19,
+ "css": 1
+ },
+ "repo": {
+ "url": "https://github.com/fuzdev/fuz_docs",
+ "slug": "fuzdev/fuz_docs",
+ "commit": "40b3d2ad892d4434088f6da83e54f7cbf69615e0",
+ "subpath": "src"
+ }
+ },
+ {
+ "path": "../fuz_gitops/src",
+ "files": 99,
+ "by_language": {
+ "svelte": 28,
+ "typescript": 70,
+ "css": 1
+ },
+ "repo": {
+ "url": "https://github.com/fuzdev/fuz_gitops",
+ "slug": "fuzdev/fuz_gitops",
+ "commit": "6d1a90269e16559259d7660ba1b7b119da5967ac",
+ "subpath": "src"
+ }
+ },
+ {
+ "path": "../fuz_mastodon/src",
+ "files": 25,
+ "by_language": {
+ "svelte": 15,
+ "typescript": 9,
+ "css": 1
+ },
+ "repo": {
+ "url": "https://github.com/fuzdev/fuz_mastodon",
+ "slug": "fuzdev/fuz_mastodon",
+ "commit": "6179d626e50dd1a53540a074fc23c354c2985321",
+ "subpath": "src"
+ }
+ },
+ {
+ "path": "../fuz_template/src",
+ "files": 16,
+ "by_language": {
+ "svelte": 10,
+ "typescript": 5,
+ "css": 1
+ },
+ "repo": {
+ "url": "https://github.com/fuzdev/fuz_template",
+ "slug": "fuzdev/fuz_template",
+ "commit": "c5c0c0332b811c983d4e60031452f668d163d28d",
+ "subpath": "src"
+ }
+ },
+ {
+ "path": "../fuz_ui/src",
+ "files": 216,
+ "by_language": {
+ "svelte": 121,
+ "typescript": 95,
+ "css": 0
+ },
+ "repo": {
+ "url": "https://github.com/fuzdev/fuz_ui",
+ "slug": "fuzdev/fuz_ui",
+ "commit": "311a8549508ec9c3d0c6ebc895c82d42321daeb3",
+ "subpath": "src"
+ }
+ },
+ {
+ "path": "../fuz_util/src",
+ "files": 145,
+ "by_language": {
+ "svelte": 8,
+ "typescript": 136,
+ "css": 1
+ },
+ "repo": {
+ "url": "https://github.com/fuzdev/fuz_util",
+ "slug": "fuzdev/fuz_util",
+ "commit": "9a9a0f941b14481dc4b96becdacdf307e071af39",
+ "subpath": "src"
+ }
+ },
+ {
+ "path": "../mdz/src",
+ "files": 71,
+ "by_language": {
+ "svelte": 22,
+ "typescript": 48,
+ "css": 1
+ },
+ "repo": {
+ "url": "https://github.com/fuzdev/mdz",
+ "slug": "fuzdev/mdz",
+ "commit": "f5dbce0755f74961da0636079714e4e00bf667f0",
+ "subpath": "src"
+ }
+ },
+ {
+ "path": "../gro/src",
+ "files": 156,
+ "by_language": {
+ "svelte": 10,
+ "typescript": 146,
+ "css": 0
+ },
+ "repo": {
+ "url": "https://github.com/fuzdev/gro",
+ "slug": "fuzdev/gro",
+ "commit": "32af831867a0e73f98b4a6f2d35f435e21fea7db",
+ "subpath": "src"
+ }
+ },
+ {
+ "path": "../svelte-docinfo/src",
+ "files": 99,
+ "by_language": {
+ "svelte": 22,
+ "typescript": 76,
+ "css": 1
+ },
+ "repo": {
+ "url": "https://github.com/fuzdev/svelte-docinfo",
+ "slug": "fuzdev/svelte-docinfo",
+ "commit": "39e10bdff2ca77a82a8c017bbff004cc2f809aa0",
+ "subpath": "src"
+ }
+ },
+ {
+ "path": "../tsv.fuz.dev/src",
+ "files": 28,
+ "by_language": {
+ "svelte": 16,
+ "typescript": 11,
+ "css": 1
+ },
+ "repo": {
+ "url": "https://github.com/fuzdev/tsv.fuz.dev",
+ "slug": "fuzdev/tsv.fuz.dev",
+ "commit": "b4259d21b582908e9e68ad9c1f774b44d0315951",
+ "subpath": "src"
+ }
+ },
+ {
+ "path": "../ryanatkn.com/src",
+ "files": 52,
+ "by_language": {
+ "svelte": 31,
+ "typescript": 20,
+ "css": 1
+ },
+ "repo": {
+ "url": "https://github.com/ryanatkn/ryanatkn.com",
+ "slug": "ryanatkn/ryanatkn.com",
+ "commit": "5e53453ae56ff1139f0bf1e49f7d4ccedc4994a7",
+ "subpath": "src"
+ }
+ },
+ {
+ "path": "../webdevladder.net/src",
+ "files": 39,
+ "by_language": {
+ "svelte": 24,
+ "typescript": 14,
+ "css": 1
+ },
+ "repo": {
+ "url": "https://github.com/ryanatkn/webdevladder.net",
+ "slug": "ryanatkn/webdevladder.net",
+ "commit": "985945c986e651a6b0a5090cab1b7879d9143c41",
+ "subpath": "src"
+ }
+ },
+ {
+ "path": "benches/js/.cache/svelte_styles",
+ "files": 18,
+ "by_language": {
+ "svelte": 0,
+ "typescript": 0,
+ "css": 18
+ }
+ },
+ {
+ "path": "../kit/packages/kit/src",
+ "files": 297,
+ "by_language": {
+ "svelte": 26,
+ "typescript": 271,
+ "css": 0
+ },
+ "repo": {
+ "url": "https://github.com/sveltejs/kit",
+ "slug": "sveltejs/kit",
+ "commit": "da5b08ea7f5182f505b5d1cb9b347effbcb35231",
+ "subpath": "packages/kit/src"
+ }
+ },
+ {
+ "path": "../svelte/packages/svelte/src",
+ "files": 415,
+ "by_language": {
+ "svelte": 0,
+ "typescript": 415,
+ "css": 0
+ },
+ "repo": {
+ "url": "https://github.com/sveltejs/svelte",
+ "slug": "sveltejs/svelte",
+ "commit": "b4d1583ae20f3869a88a731d9a265c546c099f66",
+ "subpath": "packages/svelte/src"
+ }
+ },
+ {
+ "path": "../svelte.dev/apps/svelte.dev/src",
+ "files": 145,
+ "by_language": {
+ "svelte": 60,
+ "typescript": 85,
+ "css": 0
+ },
+ "repo": {
+ "url": "https://github.com/sveltejs/svelte.dev",
+ "slug": "sveltejs/svelte.dev",
+ "commit": "c21c2d0f011915b2a783c612c978a348cec98a5c",
+ "subpath": "apps/svelte.dev/src"
+ }
+ },
+ {
+ "path": "../svelte.dev/packages/repl/src",
+ "files": 53,
+ "by_language": {
+ "svelte": 17,
+ "typescript": 34,
+ "css": 2
+ },
+ "repo": {
+ "url": "https://github.com/sveltejs/svelte.dev",
+ "slug": "sveltejs/svelte.dev",
+ "commit": "c21c2d0f011915b2a783c612c978a348cec98a5c",
+ "subpath": "packages/repl/src"
+ }
+ },
+ {
+ "path": "../svelte.dev/packages/site-kit/src",
+ "files": 70,
+ "by_language": {
+ "svelte": 26,
+ "typescript": 33,
+ "css": 11
+ },
+ "repo": {
+ "url": "https://github.com/sveltejs/svelte.dev",
+ "slug": "sveltejs/svelte.dev",
+ "commit": "c21c2d0f011915b2a783c612c978a348cec98a5c",
+ "subpath": "packages/site-kit/src"
+ }
+ }
+ ],
"versions": {
"tsv": "0.1.0",
- "svelte": "5.56.1",
+ "svelte": "5.56.4",
"acorn": "8.16.0",
- "acorn_ts": "1.0.10",
- "prettier": "3.8.3",
- "prettier_svelte": "3.5.2",
- "oxc_parser": "0.134.0",
- "oxfmt": "0.53.0",
- "biome": "2.4.16"
+ "acorn_ts": "1.0.11",
+ "prettier": "3.9.0",
+ "prettier_svelte": "4.1.1",
+ "oxc_parser": "0.140.0",
+ "oxfmt": "0.59.0",
+ "biome": "2.5.4"
},
"binary_sizes": [
{
- "label": "tsv (native)",
- "bytes": 3861760,
+ "label": "tsv (ffi)",
+ "bytes": 3407520,
"kind": "native",
- "gzip_bytes": 1622771
+ "gzip_bytes": 1473224
+ },
+ {
+ "label": "tsv format (ffi)",
+ "bytes": 3143024,
+ "kind": "native",
+ "gzip_bytes": 1366105
+ },
+ {
+ "label": "tsv parse (ffi)",
+ "bytes": 1602672,
+ "kind": "native",
+ "gzip_bytes": 704951
+ },
+ {
+ "label": "tsv (napi)",
+ "bytes": 3540848,
+ "kind": "native",
+ "gzip_bytes": 1517414
},
{
"label": "tsv_format_wasm",
- "bytes": 2196292,
+ "bytes": 2261397,
"kind": "wasm",
- "gzip_bytes": 715422
+ "gzip_bytes": 794650
},
{
"label": "tsv_parse_wasm",
- "bytes": 1699625,
+ "bytes": 1057603,
"kind": "wasm",
- "gzip_bytes": 526578
+ "gzip_bytes": 389582
},
{
"label": "tsv_wasm",
- "bytes": 2888918,
+ "bytes": 2491464,
"kind": "wasm",
- "gzip_bytes": 911323
+ "gzip_bytes": 875399
},
{
"label": "biome (wasm)",
- "bytes": 34430368,
+ "bytes": 38584187,
"kind": "wasm",
- "gzip_bytes": 8187702
+ "gzip_bytes": 9281487
},
{
- "label": "oxc-parser (native)",
- "bytes": 2658008,
+ "label": "oxc-parser (napi)",
+ "bytes": 2354904,
"kind": "native",
- "gzip_bytes": 1032774
+ "gzip_bytes": 954754
},
{
- "label": "oxfmt (native)",
- "bytes": 8016464,
+ "label": "oxfmt (napi)",
+ "bytes": 8917776,
"kind": "native",
- "gzip_bytes": 3224684
+ "gzip_bytes": 3586269
},
{
"label": "oxc-parser (wasm)",
- "bytes": 1866798,
+ "bytes": 1531455,
"kind": "wasm",
- "gzip_bytes": 518682
+ "gzip_bytes": 495227
}
],
"entries": [
{
"name": "svelte/compiler",
"group": "parse/svelte",
- "mean_ns": 353647929.8666667,
- "p50_ns": 351794280,
- "p75_ns": 355710613.5,
- "p90_ns": 358757843.8,
- "p95_ns": 359832344.1,
- "p99_ns": 361455435.21999997,
- "min_ns": 349590498,
- "max_ns": 361861208,
- "std_dev_ns": 3732468.9277879535,
- "cv": 0.010554194193064213,
- "ops_per_second": 2.8276710127414653,
- "sample_size": 15,
- "files_processed": 1193,
- "files_total": 1240,
- "files_iterated": 1192
+ "mean_ns": 454203272.7,
+ "p50_ns": 452307877,
+ "p75_ns": 458399053.5,
+ "p90_ns": 464037260,
+ "p95_ns": 466877383,
+ "p99_ns": 469149481.4,
+ "min_ns": 449538372,
+ "max_ns": 469717506,
+ "std_dev_ns": 4803639.789213478,
+ "cv": 0.01057596912646261,
+ "ops_per_second": 2.201657407828713,
+ "sample_size": 10,
+ "files_processed": 763,
+ "files_total": 763,
+ "files_iterated": 763,
+ "runtime": "node"
},
{
"name": "tsv-json",
"group": "parse/svelte",
- "mean_ns": 485025992.4444444,
- "p50_ns": 485086813,
- "p75_ns": 487644066,
- "p90_ns": 491656222,
- "p95_ns": 494258617.5,
- "p99_ns": 496340533.9,
- "min_ns": 481783564,
- "max_ns": 496861013,
- "std_dev_ns": 1892163.1540198317,
- "cv": 0.0039011582543930627,
- "ops_per_second": 2.061745175676418,
- "sample_size": 9,
- "files_processed": 1202,
- "files_total": 1240,
- "files_iterated": 1192
+ "mean_ns": 215081564.7368421,
+ "p50_ns": 215189540,
+ "p75_ns": 216663830.5,
+ "p90_ns": 220507872.8,
+ "p95_ns": 221245016.95,
+ "p99_ns": 221476217.61,
+ "min_ns": 213977606,
+ "max_ns": 221507381,
+ "std_dev_ns": 948167.8303000326,
+ "cv": 0.004408410509102166,
+ "ops_per_second": 4.649398944179739,
+ "sample_size": 19,
+ "files_processed": 763,
+ "files_total": 763,
+ "files_iterated": 763,
+ "runtime": "node"
+ },
+ {
+ "name": "tsv-json-no-locations",
+ "group": "parse/svelte",
+ "mean_ns": 136455370.6875,
+ "p50_ns": 136247751,
+ "p75_ns": 138650167,
+ "p90_ns": 140117388,
+ "p95_ns": 140433077.8,
+ "p99_ns": 140673837.16,
+ "min_ns": 134355882,
+ "max_ns": 140777218,
+ "std_dev_ns": 1333831.388734515,
+ "cv": 0.009774854459845023,
+ "ops_per_second": 7.328403381719038,
+ "sample_size": 32,
+ "files_processed": 763,
+ "files_total": 763,
+ "files_iterated": 763,
+ "runtime": "node"
},
{
"name": "tsv_wasm-json",
"group": "parse/svelte",
- "mean_ns": 515988848.71428573,
- "p50_ns": 516072939.5,
- "p75_ns": 517319991,
- "p90_ns": 519264292.5,
- "p95_ns": 520772636.25,
- "p99_ns": 521979311.25,
- "min_ns": 515560589,
- "max_ns": 522280980,
- "std_dev_ns": 253147.57598596797,
- "cv": 0.0004906066800023837,
- "ops_per_second": 1.9380263788485899,
- "sample_size": 7,
- "files_processed": 1202,
- "files_total": 1240,
- "files_iterated": 1192
+ "mean_ns": 242380851.47619048,
+ "p50_ns": 241554679,
+ "p75_ns": 243217969,
+ "p90_ns": 246015818,
+ "p95_ns": 247299957,
+ "p99_ns": 247768187.4,
+ "min_ns": 239665776,
+ "max_ns": 247885245,
+ "std_dev_ns": 2512562.793305207,
+ "cv": 0.010366176940145046,
+ "ops_per_second": 4.125738456275007,
+ "sample_size": 21,
+ "files_processed": 763,
+ "files_total": 763,
+ "files_iterated": 763,
+ "runtime": "node"
+ },
+ {
+ "name": "tsv_wasm-json-no-locations",
+ "group": "parse/svelte",
+ "mean_ns": 156798038.8,
+ "p50_ns": 156579356.5,
+ "p75_ns": 157972638,
+ "p90_ns": 160523542.9,
+ "p95_ns": 161067885.6,
+ "p99_ns": 161407787.75,
+ "min_ns": 154628462,
+ "max_ns": 161516032,
+ "std_dev_ns": 1713706.0912863356,
+ "cv": 0.010929384732115256,
+ "ops_per_second": 6.377630789601431,
+ "sample_size": 30,
+ "files_processed": 763,
+ "files_total": 763,
+ "files_iterated": 763,
+ "runtime": "node"
},
{
"name": "tsv-internal",
"group": "parse/svelte",
- "mean_ns": 37041735.949579835,
- "p50_ns": 36817941,
- "p75_ns": 38031003.5,
- "p90_ns": 39745595,
- "p95_ns": 49374707.5,
- "p99_ns": 70563356.69999997,
- "min_ns": 35490755,
- "max_ns": 73560234,
- "std_dev_ns": 1041838.039960544,
- "cv": 0.02812605870790355,
- "ops_per_second": 26.996574927297463,
- "sample_size": 119,
- "files_processed": 1202,
- "files_total": 1240,
- "files_iterated": 1192
+ "mean_ns": 20890458.785714287,
+ "p50_ns": 20811285.5,
+ "p75_ns": 21412932,
+ "p90_ns": 21589212.6,
+ "p95_ns": 21645572.95,
+ "p99_ns": 21758236.37,
+ "min_ns": 20633297,
+ "max_ns": 21784005,
+ "std_dev_ns": 269196.59333167557,
+ "cv": 0.012886102507033629,
+ "ops_per_second": 47.86874286762142,
+ "sample_size": 196,
+ "files_processed": 763,
+ "files_total": 763,
+ "files_iterated": 763,
+ "runtime": "node"
},
{
"name": "tsv_wasm-internal",
"group": "parse/svelte",
- "mean_ns": 45254932.2745098,
- "p50_ns": 45241227,
- "p75_ns": 45348407.5,
- "p90_ns": 45449227,
- "p95_ns": 45624719,
- "p99_ns": 45694463.7,
- "min_ns": 45087137,
- "max_ns": 45720491,
- "std_dev_ns": 94029.59204496059,
- "cv": 0.0020777755554817943,
- "ops_per_second": 22.097038924600444,
- "sample_size": 102,
- "files_processed": 1202,
- "files_total": 1240,
- "files_iterated": 1192
+ "mean_ns": 37349926.47014926,
+ "p50_ns": 38076093,
+ "p75_ns": 41060368.5,
+ "p90_ns": 41948649.5,
+ "p95_ns": 42197316.75,
+ "p99_ns": 43256069.93999999,
+ "min_ns": 29812236,
+ "max_ns": 44793850,
+ "std_dev_ns": 4259994.387091226,
+ "cv": 0.11405629916021097,
+ "ops_per_second": 26.773814422344802,
+ "sample_size": 134,
+ "files_processed": 763,
+ "files_total": 763,
+ "files_iterated": 763,
+ "runtime": "node"
},
{
"name": "prettier",
"group": "format/svelte",
- "mean_ns": 3156053205.2,
- "p50_ns": 3153333425,
- "p75_ns": 3161183958,
- "p90_ns": 3191384359.8,
- "p95_ns": 3201451160.4,
- "p99_ns": 3209504600.88,
- "min_ns": 3116677318,
- "max_ns": 3211517961,
- "std_dev_ns": 35365115.51584766,
- "cv": 0.011205487745763958,
- "ops_per_second": 0.316851439117811,
- "sample_size": 5,
- "files_processed": 1201,
- "files_total": 1240,
- "files_iterated": 1194
+ "mean_ns": 4410834541,
+ "p50_ns": 4409030122,
+ "p75_ns": 4424887650.5,
+ "p90_ns": 4449574229,
+ "p95_ns": 4463048301.5,
+ "p99_ns": 4473827559.5,
+ "min_ns": 4398618080,
+ "max_ns": 4476522374,
+ "std_dev_ns": 12538592.212289287,
+ "cv": 0.002842680244688255,
+ "ops_per_second": 0.2267144665492906,
+ "sample_size": 6,
+ "files_processed": 763,
+ "files_total": 763,
+ "files_iterated": 763,
+ "runtime": "node"
},
{
"name": "tsv",
"group": "format/svelte",
- "mean_ns": 253589550.66666666,
- "p50_ns": 250899714,
- "p75_ns": 261502311.75,
- "p90_ns": 270307677.1,
- "p95_ns": 291692469.25,
- "p99_ns": 299630277.84999996,
- "min_ns": 247143299,
- "max_ns": 301614730,
- "std_dev_ns": 6469364.524097994,
- "cv": 0.02551116363860637,
- "ops_per_second": 3.9433801486342004,
- "sample_size": 18,
- "files_processed": 1202,
- "files_total": 1240,
- "files_iterated": 1194
+ "mean_ns": 69682693.5,
+ "p50_ns": 69152510,
+ "p75_ns": 71057711.5,
+ "p90_ns": 71481026.5,
+ "p95_ns": 71580183.45,
+ "p99_ns": 71657562.64,
+ "min_ns": 68466829,
+ "max_ns": 71705144,
+ "std_dev_ns": 1150612.102643583,
+ "cv": 0.01651216456843166,
+ "ops_per_second": 14.35076558858908,
+ "sample_size": 72,
+ "files_processed": 763,
+ "files_total": 763,
+ "files_iterated": 763,
+ "runtime": "node"
},
{
"name": "tsv_wasm",
"group": "format/svelte",
- "mean_ns": 348305291.1666667,
- "p50_ns": 348407825,
- "p75_ns": 349005457.5,
- "p90_ns": 351966927.4,
- "p95_ns": 353359477.1,
- "p99_ns": 355469057.02,
- "min_ns": 347712508,
- "max_ns": 355996452,
- "std_dev_ns": 403574.7899962474,
- "cv": 0.0011586811921359351,
- "ops_per_second": 2.8710445272032703,
- "sample_size": 12,
- "files_processed": 1202,
- "files_total": 1240,
- "files_iterated": 1194
+ "mean_ns": 126127615.175,
+ "p50_ns": 126569065,
+ "p75_ns": 128091580.75,
+ "p90_ns": 129881477.4,
+ "p95_ns": 130874369.55,
+ "p99_ns": 131097384.84,
+ "min_ns": 119589561,
+ "max_ns": 131122908,
+ "std_dev_ns": 3059562.225237535,
+ "cv": 0.024257671256151497,
+ "ops_per_second": 7.928477824721544,
+ "sample_size": 40,
+ "files_processed": 763,
+ "files_total": 763,
+ "files_iterated": 763,
+ "runtime": "node"
},
{
"name": "oxfmt",
"group": "format/svelte",
- "mean_ns": 3363123440.8,
- "p50_ns": 3355465187,
- "p75_ns": 3380464215,
- "p90_ns": 3387858512.4,
- "p95_ns": 3390323278.2,
- "p99_ns": 3392295090.84,
- "min_ns": 3336319057,
- "max_ns": 3392788044,
- "std_dev_ns": 22995300.045456335,
- "cv": 0.006837483205786329,
- "ops_per_second": 0.2973426392467253,
- "sample_size": 5,
- "files_processed": 1201,
- "files_total": 1240,
- "files_iterated": 1194
+ "mean_ns": 4349035130.714286,
+ "p50_ns": 4378245513,
+ "p75_ns": 4397739938.5,
+ "p90_ns": 4399973164.6,
+ "p95_ns": 4400683105.3,
+ "p99_ns": 4401251057.86,
+ "min_ns": 4277748939,
+ "max_ns": 4401393046,
+ "std_dev_ns": 56604561.29704797,
+ "cv": 0.013015429766774783,
+ "ops_per_second": 0.22993605936582995,
+ "sample_size": 7,
+ "files_processed": 763,
+ "files_total": 763,
+ "files_iterated": 763,
+ "runtime": "node"
},
{
"name": "biome-wasm",
"group": "format/svelte",
- "mean_ns": 651628305.75,
- "p50_ns": 651348032.5,
- "p75_ns": 652776746.25,
- "p90_ns": 655602311.7,
- "p95_ns": 657068072.85,
- "p99_ns": 658240681.77,
- "min_ns": 646287594,
- "max_ns": 658533834,
- "std_dev_ns": 3695077.8107031505,
- "cv": 0.005670529929558927,
- "ops_per_second": 1.5346171907757093,
- "sample_size": 8,
- "files_processed": 1238,
- "files_total": 1240,
- "files_iterated": 1194
+ "mean_ns": 922724214.3333334,
+ "p50_ns": 921258645,
+ "p75_ns": 925929470,
+ "p90_ns": 931597465,
+ "p95_ns": 933881452,
+ "p99_ns": 935708641.6,
+ "min_ns": 912930502,
+ "max_ns": 936165439,
+ "std_dev_ns": 8104189.841280956,
+ "cv": 0.008782894948883745,
+ "ops_per_second": 1.0837474344623093,
+ "sample_size": 6,
+ "files_processed": 763,
+ "files_total": 763,
+ "files_iterated": 763,
+ "runtime": "node"
},
{
"name": "acorn-typescript",
"group": "parse/typescript",
- "mean_ns": 2213405912.6,
- "p50_ns": 2212095774,
- "p75_ns": 2213405114,
- "p90_ns": 2218802607.8,
- "p95_ns": 2220601772.4,
- "p99_ns": 2222041104.08,
- "min_ns": 2209487030,
- "max_ns": 2222400937,
- "std_dev_ns": 5295312.414249306,
- "cv": 0.0023923819775240017,
- "ops_per_second": 0.4517924138123132,
+ "mean_ns": 3095283600.8,
+ "p50_ns": 3095095186,
+ "p75_ns": 3097767418,
+ "p90_ns": 3099323611.6,
+ "p95_ns": 3099842342.8,
+ "p99_ns": 3100257327.76,
+ "min_ns": 3091472519,
+ "max_ns": 3100361074,
+ "std_dev_ns": 3846956.6437835894,
+ "cv": 0.0012428446436343712,
+ "ops_per_second": 0.3230721733354392,
"sample_size": 5,
- "files_processed": 3693,
- "files_total": 4025,
- "files_iterated": 3603
+ "files_processed": 2434,
+ "files_total": 2437,
+ "files_iterated": 2434,
+ "runtime": "node"
},
{
"name": "tsv-json",
"group": "parse/typescript",
- "mean_ns": 2049624429.8,
- "p50_ns": 2046277896,
- "p75_ns": 2052475955,
- "p90_ns": 2062325034.8,
- "p95_ns": 2065608061.4,
- "p99_ns": 2068234482.68,
- "min_ns": 2036381168,
- "max_ns": 2068891088,
- "std_dev_ns": 12211486.98364508,
- "cv": 0.005957914438420635,
- "ops_per_second": 0.4878942627052796,
- "sample_size": 5,
- "files_processed": 3743,
- "files_total": 4025,
- "files_iterated": 3603
+ "mean_ns": 2055685813.75,
+ "p50_ns": 2054412961,
+ "p75_ns": 2062088510,
+ "p90_ns": 2064528234.2,
+ "p95_ns": 2065341475.6,
+ "p99_ns": 2065992068.72,
+ "min_ns": 2052635587,
+ "max_ns": 2066154717,
+ "std_dev_ns": 4329871.531821806,
+ "cv": 0.0021062905152432884,
+ "ops_per_second": 0.4864556603500567,
+ "sample_size": 4,
+ "files_processed": 2437,
+ "files_total": 2437,
+ "files_iterated": 2434,
+ "runtime": "node"
+ },
+ {
+ "name": "tsv-json-no-locations",
+ "group": "parse/typescript",
+ "mean_ns": 998270334.8333334,
+ "p50_ns": 998682441,
+ "p75_ns": 999030412,
+ "p90_ns": 999361650.5,
+ "p95_ns": 999480538.25,
+ "p99_ns": 999575648.45,
+ "min_ns": 996556819,
+ "max_ns": 999599426,
+ "std_dev_ns": 1220872.2439011242,
+ "cv": 0.0012229876029572245,
+ "ops_per_second": 1.0017326620919327,
+ "sample_size": 6,
+ "files_processed": 2437,
+ "files_total": 2437,
+ "files_iterated": 2434,
+ "runtime": "node"
},
{
"name": "tsv_wasm-json",
"group": "parse/typescript",
- "mean_ns": 2223665077.4,
- "p50_ns": 2223809451,
- "p75_ns": 2224511693,
- "p90_ns": 2224643608.4,
- "p95_ns": 2224687580.2,
- "p99_ns": 2224722757.64,
- "min_ns": 2222334633,
- "max_ns": 2224731552,
- "std_dev_ns": 1021470.6667904371,
- "cv": 0.0004593635422762416,
- "ops_per_second": 0.44970801141026184,
+ "mean_ns": 2190385709.6,
+ "p50_ns": 2189048667,
+ "p75_ns": 2195488548,
+ "p90_ns": 2195623038.6,
+ "p95_ns": 2195667868.8,
+ "p99_ns": 2195703732.96,
+ "min_ns": 2183175920,
+ "max_ns": 2195712699,
+ "std_dev_ns": 5285125.287642885,
+ "cv": 0.0024128742551959193,
+ "ops_per_second": 0.4565405972186589,
+ "sample_size": 5,
+ "files_processed": 2437,
+ "files_total": 2437,
+ "files_iterated": 2434,
+ "runtime": "node"
+ },
+ {
+ "name": "tsv_wasm-json-no-locations",
+ "group": "parse/typescript",
+ "mean_ns": 1098155893.4,
+ "p50_ns": 1098004307,
+ "p75_ns": 1098812393,
+ "p90_ns": 1099122653,
+ "p95_ns": 1099226073,
+ "p99_ns": 1099308809,
+ "min_ns": 1096818332,
+ "max_ns": 1099329493,
+ "std_dev_ns": 966517.5293957684,
+ "cv": 0.0008801277989806472,
+ "ops_per_second": 0.9106175234409573,
"sample_size": 5,
- "files_processed": 3743,
- "files_total": 4025,
- "files_iterated": 3603
+ "files_processed": 2437,
+ "files_total": 2437,
+ "files_iterated": 2434,
+ "runtime": "node"
},
{
"name": "tsv-internal",
"group": "parse/typescript",
- "mean_ns": 236250428.1875,
- "p50_ns": 235985787,
- "p75_ns": 239292380,
- "p90_ns": 262673433,
- "p95_ns": 263116119,
- "p99_ns": 263948169.4,
- "min_ns": 234967322,
- "max_ns": 264156182,
- "std_dev_ns": 1336552.468610353,
- "cv": 0.005657354692917632,
- "ops_per_second": 4.232796561140413,
- "sample_size": 16,
- "files_processed": 3743,
- "files_total": 4025,
- "files_iterated": 3603
+ "mean_ns": 137932086.30769232,
+ "p50_ns": 138073024.5,
+ "p75_ns": 140428980,
+ "p90_ns": 141767891.5,
+ "p95_ns": 142291971.75,
+ "p99_ns": 142733225.2,
+ "min_ns": 136940380,
+ "max_ns": 142926911,
+ "std_dev_ns": 546559.9950337947,
+ "cv": 0.003962529746809997,
+ "ops_per_second": 7.24994471387352,
+ "sample_size": 26,
+ "files_processed": 2437,
+ "files_total": 2437,
+ "files_iterated": 2434,
+ "runtime": "node"
},
{
"name": "tsv_wasm-internal",
"group": "parse/typescript",
- "mean_ns": 292595892.125,
- "p50_ns": 292647736.5,
- "p75_ns": 292773388.75,
- "p90_ns": 293666035.6,
- "p95_ns": 294709175.9,
- "p99_ns": 294963799.18,
- "min_ns": 292266142,
- "max_ns": 295027455,
- "std_dev_ns": 262236.36256371846,
- "cv": 0.0008962407525929597,
- "ops_per_second": 3.4176829781765687,
- "sample_size": 16,
- "files_processed": 3743,
- "files_total": 4025,
- "files_iterated": 3603
+ "mean_ns": 183489849.5909091,
+ "p50_ns": 183663283.5,
+ "p75_ns": 184686170.25,
+ "p90_ns": 187722911.1,
+ "p95_ns": 187769910.1,
+ "p99_ns": 188076536.44,
+ "min_ns": 182091250,
+ "max_ns": 188185543,
+ "std_dev_ns": 725759.3611214367,
+ "cv": 0.003955310676527984,
+ "ops_per_second": 5.449892744636837,
+ "sample_size": 22,
+ "files_processed": 2437,
+ "files_total": 2437,
+ "files_iterated": 2434,
+ "runtime": "node"
},
{
"name": "oxc-parser",
"group": "parse/typescript",
- "mean_ns": 864942821.5,
- "p50_ns": 863969686.5,
- "p75_ns": 878826551,
- "p90_ns": 884913960,
- "p95_ns": 887896262.5,
- "p99_ns": 890282104.5,
- "min_ns": 845083325,
- "max_ns": 890878565,
- "std_dev_ns": 20069394.017162047,
- "cv": 0.023203145362091483,
- "ops_per_second": 1.1561457880716106,
- "sample_size": 6,
- "files_processed": 3833,
- "files_total": 4025,
- "files_iterated": 3603
+ "mean_ns": 1328575979,
+ "p50_ns": 1327625911,
+ "p75_ns": 1336923813,
+ "p90_ns": 1338162009.6,
+ "p95_ns": 1338574741.8,
+ "p99_ns": 1338904927.56,
+ "min_ns": 1318900753,
+ "max_ns": 1338987474,
+ "std_dev_ns": 9202726.679679045,
+ "cv": 0.006926759797814352,
+ "ops_per_second": 0.7526855940543842,
+ "sample_size": 5,
+ "files_processed": 2435,
+ "files_total": 2437,
+ "files_iterated": 2434,
+ "runtime": "node"
},
{
"name": "oxc-parser-wasm",
"group": "parse/typescript",
- "mean_ns": 976785685.3333334,
- "p50_ns": 975594108.5,
- "p75_ns": 979113434.75,
- "p90_ns": 982226200,
- "p95_ns": 983362576,
- "p99_ns": 984271676.8,
- "min_ns": 971966753,
- "max_ns": 984498952,
- "std_dev_ns": 4714293.024074751,
- "cv": 0.004826333037902754,
- "ops_per_second": 1.023766026688592,
- "sample_size": 6,
- "files_processed": 4025,
- "files_total": 4025,
- "files_iterated": 3603
+ "mean_ns": 1358577531.4,
+ "p50_ns": 1357675431,
+ "p75_ns": 1365281550,
+ "p90_ns": 1365570397.8,
+ "p95_ns": 1365666680.4,
+ "p99_ns": 1365743706.48,
+ "min_ns": 1349320017,
+ "max_ns": 1365762963,
+ "std_dev_ns": 7017857.984748716,
+ "cv": 0.005165592557324929,
+ "ops_per_second": 0.736063991113934,
+ "sample_size": 5,
+ "files_processed": 2435,
+ "files_total": 2437,
+ "files_iterated": 2434,
+ "runtime": "node"
},
{
"name": "prettier",
"group": "format/typescript",
- "mean_ns": 8773944013.666666,
- "p50_ns": 8784008051,
- "p75_ns": 8807150369,
- "p90_ns": 8902301987.4,
- "p95_ns": 8961594658.2,
- "p99_ns": 9009028794.84,
- "min_ns": 8712430383,
- "max_ns": 9020887329,
- "std_dev_ns": 36684367.115987,
- "cv": 0.004181057806939032,
- "ops_per_second": 0.1139738296075696,
- "sample_size": 6,
- "files_processed": 3855,
- "files_total": 4025,
- "files_iterated": 3701
+ "mean_ns": 13390193976.8,
+ "p50_ns": 13397850712,
+ "p75_ns": 13480628592.5,
+ "p90_ns": 13560690379.6,
+ "p95_ns": 13575781262.8,
+ "p99_ns": 13587853969.36,
+ "min_ns": 13360491961,
+ "max_ns": 13590872146,
+ "std_dev_ns": 21843031.171797708,
+ "cv": 0.0016312707052372197,
+ "ops_per_second": 0.07468151706634057,
+ "sample_size": 5,
+ "files_processed": 2437,
+ "files_total": 2437,
+ "files_iterated": 2435,
+ "runtime": "node"
},
{
"name": "tsv",
"group": "format/typescript",
- "mean_ns": 854559033.5,
- "p50_ns": 856988296.5,
- "p75_ns": 860171191.5,
- "p90_ns": 861313209,
- "p95_ns": 861398115.5,
- "p99_ns": 861466040.7,
- "min_ns": 838541329,
- "max_ns": 861483022,
- "std_dev_ns": 8547810.99796243,
- "cv": 0.01000259860685497,
- "ops_per_second": 1.1701941712608437,
- "sample_size": 6,
- "files_processed": 3743,
- "files_total": 4025,
- "files_iterated": 3701
+ "mean_ns": 510281312,
+ "p50_ns": 510621353.5,
+ "p75_ns": 511585299,
+ "p90_ns": 518547738.5,
+ "p95_ns": 523173736.25,
+ "p99_ns": 526874534.45,
+ "min_ns": 509042507,
+ "max_ns": 527799734,
+ "std_dev_ns": 953832.181018533,
+ "cv": 0.0018692281268935299,
+ "ops_per_second": 1.9597033567241435,
+ "sample_size": 8,
+ "files_processed": 2437,
+ "files_total": 2437,
+ "files_iterated": 2435,
+ "runtime": "node"
},
{
"name": "tsv_wasm",
"group": "format/typescript",
- "mean_ns": 1145280037.8,
- "p50_ns": 1144938011,
- "p75_ns": 1145815746,
- "p90_ns": 1146670300.2,
- "p95_ns": 1146955151.6,
- "p99_ns": 1147183032.72,
- "min_ns": 1144152898,
- "max_ns": 1147240003,
- "std_dev_ns": 1281560.5264546424,
- "cv": 0.0011189931581418527,
- "ops_per_second": 0.8731488954622204,
- "sample_size": 5,
- "files_processed": 3743,
- "files_total": 4025,
- "files_iterated": 3701
+ "mean_ns": 712429650,
+ "p50_ns": 712594376,
+ "p75_ns": 713355899.5,
+ "p90_ns": 721656199,
+ "p95_ns": 727815761.5,
+ "p99_ns": 732743411.5,
+ "min_ns": 710764653,
+ "max_ns": 733975324,
+ "std_dev_ns": 959542.0750962407,
+ "cv": 0.001346858704008516,
+ "ops_per_second": 1.4036473636379396,
+ "sample_size": 6,
+ "files_processed": 2437,
+ "files_total": 2437,
+ "files_iterated": 2435,
+ "runtime": "node"
},
{
"name": "oxfmt",
"group": "format/typescript",
- "mean_ns": 802078434.8571428,
- "p50_ns": 804423326,
- "p75_ns": 806569662,
- "p90_ns": 809150059.2,
- "p95_ns": 809881109.1,
- "p99_ns": 810465949.02,
- "min_ns": 792012009,
- "max_ns": 810612159,
- "std_dev_ns": 7054022.950975906,
- "cv": 0.008794679727590843,
- "ops_per_second": 1.246760860960074,
- "sample_size": 7,
- "files_processed": 3836,
- "files_total": 4025,
- "files_iterated": 3701
+ "mean_ns": 847114076.6666666,
+ "p50_ns": 847057173.5,
+ "p75_ns": 849430498,
+ "p90_ns": 850355305.5,
+ "p95_ns": 850425492.25,
+ "p99_ns": 850481641.65,
+ "min_ns": 842300354,
+ "max_ns": 850495679,
+ "std_dev_ns": 3054424.4941014773,
+ "cv": 0.003605682609029966,
+ "ops_per_second": 1.1804785536499742,
+ "sample_size": 6,
+ "files_processed": 2435,
+ "files_total": 2437,
+ "files_iterated": 2435,
+ "runtime": "node"
},
{
"name": "biome-wasm",
"group": "format/typescript",
- "mean_ns": 3651240436.25,
- "p50_ns": 3651115766,
- "p75_ns": 3652416674,
- "p90_ns": 3654275795,
- "p95_ns": 3654895502,
- "p99_ns": 3655391267.6,
- "min_ns": 3650446557,
- "max_ns": 3655515209,
- "std_dev_ns": 835808.0747653235,
- "cv": 0.00022891071934548872,
- "ops_per_second": 0.2738795260021409,
- "sample_size": 4,
- "files_processed": 4025,
- "files_total": 4025,
- "files_iterated": 3701
+ "mean_ns": 4640659870.333333,
+ "p50_ns": 4664197859,
+ "p75_ns": 6680537998,
+ "p90_ns": 9261928652.8,
+ "p95_ns": 10122392204.4,
+ "p99_ns": 10810763045.68,
+ "min_ns": 4598996897,
+ "max_ns": 10982855756,
+ "std_dev_ns": 36182560.209395595,
+ "cv": 0.0077968567445984025,
+ "ops_per_second": 0.21548659629049075,
+ "sample_size": 3,
+ "files_processed": 2437,
+ "files_total": 2437,
+ "files_iterated": 2435,
+ "runtime": "node"
},
{
"name": "svelte/compiler",
"group": "parse/css",
- "mean_ns": 19403785.678294573,
- "p50_ns": 19376518.5,
- "p75_ns": 19515459.5,
- "p90_ns": 19626101.3,
- "p95_ns": 19749809.1,
- "p99_ns": 19835973.34,
- "min_ns": 19083462,
- "max_ns": 19889772,
- "std_dev_ns": 170231.79292084067,
- "cv": 0.008773122716525623,
- "ops_per_second": 51.53633505232013,
- "sample_size": 258,
- "files_processed": 163,
- "files_total": 193,
- "files_iterated": 159
+ "mean_ns": 9038889.063829787,
+ "p50_ns": 8961776.5,
+ "p75_ns": 9307395,
+ "p90_ns": 9753521.9,
+ "p95_ns": 10124050.150000002,
+ "p99_ns": 13612548.960000051,
+ "min_ns": 8653794,
+ "max_ns": 18954687,
+ "std_dev_ns": 348025.76061064505,
+ "cv": 0.03850315654423865,
+ "ops_per_second": 110.63306485324856,
+ "sample_size": 517,
+ "files_processed": 49,
+ "files_total": 49,
+ "files_iterated": 49,
+ "runtime": "node"
},
{
"name": "tsv-json",
"group": "parse/css",
- "mean_ns": 176879819.27586207,
- "p50_ns": 176417640,
- "p75_ns": 178453737,
- "p90_ns": 179335842.4,
- "p95_ns": 179414368,
- "p99_ns": 179869721.35999998,
- "min_ns": 173847338,
- "max_ns": 180029857,
- "std_dev_ns": 1802916.9896241936,
- "cv": 0.010192892535763852,
- "ops_per_second": 5.653556206094932,
- "sample_size": 29,
- "files_processed": 165,
- "files_total": 193,
- "files_iterated": 159
+ "mean_ns": 17398039.684410647,
+ "p50_ns": 17328426,
+ "p75_ns": 17832813,
+ "p90_ns": 17992721.2,
+ "p95_ns": 19576760.4,
+ "p99_ns": 20362145.840000004,
+ "min_ns": 16591514,
+ "max_ns": 20753913,
+ "std_dev_ns": 355601.88164198975,
+ "cv": 0.020439192466069817,
+ "ops_per_second": 57.47773991434453,
+ "sample_size": 263,
+ "files_processed": 49,
+ "files_total": 49,
+ "files_iterated": 49,
+ "runtime": "node"
},
{
"name": "tsv_wasm-json",
"group": "parse/css",
- "mean_ns": 223033824.05555555,
- "p50_ns": 222987561,
- "p75_ns": 224042748.5,
- "p90_ns": 226002163.2,
- "p95_ns": 226359314,
- "p99_ns": 226689078.04,
- "min_ns": 222564338,
- "max_ns": 226779975,
- "std_dev_ns": 476931.02029914665,
- "cv": 0.002138379783060832,
- "ops_per_second": 4.483624868266213,
- "sample_size": 18,
- "files_processed": 165,
- "files_total": 193,
- "files_iterated": 159
+ "mean_ns": 18287059.184549358,
+ "p50_ns": 18176678,
+ "p75_ns": 18667889,
+ "p90_ns": 19460618.2,
+ "p95_ns": 20185583.2,
+ "p99_ns": 23133210.919999998,
+ "min_ns": 17916961,
+ "max_ns": 28337630,
+ "std_dev_ns": 319973.5214247198,
+ "cv": 0.017497265043854824,
+ "ops_per_second": 54.68347807639267,
+ "sample_size": 233,
+ "files_processed": 49,
+ "files_total": 49,
+ "files_iterated": 49,
+ "runtime": "node"
},
{
"name": "tsv-internal",
"group": "parse/css",
- "mean_ns": 23123759.831578948,
- "p50_ns": 23081315.5,
- "p75_ns": 23581269.25,
- "p90_ns": 25285569.600000005,
- "p95_ns": 26399632.149999995,
- "p99_ns": 34455670.899999976,
- "min_ns": 22528124,
- "max_ns": 43861736,
- "std_dev_ns": 454153.9601688399,
- "cv": 0.019640143448844544,
- "ops_per_second": 43.24556245539061,
- "sample_size": 190,
- "files_processed": 165,
- "files_total": 193,
- "files_iterated": 159
+ "mean_ns": 3426007.1822466613,
+ "p50_ns": 3420016,
+ "p75_ns": 3464659.25,
+ "p90_ns": 3531872.4000000004,
+ "p95_ns": 3565861.45,
+ "p99_ns": 3719174.36,
+ "min_ns": 3366475,
+ "max_ns": 5318998,
+ "std_dev_ns": 30650.87400915242,
+ "cv": 0.008946529408339593,
+ "ops_per_second": 291.8849689463387,
+ "sample_size": 1273,
+ "files_processed": 49,
+ "files_total": 49,
+ "files_iterated": 49,
+ "runtime": "node"
},
{
"name": "tsv_wasm-internal",
"group": "parse/css",
- "mean_ns": 32072885.51879699,
- "p50_ns": 32066954.5,
- "p75_ns": 32148053.5,
- "p90_ns": 32335215.5,
- "p95_ns": 32349665.75,
- "p99_ns": 32378088.75,
- "min_ns": 31983936,
- "max_ns": 32565198,
- "std_dev_ns": 53586.24561517934,
- "cv": 0.0016707647206789045,
- "ops_per_second": 31.178984485631293,
- "sample_size": 133,
- "files_processed": 165,
- "files_total": 193,
- "files_iterated": 159
+ "mean_ns": 4903393.332503113,
+ "p50_ns": 4899160,
+ "p75_ns": 4955431.5,
+ "p90_ns": 5024022.6,
+ "p95_ns": 5053242.6,
+ "p99_ns": 5101561.9,
+ "min_ns": 4853645,
+ "max_ns": 5268624,
+ "std_dev_ns": 24113.319974338137,
+ "cv": 0.004917680132755866,
+ "ops_per_second": 203.9404004919006,
+ "sample_size": 803,
+ "files_processed": 49,
+ "files_total": 49,
+ "files_iterated": 49,
+ "runtime": "node"
},
{
"name": "prettier",
"group": "format/css",
- "mean_ns": 731055145.8571428,
- "p50_ns": 726866934,
- "p75_ns": 737411474,
- "p90_ns": 739494874,
- "p95_ns": 740913023.5,
- "p99_ns": 742047543.1,
- "min_ns": 723869573,
- "max_ns": 742331173,
- "std_dev_ns": 7724050.9095097985,
- "cv": 0.010565620053810789,
- "ops_per_second": 1.3678858642428766,
- "sample_size": 7,
- "files_processed": 192,
- "files_total": 193,
- "files_iterated": 165
+ "mean_ns": 551732011.6,
+ "p50_ns": 548811461,
+ "p75_ns": 561576720.25,
+ "p90_ns": 564489059.2,
+ "p95_ns": 565986579.1,
+ "p99_ns": 567184595.02,
+ "min_ns": 539045160,
+ "max_ns": 567484099,
+ "std_dev_ns": 10779738.139714208,
+ "cv": 0.019537996550994773,
+ "ops_per_second": 1.8124741341363197,
+ "sample_size": 10,
+ "files_processed": 49,
+ "files_total": 49,
+ "files_iterated": 49,
+ "runtime": "node"
},
{
"name": "tsv",
"group": "format/css",
- "mean_ns": 42276700.15044248,
- "p50_ns": 42363994,
- "p75_ns": 42798711,
- "p90_ns": 43541791.3,
- "p95_ns": 44634919.449999996,
- "p99_ns": 50024498.75,
- "min_ns": 40686670,
- "max_ns": 51286794,
- "std_dev_ns": 850647.2521532943,
- "cv": 0.020120947215043962,
- "ops_per_second": 23.65369095604624,
- "sample_size": 113,
- "files_processed": 165,
- "files_total": 193,
- "files_iterated": 165
+ "mean_ns": 7032221.678899082,
+ "p50_ns": 7009244,
+ "p75_ns": 7098315.5,
+ "p90_ns": 7206890.5,
+ "p95_ns": 7244467.45,
+ "p99_ns": 7291832.67,
+ "min_ns": 6934839,
+ "max_ns": 10835306,
+ "std_dev_ns": 72742.76588589282,
+ "cv": 0.010344208303922657,
+ "ops_per_second": 142.20257063292027,
+ "sample_size": 654,
+ "files_processed": 49,
+ "files_total": 49,
+ "files_iterated": 49,
+ "runtime": "node"
},
{
"name": "tsv_wasm",
"group": "format/css",
- "mean_ns": 56842907.02898551,
- "p50_ns": 56836126.5,
- "p75_ns": 57051209.25,
- "p90_ns": 57277215.5,
- "p95_ns": 57411214.85,
- "p99_ns": 57614455.839999996,
- "min_ns": 56701796,
- "max_ns": 57937459,
- "std_dev_ns": 94515.15490540798,
- "cv": 0.0016627431608522859,
- "ops_per_second": 17.59234445011894,
- "sample_size": 69,
- "files_processed": 165,
- "files_total": 193,
- "files_iterated": 165
+ "mean_ns": 10158745.69358178,
+ "p50_ns": 10110156,
+ "p75_ns": 10257106.5,
+ "p90_ns": 10362890,
+ "p95_ns": 10401973.5,
+ "p99_ns": 10501883.000000002,
+ "min_ns": 9975082,
+ "max_ns": 13912078,
+ "std_dev_ns": 120001.65250816409,
+ "cv": 0.011812644604735035,
+ "ops_per_second": 98.43734946842822,
+ "sample_size": 483,
+ "files_processed": 49,
+ "files_total": 49,
+ "files_iterated": 49,
+ "runtime": "node"
},
{
"name": "oxfmt",
"group": "format/css",
- "mean_ns": 759715951,
- "p50_ns": 758504294,
- "p75_ns": 764356670.5,
- "p90_ns": 792120220.8000001,
- "p95_ns": 810371960.4,
- "p99_ns": 824973352.0799999,
- "min_ns": 755827467,
- "max_ns": 828623700,
- "std_dev_ns": 4300372.980217599,
- "cv": 0.005660501105126328,
- "ops_per_second": 1.3162814321375227,
- "sample_size": 6,
- "files_processed": 192,
- "files_total": 193,
- "files_iterated": 165
+ "mean_ns": 18091033.845588237,
+ "p50_ns": 18067378,
+ "p75_ns": 18433522.5,
+ "p90_ns": 18979253.5,
+ "p95_ns": 19192316.5,
+ "p99_ns": 20099620.25,
+ "min_ns": 16502367,
+ "max_ns": 20904626,
+ "std_dev_ns": 587884.7092626577,
+ "cv": 0.03249591561656505,
+ "ops_per_second": 55.27600072694931,
+ "sample_size": 272,
+ "files_processed": 49,
+ "files_total": 49,
+ "files_iterated": 49,
+ "runtime": "node"
},
{
"name": "biome-wasm",
"group": "format/css",
- "mean_ns": 207351523.875,
- "p50_ns": 207159397,
- "p75_ns": 208264123,
- "p90_ns": 208754043.4,
- "p95_ns": 209395575,
- "p99_ns": 209795343.92,
- "min_ns": 205889964,
- "max_ns": 209888054,
- "std_dev_ns": 897025.2129294197,
- "cv": 0.004326108610950857,
- "ops_per_second": 4.822727999832984,
- "sample_size": 24,
- "files_processed": 193,
- "files_total": 193,
- "files_iterated": 165
+ "mean_ns": 126426328.38461539,
+ "p50_ns": 126839392,
+ "p75_ns": 138513503.5,
+ "p90_ns": 193632761,
+ "p95_ns": 202020801.25,
+ "p99_ns": 208481402.25,
+ "min_ns": 124102387,
+ "max_ns": 209127749,
+ "std_dev_ns": 1965652.0770388045,
+ "cv": 0.0155478063956653,
+ "ops_per_second": 7.9097448512290125,
+ "sample_size": 26,
+ "files_processed": 49,
+ "files_total": 49,
+ "files_iterated": 49,
+ "runtime": "node"
}
],
- "suppressed_noise": {
- "oxfmt::textToDoc": 1,
- "panicked at crates/biome_rowan": 1
- }
+ "suppressed_noise": {},
+ "variant_parity": []
}
diff --git a/src/routes/docs/benchmarks/benchmarks_cli.ts b/src/routes/docs/benchmarks/benchmarks_cli.ts
new file mode 100644
index 0000000..25f7834
--- /dev/null
+++ b/src/routes/docs/benchmarks/benchmarks_cli.ts
@@ -0,0 +1,84 @@
+// A snapshot of the independent end-to-end CLI benchmark — a fork of Oxc's official
+// `bench-formatter` that adds tsv on its `tsv` branch
+// (https://github.com/ryanatkn/oxc-bench-formatter, forked from
+// https://github.com/oxc-project/bench-formatter — the fork's `main` tracks
+// upstream and carries neither tsv nor this analysis, so links target `tsv`).
+// Unlike the
+// in-process, single-threaded numbers elsewhere on this page, it measures the
+// WHOLE CLI: process spawn, file discovery, I/O, and each tool's default
+// multi-file parallelism. tsv, oxfmt, and biome parallelize across files while
+// prettier is effectively serial, so the wall-clock ratios scale with core count
+// and are machine-dependent — the parallelism-neutral view is CPU work (hyperfine
+// `User` time). tsv runs only in the two JSX-free scenarios (it has no JSX/TSX
+// parser). Hand-copied from the linked report; regenerate there with
+// `pnpm run update-readme`, then update the numbers below.
+
+export const BENCHMARKS_CLI_SOURCE_URL = 'https://github.com/ryanatkn/oxc-bench-formatter';
+export const BENCHMARKS_CLI_UPSTREAM_URL = 'https://github.com/oxc-project/bench-formatter';
+
+export interface CliFormatterResult {
+ /** Display label; `tsv` is the reference row every ratio is computed against. */
+ label: string;
+ /** hyperfine wall-clock mean, in milliseconds — what you experience typing the command. */
+ wall_ms: number;
+ /** hyperfine `User` time (total CPU across all threads), in ms — the parallelism-neutral view. */
+ cpu_ms: number;
+ /** Peak resident set size (RSS), in megabytes. */
+ memory_mb: number;
+}
+
+export interface CliScenario {
+ key: string;
+ heading: string;
+ /** One-line description of the corpus and what makes the comparison fair. */
+ description: string;
+ /** Whether every formatter is effectively single-threaded here (one input file). */
+ single_threaded: boolean;
+ /** Results as measured; the component sorts and computes tsv-relative ratios. */
+ results: Array;
+}
+
+export interface BenchmarksCliReport {
+ source_url: string;
+ upstream_url: string;
+ machine: string;
+ versions: {prettier: string; biome: string; oxfmt: string; tsv: string};
+ scenarios: Array;
+}
+
+export const benchmarks_cli: BenchmarksCliReport = {
+ source_url: BENCHMARKS_CLI_SOURCE_URL,
+ upstream_url: BENCHMARKS_CLI_UPSTREAM_URL,
+ machine: 'AMD Ryzen 5 PRO 7530U · 12 threads · linux x64',
+ versions: {prettier: '3.9.1', biome: '2.5.1', oxfmt: '0.59.0', tsv: '0.2.0'},
+ scenarios: [
+ {
+ key: 'ts-repo',
+ heading: 'TypeScript repo (Outline, non-JSX subset)',
+ description:
+ "Outline's .ts/.js/.mjs files — the common set every formatter supports, each scoped to it, with a preflight parse check confirming none reject anything. The fair way to put tsv on a real multi-file repo.",
+ single_threaded: false,
+ results: [
+ {label: 'tsv', wall_ms: 120.3, cpu_ms: 854.8, memory_mb: 45.2},
+ {label: 'oxfmt', wall_ms: 379.8, cpu_ms: 1826.7, memory_mb: 215.6},
+ {label: 'biome', wall_ms: 784.3, cpu_ms: 7285.0, memory_mb: 135.7},
+ {label: 'prettier + oxc-parser', wall_ms: 8979, cpu_ms: 11237, memory_mb: 331.4},
+ {label: 'prettier', wall_ms: 10911, cpu_ms: 17611, memory_mb: 447.5},
+ ],
+ },
+ {
+ key: 'large-single-file',
+ heading: 'Large single file (TypeScript compiler parser.ts, ~540KB)',
+ description:
+ 'One ~13.7K-line .ts file. With a single input every formatter is effectively single-threaded, so wall-clock is close to an engine comparison here — except oxfmt, which spins up a worker pool it cannot use, inflating its CPU time.',
+ single_threaded: true,
+ results: [
+ {label: 'tsv', wall_ms: 28.8, cpu_ms: 15.2, memory_mb: 23.1},
+ {label: 'biome', wall_ms: 138.8, cpu_ms: 107.3, memory_mb: 102.6},
+ {label: 'oxfmt', wall_ms: 236.9, cpu_ms: 456.8, memory_mb: 118.0},
+ {label: 'prettier + oxc-parser', wall_ms: 503.4, cpu_ms: 751.3, memory_mb: 184.4},
+ {label: 'prettier', wall_ms: 1597, cpu_ms: 1583, memory_mb: 303.2},
+ ],
+ },
+ ],
+};
diff --git a/src/routes/docs/benchmarks/benchmarks_conformance.json b/src/routes/docs/benchmarks/benchmarks_conformance.json
new file mode 100644
index 0000000..f95e3ca
--- /dev/null
+++ b/src/routes/docs/benchmarks/benchmarks_conformance.json
@@ -0,0 +1,643 @@
+{
+ "version": 7,
+ "runtime": "node",
+ "corpus_kind": "conformance",
+ "timestamp": "2026-07-16T11:52:16.141Z",
+ "git_commit": "135b7b93",
+ "machine": {
+ "cpu_model": "AMD Ryzen 5 PRO 7530U with Radeon Graphics",
+ "os": "linux",
+ "arch": "x86_64",
+ "runtime_version": "24.14.1"
+ },
+ "corpus": {
+ "svelte": 4539,
+ "typescript": 44224,
+ "css": 22641
+ },
+ "corpus_sources": [
+ {
+ "path": "../prettier-plugin-svelte/test",
+ "files": 318,
+ "by_language": {
+ "svelte": 318,
+ "typescript": 0,
+ "css": 0
+ },
+ "repo": {
+ "url": "https://github.com/sveltejs/prettier-plugin-svelte",
+ "slug": "sveltejs/prettier-plugin-svelte",
+ "commit": "7809486a9716faa2234c8a45d88b601034de52d8",
+ "subpath": "test"
+ }
+ },
+ {
+ "path": "../prettier/tests/format/typescript",
+ "files": 793,
+ "by_language": {
+ "svelte": 0,
+ "typescript": 793,
+ "css": 0
+ },
+ "repo": {
+ "url": "https://github.com/prettier/prettier",
+ "slug": "prettier/prettier",
+ "commit": "1dcd0b05d026356cce5bf538c6d425955b29903b",
+ "subpath": "tests/format/typescript"
+ }
+ },
+ {
+ "path": "../prettier/tests/format/js",
+ "files": 1103,
+ "by_language": {
+ "svelte": 0,
+ "typescript": 1103,
+ "css": 0
+ },
+ "repo": {
+ "url": "https://github.com/prettier/prettier",
+ "slug": "prettier/prettier",
+ "commit": "1dcd0b05d026356cce5bf538c6d425955b29903b",
+ "subpath": "tests/format/js"
+ }
+ },
+ {
+ "path": "../prettier/tests/format/css",
+ "files": 228,
+ "by_language": {
+ "svelte": 0,
+ "typescript": 78,
+ "css": 150
+ },
+ "repo": {
+ "url": "https://github.com/prettier/prettier",
+ "slug": "prettier/prettier",
+ "commit": "1dcd0b05d026356cce5bf538c6d425955b29903b",
+ "subpath": "tests/format/css"
+ }
+ },
+ {
+ "path": "../prettier/tests/format/html",
+ "files": 84,
+ "by_language": {
+ "svelte": 84,
+ "typescript": 0,
+ "css": 0
+ },
+ "repo": {
+ "url": "https://github.com/prettier/prettier",
+ "slug": "prettier/prettier",
+ "commit": "1dcd0b05d026356cce5bf538c6d425955b29903b",
+ "subpath": "tests/format/html"
+ }
+ },
+ {
+ "path": "../svelte/packages/svelte/tests",
+ "files": 4455,
+ "by_language": {
+ "svelte": 4137,
+ "typescript": 137,
+ "css": 181
+ },
+ "repo": {
+ "url": "https://github.com/sveltejs/svelte",
+ "slug": "sveltejs/svelte",
+ "commit": "b4d1583ae20f3869a88a731d9a265c546c099f66",
+ "subpath": "packages/svelte/tests"
+ }
+ },
+ {
+ "path": "benches/js/.cache/wpt_css",
+ "files": 22310,
+ "by_language": {
+ "svelte": 0,
+ "typescript": 0,
+ "css": 22310
+ },
+ "repo": {
+ "url": "https://github.com/web-platform-tests/wpt",
+ "slug": "web-platform-tests/wpt",
+ "commit": "",
+ "subpath": ""
+ }
+ },
+ {
+ "path": "benches/js/.cache/test262_files.json",
+ "files": 42113,
+ "by_language": {
+ "svelte": 0,
+ "typescript": 42113,
+ "css": 0
+ },
+ "repo": {
+ "url": "https://github.com/tc39/test262",
+ "slug": "tc39/test262",
+ "commit": "",
+ "subpath": ""
+ }
+ }
+ ],
+ "versions": {
+ "tsv": "0.1.0",
+ "svelte": "5.56.4",
+ "acorn": "8.16.0",
+ "acorn_ts": "1.0.11",
+ "prettier": "3.9.0",
+ "prettier_svelte": "4.1.1",
+ "oxc_parser": "0.140.0",
+ "oxfmt": "0.59.0",
+ "biome": "2.5.4"
+ },
+ "binary_sizes": [
+ {
+ "label": "tsv (ffi)",
+ "bytes": 3407520,
+ "kind": "native",
+ "gzip_bytes": 1473224
+ },
+ {
+ "label": "tsv format (ffi)",
+ "bytes": 3143024,
+ "kind": "native",
+ "gzip_bytes": 1366105
+ },
+ {
+ "label": "tsv parse (ffi)",
+ "bytes": 1602672,
+ "kind": "native",
+ "gzip_bytes": 704951
+ },
+ {
+ "label": "tsv (napi)",
+ "bytes": 3540848,
+ "kind": "native",
+ "gzip_bytes": 1517414
+ },
+ {
+ "label": "tsv_format_wasm",
+ "bytes": 2261397,
+ "kind": "wasm",
+ "gzip_bytes": 794650
+ },
+ {
+ "label": "tsv_parse_wasm",
+ "bytes": 1057603,
+ "kind": "wasm",
+ "gzip_bytes": 389582
+ },
+ {
+ "label": "tsv_wasm",
+ "bytes": 2491464,
+ "kind": "wasm",
+ "gzip_bytes": 875399
+ },
+ {
+ "label": "biome (wasm)",
+ "bytes": 38584187,
+ "kind": "wasm",
+ "gzip_bytes": 9281487
+ },
+ {
+ "label": "oxc-parser (napi)",
+ "bytes": 2354904,
+ "kind": "native",
+ "gzip_bytes": 954754
+ },
+ {
+ "label": "oxfmt (napi)",
+ "bytes": 8917776,
+ "kind": "native",
+ "gzip_bytes": 3586269
+ },
+ {
+ "label": "oxc-parser (wasm)",
+ "bytes": 1531455,
+ "kind": "wasm",
+ "gzip_bytes": 495227
+ }
+ ],
+ "entries": [
+ {
+ "name": "svelte/compiler",
+ "group": "parse/svelte",
+ "mean_ns": null,
+ "p50_ns": null,
+ "p75_ns": null,
+ "p90_ns": null,
+ "p95_ns": null,
+ "p99_ns": null,
+ "min_ns": null,
+ "max_ns": null,
+ "std_dev_ns": null,
+ "cv": null,
+ "ops_per_second": null,
+ "sample_size": null,
+ "files_processed": 4539,
+ "files_total": 4539,
+ "files_iterated": 4539,
+ "runtime": "node"
+ },
+ {
+ "name": "tsv-json",
+ "group": "parse/svelte",
+ "mean_ns": null,
+ "p50_ns": null,
+ "p75_ns": null,
+ "p90_ns": null,
+ "p95_ns": null,
+ "p99_ns": null,
+ "min_ns": null,
+ "max_ns": null,
+ "std_dev_ns": null,
+ "cv": null,
+ "ops_per_second": null,
+ "sample_size": null,
+ "files_processed": 4539,
+ "files_total": 4539,
+ "files_iterated": 4539,
+ "runtime": "node"
+ },
+ {
+ "name": "tsv-json-no-locations",
+ "group": "parse/svelte",
+ "mean_ns": null,
+ "p50_ns": null,
+ "p75_ns": null,
+ "p90_ns": null,
+ "p95_ns": null,
+ "p99_ns": null,
+ "min_ns": null,
+ "max_ns": null,
+ "std_dev_ns": null,
+ "cv": null,
+ "ops_per_second": null,
+ "sample_size": null,
+ "files_processed": 4539,
+ "files_total": 4539,
+ "files_iterated": 4539,
+ "runtime": "node"
+ },
+ {
+ "name": "tsv_wasm-json",
+ "group": "parse/svelte",
+ "mean_ns": null,
+ "p50_ns": null,
+ "p75_ns": null,
+ "p90_ns": null,
+ "p95_ns": null,
+ "p99_ns": null,
+ "min_ns": null,
+ "max_ns": null,
+ "std_dev_ns": null,
+ "cv": null,
+ "ops_per_second": null,
+ "sample_size": null,
+ "files_processed": 4539,
+ "files_total": 4539,
+ "files_iterated": 4539,
+ "runtime": "node"
+ },
+ {
+ "name": "tsv_wasm-json-no-locations",
+ "group": "parse/svelte",
+ "mean_ns": null,
+ "p50_ns": null,
+ "p75_ns": null,
+ "p90_ns": null,
+ "p95_ns": null,
+ "p99_ns": null,
+ "min_ns": null,
+ "max_ns": null,
+ "std_dev_ns": null,
+ "cv": null,
+ "ops_per_second": null,
+ "sample_size": null,
+ "files_processed": 4539,
+ "files_total": 4539,
+ "files_iterated": 4539,
+ "runtime": "node"
+ },
+ {
+ "name": "tsv-internal",
+ "group": "parse/svelte",
+ "mean_ns": null,
+ "p50_ns": null,
+ "p75_ns": null,
+ "p90_ns": null,
+ "p95_ns": null,
+ "p99_ns": null,
+ "min_ns": null,
+ "max_ns": null,
+ "std_dev_ns": null,
+ "cv": null,
+ "ops_per_second": null,
+ "sample_size": null,
+ "files_processed": 4539,
+ "files_total": 4539,
+ "files_iterated": 4539,
+ "runtime": "node"
+ },
+ {
+ "name": "tsv_wasm-internal",
+ "group": "parse/svelte",
+ "mean_ns": null,
+ "p50_ns": null,
+ "p75_ns": null,
+ "p90_ns": null,
+ "p95_ns": null,
+ "p99_ns": null,
+ "min_ns": null,
+ "max_ns": null,
+ "std_dev_ns": null,
+ "cv": null,
+ "ops_per_second": null,
+ "sample_size": null,
+ "files_processed": 4539,
+ "files_total": 4539,
+ "files_iterated": 4539,
+ "runtime": "node"
+ },
+ {
+ "name": "acorn-typescript",
+ "group": "parse/typescript",
+ "mean_ns": null,
+ "p50_ns": null,
+ "p75_ns": null,
+ "p90_ns": null,
+ "p95_ns": null,
+ "p99_ns": null,
+ "min_ns": null,
+ "max_ns": null,
+ "std_dev_ns": null,
+ "cv": null,
+ "ops_per_second": null,
+ "sample_size": null,
+ "files_processed": 43647,
+ "files_total": 44224,
+ "files_iterated": 43619,
+ "runtime": "node"
+ },
+ {
+ "name": "tsv-json",
+ "group": "parse/typescript",
+ "mean_ns": null,
+ "p50_ns": null,
+ "p75_ns": null,
+ "p90_ns": null,
+ "p95_ns": null,
+ "p99_ns": null,
+ "min_ns": null,
+ "max_ns": null,
+ "std_dev_ns": null,
+ "cv": null,
+ "ops_per_second": null,
+ "sample_size": null,
+ "files_processed": 44023,
+ "files_total": 44224,
+ "files_iterated": 43619,
+ "runtime": "node"
+ },
+ {
+ "name": "tsv-json-no-locations",
+ "group": "parse/typescript",
+ "mean_ns": null,
+ "p50_ns": null,
+ "p75_ns": null,
+ "p90_ns": null,
+ "p95_ns": null,
+ "p99_ns": null,
+ "min_ns": null,
+ "max_ns": null,
+ "std_dev_ns": null,
+ "cv": null,
+ "ops_per_second": null,
+ "sample_size": null,
+ "files_processed": 44023,
+ "files_total": 44224,
+ "files_iterated": 43619,
+ "runtime": "node"
+ },
+ {
+ "name": "tsv_wasm-json",
+ "group": "parse/typescript",
+ "mean_ns": null,
+ "p50_ns": null,
+ "p75_ns": null,
+ "p90_ns": null,
+ "p95_ns": null,
+ "p99_ns": null,
+ "min_ns": null,
+ "max_ns": null,
+ "std_dev_ns": null,
+ "cv": null,
+ "ops_per_second": null,
+ "sample_size": null,
+ "files_processed": 44023,
+ "files_total": 44224,
+ "files_iterated": 43619,
+ "runtime": "node"
+ },
+ {
+ "name": "tsv_wasm-json-no-locations",
+ "group": "parse/typescript",
+ "mean_ns": null,
+ "p50_ns": null,
+ "p75_ns": null,
+ "p90_ns": null,
+ "p95_ns": null,
+ "p99_ns": null,
+ "min_ns": null,
+ "max_ns": null,
+ "std_dev_ns": null,
+ "cv": null,
+ "ops_per_second": null,
+ "sample_size": null,
+ "files_processed": 44023,
+ "files_total": 44224,
+ "files_iterated": 43619,
+ "runtime": "node"
+ },
+ {
+ "name": "tsv-internal",
+ "group": "parse/typescript",
+ "mean_ns": null,
+ "p50_ns": null,
+ "p75_ns": null,
+ "p90_ns": null,
+ "p95_ns": null,
+ "p99_ns": null,
+ "min_ns": null,
+ "max_ns": null,
+ "std_dev_ns": null,
+ "cv": null,
+ "ops_per_second": null,
+ "sample_size": null,
+ "files_processed": 44023,
+ "files_total": 44224,
+ "files_iterated": 43619,
+ "runtime": "node"
+ },
+ {
+ "name": "tsv_wasm-internal",
+ "group": "parse/typescript",
+ "mean_ns": null,
+ "p50_ns": null,
+ "p75_ns": null,
+ "p90_ns": null,
+ "p95_ns": null,
+ "p99_ns": null,
+ "min_ns": null,
+ "max_ns": null,
+ "std_dev_ns": null,
+ "cv": null,
+ "ops_per_second": null,
+ "sample_size": null,
+ "files_processed": 44023,
+ "files_total": 44224,
+ "files_iterated": 43619,
+ "runtime": "node"
+ },
+ {
+ "name": "oxc-parser",
+ "group": "parse/typescript",
+ "mean_ns": null,
+ "p50_ns": null,
+ "p75_ns": null,
+ "p90_ns": null,
+ "p95_ns": null,
+ "p99_ns": null,
+ "min_ns": null,
+ "max_ns": null,
+ "std_dev_ns": null,
+ "cv": null,
+ "ops_per_second": null,
+ "sample_size": null,
+ "files_processed": 44014,
+ "files_total": 44224,
+ "files_iterated": 43619,
+ "runtime": "node"
+ },
+ {
+ "name": "oxc-parser-wasm",
+ "group": "parse/typescript",
+ "mean_ns": null,
+ "p50_ns": null,
+ "p75_ns": null,
+ "p90_ns": null,
+ "p95_ns": null,
+ "p99_ns": null,
+ "min_ns": null,
+ "max_ns": null,
+ "std_dev_ns": null,
+ "cv": null,
+ "ops_per_second": null,
+ "sample_size": null,
+ "files_processed": 44014,
+ "files_total": 44224,
+ "files_iterated": 43619,
+ "runtime": "node"
+ },
+ {
+ "name": "svelte/compiler",
+ "group": "parse/css",
+ "mean_ns": null,
+ "p50_ns": null,
+ "p75_ns": null,
+ "p90_ns": null,
+ "p95_ns": null,
+ "p99_ns": null,
+ "min_ns": null,
+ "max_ns": null,
+ "std_dev_ns": null,
+ "cv": null,
+ "ops_per_second": null,
+ "sample_size": null,
+ "files_processed": 22402,
+ "files_total": 22641,
+ "files_iterated": 22388,
+ "runtime": "node"
+ },
+ {
+ "name": "tsv-json",
+ "group": "parse/css",
+ "mean_ns": null,
+ "p50_ns": null,
+ "p75_ns": null,
+ "p90_ns": null,
+ "p95_ns": null,
+ "p99_ns": null,
+ "min_ns": null,
+ "max_ns": null,
+ "std_dev_ns": null,
+ "cv": null,
+ "ops_per_second": null,
+ "sample_size": null,
+ "files_processed": 22457,
+ "files_total": 22641,
+ "files_iterated": 22388,
+ "runtime": "node"
+ },
+ {
+ "name": "tsv_wasm-json",
+ "group": "parse/css",
+ "mean_ns": null,
+ "p50_ns": null,
+ "p75_ns": null,
+ "p90_ns": null,
+ "p95_ns": null,
+ "p99_ns": null,
+ "min_ns": null,
+ "max_ns": null,
+ "std_dev_ns": null,
+ "cv": null,
+ "ops_per_second": null,
+ "sample_size": null,
+ "files_processed": 22457,
+ "files_total": 22641,
+ "files_iterated": 22388,
+ "runtime": "node"
+ },
+ {
+ "name": "tsv-internal",
+ "group": "parse/css",
+ "mean_ns": null,
+ "p50_ns": null,
+ "p75_ns": null,
+ "p90_ns": null,
+ "p95_ns": null,
+ "p99_ns": null,
+ "min_ns": null,
+ "max_ns": null,
+ "std_dev_ns": null,
+ "cv": null,
+ "ops_per_second": null,
+ "sample_size": null,
+ "files_processed": 22457,
+ "files_total": 22641,
+ "files_iterated": 22388,
+ "runtime": "node"
+ },
+ {
+ "name": "tsv_wasm-internal",
+ "group": "parse/css",
+ "mean_ns": null,
+ "p50_ns": null,
+ "p75_ns": null,
+ "p90_ns": null,
+ "p95_ns": null,
+ "p99_ns": null,
+ "min_ns": null,
+ "max_ns": null,
+ "std_dev_ns": null,
+ "cv": null,
+ "ops_per_second": null,
+ "sample_size": null,
+ "files_processed": 22457,
+ "files_total": 22641,
+ "files_iterated": 22388,
+ "runtime": "node"
+ }
+ ],
+ "suppressed_noise": {},
+ "variant_parity": []
+}
diff --git a/src/routes/docs/benchmarks/benchmarks_conformance.ts b/src/routes/docs/benchmarks/benchmarks_conformance.ts
new file mode 100644
index 0000000..6aee0cd
--- /dev/null
+++ b/src/routes/docs/benchmarks/benchmarks_conformance.ts
@@ -0,0 +1,5 @@
+import type {BenchmarkBaseline} from './benchmark_data.ts';
+
+import json from './benchmarks_conformance.json' with {type: 'json'};
+
+export const benchmarks_conformance_json: BenchmarkBaseline = json as BenchmarkBaseline;
diff --git a/src/routes/docs/benchmarks/benchmarks_cross_runtime.json b/src/routes/docs/benchmarks/benchmarks_cross_runtime.json
new file mode 100644
index 0000000..ca995eb
--- /dev/null
+++ b/src/routes/docs/benchmarks/benchmarks_cross_runtime.json
@@ -0,0 +1,724 @@
+{
+ "version": 7,
+ "kind": "combined",
+ "generated": "2026-07-16T11:51:31.514Z",
+ "runtimes": [
+ "deno",
+ "node",
+ "bun"
+ ],
+ "mixed_vintage": false,
+ "mixed_machine": false,
+ "sources": [
+ {
+ "runtime": "deno",
+ "timestamp": "2026-07-16T11:34:23.572Z",
+ "git_commit": "135b7b93",
+ "tsv": "0.1.0",
+ "machine": {
+ "cpu_model": "AMD Ryzen 5 PRO 7530U with Radeon Graphics",
+ "os": "linux",
+ "arch": "x86_64",
+ "runtime_version": "2.8.3"
+ }
+ },
+ {
+ "runtime": "node",
+ "timestamp": "2026-07-16T11:43:29.034Z",
+ "git_commit": "135b7b93",
+ "tsv": "0.1.0",
+ "machine": {
+ "cpu_model": "AMD Ryzen 5 PRO 7530U with Radeon Graphics",
+ "os": "linux",
+ "arch": "x86_64",
+ "runtime_version": "24.14.1"
+ }
+ },
+ {
+ "runtime": "bun",
+ "timestamp": "2026-07-16T11:51:31.124Z",
+ "git_commit": "135b7b93",
+ "tsv": "0.1.0",
+ "machine": {
+ "cpu_model": "AMD Ryzen 5 PRO 7530U with Radeon Graphics",
+ "os": "linux",
+ "arch": "x86_64",
+ "runtime_version": "1.3.14"
+ }
+ }
+ ],
+ "rows": [
+ {
+ "group": "parse/svelte",
+ "name": "svelte/compiler",
+ "ops_per_second": {
+ "deno": 2.32203309333886,
+ "node": 2.201657407828713,
+ "bun": 1.3515266516636215
+ },
+ "mean_ns": {
+ "deno": 430657083.6,
+ "node": 454203272.7,
+ "bun": 739904018
+ },
+ "files_iterated": {
+ "deno": 763,
+ "node": 763,
+ "bun": 763
+ }
+ },
+ {
+ "group": "parse/svelte",
+ "name": "tsv-json",
+ "ops_per_second": {
+ "deno": 5.0827943917957965,
+ "node": 4.649398944179739,
+ "bun": 6.007332747236763
+ },
+ "mean_ns": {
+ "deno": 196742170.33333334,
+ "node": 215081564.7368421,
+ "bun": 166463227.87096775
+ },
+ "files_iterated": {
+ "deno": 763,
+ "node": 763,
+ "bun": 763
+ }
+ },
+ {
+ "group": "parse/svelte",
+ "name": "tsv-json-no-locations",
+ "ops_per_second": {
+ "deno": 7.922378990905888,
+ "node": 7.328403381719038,
+ "bun": 8.520169721099231
+ },
+ "mean_ns": {
+ "deno": 126224711.18181819,
+ "node": 136455370.6875,
+ "bun": 117368554
+ },
+ "files_iterated": {
+ "deno": 763,
+ "node": 763,
+ "bun": 763
+ }
+ },
+ {
+ "group": "parse/svelte",
+ "name": "tsv_wasm-json",
+ "ops_per_second": {
+ "deno": 4.161210076022163,
+ "node": 4.125738456275007,
+ "bun": 5.562851328976341
+ },
+ "mean_ns": {
+ "deno": 240314711.76190478,
+ "node": 242380851.47619048,
+ "bun": 179763926.96153846
+ },
+ "files_iterated": {
+ "deno": 763,
+ "node": 763,
+ "bun": 763
+ }
+ },
+ {
+ "group": "parse/svelte",
+ "name": "tsv_wasm-json-no-locations",
+ "ops_per_second": {
+ "deno": 6.299825463509322,
+ "node": 6.377630789601431,
+ "bun": 7.655585761307142
+ },
+ "mean_ns": {
+ "deno": 158734556.3448276,
+ "node": 156798038.8,
+ "bun": 130623577.5
+ },
+ "files_iterated": {
+ "deno": 763,
+ "node": 763,
+ "bun": 763
+ }
+ },
+ {
+ "group": "parse/svelte",
+ "name": "tsv-internal",
+ "ops_per_second": {
+ "deno": 47.04746259219493,
+ "node": 47.86874286762142,
+ "bun": 51.06942307255545
+ },
+ "mean_ns": {
+ "deno": 21255131.412037037,
+ "node": 20890458.785714287,
+ "bun": 19581188.504504506
+ },
+ "files_iterated": {
+ "deno": 763,
+ "node": 763,
+ "bun": 763
+ }
+ },
+ {
+ "group": "parse/svelte",
+ "name": "tsv_wasm-internal",
+ "ops_per_second": {
+ "deno": 30.474656127239356,
+ "node": 26.773814422344802,
+ "bun": 15.85067482488642
+ },
+ "mean_ns": {
+ "deno": 32814152.055555556,
+ "node": 37349926.47014926,
+ "bun": 63088796.6
+ },
+ "files_iterated": {
+ "deno": 763,
+ "node": 763,
+ "bun": 763
+ }
+ },
+ {
+ "group": "format/svelte",
+ "name": "prettier",
+ "ops_per_second": {
+ "deno": 0.237793092279713,
+ "node": 0.2267144665492906,
+ "bun": 0.2524397368523206
+ },
+ "mean_ns": {
+ "deno": 4205336624.428571,
+ "node": 4410834541,
+ "bun": 3961341476.857143
+ },
+ "files_iterated": {
+ "deno": 763,
+ "node": 763,
+ "bun": 763
+ }
+ },
+ {
+ "group": "format/svelte",
+ "name": "tsv",
+ "ops_per_second": {
+ "deno": 14.136745274233984,
+ "node": 14.35076558858908,
+ "bun": 14.12111063381393
+ },
+ "mean_ns": {
+ "deno": 70737640.14285715,
+ "node": 69682693.5,
+ "bun": 70815959.58928572
+ },
+ "files_iterated": {
+ "deno": 763,
+ "node": 763,
+ "bun": 763
+ }
+ },
+ {
+ "group": "format/svelte",
+ "name": "tsv_wasm",
+ "ops_per_second": {
+ "deno": 9.056948494404766,
+ "node": 7.928477824721544,
+ "bun": 7.193862254275549
+ },
+ "mean_ns": {
+ "deno": 110412464.04545455,
+ "node": 126127615.175,
+ "bun": 139007387.7777778
+ },
+ "files_iterated": {
+ "deno": 763,
+ "node": 763,
+ "bun": 763
+ }
+ },
+ {
+ "group": "format/svelte",
+ "name": "oxfmt",
+ "ops_per_second": {
+ "deno": 0.23968786103725603,
+ "node": 0.22993605936582995,
+ "bun": 0.1902163231156394
+ },
+ "mean_ns": {
+ "deno": 4172092803,
+ "node": 4349035130.714286,
+ "bun": 5257172379.428572
+ },
+ "files_iterated": {
+ "deno": 763,
+ "node": 763,
+ "bun": 763
+ }
+ },
+ {
+ "group": "format/svelte",
+ "name": "biome-wasm",
+ "ops_per_second": {
+ "deno": 1.4318975821011795,
+ "node": 1.0837474344623093
+ },
+ "mean_ns": {
+ "deno": 698373970.6666666,
+ "node": 922724214.3333334
+ },
+ "files_iterated": {
+ "deno": 763,
+ "node": 763
+ }
+ },
+ {
+ "group": "parse/typescript",
+ "name": "acorn-typescript",
+ "ops_per_second": {
+ "deno": 0.35227894489602546,
+ "node": 0.3230721733354392,
+ "bun": 0.1519515615378463
+ },
+ "mean_ns": {
+ "deno": 2838659575,
+ "node": 3095283600.8,
+ "bun": 6581044576.833333
+ },
+ "files_iterated": {
+ "deno": 2434,
+ "node": 2434,
+ "bun": 2434
+ }
+ },
+ {
+ "group": "parse/typescript",
+ "name": "tsv-json",
+ "ops_per_second": {
+ "deno": 0.5528114711248244,
+ "node": 0.4864556603500567,
+ "bun": 0.7400072723918691
+ },
+ "mean_ns": {
+ "deno": 1808934966.5,
+ "node": 2055685813.75,
+ "bun": 1351338071
+ },
+ "files_iterated": {
+ "deno": 2434,
+ "node": 2434,
+ "bun": 2434
+ }
+ },
+ {
+ "group": "parse/typescript",
+ "name": "tsv-json-no-locations",
+ "ops_per_second": {
+ "deno": 1.145465982082649,
+ "node": 1.0017326620919327,
+ "bun": 1.283661154144797
+ },
+ "mean_ns": {
+ "deno": 873007156.6,
+ "node": 998270334.8333334,
+ "bun": 779021782.1666666
+ },
+ "files_iterated": {
+ "deno": 2434,
+ "node": 2434,
+ "bun": 2434
+ }
+ },
+ {
+ "group": "parse/typescript",
+ "name": "tsv_wasm-json",
+ "ops_per_second": {
+ "deno": 0.47385486766100227,
+ "node": 0.4565405972186589,
+ "bun": 0.6842257166812827
+ },
+ "mean_ns": {
+ "deno": 2110350802,
+ "node": 2190385709.6,
+ "bun": 1461506014.2
+ },
+ "files_iterated": {
+ "deno": 2434,
+ "node": 2434,
+ "bun": 2434
+ }
+ },
+ {
+ "group": "parse/typescript",
+ "name": "tsv_wasm-json-no-locations",
+ "ops_per_second": {
+ "deno": 0.9401759352694344,
+ "node": 0.9106175234409573,
+ "bun": 1.1666136412897596
+ },
+ "mean_ns": {
+ "deno": 1063630712.6,
+ "node": 1098155893.4,
+ "bun": 857181816.3333334
+ },
+ "files_iterated": {
+ "deno": 2434,
+ "node": 2434,
+ "bun": 2434
+ }
+ },
+ {
+ "group": "parse/typescript",
+ "name": "tsv-internal",
+ "ops_per_second": {
+ "deno": 7.593919350391545,
+ "node": 7.24994471387352,
+ "bun": 8.29899627514646
+ },
+ "mean_ns": {
+ "deno": 131684306.06896552,
+ "node": 137932086.30769232,
+ "bun": 120496499.4375
+ },
+ "files_iterated": {
+ "deno": 2434,
+ "node": 2434,
+ "bun": 2434
+ }
+ },
+ {
+ "group": "parse/typescript",
+ "name": "tsv_wasm-internal",
+ "ops_per_second": {
+ "deno": 4.844409174820336,
+ "node": 5.449892744636837,
+ "bun": 5.698779253363164
+ },
+ "mean_ns": {
+ "deno": 206423521.2,
+ "node": 183489849.5909091,
+ "bun": 175476177.5357143
+ },
+ "files_iterated": {
+ "deno": 2434,
+ "node": 2434,
+ "bun": 2434
+ }
+ },
+ {
+ "group": "parse/typescript",
+ "name": "oxc-parser",
+ "ops_per_second": {
+ "deno": 0.8624090264812094,
+ "node": 0.7526855940543842,
+ "bun": 1.047915908090529
+ },
+ "mean_ns": {
+ "deno": 1159542594.4,
+ "node": 1328575979,
+ "bun": 954275044.6666666
+ },
+ "files_iterated": {
+ "deno": 2434,
+ "node": 2434,
+ "bun": 2434
+ }
+ },
+ {
+ "group": "parse/typescript",
+ "name": "oxc-parser-wasm",
+ "ops_per_second": {
+ "deno": 0.768719704502768,
+ "node": 0.736063991113934
+ },
+ "mean_ns": {
+ "deno": 1300864273.6,
+ "node": 1358577531.4
+ },
+ "files_iterated": {
+ "deno": 2434,
+ "node": 2434
+ }
+ },
+ {
+ "group": "format/typescript",
+ "name": "prettier",
+ "ops_per_second": {
+ "deno": 0.08265464739794595,
+ "node": 0.07468151706634057,
+ "bun": 0.0626242936249232
+ },
+ "mean_ns": {
+ "deno": 12098533252.285715,
+ "node": 13390193976.8,
+ "bun": 15968243985.142857
+ },
+ "files_iterated": {
+ "deno": 2435,
+ "node": 2435,
+ "bun": 2435
+ }
+ },
+ {
+ "group": "format/typescript",
+ "name": "tsv",
+ "ops_per_second": {
+ "deno": 1.9912721494847867,
+ "node": 1.9597033567241435,
+ "bun": 1.9399248269817906
+ },
+ "mean_ns": {
+ "deno": 502191526.28571427,
+ "node": 510281312,
+ "bun": 515483892
+ },
+ "files_iterated": {
+ "deno": 2435,
+ "node": 2435,
+ "bun": 2435
+ }
+ },
+ {
+ "group": "format/typescript",
+ "name": "tsv_wasm",
+ "ops_per_second": {
+ "deno": 1.2720122080964626,
+ "node": 1.4036473636379396,
+ "bun": 1.4393877539677782
+ },
+ "mean_ns": {
+ "deno": 786155976.8333334,
+ "node": 712429650,
+ "bun": 694739827.5714285
+ },
+ "files_iterated": {
+ "deno": 2435,
+ "node": 2435,
+ "bun": 2435
+ }
+ },
+ {
+ "group": "format/typescript",
+ "name": "oxfmt",
+ "ops_per_second": {
+ "deno": 1.2039315321874253,
+ "node": 1.1804785536499742,
+ "bun": 0.9945970863022248
+ },
+ "mean_ns": {
+ "deno": 830612018.4285715,
+ "node": 847114076.6666666,
+ "bun": 1005432263.75
+ },
+ "files_iterated": {
+ "deno": 2435,
+ "node": 2435,
+ "bun": 2435
+ }
+ },
+ {
+ "group": "format/typescript",
+ "name": "biome-wasm",
+ "ops_per_second": {
+ "deno": 0.24400636365570735,
+ "node": 0.21548659629049075
+ },
+ "mean_ns": {
+ "deno": 4098253771,
+ "node": 4640659870.333333
+ },
+ "files_iterated": {
+ "deno": 2435,
+ "node": 2435
+ }
+ },
+ {
+ "group": "parse/css",
+ "name": "svelte/compiler",
+ "ops_per_second": {
+ "deno": 99.27831750991159,
+ "node": 110.63306485324856,
+ "bun": 68.18371992928869
+ },
+ "mean_ns": {
+ "deno": 10072692.860655738,
+ "node": 9038889.063829787,
+ "bun": 14666257.591065291
+ },
+ "files_iterated": {
+ "deno": 49,
+ "node": 49,
+ "bun": 49
+ }
+ },
+ {
+ "group": "parse/css",
+ "name": "tsv-json",
+ "ops_per_second": {
+ "deno": 66.5873934340344,
+ "node": 57.47773991434453,
+ "bun": 73.63477107334026
+ },
+ "mean_ns": {
+ "deno": 15017857.71192053,
+ "node": 17398039.684410647,
+ "bun": 13580540.625352113
+ },
+ "files_iterated": {
+ "deno": 49,
+ "node": 49,
+ "bun": 49
+ }
+ },
+ {
+ "group": "parse/css",
+ "name": "tsv_wasm-json",
+ "ops_per_second": {
+ "deno": 52.32935697865012,
+ "node": 54.68347807639267,
+ "bun": 68.46106080515385
+ },
+ "mean_ns": {
+ "deno": 19109732.237068966,
+ "node": 18287059.184549358,
+ "bun": 14606843.48502994
+ },
+ "files_iterated": {
+ "deno": 49,
+ "node": 49,
+ "bun": 49
+ }
+ },
+ {
+ "group": "parse/css",
+ "name": "tsv-internal",
+ "ops_per_second": {
+ "deno": 304.7757571691321,
+ "node": 291.8849689463387,
+ "bun": 325.19575279279934
+ },
+ "mean_ns": {
+ "deno": 3281100.8634294383,
+ "node": 3426007.1822466613,
+ "bun": 3075070.9116338207
+ },
+ "files_iterated": {
+ "deno": 49,
+ "node": 49,
+ "bun": 49
+ }
+ },
+ {
+ "group": "parse/css",
+ "name": "tsv_wasm-internal",
+ "ops_per_second": {
+ "deno": 184.7561962029366,
+ "node": 203.9404004919006,
+ "bun": 217.84005813175878
+ },
+ "mean_ns": {
+ "deno": 5412538.364351245,
+ "node": 4903393.332503113,
+ "bun": 4590523.93107221
+ },
+ "files_iterated": {
+ "deno": 49,
+ "node": 49,
+ "bun": 49
+ }
+ },
+ {
+ "group": "format/css",
+ "name": "prettier",
+ "ops_per_second": {
+ "deno": 1.9335173437011668,
+ "node": 1.8124741341363197,
+ "bun": 1.912133053851122
+ },
+ "mean_ns": {
+ "deno": 517192154.1111111,
+ "node": 551732011.6,
+ "bun": 522976159
+ },
+ "files_iterated": {
+ "deno": 49,
+ "node": 49,
+ "bun": 49
+ }
+ },
+ {
+ "group": "format/css",
+ "name": "tsv",
+ "ops_per_second": {
+ "deno": 148.60107416363323,
+ "node": 142.20257063292027,
+ "bun": 147.4625555531239
+ },
+ "mean_ns": {
+ "deno": 6729426.456896552,
+ "node": 7032221.678899082,
+ "bun": 6781382.543175488
+ },
+ "files_iterated": {
+ "deno": 49,
+ "node": 49,
+ "bun": 49
+ }
+ },
+ {
+ "group": "format/css",
+ "name": "tsv_wasm",
+ "ops_per_second": {
+ "deno": 88.44974187950501,
+ "node": 98.43734946842822,
+ "bun": 101.39867650427693
+ },
+ "mean_ns": {
+ "deno": 11305855.492063493,
+ "node": 10158745.69358178,
+ "bun": 9862061.660714285
+ },
+ "files_iterated": {
+ "deno": 49,
+ "node": 49,
+ "bun": 49
+ }
+ },
+ {
+ "group": "format/css",
+ "name": "oxfmt",
+ "ops_per_second": {
+ "deno": 56.355769067169135,
+ "node": 55.27600072694931,
+ "bun": 47.442921582011735
+ },
+ "mean_ns": {
+ "deno": 17744412.26785714,
+ "node": 18091033.845588237,
+ "bun": 21077959.92857143
+ },
+ "files_iterated": {
+ "deno": 49,
+ "node": 49,
+ "bun": 49
+ }
+ },
+ {
+ "group": "format/css",
+ "name": "biome-wasm",
+ "ops_per_second": {
+ "deno": 10.267159807066765,
+ "node": 7.9097448512290125
+ },
+ "mean_ns": {
+ "deno": 97397919.07317074,
+ "node": 126426328.38461539
+ },
+ "files_iterated": {
+ "deno": 49,
+ "node": 49
+ }
+ }
+ ]
+}
diff --git a/src/routes/docs/benchmarks/benchmarks_cross_runtime.ts b/src/routes/docs/benchmarks/benchmarks_cross_runtime.ts
new file mode 100644
index 0000000..00d2a45
--- /dev/null
+++ b/src/routes/docs/benchmarks/benchmarks_cross_runtime.ts
@@ -0,0 +1,5 @@
+import type {CrossRuntimeReport} from './benchmark_data.ts';
+
+import json from './benchmarks_cross_runtime.json' with {type: 'json'};
+
+export const benchmarks_cross_runtime_json: CrossRuntimeReport = json as CrossRuntimeReport;
diff --git a/src/routes/docs/introduction/+page.svelte b/src/routes/docs/introduction/+page.svelte
index dfefa5f..478eab2 100644
--- a/src/routes/docs/introduction/+page.svelte
+++ b/src/routes/docs/introduction/+page.svelte
@@ -47,10 +47,9 @@ reconstruct_locations(ast, 'const x = 1;');`;
Compared to Oxc, Biome, and SWC, tsv is a set of focused tools, not a generic language
- platform, so the focus is web standards and there's no support for JSX/SCSS/etc, beyond Svelte
- as the only JS framework. The extensibility story is currently limited to using its Rust
- crates as libraries; bridging to JS or WASM plugins is an open question, but may not be
- supported.
+ platform, so the focus is Web standards + Svelte and there's no support for JSX/SCSS/etc. The
+ extensibility story is currently limited to using its Rust crates as libraries (or forking);
+ bridging to JS or WASM plugins is an open question (leaning against).
tsv prioritizes, in order:
@@ -60,12 +59,11 @@ reconstruct_locations(ast, 'const x = 1;');`;
extensibility (valued but deprioritized)
- See the benchmarks for stats. Compared to Oxc
- and Biome, tsv is significantly faster, smaller, and uses less memory to parse and format its supported
- languages
+ See the for stats. Compared to Oxc and Biome, tsv is faster,
+ smaller, and uses less memory to parse and format its supported languages.
- This is an early release, and reports and feedback are appreciated - see the
+ This is an early release, and reports and feedback are appreciated — see the
issues and
discussions .
@@ -95,7 +93,7 @@ reconstruct_locations(ast, 'const x = 1;');`;
See the for size and performance details.
- Native builds are not yet available but are coming in v0.2, see
+ Native builds are not yet published — follow
issue 139 .
@@ -120,14 +118,14 @@ reconstruct_locations(ast, 'const x = 1;');`;
For TypeScript and Svelte, the parsers also emit a span-only AST that drops the per-node
loc (line/column) object — Svelte also drops name_loc — for a ~46%
smaller, faster-to-materialize result, mirroring acorn's locations: false. Line
- and column stay derivable from the start/end offsets plus your source,
- so nothing is lost when you have the source.
+ and column stay derivable from the start/end offsets plus your
+ source, so nothing is lost when you have the source.
reconstruct_locations(ast, source) walks the tree and adds loc
- back, mutating in place — exact for TypeScript, approximate for Svelte (it skips the parser's
- own
+ back, mutating in place — exact for TypeScript, approximate for Svelte (it skips the
+ parser's own
name_loc and a couple of position quirks). For sparse lookups,
create_locator(source) reuses one line table across calls. CSS has no
loc, so there's no span-only variant for it.
@@ -137,11 +135,12 @@ reconstruct_locations(ast, 'const x = 1;');`;
diff --git a/src/routes/docs/playground/+page.svelte b/src/routes/docs/playground/+page.svelte
index f9c38ed..5f1af8b 100644
--- a/src/routes/docs/playground/+page.svelte
+++ b/src/routes/docs/playground/+page.svelte
@@ -10,7 +10,7 @@
const tome = tome_get_by_slug('playground');
// The `/docs` index renders every tome's component via `DocsContent`, which would
- // mount `Playground` and pull in its ~900KB WASM. Guard like fuz_css's api tome:
+ // mount `Playground` and pull in its ~900KB-gzipped WASM. Guard like fuz_css's api tome:
// show only a link at the root, the full playground at `/docs/playground`.
const at_root = $derived(page.url.pathname === DOCS_PATH);
@@ -31,7 +31,8 @@
See the for setup and API usage. The textarea above uses
fuz_code 's
- CodeTextarea component.
+ CodeTextarea component.
{/if}
diff --git a/src/routes/docs/playground/Playground.svelte b/src/routes/docs/playground/Playground.svelte
index ba20a09..ca2825b 100644
--- a/src/routes/docs/playground/Playground.svelte
+++ b/src/routes/docs/playground/Playground.svelte
@@ -6,12 +6,13 @@
import Code from '@fuzdev/fuz_code/Code.svelte';
import CodeTextarea from '@fuzdev/fuz_code/CodeTextarea.svelte';
import {supports_css_highlight_api} from '@fuzdev/fuz_code/highlight_manager.ts';
+ import CopyToClipboard from '@fuzdev/fuz_ui/CopyToClipboard.svelte';
import {to_error_message} from '@fuzdev/fuz_util/error.ts';
import {playground_example} from './playground_example.ts';
// `@fuzdev/tsv_wasm` is loaded lazily, in the browser only — a dynamic import
- // so the ~900KB WASM lands in its own chunk, fetched the first time this
+ // so the ~900KB-gzipped WASM lands in its own chunk, fetched the first time this
// component mounts and never pulled into `/docs` or the prerendered HTML.
let tsv: typeof import('@fuzdev/tsv_wasm') | null = $state(null);
let load_error: string | null = $state(null);
@@ -72,6 +73,8 @@
};
});
+ const is_base = $derived(source === playground_example);
+
const reset = (): void => {
source = playground_example;
};
@@ -84,8 +87,8 @@
};
-
- reset
+
+ reset
format
@@ -112,7 +115,10 @@
formatted
AST
-
+
+
+
+
{/if}
@@ -132,6 +138,24 @@
section :global(.ast) {
max-height: 500px;
}
+ /* float a copy button over the AST pane's top-right corner; it stays pinned as
+ the pane scrolls since it's absolute to this wrapper, not inside the scroller */
+ .ast_output {
+ position: relative;
+ }
+ .ast_output :global(.ast_copy) {
+ --font_size: var(--font_size_lg);
+ position: absolute;
+ top: var(--space_xs);
+ /* clear the pane's scrollbar */
+ right: var(--space_md);
+ z-index: 1;
+ background: var(--shade_00);
+ }
+ /* TODO hacky */
+ .ast_output :global(.ast_copy > div) {
+ width: auto !important;
+ }
.error {
color: var(--color_e_40);
white-space: pre-wrap;
diff --git a/src/test/benchmark_data.test.ts b/src/test/benchmark_data.test.ts
index 0f6e8d0..e55cc03 100644
--- a/src/test/benchmark_data.test.ts
+++ b/src/test/benchmark_data.test.ts
@@ -1,9 +1,23 @@
import {assert, describe, test} from 'vitest';
import {benchmarks_json} from '$routes/docs/benchmarks/benchmarks.ts';
+import {benchmarks_conformance_json} from '$routes/docs/benchmarks/benchmarks_conformance.ts';
+import {benchmarks_cross_runtime_json} from '$routes/docs/benchmarks/benchmarks_cross_runtime.ts';
+import {benchmarks_cli} from '$routes/docs/benchmarks/benchmarks_cli.ts';
import {
+ categorize_size_capability,
+ compute_baseline_ratio,
derive_benchmark_groups,
+ derive_conformance_groups,
+ derive_corpus_repos,
+ derive_cross_runtime_groups,
+ derive_size_groups,
derive_speedup_summary,
+ format_coverage_percent,
+ format_speedup_signed,
+ order_cross_runtime_runtimes,
+ OXC_FULL_LABEL,
+ OXFMT_WASM_LABEL,
} from '$routes/docs/benchmarks/benchmark_data.ts';
// Shape gate for the committed benchmarks.json: the bench report format drifts
@@ -12,13 +26,14 @@ import {
// When `npm run update-benchmarks` pulls in a new shape, these fail loudly.
describe('benchmarks.json shape', () => {
test('baseline version is current', () => {
- assert.isAtLeast(benchmarks_json.version, 4);
+ // version 5 is the first per-runtime report shape (carries `runtime`)
+ assert.isAtLeast(benchmarks_json.version, 5);
});
- test('binary sizes include the ratio anchors', () => {
+ test('binary sizes include the flagship tsv builds', () => {
const labels = benchmarks_json.binary_sizes.map((s) => s.label);
- assert.include(labels, 'tsv (native)'); // native anchor
- assert.include(labels, 'tsv_wasm'); // wasm anchor (the full build)
+ assert.include(labels, 'tsv (napi)'); // flagship N-API build (perf report anchor)
+ assert.include(labels, 'tsv_wasm'); // the full wasm build — smallest full-toolchain, size baseline
});
test('versions carries the keys the meta component renders', () => {
@@ -35,13 +50,105 @@ describe('benchmarks.json shape', () => {
}
});
- test('every group derives a canonical entry and a timed-set count', () => {
+ test('every group leads with its canonical entry (the default anchor) and a timed-set count', () => {
const groups = derive_benchmark_groups(benchmarks_json);
assert.isAtLeast(groups.length, 6); // format+parse × svelte/typescript/css
for (const group of groups) {
const key = `${group.operation}/${group.language}`;
assert.ok(group.canonical_entry, `${key} has no canonical entry`);
+ // the canonical reference sorts first, so it's the default 1.0x anchor the
+ // shared component reads off the leading row
+ assert.strictEqual(group.entries[0]?.category, 'canonical', `${key} canonical leads`);
assert.isNotNull(group.files_iterated, `${key} has no files_iterated`);
+ for (const entry of group.entries) {
+ if (entry.disabled) continue;
+ // measured entries render the whole-sweep mean (total corpus time); a
+ // zero would misread as an instantaneous run
+ assert.isAbove(entry.mean_ns, 0, `${key}/${entry.name} has no mean`);
+ }
+ }
+ });
+
+ test('svelte/css parse groups get disabled oxc placeholders, typescript keeps real ones', () => {
+ const groups = derive_benchmark_groups(benchmarks_json);
+ const parse = (language: string) =>
+ groups.find((g) => g.operation === 'parse' && g.language === language);
+
+ // typescript actually runs oxc-parser — its oxc entries are real, not placeholders
+ const ts = parse('typescript');
+ const ts_oxc = ts?.entries.filter((e) => e.category === 'oxc') ?? [];
+ assert.isNotEmpty(ts_oxc);
+ for (const e of ts_oxc) assert.isNotOk(e.disabled, `${e.name} should be a real entry`);
+
+ // svelte and css mirror those oxc entries in, disabled, in the same fixed slot:
+ // directly after the biome placeholder (both lead the cross-tool comparisons,
+ // right after the canonical row) and before tsv's json wires
+ for (const language of ['svelte', 'css']) {
+ const group = parse(language);
+ assert.ok(group, `${language} parse group missing`);
+ const oxc = group.entries.filter((e) => e.category === 'oxc');
+ assert.strictEqual(oxc.length, ts_oxc.length, `${language} oxc placeholder count`);
+ for (const e of oxc) {
+ assert.ok(e.disabled, `${language} ${e.name} should be disabled`);
+ assert.strictEqual(e.bar_fraction, 0, `${language} ${e.name} bar`);
+ }
+ const names = group.entries.map((e) => e.name);
+ const first_biome = names.findIndex((n) => n.includes('biome'));
+ const first_oxc = names.findIndex((n) => n.includes('oxc'));
+ const first_json = names.findIndex((n) => n.endsWith('-json') || n.endsWith('-no-locations'));
+ assert.strictEqual(first_oxc, first_biome + 1, `${language} oxc directly after biome`);
+ assert.isBelow(first_oxc, first_json, `${language} oxc before the tsv json entries`);
+ }
+ });
+
+ test('every parse group gets a disabled biome placeholder, since biome never exposes a parser to JS', () => {
+ const groups = derive_benchmark_groups(benchmarks_json);
+ for (const language of ['svelte', 'typescript', 'css']) {
+ const group = groups.find((g) => g.operation === 'parse' && g.language === language);
+ assert.ok(group, `${language} parse group missing`);
+ const biome = group.entries.filter((e) => e.category === 'biome');
+ assert.strictEqual(biome.length, 1, `${language} biome placeholder count`);
+ const [entry] = biome;
+ assert.ok(entry, `${language} biome entry missing`);
+ assert.ok(entry.disabled, `${language} biome entry should be disabled`);
+ assert.strictEqual(entry.bar_fraction, 0, `${language} biome bar`);
+
+ // biome leads the cross-tool comparisons: directly after the single canonical
+ // row (index 0), before tsv's json wires
+ const names = group.entries.map((e) => e.name);
+ const first_biome = names.findIndex((n) => n.includes('biome'));
+ const first_json = names.findIndex((n) => n.endsWith('-json') || n.endsWith('-no-locations'));
+ assert.strictEqual(first_biome, 1, `${language} biome directly after the canonical row`);
+ assert.isBelow(first_biome, first_json, `${language} biome before the tsv json entries`);
+ }
+ });
+
+ test('format/parse rows follow the fixed canonical → biome → oxc → json → internal order', () => {
+ // independently mirrors the requested row order so a regression in the
+ // derivation's comparator fails here
+ const tier = (name: string, category: string): number => {
+ if (category === 'canonical') return 0;
+ if (category === 'biome') return 1;
+ if (category === 'oxc') return 2;
+ if (name.endsWith('-no-locations')) return 3; // tsv json, span-only wire
+ if (name.endsWith('-json')) return 4; // tsv json, loc-carrying wire
+ return 5; // tsv internal engine
+ };
+ for (const group of derive_benchmark_groups(benchmarks_json)) {
+ const key = `${group.operation}/${group.language}`;
+ const tiers = group.entries.map((e) => tier(e.name, e.category));
+ assert.deepEqual(
+ tiers,
+ [...tiers].toSorted((a, b) => a - b),
+ `${key} tier order`,
+ );
+ // wasm precedes native within a tier
+ for (let i = 1; i < group.entries.length; i++) {
+ if (tiers[i] !== tiers[i - 1]) continue;
+ const prev_wasm = group.entries[i - 1]!.name.includes('wasm');
+ const curr_wasm = group.entries[i]!.name.includes('wasm');
+ assert.isFalse(!prev_wasm && curr_wasm, `${key} native precedes wasm within a tier`);
+ }
}
});
@@ -54,4 +161,289 @@ describe('benchmarks.json shape', () => {
assert.isDefined(row.format_css, row.variant);
}
});
+
+ test('binary sizes group by capability with one smallest-build ratio anchor', () => {
+ const groups = derive_size_groups(benchmarks_json.binary_sizes);
+ // full / formatter / parser, in that order, all present in the current data
+ assert.deepStrictEqual(
+ groups.map((g) => g.capability),
+ ['full', 'formatter', 'parser'],
+ );
+ for (const group of groups) {
+ assert.isNotEmpty(group.entries);
+ // every entry lands in the group its capability names
+ for (const e of group.entries) {
+ assert.strictEqual(categorize_size_capability(e.label), group.capability);
+ }
+ // the group's default ratio anchor (its 1.0x row) is the single smallest real
+ // build, and it leads the group — the shared component reads every ratio off
+ // the first row. Disabled placeholders (e.g. oxfmt's absent wasm build) never
+ // sort first.
+ const real = group.entries.filter((e) => !e.disabled);
+ const smallest = real.reduce((a, b) => (a.bytes <= b.bytes ? a : b));
+ assert.strictEqual(
+ group.entries[0]?.label,
+ smallest.label,
+ `${group.capability} smallest leads`,
+ );
+ }
+ // the parser group pits tsv against oxc-parser in both kinds
+ const parser = groups.find((g) => g.capability === 'parser');
+ const parser_labels = parser?.entries.map((e) => e.label) ?? [];
+ assert.include(parser_labels, 'oxc-parser (wasm)');
+ assert.include(parser_labels, 'oxc-parser (napi)');
+ });
+
+ test('full toolchain group carries the combined oxc-parser + oxfmt entry', () => {
+ const sizes = benchmarks_json.binary_sizes;
+ const groups = derive_size_groups(sizes);
+ const full = groups.find((g) => g.capability === 'full');
+ const combined = full?.entries.find((e) => e.label === OXC_FULL_LABEL);
+ assert.ok(combined, 'combined oxc entry missing from full toolchain group');
+
+ // its bytes and gzip are the sum of oxc's separate parser and formatter builds
+ const oxc_parser = sizes.find((s) => s.label === 'oxc-parser (napi)');
+ const oxfmt = sizes.find((s) => s.label === 'oxfmt (napi)');
+ assert.ok(oxc_parser && oxfmt, 'source oxc builds missing');
+ assert.strictEqual(combined.bytes, oxc_parser.bytes + oxfmt.bytes);
+ assert.strictEqual(combined.gzip_bytes, oxc_parser.gzip_bytes! + oxfmt.gzip_bytes!);
+
+ // native build, colored as oxc
+ assert.strictEqual(combined.kind, 'native');
+ assert.strictEqual(combined.category, 'oxc');
+ });
+
+ test('formatter group gets a disabled oxfmt (wasm) placeholder just above oxfmt (napi), since oxfmt has no wasm build', () => {
+ const groups = derive_size_groups(benchmarks_json.binary_sizes);
+ const formatter = groups.find((g) => g.capability === 'formatter');
+ assert.ok(formatter, 'formatter group missing');
+
+ const placeholder = formatter.entries.find((e) => e.label === OXFMT_WASM_LABEL);
+ assert.ok(placeholder, 'oxfmt (wasm) placeholder missing');
+ assert.ok(placeholder.disabled, 'oxfmt (wasm) should be disabled');
+ assert.strictEqual(placeholder.kind, 'wasm');
+ assert.strictEqual(placeholder.category, 'oxc');
+ assert.strictEqual(placeholder.bar_fraction, 0);
+
+ const labels = formatter.entries.map((e) => e.label);
+ const placeholder_index = labels.indexOf(OXFMT_WASM_LABEL);
+ const native_index = labels.indexOf('oxfmt (napi)');
+ assert.isAbove(native_index, -1, 'oxfmt (napi) missing from formatter group');
+ assert.strictEqual(
+ placeholder_index,
+ native_index - 1,
+ 'placeholder should sit just above oxfmt (napi)',
+ );
+ });
+
+ test('flagship report is the node runtime', () => {
+ // the headline detailed view switched to N-API under Node; guards against an
+ // `update-benchmarks` that pulls the wrong runtime's sibling report
+ assert.strictEqual(benchmarks_json.runtime, 'node');
+ });
+});
+
+// Shape gate for the committed conformance report `benchmarks_conformance.json`
+// (tsv's `report.conformance.node.json` — the parse-coverage surface over the
+// deliberately-hard fixture suites, disjoint from the perf corpus, Svelte set minus
+// canonical-rejects), consumed by the Parse conformance section.
+describe('benchmarks_conformance.json shape', () => {
+ test('report is the conformance surface at the current version', () => {
+ assert.isAtLeast(benchmarks_conformance_json.version, 6);
+ assert.strictEqual(benchmarks_conformance_json.corpus_kind, 'conformance');
+ assert.strictEqual(benchmarks_conformance_json.runtime, 'node');
+ });
+
+ test('conformance report is parse-only', () => {
+ for (const entry of benchmarks_conformance_json.entries) {
+ assert.match(entry.group, /^parse\//, `${entry.group}/${entry.name}`);
+ }
+ });
+
+ test('corpus sources disclose the composition', () => {
+ assert.isNotEmpty(benchmarks_conformance_json.corpus_sources ?? []);
+ });
+
+ test('derives one coverage group per language, each with a tsv row and full coverage data', () => {
+ const groups = derive_conformance_groups(benchmarks_conformance_json);
+ assert.strictEqual(groups.length, 3); // svelte / typescript / css
+ assert.deepEqual(
+ groups.map((g) => g.language),
+ ['svelte', 'typescript', 'css'],
+ );
+ for (const group of groups) {
+ assert.ok(
+ group.rows.some((r) => r.name === 'tsv'),
+ `${group.language} has a tsv row`,
+ );
+ // rows are ordered by coverage, highest first
+ const fractions = group.rows.map((r) => r.coverage_fraction);
+ assert.deepEqual(
+ fractions,
+ [...fractions].toSorted((a, b) => b - a),
+ `${group.language} rows descend by coverage`,
+ );
+ for (const row of group.rows) {
+ assert.isAbove(row.files_total, 0, `${group.language}/${row.name} total`);
+ assert.isAtLeast(row.coverage_fraction, 0);
+ assert.isAtMost(row.coverage_fraction, 1);
+ // engine-level rows only — binding/materialization variants are folded
+ assert.notMatch(row.name, /-internal|wasm-|-wasm/, `${group.language}/${row.name}`);
+ }
+ }
+ });
+
+ test('coverage percent floors — only exact totality reads 100%', () => {
+ // 44219/44220 rounds to 100.00% but must not display as it: floor, so a
+ // visibly non-total count never sits beside a "100.00%" label.
+ assert.strictEqual(format_coverage_percent(44_219 / 44_220), '99.99%');
+ assert.strictEqual(format_coverage_percent(1), '100.00%');
+ assert.strictEqual(format_coverage_percent(0.998549), '99.85%');
+ assert.strictEqual(format_coverage_percent(0), '0.00%');
+ });
+});
+
+describe('derive_corpus_repos', () => {
+ test('maps the committed corpus sources to deduped org/name repo links', () => {
+ const repos = derive_corpus_repos(benchmarks_json.corpus_sources);
+ assert.isNotEmpty(repos);
+ // one entry per URL — no repo appears twice even though svelte.dev contributes
+ // several source subpaths
+ const urls = repos.map((r) => r.url);
+ assert.strictEqual(new Set(urls).size, urls.length, 'urls are distinct');
+ const svelte_dev = repos.filter((r) => r.url === 'https://github.com/sveltejs/svelte.dev');
+ assert.strictEqual(svelte_dev.length, 1, 'svelte.dev collapses to one entry');
+ // each label is the linkified `org/name`, derived from (and ending) its URL
+ for (const repo of repos) {
+ assert.match(repo.label, /^[^/]+\/[^/]+$/, repo.url);
+ assert.isTrue(repo.url.endsWith(repo.label), `${repo.url} ends with ${repo.label}`);
+ }
+ });
+
+ test('collapses shared repos and drops sources with no mapped repo', () => {
+ const repos = derive_corpus_repos([
+ {path: '../zzz/src', files: 1},
+ {path: '../svelte.dev/apps/svelte.dev/src', files: 1},
+ {path: '../svelte.dev/packages/repl/src', files: 1}, // same repo → collapsed
+ {path: 'benches/js/.cache/svelte_styles', files: 1}, // not a repo → dropped
+ ]);
+ assert.deepStrictEqual(repos, [
+ {url: 'https://github.com/fuzdev/zzz', label: 'fuzdev/zzz'},
+ {
+ url: 'https://github.com/sveltejs/svelte.dev',
+ label: 'sveltejs/svelte.dev',
+ },
+ ]);
+ });
+
+ test('handles a missing corpus_sources field', () => {
+ assert.deepStrictEqual(derive_corpus_repos(undefined), []);
+ });
+});
+
+describe('format_speedup_signed', () => {
+ test('anchor-and-faster entries read as a plain multiple', () => {
+ assert.strictEqual(format_speedup_signed(1), '1.00x');
+ assert.strictEqual(format_speedup_signed(2.5), '2.50x');
+ assert.strictEqual(format_speedup_signed(12.3), '12.3x'); // >= 10 drops to one decimal
+ });
+
+ test('slower entries negate the reciprocal so the factor is directly legible', () => {
+ assert.strictEqual(format_speedup_signed(0.15), '-6.67x');
+ assert.strictEqual(format_speedup_signed(0.05), '-20.0x'); // >= 10 magnitude → one decimal
+ assert.strictEqual(format_speedup_signed(0.98), '-1.02x'); // near-parity sign flip
+ });
+});
+
+describe('compute_baseline_ratio', () => {
+ test('speed reads the anchor as the reference — faster entries exceed 1', () => {
+ // anchor 100ns; a 50ns entry is 2x faster, a 200ns entry is half the speed
+ assert.strictEqual(compute_baseline_ratio('speed', 50, 100), 2);
+ assert.strictEqual(compute_baseline_ratio('speed', 200, 100), 0.5);
+ });
+
+ test('size reads the anchor as the reference — bigger entries exceed 1', () => {
+ // anchor 100 bytes; a 300-byte build is 3x bigger, a 50-byte build is half
+ assert.strictEqual(compute_baseline_ratio('size', 300, 100), 3);
+ assert.strictEqual(compute_baseline_ratio('size', 50, 100), 0.5);
+ });
+});
+
+describe('order_cross_runtime_runtimes', () => {
+ test('reorders the report storage order to node-first display order', () => {
+ assert.deepStrictEqual(order_cross_runtime_runtimes(['deno', 'node', 'bun']), [
+ 'node',
+ 'deno',
+ 'bun',
+ ]);
+ });
+
+ test('anchors on the next runtime in display order when node is absent', () => {
+ assert.deepStrictEqual(order_cross_runtime_runtimes(['bun', 'deno']), ['deno', 'bun']);
+ assert.deepStrictEqual(order_cross_runtime_runtimes([]), []);
+ });
+});
+
+// Shape gate for the committed cross-runtime `benchmarks_cross_runtime.json` (the
+// bench composer's combined `report.json`) — a different, slimmer shape than the
+// per-runtime baseline, consumed by the Cross-runtime section.
+describe('benchmarks_cross_runtime.json shape', () => {
+ test('combined report carries the current version and kind', () => {
+ assert.isAtLeast(benchmarks_cross_runtime_json.version, 5);
+ assert.strictEqual(benchmarks_cross_runtime_json.kind, 'combined');
+ // the committed fixture must be same-vintage — if this trips, re-run every
+ // runtime and recompose rather than committing a mixed set (the site would
+ // show the unreliable-ratios warning banner)
+ assert.notStrictEqual(benchmarks_cross_runtime_json.mixed_vintage, true);
+ });
+
+ test('runtimes include the flagship and its cross-runtime peers', () => {
+ const {runtimes} = benchmarks_cross_runtime_json;
+ assert.include(runtimes, 'node'); // the flagship the headline view leads with
+ assert.include(runtimes, 'deno');
+ assert.include(runtimes, 'bun');
+ });
+
+ test('every group derives rows with the flagship runtime populated', () => {
+ const groups = derive_cross_runtime_groups(benchmarks_cross_runtime_json);
+ assert.isAtLeast(groups.length, 6); // format+parse × svelte/typescript/css
+ for (const group of groups) {
+ assert.isAbove(group.rows.length, 0, group.group);
+ for (const row of group.rows) {
+ assert.isNumber(row.ops_per_second.node, `${group.group}/${row.name} missing node ops`);
+ // ratios anchor on node (the display-order base, not the report's
+ // deno-first storage order), so node's own ratio is exactly 1
+ assert.strictEqual(row.ratio_vs_base.node, 1, `${group.group}/${row.name} node anchor`);
+ }
+ }
+ });
+});
+
+// Shape gate for the hand-maintained CLI snapshot `benchmarks_cli.ts` (copied from
+// the linked oxc-bench-formatter fork). Unlike the JSON reports it isn't generated,
+// so a bad hand-edit (a scenario missing its `tsv` reference row, a zeroed metric)
+// would silently render an empty table rather than fail to typecheck.
+describe('benchmarks_cli shape', () => {
+ test('every scenario carries a tsv reference row with positive metrics', () => {
+ assert.isAtLeast(benchmarks_cli.scenarios.length, 1);
+ for (const scenario of benchmarks_cli.scenarios) {
+ // BenchmarksCli anchors every ratio on the tsv row; without it the table is empty
+ assert.ok(
+ scenario.results.some((r) => r.label === 'tsv'),
+ `${scenario.key} is missing its tsv row`,
+ );
+ for (const r of scenario.results) {
+ assert.isAbove(r.wall_ms, 0, `${scenario.key}/${r.label} wall_ms`);
+ assert.isAbove(r.cpu_ms, 0, `${scenario.key}/${r.label} cpu_ms`);
+ assert.isAbove(r.memory_mb, 0, `${scenario.key}/${r.label} memory_mb`);
+ }
+ }
+ });
+
+ test('source links point at the fork (on its tsv branch) and its upstream', () => {
+ // the fork's `main` tracks upstream and carries neither tsv nor the analysis,
+ // so every fork link must target the `tsv` branch
+ assert.match(benchmarks_cli.source_url, /ryanatkn\/oxc-bench-formatter\/tree\/tsv$/);
+ assert.match(benchmarks_cli.upstream_url, /oxc-project\/bench-formatter/);
+ });
});
diff --git a/svelte.config.js b/svelte.config.js
index 1fd7b37..fec5a80 100644
--- a/svelte.config.js
+++ b/svelte.config.js
@@ -16,6 +16,8 @@ export default {
paths: {relative: false}, // use root-absolute paths for SSR path comparison: https://svelte.dev/docs/kit/configuration#paths
alias: {$routes: 'src/routes'},
version: {name: execSync('git rev-parse HEAD').toString().trim()},
- csp: {directives: create_csp_directives({extend: [csp_directives_of_fuzdev]})},
+ csp: {
+ directives: create_csp_directives({extend: [csp_directives_of_fuzdev]}),
+ },
},
};
diff --git a/tsconfig.json b/tsconfig.json
index 0300902..6e255c0 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -1,7 +1,9 @@
{
"extends": "./.svelte-kit/tsconfig.json",
"compilerOptions": {
- "types": ["@sveltejs/kit"],
+ "types": [
+ "@sveltejs/kit"
+ ],
"module": "nodenext",
"moduleResolution": "nodenext",
"strict": true,