cacache 16.0.6 → 16.1.1
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 +13 -0
- package/lib/content/read.js +77 -95
- package/lib/content/rm.js +9 -9
- package/lib/content/write.js +57 -64
- package/lib/entry-index.js +97 -106
- package/lib/get.js +67 -94
- package/lib/put.js +20 -27
- package/lib/util/fix-owner.js +36 -39
- package/lib/util/move-file.js +35 -48
- package/lib/util/tmp.js +5 -7
- package/lib/verify.js +156 -192
- package/package.json +3 -12
- package/lib/util/disposer.js +0 -31
package/lib/util/move-file.js
CHANGED
|
@@ -1,18 +1,13 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const fs = require('fs')
|
|
4
|
-
const util = require('util')
|
|
5
|
-
const chmod = util.promisify(fs.chmod)
|
|
6
|
-
const unlink = util.promisify(fs.unlink)
|
|
7
|
-
const stat = util.promisify(fs.stat)
|
|
3
|
+
const fs = require('@npmcli/fs')
|
|
8
4
|
const move = require('@npmcli/move-file')
|
|
9
5
|
const pinflight = require('promise-inflight')
|
|
10
6
|
|
|
11
7
|
module.exports = moveFile
|
|
12
8
|
|
|
13
|
-
function moveFile (src, dest) {
|
|
14
|
-
const isWindows =
|
|
15
|
-
process.platform === 'win32'
|
|
9
|
+
async function moveFile (src, dest) {
|
|
10
|
+
const isWindows = process.platform === 'win32'
|
|
16
11
|
|
|
17
12
|
// This isn't quite an fs.rename -- the assumption is that
|
|
18
13
|
// if `dest` already exists, and we get certain errors while
|
|
@@ -23,47 +18,39 @@ function moveFile (src, dest) {
|
|
|
23
18
|
// content their own way.
|
|
24
19
|
//
|
|
25
20
|
// Note that, as the name suggests, this strictly only supports file moves.
|
|
26
|
-
|
|
27
|
-
fs.link(src, dest
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
21
|
+
try {
|
|
22
|
+
await fs.link(src, dest)
|
|
23
|
+
} catch (err) {
|
|
24
|
+
if (isWindows && err.code === 'EPERM') {
|
|
25
|
+
// XXX This is a really weird way to handle this situation, as it
|
|
26
|
+
// results in the src file being deleted even though the dest
|
|
27
|
+
// might not exist. Since we pretty much always write files to
|
|
28
|
+
// deterministic locations based on content hash, this is likely
|
|
29
|
+
// ok (or at worst, just ends in a future cache miss). But it would
|
|
30
|
+
// be worth investigating at some time in the future if this is
|
|
31
|
+
// really what we want to do here.
|
|
32
|
+
} else if (err.code === 'EEXIST' || err.code === 'EBUSY') {
|
|
33
|
+
// file already exists, so whatever
|
|
34
|
+
} else {
|
|
35
|
+
throw err
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
try {
|
|
39
|
+
await Promise.all([
|
|
40
|
+
fs.unlink(src),
|
|
41
|
+
!isWindows && fs.chmod(dest, '0444'),
|
|
42
|
+
])
|
|
43
|
+
} catch (e) {
|
|
44
|
+
return pinflight('cacache-move-file:' + dest, async () => {
|
|
45
|
+
await fs.stat(dest).catch((err) => {
|
|
46
|
+
if (err.code !== 'ENOENT') {
|
|
47
|
+
// Something else is wrong here. Bail bail bail
|
|
48
|
+
throw err
|
|
43
49
|
}
|
|
44
|
-
} else {
|
|
45
|
-
return resolve()
|
|
46
|
-
}
|
|
47
|
-
})
|
|
48
|
-
})
|
|
49
|
-
.then(() => {
|
|
50
|
-
// content should never change for any reason, so make it read-only
|
|
51
|
-
return Promise.all([
|
|
52
|
-
unlink(src),
|
|
53
|
-
!isWindows && chmod(dest, '0444'),
|
|
54
|
-
])
|
|
55
|
-
})
|
|
56
|
-
.catch(() => {
|
|
57
|
-
return pinflight('cacache-move-file:' + dest, () => {
|
|
58
|
-
return stat(dest).catch((err) => {
|
|
59
|
-
if (err.code !== 'ENOENT') {
|
|
60
|
-
// Something else is wrong here. Bail bail bail
|
|
61
|
-
throw err
|
|
62
|
-
}
|
|
63
|
-
// file doesn't already exist! let's try a rename -> copy fallback
|
|
64
|
-
// only delete if it successfully copies
|
|
65
|
-
return move(src, dest)
|
|
66
|
-
})
|
|
67
50
|
})
|
|
51
|
+
// file doesn't already exist! let's try a rename -> copy fallback
|
|
52
|
+
// only delete if it successfully copies
|
|
53
|
+
return move(src, dest)
|
|
68
54
|
})
|
|
55
|
+
}
|
|
69
56
|
}
|
package/lib/util/tmp.js
CHANGED
|
@@ -7,15 +7,13 @@ const path = require('path')
|
|
|
7
7
|
|
|
8
8
|
module.exports.mkdir = mktmpdir
|
|
9
9
|
|
|
10
|
-
function mktmpdir (cache, opts = {}) {
|
|
10
|
+
async function mktmpdir (cache, opts = {}) {
|
|
11
11
|
const { tmpPrefix } = opts
|
|
12
12
|
const tmpDir = path.join(cache, 'tmp')
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
return fs.mkdtemp(target, { owner: 'inherit' })
|
|
18
|
-
})
|
|
13
|
+
await fs.mkdir(tmpDir, { recursive: true, owner: 'inherit' })
|
|
14
|
+
// do not use path.join(), it drops the trailing / if tmpPrefix is unset
|
|
15
|
+
const target = `${tmpDir}${path.sep}${tmpPrefix || ''}`
|
|
16
|
+
return fs.mkdtemp(target, { owner: 'inherit' })
|
|
19
17
|
}
|
|
20
18
|
|
|
21
19
|
module.exports.withTmp = withTmp
|
package/lib/verify.js
CHANGED
|
@@ -5,7 +5,7 @@ const util = require('util')
|
|
|
5
5
|
const pMap = require('p-map')
|
|
6
6
|
const contentPath = require('./content/path')
|
|
7
7
|
const fixOwner = require('./util/fix-owner')
|
|
8
|
-
const fs = require('fs')
|
|
8
|
+
const fs = require('@npmcli/fs')
|
|
9
9
|
const fsm = require('fs-minipass')
|
|
10
10
|
const glob = util.promisify(require('glob'))
|
|
11
11
|
const index = require('./entry-index')
|
|
@@ -18,11 +18,6 @@ const globify = pattern => pattern.split('\\').join('/')
|
|
|
18
18
|
const hasOwnProperty = (obj, key) =>
|
|
19
19
|
Object.prototype.hasOwnProperty.call(obj, key)
|
|
20
20
|
|
|
21
|
-
const stat = util.promisify(fs.stat)
|
|
22
|
-
const truncate = util.promisify(fs.truncate)
|
|
23
|
-
const writeFile = util.promisify(fs.writeFile)
|
|
24
|
-
const readFile = util.promisify(fs.readFile)
|
|
25
|
-
|
|
26
21
|
const verifyOpts = (opts) => ({
|
|
27
22
|
concurrency: 20,
|
|
28
23
|
log: { silly () {} },
|
|
@@ -31,7 +26,7 @@ const verifyOpts = (opts) => ({
|
|
|
31
26
|
|
|
32
27
|
module.exports = verify
|
|
33
28
|
|
|
34
|
-
function verify (cache, opts) {
|
|
29
|
+
async function verify (cache, opts) {
|
|
35
30
|
opts = verifyOpts(opts)
|
|
36
31
|
opts.log.silly('verify', 'verifying cache at', cache)
|
|
37
32
|
|
|
@@ -45,56 +40,47 @@ function verify (cache, opts) {
|
|
|
45
40
|
markEndTime,
|
|
46
41
|
]
|
|
47
42
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
stats[k] = s[k]
|
|
57
|
-
})
|
|
58
|
-
const end = new Date()
|
|
59
|
-
if (!stats.runTime) {
|
|
60
|
-
stats.runTime = {}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
stats.runTime[label] = end - start
|
|
64
|
-
return Promise.resolve(stats)
|
|
65
|
-
})
|
|
43
|
+
const stats = {}
|
|
44
|
+
for (const step of steps) {
|
|
45
|
+
const label = step.name
|
|
46
|
+
const start = new Date()
|
|
47
|
+
const s = await step(cache, opts)
|
|
48
|
+
if (s) {
|
|
49
|
+
Object.keys(s).forEach((k) => {
|
|
50
|
+
stats[k] = s[k]
|
|
66
51
|
})
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
52
|
+
}
|
|
53
|
+
const end = new Date()
|
|
54
|
+
if (!stats.runTime) {
|
|
55
|
+
stats.runTime = {}
|
|
56
|
+
}
|
|
57
|
+
stats.runTime[label] = end - start
|
|
58
|
+
}
|
|
59
|
+
stats.runTime.total = stats.endTime - stats.startTime
|
|
60
|
+
opts.log.silly(
|
|
61
|
+
'verify',
|
|
62
|
+
'verification finished for',
|
|
63
|
+
cache,
|
|
64
|
+
'in',
|
|
65
|
+
`${stats.runTime.total}ms`
|
|
66
|
+
)
|
|
67
|
+
return stats
|
|
79
68
|
}
|
|
80
69
|
|
|
81
|
-
function markStartTime (cache, opts) {
|
|
82
|
-
return
|
|
70
|
+
async function markStartTime (cache, opts) {
|
|
71
|
+
return { startTime: new Date() }
|
|
83
72
|
}
|
|
84
73
|
|
|
85
|
-
function markEndTime (cache, opts) {
|
|
86
|
-
return
|
|
74
|
+
async function markEndTime (cache, opts) {
|
|
75
|
+
return { endTime: new Date() }
|
|
87
76
|
}
|
|
88
77
|
|
|
89
|
-
function fixPerms (cache, opts) {
|
|
78
|
+
async function fixPerms (cache, opts) {
|
|
90
79
|
opts.log.silly('verify', 'fixing cache permissions')
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
return fixOwner.chownr(cache, cache)
|
|
96
|
-
})
|
|
97
|
-
.then(() => null)
|
|
80
|
+
await fixOwner.mkdirfix(cache, cache)
|
|
81
|
+
// TODO - fix file permissions too
|
|
82
|
+
await fixOwner.chownr(cache, cache)
|
|
83
|
+
return null
|
|
98
84
|
}
|
|
99
85
|
|
|
100
86
|
// Implements a naive mark-and-sweep tracing garbage collector.
|
|
@@ -106,7 +92,7 @@ function fixPerms (cache, opts) {
|
|
|
106
92
|
// 4. If content is live, verify its checksum and delete it if it fails
|
|
107
93
|
// 5. If content is not marked as live, rimraf it.
|
|
108
94
|
//
|
|
109
|
-
function garbageCollect (cache, opts) {
|
|
95
|
+
async function garbageCollect (cache, opts) {
|
|
110
96
|
opts.log.silly('verify', 'garbage collecting content')
|
|
111
97
|
const indexStream = index.lsStream(cache)
|
|
112
98
|
const liveContent = new Set()
|
|
@@ -117,156 +103,135 @@ function garbageCollect (cache, opts) {
|
|
|
117
103
|
|
|
118
104
|
liveContent.add(entry.integrity.toString())
|
|
119
105
|
})
|
|
120
|
-
|
|
106
|
+
await new Promise((resolve, reject) => {
|
|
121
107
|
indexStream.on('end', resolve).on('error', reject)
|
|
122
|
-
}).then(() => {
|
|
123
|
-
const contentDir = contentPath.contentDir(cache)
|
|
124
|
-
return glob(globify(path.join(contentDir, '**')), {
|
|
125
|
-
follow: false,
|
|
126
|
-
nodir: true,
|
|
127
|
-
nosort: true,
|
|
128
|
-
}).then((files) => {
|
|
129
|
-
return Promise.resolve({
|
|
130
|
-
verifiedContent: 0,
|
|
131
|
-
reclaimedCount: 0,
|
|
132
|
-
reclaimedSize: 0,
|
|
133
|
-
badContentCount: 0,
|
|
134
|
-
keptSize: 0,
|
|
135
|
-
}).then((stats) =>
|
|
136
|
-
pMap(
|
|
137
|
-
files,
|
|
138
|
-
(f) => {
|
|
139
|
-
const split = f.split(/[/\\]/)
|
|
140
|
-
const digest = split.slice(split.length - 3).join('')
|
|
141
|
-
const algo = split[split.length - 4]
|
|
142
|
-
const integrity = ssri.fromHex(digest, algo)
|
|
143
|
-
if (liveContent.has(integrity.toString())) {
|
|
144
|
-
return verifyContent(f, integrity).then((info) => {
|
|
145
|
-
if (!info.valid) {
|
|
146
|
-
stats.reclaimedCount++
|
|
147
|
-
stats.badContentCount++
|
|
148
|
-
stats.reclaimedSize += info.size
|
|
149
|
-
} else {
|
|
150
|
-
stats.verifiedContent++
|
|
151
|
-
stats.keptSize += info.size
|
|
152
|
-
}
|
|
153
|
-
return stats
|
|
154
|
-
})
|
|
155
|
-
} else {
|
|
156
|
-
// No entries refer to this content. We can delete.
|
|
157
|
-
stats.reclaimedCount++
|
|
158
|
-
return stat(f).then((s) => {
|
|
159
|
-
return rimraf(f).then(() => {
|
|
160
|
-
stats.reclaimedSize += s.size
|
|
161
|
-
return stats
|
|
162
|
-
})
|
|
163
|
-
})
|
|
164
|
-
}
|
|
165
|
-
},
|
|
166
|
-
{ concurrency: opts.concurrency }
|
|
167
|
-
).then(() => stats)
|
|
168
|
-
)
|
|
169
|
-
})
|
|
170
108
|
})
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
109
|
+
const contentDir = contentPath.contentDir(cache)
|
|
110
|
+
const files = await glob(globify(path.join(contentDir, '**')), {
|
|
111
|
+
follow: false,
|
|
112
|
+
nodir: true,
|
|
113
|
+
nosort: true,
|
|
114
|
+
})
|
|
115
|
+
const stats = {
|
|
116
|
+
verifiedContent: 0,
|
|
117
|
+
reclaimedCount: 0,
|
|
118
|
+
reclaimedSize: 0,
|
|
119
|
+
badContentCount: 0,
|
|
120
|
+
keptSize: 0,
|
|
121
|
+
}
|
|
122
|
+
await pMap(
|
|
123
|
+
files,
|
|
124
|
+
async (f) => {
|
|
125
|
+
const split = f.split(/[/\\]/)
|
|
126
|
+
const digest = split.slice(split.length - 3).join('')
|
|
127
|
+
const algo = split[split.length - 4]
|
|
128
|
+
const integrity = ssri.fromHex(digest, algo)
|
|
129
|
+
if (liveContent.has(integrity.toString())) {
|
|
130
|
+
const info = await verifyContent(f, integrity)
|
|
131
|
+
if (!info.valid) {
|
|
132
|
+
stats.reclaimedCount++
|
|
133
|
+
stats.badContentCount++
|
|
134
|
+
stats.reclaimedSize += info.size
|
|
135
|
+
} else {
|
|
136
|
+
stats.verifiedContent++
|
|
137
|
+
stats.keptSize += info.size
|
|
138
|
+
}
|
|
139
|
+
} else {
|
|
140
|
+
// No entries refer to this content. We can delete.
|
|
141
|
+
stats.reclaimedCount++
|
|
142
|
+
const s = await fs.stat(f)
|
|
143
|
+
await rimraf(f)
|
|
144
|
+
stats.reclaimedSize += s.size
|
|
196
145
|
}
|
|
146
|
+
return stats
|
|
147
|
+
},
|
|
148
|
+
{ concurrency: opts.concurrency }
|
|
149
|
+
)
|
|
150
|
+
return stats
|
|
151
|
+
}
|
|
197
152
|
|
|
153
|
+
async function verifyContent (filepath, sri) {
|
|
154
|
+
const contentInfo = {}
|
|
155
|
+
try {
|
|
156
|
+
const { size } = await fs.stat(filepath)
|
|
157
|
+
contentInfo.size = size
|
|
158
|
+
contentInfo.valid = true
|
|
159
|
+
await ssri.checkStream(new fsm.ReadStream(filepath), sri)
|
|
160
|
+
} catch (err) {
|
|
161
|
+
if (err.code === 'ENOENT') {
|
|
162
|
+
return { size: 0, valid: false }
|
|
163
|
+
}
|
|
164
|
+
if (err.code !== 'EINTEGRITY') {
|
|
198
165
|
throw err
|
|
199
|
-
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
await rimraf(filepath)
|
|
169
|
+
contentInfo.valid = false
|
|
170
|
+
}
|
|
171
|
+
return contentInfo
|
|
200
172
|
}
|
|
201
173
|
|
|
202
|
-
function rebuildIndex (cache, opts) {
|
|
174
|
+
async function rebuildIndex (cache, opts) {
|
|
203
175
|
opts.log.silly('verify', 'rebuilding index')
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
}
|
|
176
|
+
const entries = await index.ls(cache)
|
|
177
|
+
const stats = {
|
|
178
|
+
missingContent: 0,
|
|
179
|
+
rejectedEntries: 0,
|
|
180
|
+
totalEntries: 0,
|
|
181
|
+
}
|
|
182
|
+
const buckets = {}
|
|
183
|
+
for (const k in entries) {
|
|
184
|
+
/* istanbul ignore else */
|
|
185
|
+
if (hasOwnProperty(entries, k)) {
|
|
186
|
+
const hashed = index.hashKey(k)
|
|
187
|
+
const entry = entries[k]
|
|
188
|
+
const excluded = opts.filter && !opts.filter(entry)
|
|
189
|
+
excluded && stats.rejectedEntries++
|
|
190
|
+
if (buckets[hashed] && !excluded) {
|
|
191
|
+
buckets[hashed].push(entry)
|
|
192
|
+
} else if (buckets[hashed] && excluded) {
|
|
193
|
+
// skip
|
|
194
|
+
} else if (excluded) {
|
|
195
|
+
buckets[hashed] = []
|
|
196
|
+
buckets[hashed]._path = index.bucketPath(cache, k)
|
|
197
|
+
} else {
|
|
198
|
+
buckets[hashed] = [entry]
|
|
199
|
+
buckets[hashed]._path = index.bucketPath(cache, k)
|
|
229
200
|
}
|
|
230
201
|
}
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
202
|
+
}
|
|
203
|
+
await pMap(
|
|
204
|
+
Object.keys(buckets),
|
|
205
|
+
(key) => {
|
|
206
|
+
return rebuildBucket(cache, buckets[key], stats, opts)
|
|
207
|
+
},
|
|
208
|
+
{ concurrency: opts.concurrency }
|
|
209
|
+
)
|
|
210
|
+
return stats
|
|
239
211
|
}
|
|
240
212
|
|
|
241
|
-
function rebuildBucket (cache, bucket, stats, opts) {
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
metadata: entry.metadata,
|
|
253
|
-
size: entry.size,
|
|
254
|
-
})
|
|
255
|
-
.then(() => {
|
|
256
|
-
stats.totalEntries++
|
|
257
|
-
})
|
|
258
|
-
})
|
|
259
|
-
.catch((err) => {
|
|
260
|
-
if (err.code === 'ENOENT') {
|
|
261
|
-
stats.rejectedEntries++
|
|
262
|
-
stats.missingContent++
|
|
263
|
-
return
|
|
264
|
-
}
|
|
265
|
-
throw err
|
|
266
|
-
})
|
|
213
|
+
async function rebuildBucket (cache, bucket, stats, opts) {
|
|
214
|
+
await fs.truncate(bucket._path)
|
|
215
|
+
// This needs to be serialized because cacache explicitly
|
|
216
|
+
// lets very racy bucket conflicts clobber each other.
|
|
217
|
+
for (const entry of bucket) {
|
|
218
|
+
const content = contentPath(cache, entry.integrity)
|
|
219
|
+
try {
|
|
220
|
+
await fs.stat(content)
|
|
221
|
+
await index.insert(cache, entry.key, entry.integrity, {
|
|
222
|
+
metadata: entry.metadata,
|
|
223
|
+
size: entry.size,
|
|
267
224
|
})
|
|
268
|
-
|
|
269
|
-
|
|
225
|
+
stats.totalEntries++
|
|
226
|
+
} catch (err) {
|
|
227
|
+
if (err.code === 'ENOENT') {
|
|
228
|
+
stats.rejectedEntries++
|
|
229
|
+
stats.missingContent++
|
|
230
|
+
} else {
|
|
231
|
+
throw err
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
270
235
|
}
|
|
271
236
|
|
|
272
237
|
function cleanTmp (cache, opts) {
|
|
@@ -278,7 +243,7 @@ function writeVerifile (cache, opts) {
|
|
|
278
243
|
const verifile = path.join(cache, '_lastverified')
|
|
279
244
|
opts.log.silly('verify', 'writing verifile to ' + verifile)
|
|
280
245
|
try {
|
|
281
|
-
return writeFile(verifile,
|
|
246
|
+
return fs.writeFile(verifile, `${Date.now()}`)
|
|
282
247
|
} finally {
|
|
283
248
|
fixOwner.chownr.sync(cache, verifile)
|
|
284
249
|
}
|
|
@@ -286,8 +251,7 @@ function writeVerifile (cache, opts) {
|
|
|
286
251
|
|
|
287
252
|
module.exports.lastRun = lastRun
|
|
288
253
|
|
|
289
|
-
function lastRun (cache) {
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
)
|
|
254
|
+
async function lastRun (cache) {
|
|
255
|
+
const data = await fs.readFile(path.join(cache, '_lastverified'), { encoding: 'utf8' })
|
|
256
|
+
return new Date(+data)
|
|
293
257
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cacache",
|
|
3
|
-
"version": "16.
|
|
3
|
+
"version": "16.1.1",
|
|
4
4
|
"cache-version": {
|
|
5
5
|
"content": "2",
|
|
6
6
|
"index": "5"
|
|
@@ -12,7 +12,6 @@
|
|
|
12
12
|
"lib/"
|
|
13
13
|
],
|
|
14
14
|
"scripts": {
|
|
15
|
-
"benchmarks": "node test/benchmarks",
|
|
16
15
|
"preversion": "npm test",
|
|
17
16
|
"postversion": "npm publish",
|
|
18
17
|
"prepublishOnly": "git push origin --follow-tags",
|
|
@@ -70,24 +69,16 @@
|
|
|
70
69
|
},
|
|
71
70
|
"devDependencies": {
|
|
72
71
|
"@npmcli/eslint-config": "^3.0.1",
|
|
73
|
-
"@npmcli/template-oss": "3.
|
|
74
|
-
"benchmark": "^2.1.4",
|
|
75
|
-
"chalk": "^4.1.2",
|
|
76
|
-
"require-inject": "^1.4.4",
|
|
77
|
-
"tacks": "^1.3.0",
|
|
72
|
+
"@npmcli/template-oss": "3.5.0",
|
|
78
73
|
"tap": "^16.0.0"
|
|
79
74
|
},
|
|
80
|
-
"tap": {
|
|
81
|
-
"100": true,
|
|
82
|
-
"test-regex": "test/[^/]*.js"
|
|
83
|
-
},
|
|
84
75
|
"engines": {
|
|
85
76
|
"node": "^12.13.0 || ^14.15.0 || >=16.0.0"
|
|
86
77
|
},
|
|
87
78
|
"templateOSS": {
|
|
88
79
|
"//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.",
|
|
89
80
|
"windowsCI": false,
|
|
90
|
-
"version": "3.
|
|
81
|
+
"version": "3.5.0"
|
|
91
82
|
},
|
|
92
83
|
"author": "GitHub Inc."
|
|
93
84
|
}
|
package/lib/util/disposer.js
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
module.exports.disposer = disposer
|
|
4
|
-
|
|
5
|
-
function disposer (creatorFn, disposerFn, fn) {
|
|
6
|
-
const runDisposer = (resource, result, shouldThrow = false) => {
|
|
7
|
-
return disposerFn(resource)
|
|
8
|
-
.then(
|
|
9
|
-
// disposer resolved, do something with original fn's promise
|
|
10
|
-
() => {
|
|
11
|
-
if (shouldThrow) {
|
|
12
|
-
throw result
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
return result
|
|
16
|
-
},
|
|
17
|
-
// Disposer fn failed, crash process
|
|
18
|
-
(err) => {
|
|
19
|
-
throw err
|
|
20
|
-
// Or process.exit?
|
|
21
|
-
})
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
return creatorFn
|
|
25
|
-
.then((resource) => {
|
|
26
|
-
// fn(resource) can throw, so wrap in a promise here
|
|
27
|
-
return Promise.resolve().then(() => fn(resource))
|
|
28
|
-
.then((result) => runDisposer(resource, result))
|
|
29
|
-
.catch((err) => runDisposer(resource, err, true))
|
|
30
|
-
})
|
|
31
|
-
}
|