npm-registry-client@1.0.0

Scope all cache files under the registry hostname, using the
npm-cache-filename module.

TODO: also store tarball files in the same spots, so that two
files from different registries don't clobber one another.
This commit is contained in:
isaacs
2014-05-14 16:20:30 -07:00
parent c29a0ee5ed
commit 43b5eec98b
11 changed files with 136 additions and 47 deletions

View File

@@ -6,6 +6,7 @@ var fs = require('fs')
, url = require('url')
, path = require('path')
, npmlog
, cacheFile = require('npm-cache-filename')
try {
npmlog = require("npmlog")
@@ -45,7 +46,10 @@ function RegClient (conf) {
registry = null
}
this.registry = registry
if (!conf.get('cache')) throw new Error("Cache dir is required")
this.cacheFile = cacheFile(this.conf.get('cache'))
this.log = conf.log || conf.get('log') || npmlog
}

View File

@@ -21,18 +21,15 @@ function get (uri, timeout, nofollow, staleOk, cb) {
&& process.env.COMP_POINT !== undefined
) timeout = Math.max(timeout, 60000)
var cache = this.cacheFile(this.registry + uri) + "/.cache.json"
// /-/all is special.
// It uses timestamp-based caching and partial updates,
// because it is a monster.
if (uri === "/-/all") {
return requestAll.call(this, cb)
return requestAll.call(this, cache, cb)
}
var cacheUri = uri
// on windows ":" is not an allowed character in a foldername
cacheUri = cacheUri.replace(/:/g, '_').replace(/\?write=true$/, '')
var cache = path.join(this.conf.get('cache'), cacheUri, ".cache.json")
// If the GET is part of a write operation (PUT or DELETE), then
// skip past the cache entirely, but still save the results.
if (uri.match(/\?write=true$/))
@@ -49,28 +46,25 @@ function get (uri, timeout, nofollow, staleOk, cb) {
}.bind(this))
}
function requestAll (cb) {
var cache = path.join(this.conf.get('cache'), "/-/all", ".cache.json")
mkdir(path.join(this.conf.get('cache'), "-", "all"), function (er) {
if (er) return cb(er)
function requestAll (cache, cb) {
mkdir(path.dirname(cache), function (er) {
fs.readFile(cache, function (er, data) {
if (er) return requestAll_.call(this, 0, {}, cb)
if (er) return requestAll_.call(this, 0, {}, cache, cb)
try {
data = JSON.parse(data)
} catch (ex) {
fs.writeFile(cache, "{}", function (er) {
if (er) return cb(new Error("Broken cache."))
return requestAll_.call(this, 0, {}, cb)
return requestAll_.call(this, 0, {}, cache, cb)
}.bind(this))
}
var t = +data._updated || 0
requestAll_.call(this, t, data, cb)
requestAll_.call(this, t, data, cache, cb)
}.bind(this))
}.bind(this))
}
function requestAll_ (c, data, cb) {
function requestAll_ (c, data, cache, cb) {
// use the cache and update in the background if it's not too old
if (Date.now() - c < 60000) {
cb(null, data)
@@ -84,7 +78,6 @@ function requestAll_ (c, data, cb) {
uri = "/-/all"
}
var cache = path.join(this.conf.get('cache'), "-/all", ".cache.json")
this.request('GET', uri, function (er, updates, _, res) {
if (er) return cb(er, data)
var headers = res.headers

View File

@@ -281,26 +281,7 @@ function requestDone (method, where, cb) {
// from thinking that it didn't work when it did.
// Note that failure is an acceptable option here, since the
// only result will be a stale cache for some helper commands.
var path = require("path")
, p = url.parse(where).pathname.split("/")
, _ = "/"
, caches = p.map(function (part) {
part = part.replace(/:/g, "_")
return _ = path.join(_, part)
}).map(function (cache) {
return path.join(this.conf.get('cache'), cache, ".cache.json")
}, this)
// if the method is DELETE, then also remove the thing itself.
// Note that the search index is probably invalid. Whatever.
// That's what you get for deleting stuff. Don't do that.
if (method === "DELETE") {
p = p.slice(0, p.indexOf("-rev"))
p = p.join("/").replace(/:/g, "_")
caches.push(path.join(this.conf.get('cache'), p))
}
asyncMap(caches, rm, function () {})
rm(this.cacheFile(where), function() {})
}
return cb(er, parsed, data, response)
}.bind(this)

View File

@@ -0,0 +1,15 @@
The ISC License
Copyright (c) npm, Inc. and Contributors
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

View File

@@ -0,0 +1,21 @@
# npm-cache-filename
Given a cache folder and url, return the appropriate cache folder.
## USAGE
```javascript
var cf = require('npm-cache-filename');
console.log(cf('/tmp/cache', 'https://registry.npmjs.org:1234/foo/bar'));
// outputs: /tmp/cache/registry.npmjs.org_1234/foo/bar
```
As a bonus, you can also bind it to a specific root path:
```javascript
var cf = require('npm-cache-filename');
var getFile = cf('/tmp/cache');
console.log(getFile('https://registry.npmjs.org:1234/foo/bar'));
// outputs: /tmp/cache/registry.npmjs.org_1234/foo/bar
```

View File

@@ -0,0 +1,20 @@
var url = require('url');;
var path = require('path');;
module.exports = cf;;
function cf(root, u) {
if (!u)
return cf.bind(null, root);;
u = url.parse(u);;
var h = u.host.replace(/:/g, '_');;
// Strip off any /-rev/... or ?rev=... bits
var revre = /(\?rev=|\?.*?&rev=|\/-rev\/).*$/
var parts = u.path.replace(revre, '').split('/').slice(1)
var p = [root, h].concat(parts.map(function(part) {
return encodeURIComponent(part).replace(/%/g, '_');;
}));;
return path.join.apply(path, p);;
}

View File

@@ -0,0 +1,33 @@
{
"name": "npm-cache-filename",
"version": "1.0.1",
"description": "Given a cache folder and url, return the appropriate cache folder.",
"main": "index.js",
"dependencies": {},
"devDependencies": {
"tap": "^0.4.10"
},
"scripts": {
"test": "tap test.js"
},
"repository": {
"type": "git",
"url": "git://github.com/npm/npm-cache-filename"
},
"author": {
"name": "Isaac Z. Schlueter",
"email": "i@izs.me",
"url": "http://blog.izs.me/"
},
"license": "ISC",
"bugs": {
"url": "https://github.com/npm/npm-cache-filename/issues"
},
"homepage": "https://github.com/npm/npm-cache-filename",
"readme": "# npm-cache-filename\n\nGiven a cache folder and url, return the appropriate cache folder.\n\n## USAGE\n\n```javascript\nvar cf = require('npm-cache-filename');\nconsole.log(cf('/tmp/cache', 'https://registry.npmjs.org:1234/foo/bar'));\n// outputs: /tmp/cache/registry.npmjs.org_1234/foo/bar\n```\n\nAs a bonus, you can also bind it to a specific root path:\n\n```javascript\nvar cf = require('npm-cache-filename');\nvar getFile = cf('/tmp/cache');\n\nconsole.log(getFile('https://registry.npmjs.org:1234/foo/bar'));\n// outputs: /tmp/cache/registry.npmjs.org_1234/foo/bar\n```\n",
"readmeFilename": "README.md",
"_id": "npm-cache-filename@1.0.1",
"_shasum": "9b640f0c1a5ba1145659685372a9ff71f70c4323",
"_from": "npm-cache-filename@^1.0.0",
"_resolved": "https://registry.npmjs.org/npm-cache-filename/-/npm-cache-filename-1.0.1.tgz"
}

View File

@@ -0,0 +1,21 @@
var test = require('tap').test;;
test('it does the thing it says it does', function(t) {
var cf = require('./');;
t.equal(cf('/tmp/cache', 'https://foo:134/xyz?adf=foo:bar/baz'),
'/tmp/cache/foo_134/xyz_3Fadf_3Dfoo_3Abar/baz');;
var getFile = cf('/tmp/cache');;
t.equal(getFile('https://foo:134/xyz?adf=foo:bar/baz'),
'/tmp/cache/foo_134/xyz_3Fadf_3Dfoo_3Abar/baz');;
t.equal(cf("/tmp", "https://foo:134/xyz/-rev/baz"),
'/tmp/foo_134/xyz')
t.equal(cf("/tmp", "https://foo:134/xyz/?rev=baz"),
'/tmp/foo_134/xyz')
t.equal(cf("/tmp", "https://foo:134/xyz/?foo&rev=baz"),
'/tmp/foo_134/xyz')
t.equal(cf("/tmp", "https://foo:134/xyz-rev/baz"),
'/tmp/foo_134/xyz-rev/baz')
t.end();
});;

View File

@@ -6,7 +6,7 @@
},
"name": "npm-registry-client",
"description": "Client for the npm registry",
"version": "0.4.12",
"version": "1.0.0",
"repository": {
"url": "git://github.com/isaacs/npm-registry-client"
},
@@ -15,14 +15,15 @@
"test": "tap test/*.js"
},
"dependencies": {
"request": "2 >=2.25.0",
"chownr": "0",
"graceful-fs": "~2.0.0",
"mkdirp": "~0.3.3",
"npm-cache-filename": "^1.0.0",
"request": "2 >=2.25.0",
"retry": "0.6.0",
"rimraf": "~2",
"semver": "2 >=2.2.1",
"slide": "~1.1.3",
"chownr": "0",
"mkdirp": "~0.3.3",
"rimraf": "~2",
"retry": "0.6.0",
"npmlog": ""
},
"devDependencies": {
@@ -38,7 +39,7 @@
"url": "https://github.com/isaacs/npm-registry-client/issues"
},
"homepage": "https://github.com/isaacs/npm-registry-client",
"_id": "npm-registry-client@0.4.12",
"_shasum": "34303422f6a3da93ca3a387a2650d707c8595b99",
"_from": "npm-registry-client@latest"
"_id": "npm-registry-client@1.0.0",
"_shasum": "2a6f9dfdce5f8ebf4b9af4dbfd738384d25014e5",
"_from": "npm-registry-client@1"
}