Skip to content

fix: handle subset check across union of comparator sets - #894

Open
deepakganesh78 wants to merge 1 commit into
npm:mainfrom
deepakganesh78:fix/issue703-subset-union
Open

fix: handle subset check across union of comparator sets#894
deepakganesh78 wants to merge 1 commit into
npm:mainfrom
deepakganesh78:fix/issue703-subset-union

Conversation

@deepakganesh78

Copy link
Copy Markdown

Fixes #703

Problem

subset('>=17.2.0', '^17.2.0 || >17') returns false but should return true.

The existing subset() implementation checks each dom comparator set independently via simpleSubset(). If no single dom set covers the sub range, it returns false — even when the union of all dom sets fully covers the sub range.

For this case:

  • ^17.2.0 expands to >=17.2.0 <18.0.0-0 — covers [17.2.0, 18.0.0-0)
  • >17 expands to >=18.0.0 — covers [18.0.0, +∞)
  • Their union covers [17.2.0, +∞) which fully contains >=17.2.0

Root cause

simpleSubset() only checks sub against each dom set one at a time. There was no mechanism to consider the union of multiple dom sets.

Fix

Added unionCovers() as a fallback when dom has multiple OR-branches and no single branch covers the sub range. The algorithm:

  1. Extracts interval bounds (gt/lt comparators) from each dom set
  2. Filters out null-set branches (impossible ranges)
  3. Sorts intervals by lower bound
  4. Sweeps left-to-right, greedily extending coverage
  5. Checks if contiguous coverage spans the entire sub range

Key details:

  • Prerelease adjacency: In non-prerelease mode, <18.0.0-0 and >=18.0.0 are adjacent (no release version between them), so the algorithm recognizes this as contiguous coverage
  • Bound comparison: Correctly handles inclusive (>=/<=) vs exclusive (>, <) bounds
  • includePrerelease option: Respects the option by disabling prerelease adjacency shortcuts when enabled

Compatibility

This is a non-breaking change — it only makes subset() return true in cases where it previously (incorrectly) returned false. No existing behavior is changed.

Validation

  • All existing tests pass
  • 55+ new test cases added covering: multi-branch unions, gaps, overlapping intervals, prerelease adjacency, null-set branches, * ranges, inclusive/exclusive bounds, includePrerelease mode
  • 100% code coverage (statements, branches, lines, functions)
  • ESLint passes

When checking if a range is a subset of another range with multiple
OR-branches (e.g., subset('>=17.2.0', '^17.2.0 || >17')), the existing
code only checked each dom comparator set independently. If no single
dom set covered the sub range, it returned false — even when the union
of all dom sets would cover it.

Root cause: simpleSubset() checks sub against each dom set one at a time.
For '>=17.2.0' vs '^17.2.0 || >17', neither '^17.2.0' ([17.2.0, 18.0.0-0))
nor '>17' (>=18.0.0) alone covers [17.2.0, +inf), but their union does.

Fix: Add unionCovers() as a fallback when dom has multiple OR-branches
and no single branch covers the sub range. It extracts interval bounds
from each dom set, sorts by lower bound, and sweeps left-to-right to
check contiguous coverage. Handles prerelease adjacency (e.g.,
<18.0.0-0 is adjacent to >=18.0.0 in non-prerelease mode), null-set
branches, inclusive/exclusive bound comparisons, and includePrerelease.

Fixes npm#703

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@deepakganesh78
deepakganesh78 requested a review from a team as a code owner August 1, 2026 17:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] subset('>=17.2.0', '^17.2.0 || >17') should be true

1 participant