Semantic Version Range Evaluator
Type a range expression and a list of versions. The panel reports which candidates satisfy the range, which are excluded, and which one a resolver would select. Versions can be separated by spaces, commas or newlines.
Recognised forms: 1.2.3, =1.2, >1.2, >=1.2 <2.0, 1.x, 1.2.*, *,
~1.2.3, ^1.2.3, 1.2.3 - 2.3.4, and unions written with ||. Space
between comparators means and.
Why a range is not a wish
A range is a promise you make to a resolver about which future releases you
will accept without looking at them. ^1.2.3 says: any release up to but not
including the next major is acceptable, sight unseen. That is a delegation of
review authority to whoever publishes the package, on the strength of a version
number they chose themselves.
The number is a claim, not a guarantee. Nothing in the specification, and nothing in any registry, verifies that a minor release is actually backward compatible. The contract is social. What the resolver enforces is only the arithmetic.
Caret gets narrower below one
The rule most often misread is caret behaviour under 0.x, and the evaluator
above makes it visible. Above 1.0.0, caret allows minor and patch movement:
^1.2.3 reaches 1.9.9. Below it, the leftmost non-zero part is treated as
the breaking position instead:
^0.2.3allows patch movement only —0.2.9yes,0.3.0no.^0.0.3allows nothing above itself — not even0.0.4.
This is deliberate. Pre-1.0 releases are declared unstable by the specification
itself, so the resolver assumes any movement can break you. Teams that pin
^0.x expecting the same latitude they get from ^1.x are getting a much
tighter constraint than they think — which is usually the safer surprise, but a
surprise nonetheless.
Pre-releases are opt-in territory
A pre-release sorts below its release: 1.0.0-rc.1 is lower than 1.0.0.
Within a pre-release, identifiers are compared field by field — numeric fields
numerically, alphanumeric fields in ASCII order, with numeric ranking below
alphanumeric, and a longer identifier list outranking a shorter one where the
shared fields are equal. That is what produces the published ordering
1.0.0-alpha → 1.0.0-alpha.1 → 1.0.0-alpha.beta → 1.0.0-beta →
1.0.0-beta.2 → 1.0.0-beta.11 → 1.0.0-rc.1 → 1.0.0, and it is why
beta.11 beats beta.2 rather than losing to it on string comparison.
The rule that catches people is the one about matching. A candidate carrying a
pre-release only satisfies a range when the range itself names a pre-release on
the same major.minor.patch. So ^1.2.3 does not pick up 1.3.0-rc.1, even
though 1.3.0-rc.1 sits comfortably between the bounds. You have to ask for
pre-release territory explicitly, by writing a range that mentions one.
Try ^1.2.3-alpha.1 against 1.2.3-alpha.2, 1.2.3-beta, 1.2.3 and
1.2.4-alpha.1. The first three match; the last does not, because its
major.minor.patch is not the one the range named.
What build metadata does
Everything after + is ignored for ordering. 1.0.0+build.7 and
1.0.0+build.9 have identical precedence — they are the same version as far as
resolution is concerned. If two of your artefacts differ only in build
metadata, no range expression can prefer one over the other, and any tool that
appears to choose is choosing by something other than precedence.
On the self-test
The line under the panel is not decoration. The evaluator checks itself against
a fixed set of worked cases every time the page loads — caret above and below
1.0.0, tilde with two and three parts, x-ranges, hyphen ranges, unions,
partial comparators, pre-release exclusion, and the specification’s own
ordering chain. If any case fails, the tool switches itself off and says so
rather than returning an answer you might paste into a manifest.
Two known limits, stated rather than hidden: the parser is lenient about leading zeros in numeric identifiers, which the specification forbids, and it does not implement registry-specific extensions such as tag, URL or workspace specifiers. Both are validation concerns rather than precedence concerns.