Skip to content

Apply per-file config across all CLI apps#373

Merged
frostney merged 5 commits intomainfrom
t3code/all-cli-apps
Apr 21, 2026
Merged

Apply per-file config across all CLI apps#373
frostney merged 5 commits intomainfrom
t3code/all-cli-apps

Conversation

@frostney
Copy link
Copy Markdown
Owner

Summary

  • Extend config discovery so each CLI app resolves the nearest goccia.json / json5 / toml for the file or directory being processed.
  • Apply per-file settings for asi, compat-var, and max-memory, while keeping CLI flags as the highest-priority override.
  • Update GocciaScriptLoader, GocciaTestRunner, GocciaBundler, and GocciaBenchmarkRunner to use the effective engine settings consistently.
  • Add FindConfigEntry helpers and unit coverage for config lookup behavior.
  • Expand CI coverage to verify per-file config handling across all CLI apps, including directory bundling and bytecode mode.

Testing

  • source/shared/CLI.ConfigFile.Test.pas: added tests for FindConfigEntry returning matches, missing keys, and duplicate-key precedence.
  • .github/workflows/pr.yml: added integration checks for per-file config discovery in GocciaScriptLoader, GocciaTestRunner, GocciaBundler, and bytecode mode.
  • Not run locally.

- Discover nearest goccia.json/json5/toml for each input file
- Apply ASI, compat-var, and max-memory from file config unless overridden by CLI
- Add coverage for loader, bundler, test runner, and config lookup
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 21, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d5d2cd5b-4990-4a9f-89c6-1c568313b31e

📥 Commits

Reviewing files that changed from the base of the PR and between 5aee020 and 37c45bb.

📒 Files selected for processing (3)
  • scripts/test-cli-config.ts
  • scripts/test-cli-lexer.ts
  • scripts/test-cli-parser.ts
✅ Files skipped from review due to trivial changes (1)
  • scripts/test-cli-parser.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • scripts/test-cli-lexer.ts
  • scripts/test-cli-config.ts

📝 Walkthrough

Walkthrough

Per-file config discovery/application was added to CLI apps; parser/compile APIs now accept explicit ASI/compat-var booleans sourced from the Engine instance. CI removed large inline shell CLI tests and now runs Bun-based TypeScript CLI test scripts.

Changes

Cohort / File(s) Summary
Config core
source/shared/CLI.ConfigFile.pas, source/shared/CLI.ConfigFile.Test.pas
Added FindConfigEntry helper and three unit tests to support first-match config lookup used by CLI apps.
CLI application wiring
source/app/Goccia.CLI.Application.pas
Added DiscoverFileConfig and ApplyFileConfigToEngine; Engine creation now applies per-file config (ASI, compat-var, max-memory) with CLI > file > default precedence.
Script loader & bundler
source/app/GocciaScriptLoader.dpr, source/app/GocciaBundler.dpr
Parse/compile signatures accept explicit AASIEnabled/AVarEnabled; callers compute effective per-file flags and pass them to parsing instead of reading global EngineOptions or stored fields.
Runners (Test / Benchmark)
source/app/GocciaTestRunner.dpr, source/app/GocciaBenchmarkRunner.dpr
Removed TestRunner’s local per-file var detection; BenchmarkRunner now sources ASI/compat-var from the Engine instance for parsing.
CI / test scripts
.github/workflows/.../pr.yml, .github/workflows/.../ci.yml, scripts/test-cli*.ts, scripts/test-cli-apps.ts
Replaced extensive inline shell-based CLI smoke/regression checks with Bun-based TypeScript test scripts and added Bun setup in CI; delegated prior inline assertions to these scripts.
Misc / signatures
source/app/GocciaBundler.dpr
Removed stored per-instance flag fields; updated method signatures and callers to accept explicit per-file booleans.

Sequence Diagram

sequenceDiagram
    participant CLI as "CLI App"
    participant Discover as "DiscoverFileConfig"
    participant Config as "goccia.*"
    participant Apply as "ApplyFileConfigToEngine"
    participant Engine as "Engine Instance"
    participant Parser as "Parser"

    CLI->>Discover: DiscoverFileConfig(entryFilename)
    Discover->>Config: Search upward for goccia.json/json5/toml
    Config-->>Discover: Return TConfigEntryArray (or empty)
    CLI->>Apply: ApplyFileConfigToEngine(Engine, CLIFlags, FileConfig)
    Apply->>Engine: Set ASIEnabled / VarEnabled / MaxMemory (respecting CLI > File > Default)
    CLI->>Parser: ParseSource(..., asi=Engine.ASIEnabled, var=Engine.VarEnabled, ...)
    Parser-->>Engine: Use provided flags to configure parsing behavior
    Parser-->>CLI: Return parsed program or errors
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related issues

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: extending config discovery and application across all CLI apps.
Description check ✅ Passed The description covers the main objectives, testing approach, and key implementation details, though it notes tests were not run locally.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot added new feature New feature or request internal Refactoring, CI, tooling, cleanup labels Apr 21, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.github/workflows/pr.yml:
- Around line 1201-1204: The CI step temporarily disables errexit with "set +e"
then runs "./build/GocciaScriptLoader ... | grep -q 'SyntaxError'"; capture the
pipeline's exit status into a variable (e.g., status=$?) immediately after that
command returns, then re-enable "set -e" and assert/test the captured status
(e.g., test $status -eq 0) so the failure is not ignored; locate the block using
the commands "set +e", "./build/GocciaScriptLoader", "grep -q 'SyntaxError'",
and "set -e" to apply this change.

In `@source/app/Goccia.CLI.Application.pas`:
- Around line 452-460: The per-file max-memory logic is sticky because
AEngineOptions.MaxMemory.Present stays true after the global config is applied
and missing per-file entries leave GC.MaxBytes unchanged; update the branch in
the code around ApplyMaxMemory/AEngineOptions.MaxMemory.Present so you only
apply the global/CLI max-memory when the value was provided via the CLI (i.e.
distinguish CLI-origin vs. already-applied top-level config), otherwise consult
the per-file entry via FindConfigEntry/TryStrToInt64 and set
TGarbageCollector.Instance.MaxBytes to that parsed MemoryLimit; if no per-file
override is present explicitly, explicitly reset GC.MaxBytes to the default
(e.g. 0 or call the GC reset API) instead of leaving the previous value in
place.
- Around line 440-450: The current logic treats AEngineOptions.ASI.Present and
CompatVar.Present as "came from CLI", but Execute() already applied file config
into FAllOptions so Present can be true for config-derived values; update the
branches that set AEngine.ASIEnabled and AEngine.VarEnabled to only honor the
CLI override when the option actually originated from the command line (e.g.,
check an origin/source flag such as an IsFromCLI/Origin field on
AEngineOptions.ASI and .CompatVar) and otherwise fall back to reading the
per-file config via FindConfigEntry('asi', ValueStr) and
FindConfigEntry('compat-var', ValueStr) as currently done.

In `@source/app/GocciaBundler.dpr`:
- Around line 200-215: EngineOptions.ASI.Present and
EngineOptions.CompatVar.Present are being read after ApplyConfigFile mutated
them, so snapshot the original CLI-origin flags before any config application
and use those snapshots when resolving per-file EffectiveASI/EffectiveVar; e.g.,
in TGocciaCLIApplication.Execute capture booleans like CLIOverrideASI and
CLIOverrideCompatVar from EngineOptions.ASI.Present /
EngineOptions.CompatVar.Present before calling ApplyConfigFile, then either pass
those CLI-only booleans into CompileSource (and replace checks of
EngineOptions.ASI.Present / EngineOptions.CompatVar.Present with the new
CLIOverrideXXX variables) or reference the snapshots where EffectiveASI and
EffectiveVar are computed so per-file goccia.* config can override unless the
original CLI flag was truly supplied.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 74bada72-ea48-4a35-95cc-b5f7bd58cc31

📥 Commits

Reviewing files that changed from the base of the PR and between 267b593 and fa45700.

📒 Files selected for processing (8)
  • .github/workflows/pr.yml
  • source/app/Goccia.CLI.Application.pas
  • source/app/GocciaBenchmarkRunner.dpr
  • source/app/GocciaBundler.dpr
  • source/app/GocciaScriptLoader.dpr
  • source/app/GocciaTestRunner.dpr
  • source/shared/CLI.ConfigFile.Test.pas
  • source/shared/CLI.ConfigFile.pas

Comment thread .github/workflows/pr.yml Outdated
Comment thread source/app/Goccia.CLI.Application.pas
Comment thread source/app/Goccia.CLI.Application.pas
Comment thread source/app/GocciaBundler.dpr
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 21, 2026

Benchmark Results

386 benchmarks

Interpreted: 🟢 25 improved · 🔴 342 regressed · 19 unchanged · avg -7.0%
Bytecode: 🟢 82 improved · 🔴 57 regressed · 247 unchanged · avg +1.6%

arraybuffer.js — Interp: 🔴 12, 2 unch. · avg -8.6% · Bytecode: 🔴 3, 11 unch. · avg -0.2%
Benchmark Interpreted Δ Bytecode Δ
create ArrayBuffer(0) 197,540 ops/sec [132,336..202,513] → 166,301 ops/sec [121,443..183,039] ~ overlap (-15.8%) 231,375 ops/sec [216,171..235,150] → 233,246 ops/sec [192,849..234,189] ~ overlap (+0.8%)
create ArrayBuffer(64) 193,626 ops/sec [189,883..195,959] → 174,681 ops/sec [173,324..176,625] 🔴 -9.8% 223,740 ops/sec [217,496..224,585] → 222,874 ops/sec [221,602..223,844] ~ overlap (-0.4%)
create ArrayBuffer(1024) 160,087 ops/sec [159,448..161,107] → 149,735 ops/sec [146,629..150,563] 🔴 -6.5% 180,920 ops/sec [177,021..187,678] → 186,233 ops/sec [184,294..187,799] ~ overlap (+2.9%)
create ArrayBuffer(8192) 80,705 ops/sec [79,760..82,088] → 80,851 ops/sec [79,902..82,423] ~ overlap (+0.2%) 88,464 ops/sec [86,140..91,112] → 91,528 ops/sec [89,418..91,766] ~ overlap (+3.5%)
slice full buffer (64 bytes) 210,817 ops/sec [208,652..212,667] → 193,651 ops/sec [189,667..195,284] 🔴 -8.1% 272,160 ops/sec [270,944..272,915] → 272,462 ops/sec [266,952..275,713] ~ overlap (+0.1%)
slice half buffer (512 of 1024 bytes) 185,991 ops/sec [179,978..188,428] → 174,063 ops/sec [172,731..175,737] 🔴 -6.4% 237,104 ops/sec [236,783..239,629] → 230,553 ops/sec [228,149..235,641] 🔴 -2.8%
slice with negative indices 178,030 ops/sec [176,316..179,938] → 159,500 ops/sec [156,050..161,667] 🔴 -10.4% 249,031 ops/sec [245,489..251,086] → 241,272 ops/sec [238,507..242,404] 🔴 -3.1%
slice empty range 200,876 ops/sec [199,486..205,861] → 184,026 ops/sec [182,489..188,027] 🔴 -8.4% 258,475 ops/sec [256,420..259,716] → 251,363 ops/sec [250,567..254,513] 🔴 -2.8%
byteLength access 486,171 ops/sec [483,255..491,524] → 429,444 ops/sec [420,020..436,029] 🔴 -11.7% 690,696 ops/sec [675,987..697,847] → 680,323 ops/sec [667,834..699,929] ~ overlap (-1.5%)
Symbol.toStringTag access 394,933 ops/sec [391,693..395,944] → 358,343 ops/sec [354,615..365,337] 🔴 -9.3% 435,567 ops/sec [427,403..437,578] → 430,257 ops/sec [420,650..436,929] ~ overlap (-1.2%)
ArrayBuffer.isView 293,238 ops/sec [286,929..294,815] → 263,505 ops/sec [259,647..269,512] 🔴 -10.1% 352,443 ops/sec [344,174..360,885] → 349,266 ops/sec [343,323..355,616] ~ overlap (-0.9%)
clone ArrayBuffer(64) 189,803 ops/sec [187,034..190,562] → 173,451 ops/sec [168,175..175,262] 🔴 -8.6% 228,112 ops/sec [227,573..229,762] → 229,786 ops/sec [228,782..230,323] ~ overlap (+0.7%)
clone ArrayBuffer(1024) 155,574 ops/sec [153,513..157,091] → 144,506 ops/sec [141,358..146,977] 🔴 -7.1% 189,684 ops/sec [186,780..190,480] → 190,903 ops/sec [185,931..191,058] ~ overlap (+0.6%)
clone ArrayBuffer inside object 130,697 ops/sec [129,521..132,224] → 120,529 ops/sec [119,295..121,559] 🔴 -7.8% 146,581 ops/sec [145,741..147,831] → 148,645 ops/sec [147,597..149,691] ~ overlap (+1.4%)
arrays.js — Interp: 🔴 18, 1 unch. · avg -10.9% · Bytecode: 🟢 2, 🔴 1, 16 unch. · avg +0.1%
Benchmark Interpreted Δ Bytecode Δ
Array.from length 100 4,499 ops/sec [3,934..4,579] → 3,613 ops/sec [2,254..4,033] ~ overlap (-19.7%) 6,582 ops/sec [6,434..6,676] → 6,697 ops/sec [6,427..6,757] ~ overlap (+1.8%)
Array.from 10 elements 102,177 ops/sec [100,358..102,403] → 92,701 ops/sec [91,869..93,044] 🔴 -9.3% 96,184 ops/sec [94,613..98,227] → 98,236 ops/sec [96,546..99,246] ~ overlap (+2.1%)
Array.of 10 elements 126,136 ops/sec [124,817..127,427] → 111,658 ops/sec [109,397..113,522] 🔴 -11.5% 130,888 ops/sec [124,760..132,124] → 127,713 ops/sec [126,036..129,326] ~ overlap (-2.4%)
spread into new array 153,434 ops/sec [151,886..154,654] → 136,212 ops/sec [134,789..137,734] 🔴 -11.2% 83,212 ops/sec [81,973..83,873] → 80,683 ops/sec [77,597..82,192] ~ overlap (-3.0%)
map over 50 elements 8,304 ops/sec [8,251..8,357] → 7,266 ops/sec [7,161..7,422] 🔴 -12.5% 13,003 ops/sec [12,703..13,200] → 12,851 ops/sec [12,693..12,939] ~ overlap (-1.2%)
filter over 50 elements 7,779 ops/sec [7,696..7,807] → 6,760 ops/sec [6,733..7,013] 🔴 -13.1% 12,418 ops/sec [12,310..12,826] → 12,860 ops/sec [12,837..12,927] 🟢 +3.6%
reduce sum 50 elements 8,714 ops/sec [8,189..8,787] → 7,616 ops/sec [7,530..7,728] 🔴 -12.6% 12,246 ops/sec [11,989..12,364] → 12,404 ops/sec [12,342..12,483] ~ overlap (+1.3%)
forEach over 50 elements 7,688 ops/sec [7,632..7,722] → 6,849 ops/sec [6,803..6,862] 🔴 -10.9% 12,137 ops/sec [11,806..12,215] → 12,144 ops/sec [12,032..12,301] ~ overlap (+0.1%)
find in 50 elements 11,080 ops/sec [10,933..11,189] → 9,976 ops/sec [9,670..10,104] 🔴 -10.0% 18,755 ops/sec [18,721..18,796] → 18,564 ops/sec [18,492..18,734] ~ overlap (-1.0%)
sort 20 elements 4,194 ops/sec [4,168..4,220] → 3,733 ops/sec [3,691..3,787] 🔴 -11.0% 7,009 ops/sec [6,992..7,041] → 6,977 ops/sec [6,962..6,988] 🔴 -0.5%
flat nested array 56,994 ops/sec [56,570..57,247] → 51,951 ops/sec [51,417..52,825] 🔴 -8.8% 56,298 ops/sec [55,588..56,621] → 56,087 ops/sec [55,289..56,942] ~ overlap (-0.4%)
flatMap 31,460 ops/sec [31,347..31,646] → 28,344 ops/sec [27,989..28,811] 🔴 -9.9% 37,293 ops/sec [36,982..38,369] → 37,800 ops/sec [37,355..38,253] ~ overlap (+1.4%)
map inside map (5x5) 8,220 ops/sec [7,939..8,311] → 7,391 ops/sec [7,189..7,541] 🔴 -10.1% 10,668 ops/sec [10,541..10,751] → 10,599 ops/sec [10,453..10,695] ~ overlap (-0.6%)
filter inside map (5x10) 5,877 ops/sec [5,851..5,908] → 5,334 ops/sec [5,285..5,412] 🔴 -9.2% 8,671 ops/sec [8,326..8,821] → 8,701 ops/sec [8,574..8,787] ~ overlap (+0.3%)
reduce inside map (5x10) 6,963 ops/sec [6,833..6,983] → 6,418 ops/sec [6,358..6,441] 🔴 -7.8% 9,628 ops/sec [9,574..9,716] → 9,785 ops/sec [9,717..9,913] 🟢 +1.6%
forEach inside forEach (5x10) 6,026 ops/sec [5,978..6,032] → 5,526 ops/sec [5,454..5,601] 🔴 -8.3% 9,531 ops/sec [9,371..9,691] → 9,581 ops/sec [9,547..9,635] ~ overlap (+0.5%)
find inside some (10x10) 4,942 ops/sec [4,722..5,051] → 4,303 ops/sec [4,282..4,354] 🔴 -12.9% 7,554 ops/sec [7,518..7,576] → 7,478 ops/sec [7,421..7,531] ~ overlap (-1.0%)
map+filter chain nested (5x20) 1,825 ops/sec [1,782..1,837] → 1,653 ops/sec [1,639..1,662] 🔴 -9.4% 2,886 ops/sec [2,863..2,918] → 2,899 ops/sec [2,882..2,911] ~ overlap (+0.4%)
reduce flatten (10x5) 18,233 ops/sec [17,926..18,703] → 16,672 ops/sec [16,194..17,178] 🔴 -8.6% 8,425 ops/sec [8,366..8,461] → 8,348 ops/sec [8,158..8,464] ~ overlap (-0.9%)
async-await.js — Interp: 🔴 5, 1 unch. · avg -10.5% · Bytecode: 🔴 3, 3 unch. · avg -1.6%
Benchmark Interpreted Δ Bytecode Δ
single await 159,257 ops/sec [135,901..165,372] → 139,792 ops/sec [86,866..151,156] ~ overlap (-12.2%) 179,215 ops/sec [115,099..180,691] → 178,283 ops/sec [171,725..179,397] ~ overlap (-0.5%)
multiple awaits 76,511 ops/sec [75,990..78,389] → 71,150 ops/sec [70,398..71,392] 🔴 -7.0% 77,318 ops/sec [77,118..77,530] → 76,765 ops/sec [76,354..76,944] 🔴 -0.7%
await non-Promise value 330,104 ops/sec [327,430..332,033] → 294,275 ops/sec [288,786..301,381] 🔴 -10.9% 462,755 ops/sec [455,378..470,073] → 452,182 ops/sec [450,532..453,885] 🔴 -2.3%
await with try/catch 155,865 ops/sec [155,102..157,078] → 136,798 ops/sec [135,635..138,041] 🔴 -12.2% 174,547 ops/sec [172,176..179,588] → 165,291 ops/sec [163,129..168,363] 🔴 -5.3%
await Promise.all 25,356 ops/sec [24,967..25,563] → 22,591 ops/sec [21,920..22,951] 🔴 -10.9% 23,407 ops/sec [23,171..23,762] → 23,153 ops/sec [22,433..23,730] ~ overlap (-1.1%)
nested async function call 85,903 ops/sec [85,165..87,978] → 77,490 ops/sec [77,186..77,827] 🔴 -9.8% 105,919 ops/sec [103,987..106,913] → 106,516 ops/sec [105,901..107,111] ~ overlap (+0.6%)
base64.js — Interp: 🔴 9, 1 unch. · avg -11.6% · Bytecode: 🔴 2, 8 unch. · avg +0.3%
Benchmark Interpreted Δ Bytecode Δ
short ASCII (13 chars) 5,998 ops/sec [4,934..6,080] → 4,560 ops/sec [3,909..5,365] ~ overlap (-24.0%) 5,518 ops/sec [3,639..5,626] → 5,679 ops/sec [5,187..5,703] ~ overlap (+2.9%)
medium ASCII (450 chars) 229 ops/sec [225..237] → 207 ops/sec [200..210] 🔴 -9.8% 216 ops/sec [211..217] → 219 ops/sec [214..221] ~ overlap (+1.3%)
Latin-1 characters 6,513 ops/sec [6,446..6,558] → 5,692 ops/sec [5,619..5,825] 🔴 -12.6% 6,002 ops/sec [5,694..6,086] → 5,977 ops/sec [5,883..6,174] ~ overlap (-0.4%)
short base64 (20 chars) 3,070 ops/sec [3,054..3,094] → 2,742 ops/sec [2,689..2,819] 🔴 -10.7% 2,818 ops/sec [2,634..2,891] → 2,919 ops/sec [2,764..2,930] ~ overlap (+3.6%)
medium base64 (600 chars) 126 ops/sec [125..127] → 115 ops/sec [113..116] 🔴 -8.9% 120 ops/sec [120..121] → 118 ops/sec [117..119] 🔴 -1.4%
Latin-1 output 4,375 ops/sec [4,294..4,417] → 3,916 ops/sec [3,889..3,961] 🔴 -10.5% 4,100 ops/sec [4,075..4,118] → 4,047 ops/sec [4,008..4,071] 🔴 -1.3%
forgiving (no padding) 6,440 ops/sec [6,243..6,525] → 5,730 ops/sec [5,713..5,735] 🔴 -11.0% 6,013 ops/sec [4,362..6,164] → 5,937 ops/sec [5,895..5,985] ~ overlap (-1.3%)
with whitespace 2,760 ops/sec [2,757..2,770] → 2,513 ops/sec [2,499..2,517] 🔴 -9.0% 2,620 ops/sec [2,299..2,645] → 2,596 ops/sec [2,593..2,597] ~ overlap (-0.9%)
atob(btoa(short)) 2,023 ops/sec [2,007..2,045] → 1,817 ops/sec [1,808..1,855] 🔴 -10.2% 1,941 ops/sec [1,909..1,980] → 1,935 ops/sec [1,915..1,990] ~ overlap (-0.3%)
atob(btoa(medium)) 82 ops/sec [81..83] → 75 ops/sec [74..76] 🔴 -9.3% 77 ops/sec [76..79] → 77 ops/sec [77..78] ~ overlap (+0.5%)
classes.js — Interp: 🔴 30, 1 unch. · avg -7.9% · Bytecode: 🟢 11, 20 unch. · avg +1.5%
Benchmark Interpreted Δ Bytecode Δ
simple class new 66,056 ops/sec [63,645..66,389] → 59,374 ops/sec [59,101..60,290] 🔴 -10.1% 92,446 ops/sec [92,013..93,245] → 94,892 ops/sec [93,972..95,504] 🟢 +2.6%
class with defaults 52,869 ops/sec [52,806..53,238] → 48,604 ops/sec [47,868..49,022] 🔴 -8.1% 62,881 ops/sec [61,387..63,280] → 63,964 ops/sec [63,401..64,298] 🟢 +1.7%
50 instances via Array.from 2,409 ops/sec [2,294..2,431] → 2,164 ops/sec [2,150..2,195] 🔴 -10.2% 3,818 ops/sec [3,725..3,879] → 3,764 ops/sec [3,703..3,854] ~ overlap (-1.4%)
instance method call 31,011 ops/sec [30,731..31,263] → 28,639 ops/sec [28,365..28,756] 🔴 -7.6% 45,682 ops/sec [45,460..46,071] → 45,030 ops/sec [44,539..45,488] ~ overlap (-1.4%)
static method call 50,926 ops/sec [50,136..51,199] → 46,353 ops/sec [45,030..46,983] 🔴 -9.0% 90,476 ops/sec [88,949..92,196] → 88,864 ops/sec [87,760..90,007] ~ overlap (-1.8%)
single-level inheritance 26,213 ops/sec [25,880..26,356] → 24,052 ops/sec [23,997..24,147] 🔴 -8.2% 34,114 ops/sec [33,862..34,829] → 34,532 ops/sec [34,134..34,917] ~ overlap (+1.2%)
two-level inheritance 22,901 ops/sec [22,595..23,090] → 20,982 ops/sec [20,768..21,272] 🔴 -8.4% 27,656 ops/sec [27,218..27,710] → 28,137 ops/sec [28,045..28,226] 🟢 +1.7%
private field access 34,645 ops/sec [34,026..34,745] → 31,438 ops/sec [31,123..31,739] 🔴 -9.3% 45,200 ops/sec [44,531..45,485] → 46,516 ops/sec [46,346..46,897] 🟢 +2.9%
private methods 38,861 ops/sec [37,944..39,422] → 34,976 ops/sec [34,632..35,277] 🔴 -10.0% 49,134 ops/sec [49,042..49,606] → 50,380 ops/sec [50,201..50,532] 🟢 +2.5%
getter/setter access 36,833 ops/sec [36,244..36,843] → 32,586 ops/sec [32,351..32,853] 🔴 -11.5% 51,284 ops/sec [50,484..52,168] → 51,987 ops/sec [51,434..52,886] ~ overlap (+1.4%)
class decorator (identity) 46,666 ops/sec [46,098..47,250] → 42,838 ops/sec [41,767..42,974] 🔴 -8.2% 52,205 ops/sec [50,172..53,694] → 52,489 ops/sec [51,879..53,083] ~ overlap (+0.5%)
class decorator (wrapping) 27,904 ops/sec [27,356..28,081] → 25,070 ops/sec [24,743..25,396] 🔴 -10.2% 29,065 ops/sec [28,374..29,280] → 29,266 ops/sec [29,023..29,575] ~ overlap (+0.7%)
identity method decorator 33,502 ops/sec [33,054..34,097] → 30,370 ops/sec [30,206..30,963] 🔴 -9.3% 43,231 ops/sec [42,053..44,469] → 44,196 ops/sec [42,843..45,539] ~ overlap (+2.2%)
wrapping method decorator 27,196 ops/sec [26,180..27,482] → 24,641 ops/sec [24,438..24,720] 🔴 -9.4% 31,853 ops/sec [31,237..32,998] → 32,219 ops/sec [31,466..33,439] ~ overlap (+1.1%)
stacked method decorators (x3) 18,367 ops/sec [17,846..18,784] → 16,970 ops/sec [16,877..17,444] 🔴 -7.6% 21,680 ops/sec [21,199..22,264] → 22,972 ops/sec [22,574..23,549] 🟢 +6.0%
identity field decorator 37,763 ops/sec [36,787..38,805] → 35,111 ops/sec [34,579..35,916] 🔴 -7.0% 39,827 ops/sec [38,182..41,444] → 39,611 ops/sec [38,109..40,658] ~ overlap (-0.5%)
field initializer decorator 30,391 ops/sec [30,130..30,640] → 29,291 ops/sec [28,167..29,741] 🔴 -3.6% 34,072 ops/sec [32,598..35,538] → 34,955 ops/sec [34,264..35,253] ~ overlap (+2.6%)
getter decorator (identity) 34,663 ops/sec [33,228..34,849] → 31,594 ops/sec [31,444..31,624] 🔴 -8.9% 38,820 ops/sec [38,509..39,343] → 39,245 ops/sec [38,550..39,854] ~ overlap (+1.1%)
setter decorator (identity) 28,302 ops/sec [27,591..28,611] → 26,211 ops/sec [25,955..26,273] 🔴 -7.4% 31,692 ops/sec [31,299..31,870] → 32,546 ops/sec [32,018..32,744] 🟢 +2.7%
static method decorator 36,362 ops/sec [35,209..36,733] → 32,724 ops/sec [31,832..33,312] 🔴 -10.0% 44,551 ops/sec [44,347..45,023] → 45,576 ops/sec [45,367..45,681] 🟢 +2.3%
static field decorator 42,818 ops/sec [41,766..44,118] → 39,785 ops/sec [39,134..41,230] 🔴 -7.1% 46,924 ops/sec [45,794..49,215] → 47,626 ops/sec [46,460..49,238] ~ overlap (+1.5%)
private method decorator 28,197 ops/sec [27,504..28,715] → 25,329 ops/sec [25,049..25,796] 🔴 -10.2% 36,249 ops/sec [35,481..37,138] → 38,059 ops/sec [37,804..39,186] 🟢 +5.0%
private field decorator 30,497 ops/sec [30,048..31,287] → 28,421 ops/sec [28,092..29,397] 🔴 -6.8% 33,111 ops/sec [32,586..33,462] → 33,595 ops/sec [33,235..34,747] ~ overlap (+1.5%)
plain auto-accessor (no decorator) 52,812 ops/sec [52,043..56,089] → 49,922 ops/sec [49,126..51,208] 🔴 -5.5% 53,408 ops/sec [51,610..56,427] → 51,497 ops/sec [50,938..52,407] ~ overlap (-3.6%)
auto-accessor with decorator 29,538 ops/sec [29,121..31,385] → 27,654 ops/sec [27,281..28,491] 🔴 -6.4% 29,708 ops/sec [28,909..31,195] → 30,409 ops/sec [30,283..31,717] ~ overlap (+2.4%)
decorator writing metadata 23,663 ops/sec [22,903..23,977] → 22,157 ops/sec [21,714..22,894] 🔴 -6.4% 26,381 ops/sec [25,027..27,784] → 26,828 ops/sec [26,457..27,917] ~ overlap (+1.7%)
static getter read 63,577 ops/sec [63,043..64,207] → 60,078 ops/sec [58,898..60,833] 🔴 -5.5% 88,434 ops/sec [87,453..89,022] → 89,101 ops/sec [88,075..90,155] ~ overlap (+0.8%)
static getter/setter pair 48,376 ops/sec [48,154..48,637] → 44,991 ops/sec [44,750..45,083] 🔴 -7.0% 61,521 ops/sec [60,967..62,426] → 63,017 ops/sec [62,061..63,958] ~ overlap (+2.4%)
inherited static getter 39,642 ops/sec [36,937..40,545] → 37,945 ops/sec [37,645..38,533] ~ overlap (-4.3%) 50,335 ops/sec [50,209..50,930] → 51,751 ops/sec [51,286..52,309] 🟢 +2.8%
inherited static setter 43,638 ops/sec [43,108..43,989] → 41,584 ops/sec [41,524..41,676] 🔴 -4.7% 52,892 ops/sec [51,688..53,437] → 54,993 ops/sec [53,774..55,206] 🟢 +4.0%
inherited static getter with this binding 36,319 ops/sec [34,974..36,797] → 34,169 ops/sec [33,656..34,404] 🔴 -5.9% 45,289 ops/sec [45,097..45,631] → 45,489 ops/sec [45,203..45,606] ~ overlap (+0.4%)
closures.js — Interp: 🔴 11 · avg -12.0% · Bytecode: 🟢 1, 10 unch. · avg +0.0%
Benchmark Interpreted Δ Bytecode Δ
closure over single variable 53,116 ops/sec [52,056..53,619] → 47,503 ops/sec [47,124..48,012] 🔴 -10.6% 142,547 ops/sec [140,834..143,969] → 142,912 ops/sec [140,775..144,532] ~ overlap (+0.3%)
closure over multiple variables 52,873 ops/sec [51,788..53,325] → 47,147 ops/sec [45,767..48,107] 🔴 -10.8% 132,381 ops/sec [131,937..133,693] → 135,573 ops/sec [134,065..136,265] 🟢 +2.4%
nested closures 57,695 ops/sec [56,640..58,341] → 51,161 ops/sec [50,391..51,672] 🔴 -11.3% 136,687 ops/sec [135,752..137,800] → 138,097 ops/sec [132,087..139,194] ~ overlap (+1.0%)
function as argument 39,196 ops/sec [38,353..40,056] → 35,036 ops/sec [34,345..35,149] 🔴 -10.6% 144,944 ops/sec [143,121..146,351] → 144,999 ops/sec [143,108..146,663] ~ overlap (+0.0%)
function returning function 51,359 ops/sec [50,749..52,617] → 46,030 ops/sec [44,867..46,636] 🔴 -10.4% 164,381 ops/sec [163,090..165,486] → 162,385 ops/sec [160,981..165,060] ~ overlap (-1.2%)
compose two functions 31,966 ops/sec [31,336..32,367] → 27,593 ops/sec [26,983..27,846] 🔴 -13.7% 94,198 ops/sec [93,762..94,314] → 93,436 ops/sec [92,561..95,529] ~ overlap (-0.8%)
fn.call 72,163 ops/sec [69,471..73,154] → 62,977 ops/sec [62,026..63,206] 🔴 -12.7% 96,344 ops/sec [95,260..96,801] → 95,876 ops/sec [95,122..96,501] ~ overlap (-0.5%)
fn.apply 55,181 ops/sec [54,466..55,442] → 48,311 ops/sec [47,672..49,034] 🔴 -12.4% 93,807 ops/sec [93,307..94,459] → 94,545 ops/sec [93,815..95,639] ~ overlap (+0.8%)
fn.bind 67,380 ops/sec [65,784..69,820] → 58,660 ops/sec [56,842..58,956] 🔴 -12.9% 172,238 ops/sec [166,431..172,918] → 170,138 ops/sec [168,067..172,989] ~ overlap (-1.2%)
recursive sum to 50 4,919 ops/sec [4,736..4,937] → 4,196 ops/sec [4,183..4,243] 🔴 -14.7% 18,645 ops/sec [18,358..19,061] → 18,449 ops/sec [18,381..18,579] ~ overlap (-1.1%)
recursive tree traversal 9,379 ops/sec [9,223..9,570] → 8,308 ops/sec [7,803..8,374] 🔴 -11.4% 19,309 ops/sec [19,174..19,447] → 19,463 ops/sec [19,350..19,586] ~ overlap (+0.8%)
collections.js — Interp: 🟢 3, 🔴 9 · avg -6.0% · Bytecode: 🔴 6, 6 unch. · avg -1.4%
Benchmark Interpreted Δ Bytecode Δ
add 50 elements 2,915 ops/sec [2,661..2,982] → 3,025 ops/sec [3,015..3,035] 🟢 +3.8% 3,684 ops/sec [3,659..3,702] → 3,597 ops/sec [3,526..3,640] 🔴 -2.4%
has lookup (50 elements) 39,856 ops/sec [38,406..40,567] → 43,546 ops/sec [43,200..43,999] 🟢 +9.3% 50,577 ops/sec [50,508..50,774] → 48,852 ops/sec [48,207..49,397] 🔴 -3.4%
delete elements 21,859 ops/sec [21,804..22,006] → 23,824 ops/sec [23,573..24,063] 🟢 +9.0% 25,606 ops/sec [25,209..25,934] → 25,055 ops/sec [24,802..25,288] ~ overlap (-2.2%)
forEach iteration 6,130 ops/sec [5,992..6,323] → 5,609 ops/sec [5,510..5,751] 🔴 -8.5% 8,435 ops/sec [8,373..8,615] → 8,484 ops/sec [8,058..8,543] ~ overlap (+0.6%)
spread to array 17,242 ops/sec [16,963..17,385] → 15,868 ops/sec [15,426..16,019] 🔴 -8.0% 112,537 ops/sec [111,079..113,366] → 109,787 ops/sec [108,794..111,104] ~ overlap (-2.4%)
deduplicate array 21,992 ops/sec [21,845..22,038] → 21,078 ops/sec [20,832..21,296] 🔴 -4.2% 35,568 ops/sec [35,227..35,844] → 36,314 ops/sec [35,183..36,910] ~ overlap (+2.1%)
set 50 entries 2,604 ops/sec [2,555..2,676] → 2,376 ops/sec [2,337..2,391] 🔴 -8.7% 2,795 ops/sec [2,778..2,858] → 2,722 ops/sec [2,674..2,746] 🔴 -2.6%
get lookup (50 entries) 47,665 ops/sec [46,992..48,891] → 40,708 ops/sec [40,210..40,831] 🔴 -14.6% 44,213 ops/sec [43,806..44,698] → 43,237 ops/sec [42,936..43,338] 🔴 -2.2%
has check 70,898 ops/sec [69,999..71,840] → 59,606 ops/sec [59,379..59,970] 🔴 -15.9% 67,000 ops/sec [66,153..67,149] → 65,628 ops/sec [65,393..65,926] 🔴 -2.0%
delete entries 26,372 ops/sec [26,152..26,384] → 22,340 ops/sec [22,079..22,675] 🔴 -15.3% 23,759 ops/sec [23,597..23,953] → 23,053 ops/sec [22,940..23,069] 🔴 -3.0%
forEach iteration 6,250 ops/sec [6,201..6,332] → 5,568 ops/sec [5,410..5,677] 🔴 -10.9% 8,501 ops/sec [8,298..8,662] → 8,594 ops/sec [8,454..8,714] ~ overlap (+1.1%)
keys/values/entries 4,734 ops/sec [4,626..4,779] → 4,364 ops/sec [4,238..4,416] 🔴 -7.8% 14,718 ops/sec [14,574..15,243] → 14,665 ops/sec [14,526..14,769] ~ overlap (-0.4%)
csv.js — Interp: 🔴 13 · avg -8.8% · Bytecode: 🟢 4, 9 unch. · avg +1.7%
Benchmark Interpreted Δ Bytecode Δ
parse simple 3-column CSV 53,226 ops/sec [50,655..53,980] → 47,317 ops/sec [46,819..48,192] 🔴 -11.1% 51,196 ops/sec [50,836..51,435] → 51,197 ops/sec [50,699..51,796] ~ overlap (+0.0%)
parse 10-row CSV 14,686 ops/sec [14,484..14,838] → 13,355 ops/sec [13,212..13,452] 🔴 -9.1% 13,766 ops/sec [13,573..14,063] → 14,100 ops/sec [14,062..14,125] ~ overlap (+2.4%)
parse 100-row CSV 2,283 ops/sec [2,227..2,362] → 2,149 ops/sec [2,098..2,188] 🔴 -5.9% 2,161 ops/sec [2,123..2,210] → 2,247 ops/sec [2,221..2,286] 🟢 +4.0%
parse CSV with quoted fields 76,243 ops/sec [75,920..76,395] → 69,336 ops/sec [68,186..71,358] 🔴 -9.1% 73,907 ops/sec [71,757..75,938] → 75,488 ops/sec [75,052..76,847] ~ overlap (+2.1%)
parse without headers (array of arrays) 6,034 ops/sec [5,962..6,091] → 5,606 ops/sec [5,551..5,653] 🔴 -7.1% 5,954 ops/sec [5,881..6,121] → 5,928 ops/sec [5,856..6,082] ~ overlap (-0.4%)
parse with semicolon delimiter 10,237 ops/sec [10,229..10,251] → 9,400 ops/sec [9,273..9,508] 🔴 -8.2% 9,881 ops/sec [9,798..10,006] → 10,174 ops/sec [10,134..10,196] 🟢 +3.0%
stringify array of objects 76,304 ops/sec [75,291..77,290] → 67,001 ops/sec [66,317..67,670] 🔴 -12.2% 75,172 ops/sec [74,058..77,109] → 75,216 ops/sec [73,156..76,337] ~ overlap (+0.1%)
stringify array of arrays 26,812 ops/sec [26,666..26,970] → 24,196 ops/sec [24,060..24,481] 🔴 -9.8% 25,991 ops/sec [25,106..26,403] → 25,125 ops/sec [24,942..25,623] ~ overlap (-3.3%)
stringify with values needing escaping 56,961 ops/sec [56,700..58,088] → 50,601 ops/sec [49,386..51,048] 🔴 -11.2% 54,735 ops/sec [54,329..56,101] → 55,295 ops/sec [54,347..55,871] ~ overlap (+1.0%)
reviver converts numbers 1,477 ops/sec [1,473..1,524] → 1,342 ops/sec [1,335..1,347] 🔴 -9.2% 1,581 ops/sec [1,548..1,602] → 1,621 ops/sec [1,599..1,648] ~ overlap (+2.5%)
reviver filters empty to null 13,033 ops/sec [12,930..13,085] → 11,977 ops/sec [11,903..12,109] 🔴 -8.1% 13,612 ops/sec [13,540..13,633] → 14,342 ops/sec [14,185..14,397] 🟢 +5.4%
parse then stringify 8,949 ops/sec [8,894..8,968] → 8,353 ops/sec [8,202..8,416] 🔴 -6.7% 8,636 ops/sec [8,380..8,822] → 8,849 ops/sec [8,595..9,100] ~ overlap (+2.5%)
stringify then parse 8,753 ops/sec [8,724..8,912] → 8,103 ops/sec [8,033..8,188] 🔴 -7.4% 8,383 ops/sec [8,283..8,482] → 8,664 ops/sec [8,517..8,805] 🟢 +3.4%
destructuring.js — Interp: 🔴 22 · avg -8.6% · Bytecode: 🟢 2, 🔴 3, 17 unch. · avg -0.9%
Benchmark Interpreted Δ Bytecode Δ
simple array destructuring 191,361 ops/sec [187,700..198,235] → 178,639 ops/sec [176,734..179,226] 🔴 -6.6% 127,944 ops/sec [126,376..129,383] → 127,138 ops/sec [125,363..129,621] ~ overlap (-0.6%)
with rest element 126,852 ops/sec [125,027..128,345] → 120,644 ops/sec [118,980..120,803] 🔴 -4.9% 97,598 ops/sec [94,252..98,586] → 94,942 ops/sec [94,309..95,821] ~ overlap (-2.7%)
with defaults 193,671 ops/sec [190,689..197,559] → 177,557 ops/sec [147,210..181,738] 🔴 -8.3% 147,091 ops/sec [145,634..150,452] → 147,430 ops/sec [144,254..149,256] ~ overlap (+0.2%)
skip elements 201,808 ops/sec [199,986..204,287] → 187,256 ops/sec [184,559..190,034] 🔴 -7.2% 142,355 ops/sec [138,130..143,027] → 135,014 ops/sec [133,804..135,132] 🔴 -5.2%
nested array destructuring 96,544 ops/sec [96,057..98,155] → 86,168 ops/sec [85,237..87,166] 🔴 -10.7% 47,232 ops/sec [46,835..47,853] → 46,271 ops/sec [44,840..46,856] ~ overlap (-2.0%)
swap variables 245,538 ops/sec [239,702..248,331] → 217,271 ops/sec [215,227..225,428] 🔴 -11.5% 172,197 ops/sec [171,791..173,747] → 170,115 ops/sec [169,643..170,504] 🔴 -1.2%
simple object destructuring 164,166 ops/sec [158,456..166,096] → 146,743 ops/sec [145,617..147,925] 🔴 -10.6% 177,294 ops/sec [176,846..178,332] → 175,257 ops/sec [174,321..177,241] ~ overlap (-1.1%)
with defaults 179,111 ops/sec [174,694..188,551] → 166,048 ops/sec [164,624..167,781] 🔴 -7.3% 228,890 ops/sec [227,348..230,977] → 237,124 ops/sec [235,728..238,202] 🟢 +3.6%
with renaming 170,765 ops/sec [167,640..178,981] → 157,185 ops/sec [154,963..160,768] 🔴 -8.0% 177,163 ops/sec [174,971..178,265] → 181,320 ops/sec [180,277..182,123] 🟢 +2.3%
nested object destructuring 87,901 ops/sec [86,939..88,863] → 78,074 ops/sec [76,338..79,014] 🔴 -11.2% 88,351 ops/sec [87,461..89,138] → 87,844 ops/sec [87,296..88,128] ~ overlap (-0.6%)
rest properties 104,670 ops/sec [101,899..106,916] → 95,913 ops/sec [92,828..97,673] 🔴 -8.4% 85,851 ops/sec [85,141..86,920] → 84,377 ops/sec [83,768..85,587] ~ overlap (-1.7%)
object parameter 49,731 ops/sec [49,045..51,010] → 45,673 ops/sec [45,159..46,123] 🔴 -8.2% 69,012 ops/sec [68,639..70,980] → 70,381 ops/sec [69,707..70,518] ~ overlap (+2.0%)
array parameter 60,832 ops/sec [59,563..61,584] → 54,810 ops/sec [53,885..55,616] 🔴 -9.9% 61,882 ops/sec [61,270..62,755] → 63,075 ops/sec [61,275..64,073] ~ overlap (+1.9%)
mixed destructuring in map 14,228 ops/sec [13,788..14,595] → 12,934 ops/sec [12,662..13,074] 🔴 -9.1% 18,756 ops/sec [17,925..19,522] → 18,863 ops/sec [18,235..19,283] ~ overlap (+0.6%)
forEach with array destructuring 30,823 ops/sec [30,409..31,176] → 28,418 ops/sec [27,661..29,147] 🔴 -7.8% 24,091 ops/sec [23,636..24,512] → 23,820 ops/sec [23,028..24,081] ~ overlap (-1.1%)
map with array destructuring 31,435 ops/sec [30,925..31,642] → 28,891 ops/sec [28,295..29,631] 🔴 -8.1% 23,557 ops/sec [23,105..23,943] → 23,121 ops/sec [23,023..23,256] ~ overlap (-1.8%)
filter with array destructuring 32,873 ops/sec [32,537..33,002] → 29,800 ops/sec [29,365..30,538] 🔴 -9.3% 25,570 ops/sec [25,340..26,051] → 24,630 ops/sec [23,815..24,820] 🔴 -3.7%
reduce with array destructuring 35,379 ops/sec [35,227..35,492] → 32,596 ops/sec [31,861..32,934] 🔴 -7.9% 25,914 ops/sec [25,509..26,277] → 25,655 ops/sec [25,129..26,301] ~ overlap (-1.0%)
map with object destructuring 31,854 ops/sec [31,281..32,550] → 28,759 ops/sec [27,960..29,636] 🔴 -9.7% 43,621 ops/sec [41,905..44,671] → 42,381 ops/sec [41,128..43,319] ~ overlap (-2.8%)
map with nested destructuring 26,903 ops/sec [26,558..27,307] → 24,421 ops/sec [23,622..24,796] 🔴 -9.2% 40,427 ops/sec [39,140..41,299] → 39,187 ops/sec [38,674..40,770] ~ overlap (-3.1%)
map with rest in destructuring 19,962 ops/sec [19,521..20,129] → 18,453 ops/sec [18,291..18,628] 🔴 -7.6% 13,356 ops/sec [13,310..13,482] → 13,362 ops/sec [13,255..13,423] ~ overlap (+0.0%)
map with defaults in destructuring 25,102 ops/sec [25,043..25,763] → 22,966 ops/sec [22,554..23,366] 🔴 -8.5% 31,724 ops/sec [31,488..32,826] → 31,335 ops/sec [30,785..31,728] ~ overlap (-1.2%)
fibonacci.js — Interp: 🔴 8 · avg -10.2% · Bytecode: 🟢 2, 🔴 2, 4 unch. · avg -0.6%
Benchmark Interpreted Δ Bytecode Δ
recursive fib(15) 127 ops/sec [126..130] → 112 ops/sec [110..114] 🔴 -12.3% 509 ops/sec [499..520] → 517 ops/sec [515..518] ~ overlap (+1.6%)
recursive fib(20) 11 ops/sec [11..12] → 10 ops/sec [10..10] 🔴 -12.7% 46 ops/sec [46..46] → 47 ops/sec [47..47] 🟢 +2.0%
recursive fib(15) typed 129 ops/sec [125..130] → 114 ops/sec [112..118] 🔴 -11.8% 427 ops/sec [418..429] → 405 ops/sec [401..407] 🔴 -5.3%
recursive fib(20) typed 12 ops/sec [11..12] → 10 ops/sec [10..10] 🔴 -11.9% 39 ops/sec [39..39] → 36 ops/sec [36..37] 🔴 -6.7%
iterative fib(20) via reduce 6,176 ops/sec [6,104..6,183] → 5,615 ops/sec [5,516..5,730] 🔴 -9.1% 9,743 ops/sec [9,683..9,803] → 9,866 ops/sec [9,824..9,939] 🟢 +1.3%
iterator fib(20) 4,612 ops/sec [4,575..4,657] → 4,220 ops/sec [4,081..4,357] 🔴 -8.5% 8,003 ops/sec [7,769..8,137] → 8,058 ops/sec [7,992..8,127] ~ overlap (+0.7%)
iterator fib(20) via Iterator.from + take 7,261 ops/sec [7,224..7,370] → 6,750 ops/sec [6,566..6,806] 🔴 -7.0% 9,333 ops/sec [9,300..9,455] → 9,478 ops/sec [9,387..9,584] ~ overlap (+1.6%)
iterator fib(20) last value via reduce 5,281 ops/sec [5,271..5,318] → 4,848 ops/sec [4,701..4,904] 🔴 -8.2% 6,797 ops/sec [6,732..6,826] → 6,815 ops/sec [6,739..6,893] ~ overlap (+0.3%)
float16array.js — Interp: 🟢 4, 🔴 25, 3 unch. · avg -3.6% · Bytecode: 🔴 5, 27 unch. · avg -0.9%
Benchmark Interpreted Δ Bytecode Δ
new Float16Array(0) 161,117 ops/sec [158,418..164,424] → 152,980 ops/sec [150,228..154,782] 🔴 -5.1% 187,427 ops/sec [183,719..190,928] → 183,516 ops/sec [179,913..184,678] ~ overlap (-2.1%)
new Float16Array(100) 155,447 ops/sec [152,524..156,863] → 145,411 ops/sec [142,555..146,513] 🔴 -6.5% 175,462 ops/sec [174,224..177,437] → 170,931 ops/sec [169,864..173,221] 🔴 -2.6%
new Float16Array(1000) 121,043 ops/sec [119,182..122,690] → 120,753 ops/sec [119,102..122,647] ~ overlap (-0.2%) 139,590 ops/sec [136,798..141,827] → 140,104 ops/sec [139,649..140,301] ~ overlap (+0.4%)
Float16Array.from([...100]) 93,388 ops/sec [92,423..94,215] → 86,043 ops/sec [85,668..87,295] 🔴 -7.9% 98,106 ops/sec [95,539..100,868] → 94,083 ops/sec [93,359..94,900] 🔴 -4.1%
Float16Array.of(1.5, 2.5, 3.5, 4.5, 5.5) 144,100 ops/sec [142,376..147,041] → 138,309 ops/sec [136,048..140,117] 🔴 -4.0% 117,450 ops/sec [116,608..120,793] → 115,066 ops/sec [113,786..116,479] 🔴 -2.0%
new Float16Array(float64Array) 104,624 ops/sec [102,708..105,535] → 96,330 ops/sec [95,893..96,904] 🔴 -7.9% 111,509 ops/sec [110,529..113,914] → 107,091 ops/sec [106,109..107,254] 🔴 -4.0%
sequential write 100 elements 1,736 ops/sec [1,725..1,744] → 1,657 ops/sec [1,617..1,664] 🔴 -4.6% 3,906 ops/sec [3,828..3,955] → 3,846 ops/sec [3,795..3,909] ~ overlap (-1.5%)
sequential read 100 elements 1,919 ops/sec [1,887..1,928] → 1,813 ops/sec [1,798..1,843] 🔴 -5.5% 5,100 ops/sec [4,957..5,278] → 5,103 ops/sec [5,066..5,180] ~ overlap (+0.1%)
write special values (NaN, Inf, -0) 91,268 ops/sec [88,493..92,376] → 83,369 ops/sec [81,427..84,724] 🔴 -8.7% 121,442 ops/sec [119,420..125,483] → 122,164 ops/sec [121,205..123,121] ~ overlap (+0.6%)
Float16Array write 1,748 ops/sec [1,715..1,782] → 1,648 ops/sec [1,630..1,673] 🔴 -5.7% 3,932 ops/sec [3,824..3,997] → 3,900 ops/sec [3,865..3,911] ~ overlap (-0.8%)
Float32Array write 1,753 ops/sec [1,729..1,781] → 1,657 ops/sec [1,631..1,685] 🔴 -5.5% 3,913 ops/sec [3,816..3,997] → 3,874 ops/sec [3,837..3,895] ~ overlap (-1.0%)
Float64Array write 1,780 ops/sec [1,750..1,829] → 1,672 ops/sec [1,649..1,729] 🔴 -6.1% 3,951 ops/sec [3,885..4,116] → 3,850 ops/sec [3,755..3,949] ~ overlap (-2.6%)
Float16Array read 1,802 ops/sec [1,796..1,827] → 1,727 ops/sec [1,706..1,747] 🔴 -4.2% 4,935 ops/sec [4,818..5,062] → 4,967 ops/sec [4,933..4,981] ~ overlap (+0.6%)
Float32Array read 1,878 ops/sec [1,850..1,914] → 1,776 ops/sec [1,751..1,796] 🔴 -5.4% 5,103 ops/sec [5,040..5,376] → 5,150 ops/sec [5,101..5,350] ~ overlap (+0.9%)
Float64Array read 1,878 ops/sec [1,872..1,931] → 1,803 ops/sec [1,779..1,818] 🔴 -4.0% 5,234 ops/sec [5,176..5,248] → 5,239 ops/sec [5,176..5,290] ~ overlap (+0.1%)
fill(1.5) 24,984 ops/sec [24,886..25,016] → 22,082 ops/sec [21,971..22,337] 🔴 -11.6% 23,642 ops/sec [23,092..24,070] → 22,766 ops/sec [22,250..23,425] ~ overlap (-3.7%)
slice() 88,545 ops/sec [87,369..89,013] → 88,592 ops/sec [88,403..89,639] ~ overlap (+0.1%) 102,616 ops/sec [101,746..104,337] → 103,186 ops/sec [102,127..103,872] ~ overlap (+0.6%)
map(x => x * 2) 3,036 ops/sec [2,985..3,066] → 2,690 ops/sec [2,621..2,698] 🔴 -11.4% 3,940 ops/sec [3,880..3,952] → 4,005 ops/sec [3,730..4,015] ~ overlap (+1.6%)
filter(x => x > 25) 3,258 ops/sec [3,212..3,306] → 2,894 ops/sec [2,834..2,965] 🔴 -11.2% 4,407 ops/sec [4,378..4,416] → 4,301 ops/sec [4,235..4,393] ~ overlap (-2.4%)
reduce (sum) 3,032 ops/sec [3,005..3,083] → 2,739 ops/sec [2,711..2,769] 🔴 -9.7% 3,628 ops/sec [3,516..3,688] → 3,503 ops/sec [3,457..3,615] ~ overlap (-3.4%)
sort() 17,353 ops/sec [17,252..17,474] → 21,017 ops/sec [20,690..21,158] 🟢 +21.1% 21,717 ops/sec [21,610..21,772] → 21,540 ops/sec [21,510..21,665] ~ overlap (-0.8%)
indexOf() 99,389 ops/sec [99,034..99,552] → 109,143 ops/sec [108,682..109,763] 🟢 +9.8% 130,048 ops/sec [129,463..131,077] → 131,090 ops/sec [129,508..131,841] ~ overlap (+0.8%)
reverse() 116,945 ops/sec [110,360..117,454] → 113,697 ops/sec [112,828..115,260] ~ overlap (-2.8%) 134,228 ops/sec [133,814..135,564] → 135,488 ops/sec [133,145..135,941] ~ overlap (+0.9%)
toReversed() 49,754 ops/sec [49,468..49,893] → 54,863 ops/sec [54,041..54,977] 🟢 +10.3% 61,298 ops/sec [60,640..61,773] → 59,176 ops/sec [59,011..59,839] 🔴 -3.5%
toSorted() 689 ops/sec [687..691] → 814 ops/sec [805..823] 🟢 +18.1% 823 ops/sec [817..825] → 823 ops/sec [814..827] ~ overlap (+0.0%)
create view over existing buffer 185,263 ops/sec [179,266..187,891] → 172,529 ops/sec [170,387..172,879] 🔴 -6.9% 221,520 ops/sec [212,300..224,755] → 222,381 ops/sec [221,685..223,409] ~ overlap (+0.4%)
subarray() 202,877 ops/sec [200,183..205,847] → 190,184 ops/sec [187,829..195,219] 🔴 -6.3% 273,307 ops/sec [271,937..273,908] → 268,881 ops/sec [267,711..273,857] ~ overlap (-1.6%)
set() from array 235,532 ops/sec [232,115..236,423] → 210,926 ops/sec [209,580..211,346] 🔴 -10.4% 282,495 ops/sec [279,802..291,047] → 278,551 ops/sec [275,594..283,716] ~ overlap (-1.4%)
for-of loop 2,576 ops/sec [2,511..2,625] → 2,433 ops/sec [2,414..2,451] 🔴 -5.5% 7,243 ops/sec [7,215..7,262] → 7,203 ops/sec [7,157..7,280] ~ overlap (-0.6%)
spread into array 9,326 ops/sec [9,285..9,344] → 8,856 ops/sec [8,732..8,896] 🔴 -5.0% 39,614 ops/sec [39,160..41,249] → 39,868 ops/sec [39,493..40,660] ~ overlap (+0.6%)
f16round(1.337) 287,535 ops/sec [285,059..294,264] → 264,013 ops/sec [261,338..267,020] 🔴 -8.2% 288,504 ops/sec [282,993..289,816] → 287,446 ops/sec [282,035..291,612] ~ overlap (-0.4%)
f16round over 100 values 1,785 ops/sec [1,736..1,795] → 1,709 ops/sec [1,684..1,715] 🔴 -4.3% 3,411 ops/sec [3,370..3,465] → 3,492 ops/sec [3,439..3,498] ~ overlap (+2.4%)
for-of.js — Interp: 🔴 7 · avg -7.7% · Bytecode: 🟢 2, 5 unch. · avg +2.5%
Benchmark Interpreted Δ Bytecode Δ
for...of with 10-element array 23,836 ops/sec [23,422..24,150] → 22,259 ops/sec [21,817..22,347] 🔴 -6.6% 90,631 ops/sec [90,552..90,663] → 90,502 ops/sec [89,669..91,413] ~ overlap (-0.1%)
for...of with 100-element array 2,772 ops/sec [2,704..2,807] → 2,563 ops/sec [2,534..2,585] 🔴 -7.5% 10,314 ops/sec [10,166..10,850] → 10,773 ops/sec [10,526..10,938] ~ overlap (+4.5%)
for...of with string (10 chars) 17,820 ops/sec [17,510..17,963] → 16,113 ops/sec [15,923..16,326] 🔴 -9.6% 35,817 ops/sec [35,760..35,855] → 36,732 ops/sec [36,643..37,192] 🟢 +2.6%
for...of with Set (10 elements) 23,971 ops/sec [23,163..24,230] → 22,310 ops/sec [22,198..22,434] 🔴 -6.9% 87,571 ops/sec [87,053..87,884] → 87,422 ops/sec [85,956..88,026] ~ overlap (-0.2%)
for...of with Map entries (10 entries) 15,848 ops/sec [15,546..16,009] → 14,659 ops/sec [14,518..14,895] 🔴 -7.5% 15,787 ops/sec [15,661..15,987] → 16,287 ops/sec [15,975..16,505] ~ overlap (+3.2%)
for...of with destructuring 20,427 ops/sec [20,128..20,886] → 18,941 ops/sec [18,723..19,025] 🔴 -7.3% 21,294 ops/sec [20,671..21,500] → 21,214 ops/sec [20,838..21,620] ~ overlap (-0.4%)
for-await-of with sync array 23,000 ops/sec [22,537..23,087] → 21,037 ops/sec [20,821..21,217] 🔴 -8.5% 61,264 ops/sec [60,948..61,670] → 66,068 ops/sec [63,231..66,455] 🟢 +7.8%
helpers/bench-module.js — Interp: 0 · Bytecode: 0
Benchmark Interpreted Δ Bytecode Δ
iterators.js — Interp: 🔴 41, 1 unch. · avg -8.0% · Bytecode: 🟢 25, 🔴 2, 15 unch. · avg +2.6%
Benchmark Interpreted Δ Bytecode Δ
Iterator.from({next}).toArray() — 20 elements 7,149 ops/sec [7,135..7,163] → 6,757 ops/sec [6,709..6,768] 🔴 -5.5% 9,237 ops/sec [9,120..9,328] → 9,704 ops/sec [9,559..9,761] 🟢 +5.1%
Iterator.from({next}).toArray() — 50 elements 3,107 ops/sec [3,078..3,143] → 2,899 ops/sec [2,880..2,914] 🔴 -6.7% 4,170 ops/sec [4,116..4,238] → 4,313 ops/sec [4,256..4,356] 🟢 +3.4%
spread pre-wrapped iterator — 20 elements 5,761 ops/sec [5,748..5,776] → 5,408 ops/sec [5,359..5,452] 🔴 -6.1% 9,028 ops/sec [8,896..9,085] → 9,408 ops/sec [9,299..9,527] 🟢 +4.2%
Iterator.from({next}).forEach — 50 elements 2,075 ops/sec [2,066..2,116] → 1,931 ops/sec [1,913..1,945] 🔴 -6.9% 2,808 ops/sec [2,788..2,833] → 2,892 ops/sec [2,849..2,914] 🟢 +3.0%
Iterator.from({next}).reduce — 50 elements 2,109 ops/sec [2,094..2,118] → 1,969 ops/sec [1,953..1,989] 🔴 -6.7% 2,790 ops/sec [2,762..2,837] → 2,915 ops/sec [2,906..2,927] 🟢 +4.5%
wrap array iterator 85,725 ops/sec [84,903..86,521] → 79,252 ops/sec [78,467..79,951] 🔴 -7.6% 86,976 ops/sec [86,292..87,283] → 88,564 ops/sec [87,719..90,630] 🟢 +1.8%
wrap plain {next()} object 5,018 ops/sec [4,868..5,051] → 4,666 ops/sec [4,615..4,699] 🔴 -7.0% 6,707 ops/sec [6,671..6,746] → 6,841 ops/sec [6,794..6,941] 🟢 +2.0%
map + toArray (50 elements) 2,047 ops/sec [2,006..2,103] → 1,944 ops/sec [1,937..1,953] 🔴 -5.0% 2,856 ops/sec [2,811..2,877] → 3,000 ops/sec [2,965..3,020] 🟢 +5.1%
filter + toArray (50 elements) 2,050 ops/sec [2,029..2,056] → 1,950 ops/sec [1,931..1,960] 🔴 -4.9% 2,904 ops/sec [2,897..2,907] → 2,976 ops/sec [2,947..3,060] 🟢 +2.5%
take(10) + toArray (50 element source) 13,673 ops/sec [13,595..13,697] → 12,669 ops/sec [12,561..12,856] 🔴 -7.3% 17,184 ops/sec [17,169..17,284] → 18,426 ops/sec [18,179..18,910] 🟢 +7.2%
drop(40) + toArray (50 element source) 3,141 ops/sec [3,120..3,167] → 2,927 ops/sec [2,886..2,959] 🔴 -6.8% 4,140 ops/sec [4,057..4,169] → 4,416 ops/sec [4,305..4,447] 🟢 +6.7%
chained map + filter + take (100 element source) 3,840 ops/sec [3,772..3,876] → 3,388 ops/sec [3,338..3,409] 🔴 -11.8% 5,205 ops/sec [5,129..5,212] → 5,406 ops/sec [5,367..5,416] 🟢 +3.9%
some + every (50 elements) 1,244 ops/sec [1,224..1,261] → 1,129 ops/sec [1,118..1,138] 🔴 -9.3% 1,662 ops/sec [1,624..1,684] → 1,744 ops/sec [1,729..1,801] 🟢 +4.9%
find (50 elements) 2,770 ops/sec [2,736..2,826] → 2,465 ops/sec [2,440..2,480] 🔴 -11.0% 3,628 ops/sec [3,572..3,670] → 3,661 ops/sec [3,605..3,962] ~ overlap (+0.9%)
concat 2 arrays (10 + 10 elements) 83,477 ops/sec [81,898..84,133] → 75,474 ops/sec [74,547..77,118] 🔴 -9.6% 85,704 ops/sec [84,364..86,523] → 85,881 ops/sec [85,516..86,810] ~ overlap (+0.2%)
concat 5 arrays (10 elements each) 51,102 ops/sec [49,439..52,133] → 46,566 ops/sec [46,041..47,438] 🔴 -8.9% 51,956 ops/sec [51,573..54,315] → 52,407 ops/sec [51,685..52,878] ~ overlap (+0.9%)
concat 2 arrays (20 + 20 elements) 74,422 ops/sec [73,359..75,279] → 68,121 ops/sec [66,555..69,303] 🔴 -8.5% 79,311 ops/sec [78,918..80,080] → 78,473 ops/sec [78,266..78,540] 🔴 -1.1%
concat + filter + toArray (20 + 20 elements) 6,972 ops/sec [6,679..7,168] → 6,144 ops/sec [5,967..6,302] 🔴 -11.9% 10,251 ops/sec [10,091..10,343] → 10,063 ops/sec [9,976..10,108] ~ overlap (-1.8%)
concat + map + take (20 + 20 elements, take 10) 21,945 ops/sec [21,416..22,074] → 19,923 ops/sec [19,709..20,144] 🔴 -9.2% 28,756 ops/sec [28,570..29,225] → 29,403 ops/sec [29,026..29,545] ~ overlap (+2.2%)
concat Sets (15 + 15 elements) 75,386 ops/sec [73,993..77,852] → 70,725 ops/sec [69,730..71,423] 🔴 -6.2% 76,447 ops/sec [75,935..77,156] → 78,775 ops/sec [77,779..79,368] 🟢 +3.0%
concat strings (13 + 13 characters) 55,922 ops/sec [55,134..56,755] → 52,740 ops/sec [52,528..53,120] 🔴 -5.7% 51,902 ops/sec [51,392..52,145] → 53,926 ops/sec [53,671..54,208] 🟢 +3.9%
zip 2 arrays (10 + 10 elements) 31,855 ops/sec [31,340..32,456] → 29,907 ops/sec [29,708..29,995] 🔴 -6.1% 30,301 ops/sec [30,119..30,777] → 31,490 ops/sec [31,231..31,835] 🟢 +3.9%
zip 3 arrays (10 elements each) 29,635 ops/sec [29,485..29,747] → 27,707 ops/sec [27,652..28,137] 🔴 -6.5% 28,003 ops/sec [27,536..28,372] → 29,134 ops/sec [28,990..29,282] 🟢 +4.0%
zip 2 arrays (20 + 20 elements) 21,479 ops/sec [21,146..21,634] → 19,696 ops/sec [19,378..20,057] 🔴 -8.3% 20,569 ops/sec [19,913..20,886] → 20,925 ops/sec [20,331..21,370] ~ overlap (+1.7%)
zip 2 arrays (50 + 50 elements) 10,813 ops/sec [10,674..10,846] → 10,338 ops/sec [10,147..10,553] 🔴 -4.4% 10,329 ops/sec [10,287..10,413] → 10,445 ops/sec [10,424..10,508] 🟢 +1.1%
zip shortest mode (20 + 10 elements) 31,619 ops/sec [30,910..31,923] → 30,026 ops/sec [29,527..30,311] 🔴 -5.0% 31,350 ops/sec [30,962..31,731] → 32,208 ops/sec [31,532..32,369] ~ overlap (+2.7%)
zip longest mode (10 + 20 elements) 18,744 ops/sec [18,645..18,865] → 17,481 ops/sec [16,936..17,703] 🔴 -6.7% 17,522 ops/sec [17,462..17,793] → 18,575 ops/sec [18,513..18,610] 🟢 +6.0%
zip strict mode (20 + 20 elements) 20,206 ops/sec [19,890..20,681] → 19,076 ops/sec [18,751..19,238] 🔴 -5.6% 19,157 ops/sec [18,789..19,423] → 20,208 ops/sec [19,998..20,306] 🟢 +5.5%
zip + map + toArray (20 + 20 elements) 8,434 ops/sec [8,381..8,560] → 7,701 ops/sec [7,604..7,861] 🔴 -8.7% 6,080 ops/sec [6,022..6,114] → 6,362 ops/sec [6,234..6,409] 🟢 +4.6%
zip + filter + toArray (20 + 20 elements) 8,107 ops/sec [8,044..8,275] → 7,494 ops/sec [7,477..7,519] 🔴 -7.6% 6,122 ops/sec [6,107..6,172] → 6,426 ops/sec [6,255..6,481] 🟢 +5.0%
zip Sets (15 + 15 elements) 25,054 ops/sec [24,895..25,350] → 23,840 ops/sec [23,522..24,125] 🔴 -4.8% 24,559 ops/sec [23,889..24,895] → 25,098 ops/sec [24,778..25,317] ~ overlap (+2.2%)
zipKeyed 2 keys (10 elements each) 31,479 ops/sec [31,189..32,181] → 30,678 ops/sec [30,090..30,836] 🔴 -2.5% 31,628 ops/sec [31,370..32,286] → 31,307 ops/sec [30,884..31,695] ~ overlap (-1.0%)
zipKeyed 3 keys (20 elements each) 15,783 ops/sec [15,349..16,189] → 15,297 ops/sec [15,209..15,436] ~ overlap (-3.1%) 15,465 ops/sec [15,025..15,681] → 15,894 ops/sec [15,672..16,128] ~ overlap (+2.8%)
zipKeyed longest mode (10 + 20 elements) 18,304 ops/sec [17,989..19,030] → 17,377 ops/sec [17,187..17,577] 🔴 -5.1% 17,675 ops/sec [17,406..17,775] → 17,872 ops/sec [17,654..18,132] ~ overlap (+1.1%)
zipKeyed strict mode (20 + 20 elements) 19,301 ops/sec [19,018..19,923] → 18,522 ops/sec [17,385..18,712] 🔴 -4.0% 18,640 ops/sec [18,337..18,739] → 19,307 ops/sec [18,948..20,000] 🟢 +3.6%
zipKeyed + filter + map (20 elements) 6,266 ops/sec [6,247..6,324] → 5,633 ops/sec [5,572..5,716] 🔴 -10.1% 7,834 ops/sec [7,722..7,961] → 7,586 ops/sec [7,503..7,726] ~ overlap (-3.2%)
array.values().map().filter().toArray() 3,293 ops/sec [3,279..3,374] → 2,876 ops/sec [2,855..2,967] 🔴 -12.7% 4,928 ops/sec [4,871..5,055] → 4,854 ops/sec [4,784..4,880] ~ overlap (-1.5%)
array.values().take(5).toArray() 115,854 ops/sec [109,779..117,953] → 99,765 ops/sec [98,195..104,932] 🔴 -13.9% 124,665 ops/sec [123,720..125,465] → 121,154 ops/sec [119,212..121,957] 🔴 -2.8%
array.values().drop(45).toArray() 108,395 ops/sec [107,790..108,765] → 93,709 ops/sec [91,460..95,223] 🔴 -13.5% 119,839 ops/sec [110,299..122,540] → 121,020 ops/sec [120,111..121,748] ~ overlap (+1.0%)
map.entries() chained helpers 4,945 ops/sec [4,896..4,981] → 4,231 ops/sec [4,118..4,326] 🔴 -14.4% 3,144 ops/sec [3,103..3,164] → 3,336 ops/sec [3,296..3,356] 🟢 +6.1%
set.values() chained helpers 7,513 ops/sec [7,172..7,591] → 6,412 ops/sec [6,140..6,482] 🔴 -14.6% 10,348 ops/sec [10,220..10,491] → 10,730 ops/sec [10,651..10,996] 🟢 +3.7%
string iterator map + toArray 6,810 ops/sec [6,666..6,892] → 5,864 ops/sec [5,804..5,945] 🔴 -13.9% 7,495 ops/sec [7,302..7,553] → 7,609 ops/sec [7,212..8,053] ~ overlap (+1.5%)
json.js — Interp: 🔴 20 · avg -10.0% · Bytecode: 🟢 2, 🔴 6, 12 unch. · avg -1.0%
Benchmark Interpreted Δ Bytecode Δ
parse simple object 79,797 ops/sec [78,134..80,160] → 70,131 ops/sec [69,450..71,409] 🔴 -12.1% 75,525 ops/sec [74,707..75,720] → 74,820 ops/sec [74,238..74,996] ~ overlap (-0.9%)
parse nested object 53,930 ops/sec [52,973..54,763] → 48,707 ops/sec [47,615..48,893] 🔴 -9.7% 50,601 ops/sec [49,391..50,696] → 49,830 ops/sec [49,602..50,174] ~ overlap (-1.5%)
parse array of objects 31,585 ops/sec [31,249..32,399] → 29,587 ops/sec [28,360..29,796] 🔴 -6.3% 29,843 ops/sec [29,570..30,105] → 29,627 ops/sec [29,379..30,288] ~ overlap (-0.7%)
parse large flat object 33,868 ops/sec [33,489..34,655] → 30,822 ops/sec [30,473..31,575] 🔴 -9.0% 31,563 ops/sec [30,932..31,859] → 31,581 ops/sec [31,137..31,824] ~ overlap (+0.1%)
parse mixed types 39,188 ops/sec [38,649..39,841] → 35,776 ops/sec [35,513..35,810] 🔴 -8.7% 38,049 ops/sec [36,634..38,147] → 35,989 ops/sec [35,777..36,353] 🔴 -5.4%
stringify simple object 85,525 ops/sec [84,827..88,235] → 79,004 ops/sec [76,477..79,506] 🔴 -7.6% 77,720 ops/sec [77,247..77,944] → 78,292 ops/sec [77,510..78,640] ~ overlap (+0.7%)
stringify nested object 50,035 ops/sec [49,730..50,072] → 46,084 ops/sec [45,877..46,194] 🔴 -7.9% 43,529 ops/sec [43,301..43,665] → 43,676 ops/sec [42,549..43,829] ~ overlap (+0.3%)
stringify array of objects 21,305 ops/sec [20,975..21,512] → 18,686 ops/sec [18,379..18,749] 🔴 -12.3% 19,494 ops/sec [19,210..19,776] → 19,463 ops/sec [19,429..19,536] ~ overlap (-0.2%)
stringify mixed types 38,785 ops/sec [37,820..39,019] → 34,303 ops/sec [33,845..34,691] 🔴 -11.6% 33,245 ops/sec [32,580..33,332] → 32,967 ops/sec [32,831..33,164] ~ overlap (-0.8%)
reviver doubles numbers 16,983 ops/sec [16,886..17,196] → 15,573 ops/sec [15,503..15,787] 🔴 -8.3% 18,051 ops/sec [17,947..18,060] → 17,983 ops/sec [17,834..18,068] ~ overlap (-0.4%)
reviver filters properties 16,114 ops/sec [15,827..16,355] → 14,645 ops/sec [14,365..14,832] 🔴 -9.1% 16,442 ops/sec [16,324..17,168] → 15,972 ops/sec [15,826..16,003] 🔴 -2.9%
reviver on nested object 19,930 ops/sec [19,600..20,220] → 18,109 ops/sec [17,867..18,297] 🔴 -9.1% 20,823 ops/sec [20,686..21,327] → 19,839 ops/sec [19,722..19,893] 🔴 -4.7%
reviver on array 10,650 ops/sec [10,488..10,808] → 9,580 ops/sec [9,559..9,606] 🔴 -10.1% 11,661 ops/sec [11,647..11,684] → 11,167 ops/sec [11,019..11,281] 🔴 -4.2%
replacer function doubles numbers 20,632 ops/sec [20,324..20,886] → 18,218 ops/sec [18,116..18,674] 🔴 -11.7% 22,609 ops/sec [22,490..23,008] → 21,990 ops/sec [21,800..22,259] 🔴 -2.7%
replacer function excludes properties 27,481 ops/sec [27,088..27,763] → 24,909 ops/sec [24,806..25,003] 🔴 -9.4% 27,432 ops/sec [26,671..27,632] → 26,800 ops/sec [26,630..26,854] ~ overlap (-2.3%)
array replacer (allowlist) 54,008 ops/sec [53,147..54,338] → 49,353 ops/sec [48,902..49,556] 🔴 -8.6% 50,397 ops/sec [48,584..50,745] → 47,108 ops/sec [46,523..47,597] 🔴 -6.5%
stringify with 2-space indent 44,435 ops/sec [44,210..44,725] → 38,961 ops/sec [38,385..39,733] 🔴 -12.3% 38,749 ops/sec [38,370..39,459] → 41,042 ops/sec [40,085..41,219] 🟢 +5.9%
stringify with tab indent 44,711 ops/sec [44,279..45,233] → 39,457 ops/sec [38,874..39,824] 🔴 -11.7% 39,227 ops/sec [38,722..39,973] → 39,910 ops/sec [39,350..40,134] ~ overlap (+1.7%)
parse then stringify 26,706 ops/sec [26,552..27,273] → 22,980 ops/sec [22,781..23,563] 🔴 -14.0% 24,670 ops/sec [24,425..24,851] → 25,295 ops/sec [25,135..25,515] 🟢 +2.5%
stringify then parse 15,525 ops/sec [15,041..15,705] → 13,742 ops/sec [13,405..13,867] 🔴 -11.5% 14,231 ops/sec [14,048..14,501] → 14,394 ops/sec [14,024..14,429] ~ overlap (+1.1%)
jsx.jsx — Interp: 🔴 20, 1 unch. · avg -8.2% · Bytecode: 🔴 4, 17 unch. · avg -1.5%
Benchmark Interpreted Δ Bytecode Δ
simple element 104,719 ops/sec [103,182..106,090] → 95,695 ops/sec [95,299..95,957] 🔴 -8.6% 150,942 ops/sec [148,792..151,900] → 146,876 ops/sec [143,079..153,015] ~ overlap (-2.7%)
self-closing element 109,309 ops/sec [108,056..110,644] → 99,069 ops/sec [98,064..101,048] 🔴 -9.4% 167,817 ops/sec [165,092..169,555] → 167,212 ops/sec [163,186..175,866] ~ overlap (-0.4%)
element with string attribute 88,027 ops/sec [87,338..89,916] → 82,814 ops/sec [81,246..83,652] 🔴 -5.9% 120,587 ops/sec [118,549..122,388] → 120,146 ops/sec [118,178..121,542] ~ overlap (-0.4%)
element with multiple attributes 81,534 ops/sec [75,494..82,624] → 73,141 ops/sec [72,842..73,769] 🔴 -10.3% 91,054 ops/sec [90,489..92,409] → 90,003 ops/sec [88,405..92,070] ~ overlap (-1.2%)
element with expression attribute 86,252 ops/sec [84,538..87,930] → 82,904 ops/sec [81,632..83,675] 🔴 -3.9% 125,135 ops/sec [123,809..126,028] → 121,127 ops/sec [120,119..125,330] ~ overlap (-3.2%)
text child 107,711 ops/sec [104,613..109,113] → 99,976 ops/sec [99,551..100,641] 🔴 -7.2% 150,426 ops/sec [148,050..154,097] → 149,360 ops/sec [148,553..151,181] ~ overlap (-0.7%)
expression child 102,569 ops/sec [101,409..103,586] → 98,481 ops/sec [97,979..99,695] 🔴 -4.0% 149,316 ops/sec [147,813..150,884] → 143,833 ops/sec [143,267..144,702] 🔴 -3.7%
mixed text and expression 97,768 ops/sec [96,722..100,673] → 97,506 ops/sec [88,732..98,165] ~ overlap (-0.3%) 129,994 ops/sec [126,456..132,336] → 125,837 ops/sec [124,076..126,519] ~ overlap (-3.2%)
nested elements (3 levels) 40,448 ops/sec [39,625..41,283] → 36,918 ops/sec [36,632..37,584] 🔴 -8.7% 57,375 ops/sec [57,107..57,678] → 55,762 ops/sec [55,237..57,369] ~ overlap (-2.8%)
sibling children 31,818 ops/sec [30,105..32,776] → 27,541 ops/sec [26,813..27,802] 🔴 -13.4% 41,598 ops/sec [41,190..42,052] → 42,186 ops/sec [41,829..42,384] ~ overlap (+1.4%)
component element 84,232 ops/sec [81,351..86,410] → 72,951 ops/sec [72,235..74,113] 🔴 -13.4% 111,786 ops/sec [108,142..113,092] → 109,448 ops/sec [107,319..115,160] ~ overlap (-2.1%)
component with children 48,843 ops/sec [48,747..49,030] → 44,469 ops/sec [44,290..44,865] 🔴 -9.0% 65,424 ops/sec [61,910..69,337] → 65,558 ops/sec [64,484..66,626] ~ overlap (+0.2%)
dotted component 69,837 ops/sec [68,520..70,530] → 62,608 ops/sec [61,595..63,069] 🔴 -10.4% 82,754 ops/sec [81,689..85,864] → 85,619 ops/sec [84,352..86,151] ~ overlap (+3.5%)
empty fragment 120,763 ops/sec [117,134..122,221] → 101,401 ops/sec [100,771..101,872] 🔴 -16.0% 177,175 ops/sec [175,746..182,342] → 184,140 ops/sec [180,530..185,629] ~ overlap (+3.9%)
fragment with children 29,889 ops/sec [29,572..30,678] → 27,513 ops/sec [27,265..27,678] 🔴 -7.9% 43,131 ops/sec [41,429..43,495] → 41,895 ops/sec [41,351..42,248] ~ overlap (-2.9%)
spread attributes 57,362 ops/sec [56,314..58,137] → 53,209 ops/sec [52,070..54,058] 🔴 -7.2% 65,879 ops/sec [65,240..65,976] → 62,777 ops/sec [62,307..63,752] 🔴 -4.7%
spread with overrides 51,060 ops/sec [50,055..51,762] → 46,639 ops/sec [45,771..47,509] 🔴 -8.7% 56,958 ops/sec [56,665..57,760] → 54,977 ops/sec [54,485..55,553] 🔴 -3.5%
shorthand props 81,372 ops/sec [80,531..82,137] → 74,862 ops/sec [73,799..75,740] 🔴 -8.0% 112,853 ops/sec [111,543..113,858] → 107,737 ops/sec [106,247..109,071] 🔴 -4.5%
nav bar structure 14,349 ops/sec [14,115..14,520] → 13,199 ops/sec [12,959..13,379] 🔴 -8.0% 19,569 ops/sec [19,410..19,642] → 18,921 ops/sec [18,714..19,772] ~ overlap (-3.3%)
card component tree 16,523 ops/sec [16,410..16,890] → 15,679 ops/sec [15,278..15,828] 🔴 -5.1% 21,740 ops/sec [21,476..21,962] → 21,654 ops/sec [21,488..21,915] ~ overlap (-0.4%)
10 list items via Array.from 7,454 ops/sec [7,413..7,475] → 6,965 ops/sec [6,825..7,004] 🔴 -6.6% 9,614 ops/sec [9,415..9,834] → 9,540 ops/sec [9,265..9,604] ~ overlap (-0.8%)
modules.js — Interp: 🔴 9 · avg -8.4% · Bytecode: 🔴 3, 6 unch. · avg -1.1%
Benchmark Interpreted Δ Bytecode Δ
call imported function 185,064 ops/sec [184,482..186,459] → 169,104 ops/sec [165,477..170,683] 🔴 -8.6% 713,437 ops/sec [694,470..719,590] → 706,899 ops/sec [703,637..719,650] ~ overlap (-0.9%)
call two imported functions 104,716 ops/sec [101,884..105,715] → 96,632 ops/sec [95,012..97,225] 🔴 -7.7% 446,384 ops/sec [443,178..447,529] → 452,940 ops/sec [447,338..457,769] ~ overlap (+1.5%)
read imported constant 605,030 ops/sec [592,346..617,436] → 541,357 ops/sec [528,047..561,172] 🔴 -10.5% 1,908,362 ops/sec [1,901,003..1,912,321] → 1,898,791 ops/sec [1,880,015..1,923,476] ~ overlap (-0.5%)
read imported string 592,423 ops/sec [587,318..599,000] → 549,170 ops/sec [530,611..553,601] 🔴 -7.3% 1,900,048 ops/sec [1,883,208..1,920,353] → 1,935,099 ops/sec [1,919,803..1,956,891] ~ overlap (+1.8%)
read JSON string property 602,423 ops/sec [583,503..611,849] → 547,713 ops/sec [527,267..563,440] 🔴 -9.1% 1,915,248 ops/sec [1,900,849..1,920,504] → 1,885,100 ops/sec [1,877,054..1,945,572] ~ overlap (-1.6%)
read JSON number property 591,138 ops/sec [575,915..606,464] → 543,905 ops/sec [529,058..561,002] 🔴 -8.0% 1,912,662 ops/sec [1,906,815..1,923,848] → 1,869,977 ops/sec [1,848,170..1,883,619] 🔴 -2.2%
read JSON boolean property 604,230 ops/sec [584,495..614,287] → 550,788 ops/sec [546,920..566,761] 🔴 -8.8% 1,971,119 ops/sec [1,933,471..1,985,928] → 1,886,251 ops/sec [1,863,685..1,889,495] 🔴 -4.3%
read JSON array property 607,164 ops/sec [587,480..623,803] → 560,788 ops/sec [541,083..566,722] 🔴 -7.6% 1,954,643 ops/sec [1,929,570..1,959,212] → 1,872,130 ops/sec [1,845,826..1,910,306] 🔴 -4.2%
read multiple JSON properties 368,760 ops/sec [361,712..376,155] → 338,735 ops/sec [327,805..346,919] 🔴 -8.1% 1,541,168 ops/sec [1,527,135..1,548,843] → 1,551,285 ops/sec [1,519,143..1,555,144] ~ overlap (+0.7%)
numbers.js — Interp: 🔴 11 · avg -10.3% · Bytecode: 🟢 2, 🔴 2, 7 unch. · avg -0.4%
Benchmark Interpreted Δ Bytecode Δ
integer arithmetic 209,170 ops/sec [202,871..212,739] → 181,556 ops/sec [179,968..189,518] 🔴 -13.2% 678,525 ops/sec [668,264..681,984] → 577,957 ops/sec [574,703..582,060] 🔴 -14.8%
floating point arithmetic 237,159 ops/sec [232,369..242,057] → 212,248 ops/sec [208,561..219,659] 🔴 -10.5% 424,882 ops/sec [420,801..427,745] → 411,678 ops/sec [404,973..414,177] 🔴 -3.1%
number coercion 84,792 ops/sec [82,201..86,211] → 75,256 ops/sec [73,157..75,765] 🔴 -11.2% 95,803 ops/sec [95,472..96,096] → 96,595 ops/sec [95,382..96,834] ~ overlap (+0.8%)
toFixed 55,828 ops/sec [55,093..56,531] → 50,098 ops/sec [49,748..51,461] 🔴 -10.3% 57,033 ops/sec [56,357..58,276] → 57,818 ops/sec [57,383..58,213] ~ overlap (+1.4%)
toString 77,111 ops/sec [74,877..77,983] → 71,616 ops/sec [70,526..72,197] 🔴 -7.1% 87,952 ops/sec [87,546..89,938] → 88,894 ops/sec [88,474..89,568] ~ overlap (+1.1%)
valueOf 108,016 ops/sec [105,655..110,009] → 98,694 ops/sec [98,078..100,168] 🔴 -8.6% 122,248 ops/sec [120,336..123,999] → 123,913 ops/sec [123,606..124,562] ~ overlap (+1.4%)
toPrecision 70,838 ops/sec [70,368..71,754] → 66,062 ops/sec [65,702..67,476] 🔴 -6.7% 78,596 ops/sec [77,289..80,813] → 80,428 ops/sec [79,134..81,350] ~ overlap (+2.3%)
Number.isNaN 138,305 ops/sec [134,341..139,611] → 120,843 ops/sec [118,863..125,630] 🔴 -12.6% 133,647 ops/sec [132,619..135,261] → 137,495 ops/sec [136,989..137,638] 🟢 +2.9%
Number.isFinite 139,293 ops/sec [135,644..143,145] → 120,366 ops/sec [115,597..121,703] 🔴 -13.6% 126,712 ops/sec [123,306..127,953] → 131,579 ops/sec [130,556..132,016] 🟢 +3.8%
Number.isInteger 141,505 ops/sec [138,730..141,754] → 124,609 ops/sec [121,982..125,694] 🔴 -11.9% 137,000 ops/sec [125,498..138,908] → 138,734 ops/sec [138,085..140,135] ~ overlap (+1.3%)
Number.parseInt and parseFloat 109,091 ops/sec [106,721..113,869] → 100,446 ops/sec [98,117..101,649] 🔴 -7.9% 102,304 ops/sec [100,958..104,912] → 101,302 ops/sec [100,421..104,503] ~ overlap (-1.0%)
objects.js — Interp: 🔴 7 · avg -6.7% · Bytecode: 🟢 1, 🔴 1, 5 unch. · avg +0.1%
Benchmark Interpreted Δ Bytecode Δ
create simple object 236,780 ops/sec [235,606..237,519] → 226,299 ops/sec [223,770..229,179] 🔴 -4.4% 247,708 ops/sec [246,643..250,029] → 256,944 ops/sec [252,991..258,518] 🟢 +3.7%
create nested object 126,644 ops/sec [123,368..128,757] → 118,997 ops/sec [116,766..120,453] 🔴 -6.0% 111,219 ops/sec [108,510..113,090] → 111,830 ops/sec [111,459..112,465] ~ overlap (+0.6%)
create 50 objects via Array.from 4,539 ops/sec [4,432..4,568] → 4,204 ops/sec [4,123..4,264] 🔴 -7.4% 4,436 ops/sec [4,403..4,482] → 4,428 ops/sec [4,405..4,430] ~ overlap (-0.2%)
property read 241,478 ops/sec [235,453..245,285] → 216,553 ops/sec [213,149..219,407] 🔴 -10.3% 302,767 ops/sec [295,909..305,773] → 300,047 ops/sec [299,048..301,839] ~ overlap (-0.9%)
Object.keys 135,928 ops/sec [131,470..136,658] → 125,264 ops/sec [120,977..128,714] 🔴 -7.8% 145,056 ops/sec [143,187..145,911] → 141,749 ops/sec [141,367..142,549] 🔴 -2.3%
Object.entries 53,350 ops/sec [52,904..54,192] → 50,040 ops/sec [49,003..50,977] 🔴 -6.2% 53,283 ops/sec [52,568..53,984] → 52,884 ops/sec [52,776..53,557] ~ overlap (-0.7%)
spread operator 98,612 ops/sec [97,712..99,695] → 94,051 ops/sec [93,277..94,715] 🔴 -4.6% 98,188 ops/sec [97,397..99,047] → 98,866 ops/sec [97,554..100,377] ~ overlap (+0.7%)
promises.js — Interp: 🔴 11, 1 unch. · avg -5.9% · Bytecode: 12 unch. · avg -0.4%
Benchmark Interpreted Δ Bytecode Δ
Promise.resolve(value) 233,033 ops/sec [230,519..234,049] → 210,679 ops/sec [204,931..216,329] 🔴 -9.6% 259,583 ops/sec [255,210..261,117] → 254,776 ops/sec [248,472..257,012] ~ overlap (-1.9%)
new Promise(resolve => resolve(value)) 88,213 ops/sec [86,605..90,182] → 84,760 ops/sec [83,968..85,039] 🔴 -3.9% 111,163 ops/sec [109,213..112,196] → 110,544 ops/sec [109,482..110,730] ~ overlap (-0.6%)
Promise.reject(reason) 232,919 ops/sec [230,034..239,394] → 221,252 ops/sec [218,289..224,866] 🔴 -5.0% 242,791 ops/sec [239,930..244,817] → 243,833 ops/sec [242,613..246,256] ~ overlap (+0.4%)
resolve + then (1 handler) 84,659 ops/sec [82,833..86,205] → 78,828 ops/sec [77,339..79,274] 🔴 -6.9% 102,663 ops/sec [100,620..103,168] → 102,869 ops/sec [100,916..103,850] ~ overlap (+0.2%)
resolve + then chain (3 deep) 34,143 ops/sec [33,472..34,207] → 32,115 ops/sec [31,684..32,300] 🔴 -5.9% 41,151 ops/sec [40,366..41,414] → 42,209 ops/sec [41,250..42,670] ~ overlap (+2.6%)
resolve + then chain (10 deep) 11,578 ops/sec [11,417..11,650] → 10,584 ops/sec [10,493..10,694] 🔴 -8.6% 13,669 ops/sec [13,322..13,792] → 13,648 ops/sec [13,456..13,749] ~ overlap (-0.1%)
reject + catch + then 49,246 ops/sec [47,996..49,544] → 46,145 ops/sec [44,963..48,306] ~ overlap (-6.3%) 55,670 ops/sec [54,375..56,546] → 55,740 ops/sec [55,347..56,578] ~ overlap (+0.1%)
resolve + finally + then 42,316 ops/sec [41,760..43,073] → 40,711 ops/sec [40,397..40,747] 🔴 -3.8% 47,853 ops/sec [46,770..48,573] → 48,004 ops/sec [47,346..48,483] ~ overlap (+0.3%)
Promise.all (5 resolved) 16,961 ops/sec [16,786..17,162] → 16,588 ops/sec [16,291..16,767] 🔴 -2.2% 16,315 ops/sec [16,208..16,474] → 16,607 ops/sec [16,414..16,681] ~ overlap (+1.8%)
Promise.race (5 resolved) 18,275 ops/sec [17,813..18,440] → 16,838 ops/sec [16,541..17,010] 🔴 -7.9% 17,354 ops/sec [17,167..17,704] → 17,336 ops/sec [17,189..17,703] ~ overlap (-0.1%)
Promise.allSettled (5 mixed) 14,466 ops/sec [14,403..14,532] → 13,767 ops/sec [13,620..13,952] 🔴 -4.8% 14,491 ops/sec [14,082..14,622] → 13,833 ops/sec [13,669..14,128] ~ overlap (-4.5%)
Promise.any (5 mixed) 17,208 ops/sec [16,998..17,548] → 16,158 ops/sec [15,916..16,443] 🔴 -6.1% 16,380 ops/sec [16,156..16,449] → 15,958 ops/sec [15,873..16,252] ~ overlap (-2.6%)
regexp.js — Interp: 🔴 11 · avg -9.2% · Bytecode: 🟢 1, 10 unch. · avg +0.0%
Benchmark Interpreted Δ Bytecode Δ
regex literal creation 75,877 ops/sec [74,445..77,159] → 69,026 ops/sec [67,857..70,546] 🔴 -9.0% 64,088 ops/sec [63,338..64,564] → 63,989 ops/sec [62,713..64,660] ~ overlap (-0.2%)
new RegExp(pattern, flags) 67,076 ops/sec [65,624..67,771] → 60,658 ops/sec [59,779..61,078] 🔴 -9.6% 63,556 ops/sec [63,159..63,747] → 62,939 ops/sec [62,596..63,881] ~ overlap (-1.0%)
RegExp(existingRegex) returns the same regex 287,800 ops/sec [279,793..294,078] → 263,991 ops/sec [260,699..271,220] 🔴 -8.3% 411,611 ops/sec [407,625..417,240] → 408,158 ops/sec [402,873..418,697] ~ overlap (-0.8%)
test() on a global regex 72,766 ops/sec [72,181..74,986] → 66,564 ops/sec [65,991..67,719] 🔴 -8.5% 78,233 ops/sec [77,598..78,833] → 78,395 ops/sec [77,247..78,649] ~ overlap (+0.2%)
exec() with capture groups 63,005 ops/sec [61,636..63,742] → 57,199 ops/sec [56,999..57,558] 🔴 -9.2% 66,263 ops/sec [65,108..66,615] → 64,803 ops/sec [63,896..65,207] ~ overlap (-2.2%)
toString() 211,088 ops/sec [207,816..216,444] → 196,781 ops/sec [194,021..198,048] 🔴 -6.8% 286,811 ops/sec [283,143..290,047] → 285,668 ops/sec [283,043..288,849] ~ overlap (-0.4%)
match() with global regex 21,765 ops/sec [21,389..22,332] → 19,676 ops/sec [19,246..19,794] 🔴 -9.6% 19,630 ops/sec [19,493..19,779] → 19,871 ops/sec [19,691..19,984] ~ overlap (+1.2%)
matchAll() with capture groups 11,439 ops/sec [11,151..11,803] → 10,108 ops/sec [10,052..10,301] 🔴 -11.6% 12,239 ops/sec [12,063..12,603] → 12,290 ops/sec [12,163..12,493] ~ overlap (+0.4%)
replace() with global regex 21,296 ops/sec [20,747..21,532] → 19,087 ops/sec [18,946..19,311] 🔴 -10.4% 18,870 ops/sec [18,741..18,913] → 19,107 ops/sec [19,024..19,221] 🟢 +1.3%
search() with regex 43,366 ops/sec [42,340..43,877] → 38,479 ops/sec [37,600..39,063] 🔴 -11.3% 37,626 ops/sec [37,353..38,075] → 38,247 ops/sec [37,894..39,125] ~ overlap (+1.7%)
split() with regex separator 21,392 ops/sec [20,940..21,588] → 19,809 ops/sec [19,667..20,047] 🔴 -7.4% 19,736 ops/sec [19,590..19,834] → 19,711 ops/sec [19,687..19,961] ~ overlap (-0.1%)
strings.js — Interp: 🔴 17, 2 unch. · avg -15.2% · Bytecode: 🟢 3, 🔴 5, 11 unch. · avg -8.5%
Benchmark Interpreted Δ Bytecode Δ
string concatenation 188,633 ops/sec [180,617..190,451] → 179,185 ops/sec [177,821..179,197] 🔴 -5.0% 109,339 ops/sec [107,436..110,380] → 110,373 ops/sec [109,164..112,146] ~ overlap (+0.9%)
template literal 359,632 ops/sec [348,177..362,430] → 355,762 ops/sec [353,309..356,470] ~ overlap (-1.1%) 195,801 ops/sec [192,421..197,817] → 198,229 ops/sec [196,762..202,852] ~ overlap (+1.2%)
string repeat 205,530 ops/sec [198,933..220,660] → 188,826 ops/sec [177,490..194,663] 🔴 -8.1% 209,990 ops/sec [207,132..211,386] → 214,636 ops/sec [213,190..215,234] 🟢 +2.2%
split and join 79,734 ops/sec [78,496..84,847] → 66,748 ops/sec [65,741..67,185] 🔴 -16.3% 70,511 ops/sec [70,385..70,870] → 73,490 ops/sec [72,770..74,480] 🟢 +4.2%
indexOf and includes 92,555 ops/sec [90,756..94,055] → 77,451 ops/sec [76,351..77,563] 🔴 -16.3% 84,344 ops/sec [84,282..84,556] → 84,049 ops/sec [82,151..85,333] ~ overlap (-0.3%)
toUpperCase and toLowerCase 136,183 ops/sec [133,041..137,213] → 116,143 ops/sec [115,439..117,888] 🔴 -14.7% 148,779 ops/sec [143,980..150,247] → 152,576 ops/sec [151,730..153,445] 🟢 +2.6%
slice and substring 87,219 ops/sec [86,444..88,800] → 73,379 ops/sec [72,506..74,292] 🔴 -15.9% 88,996 ops/sec [88,434..90,219] → 88,332 ops/sec [87,633..88,775] ~ overlap (-0.7%)
trim operations 106,261 ops/sec [89,508..106,853] → 87,199 ops/sec [85,974..88,937] 🔴 -17.9% 112,265 ops/sec [110,567..114,007] → 110,295 ops/sec [109,562..110,338] 🔴 -1.8%
replace and replaceAll 104,967 ops/sec [88,447..107,353] → 97,354 ops/sec [95,985..98,178] ~ overlap (-7.3%) 100,030 ops/sec [97,951..100,682] → 100,955 ops/sec [97,042..101,730] ~ overlap (+0.9%)
startsWith and endsWith 69,694 ops/sec [68,828..71,669] → 63,678 ops/sec [62,612..63,709] 🔴 -8.6% 67,043 ops/sec [66,598..67,110] → 66,890 ops/sec [66,071..67,131] ~ overlap (-0.2%)
padStart and padEnd 103,885 ops/sec [103,319..104,497] → 95,347 ops/sec [94,021..96,005] 🔴 -8.2% 104,897 ops/sec [104,598..107,035] → 103,316 ops/sec [99,762..104,648] ~ overlap (-1.5%)
identity tag, no substitutions 199,844 ops/sec [196,869..200,208] → 181,267 ops/sec [181,055..181,622] 🔴 -9.3% 602,370 ops/sec [601,002..603,324] → 563,104 ops/sec [557,628..571,421] 🔴 -6.5%
tag with 1 substitution 44,610 ops/sec [43,390..45,239] → 40,472 ops/sec [40,343..41,014] 🔴 -9.3% 54,048 ops/sec [53,484..54,788] → 53,395 ops/sec [53,113..54,072] ~ overlap (-1.2%)
tag with 3 substitutions 24,479 ops/sec [24,151..24,984] → 22,403 ops/sec [22,083..22,762] 🔴 -8.5% 30,467 ops/sec [29,950..30,607] → 30,251 ops/sec [30,076..30,832] ~ overlap (-0.7%)
tag with 6 substitutions 14,614 ops/sec [14,444..14,714] → 13,285 ops/sec [13,166..13,421] 🔴 -9.1% 21,821 ops/sec [17,411..28,366] → 18,578 ops/sec [18,373..18,807] ~ overlap (-14.9%)
String.raw, no substitutions 276,869 ops/sec [273,415..278,883] → 243,476 ops/sec [238,249..249,313] 🔴 -12.1% 433,097 ops/sec [430,650..435,914] → 259,853 ops/sec [255,555..261,046] 🔴 -40.0%
String.raw, 2 substitutions 329,911 ops/sec [327,638..331,536] → 187,381 ops/sec [185,182..188,212] 🔴 -43.2% 310,031 ops/sec [307,132..311,288] → 187,227 ops/sec [185,268..190,048] 🔴 -39.6%
tag accessing .raw array 126,160 ops/sec [125,592..129,488] → 77,794 ops/sec [77,558..77,898] 🔴 -38.3% 153,619 ops/sec [150,889..156,573] → 98,740 ops/sec [95,035..99,619] 🔴 -35.7%
method as tag (this binding) 49,456 ops/sec [49,265..49,702] → 29,690 ops/sec [29,325..29,991] 🔴 -40.0% 62,936 ops/sec [61,937..63,416] → 43,357 ops/sec [39,996..65,241] ~ overlap (-31.1%)
tsv.js — Interp: 🔴 9 · avg -11.0% · Bytecode: 🟢 8, 1 unch. · avg +5.4%
Benchmark Interpreted Δ Bytecode Δ
parse simple 3-column TSV 51,439 ops/sec [50,729..53,299] → 46,858 ops/sec [46,510..47,566] 🔴 -8.9% 49,751 ops/sec [49,158..49,887] → 51,386 ops/sec [50,505..51,931] 🟢 +3.3%
parse 10-row TSV 14,529 ops/sec [14,190..14,822] → 12,870 ops/sec [12,799..12,904] 🔴 -11.4% 12,868 ops/sec [12,748..12,957] → 13,666 ops/sec [13,352..14,001] 🟢 +6.2%
parse 100-row TSV 2,312 ops/sec [2,274..2,448] → 2,038 ops/sec [2,005..2,113] 🔴 -11.8% 2,026 ops/sec [2,013..2,071] → 2,279 ops/sec [2,274..2,290] 🟢 +12.5%
parse TSV with backslash-escaped fields 10,695 ops/sec [10,631..10,844] → 9,418 ops/sec [9,201..9,461] 🔴 -11.9% 9,360 ops/sec [9,243..9,465] → 10,385 ops/sec [10,356..10,412] 🟢 +11.0%
parse without headers (array of arrays) 6,223 ops/sec [6,055..6,307] → 5,497 ops/sec [5,463..5,543] 🔴 -11.7% 5,552 ops/sec [5,495..5,595] → 5,962 ops/sec [5,887..6,052] 🟢 +7.4%
stringify array of objects 45,879 ops/sec [44,375..46,884] → 39,749 ops/sec [38,996..40,841] 🔴 -13.4% 43,132 ops/sec [42,635..43,314] → 43,686 ops/sec [43,478..43,813] 🟢 +1.3%
stringify array of arrays 13,118 ops/sec [12,206..13,358] → 11,823 ops/sec [11,454..11,984] 🔴 -9.9% 11,678 ops/sec [11,580..11,786] → 12,192 ops/sec [11,897..12,377] 🟢 +4.4%
stringify with values needing escaping 35,649 ops/sec [35,378..37,033] → 32,029 ops/sec [31,569..32,897] 🔴 -10.2% 33,946 ops/sec [33,662..34,173] → 33,772 ops/sec [33,296..34,781] ~ overlap (-0.5%)
parse then stringify 7,950 ops/sec [7,786..8,114] → 7,156 ops/sec [7,099..7,238] 🔴 -10.0% 7,388 ops/sec [7,290..7,470] → 7,621 ops/sec [7,546..7,654] 🟢 +3.2%
typed-arrays.js — Interp: 🟢 8, 🔴 12, 2 unch. · avg -1.8% · Bytecode: 🟢 4, 🔴 8, 10 unch. · avg -0.2%
Benchmark Interpreted Δ Bytecode Δ
new Int32Array(0) 254,907 ops/sec [250,645..257,897] → 242,659 ops/sec [241,369..243,738] 🔴 -4.8% 302,460 ops/sec [301,286..303,735] → 295,628 ops/sec [293,742..297,969] 🔴 -2.3%
new Int32Array(100) 234,089 ops/sec [232,100..234,646] → 225,452 ops/sec [224,150..227,731] 🔴 -3.7% 277,201 ops/sec [276,811..277,837] → 266,720 ops/sec [264,932..267,171] 🔴 -3.8%
new Int32Array(1000) 143,465 ops/sec [142,364..143,595] → 152,580 ops/sec [151,066..153,995] 🟢 +6.4% 173,848 ops/sec [171,857..174,818] → 173,258 ops/sec [171,954..174,484] ~ overlap (-0.3%)
new Float64Array(100) 211,948 ops/sec [211,366..214,207] → 215,866 ops/sec [214,647..217,806] 🟢 +1.8% 255,149 ops/sec [245,418..257,609] → 252,989 ops/sec [251,649..253,657] ~ overlap (-0.8%)
Int32Array.from([...]) 154,144 ops/sec [151,242..154,827] → 147,959 ops/sec [146,341..148,729] 🔴 -4.0% 167,707 ops/sec [166,607..168,055] → 162,892 ops/sec [161,696..163,453] 🔴 -2.9%
Int32Array.of(1, 2, 3, 4, 5) 232,346 ops/sec [230,122..234,836] → 229,173 ops/sec [227,026..230,947] ~ overlap (-1.4%) 279,586 ops/sec [278,983..280,356] → 269,418 ops/sec [267,106..270,838] 🔴 -3.6%
sequential write 100 elements 2,938 ops/sec [2,927..2,949] → 3,014 ops/sec [2,985..3,031] 🟢 +2.6% 9,521 ops/sec [9,487..9,697] → 9,784 ops/sec [9,591..9,802] ~ overlap (+2.8%)
sequential read 100 elements 2,900 ops/sec [2,860..2,904] → 3,050 ops/sec [3,039..3,059] 🟢 +5.2% 9,371 ops/sec [9,295..9,490] → 9,595 ops/sec [9,501..9,683] 🟢 +2.4%
Float64Array write 100 elements 2,650 ops/sec [2,632..2,687] → 2,751 ops/sec [2,747..2,764] 🟢 +3.8% 6,530 ops/sec [6,403..6,576] → 6,702 ops/sec [6,670..6,711] 🟢 +2.6%
fill(42) 44,303 ops/sec [44,139..44,461] → 43,698 ops/sec [43,539..43,774] 🔴 -1.4% 45,867 ops/sec [45,863..45,872] → 45,147 ops/sec [45,067..45,183] 🔴 -1.6%
slice() 167,444 ops/sec [166,566..167,987] → 166,041 ops/sec [161,342..167,201] ~ overlap (-0.8%) 201,687 ops/sec [182,278..203,592] → 201,217 ops/sec [199,457..203,258] ~ overlap (-0.2%)
map(x => x * 2) 5,126 ops/sec [5,018..5,160] → 4,722 ops/sec [4,704..4,768] 🔴 -7.9% 7,318 ops/sec [7,243..7,375] → 7,239 ops/sec [7,179..7,250] ~ overlap (-1.1%)
filter(x => x > 50) 5,488 ops/sec [5,411..5,519] → 5,247 ops/sec [5,175..5,294] 🔴 -4.4% 8,137 ops/sec [8,100..8,181] → 8,150 ops/sec [8,108..8,165] ~ overlap (+0.2%)
reduce (sum) 5,251 ops/sec [5,174..5,291] → 4,840 ops/sec [4,744..4,895] 🔴 -7.8% 6,752 ops/sec [6,702..6,795] → 7,093 ops/sec [7,023..7,140] 🟢 +5.1%
sort() 144,159 ops/sec [143,093..144,836] → 146,939 ops/sec [145,887..147,011] 🟢 +1.9% 171,200 ops/sec [170,349..171,476] → 171,333 ops/sec [170,826..171,891] ~ overlap (+0.1%)
indexOf() 333,567 ops/sec [331,474..336,163] → 309,542 ops/sec [307,487..310,943] 🔴 -7.2% 414,562 ops/sec [413,961..415,418] → 412,644 ops/sec [411,030..413,152] 🔴 -0.5%
reverse() 263,067 ops/sec [261,253..264,537] → 258,585 ops/sec [255,079..259,720] 🔴 -1.7% 326,912 ops/sec [325,921..327,601] → 326,894 ops/sec [326,308..327,095] ~ overlap (-0.0%)
create view over existing buffer 311,761 ops/sec [308,527..312,906] → 279,998 ops/sec [277,847..281,654] 🔴 -10.2% 363,910 ops/sec [361,557..365,228] → 368,950 ops/sec [363,845..371,742] ~ overlap (+1.4%)
subarray() 324,670 ops/sec [320,077..326,828] → 297,711 ops/sec [295,694..301,846] 🔴 -8.3% 411,552 ops/sec [410,270..413,107] → 409,159 ops/sec [404,296..411,166] ~ overlap (-0.6%)
set() from array 420,900 ops/sec [418,848..423,412] → 373,379 ops/sec [370,996..374,262] 🔴 -11.3% 502,010 ops/sec [500,411..502,591] → 492,754 ops/sec [491,606..494,831] 🔴 -1.8%
for-of loop 4,085 ops/sec [4,073..4,095] → 4,149 ops/sec [4,116..4,182] 🟢 +1.6% 14,320 ops/sec [14,280..14,402] → 14,601 ops/sec [14,446..14,821] 🟢 +2.0%
spread into array 13,461 ops/sec [13,385..13,558] → 15,175 ops/sec [14,960..15,383] 🟢 +12.7% 68,769 ops/sec [68,483..69,960] → 67,948 ops/sec [67,392..68,314] 🔴 -1.2%
uint8array-encoding.js — Interp: 🟢 10, 🔴 5, 3 unch. · avg +20.0% · Bytecode: 🟢 12, 🔴 1, 5 unch. · avg +37.0%
Benchmark Interpreted Δ Bytecode Δ
short (5 bytes) 312,331 ops/sec [305,326..316,394] → 284,544 ops/sec [281,002..287,815] 🔴 -8.9% 490,741 ops/sec [487,171..496,840] → 501,690 ops/sec [500,445..504,275] 🟢 +2.2%
medium (450 bytes) 173,494 ops/sec [169,949..176,806] → 161,080 ops/sec [159,745..162,047] 🔴 -7.2% 211,353 ops/sec [209,678..212,765] → 206,695 ops/sec [205,054..209,723] ~ overlap (-2.2%)
large (4096 bytes) 36,166 ops/sec [34,213..36,986] → 42,010 ops/sec [41,961..42,258] 🟢 +16.2% 36,274 ops/sec [35,819..38,062] → 35,487 ops/sec [35,363..36,768] ~ overlap (-2.2%)
base64url alphabet 122,646 ops/sec [118,886..124,212] → 156,519 ops/sec [155,035..157,252] 🟢 +27.6% 129,542 ops/sec [129,274..129,902] → 126,151 ops/sec [125,462..126,967] 🔴 -2.6%
omitPadding 181,365 ops/sec [178,694..184,159] → 263,950 ops/sec [262,781..265,902] 🟢 +45.5% 213,452 ops/sec [212,581..213,837] → 212,369 ops/sec [208,001..214,257] ~ overlap (-0.5%)
short (8 chars) 166,494 ops/sec [161,856..167,631] → 244,294 ops/sec [243,398..245,053] 🟢 +46.7% 185,112 ops/sec [183,370..185,594] → 286,640 ops/sec [285,434..289,586] 🟢 +54.8%
medium (600 chars) 77,395 ops/sec [76,576..77,825] → 125,845 ops/sec [124,414..126,547] 🟢 +62.6% 79,413 ops/sec [79,118..79,599] → 137,868 ops/sec [136,103..138,253] 🟢 +73.6%
large (5464 chars) 14,786 ops/sec [14,450..15,713] → 25,373 ops/sec [25,319..25,396] 🟢 +71.6% 14,613 ops/sec [14,379..14,821] → 26,217 ops/sec [25,487..26,284] 🟢 +79.4%
short (5 bytes) 323,412 ops/sec [313,951..326,842] → 494,767 ops/sec [491,789..495,191] 🟢 +53.0% 510,037 ops/sec [505,210..512,378] → 859,485 ops/sec [852,061..867,771] 🟢 +68.5%
medium (450 bytes) 160,865 ops/sec [156,829..169,602] → 236,830 ops/sec [235,521..238,351] 🟢 +47.2% 185,869 ops/sec [183,936..188,129] → 300,612 ops/sec [299,706..302,667] 🟢 +61.7%
large (4096 bytes) 41,676 ops/sec [39,742..42,119] → 46,353 ops/sec [45,938..46,608] 🟢 +11.2% 30,250 ops/sec [29,095..30,745] → 48,209 ops/sec [47,597..48,642] 🟢 +59.4%
short (10 chars) 288,931 ops/sec [285,316..294,307] → 272,651 ops/sec [270,473..274,511] 🔴 -5.6% 206,880 ops/sec [202,604..214,146] → 321,017 ops/sec [316,935..323,688] 🟢 +55.2%
medium (900 chars) 187,824 ops/sec [186,145..188,940] → 189,310 ops/sec [188,561..189,522] ~ overlap (+0.8%) 129,720 ops/sec [129,521..130,629] → 215,645 ops/sec [214,924..216,123] 🟢 +66.2%
large (8192 chars) 50,113 ops/sec [49,304..50,583] → 53,403 ops/sec [52,990..53,598] 🟢 +6.6% 31,315 ops/sec [29,800..31,635] → 56,498 ops/sec [56,359..56,554] 🟢 +80.4%
setFromBase64 (450 bytes) 123,924 ops/sec [122,787..125,056] → 120,799 ops/sec [117,829..121,507] 🔴 -2.5% 82,241 ops/sec [82,098..82,373] → 137,878 ops/sec [136,889..138,668] 🟢 +67.7%
setFromHex (450 bytes) 168,650 ops/sec [165,202..169,136] → 168,331 ops/sec [166,327..169,301] ~ overlap (-0.2%) 204,197 ops/sec [117,327..204,873] → 208,942 ops/sec [208,070..209,764] 🟢 +2.3%
toBase64 → fromBase64 (450 bytes) 92,966 ops/sec [92,286..93,735] → 90,064 ops/sec [89,688..90,611] 🔴 -3.1% 97,737 ops/sec [97,036..98,245] → 98,908 ops/sec [97,366..99,013] ~ overlap (+1.2%)
toHex → fromHex (450 bytes) 118,650 ops/sec [117,552..120,539] → 117,360 ops/sec [115,957..118,007] ~ overlap (-1.1%) 133,231 ops/sec [132,419..133,630] → 134,167 ops/sec [131,939..135,307] ~ overlap (+0.7%)

Measured on ubuntu-latest x64. Benchmark ranges compare cached main-branch min/max ops/sec with the PR run; overlapping ranges are treated as unchanged noise. Percentage deltas are secondary context.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 21, 2026

Suite Timing

Suite Metric Interpreted Bytecode
Tests Total 7518 7518
Tests Passed 7477 ✅ 7518 ✅
Tests Skipped 41 0
Tests Workers 4 4
Tests Test Duration 1.40s 1.60s
Tests Lex (cumulative) 179.9ms 130.6ms
Tests Parse (cumulative) 285.8ms 309.2ms
Tests Compile (cumulative) 183.5ms
Tests Execute (cumulative) 1.57s 1.62s
Tests Engine Total (cumulative) 2.03s 2.24s
Tests Lex (avg/worker) 45.0ms 32.6ms
Tests Parse (avg/worker) 71.4ms 77.3ms
Tests Compile (avg/worker) 45.9ms
Tests Execute (avg/worker) 392.2ms 403.8ms
Tests Engine Total (avg/worker) 508.6ms 559.6ms
Benchmarks Total 386 386
Benchmarks Workers 4 4
Benchmarks Duration 3.05min 2.83min

Measured on ubuntu-latest x64.

- Add Bun-based scripts for CLI, config, and app-specific coverage
- Simplify CI and PR workflows by replacing long inline shell blocks
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@scripts/test-cli-config.ts`:
- Around line 372-393: The test currently only inspects stdout for "asi" but
doesn't verify the process succeeded; for both Bun.spawnSync invocations
(benchInterp and benchBc) check that their exitCode === 0 before asserting
stdout.includes("asi") — if exitCode is non-zero, throw a descriptive Error
indicating the benchmark runner failed (include stderr text) so CI fails fast;
apply the same guard to the other BenchRunner blocks referenced later (the block
that creates benchInterp/benchBc equivalents around the second occurrence).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c374e7d3-a774-47e0-a04c-638abdf216e4

📥 Commits

Reviewing files that changed from the base of the PR and between fa45700 and 10b2804.

📒 Files selected for processing (5)
  • .github/workflows/ci.yml
  • .github/workflows/pr.yml
  • scripts/test-cli-apps.ts
  • scripts/test-cli-config.ts
  • scripts/test-cli.ts

Comment thread scripts/test-cli-config.ts
- Move parser error display and numeric separator tests out of the general CLI suite
- Add CI and PR workflow steps for the new parser-focused check
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@scripts/test-cli-parser.ts`:
- Around line 18-24: Replace the single-quoted echo with printf so the loader
receives a real multiline source (e.g. use printf 'const x = 1\nconst y = x +' |
${LOADER} 2>&1) so the caret/multiline rendering is exercised; also strengthen
the subsequent assertions around res/out by verifying the process exit code and
asserting concrete line and column values parsed from the JSON error output
(update the JSON assertions that currently only check presence of line/column to
compare expected numeric values, and add similar line/column checks to the
bytecode test plus an explicit exit-code check) so regressions in reported error
coordinates will fail the test.
- Around line 32-82: The JSON error-path assertions are too lax: replace
undefined checks with numeric-type checks and add exit-code validation;
specifically, in the top SyntaxError block change line/column checks to assert
typeof json.error?.line === "number" and typeof json.error?.column === "number";
in the bytecode SyntaxError block (the block using LOADER with
"--mode=bytecode") add the same typeof checks for line/column and assert the
spawn result status is non-zero (proc.status !== 0); and in the numeric
separator loop (the cases iterating over `cases`) assert proc.status !== 0 and
that typeof json.error?.line === "number" and typeof json.error?.column ===
"number" for each failing literal so regressions in error coordinates and exit
codes are caught.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 0110fc94-86e6-41f4-a18c-d66f038e41a4

📥 Commits

Reviewing files that changed from the base of the PR and between 10b2804 and 04e208a.

📒 Files selected for processing (4)
  • .github/workflows/ci.yml
  • .github/workflows/pr.yml
  • scripts/test-cli-parser.ts
  • scripts/test-cli.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • scripts/test-cli.ts

Comment thread scripts/test-cli-parser.ts Outdated
Comment thread scripts/test-cli-parser.ts Outdated
- Add dedicated lexer test for numeric separator rejection
- Keep parser test focused on error display
- Update CI and PR workflows to run both scripts
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
scripts/test-cli-lexer.ts (1)

27-35: Also assert a failing exit status for these lexer rejections.

This is the right layer for lex-time numeric-separator failures, but the loop currently accepts any { ok:false } payload even if the loader exits 0. That weakens the CLI contract check a bit. Bun.spawnSync() exposes success/exitCode, so you can fail fast before parsing JSON. (bun.sh) Based on learnings: Invalid numeric separator placement is rejected at lex time and must be verified externally via ScriptLoader/CLI invocation.

♻️ Proposed tweak
   for (const [literal, desc] of cases) {
     const proc = Bun.spawnSync([LOADER, "--output=json"], {
       stdin: new TextEncoder().encode(`${literal};\n`),
       stdout: "pipe",
       stderr: "pipe",
     });
+    if (proc.success) {
+      throw new Error(
+        `Numeric separator "${literal}" (${desc}) should fail, but exited 0: ${proc.stdout.toString()}`
+      );
+    }
     const json = JSON.parse(proc.stdout.toString());
     if (json.ok !== false || json.error?.type !== "SyntaxError") {
       throw new Error(`Numeric separator "${literal}" (${desc}) should be SyntaxError, got ok=${json.ok} type=${json.error?.type}`);
     }
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/test-cli-lexer.ts` around lines 27 - 35, Check the child process exit
status before parsing the JSON: after calling Bun.spawnSync (proc) in the loop
in scripts/test-cli-lexer.ts, assert that proc.success is false (or
proc.exitCode !== 0) and throw/fail fast if the loader exited successfully; only
then parse proc.stdout to JSON and validate json.ok === false and
json.error?.type === "SyntaxError". Use the existing Bun.spawnSync return (proc)
to perform this early exit check so lexer rejections are verified by the CLI
exit status as well as the JSON payload.
.github/workflows/ci.yml (1)

689-702: Consider centralizing this Bun CLI test fan-out.

This exact five-step list now exists here and again in .github/workflows/pr.yml Lines 227-240. Keeping it in two places makes PR/CI coverage easy to drift the next time a script is added or renamed. A small composite action or reusable workflow would keep both workflows aligned.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/ci.yml around lines 689 - 702, The five identical CI steps
("Check CLI flags across all apps", "Check lexer numeric separator rejection",
"Check parser error display", "Check config file loading and per-file config",
"Check app-specific CLI features") that run bun scripts (scripts/test-cli.ts,
scripts/test-cli-lexer.ts, scripts/test-cli-parser.ts,
scripts/test-cli-config.ts, scripts/test-cli-apps.ts) are duplicated in
.github/workflows/ci.yml and .github/workflows/pr.yml; extract them into a
single reusable workflow or composite action (e.g.,
.github/workflows/cli-tests.yml or .github/actions/cli-tests/) that runs those
five bun commands, then replace the inlined steps in both ci.yml and pr.yml with
a single call to that reusable workflow/action (using workflow_call or uses:) so
both workflows invoke the same centralized step set.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In @.github/workflows/ci.yml:
- Around line 689-702: The five identical CI steps ("Check CLI flags across all
apps", "Check lexer numeric separator rejection", "Check parser error display",
"Check config file loading and per-file config", "Check app-specific CLI
features") that run bun scripts (scripts/test-cli.ts, scripts/test-cli-lexer.ts,
scripts/test-cli-parser.ts, scripts/test-cli-config.ts,
scripts/test-cli-apps.ts) are duplicated in .github/workflows/ci.yml and
.github/workflows/pr.yml; extract them into a single reusable workflow or
composite action (e.g., .github/workflows/cli-tests.yml or
.github/actions/cli-tests/) that runs those five bun commands, then replace the
inlined steps in both ci.yml and pr.yml with a single call to that reusable
workflow/action (using workflow_call or uses:) so both workflows invoke the same
centralized step set.

In `@scripts/test-cli-lexer.ts`:
- Around line 27-35: Check the child process exit status before parsing the
JSON: after calling Bun.spawnSync (proc) in the loop in
scripts/test-cli-lexer.ts, assert that proc.success is false (or proc.exitCode
!== 0) and throw/fail fast if the loader exited successfully; only then parse
proc.stdout to JSON and validate json.ok === false and json.error?.type ===
"SyntaxError". Use the existing Bun.spawnSync return (proc) to perform this
early exit check so lexer rejections are verified by the CLI exit status as well
as the JSON payload.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b4cb6f59-4118-4d08-9452-63ff5a5d7d93

📥 Commits

Reviewing files that changed from the base of the PR and between 04e208a and 5aee020.

📒 Files selected for processing (4)
  • .github/workflows/ci.yml
  • .github/workflows/pr.yml
  • scripts/test-cli-lexer.ts
  • scripts/test-cli-parser.ts
✅ Files skipped from review due to trivial changes (1)
  • scripts/test-cli-parser.ts

- test-cli-parser.ts: Use printf for proper multiline input, add exit
  code checks, strengthen line/column assertions to check numeric type
  instead of undefined
- test-cli-lexer.ts: Assert non-zero exit code before parsing JSON
- test-cli-config.ts: Assert BenchmarkRunner exit code before matching
  stdout

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

internal Refactoring, CI, tooling, cleanup new feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant