diff --git a/node_modules/.gitignore b/node_modules/.gitignore index 2ae24fcf8a16f..851e54092990b 100644 --- a/node_modules/.gitignore +++ b/node_modules/.gitignore @@ -18,6 +18,7 @@ !/@isaacs/string-locale-compare !/@npmcli/ /@npmcli/* +!/@npmcli/agent !/@npmcli/disparity-colors !/@npmcli/fs !/@npmcli/git @@ -193,12 +194,22 @@ !/npm-packlist !/npm-pick-manifest !/npm-profile +!/npm-profile/node_modules/ +/npm-profile/node_modules/* +!/npm-profile/node_modules/npm-registry-fetch !/npm-registry-fetch +!/npm-registry-fetch/node_modules/ +/npm-registry-fetch/node_modules/* +!/npm-registry-fetch/node_modules/make-fetch-happen +!/npm-registry-fetch/node_modules/minipass !/npm-user-validate !/npmlog !/once !/p-map !/pacote +!/pacote/node_modules/ +/pacote/node_modules/* +!/pacote/node_modules/npm-registry-fetch !/parse-conflict-json !/path-is-absolute !/path-key diff --git a/node_modules/@npmcli/agent/lib/dns.js b/node_modules/@npmcli/agent/lib/dns.js new file mode 100644 index 0000000000000..10dcb8d471d10 --- /dev/null +++ b/node_modules/@npmcli/agent/lib/dns.js @@ -0,0 +1,51 @@ +'use strict' + +const LRUCache = require('lru-cache') +const dns = require('dns') + +const defaultOptions = exports.defaultOptions = { + family: undefined, + hints: dns.ADDRCONFIG, + all: false, + verbatim: undefined, +} + +const lookupCache = exports.lookupCache = new LRUCache({ max: 50 }) + +// this is a factory so that each request can have its own opts (i.e. ttl) +// while still sharing the cache across all requests +exports.getLookup = (dnsOptions) => { + return (hostname, options, callback) => { + if (typeof options === 'function') { + callback = options + options = null + } else if (typeof options === 'number') { + options = { family: options } + } + + options = { ...defaultOptions, ...options } + + const key = JSON.stringify({ + hostname, + family: options.family, + hints: options.hints, + all: options.all, + verbatim: options.verbatim, + }) + + if (lookupCache.has(key)) { + const [address, family] = lookupCache.get(key) + process.nextTick(callback, null, address, family) + return + } + + dnsOptions.lookup(hostname, options, (err, address, family) => { + if (err) { + return callback(err) + } + + lookupCache.set(key, [address, family], { ttl: dnsOptions.ttl }) + return callback(null, address, family) + }) + } +} diff --git a/node_modules/@npmcli/agent/lib/errors.js b/node_modules/@npmcli/agent/lib/errors.js new file mode 100644 index 0000000000000..9c664aeb39757 --- /dev/null +++ b/node_modules/@npmcli/agent/lib/errors.js @@ -0,0 +1,71 @@ +'use strict' + +class InvalidProxyProtocolError extends Error { + constructor (url) { + super(`Invalid protocol \`${url.protocol}\` connecting to proxy \`${url.host}\``) + this.code = 'EINVALIDPROXY' + this.proxy = url + } +} + +class InvalidProxyResponseError extends Error { + constructor (url, status) { + super(`Invalid status code \`${status}\` connecting to proxy \`${url.host}\``) + this.code = 'EINVALIDRESPONSE' + this.proxy = url + this.status = status + } +} + +class ConnectionTimeoutError extends Error { + constructor (host) { + super(`Timeout connecting to host \`${host}\``) + this.code = 'ECONNECTIONTIMEOUT' + this.host = host + } +} + +class IdleTimeoutError extends Error { + constructor (host) { + super(`Idle timeout reached for host \`${host}\``) + this.code = 'EIDLETIMEOUT' + this.host = host + } +} + +class ResponseTimeoutError extends Error { + constructor (proxy, request) { + let msg = 'Response timeout ' + if (proxy.url) { + msg += `from proxy \`${proxy.url.host}\` ` + } + msg += `connecting to host \`${request.host}\`` + super(msg) + this.code = 'ERESPONSETIMEOUT' + this.proxy = proxy.url + this.request = request + } +} + +class TransferTimeoutError extends Error { + constructor (proxy, request) { + let msg = 'Transfer timeout ' + if (proxy.url) { + msg += `from proxy \`${proxy.url.host}\` ` + } + msg += `for \`${request.host}\`` + super(msg) + this.code = 'ETRANSFERTIMEOUT' + this.proxy = proxy.url + this.request = request + } +} + +module.exports = { + InvalidProxyProtocolError, + InvalidProxyResponseError, + ConnectionTimeoutError, + IdleTimeoutError, + ResponseTimeoutError, + TransferTimeoutError, +} diff --git a/node_modules/@npmcli/agent/lib/http.js b/node_modules/@npmcli/agent/lib/http.js new file mode 100644 index 0000000000000..23512393caf3f --- /dev/null +++ b/node_modules/@npmcli/agent/lib/http.js @@ -0,0 +1,33 @@ +'use strict' + +const http = require('http') + +const { getLookup } = require('./dns.js') +const { normalizeOptions } = require('./util.js') +const createProxy = require('./proxy/index.js') + +class HttpAgent extends http.Agent { + constructor (_options = {}) { + const options = normalizeOptions(_options) + super(options) + this.proxy = createProxy({ + agent: this, + lookup: getLookup(options.dns), + proxy: options.proxy, + secure: false, + }) + } + + createConnection (_options, callback) { + const options = normalizeOptions(_options) + return this.proxy.createConnection(options, callback) + } + + addRequest (request, _options) { + const options = normalizeOptions(_options) + super.addRequest(request, _options) + return this.proxy.addRequest(request, options) + } +} + +module.exports = HttpAgent diff --git a/node_modules/@npmcli/agent/lib/https.js b/node_modules/@npmcli/agent/lib/https.js new file mode 100644 index 0000000000000..b544614d7f47f --- /dev/null +++ b/node_modules/@npmcli/agent/lib/https.js @@ -0,0 +1,33 @@ +'use strict' + +const https = require('https') + +const { getLookup } = require('./dns.js') +const { normalizeOptions } = require('./util.js') +const createProxy = require('./proxy/index.js') + +class HttpsAgent extends https.Agent { + constructor (_options) { + const options = normalizeOptions(_options) + super(options) + this.proxy = createProxy({ + agent: this, + lookup: getLookup(options.dns), + proxy: options.proxy, + secure: true, + }) + } + + createConnection (_options, callback) { + const options = normalizeOptions(_options) + return this.proxy.createConnection(options, callback) + } + + addRequest (request, _options) { + const options = normalizeOptions(_options) + super.addRequest(request, options) + return this.proxy.addRequest(request, options) + } +} + +module.exports = HttpsAgent diff --git a/node_modules/@npmcli/agent/lib/index.js b/node_modules/@npmcli/agent/lib/index.js new file mode 100644 index 0000000000000..a6f556964d86d --- /dev/null +++ b/node_modules/@npmcli/agent/lib/index.js @@ -0,0 +1,135 @@ +'use strict' + +const { normalizeOptions } = require('./util.js') +const HttpAgent = require('./http.js') +const HttpsAgent = require('./https.js') + +const AgentCache = new Map() + +const proxyEnv = {} +for (const [key, value] of Object.entries(process.env)) { + const lowerKey = key.toLowerCase() + if (['https_proxy', 'http_proxy', 'proxy', 'no_proxy'].includes(lowerKey)) { + proxyEnv[lowerKey] = value + } +} + +const getAgent = (url, options) => { + url = new URL(url) + options = normalizeOptions(options) + + // false has meaning so this can't be a simple truthiness check + if (options.agent != null) { + return options.agent + } + + const isHttps = url.protocol === 'https:' + + let proxy = options.proxy + if (!proxy) { + proxy = isHttps + ? proxyEnv.https_proxy + : (proxyEnv.https_proxy || proxyEnv.http_proxy || proxyEnv.proxy) + } + + if (proxy) { + proxy = new URL(proxy) + let noProxy = options.noProxy || proxyEnv.no_proxy + if (typeof noProxy === 'string') { + noProxy = noProxy.split(',').map((p) => p.trim()) + } + + if (noProxy) { + const hostSegments = url.hostname.split('.').reverse() + const matches = noProxy.some((no) => { + const noSegments = no.split('.').filter(Boolean).reverse() + if (!noSegments.length) { + return false + } + + for (let i = 0; i < noSegments.length; ++i) { + if (hostSegments[i] !== noSegments[i]) { + return false + } + } + + return true + }) + + if (matches) { + proxy = '' + } + } + } + + const timeouts = [ + options.timeouts.connection || 0, + options.timeouts.idle || 0, + options.timeouts.response || 0, + options.timeouts.transfer || 0, + ].join('.') + + const maxSockets = options.maxSockets || 15 + + let proxyDescriptor = 'proxy:' + if (!proxy) { + proxyDescriptor += 'null' + } else { + proxyDescriptor += `${proxy.protocol}//` + let auth = '' + + if (proxy.username) { + auth += proxy.username + } + + if (proxy.password) { + auth += `:${proxy.password}` + } + + if (auth) { + proxyDescriptor += `${auth}@` + } + + proxyDescriptor += proxy.host + } + + const key = [ + `https:${isHttps}`, + proxyDescriptor, + `local-address:${options.localAddress || 'null'}`, + `strict-ssl:${isHttps ? options.rejectUnauthorized : 'false'}`, + `ca:${isHttps && options.ca || 'null'}`, + `cert:${isHttps && options.cert || 'null'}`, + `key:${isHttps && options.key || 'null'}`, + `timeouts:${timeouts}`, + `maxSockets:${maxSockets}`, + ].join(':') + + if (AgentCache.has(key)) { + return AgentCache.get(key) + } + + const agentOptions = { + ca: options.ca, + cert: options.cert, + key: options.key, + rejectUnauthorized: options.rejectUnauthorized, + maxSockets, + timeouts: options.timeouts, + localAddress: options.localAddress, + proxy, + } + + const agent = isHttps + ? new HttpsAgent(agentOptions) + : new HttpAgent(agentOptions) + + AgentCache.set(key, agent) + return agent +} + +module.exports = { + getAgent, + HttpAgent, + HttpsAgent, +} diff --git a/node_modules/@npmcli/agent/lib/proxy/http.js b/node_modules/@npmcli/agent/lib/proxy/http.js new file mode 100644 index 0000000000000..8d092e963c084 --- /dev/null +++ b/node_modules/@npmcli/agent/lib/proxy/http.js @@ -0,0 +1,146 @@ +'use strict' + +const http = require('http') +const https = require('https') +const net = require('net') +const tls = require('tls') + +const { + ConnectionTimeoutError, + IdleTimeoutError, + InvalidProxyResponseError, + ResponseTimeoutError, + TransferTimeoutError, +} = require('../errors.js') + +// this proxy class uses the http CONNECT method +class HttpProxy { + constructor ({ agent, lookup, url, secure }) { + this.agent = agent + this.lookup = lookup + this.url = url + this.secure = secure + } + + createConnection (options, callback) { + const requestOptions = { + // pass createConnection so this request doesn't go through an agent + createConnection: (opts, cb) => { + // delete the path first, otherwise (net|tls).connect will try to open a unix socket + delete opts.path + // we also delete the timeout since we control it ourselves + delete opts.timeout + opts.family = this.agent.options.family + opts.lookup = this.lookup + + if (this.url.protocol === 'https:') { + return tls.connect(opts, cb) + } + + return net.connect(opts, cb) + }, + method: 'CONNECT', + host: this.url.hostname, + port: this.url.port, + servername: this.url.hostname, + path: `${options.host}:${options.port}`, + setHost: false, + timeout: options.timeout, + headers: { + connection: this.agent.keepAlive ? 'keep-alive' : 'close', + host: `${options.host}:${options.port}`, + }, + rejectUnauthorized: options.rejectUnauthorized, + } + + if (this.url.username || this.url.password) { + const username = decodeURIComponent(this.url.username) + const password = decodeURIComponent(this.url.password) + requestOptions.headers['proxy-authentication'] = + Buffer.from(`${username}:${password}`).toString('base64') + } + + let connectionTimeout + + const onConnect = (res, socket) => { + clearTimeout(connectionTimeout) + req.removeListener('error', onError) + + if (res.statusCode !== 200) { + return callback(new InvalidProxyResponseError(this.url, res.statusCode)) + } + + if (this.secure) { + socket = tls.connect({ ...options, socket }) + } + + socket.setKeepAlive(this.agent.keepAlive, this.agent.keepAliveMsecs) + socket.setNoDelay(this.agent.keepAlive) + + if (options.timeouts.idle) { + socket.setTimeout(options.timeouts.idle) + socket.once('timeout', () => { + socket.destroy(new IdleTimeoutError(this.url.host)) + }) + } + + return callback(null, socket) + } + + const onError = (err) => { + req.removeListener('connect', onConnect) + return callback(err) + } + + const req = this.secure + ? https.request(requestOptions) + : http.request(requestOptions) + + req.once('connect', onConnect) + req.once('error', onError) + req.end() + + if (options.timeouts.connection) { + connectionTimeout = setTimeout(() => { + return callback(new ConnectionTimeoutError(this.url.host)) + }, options.timeouts.connection) + } + } + + addRequest (request, options) { + if (this.agent.options.timeouts.response) { + let responseTimeout + + const onFinish = () => { + responseTimeout = setTimeout(() => { + request.destroy(new ResponseTimeoutError(this, request)) + }, this.agent.options.timeouts.response) + } + + const onResponse = () => { + clearTimeout(responseTimeout) + } + + request.once('finish', onFinish) + request.once('response', onResponse) + } + + if (this.agent.options.timeouts.transfer) { + let transferTimeout + + const onResponse = (res) => { + transferTimeout = setTimeout(() => { + res.destroy(new TransferTimeoutError(this, request)) + }, this.agent.options.timeouts.transfer) + + res.once('close', () => { + clearTimeout(transferTimeout) + }) + } + + request.once('response', onResponse) + } + } +} + +module.exports = HttpProxy diff --git a/node_modules/@npmcli/agent/lib/proxy/index.js b/node_modules/@npmcli/agent/lib/proxy/index.js new file mode 100644 index 0000000000000..87f628c5bbf94 --- /dev/null +++ b/node_modules/@npmcli/agent/lib/proxy/index.js @@ -0,0 +1,25 @@ +'use strict' + +const { InvalidProxyProtocolError } = require('../errors.js') +const HttpProxy = require('./http.js') +const NullProxy = require('./null.js') +const SocksProxy = require('./socks.js') + +const createProxy = ({ agent, lookup, proxy, secure }) => { + if (!proxy) { + return new NullProxy({ agent, lookup, secure }) + } + + const parsed = new URL(proxy) + if (parsed.protocol === 'http:' || parsed.protocol === 'https:') { + return new HttpProxy({ agent, lookup, url: parsed, secure }) + } + + if (parsed.protocol.startsWith('socks')) { + return new SocksProxy({ agent, lookup, url: parsed, secure }) + } + + throw new InvalidProxyProtocolError(parsed) +} + +module.exports = createProxy diff --git a/node_modules/@npmcli/agent/lib/proxy/null.js b/node_modules/@npmcli/agent/lib/proxy/null.js new file mode 100644 index 0000000000000..d2b2f6f777e92 --- /dev/null +++ b/node_modules/@npmcli/agent/lib/proxy/null.js @@ -0,0 +1,97 @@ +'use strict' + +const net = require('net') +const tls = require('tls') + +const { + ConnectionTimeoutError, + IdleTimeoutError, + ResponseTimeoutError, + TransferTimeoutError, +} = require('../errors.js') + +class NullProxy { + constructor ({ agent, lookup, secure }) { + this.agent = agent + this.lookup = lookup + this.secure = secure + } + + createConnection (options, callback) { + const socket = this.secure + ? tls.connect({ ...options, family: this.agent.options.family, lookup: this.lookup }) + : net.connect({ ...options, family: this.agent.options.family, lookup: this.lookup }) + + socket.setKeepAlive(this.agent.keepAlive, this.agent.keepAliveMsecs) + socket.setNoDelay(this.agent.keepAlive) + + let connectionTimeout + + if (options.timeouts.connection) { + connectionTimeout = setTimeout(() => { + callback(new ConnectionTimeoutError(options.host)) + }, options.timeouts.connection) + } + + if (options.timeouts.idle) { + socket.setTimeout(options.timeouts.idle) + socket.once('timeout', () => { + socket.destroy(new IdleTimeoutError(options.host)) + }) + } + + const onConnect = () => { + clearTimeout(connectionTimeout) + socket.removeListener('error', onError) + callback(null, socket) + } + + const onError = (err) => { + socket.removeListener('connect', onConnect) + callback(err) + } + + socket.once('error', onError) + socket.once(this.secure ? 'secureConnect' : 'connect', onConnect) + } + + addRequest (request, options) { + if (this.agent.options.timeouts.response) { + let responseTimeout + + const onFinish = () => { + responseTimeout = setTimeout(() => { + request.destroy(new ResponseTimeoutError(this, request)) + }, this.agent.options.timeouts.response) + } + + const onResponse = () => { + clearTimeout(responseTimeout) + } + + request.once('finish', onFinish) + request.once('response', onResponse) + } + + if (this.agent.options.timeouts.transfer) { + let transferTimeout + + const onResponse = (res) => { + transferTimeout = setTimeout(() => { + // swallow the error event on the request, this allows the one on the response + // to make it to the end user + request.once('error', () => {}) + res.destroy(new TransferTimeoutError(this, request)) + }, this.agent.options.timeouts.transfer) + + res.once('close', () => { + clearTimeout(transferTimeout) + }) + } + + request.once('response', onResponse) + } + } +} + +module.exports = NullProxy diff --git a/node_modules/@npmcli/agent/lib/proxy/socks.js b/node_modules/@npmcli/agent/lib/proxy/socks.js new file mode 100644 index 0000000000000..8cad7148e9227 --- /dev/null +++ b/node_modules/@npmcli/agent/lib/proxy/socks.js @@ -0,0 +1,153 @@ +'use strict' + +const { SocksClient } = require('socks') +const tls = require('tls') + +const { + ConnectionTimeoutError, + IdleTimeoutError, + InvalidProxyProtocolError, + ResponseTimeoutError, + TransferTimeoutError, +} = require('../errors.js') + +class SocksProxy { + constructor ({ agent, lookup, secure, url }) { + this.agent = agent + this.lookup = lookup + this.secure = secure + this.url = url + if (!this.url.port) { + this.url.port = 1080 + } + + if (this.url.protocol === 'socks4:') { + this.shouldLookup = true + this.type = 4 + } else if (this.url.protocol === 'socks4a:') { + this.shouldLookup = false + this.type = 4 + } else if (this.url.protocol === 'socks5:') { + this.shouldLookup = true + this.type = 5 + } else if (this.url.protocol === 'socks5h:' || this.url.protocol === 'socks:') { + this.shouldLookup = false + this.type = 5 + } else { + throw new InvalidProxyProtocolError(this.url) + } + } + + createConnection (options, callback) { + const socksOptions = { + proxy: { + host: this.url.hostname, + port: parseInt(this.url.port, 10), + type: this.type, + userId: this.url.username, + password: this.url.password, + }, + destination: { + host: options.host, + port: parseInt(options.port, 10), + }, + command: 'connect', + socket_options: { + family: this.agent.options.family, + lookup: this.lookup, + }, + } + + const connect = () => { + let connectionTimeout + const socksClient = new SocksClient(socksOptions) + + const onError = (err) => { + socksClient.removeListener('established', onEstablished) + return callback(err) + } + + const onEstablished = (connection) => { + clearTimeout(connectionTimeout) + socksClient.removeListener('error', onError) + + if (this.secure) { + connection.socket = tls.connect({ ...options, socket: connection.socket }) + } + + connection.socket.setKeepAlive(this.agent.keepAlive, this.agent.keepAliveMsecs) + connection.socket.setNoDelay(this.agent.keepAlive) + + if (options.timeouts.idle) { + connection.socket.setTimeout(options.timeouts.idle) + connection.socket.once('timeout', () => { + connection.socket.destroy(new IdleTimeoutError(this.url.host)) + }) + } + + return callback(null, connection.socket) + } + + socksClient.once('error', onError) + socksClient.once('established', onEstablished) + + if (options.timeouts.connection) { + connectionTimeout = setTimeout(() => { + return callback(new ConnectionTimeoutError(this.url.host)) + }, options.timeouts.connection) + } + + socksClient.connect() + } + + if (!this.shouldLookup) { + return connect() + } + + this.lookup(options.host, (err, result) => { + if (err) { + return callback(err) + } + + socksOptions.destination.host = result + connect() + }) + } + + addRequest (request, options) { + if (this.agent.options.timeouts.response) { + let responseTimeout + + const onFinish = () => { + responseTimeout = setTimeout(() => { + request.destroy(new ResponseTimeoutError(this, request)) + }, this.agent.options.timeouts.response) + } + + const onResponse = () => { + clearTimeout(responseTimeout) + } + + request.once('finish', onFinish) + request.once('response', onResponse) + } + + if (this.agent.options.timeouts.transfer) { + let transferTimeout + + const onResponse = (res) => { + transferTimeout = setTimeout(() => { + res.destroy(new TransferTimeoutError(this, request)) + }, this.agent.options.timeouts.transfer) + + res.once('close', () => { + clearTimeout(transferTimeout) + }) + } + + request.once('response', onResponse) + } + } +} + +module.exports = SocksProxy diff --git a/node_modules/@npmcli/agent/lib/util.js b/node_modules/@npmcli/agent/lib/util.js new file mode 100644 index 0000000000000..512207084d23e --- /dev/null +++ b/node_modules/@npmcli/agent/lib/util.js @@ -0,0 +1,33 @@ +'use strict' + +const dns = require('dns') + +const normalizeOptions = (_options) => { + const options = { ..._options } + + if (typeof options.keepAlive === 'undefined') { + options.keepAlive = true + } + + if (!options.timeouts) { + options.timeouts = {} + } + + if (options.timeout) { + options.timeouts.idle = options.timeout + delete options.timeout + } + + options.family = !isNaN(+options.family) ? +options.family : 0 + options.dns = { + ttl: 5 * 60 * 1000, + lookup: dns.lookup, + ...options.dns, + } + + return options +} + +module.exports = { + normalizeOptions, +} diff --git a/node_modules/@npmcli/agent/package.json b/node_modules/@npmcli/agent/package.json new file mode 100644 index 0000000000000..a3fb4262b9c86 --- /dev/null +++ b/node_modules/@npmcli/agent/package.json @@ -0,0 +1,56 @@ +{ + "name": "@npmcli/agent", + "version": "1.1.0", + "description": "the http/https agent used by the npm cli", + "main": "lib/index.js", + "scripts": { + "gencerts": "bash scripts/create-cert.sh", + "test": "tap", + "lint": "eslint \"**/*.js\"", + "postlint": "template-oss-check", + "template-oss-apply": "template-oss-apply --force", + "lintfix": "npm run lint -- --fix", + "snap": "tap", + "posttest": "npm run lint" + }, + "author": "GitHub Inc.", + "license": "ISC", + "bugs": { + "url": "https://github.com/npm/agent/issues" + }, + "homepage": "https://github.com/npm/agent#readme", + "files": [ + "bin/", + "lib/" + ], + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + }, + "templateOSS": { + "//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.", + "version": "4.15.1", + "publish": "true" + }, + "devDependencies": { + "@npmcli/eslint-config": "^4.0.0", + "@npmcli/template-oss": "4.15.1", + "minipass-fetch": "^3.0.3", + "nock": "^13.2.7", + "simple-socks": "^2.2.2", + "tap": "^16.3.0" + }, + "repository": { + "type": "git", + "url": "https://github.com/npm/agent.git" + }, + "tap": { + "nyc-arg": [ + "--exclude", + "tap-snapshots/**" + ] + }, + "dependencies": { + "lru-cache": "^7.18.3", + "socks": "^2.7.1" + } +} diff --git a/node_modules/npm-profile/node_modules/npm-registry-fetch/LICENSE.md b/node_modules/npm-profile/node_modules/npm-registry-fetch/LICENSE.md new file mode 100644 index 0000000000000..5fc208ff122e0 --- /dev/null +++ b/node_modules/npm-profile/node_modules/npm-registry-fetch/LICENSE.md @@ -0,0 +1,20 @@ + + +ISC License + +Copyright npm, Inc. + +Permission to use, copy, modify, and/or distribute this +software for any purpose with or without fee is hereby +granted, provided that the above copyright notice and this +permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND NPM DISCLAIMS ALL +WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO +EVENT SHALL NPM BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE +USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/node_modules/npm-profile/node_modules/npm-registry-fetch/lib/auth.js b/node_modules/npm-profile/node_modules/npm-registry-fetch/lib/auth.js new file mode 100644 index 0000000000000..870ce0d923cd0 --- /dev/null +++ b/node_modules/npm-profile/node_modules/npm-registry-fetch/lib/auth.js @@ -0,0 +1,145 @@ +'use strict' +const fs = require('fs') +const npa = require('npm-package-arg') +const { URL } = require('url') + +// Find the longest registry key that is used for some kind of auth +// in the options. +const regKeyFromURI = (uri, opts) => { + const parsed = new URL(uri) + // try to find a config key indicating we have auth for this registry + // can be one of :_authToken, :_auth, :_password and :username, or + // :certfile and :keyfile + // We walk up the "path" until we're left with just //[:], + // stopping when we reach '//'. + let regKey = `//${parsed.host}${parsed.pathname}` + while (regKey.length > '//'.length) { + // got some auth for this URI + if (hasAuth(regKey, opts)) { + return regKey + } + + // can be either //host/some/path/:_auth or //host/some/path:_auth + // walk up by removing EITHER what's after the slash OR the slash itself + regKey = regKey.replace(/([^/]+|\/)$/, '') + } +} + +const hasAuth = (regKey, opts) => ( + opts[`${regKey}:_authToken`] || + opts[`${regKey}:_auth`] || + opts[`${regKey}:username`] && opts[`${regKey}:_password`] || + opts[`${regKey}:certfile`] && opts[`${regKey}:keyfile`] +) + +const sameHost = (a, b) => { + const parsedA = new URL(a) + const parsedB = new URL(b) + return parsedA.host === parsedB.host +} + +const getRegistry = opts => { + const { spec } = opts + const { scope: specScope, subSpec } = spec ? npa(spec) : {} + const subSpecScope = subSpec && subSpec.scope + const scope = subSpec ? subSpecScope : specScope + const scopeReg = scope && opts[`${scope}:registry`] + return scopeReg || opts.registry +} + +const maybeReadFile = file => { + try { + return fs.readFileSync(file, 'utf8') + } catch (er) { + if (er.code !== 'ENOENT') { + throw er + } + return null + } +} + +const getAuth = (uri, opts = {}) => { + const { forceAuth } = opts + if (!uri) { + throw new Error('URI is required') + } + const regKey = regKeyFromURI(uri, forceAuth || opts) + + // we are only allowed to use what's in forceAuth if specified + if (forceAuth && !regKey) { + return new Auth({ + scopeAuthKey: null, + token: forceAuth._authToken || forceAuth.token, + username: forceAuth.username, + password: forceAuth._password || forceAuth.password, + auth: forceAuth._auth || forceAuth.auth, + certfile: forceAuth.certfile, + keyfile: forceAuth.keyfile, + }) + } + + // no auth for this URI, but might have it for the registry + if (!regKey) { + const registry = getRegistry(opts) + if (registry && uri !== registry && sameHost(uri, registry)) { + return getAuth(registry, opts) + } else if (registry !== opts.registry) { + // If making a tarball request to a different base URI than the + // registry where we logged in, but the same auth SHOULD be sent + // to that artifact host, then we track where it was coming in from, + // and warn the user if we get a 4xx error on it. + const scopeAuthKey = regKeyFromURI(registry, opts) + return new Auth({ scopeAuthKey }) + } + } + + const { + [`${regKey}:_authToken`]: token, + [`${regKey}:username`]: username, + [`${regKey}:_password`]: password, + [`${regKey}:_auth`]: auth, + [`${regKey}:certfile`]: certfile, + [`${regKey}:keyfile`]: keyfile, + } = opts + + return new Auth({ + scopeAuthKey: null, + token, + auth, + username, + password, + certfile, + keyfile, + }) +} + +class Auth { + constructor ({ token, auth, username, password, scopeAuthKey, certfile, keyfile }) { + this.scopeAuthKey = scopeAuthKey + this.token = null + this.auth = null + this.isBasicAuth = false + this.cert = null + this.key = null + if (token) { + this.token = token + } else if (auth) { + this.auth = auth + } else if (username && password) { + const p = Buffer.from(password, 'base64').toString('utf8') + this.auth = Buffer.from(`${username}:${p}`, 'utf8').toString('base64') + this.isBasicAuth = true + } + // mTLS may be used in conjunction with another auth method above + if (certfile && keyfile) { + const cert = maybeReadFile(certfile, 'utf-8') + const key = maybeReadFile(keyfile, 'utf-8') + if (cert && key) { + this.cert = cert + this.key = key + } + } + } +} + +module.exports = getAuth diff --git a/node_modules/npm-profile/node_modules/npm-registry-fetch/lib/check-response.js b/node_modules/npm-profile/node_modules/npm-registry-fetch/lib/check-response.js new file mode 100644 index 0000000000000..066ac3c32420f --- /dev/null +++ b/node_modules/npm-profile/node_modules/npm-registry-fetch/lib/check-response.js @@ -0,0 +1,100 @@ +'use strict' + +const errors = require('./errors.js') +const { Response } = require('minipass-fetch') +const defaultOpts = require('./default-opts.js') +const log = require('proc-log') +const cleanUrl = require('./clean-url.js') + +/* eslint-disable-next-line max-len */ +const moreInfoUrl = 'https://github.com/npm/cli/wiki/No-auth-for-URI,-but-auth-present-for-scoped-registry' +const checkResponse = + async ({ method, uri, res, startTime, auth, opts }) => { + opts = { ...defaultOpts, ...opts } + if (res.headers.has('npm-notice') && !res.headers.has('x-local-cache')) { + log.notice('', res.headers.get('npm-notice')) + } + + if (res.status >= 400) { + logRequest(method, res, startTime) + if (auth && auth.scopeAuthKey && !auth.token && !auth.auth) { + // we didn't have auth for THIS request, but we do have auth for + // requests to the registry indicated by the spec's scope value. + // Warn the user. + log.warn('registry', `No auth for URI, but auth present for scoped registry. + +URI: ${uri} +Scoped Registry Key: ${auth.scopeAuthKey} + +More info here: ${moreInfoUrl}`) + } + return checkErrors(method, res, startTime, opts) + } else { + res.body.on('end', () => logRequest(method, res, startTime, opts)) + if (opts.ignoreBody) { + res.body.resume() + return new Response(null, res) + } + return res + } + } +module.exports = checkResponse + +function logRequest (method, res, startTime) { + const elapsedTime = Date.now() - startTime + const attempt = res.headers.get('x-fetch-attempts') + const attemptStr = attempt && attempt > 1 ? ` attempt #${attempt}` : '' + const cacheStatus = res.headers.get('x-local-cache-status') + const cacheStr = cacheStatus ? ` (cache ${cacheStatus})` : '' + const urlStr = cleanUrl(res.url) + + log.http( + 'fetch', + `${method.toUpperCase()} ${res.status} ${urlStr} ${elapsedTime}ms${attemptStr}${cacheStr}` + ) +} + +function checkErrors (method, res, startTime, opts) { + return res.buffer() + .catch(() => null) + .then(body => { + let parsed = body + try { + parsed = JSON.parse(body.toString('utf8')) + } catch { + // ignore errors + } + if (res.status === 401 && res.headers.get('www-authenticate')) { + const auth = res.headers.get('www-authenticate') + .split(/,\s*/) + .map(s => s.toLowerCase()) + if (auth.indexOf('ipaddress') !== -1) { + throw new errors.HttpErrorAuthIPAddress( + method, res, parsed, opts.spec + ) + } else if (auth.indexOf('otp') !== -1) { + throw new errors.HttpErrorAuthOTP( + method, res, parsed, opts.spec + ) + } else { + throw new errors.HttpErrorAuthUnknown( + method, res, parsed, opts.spec + ) + } + } else if ( + res.status === 401 && + body != null && + /one-time pass/.test(body.toString('utf8')) + ) { + // Heuristic for malformed OTP responses that don't include the + // www-authenticate header. + throw new errors.HttpErrorAuthOTP( + method, res, parsed, opts.spec + ) + } else { + throw new errors.HttpErrorGeneral( + method, res, parsed, opts.spec + ) + } + }) +} diff --git a/node_modules/npm-profile/node_modules/npm-registry-fetch/lib/clean-url.js b/node_modules/npm-profile/node_modules/npm-registry-fetch/lib/clean-url.js new file mode 100644 index 0000000000000..0c2656b5653a0 --- /dev/null +++ b/node_modules/npm-profile/node_modules/npm-registry-fetch/lib/clean-url.js @@ -0,0 +1,27 @@ +const { URL } = require('url') + +const replace = '***' +const tokenRegex = /\bnpm_[a-zA-Z0-9]{36}\b/g +const guidRegex = /\b[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\b/g + +const cleanUrl = (str) => { + if (typeof str !== 'string' || !str) { + return str + } + + try { + const url = new URL(str) + if (url.password) { + url.password = replace + str = url.toString() + } + } catch { + // ignore errors + } + + return str + .replace(tokenRegex, `npm_${replace}`) + .replace(guidRegex, `npm_${replace}`) +} + +module.exports = cleanUrl diff --git a/node_modules/npm-profile/node_modules/npm-registry-fetch/lib/default-opts.js b/node_modules/npm-profile/node_modules/npm-registry-fetch/lib/default-opts.js new file mode 100644 index 0000000000000..f0847f0b507e2 --- /dev/null +++ b/node_modules/npm-profile/node_modules/npm-registry-fetch/lib/default-opts.js @@ -0,0 +1,19 @@ +const pkg = require('../package.json') +module.exports = { + maxSockets: 12, + method: 'GET', + registry: 'https://registry.npmjs.org/', + timeout: 5 * 60 * 1000, // 5 minutes + strictSSL: true, + noProxy: process.env.NOPROXY, + userAgent: `${pkg.name + }@${ + pkg.version + }/node@${ + process.version + }+${ + process.arch + } (${ + process.platform + })`, +} diff --git a/node_modules/npm-profile/node_modules/npm-registry-fetch/lib/errors.js b/node_modules/npm-profile/node_modules/npm-registry-fetch/lib/errors.js new file mode 100644 index 0000000000000..cf5ddba6f300c --- /dev/null +++ b/node_modules/npm-profile/node_modules/npm-registry-fetch/lib/errors.js @@ -0,0 +1,80 @@ +'use strict' + +const url = require('url') + +function packageName (href) { + try { + let basePath = new url.URL(href).pathname.slice(1) + if (!basePath.match(/^-/)) { + basePath = basePath.split('/') + var index = basePath.indexOf('_rewrite') + if (index === -1) { + index = basePath.length - 1 + } else { + index++ + } + return decodeURIComponent(basePath[index]) + } + } catch (_) { + // this is ok + } +} + +class HttpErrorBase extends Error { + constructor (method, res, body, spec) { + super() + this.name = this.constructor.name + this.headers = res.headers.raw() + this.statusCode = res.status + this.code = `E${res.status}` + this.method = method + this.uri = res.url + this.body = body + this.pkgid = spec ? spec.toString() : packageName(res.url) + } +} +module.exports.HttpErrorBase = HttpErrorBase + +class HttpErrorGeneral extends HttpErrorBase { + constructor (method, res, body, spec) { + super(method, res, body, spec) + this.message = `${res.status} ${res.statusText} - ${ + this.method.toUpperCase() + } ${ + this.spec || this.uri + }${ + (body && body.error) ? ' - ' + body.error : '' + }` + Error.captureStackTrace(this, HttpErrorGeneral) + } +} +module.exports.HttpErrorGeneral = HttpErrorGeneral + +class HttpErrorAuthOTP extends HttpErrorBase { + constructor (method, res, body, spec) { + super(method, res, body, spec) + this.message = 'OTP required for authentication' + this.code = 'EOTP' + Error.captureStackTrace(this, HttpErrorAuthOTP) + } +} +module.exports.HttpErrorAuthOTP = HttpErrorAuthOTP + +class HttpErrorAuthIPAddress extends HttpErrorBase { + constructor (method, res, body, spec) { + super(method, res, body, spec) + this.message = 'Login is not allowed from your IP address' + this.code = 'EAUTHIP' + Error.captureStackTrace(this, HttpErrorAuthIPAddress) + } +} +module.exports.HttpErrorAuthIPAddress = HttpErrorAuthIPAddress + +class HttpErrorAuthUnknown extends HttpErrorBase { + constructor (method, res, body, spec) { + super(method, res, body, spec) + this.message = 'Unable to authenticate, need: ' + res.headers.get('www-authenticate') + Error.captureStackTrace(this, HttpErrorAuthUnknown) + } +} +module.exports.HttpErrorAuthUnknown = HttpErrorAuthUnknown diff --git a/node_modules/npm-profile/node_modules/npm-registry-fetch/lib/index.js b/node_modules/npm-profile/node_modules/npm-registry-fetch/lib/index.js new file mode 100644 index 0000000000000..23e349c5c5b96 --- /dev/null +++ b/node_modules/npm-profile/node_modules/npm-registry-fetch/lib/index.js @@ -0,0 +1,247 @@ +'use strict' + +const { HttpErrorAuthOTP } = require('./errors.js') +const checkResponse = require('./check-response.js') +const getAuth = require('./auth.js') +const fetch = require('make-fetch-happen') +const JSONStream = require('minipass-json-stream') +const npa = require('npm-package-arg') +const qs = require('querystring') +const url = require('url') +const zlib = require('minizlib') +const { Minipass } = require('minipass') + +const defaultOpts = require('./default-opts.js') + +// WhatWG URL throws if it's not fully resolved +const urlIsValid = u => { + try { + return !!new url.URL(u) + } catch (_) { + return false + } +} + +module.exports = regFetch +function regFetch (uri, /* istanbul ignore next */ opts_ = {}) { + const opts = { + ...defaultOpts, + ...opts_, + } + + // if we did not get a fully qualified URI, then we look at the registry + // config or relevant scope to resolve it. + const uriValid = urlIsValid(uri) + let registry = opts.registry || defaultOpts.registry + if (!uriValid) { + registry = opts.registry = ( + (opts.spec && pickRegistry(opts.spec, opts)) || + opts.registry || + registry + ) + uri = `${ + registry.trim().replace(/\/?$/g, '') + }/${ + uri.trim().replace(/^\//, '') + }` + // asserts that this is now valid + new url.URL(uri) + } + + const method = opts.method || 'GET' + + // through that takes into account the scope, the prefix of `uri`, etc + const startTime = Date.now() + const auth = getAuth(uri, opts) + const headers = getHeaders(uri, auth, opts) + let body = opts.body + const bodyIsStream = Minipass.isStream(body) + const bodyIsPromise = body && + typeof body === 'object' && + typeof body.then === 'function' + + if ( + body && !bodyIsStream && !bodyIsPromise && typeof body !== 'string' && !Buffer.isBuffer(body) + ) { + headers['content-type'] = headers['content-type'] || 'application/json' + body = JSON.stringify(body) + } else if (body && !headers['content-type']) { + headers['content-type'] = 'application/octet-stream' + } + + if (opts.gzip) { + headers['content-encoding'] = 'gzip' + if (bodyIsStream) { + const gz = new zlib.Gzip() + body.on('error', /* istanbul ignore next: unlikely and hard to test */ + err => gz.emit('error', err)) + body = body.pipe(gz) + } else if (!bodyIsPromise) { + body = new zlib.Gzip().end(body).concat() + } + } + + const parsed = new url.URL(uri) + + if (opts.query) { + const q = typeof opts.query === 'string' ? qs.parse(opts.query) + : opts.query + + Object.keys(q).forEach(key => { + if (q[key] !== undefined) { + parsed.searchParams.set(key, q[key]) + } + }) + uri = url.format(parsed) + } + + if (parsed.searchParams.get('write') === 'true' && method === 'GET') { + // do not cache, because this GET is fetching a rev that will be + // used for a subsequent PUT or DELETE, so we need to conditionally + // update cache. + opts.offline = false + opts.preferOffline = false + opts.preferOnline = true + } + + const doFetch = async fetchBody => { + const p = fetch(uri, { + agent: opts.agent, + algorithms: opts.algorithms, + body: fetchBody, + cache: getCacheMode(opts), + cachePath: opts.cache, + ca: opts.ca, + cert: auth.cert || opts.cert, + headers, + integrity: opts.integrity, + key: auth.key || opts.key, + localAddress: opts.localAddress, + maxSockets: opts.maxSockets, + memoize: opts.memoize, + method: method, + noProxy: opts.noProxy, + proxy: opts.httpsProxy || opts.proxy, + retry: opts.retry ? opts.retry : { + retries: opts.fetchRetries, + factor: opts.fetchRetryFactor, + minTimeout: opts.fetchRetryMintimeout, + maxTimeout: opts.fetchRetryMaxtimeout, + }, + strictSSL: opts.strictSSL, + timeout: opts.timeout || 30 * 1000, + }).then(res => checkResponse({ + method, + uri, + res, + registry, + startTime, + auth, + opts, + })) + + if (typeof opts.otpPrompt === 'function') { + return p.catch(async er => { + if (er instanceof HttpErrorAuthOTP) { + let otp + // if otp fails to complete, we fail with that failure + try { + otp = await opts.otpPrompt() + } catch (_) { + // ignore this error + } + // if no otp provided, or otpPrompt errored, throw the original HTTP error + if (!otp) { + throw er + } + return regFetch(uri, { ...opts, otp }) + } + throw er + }) + } else { + return p + } + } + + return Promise.resolve(body).then(doFetch) +} + +module.exports.json = fetchJSON +function fetchJSON (uri, opts) { + return regFetch(uri, opts).then(res => res.json()) +} + +module.exports.json.stream = fetchJSONStream +function fetchJSONStream (uri, jsonPath, + /* istanbul ignore next */ opts_ = {}) { + const opts = { ...defaultOpts, ...opts_ } + const parser = JSONStream.parse(jsonPath, opts.mapJSON) + regFetch(uri, opts).then(res => + res.body.on('error', + /* istanbul ignore next: unlikely and difficult to test */ + er => parser.emit('error', er)).pipe(parser) + ).catch(er => parser.emit('error', er)) + return parser +} + +module.exports.pickRegistry = pickRegistry +function pickRegistry (spec, opts = {}) { + spec = npa(spec) + let registry = spec.scope && + opts[spec.scope.replace(/^@?/, '@') + ':registry'] + + if (!registry && opts.scope) { + registry = opts[opts.scope.replace(/^@?/, '@') + ':registry'] + } + + if (!registry) { + registry = opts.registry || defaultOpts.registry + } + + return registry +} + +function getCacheMode (opts) { + return opts.offline ? 'only-if-cached' + : opts.preferOffline ? 'force-cache' + : opts.preferOnline ? 'no-cache' + : 'default' +} + +function getHeaders (uri, auth, opts) { + const headers = Object.assign({ + 'user-agent': opts.userAgent, + }, opts.headers || {}) + + if (opts.authType) { + headers['npm-auth-type'] = opts.authType + } + + if (opts.scope) { + headers['npm-scope'] = opts.scope + } + + if (opts.npmSession) { + headers['npm-session'] = opts.npmSession + } + + if (opts.npmCommand) { + headers['npm-command'] = opts.npmCommand + } + + // If a tarball is hosted on a different place than the manifest, only send + // credentials on `alwaysAuth` + if (auth.token) { + headers.authorization = `Bearer ${auth.token}` + } else if (auth.auth) { + headers.authorization = `Basic ${auth.auth}` + } + + if (opts.otp) { + headers['npm-otp'] = opts.otp + } + + return headers +} + +module.exports.cleanUrl = require('./clean-url.js') diff --git a/node_modules/npm-profile/node_modules/npm-registry-fetch/package.json b/node_modules/npm-profile/node_modules/npm-registry-fetch/package.json new file mode 100644 index 0000000000000..63a44725886cc --- /dev/null +++ b/node_modules/npm-profile/node_modules/npm-registry-fetch/package.json @@ -0,0 +1,67 @@ +{ + "name": "npm-registry-fetch", + "version": "14.0.5", + "description": "Fetch-based http client for use with npm registry APIs", + "main": "lib", + "files": [ + "bin/", + "lib/" + ], + "scripts": { + "eslint": "eslint", + "lint": "eslint \"**/*.js\"", + "lintfix": "npm run lint -- --fix", + "test": "tap", + "posttest": "npm run lint", + "npmclilint": "npmcli-lint", + "postsnap": "npm run lintfix --", + "postlint": "template-oss-check", + "snap": "tap", + "template-oss-apply": "template-oss-apply --force" + }, + "repository": { + "type": "git", + "url": "https://github.com/npm/npm-registry-fetch.git" + }, + "keywords": [ + "npm", + "registry", + "fetch" + ], + "author": "GitHub Inc.", + "license": "ISC", + "dependencies": { + "make-fetch-happen": "^11.0.0", + "minipass": "^5.0.0", + "minipass-fetch": "^3.0.0", + "minipass-json-stream": "^1.0.1", + "minizlib": "^2.1.2", + "npm-package-arg": "^10.0.0", + "proc-log": "^3.0.0" + }, + "devDependencies": { + "@npmcli/eslint-config": "^4.0.0", + "@npmcli/template-oss": "4.14.1", + "cacache": "^17.0.0", + "nock": "^13.2.4", + "require-inject": "^1.4.4", + "ssri": "^10.0.0", + "tap": "^16.0.1" + }, + "tap": { + "check-coverage": true, + "test-ignore": "test[\\\\/](util|cache)[\\\\/]", + "nyc-arg": [ + "--exclude", + "tap-snapshots/**" + ] + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + }, + "templateOSS": { + "//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.", + "version": "4.14.1", + "publish": "true" + } +} diff --git a/node_modules/npm-registry-fetch/node_modules/make-fetch-happen/LICENSE b/node_modules/npm-registry-fetch/node_modules/make-fetch-happen/LICENSE new file mode 100644 index 0000000000000..1808eb2844231 --- /dev/null +++ b/node_modules/npm-registry-fetch/node_modules/make-fetch-happen/LICENSE @@ -0,0 +1,16 @@ +ISC License + +Copyright 2017-2022 (c) npm, Inc. + +Permission to use, copy, modify, and/or distribute this software for +any purpose with or without fee is hereby granted, provided that the +above copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE COPYRIGHT HOLDER DISCLAIMS +ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS +OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE +USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/node_modules/npm-registry-fetch/node_modules/make-fetch-happen/lib/cache/entry.js b/node_modules/npm-registry-fetch/node_modules/make-fetch-happen/lib/cache/entry.js new file mode 100644 index 0000000000000..45141095074ec --- /dev/null +++ b/node_modules/npm-registry-fetch/node_modules/make-fetch-happen/lib/cache/entry.js @@ -0,0 +1,469 @@ +const { Request, Response } = require('minipass-fetch') +const { Minipass } = require('minipass') +const MinipassFlush = require('minipass-flush') +const cacache = require('cacache') +const url = require('url') + +const CachingMinipassPipeline = require('../pipeline.js') +const CachePolicy = require('./policy.js') +const cacheKey = require('./key.js') +const remote = require('../remote.js') + +const hasOwnProperty = (obj, prop) => Object.prototype.hasOwnProperty.call(obj, prop) + +// allow list for request headers that will be written to the cache index +// note: we will also store any request headers +// that are named in a response's vary header +const KEEP_REQUEST_HEADERS = [ + 'accept-charset', + 'accept-encoding', + 'accept-language', + 'accept', + 'cache-control', +] + +// allow list for response headers that will be written to the cache index +// note: we must not store the real response's age header, or when we load +// a cache policy based on the metadata it will think the cached response +// is always stale +const KEEP_RESPONSE_HEADERS = [ + 'cache-control', + 'content-encoding', + 'content-language', + 'content-type', + 'date', + 'etag', + 'expires', + 'last-modified', + 'link', + 'location', + 'pragma', + 'vary', +] + +// return an object containing all metadata to be written to the index +const getMetadata = (request, response, options) => { + const metadata = { + time: Date.now(), + url: request.url, + reqHeaders: {}, + resHeaders: {}, + + // options on which we must match the request and vary the response + options: { + compress: options.compress != null ? options.compress : request.compress, + }, + } + + // only save the status if it's not a 200 or 304 + if (response.status !== 200 && response.status !== 304) { + metadata.status = response.status + } + + for (const name of KEEP_REQUEST_HEADERS) { + if (request.headers.has(name)) { + metadata.reqHeaders[name] = request.headers.get(name) + } + } + + // if the request's host header differs from the host in the url + // we need to keep it, otherwise it's just noise and we ignore it + const host = request.headers.get('host') + const parsedUrl = new url.URL(request.url) + if (host && parsedUrl.host !== host) { + metadata.reqHeaders.host = host + } + + // if the response has a vary header, make sure + // we store the relevant request headers too + if (response.headers.has('vary')) { + const vary = response.headers.get('vary') + // a vary of "*" means every header causes a different response. + // in that scenario, we do not include any additional headers + // as the freshness check will always fail anyway and we don't + // want to bloat the cache indexes + if (vary !== '*') { + // copy any other request headers that will vary the response + const varyHeaders = vary.trim().toLowerCase().split(/\s*,\s*/) + for (const name of varyHeaders) { + if (request.headers.has(name)) { + metadata.reqHeaders[name] = request.headers.get(name) + } + } + } + } + + for (const name of KEEP_RESPONSE_HEADERS) { + if (response.headers.has(name)) { + metadata.resHeaders[name] = response.headers.get(name) + } + } + + for (const name of options.cacheAdditionalHeaders) { + if (response.headers.has(name)) { + metadata.resHeaders[name] = response.headers.get(name) + } + } + + return metadata +} + +// symbols used to hide objects that may be lazily evaluated in a getter +const _request = Symbol('request') +const _response = Symbol('response') +const _policy = Symbol('policy') + +class CacheEntry { + constructor ({ entry, request, response, options }) { + if (entry) { + this.key = entry.key + this.entry = entry + // previous versions of this module didn't write an explicit timestamp in + // the metadata, so fall back to the entry's timestamp. we can't use the + // entry timestamp to determine staleness because cacache will update it + // when it verifies its data + this.entry.metadata.time = this.entry.metadata.time || this.entry.time + } else { + this.key = cacheKey(request) + } + + this.options = options + + // these properties are behind getters that lazily evaluate + this[_request] = request + this[_response] = response + this[_policy] = null + } + + // returns a CacheEntry instance that satisfies the given request + // or undefined if no existing entry satisfies + static async find (request, options) { + try { + // compacts the index and returns an array of unique entries + var matches = await cacache.index.compact(options.cachePath, cacheKey(request), (A, B) => { + const entryA = new CacheEntry({ entry: A, options }) + const entryB = new CacheEntry({ entry: B, options }) + return entryA.policy.satisfies(entryB.request) + }, { + validateEntry: (entry) => { + // clean out entries with a buggy content-encoding value + if (entry.metadata && + entry.metadata.resHeaders && + entry.metadata.resHeaders['content-encoding'] === null) { + return false + } + + // if an integrity is null, it needs to have a status specified + if (entry.integrity === null) { + return !!(entry.metadata && entry.metadata.status) + } + + return true + }, + }) + } catch (err) { + // if the compact request fails, ignore the error and return + return + } + + // a cache mode of 'reload' means to behave as though we have no cache + // on the way to the network. return undefined to allow cacheFetch to + // create a brand new request no matter what. + if (options.cache === 'reload') { + return + } + + // find the specific entry that satisfies the request + let match + for (const entry of matches) { + const _entry = new CacheEntry({ + entry, + options, + }) + + if (_entry.policy.satisfies(request)) { + match = _entry + break + } + } + + return match + } + + // if the user made a PUT/POST/PATCH then we invalidate our + // cache for the same url by deleting the index entirely + static async invalidate (request, options) { + const key = cacheKey(request) + try { + await cacache.rm.entry(options.cachePath, key, { removeFully: true }) + } catch (err) { + // ignore errors + } + } + + get request () { + if (!this[_request]) { + this[_request] = new Request(this.entry.metadata.url, { + method: 'GET', + headers: this.entry.metadata.reqHeaders, + ...this.entry.metadata.options, + }) + } + + return this[_request] + } + + get response () { + if (!this[_response]) { + this[_response] = new Response(null, { + url: this.entry.metadata.url, + counter: this.options.counter, + status: this.entry.metadata.status || 200, + headers: { + ...this.entry.metadata.resHeaders, + 'content-length': this.entry.size, + }, + }) + } + + return this[_response] + } + + get policy () { + if (!this[_policy]) { + this[_policy] = new CachePolicy({ + entry: this.entry, + request: this.request, + response: this.response, + options: this.options, + }) + } + + return this[_policy] + } + + // wraps the response in a pipeline that stores the data + // in the cache while the user consumes it + async store (status) { + // if we got a status other than 200, 301, or 308, + // or the CachePolicy forbid storage, append the + // cache status header and return it untouched + if ( + this.request.method !== 'GET' || + ![200, 301, 308].includes(this.response.status) || + !this.policy.storable() + ) { + this.response.headers.set('x-local-cache-status', 'skip') + return this.response + } + + const size = this.response.headers.get('content-length') + const cacheOpts = { + algorithms: this.options.algorithms, + metadata: getMetadata(this.request, this.response, this.options), + size, + integrity: this.options.integrity, + integrityEmitter: this.response.body.hasIntegrityEmitter && this.response.body, + } + + let body = null + // we only set a body if the status is a 200, redirects are + // stored as metadata only + if (this.response.status === 200) { + let cacheWriteResolve, cacheWriteReject + const cacheWritePromise = new Promise((resolve, reject) => { + cacheWriteResolve = resolve + cacheWriteReject = reject + }) + + body = new CachingMinipassPipeline({ events: ['integrity', 'size'] }, new MinipassFlush({ + flush () { + return cacheWritePromise + }, + })) + // this is always true since if we aren't reusing the one from the remote fetch, we + // are using the one from cacache + body.hasIntegrityEmitter = true + + const onResume = () => { + const tee = new Minipass() + const cacheStream = cacache.put.stream(this.options.cachePath, this.key, cacheOpts) + // re-emit the integrity and size events on our new response body so they can be reused + cacheStream.on('integrity', i => body.emit('integrity', i)) + cacheStream.on('size', s => body.emit('size', s)) + // stick a flag on here so downstream users will know if they can expect integrity events + tee.pipe(cacheStream) + // TODO if the cache write fails, log a warning but return the response anyway + // eslint-disable-next-line promise/catch-or-return + cacheStream.promise().then(cacheWriteResolve, cacheWriteReject) + body.unshift(tee) + body.unshift(this.response.body) + } + + body.once('resume', onResume) + body.once('end', () => body.removeListener('resume', onResume)) + } else { + await cacache.index.insert(this.options.cachePath, this.key, null, cacheOpts) + } + + // note: we do not set the x-local-cache-hash header because we do not know + // the hash value until after the write to the cache completes, which doesn't + // happen until after the response has been sent and it's too late to write + // the header anyway + this.response.headers.set('x-local-cache', encodeURIComponent(this.options.cachePath)) + this.response.headers.set('x-local-cache-key', encodeURIComponent(this.key)) + this.response.headers.set('x-local-cache-mode', 'stream') + this.response.headers.set('x-local-cache-status', status) + this.response.headers.set('x-local-cache-time', new Date().toISOString()) + const newResponse = new Response(body, { + url: this.response.url, + status: this.response.status, + headers: this.response.headers, + counter: this.options.counter, + }) + return newResponse + } + + // use the cached data to create a response and return it + async respond (method, options, status) { + let response + if (method === 'HEAD' || [301, 308].includes(this.response.status)) { + // if the request is a HEAD, or the response is a redirect, + // then the metadata in the entry already includes everything + // we need to build a response + response = this.response + } else { + // we're responding with a full cached response, so create a body + // that reads from cacache and attach it to a new Response + const body = new Minipass() + const headers = { ...this.policy.responseHeaders() } + + const onResume = () => { + const cacheStream = cacache.get.stream.byDigest( + this.options.cachePath, this.entry.integrity, { memoize: this.options.memoize } + ) + cacheStream.on('error', async (err) => { + cacheStream.pause() + if (err.code === 'EINTEGRITY') { + await cacache.rm.content( + this.options.cachePath, this.entry.integrity, { memoize: this.options.memoize } + ) + } + if (err.code === 'ENOENT' || err.code === 'EINTEGRITY') { + await CacheEntry.invalidate(this.request, this.options) + } + body.emit('error', err) + cacheStream.resume() + }) + // emit the integrity and size events based on our metadata so we're consistent + body.emit('integrity', this.entry.integrity) + body.emit('size', Number(headers['content-length'])) + cacheStream.pipe(body) + } + + body.once('resume', onResume) + body.once('end', () => body.removeListener('resume', onResume)) + response = new Response(body, { + url: this.entry.metadata.url, + counter: options.counter, + status: 200, + headers, + }) + } + + response.headers.set('x-local-cache', encodeURIComponent(this.options.cachePath)) + response.headers.set('x-local-cache-hash', encodeURIComponent(this.entry.integrity)) + response.headers.set('x-local-cache-key', encodeURIComponent(this.key)) + response.headers.set('x-local-cache-mode', 'stream') + response.headers.set('x-local-cache-status', status) + response.headers.set('x-local-cache-time', new Date(this.entry.metadata.time).toUTCString()) + return response + } + + // use the provided request along with this cache entry to + // revalidate the stored response. returns a response, either + // from the cache or from the update + async revalidate (request, options) { + const revalidateRequest = new Request(request, { + headers: this.policy.revalidationHeaders(request), + }) + + try { + // NOTE: be sure to remove the headers property from the + // user supplied options, since we have already defined + // them on the new request object. if they're still in the + // options then those will overwrite the ones from the policy + var response = await remote(revalidateRequest, { + ...options, + headers: undefined, + }) + } catch (err) { + // if the network fetch fails, return the stale + // cached response unless it has a cache-control + // of 'must-revalidate' + if (!this.policy.mustRevalidate) { + return this.respond(request.method, options, 'stale') + } + + throw err + } + + if (this.policy.revalidated(revalidateRequest, response)) { + // we got a 304, write a new index to the cache and respond from cache + const metadata = getMetadata(request, response, options) + // 304 responses do not include headers that are specific to the response data + // since they do not include a body, so we copy values for headers that were + // in the old cache entry to the new one, if the new metadata does not already + // include that header + for (const name of KEEP_RESPONSE_HEADERS) { + if ( + !hasOwnProperty(metadata.resHeaders, name) && + hasOwnProperty(this.entry.metadata.resHeaders, name) + ) { + metadata.resHeaders[name] = this.entry.metadata.resHeaders[name] + } + } + + for (const name of options.cacheAdditionalHeaders) { + const inMeta = hasOwnProperty(metadata.resHeaders, name) + const inEntry = hasOwnProperty(this.entry.metadata.resHeaders, name) + const inPolicy = hasOwnProperty(this.policy.response.headers, name) + + // if the header is in the existing entry, but it is not in the metadata + // then we need to write it to the metadata as this will refresh the on-disk cache + if (!inMeta && inEntry) { + metadata.resHeaders[name] = this.entry.metadata.resHeaders[name] + } + // if the header is in the metadata, but not in the policy, then we need to set + // it in the policy so that it's included in the immediate response. future + // responses will load a new cache entry, so we don't need to change that + if (!inPolicy && inMeta) { + this.policy.response.headers[name] = metadata.resHeaders[name] + } + } + + try { + await cacache.index.insert(options.cachePath, this.key, this.entry.integrity, { + size: this.entry.size, + metadata, + }) + } catch (err) { + // if updating the cache index fails, we ignore it and + // respond anyway + } + return this.respond(request.method, options, 'revalidated') + } + + // if we got a modified response, create a new entry based on it + const newEntry = new CacheEntry({ + request, + response, + options, + }) + + // respond with the new entry while writing it to the cache + return newEntry.store('updated') + } +} + +module.exports = CacheEntry diff --git a/node_modules/npm-registry-fetch/node_modules/make-fetch-happen/lib/cache/errors.js b/node_modules/npm-registry-fetch/node_modules/make-fetch-happen/lib/cache/errors.js new file mode 100644 index 0000000000000..67a66573bebe6 --- /dev/null +++ b/node_modules/npm-registry-fetch/node_modules/make-fetch-happen/lib/cache/errors.js @@ -0,0 +1,11 @@ +class NotCachedError extends Error { + constructor (url) { + /* eslint-disable-next-line max-len */ + super(`request to ${url} failed: cache mode is 'only-if-cached' but no cached response is available.`) + this.code = 'ENOTCACHED' + } +} + +module.exports = { + NotCachedError, +} diff --git a/node_modules/npm-registry-fetch/node_modules/make-fetch-happen/lib/cache/index.js b/node_modules/npm-registry-fetch/node_modules/make-fetch-happen/lib/cache/index.js new file mode 100644 index 0000000000000..0de49d23fb933 --- /dev/null +++ b/node_modules/npm-registry-fetch/node_modules/make-fetch-happen/lib/cache/index.js @@ -0,0 +1,49 @@ +const { NotCachedError } = require('./errors.js') +const CacheEntry = require('./entry.js') +const remote = require('../remote.js') + +// do whatever is necessary to get a Response and return it +const cacheFetch = async (request, options) => { + // try to find a cached entry that satisfies this request + const entry = await CacheEntry.find(request, options) + if (!entry) { + // no cached result, if the cache mode is 'only-if-cached' that's a failure + if (options.cache === 'only-if-cached') { + throw new NotCachedError(request.url) + } + + // otherwise, we make a request, store it and return it + const response = await remote(request, options) + const newEntry = new CacheEntry({ request, response, options }) + return newEntry.store('miss') + } + + // we have a cached response that satisfies this request, however if the cache + // mode is 'no-cache' then we send the revalidation request no matter what + if (options.cache === 'no-cache') { + return entry.revalidate(request, options) + } + + // if the cached entry is not stale, or if the cache mode is 'force-cache' or + // 'only-if-cached' we can respond with the cached entry. set the status + // based on the result of needsRevalidation and respond + const _needsRevalidation = entry.policy.needsRevalidation(request) + if (options.cache === 'force-cache' || + options.cache === 'only-if-cached' || + !_needsRevalidation) { + return entry.respond(request.method, options, _needsRevalidation ? 'stale' : 'hit') + } + + // if we got here, the cache entry is stale so revalidate it + return entry.revalidate(request, options) +} + +cacheFetch.invalidate = async (request, options) => { + if (!options.cachePath) { + return + } + + return CacheEntry.invalidate(request, options) +} + +module.exports = cacheFetch diff --git a/node_modules/npm-registry-fetch/node_modules/make-fetch-happen/lib/cache/key.js b/node_modules/npm-registry-fetch/node_modules/make-fetch-happen/lib/cache/key.js new file mode 100644 index 0000000000000..f7684d562b7fa --- /dev/null +++ b/node_modules/npm-registry-fetch/node_modules/make-fetch-happen/lib/cache/key.js @@ -0,0 +1,17 @@ +const { URL, format } = require('url') + +// options passed to url.format() when generating a key +const formatOptions = { + auth: false, + fragment: false, + search: true, + unicode: false, +} + +// returns a string to be used as the cache key for the Request +const cacheKey = (request) => { + const parsed = new URL(request.url) + return `make-fetch-happen:request-cache:${format(parsed, formatOptions)}` +} + +module.exports = cacheKey diff --git a/node_modules/npm-registry-fetch/node_modules/make-fetch-happen/lib/cache/policy.js b/node_modules/npm-registry-fetch/node_modules/make-fetch-happen/lib/cache/policy.js new file mode 100644 index 0000000000000..ada3c8600dae9 --- /dev/null +++ b/node_modules/npm-registry-fetch/node_modules/make-fetch-happen/lib/cache/policy.js @@ -0,0 +1,161 @@ +const CacheSemantics = require('http-cache-semantics') +const Negotiator = require('negotiator') +const ssri = require('ssri') + +// options passed to http-cache-semantics constructor +const policyOptions = { + shared: false, + ignoreCargoCult: true, +} + +// a fake empty response, used when only testing the +// request for storability +const emptyResponse = { status: 200, headers: {} } + +// returns a plain object representation of the Request +const requestObject = (request) => { + const _obj = { + method: request.method, + url: request.url, + headers: {}, + compress: request.compress, + } + + request.headers.forEach((value, key) => { + _obj.headers[key] = value + }) + + return _obj +} + +// returns a plain object representation of the Response +const responseObject = (response) => { + const _obj = { + status: response.status, + headers: {}, + } + + response.headers.forEach((value, key) => { + _obj.headers[key] = value + }) + + return _obj +} + +class CachePolicy { + constructor ({ entry, request, response, options }) { + this.entry = entry + this.request = requestObject(request) + this.response = responseObject(response) + this.options = options + this.policy = new CacheSemantics(this.request, this.response, policyOptions) + + if (this.entry) { + // if we have an entry, copy the timestamp to the _responseTime + // this is necessary because the CacheSemantics constructor forces + // the value to Date.now() which means a policy created from a + // cache entry is likely to always identify itself as stale + this.policy._responseTime = this.entry.metadata.time + } + } + + // static method to quickly determine if a request alone is storable + static storable (request, options) { + // no cachePath means no caching + if (!options.cachePath) { + return false + } + + // user explicitly asked not to cache + if (options.cache === 'no-store') { + return false + } + + // we only cache GET and HEAD requests + if (!['GET', 'HEAD'].includes(request.method)) { + return false + } + + // otherwise, let http-cache-semantics make the decision + // based on the request's headers + const policy = new CacheSemantics(requestObject(request), emptyResponse, policyOptions) + return policy.storable() + } + + // returns true if the policy satisfies the request + satisfies (request) { + const _req = requestObject(request) + if (this.request.headers.host !== _req.headers.host) { + return false + } + + if (this.request.compress !== _req.compress) { + return false + } + + const negotiatorA = new Negotiator(this.request) + const negotiatorB = new Negotiator(_req) + + if (JSON.stringify(negotiatorA.mediaTypes()) !== JSON.stringify(negotiatorB.mediaTypes())) { + return false + } + + if (JSON.stringify(negotiatorA.languages()) !== JSON.stringify(negotiatorB.languages())) { + return false + } + + if (JSON.stringify(negotiatorA.encodings()) !== JSON.stringify(negotiatorB.encodings())) { + return false + } + + if (this.options.integrity) { + return ssri.parse(this.options.integrity).match(this.entry.integrity) + } + + return true + } + + // returns true if the request and response allow caching + storable () { + return this.policy.storable() + } + + // NOTE: this is a hack to avoid parsing the cache-control + // header ourselves, it returns true if the response's + // cache-control contains must-revalidate + get mustRevalidate () { + return !!this.policy._rescc['must-revalidate'] + } + + // returns true if the cached response requires revalidation + // for the given request + needsRevalidation (request) { + const _req = requestObject(request) + // force method to GET because we only cache GETs + // but can serve a HEAD from a cached GET + _req.method = 'GET' + return !this.policy.satisfiesWithoutRevalidation(_req) + } + + responseHeaders () { + return this.policy.responseHeaders() + } + + // returns a new object containing the appropriate headers + // to send a revalidation request + revalidationHeaders (request) { + const _req = requestObject(request) + return this.policy.revalidationHeaders(_req) + } + + // returns true if the request/response was revalidated + // successfully. returns false if a new response was received + revalidated (request, response) { + const _req = requestObject(request) + const _res = responseObject(response) + const policy = this.policy.revalidatedPolicy(_req, _res) + return !policy.modified + } +} + +module.exports = CachePolicy diff --git a/node_modules/npm-registry-fetch/node_modules/make-fetch-happen/lib/fetch.js b/node_modules/npm-registry-fetch/node_modules/make-fetch-happen/lib/fetch.js new file mode 100644 index 0000000000000..233ba67e16550 --- /dev/null +++ b/node_modules/npm-registry-fetch/node_modules/make-fetch-happen/lib/fetch.js @@ -0,0 +1,118 @@ +'use strict' + +const { FetchError, Request, isRedirect } = require('minipass-fetch') +const url = require('url') + +const CachePolicy = require('./cache/policy.js') +const cache = require('./cache/index.js') +const remote = require('./remote.js') + +// given a Request, a Response and user options +// return true if the response is a redirect that +// can be followed. we throw errors that will result +// in the fetch being rejected if the redirect is +// possible but invalid for some reason +const canFollowRedirect = (request, response, options) => { + if (!isRedirect(response.status)) { + return false + } + + if (options.redirect === 'manual') { + return false + } + + if (options.redirect === 'error') { + throw new FetchError(`redirect mode is set to error: ${request.url}`, + 'no-redirect', { code: 'ENOREDIRECT' }) + } + + if (!response.headers.has('location')) { + throw new FetchError(`redirect location header missing for: ${request.url}`, + 'no-location', { code: 'EINVALIDREDIRECT' }) + } + + if (request.counter >= request.follow) { + throw new FetchError(`maximum redirect reached at: ${request.url}`, + 'max-redirect', { code: 'EMAXREDIRECT' }) + } + + return true +} + +// given a Request, a Response, and the user's options return an object +// with a new Request and a new options object that will be used for +// following the redirect +const getRedirect = (request, response, options) => { + const _opts = { ...options } + const location = response.headers.get('location') + const redirectUrl = new url.URL(location, /^https?:/.test(location) ? undefined : request.url) + // Comment below is used under the following license: + /** + * @license + * Copyright (c) 2010-2012 Mikeal Rogers + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an "AS + * IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ + + // Remove authorization if changing hostnames (but not if just + // changing ports or protocols). This matches the behavior of request: + // https://github.com/request/request/blob/b12a6245/lib/redirect.js#L134-L138 + if (new url.URL(request.url).hostname !== redirectUrl.hostname) { + request.headers.delete('authorization') + request.headers.delete('cookie') + } + + // for POST request with 301/302 response, or any request with 303 response, + // use GET when following redirect + if ( + response.status === 303 || + (request.method === 'POST' && [301, 302].includes(response.status)) + ) { + _opts.method = 'GET' + _opts.body = null + request.headers.delete('content-length') + } + + _opts.headers = {} + request.headers.forEach((value, key) => { + _opts.headers[key] = value + }) + + _opts.counter = ++request.counter + const redirectReq = new Request(url.format(redirectUrl), _opts) + return { + request: redirectReq, + options: _opts, + } +} + +const fetch = async (request, options) => { + const response = CachePolicy.storable(request, options) + ? await cache(request, options) + : await remote(request, options) + + // if the request wasn't a GET or HEAD, and the response + // status is between 200 and 399 inclusive, invalidate the + // request url + if (!['GET', 'HEAD'].includes(request.method) && + response.status >= 200 && + response.status <= 399) { + await cache.invalidate(request, options) + } + + if (!canFollowRedirect(request, response, options)) { + return response + } + + const redirect = getRedirect(request, response, options) + return fetch(redirect.request, redirect.options) +} + +module.exports = fetch diff --git a/node_modules/npm-registry-fetch/node_modules/make-fetch-happen/lib/index.js b/node_modules/npm-registry-fetch/node_modules/make-fetch-happen/lib/index.js new file mode 100644 index 0000000000000..2f12e8e1b6113 --- /dev/null +++ b/node_modules/npm-registry-fetch/node_modules/make-fetch-happen/lib/index.js @@ -0,0 +1,41 @@ +const { FetchError, Headers, Request, Response } = require('minipass-fetch') + +const configureOptions = require('./options.js') +const fetch = require('./fetch.js') + +const makeFetchHappen = (url, opts) => { + const options = configureOptions(opts) + + const request = new Request(url, options) + return fetch(request, options) +} + +makeFetchHappen.defaults = (defaultUrl, defaultOptions = {}, wrappedFetch = makeFetchHappen) => { + if (typeof defaultUrl === 'object') { + defaultOptions = defaultUrl + defaultUrl = null + } + + const defaultedFetch = (url, options = {}) => { + const finalUrl = url || defaultUrl + const finalOptions = { + ...defaultOptions, + ...options, + headers: { + ...defaultOptions.headers, + ...options.headers, + }, + } + return wrappedFetch(finalUrl, finalOptions) + } + + defaultedFetch.defaults = (defaultUrl1, defaultOptions1 = {}) => + makeFetchHappen.defaults(defaultUrl1, defaultOptions1, defaultedFetch) + return defaultedFetch +} + +module.exports = makeFetchHappen +module.exports.FetchError = FetchError +module.exports.Headers = Headers +module.exports.Request = Request +module.exports.Response = Response diff --git a/node_modules/npm-registry-fetch/node_modules/make-fetch-happen/lib/options.js b/node_modules/npm-registry-fetch/node_modules/make-fetch-happen/lib/options.js new file mode 100644 index 0000000000000..f77511279f831 --- /dev/null +++ b/node_modules/npm-registry-fetch/node_modules/make-fetch-happen/lib/options.js @@ -0,0 +1,54 @@ +const dns = require('dns') + +const conditionalHeaders = [ + 'if-modified-since', + 'if-none-match', + 'if-unmodified-since', + 'if-match', + 'if-range', +] + +const configureOptions = (opts) => { + const { strictSSL, ...options } = { ...opts } + options.method = options.method ? options.method.toUpperCase() : 'GET' + options.rejectUnauthorized = strictSSL !== false + + if (!options.retry) { + options.retry = { retries: 0 } + } else if (typeof options.retry === 'string') { + const retries = parseInt(options.retry, 10) + if (isFinite(retries)) { + options.retry = { retries } + } else { + options.retry = { retries: 0 } + } + } else if (typeof options.retry === 'number') { + options.retry = { retries: options.retry } + } else { + options.retry = { retries: 0, ...options.retry } + } + + options.dns = { ttl: 5 * 60 * 1000, lookup: dns.lookup, ...options.dns } + + options.cache = options.cache || 'default' + if (options.cache === 'default') { + const hasConditionalHeader = Object.keys(options.headers || {}).some((name) => { + return conditionalHeaders.includes(name.toLowerCase()) + }) + if (hasConditionalHeader) { + options.cache = 'no-store' + } + } + + options.cacheAdditionalHeaders = options.cacheAdditionalHeaders || [] + + // cacheManager is deprecated, but if it's set and + // cachePath is not we should copy it to the new field + if (options.cacheManager && !options.cachePath) { + options.cachePath = options.cacheManager + } + + return options +} + +module.exports = configureOptions diff --git a/node_modules/npm-registry-fetch/node_modules/make-fetch-happen/lib/pipeline.js b/node_modules/npm-registry-fetch/node_modules/make-fetch-happen/lib/pipeline.js new file mode 100644 index 0000000000000..b1d221b2d0ce3 --- /dev/null +++ b/node_modules/npm-registry-fetch/node_modules/make-fetch-happen/lib/pipeline.js @@ -0,0 +1,41 @@ +'use strict' + +const MinipassPipeline = require('minipass-pipeline') + +class CachingMinipassPipeline extends MinipassPipeline { + #events = [] + #data = new Map() + + constructor (opts, ...streams) { + // CRITICAL: do NOT pass the streams to the call to super(), this will start + // the flow of data and potentially cause the events we need to catch to emit + // before we've finished our own setup. instead we call super() with no args, + // finish our setup, and then push the streams into ourselves to start the + // data flow + super() + this.#events = opts.events + + /* istanbul ignore next - coverage disabled because this is pointless to test here */ + if (streams.length) { + this.push(...streams) + } + } + + on (event, handler) { + if (this.#events.includes(event) && this.#data.has(event)) { + return handler(...this.#data.get(event)) + } + + return super.on(event, handler) + } + + emit (event, ...data) { + if (this.#events.includes(event)) { + this.#data.set(event, data) + } + + return super.emit(event, ...data) + } +} + +module.exports = CachingMinipassPipeline diff --git a/node_modules/npm-registry-fetch/node_modules/make-fetch-happen/lib/remote.js b/node_modules/npm-registry-fetch/node_modules/make-fetch-happen/lib/remote.js new file mode 100644 index 0000000000000..2aef9f8f969b0 --- /dev/null +++ b/node_modules/npm-registry-fetch/node_modules/make-fetch-happen/lib/remote.js @@ -0,0 +1,127 @@ +const { Minipass } = require('minipass') +const fetch = require('minipass-fetch') +const promiseRetry = require('promise-retry') +const ssri = require('ssri') + +const CachingMinipassPipeline = require('./pipeline.js') +const { getAgent } = require('@npmcli/agent') +const pkg = require('../package.json') + +const USER_AGENT = `${pkg.name}/${pkg.version} (+https://npm.im/${pkg.name})` + +const RETRY_ERRORS = [ + 'ECONNRESET', // remote socket closed on us + 'ECONNREFUSED', // remote host refused to open connection + 'EADDRINUSE', // failed to bind to a local port (proxy?) + 'ETIMEDOUT', // someone in the transaction is WAY TOO SLOW + // from @npmcli/agent + 'ECONNECTIONTIMEOUT', + 'EIDLETIMEOUT', + 'ERESPONSETIMEOUT', + 'ETRANSFERTIMEOUT', + // Known codes we do NOT retry on: + // ENOTFOUND (getaddrinfo failure. Either bad hostname, or offline) + // EINVALIDPROXY // invalid protocol from @npmcli/agent + // EINVALIDRESPONSE // invalid status code from @npmcli/agent +] + +const RETRY_TYPES = [ + 'request-timeout', +] + +// make a request directly to the remote source, +// retrying certain classes of errors as well as +// following redirects (through the cache if necessary) +// and verifying response integrity +const remoteFetch = (request, options) => { + const agent = getAgent(request.url, options) + if (!request.headers.has('connection')) { + request.headers.set('connection', agent ? 'keep-alive' : 'close') + } + + if (!request.headers.has('user-agent')) { + request.headers.set('user-agent', USER_AGENT) + } + + // keep our own options since we're overriding the agent + // and the redirect mode + const _opts = { + ...options, + agent, + redirect: 'manual', + } + + return promiseRetry(async (retryHandler, attemptNum) => { + const req = new fetch.Request(request, _opts) + try { + let res = await fetch(req, _opts) + if (_opts.integrity && res.status === 200) { + // we got a 200 response and the user has specified an expected + // integrity value, so wrap the response in an ssri stream to verify it + const integrityStream = ssri.integrityStream({ + algorithms: _opts.algorithms, + integrity: _opts.integrity, + size: _opts.size, + }) + const pipeline = new CachingMinipassPipeline({ + events: ['integrity', 'size'], + }, res.body, integrityStream) + // we also propagate the integrity and size events out to the pipeline so we can use + // this new response body as an integrityEmitter for cacache + integrityStream.on('integrity', i => pipeline.emit('integrity', i)) + integrityStream.on('size', s => pipeline.emit('size', s)) + res = new fetch.Response(pipeline, res) + // set an explicit flag so we know if our response body will emit integrity and size + res.body.hasIntegrityEmitter = true + } + + res.headers.set('x-fetch-attempts', attemptNum) + + // do not retry POST requests, or requests with a streaming body + // do retry requests with a 408, 420, 429 or 500+ status in the response + const isStream = Minipass.isStream(req.body) + const isRetriable = req.method !== 'POST' && + !isStream && + ([408, 420, 429].includes(res.status) || res.status >= 500) + + if (isRetriable) { + if (typeof options.onRetry === 'function') { + options.onRetry(res) + } + + return retryHandler(res) + } + + return res + } catch (err) { + const code = (err.code === 'EPROMISERETRY') + ? err.retried.code + : err.code + + // err.retried will be the thing that was thrown from above + // if it's a response, we just got a bad status code and we + // can re-throw to allow the retry + const isRetryError = err.retried instanceof fetch.Response || + (RETRY_ERRORS.includes(code) && RETRY_TYPES.includes(err.type)) + + if (req.method === 'POST' || isRetryError) { + throw err + } + + if (typeof options.onRetry === 'function') { + options.onRetry(err) + } + + return retryHandler(err) + } + }, options.retry).catch((err) => { + // don't reject for http errors, just return them + if (err.status >= 400 && err.type !== 'system') { + return err + } + + throw err + }) +} + +module.exports = remoteFetch diff --git a/node_modules/npm-registry-fetch/node_modules/make-fetch-happen/package.json b/node_modules/npm-registry-fetch/node_modules/make-fetch-happen/package.json new file mode 100644 index 0000000000000..419db8fbb1289 --- /dev/null +++ b/node_modules/npm-registry-fetch/node_modules/make-fetch-happen/package.json @@ -0,0 +1,80 @@ +{ + "name": "make-fetch-happen", + "version": "12.0.0", + "description": "Opinionated, caching, retrying fetch client", + "main": "lib/index.js", + "files": [ + "bin/", + "lib/" + ], + "scripts": { + "test": "tap", + "posttest": "npm run lint", + "eslint": "eslint", + "lint": "eslint \"**/*.js\"", + "lintfix": "npm run lint -- --fix", + "postlint": "template-oss-check", + "snap": "tap", + "template-oss-apply": "template-oss-apply --force" + }, + "repository": { + "type": "git", + "url": "https://github.com/npm/make-fetch-happen.git" + }, + "keywords": [ + "http", + "request", + "fetch", + "mean girls", + "caching", + "cache", + "subresource integrity" + ], + "author": "GitHub Inc.", + "license": "ISC", + "dependencies": { + "@npmcli/agent": "^1.1.0", + "cacache": "^17.0.0", + "http-cache-semantics": "^4.1.1", + "is-lambda": "^1.0.1", + "minipass": "^7.0.2", + "minipass-fetch": "^3.0.0", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.3", + "promise-retry": "^2.0.1", + "ssri": "^10.0.0" + }, + "devDependencies": { + "@npmcli/eslint-config": "^4.0.0", + "@npmcli/template-oss": "4.18.0", + "nock": "^13.2.4", + "safe-buffer": "^5.2.1", + "standard-version": "^9.3.2", + "tap": "^16.0.0" + }, + "engines": { + "node": "^16.13.0 || >=18.0.0" + }, + "tap": { + "color": 1, + "files": "test/*.js", + "check-coverage": true, + "timeout": 60, + "nyc-arg": [ + "--exclude", + "tap-snapshots/**" + ] + }, + "templateOSS": { + "//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.", + "ciVersions": [ + "16.13.0", + "16.x", + "18.0.0", + "18.x" + ], + "version": "4.18.0", + "publish": "true" + } +} diff --git a/node_modules/npm-registry-fetch/node_modules/minipass/LICENSE b/node_modules/npm-registry-fetch/node_modules/minipass/LICENSE new file mode 100644 index 0000000000000..97f8e32ed82e4 --- /dev/null +++ b/node_modules/npm-registry-fetch/node_modules/minipass/LICENSE @@ -0,0 +1,15 @@ +The ISC License + +Copyright (c) 2017-2023 npm, Inc., Isaac Z. Schlueter, and Contributors + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/node_modules/npm-registry-fetch/node_modules/minipass/dist/cjs/index.js b/node_modules/npm-registry-fetch/node_modules/minipass/dist/cjs/index.js new file mode 100644 index 0000000000000..068c095b69793 --- /dev/null +++ b/node_modules/npm-registry-fetch/node_modules/minipass/dist/cjs/index.js @@ -0,0 +1,1028 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Minipass = exports.isWritable = exports.isReadable = exports.isStream = void 0; +const proc = typeof process === 'object' && process + ? process + : { + stdout: null, + stderr: null, + }; +const node_events_1 = require("node:events"); +const node_stream_1 = __importDefault(require("node:stream")); +const node_string_decoder_1 = require("node:string_decoder"); +/** + * Return true if the argument is a Minipass stream, Node stream, or something + * else that Minipass can interact with. + */ +const isStream = (s) => !!s && + typeof s === 'object' && + (s instanceof Minipass || + s instanceof node_stream_1.default || + (0, exports.isReadable)(s) || + (0, exports.isWritable)(s)); +exports.isStream = isStream; +/** + * Return true if the argument is a valid {@link Minipass.Readable} + */ +const isReadable = (s) => !!s && + typeof s === 'object' && + s instanceof node_events_1.EventEmitter && + typeof s.pipe === 'function' && + // node core Writable streams have a pipe() method, but it throws + s.pipe !== node_stream_1.default.Writable.prototype.pipe; +exports.isReadable = isReadable; +/** + * Return true if the argument is a valid {@link Minipass.Writable} + */ +const isWritable = (s) => !!s && + typeof s === 'object' && + s instanceof node_events_1.EventEmitter && + typeof s.write === 'function' && + typeof s.end === 'function'; +exports.isWritable = isWritable; +const EOF = Symbol('EOF'); +const MAYBE_EMIT_END = Symbol('maybeEmitEnd'); +const EMITTED_END = Symbol('emittedEnd'); +const EMITTING_END = Symbol('emittingEnd'); +const EMITTED_ERROR = Symbol('emittedError'); +const CLOSED = Symbol('closed'); +const READ = Symbol('read'); +const FLUSH = Symbol('flush'); +const FLUSHCHUNK = Symbol('flushChunk'); +const ENCODING = Symbol('encoding'); +const DECODER = Symbol('decoder'); +const FLOWING = Symbol('flowing'); +const PAUSED = Symbol('paused'); +const RESUME = Symbol('resume'); +const BUFFER = Symbol('buffer'); +const PIPES = Symbol('pipes'); +const BUFFERLENGTH = Symbol('bufferLength'); +const BUFFERPUSH = Symbol('bufferPush'); +const BUFFERSHIFT = Symbol('bufferShift'); +const OBJECTMODE = Symbol('objectMode'); +// internal event when stream is destroyed +const DESTROYED = Symbol('destroyed'); +// internal event when stream has an error +const ERROR = Symbol('error'); +const EMITDATA = Symbol('emitData'); +const EMITEND = Symbol('emitEnd'); +const EMITEND2 = Symbol('emitEnd2'); +const ASYNC = Symbol('async'); +const ABORT = Symbol('abort'); +const ABORTED = Symbol('aborted'); +const SIGNAL = Symbol('signal'); +const DATALISTENERS = Symbol('dataListeners'); +const DISCARDED = Symbol('discarded'); +const defer = (fn) => Promise.resolve().then(fn); +const nodefer = (fn) => fn(); +const isEndish = (ev) => ev === 'end' || ev === 'finish' || ev === 'prefinish'; +const isArrayBufferLike = (b) => b instanceof ArrayBuffer || + (!!b && + typeof b === 'object' && + b.constructor && + b.constructor.name === 'ArrayBuffer' && + b.byteLength >= 0); +const isArrayBufferView = (b) => !Buffer.isBuffer(b) && ArrayBuffer.isView(b); +/** + * Internal class representing a pipe to a destination stream. + * + * @internal + */ +class Pipe { + src; + dest; + opts; + ondrain; + constructor(src, dest, opts) { + this.src = src; + this.dest = dest; + this.opts = opts; + this.ondrain = () => src[RESUME](); + this.dest.on('drain', this.ondrain); + } + unpipe() { + this.dest.removeListener('drain', this.ondrain); + } + // only here for the prototype + /* c8 ignore start */ + proxyErrors(_er) { } + /* c8 ignore stop */ + end() { + this.unpipe(); + if (this.opts.end) + this.dest.end(); + } +} +/** + * Internal class representing a pipe to a destination stream where + * errors are proxied. + * + * @internal + */ +class PipeProxyErrors extends Pipe { + unpipe() { + this.src.removeListener('error', this.proxyErrors); + super.unpipe(); + } + constructor(src, dest, opts) { + super(src, dest, opts); + this.proxyErrors = er => dest.emit('error', er); + src.on('error', this.proxyErrors); + } +} +const isObjectModeOptions = (o) => !!o.objectMode; +const isEncodingOptions = (o) => !o.objectMode && !!o.encoding && o.encoding !== 'buffer'; +/** + * Main export, the Minipass class + * + * `RType` is the type of data emitted, defaults to Buffer + * + * `WType` is the type of data to be written, if RType is buffer or string, + * then any {@link Minipass.ContiguousData} is allowed. + * + * `Events` is the set of event handler signatures that this object + * will emit, see {@link Minipass.Events} + */ +class Minipass extends node_events_1.EventEmitter { + [FLOWING] = false; + [PAUSED] = false; + [PIPES] = []; + [BUFFER] = []; + [OBJECTMODE]; + [ENCODING]; + [ASYNC]; + [DECODER]; + [EOF] = false; + [EMITTED_END] = false; + [EMITTING_END] = false; + [CLOSED] = false; + [EMITTED_ERROR] = null; + [BUFFERLENGTH] = 0; + [DESTROYED] = false; + [SIGNAL]; + [ABORTED] = false; + [DATALISTENERS] = 0; + [DISCARDED] = false; + /** + * true if the stream can be written + */ + writable = true; + /** + * true if the stream can be read + */ + readable = true; + /** + * If `RType` is Buffer, then options do not need to be provided. + * Otherwise, an options object must be provided to specify either + * {@link Minipass.SharedOptions.objectMode} or + * {@link Minipass.SharedOptions.encoding}, as appropriate. + */ + constructor(...args) { + const options = (args[0] || + {}); + super(); + if (options.objectMode && typeof options.encoding === 'string') { + throw new TypeError('Encoding and objectMode may not be used together'); + } + if (isObjectModeOptions(options)) { + this[OBJECTMODE] = true; + this[ENCODING] = null; + } + else if (isEncodingOptions(options)) { + this[ENCODING] = options.encoding; + this[OBJECTMODE] = false; + } + else { + this[OBJECTMODE] = false; + this[ENCODING] = null; + } + this[ASYNC] = !!options.async; + this[DECODER] = this[ENCODING] + ? new node_string_decoder_1.StringDecoder(this[ENCODING]) + : null; + //@ts-ignore - private option for debugging and testing + if (options && options.debugExposeBuffer === true) { + Object.defineProperty(this, 'buffer', { get: () => this[BUFFER] }); + } + //@ts-ignore - private option for debugging and testing + if (options && options.debugExposePipes === true) { + Object.defineProperty(this, 'pipes', { get: () => this[PIPES] }); + } + const { signal } = options; + if (signal) { + this[SIGNAL] = signal; + if (signal.aborted) { + this[ABORT](); + } + else { + signal.addEventListener('abort', () => this[ABORT]()); + } + } + } + /** + * The amount of data stored in the buffer waiting to be read. + * + * For Buffer strings, this will be the total byte length. + * For string encoding streams, this will be the string character length, + * according to JavaScript's `string.length` logic. + * For objectMode streams, this is a count of the items waiting to be + * emitted. + */ + get bufferLength() { + return this[BUFFERLENGTH]; + } + /** + * The `BufferEncoding` currently in use, or `null` + */ + get encoding() { + return this[ENCODING]; + } + /** + * @deprecated - This is a read only property + */ + set encoding(_enc) { + throw new Error('Encoding must be set at instantiation time'); + } + /** + * @deprecated - Encoding may only be set at instantiation time + */ + setEncoding(_enc) { + throw new Error('Encoding must be set at instantiation time'); + } + /** + * True if this is an objectMode stream + */ + get objectMode() { + return this[OBJECTMODE]; + } + /** + * @deprecated - This is a read-only property + */ + set objectMode(_om) { + throw new Error('objectMode must be set at instantiation time'); + } + /** + * true if this is an async stream + */ + get ['async']() { + return this[ASYNC]; + } + /** + * Set to true to make this stream async. + * + * Once set, it cannot be unset, as this would potentially cause incorrect + * behavior. Ie, a sync stream can be made async, but an async stream + * cannot be safely made sync. + */ + set ['async'](a) { + this[ASYNC] = this[ASYNC] || !!a; + } + // drop everything and get out of the flow completely + [ABORT]() { + this[ABORTED] = true; + this.emit('abort', this[SIGNAL]?.reason); + this.destroy(this[SIGNAL]?.reason); + } + /** + * True if the stream has been aborted. + */ + get aborted() { + return this[ABORTED]; + } + /** + * No-op setter. Stream aborted status is set via the AbortSignal provided + * in the constructor options. + */ + set aborted(_) { } + write(chunk, encoding, cb) { + if (this[ABORTED]) + return false; + if (this[EOF]) + throw new Error('write after end'); + if (this[DESTROYED]) { + this.emit('error', Object.assign(new Error('Cannot call write after a stream was destroyed'), { code: 'ERR_STREAM_DESTROYED' })); + return true; + } + if (typeof encoding === 'function') { + cb = encoding; + encoding = 'utf8'; + } + if (!encoding) + encoding = 'utf8'; + const fn = this[ASYNC] ? defer : nodefer; + // convert array buffers and typed array views into buffers + // at some point in the future, we may want to do the opposite! + // leave strings and buffers as-is + // anything is only allowed if in object mode, so throw + if (!this[OBJECTMODE] && !Buffer.isBuffer(chunk)) { + if (isArrayBufferView(chunk)) { + //@ts-ignore - sinful unsafe type changing + chunk = Buffer.from(chunk.buffer, chunk.byteOffset, chunk.byteLength); + } + else if (isArrayBufferLike(chunk)) { + //@ts-ignore - sinful unsafe type changing + chunk = Buffer.from(chunk); + } + else if (typeof chunk !== 'string') { + throw new Error('Non-contiguous data written to non-objectMode stream'); + } + } + // handle object mode up front, since it's simpler + // this yields better performance, fewer checks later. + if (this[OBJECTMODE]) { + // maybe impossible? + /* c8 ignore start */ + if (this[FLOWING] && this[BUFFERLENGTH] !== 0) + this[FLUSH](true); + /* c8 ignore stop */ + if (this[FLOWING]) + this.emit('data', chunk); + else + this[BUFFERPUSH](chunk); + if (this[BUFFERLENGTH] !== 0) + this.emit('readable'); + if (cb) + fn(cb); + return this[FLOWING]; + } + // at this point the chunk is a buffer or string + // don't buffer it up or send it to the decoder + if (!chunk.length) { + if (this[BUFFERLENGTH] !== 0) + this.emit('readable'); + if (cb) + fn(cb); + return this[FLOWING]; + } + // fast-path writing strings of same encoding to a stream with + // an empty buffer, skipping the buffer/decoder dance + if (typeof chunk === 'string' && + // unless it is a string already ready for us to use + !(encoding === this[ENCODING] && !this[DECODER]?.lastNeed)) { + //@ts-ignore - sinful unsafe type change + chunk = Buffer.from(chunk, encoding); + } + if (Buffer.isBuffer(chunk) && this[ENCODING]) { + //@ts-ignore - sinful unsafe type change + chunk = this[DECODER].write(chunk); + } + // Note: flushing CAN potentially switch us into not-flowing mode + if (this[FLOWING] && this[BUFFERLENGTH] !== 0) + this[FLUSH](true); + if (this[FLOWING]) + this.emit('data', chunk); + else + this[BUFFERPUSH](chunk); + if (this[BUFFERLENGTH] !== 0) + this.emit('readable'); + if (cb) + fn(cb); + return this[FLOWING]; + } + /** + * Low-level explicit read method. + * + * In objectMode, the argument is ignored, and one item is returned if + * available. + * + * `n` is the number of bytes (or in the case of encoding streams, + * characters) to consume. If `n` is not provided, then the entire buffer + * is returned, or `null` is returned if no data is available. + * + * If `n` is greater that the amount of data in the internal buffer, + * then `null` is returned. + */ + read(n) { + if (this[DESTROYED]) + return null; + this[DISCARDED] = false; + if (this[BUFFERLENGTH] === 0 || + n === 0 || + (n && n > this[BUFFERLENGTH])) { + this[MAYBE_EMIT_END](); + return null; + } + if (this[OBJECTMODE]) + n = null; + if (this[BUFFER].length > 1 && !this[OBJECTMODE]) { + // not object mode, so if we have an encoding, then RType is string + // otherwise, must be Buffer + this[BUFFER] = [ + (this[ENCODING] + ? this[BUFFER].join('') + : Buffer.concat(this[BUFFER], this[BUFFERLENGTH])), + ]; + } + const ret = this[READ](n || null, this[BUFFER][0]); + this[MAYBE_EMIT_END](); + return ret; + } + [READ](n, chunk) { + if (this[OBJECTMODE]) + this[BUFFERSHIFT](); + else { + const c = chunk; + if (n === c.length || n === null) + this[BUFFERSHIFT](); + else if (typeof c === 'string') { + this[BUFFER][0] = c.slice(n); + chunk = c.slice(0, n); + this[BUFFERLENGTH] -= n; + } + else { + this[BUFFER][0] = c.subarray(n); + chunk = c.subarray(0, n); + this[BUFFERLENGTH] -= n; + } + } + this.emit('data', chunk); + if (!this[BUFFER].length && !this[EOF]) + this.emit('drain'); + return chunk; + } + end(chunk, encoding, cb) { + if (typeof chunk === 'function') { + cb = chunk; + chunk = undefined; + } + if (typeof encoding === 'function') { + cb = encoding; + encoding = 'utf8'; + } + if (chunk !== undefined) + this.write(chunk, encoding); + if (cb) + this.once('end', cb); + this[EOF] = true; + this.writable = false; + // if we haven't written anything, then go ahead and emit, + // even if we're not reading. + // we'll re-emit if a new 'end' listener is added anyway. + // This makes MP more suitable to write-only use cases. + if (this[FLOWING] || !this[PAUSED]) + this[MAYBE_EMIT_END](); + return this; + } + // don't let the internal resume be overwritten + [RESUME]() { + if (this[DESTROYED]) + return; + if (!this[DATALISTENERS] && !this[PIPES].length) { + this[DISCARDED] = true; + } + this[PAUSED] = false; + this[FLOWING] = true; + this.emit('resume'); + if (this[BUFFER].length) + this[FLUSH](); + else if (this[EOF]) + this[MAYBE_EMIT_END](); + else + this.emit('drain'); + } + /** + * Resume the stream if it is currently in a paused state + * + * If called when there are no pipe destinations or `data` event listeners, + * this will place the stream in a "discarded" state, where all data will + * be thrown away. The discarded state is removed if a pipe destination or + * data handler is added, if pause() is called, or if any synchronous or + * asynchronous iteration is started. + */ + resume() { + return this[RESUME](); + } + /** + * Pause the stream + */ + pause() { + this[FLOWING] = false; + this[PAUSED] = true; + this[DISCARDED] = false; + } + /** + * true if the stream has been forcibly destroyed + */ + get destroyed() { + return this[DESTROYED]; + } + /** + * true if the stream is currently in a flowing state, meaning that + * any writes will be immediately emitted. + */ + get flowing() { + return this[FLOWING]; + } + /** + * true if the stream is currently in a paused state + */ + get paused() { + return this[PAUSED]; + } + [BUFFERPUSH](chunk) { + if (this[OBJECTMODE]) + this[BUFFERLENGTH] += 1; + else + this[BUFFERLENGTH] += chunk.length; + this[BUFFER].push(chunk); + } + [BUFFERSHIFT]() { + if (this[OBJECTMODE]) + this[BUFFERLENGTH] -= 1; + else + this[BUFFERLENGTH] -= this[BUFFER][0].length; + return this[BUFFER].shift(); + } + [FLUSH](noDrain = false) { + do { } while (this[FLUSHCHUNK](this[BUFFERSHIFT]()) && + this[BUFFER].length); + if (!noDrain && !this[BUFFER].length && !this[EOF]) + this.emit('drain'); + } + [FLUSHCHUNK](chunk) { + this.emit('data', chunk); + return this[FLOWING]; + } + /** + * Pipe all data emitted by this stream into the destination provided. + * + * Triggers the flow of data. + */ + pipe(dest, opts) { + if (this[DESTROYED]) + return dest; + this[DISCARDED] = false; + const ended = this[EMITTED_END]; + opts = opts || {}; + if (dest === proc.stdout || dest === proc.stderr) + opts.end = false; + else + opts.end = opts.end !== false; + opts.proxyErrors = !!opts.proxyErrors; + // piping an ended stream ends immediately + if (ended) { + if (opts.end) + dest.end(); + } + else { + // "as" here just ignores the WType, which pipes don't care about, + // since they're only consuming from us, and writing to the dest + this[PIPES].push(!opts.proxyErrors + ? new Pipe(this, dest, opts) + : new PipeProxyErrors(this, dest, opts)); + if (this[ASYNC]) + defer(() => this[RESUME]()); + else + this[RESUME](); + } + return dest; + } + /** + * Fully unhook a piped destination stream. + * + * If the destination stream was the only consumer of this stream (ie, + * there are no other piped destinations or `'data'` event listeners) + * then the flow of data will stop until there is another consumer or + * {@link Minipass#resume} is explicitly called. + */ + unpipe(dest) { + const p = this[PIPES].find(p => p.dest === dest); + if (p) { + if (this[PIPES].length === 1) { + if (this[FLOWING] && this[DATALISTENERS] === 0) { + this[FLOWING] = false; + } + this[PIPES] = []; + } + else + this[PIPES].splice(this[PIPES].indexOf(p), 1); + p.unpipe(); + } + } + /** + * Alias for {@link Minipass#on} + */ + addListener(ev, handler) { + return this.on(ev, handler); + } + /** + * Mostly identical to `EventEmitter.on`, with the following + * behavior differences to prevent data loss and unnecessary hangs: + * + * - Adding a 'data' event handler will trigger the flow of data + * + * - Adding a 'readable' event handler when there is data waiting to be read + * will cause 'readable' to be emitted immediately. + * + * - Adding an 'endish' event handler ('end', 'finish', etc.) which has + * already passed will cause the event to be emitted immediately and all + * handlers removed. + * + * - Adding an 'error' event handler after an error has been emitted will + * cause the event to be re-emitted immediately with the error previously + * raised. + */ + on(ev, handler) { + const ret = super.on(ev, handler); + if (ev === 'data') { + this[DISCARDED] = false; + this[DATALISTENERS]++; + if (!this[PIPES].length && !this[FLOWING]) { + this[RESUME](); + } + } + else if (ev === 'readable' && this[BUFFERLENGTH] !== 0) { + super.emit('readable'); + } + else if (isEndish(ev) && this[EMITTED_END]) { + super.emit(ev); + this.removeAllListeners(ev); + } + else if (ev === 'error' && this[EMITTED_ERROR]) { + const h = handler; + if (this[ASYNC]) + defer(() => h.call(this, this[EMITTED_ERROR])); + else + h.call(this, this[EMITTED_ERROR]); + } + return ret; + } + /** + * Alias for {@link Minipass#off} + */ + removeListener(ev, handler) { + return this.off(ev, handler); + } + /** + * Mostly identical to `EventEmitter.off` + * + * If a 'data' event handler is removed, and it was the last consumer + * (ie, there are no pipe destinations or other 'data' event listeners), + * then the flow of data will stop until there is another consumer or + * {@link Minipass#resume} is explicitly called. + */ + off(ev, handler) { + const ret = super.off(ev, handler); + // if we previously had listeners, and now we don't, and we don't + // have any pipes, then stop the flow, unless it's been explicitly + // put in a discarded flowing state via stream.resume(). + if (ev === 'data') { + this[DATALISTENERS] = this.listeners('data').length; + if (this[DATALISTENERS] === 0 && + !this[DISCARDED] && + !this[PIPES].length) { + this[FLOWING] = false; + } + } + return ret; + } + /** + * Mostly identical to `EventEmitter.removeAllListeners` + * + * If all 'data' event handlers are removed, and they were the last consumer + * (ie, there are no pipe destinations), then the flow of data will stop + * until there is another consumer or {@link Minipass#resume} is explicitly + * called. + */ + removeAllListeners(ev) { + const ret = super.removeAllListeners(ev); + if (ev === 'data' || ev === undefined) { + this[DATALISTENERS] = 0; + if (!this[DISCARDED] && !this[PIPES].length) { + this[FLOWING] = false; + } + } + return ret; + } + /** + * true if the 'end' event has been emitted + */ + get emittedEnd() { + return this[EMITTED_END]; + } + [MAYBE_EMIT_END]() { + if (!this[EMITTING_END] && + !this[EMITTED_END] && + !this[DESTROYED] && + this[BUFFER].length === 0 && + this[EOF]) { + this[EMITTING_END] = true; + this.emit('end'); + this.emit('prefinish'); + this.emit('finish'); + if (this[CLOSED]) + this.emit('close'); + this[EMITTING_END] = false; + } + } + /** + * Mostly identical to `EventEmitter.emit`, with the following + * behavior differences to prevent data loss and unnecessary hangs: + * + * If the stream has been destroyed, and the event is something other + * than 'close' or 'error', then `false` is returned and no handlers + * are called. + * + * If the event is 'end', and has already been emitted, then the event + * is ignored. If the stream is in a paused or non-flowing state, then + * the event will be deferred until data flow resumes. If the stream is + * async, then handlers will be called on the next tick rather than + * immediately. + * + * If the event is 'close', and 'end' has not yet been emitted, then + * the event will be deferred until after 'end' is emitted. + * + * If the event is 'error', and an AbortSignal was provided for the stream, + * and there are no listeners, then the event is ignored, matching the + * behavior of node core streams in the presense of an AbortSignal. + * + * If the event is 'finish' or 'prefinish', then all listeners will be + * removed after emitting the event, to prevent double-firing. + */ + emit(ev, ...args) { + const data = args[0]; + // error and close are only events allowed after calling destroy() + if (ev !== 'error' && + ev !== 'close' && + ev !== DESTROYED && + this[DESTROYED]) { + return false; + } + else if (ev === 'data') { + return !this[OBJECTMODE] && !data + ? false + : this[ASYNC] + ? (defer(() => this[EMITDATA](data)), true) + : this[EMITDATA](data); + } + else if (ev === 'end') { + return this[EMITEND](); + } + else if (ev === 'close') { + this[CLOSED] = true; + // don't emit close before 'end' and 'finish' + if (!this[EMITTED_END] && !this[DESTROYED]) + return false; + const ret = super.emit('close'); + this.removeAllListeners('close'); + return ret; + } + else if (ev === 'error') { + this[EMITTED_ERROR] = data; + super.emit(ERROR, data); + const ret = !this[SIGNAL] || this.listeners('error').length + ? super.emit('error', data) + : false; + this[MAYBE_EMIT_END](); + return ret; + } + else if (ev === 'resume') { + const ret = super.emit('resume'); + this[MAYBE_EMIT_END](); + return ret; + } + else if (ev === 'finish' || ev === 'prefinish') { + const ret = super.emit(ev); + this.removeAllListeners(ev); + return ret; + } + // Some other unknown event + const ret = super.emit(ev, ...args); + this[MAYBE_EMIT_END](); + return ret; + } + [EMITDATA](data) { + for (const p of this[PIPES]) { + if (p.dest.write(data) === false) + this.pause(); + } + const ret = this[DISCARDED] ? false : super.emit('data', data); + this[MAYBE_EMIT_END](); + return ret; + } + [EMITEND]() { + if (this[EMITTED_END]) + return false; + this[EMITTED_END] = true; + this.readable = false; + return this[ASYNC] + ? (defer(() => this[EMITEND2]()), true) + : this[EMITEND2](); + } + [EMITEND2]() { + if (this[DECODER]) { + const data = this[DECODER].end(); + if (data) { + for (const p of this[PIPES]) { + p.dest.write(data); + } + if (!this[DISCARDED]) + super.emit('data', data); + } + } + for (const p of this[PIPES]) { + p.end(); + } + const ret = super.emit('end'); + this.removeAllListeners('end'); + return ret; + } + /** + * Return a Promise that resolves to an array of all emitted data once + * the stream ends. + */ + async collect() { + const buf = Object.assign([], { + dataLength: 0, + }); + if (!this[OBJECTMODE]) + buf.dataLength = 0; + // set the promise first, in case an error is raised + // by triggering the flow here. + const p = this.promise(); + this.on('data', c => { + buf.push(c); + if (!this[OBJECTMODE]) + buf.dataLength += c.length; + }); + await p; + return buf; + } + /** + * Return a Promise that resolves to the concatenation of all emitted data + * once the stream ends. + * + * Not allowed on objectMode streams. + */ + async concat() { + if (this[OBJECTMODE]) { + throw new Error('cannot concat in objectMode'); + } + const buf = await this.collect(); + return (this[ENCODING] + ? buf.join('') + : Buffer.concat(buf, buf.dataLength)); + } + /** + * Return a void Promise that resolves once the stream ends. + */ + async promise() { + return new Promise((resolve, reject) => { + this.on(DESTROYED, () => reject(new Error('stream destroyed'))); + this.on('error', er => reject(er)); + this.on('end', () => resolve()); + }); + } + /** + * Asynchronous `for await of` iteration. + * + * This will continue emitting all chunks until the stream terminates. + */ + [Symbol.asyncIterator]() { + // set this up front, in case the consumer doesn't call next() + // right away. + this[DISCARDED] = false; + let stopped = false; + const stop = async () => { + this.pause(); + stopped = true; + return { value: undefined, done: true }; + }; + const next = () => { + if (stopped) + return stop(); + const res = this.read(); + if (res !== null) + return Promise.resolve({ done: false, value: res }); + if (this[EOF]) + return stop(); + let resolve; + let reject; + const onerr = (er) => { + this.off('data', ondata); + this.off('end', onend); + this.off(DESTROYED, ondestroy); + stop(); + reject(er); + }; + const ondata = (value) => { + this.off('error', onerr); + this.off('end', onend); + this.off(DESTROYED, ondestroy); + this.pause(); + resolve({ value, done: !!this[EOF] }); + }; + const onend = () => { + this.off('error', onerr); + this.off('data', ondata); + this.off(DESTROYED, ondestroy); + stop(); + resolve({ done: true, value: undefined }); + }; + const ondestroy = () => onerr(new Error('stream destroyed')); + return new Promise((res, rej) => { + reject = rej; + resolve = res; + this.once(DESTROYED, ondestroy); + this.once('error', onerr); + this.once('end', onend); + this.once('data', ondata); + }); + }; + return { + next, + throw: stop, + return: stop, + [Symbol.asyncIterator]() { + return this; + }, + }; + } + /** + * Synchronous `for of` iteration. + * + * The iteration will terminate when the internal buffer runs out, even + * if the stream has not yet terminated. + */ + [Symbol.iterator]() { + // set this up front, in case the consumer doesn't call next() + // right away. + this[DISCARDED] = false; + let stopped = false; + const stop = () => { + this.pause(); + this.off(ERROR, stop); + this.off(DESTROYED, stop); + this.off('end', stop); + stopped = true; + return { done: true, value: undefined }; + }; + const next = () => { + if (stopped) + return stop(); + const value = this.read(); + return value === null ? stop() : { done: false, value }; + }; + this.once('end', stop); + this.once(ERROR, stop); + this.once(DESTROYED, stop); + return { + next, + throw: stop, + return: stop, + [Symbol.iterator]() { + return this; + }, + }; + } + /** + * Destroy a stream, preventing it from being used for any further purpose. + * + * If the stream has a `close()` method, then it will be called on + * destruction. + * + * After destruction, any attempt to write data, read data, or emit most + * events will be ignored. + * + * If an error argument is provided, then it will be emitted in an + * 'error' event. + */ + destroy(er) { + if (this[DESTROYED]) { + if (er) + this.emit('error', er); + else + this.emit(DESTROYED); + return this; + } + this[DESTROYED] = true; + this[DISCARDED] = true; + // throw away all buffered data, it's never coming out + this[BUFFER].length = 0; + this[BUFFERLENGTH] = 0; + const wc = this; + if (typeof wc.close === 'function' && !this[CLOSED]) + wc.close(); + if (er) + this.emit('error', er); + // if no error to emit, still reject pending promises + else + this.emit(DESTROYED); + return this; + } + /** + * Alias for {@link isStream} + * + * Former export location, maintained for backwards compatibility. + * + * @deprecated + */ + static get isStream() { + return exports.isStream; + } +} +exports.Minipass = Minipass; +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/node_modules/npm-registry-fetch/node_modules/minipass/dist/cjs/package.json b/node_modules/npm-registry-fetch/node_modules/minipass/dist/cjs/package.json new file mode 100644 index 0000000000000..5bbefffbabee3 --- /dev/null +++ b/node_modules/npm-registry-fetch/node_modules/minipass/dist/cjs/package.json @@ -0,0 +1,3 @@ +{ + "type": "commonjs" +} diff --git a/node_modules/npm-registry-fetch/node_modules/minipass/dist/mjs/index.js b/node_modules/npm-registry-fetch/node_modules/minipass/dist/mjs/index.js new file mode 100644 index 0000000000000..b5fa4513c9083 --- /dev/null +++ b/node_modules/npm-registry-fetch/node_modules/minipass/dist/mjs/index.js @@ -0,0 +1,1018 @@ +const proc = typeof process === 'object' && process + ? process + : { + stdout: null, + stderr: null, + }; +import { EventEmitter } from 'node:events'; +import Stream from 'node:stream'; +import { StringDecoder } from 'node:string_decoder'; +/** + * Return true if the argument is a Minipass stream, Node stream, or something + * else that Minipass can interact with. + */ +export const isStream = (s) => !!s && + typeof s === 'object' && + (s instanceof Minipass || + s instanceof Stream || + isReadable(s) || + isWritable(s)); +/** + * Return true if the argument is a valid {@link Minipass.Readable} + */ +export const isReadable = (s) => !!s && + typeof s === 'object' && + s instanceof EventEmitter && + typeof s.pipe === 'function' && + // node core Writable streams have a pipe() method, but it throws + s.pipe !== Stream.Writable.prototype.pipe; +/** + * Return true if the argument is a valid {@link Minipass.Writable} + */ +export const isWritable = (s) => !!s && + typeof s === 'object' && + s instanceof EventEmitter && + typeof s.write === 'function' && + typeof s.end === 'function'; +const EOF = Symbol('EOF'); +const MAYBE_EMIT_END = Symbol('maybeEmitEnd'); +const EMITTED_END = Symbol('emittedEnd'); +const EMITTING_END = Symbol('emittingEnd'); +const EMITTED_ERROR = Symbol('emittedError'); +const CLOSED = Symbol('closed'); +const READ = Symbol('read'); +const FLUSH = Symbol('flush'); +const FLUSHCHUNK = Symbol('flushChunk'); +const ENCODING = Symbol('encoding'); +const DECODER = Symbol('decoder'); +const FLOWING = Symbol('flowing'); +const PAUSED = Symbol('paused'); +const RESUME = Symbol('resume'); +const BUFFER = Symbol('buffer'); +const PIPES = Symbol('pipes'); +const BUFFERLENGTH = Symbol('bufferLength'); +const BUFFERPUSH = Symbol('bufferPush'); +const BUFFERSHIFT = Symbol('bufferShift'); +const OBJECTMODE = Symbol('objectMode'); +// internal event when stream is destroyed +const DESTROYED = Symbol('destroyed'); +// internal event when stream has an error +const ERROR = Symbol('error'); +const EMITDATA = Symbol('emitData'); +const EMITEND = Symbol('emitEnd'); +const EMITEND2 = Symbol('emitEnd2'); +const ASYNC = Symbol('async'); +const ABORT = Symbol('abort'); +const ABORTED = Symbol('aborted'); +const SIGNAL = Symbol('signal'); +const DATALISTENERS = Symbol('dataListeners'); +const DISCARDED = Symbol('discarded'); +const defer = (fn) => Promise.resolve().then(fn); +const nodefer = (fn) => fn(); +const isEndish = (ev) => ev === 'end' || ev === 'finish' || ev === 'prefinish'; +const isArrayBufferLike = (b) => b instanceof ArrayBuffer || + (!!b && + typeof b === 'object' && + b.constructor && + b.constructor.name === 'ArrayBuffer' && + b.byteLength >= 0); +const isArrayBufferView = (b) => !Buffer.isBuffer(b) && ArrayBuffer.isView(b); +/** + * Internal class representing a pipe to a destination stream. + * + * @internal + */ +class Pipe { + src; + dest; + opts; + ondrain; + constructor(src, dest, opts) { + this.src = src; + this.dest = dest; + this.opts = opts; + this.ondrain = () => src[RESUME](); + this.dest.on('drain', this.ondrain); + } + unpipe() { + this.dest.removeListener('drain', this.ondrain); + } + // only here for the prototype + /* c8 ignore start */ + proxyErrors(_er) { } + /* c8 ignore stop */ + end() { + this.unpipe(); + if (this.opts.end) + this.dest.end(); + } +} +/** + * Internal class representing a pipe to a destination stream where + * errors are proxied. + * + * @internal + */ +class PipeProxyErrors extends Pipe { + unpipe() { + this.src.removeListener('error', this.proxyErrors); + super.unpipe(); + } + constructor(src, dest, opts) { + super(src, dest, opts); + this.proxyErrors = er => dest.emit('error', er); + src.on('error', this.proxyErrors); + } +} +const isObjectModeOptions = (o) => !!o.objectMode; +const isEncodingOptions = (o) => !o.objectMode && !!o.encoding && o.encoding !== 'buffer'; +/** + * Main export, the Minipass class + * + * `RType` is the type of data emitted, defaults to Buffer + * + * `WType` is the type of data to be written, if RType is buffer or string, + * then any {@link Minipass.ContiguousData} is allowed. + * + * `Events` is the set of event handler signatures that this object + * will emit, see {@link Minipass.Events} + */ +export class Minipass extends EventEmitter { + [FLOWING] = false; + [PAUSED] = false; + [PIPES] = []; + [BUFFER] = []; + [OBJECTMODE]; + [ENCODING]; + [ASYNC]; + [DECODER]; + [EOF] = false; + [EMITTED_END] = false; + [EMITTING_END] = false; + [CLOSED] = false; + [EMITTED_ERROR] = null; + [BUFFERLENGTH] = 0; + [DESTROYED] = false; + [SIGNAL]; + [ABORTED] = false; + [DATALISTENERS] = 0; + [DISCARDED] = false; + /** + * true if the stream can be written + */ + writable = true; + /** + * true if the stream can be read + */ + readable = true; + /** + * If `RType` is Buffer, then options do not need to be provided. + * Otherwise, an options object must be provided to specify either + * {@link Minipass.SharedOptions.objectMode} or + * {@link Minipass.SharedOptions.encoding}, as appropriate. + */ + constructor(...args) { + const options = (args[0] || + {}); + super(); + if (options.objectMode && typeof options.encoding === 'string') { + throw new TypeError('Encoding and objectMode may not be used together'); + } + if (isObjectModeOptions(options)) { + this[OBJECTMODE] = true; + this[ENCODING] = null; + } + else if (isEncodingOptions(options)) { + this[ENCODING] = options.encoding; + this[OBJECTMODE] = false; + } + else { + this[OBJECTMODE] = false; + this[ENCODING] = null; + } + this[ASYNC] = !!options.async; + this[DECODER] = this[ENCODING] + ? new StringDecoder(this[ENCODING]) + : null; + //@ts-ignore - private option for debugging and testing + if (options && options.debugExposeBuffer === true) { + Object.defineProperty(this, 'buffer', { get: () => this[BUFFER] }); + } + //@ts-ignore - private option for debugging and testing + if (options && options.debugExposePipes === true) { + Object.defineProperty(this, 'pipes', { get: () => this[PIPES] }); + } + const { signal } = options; + if (signal) { + this[SIGNAL] = signal; + if (signal.aborted) { + this[ABORT](); + } + else { + signal.addEventListener('abort', () => this[ABORT]()); + } + } + } + /** + * The amount of data stored in the buffer waiting to be read. + * + * For Buffer strings, this will be the total byte length. + * For string encoding streams, this will be the string character length, + * according to JavaScript's `string.length` logic. + * For objectMode streams, this is a count of the items waiting to be + * emitted. + */ + get bufferLength() { + return this[BUFFERLENGTH]; + } + /** + * The `BufferEncoding` currently in use, or `null` + */ + get encoding() { + return this[ENCODING]; + } + /** + * @deprecated - This is a read only property + */ + set encoding(_enc) { + throw new Error('Encoding must be set at instantiation time'); + } + /** + * @deprecated - Encoding may only be set at instantiation time + */ + setEncoding(_enc) { + throw new Error('Encoding must be set at instantiation time'); + } + /** + * True if this is an objectMode stream + */ + get objectMode() { + return this[OBJECTMODE]; + } + /** + * @deprecated - This is a read-only property + */ + set objectMode(_om) { + throw new Error('objectMode must be set at instantiation time'); + } + /** + * true if this is an async stream + */ + get ['async']() { + return this[ASYNC]; + } + /** + * Set to true to make this stream async. + * + * Once set, it cannot be unset, as this would potentially cause incorrect + * behavior. Ie, a sync stream can be made async, but an async stream + * cannot be safely made sync. + */ + set ['async'](a) { + this[ASYNC] = this[ASYNC] || !!a; + } + // drop everything and get out of the flow completely + [ABORT]() { + this[ABORTED] = true; + this.emit('abort', this[SIGNAL]?.reason); + this.destroy(this[SIGNAL]?.reason); + } + /** + * True if the stream has been aborted. + */ + get aborted() { + return this[ABORTED]; + } + /** + * No-op setter. Stream aborted status is set via the AbortSignal provided + * in the constructor options. + */ + set aborted(_) { } + write(chunk, encoding, cb) { + if (this[ABORTED]) + return false; + if (this[EOF]) + throw new Error('write after end'); + if (this[DESTROYED]) { + this.emit('error', Object.assign(new Error('Cannot call write after a stream was destroyed'), { code: 'ERR_STREAM_DESTROYED' })); + return true; + } + if (typeof encoding === 'function') { + cb = encoding; + encoding = 'utf8'; + } + if (!encoding) + encoding = 'utf8'; + const fn = this[ASYNC] ? defer : nodefer; + // convert array buffers and typed array views into buffers + // at some point in the future, we may want to do the opposite! + // leave strings and buffers as-is + // anything is only allowed if in object mode, so throw + if (!this[OBJECTMODE] && !Buffer.isBuffer(chunk)) { + if (isArrayBufferView(chunk)) { + //@ts-ignore - sinful unsafe type changing + chunk = Buffer.from(chunk.buffer, chunk.byteOffset, chunk.byteLength); + } + else if (isArrayBufferLike(chunk)) { + //@ts-ignore - sinful unsafe type changing + chunk = Buffer.from(chunk); + } + else if (typeof chunk !== 'string') { + throw new Error('Non-contiguous data written to non-objectMode stream'); + } + } + // handle object mode up front, since it's simpler + // this yields better performance, fewer checks later. + if (this[OBJECTMODE]) { + // maybe impossible? + /* c8 ignore start */ + if (this[FLOWING] && this[BUFFERLENGTH] !== 0) + this[FLUSH](true); + /* c8 ignore stop */ + if (this[FLOWING]) + this.emit('data', chunk); + else + this[BUFFERPUSH](chunk); + if (this[BUFFERLENGTH] !== 0) + this.emit('readable'); + if (cb) + fn(cb); + return this[FLOWING]; + } + // at this point the chunk is a buffer or string + // don't buffer it up or send it to the decoder + if (!chunk.length) { + if (this[BUFFERLENGTH] !== 0) + this.emit('readable'); + if (cb) + fn(cb); + return this[FLOWING]; + } + // fast-path writing strings of same encoding to a stream with + // an empty buffer, skipping the buffer/decoder dance + if (typeof chunk === 'string' && + // unless it is a string already ready for us to use + !(encoding === this[ENCODING] && !this[DECODER]?.lastNeed)) { + //@ts-ignore - sinful unsafe type change + chunk = Buffer.from(chunk, encoding); + } + if (Buffer.isBuffer(chunk) && this[ENCODING]) { + //@ts-ignore - sinful unsafe type change + chunk = this[DECODER].write(chunk); + } + // Note: flushing CAN potentially switch us into not-flowing mode + if (this[FLOWING] && this[BUFFERLENGTH] !== 0) + this[FLUSH](true); + if (this[FLOWING]) + this.emit('data', chunk); + else + this[BUFFERPUSH](chunk); + if (this[BUFFERLENGTH] !== 0) + this.emit('readable'); + if (cb) + fn(cb); + return this[FLOWING]; + } + /** + * Low-level explicit read method. + * + * In objectMode, the argument is ignored, and one item is returned if + * available. + * + * `n` is the number of bytes (or in the case of encoding streams, + * characters) to consume. If `n` is not provided, then the entire buffer + * is returned, or `null` is returned if no data is available. + * + * If `n` is greater that the amount of data in the internal buffer, + * then `null` is returned. + */ + read(n) { + if (this[DESTROYED]) + return null; + this[DISCARDED] = false; + if (this[BUFFERLENGTH] === 0 || + n === 0 || + (n && n > this[BUFFERLENGTH])) { + this[MAYBE_EMIT_END](); + return null; + } + if (this[OBJECTMODE]) + n = null; + if (this[BUFFER].length > 1 && !this[OBJECTMODE]) { + // not object mode, so if we have an encoding, then RType is string + // otherwise, must be Buffer + this[BUFFER] = [ + (this[ENCODING] + ? this[BUFFER].join('') + : Buffer.concat(this[BUFFER], this[BUFFERLENGTH])), + ]; + } + const ret = this[READ](n || null, this[BUFFER][0]); + this[MAYBE_EMIT_END](); + return ret; + } + [READ](n, chunk) { + if (this[OBJECTMODE]) + this[BUFFERSHIFT](); + else { + const c = chunk; + if (n === c.length || n === null) + this[BUFFERSHIFT](); + else if (typeof c === 'string') { + this[BUFFER][0] = c.slice(n); + chunk = c.slice(0, n); + this[BUFFERLENGTH] -= n; + } + else { + this[BUFFER][0] = c.subarray(n); + chunk = c.subarray(0, n); + this[BUFFERLENGTH] -= n; + } + } + this.emit('data', chunk); + if (!this[BUFFER].length && !this[EOF]) + this.emit('drain'); + return chunk; + } + end(chunk, encoding, cb) { + if (typeof chunk === 'function') { + cb = chunk; + chunk = undefined; + } + if (typeof encoding === 'function') { + cb = encoding; + encoding = 'utf8'; + } + if (chunk !== undefined) + this.write(chunk, encoding); + if (cb) + this.once('end', cb); + this[EOF] = true; + this.writable = false; + // if we haven't written anything, then go ahead and emit, + // even if we're not reading. + // we'll re-emit if a new 'end' listener is added anyway. + // This makes MP more suitable to write-only use cases. + if (this[FLOWING] || !this[PAUSED]) + this[MAYBE_EMIT_END](); + return this; + } + // don't let the internal resume be overwritten + [RESUME]() { + if (this[DESTROYED]) + return; + if (!this[DATALISTENERS] && !this[PIPES].length) { + this[DISCARDED] = true; + } + this[PAUSED] = false; + this[FLOWING] = true; + this.emit('resume'); + if (this[BUFFER].length) + this[FLUSH](); + else if (this[EOF]) + this[MAYBE_EMIT_END](); + else + this.emit('drain'); + } + /** + * Resume the stream if it is currently in a paused state + * + * If called when there are no pipe destinations or `data` event listeners, + * this will place the stream in a "discarded" state, where all data will + * be thrown away. The discarded state is removed if a pipe destination or + * data handler is added, if pause() is called, or if any synchronous or + * asynchronous iteration is started. + */ + resume() { + return this[RESUME](); + } + /** + * Pause the stream + */ + pause() { + this[FLOWING] = false; + this[PAUSED] = true; + this[DISCARDED] = false; + } + /** + * true if the stream has been forcibly destroyed + */ + get destroyed() { + return this[DESTROYED]; + } + /** + * true if the stream is currently in a flowing state, meaning that + * any writes will be immediately emitted. + */ + get flowing() { + return this[FLOWING]; + } + /** + * true if the stream is currently in a paused state + */ + get paused() { + return this[PAUSED]; + } + [BUFFERPUSH](chunk) { + if (this[OBJECTMODE]) + this[BUFFERLENGTH] += 1; + else + this[BUFFERLENGTH] += chunk.length; + this[BUFFER].push(chunk); + } + [BUFFERSHIFT]() { + if (this[OBJECTMODE]) + this[BUFFERLENGTH] -= 1; + else + this[BUFFERLENGTH] -= this[BUFFER][0].length; + return this[BUFFER].shift(); + } + [FLUSH](noDrain = false) { + do { } while (this[FLUSHCHUNK](this[BUFFERSHIFT]()) && + this[BUFFER].length); + if (!noDrain && !this[BUFFER].length && !this[EOF]) + this.emit('drain'); + } + [FLUSHCHUNK](chunk) { + this.emit('data', chunk); + return this[FLOWING]; + } + /** + * Pipe all data emitted by this stream into the destination provided. + * + * Triggers the flow of data. + */ + pipe(dest, opts) { + if (this[DESTROYED]) + return dest; + this[DISCARDED] = false; + const ended = this[EMITTED_END]; + opts = opts || {}; + if (dest === proc.stdout || dest === proc.stderr) + opts.end = false; + else + opts.end = opts.end !== false; + opts.proxyErrors = !!opts.proxyErrors; + // piping an ended stream ends immediately + if (ended) { + if (opts.end) + dest.end(); + } + else { + // "as" here just ignores the WType, which pipes don't care about, + // since they're only consuming from us, and writing to the dest + this[PIPES].push(!opts.proxyErrors + ? new Pipe(this, dest, opts) + : new PipeProxyErrors(this, dest, opts)); + if (this[ASYNC]) + defer(() => this[RESUME]()); + else + this[RESUME](); + } + return dest; + } + /** + * Fully unhook a piped destination stream. + * + * If the destination stream was the only consumer of this stream (ie, + * there are no other piped destinations or `'data'` event listeners) + * then the flow of data will stop until there is another consumer or + * {@link Minipass#resume} is explicitly called. + */ + unpipe(dest) { + const p = this[PIPES].find(p => p.dest === dest); + if (p) { + if (this[PIPES].length === 1) { + if (this[FLOWING] && this[DATALISTENERS] === 0) { + this[FLOWING] = false; + } + this[PIPES] = []; + } + else + this[PIPES].splice(this[PIPES].indexOf(p), 1); + p.unpipe(); + } + } + /** + * Alias for {@link Minipass#on} + */ + addListener(ev, handler) { + return this.on(ev, handler); + } + /** + * Mostly identical to `EventEmitter.on`, with the following + * behavior differences to prevent data loss and unnecessary hangs: + * + * - Adding a 'data' event handler will trigger the flow of data + * + * - Adding a 'readable' event handler when there is data waiting to be read + * will cause 'readable' to be emitted immediately. + * + * - Adding an 'endish' event handler ('end', 'finish', etc.) which has + * already passed will cause the event to be emitted immediately and all + * handlers removed. + * + * - Adding an 'error' event handler after an error has been emitted will + * cause the event to be re-emitted immediately with the error previously + * raised. + */ + on(ev, handler) { + const ret = super.on(ev, handler); + if (ev === 'data') { + this[DISCARDED] = false; + this[DATALISTENERS]++; + if (!this[PIPES].length && !this[FLOWING]) { + this[RESUME](); + } + } + else if (ev === 'readable' && this[BUFFERLENGTH] !== 0) { + super.emit('readable'); + } + else if (isEndish(ev) && this[EMITTED_END]) { + super.emit(ev); + this.removeAllListeners(ev); + } + else if (ev === 'error' && this[EMITTED_ERROR]) { + const h = handler; + if (this[ASYNC]) + defer(() => h.call(this, this[EMITTED_ERROR])); + else + h.call(this, this[EMITTED_ERROR]); + } + return ret; + } + /** + * Alias for {@link Minipass#off} + */ + removeListener(ev, handler) { + return this.off(ev, handler); + } + /** + * Mostly identical to `EventEmitter.off` + * + * If a 'data' event handler is removed, and it was the last consumer + * (ie, there are no pipe destinations or other 'data' event listeners), + * then the flow of data will stop until there is another consumer or + * {@link Minipass#resume} is explicitly called. + */ + off(ev, handler) { + const ret = super.off(ev, handler); + // if we previously had listeners, and now we don't, and we don't + // have any pipes, then stop the flow, unless it's been explicitly + // put in a discarded flowing state via stream.resume(). + if (ev === 'data') { + this[DATALISTENERS] = this.listeners('data').length; + if (this[DATALISTENERS] === 0 && + !this[DISCARDED] && + !this[PIPES].length) { + this[FLOWING] = false; + } + } + return ret; + } + /** + * Mostly identical to `EventEmitter.removeAllListeners` + * + * If all 'data' event handlers are removed, and they were the last consumer + * (ie, there are no pipe destinations), then the flow of data will stop + * until there is another consumer or {@link Minipass#resume} is explicitly + * called. + */ + removeAllListeners(ev) { + const ret = super.removeAllListeners(ev); + if (ev === 'data' || ev === undefined) { + this[DATALISTENERS] = 0; + if (!this[DISCARDED] && !this[PIPES].length) { + this[FLOWING] = false; + } + } + return ret; + } + /** + * true if the 'end' event has been emitted + */ + get emittedEnd() { + return this[EMITTED_END]; + } + [MAYBE_EMIT_END]() { + if (!this[EMITTING_END] && + !this[EMITTED_END] && + !this[DESTROYED] && + this[BUFFER].length === 0 && + this[EOF]) { + this[EMITTING_END] = true; + this.emit('end'); + this.emit('prefinish'); + this.emit('finish'); + if (this[CLOSED]) + this.emit('close'); + this[EMITTING_END] = false; + } + } + /** + * Mostly identical to `EventEmitter.emit`, with the following + * behavior differences to prevent data loss and unnecessary hangs: + * + * If the stream has been destroyed, and the event is something other + * than 'close' or 'error', then `false` is returned and no handlers + * are called. + * + * If the event is 'end', and has already been emitted, then the event + * is ignored. If the stream is in a paused or non-flowing state, then + * the event will be deferred until data flow resumes. If the stream is + * async, then handlers will be called on the next tick rather than + * immediately. + * + * If the event is 'close', and 'end' has not yet been emitted, then + * the event will be deferred until after 'end' is emitted. + * + * If the event is 'error', and an AbortSignal was provided for the stream, + * and there are no listeners, then the event is ignored, matching the + * behavior of node core streams in the presense of an AbortSignal. + * + * If the event is 'finish' or 'prefinish', then all listeners will be + * removed after emitting the event, to prevent double-firing. + */ + emit(ev, ...args) { + const data = args[0]; + // error and close are only events allowed after calling destroy() + if (ev !== 'error' && + ev !== 'close' && + ev !== DESTROYED && + this[DESTROYED]) { + return false; + } + else if (ev === 'data') { + return !this[OBJECTMODE] && !data + ? false + : this[ASYNC] + ? (defer(() => this[EMITDATA](data)), true) + : this[EMITDATA](data); + } + else if (ev === 'end') { + return this[EMITEND](); + } + else if (ev === 'close') { + this[CLOSED] = true; + // don't emit close before 'end' and 'finish' + if (!this[EMITTED_END] && !this[DESTROYED]) + return false; + const ret = super.emit('close'); + this.removeAllListeners('close'); + return ret; + } + else if (ev === 'error') { + this[EMITTED_ERROR] = data; + super.emit(ERROR, data); + const ret = !this[SIGNAL] || this.listeners('error').length + ? super.emit('error', data) + : false; + this[MAYBE_EMIT_END](); + return ret; + } + else if (ev === 'resume') { + const ret = super.emit('resume'); + this[MAYBE_EMIT_END](); + return ret; + } + else if (ev === 'finish' || ev === 'prefinish') { + const ret = super.emit(ev); + this.removeAllListeners(ev); + return ret; + } + // Some other unknown event + const ret = super.emit(ev, ...args); + this[MAYBE_EMIT_END](); + return ret; + } + [EMITDATA](data) { + for (const p of this[PIPES]) { + if (p.dest.write(data) === false) + this.pause(); + } + const ret = this[DISCARDED] ? false : super.emit('data', data); + this[MAYBE_EMIT_END](); + return ret; + } + [EMITEND]() { + if (this[EMITTED_END]) + return false; + this[EMITTED_END] = true; + this.readable = false; + return this[ASYNC] + ? (defer(() => this[EMITEND2]()), true) + : this[EMITEND2](); + } + [EMITEND2]() { + if (this[DECODER]) { + const data = this[DECODER].end(); + if (data) { + for (const p of this[PIPES]) { + p.dest.write(data); + } + if (!this[DISCARDED]) + super.emit('data', data); + } + } + for (const p of this[PIPES]) { + p.end(); + } + const ret = super.emit('end'); + this.removeAllListeners('end'); + return ret; + } + /** + * Return a Promise that resolves to an array of all emitted data once + * the stream ends. + */ + async collect() { + const buf = Object.assign([], { + dataLength: 0, + }); + if (!this[OBJECTMODE]) + buf.dataLength = 0; + // set the promise first, in case an error is raised + // by triggering the flow here. + const p = this.promise(); + this.on('data', c => { + buf.push(c); + if (!this[OBJECTMODE]) + buf.dataLength += c.length; + }); + await p; + return buf; + } + /** + * Return a Promise that resolves to the concatenation of all emitted data + * once the stream ends. + * + * Not allowed on objectMode streams. + */ + async concat() { + if (this[OBJECTMODE]) { + throw new Error('cannot concat in objectMode'); + } + const buf = await this.collect(); + return (this[ENCODING] + ? buf.join('') + : Buffer.concat(buf, buf.dataLength)); + } + /** + * Return a void Promise that resolves once the stream ends. + */ + async promise() { + return new Promise((resolve, reject) => { + this.on(DESTROYED, () => reject(new Error('stream destroyed'))); + this.on('error', er => reject(er)); + this.on('end', () => resolve()); + }); + } + /** + * Asynchronous `for await of` iteration. + * + * This will continue emitting all chunks until the stream terminates. + */ + [Symbol.asyncIterator]() { + // set this up front, in case the consumer doesn't call next() + // right away. + this[DISCARDED] = false; + let stopped = false; + const stop = async () => { + this.pause(); + stopped = true; + return { value: undefined, done: true }; + }; + const next = () => { + if (stopped) + return stop(); + const res = this.read(); + if (res !== null) + return Promise.resolve({ done: false, value: res }); + if (this[EOF]) + return stop(); + let resolve; + let reject; + const onerr = (er) => { + this.off('data', ondata); + this.off('end', onend); + this.off(DESTROYED, ondestroy); + stop(); + reject(er); + }; + const ondata = (value) => { + this.off('error', onerr); + this.off('end', onend); + this.off(DESTROYED, ondestroy); + this.pause(); + resolve({ value, done: !!this[EOF] }); + }; + const onend = () => { + this.off('error', onerr); + this.off('data', ondata); + this.off(DESTROYED, ondestroy); + stop(); + resolve({ done: true, value: undefined }); + }; + const ondestroy = () => onerr(new Error('stream destroyed')); + return new Promise((res, rej) => { + reject = rej; + resolve = res; + this.once(DESTROYED, ondestroy); + this.once('error', onerr); + this.once('end', onend); + this.once('data', ondata); + }); + }; + return { + next, + throw: stop, + return: stop, + [Symbol.asyncIterator]() { + return this; + }, + }; + } + /** + * Synchronous `for of` iteration. + * + * The iteration will terminate when the internal buffer runs out, even + * if the stream has not yet terminated. + */ + [Symbol.iterator]() { + // set this up front, in case the consumer doesn't call next() + // right away. + this[DISCARDED] = false; + let stopped = false; + const stop = () => { + this.pause(); + this.off(ERROR, stop); + this.off(DESTROYED, stop); + this.off('end', stop); + stopped = true; + return { done: true, value: undefined }; + }; + const next = () => { + if (stopped) + return stop(); + const value = this.read(); + return value === null ? stop() : { done: false, value }; + }; + this.once('end', stop); + this.once(ERROR, stop); + this.once(DESTROYED, stop); + return { + next, + throw: stop, + return: stop, + [Symbol.iterator]() { + return this; + }, + }; + } + /** + * Destroy a stream, preventing it from being used for any further purpose. + * + * If the stream has a `close()` method, then it will be called on + * destruction. + * + * After destruction, any attempt to write data, read data, or emit most + * events will be ignored. + * + * If an error argument is provided, then it will be emitted in an + * 'error' event. + */ + destroy(er) { + if (this[DESTROYED]) { + if (er) + this.emit('error', er); + else + this.emit(DESTROYED); + return this; + } + this[DESTROYED] = true; + this[DISCARDED] = true; + // throw away all buffered data, it's never coming out + this[BUFFER].length = 0; + this[BUFFERLENGTH] = 0; + const wc = this; + if (typeof wc.close === 'function' && !this[CLOSED]) + wc.close(); + if (er) + this.emit('error', er); + // if no error to emit, still reject pending promises + else + this.emit(DESTROYED); + return this; + } + /** + * Alias for {@link isStream} + * + * Former export location, maintained for backwards compatibility. + * + * @deprecated + */ + static get isStream() { + return isStream; + } +} +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/node_modules/npm-registry-fetch/node_modules/minipass/dist/mjs/package.json b/node_modules/npm-registry-fetch/node_modules/minipass/dist/mjs/package.json new file mode 100644 index 0000000000000..3dbc1ca591c05 --- /dev/null +++ b/node_modules/npm-registry-fetch/node_modules/minipass/dist/mjs/package.json @@ -0,0 +1,3 @@ +{ + "type": "module" +} diff --git a/node_modules/npm-registry-fetch/node_modules/minipass/package.json b/node_modules/npm-registry-fetch/node_modules/minipass/package.json new file mode 100644 index 0000000000000..355501c0a10c1 --- /dev/null +++ b/node_modules/npm-registry-fetch/node_modules/minipass/package.json @@ -0,0 +1,82 @@ +{ + "name": "minipass", + "version": "7.0.2", + "description": "minimal implementation of a PassThrough stream", + "main": "./dist/cjs/index.js", + "module": "./dist/mjs/index.js", + "types": "./dist/cjs/index.js", + "exports": { + ".": { + "import": { + "types": "./dist/mjs/index.d.ts", + "default": "./dist/mjs/index.js" + }, + "require": { + "types": "./dist/cjs/index.d.ts", + "default": "./dist/cjs/index.js" + } + }, + "./package.json": "./package.json" + }, + "files": [ + "dist" + ], + "scripts": { + "preversion": "npm test", + "postversion": "npm publish", + "prepublishOnly": "git push origin --follow-tags", + "preprepare": "rm -rf dist", + "prepare": "tsc -p tsconfig.json && tsc -p tsconfig-esm.json && bash ./scripts/fixup.sh", + "pretest": "npm run prepare", + "presnap": "npm run prepare", + "test": "c8 tap", + "snap": "c8 tap", + "format": "prettier --write . --loglevel warn", + "typedoc": "typedoc --tsconfig tsconfig-esm.json ./src/*.ts" + }, + "tap": { + "coverage": false, + "node-arg": [ + "--enable-source-maps", + "--no-warnings", + "--loader", + "ts-node/esm" + ], + "ts": false + }, + "prettier": { + "semi": false, + "printWidth": 75, + "tabWidth": 2, + "useTabs": false, + "singleQuote": true, + "jsxSingleQuote": false, + "bracketSameLine": true, + "arrowParens": "avoid", + "endOfLine": "lf" + }, + "devDependencies": { + "@types/node": "^20.1.2", + "@types/tap": "^15.0.8", + "c8": "^7.13.0", + "prettier": "^2.6.2", + "tap": "^16.3.0", + "ts-node": "^10.9.1", + "typedoc": "^0.24.8", + "typescript": "^5.1.3", + "end-of-stream": "^1.4.0", + "node-abort-controller": "^3.1.1", + "sync-content": "^1.0.2", + "through2": "^2.0.3" + }, + "repository": "https://github.com/isaacs/minipass", + "keywords": [ + "passthrough", + "stream" + ], + "author": "Isaac Z. Schlueter (http://blog.izs.me/)", + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } +} diff --git a/node_modules/npm-registry-fetch/package.json b/node_modules/npm-registry-fetch/package.json index 63a44725886cc..8832c8a2e95d3 100644 --- a/node_modules/npm-registry-fetch/package.json +++ b/node_modules/npm-registry-fetch/package.json @@ -1,6 +1,6 @@ { "name": "npm-registry-fetch", - "version": "14.0.5", + "version": "15.0.0", "description": "Fetch-based http client for use with npm registry APIs", "main": "lib", "files": [ @@ -31,8 +31,8 @@ "author": "GitHub Inc.", "license": "ISC", "dependencies": { - "make-fetch-happen": "^11.0.0", - "minipass": "^5.0.0", + "make-fetch-happen": "^12.0.0", + "minipass": "^7.0.2", "minipass-fetch": "^3.0.0", "minipass-json-stream": "^1.0.1", "minizlib": "^2.1.2", @@ -41,7 +41,7 @@ }, "devDependencies": { "@npmcli/eslint-config": "^4.0.0", - "@npmcli/template-oss": "4.14.1", + "@npmcli/template-oss": "4.18.0", "cacache": "^17.0.0", "nock": "^13.2.4", "require-inject": "^1.4.4", @@ -57,11 +57,17 @@ ] }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^16.13.0 || >=18.0.0" }, "templateOSS": { "//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.", - "version": "4.14.1", + "ciVersions": [ + "16.13.0", + "16.x", + "18.0.0", + "18.x" + ], + "version": "4.18.0", "publish": "true" } } diff --git a/node_modules/pacote/node_modules/npm-registry-fetch/LICENSE.md b/node_modules/pacote/node_modules/npm-registry-fetch/LICENSE.md new file mode 100644 index 0000000000000..5fc208ff122e0 --- /dev/null +++ b/node_modules/pacote/node_modules/npm-registry-fetch/LICENSE.md @@ -0,0 +1,20 @@ + + +ISC License + +Copyright npm, Inc. + +Permission to use, copy, modify, and/or distribute this +software for any purpose with or without fee is hereby +granted, provided that the above copyright notice and this +permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND NPM DISCLAIMS ALL +WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO +EVENT SHALL NPM BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE +USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/node_modules/pacote/node_modules/npm-registry-fetch/lib/auth.js b/node_modules/pacote/node_modules/npm-registry-fetch/lib/auth.js new file mode 100644 index 0000000000000..870ce0d923cd0 --- /dev/null +++ b/node_modules/pacote/node_modules/npm-registry-fetch/lib/auth.js @@ -0,0 +1,145 @@ +'use strict' +const fs = require('fs') +const npa = require('npm-package-arg') +const { URL } = require('url') + +// Find the longest registry key that is used for some kind of auth +// in the options. +const regKeyFromURI = (uri, opts) => { + const parsed = new URL(uri) + // try to find a config key indicating we have auth for this registry + // can be one of :_authToken, :_auth, :_password and :username, or + // :certfile and :keyfile + // We walk up the "path" until we're left with just //[:], + // stopping when we reach '//'. + let regKey = `//${parsed.host}${parsed.pathname}` + while (regKey.length > '//'.length) { + // got some auth for this URI + if (hasAuth(regKey, opts)) { + return regKey + } + + // can be either //host/some/path/:_auth or //host/some/path:_auth + // walk up by removing EITHER what's after the slash OR the slash itself + regKey = regKey.replace(/([^/]+|\/)$/, '') + } +} + +const hasAuth = (regKey, opts) => ( + opts[`${regKey}:_authToken`] || + opts[`${regKey}:_auth`] || + opts[`${regKey}:username`] && opts[`${regKey}:_password`] || + opts[`${regKey}:certfile`] && opts[`${regKey}:keyfile`] +) + +const sameHost = (a, b) => { + const parsedA = new URL(a) + const parsedB = new URL(b) + return parsedA.host === parsedB.host +} + +const getRegistry = opts => { + const { spec } = opts + const { scope: specScope, subSpec } = spec ? npa(spec) : {} + const subSpecScope = subSpec && subSpec.scope + const scope = subSpec ? subSpecScope : specScope + const scopeReg = scope && opts[`${scope}:registry`] + return scopeReg || opts.registry +} + +const maybeReadFile = file => { + try { + return fs.readFileSync(file, 'utf8') + } catch (er) { + if (er.code !== 'ENOENT') { + throw er + } + return null + } +} + +const getAuth = (uri, opts = {}) => { + const { forceAuth } = opts + if (!uri) { + throw new Error('URI is required') + } + const regKey = regKeyFromURI(uri, forceAuth || opts) + + // we are only allowed to use what's in forceAuth if specified + if (forceAuth && !regKey) { + return new Auth({ + scopeAuthKey: null, + token: forceAuth._authToken || forceAuth.token, + username: forceAuth.username, + password: forceAuth._password || forceAuth.password, + auth: forceAuth._auth || forceAuth.auth, + certfile: forceAuth.certfile, + keyfile: forceAuth.keyfile, + }) + } + + // no auth for this URI, but might have it for the registry + if (!regKey) { + const registry = getRegistry(opts) + if (registry && uri !== registry && sameHost(uri, registry)) { + return getAuth(registry, opts) + } else if (registry !== opts.registry) { + // If making a tarball request to a different base URI than the + // registry where we logged in, but the same auth SHOULD be sent + // to that artifact host, then we track where it was coming in from, + // and warn the user if we get a 4xx error on it. + const scopeAuthKey = regKeyFromURI(registry, opts) + return new Auth({ scopeAuthKey }) + } + } + + const { + [`${regKey}:_authToken`]: token, + [`${regKey}:username`]: username, + [`${regKey}:_password`]: password, + [`${regKey}:_auth`]: auth, + [`${regKey}:certfile`]: certfile, + [`${regKey}:keyfile`]: keyfile, + } = opts + + return new Auth({ + scopeAuthKey: null, + token, + auth, + username, + password, + certfile, + keyfile, + }) +} + +class Auth { + constructor ({ token, auth, username, password, scopeAuthKey, certfile, keyfile }) { + this.scopeAuthKey = scopeAuthKey + this.token = null + this.auth = null + this.isBasicAuth = false + this.cert = null + this.key = null + if (token) { + this.token = token + } else if (auth) { + this.auth = auth + } else if (username && password) { + const p = Buffer.from(password, 'base64').toString('utf8') + this.auth = Buffer.from(`${username}:${p}`, 'utf8').toString('base64') + this.isBasicAuth = true + } + // mTLS may be used in conjunction with another auth method above + if (certfile && keyfile) { + const cert = maybeReadFile(certfile, 'utf-8') + const key = maybeReadFile(keyfile, 'utf-8') + if (cert && key) { + this.cert = cert + this.key = key + } + } + } +} + +module.exports = getAuth diff --git a/node_modules/pacote/node_modules/npm-registry-fetch/lib/check-response.js b/node_modules/pacote/node_modules/npm-registry-fetch/lib/check-response.js new file mode 100644 index 0000000000000..066ac3c32420f --- /dev/null +++ b/node_modules/pacote/node_modules/npm-registry-fetch/lib/check-response.js @@ -0,0 +1,100 @@ +'use strict' + +const errors = require('./errors.js') +const { Response } = require('minipass-fetch') +const defaultOpts = require('./default-opts.js') +const log = require('proc-log') +const cleanUrl = require('./clean-url.js') + +/* eslint-disable-next-line max-len */ +const moreInfoUrl = 'https://github.com/npm/cli/wiki/No-auth-for-URI,-but-auth-present-for-scoped-registry' +const checkResponse = + async ({ method, uri, res, startTime, auth, opts }) => { + opts = { ...defaultOpts, ...opts } + if (res.headers.has('npm-notice') && !res.headers.has('x-local-cache')) { + log.notice('', res.headers.get('npm-notice')) + } + + if (res.status >= 400) { + logRequest(method, res, startTime) + if (auth && auth.scopeAuthKey && !auth.token && !auth.auth) { + // we didn't have auth for THIS request, but we do have auth for + // requests to the registry indicated by the spec's scope value. + // Warn the user. + log.warn('registry', `No auth for URI, but auth present for scoped registry. + +URI: ${uri} +Scoped Registry Key: ${auth.scopeAuthKey} + +More info here: ${moreInfoUrl}`) + } + return checkErrors(method, res, startTime, opts) + } else { + res.body.on('end', () => logRequest(method, res, startTime, opts)) + if (opts.ignoreBody) { + res.body.resume() + return new Response(null, res) + } + return res + } + } +module.exports = checkResponse + +function logRequest (method, res, startTime) { + const elapsedTime = Date.now() - startTime + const attempt = res.headers.get('x-fetch-attempts') + const attemptStr = attempt && attempt > 1 ? ` attempt #${attempt}` : '' + const cacheStatus = res.headers.get('x-local-cache-status') + const cacheStr = cacheStatus ? ` (cache ${cacheStatus})` : '' + const urlStr = cleanUrl(res.url) + + log.http( + 'fetch', + `${method.toUpperCase()} ${res.status} ${urlStr} ${elapsedTime}ms${attemptStr}${cacheStr}` + ) +} + +function checkErrors (method, res, startTime, opts) { + return res.buffer() + .catch(() => null) + .then(body => { + let parsed = body + try { + parsed = JSON.parse(body.toString('utf8')) + } catch { + // ignore errors + } + if (res.status === 401 && res.headers.get('www-authenticate')) { + const auth = res.headers.get('www-authenticate') + .split(/,\s*/) + .map(s => s.toLowerCase()) + if (auth.indexOf('ipaddress') !== -1) { + throw new errors.HttpErrorAuthIPAddress( + method, res, parsed, opts.spec + ) + } else if (auth.indexOf('otp') !== -1) { + throw new errors.HttpErrorAuthOTP( + method, res, parsed, opts.spec + ) + } else { + throw new errors.HttpErrorAuthUnknown( + method, res, parsed, opts.spec + ) + } + } else if ( + res.status === 401 && + body != null && + /one-time pass/.test(body.toString('utf8')) + ) { + // Heuristic for malformed OTP responses that don't include the + // www-authenticate header. + throw new errors.HttpErrorAuthOTP( + method, res, parsed, opts.spec + ) + } else { + throw new errors.HttpErrorGeneral( + method, res, parsed, opts.spec + ) + } + }) +} diff --git a/node_modules/pacote/node_modules/npm-registry-fetch/lib/clean-url.js b/node_modules/pacote/node_modules/npm-registry-fetch/lib/clean-url.js new file mode 100644 index 0000000000000..0c2656b5653a0 --- /dev/null +++ b/node_modules/pacote/node_modules/npm-registry-fetch/lib/clean-url.js @@ -0,0 +1,27 @@ +const { URL } = require('url') + +const replace = '***' +const tokenRegex = /\bnpm_[a-zA-Z0-9]{36}\b/g +const guidRegex = /\b[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\b/g + +const cleanUrl = (str) => { + if (typeof str !== 'string' || !str) { + return str + } + + try { + const url = new URL(str) + if (url.password) { + url.password = replace + str = url.toString() + } + } catch { + // ignore errors + } + + return str + .replace(tokenRegex, `npm_${replace}`) + .replace(guidRegex, `npm_${replace}`) +} + +module.exports = cleanUrl diff --git a/node_modules/pacote/node_modules/npm-registry-fetch/lib/default-opts.js b/node_modules/pacote/node_modules/npm-registry-fetch/lib/default-opts.js new file mode 100644 index 0000000000000..f0847f0b507e2 --- /dev/null +++ b/node_modules/pacote/node_modules/npm-registry-fetch/lib/default-opts.js @@ -0,0 +1,19 @@ +const pkg = require('../package.json') +module.exports = { + maxSockets: 12, + method: 'GET', + registry: 'https://registry.npmjs.org/', + timeout: 5 * 60 * 1000, // 5 minutes + strictSSL: true, + noProxy: process.env.NOPROXY, + userAgent: `${pkg.name + }@${ + pkg.version + }/node@${ + process.version + }+${ + process.arch + } (${ + process.platform + })`, +} diff --git a/node_modules/pacote/node_modules/npm-registry-fetch/lib/errors.js b/node_modules/pacote/node_modules/npm-registry-fetch/lib/errors.js new file mode 100644 index 0000000000000..cf5ddba6f300c --- /dev/null +++ b/node_modules/pacote/node_modules/npm-registry-fetch/lib/errors.js @@ -0,0 +1,80 @@ +'use strict' + +const url = require('url') + +function packageName (href) { + try { + let basePath = new url.URL(href).pathname.slice(1) + if (!basePath.match(/^-/)) { + basePath = basePath.split('/') + var index = basePath.indexOf('_rewrite') + if (index === -1) { + index = basePath.length - 1 + } else { + index++ + } + return decodeURIComponent(basePath[index]) + } + } catch (_) { + // this is ok + } +} + +class HttpErrorBase extends Error { + constructor (method, res, body, spec) { + super() + this.name = this.constructor.name + this.headers = res.headers.raw() + this.statusCode = res.status + this.code = `E${res.status}` + this.method = method + this.uri = res.url + this.body = body + this.pkgid = spec ? spec.toString() : packageName(res.url) + } +} +module.exports.HttpErrorBase = HttpErrorBase + +class HttpErrorGeneral extends HttpErrorBase { + constructor (method, res, body, spec) { + super(method, res, body, spec) + this.message = `${res.status} ${res.statusText} - ${ + this.method.toUpperCase() + } ${ + this.spec || this.uri + }${ + (body && body.error) ? ' - ' + body.error : '' + }` + Error.captureStackTrace(this, HttpErrorGeneral) + } +} +module.exports.HttpErrorGeneral = HttpErrorGeneral + +class HttpErrorAuthOTP extends HttpErrorBase { + constructor (method, res, body, spec) { + super(method, res, body, spec) + this.message = 'OTP required for authentication' + this.code = 'EOTP' + Error.captureStackTrace(this, HttpErrorAuthOTP) + } +} +module.exports.HttpErrorAuthOTP = HttpErrorAuthOTP + +class HttpErrorAuthIPAddress extends HttpErrorBase { + constructor (method, res, body, spec) { + super(method, res, body, spec) + this.message = 'Login is not allowed from your IP address' + this.code = 'EAUTHIP' + Error.captureStackTrace(this, HttpErrorAuthIPAddress) + } +} +module.exports.HttpErrorAuthIPAddress = HttpErrorAuthIPAddress + +class HttpErrorAuthUnknown extends HttpErrorBase { + constructor (method, res, body, spec) { + super(method, res, body, spec) + this.message = 'Unable to authenticate, need: ' + res.headers.get('www-authenticate') + Error.captureStackTrace(this, HttpErrorAuthUnknown) + } +} +module.exports.HttpErrorAuthUnknown = HttpErrorAuthUnknown diff --git a/node_modules/pacote/node_modules/npm-registry-fetch/lib/index.js b/node_modules/pacote/node_modules/npm-registry-fetch/lib/index.js new file mode 100644 index 0000000000000..23e349c5c5b96 --- /dev/null +++ b/node_modules/pacote/node_modules/npm-registry-fetch/lib/index.js @@ -0,0 +1,247 @@ +'use strict' + +const { HttpErrorAuthOTP } = require('./errors.js') +const checkResponse = require('./check-response.js') +const getAuth = require('./auth.js') +const fetch = require('make-fetch-happen') +const JSONStream = require('minipass-json-stream') +const npa = require('npm-package-arg') +const qs = require('querystring') +const url = require('url') +const zlib = require('minizlib') +const { Minipass } = require('minipass') + +const defaultOpts = require('./default-opts.js') + +// WhatWG URL throws if it's not fully resolved +const urlIsValid = u => { + try { + return !!new url.URL(u) + } catch (_) { + return false + } +} + +module.exports = regFetch +function regFetch (uri, /* istanbul ignore next */ opts_ = {}) { + const opts = { + ...defaultOpts, + ...opts_, + } + + // if we did not get a fully qualified URI, then we look at the registry + // config or relevant scope to resolve it. + const uriValid = urlIsValid(uri) + let registry = opts.registry || defaultOpts.registry + if (!uriValid) { + registry = opts.registry = ( + (opts.spec && pickRegistry(opts.spec, opts)) || + opts.registry || + registry + ) + uri = `${ + registry.trim().replace(/\/?$/g, '') + }/${ + uri.trim().replace(/^\//, '') + }` + // asserts that this is now valid + new url.URL(uri) + } + + const method = opts.method || 'GET' + + // through that takes into account the scope, the prefix of `uri`, etc + const startTime = Date.now() + const auth = getAuth(uri, opts) + const headers = getHeaders(uri, auth, opts) + let body = opts.body + const bodyIsStream = Minipass.isStream(body) + const bodyIsPromise = body && + typeof body === 'object' && + typeof body.then === 'function' + + if ( + body && !bodyIsStream && !bodyIsPromise && typeof body !== 'string' && !Buffer.isBuffer(body) + ) { + headers['content-type'] = headers['content-type'] || 'application/json' + body = JSON.stringify(body) + } else if (body && !headers['content-type']) { + headers['content-type'] = 'application/octet-stream' + } + + if (opts.gzip) { + headers['content-encoding'] = 'gzip' + if (bodyIsStream) { + const gz = new zlib.Gzip() + body.on('error', /* istanbul ignore next: unlikely and hard to test */ + err => gz.emit('error', err)) + body = body.pipe(gz) + } else if (!bodyIsPromise) { + body = new zlib.Gzip().end(body).concat() + } + } + + const parsed = new url.URL(uri) + + if (opts.query) { + const q = typeof opts.query === 'string' ? qs.parse(opts.query) + : opts.query + + Object.keys(q).forEach(key => { + if (q[key] !== undefined) { + parsed.searchParams.set(key, q[key]) + } + }) + uri = url.format(parsed) + } + + if (parsed.searchParams.get('write') === 'true' && method === 'GET') { + // do not cache, because this GET is fetching a rev that will be + // used for a subsequent PUT or DELETE, so we need to conditionally + // update cache. + opts.offline = false + opts.preferOffline = false + opts.preferOnline = true + } + + const doFetch = async fetchBody => { + const p = fetch(uri, { + agent: opts.agent, + algorithms: opts.algorithms, + body: fetchBody, + cache: getCacheMode(opts), + cachePath: opts.cache, + ca: opts.ca, + cert: auth.cert || opts.cert, + headers, + integrity: opts.integrity, + key: auth.key || opts.key, + localAddress: opts.localAddress, + maxSockets: opts.maxSockets, + memoize: opts.memoize, + method: method, + noProxy: opts.noProxy, + proxy: opts.httpsProxy || opts.proxy, + retry: opts.retry ? opts.retry : { + retries: opts.fetchRetries, + factor: opts.fetchRetryFactor, + minTimeout: opts.fetchRetryMintimeout, + maxTimeout: opts.fetchRetryMaxtimeout, + }, + strictSSL: opts.strictSSL, + timeout: opts.timeout || 30 * 1000, + }).then(res => checkResponse({ + method, + uri, + res, + registry, + startTime, + auth, + opts, + })) + + if (typeof opts.otpPrompt === 'function') { + return p.catch(async er => { + if (er instanceof HttpErrorAuthOTP) { + let otp + // if otp fails to complete, we fail with that failure + try { + otp = await opts.otpPrompt() + } catch (_) { + // ignore this error + } + // if no otp provided, or otpPrompt errored, throw the original HTTP error + if (!otp) { + throw er + } + return regFetch(uri, { ...opts, otp }) + } + throw er + }) + } else { + return p + } + } + + return Promise.resolve(body).then(doFetch) +} + +module.exports.json = fetchJSON +function fetchJSON (uri, opts) { + return regFetch(uri, opts).then(res => res.json()) +} + +module.exports.json.stream = fetchJSONStream +function fetchJSONStream (uri, jsonPath, + /* istanbul ignore next */ opts_ = {}) { + const opts = { ...defaultOpts, ...opts_ } + const parser = JSONStream.parse(jsonPath, opts.mapJSON) + regFetch(uri, opts).then(res => + res.body.on('error', + /* istanbul ignore next: unlikely and difficult to test */ + er => parser.emit('error', er)).pipe(parser) + ).catch(er => parser.emit('error', er)) + return parser +} + +module.exports.pickRegistry = pickRegistry +function pickRegistry (spec, opts = {}) { + spec = npa(spec) + let registry = spec.scope && + opts[spec.scope.replace(/^@?/, '@') + ':registry'] + + if (!registry && opts.scope) { + registry = opts[opts.scope.replace(/^@?/, '@') + ':registry'] + } + + if (!registry) { + registry = opts.registry || defaultOpts.registry + } + + return registry +} + +function getCacheMode (opts) { + return opts.offline ? 'only-if-cached' + : opts.preferOffline ? 'force-cache' + : opts.preferOnline ? 'no-cache' + : 'default' +} + +function getHeaders (uri, auth, opts) { + const headers = Object.assign({ + 'user-agent': opts.userAgent, + }, opts.headers || {}) + + if (opts.authType) { + headers['npm-auth-type'] = opts.authType + } + + if (opts.scope) { + headers['npm-scope'] = opts.scope + } + + if (opts.npmSession) { + headers['npm-session'] = opts.npmSession + } + + if (opts.npmCommand) { + headers['npm-command'] = opts.npmCommand + } + + // If a tarball is hosted on a different place than the manifest, only send + // credentials on `alwaysAuth` + if (auth.token) { + headers.authorization = `Bearer ${auth.token}` + } else if (auth.auth) { + headers.authorization = `Basic ${auth.auth}` + } + + if (opts.otp) { + headers['npm-otp'] = opts.otp + } + + return headers +} + +module.exports.cleanUrl = require('./clean-url.js') diff --git a/node_modules/pacote/node_modules/npm-registry-fetch/package.json b/node_modules/pacote/node_modules/npm-registry-fetch/package.json new file mode 100644 index 0000000000000..63a44725886cc --- /dev/null +++ b/node_modules/pacote/node_modules/npm-registry-fetch/package.json @@ -0,0 +1,67 @@ +{ + "name": "npm-registry-fetch", + "version": "14.0.5", + "description": "Fetch-based http client for use with npm registry APIs", + "main": "lib", + "files": [ + "bin/", + "lib/" + ], + "scripts": { + "eslint": "eslint", + "lint": "eslint \"**/*.js\"", + "lintfix": "npm run lint -- --fix", + "test": "tap", + "posttest": "npm run lint", + "npmclilint": "npmcli-lint", + "postsnap": "npm run lintfix --", + "postlint": "template-oss-check", + "snap": "tap", + "template-oss-apply": "template-oss-apply --force" + }, + "repository": { + "type": "git", + "url": "https://github.com/npm/npm-registry-fetch.git" + }, + "keywords": [ + "npm", + "registry", + "fetch" + ], + "author": "GitHub Inc.", + "license": "ISC", + "dependencies": { + "make-fetch-happen": "^11.0.0", + "minipass": "^5.0.0", + "minipass-fetch": "^3.0.0", + "minipass-json-stream": "^1.0.1", + "minizlib": "^2.1.2", + "npm-package-arg": "^10.0.0", + "proc-log": "^3.0.0" + }, + "devDependencies": { + "@npmcli/eslint-config": "^4.0.0", + "@npmcli/template-oss": "4.14.1", + "cacache": "^17.0.0", + "nock": "^13.2.4", + "require-inject": "^1.4.4", + "ssri": "^10.0.0", + "tap": "^16.0.1" + }, + "tap": { + "check-coverage": true, + "test-ignore": "test[\\\\/](util|cache)[\\\\/]", + "nyc-arg": [ + "--exclude", + "tap-snapshots/**" + ] + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + }, + "templateOSS": { + "//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.", + "version": "4.14.1", + "publish": "true" + } +} diff --git a/package-lock.json b/package-lock.json index 995e2f4cc58d2..c0c42086d1b01 100644 --- a/package-lock.json +++ b/package-lock.json @@ -134,7 +134,7 @@ "npm-package-arg": "^10.1.0", "npm-pick-manifest": "^8.0.1", "npm-profile": "^7.0.1", - "npm-registry-fetch": "^14.0.5", + "npm-registry-fetch": "^15.0.0", "npm-user-validate": "^2.0.0", "npmlog": "^7.0.1", "p-map": "^4.0.0", @@ -2340,6 +2340,19 @@ "node": ">= 8" } }, + "node_modules/@npmcli/agent": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@npmcli/agent/-/agent-1.1.0.tgz", + "integrity": "sha512-I9g/2XFOkflxm5IDrGSjCcR2d12Jmic0di9w/WpJBbzYuSXmfgoL+WwEV7zY/ajxzQr7o4vSkEJh6piyFLYtuQ==", + "inBundle": true, + "dependencies": { + "lru-cache": "^7.18.3", + "socks": "^2.7.1" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, "node_modules/@npmcli/arborist": { "resolved": "workspaces/arborist", "link": true @@ -9605,7 +9618,7 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/npm-registry-fetch": { + "node_modules/npm-profile/node_modules/npm-registry-fetch": { "version": "14.0.5", "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-14.0.5.tgz", "integrity": "sha512-kIDMIo4aBm6xg7jOttupWZamsZRkAqMqwqqbVXnUqstY5+tapvv6bkH/qMR76jdgV+YljEUCyWx3hRYMrJiAgA==", @@ -9623,6 +9636,55 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, + "node_modules/npm-registry-fetch": { + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-15.0.0.tgz", + "integrity": "sha512-CMFzk0HMDQ3fmFZ4v62C05g6eBwoU3PxpzFf4QiE360vfmtKZJkj+iCpgLx+I4oJT6Kx8g67Coyk729Q27M2JQ==", + "inBundle": true, + "dependencies": { + "make-fetch-happen": "^12.0.0", + "minipass": "^7.0.2", + "minipass-fetch": "^3.0.0", + "minipass-json-stream": "^1.0.1", + "minizlib": "^2.1.2", + "npm-package-arg": "^10.0.0", + "proc-log": "^3.0.0" + }, + "engines": { + "node": "^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm-registry-fetch/node_modules/make-fetch-happen": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-12.0.0.tgz", + "integrity": "sha512-xpuA2kA8Z66uGQjaSXd7rffqJOv60iYpP8X0TsZl3uwXlqxUVmHETImjM71JOPA694TlcX37GhlaCsl6z6fNVg==", + "inBundle": true, + "dependencies": { + "@npmcli/agent": "^1.1.0", + "cacache": "^17.0.0", + "http-cache-semantics": "^4.1.1", + "is-lambda": "^1.0.1", + "minipass": "^7.0.2", + "minipass-fetch": "^3.0.0", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.3", + "promise-retry": "^2.0.1", + "ssri": "^10.0.0" + }, + "engines": { + "node": "^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm-registry-fetch/node_modules/minipass": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.2.tgz", + "integrity": "sha512-eL79dXrE1q9dBbDCLg7xfn/vl7MS4F1gvJAgjJrQli/jbQWdUttuVawphqpffoIYfRdq78LHx6GP4bU/EQ2ATA==", + "inBundle": true, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, "node_modules/npm-run-path": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", @@ -10188,6 +10250,24 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, + "node_modules/pacote/node_modules/npm-registry-fetch": { + "version": "14.0.5", + "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-14.0.5.tgz", + "integrity": "sha512-kIDMIo4aBm6xg7jOttupWZamsZRkAqMqwqqbVXnUqstY5+tapvv6bkH/qMR76jdgV+YljEUCyWx3hRYMrJiAgA==", + "inBundle": true, + "dependencies": { + "make-fetch-happen": "^11.0.0", + "minipass": "^5.0.0", + "minipass-fetch": "^3.0.0", + "minipass-json-stream": "^1.0.1", + "minizlib": "^2.1.2", + "npm-package-arg": "^10.0.0", + "proc-log": "^3.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -15630,7 +15710,7 @@ "npm-install-checks": "^6.0.0", "npm-package-arg": "^10.1.0", "npm-pick-manifest": "^8.0.1", - "npm-registry-fetch": "^14.0.3", + "npm-registry-fetch": "^15.0.0", "npmlog": "^7.0.1", "pacote": "^15.0.8", "parse-conflict-json": "^3.0.0", @@ -15689,7 +15769,7 @@ "license": "ISC", "dependencies": { "npm-package-arg": "^10.1.0", - "npm-registry-fetch": "^14.0.3" + "npm-registry-fetch": "^15.0.0" }, "devDependencies": { "@npmcli/eslint-config": "^4.0.0", @@ -15775,7 +15855,7 @@ "license": "ISC", "dependencies": { "aproba": "^2.0.0", - "npm-registry-fetch": "^14.0.3" + "npm-registry-fetch": "^15.0.0" }, "devDependencies": { "@npmcli/eslint-config": "^4.0.0", @@ -15792,7 +15872,7 @@ "license": "ISC", "dependencies": { "aproba": "^2.0.0", - "npm-registry-fetch": "^14.0.3" + "npm-registry-fetch": "^15.0.0" }, "devDependencies": { "@npmcli/eslint-config": "^4.0.0", @@ -15832,7 +15912,7 @@ "ci-info": "^3.6.1", "normalize-package-data": "^5.0.0", "npm-package-arg": "^10.1.0", - "npm-registry-fetch": "^14.0.3", + "npm-registry-fetch": "^15.0.0", "proc-log": "^3.0.0", "semver": "^7.3.7", "sigstore": "^1.4.0", @@ -15854,7 +15934,7 @@ "version": "6.0.2", "license": "ISC", "dependencies": { - "npm-registry-fetch": "^14.0.3" + "npm-registry-fetch": "^15.0.0" }, "devDependencies": { "@npmcli/eslint-config": "^4.0.0", @@ -15871,7 +15951,7 @@ "license": "ISC", "dependencies": { "aproba": "^2.0.0", - "npm-registry-fetch": "^14.0.3" + "npm-registry-fetch": "^15.0.0" }, "devDependencies": { "@npmcli/eslint-config": "^4.0.0", diff --git a/package.json b/package.json index 94b4e33758560..d0e0cd2e3ea5e 100644 --- a/package.json +++ b/package.json @@ -99,7 +99,7 @@ "npm-package-arg": "^10.1.0", "npm-pick-manifest": "^8.0.1", "npm-profile": "^7.0.1", - "npm-registry-fetch": "^14.0.5", + "npm-registry-fetch": "^15.0.0", "npm-user-validate": "^2.0.0", "npmlog": "^7.0.1", "p-map": "^4.0.0", diff --git a/workspaces/arborist/package.json b/workspaces/arborist/package.json index 2a4229e3b5d0e..4da9cc9483675 100644 --- a/workspaces/arborist/package.json +++ b/workspaces/arborist/package.json @@ -24,7 +24,7 @@ "npm-install-checks": "^6.0.0", "npm-package-arg": "^10.1.0", "npm-pick-manifest": "^8.0.1", - "npm-registry-fetch": "^14.0.3", + "npm-registry-fetch": "^15.0.0", "npmlog": "^7.0.1", "pacote": "^15.0.8", "parse-conflict-json": "^3.0.0", diff --git a/workspaces/libnpmaccess/package.json b/workspaces/libnpmaccess/package.json index 713cf8c264c98..9a73b13998b46 100644 --- a/workspaces/libnpmaccess/package.json +++ b/workspaces/libnpmaccess/package.json @@ -30,7 +30,7 @@ "homepage": "https://npmjs.com/package/libnpmaccess", "dependencies": { "npm-package-arg": "^10.1.0", - "npm-registry-fetch": "^14.0.3" + "npm-registry-fetch": "^15.0.0" }, "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" diff --git a/workspaces/libnpmhook/package.json b/workspaces/libnpmhook/package.json index 05b34dda75c41..92cc750084957 100644 --- a/workspaces/libnpmhook/package.json +++ b/workspaces/libnpmhook/package.json @@ -31,7 +31,7 @@ "license": "ISC", "dependencies": { "aproba": "^2.0.0", - "npm-registry-fetch": "^14.0.3" + "npm-registry-fetch": "^15.0.0" }, "devDependencies": { "@npmcli/eslint-config": "^4.0.0", diff --git a/workspaces/libnpmorg/package.json b/workspaces/libnpmorg/package.json index 675d03b5b2437..91c2593b38c99 100644 --- a/workspaces/libnpmorg/package.json +++ b/workspaces/libnpmorg/package.json @@ -42,7 +42,7 @@ "homepage": "https://npmjs.com/package/libnpmorg", "dependencies": { "aproba": "^2.0.0", - "npm-registry-fetch": "^14.0.3" + "npm-registry-fetch": "^15.0.0" }, "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" diff --git a/workspaces/libnpmpublish/package.json b/workspaces/libnpmpublish/package.json index 8c95f9e9aba7d..c1cec1f0088af 100644 --- a/workspaces/libnpmpublish/package.json +++ b/workspaces/libnpmpublish/package.json @@ -41,7 +41,7 @@ "ci-info": "^3.6.1", "normalize-package-data": "^5.0.0", "npm-package-arg": "^10.1.0", - "npm-registry-fetch": "^14.0.3", + "npm-registry-fetch": "^15.0.0", "proc-log": "^3.0.0", "semver": "^7.3.7", "sigstore": "^1.4.0", diff --git a/workspaces/libnpmsearch/package.json b/workspaces/libnpmsearch/package.json index 32cb1f21b6422..f825e56f51647 100644 --- a/workspaces/libnpmsearch/package.json +++ b/workspaces/libnpmsearch/package.json @@ -38,7 +38,7 @@ "bugs": "https://github.com/npm/libnpmsearch/issues", "homepage": "https://npmjs.com/package/libnpmsearch", "dependencies": { - "npm-registry-fetch": "^14.0.3" + "npm-registry-fetch": "^15.0.0" }, "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" diff --git a/workspaces/libnpmteam/package.json b/workspaces/libnpmteam/package.json index 33a77095fe848..00dde521f449b 100644 --- a/workspaces/libnpmteam/package.json +++ b/workspaces/libnpmteam/package.json @@ -32,7 +32,7 @@ "homepage": "https://npmjs.com/package/libnpmteam", "dependencies": { "aproba": "^2.0.0", - "npm-registry-fetch": "^14.0.3" + "npm-registry-fetch": "^15.0.0" }, "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0"