From 4cbc2d402174933052c7addd6ea55b1ecee202c5 Mon Sep 17 00:00:00 2001 From: Luke Karrys Date: Thu, 2 May 2024 09:13:33 -0700 Subject: [PATCH] deps: npm-profile@10.0.0 --- node_modules/npm-profile/lib/index.js | 328 +++++++++--------- node_modules/npm-profile/package.json | 6 +- node_modules/npm-registry-fetch/lib/errors.js | 26 +- node_modules/npm-registry-fetch/package.json | 2 +- package-lock.json | 18 +- package.json | 2 +- 6 files changed, 183 insertions(+), 199 deletions(-) diff --git a/node_modules/npm-profile/lib/index.js b/node_modules/npm-profile/lib/index.js index e5b5dd046baf2..69e4e49b1fcdd 100644 --- a/node_modules/npm-profile/lib/index.js +++ b/node_modules/npm-profile/lib/index.js @@ -1,37 +1,34 @@ -'use strict' - +const { URL } = require('node:url') +const timers = require('node:timers/promises') const fetch = require('npm-registry-fetch') const { HttpErrorBase } = require('npm-registry-fetch/lib/errors') -const EventEmitter = require('events') -const os = require('os') -const { URL } = require('url') const { log } = require('proc-log') // try loginWeb, catch the "not supported" message and fall back to couch -const login = (opener, prompter, opts = {}) => { - const { creds } = opts - return loginWeb(opener, opts).catch(er => { +const login = async (opener, prompter, opts = {}) => { + try { + return await loginWeb(opener, opts) + } catch (er) { if (er instanceof WebLoginNotSupported) { - log.verbose('web login not supported, trying couch') - return prompter(creds) - .then(data => loginCouch(data.username, data.password, opts)) - } else { - throw er + log.verbose('web login', 'not supported, trying couch') + const { username, password } = await prompter(opts.creds) + return loginCouch(username, password, opts) } - }) + throw er + } } -const adduser = (opener, prompter, opts = {}) => { - const { creds } = opts - return adduserWeb(opener, opts).catch(er => { +const adduser = async (opener, prompter, opts = {}) => { + try { + return await adduserWeb(opener, opts) + } catch (er) { if (er instanceof WebLoginNotSupported) { - log.verbose('web adduser not supported, trying couch') - return prompter(creds) - .then(data => adduserCouch(data.username, data.email, data.password, opts)) - } else { - throw er + log.verbose('web adduser', 'not supported, trying couch') + const { username, email, password } = await prompter(opts.creds) + return adduserCouch(username, email, password, opts) } - }) + throw er + } } const adduserWeb = (opener, opts = {}) => { @@ -47,88 +44,104 @@ const loginWeb = (opener, opts = {}) => { const isValidUrl = u => { try { return /^https?:$/.test(new URL(u).protocol) - } catch (er) { + } catch { return false } } -const webAuth = (opener, opts, body) => { - const { hostname } = opts - body.hostname = hostname || os.hostname() - const target = '/-/v1/login' - const doneEmitter = new EventEmitter() - return fetch(target, { - ...opts, - method: 'POST', - body, - }).then(res => { - return Promise.all([res, res.json()]) - }).then(([res, content]) => { - const { doneUrl, loginUrl } = content +const webAuth = async (opener, opts, body) => { + try { + const res = await fetch('/-/v1/login', { + ...opts, + method: 'POST', + body, + }) + + const content = await res.json() log.verbose('web auth', 'got response', content) + + const { doneUrl, loginUrl } = content if (!isValidUrl(doneUrl) || !isValidUrl(loginUrl)) { throw new WebLoginInvalidResponse('POST', res, content) } - return content - }).then(({ doneUrl, loginUrl }) => { - log.verbose('web auth', 'opening url pair') - - const openPromise = opener(loginUrl, doneEmitter) - const webAuthCheckPromise = webAuthCheckLogin(doneUrl, { ...opts, cache: false }) - .then(authResult => { - log.verbose('web auth', 'done-check finished') - - // cancel open prompt if it's present - doneEmitter.emit('abort') - - return authResult - }) - - return Promise.all([openPromise, webAuthCheckPromise]).then( - // pick the auth result and pass it along - ([, authResult]) => authResult - ) - }).catch(er => { - // cancel open prompt if it's present - doneEmitter.emit('abort') + return await webAuthOpener(opener, loginUrl, doneUrl, opts) + } catch (er) { if ((er.statusCode >= 400 && er.statusCode <= 499) || er.statusCode === 500) { throw new WebLoginNotSupported('POST', { status: er.statusCode, - headers: { raw: () => er.headers }, + headers: er.headers, }, er.body) - } else { - throw er } - }) + throw er + } } -const webAuthCheckLogin = (doneUrl, opts) => { - return fetch(doneUrl, opts).then(res => { - return Promise.all([res, res.json()]) - }).then(([res, content]) => { - if (res.status === 200) { - if (!content.token) { - throw new WebLoginInvalidResponse('GET', res, content) - } else { - return content - } - } else if (res.status === 202) { - const retry = +res.headers.get('retry-after') * 1000 - if (retry > 0) { - return sleep(retry).then(() => webAuthCheckLogin(doneUrl, opts)) - } else { - return webAuthCheckLogin(doneUrl, opts) - } - } else { +const webAuthOpener = async (opener, loginUrl, doneUrl, opts) => { + const abortController = new AbortController() + const { signal } = abortController + try { + log.verbose('web auth', 'opening url pair') + const [, authResult] = await Promise.all([ + opener(loginUrl, { signal }).catch((err) => { + if (err.name === 'AbortError') { + abortController.abort() + return + } + throw err + }), + webAuthCheckLogin(doneUrl, { ...opts, cache: false }, { signal }).then((r) => { + log.verbose('web auth', 'done-check finished') + abortController.abort() + return r + }), + ]) + return authResult + } catch (er) { + abortController.abort() + throw er + } +} + +const webAuthCheckLogin = async (doneUrl, opts, { signal } = {}) => { + signal?.throwIfAborted() + + const res = await fetch(doneUrl, opts) + const content = await res.json() + + if (res.status === 200) { + if (!content.token) { throw new WebLoginInvalidResponse('GET', res, content) } + return content + } + + if (res.status === 202) { + const retry = +res.headers.get('retry-after') * 1000 + if (retry > 0) { + await timers.setTimeout(retry, null, { ref: false, signal }) + } + return webAuthCheckLogin(doneUrl, opts, { signal }) + } + + throw new WebLoginInvalidResponse('GET', res, content) +} + +const couchEndpoint = (username) => `/-/user/org.couchdb.user:${encodeURIComponent(username)}` + +const putCouch = async (path, username, body, opts) => { + const result = await fetch.json(`${couchEndpoint(username)}${path}`, { + ...opts, + method: 'PUT', + body, }) + result.username = username + return result } -const adduserCouch = (username, email, password, opts = {}) => { +const adduserCouch = async (username, email, password, opts = {}) => { const body = { - _id: 'org.couchdb.user:' + username, + _id: `org.couchdb.user:${username}`, name: username, password: password, email: email, @@ -136,134 +149,107 @@ const adduserCouch = (username, email, password, opts = {}) => { roles: [], date: new Date().toISOString(), } - const logObj = { + + log.verbose('adduser', 'before first PUT', { ...body, password: 'XXXXX', - } - log.verbose('adduser', 'before first PUT', logObj) - - const target = '/-/user/org.couchdb.user:' + encodeURIComponent(username) - return fetch.json(target, { - ...opts, - method: 'PUT', - body, - }).then(result => { - result.username = username - return result }) + + return putCouch('', username, body, opts) } -const loginCouch = (username, password, opts = {}) => { +const loginCouch = async (username, password, opts = {}) => { const body = { - _id: 'org.couchdb.user:' + username, + _id: `org.couchdb.user:${username}`, name: username, password: password, type: 'user', roles: [], date: new Date().toISOString(), } - const logObj = { + + log.verbose('login', 'before first PUT', { ...body, password: 'XXXXX', - } - log.verbose('login', 'before first PUT', logObj) + }) - const target = '/-/user/org.couchdb.user:' + encodeURIComponent(username) - return fetch.json(target, { - ...opts, - method: 'PUT', - body, - }).catch(err => { + try { + return await putCouch('', username, body, opts) + } catch (err) { if (err.code === 'E400') { err.message = `There is no user with the username "${username}".` throw err } + if (err.code !== 'E409') { throw err } - return fetch.json(target, { - ...opts, - query: { write: true }, - }).then(result => { - Object.keys(result).forEach(k => { - if (!body[k] || k === 'roles') { - body[k] = result[k] - } - }) - const { otp } = opts - return fetch.json(`${target}/-rev/${body._rev}`, { - ...opts, - method: 'PUT', - body, - forceAuth: { - username, - password: Buffer.from(password, 'utf8').toString('base64'), - otp, - }, - }) - }) - }).then(result => { - result.username = username - return result - }) -} + } -const get = (opts = {}) => fetch.json('/-/npm/v1/user', opts) + const result = await fetch.json(couchEndpoint(username), { + ...opts, + query: { write: true }, + }) -const set = (profile, opts = {}) => { - Object.keys(profile).forEach(key => { - // profile keys can't be empty strings, but they CAN be null - if (profile[key] === '') { - profile[key] = null + for (const k of Object.keys(result)) { + if (!body[k] || k === 'roles') { + body[k] = result[k] } - }) - return fetch.json('/-/npm/v1/user', { + } + + return putCouch(`/-rev/${body._rev}`, username, body, { ...opts, - method: 'POST', - body: profile, + forceAuth: { + username, + password: Buffer.from(password, 'utf8').toString('base64'), + otp: opts.otp, + }, }) } -const listTokens = (opts = {}) => { - const untilLastPage = (href, objects) => { - return fetch.json(href, opts).then(result => { - objects = objects ? objects.concat(result.objects) : result.objects - if (result.urls.next) { - return untilLastPage(result.urls.next, objects) - } else { - return objects - } - }) +const get = (opts = {}) => fetch.json('/-/npm/v1/user', opts) + +const set = (profile, opts = {}) => fetch.json('/-/npm/v1/user', { + ...opts, + method: 'POST', + // profile keys can't be empty strings, but they CAN be null + body: Object.fromEntries(Object.entries(profile).map(([k, v]) => [k, v === '' ? null : v])), +}) + +const paginate = async (href, opts, items = []) => { + const result = await fetch.json(href, opts) + items = items.concat(result.objects) + if (result.urls.next) { + return paginate(result.urls.next, opts, items) } - return untilLastPage('/-/npm/v1/tokens') + return items } -const removeToken = (tokenKey, opts = {}) => { - const target = `/-/npm/v1/tokens/token/${tokenKey}` - return fetch(target, { +const listTokens = (opts = {}) => paginate('/-/npm/v1/tokens', opts) + +const removeToken = async (tokenKey, opts = {}) => { + await fetch(`/-/npm/v1/tokens/token/${tokenKey}`, { ...opts, method: 'DELETE', ignoreBody: true, - }).then(() => null) -} - -const createToken = (password, readonly, cidrs, opts = {}) => { - return fetch.json('/-/npm/v1/tokens', { - ...opts, - method: 'POST', - body: { - password: password, - readonly: readonly, - cidr_whitelist: cidrs, - }, }) + return null } +const createToken = (password, readonly, cidrs, opts = {}) => fetch.json('/-/npm/v1/tokens', { + ...opts, + method: 'POST', + body: { + password: password, + readonly: readonly, + cidr_whitelist: cidrs, + }, +}) + class WebLoginInvalidResponse extends HttpErrorBase { constructor (method, res, body) { super(method, res, body) this.message = 'Invalid response from web login endpoint' - Error.captureStackTrace(this, WebLoginInvalidResponse) } } @@ -272,12 +258,9 @@ class WebLoginNotSupported extends HttpErrorBase { super(method, res, body) this.message = 'Web login not supported' this.code = 'ENYI' - Error.captureStackTrace(this, WebLoginNotSupported) } } -const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms)) - module.exports = { adduserCouch, loginCouch, @@ -291,4 +274,5 @@ module.exports = { removeToken, createToken, webAuthCheckLogin, + webAuthOpener, } diff --git a/node_modules/npm-profile/package.json b/node_modules/npm-profile/package.json index acdf4d6baf2ee..ff93911716fa7 100644 --- a/node_modules/npm-profile/package.json +++ b/node_modules/npm-profile/package.json @@ -1,12 +1,12 @@ { "name": "npm-profile", - "version": "9.0.2", + "version": "10.0.0", "description": "Library for updating an npmjs.com profile", "keywords": [], "author": "GitHub Inc.", "license": "ISC", "dependencies": { - "npm-registry-fetch": "^17.0.0", + "npm-registry-fetch": "^17.0.1", "proc-log": "^4.0.0" }, "main": "./lib/index.js", @@ -41,7 +41,7 @@ ] }, "engines": { - "node": "^16.14.0 || >=18.0.0" + "node": ">=18.0.0" }, "templateOSS": { "//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.", diff --git a/node_modules/npm-registry-fetch/lib/errors.js b/node_modules/npm-registry-fetch/lib/errors.js index cf5ddba6f300c..5bf6b012a24ef 100644 --- a/node_modules/npm-registry-fetch/lib/errors.js +++ b/node_modules/npm-registry-fetch/lib/errors.js @@ -1,10 +1,10 @@ 'use strict' -const url = require('url') +const { URL } = require('node:url') function packageName (href) { try { - let basePath = new url.URL(href).pathname.slice(1) + let basePath = new URL(href).pathname.slice(1) if (!basePath.match(/^-/)) { basePath = basePath.split('/') var index = basePath.indexOf('_rewrite') @@ -15,7 +15,7 @@ function packageName (href) { } return decodeURIComponent(basePath[index]) } - } catch (_) { + } catch { // this is ok } } @@ -24,16 +24,16 @@ class HttpErrorBase extends Error { constructor (method, res, body, spec) { super() this.name = this.constructor.name - this.headers = res.headers.raw() + this.headers = typeof res.headers?.raw === 'function' ? res.headers.raw() : res.headers 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) + Error.captureStackTrace(this, this.constructor) } } -module.exports.HttpErrorBase = HttpErrorBase class HttpErrorGeneral extends HttpErrorBase { constructor (method, res, body, spec) { @@ -45,36 +45,36 @@ class HttpErrorGeneral extends HttpErrorBase { }${ (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 + +module.exports = { + HttpErrorBase, + HttpErrorGeneral, + HttpErrorAuthOTP, + HttpErrorAuthIPAddress, + HttpErrorAuthUnknown, +} diff --git a/node_modules/npm-registry-fetch/package.json b/node_modules/npm-registry-fetch/package.json index 52820a6a206ec..567dedb40f70b 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": "17.0.0", + "version": "17.0.1", "description": "Fetch-based http client for use with npm registry APIs", "main": "lib", "files": [ diff --git a/package-lock.json b/package-lock.json index e0ee31a1f1356..9cf74dd93f5ce 100644 --- a/package-lock.json +++ b/package-lock.json @@ -134,7 +134,7 @@ "npm-install-checks": "^6.3.0", "npm-package-arg": "^11.0.2", "npm-pick-manifest": "^9.0.0", - "npm-profile": "^9.0.2", + "npm-profile": "^10.0.0", "npm-registry-fetch": "^17.0.0", "npm-user-validate": "^2.0.0", "p-map": "^4.0.0", @@ -9012,22 +9012,22 @@ } }, "node_modules/npm-profile": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/npm-profile/-/npm-profile-9.0.2.tgz", - "integrity": "sha512-4S/fd/PNyGgjaGolsdUJFsnfEb+AxJzrrZC3I9qbTYZJ3PJy8T46tIWXA4pBoaeiGh2M2GRvK1K/xMQe1Xgbvw==", + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/npm-profile/-/npm-profile-10.0.0.tgz", + "integrity": "sha512-DXnge3nHYnEnPxmVd/kPmgcXKXwVUqFihGnU+EJUiu5mIOs3awq6zEm0rRp3kSQNhFsoqdLu8L1TIfRyeBOCog==", "inBundle": true, "dependencies": { - "npm-registry-fetch": "^17.0.0", + "npm-registry-fetch": "^17.0.1", "proc-log": "^4.0.0" }, "engines": { - "node": "^16.14.0 || >=18.0.0" + "node": ">=18.0.0" } }, "node_modules/npm-registry-fetch": { - "version": "17.0.0", - "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-17.0.0.tgz", - "integrity": "sha512-JoOpdYqru846tJX96Jn2jyYVpc1TD1o6Oox80rjVIDAZqIsS2n+nNx+/Qd02LlQm/itGhsBgzP1VUKACLQHD+Q==", + "version": "17.0.1", + "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-17.0.1.tgz", + "integrity": "sha512-fLu9MTdZTlJAHUek/VLklE6EpIiP3VZpTiuN7OOMCt2Sd67NCpSEetMaxHHEZiZxllp8ZLsUpvbEszqTFEc+wA==", "inBundle": true, "dependencies": { "@npmcli/redact": "^2.0.0", diff --git a/package.json b/package.json index 1aae41fbe7576..ad42d83d584c3 100644 --- a/package.json +++ b/package.json @@ -99,7 +99,7 @@ "npm-install-checks": "^6.3.0", "npm-package-arg": "^11.0.2", "npm-pick-manifest": "^9.0.0", - "npm-profile": "^9.0.2", + "npm-profile": "^10.0.0", "npm-registry-fetch": "^17.0.0", "npm-user-validate": "^2.0.0", "p-map": "^4.0.0",