cacache 16.0.7 → 16.1.2

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.
@@ -2,7 +2,7 @@
2
2
 
3
3
  const util = require('util')
4
4
  const crypto = require('crypto')
5
- const fs = require('fs')
5
+ const fs = require('@npmcli/fs')
6
6
  const Minipass = require('minipass')
7
7
  const path = require('path')
8
8
  const ssri = require('ssri')
@@ -17,11 +17,6 @@ const _rimraf = require('rimraf')
17
17
  const rimraf = util.promisify(_rimraf)
18
18
  rimraf.sync = _rimraf.sync
19
19
 
20
- const appendFile = util.promisify(fs.appendFile)
21
- const readFile = util.promisify(fs.readFile)
22
- const readdir = util.promisify(fs.readdir)
23
- const writeFile = util.promisify(fs.writeFile)
24
-
25
20
  module.exports.NotFoundError = class NotFoundError extends Error {
26
21
  constructor (cache, key) {
27
22
  super(`No cache entry for ${key} found in ${cache}`)
@@ -85,7 +80,7 @@ async function compact (cache, key, matchFn, opts = {}) {
85
80
  }
86
81
 
87
82
  const write = async (tmp) => {
88
- await writeFile(tmp.target, newIndex, { flag: 'wx' })
83
+ await fs.writeFile(tmp.target, newIndex, { flag: 'wx' })
89
84
  await fixOwner.mkdirfix(cache, path.dirname(bucket))
90
85
  // we use @npmcli/move-file directly here because we
91
86
  // want to overwrite the existing file
@@ -118,7 +113,7 @@ async function compact (cache, key, matchFn, opts = {}) {
118
113
 
119
114
  module.exports.insert = insert
120
115
 
121
- function insert (cache, key, integrity, opts = {}) {
116
+ async function insert (cache, key, integrity, opts = {}) {
122
117
  const { metadata, size } = opts
123
118
  const bucket = bucketPath(cache, key)
124
119
  const entry = {
@@ -128,36 +123,32 @@ function insert (cache, key, integrity, opts = {}) {
128
123
  size,
129
124
  metadata,
130
125
  }
131
- return fixOwner
132
- .mkdirfix(cache, path.dirname(bucket))
133
- .then(() => {
134
- const stringified = JSON.stringify(entry)
135
- // NOTE - Cleverness ahoy!
136
- //
137
- // This works because it's tremendously unlikely for an entry to corrupt
138
- // another while still preserving the string length of the JSON in
139
- // question. So, we just slap the length in there and verify it on read.
140
- //
141
- // Thanks to @isaacs for the whiteboarding session that ended up with
142
- // this.
143
- return appendFile(bucket, `\n${hashEntry(stringified)}\t${stringified}`)
144
- })
145
- .then(() => fixOwner.chownr(cache, bucket))
146
- .catch((err) => {
147
- if (err.code === 'ENOENT') {
148
- return undefined
149
- }
126
+ try {
127
+ await fixOwner.mkdirfix(cache, path.dirname(bucket))
128
+ const stringified = JSON.stringify(entry)
129
+ // NOTE - Cleverness ahoy!
130
+ //
131
+ // This works because it's tremendously unlikely for an entry to corrupt
132
+ // another while still preserving the string length of the JSON in
133
+ // question. So, we just slap the length in there and verify it on read.
134
+ //
135
+ // Thanks to @isaacs for the whiteboarding session that ended up with
136
+ // this.
137
+ await fs.appendFile(bucket, `\n${hashEntry(stringified)}\t${stringified}`)
138
+ await fixOwner.chownr(cache, bucket)
139
+ } catch (err) {
140
+ if (err.code === 'ENOENT') {
141
+ return undefined
142
+ }
150
143
 
151
- throw err
152
- // There's a class of race conditions that happen when things get deleted
153
- // during fixOwner, or between the two mkdirfix/chownr calls.
154
- //
155
- // It's perfectly fine to just not bother in those cases and lie
156
- // that the index entry was written. Because it's a cache.
157
- })
158
- .then(() => {
159
- return formatEntry(cache, entry)
160
- })
144
+ throw err
145
+ // There's a class of race conditions that happen when things get deleted
146
+ // during fixOwner, or between the two mkdirfix/chownr calls.
147
+ //
148
+ // It's perfectly fine to just not bother in those cases and lie
149
+ // that the index entry was written. Because it's a cache.
150
+ }
151
+ return formatEntry(cache, entry)
161
152
  }
162
153
 
163
154
  module.exports.insert.sync = insertSync
@@ -187,25 +178,24 @@ function insertSync (cache, key, integrity, opts = {}) {
187
178
 
188
179
  module.exports.find = find
189
180
 
190
- function find (cache, key) {
181
+ async function find (cache, key) {
191
182
  const bucket = bucketPath(cache, key)
192
- return bucketEntries(bucket)
193
- .then((entries) => {
194
- return entries.reduce((latest, next) => {
195
- if (next && next.key === key) {
196
- return formatEntry(cache, next)
197
- } else {
198
- return latest
199
- }
200
- }, null)
201
- })
202
- .catch((err) => {
203
- if (err.code === 'ENOENT') {
204
- return null
183
+ try {
184
+ const entries = await bucketEntries(bucket)
185
+ return entries.reduce((latest, next) => {
186
+ if (next && next.key === key) {
187
+ return formatEntry(cache, next)
205
188
  } else {
206
- throw err
189
+ return latest
207
190
  }
208
- })
191
+ }, null)
192
+ } catch (err) {
193
+ if (err.code === 'ENOENT') {
194
+ return null
195
+ } else {
196
+ throw err
197
+ }
198
+ }
209
199
  }
210
200
 
211
201
  module.exports.find.sync = findSync
@@ -257,67 +247,65 @@ function lsStream (cache) {
257
247
  const indexDir = bucketDir(cache)
258
248
  const stream = new Minipass({ objectMode: true })
259
249
 
260
- readdirOrEmpty(indexDir).then(buckets => Promise.all(
261
- buckets.map(bucket => {
250
+ // Set all this up to run on the stream and then just return the stream
251
+ Promise.resolve().then(async () => {
252
+ const buckets = await readdirOrEmpty(indexDir)
253
+ await Promise.all(buckets.map(async (bucket) => {
262
254
  const bucketPath = path.join(indexDir, bucket)
263
- return readdirOrEmpty(bucketPath).then(subbuckets => Promise.all(
264
- subbuckets.map(subbucket => {
265
- const subbucketPath = path.join(bucketPath, subbucket)
266
-
267
- // "/cachename/<bucket 0xFF>/<bucket 0xFF>./*"
268
- return readdirOrEmpty(subbucketPath).then(entries => Promise.all(
269
- entries.map(entry => {
270
- const entryPath = path.join(subbucketPath, entry)
271
- return bucketEntries(entryPath).then(entries =>
272
- // using a Map here prevents duplicate keys from
273
- // showing up twice, I guess?
274
- entries.reduce((acc, entry) => {
275
- acc.set(entry.key, entry)
276
- return acc
277
- }, new Map())
278
- ).then(reduced => {
279
- // reduced is a map of key => entry
280
- for (const entry of reduced.values()) {
281
- const formatted = formatEntry(cache, entry)
282
- if (formatted) {
283
- stream.write(formatted)
284
- }
285
- }
286
- }).catch(err => {
287
- if (err.code === 'ENOENT') {
288
- return undefined
289
- }
290
- throw err
291
- })
292
- })
293
- ))
294
- })
295
- ))
296
- })
297
- ))
298
- .then(
299
- () => stream.end(),
300
- err => stream.emit('error', err)
301
- )
255
+ const subbuckets = await readdirOrEmpty(bucketPath)
256
+ await Promise.all(subbuckets.map(async (subbucket) => {
257
+ const subbucketPath = path.join(bucketPath, subbucket)
258
+
259
+ // "/cachename/<bucket 0xFF>/<bucket 0xFF>./*"
260
+ const subbucketEntries = await readdirOrEmpty(subbucketPath)
261
+ await Promise.all(subbucketEntries.map(async (entry) => {
262
+ const entryPath = path.join(subbucketPath, entry)
263
+ try {
264
+ const entries = await bucketEntries(entryPath)
265
+ // using a Map here prevents duplicate keys from showing up
266
+ // twice, I guess?
267
+ const reduced = entries.reduce((acc, entry) => {
268
+ acc.set(entry.key, entry)
269
+ return acc
270
+ }, new Map())
271
+ // reduced is a map of key => entry
272
+ for (const entry of reduced.values()) {
273
+ const formatted = formatEntry(cache, entry)
274
+ if (formatted) {
275
+ stream.write(formatted)
276
+ }
277
+ }
278
+ } catch (err) {
279
+ if (err.code === 'ENOENT') {
280
+ return undefined
281
+ }
282
+ throw err
283
+ }
284
+ }))
285
+ }))
286
+ }))
287
+ stream.end()
288
+ return stream
289
+ }).catch(err => stream.emit('error', err))
302
290
 
303
291
  return stream
304
292
  }
305
293
 
306
294
  module.exports.ls = ls
307
295
 
308
- function ls (cache) {
309
- return lsStream(cache).collect().then(entries =>
310
- entries.reduce((acc, xs) => {
311
- acc[xs.key] = xs
312
- return acc
313
- }, {})
314
- )
296
+ async function ls (cache) {
297
+ const entries = await lsStream(cache).collect()
298
+ return entries.reduce((acc, xs) => {
299
+ acc[xs.key] = xs
300
+ return acc
301
+ }, {})
315
302
  }
316
303
 
317
304
  module.exports.bucketEntries = bucketEntries
318
305
 
319
- function bucketEntries (bucket, filter) {
320
- return readFile(bucket, 'utf8').then((data) => _bucketEntries(data, filter))
306
+ async function bucketEntries (bucket, filter) {
307
+ const data = await fs.readFile(bucket, 'utf8')
308
+ return _bucketEntries(data, filter)
321
309
  }
322
310
 
323
311
  module.exports.bucketEntries.sync = bucketEntriesSync
@@ -406,7 +394,7 @@ function formatEntry (cache, entry, keepAll) {
406
394
  }
407
395
 
408
396
  function readdirOrEmpty (dir) {
409
- return readdir(dir).catch((err) => {
397
+ return fs.readdir(dir).catch((err) => {
410
398
  if (err.code === 'ENOENT' || err.code === 'ENOTDIR') {
411
399
  return []
412
400
  }
package/lib/get.js CHANGED
@@ -8,52 +8,48 @@ const index = require('./entry-index')
8
8
  const memo = require('./memoization')
9
9
  const read = require('./content/read')
10
10
 
11
- function getData (cache, key, opts = {}) {
11
+ async function getData (cache, key, opts = {}) {
12
12
  const { integrity, memoize, size } = opts
13
13
  const memoized = memo.get(cache, key, opts)
14
14
  if (memoized && memoize !== false) {
15
- return Promise.resolve({
15
+ return {
16
16
  metadata: memoized.entry.metadata,
17
17
  data: memoized.data,
18
18
  integrity: memoized.entry.integrity,
19
19
  size: memoized.entry.size,
20
- })
20
+ }
21
21
  }
22
22
 
23
- return index.find(cache, key, opts).then((entry) => {
24
- if (!entry) {
25
- throw new index.NotFoundError(cache, key)
26
- }
23
+ const entry = await index.find(cache, key, opts)
24
+ if (!entry) {
25
+ throw new index.NotFoundError(cache, key)
26
+ }
27
+ const data = await read(cache, entry.integrity, { integrity, size })
28
+ if (memoize) {
29
+ memo.put(cache, entry, data, opts)
30
+ }
27
31
 
28
- return read(cache, entry.integrity, { integrity, size }).then((data) => {
29
- if (memoize) {
30
- memo.put(cache, entry, data, opts)
31
- }
32
-
33
- return {
34
- data,
35
- metadata: entry.metadata,
36
- size: entry.size,
37
- integrity: entry.integrity,
38
- }
39
- })
40
- })
32
+ return {
33
+ data,
34
+ metadata: entry.metadata,
35
+ size: entry.size,
36
+ integrity: entry.integrity,
37
+ }
41
38
  }
42
39
  module.exports = getData
43
40
 
44
- function getDataByDigest (cache, key, opts = {}) {
41
+ async function getDataByDigest (cache, key, opts = {}) {
45
42
  const { integrity, memoize, size } = opts
46
43
  const memoized = memo.get.byDigest(cache, key, opts)
47
44
  if (memoized && memoize !== false) {
48
- return Promise.resolve(memoized)
45
+ return memoized
49
46
  }
50
47
 
51
- return read(cache, key, { integrity, size }).then((res) => {
52
- if (memoize) {
53
- memo.put.byDigest(cache, key, res, opts)
54
- }
55
- return res
56
- })
48
+ const res = await read(cache, key, { integrity, size })
49
+ if (memoize) {
50
+ memo.put.byDigest(cache, key, res, opts)
51
+ }
52
+ return res
57
53
  }
58
54
  module.exports.byDigest = getDataByDigest
59
55
 
@@ -131,36 +127,36 @@ function getStream (cache, key, opts = {}) {
131
127
  }
132
128
 
133
129
  const stream = new Pipeline()
134
- index
135
- .find(cache, key)
136
- .then((entry) => {
137
- if (!entry) {
138
- throw new index.NotFoundError(cache, key)
139
- }
140
-
141
- stream.emit('metadata', entry.metadata)
142
- stream.emit('integrity', entry.integrity)
143
- stream.emit('size', entry.size)
144
- stream.on('newListener', function (ev, cb) {
145
- ev === 'metadata' && cb(entry.metadata)
146
- ev === 'integrity' && cb(entry.integrity)
147
- ev === 'size' && cb(entry.size)
148
- })
149
-
150
- const src = read.readStream(
151
- cache,
152
- entry.integrity,
153
- { ...opts, size: typeof size !== 'number' ? entry.size : size }
154
- )
155
-
156
- if (memoize) {
157
- const memoStream = new Collect.PassThrough()
158
- memoStream.on('collect', data => memo.put(cache, entry, data, opts))
159
- stream.unshift(memoStream)
160
- }
161
- stream.unshift(src)
130
+ // Set all this up to run on the stream and then just return the stream
131
+ Promise.resolve().then(async () => {
132
+ const entry = await index.find(cache, key)
133
+ if (!entry) {
134
+ throw new index.NotFoundError(cache, key)
135
+ }
136
+
137
+ stream.emit('metadata', entry.metadata)
138
+ stream.emit('integrity', entry.integrity)
139
+ stream.emit('size', entry.size)
140
+ stream.on('newListener', function (ev, cb) {
141
+ ev === 'metadata' && cb(entry.metadata)
142
+ ev === 'integrity' && cb(entry.integrity)
143
+ ev === 'size' && cb(entry.size)
162
144
  })
163
- .catch((err) => stream.emit('error', err))
145
+
146
+ const src = read.readStream(
147
+ cache,
148
+ entry.integrity,
149
+ { ...opts, size: typeof size !== 'number' ? entry.size : size }
150
+ )
151
+
152
+ if (memoize) {
153
+ const memoStream = new Collect.PassThrough()
154
+ memoStream.on('collect', data => memo.put(cache, entry, data, opts))
155
+ stream.unshift(memoStream)
156
+ }
157
+ stream.unshift(src)
158
+ return stream
159
+ }).catch((err) => stream.emit('error', err))
164
160
 
165
161
  return stream
166
162
  }
@@ -204,27 +200,26 @@ function info (cache, key, opts = {}) {
204
200
  }
205
201
  module.exports.info = info
206
202
 
207
- function copy (cache, key, dest, opts = {}) {
208
- return index.find(cache, key, opts).then((entry) => {
209
- if (!entry) {
210
- throw new index.NotFoundError(cache, key)
211
- }
212
- return read.copy(cache, entry.integrity, dest, opts)
213
- .then(() => {
214
- return {
215
- metadata: entry.metadata,
216
- size: entry.size,
217
- integrity: entry.integrity,
218
- }
219
- })
220
- })
203
+ async function copy (cache, key, dest, opts = {}) {
204
+ const entry = await index.find(cache, key, opts)
205
+ if (!entry) {
206
+ throw new index.NotFoundError(cache, key)
207
+ }
208
+ await read.copy(cache, entry.integrity, dest, opts)
209
+ return {
210
+ metadata: entry.metadata,
211
+ size: entry.size,
212
+ integrity: entry.integrity,
213
+ }
221
214
  }
222
215
 
223
216
  module.exports.copy = copy
224
217
 
225
- function copyByDigest (cache, key, dest, opts = {}) {
226
- return read.copy(cache, key, dest, opts).then(() => key)
218
+ async function copyByDigest (cache, key, dest, opts = {}) {
219
+ await read.copy(cache, key, dest, opts)
220
+ return key
227
221
  }
222
+
228
223
  module.exports.copy.byDigest = copyByDigest
229
224
 
230
225
  module.exports.hasContent = read.hasContent
package/lib/put.js CHANGED
@@ -14,20 +14,16 @@ const putOpts = (opts) => ({
14
14
 
15
15
  module.exports = putData
16
16
 
17
- function putData (cache, key, data, opts = {}) {
17
+ async function putData (cache, key, data, opts = {}) {
18
18
  const { memoize } = opts
19
19
  opts = putOpts(opts)
20
- return write(cache, data, opts).then((res) => {
21
- return index
22
- .insert(cache, key, res.integrity, { ...opts, size: res.size })
23
- .then((entry) => {
24
- if (memoize) {
25
- memo.put(cache, entry, data, opts)
26
- }
20
+ const res = await write(cache, data, opts)
21
+ const entry = await index.insert(cache, key, res.integrity, { ...opts, size: res.size })
22
+ if (memoize) {
23
+ memo.put(cache, entry, data, opts)
24
+ }
27
25
 
28
- return res.integrity
29
- })
30
- })
26
+ return res.integrity
31
27
  }
32
28
 
33
29
  module.exports.stream = putStream
@@ -68,17 +64,14 @@ function putStream (cache, key, opts = {}) {
68
64
  // last but not least, we write the index and emit hash and size,
69
65
  // and memoize if we're doing that
70
66
  pipeline.push(new Flush({
71
- flush () {
67
+ async flush () {
72
68
  if (!error) {
73
- return index
74
- .insert(cache, key, integrity, { ...opts, size })
75
- .then((entry) => {
76
- if (memoize && memoData) {
77
- memo.put(cache, entry, memoData, opts)
78
- }
79
- pipeline.emit('integrity', integrity)
80
- pipeline.emit('size', size)
81
- })
69
+ const entry = await index.insert(cache, key, integrity, { ...opts, size })
70
+ if (memoize && memoData) {
71
+ memo.put(cache, entry, memoData, opts)
72
+ }
73
+ pipeline.emit('integrity', integrity)
74
+ pipeline.emit('size', size)
82
75
  }
83
76
  },
84
77
  }))
@@ -33,40 +33,38 @@ const getSelf = () => {
33
33
 
34
34
  module.exports.chownr = fixOwner
35
35
 
36
- function fixOwner (cache, filepath) {
36
+ async function fixOwner (cache, filepath) {
37
37
  if (!process.getuid) {
38
38
  // This platform doesn't need ownership fixing
39
- return Promise.resolve()
39
+ return
40
40
  }
41
41
 
42
42
  getSelf()
43
43
  if (self.uid !== 0) {
44
44
  // almost certainly can't chown anyway
45
- return Promise.resolve()
45
+ return
46
46
  }
47
47
 
48
- return Promise.resolve(inferOwner(cache)).then((owner) => {
49
- const { uid, gid } = owner
48
+ const { uid, gid } = await inferOwner(cache)
50
49
 
51
- // No need to override if it's already what we used.
52
- if (self.uid === uid && self.gid === gid) {
53
- return
54
- }
50
+ // No need to override if it's already what we used.
51
+ if (self.uid === uid && self.gid === gid) {
52
+ return
53
+ }
55
54
 
56
- return inflight('fixOwner: fixing ownership on ' + filepath, () =>
57
- chownr(
58
- filepath,
59
- typeof uid === 'number' ? uid : self.uid,
60
- typeof gid === 'number' ? gid : self.gid
61
- ).catch((err) => {
62
- if (err.code === 'ENOENT') {
63
- return null
64
- }
65
-
66
- throw err
67
- })
68
- )
69
- })
55
+ return inflight('fixOwner: fixing ownership on ' + filepath, () =>
56
+ chownr(
57
+ filepath,
58
+ typeof uid === 'number' ? uid : self.uid,
59
+ typeof gid === 'number' ? gid : self.gid
60
+ ).catch((err) => {
61
+ if (err.code === 'ENOENT') {
62
+ return null
63
+ }
64
+
65
+ throw err
66
+ })
67
+ )
70
68
  }
71
69
 
72
70
  module.exports.chownr.sync = fixOwnerSync
@@ -105,26 +103,25 @@ function fixOwnerSync (cache, filepath) {
105
103
 
106
104
  module.exports.mkdirfix = mkdirfix
107
105
 
108
- function mkdirfix (cache, p, cb) {
106
+ async function mkdirfix (cache, p, cb) {
109
107
  // we have to infer the owner _before_ making the directory, even though
110
108
  // we aren't going to use the results, since the cache itself might not
111
109
  // exist yet. If we mkdirp it, then our current uid/gid will be assumed
112
110
  // to be correct if it creates the cache folder in the process.
113
- return Promise.resolve(inferOwner(cache)).then(() => {
114
- return mkdirp(p)
115
- .then((made) => {
116
- if (made) {
117
- return fixOwner(cache, made).then(() => made)
118
- }
119
- })
120
- .catch((err) => {
121
- if (err.code === 'EEXIST') {
122
- return fixOwner(cache, p).then(() => null)
123
- }
124
-
125
- throw err
126
- })
127
- })
111
+ await inferOwner(cache)
112
+ try {
113
+ const made = await mkdirp(p)
114
+ if (made) {
115
+ await fixOwner(cache, made)
116
+ return made
117
+ }
118
+ } catch (err) {
119
+ if (err.code === 'EEXIST') {
120
+ await fixOwner(cache, p)
121
+ return null
122
+ }
123
+ throw err
124
+ }
128
125
  }
129
126
 
130
127
  module.exports.mkdirfix.sync = mkdirfixSync