Socket
Socket
Sign inDemoInstall

@npmcli/arborist

Package Overview
Dependencies
Maintainers
5
Versions
192
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@npmcli/arborist - npm Package Compare versions

Comparing version 0.0.0-pre.14 to 0.0.0-pre.15

lib/arborist/audit.js

122

lib/arborist/build-ideal-tree.js

@@ -7,2 +7,3 @@ // mixin implementing the buildIdealTree method

const semver = require('semver')
const pickManifest = require('npm-pick-manifest')

@@ -63,2 +64,4 @@ const calcDepFlags = require('../calc-dep-flags.js')

const _queueNamedUpdates = Symbol('queueNamedUpdates')
const _queueVulnDependents = Symbol('queueVulnDependents')
const _avoidRange = Symbol('avoidRange')
const _shouldUpdateNode = Symbol('shouldUpdateNode')

@@ -72,4 +75,6 @@ const _resetDepFlags = Symbol('resetDepFlags')

const _globalRootNode = Symbol('globalRootNode')
const _isVulnerable = Symbol.for('isVulnerable')
// used by Reify mixin
const _force = Symbol.for('force')
const _explicitRequests = Symbol.for('explicitRequests')

@@ -95,5 +100,10 @@ const _global = Symbol.for('global')

globalStyle = false,
legacyPeerDeps = false,
force = false,
} = options
this[_force] = !!force
this.idealTree = options.idealTree
this.legacyPeerDeps = legacyPeerDeps

@@ -126,2 +136,4 @@ this[_globalStyle] = this[_global] || globalStyle

process.emit('time', 'idealTree')
if (!options.add && !options.rm && this[_global])

@@ -147,2 +159,3 @@ return Promise.reject(new Error('global requires an add or rm option'))

.then(() => {
process.emit('timeEnd', 'idealTree')
this.finishTracker('idealTree')

@@ -182,2 +195,3 @@ return this.idealTree

[_initTree] () {
process.emit('time', 'idealTree:init')
return (

@@ -204,2 +218,3 @@ this[_global] ? this[_globalRootNode]()

this.virtualTree = null
process.emit('timeEnd', 'idealTree:init')
})

@@ -229,2 +244,3 @@ }

global: this[_global],
legacyPeerDeps: this.legacyPeerDeps,
})

@@ -236,2 +252,3 @@ }

[_applyUserRequests] (options) {
process.emit('time', 'idealTree:userRequests')
// If we have a list of package names to update, and we know it's

@@ -243,2 +260,5 @@ // going to update them wherever they are, add any paths into those

if (this.auditReport && this.auditReport.size > 0)
this[_queueVulnDependents](options)
if (options.rm && options.rm.length) {

@@ -251,3 +271,6 @@ addRmPkgDeps.rm(this.idealTree.package, options.rm)

// triggers a refresh of all edgesOut
const after = () => this.idealTree.package = this.idealTree.package
const after = () => {
this.idealTree.package = this.idealTree.package
process.emit('timeEnd', 'idealTree:userRequests')
}

@@ -260,3 +283,2 @@ // these just add and remove to/from the root node

// This returns a promise because we might not have the name yet,

@@ -293,2 +315,69 @@ // and need to call pacote.manifest to find the name.

[_queueVulnDependents] (options) {
for (const [name, {nodes}] of this.auditReport.entries()) {
for (const node of nodes) {
for (const edge of node.edgesIn) {
this.addTracker('buildIdealTree', edge.from.name, edge.from.location)
this[_depsQueue].push(edge.from)
}
}
}
// note any that can't be fixed at the root level without --force
// if there's a fix, we use that. otherwise, the user has to remove it,
// find a different thing, fix the upstream, etc.
//
// XXX: how to handle top nodes that aren't the root? Maybe the report
// just tells the user to cd into that directory and fix it?
if (this[_force] && this.auditReport && this.auditReport.topVulns.size) {
options.add = options.add || []
options.rm = options.rm || []
for (const [name, topVuln] of this.auditReport.topVulns.entries()) {
const {
packument,
simpleRange,
range: avoid,
topNodes,
fixAvailable,
} = topVuln
for (const node of topNodes) {
if (node !== this.idealTree) {
// not something we're going to fix, sorry. have to cd into
// that directory and fix it yourself.
this.log.warn('audit', 'Manual fix required in linked project ' +
`at ./${node.location} for ${name}@${simpleRange}.\n` +
`'cd ./${node.location}' and run 'npm audit' for details.`)
continue
}
if (!fixAvailable) {
this.log.warn('audit', `No fix available for ${name}@${simpleRange}`)
continue
}
const { isSemVerMajor, version } = fixAvailable
const breakingMessage = isSemVerMajor
? 'a SemVer major change'
: 'outside your stated dependency range'
this.log.warn('audit', `Updating ${name} to ${version},` +
`which is ${breakingMessage}.`)
options.add.push(`${name}@${version}`)
}
}
}
}
[_isVulnerable] (node) {
return this.auditReport && this.auditReport.isVulnerable(node)
}
[_avoidRange] (name) {
if (!this.auditReport)
return null
const vuln = this.auditReport.get(name)
if (!vuln)
return null
return vuln.range
}
[_queueNamedUpdates] () {

@@ -304,2 +393,4 @@ const names = this[_updateNames]

// XXX this could be faster by doing a series of inventory.query('name')
// calls rather than walking over everything in the tree.
const set = this.idealTree.inventory

@@ -327,2 +418,3 @@ .filter(n => this[_shouldUpdateNode](n))

[_buildDeps] (node) {
process.emit('time', 'idealTree:buildDeps')
this[_depsQueue].push(this.idealTree)

@@ -332,2 +424,3 @@ this.log.silly('idealTree', 'buildDeps')

return this[_buildDepStep]()
.then(() => process.emit('timeEnd', 'idealTree:buildDeps'))
}

@@ -338,3 +431,5 @@

if (this[_currentDep]) {
this.finishTracker('idealTree', this[_currentDep].name, this[_currentDep].location)
const { location, name } = this[_currentDep]
process.emit('timeEnd', `idealTree:${location || '#root'}`)
this.finishTracker('idealTree', name, location)
this[_currentDep] = null

@@ -364,2 +459,3 @@ }

this[_currentDep] = node
process.emit('time', `idealTree:${node.location || '#root'}`)

@@ -450,5 +546,7 @@ // if any deps are missing or invalid, then we fetch the manifest for

// a context where they're likely to be resolvable.
const { legacyPeerDeps } = this
parent = parent || new Node({
path: '/virtual-root',
pkg: edge.from.package,
legacyPeerDeps,
})

@@ -475,2 +573,3 @@ const spec = npa.resolve(edge.name, edge.spec, edge.from.path)

(!edge.valid || !edge.to || this[_updateNames].includes(edge.name) ||
this[_isVulnerable](edge.to) ||
node.isRoot && this[_explicitRequests].has(edge.name)))

@@ -483,3 +582,6 @@ }

else {
const options = Object.create(this.options)
const options = {
...this.options,
avoid: this[_avoidRange](spec.name),
}
const p = pacote.manifest(spec, options)

@@ -496,6 +598,7 @@ this[_manifests].set(spec.raw, p)

// might be within another package that doesn't exist yet.
const { legacyPeerDeps } = this
return spec.type === 'directory'
? this[_linkFromSpec](name, spec, parent, edge)
: this[_fetchManifest](spec)
.then(pkg => new Node({ name, pkg, parent }), error => {
.then(pkg => new Node({ name, pkg, parent, legacyPeerDeps }), error => {
error.requiredBy = edge.from.location || '.'

@@ -509,2 +612,3 @@ // failed to load the spec, either because of enotarget or

error,
legacyPeerDeps,
})

@@ -518,4 +622,5 @@ this[_loadFailures].add(n)

const realpath = spec.fetchSpec
const { legacyPeerDeps } = this
return rpj(realpath + '/package.json').catch(() => ({})).then(pkg => {
const link = new Link({ name, parent, realpath, pkg })
const link = new Link({ name, parent, realpath, pkg, legacyPeerDeps })
this[_linkNodes].add(link)

@@ -546,3 +651,4 @@ return link

[_placeDep] (dep, node, edge, peerEntryEdge = null) {
if (edge.to && !edge.error && !this[_updateNames].includes(edge.name))
if (edge.to && !edge.error && !this[_updateNames].includes(edge.name) &&
!this[_isVulnerable](edge.to))
return []

@@ -894,2 +1000,3 @@

[_fixDepFlags] () {
process.emit('time', 'idealTree:fixDepFlags')
const metaFromDisk = this.idealTree.meta.loadedFromDisk

@@ -931,2 +1038,3 @@ // if the options set prune:false, then we don't prune, but we still

}
process.emit('timeEnd', 'idealTree:fixDepFlags')
}

@@ -933,0 +1041,0 @@

6

lib/arborist/index.js

@@ -27,6 +27,7 @@ // The arborist manages three trees:

// and path, and call out to the others.
const Reify = require('./reify.js')
const Auditor = require('./audit.js')
const {resolve} = require('path')
class Arborist extends Reify(require('events')) {
class Arborist extends Auditor(require('events')) {
constructor (options = {}) {
process.emit('time', 'arborist:ctor')
super(options)

@@ -39,2 +40,3 @@ this.options = {

this.path = resolve(this.options.path)
process.emit('timeEnd', 'arborist:ctor')
}

@@ -41,0 +43,0 @@ }

@@ -83,2 +83,4 @@ // mix-in implementing the loadActual method

return this[_loadActualActually]()
}).then(tree => {
return tree
})

@@ -165,2 +167,3 @@ }

return this[path === real ? _newNode : _newLink]({
legacyPeerDeps: this.legacyPeerDeps,
path,

@@ -197,3 +200,3 @@ realpath: real,

return process.env._TEST_ARBORIST_SLOW_LINK_TARGET_ === '1'
? new Promise(res => setTimeout(() => res(new Node(options)), 10))
? new Promise(res => setTimeout(() => res(new Node(options)), 100))
: new Node(options)

@@ -200,0 +203,0 @@ }

@@ -10,2 +10,3 @@ // mixin providing the loadVirtual method

const relpath = require('../relpath.js')
const rpj = require('read-package-json-fast')

@@ -42,3 +43,3 @@ const loadFromShrinkwrap = Symbol('loadFromShrinkwrap')

const {
root = this[loadNode]('', s.data.packages[''])
root = this[loadNode]('', s.data.packages[''] || {})
} = options

@@ -50,3 +51,3 @@

[loadFromShrinkwrap] (s, root) {
async [loadFromShrinkwrap] (s, root) {
root.meta = s

@@ -56,5 +57,5 @@ s.add(root)

const {links, nodes} = this[resolveNodes](s, root)
this[resolveLinks](links, nodes)
await this[resolveLinks](links, nodes)
this[assignParentage](nodes)
return Promise.resolve(root)
return root
}

@@ -81,5 +82,9 @@

// Set the targets to nodes in the set, if we have them (we might not)
[resolveLinks] (links, nodes) {
async [resolveLinks] (links, nodes) {
// now we've loaded the root, and all real nodes
// link up the links
const {meta} = this.virtualTree
const {loadedFromDisk, originalLockfileVersion} = meta
const oldLockfile = loadedFromDisk && !(originalLockfileVersion >= 2)
for (const [location, meta] of links.entries()) {

@@ -91,2 +96,13 @@ const targetPath = resolve(this.path, meta.resolved)

nodes.set(location, link)
nodes.set(targetLoc, link.target)
// legacy shrinkwraps do not store all the info we need for the target.
// if we're loading from disk, and have a link in place, we need to
// look in that actual folder (or at least try to) in order to get
// the dependencies of the link target and load it properly.
if (oldLockfile) {
const pj = link.realpath + '/package.json'
const pkg = await rpj(pj).catch(() => null)
if (pkg)
link.target.package = pkg
}
}

@@ -125,3 +141,3 @@ }

ppkg.bundleDependencies = [name]
else
else if (!ppkg.bundleDependencies.includes(name))
ppkg.bundleDependencies.push(name)

@@ -136,2 +152,3 @@ }

const node = new Node({
legacyPeerDeps: this.legacyPeerDeps,
root: this.virtualTree,

@@ -157,2 +174,3 @@ path,

const link = new Link({
legacyPeerDeps: this.legacyPeerDeps,
path,

@@ -159,0 +177,0 @@ realpath: resolve(this.path, targetLoc),

// mixin implementing the reify method
// XXX unsupported platforms should be failures if the node is optional
// otherwise we try anyway.
// XXX this needs to clone rather than copy, so that we can leave failed
// optional deps in the ideal tree, but remove them from the actual.
// But to do that, we need a way to clone a tree efficiently.
const npa = require('npm-package-arg')

@@ -16,2 +10,3 @@ const pacote = require('pacote')

const updateDepSpec = require('../update-dep-spec.js')
const AuditReport = require('../audit-report.js')

@@ -49,3 +44,3 @@ const boolEnv = b => b ? '1' : ''

const _handleOptionalFailure = Symbol('handleOptionalFailure')
const _loadTrees = Symbol('loadTrees')
const _loadTrees = Symbol.for('loadTrees')

@@ -66,2 +61,4 @@ // shared symbols for swapping out when testing

const _loadBundlesAndUpdateTrees = Symbol.for('loadBundlesAndUpdateTrees')
const _submitQuickAudit = Symbol('submitQuickAudit')
const _awaitQuickAudit = Symbol('awaitQuickAudit')
const _unpackNewModules = Symbol.for('unpackNewModules')

@@ -88,5 +85,5 @@ const _moveContents = Symbol.for('moveContents')

const _scriptShell = Symbol('scriptShell')
const _force = Symbol('force')
// defined by Ideal mixin
const _force = Symbol.for('force')
const _idealTreePrune = Symbol.for('idealTreePrune')

@@ -102,3 +99,2 @@ const _explicitRequests = Symbol.for('explicitRequests')

ignoreScripts = false,
force = false,
scriptShell,

@@ -113,3 +109,2 @@ savePrefix = '^',

this[_ignoreScripts] = !!ignoreScripts
this[_force] = !!force
this[_scriptShell] = scriptShell

@@ -134,2 +129,3 @@ this[_savePrefix] = savePrefix

this.addTracker('reify')
process.emit('time', 'reify')
return this[_loadTrees](options)

@@ -142,2 +138,3 @@ .then(() => this[_diffTrees]())

.then(() => this[_loadBundlesAndUpdateTrees]())
.then(() => this[_submitQuickAudit]())
.then(() => this[_unpackNewModules]())

@@ -149,4 +146,6 @@ .then(() => this[_moveBackRetiredUnchanged]())

.then(() => this[_copyIdealToActual]())
.then(() => this[_awaitQuickAudit]())
.then(() => {
this.finishTracker('reify')
process.emit('timeEnd', 'reify')
return this.actualTree

@@ -159,4 +158,6 @@ })

[_loadTrees] (options) {
process.emit('time', 'reify:loadTrees')
if (!this[_global])
return Promise.all([this.loadActual(), this.buildIdealTree(options)])
.then(() => process.emit('timeEnd', 'reify:loadTrees'))

@@ -173,5 +174,7 @@ // the global install space tends to have a lot of stuff in it. don't

.then(() => this.loadActual(actualOpts))
.then(() => process.emit('timeEnd', 'reify:loadTrees'))
}
[_diffTrees] () {
process.emit('time', 'reify:diffTrees')
// XXX if we have an existing diff already, there should be a way

@@ -190,2 +193,3 @@ // to just invalidate the parts that changed, but avoid walking the

}
process.emit('timeEnd', 'reify:diffTrees')
}

@@ -213,2 +217,3 @@

[_retireShallowNodes] () {
process.emit('time', 'reify:retireShallow')
const moves = this[_retiredPaths] = {}

@@ -226,2 +231,3 @@ for (const diff of this.diff.children) {

.catch(er => this[_rollbackRetireShallowNodes](er))
.then(() => process.emit('timeEnd', 'reify:retireShallow'))
}

@@ -247,2 +253,3 @@

[_rollbackRetireShallowNodes] (er) {
process.emit('time', 'reify:rollback:retireShallow')
const moves = this[_retiredPaths]

@@ -254,2 +261,3 @@ const movePromises = Object.entries(moves)

.catch(er => {})
.then(() => process.emit('timeEnd', 'reify:rollback:retireShallow'))
.then(() => { throw er })

@@ -264,2 +272,3 @@ }

process.emit('time', 'reify:trashOmits')
const filter = node =>

@@ -274,5 +283,7 @@ node.peer && this[_omitPeer] ||

}
process.emit('timeEnd', 'reify:trashOmits')
}
[_createSparseTree] () {
process.emit('time', 'reify:createSparse')
// if we call this fn again, we look for the previous list

@@ -289,2 +300,3 @@ // so that we can avoid making the same directory multiple times

.then(() => dirs.forEach(dir => this[_sparseTreeDirs].add(dir)))
.then(() => process.emit('timeEnd', 'reify:createSparse'))
.catch(er => this[_rollbackCreateSparseTree](er))

@@ -294,2 +306,3 @@ }

[_rollbackCreateSparseTree] (er) {
process.emit('time', 'reify:rollback:createSparse')
// cut the roots of the sparse tree, not the leaves

@@ -305,2 +318,3 @@ const moves = this[_retiredPaths]

})
.then(() => process.emit('timeEnd', 'reify:rollback:createSparse'))
.then(() => this[_rollbackRetireShallowNodes](er))

@@ -321,2 +335,4 @@ }

process.emit('time', 'reify:loadShrinkwraps')
const Arborist = this.constructor

@@ -335,2 +351,3 @@ return promiseAllRejectLate(shrinkwraps.map(diff => {

.then(() => this[_createSparseTree]())
.then(() => process.emit('timeEnd', 'reify:loadShrinkwraps'))
.catch(er => this[_rollbackCreateSparseTree](er))

@@ -350,2 +367,3 @@ }

process.emit('time', `reifyNode:${node.location}`)
this.addTracker('reify', node.name, node.location)

@@ -362,2 +380,3 @@

this.finishTracker('reify', node.name, node.location)
process.emit('timeEnd', `reifyNode:${node.location}`)
return node

@@ -410,11 +429,15 @@ })

// If we're loading from a v1 lockfile, then need to do this again later
// after reading from the disk.
// after reading from the disk. Also grab the bin, because old lockfiles
// did not track that useful bit of info.
const {meta} = this.idealTree
return meta.loadedFromDisk && meta.originalLockfileVersion < 2 &&
rpj(node.path + '/package.json').then(pkg => {
if (meta.loadedFromDisk && !(meta.originalLockfileVersion >= 2)) {
return rpj(node.path + '/package.json').then(pkg => {
node.package.bin = pkg.bin
node.package.os = pkg.os
node.package.cpu = pkg.cpu
node.package.engines = pkg.engines
meta.add(node)
return this[_checkEngineAndPlatform](node)
})
}
}

@@ -489,2 +512,4 @@

) {
if (depth === 0)
process.emit('time', 'reify:loadBundles')
const maxBundleDepth = bundlesByDepth.get('maxBundleDepth')

@@ -498,2 +523,3 @@ if (depth > maxBundleDepth) {

}
process.emit('timeEnd', 'reify:loadBundles')
return

@@ -514,3 +540,2 @@ }

// extract all the nodes with bundles
this.log.silly('reify', 'reifyNode')
return promiseAllRejectLate(set.map(node => this[_reifyNode](node)))

@@ -574,2 +599,26 @@ // then load their unpacked children and move into the ideal tree

[_submitQuickAudit] () {
if (this.options.audit === false)
return this.auditReport = null
// we submit the quick audit at this point in the process, as soon as
// we have all the deps resolved, so that it can overlap with the other
// actions as much as possible. Stash the promise, which we resolve
// before finishing the reify() and returning the tree. Thus, we do
// NOT return the promise, as the intent is for this to run in parallel
// with the reification, and be resolved at a later time.
process.emit('time', 'reify:audit')
this.auditReport = AuditReport.load(this.idealTree, this.options)
.then(res => {
process.emit('timeEnd', 'reify:audit')
this.auditReport = res
})
}
// return the promise if we're waiting for it, or the replaced result
[_awaitQuickAudit] () {
return this.auditReport
}
// ok! actually unpack stuff into their target locations!

@@ -580,2 +629,3 @@ // The sparse tree has already been created, so we walk the diff

[_unpackNewModules] () {
process.emit('time', 'reify:unpack')
const unpacks = []

@@ -597,2 +647,3 @@ dfwalk({

return promiseAllRejectLate(unpacks)
.then(() => process.emit('timeEnd', 'reify:unpack'))
.catch(er => this[_rollbackCreateSparseTree](er))

@@ -613,2 +664,3 @@ }

// shallowest nodes that we moved aside in the first place.
process.emit('time', 'reify:unretire')
const moves = this[_retiredPaths]

@@ -641,2 +693,3 @@ this[_retiredUnchanged] = {}

}))
.then(() => process.emit('timeEnd', 'reify:unretire'))
.catch(er => this[_rollbackMoveBackRetiredUnchanged](er))

@@ -677,2 +730,4 @@ }

process.emit('time', 'reify:runScripts')
// for all the things being installed, run their appropriate scripts

@@ -729,2 +784,3 @@ // run in tip->root order, so as to be more likely to build a node's

.then(() => this[_runScriptQueue]('postinstall', postinstall))
.then(() => process.emit('timeEnd', 'reify:runScripts'))
.catch(er => this[_rollbackMoveBackRetiredUnchanged](er))

@@ -737,2 +793,3 @@ }

process.emit('time', `reify:runScripts:${event}`)
return promiseCallLimit(queue.map(([node, pkg]) => () => {

@@ -762,2 +819,3 @@ const {path} = node

}))
.then(() => process.emit('timeEnd', `reify:runScripts:${event}`))
}

@@ -770,2 +828,3 @@

[_removeTrash] () {
process.emit('time', 'reify:trash')
const promises = []

@@ -783,2 +842,3 @@ const failures = []

})
.then(() => process.emit('timeEnd', 'reify:trash'))
}

@@ -798,2 +858,4 @@

process.emit('time', 'reify:save')
if (this[_resolvedAdd]) {

@@ -832,3 +894,3 @@ const root = this.idealTree

}, null, 2) + '\n'),
])
]).then(() => process.emit('timeEnd', 'reify:save'))
}

@@ -835,0 +897,0 @@

@@ -8,3 +8,3 @@ const { depth } = require('treeverse')

tree.peer = false
return depth({
const ret = depth({
tree,

@@ -15,2 +15,3 @@ visit: node => calcDepFlagsStep(node),

})
return ret
}

@@ -17,0 +18,0 @@

@@ -102,2 +102,3 @@ // a tree representing the difference between two trees

const action = getAction({actual, ideal})
// if it's a match, then get its children

@@ -121,8 +122,20 @@ // otherwise, this is the child diff node

// efficient to just fix it while we're passing through already.
//
// Note that moving over a bundled dep will break the links to other
// deps under this parent, which may have been transitively bundled.
// Breaking those links means that we'll no longer see the transitive
// dependency, meaning that it won't appear as bundled any longer!
// In order to not end up dropping transitively bundled deps, we have
// to get the list of nodes to move, then move them all at once, rather
// than moving them one at a time in the first loop.
const bd = ideal.package.bundleDependencies
if (actual && bd && bd.length) {
const bundledChildren = []
for (const [name, node] of actual.children.entries()) {
if (node.inBundle)
node.parent = ideal
bundledChildren.push(node)
}
for (const node of bundledChildren) {
node.parent = ideal
}
}

@@ -129,0 +142,0 @@ children.push(...getChildren({actual, ideal, unchanged, removed}))

@@ -82,2 +82,3 @@ // inventory, path, realpath, root, and parent

fsChildren,
legacyPeerDeps = false,
linksIn,

@@ -127,2 +128,3 @@ hasShrinkwrap,

this.hasShrinkwrap = hasShrinkwrap || pkg._hasShrinkwrap || false
this.legacyPeerDeps = legacyPeerDeps

@@ -347,3 +349,3 @@ this.children = new Map()

const pd = this.package.peerDependencies
if (pd && typeof pd === 'object') {
if (pd && typeof pd === 'object' && !this.legacyPeerDeps) {
const pm = this.package.peerDependenciesMeta || {}

@@ -350,0 +352,0 @@ const peerDependencies = {}

@@ -259,2 +259,3 @@ // a module that manages a shrinkwrap file (npm-shrinkwrap.json or

load () {
const timer = `shrinkwrap:${this.path}${this.hiddenLockfile ? ':hidden' : ''}`
// we don't need to load package-lock.json except for top of tree nodes,

@@ -303,8 +304,5 @@ // only npm-shrinkwrap.json.

this[_fixDependencies](pkg)
return this
})
}
return this
})
}).then(() => this)
}

@@ -311,0 +309,0 @@

{
"name": "@npmcli/arborist",
"version": "0.0.0-pre.14",
"version": "0.0.0-pre.15",
"description": "Manage node_modules trees",

@@ -8,3 +8,3 @@ "dependencies": {

"@npmcli/name-from-folder": "^1.0.1",
"@npmcli/run-script": "^1.2.1",
"@npmcli/run-script": "^1.3.1",
"bin-links": "^2.1.2",

@@ -15,3 +15,4 @@ "json-stringify-nice": "^1.1.1",

"npm-package-arg": "^8.0.0",
"pacote": "^11.1.0",
"npm-pick-manifest": "^6.1.0",
"pacote": "^11.1.6",
"parse-conflict-json": "^1.0.0",

@@ -29,3 +30,3 @@ "promise-all-reject-late": "^1.0.0",

"require-inject": "^1.4.4",
"tap": "^14.10.6",
"tap": "^14.10.7",
"tcompare": "^3.0.4"

@@ -32,0 +33,0 @@ },

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