summaryrefslogtreecommitdiff
path: root/deps/npm/lib
diff options
context:
space:
mode:
authorKat Marchán <kzm@zkat.tech>2019-01-29 14:43:00 -0800
committerMyles Borins <mylesborins@google.com>2019-02-12 00:06:29 -0800
commit43dd49c9782848c25e5b03448c8a0f923f13c158 (patch)
treef7ac5d645019b2b844f26be66c291bbae734d097 /deps/npm/lib
parentb361f9577fbd72e518438d3fa0b01f7d34d814a5 (diff)
downloadnode-new-43dd49c9782848c25e5b03448c8a0f923f13c158.tar.gz
deps: upgrade npm to 6.7.0
PR-URL: https://github.com/nodejs/node/pull/25804 Reviewed-By: Myles Borins <myles.borins@gmail.com>
Diffstat (limited to 'deps/npm/lib')
-rw-r--r--deps/npm/lib/access.js216
-rw-r--r--deps/npm/lib/audit.js65
-rw-r--r--deps/npm/lib/auth/legacy.js72
-rw-r--r--deps/npm/lib/auth/sso.js103
-rw-r--r--deps/npm/lib/cache.js6
-rw-r--r--deps/npm/lib/ci.js35
-rw-r--r--deps/npm/lib/config/cmd-list.js5
-rw-r--r--deps/npm/lib/config/defaults.js2
-rw-r--r--deps/npm/lib/config/figgy-config.js87
-rw-r--r--deps/npm/lib/config/pacote.js141
-rw-r--r--deps/npm/lib/config/reg-client.js29
-rw-r--r--deps/npm/lib/deprecate.js101
-rw-r--r--deps/npm/lib/dist-tag.js208
-rw-r--r--deps/npm/lib/doctor/check-ping.js8
-rw-r--r--deps/npm/lib/fetch-package-metadata.js16
-rw-r--r--deps/npm/lib/hook.js203
-rw-r--r--deps/npm/lib/install/action/extract-worker.js10
-rw-r--r--deps/npm/lib/install/action/extract.js43
-rw-r--r--deps/npm/lib/install/action/fetch.js4
-rw-r--r--deps/npm/lib/install/audit.js141
-rw-r--r--deps/npm/lib/install/is-only-dev.js1
-rw-r--r--deps/npm/lib/install/is-only-optional.js1
-rw-r--r--deps/npm/lib/logout.js51
-rw-r--r--deps/npm/lib/npm.js16
-rw-r--r--deps/npm/lib/org.js151
-rw-r--r--deps/npm/lib/outdated.js90
-rw-r--r--deps/npm/lib/owner.js340
-rw-r--r--deps/npm/lib/pack.js6
-rw-r--r--deps/npm/lib/ping.js46
-rw-r--r--deps/npm/lib/profile.js125
-rw-r--r--deps/npm/lib/publish.js188
-rw-r--r--deps/npm/lib/repo.js20
-rw-r--r--deps/npm/lib/search.js70
-rw-r--r--deps/npm/lib/search/all-package-metadata.js290
-rw-r--r--deps/npm/lib/search/all-package-search.js2
-rw-r--r--deps/npm/lib/search/esearch.js64
-rw-r--r--deps/npm/lib/shrinkwrap.js2
-rw-r--r--deps/npm/lib/star.js82
-rw-r--r--deps/npm/lib/stars.js66
-rw-r--r--deps/npm/lib/team.js157
-rw-r--r--deps/npm/lib/token.js2
-rw-r--r--deps/npm/lib/unpublish.js201
-rw-r--r--deps/npm/lib/utils/error-handler.js2
-rw-r--r--deps/npm/lib/utils/error-message.js3
-rw-r--r--deps/npm/lib/utils/get-publish-config.js29
-rw-r--r--deps/npm/lib/utils/map-to-registry.js103
-rw-r--r--deps/npm/lib/utils/metrics.js34
-rw-r--r--deps/npm/lib/utils/otplease.js27
-rw-r--r--deps/npm/lib/view.js266
-rw-r--r--deps/npm/lib/whoami.js80
50 files changed, 2083 insertions, 1927 deletions
diff --git a/deps/npm/lib/access.js b/deps/npm/lib/access.js
index 164ea3b7d7..4bb93fda1d 100644
--- a/deps/npm/lib/access.js
+++ b/deps/npm/lib/access.js
@@ -1,28 +1,50 @@
'use strict'
/* eslint-disable standard/no-callback-literal */
-var resolve = require('path').resolve
+const BB = require('bluebird')
-var readPackageJson = require('read-package-json')
-var mapToRegistry = require('./utils/map-to-registry.js')
-var npm = require('./npm.js')
-var output = require('./utils/output.js')
-
-var whoami = require('./whoami')
+const figgyPudding = require('figgy-pudding')
+const libaccess = require('libnpm/access')
+const npmConfig = require('./config/figgy-config.js')
+const output = require('./utils/output.js')
+const otplease = require('./utils/otplease.js')
+const path = require('path')
+const prefix = require('./npm.js').prefix
+const readPackageJson = BB.promisify(require('read-package-json'))
+const usage = require('./utils/usage.js')
+const whoami = require('./whoami.js')
module.exports = access
-access.usage =
+access.usage = usage(
+ 'npm access',
'npm access public [<package>]\n' +
'npm access restricted [<package>]\n' +
'npm access grant <read-only|read-write> <scope:team> [<package>]\n' +
'npm access revoke <scope:team> [<package>]\n' +
+ 'npm access 2fa-required [<package>]\n' +
+ 'npm access 2fa-not-required [<package>]\n' +
'npm access ls-packages [<user>|<scope>|<scope:team>]\n' +
'npm access ls-collaborators [<package> [<user>]]\n' +
'npm access edit [<package>]'
+)
+
+access.subcommands = [
+ 'public', 'restricted', 'grant', 'revoke',
+ 'ls-packages', 'ls-collaborators', 'edit',
+ '2fa-required', '2fa-not-required'
+]
+
+const AccessConfig = figgyPudding({
+ json: {}
+})
-access.subcommands = ['public', 'restricted', 'grant', 'revoke',
- 'ls-packages', 'ls-collaborators', 'edit']
+function UsageError (msg = '') {
+ throw Object.assign(new Error(
+ (msg ? `\nUsage: ${msg}\n\n` : '') +
+ access.usage
+ ), {code: 'EUSAGE'})
+}
access.completion = function (opts, cb) {
var argv = opts.conf.argv.remain
@@ -42,6 +64,8 @@ access.completion = function (opts, cb) {
case 'ls-packages':
case 'ls-collaborators':
case 'edit':
+ case '2fa-required':
+ case '2fa-not-required':
return cb(null, [])
case 'revoke':
return cb(null, [])
@@ -50,81 +74,125 @@ access.completion = function (opts, cb) {
}
}
-function access (args, cb) {
- var cmd = args.shift()
- var params
- return parseParams(cmd, args, function (err, p) {
- if (err) { return cb(err) }
- params = p
- return mapToRegistry(params.package, npm.config, invokeCmd)
- })
+function access ([cmd, ...args], cb) {
+ return BB.try(() => {
+ const fn = access.subcommands.includes(cmd) && access[cmd]
+ if (!cmd) { UsageError('Subcommand is required.') }
+ if (!fn) { UsageError(`${cmd} is not a recognized subcommand.`) }
- function invokeCmd (err, uri, auth, base) {
- if (err) { return cb(err) }
- params.auth = auth
- try {
- return npm.registry.access(cmd, uri, params, function (err, data) {
- if (!err && data) {
- output(JSON.stringify(data, undefined, 2))
- }
- cb(err, data)
- })
- } catch (e) {
- cb(e.message + '\n\nUsage:\n' + access.usage)
- }
- }
+ return fn(args, AccessConfig(npmConfig()))
+ }).then(
+ x => cb(null, x),
+ err => err.code === 'EUSAGE' ? cb(err.message) : cb(err)
+ )
}
-function parseParams (cmd, args, cb) {
- // mapToRegistry will complain if package is undefined,
- // but it's not needed for ls-packages
- var params = { 'package': '' }
- if (cmd === 'grant') {
- params.permissions = args.shift()
- }
- if (['grant', 'revoke', 'ls-packages'].indexOf(cmd) !== -1) {
- var entity = (args.shift() || '').split(':')
- params.scope = entity[0]
- params.team = entity[1]
- }
+access.public = ([pkg], opts) => {
+ return modifyPackage(pkg, opts, libaccess.public)
+}
- if (cmd === 'ls-packages') {
- if (!params.scope) {
- whoami([], true, function (err, scope) {
- params.scope = scope
- cb(err, params)
- })
- } else {
- cb(null, params)
+access.restricted = ([pkg], opts) => {
+ return modifyPackage(pkg, opts, libaccess.restricted)
+}
+
+access.grant = ([perms, scopeteam, pkg], opts) => {
+ return BB.try(() => {
+ if (!perms || (perms !== 'read-only' && perms !== 'read-write')) {
+ UsageError('First argument must be either `read-only` or `read-write.`')
}
- } else {
- getPackage(args.shift(), function (err, pkg) {
- if (err) return cb(err)
- params.package = pkg
+ if (!scopeteam) {
+ UsageError('`<scope:team>` argument is required.')
+ }
+ const [, scope, team] = scopeteam.match(/^@?([^:]+):(.*)$/) || []
+ if (!scope && !team) {
+ UsageError(
+ 'Second argument used incorrect format.\n' +
+ 'Example: @example:developers'
+ )
+ }
+ return modifyPackage(pkg, opts, (pkgName, opts) => {
+ return libaccess.grant(pkgName, scopeteam, perms, opts)
+ })
+ })
+}
- if (cmd === 'ls-collaborators') params.user = args.shift()
- cb(null, params)
+access.revoke = ([scopeteam, pkg], opts) => {
+ return BB.try(() => {
+ if (!scopeteam) {
+ UsageError('`<scope:team>` argument is required.')
+ }
+ const [, scope, team] = scopeteam.match(/^@?([^:]+):(.*)$/) || []
+ if (!scope || !team) {
+ UsageError(
+ 'First argument used incorrect format.\n' +
+ 'Example: @example:developers'
+ )
+ }
+ return modifyPackage(pkg, opts, (pkgName, opts) => {
+ return libaccess.revoke(pkgName, scopeteam, opts)
})
- }
+ })
+}
+
+access['2fa-required'] = access.tfaRequired = ([pkg], opts) => {
+ return modifyPackage(pkg, opts, libaccess.tfaRequired, false)
+}
+
+access['2fa-not-required'] = access.tfaNotRequired = ([pkg], opts) => {
+ return modifyPackage(pkg, opts, libaccess.tfaNotRequired, false)
+}
+
+access['ls-packages'] = access.lsPackages = ([owner], opts) => {
+ return (
+ owner ? BB.resolve(owner) : BB.fromNode(cb => whoami([], true, cb))
+ ).then(owner => {
+ return libaccess.lsPackages(owner, opts)
+ }).then(pkgs => {
+ // TODO - print these out nicely (breaking change)
+ output(JSON.stringify(pkgs, null, 2))
+ })
+}
+
+access['ls-collaborators'] = access.lsCollaborators = ([pkg, usr], opts) => {
+ return getPackage(pkg).then(pkgName =>
+ libaccess.lsCollaborators(pkgName, usr, opts)
+ ).then(collabs => {
+ // TODO - print these out nicely (breaking change)
+ output(JSON.stringify(collabs, null, 2))
+ })
}
-function getPackage (name, cb) {
- if (name && name.trim()) {
- cb(null, name.trim())
- } else {
- readPackageJson(
- resolve(npm.prefix, 'package.json'),
- function (err, data) {
- if (err) {
+access['edit'] = () => BB.reject(new Error('edit subcommand is not implemented yet'))
+
+function modifyPackage (pkg, opts, fn, requireScope = true) {
+ return getPackage(pkg, requireScope).then(pkgName =>
+ otplease(opts, opts => fn(pkgName, opts))
+ )
+}
+
+function getPackage (name, requireScope = true) {
+ return BB.try(() => {
+ if (name && name.trim()) {
+ return name.trim()
+ } else {
+ return readPackageJson(
+ path.resolve(prefix, 'package.json')
+ ).then(
+ data => data.name,
+ err => {
if (err.code === 'ENOENT') {
- cb(new Error('no package name passed to command and no package.json found'))
+ throw new Error('no package name passed to command and no package.json found')
} else {
- cb(err)
+ throw err
}
- } else {
- cb(null, data.name)
}
- }
- )
- }
+ )
+ }
+ }).then(name => {
+ if (requireScope && !name.match(/^@[^/]+\/.*$/)) {
+ UsageError('This command is only available for scoped packages.')
+ } else {
+ return name
+ }
+ })
}
diff --git a/deps/npm/lib/audit.js b/deps/npm/lib/audit.js
index 06852610e6..2cabef9d27 100644
--- a/deps/npm/lib/audit.js
+++ b/deps/npm/lib/audit.js
@@ -3,17 +3,37 @@
const Bluebird = require('bluebird')
const audit = require('./install/audit.js')
+const figgyPudding = require('figgy-pudding')
const fs = require('graceful-fs')
const Installer = require('./install.js').Installer
const lockVerify = require('lock-verify')
const log = require('npmlog')
-const npa = require('npm-package-arg')
+const npa = require('libnpm/parse-arg')
const npm = require('./npm.js')
+const npmConfig = require('./config/figgy-config.js')
const output = require('./utils/output.js')
const parseJson = require('json-parse-better-errors')
const readFile = Bluebird.promisify(fs.readFile)
+const AuditConfig = figgyPudding({
+ also: {},
+ 'audit-level': {},
+ deepArgs: 'deep-args',
+ 'deep-args': {},
+ dev: {},
+ force: {},
+ 'dry-run': {},
+ global: {},
+ json: {},
+ only: {},
+ parseable: {},
+ prod: {},
+ production: {},
+ registry: {},
+ runId: {}
+})
+
module.exports = auditCmd
const usage = require('./utils/usage')
@@ -110,12 +130,12 @@ function maybeReadFile (name) {
})
}
-function filterEnv (action) {
- const includeDev = npm.config.get('dev') ||
- (!/^prod(uction)?$/.test(npm.config.get('only')) && !npm.config.get('production')) ||
- /^dev(elopment)?$/.test(npm.config.get('only')) ||
- /^dev(elopment)?$/.test(npm.config.get('also'))
- const includeProd = !/^dev(elopment)?$/.test(npm.config.get('only'))
+function filterEnv (action, opts) {
+ const includeDev = opts.dev ||
+ (!/^prod(uction)?$/.test(opts.only) && !opts.production) ||
+ /^dev(elopment)?$/.test(opts.only) ||
+ /^dev(elopment)?$/.test(opts.also)
+ const includeProd = !/^dev(elopment)?$/.test(opts.only)
const resolves = action.resolves.filter(({dev}) => {
return (dev && includeDev) || (!dev && includeProd)
})
@@ -125,7 +145,8 @@ function filterEnv (action) {
}
function auditCmd (args, cb) {
- if (npm.config.get('global')) {
+ const opts = AuditConfig(npmConfig())
+ if (opts.global) {
const err = new Error('`npm audit` does not support testing globals')
err.code = 'EAUDITGLOBAL'
throw err
@@ -168,8 +189,16 @@ function auditCmd (args, cb) {
}).then((auditReport) => {
return audit.submitForFullReport(auditReport)
}).catch((err) => {
- if (err.statusCode === 404 || err.statusCode >= 500) {
- const ne = new Error(`Your configured registry (${npm.config.get('registry')}) does not support audit requests.`)
+ if (err.statusCode >= 400) {
+ let msg
+ if (err.statusCode === 401) {
+ msg = `Either your login credentials are invalid or your registry (${opts.registry}) does not support audit.`
+ } else if (err.statusCode === 404) {
+ msg = `Your configured registry (${opts.registry}) does not support audit requests.`
+ } else {
+ msg = `Your configured registry (${opts.registry}) does not support audit requests, or the audit endpoint is temporarily unavailable.`
+ }
+ const ne = new Error(msg)
ne.code = 'ENOAUDIT'
ne.wrapped = err
throw ne
@@ -178,7 +207,7 @@ function auditCmd (args, cb) {
}).then((auditResult) => {
if (args[0] === 'fix') {
const actions = (auditResult.actions || []).reduce((acc, action) => {
- action = filterEnv(action)
+ action = filterEnv(action, opts)
if (!action) { return acc }
if (action.isMajor) {
acc.major.add(`${action.module}@${action.target}`)
@@ -215,7 +244,7 @@ function auditCmd (args, cb) {
review: new Set()
})
return Bluebird.try(() => {
- const installMajor = npm.config.get('force')
+ const installMajor = opts.force
const installCount = actions.install.size + (installMajor ? actions.major.size : 0) + actions.update.size
const vulnFixCount = new Set([...actions.installFixes, ...actions.updateFixes, ...(installMajor ? actions.majorFixes : [])]).size
const metavuln = auditResult.metadata.vulnerabilities
@@ -230,16 +259,16 @@ function auditCmd (args, cb) {
return Bluebird.fromNode(cb => {
new Auditor(
npm.prefix,
- !!npm.config.get('dry-run'),
+ !!opts['dry-run'],
[...actions.install, ...(installMajor ? actions.major : [])],
- {
+ opts.concat({
runId: auditResult.runId,
deepArgs: [...actions.update].map(u => u.split('>'))
- }
+ }).toJSON()
).run(cb)
}).then(() => {
const numScanned = auditResult.metadata.totalDependencies
- if (!npm.config.get('json') && !npm.config.get('parseable')) {
+ if (!opts.json && !opts.parseable) {
output(`fixed ${vulnFixCount} of ${total} vulnerabilit${total === 1 ? 'y' : 'ies'} in ${numScanned} scanned package${numScanned === 1 ? '' : 's'}`)
if (actions.review.size) {
output(` ${actions.review.size} vulnerabilit${actions.review.size === 1 ? 'y' : 'ies'} required manual review and could not be updated`)
@@ -258,12 +287,12 @@ function auditCmd (args, cb) {
})
} else {
const levels = ['low', 'moderate', 'high', 'critical']
- const minLevel = levels.indexOf(npm.config.get('audit-level'))
+ const minLevel = levels.indexOf(opts['audit-level'])
const vulns = levels.reduce((count, level, i) => {
return i < minLevel ? count : count + (auditResult.metadata.vulnerabilities[level] || 0)
}, 0)
if (vulns > 0) process.exitCode = 1
- if (npm.config.get('parseable')) {
+ if (opts.parseable) {
return audit.printParseableReport(auditResult)
} else {
return audit.printFullReport(auditResult)
diff --git a/deps/npm/lib/auth/legacy.js b/deps/npm/lib/auth/legacy.js
index 8c25df0288..7ad678be5e 100644
--- a/deps/npm/lib/auth/legacy.js
+++ b/deps/npm/lib/auth/legacy.js
@@ -1,11 +1,11 @@
'use strict'
+
const read = require('../utils/read-user-info.js')
-const profile = require('npm-profile')
+const profile = require('libnpm/profile')
const log = require('npmlog')
-const npm = require('../npm.js')
+const figgyPudding = require('figgy-pudding')
+const npmConfig = require('../config/figgy-config.js')
const output = require('../utils/output.js')
-const pacoteOpts = require('../config/pacote')
-const fetchOpts = require('../config/fetch-opts')
const openUrl = require('../utils/open-url')
const openerPromise = (url) => new Promise((resolve, reject) => {
@@ -26,54 +26,54 @@ const loginPrompter = (creds) => {
})
}
-module.exports.login = (creds, registry, scope, cb) => {
- const conf = {
- log: log,
- creds: creds,
- registry: registry,
- auth: {
- otp: npm.config.get('otp')
- },
- scope: scope,
- opts: fetchOpts.fromPacote(pacoteOpts())
- }
- login(conf).then((newCreds) => cb(null, newCreds)).catch(cb)
+const LoginOpts = figgyPudding({
+ 'always-auth': {},
+ creds: {},
+ log: {default: () => log},
+ registry: {},
+ scope: {}
+})
+
+module.exports.login = (creds = {}, registry, scope, cb) => {
+ const opts = LoginOpts(npmConfig()).concat({scope, registry, creds})
+ login(opts).then((newCreds) => cb(null, newCreds)).catch(cb)
}
-function login (conf) {
- return profile.login(openerPromise, loginPrompter, conf)
+function login (opts) {
+ return profile.login(openerPromise, loginPrompter, opts)
.catch((err) => {
if (err.code === 'EOTP') throw err
- const u = conf.creds.username
- const p = conf.creds.password
- const e = conf.creds.email
+ const u = opts.creds.username
+ const p = opts.creds.password
+ const e = opts.creds.email
if (!(u && p && e)) throw err
- return profile.adduserCouch(u, e, p, conf)
+ return profile.adduserCouch(u, e, p, opts)
})
.catch((err) => {
if (err.code !== 'EOTP') throw err
- return read.otp('Enter one-time password from your authenticator app: ').then((otp) => {
- conf.auth.otp = otp
- const u = conf.creds.username
- const p = conf.creds.password
- return profile.loginCouch(u, p, conf)
+ return read.otp(
+ 'Enter one-time password from your authenticator app: '
+ ).then(otp => {
+ const u = opts.creds.username
+ const p = opts.creds.password
+ return profile.loginCouch(u, p, opts.concat({otp}))
})
}).then((result) => {
const newCreds = {}
if (result && result.token) {
newCreds.token = result.token
} else {
- newCreds.username = conf.creds.username
- newCreds.password = conf.creds.password
- newCreds.email = conf.creds.email
- newCreds.alwaysAuth = npm.config.get('always-auth')
+ newCreds.username = opts.creds.username
+ newCreds.password = opts.creds.password
+ newCreds.email = opts.creds.email
+ newCreds.alwaysAuth = opts['always-auth']
}
- const usermsg = conf.creds.username ? ' user ' + conf.creds.username : ''
- conf.log.info('login', 'Authorized' + usermsg)
- const scopeMessage = conf.scope ? ' to scope ' + conf.scope : ''
- const userout = conf.creds.username ? ' as ' + conf.creds.username : ''
- output('Logged in%s%s on %s.', userout, scopeMessage, conf.registry)
+ const usermsg = opts.creds.username ? ' user ' + opts.creds.username : ''
+ opts.log.info('login', 'Authorized' + usermsg)
+ const scopeMessage = opts.scope ? ' to scope ' + opts.scope : ''
+ const userout = opts.creds.username ? ' as ' + opts.creds.username : ''
+ output('Logged in%s%s on %s.', userout, scopeMessage, opts.registry)
return newCreds
})
}
diff --git a/deps/npm/lib/auth/sso.js b/deps/npm/lib/auth/sso.js
index 519ca8496c..099e764e3a 100644
--- a/deps/npm/lib/auth/sso.js
+++ b/deps/npm/lib/auth/sso.js
@@ -1,56 +1,73 @@
-var log = require('npmlog')
-var npm = require('../npm.js')
-var output = require('../utils/output')
-var openUrl = require('../utils/open-url')
+'use strict'
+
+const BB = require('bluebird')
+
+const figgyPudding = require('figgy-pudding')
+const log = require('npmlog')
+const npmConfig = require('../config/figgy-config.js')
+const npmFetch = require('npm-registry-fetch')
+const output = require('../utils/output.js')
+const openUrl = BB.promisify(require('../utils/open-url.js'))
+const otplease = require('../utils/otplease.js')
+const profile = require('libnpm/profile')
+
+const SsoOpts = figgyPudding({
+ ssoType: 'sso-type',
+ 'sso-type': {},
+ ssoPollFrequency: 'sso-poll-frequency',
+ 'sso-poll-frequency': {}
+})
module.exports.login = function login (creds, registry, scope, cb) {
- var ssoType = npm.config.get('sso-type')
+ const opts = SsoOpts(npmConfig()).concat({creds, registry, scope})
+ const ssoType = opts.ssoType
if (!ssoType) { return cb(new Error('Missing option: sso-type')) }
- var params = {
- // We're reusing the legacy login endpoint, so we need some dummy
- // stuff here to pass validation. They're never used.
- auth: {
- username: 'npm_' + ssoType + '_auth_dummy_user',
- password: 'placeholder',
- email: 'support@npmjs.com',
- authType: ssoType
- }
+ // We're reusing the legacy login endpoint, so we need some dummy
+ // stuff here to pass validation. They're never used.
+ const auth = {
+ username: 'npm_' + ssoType + '_auth_dummy_user',
+ password: 'placeholder',
+ email: 'support@npmjs.com',
+ authType: ssoType
}
- npm.registry.adduser(registry, params, function (er, doc) {
- if (er) return cb(er)
- if (!doc || !doc.token) return cb(new Error('no SSO token returned'))
- if (!doc.sso) return cb(new Error('no SSO URL returned by services'))
-
- openUrl(doc.sso, 'to complete your login please visit', function () {
- pollForSession(registry, doc.token, function (err, username) {
- if (err) return cb(err)
- log.info('adduser', 'Authorized user %s', username)
- var scopeMessage = scope ? ' to scope ' + scope : ''
- output('Logged in as %s%s on %s.', username, scopeMessage, registry)
-
- cb(null, { token: doc.token })
- })
+ otplease(opts,
+ opts => profile.loginCouch(auth.username, auth.password, opts)
+ ).then(({token, sso}) => {
+ if (!token) { throw new Error('no SSO token returned') }
+ if (!sso) { throw new Error('no SSO URL returned by services') }
+ return openUrl(sso, 'to complete your login please visit').then(() => {
+ return pollForSession(registry, token, opts)
+ }).then(username => {
+ log.info('adduser', 'Authorized user %s', username)
+ var scopeMessage = scope ? ' to scope ' + scope : ''
+ output('Logged in as %s%s on %s.', username, scopeMessage, registry)
+ return {token}
})
- })
+ }).nodeify(cb)
}
-function pollForSession (registry, token, cb) {
+function pollForSession (registry, token, opts) {
log.info('adduser', 'Polling for validated SSO session')
- npm.registry.whoami(registry, {
- auth: {
- token: token
- }
- }, function (er, username) {
- if (er && er.statusCode !== 401) {
- cb(er)
- } else if (!username) {
- setTimeout(function () {
- pollForSession(registry, token, cb)
- }, npm.config.get('sso-poll-frequency'))
- } else {
- cb(null, username)
+ return npmFetch.json(
+ '/-/whoami', opts.concat({registry, forceAuth: {token}})
+ ).then(
+ ({username}) => username,
+ err => {
+ if (err.code === 'E401') {
+ return sleep(opts['sso-poll-frequency']).then(() => {
+ return pollForSession(registry, token, opts)
+ })
+ } else {
+ throw err
+ }
}
+ )
+}
+
+function sleep (time) {
+ return new BB((resolve) => {
+ setTimeout(resolve, time)
})
}
diff --git a/deps/npm/lib/cache.js b/deps/npm/lib/cache.js
index 169f192cad..00abd8c746 100644
--- a/deps/npm/lib/cache.js
+++ b/deps/npm/lib/cache.js
@@ -9,9 +9,9 @@ const finished = BB.promisify(require('mississippi').finished)
const log = require('npmlog')
const npa = require('npm-package-arg')
const npm = require('./npm.js')
+const npmConfig = require('./config/figgy-config.js')
const output = require('./utils/output.js')
const pacote = require('pacote')
-const pacoteOpts = require('./config/pacote')
const path = require('path')
const rm = BB.promisify(require('./utils/gently-rm.js'))
const unbuild = BB.promisify(npm.commands.unbuild)
@@ -107,7 +107,7 @@ function add (args, where) {
log.verbose('cache add', 'spec', spec)
if (!spec) return BB.reject(new Error(usage))
log.silly('cache add', 'parsed spec', spec)
- return finished(pacote.tarball.stream(spec, pacoteOpts({where})).resume())
+ return finished(pacote.tarball.stream(spec, npmConfig({where})).resume())
}
cache.verify = verify
@@ -131,7 +131,7 @@ function verify () {
cache.unpack = unpack
function unpack (pkg, ver, unpackTarget, dmode, fmode, uid, gid) {
return unbuild([unpackTarget], true).then(() => {
- const opts = pacoteOpts({dmode, fmode, uid, gid, offline: true})
+ const opts = npmConfig({dmode, fmode, uid, gid, offline: true})
return pacote.extract(npa.resolve(pkg, ver), unpackTarget, opts)
})
}
diff --git a/deps/npm/lib/ci.js b/deps/npm/lib/ci.js
index 03822b9528..1fbb28b570 100644
--- a/deps/npm/lib/ci.js
+++ b/deps/npm/lib/ci.js
@@ -1,40 +1,19 @@
'use strict'
const Installer = require('libcipm')
-const lifecycleOpts = require('./config/lifecycle.js')
-const npm = require('./npm.js')
+const npmConfig = require('./config/figgy-config.js')
const npmlog = require('npmlog')
-const pacoteOpts = require('./config/pacote.js')
ci.usage = 'npm ci'
ci.completion = (cb) => cb(null, [])
-Installer.CipmConfig.impl(npm.config, {
- get: npm.config.get,
- set: npm.config.set,
- toLifecycle (moreOpts) {
- return lifecycleOpts(moreOpts)
- },
- toPacote (moreOpts) {
- return pacoteOpts(moreOpts)
- }
-})
-
module.exports = ci
function ci (args, cb) {
- return new Installer({
- config: npm.config,
- log: npmlog
- })
- .run()
- .then(
- (details) => {
- npmlog.disableProgress()
- console.log(`added ${details.pkgCount} packages in ${
- details.runTime / 1000
- }s`)
- }
- )
- .then(() => cb(), cb)
+ return new Installer(npmConfig({ log: npmlog })).run().then(details => {
+ npmlog.disableProgress()
+ console.log(`added ${details.pkgCount} packages in ${
+ details.runTime / 1000
+ }s`)
+ }).then(() => cb(), cb)
}
diff --git a/deps/npm/lib/config/cmd-list.js b/deps/npm/lib/config/cmd-list.js
index a453082adc..fa4390fcdc 100644
--- a/deps/npm/lib/config/cmd-list.js
+++ b/deps/npm/lib/config/cmd-list.js
@@ -50,7 +50,9 @@ var affordances = {
'rm': 'uninstall',
'r': 'uninstall',
'rum': 'run-script',
- 'sit': 'cit'
+ 'sit': 'cit',
+ 'urn': 'run-script',
+ 'ogr': 'org'
}
// these are filenames in .
@@ -89,6 +91,7 @@ var cmdList = [
'token',
'profile',
'audit',
+ 'org',
'help',
'help-search',
diff --git a/deps/npm/lib/config/defaults.js b/deps/npm/lib/config/defaults.js
index 991a2129f6..2592659539 100644
--- a/deps/npm/lib/config/defaults.js
+++ b/deps/npm/lib/config/defaults.js
@@ -239,7 +239,7 @@ Object.defineProperty(exports, 'defaults', {get: function () {
process.getuid() !== 0,
'update-notifier': true,
usage: false,
- user: process.platform === 'win32' ? 0 : 'nobody',
+ user: (process.platform === 'win32' || os.type() === 'OS400') ? 0 : 'nobody',
userconfig: path.resolve(home, '.npmrc'),
umask: process.umask ? process.umask() : umask.fromString('022'),
version: false,
diff --git a/deps/npm/lib/config/figgy-config.js b/deps/npm/lib/config/figgy-config.js
new file mode 100644
index 0000000000..9e9ca0ba56
--- /dev/null
+++ b/deps/npm/lib/config/figgy-config.js
@@ -0,0 +1,87 @@
+'use strict'
+
+const BB = require('bluebird')
+
+const crypto = require('crypto')
+const figgyPudding = require('figgy-pudding')
+const log = require('npmlog')
+const npm = require('../npm.js')
+const pack = require('../pack.js')
+const path = require('path')
+
+const npmSession = crypto.randomBytes(8).toString('hex')
+log.verbose('npm-session', npmSession)
+
+const SCOPE_REGISTRY_REGEX = /@.*:registry$/gi
+const NpmConfig = figgyPudding({}, {
+ other (key) {
+ return key.match(SCOPE_REGISTRY_REGEX)
+ }
+})
+
+let baseConfig
+
+module.exports = mkConfig
+function mkConfig (...providers) {
+ if (!baseConfig) {
+ baseConfig = NpmConfig(npm.config, {
+ // Add some non-npm-config opts by hand.
+ cache: path.join(npm.config.get('cache'), '_cacache'),
+ // NOTE: npm has some magic logic around color distinct from the config
+ // value, so we have to override it here
+ color: !!npm.color,
+ dirPacker: pack.packGitDep,
+ hashAlgorithm: 'sha1',
+ includeDeprecated: false,
+ log,
+ 'npm-session': npmSession,
+ 'project-scope': npm.projectScope,
+ refer: npm.referer,
+ dmode: npm.modes.exec,
+ fmode: npm.modes.file,
+ umask: npm.modes.umask,
+ npmVersion: npm.version,
+ tmp: npm.tmp,
+ Promise: BB
+ })
+ const ownerStats = calculateOwner()
+ if (ownerStats.uid != null || ownerStats.gid != null) {
+ baseConfig = baseConfig.concat(ownerStats)
+ }
+ }
+ let conf = baseConfig.concat(...providers)
+ // Adapt some other configs if missing
+ if (npm.config.get('prefer-online') === undefined) {
+ conf = conf.concat({
+ 'prefer-online': npm.config.get('cache-max') <= 0
+ })
+ }
+ if (npm.config.get('prefer-online') === undefined) {
+ conf = conf.concat({
+ 'prefer-online': npm.config.get('cache-min') >= 9999
+ })
+ }
+ return conf
+}
+
+let effectiveOwner
+function calculateOwner () {
+ if (!effectiveOwner) {
+ effectiveOwner = { uid: 0, gid: 0 }
+
+ // Pretty much only on windows
+ if (!process.getuid) {
+ return effectiveOwner
+ }
+
+ effectiveOwner.uid = +process.getuid()
+ effectiveOwner.gid = +process.getgid()
+
+ if (effectiveOwner.uid === 0) {
+ if (process.env.SUDO_UID) effectiveOwner.uid = +process.env.SUDO_UID
+ if (process.env.SUDO_GID) effectiveOwner.gid = +process.env.SUDO_GID
+ }
+ }
+
+ return effectiveOwner
+}
diff --git a/deps/npm/lib/config/pacote.js b/deps/npm/lib/config/pacote.js
deleted file mode 100644
index 505b69da37..0000000000
--- a/deps/npm/lib/config/pacote.js
+++ /dev/null
@@ -1,141 +0,0 @@
-'use strict'
-
-const Buffer = require('safe-buffer').Buffer
-
-const crypto = require('crypto')
-const npm = require('../npm')
-const log = require('npmlog')
-let pack
-const path = require('path')
-
-let effectiveOwner
-
-const npmSession = crypto.randomBytes(8).toString('hex')
-log.verbose('npm-session', npmSession)
-
-module.exports = pacoteOpts
-function pacoteOpts (moreOpts) {
- if (!pack) {
- pack = require('../pack.js')
- }
- const ownerStats = calculateOwner()
- const opts = {
- cache: path.join(npm.config.get('cache'), '_cacache'),
- ca: npm.config.get('ca'),
- cert: npm.config.get('cert'),
- defaultTag: npm.config.get('tag'),
- dirPacker: pack.packGitDep,
- hashAlgorithm: 'sha1',
- includeDeprecated: false,
- key: npm.config.get('key'),
- localAddress: npm.config.get('local-address'),
- log: log,
- maxAge: npm.config.get('cache-min'),
- maxSockets: npm.config.get('maxsockets'),
- npmSession: npmSession,
- offline: npm.config.get('offline'),
- preferOffline: npm.config.get('prefer-offline') || npm.config.get('cache-min') > 9999,
- preferOnline: npm.config.get('prefer-online') || npm.config.get('cache-max') <= 0,
- projectScope: npm.projectScope,
- proxy: npm.config.get('https-proxy') || npm.config.get('proxy'),
- noProxy: npm.config.get('noproxy'),
- refer: npm.registry.refer,
- registry: npm.config.get('registry'),
- retry: {
- retries: npm.config.get('fetch-retries'),
- factor: npm.config.get('fetch-retry-factor'),
- minTimeout: npm.config.get('fetch-retry-mintimeout'),
- maxTimeout: npm.config.get('fetch-retry-maxtimeout')
- },
- scope: npm.config.get('scope'),
- strictSSL: npm.config.get('strict-ssl'),
- userAgent: npm.config.get('user-agent'),
-
- dmode: npm.modes.exec,
- fmode: npm.modes.file,
- umask: npm.modes.umask
- }
-
- if (ownerStats.uid != null || ownerStats.gid != null) {
- Object.assign(opts, ownerStats)
- }
-
- npm.config.keys.forEach(function (k) {
- const authMatchGlobal = k.match(
- /^(_authToken|username|_password|password|email|always-auth|_auth)$/
- )
- const authMatchScoped = k[0] === '/' && k.match(
- /(.*):(_authToken|username|_password|password|email|always-auth|_auth)$/
- )
-
- // if it matches scoped it will also match global
- if (authMatchGlobal || authMatchScoped) {
- let nerfDart = null
- let key = null
- let val = null
-
- if (!opts.auth) { opts.auth = {} }
-
- if (authMatchScoped) {
- nerfDart = authMatchScoped[1]
- key = authMatchScoped[2]
- val = npm.config.get(k)
- if (!opts.auth[nerfDart]) {
- opts.auth[nerfDart] = {
- alwaysAuth: !!npm.config.get('always-auth')
- }
- }
- } else {
- key = authMatchGlobal[1]
- val = npm.config.get(k)
- opts.auth.alwaysAuth = !!npm.config.get('always-auth')
- }
-
- const auth = authMatchScoped ? opts.auth[nerfDart] : opts.auth
- if (key === '_authToken') {
- auth.token = val
- } else if (key.match(/password$/i)) {
- auth.password =
- // the config file stores password auth already-encoded. pacote expects
- // the actual username/password pair.
- Buffer.from(val, 'base64').toString('utf8')
- } else if (key === 'always-auth') {
- auth.alwaysAuth = val === 'false' ? false : !!val
- } else {
- auth[key] = val
- }
- }
-
- if (k[0] === '@') {
- if (!opts.scopeTargets) { opts.scopeTargets = {} }
- opts.scopeTargets[k.replace(/:registry$/, '')] = npm.config.get(k)
- }
- })
-
- Object.keys(moreOpts || {}).forEach((k) => {
- opts[k] = moreOpts[k]
- })
-
- return opts
-}
-
-function calculateOwner () {
- if (!effectiveOwner) {
- effectiveOwner = { uid: 0, gid: 0 }
-
- // Pretty much only on windows
- if (!process.getuid) {
- return effectiveOwner
- }
-
- effectiveOwner.uid = +process.getuid()
- effectiveOwner.gid = +process.getgid()
-
- if (effectiveOwner.uid === 0) {
- if (process.env.SUDO_UID) effectiveOwner.uid = +process.env.SUDO_UID
- if (process.env.SUDO_GID) effectiveOwner.gid = +process.env.SUDO_GID
- }
- }
-
- return effectiveOwner
-}
diff --git a/deps/npm/lib/config/reg-client.js b/deps/npm/lib/config/reg-client.js
deleted file mode 100644
index d4e2417097..0000000000
--- a/deps/npm/lib/config/reg-client.js
+++ /dev/null
@@ -1,29 +0,0 @@
-'use strict'
-
-module.exports = regClientConfig
-function regClientConfig (npm, log, config) {
- return {
- proxy: {
- http: config.get('proxy'),
- https: config.get('https-proxy'),
- localAddress: config.get('local-address')
- },
- ssl: {
- certificate: config.get('cert'),
- key: config.get('key'),
- ca: config.get('ca'),
- strict: config.get('strict-ssl')
- },
- retry: {
- retries: config.get('fetch-retries'),
- factor: config.get('fetch-retry-factor'),
- minTimeout: config.get('fetch-retry-mintimeout'),
- maxTimeout: config.get('fetch-retry-maxtimeout')
- },
- userAgent: config.get('user-agent'),
- log: log,
- defaultTag: config.get('tag'),
- maxSockets: config.get('maxsockets'),
- scope: npm.projectScope
- }
-}
diff --git a/deps/npm/lib/deprecate.js b/deps/npm/lib/deprecate.js
index 9b71d1de49..7fe2fbed4b 100644
--- a/deps/npm/lib/deprecate.js
+++ b/deps/npm/lib/deprecate.js
@@ -1,55 +1,72 @@
-/* eslint-disable standard/no-callback-literal */
-var npm = require('./npm.js')
-var mapToRegistry = require('./utils/map-to-registry.js')
-var npa = require('npm-package-arg')
+'use strict'
+
+const BB = require('bluebird')
+
+const npmConfig = require('./config/figgy-config.js')
+const fetch = require('libnpm/fetch')
+const figgyPudding = require('figgy-pudding')
+const otplease = require('./utils/otplease.js')
+const npa = require('libnpm/parse-arg')
+const semver = require('semver')
+const whoami = require('./whoami.js')
+
+const DeprecateConfig = figgyPudding({})
module.exports = deprecate
deprecate.usage = 'npm deprecate <pkg>[@<version>] <message>'
deprecate.completion = function (opts, cb) {
- // first, get a list of remote packages this user owns.
- // once we have a user account, then don't complete anything.
- if (opts.conf.argv.remain.length > 2) return cb()
- // get the list of packages by user
- var path = '/-/by-user/'
- mapToRegistry(path, npm.config, function (er, uri, c) {
- if (er) return cb(er)
-
- if (!(c && c.username)) return cb()
-
- var params = {
- timeout: 60000,
- auth: c
- }
- npm.registry.get(uri + c.username, params, function (er, list) {
- if (er) return cb()
- console.error(list)
- return cb(null, list[c.username])
+ return BB.try(() => {
+ if (opts.conf.argv.remain.length > 2) { return }
+ return whoami([], true, () => {}).then(username => {
+ if (username) {
+ // first, get a list of remote packages this user owns.
+ // once we have a user account, then don't complete anything.
+ // get the list of packages by user
+ return fetch(
+ `/-/by-user/${encodeURIComponent(username)}`,
+ DeprecateConfig()
+ ).then(list => list[username])
+ }
})
- })
+ }).nodeify(cb)
}
-function deprecate (args, cb) {
- var pkg = args[0]
- var msg = args[1]
- if (msg === undefined) return cb('Usage: ' + deprecate.usage)
+function deprecate ([pkg, msg], opts, cb) {
+ if (typeof cb !== 'function') {
+ cb = opts
+ opts = null
+ }
+ opts = DeprecateConfig(opts || npmConfig())
+ return BB.try(() => {
+ if (msg == null) throw new Error(`Usage: ${deprecate.usage}`)
+ // fetch the data and make sure it exists.
+ const p = npa(pkg)
- // fetch the data and make sure it exists.
- var p = npa(pkg)
+ // npa makes the default spec "latest", but for deprecation
+ // "*" is the appropriate default.
+ const spec = p.rawSpec === '' ? '*' : p.fetchSpec
- // npa makes the default spec "latest", but for deprecation
- // "*" is the appropriate default.
- var spec = p.rawSpec === '' ? '*' : p.fetchSpec
-
- mapToRegistry(p.name, npm.config, function (er, uri, auth) {
- if (er) return cb(er)
-
- var params = {
- version: spec,
- message: msg,
- auth: auth
+ if (semver.validRange(spec, true) === null) {
+ throw new Error('invalid version range: ' + spec)
}
- npm.registry.deprecate(uri, params, cb)
- })
+
+ const uri = '/' + p.escapedName
+ return fetch.json(uri, opts.concat({
+ spec: p,
+ query: {write: true}
+ })).then(packument => {
+ // filter all the versions that match
+ Object.keys(packument.versions)
+ .filter(v => semver.satisfies(v, spec))
+ .forEach(v => { packument.versions[v].deprecated = msg })
+ return otplease(opts, opts => fetch(uri, opts.concat({
+ spec: p,
+ method: 'PUT',
+ body: packument,
+ ignoreBody: true
+ })))
+ })
+ }).nodeify(cb)
}
diff --git a/deps/npm/lib/dist-tag.js b/deps/npm/lib/dist-tag.js
index bd0c5ae8a2..176e61221e 100644
--- a/deps/npm/lib/dist-tag.js
+++ b/deps/npm/lib/dist-tag.js
@@ -1,15 +1,22 @@
/* eslint-disable standard/no-callback-literal */
module.exports = distTag
-var log = require('npmlog')
-var npa = require('npm-package-arg')
-var semver = require('semver')
-
-var npm = require('./npm.js')
-var mapToRegistry = require('./utils/map-to-registry.js')
-var readLocalPkg = require('./utils/read-local-package.js')
-var usage = require('./utils/usage')
-var output = require('./utils/output.js')
+const BB = require('bluebird')
+
+const figgyPudding = require('figgy-pudding')
+const log = require('npmlog')
+const npa = require('libnpm/parse-arg')
+const npmConfig = require('./config/figgy-config.js')
+const output = require('./utils/output.js')
+const otplease = require('./utils/otplease.js')
+const readLocalPkg = BB.promisify(require('./utils/read-local-package.js'))
+const regFetch = require('libnpm/fetch')
+const semver = require('semver')
+const usage = require('./utils/usage')
+
+const DistTagOpts = figgyPudding({
+ tag: {}
+})
distTag.usage = usage(
'dist-tag',
@@ -30,130 +37,127 @@ distTag.completion = function (opts, cb) {
}
}
-function distTag (args, cb) {
- var cmd = args.shift()
- switch (cmd) {
- case 'add': case 'a': case 'set': case 's':
- return add(args[0], args[1], cb)
- case 'rm': case 'r': case 'del': case 'd': case 'remove':
- return remove(args[1], args[0], cb)
- case 'ls': case 'l': case 'sl': case 'list':
- return list(args[0], cb)
- default:
- return cb('Usage:\n' + distTag.usage)
- }
+function UsageError () {
+ throw Object.assign(new Error('Usage:\n' + distTag.usage), {
+ code: 'EUSAGE'
+ })
}
-function add (spec, tag, cb) {
- var thing = npa(spec || '')
- var pkg = thing.name
- var version = thing.rawSpec
- var t = (tag || npm.config.get('tag')).trim()
+function distTag ([cmd, pkg, tag], cb) {
+ const opts = DistTagOpts(npmConfig())
+ return BB.try(() => {
+ switch (cmd) {
+ case 'add': case 'a': case 'set': case 's':
+ return add(pkg, tag, opts)
+ case 'rm': case 'r': case 'del': case 'd': case 'remove':
+ return remove(pkg, tag, opts)
+ case 'ls': case 'l': case 'sl': case 'list':
+ return list(pkg, opts)
+ default:
+ if (!pkg) {
+ return list(cmd, opts)
+ } else {
+ UsageError()
+ }
+ }
+ }).then(
+ x => cb(null, x),
+ err => {
+ if (err.code === 'EUSAGE') {
+ cb(err.message)
+ } else {
+ cb(err)
+ }
+ }
+ )
+}
- log.verbose('dist-tag add', t, 'to', pkg + '@' + version)
+function add (spec, tag, opts) {
+ spec = npa(spec || '')
+ const version = spec.rawSpec
+ const t = (tag || opts.tag).trim()
- if (!pkg || !version || !t) return cb('Usage:\n' + distTag.usage)
+ log.verbose('dist-tag add', t, 'to', spec.name + '@' + version)
+
+ if (!spec || !version || !t) UsageError()
if (semver.validRange(t)) {
- var er = new Error('Tag name must not be a valid SemVer range: ' + t)
- return cb(er)
+ throw new Error('Tag name must not be a valid SemVer range: ' + t)
}
- fetchTags(pkg, function (er, tags) {
- if (er) return cb(er)
-
+ return fetchTags(spec, opts).then(tags => {
if (tags[t] === version) {
log.warn('dist-tag add', t, 'is already set to version', version)
- return cb()
+ return
}
tags[t] = version
-
- mapToRegistry(pkg, npm.config, function (er, uri, auth, base) {
- var params = {
- 'package': pkg,
- distTag: t,
- version: version,
- auth: auth
- }
-
- npm.registry.distTags.add(base, params, function (er) {
- if (er) return cb(er)
-
- output('+' + t + ': ' + pkg + '@' + version)
- cb()
- })
+ const url = `/-/package/${spec.escapedName}/dist-tags/${encodeURIComponent(t)}`
+ const reqOpts = opts.concat({
+ method: 'PUT',
+ body: JSON.stringify(version),
+ headers: {
+ 'content-type': 'application/json'
+ },
+ spec
+ })
+ return otplease(reqOpts, reqOpts => regFetch(url, reqOpts)).then(() => {
+ output(`+${t}: ${spec.name}@${version}`)
})
})
}
-function remove (tag, pkg, cb) {
- log.verbose('dist-tag del', tag, 'from', pkg)
-
- fetchTags(pkg, function (er, tags) {
- if (er) return cb(er)
+function remove (spec, tag, opts) {
+ spec = npa(spec || '')
+ log.verbose('dist-tag del', tag, 'from', spec.name)
+ return fetchTags(spec, opts).then(tags => {
if (!tags[tag]) {
- log.info('dist-tag del', tag, 'is not a dist-tag on', pkg)
- return cb(new Error(tag + ' is not a dist-tag on ' + pkg))
+ log.info('dist-tag del', tag, 'is not a dist-tag on', spec.name)
+ throw new Error(tag + ' is not a dist-tag on ' + spec.name)
}
-
- var version = tags[tag]
+ const version = tags[tag]
delete tags[tag]
-
- mapToRegistry(pkg, npm.config, function (er, uri, auth, base) {
- var params = {
- 'package': pkg,
- distTag: tag,
- auth: auth
- }
-
- npm.registry.distTags.rm(base, params, function (er) {
- if (er) return cb(er)
-
- output('-' + tag + ': ' + pkg + '@' + version)
- cb()
- })
+ const url = `/-/package/${spec.escapedName}/dist-tags/${encodeURIComponent(tag)}`
+ const reqOpts = opts.concat({
+ method: 'DELETE'
+ })
+ return otplease(reqOpts, reqOpts => regFetch(url, reqOpts)).then(() => {
+ output(`-${tag}: ${spec.name}@${version}`)
})
})
}
-function list (pkg, cb) {
- if (!pkg) {
- return readLocalPkg(function (er, pkg) {
- if (er) return cb(er)
- if (!pkg) return cb(distTag.usage)
- list(pkg, cb)
+function list (spec, opts) {
+ if (!spec) {
+ return readLocalPkg().then(pkg => {
+ if (!pkg) { UsageError() }
+ return list(pkg, opts)
})
}
+ spec = npa(spec)
- fetchTags(pkg, function (er, tags) {
- if (er) {
- log.error('dist-tag ls', "Couldn't get dist-tag data for", pkg)
- return cb(er)
- }
- var msg = Object.keys(tags).map(function (k) {
- return k + ': ' + tags[k]
- }).sort().join('\n')
+ return fetchTags(spec, opts).then(tags => {
+ var msg = Object.keys(tags).map(k => `${k}: ${tags[k]}`).sort().join('\n')
output(msg)
- cb(er, tags)
+ return tags
+ }, err => {
+ log.error('dist-tag ls', "Couldn't get dist-tag data for", spec)
+ throw err
})
}
-function fetchTags (pkg, cb) {
- mapToRegistry(pkg, npm.config, function (er, uri, auth, base) {
- if (er) return cb(er)
-
- var params = {
- 'package': pkg,
- auth: auth
- }
- npm.registry.distTags.fetch(base, params, function (er, tags) {
- if (er) return cb(er)
- if (!tags || !Object.keys(tags).length) {
- return cb(new Error('No dist-tags found for ' + pkg))
- }
-
- cb(null, tags)
+function fetchTags (spec, opts) {
+ return regFetch.json(
+ `/-/package/${spec.escapedName}/dist-tags`,
+ opts.concat({
+ 'prefer-online': true,
+ spec
})
+ ).then(data => {
+ if (data && typeof data === 'object') delete data._etag
+ if (!data || !Object.keys(data).length) {
+ throw new Error('No dist-tags found for ' + spec.name)
+ }
+ return data
})
}
diff --git a/deps/npm/lib/doctor/check-ping.js b/deps/npm/lib/doctor/check-ping.js
index e7e82902a7..70db255480 100644
--- a/deps/npm/lib/doctor/check-ping.js
+++ b/deps/npm/lib/doctor/check-ping.js
@@ -4,8 +4,12 @@ var ping = require('../ping.js')
function checkPing (cb) {
var tracker = log.newItem('checkPing', 1)
tracker.info('checkPing', 'Pinging registry')
- ping({}, true, (_err, pong, data, res) => {
- cb(null, [res.statusCode, res.statusMessage])
+ ping({}, true, (err, pong) => {
+ if (err && err.code && err.code.match(/^E\d{3}$/)) {
+ return cb(null, [err.code.substr(1)])
+ } else {
+ cb(null, [200, 'OK'])
+ }
})
}
diff --git a/deps/npm/lib/fetch-package-metadata.js b/deps/npm/lib/fetch-package-metadata.js
index cca6dc64f4..78eed42bdf 100644
--- a/deps/npm/lib/fetch-package-metadata.js
+++ b/deps/npm/lib/fetch-package-metadata.js
@@ -8,11 +8,11 @@ const rimraf = require('rimraf')
const validate = require('aproba')
const npa = require('npm-package-arg')
const npm = require('./npm')
+let npmConfig
const npmlog = require('npmlog')
const limit = require('call-limit')
const tempFilename = require('./utils/temp-filename')
const pacote = require('pacote')
-let pacoteOpts
const isWindows = require('./utils/is-windows.js')
function andLogAndFinish (spec, tracker, done) {
@@ -52,10 +52,10 @@ function fetchPackageMetadata (spec, where, opts, done) {
err.code = 'EWINDOWSPATH'
return logAndFinish(err)
}
- if (!pacoteOpts) {
- pacoteOpts = require('./config/pacote')
+ if (!npmConfig) {
+ npmConfig = require('./config/figgy-config.js')
}
- pacote.manifest(dep, pacoteOpts({
+ pacote.manifest(dep, npmConfig({
annotate: true,
fullMetadata: opts.fullMetadata,
log: tracker || npmlog,
@@ -85,9 +85,6 @@ function fetchPackageMetadata (spec, where, opts, done) {
module.exports.addBundled = addBundled
function addBundled (pkg, next) {
validate('OF', arguments)
- if (!pacoteOpts) {
- pacoteOpts = require('./config/pacote')
- }
if (pkg._bundled !== undefined) return next(null, pkg)
if (!pkg.bundleDependencies && pkg._requested.type !== 'directory') return next(null, pkg)
@@ -101,7 +98,10 @@ function addBundled (pkg, next) {
}
pkg._bundled = null
const target = tempFilename('unpack')
- const opts = pacoteOpts({integrity: pkg._integrity})
+ if (!npmConfig) {
+ npmConfig = require('./config/figgy-config.js')
+ }
+ const opts = npmConfig({integrity: pkg._integrity})
pacote.extract(pkg._resolved || pkg._requested || npa.resolve(pkg.name, pkg.version), target, opts).then(() => {
log.silly('addBundled', 'read tarball')
readPackageTree(target, (err, tree) => {
diff --git a/deps/npm/lib/hook.js b/deps/npm/lib/hook.js
index b0552c7474..54aea9f1e9 100644
--- a/deps/npm/lib/hook.js
+++ b/deps/npm/lib/hook.js
@@ -2,129 +2,146 @@
const BB = require('bluebird')
-const crypto = require('crypto')
-const hookApi = require('libnpmhook')
-const log = require('npmlog')
-const npm = require('./npm.js')
+const hookApi = require('libnpm/hook')
+const npmConfig = require('./config/figgy-config.js')
const output = require('./utils/output.js')
+const otplease = require('./utils/otplease.js')
const pudding = require('figgy-pudding')
const relativeDate = require('tiny-relative-date')
const Table = require('cli-table3')
-const usage = require('./utils/usage.js')
const validate = require('aproba')
-hook.usage = usage([
+hook.usage = [
'npm hook add <pkg> <url> <secret> [--type=<type>]',
'npm hook ls [pkg]',
'npm hook rm <id>',
'npm hook update <id> <url> <secret>'
-])
+].join('\n')
hook.completion = (opts, cb) => {
validate('OF', [opts, cb])
return cb(null, []) // fill in this array with completion values
}
-const npmSession = crypto.randomBytes(8).toString('hex')
-const hookConfig = pudding()
-function config () {
- return hookConfig({
- refer: npm.refer,
- projectScope: npm.projectScope,
- log,
- npmSession
- }, npm.config)
+const HookConfig = pudding({
+ json: {},
+ loglevel: {},
+ parseable: {},
+ silent: {},
+ unicode: {}
+})
+
+function UsageError () {
+ throw Object.assign(new Error(hook.usage), {code: 'EUSAGE'})
}
-module.exports = (args, cb) => BB.try(() => hook(args)).nodeify(cb)
+module.exports = (args, cb) => BB.try(() => hook(args)).then(
+ val => cb(null, val),
+ err => err.code === 'EUSAGE' ? cb(err.message) : cb(err)
+)
function hook (args) {
- switch (args[0]) {
- case 'add':
- return add(args[1], args[2], args[3])
- case 'ls':
- return ls(args[1])
- case 'rm':
- return rm(args[1])
- case 'update':
- case 'up':
- return update(args[1], args[2], args[3])
- }
+ return otplease(npmConfig(), opts => {
+ opts = HookConfig(opts)
+ switch (args[0]) {
+ case 'add':
+ return add(args[1], args[2], args[3], opts)
+ case 'ls':
+ return ls(args[1], opts)
+ case 'rm':
+ return rm(args[1], opts)
+ case 'update':
+ case 'up':
+ return update(args[1], args[2], args[3], opts)
+ default:
+ UsageError()
+ }
+ })
}
-function add (pkg, uri, secret) {
- return hookApi.add(pkg, uri, secret, config())
- .then((hook) => {
- if (npm.config.get('json')) {
- output(JSON.stringify(hook, null, 2))
- } else {
- output(`+ ${hookName(hook)} ${
- npm.config.get('unicode') ? ' ➜ ' : ' -> '
- } ${hook.endpoint}`)
- }
- })
+function add (pkg, uri, secret, opts) {
+ return hookApi.add(pkg, uri, secret, opts).then(hook => {
+ if (opts.json) {
+ output(JSON.stringify(hook, null, 2))
+ } else if (opts.parseable) {
+ output(Object.keys(hook).join('\t'))
+ output(Object.keys(hook).map(k => hook[k]).join('\t'))
+ } else if (!opts.silent && opts.loglevel !== 'silent') {
+ output(`+ ${hookName(hook)} ${
+ opts.unicode ? ' ➜ ' : ' -> '
+ } ${hook.endpoint}`)
+ }
+ })
}
-function ls (pkg) {
- return hookApi.ls(pkg, config())
- .then((hooks) => {
- if (npm.config.get('json')) {
- output(JSON.stringify(hooks, null, 2))
- } else if (!hooks.length) {
- output("You don't have any hooks configured yet.")
+function ls (pkg, opts) {
+ return hookApi.ls(opts.concat({package: pkg})).then(hooks => {
+ if (opts.json) {
+ output(JSON.stringify(hooks, null, 2))
+ } else if (opts.parseable) {
+ output(Object.keys(hooks[0]).join('\t'))
+ hooks.forEach(hook => {
+ output(Object.keys(hook).map(k => hook[k]).join('\t'))
+ })
+ } else if (!hooks.length) {
+ output("You don't have any hooks configured yet.")
+ } else if (!opts.silent && opts.loglevel !== 'silent') {
+ if (hooks.length === 1) {
+ output('You have one hook configured.')
} else {
- if (hooks.length === 1) {
- output('You have one hook configured.')
- } else {
- output(`You have ${hooks.length} hooks configured.`)
- }
- const table = new Table({head: ['id', 'target', 'endpoint']})
- hooks.forEach((hook) => {
+ output(`You have ${hooks.length} hooks configured.`)
+ }
+ const table = new Table({head: ['id', 'target', 'endpoint']})
+ hooks.forEach((hook) => {
+ table.push([
+ {rowSpan: 2, content: hook.id},
+ hookName(hook),
+ hook.endpoint
+ ])
+ if (hook.last_delivery) {
table.push([
- {rowSpan: 2, content: hook.id},
- hookName(hook),
- hook.endpoint
+ {
+ colSpan: 1,
+ content: `triggered ${relativeDate(hook.last_delivery)}`
+ },
+ hook.response_code
])
- if (hook.last_delivery) {
- table.push([
- {
- colSpan: 1,
- content: `triggered ${relativeDate(hook.last_delivery)}`
- },
- hook.response_code
- ])
- } else {
- table.push([{colSpan: 2, content: 'never triggered'}])
- }
- })
- output(table.toString())
- }
- })
+ } else {
+ table.push([{colSpan: 2, content: 'never triggered'}])
+ }
+ })
+ output(table.toString())
+ }
+ })
}
-function rm (id) {
- return hookApi.rm(id, config())
- .then((hook) => {
- if (npm.config.get('json')) {
- output(JSON.stringify(hook, null, 2))
- } else {
- output(`- ${hookName(hook)} ${
- npm.config.get('unicode') ? ' ✘ ' : ' X '
- } ${hook.endpoint}`)
- }
- })
+function rm (id, opts) {
+ return hookApi.rm(id, opts).then(hook => {
+ if (opts.json) {
+ output(JSON.stringify(hook, null, 2))
+ } else if (opts.parseable) {
+ output(Object.keys(hook).join('\t'))
+ output(Object.keys(hook).map(k => hook[k]).join('\t'))
+ } else if (!opts.silent && opts.loglevel !== 'silent') {
+ output(`- ${hookName(hook)} ${
+ opts.unicode ? ' ✘ ' : ' X '
+ } ${hook.endpoint}`)
+ }
+ })
}
-function update (id, uri, secret) {
- return hookApi.update(id, uri, secret, config())
- .then((hook) => {
- if (npm.config.get('json')) {
- output(JSON.stringify(hook, null, 2))
- } else {
- output(`+ ${hookName(hook)} ${
- npm.config.get('unicode') ? ' ➜ ' : ' -> '
- } ${hook.endpoint}`)
- }
- })
+function update (id, uri, secret, opts) {
+ return hookApi.update(id, uri, secret, opts).then(hook => {
+ if (opts.json) {
+ output(JSON.stringify(hook, null, 2))
+ } else if (opts.parseable) {
+ output(Object.keys(hook).join('\t'))
+ output(Object.keys(hook).map(k => hook[k]).join('\t'))
+ } else if (!opts.silent && opts.loglevel !== 'silent') {
+ output(`+ ${hookName(hook)} ${
+ opts.unicode ? ' ➜ ' : ' -> '
+ } ${hook.endpoint}`)
+ }
+ })
}
function hookName (hook) {
diff --git a/deps/npm/lib/install/action/extract-worker.js b/deps/npm/lib/install/action/extract-worker.js
index 2b082b4a57..225e5b4aea 100644
--- a/deps/npm/lib/install/action/extract-worker.js
+++ b/deps/npm/lib/install/action/extract-worker.js
@@ -3,16 +3,16 @@
const BB = require('bluebird')
const extract = require('pacote/extract')
-const npmlog = require('npmlog')
+// const npmlog = require('npmlog')
module.exports = (args, cb) => {
const parsed = typeof args === 'string' ? JSON.parse(args) : args
const spec = parsed[0]
const extractTo = parsed[1]
const opts = parsed[2]
- if (!opts.log) {
- opts.log = npmlog
- }
- opts.log.level = opts.loglevel || opts.log.level
+ // if (!opts.log) {
+ // opts.log = npmlog
+ // }
+ // opts.log.level = opts.loglevel || opts.log.level
BB.resolve(extract(spec, extractTo, opts)).nodeify(cb)
}
diff --git a/deps/npm/lib/install/action/extract.js b/deps/npm/lib/install/action/extract.js
index e8d7a6c4f6..c1c17cdf6c 100644
--- a/deps/npm/lib/install/action/extract.js
+++ b/deps/npm/lib/install/action/extract.js
@@ -2,6 +2,7 @@
const BB = require('bluebird')
+const figgyPudding = require('figgy-pudding')
const stat = BB.promisify(require('graceful-fs').stat)
const gentlyRm = BB.promisify(require('../../utils/gently-rm.js'))
const mkdirp = BB.promisify(require('mkdirp'))
@@ -9,8 +10,8 @@ const moduleStagingPath = require('../module-staging-path.js')
const move = require('../../utils/move.js')
const npa = require('npm-package-arg')
const npm = require('../../npm.js')
+let npmConfig
const packageId = require('../../utils/package-id.js')
-let pacoteOpts
const path = require('path')
const localWorker = require('./extract-worker.js')
const workerFarm = require('worker-farm')
@@ -19,19 +20,12 @@ const isRegistry = require('../../utils/is-registry.js')
const WORKER_PATH = require.resolve('./extract-worker.js')
let workers
-// NOTE: temporarily disabled on non-OSX due to ongoing issues:
-//
-// * Seems to make Windows antivirus issues much more common
-// * Messes with Docker (I think)
-//
-// There are other issues that should be fixed that affect OSX too:
-//
-// * Logging is messed up right now because pacote does its own thing
-// * Global deduplication in pacote breaks due to multiple procs
-//
-// As these get fixed, we can start experimenting with re-enabling it
-// at least on some platforms.
-const ENABLE_WORKERS = process.platform === 'darwin'
+const ExtractOpts = figgyPudding({
+ log: {}
+}, { other () { return true } })
+
+// Disabled for now. Re-enable someday. Just not today.
+const ENABLE_WORKERS = false
extract.init = () => {
if (ENABLE_WORKERS) {
@@ -53,10 +47,10 @@ module.exports = extract
function extract (staging, pkg, log) {
log.silly('extract', packageId(pkg))
const extractTo = moduleStagingPath(staging, pkg)
- if (!pacoteOpts) {
- pacoteOpts = require('../../config/pacote')
+ if (!npmConfig) {
+ npmConfig = require('../../config/figgy-config.js')
}
- const opts = pacoteOpts({
+ let opts = ExtractOpts(npmConfig()).concat({
integrity: pkg.package._integrity,
resolved: pkg.package._resolved
})
@@ -72,9 +66,18 @@ function extract (staging, pkg, log) {
args[0] = spec.raw
if (ENABLE_WORKERS && (isRegistry(spec) || spec.type === 'remote')) {
// We can't serialize these options
- opts.loglevel = opts.log.level
- opts.log = null
- opts.dirPacker = null
+ opts = opts.concat({
+ loglevel: opts.log.level,
+ log: null,
+ dirPacker: null,
+ Promise: null,
+ _events: null,
+ _eventsCount: null,
+ list: null,
+ sources: null,
+ _maxListeners: null,
+ root: null
+ })
// workers will run things in parallel!
launcher = workers
try {
diff --git a/deps/npm/lib/install/action/fetch.js b/deps/npm/lib/install/action/fetch.js
index 5ad34e29dd..346194e516 100644
--- a/deps/npm/lib/install/action/fetch.js
+++ b/deps/npm/lib/install/action/fetch.js
@@ -3,14 +3,14 @@
const BB = require('bluebird')
const finished = BB.promisify(require('mississippi').finished)
+const npmConfig = require('../../config/figgy-config.js')
const packageId = require('../../utils/package-id.js')
const pacote = require('pacote')
-const pacoteOpts = require('../../config/pacote')
module.exports = fetch
function fetch (staging, pkg, log, next) {
log.silly('fetch', packageId(pkg))
- const opts = pacoteOpts({integrity: pkg.package._integrity})
+ const opts = npmConfig({integrity: pkg.package._integrity})
return finished(pacote.tarball.stream(pkg.package._requested, opts))
.then(() => next(), next)
}
diff --git a/deps/npm/lib/install/audit.js b/deps/npm/lib/install/audit.js
index f372b425a6..f5bc5ae1a9 100644
--- a/deps/npm/lib/install/audit.js
+++ b/deps/npm/lib/install/audit.js
@@ -7,118 +7,115 @@ exports.printInstallReport = printInstallReport
exports.printParseableReport = printParseableReport
exports.printFullReport = printFullReport
-const Bluebird = require('bluebird')
const auditReport = require('npm-audit-report')
+const npmConfig = require('../config/figgy-config.js')
+const figgyPudding = require('figgy-pudding')
const treeToShrinkwrap = require('../shrinkwrap.js').treeToShrinkwrap
const packageId = require('../utils/package-id.js')
const output = require('../utils/output.js')
const npm = require('../npm.js')
const qw = require('qw')
-const registryFetch = require('npm-registry-fetch')
-const zlib = require('zlib')
-const gzip = Bluebird.promisify(zlib.gzip)
-const log = require('npmlog')
+const regFetch = require('npm-registry-fetch')
const perf = require('../utils/perf.js')
-const url = require('url')
const npa = require('npm-package-arg')
const uuid = require('uuid')
const ssri = require('ssri')
const cloneDeep = require('lodash.clonedeep')
-const pacoteOpts = require('../config/pacote.js')
// used when scrubbing module names/specifiers
const runId = uuid.v4()
+const InstallAuditConfig = figgyPudding({
+ color: {},
+ json: {},
+ unicode: {}
+}, {
+ other (key) {
+ return /:registry$/.test(key)
+ }
+})
+
function submitForInstallReport (auditData) {
- const cfg = npm.config // avoid the no-dynamic-lookups test
- const scopedRegistries = cfg.keys.filter(_ => /:registry$/.test(_)).map(_ => cfg.get(_))
- perf.emit('time', 'audit compress')
- // TODO: registryFetch will be adding native support for `Content-Encoding: gzip` at which point
- // we'll pass in something like `gzip: true` and not need to JSON stringify, gzip or headers.
- return gzip(JSON.stringify(auditData)).then(body => {
- perf.emit('timeEnd', 'audit compress')
- log.info('audit', 'Submitting payload of ' + body.length + 'bytes')
- scopedRegistries.forEach(reg => {
- // we don't care about the response so destroy the stream if we can, or leave it flowing
- // so it can eventually finish and clean up after itself
- fetchAudit(url.resolve(reg, '/-/npm/v1/security/audits/quick'))
- .then(_ => {
- _.body.on('error', () => {})
- if (_.body.destroy) {
- _.body.destroy()
- } else {
- _.body.resume()
- }
- }, _ => {})
- })
- perf.emit('time', 'audit submit')
- return fetchAudit('/-/npm/v1/security/audits/quick', body).then(response => {
- perf.emit('timeEnd', 'audit submit')
- perf.emit('time', 'audit body')
- return response.json()
- }).then(result => {
- perf.emit('timeEnd', 'audit body')
- return result
- })
+ const opts = InstallAuditConfig(npmConfig())
+ const scopedRegistries = [...opts.keys()].filter(
+ k => /:registry$/.test(k)
+ ).map(k => opts[k])
+ scopedRegistries.forEach(registry => {
+ // we don't care about the response so destroy the stream if we can, or leave it flowing
+ // so it can eventually finish and clean up after itself
+ regFetch('/-/npm/v1/security/audits/quick', opts.concat({
+ method: 'POST',
+ registry,
+ gzip: true,
+ body: auditData
+ })).then(_ => {
+ _.body.on('error', () => {})
+ if (_.body.destroy) {
+ _.body.destroy()
+ } else {
+ _.body.resume()
+ }
+ }, _ => {})
})
-}
-
-function submitForFullReport (auditData) {
- perf.emit('time', 'audit compress')
- // TODO: registryFetch will be adding native support for `Content-Encoding: gzip` at which point
- // we'll pass in something like `gzip: true` and not need to JSON stringify, gzip or headers.
- return gzip(JSON.stringify(auditData)).then(body => {
- perf.emit('timeEnd', 'audit compress')
- log.info('audit', 'Submitting payload of ' + body.length + ' bytes')
- perf.emit('time', 'audit submit')
- return fetchAudit('/-/npm/v1/security/audits', body).then(response => {
- perf.emit('timeEnd', 'audit submit')
- perf.emit('time', 'audit body')
- return response.json()
- }).then(result => {
- perf.emit('timeEnd', 'audit body')
- result.runId = runId
- return result
- })
+ perf.emit('time', 'audit submit')
+ return regFetch('/-/npm/v1/security/audits/quick', opts.concat({
+ method: 'POST',
+ gzip: true,
+ body: auditData
+ })).then(response => {
+ perf.emit('timeEnd', 'audit submit')
+ perf.emit('time', 'audit body')
+ return response.json()
+ }).then(result => {
+ perf.emit('timeEnd', 'audit body')
+ return result
})
}
-function fetchAudit (href, body) {
- const opts = pacoteOpts()
- return registryFetch(href, {
+function submitForFullReport (auditData) {
+ perf.emit('time', 'audit submit')
+ const opts = InstallAuditConfig(npmConfig())
+ return regFetch('/-/npm/v1/security/audits', opts.concat({
method: 'POST',
- headers: { 'content-encoding': 'gzip', 'content-type': 'application/json' },
- config: npm.config,
- npmSession: opts.npmSession,
- projectScope: npm.projectScope,
- log: log,
- body: body
+ gzip: true,
+ body: auditData
+ })).then(response => {
+ perf.emit('timeEnd', 'audit submit')
+ perf.emit('time', 'audit body')
+ return response.json()
+ }).then(result => {
+ perf.emit('timeEnd', 'audit body')
+ result.runId = runId
+ return result
})
}
function printInstallReport (auditResult) {
+ const opts = InstallAuditConfig(npmConfig())
return auditReport(auditResult, {
reporter: 'install',
- withColor: npm.color,
- withUnicode: npm.config.get('unicode')
+ withColor: opts.color,
+ withUnicode: opts.unicode
}).then(result => output(result.report))
}
function printFullReport (auditResult) {
+ const opts = InstallAuditConfig(npmConfig())
return auditReport(auditResult, {
log: output,
- reporter: npm.config.get('json') ? 'json' : 'detail',
- withColor: npm.color,
- withUnicode: npm.config.get('unicode')
+ reporter: opts.json ? 'json' : 'detail',
+ withColor: opts.color,
+ withUnicode: opts.unicode
}).then(result => output(result.report))
}
function printParseableReport (auditResult) {
+ const opts = InstallAuditConfig(npmConfig())
return auditReport(auditResult, {
log: output,
reporter: 'parseable',
- withColor: npm.color,
- withUnicode: npm.config.get('unicode')
+ withColor: opts.color,
+ withUnicode: opts.unicode
}).then(result => output(result.report))
}
diff --git a/deps/npm/lib/install/is-only-dev.js b/deps/npm/lib/install/is-only-dev.js
index ef41e8ad1a..2877c61a22 100644
--- a/deps/npm/lib/install/is-only-dev.js
+++ b/deps/npm/lib/install/is-only-dev.js
@@ -28,6 +28,7 @@ function andIsOnlyDev (name, seen) {
return isDev && !isProd
} else {
if (seen.has(req)) return true
+ seen = new Set(seen)
seen.add(req)
return isOnlyDev(req, seen)
}
diff --git a/deps/npm/lib/install/is-only-optional.js b/deps/npm/lib/install/is-only-optional.js
index 72d6f065e6..f1b731578d 100644
--- a/deps/npm/lib/install/is-only-optional.js
+++ b/deps/npm/lib/install/is-only-optional.js
@@ -10,6 +10,7 @@ function isOptional (node, seen) {
if (seen.has(node) || node.requiredBy.length === 0) {
return false
}
+ seen = new Set(seen)
seen.add(node)
const swOptional = node.fromShrinkwrap && node.package._optional
return node.requiredBy.every(function (req) {
diff --git a/deps/npm/lib/logout.js b/deps/npm/lib/logout.js
index a3287d42d1..411f547210 100644
--- a/deps/npm/lib/logout.js
+++ b/deps/npm/lib/logout.js
@@ -1,43 +1,44 @@
-module.exports = logout
+'use strict'
-var dezalgo = require('dezalgo')
-var log = require('npmlog')
+const BB = require('bluebird')
-var npm = require('./npm.js')
-var mapToRegistry = require('./utils/map-to-registry.js')
+const eu = encodeURIComponent
+const getAuth = require('npm-registry-fetch/auth.js')
+const log = require('npmlog')
+const npm = require('./npm.js')
+const npmConfig = require('./config/figgy-config.js')
+const npmFetch = require('libnpm/fetch')
logout.usage = 'npm logout [--registry=<url>] [--scope=<@scope>]'
-function afterLogout (normalized, cb) {
+function afterLogout (normalized) {
var scope = npm.config.get('scope')
if (scope) npm.config.del(scope + ':registry')
npm.config.clearCredentialsByURI(normalized)
- npm.config.save('user', cb)
+ return BB.fromNode(cb => npm.config.save('user', cb))
}
+module.exports = logout
function logout (args, cb) {
- cb = dezalgo(cb)
-
- mapToRegistry('/', npm.config, function (err, uri, auth, normalized) {
- if (err) return cb(err)
-
+ const opts = npmConfig()
+ BB.try(() => {
+ const reg = npmFetch.pickRegistry('foo', opts)
+ const auth = getAuth(reg, opts)
if (auth.token) {
- log.verbose('logout', 'clearing session token for', normalized)
- npm.registry.logout(normalized, { auth: auth }, function (err) {
- if (err) return cb(err)
-
- afterLogout(normalized, cb)
- })
+ log.verbose('logout', 'clearing session token for', reg)
+ return npmFetch(`/-/user/token/${eu(auth.token)}`, opts.concat({
+ method: 'DELETE',
+ ignoreBody: true
+ })).then(() => afterLogout(reg))
} else if (auth.username || auth.password) {
- log.verbose('logout', 'clearing user credentials for', normalized)
-
- afterLogout(normalized, cb)
+ log.verbose('logout', 'clearing user credentials for', reg)
+ return afterLogout(reg)
} else {
- cb(new Error(
- 'Not logged in to', normalized + ',', "so can't log out."
- ))
+ throw new Error(
+ 'Not logged in to', reg + ',', "so can't log out."
+ )
}
- })
+ }).nodeify(cb)
}
diff --git a/deps/npm/lib/npm.js b/deps/npm/lib/npm.js
index da5a363602..2ee9a99126 100644
--- a/deps/npm/lib/npm.js
+++ b/deps/npm/lib/npm.js
@@ -40,9 +40,7 @@
var which = require('which')
var glob = require('glob')
var rimraf = require('rimraf')
- var lazyProperty = require('lazy-property')
var parseJSON = require('./utils/parse-json.js')
- var clientConfig = require('./config/reg-client.js')
var aliases = require('./config/cmd-list').aliases
var cmdList = require('./config/cmd-list').cmdList
var plumbing = require('./config/cmd-list').plumbing
@@ -106,7 +104,6 @@
})
var registryRefer
- var registryLoaded
Object.keys(abbrevs).concat(plumbing).forEach(function addCommand (c) {
Object.defineProperty(npm.commands, c, { get: function () {
@@ -153,7 +150,7 @@
}).filter(function (arg) {
return arg && arg.match
}).join(' ')
- if (registryLoaded) npm.registry.refer = registryRefer
+ npm.referer = registryRefer
}
cmd.apply(npm, args)
@@ -357,17 +354,6 @@
npm.projectScope = config.get('scope') ||
scopeifyScope(getProjectScope(npm.prefix))
- // at this point the configs are all set.
- // go ahead and spin up the registry client.
- lazyProperty(npm, 'registry', function () {
- registryLoaded = true
- var RegClient = require('npm-registry-client')
- var registry = new RegClient(clientConfig(npm, log, npm.config))
- registry.version = npm.version
- registry.refer = registryRefer
- return registry
- })
-
startMetrics()
return cb(null, npm)
diff --git a/deps/npm/lib/org.js b/deps/npm/lib/org.js
new file mode 100644
index 0000000000..d8f857e3df
--- /dev/null
+++ b/deps/npm/lib/org.js
@@ -0,0 +1,151 @@
+'use strict'
+
+const figgyPudding = require('figgy-pudding')
+const liborg = require('libnpm/org')
+const npmConfig = require('./config/figgy-config.js')
+const output = require('./utils/output.js')
+const otplease = require('./utils/otplease.js')
+const Table = require('cli-table3')
+
+module.exports = org
+
+org.subcommands = ['set', 'rm', 'ls']
+
+org.usage =
+ 'npm org set orgname username [developer | admin | owner]\n' +
+ 'npm org rm orgname username\n' +
+ 'npm org ls orgname [<username>]'
+
+const OrgConfig = figgyPudding({
+ json: {},
+ loglevel: {},
+ parseable: {},
+ silent: {}
+})
+
+org.completion = function (opts, cb) {
+ var argv = opts.conf.argv.remain
+ if (argv.length === 2) {
+ return cb(null, org.subcommands)
+ }
+ switch (argv[2]) {
+ case 'ls':
+ case 'add':
+ case 'rm':
+ case 'set':
+ return cb(null, [])
+ default:
+ return cb(new Error(argv[2] + ' not recognized'))
+ }
+}
+
+function UsageError () {
+ throw Object.assign(new Error(org.usage), {code: 'EUSAGE'})
+}
+
+function org ([cmd, orgname, username, role], cb) {
+ otplease(npmConfig(), opts => {
+ opts = OrgConfig(opts)
+ switch (cmd) {
+ case 'add':
+ case 'set':
+ return orgSet(orgname, username, role, opts)
+ case 'rm':
+ return orgRm(orgname, username, opts)
+ case 'ls':
+ return orgList(orgname, username, opts)
+ default:
+ UsageError()
+ }
+ }).then(
+ x => cb(null, x),
+ err => cb(err.code === 'EUSAGE' ? err.message : err)
+ )
+}
+
+function orgSet (org, user, role, opts) {
+ role = role || 'developer'
+ if (!org) {
+ throw new Error('First argument `orgname` is required.')
+ }
+ if (!user) {
+ throw new Error('Second argument `username` is required.')
+ }
+ if (!['owner', 'admin', 'developer'].find(x => x === role)) {
+ throw new Error('Third argument `role` must be one of `owner`, `admin`, or `developer`, with `developer` being the default value if omitted.')
+ }
+ return liborg.set(org, user, role, opts).then(memDeets => {
+ if (opts.json) {
+ output(JSON.stringify(memDeets, null, 2))
+ } else if (opts.parseable) {
+ output(['org', 'orgsize', 'user', 'role'].join('\t'))
+ output([
+ memDeets.org.name,
+ memDeets.org.size,
+ memDeets.user,
+ memDeets.role
+ ])
+ } else if (!opts.silent && opts.loglevel !== 'silent') {
+ output(`Added ${memDeets.user} as ${memDeets.role} to ${memDeets.org.name}. You now ${memDeets.org.size} member${memDeets.org.size === 1 ? '' : 's'} in this org.`)
+ }
+ return memDeets
+ })
+}
+
+function orgRm (org, user, opts) {
+ if (!org) {
+ throw new Error('First argument `orgname` is required.')
+ }
+ if (!user) {
+ throw new Error('Second argument `username` is required.')
+ }
+ return liborg.rm(org, user, opts).then(() => {
+ return liborg.ls(org, opts)
+ }).then(roster => {
+ user = user.replace(/^[~@]?/, '')
+ org = org.replace(/^[~@]?/, '')
+ const userCount = Object.keys(roster).length
+ if (opts.json) {
+ output(JSON.stringify({
+ user,
+ org,
+ userCount,
+ deleted: true
+ }))
+ } else if (opts.parseable) {
+ output(['user', 'org', 'userCount', 'deleted'].join('\t'))
+ output([user, org, userCount, true].join('\t'))
+ } else if (!opts.silent && opts.loglevel !== 'silent') {
+ output(`Successfully removed ${user} from ${org}. You now have ${userCount} member${userCount === 1 ? '' : 's'} in this org.`)
+ }
+ })
+}
+
+function orgList (org, user, opts) {
+ if (!org) {
+ throw new Error('First argument `orgname` is required.')
+ }
+ return liborg.ls(org, opts).then(roster => {
+ if (user) {
+ const newRoster = {}
+ if (roster[user]) {
+ newRoster[user] = roster[user]
+ }
+ roster = newRoster
+ }
+ if (opts.json) {
+ output(JSON.stringify(roster, null, 2))
+ } else if (opts.parseable) {
+ output(['user', 'role'].join('\t'))
+ Object.keys(roster).forEach(user => {
+ output([user, roster[user]].join('\t'))
+ })
+ } else if (!opts.silent && opts.loglevel !== 'silent') {
+ const table = new Table({head: ['user', 'role']})
+ Object.keys(roster).sort().forEach(user => {
+ table.push([user, roster[user]])
+ })
+ output(table.toString())
+ }
+ })
+}
diff --git a/deps/npm/lib/outdated.js b/deps/npm/lib/outdated.js
index 024e076c4f..ebd67fb6b3 100644
--- a/deps/npm/lib/outdated.js
+++ b/deps/npm/lib/outdated.js
@@ -29,13 +29,15 @@ var color = require('ansicolors')
var styles = require('ansistyles')
var table = require('text-table')
var semver = require('semver')
-var npa = require('npm-package-arg')
+var npa = require('libnpm/parse-arg')
var pickManifest = require('npm-pick-manifest')
var fetchPackageMetadata = require('./fetch-package-metadata.js')
var mutateIntoLogicalTree = require('./install/mutate-into-logical-tree.js')
var npm = require('./npm.js')
+const npmConfig = require('./config/figgy-config.js')
+const figgyPudding = require('figgy-pudding')
+const packument = require('libnpm/packument')
var long = npm.config.get('long')
-var mapToRegistry = require('./utils/map-to-registry.js')
var isExtraneous = require('./install/is-extraneous.js')
var computeMetadata = require('./install/deps.js').computeMetadata
var computeVersionSpec = require('./install/deps.js').computeVersionSpec
@@ -43,6 +45,23 @@ var moduleName = require('./utils/module-name.js')
var output = require('./utils/output.js')
var ansiTrim = require('./utils/ansi-trim')
+const OutdatedConfig = figgyPudding({
+ also: {},
+ color: {},
+ depth: {},
+ dev: 'development',
+ development: {},
+ global: {},
+ json: {},
+ only: {},
+ parseable: {},
+ prod: 'production',
+ production: {},
+ save: {},
+ 'save-dev': {},
+ 'save-optional': {}
+})
+
function uniq (list) {
// we maintain the array because we need an array, not iterator, return
// value.
@@ -68,26 +87,27 @@ function outdated (args, silent, cb) {
cb = silent
silent = false
}
+ let opts = OutdatedConfig(npmConfig())
var dir = path.resolve(npm.dir, '..')
// default depth for `outdated` is 0 (cf. `ls`)
- if (npm.config.get('depth') === Infinity) npm.config.set('depth', 0)
+ if (opts.depth) opts = opts.concat({depth: 0})
readPackageTree(dir, andComputeMetadata(function (er, tree) {
if (!tree) return cb(er)
mutateIntoLogicalTree(tree)
- outdated_(args, '', tree, {}, 0, function (er, list) {
+ outdated_(args, '', tree, {}, 0, opts, function (er, list) {
list = uniq(list || []).sort(function (aa, bb) {
return aa[0].path.localeCompare(bb[0].path) ||
aa[1].localeCompare(bb[1])
})
if (er || silent || list.length === 0) return cb(er, list)
- if (npm.config.get('json')) {
- output(makeJSON(list))
- } else if (npm.config.get('parseable')) {
- output(makeParseable(list))
+ if (opts.json) {
+ output(makeJSON(list, opts))
+ } else if (opts.parseable) {
+ output(makeParseable(list, opts))
} else {
- var outList = list.map(makePretty)
+ var outList = list.map(x => makePretty(x, opts))
var outHead = [ 'Package',
'Current',
'Wanted',
@@ -97,7 +117,7 @@ function outdated (args, silent, cb) {
if (long) outHead.push('Package Type', 'Homepage')
var outTable = [outHead].concat(outList)
- if (npm.color) {
+ if (opts.color) {
outTable[0] = outTable[0].map(function (heading) {
return styles.underline(heading)
})
@@ -116,14 +136,14 @@ function outdated (args, silent, cb) {
}
// [[ dir, dep, has, want, latest, type ]]
-function makePretty (p) {
+function makePretty (p, opts) {
var depname = p[1]
var has = p[2]
var want = p[3]
var latest = p[4]
var type = p[6]
var deppath = p[7]
- var homepage = p[0].package.homepage
+ var homepage = p[0].package.homepage || ''
var columns = [ depname,
has || 'MISSING',
@@ -136,7 +156,7 @@ function makePretty (p) {
columns[6] = homepage
}
- if (npm.color) {
+ if (opts.color) {
columns[0] = color[has === want || want === 'linked' ? 'yellow' : 'red'](columns[0]) // dep
columns[2] = color.green(columns[2]) // want
columns[3] = color.magenta(columns[3]) // latest
@@ -167,7 +187,7 @@ function makeParseable (list) {
}).join(os.EOL)
}
-function makeJSON (list) {
+function makeJSON (list, opts) {
var out = {}
list.forEach(function (p) {
var dep = p[0]
@@ -177,7 +197,7 @@ function makeJSON (list) {
var want = p[3]
var latest = p[4]
var type = p[6]
- if (!npm.config.get('global')) {
+ if (!opts.global) {
dir = path.relative(process.cwd(), dir)
}
out[depname] = { current: has,
@@ -193,11 +213,11 @@ function makeJSON (list) {
return JSON.stringify(out, null, 2)
}
-function outdated_ (args, path, tree, parentHas, depth, cb) {
+function outdated_ (args, path, tree, parentHas, depth, opts, cb) {
if (!tree.package) tree.package = {}
if (path && tree.package.name) path += ' > ' + tree.package.name
if (!path && tree.package.name) path = tree.package.name
- if (depth > npm.config.get('depth')) {
+ if (depth > opts.depth) {
return cb(null, [])
}
var types = {}
@@ -227,11 +247,14 @@ function outdated_ (args, path, tree, parentHas, depth, cb) {
// (All the save checking here is because this gets called from npm-update currently
// and that requires this logic around dev deps.)
// FIXME: Refactor npm update to not be in terms of outdated.
- var dev = npm.config.get('dev') || /^dev(elopment)?$/.test(npm.config.get('also'))
- var prod = npm.config.get('production') || /^prod(uction)?$/.test(npm.config.get('only'))
- if ((dev || !prod) &&
- (npm.config.get('save-dev') || (
- !npm.config.get('save') && !npm.config.get('save-optional')))) {
+ var dev = opts.dev || /^dev(elopment)?$/.test(opts.also)
+ var prod = opts.production || /^prod(uction)?$/.test(opts.only)
+ if (
+ (dev || !prod) &&
+ (
+ opts['save-dev'] || (!opts.save && !opts['save-optional'])
+ )
+ ) {
Object.keys(tree.missingDevDeps).forEach(function (name) {
deps.push({
package: { name: name },
@@ -245,15 +268,15 @@ function outdated_ (args, path, tree, parentHas, depth, cb) {
})
}
- if (npm.config.get('save-dev')) {
+ if (opts['save-dev']) {
deps = deps.filter(function (dep) { return pkg.devDependencies[moduleName(dep)] })
deps.forEach(function (dep) {
types[moduleName(dep)] = 'devDependencies'
})
- } else if (npm.config.get('save')) {
+ } else if (opts.save) {
// remove optional dependencies from dependencies during --save.
deps = deps.filter(function (dep) { return !pkg.optionalDependencies[moduleName(dep)] })
- } else if (npm.config.get('save-optional')) {
+ } else if (opts['save-optional']) {
deps = deps.filter(function (dep) { return pkg.optionalDependencies[moduleName(dep)] })
deps.forEach(function (dep) {
types[moduleName(dep)] = 'optionalDependencies'
@@ -262,7 +285,7 @@ function outdated_ (args, path, tree, parentHas, depth, cb) {
var doUpdate = dev || (
!prod &&
!Object.keys(parentHas).length &&
- !npm.config.get('global')
+ !opts.global
)
if (doUpdate) {
Object.keys(pkg.devDependencies || {}).forEach(function (k) {
@@ -300,13 +323,13 @@ function outdated_ (args, path, tree, parentHas, depth, cb) {
required = computeVersionSpec(tree, dep)
}
- if (!long) return shouldUpdate(args, dep, name, has, required, depth, path, cb)
+ if (!long) return shouldUpdate(args, dep, name, has, required, depth, path, opts, cb)
- shouldUpdate(args, dep, name, has, required, depth, path, cb, types[name])
+ shouldUpdate(args, dep, name, has, required, depth, path, opts, cb, types[name])
}, cb)
}
-function shouldUpdate (args, tree, dep, has, req, depth, pkgpath, cb, type) {
+function shouldUpdate (args, tree, dep, has, req, depth, pkgpath, opts, cb, type) {
// look up the most recent version.
// if that's what we already have, or if it's not on the args list,
// then dive into it. Otherwise, cb() with the data.
@@ -322,6 +345,7 @@ function shouldUpdate (args, tree, dep, has, req, depth, pkgpath, cb, type) {
tree,
has,
depth + 1,
+ opts,
cb)
}
@@ -350,11 +374,9 @@ function shouldUpdate (args, tree, dep, has, req, depth, pkgpath, cb, type) {
} else if (parsed.type === 'file') {
return updateLocalDeps()
} else {
- return mapToRegistry(dep, npm.config, function (er, uri, auth) {
- if (er) return cb(er)
-
- npm.registry.get(uri, { auth: auth }, updateDeps)
- })
+ return packument(dep, opts.concat({
+ 'prefer-online': true
+ })).nodeify(updateDeps)
}
function updateLocalDeps (latestRegistryVersion) {
diff --git a/deps/npm/lib/owner.js b/deps/npm/lib/owner.js
index 3c2660ace1..a64cb5e14c 100644
--- a/deps/npm/lib/owner.js
+++ b/deps/npm/lib/owner.js
@@ -1,12 +1,17 @@
-/* eslint-disable standard/no-callback-literal */
module.exports = owner
-var npm = require('./npm.js')
-var log = require('npmlog')
-var mapToRegistry = require('./utils/map-to-registry.js')
-var readLocalPkg = require('./utils/read-local-package.js')
-var usage = require('./utils/usage')
-var output = require('./utils/output.js')
+const BB = require('bluebird')
+
+const log = require('npmlog')
+const npa = require('libnpm/parse-arg')
+const npmConfig = require('./config/figgy-config.js')
+const npmFetch = require('libnpm/fetch')
+const output = require('./utils/output.js')
+const otplease = require('./utils/otplease.js')
+const packument = require('libnpm/packument')
+const readLocalPkg = BB.promisify(require('./utils/read-local-package.js'))
+const usage = require('./utils/usage')
+const whoami = BB.promisify(require('./whoami.js'))
owner.usage = usage(
'owner',
@@ -14,8 +19,9 @@ owner.usage = usage(
'\nnpm owner rm <user> [<@scope>/]<pkg>' +
'\nnpm owner ls [<@scope>/]<pkg>'
)
+
owner.completion = function (opts, cb) {
- var argv = opts.conf.argv.remain
+ const argv = opts.conf.argv.remain
if (argv.length > 4) return cb()
if (argv.length <= 2) {
var subs = ['add', 'rm']
@@ -23,130 +29,109 @@ owner.completion = function (opts, cb) {
else subs.push('ls', 'list')
return cb(null, subs)
}
-
- npm.commands.whoami([], true, function (er, username) {
- if (er) return cb()
-
- var un = encodeURIComponent(username)
- var byUser, theUser
- switch (argv[2]) {
- case 'ls':
- // FIXME: there used to be registry completion here, but it stopped
- // making sense somewhere around 50,000 packages on the registry
- return cb()
-
- case 'rm':
- if (argv.length > 3) {
- theUser = encodeURIComponent(argv[3])
- byUser = '-/by-user/' + theUser + '|' + un
- return mapToRegistry(byUser, npm.config, function (er, uri, auth) {
- if (er) return cb(er)
-
- console.error(uri)
- npm.registry.get(uri, { auth: auth }, function (er, d) {
- if (er) return cb(er)
- // return the intersection
- return cb(null, d[theUser].filter(function (p) {
+ BB.try(() => {
+ const opts = npmConfig()
+ return whoami([], true).then(username => {
+ const un = encodeURIComponent(username)
+ let byUser, theUser
+ switch (argv[2]) {
+ case 'ls':
+ // FIXME: there used to be registry completion here, but it stopped
+ // making sense somewhere around 50,000 packages on the registry
+ return
+ case 'rm':
+ if (argv.length > 3) {
+ theUser = encodeURIComponent(argv[3])
+ byUser = `/-/by-user/${theUser}|${un}`
+ return npmFetch.json(byUser, opts).then(d => {
+ return d[theUser].filter(
// kludge for server adminery.
- return un === 'isaacs' || d[un].indexOf(p) === -1
- }))
+ p => un === 'isaacs' || d[un].indexOf(p) === -1
+ )
})
- })
- }
- // else fallthrough
- /* eslint no-fallthrough:0 */
- case 'add':
- if (argv.length > 3) {
- theUser = encodeURIComponent(argv[3])
- byUser = '-/by-user/' + theUser + '|' + un
- return mapToRegistry(byUser, npm.config, function (er, uri, auth) {
- if (er) return cb(er)
-
- console.error(uri)
- npm.registry.get(uri, { auth: auth }, function (er, d) {
- console.error(uri, er || d)
- // return mine that they're not already on.
- if (er) return cb(er)
+ }
+ // else fallthrough
+ /* eslint no-fallthrough:0 */
+ case 'add':
+ if (argv.length > 3) {
+ theUser = encodeURIComponent(argv[3])
+ byUser = `/-/by-user/${theUser}|${un}`
+ return npmFetch.json(byUser, opts).then(d => {
var mine = d[un] || []
var theirs = d[theUser] || []
- return cb(null, mine.filter(function (p) {
- return theirs.indexOf(p) === -1
- }))
+ return mine.filter(p => theirs.indexOf(p) === -1)
})
- })
- }
- // just list all users who aren't me.
- return mapToRegistry('-/users', npm.config, function (er, uri, auth) {
- if (er) return cb(er)
+ } else {
+ // just list all users who aren't me.
+ return npmFetch.json('/-/users', opts).then(list => {
+ return Object.keys(list).filter(n => n !== un)
+ })
+ }
- npm.registry.get(uri, { auth: auth }, function (er, list) {
- if (er) return cb()
- return cb(null, Object.keys(list).filter(function (n) {
- return n !== un
- }))
- })
- })
+ default:
+ return cb()
+ }
+ })
+ }).nodeify(cb)
+}
- default:
- return cb()
- }
- })
+function UsageError () {
+ throw Object.assign(new Error(owner.usage), {code: 'EUSAGE'})
}
-function owner (args, cb) {
- var action = args.shift()
- switch (action) {
- case 'ls': case 'list': return ls(args[0], cb)
- case 'add': return add(args[0], args[1], cb)
- case 'rm': case 'remove': return rm(args[0], args[1], cb)
- default: return unknown(action, cb)
- }
+function owner ([action, ...args], cb) {
+ const opts = npmConfig()
+ BB.try(() => {
+ switch (action) {
+ case 'ls': case 'list': return ls(args[0], opts)
+ case 'add': return add(args[0], args[1], opts)
+ case 'rm': case 'remove': return rm(args[0], args[1], opts)
+ default: UsageError()
+ }
+ }).then(
+ data => cb(null, data),
+ err => err.code === 'EUSAGE' ? cb(err.message) : cb(err)
+ )
}
-function ls (pkg, cb) {
+function ls (pkg, opts) {
if (!pkg) {
- return readLocalPkg(function (er, pkg) {
- if (er) return cb(er)
- if (!pkg) return cb(owner.usage)
- ls(pkg, cb)
+ return readLocalPkg().then(pkg => {
+ if (!pkg) { UsageError() }
+ return ls(pkg, opts)
})
}
- mapToRegistry(pkg, npm.config, function (er, uri, auth) {
- if (er) return cb(er)
-
- npm.registry.get(uri, { auth: auth }, function (er, data) {
- var msg = ''
- if (er) {
- log.error('owner ls', "Couldn't get owner data", pkg)
- return cb(er)
- }
+ const spec = npa(pkg)
+ return packument(spec, opts.concat({fullMetadata: true})).then(
+ data => {
var owners = data.maintainers
if (!owners || !owners.length) {
- msg = 'admin party!'
+ output('admin party!')
} else {
- msg = owners.map(function (o) {
- return o.name + ' <' + o.email + '>'
- }).join('\n')
+ output(owners.map(o => `${o.name} <${o.email}>`).join('\n'))
}
- output(msg)
- cb(er, owners)
- })
- })
+ return owners
+ },
+ err => {
+ log.error('owner ls', "Couldn't get owner data", pkg)
+ throw err
+ }
+ )
}
-function add (user, pkg, cb) {
- if (!user) return cb(owner.usage)
+function add (user, pkg, opts) {
+ if (!user) { UsageError() }
if (!pkg) {
- return readLocalPkg(function (er, pkg) {
- if (er) return cb(er)
- if (!pkg) return cb(new Error(owner.usage))
- add(user, pkg, cb)
+ return readLocalPkg().then(pkg => {
+ if (!pkg) { UsageError() }
+ return add(user, pkg, opts)
})
}
-
log.verbose('owner add', '%s to %s', user, pkg)
- mutate(pkg, user, function (u, owners) {
+
+ const spec = npa(pkg)
+ return withMutation(spec, user, opts, (u, owners) => {
if (!owners) owners = []
for (var i = 0, l = owners.length; i < l; i++) {
var o = owners[i]
@@ -160,22 +145,23 @@ function add (user, pkg, cb) {
}
owners.push(u)
return owners
- }, cb)
+ })
}
-function rm (user, pkg, cb) {
+function rm (user, pkg, opts) {
+ if (!user) { UsageError() }
if (!pkg) {
- return readLocalPkg(function (er, pkg) {
- if (er) return cb(er)
- if (!pkg) return cb(new Error(owner.usage))
- rm(user, pkg, cb)
+ return readLocalPkg().then(pkg => {
+ if (!pkg) { UsageError() }
+ return add(user, pkg, opts)
})
}
-
log.verbose('owner rm', '%s from %s', user, pkg)
- mutate(pkg, user, function (u, owners) {
- var found = false
- var m = owners.filter(function (o) {
+
+ const spec = npa(pkg)
+ return withMutation(spec, user, opts, function (u, owners) {
+ let found = false
+ const m = owners.filter(function (o) {
var match = (o.name === user)
found = found || match
return !match
@@ -187,92 +173,70 @@ function rm (user, pkg, cb) {
}
if (!m.length) {
- return new Error(
+ throw new Error(
'Cannot remove all owners of a package. Add someone else first.'
)
}
return m
- }, cb)
+ })
}
-function mutate (pkg, user, mutation, cb) {
- if (user) {
- var byUser = '-/user/org.couchdb.user:' + user
- mapToRegistry(byUser, npm.config, function (er, uri, auth) {
- if (er) return cb(er)
-
- npm.registry.get(uri, { auth: auth }, mutate_)
- })
- } else {
- mutate_(null, null)
- }
+function withMutation (spec, user, opts, mutation) {
+ return BB.try(() => {
+ if (user) {
+ const uri = `/-/user/org.couchdb.user:${encodeURIComponent(user)}`
+ return npmFetch.json(uri, opts).then(mutate_, err => {
+ log.error('owner mutate', 'Error getting user data for %s', user)
+ throw err
+ })
+ } else {
+ return mutate_(null)
+ }
+ })
- function mutate_ (er, u) {
- if (!er && user && (!u || u.error)) {
- er = new Error(
+ function mutate_ (u) {
+ if (user && (!u || u.error)) {
+ throw new Error(
"Couldn't get user data for " + user + ': ' + JSON.stringify(u)
)
}
- if (er) {
- log.error('owner mutate', 'Error getting user data for %s', user)
- return cb(er)
- }
-
if (u) u = { name: u.name, email: u.email }
- mapToRegistry(pkg, npm.config, function (er, uri, auth) {
- if (er) return cb(er)
-
- npm.registry.get(uri, { auth: auth }, function (er, data) {
- if (er) {
- log.error('owner mutate', 'Error getting package data for %s', pkg)
- return cb(er)
- }
-
- // save the number of maintainers before mutation so that we can figure
- // out if maintainers were added or removed
- var beforeMutation = data.maintainers.length
-
- var m = mutation(u, data.maintainers)
- if (!m) return cb() // handled
- if (m instanceof Error) return cb(m) // error
-
- data = {
- _id: data._id,
- _rev: data._rev,
- maintainers: m
- }
- var dataPath = pkg.replace('/', '%2f') + '/-rev/' + data._rev
- mapToRegistry(dataPath, npm.config, function (er, uri, auth) {
- if (er) return cb(er)
-
- var params = {
- method: 'PUT',
- body: data,
- auth: auth
- }
- npm.registry.request(uri, params, function (er, data) {
- if (!er && data.error) {
- er = new Error('Failed to update package metadata: ' + JSON.stringify(data))
- }
-
- if (er) {
- log.error('owner mutate', 'Failed to update package metadata')
- } else if (m.length > beforeMutation) {
- output('+ %s (%s)', user, pkg)
- } else if (m.length < beforeMutation) {
- output('- %s (%s)', user, pkg)
- }
-
- cb(er, data)
- })
+ return packument(spec, opts.concat({
+ fullMetadata: true
+ })).then(data => {
+ // save the number of maintainers before mutation so that we can figure
+ // out if maintainers were added or removed
+ const beforeMutation = data.maintainers.length
+
+ const m = mutation(u, data.maintainers)
+ if (!m) return // handled
+ if (m instanceof Error) throw m // error
+
+ data = {
+ _id: data._id,
+ _rev: data._rev,
+ maintainers: m
+ }
+ const dataPath = `/${spec.escapedName}/-rev/${encodeURIComponent(data._rev)}`
+ return otplease(opts, opts => {
+ const reqOpts = opts.concat({
+ method: 'PUT',
+ body: data,
+ spec
})
+ return npmFetch.json(dataPath, reqOpts)
+ }).then(data => {
+ if (data.error) {
+ throw new Error('Failed to update package metadata: ' + JSON.stringify(data))
+ } else if (m.length > beforeMutation) {
+ output('+ %s (%s)', user, spec.name)
+ } else if (m.length < beforeMutation) {
+ output('- %s (%s)', user, spec.name)
+ }
+ return data
})
})
}
}
-
-function unknown (action, cb) {
- cb('Usage: \n' + owner.usage)
-}
diff --git a/deps/npm/lib/pack.js b/deps/npm/lib/pack.js
index 3b3f5b7bbc..78e5bfd174 100644
--- a/deps/npm/lib/pack.js
+++ b/deps/npm/lib/pack.js
@@ -18,9 +18,9 @@ const lifecycle = BB.promisify(require('./utils/lifecycle'))
const log = require('npmlog')
const move = require('move-concurrently')
const npm = require('./npm')
+const npmConfig = require('./config/figgy-config.js')
const output = require('./utils/output')
const pacote = require('pacote')
-const pacoteOpts = require('./config/pacote')
const path = require('path')
const PassThrough = require('stream').PassThrough
const pathIsInside = require('path-is-inside')
@@ -88,8 +88,8 @@ function pack_ (pkg, dir) {
}
function packFromPackage (arg, target, filename) {
- const opts = pacoteOpts()
- return pacote.tarball.toFile(arg, target, pacoteOpts())
+ const opts = npmConfig()
+ return pacote.tarball.toFile(arg, target, opts)
.then(() => cacache.tmp.withTmp(npm.tmp, {tmpPrefix: 'unpacking'}, (tmp) => {
const tmpTarget = path.join(tmp, filename)
return pacote.extract(arg, tmpTarget, opts)
diff --git a/deps/npm/lib/ping.js b/deps/npm/lib/ping.js
index 13f390397c..3023bab00e 100644
--- a/deps/npm/lib/ping.js
+++ b/deps/npm/lib/ping.js
@@ -1,5 +1,16 @@
-var npm = require('./npm.js')
-var output = require('./utils/output.js')
+'use strict'
+
+const npmConfig = require('./config/figgy-config.js')
+const fetch = require('libnpm/fetch')
+const figgyPudding = require('figgy-pudding')
+const log = require('npmlog')
+const npm = require('./npm.js')
+const output = require('./utils/output.js')
+
+const PingConfig = figgyPudding({
+ json: {},
+ registry: {}
+})
module.exports = ping
@@ -10,18 +21,27 @@ function ping (args, silent, cb) {
cb = silent
silent = false
}
- var registry = npm.config.get('registry')
- if (!registry) return cb(new Error('no default registry set'))
- var auth = npm.config.getCredentialsByURI(registry)
- npm.registry.ping(registry, {auth: auth}, function (er, pong, data, res) {
- if (!silent) {
- if (er) {
- output('Ping error: ' + er)
- } else {
- output('Ping success: ' + JSON.stringify(pong))
+ const opts = PingConfig(npmConfig())
+ const registry = opts.registry
+ log.notice('PING', registry)
+ const start = Date.now()
+ return fetch('/-/ping?write=true', opts).then(
+ res => res.json().catch(() => ({}))
+ ).then(details => {
+ if (silent) {
+ } else {
+ const time = Date.now() - start
+ log.notice('PONG', `${time / 1000}ms`)
+ if (npm.config.get('json')) {
+ output(JSON.stringify({
+ registry,
+ time,
+ details
+ }, null, 2))
+ } else if (Object.keys(details).length) {
+ log.notice('PONG', `${JSON.stringify(details, null, 2)}`)
}
}
- cb(er, er ? null : pong, data, res)
- })
+ }).nodeify(cb)
}
diff --git a/deps/npm/lib/profile.js b/deps/npm/lib/profile.js
index ff01db90f7..7ce9cb5cce 100644
--- a/deps/npm/lib/profile.js
+++ b/deps/npm/lib/profile.js
@@ -1,18 +1,23 @@
'use strict'
-const profile = require('npm-profile')
-const npm = require('./npm.js')
+
+const BB = require('bluebird')
+
+const ansistyles = require('ansistyles')
+const figgyPudding = require('figgy-pudding')
+const inspect = require('util').inspect
const log = require('npmlog')
+const npm = require('./npm.js')
+const npmConfig = require('./config/figgy-config.js')
+const otplease = require('./utils/otplease.js')
const output = require('./utils/output.js')
+const profile = require('libnpm/profile')
+const pulseTillDone = require('./utils/pulse-till-done.js')
+const qrcodeTerminal = require('qrcode-terminal')
+const queryString = require('query-string')
const qw = require('qw')
-const Table = require('cli-table3')
-const ansistyles = require('ansistyles')
-const Bluebird = require('bluebird')
const readUserInfo = require('./utils/read-user-info.js')
-const qrcodeTerminal = require('qrcode-terminal')
+const Table = require('cli-table3')
const url = require('url')
-const queryString = require('query-string')
-const pulseTillDone = require('./utils/pulse-till-done.js')
-const inspect = require('util').inspect
module.exports = profileCmd
@@ -48,6 +53,13 @@ function withCb (prom, cb) {
prom.then((value) => cb(null, value), cb)
}
+const ProfileOpts = figgyPudding({
+ json: {},
+ otp: {},
+ parseable: {},
+ registry: {}
+})
+
function profileCmd (args, cb) {
if (args.length === 0) return cb(new Error(profileCmd.usage))
log.gauge.show('profile')
@@ -75,36 +87,13 @@ function profileCmd (args, cb) {
}
}
-function config () {
- const conf = {
- json: npm.config.get('json'),
- parseable: npm.config.get('parseable'),
- registry: npm.config.get('registry'),
- otp: npm.config.get('otp')
- }
- const creds = npm.config.getCredentialsByURI(conf.registry)
- if (creds.token) {
- conf.auth = {token: creds.token}
- } else if (creds.username) {
- conf.auth = {basic: {username: creds.username, password: creds.password}}
- } else if (creds.auth) {
- const auth = Buffer.from(creds.auth, 'base64').toString().split(':', 2)
- conf.auth = {basic: {username: auth[0], password: auth[1]}}
- } else {
- conf.auth = {}
- }
-
- if (conf.otp) conf.auth.otp = conf.otp
- return conf
-}
-
const knownProfileKeys = qw`
name email ${'two-factor auth'} fullname homepage
freenode twitter github created updated`
function get (args) {
const tfa = 'two-factor auth'
- const conf = config()
+ const conf = ProfileOpts(npmConfig())
return pulseTillDone.withPromise(profile.get(conf)).then((info) => {
if (!info.cidr_whitelist) delete info.cidr_whitelist
if (conf.json) {
@@ -150,7 +139,7 @@ const writableProfileKeys = qw`
email password fullname homepage freenode twitter github`
function set (args) {
- const conf = config()
+ let conf = ProfileOpts(npmConfig())
const prop = (args[0] || '').toLowerCase().trim()
let value = args.length > 1 ? args.slice(1).join(' ') : null
if (prop !== 'password' && value === null) {
@@ -164,7 +153,7 @@ function set (args) {
if (writableProfileKeys.indexOf(prop) === -1) {
return Promise.reject(Error(`"${prop}" is not a property we can set. Valid properties are: ` + writableProfileKeys.join(', ')))
}
- return Bluebird.try(() => {
+ return BB.try(() => {
if (prop === 'password') {
return readUserInfo.password('Current password: ').then((current) => {
return readPasswords().then((newpassword) => {
@@ -193,23 +182,18 @@ function set (args) {
const newUser = {}
writableProfileKeys.forEach((k) => { newUser[k] = user[k] })
newUser[prop] = value
- return profile.set(newUser, conf).catch((err) => {
- if (err.code !== 'EOTP') throw err
- return readUserInfo.otp().then((otp) => {
- conf.auth.otp = otp
- return profile.set(newUser, conf)
+ return otplease(conf, conf => profile.set(newUser, conf))
+ .then((result) => {
+ if (conf.json) {
+ output(JSON.stringify({[prop]: result[prop]}, null, 2))
+ } else if (conf.parseable) {
+ output(prop + '\t' + result[prop])
+ } else if (result[prop] != null) {
+ output('Set', prop, 'to', result[prop])
+ } else {
+ output('Set', prop)
+ }
})
- }).then((result) => {
- if (conf.json) {
- output(JSON.stringify({[prop]: result[prop]}, null, 2))
- } else if (conf.parseable) {
- output(prop + '\t' + result[prop])
- } else if (result[prop] != null) {
- output('Set', prop, 'to', result[prop])
- } else {
- output('Set', prop)
- }
- })
}))
})
}
@@ -225,7 +209,7 @@ function enable2fa (args) {
' auth-only - Require two-factor authentication only when logging in\n' +
' auth-and-writes - Require two-factor authentication when logging in AND when publishing'))
}
- const conf = config()
+ const conf = ProfileOpts(npmConfig())
if (conf.json || conf.parseable) {
return Promise.reject(new Error(
'Enabling two-factor authentication is an interactive operation and ' +
@@ -238,15 +222,18 @@ function enable2fa (args) {
}
}
- return Bluebird.try(() => {
+ return BB.try(() => {
// if they're using legacy auth currently then we have to update them to a
// bearer token before continuing.
- if (conf.auth.basic) {
+ const auth = getAuth(conf)
+ if (auth.basic) {
log.info('profile', 'Updating authentication to bearer token')
- return profile.login(conf.auth.basic.username, conf.auth.basic.password, conf).then((result) => {
+ return profile.createToken(
+ auth.basic.password, false, [], conf
+ ).then((result) => {
if (!result.token) throw new Error('Your registry ' + conf.registry + 'does not seem to support bearer tokens. Bearer tokens are required for two-factor authentication')
npm.config.setCredentialsByURI(conf.registry, {token: result.token})
- return Bluebird.fromNode((cb) => npm.config.save('user', cb))
+ return BB.fromNode((cb) => npm.config.save('user', cb))
})
}
}).then(() => {
@@ -295,18 +282,36 @@ function enable2fa (args) {
})
}
+function getAuth (conf) {
+ const creds = npm.config.getCredentialsByURI(conf.registry)
+ let auth
+ if (creds.token) {
+ auth = {token: creds.token}
+ } else if (creds.username) {
+ auth = {basic: {username: creds.username, password: creds.password}}
+ } else if (creds.auth) {
+ const basic = Buffer.from(creds.auth, 'base64').toString().split(':', 2)
+ auth = {basic: {username: basic[0], password: basic[1]}}
+ } else {
+ auth = {}
+ }
+
+ if (conf.otp) auth.otp = conf.otp
+ return auth
+}
+
function disable2fa (args) {
- const conf = config()
+ let conf = ProfileOpts(npmConfig())
return pulseTillDone.withPromise(profile.get(conf)).then((info) => {
if (!info.tfa || info.tfa.pending) {
output('Two factor authentication not enabled.')
return
}
return readUserInfo.password().then((password) => {
- return Bluebird.try(() => {
- if (conf.auth.otp) return
+ return BB.try(() => {
+ if (conf.otp) return
return readUserInfo.otp('Enter one-time password from your authenticator: ').then((otp) => {
- conf.auth.otp = otp
+ conf = conf.concat({otp})
})
}).then(() => {
log.info('profile', 'disabling tfa')
diff --git a/deps/npm/lib/publish.js b/deps/npm/lib/publish.js
index 25f2134b1b..e81fc1a057 100644
--- a/deps/npm/lib/publish.js
+++ b/deps/npm/lib/publish.js
@@ -3,20 +3,20 @@
const BB = require('bluebird')
const cacache = require('cacache')
-const createReadStream = require('graceful-fs').createReadStream
-const getPublishConfig = require('./utils/get-publish-config.js')
+const figgyPudding = require('figgy-pudding')
+const libpub = require('libnpm/publish')
+const libunpub = require('libnpm/unpublish')
const lifecycle = BB.promisify(require('./utils/lifecycle.js'))
const log = require('npmlog')
-const mapToRegistry = require('./utils/map-to-registry.js')
-const npa = require('npm-package-arg')
-const npm = require('./npm.js')
+const npa = require('libnpm/parse-arg')
+const npmConfig = require('./config/figgy-config.js')
const output = require('./utils/output.js')
+const otplease = require('./utils/otplease.js')
const pack = require('./pack')
-const pacote = require('pacote')
-const pacoteOpts = require('./config/pacote')
+const { tarball, extract } = require('libnpm')
const path = require('path')
+const readFileAsync = BB.promisify(require('graceful-fs').readFile)
const readJson = BB.promisify(require('read-package-json'))
-const readUserInfo = require('./utils/read-user-info.js')
const semver = require('semver')
const statAsync = BB.promisify(require('graceful-fs').stat)
@@ -31,6 +31,16 @@ publish.completion = function (opts, cb) {
return cb()
}
+const PublishConfig = figgyPudding({
+ dryRun: 'dry-run',
+ 'dry-run': { default: false },
+ force: { default: false },
+ json: { default: false },
+ Promise: { default: () => Promise },
+ tag: { default: 'latest' },
+ tmp: {}
+})
+
module.exports = publish
function publish (args, isRetry, cb) {
if (typeof cb !== 'function') {
@@ -42,15 +52,16 @@ function publish (args, isRetry, cb) {
log.verbose('publish', args)
- const t = npm.config.get('tag').trim()
+ const opts = PublishConfig(npmConfig())
+ const t = opts.tag.trim()
if (semver.validRange(t)) {
return cb(new Error('Tag name must not be a valid SemVer range: ' + t))
}
- return publish_(args[0])
+ return publish_(args[0], opts)
.then((tarball) => {
const silent = log.level === 'silent'
- if (!silent && npm.config.get('json')) {
+ if (!silent && opts.json) {
output(JSON.stringify(tarball, null, 2))
} else if (!silent) {
output(`+ ${tarball.id}`)
@@ -59,7 +70,7 @@ function publish (args, isRetry, cb) {
.nodeify(cb)
}
-function publish_ (arg) {
+function publish_ (arg, opts) {
return statAsync(arg).then((stat) => {
if (stat.isDirectory()) {
return stat
@@ -69,17 +80,17 @@ function publish_ (arg) {
throw err
}
}).then(() => {
- return publishFromDirectory(arg)
+ return publishFromDirectory(arg, opts)
}, (err) => {
if (err.code !== 'ENOENT' && err.code !== 'ENOTDIR') {
throw err
} else {
- return publishFromPackage(arg)
+ return publishFromPackage(arg, opts)
}
})
}
-function publishFromDirectory (arg) {
+function publishFromDirectory (arg, opts) {
// All this readJson is because any of the given scripts might modify the
// package.json in question, so we need to refresh after every step.
let contents
@@ -90,12 +101,12 @@ function publishFromDirectory (arg) {
}).then(() => {
return readJson(path.join(arg, 'package.json'))
}).then((pkg) => {
- return cacache.tmp.withTmp(npm.tmp, {tmpPrefix: 'fromDir'}, (tmpDir) => {
+ return cacache.tmp.withTmp(opts.tmp, {tmpPrefix: 'fromDir'}, (tmpDir) => {
const target = path.join(tmpDir, 'package.tgz')
return pack.packDirectory(pkg, arg, target, null, true)
.tap((c) => { contents = c })
- .then((c) => !npm.config.get('json') && pack.logContents(c))
- .then(() => upload(arg, pkg, false, target))
+ .then((c) => !opts.json && pack.logContents(c))
+ .then(() => upload(pkg, false, target, opts))
})
}).then(() => {
return readJson(path.join(arg, 'package.json'))
@@ -107,121 +118,50 @@ function publishFromDirectory (arg) {
.then(() => contents)
}
-function publishFromPackage (arg) {
- return cacache.tmp.withTmp(npm.tmp, {tmpPrefix: 'fromPackage'}, (tmp) => {
+function publishFromPackage (arg, opts) {
+ return cacache.tmp.withTmp(opts.tmp, {tmpPrefix: 'fromPackage'}, tmp => {
const extracted = path.join(tmp, 'package')
const target = path.join(tmp, 'package.json')
- const opts = pacoteOpts()
- return pacote.tarball.toFile(arg, target, opts)
- .then(() => pacote.extract(arg, extracted, opts))
+ return tarball.toFile(arg, target, opts)
+ .then(() => extract(arg, extracted, opts))
.then(() => readJson(path.join(extracted, 'package.json')))
.then((pkg) => {
return BB.resolve(pack.getContents(pkg, target))
- .tap((c) => !npm.config.get('json') && pack.logContents(c))
- .tap(() => upload(arg, pkg, false, target))
+ .tap((c) => !opts.json && pack.logContents(c))
+ .tap(() => upload(pkg, false, target, opts))
})
})
}
-function upload (arg, pkg, isRetry, cached) {
- if (!pkg) {
- return BB.reject(new Error('no package.json file found'))
- }
- if (pkg.private) {
- return BB.reject(new Error(
- 'This package has been marked as private\n' +
- "Remove the 'private' field from the package.json to publish it."
- ))
- }
- const mappedConfig = getPublishConfig(
- pkg.publishConfig,
- npm.config,
- npm.registry
- )
- const config = mappedConfig.config
- const registry = mappedConfig.client
-
- pkg._npmVersion = npm.version
- pkg._nodeVersion = process.versions.node
-
- delete pkg.modules
-
- return BB.fromNode((cb) => {
- mapToRegistry(pkg.name, config, (err, registryURI, auth, registryBase) => {
- if (err) { return cb(err) }
- cb(null, [registryURI, auth, registryBase])
- })
- }).spread((registryURI, auth, registryBase) => {
- // we just want the base registry URL in this case
- log.verbose('publish', 'registryBase', registryBase)
- log.silly('publish', 'uploading', cached)
-
- pkg._npmUser = {
- name: auth.username,
- email: auth.email
- }
-
- const params = {
- metadata: pkg,
- body: !npm.config.get('dry-run') && createReadStream(cached),
- auth: auth
- }
-
- function closeFile () {
- if (!npm.config.get('dry-run')) {
- params.body.close()
- }
- }
-
- // registry-frontdoor cares about the access level, which is only
- // configurable for scoped packages
- if (config.get('access')) {
- if (!npa(pkg.name).scope && config.get('access') === 'restricted') {
- throw new Error("Can't restrict access to unscoped packages.")
- }
-
- params.access = config.get('access')
- }
-
- if (npm.config.get('dry-run')) {
- log.verbose('publish', '--dry-run mode enabled. Skipping upload.')
- return BB.resolve()
- }
-
- log.showProgress('publish:' + pkg._id)
- return BB.fromNode((cb) => {
- registry.publish(registryBase, params, cb)
- }).catch((err) => {
- if (
- err.code === 'EPUBLISHCONFLICT' &&
- npm.config.get('force') &&
- !isRetry
- ) {
- log.warn('publish', 'Forced publish over ' + pkg._id)
- return BB.fromNode((cb) => {
- npm.commands.unpublish([pkg._id], cb)
- }).finally(() => {
- // close the file we are trying to upload, we will open it again.
- closeFile()
- // ignore errors. Use the force. Reach out with your feelings.
- return upload(arg, pkg, true, cached).catch(() => {
- // but if it fails again, then report the first error.
- throw err
+function upload (pkg, isRetry, cached, opts) {
+ if (!opts.dryRun) {
+ return readFileAsync(cached).then(tarball => {
+ return otplease(opts, opts => {
+ return libpub(pkg, tarball, opts)
+ }).catch(err => {
+ if (
+ err.code === 'EPUBLISHCONFLICT' &&
+ opts.force &&
+ !isRetry
+ ) {
+ log.warn('publish', 'Forced publish over ' + pkg._id)
+ return otplease(opts, opts => libunpub(
+ npa.resolve(pkg.name, pkg.version), opts
+ )).finally(() => {
+ // ignore errors. Use the force. Reach out with your feelings.
+ return otplease(opts, opts => {
+ return upload(pkg, true, tarball, opts)
+ }).catch(() => {
+ // but if it fails again, then report the first error.
+ throw err
+ })
})
- })
- } else {
- // close the file we are trying to upload, all attempts to resume will open it again
- closeFile()
- throw err
- }
- })
- }).catch((err) => {
- if (err.code !== 'EOTP' && !(err.code === 'E401' && /one-time pass/.test(err.message))) throw err
- // we prompt on stdout and read answers from stdin, so they need to be ttys.
- if (!process.stdin.isTTY || !process.stdout.isTTY) throw err
- return readUserInfo.otp().then((otp) => {
- npm.config.set('otp', otp)
- return upload(arg, pkg, isRetry, cached)
+ } else {
+ throw err
+ }
+ })
})
- })
+ } else {
+ return opts.Promise.resolve(true)
+ }
}
diff --git a/deps/npm/lib/repo.js b/deps/npm/lib/repo.js
index d5aa81a6a0..b930402aed 100644
--- a/deps/npm/lib/repo.js
+++ b/deps/npm/lib/repo.js
@@ -2,10 +2,10 @@ module.exports = repo
repo.usage = 'npm repo [<pkg>]'
-var openUrl = require('./utils/open-url')
-var hostedGitInfo = require('hosted-git-info')
-var url_ = require('url')
-var fetchPackageMetadata = require('./fetch-package-metadata.js')
+const openUrl = require('./utils/open-url')
+const hostedGitInfo = require('hosted-git-info')
+const url_ = require('url')
+const fetchPackageMetadata = require('./fetch-package-metadata.js')
repo.completion = function (opts, cb) {
// FIXME: there used to be registry completion here, but it stopped making
@@ -14,7 +14,7 @@ repo.completion = function (opts, cb) {
}
function repo (args, cb) {
- var n = args.length ? args[0] : '.'
+ const n = args.length ? args[0] : '.'
fetchPackageMetadata(n, '.', {fullMetadata: true}, function (er, d) {
if (er) return cb(er)
getUrlAndOpen(d, cb)
@@ -22,12 +22,12 @@ function repo (args, cb) {
}
function getUrlAndOpen (d, cb) {
- var r = d.repository
+ const r = d.repository
if (!r) return cb(new Error('no repository'))
// XXX remove this when npm@v1.3.10 from node 0.10 is deprecated
// from https://github.com/npm/npm-www/issues/418
- var info = hostedGitInfo.fromUrl(r.url)
- var url = info ? info.browse() : unknownHostedUrl(r.url)
+ const info = hostedGitInfo.fromUrl(r.url)
+ const url = info ? info.browse() : unknownHostedUrl(r.url)
if (!url) return cb(new Error('no repository: could not get url'))
@@ -36,12 +36,12 @@ function getUrlAndOpen (d, cb) {
function unknownHostedUrl (url) {
try {
- var idx = url.indexOf('@')
+ const idx = url.indexOf('@')
if (idx !== -1) {
url = url.slice(idx + 1).replace(/:([^\d]+)/, '/$1')
}
url = url_.parse(url)
- var protocol = url.protocol === 'https:'
+ const protocol = url.protocol === 'https:'
? 'https:'
: 'http:'
return protocol + '//' + (url.host || '') +
diff --git a/deps/npm/lib/search.js b/deps/npm/lib/search.js
index 3987be135c..3c59f8b43d 100644
--- a/deps/npm/lib/search.js
+++ b/deps/npm/lib/search.js
@@ -2,14 +2,16 @@
module.exports = exports = search
-var npm = require('./npm.js')
-var allPackageSearch = require('./search/all-package-search')
-var esearch = require('./search/esearch.js')
-var formatPackageStream = require('./search/format-package-stream.js')
-var usage = require('./utils/usage')
-var output = require('./utils/output.js')
-var log = require('npmlog')
-var ms = require('mississippi')
+const npm = require('./npm.js')
+const allPackageSearch = require('./search/all-package-search')
+const figgyPudding = require('figgy-pudding')
+const formatPackageStream = require('./search/format-package-stream.js')
+const libSearch = require('libnpm/search')
+const log = require('npmlog')
+const ms = require('mississippi')
+const npmConfig = require('./config/figgy-config.js')
+const output = require('./utils/output.js')
+const usage = require('./utils/usage')
search.usage = usage(
'search',
@@ -20,46 +22,50 @@ search.completion = function (opts, cb) {
cb(null, [])
}
+const SearchOpts = figgyPudding({
+ description: {},
+ exclude: {},
+ include: {},
+ limit: {},
+ log: {},
+ staleness: {},
+ unicode: {}
+})
+
function search (args, cb) {
- var searchOpts = {
+ const opts = SearchOpts(npmConfig()).concat({
description: npm.config.get('description'),
exclude: prepareExcludes(npm.config.get('searchexclude')),
include: prepareIncludes(args, npm.config.get('searchopts')),
- limit: npm.config.get('searchlimit'),
+ limit: npm.config.get('searchlimit') || 20,
log: log,
staleness: npm.config.get('searchstaleness'),
unicode: npm.config.get('unicode')
- }
-
- if (searchOpts.include.length === 0) {
+ })
+ if (opts.include.length === 0) {
return cb(new Error('search must be called with arguments'))
}
// Used later to figure out whether we had any packages go out
- var anyOutput = false
+ let anyOutput = false
- var entriesStream = ms.through.obj()
+ const entriesStream = ms.through.obj()
- var esearchWritten = false
- esearch(searchOpts).on('data', function (pkg) {
+ let esearchWritten = false
+ libSearch.stream(opts.include, opts).on('data', pkg => {
entriesStream.write(pkg)
!esearchWritten && (esearchWritten = true)
- }).on('error', function (e) {
+ }).on('error', err => {
if (esearchWritten) {
// If esearch errored after already starting output, we can't fall back.
- return entriesStream.emit('error', e)
+ return entriesStream.emit('error', err)
}
log.warn('search', 'fast search endpoint errored. Using old search.')
- allPackageSearch(searchOpts).on('data', function (pkg) {
- entriesStream.write(pkg)
- }).on('error', function (e) {
- entriesStream.emit('error', e)
- }).on('end', function () {
- entriesStream.end()
- })
- }).on('end', function () {
- entriesStream.end()
- })
+ allPackageSearch(opts)
+ .on('data', pkg => entriesStream.write(pkg))
+ .on('error', err => entriesStream.emit('error', err))
+ .on('end', () => entriesStream.end())
+ }).on('end', () => entriesStream.end())
// Grab a configured output stream that will spit out packages in the
// desired format.
@@ -71,14 +77,14 @@ function search (args, cb) {
parseable: npm.config.get('parseable'),
color: npm.color
})
- outputStream.on('data', function (chunk) {
+ outputStream.on('data', chunk => {
if (!anyOutput) { anyOutput = true }
output(chunk.toString('utf8'))
})
log.silly('search', 'searching packages')
- ms.pipe(entriesStream, outputStream, function (er) {
- if (er) return cb(er)
+ ms.pipe(entriesStream, outputStream, err => {
+ if (err) return cb(err)
if (!anyOutput && !npm.config.get('json') && !npm.config.get('parseable')) {
output('No matches found for ' + (args.map(JSON.stringify).join(' ')))
}
diff --git a/deps/npm/lib/search/all-package-metadata.js b/deps/npm/lib/search/all-package-metadata.js
index 5a27bdbcee..5883def5c7 100644
--- a/deps/npm/lib/search/all-package-metadata.js
+++ b/deps/npm/lib/search/all-package-metadata.js
@@ -1,21 +1,28 @@
'use strict'
-var fs = require('graceful-fs')
-var path = require('path')
-var mkdir = require('mkdirp')
-var chownr = require('chownr')
-var npm = require('../npm.js')
-var log = require('npmlog')
-var cacheFile = require('npm-cache-filename')
-var correctMkdir = require('../utils/correct-mkdir.js')
-var mapToRegistry = require('../utils/map-to-registry.js')
-var jsonstream = require('JSONStream')
-var writeStreamAtomic = require('fs-write-stream-atomic')
-var ms = require('mississippi')
-var sortedUnionStream = require('sorted-union-stream')
-var once = require('once')
-var gunzip = require('../utils/gunzip-maybe')
+const BB = require('bluebird')
+const cacheFile = require('npm-cache-filename')
+const chownr = BB.promisify(require('chownr'))
+const correctMkdir = BB.promisify(require('../utils/correct-mkdir.js'))
+const figgyPudding = require('figgy-pudding')
+const fs = require('graceful-fs')
+const JSONStream = require('JSONStream')
+const log = require('npmlog')
+const mkdir = BB.promisify(require('mkdirp'))
+const ms = require('mississippi')
+const npmFetch = require('libnpm/fetch')
+const path = require('path')
+const sortedUnionStream = require('sorted-union-stream')
+const url = require('url')
+const writeStreamAtomic = require('fs-write-stream-atomic')
+
+const statAsync = BB.promisify(fs.stat)
+
+const APMOpts = figgyPudding({
+ cache: {},
+ registry: {}
+})
// Returns a sorted stream of all package metadata. Internally, takes care of
// maintaining its metadata cache and making partial or full remote requests,
// according to staleness, validity, etc.
@@ -27,63 +34,70 @@ var gunzip = require('../utils/gunzip-maybe')
// 4. It must include all entries that exist in the metadata endpoint as of
// the value in `_updated`
module.exports = allPackageMetadata
-function allPackageMetadata (staleness) {
- var stream = ms.through.obj()
-
- mapToRegistry('-/all', npm.config, function (er, uri, auth) {
- if (er) return stream.emit('error', er)
-
- var cacheBase = cacheFile(npm.config.get('cache'))(uri)
- var cachePath = path.join(cacheBase, '.cache.json')
+function allPackageMetadata (opts) {
+ const staleness = opts.staleness
+ const stream = ms.through.obj()
- createEntryStream(cachePath, uri, auth, staleness, function (err, entryStream, latest, newEntries) {
- if (err) return stream.emit('error', err)
- log.silly('all-package-metadata', 'entry stream created')
- if (entryStream && newEntries) {
- createCacheWriteStream(cachePath, latest, function (err, writeStream) {
- if (err) return stream.emit('error', err)
- log.silly('all-package-metadata', 'output stream created')
- ms.pipeline.obj(entryStream, writeStream, stream)
- })
- } else if (entryStream) {
- ms.pipeline.obj(entryStream, stream)
- } else {
- stream.emit('error', new Error('No search sources available'))
- }
- })
- })
+ opts = APMOpts(opts)
+ const cacheBase = cacheFile(path.resolve(path.dirname(opts.cache)))(url.resolve(opts.registry, '/-/all'))
+ const cachePath = path.join(cacheBase, '.cache.json')
+ createEntryStream(
+ cachePath, staleness, opts
+ ).then(({entryStream, latest, newEntries}) => {
+ log.silly('all-package-metadata', 'entry stream created')
+ if (entryStream && newEntries) {
+ return createCacheWriteStream(cachePath, latest, opts).then(writer => {
+ log.silly('all-package-metadata', 'output stream created')
+ ms.pipeline.obj(entryStream, writer, stream)
+ })
+ } else if (entryStream) {
+ ms.pipeline.obj(entryStream, stream)
+ } else {
+ stream.emit('error', new Error('No search sources available'))
+ }
+ }).catch(err => stream.emit('error', err))
return stream
}
// Creates a stream of the latest available package metadata.
// Metadata will come from a combination of the local cache and remote data.
module.exports._createEntryStream = createEntryStream
-function createEntryStream (cachePath, uri, auth, staleness, cb) {
- createCacheEntryStream(cachePath, function (err, cacheStream, cacheLatest) {
+function createEntryStream (cachePath, staleness, opts) {
+ return createCacheEntryStream(
+ cachePath, opts
+ ).catch(err => {
+ log.warn('', 'Failed to read search cache. Rebuilding')
+ log.silly('all-package-metadata', 'cache read error: ', err)
+ return {}
+ }).then(({
+ updateStream: cacheStream,
+ updatedLatest: cacheLatest
+ }) => {
cacheLatest = cacheLatest || 0
- if (err) {
- log.warn('', 'Failed to read search cache. Rebuilding')
- log.silly('all-package-metadata', 'cache read error: ', err)
- }
- createEntryUpdateStream(uri, auth, staleness, cacheLatest, function (err, updateStream, updatedLatest) {
+ return createEntryUpdateStream(staleness, cacheLatest, opts).catch(err => {
+ log.warn('', 'Search data request failed, search might be stale')
+ log.silly('all-package-metadata', 'update request error: ', err)
+ return {}
+ }).then(({updateStream, updatedLatest}) => {
updatedLatest = updatedLatest || 0
- var latest = updatedLatest || cacheLatest
+ const latest = updatedLatest || cacheLatest
if (!cacheStream && !updateStream) {
- return cb(new Error('No search sources available'))
- }
- if (err) {
- log.warn('', 'Search data request failed, search might be stale')
- log.silly('all-package-metadata', 'update request error: ', err)
+ throw new Error('No search sources available')
}
if (cacheStream && updateStream) {
// Deduped, unioned, sorted stream from the combination of both.
- cb(null,
- createMergedStream(cacheStream, updateStream),
+ return {
+ entryStream: createMergedStream(cacheStream, updateStream),
latest,
- !!updatedLatest)
+ newEntries: !!updatedLatest
+ }
} else {
// Either one works if one or the other failed
- cb(null, cacheStream || updateStream, latest, !!updatedLatest)
+ return {
+ entryStream: cacheStream || updateStream,
+ latest,
+ newEntries: !!updatedLatest
+ }
}
})
})
@@ -96,66 +110,51 @@ function createEntryStream (cachePath, uri, auth, staleness, cb) {
module.exports._createMergedStream = createMergedStream
function createMergedStream (a, b) {
linkStreams(a, b)
- return sortedUnionStream(b, a, function (pkg) { return pkg.name })
+ return sortedUnionStream(b, a, ({name}) => name)
}
// Reads the local index and returns a stream that spits out package data.
module.exports._createCacheEntryStream = createCacheEntryStream
-function createCacheEntryStream (cacheFile, cb) {
+function createCacheEntryStream (cacheFile, opts) {
log.verbose('all-package-metadata', 'creating entry stream from local cache')
log.verbose('all-package-metadata', cacheFile)
- fs.stat(cacheFile, function (err, stat) {
- if (err) return cb(err)
+ return statAsync(cacheFile).then(stat => {
// TODO - This isn't very helpful if `cacheFile` is empty or just `{}`
- var entryStream = ms.pipeline.obj(
+ const entryStream = ms.pipeline.obj(
fs.createReadStream(cacheFile),
- jsonstream.parse('*'),
+ JSONStream.parse('*'),
// I believe this passthrough is necessary cause `jsonstream` returns
// weird custom streams that behave funny sometimes.
ms.through.obj()
)
- extractUpdated(entryStream, 'cached-entry-stream', cb)
+ return extractUpdated(entryStream, 'cached-entry-stream', opts)
})
}
// Stream of entry updates from the server. If `latest` is `0`, streams the
// entire metadata object from the registry.
module.exports._createEntryUpdateStream = createEntryUpdateStream
-function createEntryUpdateStream (all, auth, staleness, latest, cb) {
+function createEntryUpdateStream (staleness, latest, opts) {
log.verbose('all-package-metadata', 'creating remote entry stream')
- var params = {
- timeout: 600,
- follow: true,
- staleOk: true,
- auth: auth,
- streaming: true
- }
- var partialUpdate = false
+ let partialUpdate = false
+ let uri = '/-/all'
if (latest && (Date.now() - latest < (staleness * 1000))) {
// Skip the request altogether if our `latest` isn't stale.
log.verbose('all-package-metadata', 'Local data up to date, skipping update')
- return cb(null)
+ return BB.resolve({})
} else if (latest === 0) {
log.warn('', 'Building the local index for the first time, please be patient')
log.verbose('all-package-metadata', 'No cached data: requesting full metadata db')
} else {
log.verbose('all-package-metadata', 'Cached data present with timestamp:', latest, 'requesting partial index update')
- all += '/since?stale=update_after&startkey=' + latest
+ uri += '/since?stale=update_after&startkey=' + latest
partialUpdate = true
}
- npm.registry.request(all, params, function (er, res) {
- if (er) return cb(er)
+ return npmFetch(uri, opts).then(res => {
log.silly('all-package-metadata', 'request stream opened, code:', res.statusCode)
- // NOTE - The stream returned by `request` seems to be very persnickety
- // and this is almost a magic incantation to get it to work.
- // Modify how `res` is used here at your own risk.
- var entryStream = ms.pipeline.obj(
- res,
- ms.through(function (chunk, enc, cb) {
- cb(null, chunk)
- }),
- gunzip(),
- jsonstream.parse('*', function (pkg, key) {
+ let entryStream = ms.pipeline.obj(
+ res.body,
+ JSONStream.parse('*', (pkg, key) => {
if (key[0] === '_updated' || key[0][0] !== '_') {
return pkg
}
@@ -164,9 +163,12 @@ function createEntryUpdateStream (all, auth, staleness, latest, cb) {
if (partialUpdate) {
// The `/all/since` endpoint doesn't return `_updated`, so we
// just use the request's own timestamp.
- cb(null, entryStream, Date.parse(res.headers.date))
+ return {
+ updateStream: entryStream,
+ updatedLatest: Date.parse(res.headers.get('date'))
+ }
} else {
- extractUpdated(entryStream, 'entry-update-stream', cb)
+ return extractUpdated(entryStream, 'entry-update-stream', opts)
}
})
}
@@ -175,36 +177,37 @@ function createEntryUpdateStream (all, auth, staleness, latest, cb) {
// first returned entries. This is the "latest" unix timestamp for the metadata
// in question. This code does a bit of juggling with the data streams
// so that we can pretend that field doesn't exist, but still extract `latest`
-function extractUpdated (entryStream, label, cb) {
- cb = once(cb)
+function extractUpdated (entryStream, label, opts) {
log.silly('all-package-metadata', 'extracting latest')
- function nope (msg) {
- return function () {
- log.warn('all-package-metadata', label, msg)
- entryStream.removeAllListeners()
- entryStream.destroy()
- cb(new Error(msg))
- }
- }
- var onErr = nope('Failed to read stream')
- var onEnd = nope('Empty or invalid stream')
- entryStream.on('error', onErr)
- entryStream.on('end', onEnd)
- entryStream.once('data', function (latest) {
- log.silly('all-package-metadata', 'got first stream entry for', label, latest)
- entryStream.removeListener('error', onErr)
- entryStream.removeListener('end', onEnd)
- // Because `.once()` unpauses the stream, we re-pause it after the first
- // entry so we don't vomit entries into the void.
- entryStream.pause()
- if (typeof latest === 'number') {
- // The extra pipeline is to return a stream that will implicitly unpause
- // after having an `.on('data')` listener attached, since using this
- // `data` event broke its initial state.
- cb(null, ms.pipeline.obj(entryStream, ms.through.obj()), latest)
- } else {
- cb(new Error('expected first entry to be _updated'))
+ return new BB((resolve, reject) => {
+ function nope (msg) {
+ return function () {
+ log.warn('all-package-metadata', label, msg)
+ entryStream.removeAllListeners()
+ entryStream.destroy()
+ reject(new Error(msg))
+ }
}
+ const onErr = nope('Failed to read stream')
+ const onEnd = nope('Empty or invalid stream')
+ entryStream.on('error', onErr)
+ entryStream.on('end', onEnd)
+ entryStream.once('data', latest => {
+ log.silly('all-package-metadata', 'got first stream entry for', label, latest)
+ entryStream.removeListener('error', onErr)
+ entryStream.removeListener('end', onEnd)
+ if (typeof latest === 'number') {
+ // The extra pipeline is to return a stream that will implicitly unpause
+ // after having an `.on('data')` listener attached, since using this
+ // `data` event broke its initial state.
+ resolve({
+ updateStream: entryStream.pipe(ms.through.obj()),
+ updatedLatest: latest
+ })
+ } else {
+ reject(new Error('expected first entry to be _updated'))
+ }
+ })
})
}
@@ -213,44 +216,43 @@ function extractUpdated (entryStream, label, cb) {
// The stream is also passthrough, so entries going through it will also
// be output from it.
module.exports._createCacheWriteStream = createCacheWriteStream
-function createCacheWriteStream (cacheFile, latest, cb) {
- _ensureCacheDirExists(cacheFile, function (err) {
- if (err) return cb(err)
+function createCacheWriteStream (cacheFile, latest, opts) {
+ return _ensureCacheDirExists(cacheFile, opts).then(() => {
log.silly('all-package-metadata', 'creating output stream')
- var outStream = _createCacheOutStream()
- var cacheFileStream = writeStreamAtomic(cacheFile)
- var inputStream = _createCacheInStream(cacheFileStream, outStream, latest)
+ const outStream = _createCacheOutStream()
+ const cacheFileStream = writeStreamAtomic(cacheFile)
+ const inputStream = _createCacheInStream(
+ cacheFileStream, outStream, latest
+ )
// Glue together the various streams so they fail together.
// `cacheFileStream` errors are already handled by the `inputStream`
// pipeline
- var errEmitted = false
- linkStreams(inputStream, outStream, function () { errEmitted = true })
+ let errEmitted = false
+ linkStreams(inputStream, outStream, () => { errEmitted = true })
- cacheFileStream.on('close', function () { !errEmitted && outStream.end() })
+ cacheFileStream.on('close', () => !errEmitted && outStream.end())
- cb(null, ms.duplex.obj(inputStream, outStream))
+ return ms.duplex.obj(inputStream, outStream)
})
}
-function _ensureCacheDirExists (cacheFile, cb) {
+function _ensureCacheDirExists (cacheFile, opts) {
var cacheBase = path.dirname(cacheFile)
log.silly('all-package-metadata', 'making sure cache dir exists at', cacheBase)
- correctMkdir(npm.cache, function (er, st) {
- if (er) return cb(er)
- mkdir(cacheBase, function (er, made) {
- if (er) return cb(er)
- chownr(made || cacheBase, st.uid, st.gid, cb)
+ return correctMkdir(opts.cache).then(st => {
+ return mkdir(cacheBase).then(made => {
+ return chownr(made || cacheBase, st.uid, st.gid)
})
})
}
function _createCacheOutStream () {
+ // NOTE: this looks goofy, but it's necessary in order to get
+ // JSONStream to play nice with the rest of everything.
return ms.pipeline.obj(
- // These two passthrough `through` streams compensate for some
- // odd behavior with `jsonstream`.
ms.through(),
- jsonstream.parse('*', function (obj, key) {
+ JSONStream.parse('*', (obj, key) => {
// This stream happens to get _updated passed through it, for
// implementation reasons. We make sure to filter it out cause
// the fact that it comes t
@@ -263,9 +265,9 @@ function _createCacheOutStream () {
}
function _createCacheInStream (writer, outStream, latest) {
- var updatedWritten = false
- var inStream = ms.pipeline.obj(
- ms.through.obj(function (pkg, enc, cb) {
+ let updatedWritten = false
+ const inStream = ms.pipeline.obj(
+ ms.through.obj((pkg, enc, cb) => {
if (!updatedWritten && typeof pkg === 'number') {
// This is the `_updated` value getting sent through.
updatedWritten = true
@@ -277,13 +279,11 @@ function _createCacheInStream (writer, outStream, latest) {
cb(null, [pkg.name, pkg])
}
}),
- jsonstream.stringifyObject('{', ',', '}'),
- ms.through(function (chunk, enc, cb) {
+ JSONStream.stringifyObject('{', ',', '}'),
+ ms.through((chunk, enc, cb) => {
// This tees off the buffer data to `outStream`, and then continues
// the pipeline as usual
- outStream.write(chunk, enc, function () {
- cb(null, chunk)
- })
+ outStream.write(chunk, enc, () => cb(null, chunk))
}),
// And finally, we write to the cache file.
writer
@@ -300,14 +300,14 @@ function linkStreams (a, b, cb) {
if (err !== lastError) {
lastError = err
b.emit('error', err)
- cb(err)
+ cb && cb(err)
}
})
b.on('error', function (err) {
if (err !== lastError) {
lastError = err
a.emit('error', err)
- cb(err)
+ cb && cb(err)
}
})
}
diff --git a/deps/npm/lib/search/all-package-search.js b/deps/npm/lib/search/all-package-search.js
index 7a893d517b..fef343bcbc 100644
--- a/deps/npm/lib/search/all-package-search.js
+++ b/deps/npm/lib/search/all-package-search.js
@@ -8,7 +8,7 @@ function allPackageSearch (opts) {
// Get a stream with *all* the packages. This takes care of dealing
// with the local cache as well, but that's an internal detail.
- var allEntriesStream = allPackageMetadata(opts.staleness)
+ var allEntriesStream = allPackageMetadata(opts)
// Grab a stream that filters those packages according to given params.
var filterStream = streamFilter(function (pkg) {
diff --git a/deps/npm/lib/search/esearch.js b/deps/npm/lib/search/esearch.js
deleted file mode 100644
index f4beb7ade6..0000000000
--- a/deps/npm/lib/search/esearch.js
+++ /dev/null
@@ -1,64 +0,0 @@
-'use strict'
-
-var npm = require('../npm.js')
-var log = require('npmlog')
-var mapToRegistry = require('../utils/map-to-registry.js')
-var jsonstream = require('JSONStream')
-var ms = require('mississippi')
-var gunzip = require('../utils/gunzip-maybe')
-
-module.exports = esearch
-
-function esearch (opts) {
- var stream = ms.through.obj()
-
- mapToRegistry('-/v1/search', npm.config, function (er, uri, auth) {
- if (er) return stream.emit('error', er)
- createResultStream(uri, auth, opts, function (err, resultStream) {
- if (err) return stream.emit('error', err)
- ms.pipeline.obj(resultStream, stream)
- })
- })
- return stream
-}
-
-function createResultStream (uri, auth, opts, cb) {
- log.verbose('esearch', 'creating remote entry stream')
- var params = {
- timeout: 600,
- follow: true,
- staleOk: true,
- auth: auth,
- streaming: true
- }
- var q = buildQuery(opts)
- npm.registry.request(uri + '?text=' + encodeURIComponent(q) + '&size=' + opts.limit, params, function (err, res) {
- if (err) return cb(err)
- log.silly('esearch', 'request stream opened, code:', res.statusCode)
- // NOTE - The stream returned by `request` seems to be very persnickety
- // and this is almost a magic incantation to get it to work.
- // Modify how `res` is used here at your own risk.
- var entryStream = ms.pipeline.obj(
- res,
- ms.through(function (chunk, enc, cb) {
- cb(null, chunk)
- }),
- gunzip(),
- jsonstream.parse('objects.*.package', function (data) {
- return {
- name: data.name,
- description: data.description,
- maintainers: data.maintainers,
- keywords: data.keywords,
- version: data.version,
- date: data.date ? new Date(data.date) : null
- }
- })
- )
- return cb(null, entryStream)
- })
-}
-
-function buildQuery (opts) {
- return opts.include.join(' ')
-}
diff --git a/deps/npm/lib/shrinkwrap.js b/deps/npm/lib/shrinkwrap.js
index 90a4426523..dbb12b5bd4 100644
--- a/deps/npm/lib/shrinkwrap.js
+++ b/deps/npm/lib/shrinkwrap.js
@@ -167,6 +167,8 @@ function childVersion (top, child, req) {
function childRequested (top, child, requested) {
if (requested.type === 'directory' || requested.type === 'file') {
return 'file:' + unixFormatPath(path.relative(top.path, child.package._resolved || requested.fetchSpec))
+ } else if (requested.type === 'git' && child.package._from) {
+ return child.package._from
} else if (!isRegistry(requested) && !child.fromBundle) {
return child.package._resolved || requested.saveSpec || requested.rawSpec
} else if (requested.type === 'tag') {
diff --git a/deps/npm/lib/star.js b/deps/npm/lib/star.js
index f19cb4b07b..44a762b15c 100644
--- a/deps/npm/lib/star.js
+++ b/deps/npm/lib/star.js
@@ -1,11 +1,20 @@
-module.exports = star
+'use strict'
+
+const BB = require('bluebird')
+
+const fetch = require('libnpm/fetch')
+const figgyPudding = require('figgy-pudding')
+const log = require('npmlog')
+const npa = require('libnpm/parse-arg')
+const npm = require('./npm.js')
+const npmConfig = require('./config/figgy-config.js')
+const output = require('./utils/output.js')
+const usage = require('./utils/usage.js')
+const whoami = require('./whoami.js')
-var npm = require('./npm.js')
-var log = require('npmlog')
-var asyncMap = require('slide').asyncMap
-var mapToRegistry = require('./utils/map-to-registry.js')
-var usage = require('./utils/usage')
-var output = require('./utils/output.js')
+const StarConfig = figgyPudding({
+ 'unicode': {}
+})
star.usage = usage(
'star',
@@ -19,27 +28,50 @@ star.completion = function (opts, cb) {
cb()
}
+module.exports = star
function star (args, cb) {
- if (!args.length) return cb(star.usage)
- var s = npm.config.get('unicode') ? '\u2605 ' : '(*)'
- var u = npm.config.get('unicode') ? '\u2606 ' : '( )'
- var using = !(npm.command.match(/^un/))
- if (!using) s = u
- asyncMap(args, function (pkg, cb) {
- mapToRegistry(pkg, npm.config, function (er, uri, auth) {
- if (er) return cb(er)
+ const opts = StarConfig(npmConfig())
+ return BB.try(() => {
+ if (!args.length) throw new Error(star.usage)
+ let s = opts.unicode ? '\u2605 ' : '(*)'
+ const u = opts.unicode ? '\u2606 ' : '( )'
+ const using = !(npm.command.match(/^un/))
+ if (!using) s = u
+ return BB.map(args.map(npa), pkg => {
+ return BB.all([
+ whoami([pkg], true, () => {}),
+ fetch.json(pkg.escapedName, opts.concat({
+ spec: pkg,
+ query: {write: true},
+ 'prefer-online': true
+ }))
+ ]).then(([username, fullData]) => {
+ if (!username) { throw new Error('You need to be logged in!') }
+ const body = {
+ _id: fullData._id,
+ _rev: fullData._rev,
+ users: fullData.users || {}
+ }
- var params = {
- starred: using,
- auth: auth
- }
- npm.registry.star(uri, params, function (er, data, raw, req) {
- if (!er) {
- output(s + ' ' + pkg)
- log.verbose('star', data)
+ if (using) {
+ log.info('star', 'starring', body._id)
+ body.users[username] = true
+ log.verbose('star', 'starring', body)
+ } else {
+ delete body.users[username]
+ log.info('star', 'unstarring', body._id)
+ log.verbose('star', 'unstarring', body)
}
- cb(er, data, raw, req)
+ return fetch.json(pkg.escapedName, opts.concat({
+ spec: pkg,
+ method: 'PUT',
+ body
+ }))
+ }).then(data => {
+ output(s + ' ' + pkg.name)
+ log.verbose('star', data)
+ return data
})
})
- }, cb)
+ }).nodeify(cb)
}
diff --git a/deps/npm/lib/stars.js b/deps/npm/lib/stars.js
index 4771079356..ea3581f1d4 100644
--- a/deps/npm/lib/stars.js
+++ b/deps/npm/lib/stars.js
@@ -1,47 +1,37 @@
-module.exports = stars
-
-stars.usage = 'npm stars [<user>]'
-
-var npm = require('./npm.js')
-var log = require('npmlog')
-var mapToRegistry = require('./utils/map-to-registry.js')
-var output = require('./utils/output.js')
+'use strict'
-function stars (args, cb) {
- npm.commands.whoami([], true, function (er, username) {
- var name = args.length === 1 ? args[0] : username
+const BB = require('bluebird')
- if (er) {
- if (er.code === 'ENEEDAUTH' && !name) {
- var needAuth = new Error("'npm stars' on your own user account requires auth")
- needAuth.code = 'ENEEDAUTH'
- return cb(needAuth)
- }
-
- if (er.code !== 'ENEEDAUTH') return cb(er)
- }
+const npmConfig = require('./config/figgy-config.js')
+const fetch = require('libnpm/fetch')
+const log = require('npmlog')
+const output = require('./utils/output.js')
+const whoami = require('./whoami.js')
- mapToRegistry('', npm.config, function (er, uri, auth) {
- if (er) return cb(er)
+stars.usage = 'npm stars [<user>]'
- var params = {
- username: name,
- auth: auth
+module.exports = stars
+function stars ([user], cb) {
+ const opts = npmConfig()
+ return BB.try(() => {
+ return (user ? BB.resolve(user) : whoami([], true, () => {})).then(usr => {
+ return fetch.json('/-/_view/starredByUser', opts.concat({
+ query: {key: `"${usr}"`} // WHY. WHY THE ""?!
+ }))
+ }).then(data => data.rows).then(stars => {
+ if (stars.length === 0) {
+ log.warn('stars', 'user has not starred any packages.')
+ } else {
+ stars.forEach(s => output(s.value))
}
- npm.registry.stars(uri, params, showstars)
})
- })
-
- function showstars (er, data) {
- if (er) return cb(er)
-
- if (data.rows.length === 0) {
- log.warn('stars', 'user has not starred any packages.')
- } else {
- data.rows.forEach(function (a) {
- output(a.value)
+ }).catch(err => {
+ if (err.code === 'ENEEDAUTH') {
+ throw Object.assign(new Error("'npm starts' on your own user account requires auth"), {
+ code: 'ENEEDAUTH'
})
+ } else {
+ throw err
}
- cb()
- }
+ }).nodeify(cb)
}
diff --git a/deps/npm/lib/team.js b/deps/npm/lib/team.js
index 2d9e61cd43..2b56e3b14f 100644
--- a/deps/npm/lib/team.js
+++ b/deps/npm/lib/team.js
@@ -1,19 +1,37 @@
/* eslint-disable standard/no-callback-literal */
-var mapToRegistry = require('./utils/map-to-registry.js')
-var npm = require('./npm')
-var output = require('./utils/output.js')
+
+const columns = require('cli-columns')
+const figgyPudding = require('figgy-pudding')
+const libteam = require('libnpm/team')
+const npmConfig = require('./config/figgy-config.js')
+const output = require('./utils/output.js')
+const otplease = require('./utils/otplease.js')
+const usage = require('./utils/usage')
module.exports = team
team.subcommands = ['create', 'destroy', 'add', 'rm', 'ls', 'edit']
-team.usage =
+team.usage = usage(
+ 'team',
'npm team create <scope:team>\n' +
'npm team destroy <scope:team>\n' +
'npm team add <scope:team> <user>\n' +
'npm team rm <scope:team> <user>\n' +
'npm team ls <scope>|<scope:team>\n' +
'npm team edit <scope:team>'
+)
+
+const TeamConfig = figgyPudding({
+ json: {},
+ loglevel: {},
+ parseable: {},
+ silent: {}
+})
+
+function UsageError () {
+ throw Object.assign(new Error(team.usage), {code: 'EUSAGE'})
+}
team.completion = function (opts, cb) {
var argv = opts.conf.argv.remain
@@ -33,24 +51,121 @@ team.completion = function (opts, cb) {
}
}
-function team (args, cb) {
+function team ([cmd, entity = '', user = ''], cb) {
// Entities are in the format <scope>:<team>
- var cmd = args.shift()
- var entity = (args.shift() || '').split(':')
- return mapToRegistry('/', npm.config, function (err, uri, auth) {
- if (err) { return cb(err) }
- try {
- return npm.registry.team(cmd, uri, {
- auth: auth,
- scope: entity[0].replace(/^@/, ''), // '@' prefix on scope is optional.
- team: entity[1],
- user: args.shift()
- }, function (err, data) {
- !err && data && output(JSON.stringify(data, undefined, 2))
- cb(err, data)
- })
- } catch (e) {
- cb(e.message + '\n\nUsage:\n' + team.usage)
+ otplease(npmConfig(), opts => {
+ opts = TeamConfig(opts).concat({description: null})
+ entity = entity.replace(/^@/, '')
+ switch (cmd) {
+ case 'create': return teamCreate(entity, opts)
+ case 'destroy': return teamDestroy(entity, opts)
+ case 'add': return teamAdd(entity, user, opts)
+ case 'rm': return teamRm(entity, user, opts)
+ case 'ls': {
+ const match = entity.match(/[^:]+:.+/)
+ if (match) {
+ return teamListUsers(entity, opts)
+ } else {
+ return teamListTeams(entity, opts)
+ }
+ }
+ case 'edit':
+ throw new Error('`npm team edit` is not implemented yet.')
+ default:
+ UsageError()
+ }
+ }).then(
+ data => cb(null, data),
+ err => err.code === 'EUSAGE' ? cb(err.message) : cb(err)
+ )
+}
+
+function teamCreate (entity, opts) {
+ return libteam.create(entity, opts).then(() => {
+ if (opts.json) {
+ output(JSON.stringify({
+ created: true,
+ team: entity
+ }))
+ } else if (opts.parseable) {
+ output(`${entity}\tcreated`)
+ } else if (!opts.silent && opts.loglevel !== 'silent') {
+ output(`+@${entity}`)
+ }
+ })
+}
+
+function teamDestroy (entity, opts) {
+ return libteam.destroy(entity, opts).then(() => {
+ if (opts.json) {
+ output(JSON.stringify({
+ deleted: true,
+ team: entity
+ }))
+ } else if (opts.parseable) {
+ output(`${entity}\tdeleted`)
+ } else if (!opts.silent && opts.loglevel !== 'silent') {
+ output(`-@${entity}`)
+ }
+ })
+}
+
+function teamAdd (entity, user, opts) {
+ return libteam.add(user, entity, opts).then(() => {
+ if (opts.json) {
+ output(JSON.stringify({
+ added: true,
+ team: entity,
+ user
+ }))
+ } else if (opts.parseable) {
+ output(`${user}\t${entity}\tadded`)
+ } else if (!opts.silent && opts.loglevel !== 'silent') {
+ output(`${user} added to @${entity}`)
+ }
+ })
+}
+
+function teamRm (entity, user, opts) {
+ return libteam.rm(user, entity, opts).then(() => {
+ if (opts.json) {
+ output(JSON.stringify({
+ removed: true,
+ team: entity,
+ user
+ }))
+ } else if (opts.parseable) {
+ output(`${user}\t${entity}\tremoved`)
+ } else if (!opts.silent && opts.loglevel !== 'silent') {
+ output(`${user} removed from @${entity}`)
+ }
+ })
+}
+
+function teamListUsers (entity, opts) {
+ return libteam.lsUsers(entity, opts).then(users => {
+ users = users.sort()
+ if (opts.json) {
+ output(JSON.stringify(users, null, 2))
+ } else if (opts.parseable) {
+ output(users.join('\n'))
+ } else if (!opts.silent && opts.loglevel !== 'silent') {
+ output(`\n@${entity} has ${users.length} user${users.length === 1 ? '' : 's'}:\n`)
+ output(columns(users, {padding: 1}))
+ }
+ })
+}
+
+function teamListTeams (entity, opts) {
+ return libteam.lsTeams(entity, opts).then(teams => {
+ teams = teams.sort()
+ if (opts.json) {
+ output(JSON.stringify(teams, null, 2))
+ } else if (opts.parseable) {
+ output(teams.join('\n'))
+ } else if (!opts.silent && opts.loglevel !== 'silent') {
+ output(`\n@${entity} has ${teams.length} team${teams.length === 1 ? '' : 's'}:\n`)
+ output(columns(teams.map(t => `@${t}`), {padding: 1}))
}
})
}
diff --git a/deps/npm/lib/token.js b/deps/npm/lib/token.js
index d442d37eb8..cccbba2f9a 100644
--- a/deps/npm/lib/token.js
+++ b/deps/npm/lib/token.js
@@ -1,5 +1,5 @@
'use strict'
-const profile = require('npm-profile')
+const profile = require('libnpm/profile')
const npm = require('./npm.js')
const output = require('./utils/output.js')
const Table = require('cli-table3')
diff --git a/deps/npm/lib/unpublish.js b/deps/npm/lib/unpublish.js
index c2e9edd800..bf5867a268 100644
--- a/deps/npm/lib/unpublish.js
+++ b/deps/npm/lib/unpublish.js
@@ -1,119 +1,110 @@
/* eslint-disable standard/no-callback-literal */
+'use strict'
module.exports = unpublish
-var log = require('npmlog')
-var npm = require('./npm.js')
-var readJson = require('read-package-json')
-var path = require('path')
-var mapToRegistry = require('./utils/map-to-registry.js')
-var npa = require('npm-package-arg')
-var getPublishConfig = require('./utils/get-publish-config.js')
-var output = require('./utils/output.js')
-
-unpublish.usage = 'npm unpublish [<@scope>/]<pkg>[@<version>]'
-
-unpublish.completion = function (opts, cb) {
- if (opts.conf.argv.remain.length >= 3) return cb()
- npm.commands.whoami([], true, function (er, username) {
- if (er) return cb()
-
- var un = encodeURIComponent(username)
- if (!un) return cb()
- var byUser = '-/by-user/' + un
- mapToRegistry(byUser, npm.config, function (er, uri, auth) {
- if (er) return cb(er)
-
- npm.registry.get(uri, { auth: auth }, function (er, pkgs) {
- // do a bit of filtering at this point, so that we don't need
- // to fetch versions for more than one thing, but also don't
- // accidentally a whole project.
- pkgs = pkgs[un]
- if (!pkgs || !pkgs.length) return cb()
- var pp = npa(opts.partialWord).name
- pkgs = pkgs.filter(function (p) {
- return p.indexOf(pp) === 0
- })
- if (pkgs.length > 1) return cb(null, pkgs)
- mapToRegistry(pkgs[0], npm.config, function (er, uri, auth) {
- if (er) return cb(er)
+const BB = require('bluebird')
+
+const figgyPudding = require('figgy-pudding')
+const libaccess = require('libnpm/access')
+const libunpub = require('libnpm/unpublish')
+const log = require('npmlog')
+const npa = require('npm-package-arg')
+const npm = require('./npm.js')
+const npmConfig = require('./config/figgy-config.js')
+const npmFetch = require('npm-registry-fetch')
+const otplease = require('./utils/otplease.js')
+const output = require('./utils/output.js')
+const path = require('path')
+const readJson = BB.promisify(require('read-package-json'))
+const usage = require('./utils/usage.js')
+const whoami = BB.promisify(require('./whoami.js'))
+
+unpublish.usage = usage('npm unpublish [<@scope>/]<pkg>[@<version>]')
+
+function UsageError () {
+ throw Object.assign(new Error(`Usage: ${unpublish.usage}`), {
+ code: 'EUSAGE'
+ })
+}
- npm.registry.get(uri, { auth: auth }, function (er, d) {
- if (er) return cb(er)
- var vers = Object.keys(d.versions)
- if (!vers.length) return cb(null, pkgs)
- return cb(null, vers.map(function (v) {
- return pkgs[0] + '@' + v
- }))
- })
- })
+const UnpublishConfig = figgyPudding({
+ force: {},
+ loglevel: {},
+ silent: {}
+})
+
+unpublish.completion = function (cliOpts, cb) {
+ if (cliOpts.conf.argv.remain.length >= 3) return cb()
+
+ whoami([], true).then(username => {
+ if (!username) { return [] }
+ const opts = UnpublishConfig(npmConfig())
+ return libaccess.lsPackages(username, opts).then(access => {
+ // do a bit of filtering at this point, so that we don't need
+ // to fetch versions for more than one thing, but also don't
+ // accidentally a whole project.
+ let pkgs = Object.keys(access)
+ if (!cliOpts.partialWord || !pkgs.length) { return pkgs }
+ const pp = npa(cliOpts.partialWord).name
+ pkgs = pkgs.filter(p => !p.indexOf(pp))
+ if (pkgs.length > 1) return pkgs
+ return npmFetch.json(npa(pkgs[0]).escapedName, opts).then(doc => {
+ const vers = Object.keys(doc.versions)
+ if (!vers.length) {
+ return pkgs
+ } else {
+ return vers.map(v => `${pkgs[0]}@${v}`)
+ }
})
})
- })
+ }).nodeify(cb)
}
function unpublish (args, cb) {
if (args.length > 1) return cb(unpublish.usage)
- var thing = args.length ? npa(args[0]) : {}
- var project = thing.name
- var version = thing.rawSpec
-
- log.silly('unpublish', 'args[0]', args[0])
- log.silly('unpublish', 'thing', thing)
- if (!version && !npm.config.get('force')) {
- return cb(
- 'Refusing to delete entire project.\n' +
- 'Run with --force to do this.\n' +
- unpublish.usage
- )
- }
-
- if (!project || path.resolve(project) === npm.localPrefix) {
- // if there's a package.json in the current folder, then
- // read the package name and version out of that.
- var cwdJson = path.join(npm.localPrefix, 'package.json')
- return readJson(cwdJson, function (er, data) {
- if (er && er.code !== 'ENOENT' && er.code !== 'ENOTDIR') return cb(er)
- if (er) return cb('Usage:\n' + unpublish.usage)
- log.verbose('unpublish', data)
- gotProject(data.name, data.version, data.publishConfig, cb)
- })
- }
- return gotProject(project, version, cb)
-}
-
-function gotProject (project, version, publishConfig, cb_) {
- if (typeof cb_ !== 'function') {
- cb_ = publishConfig
- publishConfig = null
- }
-
- function cb (er) {
- if (er) return cb_(er)
- output('- ' + project + (version ? '@' + version : ''))
- cb_()
- }
-
- var mappedConfig = getPublishConfig(publishConfig, npm.config, npm.registry)
- var config = mappedConfig.config
- var registry = mappedConfig.client
-
- // remove from the cache first
- // npm.commands.cache(['clean', project, version], function (er) {
- // if (er) {
- // log.error('unpublish', 'Failed to clean cache')
- // return cb(er)
- // }
-
- mapToRegistry(project, config, function (er, uri, auth) {
- if (er) return cb(er)
-
- var params = {
- version: version,
- auth: auth
+ const spec = args.length && npa(args[0])
+ const opts = UnpublishConfig(npmConfig())
+ const version = spec.rawSpec
+ BB.try(() => {
+ log.silly('unpublish', 'args[0]', args[0])
+ log.silly('unpublish', 'spec', spec)
+ if (!version && !opts.force) {
+ throw Object.assign(new Error(
+ 'Refusing to delete entire project.\n' +
+ 'Run with --force to do this.\n' +
+ unpublish.usage
+ ), {code: 'EUSAGE'})
}
- registry.unpublish(uri, params, cb)
- })
- // })
+ if (!spec || path.resolve(spec.name) === npm.localPrefix) {
+ // if there's a package.json in the current folder, then
+ // read the package name and version out of that.
+ const cwdJson = path.join(npm.localPrefix, 'package.json')
+ return readJson(cwdJson).then(data => {
+ log.verbose('unpublish', data)
+ return otplease(opts, opts => {
+ return libunpub(npa.resolve(data.name, data.version), opts.concat(data.publishConfig))
+ })
+ }, err => {
+ if (err && err.code !== 'ENOENT' && err.code !== 'ENOTDIR') {
+ throw err
+ } else {
+ UsageError()
+ }
+ })
+ } else {
+ return otplease(opts, opts => libunpub(spec, opts))
+ }
+ }).then(
+ ret => {
+ if (!opts.silent && opts.loglevel !== 'silent') {
+ output(`-${spec.name}${
+ spec.type === 'version' ? `@${spec.rawSpec}` : ''
+ }`)
+ }
+ cb(null, ret)
+ },
+ err => err.code === 'EUSAGE' ? cb(err.message) : cb(err)
+ )
}
diff --git a/deps/npm/lib/utils/error-handler.js b/deps/npm/lib/utils/error-handler.js
index c6481abf67..ba9d9f8e25 100644
--- a/deps/npm/lib/utils/error-handler.js
+++ b/deps/npm/lib/utils/error-handler.js
@@ -202,7 +202,7 @@ function errorHandler (er) {
msg.summary.concat(msg.detail).forEach(function (errline) {
log.error.apply(log, errline)
})
- if (npm.config.get('json')) {
+ if (npm.config && npm.config.get('json')) {
var error = {
error: {
code: er.code,
diff --git a/deps/npm/lib/utils/error-message.js b/deps/npm/lib/utils/error-message.js
index 6e14898183..55c5463454 100644
--- a/deps/npm/lib/utils/error-message.js
+++ b/deps/npm/lib/utils/error-message.js
@@ -103,8 +103,7 @@ function errorMessage (er) {
case 'EOTP':
case 'E401':
- // the E401 message checking is a hack till we replace npm-registry-client with something
- // OTP aware.
+ // E401 is for places where we accidentally neglect OTP stuff
if (er.code === 'EOTP' || /one-time pass/.test(er.message)) {
short.push(['', 'This operation requires a one-time password from your authenticator.'])
detail.push([
diff --git a/deps/npm/lib/utils/get-publish-config.js b/deps/npm/lib/utils/get-publish-config.js
deleted file mode 100644
index ac0ef09342..0000000000
--- a/deps/npm/lib/utils/get-publish-config.js
+++ /dev/null
@@ -1,29 +0,0 @@
-'use strict'
-
-const clientConfig = require('../config/reg-client.js')
-const Conf = require('../config/core.js').Conf
-const log = require('npmlog')
-const npm = require('../npm.js')
-const RegClient = require('npm-registry-client')
-
-module.exports = getPublishConfig
-
-function getPublishConfig (publishConfig, defaultConfig, defaultClient) {
- let config = defaultConfig
- let client = defaultClient
- log.verbose('getPublishConfig', publishConfig)
- if (publishConfig) {
- config = new Conf(defaultConfig)
- config.save = defaultConfig.save.bind(defaultConfig)
-
- // don't modify the actual publishConfig object, in case we have
- // to set a login token or some other data.
- config.unshift(Object.keys(publishConfig).reduce(function (s, k) {
- s[k] = publishConfig[k]
- return s
- }, {}))
- client = new RegClient(clientConfig(npm, log, config))
- }
-
- return { config: config, client: client }
-}
diff --git a/deps/npm/lib/utils/map-to-registry.js b/deps/npm/lib/utils/map-to-registry.js
deleted file mode 100644
index d6e0a5b01f..0000000000
--- a/deps/npm/lib/utils/map-to-registry.js
+++ /dev/null
@@ -1,103 +0,0 @@
-var url = require('url')
-
-var log = require('npmlog')
-var npa = require('npm-package-arg')
-var config
-
-module.exports = mapToRegistry
-
-function mapToRegistry (name, config, cb) {
- log.silly('mapToRegistry', 'name', name)
- var registry
-
- // the name itself takes precedence
- var data = npa(name)
- if (data.scope) {
- // the name is definitely scoped, so escape now
- name = name.replace('/', '%2f')
-
- log.silly('mapToRegistry', 'scope (from package name)', data.scope)
-
- registry = config.get(data.scope + ':registry')
- if (!registry) {
- log.verbose('mapToRegistry', 'no registry URL found in name for scope', data.scope)
- }
- }
-
- // ...then --scope=@scope or --scope=scope
- var scope = config.get('scope')
- if (!registry && scope) {
- // I'm an enabler, sorry
- if (scope.charAt(0) !== '@') scope = '@' + scope
-
- log.silly('mapToRegistry', 'scope (from config)', scope)
-
- registry = config.get(scope + ':registry')
- if (!registry) {
- log.verbose('mapToRegistry', 'no registry URL found in config for scope', scope)
- }
- }
-
- // ...and finally use the default registry
- if (!registry) {
- log.silly('mapToRegistry', 'using default registry')
- registry = config.get('registry')
- }
-
- log.silly('mapToRegistry', 'registry', registry)
-
- var auth = config.getCredentialsByURI(registry)
-
- // normalize registry URL so resolution doesn't drop a piece of registry URL
- var normalized = registry.slice(-1) !== '/' ? registry + '/' : registry
- var uri
- log.silly('mapToRegistry', 'data', data)
- if (data.type === 'remote') {
- uri = data.fetchSpec
- } else {
- uri = url.resolve(normalized, name)
- }
-
- log.silly('mapToRegistry', 'uri', uri)
-
- cb(null, uri, scopeAuth(uri, registry, auth), normalized)
-}
-
-function scopeAuth (uri, registry, auth) {
- var cleaned = {
- scope: auth.scope,
- email: auth.email,
- alwaysAuth: auth.alwaysAuth,
- token: undefined,
- username: undefined,
- password: undefined,
- auth: undefined
- }
-
- var requestHost
- var registryHost
-
- if (auth.token || auth.auth || (auth.username && auth.password)) {
- requestHost = url.parse(uri).hostname
- registryHost = url.parse(registry).hostname
-
- if (requestHost === registryHost) {
- cleaned.token = auth.token
- cleaned.auth = auth.auth
- cleaned.username = auth.username
- cleaned.password = auth.password
- } else if (auth.alwaysAuth) {
- log.verbose('scopeAuth', 'alwaysAuth set for', registry)
- cleaned.token = auth.token
- cleaned.auth = auth.auth
- cleaned.username = auth.username
- cleaned.password = auth.password
- } else {
- log.silly('scopeAuth', uri, "doesn't share host with registry", registry)
- }
- if (!config) config = require('../npm').config
- if (config.get('otp')) cleaned.otp = config.get('otp')
- }
-
- return cleaned
-}
diff --git a/deps/npm/lib/utils/metrics.js b/deps/npm/lib/utils/metrics.js
index c51136e78c..0f99c841db 100644
--- a/deps/npm/lib/utils/metrics.js
+++ b/deps/npm/lib/utils/metrics.js
@@ -4,12 +4,13 @@ exports.stop = stopMetrics
exports.save = saveMetrics
exports.send = sendMetrics
-var fs = require('fs')
-var path = require('path')
-var npm = require('../npm.js')
-var uuid = require('uuid')
+const fs = require('fs')
+const path = require('path')
+const npm = require('../npm.js')
+const regFetch = require('libnpm/fetch')
+const uuid = require('uuid')
-var inMetrics = false
+let inMetrics = false
function startMetrics () {
if (inMetrics) return
@@ -59,15 +60,18 @@ function saveMetrics (itWorked) {
function sendMetrics (metricsFile, metricsRegistry) {
inMetrics = true
var cliMetrics = JSON.parse(fs.readFileSync(metricsFile))
- npm.load({}, function (err) {
- if (err) return
- npm.registry.config.retry.retries = 0
- npm.registry.sendAnonymousCLIMetrics(metricsRegistry, cliMetrics, function (err) {
- if (err) {
- fs.writeFileSync(path.join(path.dirname(metricsFile), 'last-send-metrics-error.txt'), err.stack)
- } else {
- fs.unlinkSync(metricsFile)
- }
- })
+ regFetch(
+ `/-/npm/anon-metrics/v1/${encodeURIComponent(cliMetrics.metricId)}`,
+ // NOTE: skip npmConfig() to prevent auth
+ {
+ registry: metricsRegistry,
+ method: 'PUT',
+ body: cliMetrics.metrics,
+ retry: false
+ }
+ ).then(() => {
+ fs.unlinkSync(metricsFile)
+ }, err => {
+ fs.writeFileSync(path.join(path.dirname(metricsFile), 'last-send-metrics-error.txt'), err.stack)
})
}
diff --git a/deps/npm/lib/utils/otplease.js b/deps/npm/lib/utils/otplease.js
new file mode 100644
index 0000000000..d0477a896d
--- /dev/null
+++ b/deps/npm/lib/utils/otplease.js
@@ -0,0 +1,27 @@
+'use strict'
+
+const BB = require('bluebird')
+
+const optCheck = require('figgy-pudding')({
+ prompt: {default: 'This operation requires a one-time password.\nEnter OTP:'},
+ otp: {}
+})
+const readUserInfo = require('./read-user-info.js')
+
+module.exports = otplease
+function otplease (opts, fn) {
+ opts = opts.concat ? opts : optCheck(opts)
+ return BB.try(() => {
+ return fn(opts)
+ }).catch(err => {
+ if (err.code !== 'EOTP' && !(err.code === 'E401' && /one-time pass/.test(err.body))) {
+ throw err
+ } else if (!process.stdin.isTTY || !process.stdout.isTTY) {
+ throw err
+ } else {
+ return readUserInfo.otp(
+ optCheck(opts).prompt
+ ).then(otp => fn(opts.concat({otp})))
+ }
+ })
+}
diff --git a/deps/npm/lib/view.js b/deps/npm/lib/view.js
index b7d7f6ec80..5dd605029b 100644
--- a/deps/npm/lib/view.js
+++ b/deps/npm/lib/view.js
@@ -8,17 +8,27 @@ const BB = require('bluebird')
const byteSize = require('byte-size')
const color = require('ansicolors')
const columns = require('cli-columns')
+const npmConfig = require('./config/figgy-config.js')
+const log = require('npmlog')
+const figgyPudding = require('figgy-pudding')
+const npa = require('libnpm/parse-arg')
+const npm = require('./npm.js')
+const packument = require('libnpm/packument')
+const path = require('path')
+const readJson = require('libnpm/read-json')
const relativeDate = require('tiny-relative-date')
+const semver = require('semver')
const style = require('ansistyles')
-var npm = require('./npm.js')
-var readJson = require('read-package-json')
-var log = require('npmlog')
-var util = require('util')
-var semver = require('semver')
-var mapToRegistry = require('./utils/map-to-registry.js')
-var npa = require('npm-package-arg')
-var path = require('path')
-var usage = require('./utils/usage')
+const usage = require('./utils/usage')
+const util = require('util')
+const validateName = require('validate-npm-package-name')
+
+const ViewConfig = figgyPudding({
+ global: {},
+ json: {},
+ tag: {},
+ unicode: {}
+})
view.usage = usage(
'view',
@@ -32,19 +42,14 @@ view.completion = function (opts, cb) {
return cb()
}
// have the package, get the fields.
- var tag = npm.config.get('tag')
- mapToRegistry(opts.conf.argv.remain[2], npm.config, function (er, uri, auth) {
- if (er) return cb(er)
-
- npm.registry.get(uri, { auth: auth }, function (er, d) {
- if (er) return cb(er)
- var dv = d.versions[d['dist-tags'][tag]]
- var fields = []
- d.versions = Object.keys(d.versions).sort(semver.compareLoose)
- fields = getFields(d).concat(getFields(dv))
- cb(null, fields)
- })
- })
+ const config = ViewConfig(npmConfig())
+ const tag = config.tag
+ const spec = npa(opts.conf.argv.remain[2])
+ return packument(spec, config).then(d => {
+ const dv = d.versions[d['dist-tags'][tag]]
+ d.versions = Object.keys(d.versions).sort(semver.compareLoose)
+ return getFields(d).concat(getFields(dv))
+ }).nodeify(cb)
function getFields (d, f, pref) {
f = f || []
@@ -52,11 +57,11 @@ view.completion = function (opts, cb) {
pref = pref || []
Object.keys(d).forEach(function (k) {
if (k.charAt(0) === '_' || k.indexOf('.') !== -1) return
- var p = pref.concat(k).join('.')
+ const p = pref.concat(k).join('.')
f.push(p)
if (Array.isArray(d[k])) {
d[k].forEach(function (val, i) {
- var pi = p + '[' + i + ']'
+ const pi = p + '[' + i + ']'
if (val && typeof val === 'object') getFields(val, f, [p])
else f.push(pi)
})
@@ -76,113 +81,132 @@ function view (args, silent, cb) {
if (!args.length) args = ['.']
- var pkg = args.shift()
- var nv
+ const opts = ViewConfig(npmConfig())
+ const pkg = args.shift()
+ let nv
if (/^[.]@/.test(pkg)) {
nv = npa.resolve(null, pkg.slice(2))
} else {
nv = npa(pkg)
}
- var name = nv.name
- var local = (name === '.' || !name)
+ const name = nv.name
+ const local = (name === '.' || !name)
- if (npm.config.get('global') && local) {
+ if (opts.global && local) {
return cb(new Error('Cannot use view command in global mode.'))
}
if (local) {
- var dir = npm.prefix
- readJson(path.resolve(dir, 'package.json'), function (er, d) {
+ const dir = npm.prefix
+ BB.resolve(readJson(path.resolve(dir, 'package.json'))).nodeify((er, d) => {
d = d || {}
if (er && er.code !== 'ENOENT' && er.code !== 'ENOTDIR') return cb(er)
if (!d.name) return cb(new Error('Invalid package.json'))
- var p = d.name
+ const p = d.name
nv = npa(p)
if (pkg && ~pkg.indexOf('@')) {
nv.rawSpec = pkg.split('@')[pkg.indexOf('@')]
}
- fetchAndRead(nv, args, silent, cb)
+ fetchAndRead(nv, args, silent, opts, cb)
})
} else {
- fetchAndRead(nv, args, silent, cb)
+ fetchAndRead(nv, args, silent, opts, cb)
}
}
-function fetchAndRead (nv, args, silent, cb) {
+function fetchAndRead (nv, args, silent, opts, cb) {
// get the data about this package
- var name = nv.name
- var version = nv.rawSpec || npm.config.get('tag')
-
- mapToRegistry(name, npm.config, function (er, uri, auth) {
- if (er) return cb(er)
-
- npm.registry.get(uri, { auth: auth }, function (er, data) {
- if (er) return cb(er)
- if (data['dist-tags'] && data['dist-tags'][version]) {
- version = data['dist-tags'][version]
- }
-
- if (data.time && data.time.unpublished) {
- var u = data.time.unpublished
- er = new Error('Unpublished by ' + u.name + ' on ' + u.time)
- er.statusCode = 404
- er.code = 'E404'
- er.pkgid = data._id
- return cb(er, data)
+ let version = nv.rawSpec || npm.config.get('tag')
+
+ return packument(nv, opts.concat({
+ fullMetadata: true,
+ 'prefer-online': true
+ })).catch(err => {
+ // TODO - this should probably go into pacote, but the tests expect it.
+ if (err.code === 'E404') {
+ err.message = `'${nv.name}' is not in the npm registry.`
+ const validated = validateName(nv.name)
+ if (!validated.validForNewPackages) {
+ err.message += '\n'
+ err.message += (validated.errors || []).join('\n')
+ err.message += (validated.warnings || []).join('\n')
+ } else {
+ err.message += '\nYou should bug the author to publish it'
+ err.message += '\n(or use the name yourself!)'
+ err.message += '\n'
+ err.message += '\nNote that you can also install from a'
+ err.message += '\ntarball, folder, http url, or git url.'
}
+ }
+ throw err
+ }).then(data => {
+ if (data['dist-tags'] && data['dist-tags'][version]) {
+ version = data['dist-tags'][version]
+ }
- var results = []
- var error = null
- var versions = data.versions || {}
- data.versions = Object.keys(versions).sort(semver.compareLoose)
- if (!args.length) args = ['']
+ if (data.time && data.time.unpublished) {
+ const u = data.time.unpublished
+ let er = new Error('Unpublished by ' + u.name + ' on ' + u.time)
+ er.statusCode = 404
+ er.code = 'E404'
+ er.pkgid = data._id
+ throw er
+ }
- // remove readme unless we asked for it
- if (args.indexOf('readme') === -1) {
- delete data.readme
- }
+ const results = []
+ let error = null
+ const versions = data.versions || {}
+ data.versions = Object.keys(versions).sort(semver.compareLoose)
+ if (!args.length) args = ['']
- Object.keys(versions).forEach(function (v) {
- if (semver.satisfies(v, version, true)) {
- args.forEach(function (args) {
- // remove readme unless we asked for it
- if (args.indexOf('readme') !== -1) {
- delete versions[v].readme
- }
- results.push(showFields(data, versions[v], args))
- })
- }
- })
- var retval = results.reduce(reducer, {})
-
- if (args.length === 1 && args[0] === '') {
- retval = cleanBlanks(retval)
- log.silly('cleanup', retval)
- }
+ // remove readme unless we asked for it
+ if (args.indexOf('readme') === -1) {
+ delete data.readme
+ }
- if (error || silent) {
- cb(error, retval)
- } else if (
- !npm.config.get('json') &&
- args.length === 1 &&
- args[0] === ''
- ) {
- data.version = version
- BB.all(results.map((v) => prettyView(data, v[Object.keys(v)[0]][''])))
- .nodeify(cb)
- .then(() => retval)
- } else {
- printData(retval, data._id, cb.bind(null, error, retval))
+ Object.keys(versions).forEach(function (v) {
+ if (semver.satisfies(v, version, true)) {
+ args.forEach(function (args) {
+ // remove readme unless we asked for it
+ if (args.indexOf('readme') !== -1) {
+ delete versions[v].readme
+ }
+ results.push(showFields(data, versions[v], args))
+ })
}
})
- })
+ let retval = results.reduce(reducer, {})
+
+ if (args.length === 1 && args[0] === '') {
+ retval = cleanBlanks(retval)
+ log.silly('view', retval)
+ }
+
+ if (silent) {
+ } else if (error) {
+ throw error
+ } else if (
+ !opts.json &&
+ args.length === 1 &&
+ args[0] === ''
+ ) {
+ data.version = version
+ return BB.all(
+ results.map((v) => prettyView(data, v[Object.keys(v)[0]][''], opts))
+ ).then(() => retval)
+ } else {
+ return BB.fromNode(cb => {
+ printData(retval, data._id, opts, cb)
+ }).then(() => retval)
+ }
+ }).nodeify(cb)
}
-function prettyView (packument, manifest) {
+function prettyView (packument, manifest, opts) {
// More modern, pretty printing of default view
- const unicode = npm.config.get('unicode')
+ const unicode = opts.unicode
return BB.try(() => {
if (!manifest) {
log.error(
@@ -312,7 +336,7 @@ function prettyView (packument, manifest) {
}
function cleanBlanks (obj) {
- var clean = {}
+ const clean = {}
Object.keys(obj).forEach(function (version) {
clean[version] = obj[version]['']
})
@@ -334,7 +358,7 @@ function reducer (l, r) {
// return whatever was printed
function showFields (data, version, fields) {
- var o = {}
+ const o = {}
;[data, version].forEach(function (s) {
Object.keys(s).forEach(function (k) {
o[k] = s[k]
@@ -344,18 +368,18 @@ function showFields (data, version, fields) {
}
function search (data, fields, version, title) {
- var field
- var tail = fields
+ let field
+ const tail = fields
while (!field && fields.length) field = tail.shift()
fields = [field].concat(tail)
- var o
+ let o
if (!field && !tail.length) {
o = {}
o[version] = {}
o[version][title] = data
return o
}
- var index = field.match(/(.+)\[([^\]]+)\]$/)
+ let index = field.match(/(.+)\[([^\]]+)\]$/)
if (index) {
field = index[1]
index = index[2]
@@ -369,10 +393,10 @@ function search (data, fields, version, title) {
if (data.length === 1) {
return search(data[0], fields, version, title)
}
- var results = []
+ let results = []
data.forEach(function (data, i) {
- var tl = title.length
- var newt = title.substr(0, tl - fields.join('.').length - 1) +
+ const tl = title.length
+ const newt = title.substr(0, tl - fields.join('.').length - 1) +
'[' + i + ']' + [''].concat(fields).join('.')
results.push(search(data, fields.slice(), version, newt))
})
@@ -395,32 +419,32 @@ function search (data, fields, version, title) {
return o
}
-function printData (data, name, cb) {
- var versions = Object.keys(data)
- var msg = ''
- var msgJson = []
- var includeVersions = versions.length > 1
- var includeFields
+function printData (data, name, opts, cb) {
+ const versions = Object.keys(data)
+ let msg = ''
+ let msgJson = []
+ const includeVersions = versions.length > 1
+ let includeFields
versions.forEach(function (v) {
- var fields = Object.keys(data[v])
+ const fields = Object.keys(data[v])
includeFields = includeFields || (fields.length > 1)
- if (npm.config.get('json')) msgJson.push({})
+ if (opts.json) msgJson.push({})
fields.forEach(function (f) {
- var d = cleanup(data[v][f])
- if (fields.length === 1 && npm.config.get('json')) {
+ let d = cleanup(data[v][f])
+ if (fields.length === 1 && opts.json) {
msgJson[msgJson.length - 1][f] = d
}
if (includeVersions || includeFields || typeof d !== 'string') {
- if (npm.config.get('json')) {
+ if (opts.json) {
msgJson[msgJson.length - 1][f] = d
} else {
d = util.inspect(d, { showHidden: false, depth: 5, colors: npm.color, maxArrayLength: null })
}
- } else if (typeof d === 'string' && npm.config.get('json')) {
+ } else if (typeof d === 'string' && opts.json) {
d = JSON.stringify(d)
}
- if (!npm.config.get('json')) {
+ if (!opts.json) {
if (f && includeFields) f += ' = '
if (d.indexOf('\n') !== -1) d = ' \n' + d
msg += (includeVersions ? name + '@' + v + ' ' : '') +
@@ -429,9 +453,9 @@ function printData (data, name, cb) {
})
})
- if (npm.config.get('json')) {
+ if (opts.json) {
if (msgJson.length && Object.keys(msgJson[0]).length === 1) {
- var k = Object.keys(msgJson[0])[0]
+ const k = Object.keys(msgJson[0])[0]
msgJson = msgJson.map(function (m) { return m[k] })
}
@@ -465,7 +489,7 @@ function cleanup (data) {
data.versions = Object.keys(data.versions || {})
}
- var keys = Object.keys(data)
+ let keys = Object.keys(data)
keys.forEach(function (d) {
if (d.charAt(0) === '_') delete data[d]
else if (typeof data[d] === 'object') data[d] = cleanup(data[d])
diff --git a/deps/npm/lib/whoami.js b/deps/npm/lib/whoami.js
index e8af6595d1..5145b447de 100644
--- a/deps/npm/lib/whoami.js
+++ b/deps/npm/lib/whoami.js
@@ -1,47 +1,63 @@
-var npm = require('./npm.js')
-var output = require('./utils/output.js')
+'use strict'
+
+const BB = require('bluebird')
+
+const npmConfig = require('./config/figgy-config.js')
+const fetch = require('libnpm/fetch')
+const figgyPudding = require('figgy-pudding')
+const npm = require('./npm.js')
+const output = require('./utils/output.js')
+
+const WhoamiConfig = figgyPudding({
+ json: {},
+ registry: {}
+})
module.exports = whoami
whoami.usage = 'npm whoami [--registry <registry>]\n(just prints username according to given registry)'
-function whoami (args, silent, cb) {
+function whoami ([spec], silent, cb) {
// FIXME: need tighter checking on this, but is a breaking change
if (typeof cb !== 'function') {
cb = silent
silent = false
}
-
- var registry = npm.config.get('registry')
- if (!registry) return cb(new Error('no default registry set'))
-
- var auth = npm.config.getCredentialsByURI(registry)
- if (auth) {
- if (auth.username) {
- if (!silent) output(auth.username)
- return process.nextTick(cb.bind(this, null, auth.username))
- } else if (auth.token) {
- return npm.registry.whoami(registry, { auth: auth }, function (er, username) {
- if (er) return cb(er)
- if (!username) {
- var needNewSession = new Error(
+ const opts = WhoamiConfig(npmConfig())
+ return BB.try(() => {
+ // First, check if we have a user/pass-based auth
+ const registry = opts.registry
+ if (!registry) throw new Error('no default registry set')
+ return npm.config.getCredentialsByURI(registry)
+ }).then(({username, token}) => {
+ if (username) {
+ return username
+ } else if (token) {
+ return fetch.json('/-/whoami', opts.concat({
+ spec
+ })).then(({username}) => {
+ if (username) {
+ return username
+ } else {
+ throw Object.assign(new Error(
'Your auth token is no longer valid. Please log in again.'
- )
- needNewSession.code = 'ENEEDAUTH'
- return cb(needNewSession)
+ ), {code: 'ENEEDAUTH'})
}
-
- if (!silent) output(username)
- cb(null, username)
})
+ } else {
+ // At this point, if they have a credentials object, it doesn't have a
+ // token or auth in it. Probably just the default registry.
+ throw Object.assign(new Error(
+ 'This command requires you to be logged in.'
+ ), {code: 'ENEEDAUTH'})
}
- }
-
- // At this point, if they have a credentials object, it doesn't have a token
- // or auth in it. Probably just the default registry.
- var needAuth = new Error(
- 'this command requires you to be logged in.'
- )
- needAuth.code = 'ENEEDAUTH'
- process.nextTick(cb.bind(this, needAuth))
+ }).then(username => {
+ if (silent) {
+ } else if (opts.json) {
+ output(JSON.stringify(username))
+ } else {
+ output(username)
+ }
+ return username
+ }).nodeify(cb)
}