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