11cb0ef41Sopenharmony_ci// This is the base class that the other fetcher types in lib
21cb0ef41Sopenharmony_ci// all descend from.
31cb0ef41Sopenharmony_ci// It handles the unpacking and retry logic that is shared among
41cb0ef41Sopenharmony_ci// all of the other Fetcher types.
51cb0ef41Sopenharmony_ci
61cb0ef41Sopenharmony_ciconst npa = require('npm-package-arg')
71cb0ef41Sopenharmony_ciconst ssri = require('ssri')
81cb0ef41Sopenharmony_ciconst { promisify } = require('util')
91cb0ef41Sopenharmony_ciconst { basename, dirname } = require('path')
101cb0ef41Sopenharmony_ciconst tar = require('tar')
111cb0ef41Sopenharmony_ciconst log = require('proc-log')
121cb0ef41Sopenharmony_ciconst retry = require('promise-retry')
131cb0ef41Sopenharmony_ciconst fs = require('fs/promises')
141cb0ef41Sopenharmony_ciconst fsm = require('fs-minipass')
151cb0ef41Sopenharmony_ciconst cacache = require('cacache')
161cb0ef41Sopenharmony_ciconst isPackageBin = require('./util/is-package-bin.js')
171cb0ef41Sopenharmony_ciconst removeTrailingSlashes = require('./util/trailing-slashes.js')
181cb0ef41Sopenharmony_ciconst getContents = require('@npmcli/installed-package-contents')
191cb0ef41Sopenharmony_ciconst readPackageJsonFast = require('read-package-json-fast')
201cb0ef41Sopenharmony_ciconst readPackageJson = promisify(require('read-package-json'))
211cb0ef41Sopenharmony_ciconst { Minipass } = require('minipass')
221cb0ef41Sopenharmony_ci
231cb0ef41Sopenharmony_ciconst cacheDir = require('./util/cache-dir.js')
241cb0ef41Sopenharmony_ci
251cb0ef41Sopenharmony_ci// Private methods.
261cb0ef41Sopenharmony_ci// Child classes should not have to override these.
271cb0ef41Sopenharmony_ci// Users should never call them.
281cb0ef41Sopenharmony_ciconst _extract = Symbol('_extract')
291cb0ef41Sopenharmony_ciconst _mkdir = Symbol('_mkdir')
301cb0ef41Sopenharmony_ciconst _empty = Symbol('_empty')
311cb0ef41Sopenharmony_ciconst _toFile = Symbol('_toFile')
321cb0ef41Sopenharmony_ciconst _tarxOptions = Symbol('_tarxOptions')
331cb0ef41Sopenharmony_ciconst _entryMode = Symbol('_entryMode')
341cb0ef41Sopenharmony_ciconst _istream = Symbol('_istream')
351cb0ef41Sopenharmony_ciconst _assertType = Symbol('_assertType')
361cb0ef41Sopenharmony_ciconst _tarballFromCache = Symbol('_tarballFromCache')
371cb0ef41Sopenharmony_ciconst _tarballFromResolved = Symbol.for('pacote.Fetcher._tarballFromResolved')
381cb0ef41Sopenharmony_ciconst _cacheFetches = Symbol.for('pacote.Fetcher._cacheFetches')
391cb0ef41Sopenharmony_ciconst _readPackageJson = Symbol.for('package.Fetcher._readPackageJson')
401cb0ef41Sopenharmony_ci
411cb0ef41Sopenharmony_ciclass FetcherBase {
421cb0ef41Sopenharmony_ci  constructor (spec, opts) {
431cb0ef41Sopenharmony_ci    if (!opts || typeof opts !== 'object') {
441cb0ef41Sopenharmony_ci      throw new TypeError('options object is required')
451cb0ef41Sopenharmony_ci    }
461cb0ef41Sopenharmony_ci    this.spec = npa(spec, opts.where)
471cb0ef41Sopenharmony_ci
481cb0ef41Sopenharmony_ci    this.allowGitIgnore = !!opts.allowGitIgnore
491cb0ef41Sopenharmony_ci
501cb0ef41Sopenharmony_ci    // a bit redundant because presumably the caller already knows this,
511cb0ef41Sopenharmony_ci    // but it makes it easier to not have to keep track of the requested
521cb0ef41Sopenharmony_ci    // spec when we're dispatching thousands of these at once, and normalizing
531cb0ef41Sopenharmony_ci    // is nice.  saveSpec is preferred if set, because it turns stuff like
541cb0ef41Sopenharmony_ci    // x/y#committish into github:x/y#committish.  use name@rawSpec for
551cb0ef41Sopenharmony_ci    // registry deps so that we turn xyz and xyz@ -> xyz@
561cb0ef41Sopenharmony_ci    this.from = this.spec.registry
571cb0ef41Sopenharmony_ci      ? `${this.spec.name}@${this.spec.rawSpec}` : this.spec.saveSpec
581cb0ef41Sopenharmony_ci
591cb0ef41Sopenharmony_ci    this[_assertType]()
601cb0ef41Sopenharmony_ci    // clone the opts object so that others aren't upset when we mutate it
611cb0ef41Sopenharmony_ci    // by adding/modifying the integrity value.
621cb0ef41Sopenharmony_ci    this.opts = { ...opts }
631cb0ef41Sopenharmony_ci
641cb0ef41Sopenharmony_ci    this.cache = opts.cache || cacheDir().cacache
651cb0ef41Sopenharmony_ci    this.tufCache = opts.tufCache || cacheDir().tufcache
661cb0ef41Sopenharmony_ci    this.resolved = opts.resolved || null
671cb0ef41Sopenharmony_ci
681cb0ef41Sopenharmony_ci    // default to caching/verifying with sha512, that's what we usually have
691cb0ef41Sopenharmony_ci    // need to change this default, or start overriding it, when sha512
701cb0ef41Sopenharmony_ci    // is no longer strong enough.
711cb0ef41Sopenharmony_ci    this.defaultIntegrityAlgorithm = opts.defaultIntegrityAlgorithm || 'sha512'
721cb0ef41Sopenharmony_ci
731cb0ef41Sopenharmony_ci    if (typeof opts.integrity === 'string') {
741cb0ef41Sopenharmony_ci      this.opts.integrity = ssri.parse(opts.integrity)
751cb0ef41Sopenharmony_ci    }
761cb0ef41Sopenharmony_ci
771cb0ef41Sopenharmony_ci    this.package = null
781cb0ef41Sopenharmony_ci    this.type = this.constructor.name
791cb0ef41Sopenharmony_ci    this.fmode = opts.fmode || 0o666
801cb0ef41Sopenharmony_ci    this.dmode = opts.dmode || 0o777
811cb0ef41Sopenharmony_ci    // we don't need a default umask, because we don't chmod files coming
821cb0ef41Sopenharmony_ci    // out of package tarballs.  they're forced to have a mode that is
831cb0ef41Sopenharmony_ci    // valid, regardless of what's in the tarball entry, and then we let
841cb0ef41Sopenharmony_ci    // the process's umask setting do its job.  but if configured, we do
851cb0ef41Sopenharmony_ci    // respect it.
861cb0ef41Sopenharmony_ci    this.umask = opts.umask || 0
871cb0ef41Sopenharmony_ci
881cb0ef41Sopenharmony_ci    this.preferOnline = !!opts.preferOnline
891cb0ef41Sopenharmony_ci    this.preferOffline = !!opts.preferOffline
901cb0ef41Sopenharmony_ci    this.offline = !!opts.offline
911cb0ef41Sopenharmony_ci
921cb0ef41Sopenharmony_ci    this.before = opts.before
931cb0ef41Sopenharmony_ci    this.fullMetadata = this.before ? true : !!opts.fullMetadata
941cb0ef41Sopenharmony_ci    this.fullReadJson = !!opts.fullReadJson
951cb0ef41Sopenharmony_ci    if (this.fullReadJson) {
961cb0ef41Sopenharmony_ci      this[_readPackageJson] = readPackageJson
971cb0ef41Sopenharmony_ci    } else {
981cb0ef41Sopenharmony_ci      this[_readPackageJson] = readPackageJsonFast
991cb0ef41Sopenharmony_ci    }
1001cb0ef41Sopenharmony_ci
1011cb0ef41Sopenharmony_ci    // rrh is a registry hostname or 'never' or 'always'
1021cb0ef41Sopenharmony_ci    // defaults to registry.npmjs.org
1031cb0ef41Sopenharmony_ci    this.replaceRegistryHost = (!opts.replaceRegistryHost || opts.replaceRegistryHost === 'npmjs') ?
1041cb0ef41Sopenharmony_ci      'registry.npmjs.org' : opts.replaceRegistryHost
1051cb0ef41Sopenharmony_ci
1061cb0ef41Sopenharmony_ci    this.defaultTag = opts.defaultTag || 'latest'
1071cb0ef41Sopenharmony_ci    this.registry = removeTrailingSlashes(opts.registry || 'https://registry.npmjs.org')
1081cb0ef41Sopenharmony_ci
1091cb0ef41Sopenharmony_ci    // command to run 'prepare' scripts on directories and git dirs
1101cb0ef41Sopenharmony_ci    // To use pacote with yarn, for example, set npmBin to 'yarn'
1111cb0ef41Sopenharmony_ci    // and npmCliConfig with yarn's equivalents.
1121cb0ef41Sopenharmony_ci    this.npmBin = opts.npmBin || 'npm'
1131cb0ef41Sopenharmony_ci
1141cb0ef41Sopenharmony_ci    // command to install deps for preparing
1151cb0ef41Sopenharmony_ci    this.npmInstallCmd = opts.npmInstallCmd || ['install', '--force']
1161cb0ef41Sopenharmony_ci
1171cb0ef41Sopenharmony_ci    // XXX fill more of this in based on what we know from this.opts
1181cb0ef41Sopenharmony_ci    // we explicitly DO NOT fill in --tag, though, since we are often
1191cb0ef41Sopenharmony_ci    // going to be packing in the context of a publish, which may set
1201cb0ef41Sopenharmony_ci    // a dist-tag, but certainly wants to keep defaulting to latest.
1211cb0ef41Sopenharmony_ci    this.npmCliConfig = opts.npmCliConfig || [
1221cb0ef41Sopenharmony_ci      `--cache=${dirname(this.cache)}`,
1231cb0ef41Sopenharmony_ci      `--prefer-offline=${!!this.preferOffline}`,
1241cb0ef41Sopenharmony_ci      `--prefer-online=${!!this.preferOnline}`,
1251cb0ef41Sopenharmony_ci      `--offline=${!!this.offline}`,
1261cb0ef41Sopenharmony_ci      ...(this.before ? [`--before=${this.before.toISOString()}`] : []),
1271cb0ef41Sopenharmony_ci      '--no-progress',
1281cb0ef41Sopenharmony_ci      '--no-save',
1291cb0ef41Sopenharmony_ci      '--no-audit',
1301cb0ef41Sopenharmony_ci      // override any omit settings from the environment
1311cb0ef41Sopenharmony_ci      '--include=dev',
1321cb0ef41Sopenharmony_ci      '--include=peer',
1331cb0ef41Sopenharmony_ci      '--include=optional',
1341cb0ef41Sopenharmony_ci      // we need the actual things, not just the lockfile
1351cb0ef41Sopenharmony_ci      '--no-package-lock-only',
1361cb0ef41Sopenharmony_ci      '--no-dry-run',
1371cb0ef41Sopenharmony_ci    ]
1381cb0ef41Sopenharmony_ci  }
1391cb0ef41Sopenharmony_ci
1401cb0ef41Sopenharmony_ci  get integrity () {
1411cb0ef41Sopenharmony_ci    return this.opts.integrity || null
1421cb0ef41Sopenharmony_ci  }
1431cb0ef41Sopenharmony_ci
1441cb0ef41Sopenharmony_ci  set integrity (i) {
1451cb0ef41Sopenharmony_ci    if (!i) {
1461cb0ef41Sopenharmony_ci      return
1471cb0ef41Sopenharmony_ci    }
1481cb0ef41Sopenharmony_ci
1491cb0ef41Sopenharmony_ci    i = ssri.parse(i)
1501cb0ef41Sopenharmony_ci    const current = this.opts.integrity
1511cb0ef41Sopenharmony_ci
1521cb0ef41Sopenharmony_ci    // do not ever update an existing hash value, but do
1531cb0ef41Sopenharmony_ci    // merge in NEW algos and hashes that we don't already have.
1541cb0ef41Sopenharmony_ci    if (current) {
1551cb0ef41Sopenharmony_ci      current.merge(i)
1561cb0ef41Sopenharmony_ci    } else {
1571cb0ef41Sopenharmony_ci      this.opts.integrity = i
1581cb0ef41Sopenharmony_ci    }
1591cb0ef41Sopenharmony_ci  }
1601cb0ef41Sopenharmony_ci
1611cb0ef41Sopenharmony_ci  get notImplementedError () {
1621cb0ef41Sopenharmony_ci    return new Error('not implemented in this fetcher type: ' + this.type)
1631cb0ef41Sopenharmony_ci  }
1641cb0ef41Sopenharmony_ci
1651cb0ef41Sopenharmony_ci  // override in child classes
1661cb0ef41Sopenharmony_ci  // Returns a Promise that resolves to this.resolved string value
1671cb0ef41Sopenharmony_ci  resolve () {
1681cb0ef41Sopenharmony_ci    return this.resolved ? Promise.resolve(this.resolved)
1691cb0ef41Sopenharmony_ci      : Promise.reject(this.notImplementedError)
1701cb0ef41Sopenharmony_ci  }
1711cb0ef41Sopenharmony_ci
1721cb0ef41Sopenharmony_ci  packument () {
1731cb0ef41Sopenharmony_ci    return Promise.reject(this.notImplementedError)
1741cb0ef41Sopenharmony_ci  }
1751cb0ef41Sopenharmony_ci
1761cb0ef41Sopenharmony_ci  // override in child class
1771cb0ef41Sopenharmony_ci  // returns a manifest containing:
1781cb0ef41Sopenharmony_ci  // - name
1791cb0ef41Sopenharmony_ci  // - version
1801cb0ef41Sopenharmony_ci  // - _resolved
1811cb0ef41Sopenharmony_ci  // - _integrity
1821cb0ef41Sopenharmony_ci  // - plus whatever else was in there (corgi, full metadata, or pj file)
1831cb0ef41Sopenharmony_ci  manifest () {
1841cb0ef41Sopenharmony_ci    return Promise.reject(this.notImplementedError)
1851cb0ef41Sopenharmony_ci  }
1861cb0ef41Sopenharmony_ci
1871cb0ef41Sopenharmony_ci  // private, should be overridden.
1881cb0ef41Sopenharmony_ci  // Note that they should *not* calculate or check integrity or cache,
1891cb0ef41Sopenharmony_ci  // but *just*  return the raw tarball data stream.
1901cb0ef41Sopenharmony_ci  [_tarballFromResolved] () {
1911cb0ef41Sopenharmony_ci    throw this.notImplementedError
1921cb0ef41Sopenharmony_ci  }
1931cb0ef41Sopenharmony_ci
1941cb0ef41Sopenharmony_ci  // public, should not be overridden
1951cb0ef41Sopenharmony_ci  tarball () {
1961cb0ef41Sopenharmony_ci    return this.tarballStream(stream => stream.concat().then(data => {
1971cb0ef41Sopenharmony_ci      data.integrity = this.integrity && String(this.integrity)
1981cb0ef41Sopenharmony_ci      data.resolved = this.resolved
1991cb0ef41Sopenharmony_ci      data.from = this.from
2001cb0ef41Sopenharmony_ci      return data
2011cb0ef41Sopenharmony_ci    }))
2021cb0ef41Sopenharmony_ci  }
2031cb0ef41Sopenharmony_ci
2041cb0ef41Sopenharmony_ci  // private
2051cb0ef41Sopenharmony_ci  // Note: cacache will raise a EINTEGRITY error if the integrity doesn't match
2061cb0ef41Sopenharmony_ci  [_tarballFromCache] () {
2071cb0ef41Sopenharmony_ci    return cacache.get.stream.byDigest(this.cache, this.integrity, this.opts)
2081cb0ef41Sopenharmony_ci  }
2091cb0ef41Sopenharmony_ci
2101cb0ef41Sopenharmony_ci  get [_cacheFetches] () {
2111cb0ef41Sopenharmony_ci    return true
2121cb0ef41Sopenharmony_ci  }
2131cb0ef41Sopenharmony_ci
2141cb0ef41Sopenharmony_ci  [_istream] (stream) {
2151cb0ef41Sopenharmony_ci    // if not caching this, just return it
2161cb0ef41Sopenharmony_ci    if (!this.opts.cache || !this[_cacheFetches]) {
2171cb0ef41Sopenharmony_ci      // instead of creating a new integrity stream, we only piggyback on the
2181cb0ef41Sopenharmony_ci      // provided stream's events
2191cb0ef41Sopenharmony_ci      if (stream.hasIntegrityEmitter) {
2201cb0ef41Sopenharmony_ci        stream.on('integrity', i => this.integrity = i)
2211cb0ef41Sopenharmony_ci        return stream
2221cb0ef41Sopenharmony_ci      }
2231cb0ef41Sopenharmony_ci
2241cb0ef41Sopenharmony_ci      const istream = ssri.integrityStream(this.opts)
2251cb0ef41Sopenharmony_ci      istream.on('integrity', i => this.integrity = i)
2261cb0ef41Sopenharmony_ci      stream.on('error', err => istream.emit('error', err))
2271cb0ef41Sopenharmony_ci      return stream.pipe(istream)
2281cb0ef41Sopenharmony_ci    }
2291cb0ef41Sopenharmony_ci
2301cb0ef41Sopenharmony_ci    // we have to return a stream that gets ALL the data, and proxies errors,
2311cb0ef41Sopenharmony_ci    // but then pipe from the original tarball stream into the cache as well.
2321cb0ef41Sopenharmony_ci    // To do this without losing any data, and since the cacache put stream
2331cb0ef41Sopenharmony_ci    // is not a passthrough, we have to pipe from the original stream into
2341cb0ef41Sopenharmony_ci    // the cache AFTER we pipe into the middleStream.  Since the cache stream
2351cb0ef41Sopenharmony_ci    // has an asynchronous flush to write its contents to disk, we need to
2361cb0ef41Sopenharmony_ci    // defer the middleStream end until the cache stream ends.
2371cb0ef41Sopenharmony_ci    const middleStream = new Minipass()
2381cb0ef41Sopenharmony_ci    stream.on('error', err => middleStream.emit('error', err))
2391cb0ef41Sopenharmony_ci    stream.pipe(middleStream, { end: false })
2401cb0ef41Sopenharmony_ci    const cstream = cacache.put.stream(
2411cb0ef41Sopenharmony_ci      this.opts.cache,
2421cb0ef41Sopenharmony_ci      `pacote:tarball:${this.from}`,
2431cb0ef41Sopenharmony_ci      this.opts
2441cb0ef41Sopenharmony_ci    )
2451cb0ef41Sopenharmony_ci    cstream.on('integrity', i => this.integrity = i)
2461cb0ef41Sopenharmony_ci    cstream.on('error', err => stream.emit('error', err))
2471cb0ef41Sopenharmony_ci    stream.pipe(cstream)
2481cb0ef41Sopenharmony_ci
2491cb0ef41Sopenharmony_ci    // eslint-disable-next-line promise/catch-or-return
2501cb0ef41Sopenharmony_ci    cstream.promise().catch(() => {}).then(() => middleStream.end())
2511cb0ef41Sopenharmony_ci    return middleStream
2521cb0ef41Sopenharmony_ci  }
2531cb0ef41Sopenharmony_ci
2541cb0ef41Sopenharmony_ci  pickIntegrityAlgorithm () {
2551cb0ef41Sopenharmony_ci    return this.integrity ? this.integrity.pickAlgorithm(this.opts)
2561cb0ef41Sopenharmony_ci      : this.defaultIntegrityAlgorithm
2571cb0ef41Sopenharmony_ci  }
2581cb0ef41Sopenharmony_ci
2591cb0ef41Sopenharmony_ci  // TODO: check error class, once those are rolled out to our deps
2601cb0ef41Sopenharmony_ci  isDataCorruptionError (er) {
2611cb0ef41Sopenharmony_ci    return er.code === 'EINTEGRITY' || er.code === 'Z_DATA_ERROR'
2621cb0ef41Sopenharmony_ci  }
2631cb0ef41Sopenharmony_ci
2641cb0ef41Sopenharmony_ci  // override the types getter
2651cb0ef41Sopenharmony_ci  get types () {
2661cb0ef41Sopenharmony_ci    return false
2671cb0ef41Sopenharmony_ci  }
2681cb0ef41Sopenharmony_ci
2691cb0ef41Sopenharmony_ci  [_assertType] () {
2701cb0ef41Sopenharmony_ci    if (this.types && !this.types.includes(this.spec.type)) {
2711cb0ef41Sopenharmony_ci      throw new TypeError(`Wrong spec type (${
2721cb0ef41Sopenharmony_ci        this.spec.type
2731cb0ef41Sopenharmony_ci      }) for ${
2741cb0ef41Sopenharmony_ci        this.constructor.name
2751cb0ef41Sopenharmony_ci      }. Supported types: ${this.types.join(', ')}`)
2761cb0ef41Sopenharmony_ci    }
2771cb0ef41Sopenharmony_ci  }
2781cb0ef41Sopenharmony_ci
2791cb0ef41Sopenharmony_ci  // We allow ENOENTs from cacache, but not anywhere else.
2801cb0ef41Sopenharmony_ci  // An ENOENT trying to read a tgz file, for example, is Right Out.
2811cb0ef41Sopenharmony_ci  isRetriableError (er) {
2821cb0ef41Sopenharmony_ci    // TODO: check error class, once those are rolled out to our deps
2831cb0ef41Sopenharmony_ci    return this.isDataCorruptionError(er) ||
2841cb0ef41Sopenharmony_ci      er.code === 'ENOENT' ||
2851cb0ef41Sopenharmony_ci      er.code === 'EISDIR'
2861cb0ef41Sopenharmony_ci  }
2871cb0ef41Sopenharmony_ci
2881cb0ef41Sopenharmony_ci  // Mostly internal, but has some uses
2891cb0ef41Sopenharmony_ci  // Pass in a function which returns a promise
2901cb0ef41Sopenharmony_ci  // Function will be called 1 or more times with streams that may fail.
2911cb0ef41Sopenharmony_ci  // Retries:
2921cb0ef41Sopenharmony_ci  // Function MUST handle errors on the stream by rejecting the promise,
2931cb0ef41Sopenharmony_ci  // so that retry logic can pick it up and either retry or fail whatever
2941cb0ef41Sopenharmony_ci  // promise it was making (ie, failing extraction, etc.)
2951cb0ef41Sopenharmony_ci  //
2961cb0ef41Sopenharmony_ci  // The return value of this method is a Promise that resolves the same
2971cb0ef41Sopenharmony_ci  // as whatever the streamHandler resolves to.
2981cb0ef41Sopenharmony_ci  //
2991cb0ef41Sopenharmony_ci  // This should never be overridden by child classes, but it is public.
3001cb0ef41Sopenharmony_ci  tarballStream (streamHandler) {
3011cb0ef41Sopenharmony_ci    // Only short-circuit via cache if we have everything else we'll need,
3021cb0ef41Sopenharmony_ci    // and the user has not expressed a preference for checking online.
3031cb0ef41Sopenharmony_ci
3041cb0ef41Sopenharmony_ci    const fromCache = (
3051cb0ef41Sopenharmony_ci      !this.preferOnline &&
3061cb0ef41Sopenharmony_ci      this.integrity &&
3071cb0ef41Sopenharmony_ci      this.resolved
3081cb0ef41Sopenharmony_ci    ) ? streamHandler(this[_tarballFromCache]()).catch(er => {
3091cb0ef41Sopenharmony_ci        if (this.isDataCorruptionError(er)) {
3101cb0ef41Sopenharmony_ci          log.warn('tarball', `cached data for ${
3111cb0ef41Sopenharmony_ci          this.spec
3121cb0ef41Sopenharmony_ci        } (${this.integrity}) seems to be corrupted. Refreshing cache.`)
3131cb0ef41Sopenharmony_ci          return this.cleanupCached().then(() => {
3141cb0ef41Sopenharmony_ci            throw er
3151cb0ef41Sopenharmony_ci          })
3161cb0ef41Sopenharmony_ci        } else {
3171cb0ef41Sopenharmony_ci          throw er
3181cb0ef41Sopenharmony_ci        }
3191cb0ef41Sopenharmony_ci      }) : null
3201cb0ef41Sopenharmony_ci
3211cb0ef41Sopenharmony_ci    const fromResolved = er => {
3221cb0ef41Sopenharmony_ci      if (er) {
3231cb0ef41Sopenharmony_ci        if (!this.isRetriableError(er)) {
3241cb0ef41Sopenharmony_ci          throw er
3251cb0ef41Sopenharmony_ci        }
3261cb0ef41Sopenharmony_ci        log.silly('tarball', `no local data for ${
3271cb0ef41Sopenharmony_ci          this.spec
3281cb0ef41Sopenharmony_ci        }. Extracting by manifest.`)
3291cb0ef41Sopenharmony_ci      }
3301cb0ef41Sopenharmony_ci      return this.resolve().then(() => retry(tryAgain =>
3311cb0ef41Sopenharmony_ci        streamHandler(this[_istream](this[_tarballFromResolved]()))
3321cb0ef41Sopenharmony_ci          .catch(streamErr => {
3331cb0ef41Sopenharmony_ci          // Most likely data integrity.  A cache ENOENT error is unlikely
3341cb0ef41Sopenharmony_ci          // here, since we're definitely not reading from the cache, but it
3351cb0ef41Sopenharmony_ci          // IS possible that the fetch subsystem accessed the cache, and the
3361cb0ef41Sopenharmony_ci          // entry got blown away or something.  Try one more time to be sure.
3371cb0ef41Sopenharmony_ci            if (this.isRetriableError(streamErr)) {
3381cb0ef41Sopenharmony_ci              log.warn('tarball', `tarball data for ${
3391cb0ef41Sopenharmony_ci              this.spec
3401cb0ef41Sopenharmony_ci            } (${this.integrity}) seems to be corrupted. Trying again.`)
3411cb0ef41Sopenharmony_ci              return this.cleanupCached().then(() => tryAgain(streamErr))
3421cb0ef41Sopenharmony_ci            }
3431cb0ef41Sopenharmony_ci            throw streamErr
3441cb0ef41Sopenharmony_ci          }), { retries: 1, minTimeout: 0, maxTimeout: 0 }))
3451cb0ef41Sopenharmony_ci    }
3461cb0ef41Sopenharmony_ci
3471cb0ef41Sopenharmony_ci    return fromCache ? fromCache.catch(fromResolved) : fromResolved()
3481cb0ef41Sopenharmony_ci  }
3491cb0ef41Sopenharmony_ci
3501cb0ef41Sopenharmony_ci  cleanupCached () {
3511cb0ef41Sopenharmony_ci    return cacache.rm.content(this.cache, this.integrity, this.opts)
3521cb0ef41Sopenharmony_ci  }
3531cb0ef41Sopenharmony_ci
3541cb0ef41Sopenharmony_ci  [_empty] (path) {
3551cb0ef41Sopenharmony_ci    return getContents({ path, depth: 1 }).then(contents => Promise.all(
3561cb0ef41Sopenharmony_ci      contents.map(entry => fs.rm(entry, { recursive: true, force: true }))))
3571cb0ef41Sopenharmony_ci  }
3581cb0ef41Sopenharmony_ci
3591cb0ef41Sopenharmony_ci  async [_mkdir] (dest) {
3601cb0ef41Sopenharmony_ci    await this[_empty](dest)
3611cb0ef41Sopenharmony_ci    return await fs.mkdir(dest, { recursive: true })
3621cb0ef41Sopenharmony_ci  }
3631cb0ef41Sopenharmony_ci
3641cb0ef41Sopenharmony_ci  // extraction is always the same.  the only difference is where
3651cb0ef41Sopenharmony_ci  // the tarball comes from.
3661cb0ef41Sopenharmony_ci  async extract (dest) {
3671cb0ef41Sopenharmony_ci    await this[_mkdir](dest)
3681cb0ef41Sopenharmony_ci    return this.tarballStream((tarball) => this[_extract](dest, tarball))
3691cb0ef41Sopenharmony_ci  }
3701cb0ef41Sopenharmony_ci
3711cb0ef41Sopenharmony_ci  [_toFile] (dest) {
3721cb0ef41Sopenharmony_ci    return this.tarballStream(str => new Promise((res, rej) => {
3731cb0ef41Sopenharmony_ci      const writer = new fsm.WriteStream(dest)
3741cb0ef41Sopenharmony_ci      str.on('error', er => writer.emit('error', er))
3751cb0ef41Sopenharmony_ci      writer.on('error', er => rej(er))
3761cb0ef41Sopenharmony_ci      writer.on('close', () => res({
3771cb0ef41Sopenharmony_ci        integrity: this.integrity && String(this.integrity),
3781cb0ef41Sopenharmony_ci        resolved: this.resolved,
3791cb0ef41Sopenharmony_ci        from: this.from,
3801cb0ef41Sopenharmony_ci      }))
3811cb0ef41Sopenharmony_ci      str.pipe(writer)
3821cb0ef41Sopenharmony_ci    }))
3831cb0ef41Sopenharmony_ci  }
3841cb0ef41Sopenharmony_ci
3851cb0ef41Sopenharmony_ci  // don't use this[_mkdir] because we don't want to rimraf anything
3861cb0ef41Sopenharmony_ci  async tarballFile (dest) {
3871cb0ef41Sopenharmony_ci    const dir = dirname(dest)
3881cb0ef41Sopenharmony_ci    await fs.mkdir(dir, { recursive: true })
3891cb0ef41Sopenharmony_ci    return this[_toFile](dest)
3901cb0ef41Sopenharmony_ci  }
3911cb0ef41Sopenharmony_ci
3921cb0ef41Sopenharmony_ci  [_extract] (dest, tarball) {
3931cb0ef41Sopenharmony_ci    const extractor = tar.x(this[_tarxOptions]({ cwd: dest }))
3941cb0ef41Sopenharmony_ci    const p = new Promise((resolve, reject) => {
3951cb0ef41Sopenharmony_ci      extractor.on('end', () => {
3961cb0ef41Sopenharmony_ci        resolve({
3971cb0ef41Sopenharmony_ci          resolved: this.resolved,
3981cb0ef41Sopenharmony_ci          integrity: this.integrity && String(this.integrity),
3991cb0ef41Sopenharmony_ci          from: this.from,
4001cb0ef41Sopenharmony_ci        })
4011cb0ef41Sopenharmony_ci      })
4021cb0ef41Sopenharmony_ci
4031cb0ef41Sopenharmony_ci      extractor.on('error', er => {
4041cb0ef41Sopenharmony_ci        log.warn('tar', er.message)
4051cb0ef41Sopenharmony_ci        log.silly('tar', er)
4061cb0ef41Sopenharmony_ci        reject(er)
4071cb0ef41Sopenharmony_ci      })
4081cb0ef41Sopenharmony_ci
4091cb0ef41Sopenharmony_ci      tarball.on('error', er => reject(er))
4101cb0ef41Sopenharmony_ci    })
4111cb0ef41Sopenharmony_ci
4121cb0ef41Sopenharmony_ci    tarball.pipe(extractor)
4131cb0ef41Sopenharmony_ci    return p
4141cb0ef41Sopenharmony_ci  }
4151cb0ef41Sopenharmony_ci
4161cb0ef41Sopenharmony_ci  // always ensure that entries are at least as permissive as our configured
4171cb0ef41Sopenharmony_ci  // dmode/fmode, but never more permissive than the umask allows.
4181cb0ef41Sopenharmony_ci  [_entryMode] (path, mode, type) {
4191cb0ef41Sopenharmony_ci    const m = /Directory|GNUDumpDir/.test(type) ? this.dmode
4201cb0ef41Sopenharmony_ci      : /File$/.test(type) ? this.fmode
4211cb0ef41Sopenharmony_ci      : /* istanbul ignore next - should never happen in a pkg */ 0
4221cb0ef41Sopenharmony_ci
4231cb0ef41Sopenharmony_ci    // make sure package bins are executable
4241cb0ef41Sopenharmony_ci    const exe = isPackageBin(this.package, path) ? 0o111 : 0
4251cb0ef41Sopenharmony_ci    // always ensure that files are read/writable by the owner
4261cb0ef41Sopenharmony_ci    return ((mode | m) & ~this.umask) | exe | 0o600
4271cb0ef41Sopenharmony_ci  }
4281cb0ef41Sopenharmony_ci
4291cb0ef41Sopenharmony_ci  [_tarxOptions] ({ cwd }) {
4301cb0ef41Sopenharmony_ci    const sawIgnores = new Set()
4311cb0ef41Sopenharmony_ci    return {
4321cb0ef41Sopenharmony_ci      cwd,
4331cb0ef41Sopenharmony_ci      noChmod: true,
4341cb0ef41Sopenharmony_ci      noMtime: true,
4351cb0ef41Sopenharmony_ci      filter: (name, entry) => {
4361cb0ef41Sopenharmony_ci        if (/Link$/.test(entry.type)) {
4371cb0ef41Sopenharmony_ci          return false
4381cb0ef41Sopenharmony_ci        }
4391cb0ef41Sopenharmony_ci        entry.mode = this[_entryMode](entry.path, entry.mode, entry.type)
4401cb0ef41Sopenharmony_ci        // this replicates the npm pack behavior where .gitignore files
4411cb0ef41Sopenharmony_ci        // are treated like .npmignore files, but only if a .npmignore
4421cb0ef41Sopenharmony_ci        // file is not present.
4431cb0ef41Sopenharmony_ci        if (/File$/.test(entry.type)) {
4441cb0ef41Sopenharmony_ci          const base = basename(entry.path)
4451cb0ef41Sopenharmony_ci          if (base === '.npmignore') {
4461cb0ef41Sopenharmony_ci            sawIgnores.add(entry.path)
4471cb0ef41Sopenharmony_ci          } else if (base === '.gitignore' && !this.allowGitIgnore) {
4481cb0ef41Sopenharmony_ci            // rename, but only if there's not already a .npmignore
4491cb0ef41Sopenharmony_ci            const ni = entry.path.replace(/\.gitignore$/, '.npmignore')
4501cb0ef41Sopenharmony_ci            if (sawIgnores.has(ni)) {
4511cb0ef41Sopenharmony_ci              return false
4521cb0ef41Sopenharmony_ci            }
4531cb0ef41Sopenharmony_ci            entry.path = ni
4541cb0ef41Sopenharmony_ci          }
4551cb0ef41Sopenharmony_ci          return true
4561cb0ef41Sopenharmony_ci        }
4571cb0ef41Sopenharmony_ci      },
4581cb0ef41Sopenharmony_ci      strip: 1,
4591cb0ef41Sopenharmony_ci      onwarn: /* istanbul ignore next - we can trust that tar logs */
4601cb0ef41Sopenharmony_ci      (code, msg, data) => {
4611cb0ef41Sopenharmony_ci        log.warn('tar', code, msg)
4621cb0ef41Sopenharmony_ci        log.silly('tar', code, msg, data)
4631cb0ef41Sopenharmony_ci      },
4641cb0ef41Sopenharmony_ci      umask: this.umask,
4651cb0ef41Sopenharmony_ci      // always ignore ownership info from tarball metadata
4661cb0ef41Sopenharmony_ci      preserveOwner: false,
4671cb0ef41Sopenharmony_ci    }
4681cb0ef41Sopenharmony_ci  }
4691cb0ef41Sopenharmony_ci}
4701cb0ef41Sopenharmony_ci
4711cb0ef41Sopenharmony_cimodule.exports = FetcherBase
4721cb0ef41Sopenharmony_ci
4731cb0ef41Sopenharmony_ci// Child classes
4741cb0ef41Sopenharmony_ciconst GitFetcher = require('./git.js')
4751cb0ef41Sopenharmony_ciconst RegistryFetcher = require('./registry.js')
4761cb0ef41Sopenharmony_ciconst FileFetcher = require('./file.js')
4771cb0ef41Sopenharmony_ciconst DirFetcher = require('./dir.js')
4781cb0ef41Sopenharmony_ciconst RemoteFetcher = require('./remote.js')
4791cb0ef41Sopenharmony_ci
4801cb0ef41Sopenharmony_ci// Get an appropriate fetcher object from a spec and options
4811cb0ef41Sopenharmony_ciFetcherBase.get = (rawSpec, opts = {}) => {
4821cb0ef41Sopenharmony_ci  const spec = npa(rawSpec, opts.where)
4831cb0ef41Sopenharmony_ci  switch (spec.type) {
4841cb0ef41Sopenharmony_ci    case 'git':
4851cb0ef41Sopenharmony_ci      return new GitFetcher(spec, opts)
4861cb0ef41Sopenharmony_ci
4871cb0ef41Sopenharmony_ci    case 'remote':
4881cb0ef41Sopenharmony_ci      return new RemoteFetcher(spec, opts)
4891cb0ef41Sopenharmony_ci
4901cb0ef41Sopenharmony_ci    case 'version':
4911cb0ef41Sopenharmony_ci    case 'range':
4921cb0ef41Sopenharmony_ci    case 'tag':
4931cb0ef41Sopenharmony_ci    case 'alias':
4941cb0ef41Sopenharmony_ci      return new RegistryFetcher(spec.subSpec || spec, opts)
4951cb0ef41Sopenharmony_ci
4961cb0ef41Sopenharmony_ci    case 'file':
4971cb0ef41Sopenharmony_ci      return new FileFetcher(spec, opts)
4981cb0ef41Sopenharmony_ci
4991cb0ef41Sopenharmony_ci    case 'directory':
5001cb0ef41Sopenharmony_ci      return new DirFetcher(spec, opts)
5011cb0ef41Sopenharmony_ci
5021cb0ef41Sopenharmony_ci    default:
5031cb0ef41Sopenharmony_ci      throw new TypeError('Unknown spec type: ' + spec.type)
5041cb0ef41Sopenharmony_ci  }
5051cb0ef41Sopenharmony_ci}
506