fix: compare large numeric prerelease ids without precision loss#880
fix: compare large numeric prerelease ids without precision loss#880spokodev wants to merge 1 commit into
Conversation
Numeric prerelease identifiers >= MAX_SAFE_INTEGER (2^53) are kept as strings by classes/semver.js to avoid IEEE-754 precision loss, but compareIdentifiers re-introduced that loss by coercing all-digit ids with Number (`+a`). This collapsed 2^53 and 2^53+1 to the same double, so compare/eq/gt reported distinct versions as equal, violating SemVer 2.0.0 §11.4.1 (numeric identifiers compared numerically). Compare all-digit identifiers with BigInt to preserve full integer precision. Behavior for normal numeric ids and alphanumeric ids is unchanged.
|
npm doesn't follow semver 2, it follows semver 1, which only says
https://semver.org/spec/v1.0.0.html so realistically they should always be compared with |
|
Good point on the lineage, and I should have framed this around behavior rather than a spec number. Here is why I think it still needs a change. node-semver already orders all-digit identifiers numerically today: For the reported pair the choice does not matter:
All three give The PR keeps that numeric ordering and only removes the precision loss. If BigInt is unwelcome in this path, I can switch it to |
|
BigInt seems fine if a numeric comparison is what's needed (and already present). |
|
Right, that matches the scope here. The PR keeps the existing numeric comparison for all digit identifiers and only changes the parse to |
Problem
Two valid versions whose prerelease numeric identifiers are >=
2^53are reported equal and mis-sorted:Both versions are valid SemVer (there is no magnitude cap on numeric prerelease identifiers in §9), so this violates SemVer 2.0.0 §11.4.1:
9007199254740992and9007199254740993are distinct integers and must order accordingly.Root cause
classes/semver.jsdeliberately keeps numeric prerelease ids>= MAX_SAFE_INTEGERas strings to avoid IEEE-754 precision loss in storage:But
internal/identifiers.jscompareIdentifiersre-introduced that loss for any all-digit identifier:+'9007199254740992'and+'9007199254740993'both evaluate to the same double (9007199254740992), so the comparison sees them as equal.Fix
Compare all-digit identifiers with
BigIntinstead ofNumber, preserving full integer precision:The already-numberified fast path (
typeof a === 'number' && typeof b === 'number', for ids< MAX_SAFE_INTEGER) is untouched, and mixed alphanumeric ordering is unchanged.Tests
Added a unit case in
test/internal/identifiers.jsforcompareIdentifiers/rcompareIdentifierswith ids beyond2^53, and a fixture entry intest/fixtures/comparisons.jsexercisingcompare/gt/eqend to end. Both fail on the unpatched code and pass with the fix. Fullnpm testsuite (including lint and 100% coverage) is green.