11cb0ef41Sopenharmony_ci'use strict'
21cb0ef41Sopenharmony_ci
31cb0ef41Sopenharmony_ciconst { HttpErrorAuthOTP } = require('./errors.js')
41cb0ef41Sopenharmony_ciconst checkResponse = require('./check-response.js')
51cb0ef41Sopenharmony_ciconst getAuth = require('./auth.js')
61cb0ef41Sopenharmony_ciconst fetch = require('make-fetch-happen')
71cb0ef41Sopenharmony_ciconst JSONStream = require('minipass-json-stream')
81cb0ef41Sopenharmony_ciconst npa = require('npm-package-arg')
91cb0ef41Sopenharmony_ciconst qs = require('querystring')
101cb0ef41Sopenharmony_ciconst url = require('url')
111cb0ef41Sopenharmony_ciconst zlib = require('minizlib')
121cb0ef41Sopenharmony_ciconst { Minipass } = require('minipass')
131cb0ef41Sopenharmony_ci
141cb0ef41Sopenharmony_ciconst defaultOpts = require('./default-opts.js')
151cb0ef41Sopenharmony_ci
161cb0ef41Sopenharmony_ci// WhatWG URL throws if it's not fully resolved
171cb0ef41Sopenharmony_ciconst urlIsValid = u => {
181cb0ef41Sopenharmony_ci  try {
191cb0ef41Sopenharmony_ci    return !!new url.URL(u)
201cb0ef41Sopenharmony_ci  } catch (_) {
211cb0ef41Sopenharmony_ci    return false
221cb0ef41Sopenharmony_ci  }
231cb0ef41Sopenharmony_ci}
241cb0ef41Sopenharmony_ci
251cb0ef41Sopenharmony_cimodule.exports = regFetch
261cb0ef41Sopenharmony_cifunction regFetch (uri, /* istanbul ignore next */ opts_ = {}) {
271cb0ef41Sopenharmony_ci  const opts = {
281cb0ef41Sopenharmony_ci    ...defaultOpts,
291cb0ef41Sopenharmony_ci    ...opts_,
301cb0ef41Sopenharmony_ci  }
311cb0ef41Sopenharmony_ci
321cb0ef41Sopenharmony_ci  // if we did not get a fully qualified URI, then we look at the registry
331cb0ef41Sopenharmony_ci  // config or relevant scope to resolve it.
341cb0ef41Sopenharmony_ci  const uriValid = urlIsValid(uri)
351cb0ef41Sopenharmony_ci  let registry = opts.registry || defaultOpts.registry
361cb0ef41Sopenharmony_ci  if (!uriValid) {
371cb0ef41Sopenharmony_ci    registry = opts.registry = (
381cb0ef41Sopenharmony_ci      (opts.spec && pickRegistry(opts.spec, opts)) ||
391cb0ef41Sopenharmony_ci      opts.registry ||
401cb0ef41Sopenharmony_ci      registry
411cb0ef41Sopenharmony_ci    )
421cb0ef41Sopenharmony_ci    uri = `${
431cb0ef41Sopenharmony_ci      registry.trim().replace(/\/?$/g, '')
441cb0ef41Sopenharmony_ci    }/${
451cb0ef41Sopenharmony_ci      uri.trim().replace(/^\//, '')
461cb0ef41Sopenharmony_ci    }`
471cb0ef41Sopenharmony_ci    // asserts that this is now valid
481cb0ef41Sopenharmony_ci    new url.URL(uri)
491cb0ef41Sopenharmony_ci  }
501cb0ef41Sopenharmony_ci
511cb0ef41Sopenharmony_ci  const method = opts.method || 'GET'
521cb0ef41Sopenharmony_ci
531cb0ef41Sopenharmony_ci  // through that takes into account the scope, the prefix of `uri`, etc
541cb0ef41Sopenharmony_ci  const startTime = Date.now()
551cb0ef41Sopenharmony_ci  const auth = getAuth(uri, opts)
561cb0ef41Sopenharmony_ci  const headers = getHeaders(uri, auth, opts)
571cb0ef41Sopenharmony_ci  let body = opts.body
581cb0ef41Sopenharmony_ci  const bodyIsStream = Minipass.isStream(body)
591cb0ef41Sopenharmony_ci  const bodyIsPromise = body &&
601cb0ef41Sopenharmony_ci    typeof body === 'object' &&
611cb0ef41Sopenharmony_ci    typeof body.then === 'function'
621cb0ef41Sopenharmony_ci
631cb0ef41Sopenharmony_ci  if (
641cb0ef41Sopenharmony_ci    body && !bodyIsStream && !bodyIsPromise && typeof body !== 'string' && !Buffer.isBuffer(body)
651cb0ef41Sopenharmony_ci  ) {
661cb0ef41Sopenharmony_ci    headers['content-type'] = headers['content-type'] || 'application/json'
671cb0ef41Sopenharmony_ci    body = JSON.stringify(body)
681cb0ef41Sopenharmony_ci  } else if (body && !headers['content-type']) {
691cb0ef41Sopenharmony_ci    headers['content-type'] = 'application/octet-stream'
701cb0ef41Sopenharmony_ci  }
711cb0ef41Sopenharmony_ci
721cb0ef41Sopenharmony_ci  if (opts.gzip) {
731cb0ef41Sopenharmony_ci    headers['content-encoding'] = 'gzip'
741cb0ef41Sopenharmony_ci    if (bodyIsStream) {
751cb0ef41Sopenharmony_ci      const gz = new zlib.Gzip()
761cb0ef41Sopenharmony_ci      body.on('error', /* istanbul ignore next: unlikely and hard to test */
771cb0ef41Sopenharmony_ci        err => gz.emit('error', err))
781cb0ef41Sopenharmony_ci      body = body.pipe(gz)
791cb0ef41Sopenharmony_ci    } else if (!bodyIsPromise) {
801cb0ef41Sopenharmony_ci      body = new zlib.Gzip().end(body).concat()
811cb0ef41Sopenharmony_ci    }
821cb0ef41Sopenharmony_ci  }
831cb0ef41Sopenharmony_ci
841cb0ef41Sopenharmony_ci  const parsed = new url.URL(uri)
851cb0ef41Sopenharmony_ci
861cb0ef41Sopenharmony_ci  if (opts.query) {
871cb0ef41Sopenharmony_ci    const q = typeof opts.query === 'string' ? qs.parse(opts.query)
881cb0ef41Sopenharmony_ci      : opts.query
891cb0ef41Sopenharmony_ci
901cb0ef41Sopenharmony_ci    Object.keys(q).forEach(key => {
911cb0ef41Sopenharmony_ci      if (q[key] !== undefined) {
921cb0ef41Sopenharmony_ci        parsed.searchParams.set(key, q[key])
931cb0ef41Sopenharmony_ci      }
941cb0ef41Sopenharmony_ci    })
951cb0ef41Sopenharmony_ci    uri = url.format(parsed)
961cb0ef41Sopenharmony_ci  }
971cb0ef41Sopenharmony_ci
981cb0ef41Sopenharmony_ci  if (parsed.searchParams.get('write') === 'true' && method === 'GET') {
991cb0ef41Sopenharmony_ci    // do not cache, because this GET is fetching a rev that will be
1001cb0ef41Sopenharmony_ci    // used for a subsequent PUT or DELETE, so we need to conditionally
1011cb0ef41Sopenharmony_ci    // update cache.
1021cb0ef41Sopenharmony_ci    opts.offline = false
1031cb0ef41Sopenharmony_ci    opts.preferOffline = false
1041cb0ef41Sopenharmony_ci    opts.preferOnline = true
1051cb0ef41Sopenharmony_ci  }
1061cb0ef41Sopenharmony_ci
1071cb0ef41Sopenharmony_ci  const doFetch = async fetchBody => {
1081cb0ef41Sopenharmony_ci    const p = fetch(uri, {
1091cb0ef41Sopenharmony_ci      agent: opts.agent,
1101cb0ef41Sopenharmony_ci      algorithms: opts.algorithms,
1111cb0ef41Sopenharmony_ci      body: fetchBody,
1121cb0ef41Sopenharmony_ci      cache: getCacheMode(opts),
1131cb0ef41Sopenharmony_ci      cachePath: opts.cache,
1141cb0ef41Sopenharmony_ci      ca: opts.ca,
1151cb0ef41Sopenharmony_ci      cert: auth.cert || opts.cert,
1161cb0ef41Sopenharmony_ci      headers,
1171cb0ef41Sopenharmony_ci      integrity: opts.integrity,
1181cb0ef41Sopenharmony_ci      key: auth.key || opts.key,
1191cb0ef41Sopenharmony_ci      localAddress: opts.localAddress,
1201cb0ef41Sopenharmony_ci      maxSockets: opts.maxSockets,
1211cb0ef41Sopenharmony_ci      memoize: opts.memoize,
1221cb0ef41Sopenharmony_ci      method: method,
1231cb0ef41Sopenharmony_ci      noProxy: opts.noProxy,
1241cb0ef41Sopenharmony_ci      proxy: opts.httpsProxy || opts.proxy,
1251cb0ef41Sopenharmony_ci      retry: opts.retry ? opts.retry : {
1261cb0ef41Sopenharmony_ci        retries: opts.fetchRetries,
1271cb0ef41Sopenharmony_ci        factor: opts.fetchRetryFactor,
1281cb0ef41Sopenharmony_ci        minTimeout: opts.fetchRetryMintimeout,
1291cb0ef41Sopenharmony_ci        maxTimeout: opts.fetchRetryMaxtimeout,
1301cb0ef41Sopenharmony_ci      },
1311cb0ef41Sopenharmony_ci      strictSSL: opts.strictSSL,
1321cb0ef41Sopenharmony_ci      timeout: opts.timeout || 30 * 1000,
1331cb0ef41Sopenharmony_ci    }).then(res => checkResponse({
1341cb0ef41Sopenharmony_ci      method,
1351cb0ef41Sopenharmony_ci      uri,
1361cb0ef41Sopenharmony_ci      res,
1371cb0ef41Sopenharmony_ci      registry,
1381cb0ef41Sopenharmony_ci      startTime,
1391cb0ef41Sopenharmony_ci      auth,
1401cb0ef41Sopenharmony_ci      opts,
1411cb0ef41Sopenharmony_ci    }))
1421cb0ef41Sopenharmony_ci
1431cb0ef41Sopenharmony_ci    if (typeof opts.otpPrompt === 'function') {
1441cb0ef41Sopenharmony_ci      return p.catch(async er => {
1451cb0ef41Sopenharmony_ci        if (er instanceof HttpErrorAuthOTP) {
1461cb0ef41Sopenharmony_ci          let otp
1471cb0ef41Sopenharmony_ci          // if otp fails to complete, we fail with that failure
1481cb0ef41Sopenharmony_ci          try {
1491cb0ef41Sopenharmony_ci            otp = await opts.otpPrompt()
1501cb0ef41Sopenharmony_ci          } catch (_) {
1511cb0ef41Sopenharmony_ci            // ignore this error
1521cb0ef41Sopenharmony_ci          }
1531cb0ef41Sopenharmony_ci          // if no otp provided, or otpPrompt errored, throw the original HTTP error
1541cb0ef41Sopenharmony_ci          if (!otp) {
1551cb0ef41Sopenharmony_ci            throw er
1561cb0ef41Sopenharmony_ci          }
1571cb0ef41Sopenharmony_ci          return regFetch(uri, { ...opts, otp })
1581cb0ef41Sopenharmony_ci        }
1591cb0ef41Sopenharmony_ci        throw er
1601cb0ef41Sopenharmony_ci      })
1611cb0ef41Sopenharmony_ci    } else {
1621cb0ef41Sopenharmony_ci      return p
1631cb0ef41Sopenharmony_ci    }
1641cb0ef41Sopenharmony_ci  }
1651cb0ef41Sopenharmony_ci
1661cb0ef41Sopenharmony_ci  return Promise.resolve(body).then(doFetch)
1671cb0ef41Sopenharmony_ci}
1681cb0ef41Sopenharmony_ci
1691cb0ef41Sopenharmony_cimodule.exports.getAuth = getAuth
1701cb0ef41Sopenharmony_ci
1711cb0ef41Sopenharmony_cimodule.exports.json = fetchJSON
1721cb0ef41Sopenharmony_cifunction fetchJSON (uri, opts) {
1731cb0ef41Sopenharmony_ci  return regFetch(uri, opts).then(res => res.json())
1741cb0ef41Sopenharmony_ci}
1751cb0ef41Sopenharmony_ci
1761cb0ef41Sopenharmony_cimodule.exports.json.stream = fetchJSONStream
1771cb0ef41Sopenharmony_cifunction fetchJSONStream (uri, jsonPath,
1781cb0ef41Sopenharmony_ci  /* istanbul ignore next */ opts_ = {}) {
1791cb0ef41Sopenharmony_ci  const opts = { ...defaultOpts, ...opts_ }
1801cb0ef41Sopenharmony_ci  const parser = JSONStream.parse(jsonPath, opts.mapJSON)
1811cb0ef41Sopenharmony_ci  regFetch(uri, opts).then(res =>
1821cb0ef41Sopenharmony_ci    res.body.on('error',
1831cb0ef41Sopenharmony_ci      /* istanbul ignore next: unlikely and difficult to test */
1841cb0ef41Sopenharmony_ci      er => parser.emit('error', er)).pipe(parser)
1851cb0ef41Sopenharmony_ci  ).catch(er => parser.emit('error', er))
1861cb0ef41Sopenharmony_ci  return parser
1871cb0ef41Sopenharmony_ci}
1881cb0ef41Sopenharmony_ci
1891cb0ef41Sopenharmony_cimodule.exports.pickRegistry = pickRegistry
1901cb0ef41Sopenharmony_cifunction pickRegistry (spec, opts = {}) {
1911cb0ef41Sopenharmony_ci  spec = npa(spec)
1921cb0ef41Sopenharmony_ci  let registry = spec.scope &&
1931cb0ef41Sopenharmony_ci    opts[spec.scope.replace(/^@?/, '@') + ':registry']
1941cb0ef41Sopenharmony_ci
1951cb0ef41Sopenharmony_ci  if (!registry && opts.scope) {
1961cb0ef41Sopenharmony_ci    registry = opts[opts.scope.replace(/^@?/, '@') + ':registry']
1971cb0ef41Sopenharmony_ci  }
1981cb0ef41Sopenharmony_ci
1991cb0ef41Sopenharmony_ci  if (!registry) {
2001cb0ef41Sopenharmony_ci    registry = opts.registry || defaultOpts.registry
2011cb0ef41Sopenharmony_ci  }
2021cb0ef41Sopenharmony_ci
2031cb0ef41Sopenharmony_ci  return registry
2041cb0ef41Sopenharmony_ci}
2051cb0ef41Sopenharmony_ci
2061cb0ef41Sopenharmony_cifunction getCacheMode (opts) {
2071cb0ef41Sopenharmony_ci  return opts.offline ? 'only-if-cached'
2081cb0ef41Sopenharmony_ci    : opts.preferOffline ? 'force-cache'
2091cb0ef41Sopenharmony_ci    : opts.preferOnline ? 'no-cache'
2101cb0ef41Sopenharmony_ci    : 'default'
2111cb0ef41Sopenharmony_ci}
2121cb0ef41Sopenharmony_ci
2131cb0ef41Sopenharmony_cifunction getHeaders (uri, auth, opts) {
2141cb0ef41Sopenharmony_ci  const headers = Object.assign({
2151cb0ef41Sopenharmony_ci    'user-agent': opts.userAgent,
2161cb0ef41Sopenharmony_ci  }, opts.headers || {})
2171cb0ef41Sopenharmony_ci
2181cb0ef41Sopenharmony_ci  if (opts.authType) {
2191cb0ef41Sopenharmony_ci    headers['npm-auth-type'] = opts.authType
2201cb0ef41Sopenharmony_ci  }
2211cb0ef41Sopenharmony_ci
2221cb0ef41Sopenharmony_ci  if (opts.scope) {
2231cb0ef41Sopenharmony_ci    headers['npm-scope'] = opts.scope
2241cb0ef41Sopenharmony_ci  }
2251cb0ef41Sopenharmony_ci
2261cb0ef41Sopenharmony_ci  if (opts.npmSession) {
2271cb0ef41Sopenharmony_ci    headers['npm-session'] = opts.npmSession
2281cb0ef41Sopenharmony_ci  }
2291cb0ef41Sopenharmony_ci
2301cb0ef41Sopenharmony_ci  if (opts.npmCommand) {
2311cb0ef41Sopenharmony_ci    headers['npm-command'] = opts.npmCommand
2321cb0ef41Sopenharmony_ci  }
2331cb0ef41Sopenharmony_ci
2341cb0ef41Sopenharmony_ci  // If a tarball is hosted on a different place than the manifest, only send
2351cb0ef41Sopenharmony_ci  // credentials on `alwaysAuth`
2361cb0ef41Sopenharmony_ci  if (auth.token) {
2371cb0ef41Sopenharmony_ci    headers.authorization = `Bearer ${auth.token}`
2381cb0ef41Sopenharmony_ci  } else if (auth.auth) {
2391cb0ef41Sopenharmony_ci    headers.authorization = `Basic ${auth.auth}`
2401cb0ef41Sopenharmony_ci  }
2411cb0ef41Sopenharmony_ci
2421cb0ef41Sopenharmony_ci  if (opts.otp) {
2431cb0ef41Sopenharmony_ci    headers['npm-otp'] = opts.otp
2441cb0ef41Sopenharmony_ci  }
2451cb0ef41Sopenharmony_ci
2461cb0ef41Sopenharmony_ci  return headers
2471cb0ef41Sopenharmony_ci}
2481cb0ef41Sopenharmony_ci
2491cb0ef41Sopenharmony_cimodule.exports.cleanUrl = require('./clean-url.js')
250