cacache 15.1.0 → 16.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.
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
 
@@ -23,6 +23,7 @@ function read (cache, integrity, opts = {}) {
23
23
  if (typeof size === 'number' && stat.size !== size) {
24
24
  throw sizeError(size, stat.size)
25
25
  }
26
+
26
27
  if (stat.size > MAX_SINGLE_READ_SIZE) {
27
28
  return readPipeline(cpath, stat.size, sri, new Pipeline()).concat()
28
29
  }
@@ -31,6 +32,7 @@ function read (cache, integrity, opts = {}) {
31
32
  if (!ssri.checkData(data, sri)) {
32
33
  throw integrityError(sri, cpath)
33
34
  }
35
+
34
36
  return data
35
37
  })
36
38
  })
@@ -40,11 +42,11 @@ const readPipeline = (cpath, size, sri, stream) => {
40
42
  stream.push(
41
43
  new fsm.ReadStream(cpath, {
42
44
  size,
43
- readSize: MAX_SINGLE_READ_SIZE
45
+ readSize: MAX_SINGLE_READ_SIZE,
44
46
  }),
45
47
  ssri.integrityStream({
46
48
  integrity: sri,
47
- size
49
+ size,
48
50
  })
49
51
  )
50
52
  return stream
@@ -81,6 +83,7 @@ function readStream (cache, integrity, opts = {}) {
81
83
  if (typeof size === 'number' && size !== stat.size) {
82
84
  return stream.emit('error', sizeError(size, stat.size))
83
85
  }
86
+
84
87
  readPipeline(cpath, stat.size, sri, stream)
85
88
  }, er => stream.emit('error', er))
86
89
 
@@ -112,12 +115,14 @@ function hasContent (cache, integrity) {
112
115
  if (!integrity) {
113
116
  return Promise.resolve(false)
114
117
  }
118
+
115
119
  return withContentSri(cache, integrity, (cpath, sri) => {
116
120
  return lstat(cpath).then((stat) => ({ size: stat.size, sri, stat }))
117
121
  }).catch((err) => {
118
122
  if (err.code === 'ENOENT') {
119
123
  return false
120
124
  }
125
+
121
126
  if (err.code === 'EPERM') {
122
127
  /* istanbul ignore else */
123
128
  if (process.platform !== 'win32') {
@@ -135,6 +140,7 @@ function hasContentSync (cache, integrity) {
135
140
  if (!integrity) {
136
141
  return false
137
142
  }
143
+
138
144
  return withContentSriSync(cache, integrity, (cpath, sri) => {
139
145
  try {
140
146
  const stat = fs.lstatSync(cpath)
@@ -143,6 +149,7 @@ function hasContentSync (cache, integrity) {
143
149
  if (err.code === 'ENOENT') {
144
150
  return false
145
151
  }
152
+
146
153
  if (err.code === 'EPERM') {
147
154
  /* istanbul ignore else */
148
155
  if (process.platform !== 'win32') {
@@ -167,7 +174,8 @@ function withContentSri (cache, integrity, fn) {
167
174
  const cpath = contentPath(cache, digests[0])
168
175
  return fn(cpath, digests[0])
169
176
  } else {
170
- // Can't use race here because a generic error can happen before a ENOENT error, and can happen before a valid result
177
+ // Can't use race here because a generic error can happen before
178
+ // a ENOENT error, and can happen before a valid result
171
179
  return Promise
172
180
  .all(digests.map((meta) => {
173
181
  return withContentSri(cache, meta, fn)
@@ -234,6 +242,7 @@ function withContentSriSync (cache, integrity, fn) {
234
242
  }
235
243
 
236
244
  function sizeError (expected, found) {
245
+ /* eslint-disable-next-line max-len */
237
246
  const err = new Error(`Bad data size: expected inserted data to be ${expected} bytes, but got ${found} instead`)
238
247
  err.expected = expected
239
248
  err.found = found
@@ -25,9 +25,11 @@ function write (cache, data, opts = {}) {
25
25
  if (algorithms && algorithms.length > 1) {
26
26
  throw new Error('opts.algorithms only supports a single algorithm for now')
27
27
  }
28
+
28
29
  if (typeof size === 'number' && data.length !== size) {
29
30
  return Promise.reject(sizeError(size, data.length))
30
31
  }
32
+
31
33
  const sri = ssri.fromData(data, algorithms ? { algorithms } : {})
32
34
  if (integrity && !ssri.checkData(data, integrity, opts)) {
33
35
  return Promise.reject(checksumError(integrity, sri))
@@ -112,13 +114,17 @@ function pipeToTmp (inputStream, cache, tmpTarget, opts) {
112
114
  const hashStream = ssri.integrityStream({
113
115
  integrity: opts.integrity,
114
116
  algorithms: opts.algorithms,
115
- size: opts.size
117
+ size: opts.size,
118
+ })
119
+ hashStream.on('integrity', i => {
120
+ integrity = i
121
+ })
122
+ hashStream.on('size', s => {
123
+ size = s
116
124
  })
117
- hashStream.on('integrity', i => { integrity = i })
118
- hashStream.on('size', s => { size = s })
119
125
 
120
126
  const outStream = new fsm.WriteStream(tmpTarget, {
121
- flags: 'wx'
127
+ flags: 'wx',
122
128
  })
123
129
 
124
130
  // NB: this can throw if the hashStream has a problem with
@@ -132,14 +138,16 @@ function pipeToTmp (inputStream, cache, tmpTarget, opts) {
132
138
 
133
139
  return pipeline.promise()
134
140
  .then(() => ({ integrity, size }))
135
- .catch(er => rimraf(tmpTarget).then(() => { throw er }))
141
+ .catch(er => rimraf(tmpTarget).then(() => {
142
+ throw er
143
+ }))
136
144
  }
137
145
 
138
146
  function makeTmp (cache, opts) {
139
147
  const tmpTarget = uniqueFilename(path.join(cache, 'tmp'), opts.tmpPrefix)
140
148
  return fixOwner.mkdirfix(cache, path.dirname(tmpTarget)).then(() => ({
141
149
  target: tmpTarget,
142
- moved: false
150
+ moved: false,
143
151
  }))
144
152
  }
145
153
 
@@ -147,6 +155,7 @@ function makeTmpDisposer (tmp) {
147
155
  if (tmp.moved) {
148
156
  return Promise.resolve()
149
157
  }
158
+
150
159
  return rimraf(tmp.target)
151
160
  }
152
161
 
@@ -166,6 +175,7 @@ function moveToDestination (tmp, cache, sri, opts) {
166
175
  }
167
176
 
168
177
  function sizeError (expected, found) {
178
+ /* eslint-disable-next-line max-len */
169
179
  const err = new Error(`Bad data size: expected inserted data to be ${expected} bytes, but got ${found} instead`)
170
180
  err.expected = expected
171
181
  err.found = found
@@ -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,32 @@ 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
+ 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
43
54
  }
44
55
 
45
- return acc
46
- }, [])
56
+ // if this entry is valid, and it is either the first entry or
57
+ // the newEntries array doesn't already include an entry that
58
+ // matches this one based on the provided matchFn, then we add
59
+ // it to the beginning of our list
60
+ if ((!opts.validateEntry || opts.validateEntry(entry) === true) &&
61
+ (newEntries.length === 0 ||
62
+ !newEntries.find((oldEntry) => matchFn(oldEntry, entry)))) {
63
+ newEntries.unshift(entry)
64
+ }
65
+ }
47
66
 
48
67
  const newIndex = '\n' + newEntries.map((entry) => {
49
68
  const stringified = JSON.stringify(entry)
@@ -56,7 +75,7 @@ async function compact (cache, key, matchFn, opts = {}) {
56
75
  await fixOwner.mkdirfix(cache, path.dirname(target))
57
76
  return {
58
77
  target,
59
- moved: false
78
+ moved: false,
60
79
  }
61
80
  }
62
81
 
@@ -85,7 +104,12 @@ async function compact (cache, key, matchFn, opts = {}) {
85
104
  // write the file atomically
86
105
  await disposer(setup(), teardown, write)
87
106
 
88
- return newEntries.map((entry) => formatEntry(cache, entry, true))
107
+ // we reverse the list we generated such that the newest
108
+ // entries come first in order to make looping through them easier
109
+ // the true passed to formatEntry tells it to keep null
110
+ // integrity values, if they made it this far it's because
111
+ // validateEntry returned true, and as such we should return it
112
+ return newEntries.reverse().map((entry) => formatEntry(cache, entry, true))
89
113
  }
90
114
 
91
115
  module.exports.insert = insert
@@ -98,7 +122,7 @@ function insert (cache, key, integrity, opts = {}) {
98
122
  integrity: integrity && ssri.stringify(integrity),
99
123
  time: Date.now(),
100
124
  size,
101
- metadata
125
+ metadata,
102
126
  }
103
127
  return fixOwner
104
128
  .mkdirfix(cache, path.dirname(bucket))
@@ -110,7 +134,8 @@ function insert (cache, key, integrity, opts = {}) {
110
134
  // another while still preserving the string length of the JSON in
111
135
  // question. So, we just slap the length in there and verify it on read.
112
136
  //
113
- // Thanks to @isaacs for the whiteboarding session that ended up with this.
137
+ // Thanks to @isaacs for the whiteboarding session that ended up with
138
+ // this.
114
139
  return appendFile(bucket, `\n${hashEntry(stringified)}\t${stringified}`)
115
140
  })
116
141
  .then(() => fixOwner.chownr(cache, bucket))
@@ -118,6 +143,7 @@ function insert (cache, key, integrity, opts = {}) {
118
143
  if (err.code === 'ENOENT') {
119
144
  return undefined
120
145
  }
146
+
121
147
  throw err
122
148
  // There's a class of race conditions that happen when things get deleted
123
149
  // during fixOwner, or between the two mkdirfix/chownr calls.
@@ -140,7 +166,7 @@ function insertSync (cache, key, integrity, opts = {}) {
140
166
  integrity: integrity && ssri.stringify(integrity),
141
167
  time: Date.now(),
142
168
  size,
143
- metadata
169
+ metadata,
144
170
  }
145
171
  fixOwner.mkdirfix.sync(cache, path.dirname(bucket))
146
172
  const stringified = JSON.stringify(entry)
@@ -201,14 +227,24 @@ function findSync (cache, key) {
201
227
 
202
228
  module.exports.delete = del
203
229
 
204
- function del (cache, key, opts) {
205
- return insert(cache, key, null, opts)
230
+ function del (cache, key, opts = {}) {
231
+ if (!opts.removeFully) {
232
+ return insert(cache, key, null, opts)
233
+ }
234
+
235
+ const bucket = bucketPath(cache, key)
236
+ return rimraf(bucket)
206
237
  }
207
238
 
208
239
  module.exports.delete.sync = delSync
209
240
 
210
- function delSync (cache, key, opts) {
211
- return insertSync(cache, key, null, opts)
241
+ function delSync (cache, key, opts = {}) {
242
+ if (!opts.removeFully) {
243
+ return insertSync(cache, key, null, opts)
244
+ }
245
+
246
+ const bucket = bucketPath(cache, key)
247
+ return rimraf.sync(bucket)
212
248
  }
213
249
 
214
250
  module.exports.lsStream = lsStream
@@ -244,7 +280,9 @@ function lsStream (cache) {
244
280
  }
245
281
  }
246
282
  }).catch(err => {
247
- if (err.code === 'ENOENT') { return undefined }
283
+ if (err.code === 'ENOENT') {
284
+ return undefined
285
+ }
248
286
  throw err
249
287
  })
250
288
  })
@@ -291,6 +329,7 @@ function _bucketEntries (data, filter) {
291
329
  if (!entry) {
292
330
  return
293
331
  }
332
+
294
333
  const pieces = entry.split('\t')
295
334
  if (!pieces[1] || hashEntry(pieces[1]) !== pieces[0]) {
296
335
  // Hash is no good! Corruption or malice? Doesn't matter!
@@ -351,13 +390,14 @@ function formatEntry (cache, entry, keepAll) {
351
390
  if (!entry.integrity && !keepAll) {
352
391
  return null
353
392
  }
393
+
354
394
  return {
355
395
  key: entry.key,
356
396
  integrity: entry.integrity,
357
397
  path: entry.integrity ? contentPath(cache, entry.integrity) : undefined,
358
398
  size: entry.size,
359
399
  time: entry.time,
360
- metadata: entry.metadata
400
+ metadata: entry.metadata,
361
401
  }
362
402
  }
363
403
 
package/lib/get.js ADDED
@@ -0,0 +1,251 @@
1
+ 'use strict'
2
+
3
+ const Collect = require('minipass-collect')
4
+ const Minipass = require('minipass')
5
+ const Pipeline = require('minipass-pipeline')
6
+ const fs = require('fs')
7
+ const util = require('util')
8
+
9
+ const index = require('./entry-index')
10
+ const memo = require('./memoization')
11
+ const read = require('./content/read')
12
+
13
+ const writeFile = util.promisify(fs.writeFile)
14
+
15
+ function getData (cache, key, opts = {}) {
16
+ const { integrity, memoize, size } = opts
17
+ const memoized = memo.get(cache, key, opts)
18
+ if (memoized && memoize !== false) {
19
+ return Promise.resolve({
20
+ metadata: memoized.entry.metadata,
21
+ data: memoized.data,
22
+ integrity: memoized.entry.integrity,
23
+ size: memoized.entry.size,
24
+ })
25
+ }
26
+
27
+ return index.find(cache, key, opts).then((entry) => {
28
+ if (!entry) {
29
+ throw new index.NotFoundError(cache, key)
30
+ }
31
+
32
+ return read(cache, entry.integrity, { integrity, size }).then((data) => {
33
+ if (memoize) {
34
+ memo.put(cache, entry, data, opts)
35
+ }
36
+
37
+ return {
38
+ data,
39
+ metadata: entry.metadata,
40
+ size: entry.size,
41
+ integrity: entry.integrity,
42
+ }
43
+ })
44
+ })
45
+ }
46
+ module.exports = getData
47
+
48
+ function getDataByDigest (cache, key, opts = {}) {
49
+ const { integrity, memoize, size } = opts
50
+ const memoized = memo.get.byDigest(cache, key, opts)
51
+ if (memoized && memoize !== false) {
52
+ return Promise.resolve(memoized)
53
+ }
54
+
55
+ return read(cache, key, { integrity, size }).then((res) => {
56
+ if (memoize) {
57
+ memo.put.byDigest(cache, key, res, opts)
58
+ }
59
+ return res
60
+ })
61
+ }
62
+ module.exports.byDigest = getDataByDigest
63
+
64
+ function getDataSync (cache, key, opts = {}) {
65
+ const { integrity, memoize, size } = opts
66
+ const memoized = memo.get(cache, key, opts)
67
+
68
+ if (memoized && memoize !== false) {
69
+ return {
70
+ metadata: memoized.entry.metadata,
71
+ data: memoized.data,
72
+ integrity: memoized.entry.integrity,
73
+ size: memoized.entry.size,
74
+ }
75
+ }
76
+ const entry = index.find.sync(cache, key, opts)
77
+ if (!entry) {
78
+ throw new index.NotFoundError(cache, key)
79
+ }
80
+ const data = read.sync(cache, entry.integrity, {
81
+ integrity: integrity,
82
+ size: size,
83
+ })
84
+ const res = {
85
+ metadata: entry.metadata,
86
+ data: data,
87
+ size: entry.size,
88
+ integrity: entry.integrity,
89
+ }
90
+ if (memoize) {
91
+ memo.put(cache, entry, res.data, opts)
92
+ }
93
+
94
+ return res
95
+ }
96
+
97
+ module.exports.sync = getDataSync
98
+
99
+ function getDataByDigestSync (cache, digest, opts = {}) {
100
+ const { integrity, memoize, size } = opts
101
+ const memoized = memo.get.byDigest(cache, digest, opts)
102
+
103
+ if (memoized && memoize !== false) {
104
+ return memoized
105
+ }
106
+
107
+ const res = read.sync(cache, digest, {
108
+ integrity: integrity,
109
+ size: size,
110
+ })
111
+ if (memoize) {
112
+ memo.put.byDigest(cache, digest, res, opts)
113
+ }
114
+
115
+ return res
116
+ }
117
+ module.exports.sync.byDigest = getDataByDigestSync
118
+
119
+ const getMemoizedStream = (memoized) => {
120
+ const stream = new Minipass()
121
+ stream.on('newListener', function (ev, cb) {
122
+ ev === 'metadata' && cb(memoized.entry.metadata)
123
+ ev === 'integrity' && cb(memoized.entry.integrity)
124
+ ev === 'size' && cb(memoized.entry.size)
125
+ })
126
+ stream.end(memoized.data)
127
+ return stream
128
+ }
129
+
130
+ function getStream (cache, key, opts = {}) {
131
+ const { memoize, size } = opts
132
+ const memoized = memo.get(cache, key, opts)
133
+ if (memoized && memoize !== false) {
134
+ return getMemoizedStream(memoized)
135
+ }
136
+
137
+ const stream = new Pipeline()
138
+ index
139
+ .find(cache, key)
140
+ .then((entry) => {
141
+ if (!entry) {
142
+ throw new index.NotFoundError(cache, key)
143
+ }
144
+
145
+ stream.emit('metadata', entry.metadata)
146
+ stream.emit('integrity', entry.integrity)
147
+ stream.emit('size', entry.size)
148
+ stream.on('newListener', function (ev, cb) {
149
+ ev === 'metadata' && cb(entry.metadata)
150
+ ev === 'integrity' && cb(entry.integrity)
151
+ ev === 'size' && cb(entry.size)
152
+ })
153
+
154
+ const src = read.readStream(
155
+ cache,
156
+ entry.integrity,
157
+ { ...opts, size: typeof size !== 'number' ? entry.size : size }
158
+ )
159
+
160
+ if (memoize) {
161
+ const memoStream = new Collect.PassThrough()
162
+ memoStream.on('collect', data => memo.put(cache, entry, data, opts))
163
+ stream.unshift(memoStream)
164
+ }
165
+ stream.unshift(src)
166
+ })
167
+ .catch((err) => stream.emit('error', err))
168
+
169
+ return stream
170
+ }
171
+
172
+ module.exports.stream = getStream
173
+
174
+ function getStreamDigest (cache, integrity, opts = {}) {
175
+ const { memoize } = opts
176
+ const memoized = memo.get.byDigest(cache, integrity, opts)
177
+ if (memoized && memoize !== false) {
178
+ const stream = new Minipass()
179
+ stream.end(memoized)
180
+ return stream
181
+ } else {
182
+ const stream = read.readStream(cache, integrity, opts)
183
+ if (!memoize) {
184
+ return stream
185
+ }
186
+
187
+ const memoStream = new Collect.PassThrough()
188
+ memoStream.on('collect', data => memo.put.byDigest(
189
+ cache,
190
+ integrity,
191
+ data,
192
+ opts
193
+ ))
194
+ return new Pipeline(stream, memoStream)
195
+ }
196
+ }
197
+
198
+ module.exports.stream.byDigest = getStreamDigest
199
+
200
+ function info (cache, key, opts = {}) {
201
+ const { memoize } = opts
202
+ const memoized = memo.get(cache, key, opts)
203
+ if (memoized && memoize !== false) {
204
+ return Promise.resolve(memoized.entry)
205
+ } else {
206
+ return index.find(cache, key)
207
+ }
208
+ }
209
+ module.exports.info = info
210
+
211
+ function copy (cache, key, dest, opts = {}) {
212
+ if (read.copy) {
213
+ return index.find(cache, key, opts).then((entry) => {
214
+ if (!entry) {
215
+ throw new index.NotFoundError(cache, key)
216
+ }
217
+ return read.copy(cache, entry.integrity, dest, opts)
218
+ .then(() => {
219
+ return {
220
+ metadata: entry.metadata,
221
+ size: entry.size,
222
+ integrity: entry.integrity,
223
+ }
224
+ })
225
+ })
226
+ }
227
+
228
+ return getData(cache, key, opts).then((res) => {
229
+ return writeFile(dest, res.data).then(() => {
230
+ return {
231
+ metadata: res.metadata,
232
+ size: res.size,
233
+ integrity: res.integrity,
234
+ }
235
+ })
236
+ })
237
+ }
238
+ module.exports.copy = copy
239
+
240
+ function copyByDigest (cache, key, dest, opts = {}) {
241
+ if (read.copy) {
242
+ return read.copy(cache, key, dest, opts).then(() => key)
243
+ }
244
+
245
+ return getDataByDigest(cache, key, opts).then((res) => {
246
+ return writeFile(dest, res).then(() => key)
247
+ })
248
+ }
249
+ module.exports.copy.byDigest = copyByDigest
250
+
251
+ module.exports.hasContent = read.hasContent
@@ -1,20 +1,19 @@
1
1
  'use strict'
2
2
 
3
- const ls = require('./ls.js')
4
3
  const get = require('./get.js')
5
4
  const put = require('./put.js')
6
5
  const rm = require('./rm.js')
7
6
  const verify = require('./verify.js')
8
- const { clearMemoized } = require('./lib/memoization.js')
9
- const tmp = require('./lib/util/tmp.js')
10
- const index = require('./lib/entry-index.js')
7
+ const { clearMemoized } = require('./memoization.js')
8
+ const tmp = require('./util/tmp.js')
9
+ const index = require('./entry-index.js')
11
10
 
12
11
  module.exports.index = {}
13
12
  module.exports.index.compact = index.compact
14
13
  module.exports.index.insert = index.insert
15
14
 
16
- module.exports.ls = ls
17
- module.exports.ls.stream = ls.stream
15
+ module.exports.ls = index.ls
16
+ module.exports.ls.stream = index.lsStream
18
17
 
19
18
  module.exports.get = get
20
19
  module.exports.get.byDigest = get.byDigest
@@ -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
@@ -1,15 +1,15 @@
1
1
  'use strict'
2
2
 
3
- const index = require('./lib/entry-index')
4
- const memo = require('./lib/memoization')
5
- const write = require('./lib/content/write')
3
+ const index = require('./entry-index')
4
+ const memo = require('./memoization')
5
+ const write = require('./content/write')
6
6
  const Flush = require('minipass-flush')
7
7
  const { PassThrough } = require('minipass-collect')
8
8
  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
@@ -24,6 +24,7 @@ function putData (cache, key, data, opts = {}) {
24
24
  if (memoize) {
25
25
  memo.put(cache, entry, data, opts)
26
26
  }
27
+
27
28
  return res.integrity
28
29
  })
29
30
  })
@@ -70,14 +71,16 @@ function putStream (cache, key, opts = {}) {
70
71
  if (memoize && memoData) {
71
72
  memo.put(cache, entry, memoData, opts)
72
73
  }
74
+
73
75
  if (integrity) {
74
76
  pipeline.emit('integrity', integrity)
75
77
  }
78
+
76
79
  if (size) {
77
80
  pipeline.emit('size', size)
78
81
  }
79
82
  })
80
- }
83
+ },
81
84
  }))
82
85
 
83
86
  return pipeline
@@ -2,18 +2,18 @@
2
2
 
3
3
  const util = require('util')
4
4
 
5
- const index = require('./lib/entry-index')
6
- const memo = require('./lib/memoization')
5
+ const index = require('./entry-index')
6
+ const memo = require('./memoization')
7
7
  const path = require('path')
8
8
  const rimraf = util.promisify(require('rimraf'))
9
- const rmContent = require('./lib/content/rm')
9
+ const rmContent = require('./content/rm')
10
10
 
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
@@ -11,6 +11,7 @@ function disposer (creatorFn, disposerFn, fn) {
11
11
  if (shouldThrow) {
12
12
  throw result
13
13
  }
14
+
14
15
  return result
15
16
  },
16
17
  // Disposer fn failed, crash process
@@ -62,6 +62,7 @@ function fixOwner (cache, filepath) {
62
62
  if (err.code === 'ENOENT') {
63
63
  return null
64
64
  }
65
+
65
66
  throw err
66
67
  })
67
68
  )
@@ -97,6 +98,7 @@ function fixOwnerSync (cache, filepath) {
97
98
  if (err.code === 'ENOENT') {
98
99
  return null
99
100
  }
101
+
100
102
  throw err
101
103
  }
102
104
  }
@@ -119,6 +121,7 @@ function mkdirfix (cache, p, cb) {
119
121
  if (err.code === 'EEXIST') {
120
122
  return fixOwner(cache, p).then(() => null)
121
123
  }
124
+
122
125
  throw err
123
126
  })
124
127
  })
@@ -50,7 +50,7 @@ function moveFile (src, dest) {
50
50
  // content should never change for any reason, so make it read-only
51
51
  return Promise.all([
52
52
  unlink(src),
53
- !isWindows && chmod(dest, '0444')
53
+ !isWindows && chmod(dest, '0444'),
54
54
  ])
55
55
  })
56
56
  .catch(() => {
package/lib/util/tmp.js CHANGED
@@ -1,21 +1,21 @@
1
1
  'use strict'
2
2
 
3
- const util = require('util')
3
+ const fs = require('@npmcli/fs')
4
4
 
5
5
  const fixOwner = require('./fix-owner')
6
6
  const path = require('path')
7
- const rimraf = util.promisify(require('rimraf'))
8
- const uniqueFilename = require('unique-filename')
9
- const { disposer } = require('./disposer')
10
7
 
11
8
  module.exports.mkdir = mktmpdir
12
9
 
13
10
  function mktmpdir (cache, opts = {}) {
14
11
  const { tmpPrefix } = opts
15
- const tmpTarget = uniqueFilename(path.join(cache, 'tmp'), tmpPrefix)
16
- return fixOwner.mkdirfix(cache, tmpTarget).then(() => {
17
- return tmpTarget
18
- })
12
+ const tmpDir = path.join(cache, 'tmp')
13
+ return fs.mkdir(tmpDir, { recursive: true, owner: 'inherit' })
14
+ .then(() => {
15
+ // do not use path.join(), it drops the trailing / if tmpPrefix is unset
16
+ const target = `${tmpDir}${path.sep}${tmpPrefix || ''}`
17
+ return fs.mkdtemp(target, { owner: 'inherit' })
18
+ })
19
19
  }
20
20
 
21
21
  module.exports.withTmp = withTmp
@@ -25,7 +25,7 @@ function withTmp (cache, opts, cb) {
25
25
  cb = opts
26
26
  opts = {}
27
27
  }
28
- return disposer(mktmpdir(cache, opts), rimraf, cb)
28
+ return fs.withTempDir(path.join(cache, 'tmp'), cb, opts)
29
29
  }
30
30
 
31
31
  module.exports.fix = fixtmpdir
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
@@ -57,6 +57,7 @@ function verify (cache, opts) {
57
57
  if (!stats.runTime) {
58
58
  stats.runTime = {}
59
59
  }
60
+
60
61
  stats.runTime[label] = end - start
61
62
  return Promise.resolve(stats)
62
63
  })
@@ -111,6 +112,7 @@ function garbageCollect (cache, opts) {
111
112
  if (opts.filter && !opts.filter(entry)) {
112
113
  return
113
114
  }
115
+
114
116
  liveContent.add(entry.integrity.toString())
115
117
  })
116
118
  return new Promise((resolve, reject) => {
@@ -120,14 +122,14 @@ function garbageCollect (cache, opts) {
120
122
  return glob(path.join(contentDir, '**'), {
121
123
  follow: false,
122
124
  nodir: true,
123
- nosort: true
125
+ nosort: true,
124
126
  }).then((files) => {
125
127
  return Promise.resolve({
126
128
  verifiedContent: 0,
127
129
  reclaimedCount: 0,
128
130
  reclaimedSize: 0,
129
131
  badContentCount: 0,
130
- keptSize: 0
132
+ keptSize: 0,
131
133
  }).then((stats) =>
132
134
  pMap(
133
135
  files,
@@ -171,7 +173,7 @@ function verifyContent (filepath, sri) {
171
173
  .then((s) => {
172
174
  const contentInfo = {
173
175
  size: s.size,
174
- valid: true
176
+ valid: true,
175
177
  }
176
178
  return ssri
177
179
  .checkStream(new fsm.ReadStream(filepath), sri)
@@ -179,6 +181,7 @@ function verifyContent (filepath, sri) {
179
181
  if (err.code !== 'EINTEGRITY') {
180
182
  throw err
181
183
  }
184
+
182
185
  return rimraf(filepath).then(() => {
183
186
  contentInfo.valid = false
184
187
  })
@@ -189,6 +192,7 @@ function verifyContent (filepath, sri) {
189
192
  if (err.code === 'ENOENT') {
190
193
  return { size: 0, valid: false }
191
194
  }
195
+
192
196
  throw err
193
197
  })
194
198
  }
@@ -199,7 +203,7 @@ function rebuildIndex (cache, opts) {
199
203
  const stats = {
200
204
  missingContent: 0,
201
205
  rejectedEntries: 0,
202
- totalEntries: 0
206
+ totalEntries: 0,
203
207
  }
204
208
  const buckets = {}
205
209
  for (const k in entries) {
@@ -244,7 +248,7 @@ function rebuildBucket (cache, bucket, stats, opts) {
244
248
  return index
245
249
  .insert(cache, entry.key, entry.integrity, {
246
250
  metadata: entry.metadata,
247
- size: entry.size
251
+ size: entry.size,
248
252
  })
249
253
  .then(() => {
250
254
  stats.totalEntries++
package/package.json CHANGED
@@ -1,27 +1,32 @@
1
1
  {
2
2
  "name": "cacache",
3
- "version": "15.1.0",
3
+ "version": "16.0.0",
4
4
  "cache-version": {
5
5
  "content": "2",
6
6
  "index": "5"
7
7
  },
8
8
  "description": "Fast, fault-tolerant, cross-platform, disk-based, data-agnostic, content-addressable cache.",
9
- "main": "index.js",
9
+ "main": "lib/index.js",
10
10
  "files": [
11
- "*.js",
11
+ "bin",
12
12
  "lib"
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": "eslint '**/*.js'",
24
+ "npmclilint": "npmcli-lint",
25
+ "lintfix": "npm run lint -- --fix",
26
+ "postsnap": "npm run lintfix --",
27
+ "postlint": "npm-template-check",
28
+ "template-copy": "npm-template-copy --force",
29
+ "posttest": "npm run lint"
25
30
  },
26
31
  "repository": "https://github.com/npm/cacache",
27
32
  "keywords": [
@@ -39,49 +44,32 @@
39
44
  "disk cache",
40
45
  "disk storage"
41
46
  ],
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
47
  "license": "ISC",
60
48
  "dependencies": {
61
- "@npmcli/move-file": "^1.0.1",
49
+ "@npmcli/fs": "^1.0.0",
50
+ "@npmcli/move-file": "^1.1.2",
62
51
  "chownr": "^2.0.0",
63
- "fs-minipass": "^2.0.0",
52
+ "fs-minipass": "^2.1.0",
64
53
  "glob": "^7.1.4",
65
54
  "infer-owner": "^1.0.4",
66
55
  "lru-cache": "^6.0.0",
67
56
  "minipass": "^3.1.1",
68
57
  "minipass-collect": "^1.0.2",
69
58
  "minipass-flush": "^1.0.5",
70
- "minipass-pipeline": "^1.2.2",
71
- "mkdirp": "^1.0.3",
59
+ "minipass-pipeline": "^1.2.4",
60
+ "mkdirp": "^1.0.4",
72
61
  "p-map": "^4.0.0",
73
62
  "promise-inflight": "^1.0.1",
74
63
  "rimraf": "^3.0.2",
75
64
  "ssri": "^8.0.1",
76
- "tar": "^6.0.2",
65
+ "tar": "^6.1.11",
77
66
  "unique-filename": "^1.1.1"
78
67
  },
79
68
  "devDependencies": {
69
+ "@npmcli/template-oss": "^2.9.2",
80
70
  "benchmark": "^2.1.4",
81
71
  "chalk": "^4.0.0",
82
72
  "require-inject": "^1.4.4",
83
- "standard": "^14.3.1",
84
- "standard-version": "^7.1.0",
85
73
  "tacks": "^1.3.0",
86
74
  "tap": "^15.0.9"
87
75
  },
@@ -90,6 +78,11 @@
90
78
  "test-regex": "test/[^/]*.js"
91
79
  },
92
80
  "engines": {
93
- "node": ">= 10"
94
- }
81
+ "node": "^12.13.0 || ^14.15.0 || >=16"
82
+ },
83
+ "templateOSS": {
84
+ "windowsCI": false,
85
+ "version": "2.9.2"
86
+ },
87
+ "author": "GitHub Inc."
95
88
  }
package/get.js DELETED
@@ -1,255 +0,0 @@
1
- 'use strict'
2
-
3
- const util = require('util')
4
- const fs = require('fs')
5
- const index = require('./lib/entry-index')
6
- const memo = require('./lib/memoization')
7
- const read = require('./lib/content/read')
8
-
9
- const Minipass = require('minipass')
10
- const Collect = require('minipass-collect')
11
- const Pipeline = require('minipass-pipeline')
12
-
13
- const writeFile = util.promisify(fs.writeFile)
14
-
15
- module.exports = function get (cache, key, opts) {
16
- return getData(false, cache, key, opts)
17
- }
18
- module.exports.byDigest = function getByDigest (cache, digest, opts) {
19
- return getData(true, cache, digest, opts)
20
- }
21
-
22
- function getData (byDigest, cache, key, opts = {}) {
23
- const { integrity, memoize, size } = opts
24
- const memoized = byDigest
25
- ? memo.get.byDigest(cache, key, opts)
26
- : memo.get(cache, key, opts)
27
- if (memoized && memoize !== false) {
28
- return Promise.resolve(
29
- byDigest
30
- ? memoized
31
- : {
32
- metadata: memoized.entry.metadata,
33
- data: memoized.data,
34
- integrity: memoized.entry.integrity,
35
- size: memoized.entry.size
36
- }
37
- )
38
- }
39
- return (byDigest ? Promise.resolve(null) : index.find(cache, key, opts)).then(
40
- (entry) => {
41
- if (!entry && !byDigest) {
42
- throw new index.NotFoundError(cache, key)
43
- }
44
- return read(cache, byDigest ? key : entry.integrity, {
45
- integrity,
46
- size
47
- })
48
- .then((data) =>
49
- byDigest
50
- ? data
51
- : {
52
- data,
53
- metadata: entry.metadata,
54
- size: entry.size,
55
- integrity: entry.integrity
56
- }
57
- )
58
- .then((res) => {
59
- if (memoize && byDigest) {
60
- memo.put.byDigest(cache, key, res, opts)
61
- } else if (memoize) {
62
- memo.put(cache, entry, res.data, opts)
63
- }
64
- return res
65
- })
66
- }
67
- )
68
- }
69
-
70
- module.exports.sync = function get (cache, key, opts) {
71
- return getDataSync(false, cache, key, opts)
72
- }
73
- module.exports.sync.byDigest = function getByDigest (cache, digest, opts) {
74
- return getDataSync(true, cache, digest, opts)
75
- }
76
-
77
- function getDataSync (byDigest, cache, key, opts = {}) {
78
- const { integrity, memoize, size } = opts
79
- const memoized = byDigest
80
- ? memo.get.byDigest(cache, key, opts)
81
- : memo.get(cache, key, opts)
82
- if (memoized && memoize !== false) {
83
- return byDigest
84
- ? memoized
85
- : {
86
- metadata: memoized.entry.metadata,
87
- data: memoized.data,
88
- integrity: memoized.entry.integrity,
89
- size: memoized.entry.size
90
- }
91
- }
92
- const entry = !byDigest && index.find.sync(cache, key, opts)
93
- if (!entry && !byDigest) {
94
- throw new index.NotFoundError(cache, key)
95
- }
96
- const data = read.sync(cache, byDigest ? key : entry.integrity, {
97
- integrity: integrity,
98
- size: size
99
- })
100
- const res = byDigest
101
- ? data
102
- : {
103
- metadata: entry.metadata,
104
- data: data,
105
- size: entry.size,
106
- integrity: entry.integrity
107
- }
108
- if (memoize && byDigest) {
109
- memo.put.byDigest(cache, key, res, opts)
110
- } else if (memoize) {
111
- memo.put(cache, entry, res.data, opts)
112
- }
113
- return res
114
- }
115
-
116
- module.exports.stream = getStream
117
-
118
- const getMemoizedStream = (memoized) => {
119
- const stream = new Minipass()
120
- stream.on('newListener', function (ev, cb) {
121
- ev === 'metadata' && cb(memoized.entry.metadata)
122
- ev === 'integrity' && cb(memoized.entry.integrity)
123
- ev === 'size' && cb(memoized.entry.size)
124
- })
125
- stream.end(memoized.data)
126
- return stream
127
- }
128
-
129
- function getStream (cache, key, opts = {}) {
130
- const { memoize, size } = opts
131
- const memoized = memo.get(cache, key, opts)
132
- if (memoized && memoize !== false) {
133
- return getMemoizedStream(memoized)
134
- }
135
-
136
- const stream = new Pipeline()
137
- index
138
- .find(cache, key)
139
- .then((entry) => {
140
- if (!entry) {
141
- throw new index.NotFoundError(cache, key)
142
- }
143
- stream.emit('metadata', entry.metadata)
144
- stream.emit('integrity', entry.integrity)
145
- stream.emit('size', entry.size)
146
- stream.on('newListener', function (ev, cb) {
147
- ev === 'metadata' && cb(entry.metadata)
148
- ev === 'integrity' && cb(entry.integrity)
149
- ev === 'size' && cb(entry.size)
150
- })
151
-
152
- const src = read.readStream(
153
- cache,
154
- entry.integrity,
155
- { ...opts, size: typeof size !== 'number' ? entry.size : size }
156
- )
157
-
158
- if (memoize) {
159
- const memoStream = new Collect.PassThrough()
160
- memoStream.on('collect', data => memo.put(cache, entry, data, opts))
161
- stream.unshift(memoStream)
162
- }
163
- stream.unshift(src)
164
- })
165
- .catch((err) => stream.emit('error', err))
166
-
167
- return stream
168
- }
169
-
170
- module.exports.stream.byDigest = getStreamDigest
171
-
172
- function getStreamDigest (cache, integrity, opts = {}) {
173
- const { memoize } = opts
174
- const memoized = memo.get.byDigest(cache, integrity, opts)
175
- if (memoized && memoize !== false) {
176
- const stream = new Minipass()
177
- stream.end(memoized)
178
- return stream
179
- } else {
180
- const stream = read.readStream(cache, integrity, opts)
181
- if (!memoize) {
182
- return stream
183
- }
184
- const memoStream = new Collect.PassThrough()
185
- memoStream.on('collect', data => memo.put.byDigest(
186
- cache,
187
- integrity,
188
- data,
189
- opts
190
- ))
191
- return new Pipeline(stream, memoStream)
192
- }
193
- }
194
-
195
- module.exports.info = info
196
-
197
- function info (cache, key, opts = {}) {
198
- const { memoize } = opts
199
- const memoized = memo.get(cache, key, opts)
200
- if (memoized && memoize !== false) {
201
- return Promise.resolve(memoized.entry)
202
- } else {
203
- return index.find(cache, key)
204
- }
205
- }
206
-
207
- module.exports.hasContent = read.hasContent
208
-
209
- function cp (cache, key, dest, opts) {
210
- return copy(false, cache, key, dest, opts)
211
- }
212
-
213
- module.exports.copy = cp
214
-
215
- function cpDigest (cache, digest, dest, opts) {
216
- return copy(true, cache, digest, dest, opts)
217
- }
218
-
219
- module.exports.copy.byDigest = cpDigest
220
-
221
- function copy (byDigest, cache, key, dest, opts = {}) {
222
- if (read.copy) {
223
- return (byDigest
224
- ? Promise.resolve(null)
225
- : index.find(cache, key, opts)
226
- ).then((entry) => {
227
- if (!entry && !byDigest) {
228
- throw new index.NotFoundError(cache, key)
229
- }
230
- return read
231
- .copy(cache, byDigest ? key : entry.integrity, dest, opts)
232
- .then(() => {
233
- return byDigest
234
- ? key
235
- : {
236
- metadata: entry.metadata,
237
- size: entry.size,
238
- integrity: entry.integrity
239
- }
240
- })
241
- })
242
- }
243
-
244
- return getData(byDigest, cache, key, opts).then((res) => {
245
- return writeFile(dest, byDigest ? res : res.data).then(() => {
246
- return byDigest
247
- ? key
248
- : {
249
- metadata: res.metadata,
250
- size: res.size,
251
- integrity: res.integrity
252
- }
253
- })
254
- })
255
- }
package/ls.js DELETED
@@ -1,6 +0,0 @@
1
- 'use strict'
2
-
3
- const index = require('./lib/entry-index')
4
-
5
- module.exports = index.ls
6
- module.exports.stream = index.lsStream
package/verify.js DELETED
@@ -1,3 +0,0 @@
1
- 'use strict'
2
-
3
- module.exports = require('./lib/verify')