pacote 13.5.0 → 13.6.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.
package/README.md CHANGED
@@ -237,6 +237,7 @@ In addition to the common `package.json` fields, manifests include:
237
237
  artifact can be found.
238
238
  * `manifest._from` A normalized form of the spec passed in as an argument.
239
239
  * `manifest._integrity` The integrity value for the package artifact.
240
+ * `manifest._id` The canonical spec of this package version: name@version.
240
241
  * `manifest.dist` Registry manifests (those included in a packument) have a
241
242
  `dist` object. Only `tarball` is required, though at least one of
242
243
  `shasum` or `integrity` is almost always present.
@@ -274,3 +275,8 @@ For Pacote's purposes, the following fields are relevant:
274
275
  `foo@latest` gets turned into `foo@1.2.3`.
275
276
  * `time` In the full packument, an object mapping version numbers to
276
277
  publication times, for the `opts.before` functionality.
278
+
279
+ Pacote adds the following fields, regardless of the accept header:
280
+
281
+ * `_cached` Whether the packument was fetched from the network or the local cache.
282
+ * `_contentLength` The size of the packument.
package/lib/bin.js CHANGED
@@ -18,10 +18,15 @@ const run = conf => {
18
18
  case 'tarball':
19
19
  if (!conf._[2] || conf._[2] === '-') {
20
20
  return pacote.tarball.stream(conf._[1], stream => {
21
- stream.pipe(conf.testStdout ||
22
- /* istanbul ignore next */ process.stdout)
21
+ stream.pipe(
22
+ conf.testStdout ||
23
+ /* istanbul ignore next */
24
+ process.stdout
25
+ )
23
26
  // make sure it resolves something falsey
24
- return stream.promise().then(() => {})
27
+ return stream.promise().then(() => {
28
+ return false
29
+ })
25
30
  }, conf)
26
31
  } else {
27
32
  return pacote.tarball.file(conf._[1], conf._[2], conf)
package/lib/fetcher.js CHANGED
@@ -18,6 +18,7 @@ const removeTrailingSlashes = require('./util/trailing-slashes.js')
18
18
  const getContents = require('@npmcli/installed-package-contents')
19
19
  const readPackageJsonFast = require('read-package-json-fast')
20
20
  const readPackageJson = promisify(require('read-package-json'))
21
+ const Minipass = require('minipass')
21
22
 
22
23
  // we only change ownership on unix platforms, and only if uid is 0
23
24
  const selfOwner = process.getuid && process.getuid() === 0 ? {
@@ -105,15 +106,10 @@ class FetcherBase {
105
106
  this[_readPackageJson] = readPackageJsonFast
106
107
  }
107
108
 
108
- // config values: npmjs (default), never, always
109
- // we don't want to mutate the original value
110
- if (opts.replaceRegistryHost !== 'never'
111
- && opts.replaceRegistryHost !== 'always'
112
- ) {
113
- this.replaceRegistryHost = 'npmjs'
114
- } else {
115
- this.replaceRegistryHost = opts.replaceRegistryHost
116
- }
109
+ // rrh is a registry hostname or 'never' or 'always'
110
+ // defaults to registry.npmjs.org
111
+ this.replaceRegistryHost = (!opts.replaceRegistryHost || opts.replaceRegistryHost === 'npmjs') ?
112
+ 'registry.npmjs.org' : opts.replaceRegistryHost
117
113
 
118
114
  this.defaultTag = opts.defaultTag || 'latest'
119
115
  this.registry = removeTrailingSlashes(opts.registry || 'https://registry.npmjs.org')
@@ -224,18 +220,18 @@ class FetcherBase {
224
220
  }
225
221
 
226
222
  [_istream] (stream) {
227
- // everyone will need one of these, either for verifying or calculating
228
- // We always set it, because we have might only have a weak legacy hex
229
- // sha1 in the packument, and this MAY upgrade it to a stronger algo.
230
- // If we had an integrity, and it doesn't match, then this does not
231
- // override that error; the istream will raise the error before it
232
- // gets to the point of re-setting the integrity.
233
- const istream = ssri.integrityStream(this.opts)
234
- istream.on('integrity', i => this.integrity = i)
235
- stream.on('error', er => istream.emit('error', er))
236
-
237
- // if not caching this, just pipe through to the istream and return it
223
+ // if not caching this, just return it
238
224
  if (!this.opts.cache || !this[_cacheFetches]) {
225
+ // instead of creating a new integrity stream, we only piggyback on the
226
+ // provided stream's events
227
+ if (stream.hasIntegrityEmitter) {
228
+ stream.on('integrity', i => this.integrity = i)
229
+ return stream
230
+ }
231
+
232
+ const istream = ssri.integrityStream(this.opts)
233
+ istream.on('integrity', i => this.integrity = i)
234
+ stream.on('error', err => istream.emit('error', err))
239
235
  return stream.pipe(istream)
240
236
  }
241
237
 
@@ -243,21 +239,24 @@ class FetcherBase {
243
239
  // but then pipe from the original tarball stream into the cache as well.
244
240
  // To do this without losing any data, and since the cacache put stream
245
241
  // is not a passthrough, we have to pipe from the original stream into
246
- // the cache AFTER we pipe into the istream. Since the cache stream
242
+ // the cache AFTER we pipe into the middleStream. Since the cache stream
247
243
  // has an asynchronous flush to write its contents to disk, we need to
248
- // defer the istream end until the cache stream ends.
249
- stream.pipe(istream, { end: false })
244
+ // defer the middleStream end until the cache stream ends.
245
+ const middleStream = new Minipass()
246
+ stream.on('error', err => middleStream.emit('error', err))
247
+ stream.pipe(middleStream, { end: false })
250
248
  const cstream = cacache.put.stream(
251
249
  this.opts.cache,
252
250
  `pacote:tarball:${this.from}`,
253
251
  this.opts
254
252
  )
253
+ cstream.on('integrity', i => this.integrity = i)
254
+ cstream.on('error', err => stream.emit('error', err))
255
255
  stream.pipe(cstream)
256
- // defer istream end until after cstream
257
- // cache write errors should not crash the fetch, this is best-effort.
258
- cstream.promise().catch(() => {}).then(() => istream.end())
259
256
 
260
- return istream
257
+ // eslint-disable-next-line promise/catch-or-return
258
+ cstream.promise().catch(() => {}).then(() => middleStream.end())
259
+ return middleStream
261
260
  }
262
261
 
263
262
  pickIntegrityAlgorithm () {
@@ -271,7 +270,10 @@ class FetcherBase {
271
270
  }
272
271
 
273
272
  // override the types getter
274
- get types () {}
273
+ get types () {
274
+ return false
275
+ }
276
+
275
277
  [_assertType] () {
276
278
  if (this.types && !this.types.includes(this.spec.type)) {
277
279
  throw new TypeError(`Wrong spec type (${
package/lib/git.js CHANGED
@@ -239,7 +239,7 @@ class GitFetcher extends Fetcher {
239
239
  tarballOk = tarballOk &&
240
240
  h && resolved === repoUrl(h, { noCommittish: false }) && h.tarball
241
241
 
242
- return cacache.tmp.withTmp(this.cache, o, tmp => {
242
+ return cacache.tmp.withTmp(this.cache, o, async tmp => {
243
243
  // if we're resolved, and have a tarball url, shell out to RemoteFetcher
244
244
  if (tarballOk) {
245
245
  const nameat = this.spec.name ? `${this.spec.name}@` : ''
@@ -259,16 +259,15 @@ class GitFetcher extends Fetcher {
259
259
  })
260
260
  }
261
261
 
262
- return (
262
+ const sha = await (
263
263
  h ? this[_cloneHosted](ref, tmp)
264
264
  : this[_cloneRepo](this.spec.fetchSpec, ref, tmp)
265
- ).then(sha => {
266
- this.resolvedSha = sha
267
- if (!this.resolved) {
268
- this[_addGitSha](sha)
269
- }
270
- })
271
- .then(() => handler(tmp))
265
+ )
266
+ this.resolvedSha = sha
267
+ if (!this.resolved) {
268
+ await this[_addGitSha](sha)
269
+ }
270
+ return handler(tmp)
272
271
  })
273
272
  }
274
273
 
package/lib/registry.js CHANGED
@@ -122,11 +122,12 @@ class RegistryFetcher extends Fetcher {
122
122
  }
123
123
 
124
124
  const packument = await this.packument()
125
- const mani = await pickManifest(packument, this.spec.fetchSpec, {
125
+ let mani = await pickManifest(packument, this.spec.fetchSpec, {
126
126
  ...this.opts,
127
127
  defaultTag: this.defaultTag,
128
128
  before: this.before,
129
129
  })
130
+ mani = rpj.normalize(mani)
130
131
  /* XXX add ETARGET and E403 revalidation of cached packuments here */
131
132
 
132
133
  // add _resolved and _integrity from dist object
@@ -165,49 +166,53 @@ class RegistryFetcher extends Fetcher {
165
166
  mani._integrity = String(this.integrity)
166
167
  if (dist.signatures) {
167
168
  if (this.opts.verifySignatures) {
168
- if (this.registryKeys) {
169
- // validate and throw on error, then set _signatures
170
- const message = `${mani._id}:${mani._integrity}`
171
- for (const signature of dist.signatures) {
172
- const publicKey = this.registryKeys.filter(key => (key.keyid === signature.keyid))[0]
173
- if (!publicKey) {
174
- throw Object.assign(new Error(
175
- `${mani._id} has a signature with keyid: ${signature.keyid} ` +
176
- 'but no corresponding public key can be found.'
177
- ), { code: 'EMISSINGSIGNATUREKEY' })
178
- }
179
- const validPublicKey =
180
- !publicKey.expires || (Date.parse(publicKey.expires) > Date.now())
181
- if (!validPublicKey) {
182
- throw Object.assign(new Error(
183
- `${mani._id} has a signature with keyid: ${signature.keyid} ` +
169
+ // validate and throw on error, then set _signatures
170
+ const message = `${mani._id}:${mani._integrity}`
171
+ for (const signature of dist.signatures) {
172
+ const publicKey = this.registryKeys &&
173
+ this.registryKeys.filter(key => (key.keyid === signature.keyid))[0]
174
+ if (!publicKey) {
175
+ throw Object.assign(new Error(
176
+ `${mani._id} has a registry signature with keyid: ${signature.keyid} ` +
177
+ 'but no corresponding public key can be found'
178
+ ), { code: 'EMISSINGSIGNATUREKEY' })
179
+ }
180
+ const validPublicKey =
181
+ !publicKey.expires || (Date.parse(publicKey.expires) > Date.now())
182
+ if (!validPublicKey) {
183
+ throw Object.assign(new Error(
184
+ `${mani._id} has a registry signature with keyid: ${signature.keyid} ` +
184
185
  `but the corresponding public key has expired ${publicKey.expires}`
185
- ), { code: 'EEXPIREDSIGNATUREKEY' })
186
- }
187
- const verifier = crypto.createVerify('SHA256')
188
- verifier.write(message)
189
- verifier.end()
190
- const valid = verifier.verify(
191
- publicKey.pemkey,
192
- signature.sig,
193
- 'base64'
194
- )
195
- if (!valid) {
196
- throw Object.assign(new Error(
197
- 'Integrity checksum signature failed: ' +
198
- `key ${publicKey.keyid} signature ${signature.sig}`
199
- ), { code: 'EINTEGRITYSIGNATURE' })
200
- }
186
+ ), { code: 'EEXPIREDSIGNATUREKEY' })
187
+ }
188
+ const verifier = crypto.createVerify('SHA256')
189
+ verifier.write(message)
190
+ verifier.end()
191
+ const valid = verifier.verify(
192
+ publicKey.pemkey,
193
+ signature.sig,
194
+ 'base64'
195
+ )
196
+ if (!valid) {
197
+ throw Object.assign(new Error(
198
+ `${mani._id} has an invalid registry signature with ` +
199
+ `keyid: ${publicKey.keyid} and signature: ${signature.sig}`
200
+ ), {
201
+ code: 'EINTEGRITYSIGNATURE',
202
+ keyid: publicKey.keyid,
203
+ signature: signature.sig,
204
+ resolved: mani._resolved,
205
+ integrity: mani._integrity,
206
+ })
201
207
  }
202
- mani._signatures = dist.signatures
203
208
  }
204
- // if no keys, don't set _signatures
209
+ mani._signatures = dist.signatures
205
210
  } else {
206
211
  mani._signatures = dist.signatures
207
212
  }
208
213
  }
209
214
  }
210
- this.package = rpj.normalize(mani)
215
+ this.package = mani
211
216
  return this.package
212
217
  }
213
218
 
package/lib/remote.js CHANGED
@@ -4,8 +4,6 @@ const _tarballFromResolved = Symbol.for('pacote.Fetcher._tarballFromResolved')
4
4
  const pacoteVersion = require('../package.json').version
5
5
  const fetch = require('npm-registry-fetch')
6
6
  const Minipass = require('minipass')
7
- // The default registry URL is a string of great magic.
8
- const magicHost = 'https://registry.npmjs.org'
9
7
 
10
8
  const _cacheFetches = Symbol.for('pacote.Fetcher._cacheFetches')
11
9
  const _headers = Symbol('_headers')
@@ -14,11 +12,9 @@ class RemoteFetcher extends Fetcher {
14
12
  super(spec, opts)
15
13
  this.resolved = this.spec.fetchSpec
16
14
  const resolvedURL = new URL(this.resolved)
17
- if (
18
- (this.replaceRegistryHost === 'npmjs'
19
- && resolvedURL.origin === magicHost)
20
- || this.replaceRegistryHost === 'always'
21
- ) {
15
+ if (this.replaceRegistryHost !== 'never'
16
+ && (this.replaceRegistryHost === 'always'
17
+ || this.replaceRegistryHost === resolvedURL.host)) {
22
18
  this.resolved = new URL(resolvedURL.pathname, this.registry).href
23
19
  }
24
20
 
@@ -35,6 +31,8 @@ class RemoteFetcher extends Fetcher {
35
31
 
36
32
  [_tarballFromResolved] () {
37
33
  const stream = new Minipass()
34
+ stream.hasIntegrityEmitter = true
35
+
38
36
  const fetchOpts = {
39
37
  ...this.opts,
40
38
  headers: this[_headers](),
@@ -42,16 +40,20 @@ class RemoteFetcher extends Fetcher {
42
40
  integrity: this.integrity,
43
41
  algorithms: [this.pickIntegrityAlgorithm()],
44
42
  }
45
- fetch(this.resolved, fetchOpts).then(res => {
46
- const hash = res.headers.get('x-local-cache-hash')
47
- if (hash) {
48
- this.integrity = decodeURIComponent(hash)
49
- }
50
43
 
44
+ // eslint-disable-next-line promise/always-return
45
+ fetch(this.resolved, fetchOpts).then(res => {
51
46
  res.body.on('error',
52
47
  /* istanbul ignore next - exceedingly rare and hard to simulate */
53
48
  er => stream.emit('error', er)
54
- ).pipe(stream)
49
+ )
50
+
51
+ res.body.on('integrity', i => {
52
+ this.integrity = i
53
+ stream.emit('integrity', i)
54
+ })
55
+
56
+ res.body.pipe(stream)
55
57
  }).catch(er => stream.emit('error', er))
56
58
 
57
59
  return stream
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pacote",
3
- "version": "13.5.0",
3
+ "version": "13.6.2",
4
4
  "description": "JavaScript package downloader",
5
5
  "author": "GitHub Inc.",
6
6
  "bin": {
@@ -21,8 +21,7 @@
21
21
  "template-oss-apply": "template-oss-apply --force"
22
22
  },
23
23
  "tap": {
24
- "timeout": 300,
25
- "coverage-map": "map.js"
24
+ "timeout": 300
26
25
  },
27
26
  "devDependencies": {
28
27
  "@npmcli/eslint-config": "^3.0.1",
@@ -46,7 +45,7 @@
46
45
  "@npmcli/git": "^3.0.0",
47
46
  "@npmcli/installed-package-contents": "^1.0.7",
48
47
  "@npmcli/promise-spawn": "^3.0.0",
49
- "@npmcli/run-script": "^3.0.1",
48
+ "@npmcli/run-script": "^4.1.0",
50
49
  "cacache": "^16.0.0",
51
50
  "chownr": "^2.0.0",
52
51
  "fs-minipass": "^2.1.0",