You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[BUG] subset() returns false for an upper-bound-only range such as subset('<2.0.0', '*') #949
subset(sub, dom) returns false when sub has only an upper bound (< or <=) and dom has a lower bound, including *, which subset treats as >=0.0.0. Every version that satisfies <2.0.0 also satisfies *, so the README's "entirely contained" definition gives true.
subset('<1.0.0', '0.x') is a related false negative: 0.x is >=0.0.0 <1.0.0-0, which admits the same non-prerelease versions as <1.0.0.
Expected Behavior
The first five lines below print true.
In ranges/subset.js, simpleSubset replaces * in dom with >=0.0.0 and then returns false for lt && hasDomGT && !gt, which treats a missing lower bound in sub as unbounded below instead of >=0.0.0.
Reproduced on main (b37b033). Two separate spots in ranges/subset.js:
<2.0.0 vs * / >=0.0.0: after * becomes >=0.0.0, the lt && hasDomGT && !gt check returns false outright. A missing lower bound in sub really means "bounded by the lowest version", so it should only fail if some >/>= in dom rejects that minimum.
<1.0.0 vs 0.x:0.x parses to <1.0.0-0, and lowerLT treats that as stricter than <1.0.0. Without includePrerelease they admit the same versions (the needDomLTPre block already makes this exception for sub, just not for dom).
Added those as cases in test/ranges/subset.js. npm test: all 54 test files pass, coverage stays at 100%.
I also brute-forced ~8k range pairs against satisfies() over a version grid. False negatives went from 316 to 158, and there are no new false positives. The 84 pre-existing ones are all prerelease sub ranges, so that's a separate issue.
Activity
hamarshehmhmd commentedon Nov 7, 2026
Reproduced on main (b37b033). Two separate spots in
ranges/subset.js:<2.0.0vs*/>=0.0.0: after*becomes>=0.0.0, thelt && hasDomGT && !gtcheck returns false outright. A missing lower bound insubreally means "bounded by the lowest version", so it should only fail if some>/>=indomrejects that minimum.<1.0.0vs0.x:0.xparses to<1.0.0-0, andlowerLTtreats that as stricter than<1.0.0. WithoutincludePrereleasethey admit the same versions (theneedDomLTPreblock already makes this exception forsub, just not fordom).Patch:
if (c.operator === '<' || c.operator === '<=') { lower = lowerLT(lt, c, options) - if (lower === c && lower !== lt) { + if (lower === c && lower !== lt && !sameUpperBound(lt, c, options)) { return false } @@ if (lt && hasDomGT && !gt && gtltComp !== 0) { - return false + const min = options.includePrerelease + ? minimumVersionWithPreRelease[0].semver + : minimumVersion[0].semver + const domAdmitsMin = dom.every(c => + (c.operator !== '>' && c.operator !== '>=') || c.test(min)) + if (!domAdmitsMin) { + return false + } } @@ +// without includePrerelease, <1.2.3-0 admits the same versions as <1.2.3 +const sameUpperBound = (lt, c, options) => + !options.includePrerelease && + lt.operator === '<' && c.operator === '<' && + !lt.semver.prerelease.length && + c.semver.prerelease.length === 1 && c.semver.prerelease[0] === 0 && + c.semver.major === lt.semver.major && + c.semver.minor === lt.semver.minor && + c.semver.patch === lt.semver.patchWith the patch:
test/ranges/subset.js.npm test: all 54 test files pass, coverage stays at 100%.satisfies()over a version grid. False negatives went from 316 to 158, and there are no new false positives. The 84 pre-existing ones are all prereleasesubranges, so that's a separate issue.Happy to open a PR.