11cb0ef41Sopenharmony_ci'use strict'
21cb0ef41Sopenharmony_ci
31cb0ef41Sopenharmony_ciconst npa = require('npm-package-arg')
41cb0ef41Sopenharmony_ciconst semver = require('semver')
51cb0ef41Sopenharmony_ciconst { checkEngine } = require('npm-install-checks')
61cb0ef41Sopenharmony_ciconst normalizeBin = require('npm-normalize-package-bin')
71cb0ef41Sopenharmony_ci
81cb0ef41Sopenharmony_ciconst engineOk = (manifest, npmVersion, nodeVersion) => {
91cb0ef41Sopenharmony_ci  try {
101cb0ef41Sopenharmony_ci    checkEngine(manifest, npmVersion, nodeVersion)
111cb0ef41Sopenharmony_ci    return true
121cb0ef41Sopenharmony_ci  } catch (_) {
131cb0ef41Sopenharmony_ci    return false
141cb0ef41Sopenharmony_ci  }
151cb0ef41Sopenharmony_ci}
161cb0ef41Sopenharmony_ci
171cb0ef41Sopenharmony_ciconst isBefore = (verTimes, ver, time) =>
181cb0ef41Sopenharmony_ci  !verTimes || !verTimes[ver] || Date.parse(verTimes[ver]) <= time
191cb0ef41Sopenharmony_ci
201cb0ef41Sopenharmony_ciconst avoidSemverOpt = { includePrerelease: true, loose: true }
211cb0ef41Sopenharmony_ciconst shouldAvoid = (ver, avoid) =>
221cb0ef41Sopenharmony_ci  avoid && semver.satisfies(ver, avoid, avoidSemverOpt)
231cb0ef41Sopenharmony_ci
241cb0ef41Sopenharmony_ciconst decorateAvoid = (result, avoid) =>
251cb0ef41Sopenharmony_ci  result && shouldAvoid(result.version, avoid)
261cb0ef41Sopenharmony_ci    ? { ...result, _shouldAvoid: true }
271cb0ef41Sopenharmony_ci    : result
281cb0ef41Sopenharmony_ci
291cb0ef41Sopenharmony_ciconst pickManifest = (packument, wanted, opts) => {
301cb0ef41Sopenharmony_ci  const {
311cb0ef41Sopenharmony_ci    defaultTag = 'latest',
321cb0ef41Sopenharmony_ci    before = null,
331cb0ef41Sopenharmony_ci    nodeVersion = process.version,
341cb0ef41Sopenharmony_ci    npmVersion = null,
351cb0ef41Sopenharmony_ci    includeStaged = false,
361cb0ef41Sopenharmony_ci    avoid = null,
371cb0ef41Sopenharmony_ci    avoidStrict = false,
381cb0ef41Sopenharmony_ci  } = opts
391cb0ef41Sopenharmony_ci
401cb0ef41Sopenharmony_ci  const { name, time: verTimes } = packument
411cb0ef41Sopenharmony_ci  const versions = packument.versions || {}
421cb0ef41Sopenharmony_ci
431cb0ef41Sopenharmony_ci  if (avoidStrict) {
441cb0ef41Sopenharmony_ci    const looseOpts = {
451cb0ef41Sopenharmony_ci      ...opts,
461cb0ef41Sopenharmony_ci      avoidStrict: false,
471cb0ef41Sopenharmony_ci    }
481cb0ef41Sopenharmony_ci
491cb0ef41Sopenharmony_ci    const result = pickManifest(packument, wanted, looseOpts)
501cb0ef41Sopenharmony_ci    if (!result || !result._shouldAvoid) {
511cb0ef41Sopenharmony_ci      return result
521cb0ef41Sopenharmony_ci    }
531cb0ef41Sopenharmony_ci
541cb0ef41Sopenharmony_ci    const caret = pickManifest(packument, `^${result.version}`, looseOpts)
551cb0ef41Sopenharmony_ci    if (!caret || !caret._shouldAvoid) {
561cb0ef41Sopenharmony_ci      return {
571cb0ef41Sopenharmony_ci        ...caret,
581cb0ef41Sopenharmony_ci        _outsideDependencyRange: true,
591cb0ef41Sopenharmony_ci        _isSemVerMajor: false,
601cb0ef41Sopenharmony_ci      }
611cb0ef41Sopenharmony_ci    }
621cb0ef41Sopenharmony_ci
631cb0ef41Sopenharmony_ci    const star = pickManifest(packument, '*', looseOpts)
641cb0ef41Sopenharmony_ci    if (!star || !star._shouldAvoid) {
651cb0ef41Sopenharmony_ci      return {
661cb0ef41Sopenharmony_ci        ...star,
671cb0ef41Sopenharmony_ci        _outsideDependencyRange: true,
681cb0ef41Sopenharmony_ci        _isSemVerMajor: true,
691cb0ef41Sopenharmony_ci      }
701cb0ef41Sopenharmony_ci    }
711cb0ef41Sopenharmony_ci
721cb0ef41Sopenharmony_ci    throw Object.assign(new Error(`No avoidable versions for ${name}`), {
731cb0ef41Sopenharmony_ci      code: 'ETARGET',
741cb0ef41Sopenharmony_ci      name,
751cb0ef41Sopenharmony_ci      wanted,
761cb0ef41Sopenharmony_ci      avoid,
771cb0ef41Sopenharmony_ci      before,
781cb0ef41Sopenharmony_ci      versions: Object.keys(versions),
791cb0ef41Sopenharmony_ci    })
801cb0ef41Sopenharmony_ci  }
811cb0ef41Sopenharmony_ci
821cb0ef41Sopenharmony_ci  const staged = (includeStaged && packument.stagedVersions &&
831cb0ef41Sopenharmony_ci    packument.stagedVersions.versions) || {}
841cb0ef41Sopenharmony_ci  const restricted = (packument.policyRestrictions &&
851cb0ef41Sopenharmony_ci    packument.policyRestrictions.versions) || {}
861cb0ef41Sopenharmony_ci
871cb0ef41Sopenharmony_ci  const time = before && verTimes ? +(new Date(before)) : Infinity
881cb0ef41Sopenharmony_ci  const spec = npa.resolve(name, wanted || defaultTag)
891cb0ef41Sopenharmony_ci  const type = spec.type
901cb0ef41Sopenharmony_ci  const distTags = packument['dist-tags'] || {}
911cb0ef41Sopenharmony_ci
921cb0ef41Sopenharmony_ci  if (type !== 'tag' && type !== 'version' && type !== 'range') {
931cb0ef41Sopenharmony_ci    throw new Error('Only tag, version, and range are supported')
941cb0ef41Sopenharmony_ci  }
951cb0ef41Sopenharmony_ci
961cb0ef41Sopenharmony_ci  // if the type is 'tag', and not just the implicit default, then it must
971cb0ef41Sopenharmony_ci  // be that exactly, or nothing else will do.
981cb0ef41Sopenharmony_ci  if (wanted && type === 'tag') {
991cb0ef41Sopenharmony_ci    const ver = distTags[wanted]
1001cb0ef41Sopenharmony_ci    // if the version in the dist-tags is before the before date, then
1011cb0ef41Sopenharmony_ci    // we use that.  Otherwise, we get the highest precedence version
1021cb0ef41Sopenharmony_ci    // prior to the dist-tag.
1031cb0ef41Sopenharmony_ci    if (isBefore(verTimes, ver, time)) {
1041cb0ef41Sopenharmony_ci      return decorateAvoid(versions[ver] || staged[ver] || restricted[ver], avoid)
1051cb0ef41Sopenharmony_ci    } else {
1061cb0ef41Sopenharmony_ci      return pickManifest(packument, `<=${ver}`, opts)
1071cb0ef41Sopenharmony_ci    }
1081cb0ef41Sopenharmony_ci  }
1091cb0ef41Sopenharmony_ci
1101cb0ef41Sopenharmony_ci  // similarly, if a specific version, then only that version will do
1111cb0ef41Sopenharmony_ci  if (wanted && type === 'version') {
1121cb0ef41Sopenharmony_ci    const ver = semver.clean(wanted, { loose: true })
1131cb0ef41Sopenharmony_ci    const mani = versions[ver] || staged[ver] || restricted[ver]
1141cb0ef41Sopenharmony_ci    return isBefore(verTimes, ver, time) ? decorateAvoid(mani, avoid) : null
1151cb0ef41Sopenharmony_ci  }
1161cb0ef41Sopenharmony_ci
1171cb0ef41Sopenharmony_ci  // ok, sort based on our heuristics, and pick the best fit
1181cb0ef41Sopenharmony_ci  const range = type === 'range' ? wanted : '*'
1191cb0ef41Sopenharmony_ci
1201cb0ef41Sopenharmony_ci  // if the range is *, then we prefer the 'latest' if available
1211cb0ef41Sopenharmony_ci  // but skip this if it should be avoided, in that case we have
1221cb0ef41Sopenharmony_ci  // to try a little harder.
1231cb0ef41Sopenharmony_ci  const defaultVer = distTags[defaultTag]
1241cb0ef41Sopenharmony_ci  if (defaultVer &&
1251cb0ef41Sopenharmony_ci      (range === '*' || semver.satisfies(defaultVer, range, { loose: true })) &&
1261cb0ef41Sopenharmony_ci      !shouldAvoid(defaultVer, avoid)) {
1271cb0ef41Sopenharmony_ci    const mani = versions[defaultVer]
1281cb0ef41Sopenharmony_ci    if (mani && isBefore(verTimes, defaultVer, time)) {
1291cb0ef41Sopenharmony_ci      return mani
1301cb0ef41Sopenharmony_ci    }
1311cb0ef41Sopenharmony_ci  }
1321cb0ef41Sopenharmony_ci
1331cb0ef41Sopenharmony_ci  // ok, actually have to sort the list and take the winner
1341cb0ef41Sopenharmony_ci  const allEntries = Object.entries(versions)
1351cb0ef41Sopenharmony_ci    .concat(Object.entries(staged))
1361cb0ef41Sopenharmony_ci    .concat(Object.entries(restricted))
1371cb0ef41Sopenharmony_ci    .filter(([ver, mani]) => isBefore(verTimes, ver, time))
1381cb0ef41Sopenharmony_ci
1391cb0ef41Sopenharmony_ci  if (!allEntries.length) {
1401cb0ef41Sopenharmony_ci    throw Object.assign(new Error(`No versions available for ${name}`), {
1411cb0ef41Sopenharmony_ci      code: 'ENOVERSIONS',
1421cb0ef41Sopenharmony_ci      name,
1431cb0ef41Sopenharmony_ci      type,
1441cb0ef41Sopenharmony_ci      wanted,
1451cb0ef41Sopenharmony_ci      before,
1461cb0ef41Sopenharmony_ci      versions: Object.keys(versions),
1471cb0ef41Sopenharmony_ci    })
1481cb0ef41Sopenharmony_ci  }
1491cb0ef41Sopenharmony_ci
1501cb0ef41Sopenharmony_ci  const sortSemverOpt = { loose: true }
1511cb0ef41Sopenharmony_ci  const entries = allEntries.filter(([ver, mani]) =>
1521cb0ef41Sopenharmony_ci    semver.satisfies(ver, range, { loose: true }))
1531cb0ef41Sopenharmony_ci    .sort((a, b) => {
1541cb0ef41Sopenharmony_ci      const [vera, mania] = a
1551cb0ef41Sopenharmony_ci      const [verb, manib] = b
1561cb0ef41Sopenharmony_ci      const notavoida = !shouldAvoid(vera, avoid)
1571cb0ef41Sopenharmony_ci      const notavoidb = !shouldAvoid(verb, avoid)
1581cb0ef41Sopenharmony_ci      const notrestra = !restricted[a]
1591cb0ef41Sopenharmony_ci      const notrestrb = !restricted[b]
1601cb0ef41Sopenharmony_ci      const notstagea = !staged[a]
1611cb0ef41Sopenharmony_ci      const notstageb = !staged[b]
1621cb0ef41Sopenharmony_ci      const notdepra = !mania.deprecated
1631cb0ef41Sopenharmony_ci      const notdeprb = !manib.deprecated
1641cb0ef41Sopenharmony_ci      const enginea = engineOk(mania, npmVersion, nodeVersion)
1651cb0ef41Sopenharmony_ci      const engineb = engineOk(manib, npmVersion, nodeVersion)
1661cb0ef41Sopenharmony_ci      // sort by:
1671cb0ef41Sopenharmony_ci      // - not an avoided version
1681cb0ef41Sopenharmony_ci      // - not restricted
1691cb0ef41Sopenharmony_ci      // - not staged
1701cb0ef41Sopenharmony_ci      // - not deprecated and engine ok
1711cb0ef41Sopenharmony_ci      // - engine ok
1721cb0ef41Sopenharmony_ci      // - not deprecated
1731cb0ef41Sopenharmony_ci      // - semver
1741cb0ef41Sopenharmony_ci      return (notavoidb - notavoida) ||
1751cb0ef41Sopenharmony_ci        (notrestrb - notrestra) ||
1761cb0ef41Sopenharmony_ci        (notstageb - notstagea) ||
1771cb0ef41Sopenharmony_ci        ((notdeprb && engineb) - (notdepra && enginea)) ||
1781cb0ef41Sopenharmony_ci        (engineb - enginea) ||
1791cb0ef41Sopenharmony_ci        (notdeprb - notdepra) ||
1801cb0ef41Sopenharmony_ci        semver.rcompare(vera, verb, sortSemverOpt)
1811cb0ef41Sopenharmony_ci    })
1821cb0ef41Sopenharmony_ci
1831cb0ef41Sopenharmony_ci  return decorateAvoid(entries[0] && entries[0][1], avoid)
1841cb0ef41Sopenharmony_ci}
1851cb0ef41Sopenharmony_ci
1861cb0ef41Sopenharmony_cimodule.exports = (packument, wanted, opts = {}) => {
1871cb0ef41Sopenharmony_ci  const mani = pickManifest(packument, wanted, opts)
1881cb0ef41Sopenharmony_ci  const picked = mani && normalizeBin(mani)
1891cb0ef41Sopenharmony_ci  const policyRestrictions = packument.policyRestrictions
1901cb0ef41Sopenharmony_ci  const restricted = (policyRestrictions && policyRestrictions.versions) || {}
1911cb0ef41Sopenharmony_ci
1921cb0ef41Sopenharmony_ci  if (picked && !restricted[picked.version]) {
1931cb0ef41Sopenharmony_ci    return picked
1941cb0ef41Sopenharmony_ci  }
1951cb0ef41Sopenharmony_ci
1961cb0ef41Sopenharmony_ci  const { before = null, defaultTag = 'latest' } = opts
1971cb0ef41Sopenharmony_ci  const bstr = before ? new Date(before).toLocaleString() : ''
1981cb0ef41Sopenharmony_ci  const { name } = packument
1991cb0ef41Sopenharmony_ci  const pckg = `${name}@${wanted}` +
2001cb0ef41Sopenharmony_ci    (before ? ` with a date before ${bstr}` : '')
2011cb0ef41Sopenharmony_ci
2021cb0ef41Sopenharmony_ci  const isForbidden = picked && !!restricted[picked.version]
2031cb0ef41Sopenharmony_ci  const polMsg = isForbidden ? policyRestrictions.message : ''
2041cb0ef41Sopenharmony_ci
2051cb0ef41Sopenharmony_ci  const msg = !isForbidden ? `No matching version found for ${pckg}.`
2061cb0ef41Sopenharmony_ci    : `Could not download ${pckg} due to policy violations:\n${polMsg}`
2071cb0ef41Sopenharmony_ci
2081cb0ef41Sopenharmony_ci  const code = isForbidden ? 'E403' : 'ETARGET'
2091cb0ef41Sopenharmony_ci  throw Object.assign(new Error(msg), {
2101cb0ef41Sopenharmony_ci    code,
2111cb0ef41Sopenharmony_ci    type: npa.resolve(packument.name, wanted).type,
2121cb0ef41Sopenharmony_ci    wanted,
2131cb0ef41Sopenharmony_ci    versions: Object.keys(packument.versions ?? {}),
2141cb0ef41Sopenharmony_ci    name,
2151cb0ef41Sopenharmony_ci    distTags: packument['dist-tags'],
2161cb0ef41Sopenharmony_ci    defaultTag,
2171cb0ef41Sopenharmony_ci  })
2181cb0ef41Sopenharmony_ci}
219