diff options
39 files changed, 783 insertions, 483 deletions
diff --git a/doc/misc/npm-config.md b/doc/misc/npm-config.md index 035923fa6..4413c0cd3 100644 --- a/doc/misc/npm-config.md +++ b/doc/misc/npm-config.md @@ -663,14 +663,25 @@ Only works if there is already a package.json file present. * Default: '^' * Type: String -Configure how versions of packages installed to a package.json file via +Configure how versions of packages installed to a package.json file via `--save` or `--save-dev` get prefixed. For example if a package has version `1.2.3`, by default it's version is -set to `^1.2.3` which allows minor upgrades for that package, but after +set to `^1.2.3` which allows minor upgrades for that package, but after `npm config set save-prefix='~'` it would be set to `~1.2.3` which only allows patch upgrades. +### scope + +* Default: "" +* Type: String + +Associate an operation with a scope for a scoped registry. Useful when logging +in to a private registry for the first time: +`npm login --scope=@organization --registry=registry.organization.com`, which +will cause `@organization` to be mapped to the registry for future installation +of packages specified according to the pattern `@organization/package`. + ### searchopts * Default: "" diff --git a/lib/adduser.js b/lib/adduser.js index 8315809c5..23c073ae7 100644 --- a/lib/adduser.js +++ b/lib/adduser.js @@ -95,7 +95,7 @@ function readPassword (c, u, cb) { return readPassword(c, u, cb) } - c.changed = c.changed || c.p != pw + c.changed = c.changed || c.p !== pw u.p = pw cb(er) }) @@ -133,24 +133,48 @@ function save (c, u, cb) { registry.password = u.p } npm.spinner.start() + // save existing configs, but yank off for this PUT - var uri = npm.config.get("registry") + var uri = npm.config.get("registry") + var scope = npm.config.get("scope") + + // there may be a saved scope and no --registry (for login) + if (scope) { + if (scope.charAt(0) !== "@") scope = "@" + scope + + var scopedRegistry = npm.config.get(scope + ":registry") + if (scopedRegistry) uri = scopedRegistry + } + registry.adduser(uri, u.u, u.p, u.e, function (er, doc) { npm.spinner.stop() if (er) return cb(er) + registry.username = u.u registry.password = u.p registry.email = u.e + // don't want this polluting the configuration npm.config.del("_token", "user") - npm.config.set("username", u.u, "user") - npm.config.set("_password", u.p, "user") + + if (scope) { + npm.config.set(scope + ":registry", uri, "user") + } + + var munged = url.parse(uri) + delete munged.protocol + // 'nerf dart' courtesy jennmoneydollaz + var nerfDart = url.format(munged) + if (doc && doc.token) { - var munged = url.parse(uri) - delete munged.protocol - npm.config.set(url.format(munged) + ":_authToken", doc.token, "user") + npm.config.set(nerfDart + ":_authToken", doc.token, "user") } - npm.config.set("email", u.e, "user") + + var auth = u.u + ":" + u.p + var quasiSecret = new Buffer(auth, "utf8").toString("base64") + npm.config.set(nerfDart + ":_auth", quasiSecret, "user") + npm.config.set(nerfDart + ":email", u.e, "user") + log.info("adduser", "Authorized user %s", u.u) npm.config.save("user", cb) }) diff --git a/lib/bugs.js b/lib/bugs.js index b3022bf2a..41f1386b0 100644 --- a/lib/bugs.js +++ b/lib/bugs.js @@ -10,13 +10,16 @@ var npm = require("./npm.js") , path = require("path") , readJson = require("read-package-json") , fs = require("fs") - , url = require("url") + , mapToRegistry = require("./utils/map-to-registry.js") bugs.completion = function (opts, cb) { if (opts.conf.argv.remain.length > 2) return cb() - var uri = url.resolve(npm.config.get("registry"), "-/short") - registry.get(uri, { timeout : 60000 }, function (er, list) { - return cb(null, list || []) + mapToRegistry("-/short", npm.config, function (er, uri) { + if (er) return cb(er) + + registry.get(uri, { timeout : 60000 }, function (er, list) { + return cb(null, list || []) + }) }) } @@ -56,9 +59,13 @@ function getUrlAndOpen (d, cb) { } function callRegistry (n, cb) { - var uri = url.resolve(npm.config.get("registry"), n + "/latest") - registry.get(uri, { timeout : 3600 }, function (er, d) { + mapToRegistry(n, npm.config, function (er, uri) { if (er) return cb(er) - getUrlAndOpen (d, cb) + + registry.get(uri + "/latest", { timeout : 3600 }, function (er, d) { + if (er) return cb(er) + + getUrlAndOpen (d, cb) + }) }) } diff --git a/lib/cache.js b/lib/cache.js index 01b7d4cba..063cbe38b 100644 --- a/lib/cache.js +++ b/lib/cache.js @@ -343,22 +343,30 @@ function maybeFile (spec, cb) { fs.stat(spec, function (er) { if (!er) { // definitely a local thing + log.verbose("maybeFile", "statted local spec", spec) return addLocal(spec, {}, cb) } - maybeAt(spec, cb) - }) -} + var pivot + // scoped modules might have @ as first character + if (spec.charAt(0) === "@") { + pivot = spec.slice(1).indexOf("@") + 1 + } + else { + pivot = spec.indexOf("@") + } -function maybeAt (spec, cb) { - // scoped modules might have @ as first character - var pivot = spec.lastIndexOf("@") - if (pivot > 0) { - add([spec.slice(0, pivot), spec.slice(pivot + 1)], cb) - } else { - // already know it's not a url, so must be local - addLocal(spec, {}, cb) - } + if (pivot > 0) { + var name = spec.slice(0, pivot) + spec = spec.slice(pivot + 1) + log.verbose("maybeFile", "name=%s, spec=%s", name, spec) + add([name, spec], cb) + } else { + // already know it's not a url, so must be local + log.verbose("maybeFile", "found local spec", spec) + addLocal(spec, {}, cb) + } + }) } function needName(er, data) { diff --git a/lib/cache/add-named.js b/lib/cache/add-named.js index f8c09aa03..e595db228 100644 --- a/lib/cache/add-named.js +++ b/lib/cache/add-named.js @@ -15,7 +15,7 @@ var path = require("path") , unlock = locker.unlock , maybeGithub = require("./maybe-github.js") , addRemoteTarball = require("./add-remote-tarball.js") - , mapNameToRegistry = require("../utils/map-name-to-registry.js") + , mapToRegistry = require("../utils/map-to-registry.js") module.exports = addNamed @@ -57,6 +57,12 @@ function addNameTag (name, tag, data, cb_) { tag = npm.config.get("tag") } + mapToRegistry(name, npm.config, function (er, uri) { + if (er) return cb(er) + + registry.get(uri, null, next) + }) + function cb(er, data) { // might be username/project // in that case, try it as a github url. @@ -66,28 +72,24 @@ function addNameTag (name, tag, data, cb_) { return cb_(er, data) } - mapNameToRegistry(name, npm.config, function (er, uri) { + function next (er, data, json, resp) { + if (!er) { + er = errorResponse(name, resp) + } if (er) return cb(er) + engineFilter(data) + if (data["dist-tags"] && data["dist-tags"][tag] + && data.versions[data["dist-tags"][tag]]) { + var ver = data["dist-tags"][tag] + return addNamed(name, ver, data.versions[ver], cb) + } + if (!explicit && Object.keys(data.versions).length) { + return addNamed(name, "*", data, cb) + } - registry.get(uri, null, function (er, data, json, resp) { - if (!er) { - er = errorResponse(name, resp) - } - if (er) return cb(er) - engineFilter(data) - if (data["dist-tags"] && data["dist-tags"][tag] - && data.versions[data["dist-tags"][tag]]) { - var ver = data["dist-tags"][tag] - return addNamed(name, ver, data.versions[ver], cb) - } - if (!explicit && Object.keys(data.versions).length) { - return addNamed(name, "*", data, cb) - } - - er = installTargetsError(tag, data) - return cb(er) - }) - }) + er = installTargetsError(tag, data) + return cb(er) + } } function engineFilter (data) { @@ -119,26 +121,28 @@ function addNameVersion (name, v, data, cb) { return next() } - mapNameToRegistry(name, npm.config, function (er, uri) { + mapToRegistry(name, npm.config, function (er, uri) { if (er) return cb(er) - registry.get(uri, null, function (er, d, json, resp) { - if (!er) { - er = errorResponse(name, resp) - } - if (er) return cb(er) - data = d && d.versions[ver] - if (!data) { - er = new Error('version not found: ' + name + '@' + ver) - er.package = name - er.statusCode = 404 - return cb(er) - } - response = resp - next() - }) + registry.get(uri, null, setData) }) + function setData (er, d, json, resp) { + if (!er) { + er = errorResponse(name, resp) + } + if (er) return cb(er) + data = d && d.versions[ver] + if (!data) { + er = new Error("version not found: "+name+"@"+ver) + er.package = name + er.statusCode = 404 + return cb(er) + } + response = resp + next() + } + function next () { deprCheck(data) var dist = data.dist @@ -205,19 +209,21 @@ function addNameRange (name, range, data, cb) { if (data) return next() - mapNameToRegistry(name, npm.config, function (er, uri) { + mapToRegistry(name, npm.config, function (er, uri) { if (er) return cb(er) - registry.get(uri, null, function (er, d, json, resp) { - if (!er) { - er = errorResponse(name, resp) - } - if (er) return cb(er) - data = d - next() - }) + registry.get(uri, null, setData) }) + function setData (er, d, json, resp) { + if (!er) { + er = errorResponse(name, resp) + } + if (er) return cb(er) + data = d + next() + } + function next () { log.silly( "addNameRange", "number 2" , {name:name, range:range, hasData:!!data}) diff --git a/lib/dedupe.js b/lib/dedupe.js index e6762e15b..70a63a3ab 100644 --- a/lib/dedupe.js +++ b/lib/dedupe.js @@ -7,7 +7,6 @@ // much better "put pkg X at folder Y" abstraction. Oh well, // whatever. Perfect enemy of the good, and all that. -var url = require("url") var fs = require("fs") var asyncMap = require("slide").asyncMap var path = require("path") @@ -16,6 +15,7 @@ var semver = require("semver") var rm = require("./utils/gently-rm.js") var log = require("npmlog") var npm = require("./npm.js") +var mapToRegistry = require("./utils/map-to-registry.js") module.exports = dedupe @@ -240,13 +240,19 @@ function findVersions (npm, summary, cb) { var versions = data.versions var ranges = data.ranges - var uri = url.resolve(npm.config.get("registry"), name) - npm.registry.get(uri, null, function (er, data) { + mapToRegistry(name, npm.config, function (er, uri) { + if (er) return cb(er) + + npm.registry.get(uri, null, next) + }) + + function next (er, data) { var regVersions = er ? [] : Object.keys(data.versions) var locMatch = bestMatch(versions, ranges) - var regMatch; var tag = npm.config.get("tag") var distTag = data["dist-tags"] && data["dist-tags"][tag] + + var regMatch if (distTag && data.versions[distTag] && matches(distTag, ranges)) { regMatch = distTag } else { @@ -254,7 +260,7 @@ function findVersions (npm, summary, cb) { } cb(null, [[name, has, loc, locMatch, regMatch, locs]]) - }) + } }, cb) } diff --git a/lib/deprecate.js b/lib/deprecate.js index 175b69ceb..17c029f92 100644 --- a/lib/deprecate.js +++ b/lib/deprecate.js @@ -1,5 +1,5 @@ -var url = require("url") - , npm = require("./npm.js") +var npm = require("./npm.js") + , mapToRegistry = require("./utils/map-to-registry.js") module.exports = deprecate @@ -13,11 +13,14 @@ deprecate.completion = function (opts, cb) { if (opts.conf.argv.remain.length > 2) return cb() // get the list of packages by user var path = "/-/by-user/"+encodeURIComponent(un) - , uri = url.resolve(npm.config.get("registry"), path) - npm.registry.get(uri, { timeout : 60000 }, function (er, list) { - if (er) return cb() - console.error(list) - return cb(null, list[un]) + mapToRegistry(path, npm.config, function (er, uri) { + if (er) return cb(er) + + npm.registry.get(uri, { timeout : 60000 }, function (er, list) { + if (er) return cb() + console.error(list) + return cb(null, list[un]) + }) }) } @@ -25,11 +28,18 @@ function deprecate (args, cb) { var pkg = args[0] , msg = args[1] if (msg === undefined) return cb("Usage: " + deprecate.usage) + // fetch the data and make sure it exists. - pkg = pkg.split(/@/) - var name = pkg.shift() - , ver = pkg.join("@") - , uri = url.resolve(npm.config.get("registry"), name) + var pivot = pkg.lastIndexOf("@") + if (pivot === 0) pivot = pkg.length + var name = pkg.substr(0, pivot) + , ver = pkg.substr(pivot + 1) + + mapToRegistry(name, npm.config, next) + + function next (er, uri) { + if (er) return cb(er) - npm.registry.deprecate(uri, ver, msg, cb) + npm.registry.deprecate(uri, ver, msg, cb) + } } diff --git a/lib/docs.js b/lib/docs.js index 77073fbb9..2c59b2672 100644 --- a/lib/docs.js +++ b/lib/docs.js @@ -5,18 +5,21 @@ docs.usage += "\n" docs.usage += "npm docs ." docs.completion = function (opts, cb) { - var uri = url_.resolve(npm.config.get("registry"), "/-/short") - registry.get(uri, { timeout : 60000 }, function (er, list) { - return cb(null, list || []) + mapToRegistry("/-/short", npm.config, function (er, uri) { + if (er) return cb(er) + + registry.get(uri, { timeout : 60000 }, function (er, list) { + return cb(null, list || []) + }) }) } -var url_ = require("url") - , npm = require("./npm.js") +var npm = require("./npm.js") , registry = npm.registry , opener = require("opener") , path = require("path") , log = require("npmlog") + , mapToRegistry = require("./utils/map-to-registry.js") function url (json) { return json.homepage ? json.homepage : "https://npmjs.org/package/" + json.name @@ -54,8 +57,13 @@ function getDoc (project, cb) { return opener(url(json), { command: npm.config.get("browser") }, cb) } - var uri = url_.resolve(npm.config.get("registry"), project + "/latest") - registry.get(uri, { timeout : 3600 }, function (er, json) { + mapToRegistry(project, npm.config, function (er, uri) { + if (er) return cb(er) + + registry.get(uri + "/latest", { timeout : 3600 }, next) + }) + + function next (er, json) { var github = "https://github.com/" + project + "#readme" if (er) { @@ -64,5 +72,5 @@ function getDoc (project, cb) { } return opener(url(json), { command: npm.config.get("browser") }, cb) - }) + } } diff --git a/lib/install.js b/lib/install.js index 8f20bc839..6d07a8294 100644 --- a/lib/install.js +++ b/lib/install.js @@ -34,28 +34,34 @@ install.completion = function (opts, cb) { // if it starts with https?://, then just give up, because it's a url // for now, not yet implemented. var registry = npm.registry - , uri = url.resolve(npm.config.get("registry"), "-/short") - registry.get(uri, null, function (er, pkgs) { - if (er) return cb() - if (!opts.partialWord) return cb(null, pkgs) + mapToRegistry("-/short", npm.config, function (er, uri) { + if (er) return cb(er) - var name = opts.partialWord.split("@").shift() - pkgs = pkgs.filter(function (p) { - return p.indexOf(name) === 0 - }) + registry.get(uri, null, function (er, pkgs) { + if (er) return cb() + if (!opts.partialWord) return cb(null, pkgs) - if (pkgs.length !== 1 && opts.partialWord === name) { - return cb(null, pkgs) - } + var name = opts.partialWord.split("@").shift() + pkgs = pkgs.filter(function (p) { + return p.indexOf(name) === 0 + }) - uri = url.resolve(npm.config.get("registry"), pkgs[0]) - registry.get(uri, null, function (er, d) { - if (er) return cb() - return cb(null, Object.keys(d["dist-tags"] || {}) - .concat(Object.keys(d.versions || {})) - .map(function (t) { - return pkgs[0] + "@" + t - })) + if (pkgs.length !== 1 && opts.partialWord === name) { + return cb(null, pkgs) + } + + mapToRegistry(pkgs[0], npm.config, function (er, uri) { + if (er) return cb(er) + + registry.get(uri, null, function (er, d) { + if (er) return cb() + return cb(null, Object.keys(d["dist-tags"] || {}) + .concat(Object.keys(d.versions || {})) + .map(function (t) { + return pkgs[0] + "@" + t + })) + }) + }) }) }) } @@ -77,6 +83,7 @@ var npm = require("./npm.js") , isGitUrl = require("./utils/is-git-url.js") , npmInstallChecks = require("npm-install-checks") , sortedObject = require("sorted-object") + , mapToRegistry = require("./utils/map-to-registry.js") function install (args, cb_) { var hasArguments = !!args.length @@ -412,12 +419,12 @@ function save (where, installed, tree, pretty, hasArguments, cb) { // that the submodules are not immediately require()able. // TODO: Show the complete tree, ls-style, but only if --long is provided function prettify (tree, installed) { - if (npm.config.get("json")) { - function red (set, kv) { - set[kv[0]] = kv[1] - return set - } + function red (set, kv) { + set[kv[0]] = kv[1] + return set + } + if (npm.config.get("json")) { tree = Object.keys(tree).map(function (p) { if (!tree[p]) return null var what = tree[p].what.split("@") @@ -771,6 +778,13 @@ function localLink (target, where, context, cb) { , parent = context.parent readJson(jsonFile, log.warn, function (er, data) { + function thenLink () { + npm.commands.link([target.name], function (er, d) { + log.silly("localLink", "back from link", [er, d]) + cb(er, [resultList(target, where, parent && parent._id)]) + }) + } + if (er && er.code !== "ENOENT" && er.code !== "ENOTDIR") return cb(er) if (er || data._id === target._id) { if (er) { @@ -781,14 +795,6 @@ function localLink (target, where, context, cb) { thenLink() }) } else thenLink() - - function thenLink () { - npm.commands.link([target.name], function (er, d) { - log.silly("localLink", "back from link", [er, d]) - cb(er, [resultList(target, where, parent && parent._id)]) - }) - } - } else { log.verbose("localLink", "install locally (no link)", target._id) installOne_(target, where, context, cb) diff --git a/lib/outdated.js b/lib/outdated.js index b29ea9fe6..bfed369ba 100644 --- a/lib/outdated.js +++ b/lib/outdated.js @@ -34,6 +34,7 @@ var path = require("path") , table = require("text-table") , semver = require("semver") , os = require("os") + , mapToRegistry = require("./utils/map-to-registry.js") function outdated (args, silent, cb) { if (typeof cb !== "function") cb = silent, silent = false @@ -268,8 +269,13 @@ function shouldUpdate (args, dir, dep, has, req, depth, cb) { return doIt("git", "git") // search for the latest package - var uri = url.resolve(npm.config.get("registry"), dep) - npm.registry.get(uri, null, function (er, d) { + mapToRegistry(dep, npm.config, function (er, uri) { + if (er) return cb(er) + + npm.registry.get(uri, null, updateDeps) + }) + + function updateDeps (er, d) { if (er) return cb() if (!d || !d['dist-tags'] || !d.versions) return cb() var l = d.versions[d['dist-tags'].latest] @@ -315,6 +321,5 @@ function shouldUpdate (args, dir, dep, has, req, depth, cb) { else skip() } - - }) + } } diff --git a/lib/owner.js b/lib/owner.js index 34dbbc247..1f9d79b25 100644 --- a/lib/owner.js +++ b/lib/owner.js @@ -15,51 +15,65 @@ owner.completion = function (opts, cb) { return cb(null, subs) } var un = encodeURIComponent(npm.config.get("username")) - var theUser, uri + var byUser, theUser switch (argv[2]) { case "ls": if (argv.length > 3) return cb() - uri = url.resolve(npm.config.get("registry"), "-/short") - return registry.get(uri, null, cb) + return mapToRegistry("-/short", npm.config, function (er, uri) { + if (er) return cb(er) + + registry.get(uri, null, cb) + }) case "rm": if (argv.length > 3) { theUser = encodeURIComponent(argv[3]) - uri = url.resolve(npm.config.get("registry"), "-/by-user/"+theUser+"|"+un) - console.error(uri) - return registry.get(uri, null, function (er, d) { + byUser = "-/by-user/" + theUser + "|" + un + return mapToRegistry(byUser, npm.config, function (er, uri) { if (er) return cb(er) - // return the intersection - return cb(null, d[theUser].filter(function (p) { - // kludge for server adminery. - return un === "isaacs" || d[un].indexOf(p) === -1 - })) + + console.error(uri) + registry.get(uri, null, function (er, d) { + if (er) return cb(er) + // return the intersection + return cb(null, d[theUser].filter(function (p) { + // kludge for server adminery. + return un === "isaacs" || d[un].indexOf(p) === -1 + })) + }) }) } // else fallthrough case "add": if (argv.length > 3) { theUser = encodeURIComponent(argv[3]) - uri = url.resolve(npm.config.get("registry"), "-/by-user/"+theUser+"|"+un) - console.error(uri) - return registry.get(uri, null, function (er, d) { - console.error(uri, er || d) - // return mine that they're not already on. + byUser = "-/by-user/"+theUser+"|"+un + return mapToRegistry(byUser, npm.config, function (er, uri) { if (er) return cb(er) - var mine = d[un] || [] - , theirs = d[theUser] || [] - return cb(null, mine.filter(function (p) { - return theirs.indexOf(p) === -1 - })) + + console.error(uri) + registry.get(uri, null, function (er, d) { + console.error(uri, er || d) + // return mine that they're not already on. + if (er) return cb(er) + var mine = d[un] || [] + , theirs = d[theUser] || [] + return cb(null, mine.filter(function (p) { + return theirs.indexOf(p) === -1 + })) + }) }) } // just list all users who aren't me. - uri = url.resolve(npm.config.get("registry"), "-/users") - return registry.get(uri, null, function (er, list) { - if (er) return cb() - return cb(null, Object.keys(list).filter(function (n) { - return n !== un - })) + return mapToRegistry("-/users", npm.config, function (er, uri) { + if (er) return cb(er) + + registry.get(uri, null, function (er, list) { + if (er) return cb() + return cb(null, Object.keys(list).filter(function (n) { + return n !== un + })) + }) }) default: @@ -71,7 +85,7 @@ var npm = require("./npm.js") , registry = npm.registry , log = require("npmlog") , readJson = require("read-package-json") - , url = require("url") + , mapToRegistry = require("./utils/map-to-registry.js") function owner (args, cb) { var action = args.shift() @@ -90,18 +104,21 @@ function ls (pkg, cb) { ls(pkg, cb) }) - var uri = url.resolve(npm.config.get("registry"), pkg) - registry.get(uri, null, function (er, data) { - var msg = "" - if (er) { - log.error("owner ls", "Couldn't get owner data", pkg) - return cb(er) - } - var owners = data.maintainers - if (!owners || !owners.length) msg = "admin party!" - else msg = owners.map(function (o) { return o.name +" <"+o.email+">" }).join("\n") - console.log(msg) - cb(er, owners) + mapToRegistry(pkg, npm.config, function (er, uri) { + if (er) return cb(er) + + registry.get(uri, null, function (er, data) { + var msg = "" + if (er) { + log.error("owner ls", "Couldn't get owner data", pkg) + return cb(er) + } + var owners = data.maintainers + if (!owners || !owners.length) msg = "admin party!" + else msg = owners.map(function (o) { return o.name +" <"+o.email+">" }).join("\n") + console.log(msg) + cb(er, owners) + }) }) } @@ -156,8 +173,12 @@ function rm (user, pkg, cb) { function mutate (pkg, user, mutation, cb) { if (user) { - var uri = url.resolve(npm.config.get("registry"), "-/user/org.couchdb.user:"+user) - registry.get(uri, null, mutate_) + var byUser = "-/user/org.couchdb.user:" + user + mapToRegistry(byUser, npm.config, function (er, uri) { + if (er) return cb(er) + + registry.get(uri, null, mutate_) + }) } else { mutate_(null, null) } @@ -172,27 +193,34 @@ function mutate (pkg, user, mutation, cb) { } if (u) u = { "name" : u.name, "email" : u.email } - var uri = url.resolve(npm.config.get("registry"), pkg) - registry.get(uri, null, function (er, data) { - if (er) { - log.error("owner mutate", "Error getting package data for %s", pkg) - return cb(er) - } - 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 uri = url.resolve(npm.config.get("registry"), pkg+"/-rev/"+data._rev) - registry.request("PUT", uri, { body : data }, function (er, data) { - if (!er && data.error) er = new Error( - "Failed to update package metadata: "+JSON.stringify(data)) + mapToRegistry(pkg, npm.config, function (er, uri) { + if (er) return cb(er) + + registry.get(uri, null, function (er, data) { if (er) { - log.error("owner mutate", "Failed to update package metadata") + log.error("owner mutate", "Error getting package data for %s", pkg) + return cb(er) } - cb(er, data) + 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 + "/-rev/" + data._rev + mapToRegistry(dataPath, npm.config, function (er, uri) { + if (er) return cb(er) + + registry.request("PUT", uri, { body : data }, 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") + } + cb(er, data) + }) + }) }) }) } diff --git a/lib/publish.js b/lib/publish.js index 32b09f8e0..349ac00a7 100644 --- a/lib/publish.js +++ b/lib/publish.js @@ -1,7 +1,8 @@ module.exports = publish -var npm = require("./npm.js") +var url = require("url") + , npm = require("./npm.js") , log = require("npmlog") , path = require("path") , readJson = require("read-package-json") @@ -9,7 +10,7 @@ var npm = require("./npm.js") , chain = require("slide").chain , Conf = require("npmconf").Conf , RegClient = require("npm-registry-client") - , isOrganized = require("./utils/is-organized.js") + , mapToRegistry = require("./utils/map-to-registry.js") publish.usage = "npm publish <tarball>" + "\nnpm publish <folder>" @@ -84,47 +85,43 @@ function publish_ (arg, data, isRetry, cachedir, cb) { registry = new RegClient(config) } - var registryURI - if (isOrganized(data.name)) { - var org = data.name.match(/^(@[^@\/]+)\//)[1] - if (!org) return cb(new Error("No organization found in scoped package name")) - log.silly("publish", "scope", org) - - var base = config.get(org + ":registry") - if (!base) { - return cb(new Error("no registry URL found for scope " + org)) - } - registryURI = base - } - else { - registryURI = config.get("registry") - } - data._npmVersion = npm.version data._npmUser = { name: npm.config.get("username") , email: npm.config.get("email") } delete data.modules - if (data.private) return cb(new Error - ("This package has been marked as private\n" - +"Remove the 'private' field from the package.json to publish it.")) - - var tarball = cachedir + ".tgz" - registry.publish(registryURI, data, tarball, function (er) { - if (er && er.code === "EPUBLISHCONFLICT" - && npm.config.get("force") && !isRetry) { - log.warn("publish", "Forced publish over "+data._id) - return npm.commands.unpublish([data._id], function (er) { - // ignore errors. Use the force. Reach out with your feelings. - // but if it fails again, then report the first error. - publish([arg], er || true, cb) - }) - } - // report the unpublish error if this was a retry and unpublish failed - if (er && isRetry && isRetry !== true) return cb(isRetry) + if (data.private) return cb( + new Error( + "This package has been marked as private\n" + + "Remove the 'private' field from the package.json to publish it." + ) + ) + + mapToRegistry(data.name, config, function (er, registryURI) { if (er) return cb(er) - console.log("+ " + data._id) - cb() + + var tarball = cachedir + ".tgz" + + // we just want the base registry URL in this case + var registryBase = url.resolve(registryURI, ".") + log.verbose("publish", "registryBase", registryBase) + + registry.publish(registryBase, data, tarball, function (er) { + if (er && er.code === "EPUBLISHCONFLICT" + && npm.config.get("force") && !isRetry) { + log.warn("publish", "Forced publish over " + data._id) + return npm.commands.unpublish([data._id], function (er) { + // ignore errors. Use the force. Reach out with your feelings. + // but if it fails again, then report the first error. + publish([arg], er || true, cb) + }) + } + // report the unpublish error if this was a retry and unpublish failed + if (er && isRetry && isRetry !== true) return cb(isRetry) + if (er) return cb(er) + console.log("+ " + data._id) + cb() + }) }) } diff --git a/lib/repo.js b/lib/repo.js index d209c3ca8..1fe81f293 100644 --- a/lib/repo.js +++ b/lib/repo.js @@ -5,9 +5,12 @@ repo.usage = "npm repo <pkgname>" repo.completion = function (opts, cb) { if (opts.conf.argv.remain.length > 2) return cb() - var uri = url_.resolve(npm.config.get("registry"), "/-/short") - registry.get(uri, { timeout : 60000 }, function (er, list) { - return cb(null, list || []) + mapToRegistry("/-/short", npm.config, function (er, uri) { + if (er) return cb(er) + + registry.get(uri, { timeout : 60000 }, function (er, list) { + return cb(null, list || []) + }) }) } @@ -19,10 +22,11 @@ var npm = require("./npm.js") , path = require("path") , readJson = require("read-package-json") , fs = require("fs") - , url_ = require('url') + , url_ = require("url") + , mapToRegistry = require("./utils/map-to-registry.js") function repo (args, cb) { - var n = args.length && args[0].split("@").shift() || '.' + var n = args.length && args[0].split("@").shift() || "." fs.stat(n, function (er, s) { if (er && er.code === "ENOENT") return callRegistry(n, cb) else if (er) return cb(er) @@ -35,8 +39,8 @@ function repo (args, cb) { } function getUrlAndOpen (d, cb) { - var r = d.repository; - if (!r) return cb(new Error('no repository')); + var 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 if (githubUserRepo(r.url)) @@ -52,10 +56,13 @@ function getUrlAndOpen (d, cb) { } function callRegistry (n, cb) { - var uri = url_.resolve(npm.config.get("registry"), n + "/latest") - registry.get(uri, { timeout : 3600 }, function (er, d) { + mapToRegistry(n, npm.config, function (er, uri) { if (er) return cb(er) - getUrlAndOpen(d, cb) + + registry.get(uri + "/latest", { timeout : 3600 }, function (er, d) { + if (er) return cb(er) + getUrlAndOpen(d, cb) + }) }) } diff --git a/lib/search.js b/lib/search.js index e7892350c..3be8b0d27 100644 --- a/lib/search.js +++ b/lib/search.js @@ -1,10 +1,10 @@ module.exports = exports = search -var url = require("url") - , npm = require("./npm.js") +var npm = require("./npm.js") , registry = npm.registry , columnify = require('columnify') + , mapToRegistry = require("./utils/map-to-registry.js") search.usage = "npm search [some search terms ...]" @@ -63,10 +63,13 @@ function getFilteredData (staleness, args, notArgs, cb) { follow : true, staleOk : true } - var uri = url.resolve(npm.config.get("registry"), "-/all") - registry.get(uri, opts, function (er, data) { + mapToRegistry("-/all", npm.config, function (er, uri) { if (er) return cb(er) - return cb(null, filter(data, args, notArgs)) + + registry.get(uri, opts, function (er, data) { + if (er) return cb(er) + return cb(null, filter(data, args, notArgs)) + }) }) } diff --git a/lib/star.js b/lib/star.js index 9c0b4ea9e..123c4ebbb 100644 --- a/lib/star.js +++ b/lib/star.js @@ -1,19 +1,22 @@ module.exports = star -var url = require("url") - , npm = require("./npm.js") +var npm = require("./npm.js") , registry = npm.registry , log = require("npmlog") , asyncMap = require("slide").asyncMap + , mapToRegistry = require("./utils/map-to-registry.js") star.usage = "npm star <package> [pkg, pkg, ...]\n" + "npm unstar <package> [pkg, pkg, ...]" star.completion = function (opts, cb) { - var uri = url.resolve(npm.config.get("registry"), "-/short") - registry.get(uri, { timeout : 60000 }, function (er, list) { - return cb(null, list || []) + mapToRegistry("-/short", npm.config, function (er, uri) { + if (er) return cb(er) + + registry.get(uri, { timeout : 60000 }, function (er, list) { + return cb(null, list || []) + }) }) } @@ -24,13 +27,16 @@ function star (args, cb) { , using = !(npm.command.match(/^un/)) if (!using) s = u asyncMap(args, function (pkg, cb) { - var uri = url.resolve(npm.config.get("registry"), pkg) - registry.star(uri, using, function (er, data, raw, req) { - if (!er) { - console.log(s + " "+pkg) - log.verbose("star", data) - } - cb(er, data, raw, req) + mapToRegistry(pkg, npm.config, function (er, uri) { + if (er) return cb(er) + + registry.star(uri, using, function (er, data, raw, req) { + if (!er) { + console.log(s + " "+pkg) + log.verbose("star", data) + } + cb(er, data, raw, req) + }) }) }, cb) } diff --git a/lib/stars.js b/lib/stars.js index f0d2ef73a..a482a4b4a 100644 --- a/lib/stars.js +++ b/lib/stars.js @@ -2,15 +2,18 @@ module.exports = stars stars.usage = "npm stars [username]" -var url = require("url") - , npm = require("./npm.js") +var npm = require("./npm.js") , registry = npm.registry , log = require("npmlog") + , mapToRegistry = require("./utils/map-to-registry.js") function stars (args, cb) { var name = args.length === 1 ? args[0] : npm.config.get("username") - , uri = url.resolve(npm.config.get("registry"), name) - registry.stars(uri, showstars) + mapToRegistry(name, npm.config, function (er, uri) { + if (er) return cb(er) + + registry.stars(uri, showstars) + }) function showstars (er, data) { if (er) { diff --git a/lib/tag.js b/lib/tag.js index 1d04ad1f7..cf7752a69 100644 --- a/lib/tag.js +++ b/lib/tag.js @@ -5,9 +5,9 @@ tag.usage = "npm tag <project>@<version> [<tag>]" tag.completion = require("./unpublish.js").completion -var url = require("url") - , npm = require("./npm.js") +var npm = require("./npm.js") , registry = npm.registry + , mapToRegistry = require("./utils/map-to-registry.js") function tag (args, cb) { var thing = (args.shift() || "").split("@") @@ -15,6 +15,9 @@ function tag (args, cb) { , version = thing.join("@") , t = args.shift() || npm.config.get("tag") if (!project || !version || !t) return cb("Usage:\n"+tag.usage) - var uri = url.resolve(npm.config.get("registry"), project) - registry.tag(uri, version, t, cb) + mapToRegistry(project, npm.config, function (er, uri) { + if (er) return cb(er) + + registry.tag(uri, version, t, cb) + }) } diff --git a/lib/unpublish.js b/lib/unpublish.js index 225c1c3c4..cd4f18deb 100644 --- a/lib/unpublish.js +++ b/lib/unpublish.js @@ -1,12 +1,12 @@ module.exports = unpublish -var url = require("url") - , log = require("npmlog") +var log = require("npmlog") , npm = require("./npm.js") , registry = npm.registry , readJson = require("read-package-json") , path = require("path") + , mapToRegistry = require("./utils/map-to-registry.js") unpublish.usage = "npm unpublish <project>[@<version>]" @@ -14,27 +14,34 @@ unpublish.completion = function (opts, cb) { if (opts.conf.argv.remain.length >= 3) return cb() var un = encodeURIComponent(npm.config.get("username")) if (!un) return cb() - var uri = url.resolve(npm.config.get("registry"), "-/by-user/"+un) - registry.get(uri, null, 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 partial = opts.partialWord.split("@") - , pp = partial.shift() - pkgs = pkgs.filter(function (p) { - return p.indexOf(pp) === 0 - }) - if (pkgs.length > 1) return cb(null, pkgs) - var uri = url.resolve(npm.config.get("registry"), pkgs[0]) - registry.get(uri, null, 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 - })) + var byUser = "-/by-user/" + un + mapToRegistry(byUser, npm.config, function (er, uri) { + if (er) return cb(er) + + registry.get(uri, null, 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 partial = opts.partialWord.split("@") + , pp = partial.shift() + 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) { + if (er) return cb(er) + + registry.get(uri, null, 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 + })) + }) + }) }) }) } @@ -79,7 +86,10 @@ function gotProject (project, version, cb_) { return cb(er) } - var uri = url.resolve(npm.config.get("registry"), project) - registry.unpublish(uri, version, cb) + mapToRegistry(project, npm.config, function (er, uri) { + if (er) return cb(er) + + registry.unpublish(uri, version, cb) + }) }) } diff --git a/lib/utils/is-organized.js b/lib/utils/is-organized.js deleted file mode 100644 index 61b45508f..000000000 --- a/lib/utils/is-organized.js +++ /dev/null @@ -1,5 +0,0 @@ -module.exports = isOrganized - -function isOrganized(spec) { - return !!/^@[^@\/]+\//.test(spec) -} diff --git a/lib/utils/is-scoped.js b/lib/utils/is-scoped.js new file mode 100644 index 000000000..fb34f558e --- /dev/null +++ b/lib/utils/is-scoped.js @@ -0,0 +1,16 @@ +module.exports = isScoped + +function isScoped(spec) { + if (spec.charAt(0) !== "@") return null + + var rest = spec.slice(1).split("/") + if (rest.length !== 2) return null + + if (!(rest[0] && rest[1] && + rest[0] === encodeURIComponent(rest[0]) && + rest[1] === encodeURIComponent(rest[1]))) { + return null + } + + return {scope : rest[0], name : rest[1]} +} diff --git a/lib/utils/map-name-to-registry.js b/lib/utils/map-name-to-registry.js deleted file mode 100644 index 2b2e2a062..000000000 --- a/lib/utils/map-name-to-registry.js +++ /dev/null @@ -1,28 +0,0 @@ -var url = require("url") - -var isOrganized = require("./is-organized.js") - , log = require("npmlog") - -module.exports = mapNameToRegistry - -function mapNameToRegistry(name, config, cb) { - var uri - - if (isOrganized(name)) { - var org = name.match(/^(@[^@\/]+)\//)[1] - if (!org) return cb(new Error("No organization found in scoped package name")) - log.silly("mapNameToRegistry", "org", org) - - var escaped = name.replace("/", "%2f") - var orgRegistry = config.get(org + ":registry") - if (!orgRegistry) return cb(new Error("no registry URL found for scope " + org)) - - uri = url.resolve(orgRegistry, escaped) - } - else { - uri = url.resolve(config.get("registry"), name) - } - - log.verbose("mapNameToRegistry", "uri", uri) - cb(null, uri) -} diff --git a/lib/utils/map-to-registry.js b/lib/utils/map-to-registry.js new file mode 100644 index 000000000..169167f76 --- /dev/null +++ b/lib/utils/map-to-registry.js @@ -0,0 +1,51 @@ +var url = require("url") + +var isScoped = require("./is-scoped.js") + , log = require("npmlog") + +module.exports = mapToRegistry + +function mapToRegistry(name, config, cb) { + var uri + var scopedRegistry + + // the name itself takes precedence + var data = isScoped(name) + if (data) { + var packageScope = "@" + data.scope + if (!packageScope) return cb(new Error("No scope found in scoped package named " + name)) + log.silly("mapToRegistry", "packageScope", packageScope) + + var escaped = name.replace("/", "%2f") + scopedRegistry = config.get(packageScope + ":registry") + if (scopedRegistry) { + uri = url.resolve(scopedRegistry, escaped) + } + else { + log.verbose("mapToRegistry", "no registry URL found for scope", packageScope) + } + } + + // ...then --scope=@scope or --scope=scope + var scope = config.get("scope") + if (!uri && scope) { + // I'm an enabler, sorry + if (scope.charAt(0) !== "@") scope = "@" + scope + + scopedRegistry = config.get(scope + ":registry") + if (scopedRegistry) { + uri = url.resolve(scopedRegistry, name) + } + else { + log.verbose("mapToRegistry", "no registry URL found for scope", scope) + } + } + + // ...and finally use the default registry + if (!uri) { + uri = url.resolve(config.get("registry"), name) + } + + log.verbose("mapToRegistry", "uri", uri) + cb(null, uri) +} diff --git a/lib/view.js b/lib/view.js index 33bf550dd..8e0c97cf8 100644 --- a/lib/view.js +++ b/lib/view.js @@ -4,21 +4,26 @@ module.exports = view view.usage = "npm view pkg[@version] [<field>[.subfield]...]" view.completion = function (opts, cb) { - var uri if (opts.conf.argv.remain.length <= 2) { - uri = url.resolve(npm.config.get("registry"), "-/short") - return registry.get(uri, null, cb) + return mapToRegistry("-/short", npm.config, function (er, uri) { + if (er) return cb(er) + + registry.get(uri, null, cb) + }) } // have the package, get the fields. var tag = npm.config.get("tag") - uri = url.resolve(npm.config.get("registry"), opts.conf.argv.remain[2]) - registry.get(uri, null, function (er, d) { + mapToRegistry(opts.conf.argv.remain[2], npm.config, function (er, uri) { if (er) return cb(er) - var dv = d.versions[d["dist-tags"][tag]] - , fields = [] - d.versions = Object.keys(d.versions).sort(semver.compareLoose) - fields = getFields(d).concat(getFields(dv)) - cb(null, fields) + + registry.get(uri, null, function (er, d) { + if (er) return cb(er) + var dv = d.versions[d["dist-tags"][tag]] + , fields = [] + d.versions = Object.keys(d.versions).sort(semver.compareLoose) + fields = getFields(d).concat(getFields(dv)) + cb(null, fields) + }) }) function getFields (d, f, pref) { @@ -42,12 +47,12 @@ view.completion = function (opts, cb) { } } -var url = require("url") - , npm = require("./npm.js") +var npm = require("./npm.js") , registry = npm.registry , log = require("npmlog") , util = require("util") , semver = require("semver") + , mapToRegistry = require("./utils/map-to-registry.js") function view (args, silent, cb) { if (typeof cb !== "function") cb = silent, silent = false @@ -60,53 +65,56 @@ function view (args, silent, cb) { if (name === ".") return cb(view.usage) // get the data about this package - var uri = url.resolve(npm.config.get("registry"), name) - registry.get(uri, null, function (er, data) { + mapToRegistry(name, npm.config, function (er, uri) { if (er) return cb(er) - if (data["dist-tags"] && data["dist-tags"].hasOwnProperty(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) - } + registry.get(uri, null, function (er, data) { + if (er) return cb(er) + if (data["dist-tags"] && data["dist-tags"].hasOwnProperty(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) + } - var results = [] - , error = null - , versions = data.versions || {} - data.versions = Object.keys(versions).sort(semver.compareLoose) - if (!args.length) args = [""] - // remove readme unless we asked for it - if (-1 === args.indexOf("readme")) { - delete data.readme - } + var results = [] + , error = null + , versions = data.versions || {} + data.versions = Object.keys(versions).sort(semver.compareLoose) + if (!args.length) args = [""] + + // remove readme unless we asked for it + if (-1 === args.indexOf("readme")) { + delete data.readme + } - Object.keys(versions).forEach(function (v) { - if (semver.satisfies(v, version, true)) args.forEach(function (args) { - // remove readme unless we asked for it - if (-1 === args.indexOf("readme")) { - delete versions[v].readme - } - results.push(showFields(data, versions[v], 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 (-1 === args.indexOf("readme")) { + delete versions[v].readme + } + results.push(showFields(data, versions[v], args)) + }) }) - }) - results = results.reduce(reducer, {}) - var retval = results + results = results.reduce(reducer, {}) + var retval = results - if (args.length === 1 && args[0] === "") { - retval = cleanBlanks(retval) - log.silly("cleanup", retval) - } + if (args.length === 1 && args[0] === "") { + retval = cleanBlanks(retval) + log.silly("cleanup", retval) + } - if (error || silent) cb(error, retval) - else printData(results, data._id, cb.bind(null, error, retval)) + if (error || silent) cb(error, retval) + else printData(results, data._id, cb.bind(null, error, retval)) + }) }) } diff --git a/node_modules/npm-registry-client/lib/publish.js b/node_modules/npm-registry-client/lib/publish.js index 745d93297..aea8565bf 100644 --- a/node_modules/npm-registry-client/lib/publish.js +++ b/node_modules/npm-registry-client/lib/publish.js @@ -7,14 +7,22 @@ var url = require("url") , fs = require("fs") , fixNameField = require("normalize-package-data/lib/fixer.js").fixNameField +var toNerfDart = require("./util/nerf-dart.js") + function escaped(name) { return name.replace("/", "%2f") } function publish (uri, data, tarball, cb) { - var email = this.conf.get('email') - var auth = this.conf.get('_auth') - var username = this.conf.get('username') + var email = this.conf.get('email') || + this.conf.get(toNerfDart(uri) + ':email') + var auth = this.conf.get('_auth') || + this.conf.get(toNerfDart(uri) + ':_auth') + var username + if (auth) { + var creds = new Buffer(auth, "base64").toString("utf8") + if (creds) username = creds.split(":")[0] + } if (!email || !auth || !username) { var er = new Error("auth and email required for publishing") diff --git a/node_modules/npm-registry-client/lib/request.js b/node_modules/npm-registry-client/lib/request.js index 1925dd25f..d26be15dd 100644 --- a/node_modules/npm-registry-client/lib/request.js +++ b/node_modules/npm-registry-client/lib/request.js @@ -8,8 +8,9 @@ var url = require("url") , request = require("request") , retry = require("retry") , crypto = require("crypto") - , pkg = require("../package.json") +var pkg = require("../package.json") + , toNerfDart = require("./util/nerf-dart.js") // npm: means // 1. https @@ -65,10 +66,9 @@ function regRequest (method, uri, options, cb_) { , adduserNew = /^\/?-\/user\/org\.couchdb\.user:([^\/]+)/ , nu = where.match(adduserNew) , uc = where.match(adduserChange) - , alwaysAuth = this.conf.get('always-auth') + , alwaysAuth = this.conf.get("always-auth") , isDel = method === "DELETE" , isWrite = what || isDel - , authRequired = (authThis || alwaysAuth || isWrite) && !nu || uc || isDel // resolve to a full url on the registry if (!where.match(/^https?:\/\//)) { @@ -94,32 +94,39 @@ function regRequest (method, uri, options, cb_) { this.log.verbose("request", "where is", where) var remote = url.parse(where) - , auth = this.conf.get('_auth') - - if (authRequired && !auth) { - var un = this.conf.get('username') - var pw = this.conf.get('_password') + if ((authThis || alwaysAuth || isWrite) && !nu || uc || isDel) { + // 1. see if there's multi-registry auth in there + var auth = this.conf.get(toNerfDart(where) + ":_auth") + + // 2. check for (deprecated) generic auth + if (!auth) auth = this.conf.get("_auth") + + // 3. check to see if npmconf has unbundled the credentials + if (!auth) { + var un = this.conf.get("username") + var pw = this.conf.get("_password") + + if (!(un && pw)) { + return cb(new Error( + "This request requires auth credentials. Run `npm login` and repeat the request." + )) + } - if (!(un && pw)) { - return cb(new Error( - "This request requires auth credentials. Run `npm login` and repeat the request." - )) + auth = new Buffer(un + ":" + pw).toString("base64") } - auth = new Buffer(un + ':' + pw).toString('base64') - } - - if (auth && authRequired) { - remote.auth = new Buffer(auth, "base64").toString("utf8") + if (auth) { + remote.auth = new Buffer(auth, "base64").toString("utf8") + } } // Tuned to spread 3 attempts over about a minute. // See formula at <https://github.com/tim-kos/node-retry>. var operation = retry.operation({ - retries: this.conf.get('fetch-retries') || 2, - factor: this.conf.get('fetch-retry-factor'), - minTimeout: this.conf.get('fetch-retry-mintimeout') || 10000, - maxTimeout: this.conf.get('fetch-retry-maxtimeout') || 60000 + retries: this.conf.get("fetch-retries") || 2, + factor: this.conf.get("fetch-retry-factor"), + minTimeout: this.conf.get("fetch-retry-mintimeout") || 10000, + maxTimeout: this.conf.get("fetch-retry-maxtimeout") || 60000 }) var self = this @@ -130,7 +137,7 @@ function regRequest (method, uri, options, cb_) { , function (er, parsed, raw, response) { if (!er || (er.message && er.message.match(/^SSL Error/))) { if (er) - er.code = 'ESSL' + er.code = "ESSL" return cb(er, parsed, raw, response) } @@ -142,7 +149,7 @@ function regRequest (method, uri, options, cb_) { var statusRetry = !statusCode || timeout || serverError if (er && statusRetry && operation.retry(er)) { self.log.info("retry", "will retry, error on last attempt: " + er) - return + return undefined } if (response) this.log.verbose("headers", response.headers) @@ -159,15 +166,15 @@ function makeRequest (method, remote, where, what, etag, nofollow, cb_) { cb_.apply(null, arguments) } - var strict = this.conf.get('strict-ssl') + var strict = this.conf.get("strict-ssl") if (strict === undefined) strict = true var opts = { url: remote , method: method , encoding: null // tell request let body be Buffer instance - , ca: this.conf.get('ca') - , localAddress: this.conf.get('local-address') - , cert: this.conf.get('cert') - , key: this.conf.get('key') + , ca: this.conf.get("ca") + , localAddress: this.conf.get("local-address") + , cert: this.conf.get("cert") + , key: this.conf.get("key") , strictSSL: strict } , headers = opts.headers = {} if (etag) { @@ -175,7 +182,7 @@ function makeRequest (method, remote, where, what, etag, nofollow, cb_) { headers[method === "GET" ? "if-none-match" : "if-match"] = etag } - headers['npm-session'] = this.sessionToken + headers["npm-session"] = this.sessionToken headers.version = this.version || pkg.version if (this.refer) { @@ -183,12 +190,12 @@ function makeRequest (method, remote, where, what, etag, nofollow, cb_) { } headers.accept = "application/json" - headers['accept-encoding'] = 'gzip' + headers["accept-encoding"] = "gzip" - headers["user-agent"] = this.conf.get('user-agent') || - 'node/' + process.version + headers["user-agent"] = this.conf.get("user-agent") || + "node/" + process.version - var tokenKey = toKey(url.format(remote)) + var tokenKey = toNerfDart(url.format(remote)) + ":_authToken" this.log.silly("tokenKey", tokenKey) var authToken = this.conf.get(tokenKey) if (authToken) { @@ -197,11 +204,11 @@ function makeRequest (method, remote, where, what, etag, nofollow, cb_) { headers.authorization = "Bearer " + authToken } - var p = this.conf.get('proxy') - var sp = this.conf.get('https-proxy') || p + var p = this.conf.get("proxy") + var sp = this.conf.get("https-proxy") || p opts.proxy = remote.protocol === "https:" ? sp : p - // figure out wth 'what' is + // figure out wth "what" is if (what) { if (Buffer.isBuffer(what) || typeof what === "string") { opts.body = what @@ -245,7 +252,7 @@ function decodeResponseBody(cb) { response.socket.destroy() } - if (response.headers['content-encoding'] !== 'gzip') return cb(er, response, data) + if (response.headers["content-encoding"] !== "gzip") return cb(er, response, data) zlib.gunzip(data, function (er, buf) { if (er) return cb(er, response, data) @@ -262,7 +269,7 @@ function requestDone (method, where, cb) { var urlObj = url.parse(where) if (urlObj.auth) - urlObj.auth = '***' + urlObj.auth = "***" this.log.http(response.statusCode, url.format(urlObj)) var parsed @@ -303,7 +310,7 @@ function requestDone (method, where, cb) { if (!w.match(/^-/) && parsed.error === "not_found") { w = w.split("/") name = w[w.indexOf("_rewrite") + 1] - er = new Error("404 Not Found: "+name) + er = new Error("404 Not Found: " + name) er.code = "E404" er.pkgid = name } else { @@ -322,12 +329,3 @@ function requestDone (method, where, cb) { return cb(er, parsed, data, response) }.bind(this) } - -function toKey(uri) { - var parsed = url.parse(uri) - parsed.pathname = "/" - delete parsed.protocol - delete parsed.auth - - return url.format(parsed) + ":_authToken" -} diff --git a/node_modules/npm-registry-client/lib/util/nerf-dart.js b/node_modules/npm-registry-client/lib/util/nerf-dart.js new file mode 100644 index 000000000..3b26a56c6 --- /dev/null +++ b/node_modules/npm-registry-client/lib/util/nerf-dart.js @@ -0,0 +1,21 @@ +var url = require("url") + +module.exports = toNerfDart + +/** + * Maps a URL to an identifier. + * + * Name courtesy schiffertronix media LLC, a New Jersey corporation + * + * @param {String} uri The URL to be nerfed. + * + * @returns {String} A nerfed URL. + */ +function toNerfDart(uri) { + var parsed = url.parse(uri) + parsed.pathname = "/" + delete parsed.protocol + delete parsed.auth + + return url.format(parsed) +} diff --git a/node_modules/npm-registry-client/node_modules/normalize-package-data/lib/fixer.js b/node_modules/npm-registry-client/node_modules/normalize-package-data/lib/fixer.js index 82c1348be..72836002f 100644 --- a/node_modules/npm-registry-client/node_modules/normalize-package-data/lib/fixer.js +++ b/node_modules/npm-registry-client/node_modules/normalize-package-data/lib/fixer.js @@ -290,24 +290,25 @@ var fixer = module.exports = { } } -function ensureValidName (name, strict) { - function scoped(spec) { - if (spec.charAt(0) !== '@') return false +function isValidScopedPackageName(spec) { + if (spec.charAt(0) !== '@') return false - var rest = spec.slice(1).split('/') - if (rest.length !== 2) return false + var rest = spec.slice(1).split('/') + if (rest.length !== 2) return false - return rest[0] === encodeURIComponent(rest[0]) && - rest[1] === encodeURIComponent(rest[1]) - } + return rest[0] && rest[1] && + rest[0] === encodeURIComponent(rest[0]) && + rest[1] === encodeURIComponent(rest[1]) +} - function scopedOrEncoded(spec) { - return !scoped(spec) && - (spec.match(/[\/@\s\+%:]/) || spec !== encodeURIComponent(spec)) - } +function isCorrectlyEncodedName(spec) { + return !spec.match(/[\/@\s\+%:]/) && + spec === encodeURIComponent(spec) +} +function ensureValidName (name, strict) { if (name.charAt(0) === "." || - scopedOrEncoded(name) || + !(isValidScopedPackageName(name) || isCorrectlyEncodedName(name)) || (strict && name !== name.toLowerCase()) || name.toLowerCase() === "node_modules" || name.toLowerCase() === "favicon.ico") { diff --git a/node_modules/npm-registry-client/node_modules/normalize-package-data/package.json b/node_modules/npm-registry-client/node_modules/normalize-package-data/package.json index 3c1737d50..18fdd92ad 100644 --- a/node_modules/npm-registry-client/node_modules/normalize-package-data/package.json +++ b/node_modules/npm-registry-client/node_modules/normalize-package-data/package.json @@ -45,7 +45,7 @@ }, "homepage": "https://github.com/meryn/normalize-package-data", "_id": "normalize-package-data@0.3.0", - "_shasum": "d763a0bdb2bc4f4cf9d355aab9991bd7585e0c57", - "_resolved": "git://github.com/npm/normalize-package-data#15869dc14c1ac8e3b202586f62f755c69443e403", + "_shasum": "61130418af14cee0f569f5a5970a3f3960c5b36d", + "_resolved": "git://github.com/npm/normalize-package-data#84bae1f6f8061b664c05c5f06abae316f3cc0b53", "_from": "normalize-package-data@git://github.com/npm/normalize-package-data#othiym23/scoped" } diff --git a/node_modules/npm-registry-client/node_modules/normalize-package-data/test/scoped.js b/node_modules/npm-registry-client/node_modules/normalize-package-data/test/scoped.js index 0f17e9f5b..31bbf4f7f 100644 --- a/node_modules/npm-registry-client/node_modules/normalize-package-data/test/scoped.js +++ b/node_modules/npm-registry-client/node_modules/normalize-package-data/test/scoped.js @@ -12,8 +12,7 @@ test("a simple scoped module has a valid name", function (t) { test("'org@package' is not a valid name", function (t) { t.throws(function () { - var data = {name : "org@package"} - fixNameField(data, false) + fixNameField({name : "org@package"}, false) }, "blows up as expected") t.end() @@ -21,8 +20,7 @@ test("'org@package' is not a valid name", function (t) { test("'org=package' is not a valid name", function (t) { t.throws(function () { - var data = {name : "org=package"} - fixNameField(data, false) + fixNameField({name : "org=package"}, false) }, "blows up as expected") t.end() @@ -30,8 +28,23 @@ test("'org=package' is not a valid name", function (t) { test("'@org=sub/package' is not a valid name", function (t) { t.throws(function () { - var data = {name : "@org=sub/package"} - fixNameField(data, false) + fixNameField({name : "@org=sub/package"}, false) + }, "blows up as expected") + + t.end() +}) + +test("'@org/' is not a valid name", function (t) { + t.throws(function () { + fixNameField({name : "@org/"}, false) + }, "blows up as expected") + + t.end() +}) + +test("'@/package' is not a valid name", function (t) { + t.throws(function () { + fixNameField({name : "@/package"}, false) }, "blows up as expected") t.end() diff --git a/node_modules/npm-registry-client/package.json b/node_modules/npm-registry-client/package.json index 87b635560..2ac5ea15c 100644 --- a/node_modules/npm-registry-client/package.json +++ b/node_modules/npm-registry-client/package.json @@ -41,7 +41,7 @@ }, "homepage": "https://github.com/isaacs/npm-registry-client", "_id": "npm-registry-client@2.0.2", - "_shasum": "d69124ffd5c82173dfa5cb092132799def53056c", - "_resolved": "git://github.com/npm/npm-registry-client#a5ee4ea90bb066466837cc42ed59fb3d5101c4ed", + "_shasum": "13578a17a2876db802542dff7e0b2950efcc23e3", + "_resolved": "git://github.com/npm/npm-registry-client#74a250ce75853a173f094e7cbae2570453f17b3c", "_from": "git://github.com/npm/npm-registry-client#othiym23/multi-registry" } diff --git a/node_modules/npm-registry-client/test/lib/common.js b/node_modules/npm-registry-client/test/lib/common.js index f9048c094..8d369a418 100644 --- a/node_modules/npm-registry-client/test/lib/common.js +++ b/node_modules/npm-registry-client/test/lib/common.js @@ -1,16 +1,20 @@ var resolve = require("path").resolve -var server = require('./server.js') -var RC = require('../../') +var server = require("./server.js") +var RC = require("../../") + +var REGISTRY = "http://localhost:" + server.port module.exports = { + port : server.port, + registry : REGISTRY, freshClient : function freshClient(config) { config = config || {} - config.cache = resolve(__dirname, '../fixtures/cache') - config.registry = 'http://localhost:' + server.port + config.cache = resolve(__dirname, "../fixtures/cache") + config.registry = REGISTRY var client = new RC(config) server.log = client.log - client.log.level = 'silent' + client.log.level = "silent" return client } diff --git a/node_modules/npm-registry-client/test/publish-scoped-auth-token.js b/node_modules/npm-registry-client/test/publish-scoped-auth-token.js new file mode 100644 index 000000000..8adec406b --- /dev/null +++ b/node_modules/npm-registry-client/test/publish-scoped-auth-token.js @@ -0,0 +1,60 @@ +var tap = require("tap") +var crypto = require("crypto") +var fs = require("fs") + +var toNerfDart = require("../lib/util/nerf-dart.js") +var server = require("./lib/server.js") +var common = require("./lib/common.js") + +var configuration = {"always-auth" : true} + +var authKey = toNerfDart(common.registry) + ":_auth" +configuration[authKey] = new Buffer("username:password").toString("base64") + +var emailKey = toNerfDart(common.registry) + ":email" +configuration[emailKey] = "ogd@aoaioxxysz.net" + +var tokenKey = toNerfDart(common.registry) + ":_authToken" +configuration[tokenKey] = "of-glad-tidings" + +var client = common.freshClient(configuration) + +tap.test("publish", function (t) { + // not really a tarball, but doesn't matter + var tarball = require.resolve("../package.json") + var pd = fs.readFileSync(tarball, "base64") + var pkg = require("../package.json") + pkg.name = "@npm/npm-registry-client" + + server.expect("/@npm%2fnpm-registry-client", function (req, res) { + t.equal(req.method, "PUT") + t.equal(req.headers.authorization, "Bearer of-glad-tidings") + + var b = "" + req.setEncoding("utf8") + req.on("data", function (d) { + b += d + }) + + req.on("end", function () { + var o = JSON.parse(b) + t.equal(o._id, "@npm/npm-registry-client") + t.equal(o["dist-tags"].latest, pkg.version) + t.has(o.versions[pkg.version], pkg) + t.same(o.maintainers, [ { name: "username", email: "ogd@aoaioxxysz.net" } ]) + t.same(o.maintainers, o.versions[pkg.version].maintainers) + var att = o._attachments[ pkg.name + "-" + pkg.version + ".tgz" ] + t.same(att.data, pd) + var hash = crypto.createHash("sha1").update(pd, "base64").digest("hex") + t.equal(o.versions[pkg.version].dist.shasum, hash) + res.statusCode = 201 + res.json({created:true}) + }) + }) + + client.publish(common.registry, pkg, tarball, function (er, data) { + if (er) throw er + t.deepEqual(data, { created: true }) + t.end() + }) +}) diff --git a/node_modules/npm-registry-client/test/publish-scoped.js b/node_modules/npm-registry-client/test/publish-scoped.js index 9b83341dd..e71d68d49 100644 --- a/node_modules/npm-registry-client/test/publish-scoped.js +++ b/node_modules/npm-registry-client/test/publish-scoped.js @@ -2,20 +2,32 @@ var tap = require("tap") var crypto = require("crypto") var fs = require("fs") +var toNerfDart = require("../lib/util/nerf-dart.js") var server = require("./lib/server.js") var common = require("./lib/common.js") -var client = common.freshClient({ - username: "username", - password: "password", - email: "i@izs.me", - _auth: new Buffer("username:password").toString("base64"), - "always-auth": true -}) +var configuration = {"always-auth" : true} + +var authKey = toNerfDart(common.registry) + ":_auth" +var _auth = new Buffer("username:password").toString("base64") +configuration[authKey] = _auth + +var emailKey = toNerfDart(common.registry) + ":email" +configuration[emailKey] = "ogd@aoaioxxysz.net" + +var client = common.freshClient(configuration) tap.test("publish", function (t) { + // not really a tarball, but doesn't matter + var tarball = require.resolve("../package.json") + var pd = fs.readFileSync(tarball, "base64") + var pkg = require("../package.json") + pkg.name = "@npm/npm-registry-client" + server.expect("/@npm%2fnpm-registry-client", function (req, res) { t.equal(req.method, "PUT") + t.equal(req.headers.authorization, "Basic " + _auth) + var b = "" req.setEncoding("utf8") req.on("data", function (d) { @@ -27,7 +39,7 @@ tap.test("publish", function (t) { t.equal(o._id, "@npm/npm-registry-client") t.equal(o["dist-tags"].latest, pkg.version) t.has(o.versions[pkg.version], pkg) - t.same(o.maintainers, [ { name: "username", email: "i@izs.me" } ]) + t.same(o.maintainers, [ { name: "username", email: "ogd@aoaioxxysz.net" } ]) t.same(o.maintainers, o.versions[pkg.version].maintainers) var att = o._attachments[ pkg.name + "-" + pkg.version + ".tgz" ] t.same(att.data, pd) @@ -38,12 +50,7 @@ tap.test("publish", function (t) { }) }) - // not really a tarball, but doesn't matter - var tarball = require.resolve("../package.json") - var pd = fs.readFileSync(tarball, "base64") - var pkg = require("../package.json") - pkg.name = '@npm/npm-registry-client' - client.publish("http://localhost:1337/", pkg, tarball, function (er, data) { + client.publish(common.registry, pkg, tarball, function (er, data) { if (er) throw er t.deepEqual(data, { created: true }) t.end() diff --git a/test/tap/config-meta.js b/test/tap/config-meta.js index b116ca8a0..94261e46b 100644 --- a/test/tap/config-meta.js +++ b/test/tap/config-meta.js @@ -20,7 +20,8 @@ var exceptions = [ path.resolve(lib, "config.js"), path.resolve(lib, "publish.js"), path.resolve(lib, "utils", "lifecycle.js"), - path.resolve(lib, "utils", "map-name-to-registry.js"), + path.resolve(lib, "utils", "map-to-registry.js"), + path.resolve(nm, "npm-registry-client", "lib", "publish.js"), path.resolve(nm, "npm-registry-client", "lib", "request.js") ] diff --git a/test/tap/install-at-locally.js b/test/tap/install-at-locally.js index 18ea6c3a6..f6290e108 100644 --- a/test/tap/install-at-locally.js +++ b/test/tap/install-at-locally.js @@ -1,7 +1,6 @@ var common = require('../common-tap.js') var test = require('tap').test var npm = require('../../') -var osenv = require('osenv') var path = require('path') var fs = require('fs') var rimraf = require('rimraf') diff --git a/test/tap/install-save-exact.js b/test/tap/install-save-exact.js index cf25b779b..c9f486660 100644 --- a/test/tap/install-save-exact.js +++ b/test/tap/install-save-exact.js @@ -1,7 +1,6 @@ var common = require('../common-tap.js') var test = require('tap').test var npm = require('../../') -var osenv = require('osenv') var path = require('path') var fs = require('fs') var rimraf = require('rimraf') diff --git a/test/tap/install-save-prefix.js b/test/tap/install-save-prefix.js index bbdeddf3f..0e78005b6 100644 --- a/test/tap/install-save-prefix.js +++ b/test/tap/install-save-prefix.js @@ -1,7 +1,6 @@ var common = require('../common-tap.js') var test = require('tap').test var npm = require('../../') -var osenv = require('osenv') var path = require('path') var fs = require('fs') var rimraf = require('rimraf') diff --git a/test/tap/publish-organized.js b/test/tap/publish-scoped.js index 8ed2fd57a..4e7d557ff 100644 --- a/test/tap/publish-organized.js +++ b/test/tap/publish-scoped.js @@ -1,4 +1,4 @@ -var fs = require('fs') +var fs = require("fs") var path = require("path") var test = require("tap").test @@ -6,8 +6,8 @@ var mkdirp = require("mkdirp") var rimraf = require("rimraf") var nock = require("nock") -var npm = require('../../') -var common = require('../common-tap.js') +var npm = require("../../") +var common = require("../common-tap.js") var pkg = path.join(__dirname, "prepublish_package") @@ -15,7 +15,6 @@ test("setup", function (t) { mkdirp(path.join(pkg, "cache"), next) function next () { - console.log("in next") process.chdir(pkg) fs.writeFile( path.join(pkg, "package.json"), @@ -40,9 +39,12 @@ test("npm publish should honor scoping", function (t) { var configuration = { cache : path.join(pkg, "cache"), loglevel : "silent", - registry : "http://nonexistent.lvh.me" + registry : "http://nonexistent.lvh.me", + "//localhost:1337/:_auth" : new Buffer("testuser:password", "utf8").toString("base64") } + npm.load(configuration, onload) + function onload (er) { t.ifError(er, "npm bootstrapped successfully") @@ -55,8 +57,6 @@ test("npm publish should honor scoping", function (t) { t.end() }) } - - npm.load(configuration, onload) }) test("cleanup", function(t) { diff --git a/test/tap/scoped-module-specifiers.js b/test/tap/scoped-module-specifiers.js index b58563eb0..4b8f105ec 100644 --- a/test/tap/scoped-module-specifiers.js +++ b/test/tap/scoped-module-specifiers.js @@ -2,7 +2,7 @@ var util = require("util") var test = require("tap").test -var isOrganized = require("../../lib/utils/is-organized.js") +var isScoped = require("../../lib/utils/is-scoped.js") function Config() { this._store = Object.create(null) @@ -52,7 +52,7 @@ test("recognizing organized URLs", function (t) { function canonicalize(spec) { var expanded = splitSpec(spec) - if (isOrganized(expanded.name)) { + if (isScoped(expanded.name)) { return util.format( "npm://%s/%s, v: %s", mapRegistry(config, expanded.name), |