cacache 12.0.4 → 15.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,14 +1,19 @@
1
1
  'use strict'
2
2
 
3
- const fs = require('graceful-fs')
4
- const BB = require('bluebird')
5
- const chmod = BB.promisify(fs.chmod)
6
- const unlink = BB.promisify(fs.unlink)
7
- let move
8
- let pinflight
3
+ const fs = require('fs')
4
+ const util = require('util')
5
+ const chmod = util.promisify(fs.chmod)
6
+ const unlink = util.promisify(fs.unlink)
7
+ const stat = util.promisify(fs.stat)
8
+ const move = require('move-concurrently')
9
+ const pinflight = require('promise-inflight')
9
10
 
10
11
  module.exports = moveFile
12
+
11
13
  function moveFile (src, dest) {
14
+ const isWindows = global.__CACACHE_TEST_FAKE_WINDOWS__ ||
15
+ process.platform === 'win32'
16
+
12
17
  // This isn't quite an fs.rename -- the assumption is that
13
18
  // if `dest` already exists, and we get certain errors while
14
19
  // trying to move it, we should just not bother.
@@ -18,34 +23,46 @@ function moveFile (src, dest) {
18
23
  // content their own way.
19
24
  //
20
25
  // Note that, as the name suggests, this strictly only supports file moves.
21
- return BB.fromNode(cb => {
22
- fs.link(src, dest, err => {
26
+ return new Promise((resolve, reject) => {
27
+ fs.link(src, dest, (err) => {
23
28
  if (err) {
24
- if (err.code === 'EEXIST' || err.code === 'EBUSY') {
29
+ if (isWindows && err.code === 'EPERM') {
30
+ // XXX This is a really weird way to handle this situation, as it
31
+ // results in the src file being deleted even though the dest
32
+ // might not exist. Since we pretty much always write files to
33
+ // deterministic locations based on content hash, this is likely
34
+ // ok (or at worst, just ends in a future cache miss). But it would
35
+ // be worth investigating at some time in the future if this is
36
+ // really what we want to do here.
37
+ return resolve()
38
+ } else if (err.code === 'EEXIST' || err.code === 'EBUSY') {
25
39
  // file already exists, so whatever
26
- } else if (err.code === 'EPERM' && process.platform === 'win32') {
27
- // file handle stayed open even past graceful-fs limits
40
+ return resolve()
28
41
  } else {
29
- return cb(err)
42
+ return reject(err)
30
43
  }
44
+ } else {
45
+ return resolve()
31
46
  }
32
- return cb()
33
47
  })
34
- }).then(() => {
35
- // content should never change for any reason, so make it read-only
36
- return BB.join(unlink(src), process.platform !== 'win32' && chmod(dest, '0444'))
37
- }).catch(() => {
38
- if (!pinflight) { pinflight = require('promise-inflight') }
39
- return pinflight('cacache-move-file:' + dest, () => {
40
- return BB.promisify(fs.stat)(dest).catch(err => {
41
- if (err.code !== 'ENOENT') {
42
- // Something else is wrong here. Bail bail bail
43
- throw err
44
- }
45
- // file doesn't already exist! let's try a rename -> copy fallback
46
- if (!move) { move = require('move-concurrently') }
47
- return move(src, dest, { BB, fs })
48
+ })
49
+ .then(() => {
50
+ // content should never change for any reason, so make it read-only
51
+ return Promise.all([
52
+ unlink(src),
53
+ !isWindows && chmod(dest, '0444')
54
+ ])
55
+ })
56
+ .catch(() => {
57
+ return pinflight('cacache-move-file:' + dest, () => {
58
+ return stat(dest).catch((err) => {
59
+ if (err.code !== 'ENOENT') {
60
+ // Something else is wrong here. Bail bail bail
61
+ throw err
62
+ }
63
+ // file doesn't already exist! let's try a rename -> copy fallback
64
+ return move(src, dest, { Promise, fs })
65
+ })
48
66
  })
49
67
  })
50
- })
51
68
  }
package/lib/util/tmp.js CHANGED
@@ -1,37 +1,35 @@
1
1
  'use strict'
2
2
 
3
- const BB = require('bluebird')
3
+ const util = require('util')
4
4
 
5
- const figgyPudding = require('figgy-pudding')
6
5
  const fixOwner = require('./fix-owner')
7
6
  const path = require('path')
8
- const rimraf = BB.promisify(require('rimraf'))
7
+ const rimraf = util.promisify(require('rimraf'))
9
8
  const uniqueFilename = require('unique-filename')
10
-
11
- const TmpOpts = figgyPudding({
12
- tmpPrefix: {}
13
- })
9
+ const { disposer } = require('./disposer')
14
10
 
15
11
  module.exports.mkdir = mktmpdir
16
- function mktmpdir (cache, opts) {
17
- opts = TmpOpts(opts)
18
- const tmpTarget = uniqueFilename(path.join(cache, 'tmp'), opts.tmpPrefix)
12
+
13
+ function mktmpdir (cache, opts = {}) {
14
+ const { tmpPrefix } = opts
15
+ const tmpTarget = uniqueFilename(path.join(cache, 'tmp'), tmpPrefix)
19
16
  return fixOwner.mkdirfix(cache, tmpTarget).then(() => {
20
17
  return tmpTarget
21
18
  })
22
19
  }
23
20
 
24
21
  module.exports.withTmp = withTmp
22
+
25
23
  function withTmp (cache, opts, cb) {
26
24
  if (!cb) {
27
25
  cb = opts
28
- opts = null
26
+ opts = {}
29
27
  }
30
- opts = TmpOpts(opts)
31
- return BB.using(mktmpdir(cache, opts).disposer(rimraf), cb)
28
+ return disposer(mktmpdir(cache, opts), rimraf, cb)
32
29
  }
33
30
 
34
31
  module.exports.fix = fixtmpdir
32
+
35
33
  function fixtmpdir (cache) {
36
34
  return fixOwner(cache, path.join(cache, 'tmp'))
37
35
  }
package/lib/verify.js CHANGED
@@ -1,35 +1,39 @@
1
1
  'use strict'
2
2
 
3
- const BB = require('bluebird')
3
+ const util = require('util')
4
4
 
5
+ const pMap = require('p-map')
5
6
  const contentPath = require('./content/path')
6
- const figgyPudding = require('figgy-pudding')
7
- const finished = BB.promisify(require('mississippi').finished)
8
7
  const fixOwner = require('./util/fix-owner')
9
- const fs = require('graceful-fs')
10
- const glob = BB.promisify(require('glob'))
8
+ const fs = require('fs')
9
+ const fsm = require('fs-minipass')
10
+ const glob = util.promisify(require('glob'))
11
11
  const index = require('./entry-index')
12
12
  const path = require('path')
13
- const rimraf = BB.promisify(require('rimraf'))
13
+ const rimraf = util.promisify(require('rimraf'))
14
14
  const ssri = require('ssri')
15
15
 
16
- BB.promisifyAll(fs)
16
+ const hasOwnProperty = (obj, key) =>
17
+ Object.prototype.hasOwnProperty.call(obj, key)
17
18
 
18
- const VerifyOpts = figgyPudding({
19
- concurrency: {
20
- default: 20
21
- },
22
- filter: {},
23
- log: {
24
- default: { silly () {} }
25
- }
19
+ const stat = util.promisify(fs.stat)
20
+ const truncate = util.promisify(fs.truncate)
21
+ const writeFile = util.promisify(fs.writeFile)
22
+ const readFile = util.promisify(fs.readFile)
23
+
24
+ const verifyOpts = (opts) => ({
25
+ concurrency: 20,
26
+ log: { silly () {} },
27
+ ...opts
26
28
  })
27
29
 
28
30
  module.exports = verify
31
+
29
32
  function verify (cache, opts) {
30
- opts = VerifyOpts(opts)
33
+ opts = verifyOpts(opts)
31
34
  opts.log.silly('verify', 'verifying cache at', cache)
32
- return BB.reduce([
35
+
36
+ const steps = [
33
37
  markStartTime,
34
38
  fixPerms,
35
39
  garbageCollect,
@@ -37,38 +41,57 @@ function verify (cache, opts) {
37
41
  cleanTmp,
38
42
  writeVerifile,
39
43
  markEndTime
40
- ], (stats, step, i) => {
41
- const label = step.name || `step #${i}`
42
- const start = new Date()
43
- return BB.resolve(step(cache, opts)).then(s => {
44
- s && Object.keys(s).forEach(k => {
45
- stats[k] = s[k]
44
+ ]
45
+
46
+ return steps
47
+ .reduce((promise, step, i) => {
48
+ const label = step.name
49
+ const start = new Date()
50
+ return promise.then((stats) => {
51
+ return step(cache, opts).then((s) => {
52
+ s &&
53
+ Object.keys(s).forEach((k) => {
54
+ stats[k] = s[k]
55
+ })
56
+ const end = new Date()
57
+ if (!stats.runTime) {
58
+ stats.runTime = {}
59
+ }
60
+ stats.runTime[label] = end - start
61
+ return Promise.resolve(stats)
62
+ })
46
63
  })
47
- const end = new Date()
48
- if (!stats.runTime) { stats.runTime = {} }
49
- stats.runTime[label] = end - start
64
+ }, Promise.resolve({}))
65
+ .then((stats) => {
66
+ stats.runTime.total = stats.endTime - stats.startTime
67
+ opts.log.silly(
68
+ 'verify',
69
+ 'verification finished for',
70
+ cache,
71
+ 'in',
72
+ `${stats.runTime.total}ms`
73
+ )
50
74
  return stats
51
75
  })
52
- }, {}).tap(stats => {
53
- stats.runTime.total = stats.endTime - stats.startTime
54
- opts.log.silly('verify', 'verification finished for', cache, 'in', `${stats.runTime.total}ms`)
55
- })
56
76
  }
57
77
 
58
78
  function markStartTime (cache, opts) {
59
- return { startTime: new Date() }
79
+ return Promise.resolve({ startTime: new Date() })
60
80
  }
61
81
 
62
82
  function markEndTime (cache, opts) {
63
- return { endTime: new Date() }
83
+ return Promise.resolve({ endTime: new Date() })
64
84
  }
65
85
 
66
86
  function fixPerms (cache, opts) {
67
87
  opts.log.silly('verify', 'fixing cache permissions')
68
- return fixOwner.mkdirfix(cache, cache).then(() => {
69
- // TODO - fix file permissions too
70
- return fixOwner.chownr(cache, cache)
71
- }).then(() => null)
88
+ return fixOwner
89
+ .mkdirfix(cache, cache)
90
+ .then(() => {
91
+ // TODO - fix file permissions too
92
+ return fixOwner.chownr(cache, cache)
93
+ })
94
+ .then(() => null)
72
95
  }
73
96
 
74
97
  // Implements a naive mark-and-sweep tracing garbage collector.
@@ -84,85 +107,105 @@ function garbageCollect (cache, opts) {
84
107
  opts.log.silly('verify', 'garbage collecting content')
85
108
  const indexStream = index.lsStream(cache)
86
109
  const liveContent = new Set()
87
- indexStream.on('data', entry => {
88
- if (opts.filter && !opts.filter(entry)) { return }
110
+ indexStream.on('data', (entry) => {
111
+ if (opts.filter && !opts.filter(entry)) {
112
+ return
113
+ }
89
114
  liveContent.add(entry.integrity.toString())
90
115
  })
91
- return finished(indexStream).then(() => {
92
- const contentDir = contentPath._contentDir(cache)
116
+ return new Promise((resolve, reject) => {
117
+ indexStream.on('end', resolve).on('error', reject)
118
+ }).then(() => {
119
+ const contentDir = contentPath.contentDir(cache)
93
120
  return glob(path.join(contentDir, '**'), {
94
121
  follow: false,
95
122
  nodir: true,
96
123
  nosort: true
97
- }).then(files => {
98
- return BB.resolve({
124
+ }).then((files) => {
125
+ return Promise.resolve({
99
126
  verifiedContent: 0,
100
127
  reclaimedCount: 0,
101
128
  reclaimedSize: 0,
102
129
  badContentCount: 0,
103
130
  keptSize: 0
104
- }).tap((stats) => BB.map(files, (f) => {
105
- const split = f.split(/[/\\]/)
106
- const digest = split.slice(split.length - 3).join('')
107
- const algo = split[split.length - 4]
108
- const integrity = ssri.fromHex(digest, algo)
109
- if (liveContent.has(integrity.toString())) {
110
- return verifyContent(f, integrity).then(info => {
111
- if (!info.valid) {
112
- stats.reclaimedCount++
113
- stats.badContentCount++
114
- stats.reclaimedSize += info.size
131
+ }).then((stats) =>
132
+ pMap(
133
+ files,
134
+ (f) => {
135
+ const split = f.split(/[/\\]/)
136
+ const digest = split.slice(split.length - 3).join('')
137
+ const algo = split[split.length - 4]
138
+ const integrity = ssri.fromHex(digest, algo)
139
+ if (liveContent.has(integrity.toString())) {
140
+ return verifyContent(f, integrity).then((info) => {
141
+ if (!info.valid) {
142
+ stats.reclaimedCount++
143
+ stats.badContentCount++
144
+ stats.reclaimedSize += info.size
145
+ } else {
146
+ stats.verifiedContent++
147
+ stats.keptSize += info.size
148
+ }
149
+ return stats
150
+ })
115
151
  } else {
116
- stats.verifiedContent++
117
- stats.keptSize += info.size
152
+ // No entries refer to this content. We can delete.
153
+ stats.reclaimedCount++
154
+ return stat(f).then((s) => {
155
+ return rimraf(f).then(() => {
156
+ stats.reclaimedSize += s.size
157
+ return stats
158
+ })
159
+ })
118
160
  }
119
- return stats
120
- })
121
- } else {
122
- // No entries refer to this content. We can delete.
123
- stats.reclaimedCount++
124
- return fs.statAsync(f).then(s => {
125
- return rimraf(f).then(() => {
126
- stats.reclaimedSize += s.size
127
- return stats
128
- })
129
- })
130
- }
131
- }, { concurrency: opts.concurrency }))
161
+ },
162
+ { concurrency: opts.concurrency }
163
+ ).then(() => stats)
164
+ )
132
165
  })
133
166
  })
134
167
  }
135
168
 
136
169
  function verifyContent (filepath, sri) {
137
- return fs.statAsync(filepath).then(stat => {
138
- const contentInfo = {
139
- size: stat.size,
140
- valid: true
141
- }
142
- return ssri.checkStream(
143
- fs.createReadStream(filepath),
144
- sri
145
- ).catch(err => {
146
- if (err.code !== 'EINTEGRITY') { throw err }
147
- return rimraf(filepath).then(() => {
148
- contentInfo.valid = false
149
- })
150
- }).then(() => contentInfo)
151
- }).catch({ code: 'ENOENT' }, () => ({ size: 0, valid: false }))
170
+ return stat(filepath)
171
+ .then((s) => {
172
+ const contentInfo = {
173
+ size: s.size,
174
+ valid: true
175
+ }
176
+ return ssri
177
+ .checkStream(new fsm.ReadStream(filepath), sri)
178
+ .catch((err) => {
179
+ if (err.code !== 'EINTEGRITY') {
180
+ throw err
181
+ }
182
+ return rimraf(filepath).then(() => {
183
+ contentInfo.valid = false
184
+ })
185
+ })
186
+ .then(() => contentInfo)
187
+ })
188
+ .catch((err) => {
189
+ if (err.code === 'ENOENT') {
190
+ return { size: 0, valid: false }
191
+ }
192
+ throw err
193
+ })
152
194
  }
153
195
 
154
196
  function rebuildIndex (cache, opts) {
155
197
  opts.log.silly('verify', 'rebuilding index')
156
- return index.ls(cache).then(entries => {
198
+ return index.ls(cache).then((entries) => {
157
199
  const stats = {
158
200
  missingContent: 0,
159
201
  rejectedEntries: 0,
160
202
  totalEntries: 0
161
203
  }
162
204
  const buckets = {}
163
- for (let k in entries) {
164
- if (entries.hasOwnProperty(k)) {
165
- const hashed = index._hashKey(k)
205
+ for (const k in entries) {
206
+ /* istanbul ignore else */
207
+ if (hasOwnProperty(entries, k)) {
208
+ const hashed = index.hashKey(k)
166
209
  const entry = entries[k]
167
210
  const excluded = opts.filter && !opts.filter(entry)
168
211
  excluded && stats.rejectedEntries++
@@ -172,35 +215,51 @@ function rebuildIndex (cache, opts) {
172
215
  // skip
173
216
  } else if (excluded) {
174
217
  buckets[hashed] = []
175
- buckets[hashed]._path = index._bucketPath(cache, k)
218
+ buckets[hashed]._path = index.bucketPath(cache, k)
176
219
  } else {
177
220
  buckets[hashed] = [entry]
178
- buckets[hashed]._path = index._bucketPath(cache, k)
221
+ buckets[hashed]._path = index.bucketPath(cache, k)
179
222
  }
180
223
  }
181
224
  }
182
- return BB.map(Object.keys(buckets), key => {
183
- return rebuildBucket(cache, buckets[key], stats, opts)
184
- }, { concurrency: opts.concurrency }).then(() => stats)
225
+ return pMap(
226
+ Object.keys(buckets),
227
+ (key) => {
228
+ return rebuildBucket(cache, buckets[key], stats, opts)
229
+ },
230
+ { concurrency: opts.concurrency }
231
+ ).then(() => stats)
185
232
  })
186
233
  }
187
234
 
188
235
  function rebuildBucket (cache, bucket, stats, opts) {
189
- return fs.truncateAsync(bucket._path).then(() => {
236
+ return truncate(bucket._path).then(() => {
190
237
  // This needs to be serialized because cacache explicitly
191
238
  // lets very racy bucket conflicts clobber each other.
192
- return BB.mapSeries(bucket, entry => {
193
- const content = contentPath(cache, entry.integrity)
194
- return fs.statAsync(content).then(() => {
195
- return index.insert(cache, entry.key, entry.integrity, {
196
- metadata: entry.metadata,
197
- size: entry.size
198
- }).then(() => { stats.totalEntries++ })
199
- }).catch({ code: 'ENOENT' }, () => {
200
- stats.rejectedEntries++
201
- stats.missingContent++
239
+ return bucket.reduce((promise, entry) => {
240
+ return promise.then(() => {
241
+ const content = contentPath(cache, entry.integrity)
242
+ return stat(content)
243
+ .then(() => {
244
+ return index
245
+ .insert(cache, entry.key, entry.integrity, {
246
+ metadata: entry.metadata,
247
+ size: entry.size
248
+ })
249
+ .then(() => {
250
+ stats.totalEntries++
251
+ })
252
+ })
253
+ .catch((err) => {
254
+ if (err.code === 'ENOENT') {
255
+ stats.rejectedEntries++
256
+ stats.missingContent++
257
+ return
258
+ }
259
+ throw err
260
+ })
202
261
  })
203
- })
262
+ }, Promise.resolve())
204
263
  })
205
264
  }
206
265
 
@@ -213,15 +272,16 @@ function writeVerifile (cache, opts) {
213
272
  const verifile = path.join(cache, '_lastverified')
214
273
  opts.log.silly('verify', 'writing verifile to ' + verifile)
215
274
  try {
216
- return fs.writeFileAsync(verifile, '' + (+(new Date())))
275
+ return writeFile(verifile, '' + +new Date())
217
276
  } finally {
218
277
  fixOwner.chownr.sync(cache, verifile)
219
278
  }
220
279
  }
221
280
 
222
281
  module.exports.lastRun = lastRun
282
+
223
283
  function lastRun (cache) {
224
- return fs.readFileAsync(
225
- path.join(cache, '_lastverified'), 'utf8'
226
- ).then(data => new Date(+data))
284
+ return readFile(path.join(cache, '_lastverified'), 'utf8').then(
285
+ (data) => new Date(+data)
286
+ )
227
287
  }
package/ls.js CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict'
2
2
 
3
- var index = require('./lib/entry-index')
3
+ const index = require('./lib/entry-index')
4
4
 
5
5
  module.exports = index.ls
6
6
  module.exports.stream = index.lsStream
package/package.json CHANGED
@@ -1,9 +1,6 @@
1
1
  {
2
2
  "name": "cacache",
3
- "publishConfig": {
4
- "tag": "legacy"
5
- },
6
- "version": "12.0.4",
3
+ "version": "15.0.0",
7
4
  "cache-version": {
8
5
  "content": "2",
9
6
  "index": "5"
@@ -12,16 +9,18 @@
12
9
  "main": "index.js",
13
10
  "files": [
14
11
  "*.js",
15
- "lib",
16
- "locales"
12
+ "lib"
17
13
  ],
18
14
  "scripts": {
19
15
  "benchmarks": "node test/benchmarks",
16
+ "lint": "standard",
17
+ "postrelease": "npm publish",
18
+ "posttest": "npm run lint",
19
+ "prepublishOnly": "git push --follow-tags",
20
20
  "prerelease": "npm t",
21
- "postrelease": "npm publish && git push --follow-tags",
22
- "pretest": "standard",
23
21
  "release": "standard-version -s",
24
- "test": "cross-env CACACHE_UPDATE_LOCALE_FILES=true tap --coverage --nyc-arg=--all -J test/*.js",
22
+ "test": "tap",
23
+ "coverage": "tap",
25
24
  "test-docker": "docker run -it --rm --name pacotest -v \"$PWD\":/tmp -w /tmp node:latest npm test"
26
25
  },
27
26
  "repository": "https://github.com/npm/cacache",
@@ -59,38 +58,38 @@
59
58
  ],
60
59
  "license": "ISC",
61
60
  "dependencies": {
62
- "bluebird": "^3.5.5",
63
- "chownr": "^1.1.1",
64
- "figgy-pudding": "^3.5.1",
61
+ "chownr": "^1.1.2",
62
+ "fs-minipass": "^2.0.0",
65
63
  "glob": "^7.1.4",
66
- "graceful-fs": "^4.1.15",
67
- "infer-owner": "^1.0.3",
64
+ "infer-owner": "^1.0.4",
68
65
  "lru-cache": "^5.1.1",
69
- "mississippi": "^3.0.0",
70
- "mkdirp": "^0.5.1",
66
+ "minipass": "^3.1.1",
67
+ "minipass-collect": "^1.0.2",
68
+ "minipass-flush": "^1.0.5",
69
+ "minipass-pipeline": "^1.2.2",
70
+ "mkdirp": "^1.0.3",
71
71
  "move-concurrently": "^1.0.1",
72
+ "p-map": "^3.0.0",
72
73
  "promise-inflight": "^1.0.1",
73
- "rimraf": "^2.6.3",
74
- "ssri": "^6.0.1",
75
- "unique-filename": "^1.1.1",
76
- "y18n": "^4.0.0"
74
+ "rimraf": "^2.7.1",
75
+ "ssri": "^8.0.0",
76
+ "tar": "^6.0.1",
77
+ "unique-filename": "^1.1.1"
77
78
  },
78
79
  "devDependencies": {
79
80
  "benchmark": "^2.1.4",
80
81
  "chalk": "^2.4.2",
81
- "cross-env": "^5.1.4",
82
82
  "require-inject": "^1.4.4",
83
- "standard": "^12.0.1",
84
- "standard-version": "^6.0.1",
83
+ "standard": "^14.3.1",
84
+ "standard-version": "^7.1.0",
85
85
  "tacks": "^1.3.0",
86
- "tap": "^12.7.0"
86
+ "tap": "^14.10.6"
87
87
  },
88
- "config": {
89
- "nyc": {
90
- "exclude": [
91
- "node_modules/**",
92
- "test/**"
93
- ]
94
- }
88
+ "tap": {
89
+ "100": true,
90
+ "test-regex": "test/[^/]*.js"
91
+ },
92
+ "engines": {
93
+ "node": ">= 10"
95
94
  }
96
95
  }