Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Best way to find the best theoretical version within a range #166

Closed
ronkorving opened this issue Sep 9, 2016 · 3 comments
Closed

Best way to find the best theoretical version within a range #166

ronkorving opened this issue Sep 9, 2016 · 3 comments

Comments

@ronkorving
Copy link

I understand this may be asking for the impossible. But what's the best approach to figuring out the best theoretical version within a given range, when you don't know what versions exist (ie: maxSatisfying won't do the trick).

The only approach with the existing APIs I've found so far is to brute force attempt major/minor/patch combinations until it no longer satisfies the range. But with the semver range parser, I can imagine a faster solution might be possible. I understand that "don't do this" is a valid response, but nonetheless, thoughts and ideas welcome.

@isaacs
Copy link
Contributor

isaacs commented Sep 9, 2016

So, let me start by saying that what you're looking for is mathematically impossible. Here's why:

Let R be the set of versions that satisfy the range ^X.0.0, for integer value X. (Note that this is functionally equivalent to >=X.0.0 <(X+1).0.0.)
For any version in R, X.Y.Z, there exists some version X.Y.(Z+1) which also satisfies the range, and is higher precedence than X.Y.Z, as well as some version X.(Y+1).0 which satisfies the range.
QED, there is no single "maximum version number" which satisfies the range ^X.0.0.

If the version range does not have an upper bound, then it's even more transparently impossible.

Given the set R of all versions satisfying the range >X.Y.Z, for all versions A.B.C in R, there exists some version (A+1).B.C which is also in R.

For ranges with an inclusive upper limit, it is be possible. For example, the range <=X.0.0 is satisfied by X.0.0, but not X.0.1.

If you wanted to determine if a range has an upper bound, and what that upper bound is, you could do this:

function hasUpperBound (range) {
  range = new semver.Range(range)
  if (!range) return false
  return range.set.filter(function (subset) {
    return subset.some(function (comparator) {
      return comparator.operator.match(/^</)
    })
  }).length === range.set.length
}

Of course, if by "best" you mean something other than "highest precedence order", then I'm not sure what kind of normativity you're applying here, and the problem becomes even less well-defined :)

@isaacs isaacs closed this as completed Sep 9, 2016
@isaacs
Copy link
Contributor

isaacs commented Sep 9, 2016

To find out what the upper bound is, you can use this:

function upperBound (range) {
  range = new semver.Range(range)
  if (!hasUpperBound(range)) return null
  return range.set.map(function (subset) {
    return subset.filter(function (comparator) {
      return /^</.test(comparator.operator)
    })
  }).map(function (subset) {
    return subset.sort(function (a, b) {
      return semver.compare(a.semver, b.semver)
    })[0]
  }).sort(function (a, b) {
    return semver.rcompare(a.semver, b.semver)
  }).slice(0, 1).map(function (comparator) {
    return comparator.value
  })[0]
}

@connorjclark
Copy link

FYI, the above doesn't take into account ranges with prerelease tags (5.0-rc). This should work:

function hasUpperBound(rangeString) {
  const range = new semver.Range(rangeString);
  if (!range) return false;

  for (const subset of range.set) {
    // Upperbound exists if...

    // < or <= is in one of the subset's clauses (= gets normalized to >= and <).
    if (subset.some(comparator => comparator.operator.match(/^</))) {
      continue;
    }

    // Subset has a prerelease tag (operator will be empty).
    if (subset.length === 1 && subset[0].operator === '') {
      continue;
    }

    return false;
  }

  return true;
}

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

No branches or pull requests

3 participants