Socket
Socket
Sign inDemoInstall

npm-pick-manifest

Package Overview
Dependencies
Maintainers
7
Versions
30
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

npm-pick-manifest - npm Package Compare versions

Comparing version 5.0.0 to 6.0.0

20

CHANGELOG.md

@@ -5,2 +5,22 @@ # Changelog

## [6.0.0](https://github.com/npm/npm-pick-manifest/compare/v5.0.0...v6.0.0) (2020-02-18)
### ⚠ BREAKING CHANGES
* 'enjoyBy' is no longer an acceptable alias.
### Features
* add GitHub Actions file for ci ([8985247](https://github.com/npm/npm-pick-manifest/commit/898524727fa157f46fdf4eb0c11148ae4808226b))
### Bug Fixes
* Handle edge cases around before:Date and filtering staged publishes ([ed2f92e](https://github.com/npm/npm-pick-manifest/commit/ed2f92e7fdc9cc7836b13ebc73e17d8fd296a07e))
* remove figgy pudding ([c24fed2](https://github.com/npm/npm-pick-manifest/commit/c24fed25b8f77fbbcc3107030f2dfed55fa54222))
* remove outdated cruft from docs ([aae7ef7](https://github.com/npm/npm-pick-manifest/commit/aae7ef7625ddddbac0548287e5d57b8f76593322))
* update some missing {loose:true} semver configs ([4015424](https://github.com/npm/npm-pick-manifest/commit/40154244a3fe1af86462bc1d6165199fc3315c10))
* Use canonical 'before' config name ([029de59](https://github.com/npm/npm-pick-manifest/commit/029de59bda6d3376f03760a00efe4ac9d997c623))
## [5.0.0](https://github.com/npm/npm-pick-manifest/compare/v4.0.0...v5.0.0) (2019-12-15)

@@ -7,0 +27,0 @@

235

index.js
'use strict'
const figgyPudding = require('figgy-pudding')
const npa = require('npm-package-arg')
const semver = require('semver')
const { checkEngine } = require('npm-install-checks')
const PickerOpts = figgyPudding({
defaultTag: { default: 'latest' },
enjoyBy: {},
includeDeprecated: { default: false }
})
const engineOk = (manifest, npmVersion, nodeVersion) => {
try {
checkEngine(manifest, npmVersion, nodeVersion)
return true
} catch (_) {
return false
}
}
module.exports = pickManifest
function pickManifest (packument, wanted, opts) {
opts = PickerOpts(opts)
const time = opts.enjoyBy && packument.time && +(new Date(opts.enjoyBy))
const spec = npa.resolve(packument.name, wanted)
const isBefore = (verTimes, ver, time) =>
!verTimes || !verTimes[ver] || Date.parse(verTimes[ver]) <= time
const pickManifest = (packument, wanted, opts) => {
const {
defaultTag = 'latest',
before = null,
nodeVersion = process.version,
npmVersion = null,
includeStaged = false
} = opts
const { name, time: verTimes } = packument
const versions = packument.versions || {}
const staged = (includeStaged && packument.stagedVersions &&
packument.stagedVersions.versions) || {}
const restricted = (packument.policyRestrictions &&
packument.policyRestrictions.versions) || {}
const time = before && verTimes ? +(new Date(before)) : Infinity
const spec = npa.resolve(name, wanted || defaultTag)
const type = spec.type
if (type === 'version' || type === 'range') {
wanted = semver.clean(wanted, true) || wanted
}
const distTags = packument['dist-tags'] || {}
const versions = Object.keys(packument.versions || {}).filter(v => {
return semver.valid(v, true)
})
const policyRestrictions = packument.policyRestrictions
const restrictedVersions = policyRestrictions
? Object.keys(policyRestrictions.versions) : []
function enjoyableBy (v) {
return !time || (
packument.time[v] && time >= +(new Date(packument.time[v]))
)
if (type !== 'tag' && type !== 'version' && type !== 'range') {
throw new Error('Only tag, version, and range are supported')
}
let err
// if the type is 'tag', and not just the implicit default, then it must
// be that exactly, or nothing else will do.
if (wanted && type === 'tag') {
const ver = distTags[wanted]
// if the version in the dist-tags is before the before date, then
// we use that. Otherwise, we get the highest precedence version
// prior to the dist-tag.
if (isBefore(verTimes, ver, time)) {
return versions[ver] || staged[ver] || restricted[ver]
} else {
return pickManifest(packument, `<=${ver}`, opts)
}
}
if (!versions.length && !restrictedVersions.length) {
err = new Error(`No valid versions available for ${packument.name}`)
err.code = 'ENOVERSIONS'
err.name = packument.name
err.type = type
err.wanted = wanted
throw err
// similarly, if a specific version, then only that version will do
if (wanted && type === 'version') {
const ver = semver.clean(wanted, { loose: true })
const mani = versions[ver] || staged[ver] || restricted[ver]
return isBefore(verTimes, ver, time) ? mani : null
}
let target
// ok, sort based on our heuristics, and pick the best fit
const range = type === 'range' ? wanted : '*'
if (type === 'tag' && enjoyableBy(distTags[wanted])) {
target = distTags[wanted]
} else if (type === 'version') {
target = wanted
} else if (type !== 'range' && enjoyableBy(distTags[wanted])) {
throw new Error('Only tag, version, and range are supported')
// if the range is *, then we prefer the 'latest' if available
const defaultVer = distTags[defaultTag]
if (defaultVer && (range === '*' || semver.satisfies(defaultVer, range, { loose: true }))) {
const mani = versions[defaultVer]
if (mani && isBefore(verTimes, defaultVer, time)) {
return mani
}
}
const tagVersion = distTags[opts.defaultTag]
// ok, actually have to sort the list and take the winner
const allEntries = Object.entries(versions)
.concat(Object.entries(staged))
.concat(Object.entries(restricted))
.filter(([ver, mani]) => isBefore(verTimes, ver, time))
if (
!target &&
tagVersion &&
packument.versions[tagVersion] &&
enjoyableBy(tagVersion) &&
semver.satisfies(tagVersion, wanted, true)
) {
target = tagVersion
if (!allEntries.length) {
throw Object.assign(new Error(`No valid versions available for ${name}`), {
code: 'ENOVERSIONS',
name,
type,
wanted,
versions: Object.keys(versions)
})
}
if (!target && !opts.includeDeprecated) {
const undeprecated = versions.filter(v => !packument.versions[v].deprecated && enjoyableBy(v)
)
target = semver.maxSatisfying(undeprecated, wanted, true)
}
if (!target) {
const stillFresh = versions.filter(enjoyableBy)
target = semver.maxSatisfying(stillFresh, wanted, true)
}
const entries = allEntries.filter(([ver, mani]) =>
semver.satisfies(ver, range, { loose: true }))
.sort((a, b) => {
const [vera, mania] = a
const [verb, manib] = b
const notrestra = !restricted[a]
const notrestrb = !restricted[b]
const notstagea = !staged[a]
const notstageb = !staged[b]
const notdepra = !mania.deprecated
const notdeprb = !manib.deprecated
const enginea = engineOk(mania, npmVersion, nodeVersion)
const engineb = engineOk(manib, npmVersion, nodeVersion)
// sort by:
// - not restricted
// - not staged
// - not deprecated and engine ok
// - engine ok
// - not deprecated
// - semver
return (notrestrb - notrestra) ||
(notstageb - notstagea) ||
((notdeprb && engineb) - (notdepra && enginea)) ||
(engineb - enginea) ||
(notdeprb - notdepra) ||
semver.rcompare(vera, verb, { loose: true })
})
if (!target && wanted === '*' && enjoyableBy(tagVersion)) {
// This specific corner is meant for the case where
// someone is using `*` as a selector, but all versions
// are pre-releases, which don't match ranges at all.
target = tagVersion
}
return entries[0] && entries[0][1]
}
if (
!target &&
time &&
type === 'tag' &&
distTags[wanted] &&
!enjoyableBy(distTags[wanted])
) {
const stillFresh = versions.filter(v =>
enjoyableBy(v) && semver.lte(v, distTags[wanted], true)
).sort(semver.rcompare)
target = stillFresh[0]
}
module.exports = (packument, wanted, opts = {}) => {
const picked = pickManifest(packument, wanted, opts)
const policyRestrictions = packument.policyRestrictions
const restricted = (policyRestrictions && policyRestrictions.versions) || {}
if (!target && restrictedVersions) {
target = semver.maxSatisfying(restrictedVersions, wanted, true)
if (picked && !restricted[picked.version]) {
return picked
}
const manifest = (
target &&
packument.versions[target]
)
if (!manifest) {
// Check if target is forbidden
const isForbidden = target && policyRestrictions && policyRestrictions.versions[target]
const pckg = `${packument.name}@${wanted}${
opts.enjoyBy
? ` with an Enjoy By date of ${
new Date(opts.enjoyBy).toLocaleString()
}. Maybe try a different date?`
: ''
}`
const { before = null, defaultTag = 'latest' } = opts
const bstr = before ? new Date(before).toLocaleString() : ''
const { name } = packument
const pckg = `${name}@${wanted}` +
(before ? ` with a date before ${bstr}` : '')
if (isForbidden) {
err = new Error(`Could not download ${pckg} due to policy violations.\n${policyRestrictions.message}\n`)
err.code = 'E403'
} else {
err = new Error(`No matching version found for ${pckg}.`)
err.code = 'ETARGET'
}
const isForbidden = picked && !!restricted[picked.version]
const polMsg = isForbidden ? policyRestrictions.message : ''
err.name = packument.name
err.type = type
err.wanted = wanted
err.versions = versions
err.distTags = distTags
err.defaultTag = opts.defaultTag
throw err
} else {
return manifest
}
const msg = !isForbidden ? `No matching version found for ${pckg}.`
: `Could not download ${pckg} due to policy violations:\n${polMsg}`
const code = isForbidden ? 'E403' : 'ETARGET'
throw Object.assign(new Error(msg), {
code,
type: npa.resolve(packument.name, wanted).type,
wanted,
versions: Object.keys(packument.versions),
name,
distTags: packument['dist-tags'],
defaultTag
})
}
{
"name": "npm-pick-manifest",
"version": "5.0.0",
"version": "6.0.0",
"description": "Resolves a matching manifest from a package metadata document according to standard npm semver resolution rules.",

@@ -10,4 +10,6 @@ "main": "index.js",

"scripts": {
"coverage": "tap",
"lint": "standard",
"postrelease": "npm publish",
"posttest": "standard",
"posttest": "npm run lint",
"prepublishOnly": "git push --follow-tags",

@@ -31,3 +33,3 @@ "prerelease": "npm t",

"dependencies": {
"figgy-pudding": "^3.5.1",
"npm-install-checks": "^4.0.0",
"npm-package-arg": "^8.0.0",

@@ -34,0 +36,0 @@ "semver": "^7.0.0"

@@ -1,2 +0,2 @@

# npm-pick-manifest [![npm version](https://img.shields.io/npm/v/npm-pick-manifest.svg)](https://npm.im/npm-pick-manifest) [![license](https://img.shields.io/npm/l/npm-pick-manifest.svg)](https://npm.im/npm-pick-manifest) [![Travis](https://img.shields.io/travis/npm/npm-pick-manifest.svg)](https://travis-ci.org/npm/npm-pick-manifest) [![AppVeyor](https://ci.appveyor.com/api/projects/status/github/npm/npm-pick-manifest?svg=true)](https://ci.appveyor.com/project/npm/npm-pick-manifest) [![Coverage Status](https://coveralls.io/repos/github/npm/npm-pick-manifest/badge.svg?branch=latest)](https://coveralls.io/github/npm/npm-pick-manifest?branch=latest)
# npm-pick-manifest [![npm version](https://img.shields.io/npm/v/npm-pick-manifest.svg)](https://npm.im/npm-pick-manifest) [![license](https://img.shields.io/npm/l/npm-pick-manifest.svg)](https://npm.im/npm-pick-manifest) [![Travis](https://img.shields.io/travis/npm/npm-pick-manifest.svg)](https://travis-ci.org/npm/npm-pick-manifest) [![Coverage Status](https://coveralls.io/repos/github/npm/npm-pick-manifest/badge.svg?branch=latest)](https://coveralls.io/github/npm/npm-pick-manifest?branch=latest)

@@ -14,3 +14,2 @@ [`npm-pick-manifest`](https://github.com/npm/npm-pick-manifest) is a standalone

* [Features](#features)
* [Contributing](#contributing)
* [API](#api)

@@ -33,13 +32,8 @@ * [`pickManifest()`](#pick-manifest)

* Uses npm's exact semver resolution algorithm
* Supports ranges, tags, and versions
* Uses npm's exact [semver resolution algorithm](http://npm.im/semver).
* Supports ranges, tags, and versions.
* Prefers non-deprecated versions to deprecated versions.
* Prefers versions whose `engines` requirements are satisfied over those
that will raise a warning or error at install time.
### Contributing
The npm-pick-manifest team enthusiastically welcomes contributions and project participation!
There's a bunch of things you can do if you want to contribute! The [Contributor
Guide](CONTRIBUTING.md) has all the information you need for everything from
reporting bugs to contributing entire new features. Please don't hesitate to
jump in if you'd like to, or even ask us questions if something isn't clear.
### API

@@ -49,3 +43,3 @@

Returns the manifest that matches `selector`, or throws an error.
Returns the manifest that best matches `selector`, or throws an error.

@@ -71,18 +65,71 @@ Packuments are anything returned by metadata URLs from the npm registry. That

The algorithm will follow npm's algorithm for semver resolution, and only `tag`,
`range`, and `version` selectors are supported.
The algorithm will follow npm's algorithm for semver resolution, and only
`tag`, `range`, and `version` selectors are supported.
The function will throw `ETARGET` if there was no matching manifest, and
`ENOVERSIONS` if the packument object has no valid versions in `versions`.
If the only matching manifest is included in a `policyRestrictions` section
of the packument, then an `E403` is raised.
If `opts.defaultTag` is provided, it will be used instead of `latest`. That is,
if that tag matches the selector, it will be used, even if a higher available
version matches the range.
#### <a name="pick-manifest-options"></a> Options
If `opts.enjoyBy` is provided, it should be something that can be passed to `new
Date(x)`, such as a `Date` object or a timestamp string. It will be used to
filter the selected versions such that only versions less than or equal to
`enjoyBy` are considered.
All options are optional.
If `opts.includeDeprecated` passed in as true, deprecated versions will be
selected. By default, deprecated versions other than `defaultTag` are ignored.
* `includeStaged` - Boolean, default `false`. Include manifests in the
`stagedVersions.versions` set, to support installing [staged
packages](https://github.com/npm/rfcs/pull/92) when appropriate. Note
that staged packages are always treated as lower priority than actual
publishes, even when `includeStaged` is set.
* `defaultTag` - String, default `'latest'`. The default `dist-tag` to
install when no specifier is provided. Note that the version indicated
by this specifier will be given top priority if it matches a supplied
semver range.
* `before` - String, Date, or Number, default `null`. This is passed to
`new Date()`, so anything that works there will be valid. Do not
consider _any_ manifests that were published after the date indicated.
Note that this is only relevant when the packument includes a `time`
field listing the publish date of all the packages.
* `nodeVersion` - String, default `process.version`. The Node.js version
to use when checking manifests for `engines` requirement satisfaction.
* `npmVersion` - String, default `null`. The npm version to use when
checking manifest for `engines` requirement satisfaction. (If `null`,
then this particular check is skipped.)
### Algorithm
1. Create list of all versions in `versions`,
`policyRestrictions.versions`, and (if `includeStaged` is set)
`stagedVersions.versions`.
2. If a `dist-tag` is requested,
1. If the manifest is not after the specified `before` date, then
select that from the set.
2. If the manifest is after the specified `before` date, then re-start
the selection looking for the highest SemVer range that is equal to
or less than the `dist-tag` target.
3. If a specific version is requested,
1. If the manifest is not after the specified `before` date, then
select the specified manifest.
2. If the manifest is after the specified `before` date, then raise
`ETARGET` error. (NB: this is a breaking change from v5, where a
specified version would override the `before` setting.)
4. (At this point we know a range is requested.)
5. If the `defaultTag` refers to a `dist-tag` that satisfies the range (or
if the range is `'*'` or `''`), and the manifest is published before the
`before` setting, then select that manifest.
6. If nothing is yet selected, sort by the following heuristics in order,
and select the top item:
1. Prioritize versions that are not in `policyRestrictions` over those
that are.
2. Prioritize published versions over staged versions.
3. Prioritize versions that are not deprecated, and which have a
satisfied engines requirement, over those that are either deprecated
or have an engines mismatch.
4. Prioritize versions that have a satisfied engines requirement over
those that do not.
5. Prioritize versions that are not are not deprecated (but have a
mismatched engines requirement) over those that are deprecated.
6. Prioritize higher SemVer precedence over lower SemVer precedence.
7. If no manifest was selected, raise an `ETARGET` error.
8. If the selected item is in the `policyRestrictions.versions` list, raise
an `E403` error.
9. Return the selected manifest.
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc