cacache 12.0.3 → 14.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
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
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,18 +1,20 @@
1
1
  'use strict'
2
2
 
3
- const BB = require('bluebird')
3
+ const util = require('util')
4
4
 
5
5
  const figgyPudding = require('figgy-pudding')
6
6
  const fixOwner = require('./fix-owner')
7
7
  const path = require('path')
8
- const rimraf = BB.promisify(require('rimraf'))
8
+ const rimraf = util.promisify(require('rimraf'))
9
9
  const uniqueFilename = require('unique-filename')
10
+ const { disposer } = require('./disposer')
10
11
 
11
12
  const TmpOpts = figgyPudding({
12
13
  tmpPrefix: {}
13
14
  })
14
15
 
15
16
  module.exports.mkdir = mktmpdir
17
+
16
18
  function mktmpdir (cache, opts) {
17
19
  opts = TmpOpts(opts)
18
20
  const tmpTarget = uniqueFilename(path.join(cache, 'tmp'), opts.tmpPrefix)
@@ -22,16 +24,19 @@ function mktmpdir (cache, opts) {
22
24
  }
23
25
 
24
26
  module.exports.withTmp = withTmp
27
+
25
28
  function withTmp (cache, opts, cb) {
26
29
  if (!cb) {
27
30
  cb = opts
28
31
  opts = null
29
32
  }
30
33
  opts = TmpOpts(opts)
31
- return BB.using(mktmpdir(cache, opts).disposer(rimraf), cb)
34
+
35
+ return disposer(mktmpdir(cache, opts), rimraf, cb)
32
36
  }
33
37
 
34
38
  module.exports.fix = fixtmpdir
39
+
35
40
  function fixtmpdir (cache) {
36
41
  return fixOwner(cache, path.join(cache, 'tmp'))
37
42
  }
package/lib/verify.js CHANGED
@@ -1,19 +1,26 @@
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
7
  const figgyPudding = require('figgy-pudding')
7
- const finished = BB.promisify(require('mississippi').finished)
8
8
  const fixOwner = require('./util/fix-owner')
9
9
  const fs = require('graceful-fs')
10
- const glob = BB.promisify(require('glob'))
10
+ const fsm = require('fs-minipass')
11
+ const glob = util.promisify(require('glob'))
11
12
  const index = require('./entry-index')
12
13
  const path = require('path')
13
- const rimraf = BB.promisify(require('rimraf'))
14
+ const rimraf = util.promisify(require('rimraf'))
14
15
  const ssri = require('ssri')
15
16
 
16
- BB.promisifyAll(fs)
17
+ const hasOwnProperty = (obj, key) =>
18
+ Object.prototype.hasOwnProperty.call(obj, key)
19
+
20
+ const stat = util.promisify(fs.stat)
21
+ const truncate = util.promisify(fs.truncate)
22
+ const writeFile = util.promisify(fs.writeFile)
23
+ const readFile = util.promisify(fs.readFile)
17
24
 
18
25
  const VerifyOpts = figgyPudding({
19
26
  concurrency: {
@@ -26,10 +33,12 @@ const VerifyOpts = figgyPudding({
26
33
  })
27
34
 
28
35
  module.exports = verify
36
+
29
37
  function verify (cache, opts) {
30
38
  opts = VerifyOpts(opts)
31
39
  opts.log.silly('verify', 'verifying cache at', cache)
32
- return BB.reduce([
40
+
41
+ const steps = [
33
42
  markStartTime,
34
43
  fixPerms,
35
44
  garbageCollect,
@@ -37,38 +46,57 @@ function verify (cache, opts) {
37
46
  cleanTmp,
38
47
  writeVerifile,
39
48
  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]
49
+ ]
50
+
51
+ return steps
52
+ .reduce((promise, step, i) => {
53
+ const label = step.name
54
+ const start = new Date()
55
+ return promise.then((stats) => {
56
+ return step(cache, opts).then((s) => {
57
+ s &&
58
+ Object.keys(s).forEach((k) => {
59
+ stats[k] = s[k]
60
+ })
61
+ const end = new Date()
62
+ if (!stats.runTime) {
63
+ stats.runTime = {}
64
+ }
65
+ stats.runTime[label] = end - start
66
+ return Promise.resolve(stats)
67
+ })
46
68
  })
47
- const end = new Date()
48
- if (!stats.runTime) { stats.runTime = {} }
49
- stats.runTime[label] = end - start
69
+ }, Promise.resolve({}))
70
+ .then((stats) => {
71
+ stats.runTime.total = stats.endTime - stats.startTime
72
+ opts.log.silly(
73
+ 'verify',
74
+ 'verification finished for',
75
+ cache,
76
+ 'in',
77
+ `${stats.runTime.total}ms`
78
+ )
50
79
  return stats
51
80
  })
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
81
  }
57
82
 
58
83
  function markStartTime (cache, opts) {
59
- return { startTime: new Date() }
84
+ return Promise.resolve({ startTime: new Date() })
60
85
  }
61
86
 
62
87
  function markEndTime (cache, opts) {
63
- return { endTime: new Date() }
88
+ return Promise.resolve({ endTime: new Date() })
64
89
  }
65
90
 
66
91
  function fixPerms (cache, opts) {
67
92
  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)
93
+ return fixOwner
94
+ .mkdirfix(cache, cache)
95
+ .then(() => {
96
+ // TODO - fix file permissions too
97
+ return fixOwner.chownr(cache, cache)
98
+ })
99
+ .then(() => null)
72
100
  }
73
101
 
74
102
  // Implements a naive mark-and-sweep tracing garbage collector.
@@ -84,85 +112,105 @@ function garbageCollect (cache, opts) {
84
112
  opts.log.silly('verify', 'garbage collecting content')
85
113
  const indexStream = index.lsStream(cache)
86
114
  const liveContent = new Set()
87
- indexStream.on('data', entry => {
88
- if (opts.filter && !opts.filter(entry)) { return }
115
+ indexStream.on('data', (entry) => {
116
+ if (opts.filter && !opts.filter(entry)) {
117
+ return
118
+ }
89
119
  liveContent.add(entry.integrity.toString())
90
120
  })
91
- return finished(indexStream).then(() => {
92
- const contentDir = contentPath._contentDir(cache)
121
+ return new Promise((resolve, reject) => {
122
+ indexStream.on('end', resolve).on('error', reject)
123
+ }).then(() => {
124
+ const contentDir = contentPath.contentDir(cache)
93
125
  return glob(path.join(contentDir, '**'), {
94
126
  follow: false,
95
127
  nodir: true,
96
128
  nosort: true
97
- }).then(files => {
98
- return BB.resolve({
129
+ }).then((files) => {
130
+ return Promise.resolve({
99
131
  verifiedContent: 0,
100
132
  reclaimedCount: 0,
101
133
  reclaimedSize: 0,
102
134
  badContentCount: 0,
103
135
  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
136
+ }).then((stats) =>
137
+ pMap(
138
+ files,
139
+ (f) => {
140
+ const split = f.split(/[/\\]/)
141
+ const digest = split.slice(split.length - 3).join('')
142
+ const algo = split[split.length - 4]
143
+ const integrity = ssri.fromHex(digest, algo)
144
+ if (liveContent.has(integrity.toString())) {
145
+ return verifyContent(f, integrity).then((info) => {
146
+ if (!info.valid) {
147
+ stats.reclaimedCount++
148
+ stats.badContentCount++
149
+ stats.reclaimedSize += info.size
150
+ } else {
151
+ stats.verifiedContent++
152
+ stats.keptSize += info.size
153
+ }
154
+ return stats
155
+ })
115
156
  } else {
116
- stats.verifiedContent++
117
- stats.keptSize += info.size
157
+ // No entries refer to this content. We can delete.
158
+ stats.reclaimedCount++
159
+ return stat(f).then((s) => {
160
+ return rimraf(f).then(() => {
161
+ stats.reclaimedSize += s.size
162
+ return stats
163
+ })
164
+ })
118
165
  }
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 }))
166
+ },
167
+ { concurrency: opts.concurrency }
168
+ ).then(() => stats)
169
+ )
132
170
  })
133
171
  })
134
172
  }
135
173
 
136
174
  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 }))
175
+ return stat(filepath)
176
+ .then((s) => {
177
+ const contentInfo = {
178
+ size: s.size,
179
+ valid: true
180
+ }
181
+ return ssri
182
+ .checkStream(new fsm.ReadStream(filepath), sri)
183
+ .catch((err) => {
184
+ if (err.code !== 'EINTEGRITY') {
185
+ throw err
186
+ }
187
+ return rimraf(filepath).then(() => {
188
+ contentInfo.valid = false
189
+ })
190
+ })
191
+ .then(() => contentInfo)
192
+ })
193
+ .catch((err) => {
194
+ if (err.code === 'ENOENT') {
195
+ return { size: 0, valid: false }
196
+ }
197
+ throw err
198
+ })
152
199
  }
153
200
 
154
201
  function rebuildIndex (cache, opts) {
155
202
  opts.log.silly('verify', 'rebuilding index')
156
- return index.ls(cache).then(entries => {
203
+ return index.ls(cache).then((entries) => {
157
204
  const stats = {
158
205
  missingContent: 0,
159
206
  rejectedEntries: 0,
160
207
  totalEntries: 0
161
208
  }
162
209
  const buckets = {}
163
- for (let k in entries) {
164
- if (entries.hasOwnProperty(k)) {
165
- const hashed = index._hashKey(k)
210
+ for (const k in entries) {
211
+ /* istanbul ignore else */
212
+ if (hasOwnProperty(entries, k)) {
213
+ const hashed = index.hashKey(k)
166
214
  const entry = entries[k]
167
215
  const excluded = opts.filter && !opts.filter(entry)
168
216
  excluded && stats.rejectedEntries++
@@ -172,35 +220,51 @@ function rebuildIndex (cache, opts) {
172
220
  // skip
173
221
  } else if (excluded) {
174
222
  buckets[hashed] = []
175
- buckets[hashed]._path = index._bucketPath(cache, k)
223
+ buckets[hashed]._path = index.bucketPath(cache, k)
176
224
  } else {
177
225
  buckets[hashed] = [entry]
178
- buckets[hashed]._path = index._bucketPath(cache, k)
226
+ buckets[hashed]._path = index.bucketPath(cache, k)
179
227
  }
180
228
  }
181
229
  }
182
- return BB.map(Object.keys(buckets), key => {
183
- return rebuildBucket(cache, buckets[key], stats, opts)
184
- }, { concurrency: opts.concurrency }).then(() => stats)
230
+ return pMap(
231
+ Object.keys(buckets),
232
+ (key) => {
233
+ return rebuildBucket(cache, buckets[key], stats, opts)
234
+ },
235
+ { concurrency: opts.concurrency }
236
+ ).then(() => stats)
185
237
  })
186
238
  }
187
239
 
188
240
  function rebuildBucket (cache, bucket, stats, opts) {
189
- return fs.truncateAsync(bucket._path).then(() => {
241
+ return truncate(bucket._path).then(() => {
190
242
  // This needs to be serialized because cacache explicitly
191
243
  // 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++
244
+ return bucket.reduce((promise, entry) => {
245
+ return promise.then(() => {
246
+ const content = contentPath(cache, entry.integrity)
247
+ return stat(content)
248
+ .then(() => {
249
+ return index
250
+ .insert(cache, entry.key, entry.integrity, {
251
+ metadata: entry.metadata,
252
+ size: entry.size
253
+ })
254
+ .then(() => {
255
+ stats.totalEntries++
256
+ })
257
+ })
258
+ .catch((err) => {
259
+ if (err.code === 'ENOENT') {
260
+ stats.rejectedEntries++
261
+ stats.missingContent++
262
+ return
263
+ }
264
+ throw err
265
+ })
202
266
  })
203
- })
267
+ }, Promise.resolve())
204
268
  })
205
269
  }
206
270
 
@@ -213,15 +277,16 @@ function writeVerifile (cache, opts) {
213
277
  const verifile = path.join(cache, '_lastverified')
214
278
  opts.log.silly('verify', 'writing verifile to ' + verifile)
215
279
  try {
216
- return fs.writeFileAsync(verifile, '' + (+(new Date())))
280
+ return writeFile(verifile, '' + +new Date())
217
281
  } finally {
218
282
  fixOwner.chownr.sync(cache, verifile)
219
283
  }
220
284
  }
221
285
 
222
286
  module.exports.lastRun = lastRun
287
+
223
288
  function lastRun (cache) {
224
- return fs.readFileAsync(
225
- path.join(cache, '_lastverified'), 'utf8'
226
- ).then(data => new Date(+data))
289
+ return readFile(path.join(cache, '_lastverified'), 'utf8').then(
290
+ (data) => new Date(+data)
291
+ )
227
292
  }
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,6 +1,6 @@
1
1
  {
2
2
  "name": "cacache",
3
- "version": "12.0.3",
3
+ "version": "14.0.0",
4
4
  "cache-version": {
5
5
  "content": "2",
6
6
  "index": "5"
@@ -18,7 +18,7 @@
18
18
  "postrelease": "npm publish && git push --follow-tags",
19
19
  "pretest": "standard",
20
20
  "release": "standard-version -s",
21
- "test": "cross-env CACACHE_UPDATE_LOCALE_FILES=true tap --coverage --nyc-arg=--all -J test/*.js",
21
+ "test": "tap test/*.js",
22
22
  "test-docker": "docker run -it --rm --name pacotest -v \"$PWD\":/tmp -w /tmp node:latest npm test",
23
23
  "update-coc": "weallbehave -o . && git add CODE_OF_CONDUCT.md && git commit -m 'docs(coc): updated CODE_OF_CONDUCT.md'",
24
24
  "update-contrib": "weallcontribute -o . && git add CONTRIBUTING.md && git commit -m 'docs(contributing): updated CONTRIBUTING.md'"
@@ -58,40 +58,42 @@
58
58
  ],
59
59
  "license": "ISC",
60
60
  "dependencies": {
61
- "bluebird": "^3.5.5",
62
- "chownr": "^1.1.1",
61
+ "chownr": "^1.1.2",
63
62
  "figgy-pudding": "^3.5.1",
63
+ "fs-minipass": "^2.0.0",
64
64
  "glob": "^7.1.4",
65
- "graceful-fs": "^4.1.15",
66
- "infer-owner": "^1.0.3",
65
+ "graceful-fs": "^4.2.2",
66
+ "infer-owner": "^1.0.4",
67
67
  "lru-cache": "^5.1.1",
68
- "mississippi": "^3.0.0",
69
- "mkdirp": "^0.5.1",
68
+ "minipass": "^3.0.0",
69
+ "minipass-collect": "^1.0.2",
70
+ "minipass-flush": "^1.0.5",
71
+ "minipass-pipeline": "^1.2.2",
72
+ "mkdirp": "^1.0.3",
70
73
  "move-concurrently": "^1.0.1",
74
+ "p-map": "^3.0.0",
71
75
  "promise-inflight": "^1.0.1",
72
- "rimraf": "^2.6.3",
73
- "ssri": "^6.0.1",
74
- "unique-filename": "^1.1.1",
75
- "y18n": "^4.0.0"
76
+ "rimraf": "^2.7.1",
77
+ "ssri": "^7.0.0",
78
+ "tar": "^6.0.0",
79
+ "unique-filename": "^1.1.1"
76
80
  },
77
81
  "devDependencies": {
78
82
  "benchmark": "^2.1.4",
79
83
  "chalk": "^2.4.2",
80
- "cross-env": "^5.1.4",
84
+ "cross-env": "^5.2.1",
81
85
  "require-inject": "^1.4.4",
82
- "standard": "^12.0.1",
83
- "standard-version": "^6.0.1",
86
+ "standard": "^14.3.0",
87
+ "standard-version": "^7.0.0",
84
88
  "tacks": "^1.3.0",
85
- "tap": "^12.7.0",
89
+ "tap": "^14.6.9",
86
90
  "weallbehave": "^1.2.0",
87
91
  "weallcontribute": "^1.0.9"
88
92
  },
89
- "config": {
90
- "nyc": {
91
- "exclude": [
92
- "node_modules/**",
93
- "test/**"
94
- ]
95
- }
93
+ "tap": {
94
+ "100": true
95
+ },
96
+ "engines": {
97
+ "node": ">= 10"
96
98
  }
97
99
  }