Repository navigation
Description
Part of #649 (ruling 3: this is the first benchmark). Related: #554, and #554 Q7: a swap to bun check reopens the Next type-check skip.
Benchmark
| Field | Value |
|---|---|
| Metric | (1) Type-check wall time for all 15 workspaces, in seconds, and peak RSS in MiB. (2) Gate: differing diagnostic lines per workspace, split into "TS 7 change" and "bun check only". Exact count. |
| Before | Laptop: not measured yet (agent in the repo, Step 0, before any other step). CI reference, from the 2026-11-11 milestone review read of the Run Report: 📝 Type Check All step median 22 s (18–32 s, n = 7, 2026-11-07 to 2026-11-11). |
| After | Not measured yet. |
| Condition | Named laptop (model, cores, RAM, OS). Bun 1.4.4, tsc 5.9.3 and 6.0.3, the TS 7 control version, pinned to one exact typescript@7.x.y and recorded here before Step 1. HEAD SHA. Fixed .tsbuildinfo and .next/types state. No other load. |
| How to run | Step 0: bun run typecheck, n = 5 after one discarded warm-up. Step 4: the same command shape with bun check in each workspace, n = 5 after one discarded warm-up. Time with /usr/bin/time -l on macOS or /usr/bin/time -v on Linux. Diagnostics: Steps 1 to 3. CI: read 📝 Type Check All from 5 runs where the job ran. |
| Pass rule | Adopt only if "bun check only" diffs are 0 in every workspace and the whole run is at least 2x faster (median against median). Any "bun check only" diff: do not adopt; post the diff. Under 2x: close as not worth the change. Timing runs in every case. |
| Guard | Seeded-error control: both checkers fail in every workspace with the same TS code. This gate is the only type check before a webapp deploy, because #560 skips the one in the webapp Docker build. |
| Owner | Agent in the repo: Steps 0 to 6 and the Measured comment. Maintainer: rules the kill rule and the swap. |
| Claim | Adopt: "Type-checking all 15 docs.plus workspaces fell from s to s on (median of 5)." Kill: "bun check reported diagnostics that tsc did not, so docs.plus did not adopt it." |
Why
Bun 1.4.3 added bun check, a port of typescript-go. Vendor numbers: 3.0–6.4x faster than tsc 7.0.2, 2.2–4.9x less memory, and 51,210 of 51,210 TypeScript conformance tests passed.
Our baseline is the JavaScript tsc 5.9.3 and 6.0.3, not tsc 7, so the vendor ratio does not predict our gain. The real gate is whether the diagnostics match.
Our typecheck runs tsc --noEmit in 15 workspaces (HEAD 0b302b222) through bun run --filter '*' typecheck. Three extensions also run tsc -p tsconfig.test.json. It is a check:ci gate (scripts/check-ci.ts:244), so it runs on every pre-push. The CI Type Check job is path-gated (the 📝 Type Check job and its 📝 Type Check All step in prod.docs.plus.yml).
CI before, 2026-11-11: 📝 Type Check All median 22 s (18–32 s, n = 7). The step is off the deploy critical path, so a faster check cannot move M1 (#569). The gain is the local pre-push loop and PR feedback.
What bun check must match
- It follows TypeScript 7.1. 11 workspaces use
tsc5.9.3 through the catalog. 4 use 6.0.3: webapp, hocuspocus.server, email-templates and pad-url. Most workspaces therefore jump two majors. - The root
tsconfig.jsonis not in the gate. No workspace extends it. apps/admin-dashboard/tsconfig.json:5sets"baseUrl": ".", and itspathsentries (:19-27) have no./prefix. The Bun docs say TypeScript 7 reports TS5102 forbaseUrl.tsconfig.base.json:15sets"ignoreDeprecations": "5.0", and 13 configs inherit it. Its TypeScript 7 behaviour is not verified.apps/webappandapps/admin-dashboardsetincremental: true(tsconfig.json:17). A warmtscrun there reuses.tsbuildinfo. CI starts with none, andbun checkwrites none.next-env.d.tsin both apps reads.next/types/. Record whether those files exist, and keep that state fixed for every run. No tsconfig setsplugins, so the Next plugin is not a factor.
Steps
Run on Bun 1.4.4, on the real tree. Never use a branch or a worktree. Pass --incremental false to every tsc control run, so the repo's .tsbuildinfo is not overwritten.
- 0. Before. On the named laptop, time
bun run typecheck5 times after one discarded run, before any other step. Record median and min–max wall time and peak RSS. - 1. Diagnostics first, one run per workspace. Run
tsc --noEmit --pretty false --incremental falseandbun check --no-script --no-pretty. In the three extensions, also run both with-p tsconfig.test.json. Normalise tofile:line:col TScodeand diff.- TS 7 control. Also run
bunx --package typescript@<7.x.y> tsc --noEmit --pretty falseper workspace. Use the one exact version recorded in Condition for every run, so the control does not float. Sort each diff line as "TS 7 change" (tsc 7 shows it too) or "bun check only".
- TS 7 control. Also run
- 2. Same files. Compare
tsc --listFilesOnlywithbun check --no-script --listFiles, without lib files.- Pre-check:
bun check --no-script --listFiles | headmust print paths. If not, use--explainFiles. If neither works, drop Step 2 and list it under "Not measured".
- Pre-check:
- 3. Non-empty control. Run both with
--noUncheckedIndexedAccess --incremental false. Both must report the same errors.- Seeded-error control. In a
git archivecopy, put a wrong argument type, a missing property and an import of a missing export in each workspace. Both checkers must fail in every workspace with the same TS code.
- Seeded-error control. In a
- 4. Timing, in every case. Label it "not adoptable" when "bun check only" diffs exist. Use n = 5 per command after one discarded warm-up, and report median and min–max wall time and peak RSS.
- Use
/usr/bin/time -lon macOS or/usr/bin/time -von Linux for peak RSS. Name the model, cores, RAM, OS, Bun andtscversions, and the.tsbuildinfoand.next/typesstate. - In
apps/webappandapps/admin-dashboard, timetsctwice: as the script runs (buildinfo present, like pre-push), and with--incremental false(like CI). - Whole run: run both checkers the same way. Either run both through
bun run --filter '*'with a temporary script, or run both in a sequential loop.
- Use
- 5. CI. Read
📝 Type Check Allfrom 5 recent runs where the job ran: median and min–max. A CIbun checknumber needs a CI run. That is out of scope until the swap issue. - 6. Post a
Measuredcomment in the [Tracking] Bun 1.4.x: release-age guard, bunfig cleanup, and measured bun check, bun fmt and bun test numbers #649 format, with every table.
Kill rules
- Any "bun check only" diagnostics difference in any workspace: do not adopt. Post the diff, still post the Step 4 timing, and raise the TypeScript 7 question as a new issue. A "TS 7 change" diff alone does not kill adoption; list it for the TypeScript 7 question.
- No "bun check only" diffs, but the whole run falls by less than 2x: close as not worth the change.
- Otherwise: open the swap as a child of [Tracking] Faster, change-aware CI/CD pipeline #554, listing every script to change. The swap child keeps
tscas a one-line rollback.
Decisions (only if the diagnostics match and the gain is at least 2x)
- Swap only the
check:cigate, or every workspacetypecheckscript? Recommended: everytypecheckscript, so one checker runs everywhere. - Keep the
typescriptpackage? Recommended: yes. Editors,next build, the tsupdtsbuilds andtypescript-eslintstill need it.
Activity
HMarzban commentedon Nov 12, 2026
Measured, 2026-11-12
Question: Does
bun checkreport the same diagnostics as ourtscin all 15 workspaces, and is the whole type-check run at least 2x faster?Setup
cfac48d87(the issue names0b302b222; HEAD moved before the run)tscbun.lock)typescript@7.0.2, thelatesttag, released 2026-07-08. One version for every control run.git archive cfac48d87copy, outside the repo. Thenbun install --frozen-lockfile --ignore-scripts,bun x prisma generatein hocuspocus.server, andbash scripts/build-extensions.sh. No env files.next-env.d.tsand no.next/typesin either app, the same as a CI checkout..tsbuildinfo--incremental false.The issue says "run on the real tree". Other changes were open in the working tree, so every run used the copy. The copy also matches CI.
Method
/usr/bin/time -l bun run typecheck, 1 discarded warm-up, then timed runs until 5 counted.-p tsconfig.test.jsonin indent, hyperlink and mermaid).tsc --noEmit --pretty false --incremental falsebun check --no-script --no-pretty --allbunx --package typescript@7.0.2 tsc --noEmit --pretty false --incremental falsefile:line:col TScodeand sorted. A tsc/bun difference that TS 7.0.2 shares is a "TS 7 change". Any other difference is "bun check only".tsc --listFilesOnlyagainstbun check --no-script --listFiles, lib files removed, paths resolved.--noUncheckedIndexedAccess. Seeded errors ran in a second copy, with two new files in each workspace'ssrc/. They hold a wrong argument type (TS2345), a missing property (TS2741) and an import of a missing export (TS2305).typecheckscript pointed atbun check --no-script, the three extensions also with-p tsconfig.test.json. Thenbun run typecheck, so the shape matches Step 0. The four shapes ran in turn, round by round, 1 discarded warm-up, then runs until 5 counted each.tsc --noEmitwith buildinfo,tsc --noEmit --incremental false, andbun check --no-script.📝 Type Check Allstep start and end, fromgh api .../actions/runs/<id>/jobs, for the 5 latestprod.docs.plus.ymlruns where the job ran./usr/bin/time -l. Forbun run --filter, it is the largest single process, not the sum.Result: diagnostics (Steps 1 to 3)
--noUncheckedIndexedAccessThe 12 Step 1 lines, all also reported by TS 7.0.2:
tsconfig.json:5:5 TS5102,tsconfig.json:20-27 TS5090(8 lines)baseUrlis removed in TS 7, and the 8pathsentries lack./src/modules/document-conversion/domain/markdownExport.ts:34:3 TS2322,:35:20 TS2322-p tsconfig.test.jsonsrc/link-icons/__tests__/setupDom.ts:38:1 TS2322Element.matchesmust now be a type predicatebun check. It stops on the 9 config errors: "Stopped before type checking 74 files". So its Step 1 "0 bun check only" says nothing about its code.baseUrland added./to the 8paths. Then all three checkers gave 0 lines in admin-dashboard, and 5 equal lines each under--noUncheckedIndexedAccess.tsc5.9.3 also passes with that config.--noUncheckedIndexedAccess:tsc698 lines,bun check708, TS 7.0.2 708. The 26 diff lines are admin-dashboard (9 config lines, plus 5tsclines thatbun checknever reached), hocuspocus.server (11) and the hyperlink test config (1). In hocuspocus.server, 9 of its 11 lines are TS2769 overload errors in one test file. TS 7 puts them at other columns: 6 lines appear and 3 go.bun checkfails with TS5102 and TS5090 only. With the scratch config fix, it gives the 3 seeded codes too.tsconly) and webapp (4tsconly, 1bun checkonly), all undernode_modules.Result: timing (Steps 0, 4 and 5)
Whole run,
bun run typecheck, all 15 workspaces, n = 5 each, 1 warm-up discarded:tsc, buildinfo presenttsc, buildinfo presenttsc, apps with--incremental false(CI-like)bun check(not adoptable as is: exit 1)bun check, admin-dashboard config fixed in the copyRatios, median against median: 5.97 / 0.56 = 10.7x from the Step 0 before. 5.53 / 0.56 = 9.9x for the interleaved pair. 6.59 / 0.56 = 11.8x against the CI-like run. Median CPU time was 19.9 s user for Step 0 and 2.2 s user for
bun check.bun checkexits 1 because of the 12 "TS 7 change" lines above. All 15 workspaces still ran to the end in every run.Apps alone, n = 5 each, 1 warm-up discarded:
tsc --noEmit, buildinfo presenttsc --noEmit --incremental falsebun check --no-scripttsc --noEmit, buildinfo presenttsc --noEmit --incremental falsebun check --no-script(checks 0 files)bun check --no-script, config fixed in the copyCI,
📝 Type Check Allstep,tsc, 5 latestprod.docs.plus.ymlruns where the job ran:Step times come from whole-second timestamps. A CI
bun checknumber needs a CI run, so it waits for the swap issue.Raw data
Attached:
654-step0-runs.jsonl,654-step4-whole.jsonl,654-step4-apps.jsonl,654-step1-summary.json,654-step2-listfiles.json,654-step3-nuia-summary.json,654-step3-seeded-summary.json,654-step5-ci.tsv. Each timing line holds the load average and free memory at its start.Not measured
tscscript. The CI-like row uses--incremental falseinstead.bun checkin CI.next build, tsupdtsandtypescript-eslintbehaviour. They keep thetypescriptpackage.Verdict
Adopt rule met: 0 "bun check only" lines in 18 configs, and the whole run is 9.9x faster. The swap child of #554 must first clear the 12 "TS 7 change" lines, because
bun checkexits 1 atcfac48d87. The maintainer rules the swap.HMarzban commentedon Nov 14, 2026
Ruling, 2026-11-14: adopt
The maintainer ruled the swap at the v2.3.6 release audit. The adopt rule passed: 0 "bun check only" lines in 18 configs, and the whole run is 9.9x faster (Measured).
typecheckscript.typescriptpackage.The swap is #794, a child of #554. It clears the 12 "TS 7 change" lines first, because
bun checkexits 1 on them today. It keepstscas a one-line rollback.Closing as completed.