Skip to content

Add public Goccia.semver API namespace#192

Merged
frostney merged 5 commits into
mainfrom
t3code/semver-public-api
Apr 7, 2026
Merged

Add public Goccia.semver API namespace#192
frostney merged 5 commits into
mainfrom
t3code/semver-public-api

Conversation

@frostney
Copy link
Copy Markdown
Owner

@frostney frostney commented Apr 7, 2026

Summary

  • Adds a new Goccia.semver namespace on the global Goccia object with SemVer 2.0.0 constants, constructors, helper groups, and top-level functions.
  • Exposes SemVer, Comparator, and Range objects with node-semver-like fields and methods.
  • Updates the built-ins documentation to describe the new namespace and its public surface.
  • Adds end-to-end tests covering the global namespace, constructors, and representative helpers like valid, clean, compare, inc, satisfies, and toComparators.

Testing

  • Added JS coverage for Goccia.semver namespace exposure and key API behavior under tests/built-ins/Goccia/semver/

Summary by CodeRabbit

Release Notes

  • New Features

    • Introduced Goccia.semver namespace providing semantic versioning (SemVer 2.0.0) functionality, including parsing, comparison, validation, range handling, and version incrementation.
  • Documentation

    • Added comprehensive documentation for the SemVer API and its available functions and classes.
  • Tests

    • Added extensive test coverage for all SemVer API functions and features.

- Expose SemVer, Comparator, and Range constructors on `Goccia.semver`
- Wire semver helper functions through the global `Goccia` object
- Add docs and end-to-end coverage for the new public surface
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 7, 2026

Warning

Rate limit exceeded

@frostney has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 16 minutes and 57 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 16 minutes and 57 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 18a58bac-8828-4fb7-b415-48f033c47048

📥 Commits

Reviewing files that changed from the base of the PR and between 76a075f and 96e28ca.

📒 Files selected for processing (1)
  • tests/built-ins/Goccia/semver/prototype/compareMain.js
📝 Walkthrough

Walkthrough

This pull request introduces a full SemVer 2.0.0 API to GocciaScript through a new Goccia.semver global namespace. The implementation includes version parsing, comparison, range handling, and incrementing operations, integrated into the engine bootstrap and exposed as Goccia objects with comprehensive test coverage.

Changes

Cohort / File(s) Summary
Documentation
docs/built-ins.md
Adds API reference for Goccia.semver namespace, documenting all exported constructors (SemVer, Comparator, Range), constants (SEMVER_SPEC_VERSION, RELEASE_TYPES), top-level functions, and namespace aliases (classes, functions, ranges).
Core SemVer Implementation
units/Goccia.Semver.pas
Comprehensive SemVer 2.0.0 toolkit (2189 lines) implementing parsing, validation, comparison, range evaluation, version incrementing, and extrema computation. Includes exception types, configuration records, and internal parsing/expansion logic for ~, ^, and wildcard ranges.
Goccia Bindings
units/Goccia.Builtins.Semver.pas
Wraps SemVer API as Goccia objects (1486 lines), creating prototypes for SemVer, Comparator, Range with instance methods and static namespace functions. Handles exception-to-error conversion and option parsing.
Engine Integration
units/Goccia.Engine.pas, units/Goccia.Runtime.Bootstrap.pas
Registers Goccia.semver as a global lexical binding in engine initialization, creating a namespace object with the SemVer API.
Constants
units/Goccia.Constants.ConstructorNames.pas
Adds three new constructor name constants: CONSTRUCTOR_SEMVER, CONSTRUCTOR_COMPARATOR, CONSTRUCTOR_RANGE.
Core Function Tests
tests/built-ins/Goccia/semver/{valid, clean, compare, satisfies, coerce, inc}.js
Test coverage for top-level SemVer functions including validation, normalization, comparison, range satisfaction, and version coercion, with edge cases and error handling.
Constructor/Class Tests
tests/built-ins/Goccia/semver/{SemVer, Comparator, Range}.js
Validates constructor instantiation, property exposure (version components, operator, range strings), instance methods (test), and error handling for malformed inputs.
Advanced Function Tests
tests/built-ins/Goccia/semver/{toComparators, validRange, intersects}.js
Tests range expansion, normalization, and intersection logic across shorthand and complex range formats.
Prototype Method Tests
tests/built-ins/Goccia/semver/prototype/{compareMain, comparePre}.js
Validates instance comparison methods that handle prerelease precedence and main version components independently.
Global Property Tests
tests/built-ins/global-properties/goccia.js
Verifies presence and structure of Goccia.semver global, including constants and nested namespace aliases.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Poem

🐰 A bunny hops through versions high,
SemVer ranges reaching the sky,
Compare and parse, increment with care,
Semantic versioning, now everywhere! 🌟
Goccia.semver blooms, robust and true.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly and accurately reflects the main change: adding a public Goccia.semver API namespace exposed as a global. The changeset is entirely focused on this feature.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch t3code/semver-public-api

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 7, 2026

Suite Timing

Suite Metric Interpreted Bytecode
Tests Total 3715 3715
Tests Passed 3674 ✅ 3715 ✅
Tests Skipped 41 0
Tests Execution 216.9ms 191.9ms
Tests Engine 399.1ms 717.3ms
Benchmarks Total 274 274
Benchmarks Duration 7.48min 6.39min

Measured on ubuntu-latest x64.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 7, 2026

Benchmark Results

274 benchmarks

Interpreted: 🟢 66 improved · 🔴 94 regressed · 114 unchanged · avg -0.8%
Bytecode: 🟢 2 improved · 🔴 254 regressed · 18 unchanged · avg -8.3%

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

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.

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: 10

🧹 Nitpick comments (2)
tests/built-ins/global-properties/goccia.js (1)

14-16: Consider locking down representative members in alias groups.

You assert functions/ranges/classes objects exist; adding a few key-level checks (for documented entries) would better guard API-surface regressions.

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

In `@tests/built-ins/global-properties/goccia.js` around lines 14 - 16, The tests
currently only assert the existence of Goccia.semver.functions, .ranges, and
.classes; update the test to lock down representative members by adding
assertions that specific documented entries exist and have expected types (e.g.,
check that known function keys exist on Goccia.semver.functions, known range
helpers exist on Goccia.semver.ranges, and documented constructors/classes exist
on Goccia.semver.classes) using property presence checks (hasOwnProperty or
'in') and simple typeof checks to guard the API surface.
tests/built-ins/Goccia/semver/toComparators.js (1)

6-11: Add edge/error assertions for toComparators contract.

This currently validates only one happy-path expansion. Please add at least one edge case and one error-path assertion in this same file to match method-level test coverage expectations.

As per coding guidelines: "Tests should cover happy paths, edge cases, and error cases — all in the same file for each method."

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

In `@tests/built-ins/Goccia/semver/toComparators.js` around lines 6 - 11, The test
only covers the happy path for Goccia.semver.toComparators; add one edge-case
assertion (for example a loose/wildcard input like "1.2" or "*" and assert the
expanded comparator array contains the expected lower/upper bounds) and one
error-path assertion (call Goccia.semver.toComparators with an invalid input
such as "invalid" or null and assert it throws using expect(() =>
...).toThrow()/toThrowError()). Update the existing test file
tests/built-ins/Goccia/semver/toComparators.js by extending the same test block
or adding another test in the file that references Goccia.semver.toComparators
and includes both the edge-case expectations and the thrown-error expectation.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@tests/built-ins/Goccia/semver/Comparator.js`:
- Around line 6-13: Add negative and edge-case assertions for
Goccia.semver.Comparator: add one test that constructing with a malformed
comparator string (e.g., ">>1.2.3" or ">=foo") throws (use expect(() => new
Goccia.semver.Comparator("...")).toThrow()) and another test for an edge input
(empty string or null) that either throws or produces the documented behavior
(use expect(...) toThrow or toEqual per spec). Place these assertions in the
same Comparator.js test file alongside the existing happy-path test and
reference Goccia.semver.Comparator, the constructor, and the instance.test
method to verify error-path coverage.

In `@tests/built-ins/Goccia/semver/inc.js`:
- Around line 6-10: Add bad-input and edge-case assertions for
Goccia.semver.inc: include at least one test expecting an error (or specific
return) when passing a malformed version like "1.2" or "not-a-version" and when
passing an unknown release type like "foobar", and add an edge-case test such as
incrementing a prerelease on a version without prerelease metadata (e.g.,
"1.2.3" with "prerelease") or max-component values; update the existing test
block in tests/built-ins/Goccia/semver/inc.js around the Goccia.semver.inc calls
to include these assertions using the same test() structure and expect(...)
style so happy paths, edge cases, and error cases are all covered.

In `@tests/built-ins/Goccia/semver/satisfies.js`:
- Around line 6-13: Add an error-case assertion for Goccia.semver.satisfies:
within the same test file (either the existing "Goccia.semver.satisfies respects
range and prerelease matching" test or a new nearby test), add an expect that
calling Goccia.semver.satisfies with a malformed version or range (e.g.,
"not-a-version" or an empty/null input) throws an error—use Jest's
toThrow/toThrowError matcher to assert the method raises on invalid input so the
method has happy, edge, and error coverage.

In `@tests/built-ins/Goccia/semver/SemVer.js`:
- Around line 6-17: Add two more assertions to
tests/built-ins/Goccia/semver/SemVer.js: one error-case that constructs
Goccia.semver.SemVer with a malformed version string (e.g., "1..2" or "invalid")
and asserts that it throws (use the test framework's expect(() => new
Goccia.semver.SemVer(...)).toThrow()), and one edge-case that constructs with a
minimal valid semver (e.g., "0.0.0" or "1.0.0+meta" / "1.0.0-0") and asserts the
parsed fields (version, major, minor, patch, prerelease/build) match expected
values; target the existing test block for new Goccia.semver.SemVer to keep
happy, edge and error cases together and use the class name Goccia.semver.SemVer
to locate where to add these assertions.

In `@tests/built-ins/Goccia/semver/validRange.js`:
- Around line 6-9: Add assertions to tests/built-ins/Goccia/semver/validRange.js
covering an invalid input that should return null and an edge-case input (e.g.,
wildcard or empty/partial version) to assert the expected normalized string or
null; specifically, update the test that calls Goccia.semver.validRange to
include one expect(Goccia.semver.validRange("<invalid-input>")).toBeNull() and
one
expect(Goccia.semver.validRange("<edge-input>")).toBe("<expected-normalized-range>")
so the file covers happy, edge, and error paths for Goccia.semver.validRange.

In `@units/Goccia.Builtins.Semver.pas`:
- Around line 523-538: SemVerCompareMainMethod currently considers prerelease
when it should only compare main version components: modify
TGocciaSemverNamespaceHost.SemVerCompareMainMethod (and use ReadSemver/Compare)
to ignore prerelease data (e.g., clear or exclude prerelease from ThisSemver and
OtherSemver) before calling Compare so it returns only major/minor/patch
ordering; change TGocciaSemverNamespaceHost.SemVerComparePreMethod to first call
the main-version comparison (ignoring prereleases) and only if that returns 0
perform a full Compare (including prerelease) to determine prerelease
precedence, returning the appropriate IntegerValue.
- Around line 498-540: Multiple exported methods (e.g., SemVerFormat,
SemVerToStringMethod, SemVerCompareMethod, SemVerCompareMainMethod,
SemVerComparePreMethod and other callbacks in the 576-640, 642-963 ranges) call
semver routines directly and will leak raw EGocciaSemver* exceptions to JS; wrap
the body of each exported callback in the same exception-to-JS-error translation
used by the constructors (reuse the existing HandleSemverException pattern) so
any EGocciaSemver* is caught and converted to the appropriate
ThrowTypeError/ThrowRangeError/ThrowError/etc helper before re-raising, and
ensure ReadSemver, MustParseSemver, Compare, RangeIntersects, MinVersion,
ToComparators and similar calls are executed inside that try/except translation.
- Around line 184-185: GSemverHosts is declared as a raw TObjectList<TObject>
which violates the FPC 3.2.2/AArch64 guideline; define a named type alias (e.g.,
TSemverHostList = class(TObjectList<TObject>) or type TSemverHostList =
TObjectList<TObject>) and replace the raw specialization with that alias in the
GSemverHosts declaration and wherever it is initialized (the initialization code
around the current GSemverHosts creation at/near the block starting at line
~1141). Update any Free/OwnsObjects usage to reference the alias so all
references use the named type instead of TObjectList<TObject>.

In `@units/Goccia.Semver.pas`:
- Around line 1790-1801: The current nested loops in the intersection check
prematurely return False on the first non-intersecting comparator pair, which
aborts checking other OR-disjunct pairs; change the control flow in the block
using LeftRange.SetOfComparators, RightRange.SetOfComparators,
ComparatorIntersects and IsSatisfiableComparatorSet so that a non-intersecting
comparator only cancels the current Left[i]/Right[j] combination (e.g., set a
local flag or break out to the next J) instead of calling Exit(False), and only
when all comparators within a given Left[i]/Right[j] fully intersect should you
set Result := True and Exit; keep existing satisfiability checks via
IsSatisfiableComparatorSet.
- Around line 2137-2142: The branch handling GreaterThanComparator has incorrect
operator precedence; update the condition in the loop over DomainComparator so
the empty-value check applies to both '>' and '>=' operators—i.e., change the if
to check (GreaterThanComparator.Value <> '') and ((DomainComparator.Operator =
'>') or (DomainComparator.Operator = '>=')) before calling
HigherGreaterThanComparator(GreaterThanComparator, DomainComparator, AOptions)
and checking its Value; keep the rest of the logic unchanged.

---

Nitpick comments:
In `@tests/built-ins/global-properties/goccia.js`:
- Around line 14-16: The tests currently only assert the existence of
Goccia.semver.functions, .ranges, and .classes; update the test to lock down
representative members by adding assertions that specific documented entries
exist and have expected types (e.g., check that known function keys exist on
Goccia.semver.functions, known range helpers exist on Goccia.semver.ranges, and
documented constructors/classes exist on Goccia.semver.classes) using property
presence checks (hasOwnProperty or 'in') and simple typeof checks to guard the
API surface.

In `@tests/built-ins/Goccia/semver/toComparators.js`:
- Around line 6-11: The test only covers the happy path for
Goccia.semver.toComparators; add one edge-case assertion (for example a
loose/wildcard input like "1.2" or "*" and assert the expanded comparator array
contains the expected lower/upper bounds) and one error-path assertion (call
Goccia.semver.toComparators with an invalid input such as "invalid" or null and
assert it throws using expect(() => ...).toThrow()/toThrowError()). Update the
existing test file tests/built-ins/Goccia/semver/toComparators.js by extending
the same test block or adding another test in the file that references
Goccia.semver.toComparators and includes both the edge-case expectations and the
thrown-error expectation.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 86cb62f7-7d3d-4fc8-9fae-dc19da4b4a8a

📥 Commits

Reviewing files that changed from the base of the PR and between d16da08 and a4de10a.

📒 Files selected for processing (19)
  • docs/built-ins.md
  • tests/built-ins/Goccia/semver/Comparator.js
  • tests/built-ins/Goccia/semver/Range.js
  • tests/built-ins/Goccia/semver/SemVer.js
  • tests/built-ins/Goccia/semver/clean.js
  • tests/built-ins/Goccia/semver/coerce.js
  • tests/built-ins/Goccia/semver/compare.js
  • tests/built-ins/Goccia/semver/inc.js
  • tests/built-ins/Goccia/semver/satisfies.js
  • tests/built-ins/Goccia/semver/toComparators.js
  • tests/built-ins/Goccia/semver/valid.js
  • tests/built-ins/Goccia/semver/validRange.js
  • tests/built-ins/global-properties/goccia.js
  • units/Goccia.Builtins.Semver.pas
  • units/Goccia.Constants.ConstructorNames.pas
  • units/Goccia.Constants.PropertyNames.pas
  • units/Goccia.Engine.pas
  • units/Goccia.Runtime.Bootstrap.pas
  • units/Goccia.Semver.pas

Comment thread tests/built-ins/Goccia/semver/Comparator.js Outdated
Comment thread tests/built-ins/Goccia/semver/inc.js Outdated
Comment thread tests/built-ins/Goccia/semver/satisfies.js Outdated
Comment thread tests/built-ins/Goccia/semver/SemVer.js Outdated
Comment thread tests/built-ins/Goccia/semver/validRange.js Outdated
Comment thread units/Goccia.Builtins.Semver.pas Outdated
Comment thread units/Goccia.Builtins.Semver.pas
Comment thread units/Goccia.Builtins.Semver.pas
Comment thread units/Goccia.Semver.pas Outdated
Comment thread units/Goccia.Semver.pas
frostney added 2 commits April 7, 2026 09:38
- Move semver property name to a shared constant
- Add public API coverage for `Goccia.semver`
- Update semver built-in tests to run only when the namespace exists
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

🧹 Nitpick comments (4)
units/Goccia.Builtins.Semver.pas (2)

1374-1382: Consider using a loop to populate ReleaseTypesArray.

The manual element-by-element copy with hardcoded indices is fragile if RELEASE_TYPES length changes. A loop would be more maintainable:

♻️ Suggested refactor
   SetLength(ReleaseTypesArray, Length(RELEASE_TYPES));
-  ReleaseTypesArray[0] := RELEASE_TYPES[0];
-  ReleaseTypesArray[1] := RELEASE_TYPES[1];
-  ReleaseTypesArray[2] := RELEASE_TYPES[2];
-  ReleaseTypesArray[3] := RELEASE_TYPES[3];
-  ReleaseTypesArray[4] := RELEASE_TYPES[4];
-  ReleaseTypesArray[5] := RELEASE_TYPES[5];
-  ReleaseTypesArray[6] := RELEASE_TYPES[6];
+  for I := 0 to High(RELEASE_TYPES) do
+    ReleaseTypesArray[I] := RELEASE_TYPES[I];
   NamespaceObject.AssignProperty('RELEASE_TYPES', Host.StringArrayValue(ReleaseTypesArray));
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@units/Goccia.Builtins.Semver.pas` around lines 1374 - 1382, The manual
element-by-element copy from RELEASE_TYPES into ReleaseTypesArray is fragile;
replace the hardcoded assignments with a loop that iterates over the range of
RELEASE_TYPES (or ReleaseTypesArray after SetLength) and assigns each element
dynamically, then call NamespaceObject.AssignProperty('RELEASE_TYPES',
Host.StringArrayValue(ReleaseTypesArray)) as before; update the code around
ReleaseTypesArray, RELEASE_TYPES and the SetLength call to use a for-loop so it
adapts automatically if RELEASE_TYPES changes.

1091-1141: Consider using a more efficient sorting algorithm.

The current bubble sort implementation is O(n²). For typical semver array sizes this is likely acceptable, but if larger arrays are expected, consider using a more efficient algorithm like quicksort or leveraging Pascal's built-in sorting utilities.

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

In `@units/Goccia.Builtins.Semver.pas` around lines 1091 - 1141, Replace the O(n²)
manual bubble-sort loops in NamespaceSort and NamespaceRSort with a single call
to a faster built-in sort (e.g., TArray.Sort or creating a TList<string> and
calling its Sort) using a comparer that calls CompareBuild(Left, Right,
Options); for NamespaceRSort invert the comparer result (or swap arguments) to
get descending order. Update TGocciaSemverNamespaceHost.NamespaceSort and
NamespaceRSort to build the comparer capturing Options, call the library sort on
Values, and then return StringArrayValue(Values); keep the existing exception
handling.
units/Goccia.Semver.pas (2)

1082-1085: Misleading indentation on the fallthrough raise.

The raise statement on line 1084 is indented at the same level as Exit(...) on line 1083, making it appear to be part of the if AOperator = '<=' block. However, without begin/end, only the Exit is conditional—the raise executes unconditionally after the if when all prior conditions fail.

The code is functionally correct (the raise properly handles invalid operators), but the indentation is confusing.

♻️ Suggested fix to clarify control flow
   if AOperator = '<=' then
     Exit(Compare(ALeft, ARight, AOptions) <= 0);
-    raise EGocciaSemverTypeError.Create('Invalid operator: ' + AOperator);
+  raise EGocciaSemverTypeError.Create('Invalid operator: ' + AOperator);
 end;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@units/Goccia.Semver.pas` around lines 1082 - 1085, The raise is visually
nested under the if for AOperator = '<=' but actually executes unconditionally;
update the control flow clarity by either outdenting the raise statement so it
aligns with the surrounding if/Exit lines or wrap the conditional block with
begin/end if you intend the raise to be part of the if/else structure—adjust the
lines that use AOperator = '<=', the Exit(Compare(ALeft, ARight, AOptions) <= 0)
call, and the EGocciaSemverTypeError.Create('Invalid operator: ' + AOperator)
raise to make the intended flow explicit.

1987-1991: Remove dead code: unnecessary while loop.

This while True do ... Break construct is equivalent to a single assignment Result := ''. The loop executes exactly once, sets Result to empty, and immediately breaks. Since Result is reassigned later (lines 2024-2047), this block serves no purpose.

🧹 Suggested cleanup
   for I := Low(AVersions) to High(AVersions) do
     SortedVersions[I - Low(AVersions)] := AVersions[I];
-  while True do
-  begin
-    Result := '';
-    Break;
-  end;

   FirstValue := '';
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@units/Goccia.Semver.pas` around lines 1987 - 1991, Remove the redundant
infinite loop block "while True do begin Result := ''; Break; end;" — it only
sets Result once and immediately breaks; either delete the entire loop or
replace it with the single assignment "Result := ''" if an initial value is
needed, and ensure the surrounding function (the routine that returns Result)
continues to use the later reassignment logic unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@tests/built-ins/Goccia/semver/SemVer.comparePre.js`:
- Around line 1-15: The test file currently testing the prototype method
SemVer.comparePre should be moved into the prototype/ tests folder and its
import/run references updated accordingly: relocate the file to the
tests/built-ins/Goccia/semver/prototype/ directory (keeping the same filename),
ensure the test continues to reference Goccia.semver.SemVer and
describe.runIf(hasGoccia) as before, and update any test harness or index that
collects tests so prototype tests are discovered from the new prototype/ path.
- Around line 9-14: Add edge and error-path tests for SemVer.comparePre: include
assertions that comparePre returns 0 for identical prerelease precedence (e.g.,
two SemVer instances with the same prerelease identifiers), tests for differing
prerelease lengths/order (e.g., "1.2.3-alpha" vs "1.2.3-alpha.1" and
"1.2.3-alpha.2" vs "1.2.3-alpha.10") asserting the expected -1/1 results, and
explicit error-path assertions that malformed version strings or invalid inputs
to new Goccia.semver.SemVer(...) or comparePre(...) produce the API-defined
failure (use expect(...).toThrow() or the project's defined error behavior).
Ensure all tests live in this same file alongside the existing comparePre tests
and reference Goccia.semver.SemVer and its comparePre method.

---

Nitpick comments:
In `@units/Goccia.Builtins.Semver.pas`:
- Around line 1374-1382: The manual element-by-element copy from RELEASE_TYPES
into ReleaseTypesArray is fragile; replace the hardcoded assignments with a loop
that iterates over the range of RELEASE_TYPES (or ReleaseTypesArray after
SetLength) and assigns each element dynamically, then call
NamespaceObject.AssignProperty('RELEASE_TYPES',
Host.StringArrayValue(ReleaseTypesArray)) as before; update the code around
ReleaseTypesArray, RELEASE_TYPES and the SetLength call to use a for-loop so it
adapts automatically if RELEASE_TYPES changes.
- Around line 1091-1141: Replace the O(n²) manual bubble-sort loops in
NamespaceSort and NamespaceRSort with a single call to a faster built-in sort
(e.g., TArray.Sort or creating a TList<string> and calling its Sort) using a
comparer that calls CompareBuild(Left, Right, Options); for NamespaceRSort
invert the comparer result (or swap arguments) to get descending order. Update
TGocciaSemverNamespaceHost.NamespaceSort and NamespaceRSort to build the
comparer capturing Options, call the library sort on Values, and then return
StringArrayValue(Values); keep the existing exception handling.

In `@units/Goccia.Semver.pas`:
- Around line 1082-1085: The raise is visually nested under the if for AOperator
= '<=' but actually executes unconditionally; update the control flow clarity by
either outdenting the raise statement so it aligns with the surrounding if/Exit
lines or wrap the conditional block with begin/end if you intend the raise to be
part of the if/else structure—adjust the lines that use AOperator = '<=', the
Exit(Compare(ALeft, ARight, AOptions) <= 0) call, and the
EGocciaSemverTypeError.Create('Invalid operator: ' + AOperator) raise to make
the intended flow explicit.
- Around line 1987-1991: Remove the redundant infinite loop block "while True do
begin Result := ''; Break; end;" — it only sets Result once and immediately
breaks; either delete the entire loop or replace it with the single assignment
"Result := ''" if an initial value is needed, and ensure the surrounding
function (the routine that returns Result) continues to use the later
reassignment logic unchanged.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1aa5074f-0ff2-4e47-a907-18b68c3961bc

📥 Commits

Reviewing files that changed from the base of the PR and between a4de10a and c11ca81.

📒 Files selected for processing (19)
  • tests/built-ins/Goccia/semver/Comparator.js
  • tests/built-ins/Goccia/semver/Range.js
  • tests/built-ins/Goccia/semver/SemVer.compareMain.js
  • tests/built-ins/Goccia/semver/SemVer.comparePre.js
  • tests/built-ins/Goccia/semver/SemVer.js
  • tests/built-ins/Goccia/semver/clean.js
  • tests/built-ins/Goccia/semver/coerce.js
  • tests/built-ins/Goccia/semver/compare.js
  • tests/built-ins/Goccia/semver/inc.js
  • tests/built-ins/Goccia/semver/intersects.js
  • tests/built-ins/Goccia/semver/satisfies.js
  • tests/built-ins/Goccia/semver/toComparators.js
  • tests/built-ins/Goccia/semver/valid.js
  • tests/built-ins/Goccia/semver/validRange.js
  • tests/built-ins/global-properties/goccia.js
  • units/Goccia.Builtins.Semver.pas
  • units/Goccia.Engine.pas
  • units/Goccia.Runtime.Bootstrap.pas
  • units/Goccia.Semver.pas
✅ Files skipped from review due to trivial changes (10)
  • tests/built-ins/Goccia/semver/valid.js
  • tests/built-ins/Goccia/semver/inc.js
  • tests/built-ins/Goccia/semver/clean.js
  • tests/built-ins/global-properties/goccia.js
  • tests/built-ins/Goccia/semver/coerce.js
  • tests/built-ins/Goccia/semver/SemVer.compareMain.js
  • tests/built-ins/Goccia/semver/Range.js
  • tests/built-ins/Goccia/semver/intersects.js
  • tests/built-ins/Goccia/semver/Comparator.js
  • tests/built-ins/Goccia/semver/satisfies.js
🚧 Files skipped from review as they are similar to previous changes (4)
  • tests/built-ins/Goccia/semver/SemVer.js
  • tests/built-ins/Goccia/semver/compare.js
  • tests/built-ins/Goccia/semver/toComparators.js
  • units/Goccia.Runtime.Bootstrap.pas

Comment thread tests/built-ins/Goccia/semver/SemVer.comparePre.js Outdated
Comment thread tests/built-ins/Goccia/semver/SemVer.comparePre.js Outdated
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 (1)
tests/built-ins/Goccia/semver/prototype/compareMain.js (1)

8-13: Missing error cases and edge case coverage.

Per coding guidelines, tests should cover happy paths, edge cases, and error cases in the same file. This file only has one test covering two assertions. Based on the implementation context (SemVerCompareMainMethod calls ReadSemver which throws for invalid inputs), error handling should be tested.

Consider adding:

📝 Suggested additional test coverage
 describe.runIf(hasGoccia)("Goccia.semver.SemVer.prototype.compareMain", () => {
   test("compareMain ignores prerelease metadata", () => {
     const version = new Goccia.semver.SemVer("1.2.3-alpha.1");
     expect(version.compareMain("1.2.3")).toBe(0);
     expect(version.compareMain("1.2.4-alpha.1")).toBe(-1);
   });
+
+  test("compareMain compares major, minor, and patch components", () => {
+    const version = new Goccia.semver.SemVer("2.3.4");
+    expect(version.compareMain("1.3.4")).toBe(1);
+    expect(version.compareMain("2.2.4")).toBe(1);
+    expect(version.compareMain("2.3.3")).toBe(1);
+    expect(version.compareMain("3.0.0")).toBe(-1);
+  });
+
+  test("compareMain throws for invalid versions", () => {
+    expect(() => new Goccia.semver.SemVer("invalid")).toThrow(TypeError);
+    expect(() => new Goccia.semver.SemVer("1.2.3").compareMain("bad")).toThrow(TypeError);
+  });
 });

As per coding guidelines: "Tests should cover happy paths, edge cases, and error cases — all in the same file for each method."

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

In `@tests/built-ins/Goccia/semver/prototype/compareMain.js` around lines 8 - 13,
Add tests to cover edge and error cases for
Goccia.semver.SemVer.prototype.compareMain: add cases that call compareMain with
invalid inputs that trigger ReadSemver to throw (e.g., non-string, malformed
version), assert that the method throws the expected error, and include
boundary/edge comparisons (equal mains with different prerelease, larger
major/minor differences, and inputs with build metadata). Reference the SemVer
constructor, compareMain, and ReadSemver when adding tests and use the same
describe.runIf(hasGoccia) / test structure to keep style consistent.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@tests/built-ins/Goccia/semver/prototype/compareMain.js`:
- Around line 8-13: Add tests to cover edge and error cases for
Goccia.semver.SemVer.prototype.compareMain: add cases that call compareMain with
invalid inputs that trigger ReadSemver to throw (e.g., non-string, malformed
version), assert that the method throws the expected error, and include
boundary/edge comparisons (equal mains with different prerelease, larger
major/minor differences, and inputs with build metadata). Reference the SemVer
constructor, compareMain, and ReadSemver when adding tests and use the same
describe.runIf(hasGoccia) / test structure to keep style consistent.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: e0685a62-641b-4f83-b0a5-592617b908bf

📥 Commits

Reviewing files that changed from the base of the PR and between c11ca81 and 76a075f.

📒 Files selected for processing (2)
  • tests/built-ins/Goccia/semver/prototype/compareMain.js
  • tests/built-ins/Goccia/semver/prototype/comparePre.js

@frostney frostney merged commit deca163 into main Apr 7, 2026
9 checks passed
@frostney frostney deleted the t3code/semver-public-api branch April 7, 2026 18:14
@frostney frostney added the new feature New feature or request label Apr 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

new feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant