cacache 15.1.0 → 15.2.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.
package/README.md CHANGED
@@ -458,13 +458,17 @@ cacache.rm.all(cachePath).then(() => {
458
458
  })
459
459
  ```
460
460
 
461
- #### <a name="rm-entry"></a> `> cacache.rm.entry(cache, key) -> Promise`
461
+ #### <a name="rm-entry"></a> `> cacache.rm.entry(cache, key, [opts]) -> Promise`
462
462
 
463
463
  Alias: `cacache.rm`
464
464
 
465
465
  Removes the index entry for `key`. Content will still be accessible if
466
466
  requested directly by content address ([`get.stream.byDigest`](#get-stream)).
467
467
 
468
+ By default, this appends a new entry to the index with an integrity of `null`.
469
+ If `opts.removeFully` is set to `true` then the index file itself will be
470
+ physically deleted rather than appending a `null`.
471
+
468
472
  To remove the content itself (which might still be used by other entries), use
469
473
  [`rm.content`](#rm-content). Or, to safely vacuum any unused content, use
470
474
  [`verify`](#verify).
@@ -491,12 +495,21 @@ cacache.rm.content(cachePath, 'sha512-SoMeDIGest/IN+BaSE64==').then(() => {
491
495
  })
492
496
  ```
493
497
 
494
- #### <a name="index-compact"></a> `> cacache.index.compact(cache, key, matchFn) -> Promise`
498
+ #### <a name="index-compact"></a> `> cacache.index.compact(cache, key, matchFn, [opts]) -> Promise`
495
499
 
496
500
  Uses `matchFn`, which must be a synchronous function that accepts two entries
497
501
  and returns a boolean indicating whether or not the two entries match, to
498
502
  deduplicate all entries in the cache for the given `key`.
499
503
 
504
+ If `opts.validateEntry` is provided, it will be called as a function with the
505
+ only parameter being a single index entry. The function must return a Boolean,
506
+ if it returns `true` the entry is considered valid and will be kept in the index,
507
+ if it returns `false` the entry will be removed from the index.
508
+
509
+ If `opts.validateEntry` is not provided, however, every entry in the index will
510
+ be deduplicated and kept until the first `null` integrity is reached, removing
511
+ all entries that were written before the `null`.
512
+
500
513
  The deduplicated list of entries is both written to the index, replacing the
501
514
  existing content, and returned in the Promise.
502
515
 
package/get.js CHANGED
@@ -32,18 +32,18 @@ function getData (byDigest, cache, key, opts = {}) {
32
32
  metadata: memoized.entry.metadata,
33
33
  data: memoized.data,
34
34
  integrity: memoized.entry.integrity,
35
- size: memoized.entry.size
35
+ size: memoized.entry.size,
36
36
  }
37
37
  )
38
38
  }
39
39
  return (byDigest ? Promise.resolve(null) : index.find(cache, key, opts)).then(
40
40
  (entry) => {
41
- if (!entry && !byDigest) {
41
+ if (!entry && !byDigest)
42
42
  throw new index.NotFoundError(cache, key)
43
- }
43
+
44
44
  return read(cache, byDigest ? key : entry.integrity, {
45
45
  integrity,
46
- size
46
+ size,
47
47
  })
48
48
  .then((data) =>
49
49
  byDigest
@@ -52,15 +52,15 @@ function getData (byDigest, cache, key, opts = {}) {
52
52
  data,
53
53
  metadata: entry.metadata,
54
54
  size: entry.size,
55
- integrity: entry.integrity
55
+ integrity: entry.integrity,
56
56
  }
57
57
  )
58
58
  .then((res) => {
59
- if (memoize && byDigest) {
59
+ if (memoize && byDigest)
60
60
  memo.put.byDigest(cache, key, res, opts)
61
- } else if (memoize) {
61
+ else if (memoize)
62
62
  memo.put(cache, entry, res.data, opts)
63
- }
63
+
64
64
  return res
65
65
  })
66
66
  }
@@ -86,16 +86,16 @@ function getDataSync (byDigest, cache, key, opts = {}) {
86
86
  metadata: memoized.entry.metadata,
87
87
  data: memoized.data,
88
88
  integrity: memoized.entry.integrity,
89
- size: memoized.entry.size
89
+ size: memoized.entry.size,
90
90
  }
91
91
  }
92
92
  const entry = !byDigest && index.find.sync(cache, key, opts)
93
- if (!entry && !byDigest) {
93
+ if (!entry && !byDigest)
94
94
  throw new index.NotFoundError(cache, key)
95
- }
95
+
96
96
  const data = read.sync(cache, byDigest ? key : entry.integrity, {
97
97
  integrity: integrity,
98
- size: size
98
+ size: size,
99
99
  })
100
100
  const res = byDigest
101
101
  ? data
@@ -103,13 +103,13 @@ function getDataSync (byDigest, cache, key, opts = {}) {
103
103
  metadata: entry.metadata,
104
104
  data: data,
105
105
  size: entry.size,
106
- integrity: entry.integrity
106
+ integrity: entry.integrity,
107
107
  }
108
- if (memoize && byDigest) {
108
+ if (memoize && byDigest)
109
109
  memo.put.byDigest(cache, key, res, opts)
110
- } else if (memoize) {
110
+ else if (memoize)
111
111
  memo.put(cache, entry, res.data, opts)
112
- }
112
+
113
113
  return res
114
114
  }
115
115
 
@@ -129,17 +129,16 @@ const getMemoizedStream = (memoized) => {
129
129
  function getStream (cache, key, opts = {}) {
130
130
  const { memoize, size } = opts
131
131
  const memoized = memo.get(cache, key, opts)
132
- if (memoized && memoize !== false) {
132
+ if (memoized && memoize !== false)
133
133
  return getMemoizedStream(memoized)
134
- }
135
134
 
136
135
  const stream = new Pipeline()
137
136
  index
138
137
  .find(cache, key)
139
138
  .then((entry) => {
140
- if (!entry) {
139
+ if (!entry)
141
140
  throw new index.NotFoundError(cache, key)
142
- }
141
+
143
142
  stream.emit('metadata', entry.metadata)
144
143
  stream.emit('integrity', entry.integrity)
145
144
  stream.emit('size', entry.size)
@@ -178,9 +177,9 @@ function getStreamDigest (cache, integrity, opts = {}) {
178
177
  return stream
179
178
  } else {
180
179
  const stream = read.readStream(cache, integrity, opts)
181
- if (!memoize) {
180
+ if (!memoize)
182
181
  return stream
183
- }
182
+
184
183
  const memoStream = new Collect.PassThrough()
185
184
  memoStream.on('collect', data => memo.put.byDigest(
186
185
  cache,
@@ -197,11 +196,10 @@ module.exports.info = info
197
196
  function info (cache, key, opts = {}) {
198
197
  const { memoize } = opts
199
198
  const memoized = memo.get(cache, key, opts)
200
- if (memoized && memoize !== false) {
199
+ if (memoized && memoize !== false)
201
200
  return Promise.resolve(memoized.entry)
202
- } else {
201
+ else
203
202
  return index.find(cache, key)
204
- }
205
203
  }
206
204
 
207
205
  module.exports.hasContent = read.hasContent
@@ -224,9 +222,9 @@ function copy (byDigest, cache, key, dest, opts = {}) {
224
222
  ? Promise.resolve(null)
225
223
  : index.find(cache, key, opts)
226
224
  ).then((entry) => {
227
- if (!entry && !byDigest) {
225
+ if (!entry && !byDigest)
228
226
  throw new index.NotFoundError(cache, key)
229
- }
227
+
230
228
  return read
231
229
  .copy(cache, byDigest ? key : entry.integrity, dest, opts)
232
230
  .then(() => {
@@ -235,7 +233,7 @@ function copy (byDigest, cache, key, dest, opts = {}) {
235
233
  : {
236
234
  metadata: entry.metadata,
237
235
  size: entry.size,
238
- integrity: entry.integrity
236
+ integrity: entry.integrity,
239
237
  }
240
238
  })
241
239
  })
@@ -248,7 +246,7 @@ function copy (byDigest, cache, key, dest, opts = {}) {
248
246
  : {
249
247
  metadata: res.metadata,
250
248
  size: res.size,
251
- integrity: res.integrity
249
+ integrity: res.integrity,
252
250
  }
253
251
  })
254
252
  })
@@ -20,17 +20,16 @@ function read (cache, integrity, opts = {}) {
20
20
  // get size
21
21
  return lstat(cpath).then(stat => ({ stat, cpath, sri }))
22
22
  }).then(({ stat, cpath, sri }) => {
23
- if (typeof size === 'number' && stat.size !== size) {
23
+ if (typeof size === 'number' && stat.size !== size)
24
24
  throw sizeError(size, stat.size)
25
- }
26
- if (stat.size > MAX_SINGLE_READ_SIZE) {
25
+
26
+ if (stat.size > MAX_SINGLE_READ_SIZE)
27
27
  return readPipeline(cpath, stat.size, sri, new Pipeline()).concat()
28
- }
29
28
 
30
29
  return readFile(cpath, null).then((data) => {
31
- if (!ssri.checkData(data, sri)) {
30
+ if (!ssri.checkData(data, sri))
32
31
  throw integrityError(sri, cpath)
33
- }
32
+
34
33
  return data
35
34
  })
36
35
  })
@@ -40,11 +39,11 @@ const readPipeline = (cpath, size, sri, stream) => {
40
39
  stream.push(
41
40
  new fsm.ReadStream(cpath, {
42
41
  size,
43
- readSize: MAX_SINGLE_READ_SIZE
42
+ readSize: MAX_SINGLE_READ_SIZE,
44
43
  }),
45
44
  ssri.integrityStream({
46
45
  integrity: sri,
47
- size
46
+ size,
48
47
  })
49
48
  )
50
49
  return stream
@@ -56,13 +55,11 @@ function readSync (cache, integrity, opts = {}) {
56
55
  const { size } = opts
57
56
  return withContentSriSync(cache, integrity, (cpath, sri) => {
58
57
  const data = fs.readFileSync(cpath)
59
- if (typeof size === 'number' && size !== data.length) {
58
+ if (typeof size === 'number' && size !== data.length)
60
59
  throw sizeError(size, data.length)
61
- }
62
60
 
63
- if (ssri.checkData(data, sri)) {
61
+ if (ssri.checkData(data, sri))
64
62
  return data
65
- }
66
63
 
67
64
  throw integrityError(sri, cpath)
68
65
  })
@@ -78,9 +75,9 @@ function readStream (cache, integrity, opts = {}) {
78
75
  // just lstat to ensure it exists
79
76
  return lstat(cpath).then((stat) => ({ stat, cpath, sri }))
80
77
  }).then(({ stat, cpath, sri }) => {
81
- if (typeof size === 'number' && size !== stat.size) {
78
+ if (typeof size === 'number' && size !== stat.size)
82
79
  return stream.emit('error', sizeError(size, stat.size))
83
- }
80
+
84
81
  readPipeline(cpath, stat.size, sri, stream)
85
82
  }, er => stream.emit('error', er))
86
83
 
@@ -109,22 +106,21 @@ function copySync (cache, integrity, dest) {
109
106
  module.exports.hasContent = hasContent
110
107
 
111
108
  function hasContent (cache, integrity) {
112
- if (!integrity) {
109
+ if (!integrity)
113
110
  return Promise.resolve(false)
114
- }
111
+
115
112
  return withContentSri(cache, integrity, (cpath, sri) => {
116
113
  return lstat(cpath).then((stat) => ({ size: stat.size, sri, stat }))
117
114
  }).catch((err) => {
118
- if (err.code === 'ENOENT') {
115
+ if (err.code === 'ENOENT')
119
116
  return false
120
- }
117
+
121
118
  if (err.code === 'EPERM') {
122
119
  /* istanbul ignore else */
123
- if (process.platform !== 'win32') {
120
+ if (process.platform !== 'win32')
124
121
  throw err
125
- } else {
122
+ else
126
123
  return false
127
- }
128
124
  }
129
125
  })
130
126
  }
@@ -132,24 +128,23 @@ function hasContent (cache, integrity) {
132
128
  module.exports.hasContent.sync = hasContentSync
133
129
 
134
130
  function hasContentSync (cache, integrity) {
135
- if (!integrity) {
131
+ if (!integrity)
136
132
  return false
137
- }
133
+
138
134
  return withContentSriSync(cache, integrity, (cpath, sri) => {
139
135
  try {
140
136
  const stat = fs.lstatSync(cpath)
141
137
  return { size: stat.size, sri, stat }
142
138
  } catch (err) {
143
- if (err.code === 'ENOENT') {
139
+ if (err.code === 'ENOENT')
144
140
  return false
145
- }
141
+
146
142
  if (err.code === 'EPERM') {
147
143
  /* istanbul ignore else */
148
- if (process.platform !== 'win32') {
144
+ if (process.platform !== 'win32')
149
145
  throw err
150
- } else {
146
+ else
151
147
  return false
152
- }
153
148
  }
154
149
  }
155
150
  })
@@ -167,7 +162,8 @@ function withContentSri (cache, integrity, fn) {
167
162
  const cpath = contentPath(cache, digests[0])
168
163
  return fn(cpath, digests[0])
169
164
  } else {
170
- // Can't use race here because a generic error can happen before a ENOENT error, and can happen before a valid result
165
+ // Can't use race here because a generic error can happen before
166
+ // a ENOENT error, and can happen before a valid result
171
167
  return Promise
172
168
  .all(digests.map((meta) => {
173
169
  return withContentSri(cache, meta, fn)
@@ -184,15 +180,13 @@ function withContentSri (cache, integrity, fn) {
184
180
  .then((results) => {
185
181
  // Return the first non error if it is found
186
182
  const result = results.find((r) => !(r instanceof Error))
187
- if (result) {
183
+ if (result)
188
184
  return result
189
- }
190
185
 
191
186
  // Throw the No matching content found error
192
187
  const enoentError = results.find((r) => r.code === 'ENOENT')
193
- if (enoentError) {
188
+ if (enoentError)
194
189
  throw enoentError
195
- }
196
190
 
197
191
  // Throw generic error
198
192
  throw results.find((r) => r instanceof Error)
package/lib/content/rm.js CHANGED
@@ -11,10 +11,9 @@ module.exports = rm
11
11
  function rm (cache, integrity) {
12
12
  return hasContent(cache, integrity).then((content) => {
13
13
  // ~pretty~ sure we can't end up with a content lacking sri, but be safe
14
- if (content && content.sri) {
14
+ if (content && content.sri)
15
15
  return rimraf(contentPath(cache, content.sri)).then(() => true)
16
- } else {
16
+ else
17
17
  return false
18
- }
19
18
  })
20
19
  }
@@ -22,16 +22,15 @@ module.exports = write
22
22
 
23
23
  function write (cache, data, opts = {}) {
24
24
  const { algorithms, size, integrity } = opts
25
- if (algorithms && algorithms.length > 1) {
25
+ if (algorithms && algorithms.length > 1)
26
26
  throw new Error('opts.algorithms only supports a single algorithm for now')
27
- }
28
- if (typeof size === 'number' && data.length !== size) {
27
+
28
+ if (typeof size === 'number' && data.length !== size)
29
29
  return Promise.reject(sizeError(size, data.length))
30
- }
30
+
31
31
  const sri = ssri.fromData(data, algorithms ? { algorithms } : {})
32
- if (integrity && !ssri.checkData(data, integrity, opts)) {
32
+ if (integrity && !ssri.checkData(data, integrity, opts))
33
33
  return Promise.reject(checksumError(integrity, sri))
34
- }
35
34
 
36
35
  return disposer(makeTmp(cache, opts), makeTmpDisposer,
37
36
  (tmp) => {
@@ -112,13 +111,17 @@ function pipeToTmp (inputStream, cache, tmpTarget, opts) {
112
111
  const hashStream = ssri.integrityStream({
113
112
  integrity: opts.integrity,
114
113
  algorithms: opts.algorithms,
115
- size: opts.size
114
+ size: opts.size,
115
+ })
116
+ hashStream.on('integrity', i => {
117
+ integrity = i
118
+ })
119
+ hashStream.on('size', s => {
120
+ size = s
116
121
  })
117
- hashStream.on('integrity', i => { integrity = i })
118
- hashStream.on('size', s => { size = s })
119
122
 
120
123
  const outStream = new fsm.WriteStream(tmpTarget, {
121
- flags: 'wx'
124
+ flags: 'wx',
122
125
  })
123
126
 
124
127
  // NB: this can throw if the hashStream has a problem with
@@ -132,21 +135,23 @@ function pipeToTmp (inputStream, cache, tmpTarget, opts) {
132
135
 
133
136
  return pipeline.promise()
134
137
  .then(() => ({ integrity, size }))
135
- .catch(er => rimraf(tmpTarget).then(() => { throw er }))
138
+ .catch(er => rimraf(tmpTarget).then(() => {
139
+ throw er
140
+ }))
136
141
  }
137
142
 
138
143
  function makeTmp (cache, opts) {
139
144
  const tmpTarget = uniqueFilename(path.join(cache, 'tmp'), opts.tmpPrefix)
140
145
  return fixOwner.mkdirfix(cache, path.dirname(tmpTarget)).then(() => ({
141
146
  target: tmpTarget,
142
- moved: false
147
+ moved: false,
143
148
  }))
144
149
  }
145
150
 
146
151
  function makeTmpDisposer (tmp) {
147
- if (tmp.moved) {
152
+ if (tmp.moved)
148
153
  return Promise.resolve()
149
- }
154
+
150
155
  return rimraf(tmp.target)
151
156
  }
152
157
 
@@ -14,7 +14,9 @@ const fixOwner = require('./util/fix-owner')
14
14
  const hashToSegments = require('./util/hash-to-segments')
15
15
  const indexV = require('../package.json')['cache-version'].index
16
16
  const moveFile = require('@npmcli/move-file')
17
- const rimraf = util.promisify(require('rimraf'))
17
+ const _rimraf = require('rimraf')
18
+ const rimraf = util.promisify(_rimraf)
19
+ rimraf.sync = _rimraf.sync
18
20
 
19
21
  const appendFile = util.promisify(fs.appendFile)
20
22
  const readFile = util.promisify(fs.readFile)
@@ -35,15 +37,30 @@ module.exports.compact = compact
35
37
  async function compact (cache, key, matchFn, opts = {}) {
36
38
  const bucket = bucketPath(cache, key)
37
39
  const entries = await bucketEntries(bucket)
38
- // reduceRight because the bottom-most result is the newest
40
+ const newEntries = []
41
+ // we loop backwards because the bottom-most result is the newest
39
42
  // since we add new entries with appendFile
40
- const newEntries = entries.reduceRight((acc, newEntry) => {
41
- if (!acc.find((oldEntry) => matchFn(oldEntry, newEntry))) {
42
- acc.push(newEntry)
43
- }
44
-
45
- return acc
46
- }, [])
43
+ for (let i = entries.length - 1; i >= 0; --i) {
44
+ const entry = entries[i]
45
+ // a null integrity could mean either a delete was appended
46
+ // or the user has simply stored an index that does not map
47
+ // to any content. we determine if the user wants to keep the
48
+ // null integrity based on the validateEntry function passed in options.
49
+ // if the integrity is null and no validateEntry is provided, we break
50
+ // as we consider the null integrity to be a deletion of everything
51
+ // that came before it.
52
+ if (entry.integrity === null && !opts.validateEntry)
53
+ break
54
+
55
+ // if this entry is valid, and it is either the first entry or
56
+ // the newEntries array doesn't already include an entry that
57
+ // matches this one based on the provided matchFn, then we add
58
+ // it to the beginning of our list
59
+ if ((!opts.validateEntry || opts.validateEntry(entry) === true) &&
60
+ (newEntries.length === 0 ||
61
+ !newEntries.find((oldEntry) => matchFn(oldEntry, entry))))
62
+ newEntries.unshift(entry)
63
+ }
47
64
 
48
65
  const newIndex = '\n' + newEntries.map((entry) => {
49
66
  const stringified = JSON.stringify(entry)
@@ -56,14 +73,13 @@ async function compact (cache, key, matchFn, opts = {}) {
56
73
  await fixOwner.mkdirfix(cache, path.dirname(target))
57
74
  return {
58
75
  target,
59
- moved: false
76
+ moved: false,
60
77
  }
61
78
  }
62
79
 
63
80
  const teardown = async (tmp) => {
64
- if (!tmp.moved) {
81
+ if (!tmp.moved)
65
82
  return rimraf(tmp.target)
66
- }
67
83
  }
68
84
 
69
85
  const write = async (tmp) => {
@@ -76,16 +92,20 @@ async function compact (cache, key, matchFn, opts = {}) {
76
92
  try {
77
93
  await fixOwner.chownr(cache, bucket)
78
94
  } catch (err) {
79
- if (err.code !== 'ENOENT') {
95
+ if (err.code !== 'ENOENT')
80
96
  throw err
81
- }
82
97
  }
83
98
  }
84
99
 
85
100
  // write the file atomically
86
101
  await disposer(setup(), teardown, write)
87
102
 
88
- return newEntries.map((entry) => formatEntry(cache, entry, true))
103
+ // we reverse the list we generated such that the newest
104
+ // entries come first in order to make looping through them easier
105
+ // the true passed to formatEntry tells it to keep null
106
+ // integrity values, if they made it this far it's because
107
+ // validateEntry returned true, and as such we should return it
108
+ return newEntries.reverse().map((entry) => formatEntry(cache, entry, true))
89
109
  }
90
110
 
91
111
  module.exports.insert = insert
@@ -98,7 +118,7 @@ function insert (cache, key, integrity, opts = {}) {
98
118
  integrity: integrity && ssri.stringify(integrity),
99
119
  time: Date.now(),
100
120
  size,
101
- metadata
121
+ metadata,
102
122
  }
103
123
  return fixOwner
104
124
  .mkdirfix(cache, path.dirname(bucket))
@@ -110,14 +130,15 @@ function insert (cache, key, integrity, opts = {}) {
110
130
  // another while still preserving the string length of the JSON in
111
131
  // question. So, we just slap the length in there and verify it on read.
112
132
  //
113
- // Thanks to @isaacs for the whiteboarding session that ended up with this.
133
+ // Thanks to @isaacs for the whiteboarding session that ended up with
134
+ // this.
114
135
  return appendFile(bucket, `\n${hashEntry(stringified)}\t${stringified}`)
115
136
  })
116
137
  .then(() => fixOwner.chownr(cache, bucket))
117
138
  .catch((err) => {
118
- if (err.code === 'ENOENT') {
139
+ if (err.code === 'ENOENT')
119
140
  return undefined
120
- }
141
+
121
142
  throw err
122
143
  // There's a class of race conditions that happen when things get deleted
123
144
  // during fixOwner, or between the two mkdirfix/chownr calls.
@@ -140,7 +161,7 @@ function insertSync (cache, key, integrity, opts = {}) {
140
161
  integrity: integrity && ssri.stringify(integrity),
141
162
  time: Date.now(),
142
163
  size,
143
- metadata
164
+ metadata,
144
165
  }
145
166
  fixOwner.mkdirfix.sync(cache, path.dirname(bucket))
146
167
  const stringified = JSON.stringify(entry)
@@ -148,9 +169,8 @@ function insertSync (cache, key, integrity, opts = {}) {
148
169
  try {
149
170
  fixOwner.chownr.sync(cache, bucket)
150
171
  } catch (err) {
151
- if (err.code !== 'ENOENT') {
172
+ if (err.code !== 'ENOENT')
152
173
  throw err
153
- }
154
174
  }
155
175
  return formatEntry(cache, entry)
156
176
  }
@@ -162,19 +182,17 @@ function find (cache, key) {
162
182
  return bucketEntries(bucket)
163
183
  .then((entries) => {
164
184
  return entries.reduce((latest, next) => {
165
- if (next && next.key === key) {
185
+ if (next && next.key === key)
166
186
  return formatEntry(cache, next)
167
- } else {
187
+ else
168
188
  return latest
169
- }
170
189
  }, null)
171
190
  })
172
191
  .catch((err) => {
173
- if (err.code === 'ENOENT') {
192
+ if (err.code === 'ENOENT')
174
193
  return null
175
- } else {
194
+ else
176
195
  throw err
177
- }
178
196
  })
179
197
  }
180
198
 
@@ -184,31 +202,37 @@ function findSync (cache, key) {
184
202
  const bucket = bucketPath(cache, key)
185
203
  try {
186
204
  return bucketEntriesSync(bucket).reduce((latest, next) => {
187
- if (next && next.key === key) {
205
+ if (next && next.key === key)
188
206
  return formatEntry(cache, next)
189
- } else {
207
+ else
190
208
  return latest
191
- }
192
209
  }, null)
193
210
  } catch (err) {
194
- if (err.code === 'ENOENT') {
211
+ if (err.code === 'ENOENT')
195
212
  return null
196
- } else {
213
+ else
197
214
  throw err
198
- }
199
215
  }
200
216
  }
201
217
 
202
218
  module.exports.delete = del
203
219
 
204
- function del (cache, key, opts) {
205
- return insert(cache, key, null, opts)
220
+ function del (cache, key, opts = {}) {
221
+ if (!opts.removeFully)
222
+ return insert(cache, key, null, opts)
223
+
224
+ const bucket = bucketPath(cache, key)
225
+ return rimraf(bucket)
206
226
  }
207
227
 
208
228
  module.exports.delete.sync = delSync
209
229
 
210
- function delSync (cache, key, opts) {
211
- return insertSync(cache, key, null, opts)
230
+ function delSync (cache, key, opts = {}) {
231
+ if (!opts.removeFully)
232
+ return insertSync(cache, key, null, opts)
233
+
234
+ const bucket = bucketPath(cache, key)
235
+ return rimraf.sync(bucket)
212
236
  }
213
237
 
214
238
  module.exports.lsStream = lsStream
@@ -239,12 +263,12 @@ function lsStream (cache) {
239
263
  // reduced is a map of key => entry
240
264
  for (const entry of reduced.values()) {
241
265
  const formatted = formatEntry(cache, entry)
242
- if (formatted) {
266
+ if (formatted)
243
267
  stream.write(formatted)
244
- }
245
268
  }
246
269
  }).catch(err => {
247
- if (err.code === 'ENOENT') { return undefined }
270
+ if (err.code === 'ENOENT')
271
+ return undefined
248
272
  throw err
249
273
  })
250
274
  })
@@ -288,9 +312,9 @@ function bucketEntriesSync (bucket, filter) {
288
312
  function _bucketEntries (data, filter) {
289
313
  const entries = []
290
314
  data.split('\n').forEach((entry) => {
291
- if (!entry) {
315
+ if (!entry)
292
316
  return
293
- }
317
+
294
318
  const pieces = entry.split('\t')
295
319
  if (!pieces[1] || hashEntry(pieces[1]) !== pieces[0]) {
296
320
  // Hash is no good! Corruption or malice? Doesn't matter!
@@ -304,9 +328,8 @@ function _bucketEntries (data, filter) {
304
328
  // Entry is corrupted!
305
329
  return
306
330
  }
307
- if (obj) {
331
+ if (obj)
308
332
  entries.push(obj)
309
- }
310
333
  })
311
334
  return entries
312
335
  }
@@ -348,24 +371,23 @@ function hash (str, digest) {
348
371
 
349
372
  function formatEntry (cache, entry, keepAll) {
350
373
  // Treat null digests as deletions. They'll shadow any previous entries.
351
- if (!entry.integrity && !keepAll) {
374
+ if (!entry.integrity && !keepAll)
352
375
  return null
353
- }
376
+
354
377
  return {
355
378
  key: entry.key,
356
379
  integrity: entry.integrity,
357
380
  path: entry.integrity ? contentPath(cache, entry.integrity) : undefined,
358
381
  size: entry.size,
359
382
  time: entry.time,
360
- metadata: entry.metadata
383
+ metadata: entry.metadata,
361
384
  }
362
385
  }
363
386
 
364
387
  function readdirOrEmpty (dir) {
365
388
  return readdir(dir).catch((err) => {
366
- if (err.code === 'ENOENT' || err.code === 'ENOTDIR') {
389
+ if (err.code === 'ENOENT' || err.code === 'ENOTDIR')
367
390
  return []
368
- }
369
391
 
370
392
  throw err
371
393
  })
@@ -8,7 +8,7 @@ const MAX_AGE = 3 * 60 * 1000
8
8
  const MEMOIZED = new LRU({
9
9
  max: MAX_SIZE,
10
10
  maxAge: MAX_AGE,
11
- length: (entry, key) => key.startsWith('key:') ? entry.data.length : entry.length
11
+ length: (entry, key) => key.startsWith('key:') ? entry.data.length : entry.length,
12
12
  })
13
13
 
14
14
  module.exports.clearMemoized = clearMemoized
@@ -62,13 +62,12 @@ class ObjProxy {
62
62
  }
63
63
 
64
64
  function pickMem (opts) {
65
- if (!opts || !opts.memoize) {
65
+ if (!opts || !opts.memoize)
66
66
  return MEMOIZED
67
- } else if (opts.memoize.get && opts.memoize.set) {
67
+ else if (opts.memoize.get && opts.memoize.set)
68
68
  return opts.memoize
69
- } else if (typeof opts.memoize === 'object') {
69
+ else if (typeof opts.memoize === 'object')
70
70
  return new ObjProxy(opts.memoize)
71
- } else {
71
+ else
72
72
  return MEMOIZED
73
- }
74
73
  }
@@ -8,9 +8,9 @@ function disposer (creatorFn, disposerFn, fn) {
8
8
  .then(
9
9
  // disposer resolved, do something with original fn's promise
10
10
  () => {
11
- if (shouldThrow) {
11
+ if (shouldThrow)
12
12
  throw result
13
- }
13
+
14
14
  return result
15
15
  },
16
16
  // Disposer fn failed, crash process
@@ -49,9 +49,8 @@ function fixOwner (cache, filepath) {
49
49
  const { uid, gid } = owner
50
50
 
51
51
  // No need to override if it's already what we used.
52
- if (self.uid === uid && self.gid === gid) {
52
+ if (self.uid === uid && self.gid === gid)
53
53
  return
54
- }
55
54
 
56
55
  return inflight('fixOwner: fixing ownership on ' + filepath, () =>
57
56
  chownr(
@@ -59,9 +58,9 @@ function fixOwner (cache, filepath) {
59
58
  typeof uid === 'number' ? uid : self.uid,
60
59
  typeof gid === 'number' ? gid : self.gid
61
60
  ).catch((err) => {
62
- if (err.code === 'ENOENT') {
61
+ if (err.code === 'ENOENT')
63
62
  return null
64
- }
63
+
65
64
  throw err
66
65
  })
67
66
  )
@@ -94,9 +93,9 @@ function fixOwnerSync (cache, filepath) {
94
93
  )
95
94
  } catch (err) {
96
95
  // only catch ENOENT, any other error is a problem.
97
- if (err.code === 'ENOENT') {
96
+ if (err.code === 'ENOENT')
98
97
  return null
99
- }
98
+
100
99
  throw err
101
100
  }
102
101
  }
@@ -111,14 +110,13 @@ function mkdirfix (cache, p, cb) {
111
110
  return Promise.resolve(inferOwner(cache)).then(() => {
112
111
  return mkdirp(p)
113
112
  .then((made) => {
114
- if (made) {
113
+ if (made)
115
114
  return fixOwner(cache, made).then(() => made)
116
- }
117
115
  })
118
116
  .catch((err) => {
119
- if (err.code === 'EEXIST') {
117
+ if (err.code === 'EEXIST')
120
118
  return fixOwner(cache, p).then(() => null)
121
- }
119
+
122
120
  throw err
123
121
  })
124
122
  })
@@ -138,8 +136,7 @@ function mkdirfixSync (cache, p) {
138
136
  if (err.code === 'EEXIST') {
139
137
  fixOwnerSync(cache, p)
140
138
  return null
141
- } else {
139
+ } else
142
140
  throw err
143
- }
144
141
  }
145
142
  }
@@ -38,19 +38,17 @@ function moveFile (src, dest) {
38
38
  } else if (err.code === 'EEXIST' || err.code === 'EBUSY') {
39
39
  // file already exists, so whatever
40
40
  return resolve()
41
- } else {
41
+ } else
42
42
  return reject(err)
43
- }
44
- } else {
43
+ } else
45
44
  return resolve()
46
- }
47
45
  })
48
46
  })
49
47
  .then(() => {
50
48
  // content should never change for any reason, so make it read-only
51
49
  return Promise.all([
52
50
  unlink(src),
53
- !isWindows && chmod(dest, '0444')
51
+ !isWindows && chmod(dest, '0444'),
54
52
  ])
55
53
  })
56
54
  .catch(() => {
package/lib/verify.js CHANGED
@@ -24,7 +24,7 @@ const readFile = util.promisify(fs.readFile)
24
24
  const verifyOpts = (opts) => ({
25
25
  concurrency: 20,
26
26
  log: { silly () {} },
27
- ...opts
27
+ ...opts,
28
28
  })
29
29
 
30
30
  module.exports = verify
@@ -40,7 +40,7 @@ function verify (cache, opts) {
40
40
  rebuildIndex,
41
41
  cleanTmp,
42
42
  writeVerifile,
43
- markEndTime
43
+ markEndTime,
44
44
  ]
45
45
 
46
46
  return steps
@@ -54,9 +54,9 @@ function verify (cache, opts) {
54
54
  stats[k] = s[k]
55
55
  })
56
56
  const end = new Date()
57
- if (!stats.runTime) {
57
+ if (!stats.runTime)
58
58
  stats.runTime = {}
59
- }
59
+
60
60
  stats.runTime[label] = end - start
61
61
  return Promise.resolve(stats)
62
62
  })
@@ -108,9 +108,9 @@ function garbageCollect (cache, opts) {
108
108
  const indexStream = index.lsStream(cache)
109
109
  const liveContent = new Set()
110
110
  indexStream.on('data', (entry) => {
111
- if (opts.filter && !opts.filter(entry)) {
111
+ if (opts.filter && !opts.filter(entry))
112
112
  return
113
- }
113
+
114
114
  liveContent.add(entry.integrity.toString())
115
115
  })
116
116
  return new Promise((resolve, reject) => {
@@ -120,14 +120,14 @@ function garbageCollect (cache, opts) {
120
120
  return glob(path.join(contentDir, '**'), {
121
121
  follow: false,
122
122
  nodir: true,
123
- nosort: true
123
+ nosort: true,
124
124
  }).then((files) => {
125
125
  return Promise.resolve({
126
126
  verifiedContent: 0,
127
127
  reclaimedCount: 0,
128
128
  reclaimedSize: 0,
129
129
  badContentCount: 0,
130
- keptSize: 0
130
+ keptSize: 0,
131
131
  }).then((stats) =>
132
132
  pMap(
133
133
  files,
@@ -171,14 +171,14 @@ function verifyContent (filepath, sri) {
171
171
  .then((s) => {
172
172
  const contentInfo = {
173
173
  size: s.size,
174
- valid: true
174
+ valid: true,
175
175
  }
176
176
  return ssri
177
177
  .checkStream(new fsm.ReadStream(filepath), sri)
178
178
  .catch((err) => {
179
- if (err.code !== 'EINTEGRITY') {
179
+ if (err.code !== 'EINTEGRITY')
180
180
  throw err
181
- }
181
+
182
182
  return rimraf(filepath).then(() => {
183
183
  contentInfo.valid = false
184
184
  })
@@ -186,9 +186,9 @@ function verifyContent (filepath, sri) {
186
186
  .then(() => contentInfo)
187
187
  })
188
188
  .catch((err) => {
189
- if (err.code === 'ENOENT') {
189
+ if (err.code === 'ENOENT')
190
190
  return { size: 0, valid: false }
191
- }
191
+
192
192
  throw err
193
193
  })
194
194
  }
@@ -199,7 +199,7 @@ function rebuildIndex (cache, opts) {
199
199
  const stats = {
200
200
  missingContent: 0,
201
201
  rejectedEntries: 0,
202
- totalEntries: 0
202
+ totalEntries: 0,
203
203
  }
204
204
  const buckets = {}
205
205
  for (const k in entries) {
@@ -209,9 +209,9 @@ function rebuildIndex (cache, opts) {
209
209
  const entry = entries[k]
210
210
  const excluded = opts.filter && !opts.filter(entry)
211
211
  excluded && stats.rejectedEntries++
212
- if (buckets[hashed] && !excluded) {
212
+ if (buckets[hashed] && !excluded)
213
213
  buckets[hashed].push(entry)
214
- } else if (buckets[hashed] && excluded) {
214
+ else if (buckets[hashed] && excluded) {
215
215
  // skip
216
216
  } else if (excluded) {
217
217
  buckets[hashed] = []
@@ -244,7 +244,7 @@ function rebuildBucket (cache, bucket, stats, opts) {
244
244
  return index
245
245
  .insert(cache, entry.key, entry.integrity, {
246
246
  metadata: entry.metadata,
247
- size: entry.size
247
+ size: entry.size,
248
248
  })
249
249
  .then(() => {
250
250
  stats.totalEntries++
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cacache",
3
- "version": "15.1.0",
3
+ "version": "15.2.0",
4
4
  "cache-version": {
5
5
  "content": "2",
6
6
  "index": "5"
@@ -13,15 +13,17 @@
13
13
  ],
14
14
  "scripts": {
15
15
  "benchmarks": "node test/benchmarks",
16
- "lint": "standard",
17
- "postrelease": "npm publish",
18
- "posttest": "npm run lint",
19
- "prepublishOnly": "git push --follow-tags",
20
- "prerelease": "npm t",
21
- "release": "standard-version -s",
16
+ "preversion": "npm test",
17
+ "postversion": "npm publish",
18
+ "prepublishOnly": "git push origin --follow-tags",
22
19
  "test": "tap",
20
+ "snap": "tap",
23
21
  "coverage": "tap",
24
- "test-docker": "docker run -it --rm --name pacotest -v \"$PWD\":/tmp -w /tmp node:latest npm test"
22
+ "test-docker": "docker run -it --rm --name pacotest -v \"$PWD\":/tmp -w /tmp node:latest npm test",
23
+ "lint": "npm run npmclilint -- \"*.*js\" \"lib/**/*.*js\" \"test/**/*.*js\"",
24
+ "npmclilint": "npmcli-lint",
25
+ "lintfix": "npm run lint -- --fix",
26
+ "postsnap": "npm run lintfix --"
25
27
  },
26
28
  "repository": "https://github.com/npm/cacache",
27
29
  "keywords": [
@@ -39,23 +41,6 @@
39
41
  "disk cache",
40
42
  "disk storage"
41
43
  ],
42
- "author": {
43
- "name": "Kat Marchán",
44
- "email": "kzm@sykosomatic.org",
45
- "twitter": "maybekatz"
46
- },
47
- "contributors": [
48
- {
49
- "name": "Charlotte Spencer",
50
- "email": "charlottelaspencer@gmail.com",
51
- "twitter": "charlotteis"
52
- },
53
- {
54
- "name": "Rebecca Turner",
55
- "email": "me@re-becca.org",
56
- "twitter": "ReBeccaOrg"
57
- }
58
- ],
59
44
  "license": "ISC",
60
45
  "dependencies": {
61
46
  "@npmcli/move-file": "^1.0.1",
@@ -77,11 +62,10 @@
77
62
  "unique-filename": "^1.1.1"
78
63
  },
79
64
  "devDependencies": {
65
+ "@npmcli/lint": "^1.0.1",
80
66
  "benchmark": "^2.1.4",
81
67
  "chalk": "^4.0.0",
82
68
  "require-inject": "^1.4.4",
83
- "standard": "^14.3.1",
84
- "standard-version": "^7.1.0",
85
69
  "tacks": "^1.3.0",
86
70
  "tap": "^15.0.9"
87
71
  },
package/put.js CHANGED
@@ -9,7 +9,7 @@ const Pipeline = require('minipass-pipeline')
9
9
 
10
10
  const putOpts = (opts) => ({
11
11
  algorithms: ['sha512'],
12
- ...opts
12
+ ...opts,
13
13
  })
14
14
 
15
15
  module.exports = putData
@@ -21,9 +21,9 @@ function putData (cache, key, data, opts = {}) {
21
21
  return index
22
22
  .insert(cache, key, res.integrity, { ...opts, size: res.size })
23
23
  .then((entry) => {
24
- if (memoize) {
24
+ if (memoize)
25
25
  memo.put(cache, entry, data, opts)
26
- }
26
+
27
27
  return res.integrity
28
28
  })
29
29
  })
@@ -67,17 +67,16 @@ function putStream (cache, key, opts = {}) {
67
67
  return index
68
68
  .insert(cache, key, integrity, { ...opts, size })
69
69
  .then((entry) => {
70
- if (memoize && memoData) {
70
+ if (memoize && memoData)
71
71
  memo.put(cache, entry, memoData, opts)
72
- }
73
- if (integrity) {
72
+
73
+ if (integrity)
74
74
  pipeline.emit('integrity', integrity)
75
- }
76
- if (size) {
75
+
76
+ if (size)
77
77
  pipeline.emit('size', size)
78
- }
79
78
  })
80
- }
79
+ },
81
80
  }))
82
81
 
83
82
  return pipeline
package/rm.js CHANGED
@@ -11,9 +11,9 @@ const rmContent = require('./lib/content/rm')
11
11
  module.exports = entry
12
12
  module.exports.entry = entry
13
13
 
14
- function entry (cache, key) {
14
+ function entry (cache, key, opts) {
15
15
  memo.clearMemoized()
16
- return index.delete(cache, key)
16
+ return index.delete(cache, key, opts)
17
17
  }
18
18
 
19
19
  module.exports.content = content