From 65d76dbd2fb11c83141302500ec4a3f5128ff12f Mon Sep 17 00:00:00 2001 From: Gar Date: Tue, 30 Apr 2024 12:34:56 -0700 Subject: [PATCH] deps: npm-profile@9.0.2 --- node_modules/.gitignore | 6 + node_modules/npm-profile/lib/index.js | 3 +- .../node_modules/@npmcli/redact/LICENSE | 21 ++ .../@npmcli/redact/lib/deep-map.js | 59 +++++ .../node_modules/@npmcli/redact/lib/index.js | 44 ++++ .../@npmcli/redact/lib/matchers.js | 81 ++++++ .../node_modules/@npmcli/redact/lib/server.js | 34 +++ .../node_modules/@npmcli/redact/lib/utils.js | 202 ++++++++++++++ .../node_modules/@npmcli/redact/package.json | 51 ++++ .../npm-registry-fetch/LICENSE.md | 20 ++ .../npm-registry-fetch/lib/auth.js | 181 +++++++++++++ .../npm-registry-fetch/lib/check-response.js | 100 +++++++ .../npm-registry-fetch/lib/default-opts.js | 19 ++ .../npm-registry-fetch/lib/errors.js | 80 ++++++ .../npm-registry-fetch/lib/index.js | 247 ++++++++++++++++++ .../npm-registry-fetch/package.json | 68 +++++ node_modules/npm-profile/package.json | 8 +- node_modules/sprintf-js/bower.json | 14 + node_modules/sprintf-js/demo/angular.html | 20 ++ node_modules/sprintf-js/gruntfile.js | 36 +++ node_modules/sprintf-js/test/test.js | 82 ++++++ package-lock.json | 38 ++- package.json | 2 +- 23 files changed, 1404 insertions(+), 12 deletions(-) create mode 100644 node_modules/npm-profile/node_modules/@npmcli/redact/LICENSE create mode 100644 node_modules/npm-profile/node_modules/@npmcli/redact/lib/deep-map.js create mode 100644 node_modules/npm-profile/node_modules/@npmcli/redact/lib/index.js create mode 100644 node_modules/npm-profile/node_modules/@npmcli/redact/lib/matchers.js create mode 100644 node_modules/npm-profile/node_modules/@npmcli/redact/lib/server.js create mode 100644 node_modules/npm-profile/node_modules/@npmcli/redact/lib/utils.js create mode 100644 node_modules/npm-profile/node_modules/@npmcli/redact/package.json create mode 100644 node_modules/npm-profile/node_modules/npm-registry-fetch/LICENSE.md create mode 100644 node_modules/npm-profile/node_modules/npm-registry-fetch/lib/auth.js create mode 100644 node_modules/npm-profile/node_modules/npm-registry-fetch/lib/check-response.js create mode 100644 node_modules/npm-profile/node_modules/npm-registry-fetch/lib/default-opts.js create mode 100644 node_modules/npm-profile/node_modules/npm-registry-fetch/lib/errors.js create mode 100644 node_modules/npm-profile/node_modules/npm-registry-fetch/lib/index.js create mode 100644 node_modules/npm-profile/node_modules/npm-registry-fetch/package.json create mode 100644 node_modules/sprintf-js/bower.json create mode 100644 node_modules/sprintf-js/demo/angular.html create mode 100644 node_modules/sprintf-js/gruntfile.js create mode 100644 node_modules/sprintf-js/test/test.js diff --git a/node_modules/.gitignore b/node_modules/.gitignore index 2075e41dc40b5..ae31d3aa122e3 100644 --- a/node_modules/.gitignore +++ b/node_modules/.gitignore @@ -157,6 +157,12 @@ !/npm-packlist !/npm-pick-manifest !/npm-profile +!/npm-profile/node_modules/ +/npm-profile/node_modules/* +!/npm-profile/node_modules/@npmcli/ +/npm-profile/node_modules/@npmcli/* +!/npm-profile/node_modules/@npmcli/redact +!/npm-profile/node_modules/npm-registry-fetch !/npm-registry-fetch !/npm-user-validate !/p-map diff --git a/node_modules/npm-profile/lib/index.js b/node_modules/npm-profile/lib/index.js index a56a3794ba891..e5b5dd046baf2 100644 --- a/node_modules/npm-profile/lib/index.js +++ b/node_modules/npm-profile/lib/index.js @@ -276,8 +276,7 @@ class WebLoginNotSupported extends HttpErrorBase { } } -const sleep = (ms) => - new Promise((resolve, reject) => setTimeout(resolve, ms)) +const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms)) module.exports = { adduserCouch, diff --git a/node_modules/npm-profile/node_modules/@npmcli/redact/LICENSE b/node_modules/npm-profile/node_modules/@npmcli/redact/LICENSE new file mode 100644 index 0000000000000..c21644115c85d --- /dev/null +++ b/node_modules/npm-profile/node_modules/@npmcli/redact/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 npm + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/node_modules/npm-profile/node_modules/@npmcli/redact/lib/deep-map.js b/node_modules/npm-profile/node_modules/@npmcli/redact/lib/deep-map.js new file mode 100644 index 0000000000000..ad042dbdfc534 --- /dev/null +++ b/node_modules/npm-profile/node_modules/@npmcli/redact/lib/deep-map.js @@ -0,0 +1,59 @@ +const deepMap = (input, handler = v => v, path = ['$'], seen = new Set([input])) => { + if (Array.isArray(input)) { + const result = [] + for (let i = 0; i < input.length; i++) { + const element = input[i] + const elementPath = [...path, i] + if (element instanceof Object) { + if (!seen.has(element)) { // avoid getting stuck in circular reference + seen.add(element) + result.push(deepMap(handler(element, elementPath), handler, elementPath, seen)) + } + } else { + result.push(handler(element, elementPath)) + } + } + return result + } + + if (input === null) { + return null + } else if (typeof input === 'object' || typeof input === 'function') { + const result = {} + + if (input instanceof Error) { + // `name` property is not included in `Object.getOwnPropertyNames(error)` + result.errorType = input.name + } + + for (const propertyName of Object.getOwnPropertyNames(input)) { + // skip logging internal properties + if (propertyName.startsWith('_')) { + continue + } + + try { + const property = input[propertyName] + const propertyPath = [...path, propertyName] + if (property instanceof Object) { + if (!seen.has(property)) { // avoid getting stuck in circular reference + seen.add(property) + result[propertyName] = deepMap( + handler(property, propertyPath), handler, propertyPath, seen + ) + } + } else { + result[propertyName] = handler(property, propertyPath) + } + } catch (err) { + // a getter may throw an error + result[propertyName] = `[error getting value: ${err.message}]` + } + } + return result + } + + return handler(input, path) +} + +module.exports = { deepMap } diff --git a/node_modules/npm-profile/node_modules/@npmcli/redact/lib/index.js b/node_modules/npm-profile/node_modules/@npmcli/redact/lib/index.js new file mode 100644 index 0000000000000..9b10c7f6a0081 --- /dev/null +++ b/node_modules/npm-profile/node_modules/@npmcli/redact/lib/index.js @@ -0,0 +1,44 @@ +const matchers = require('./matchers') +const { redactUrlPassword } = require('./utils') + +const REPLACE = '***' + +const redact = (value) => { + if (typeof value !== 'string' || !value) { + return value + } + return redactUrlPassword(value, REPLACE) + .replace(matchers.NPM_SECRET.pattern, `npm_${REPLACE}`) + .replace(matchers.UUID.pattern, REPLACE) +} + +// split on \s|= similar to how nopt parses options +const splitAndRedact = (str) => { + // stateful regex, don't move out of this scope + const splitChars = /[\s=]/g + + let match = null + let result = '' + let index = 0 + while (match = splitChars.exec(str)) { + result += redact(str.slice(index, match.index)) + match[0] + index = splitChars.lastIndex + } + + return result + redact(str.slice(index)) +} + +// replaces auth info in an array of arguments or in a strings +const redactLog = (arg) => { + if (typeof arg === 'string') { + return splitAndRedact(arg) + } else if (Array.isArray(arg)) { + return arg.map((a) => typeof a === 'string' ? splitAndRedact(a) : a) + } + return arg +} + +module.exports = { + redact, + redactLog, +} diff --git a/node_modules/npm-profile/node_modules/@npmcli/redact/lib/matchers.js b/node_modules/npm-profile/node_modules/@npmcli/redact/lib/matchers.js new file mode 100644 index 0000000000000..fe9b9071de8a1 --- /dev/null +++ b/node_modules/npm-profile/node_modules/@npmcli/redact/lib/matchers.js @@ -0,0 +1,81 @@ +const TYPE_REGEX = 'regex' +const TYPE_URL = 'url' +const TYPE_PATH = 'path' + +const NPM_SECRET = { + type: TYPE_REGEX, + pattern: /\b(npms?_)[a-zA-Z0-9]{36,48}\b/gi, + replacement: `[REDACTED_NPM_SECRET]`, +} + +const AUTH_HEADER = { + type: TYPE_REGEX, + pattern: /\b(Basic\s+|Bearer\s+)[\w+=\-.]+\b/gi, + replacement: `[REDACTED_AUTH_HEADER]`, +} + +const JSON_WEB_TOKEN = { + type: TYPE_REGEX, + pattern: /\b[A-Za-z0-9-_]{10,}(?!\.\d+\.)\.[A-Za-z0-9-_]{3,}\.[A-Za-z0-9-_]{20,}\b/gi, + replacement: `[REDACTED_JSON_WEB_TOKEN]`, +} + +const UUID = { + type: TYPE_REGEX, + pattern: /\b[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\b/gi, + replacement: `[REDACTED_UUID]`, +} + +const URL_MATCHER = { + type: TYPE_REGEX, + pattern: /(?:https?|ftp):\/\/[^\s/"$.?#].[^\s"]*/gi, + replacement: '[REDACTED_URL]', +} + +const DEEP_HEADER_AUTHORIZATION = { + type: TYPE_PATH, + predicate: ({ path }) => path.endsWith('.headers.authorization'), + replacement: '[REDACTED_HEADER_AUTHORIZATION]', +} + +const DEEP_HEADER_SET_COOKIE = { + type: TYPE_PATH, + predicate: ({ path }) => path.endsWith('.headers.set-cookie'), + replacement: '[REDACTED_HEADER_SET_COOKIE]', +} + +const REWRITE_REQUEST = { + type: TYPE_PATH, + predicate: ({ path }) => path.endsWith('.request'), + replacement: (input) => ({ + method: input?.method, + path: input?.path, + headers: input?.headers, + url: input?.url, + }), +} + +const REWRITE_RESPONSE = { + type: TYPE_PATH, + predicate: ({ path }) => path.endsWith('.response'), + replacement: (input) => ({ + data: input?.data, + status: input?.status, + headers: input?.headers, + }), +} + +module.exports = { + TYPE_REGEX, + TYPE_URL, + TYPE_PATH, + NPM_SECRET, + AUTH_HEADER, + JSON_WEB_TOKEN, + UUID, + URL_MATCHER, + DEEP_HEADER_AUTHORIZATION, + DEEP_HEADER_SET_COOKIE, + REWRITE_REQUEST, + REWRITE_RESPONSE, +} diff --git a/node_modules/npm-profile/node_modules/@npmcli/redact/lib/server.js b/node_modules/npm-profile/node_modules/@npmcli/redact/lib/server.js new file mode 100644 index 0000000000000..669e834da6131 --- /dev/null +++ b/node_modules/npm-profile/node_modules/@npmcli/redact/lib/server.js @@ -0,0 +1,34 @@ +const { + AUTH_HEADER, + JSON_WEB_TOKEN, + NPM_SECRET, + DEEP_HEADER_AUTHORIZATION, + DEEP_HEADER_SET_COOKIE, + REWRITE_REQUEST, + REWRITE_RESPONSE, +} = require('./matchers') + +const { + redactUrlMatcher, + redactUrlPasswordMatcher, + redactMatchers, +} = require('./utils') + +const { deepMap } = require('./deep-map') + +const _redact = redactMatchers( + NPM_SECRET, + AUTH_HEADER, + JSON_WEB_TOKEN, + DEEP_HEADER_AUTHORIZATION, + DEEP_HEADER_SET_COOKIE, + REWRITE_REQUEST, + REWRITE_RESPONSE, + redactUrlMatcher( + redactUrlPasswordMatcher() + ) +) + +const redact = (input) => deepMap(input, (value, path) => _redact(value, { path })) + +module.exports = { redact } diff --git a/node_modules/npm-profile/node_modules/@npmcli/redact/lib/utils.js b/node_modules/npm-profile/node_modules/@npmcli/redact/lib/utils.js new file mode 100644 index 0000000000000..8395ab25fc373 --- /dev/null +++ b/node_modules/npm-profile/node_modules/@npmcli/redact/lib/utils.js @@ -0,0 +1,202 @@ +const { + URL_MATCHER, + TYPE_URL, + TYPE_REGEX, + TYPE_PATH, +} = require('./matchers') + +/** + * creates a string of asterisks, + * this forces a minimum asterisk for security purposes + */ +const asterisk = (length = 0) => { + length = typeof length === 'string' ? length.length : length + if (length < 8) { + return '*'.repeat(8) + } + return '*'.repeat(length) +} + +/** + * escapes all special regex chars + * @see https://stackoverflow.com/a/9310752 + * @see https://github.com/tc39/proposal-regex-escaping + */ +const escapeRegExp = (text) => { + return text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, `\\$&`) +} + +/** + * provieds a regex "or" of the url versions of a string + */ +const urlEncodeRegexGroup = (value) => { + const decoded = decodeURIComponent(value) + const encoded = encodeURIComponent(value) + const union = [...new Set([encoded, decoded, value])].map(escapeRegExp).join('|') + return union +} + +/** + * a tagged template literal that returns a regex ensures all variables are excaped + */ +const urlEncodeRegexTag = (strings, ...values) => { + let pattern = '' + for (let i = 0; i < values.length; i++) { + pattern += strings[i] + `(${urlEncodeRegexGroup(values[i])})` + } + pattern += strings[strings.length - 1] + return new RegExp(pattern) +} + +/** + * creates a matcher for redacting url hostname + */ +const redactUrlHostnameMatcher = ({ hostname, replacement } = {}) => ({ + type: TYPE_URL, + predicate: ({ url }) => url.hostname === hostname, + pattern: ({ url }) => { + return urlEncodeRegexTag`(^${url.protocol}//${url.username}:.+@)?${url.hostname}` + }, + replacement: `$1${replacement || asterisk()}`, +}) + +/** + * creates a matcher for redacting url search / query parameter values + */ +const redactUrlSearchParamsMatcher = ({ param, replacement } = {}) => ({ + type: TYPE_URL, + predicate: ({ url }) => url.searchParams.has(param), + pattern: ({ url }) => urlEncodeRegexTag`(${param}=)${url.searchParams.get(param)}`, + replacement: `$1${replacement || asterisk()}`, +}) + +/** creates a matcher for redacting the url password */ +const redactUrlPasswordMatcher = ({ replacement } = {}) => ({ + type: TYPE_URL, + predicate: ({ url }) => url.password, + pattern: ({ url }) => urlEncodeRegexTag`(^${url.protocol}//${url.username}:)${url.password}`, + replacement: `$1${replacement || asterisk()}`, +}) + +const redactUrlReplacement = (...matchers) => (subValue) => { + try { + const url = new URL(subValue) + return redactMatchers(...matchers)(subValue, { url }) + } catch (err) { + return subValue + } +} + +/** + * creates a matcher / submatcher for urls, this function allows you to first + * collect all urls within a larger string and then pass those urls to a + * submatcher + * + * @example + * console.log("this will first match all urls, then pass those urls to the password patcher") + * redactMatchers(redactUrlMatcher(redactUrlPasswordMatcher())) + * + * @example + * console.log( + * "this will assume you are passing in a string that is a url, and will redact the password" + * ) + * redactMatchers(redactUrlPasswordMatcher()) + * + */ +const redactUrlMatcher = (...matchers) => { + return { + ...URL_MATCHER, + replacement: redactUrlReplacement(...matchers), + } +} + +const matcherFunctions = { + [TYPE_REGEX]: (matcher) => (value) => { + if (typeof value === 'string') { + value = value.replace(matcher.pattern, matcher.replacement) + } + return value + }, + [TYPE_URL]: (matcher) => (value, ctx) => { + if (typeof value === 'string') { + try { + const url = ctx?.url || new URL(value) + const { predicate, pattern } = matcher + const predicateValue = predicate({ url }) + if (predicateValue) { + value = value.replace(pattern({ url }), matcher.replacement) + } + } catch (_e) { + return value + } + } + return value + }, + [TYPE_PATH]: (matcher) => (value, ctx) => { + const rawPath = ctx?.path + const path = rawPath.join('.').toLowerCase() + const { predicate, replacement } = matcher + const replace = typeof replacement === 'function' ? replacement : () => replacement + const shouldRun = predicate({ rawPath, path }) + if (shouldRun) { + value = replace(value, { rawPath, path }) + } + return value + }, +} + +/** converts a matcher to a function */ +const redactMatcher = (matcher) => { + return matcherFunctions[matcher.type](matcher) +} + +/** converts a series of matchers to a function */ +const redactMatchers = (...matchers) => (value, ctx) => { + const flatMatchers = matchers.flat() + return flatMatchers.reduce((result, matcher) => { + const fn = (typeof matcher === 'function') ? matcher : redactMatcher(matcher) + return fn(result, ctx) + }, value) +} + +/** + * replacement handler, keeping $1 (if it exists) and replacing the + * rest of the string with asterisks, maintaining string length + */ +const redactDynamicReplacement = () => (value, start) => { + if (typeof start === 'number') { + return asterisk(value) + } + return start + asterisk(value.substring(start.length).length) +} + +/** + * replacement handler, keeping $1 (if it exists) and replacing the + * rest of the string with a fixed number of asterisks + */ +const redactFixedReplacement = (length) => (_value, start) => { + if (typeof start === 'number') { + return asterisk(length) + } + return start + asterisk(length) +} + +const redactUrlPassword = (value, replacement) => { + return redactMatchers(redactUrlPasswordMatcher({ replacement }))(value) +} + +module.exports = { + asterisk, + escapeRegExp, + urlEncodeRegexGroup, + urlEncodeRegexTag, + redactUrlHostnameMatcher, + redactUrlSearchParamsMatcher, + redactUrlPasswordMatcher, + redactUrlMatcher, + redactUrlReplacement, + redactDynamicReplacement, + redactFixedReplacement, + redactMatchers, + redactUrlPassword, +} diff --git a/node_modules/npm-profile/node_modules/@npmcli/redact/package.json b/node_modules/npm-profile/node_modules/@npmcli/redact/package.json new file mode 100644 index 0000000000000..2bcee9ea0884b --- /dev/null +++ b/node_modules/npm-profile/node_modules/@npmcli/redact/package.json @@ -0,0 +1,51 @@ +{ + "name": "@npmcli/redact", + "version": "2.0.0", + "description": "Redact sensitive npm information from output", + "main": "lib/index.js", + "exports": { + ".": "./lib/index.js", + "./server": "./lib/server.js", + "./package.json": "./package.json" + }, + "scripts": { + "test": "tap", + "lint": "eslint \"**/*.{js,cjs,ts,mjs,jsx,tsx}\"", + "postlint": "template-oss-check", + "template-oss-apply": "template-oss-apply --force", + "lintfix": "npm run lint -- --fix", + "snap": "tap", + "posttest": "npm run lint" + }, + "keywords": [], + "author": "GitHub Inc.", + "license": "ISC", + "files": [ + "bin/", + "lib/" + ], + "repository": { + "type": "git", + "url": "https://github.com/npm/redact.git" + }, + "templateOSS": { + "//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.", + "version": "4.21.3", + "publish": true + }, + "tap": { + "nyc-arg": [ + "--exclude", + "tap-snapshots/**" + ], + "timeout": 120 + }, + "devDependencies": { + "@npmcli/eslint-config": "^4.0.2", + "@npmcli/template-oss": "4.21.3", + "tap": "^16.3.10" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } +} 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..9270025fa8d90 --- /dev/null +++ b/node_modules/npm-profile/node_modules/npm-registry-fetch/lib/auth.js @@ -0,0 +1,181 @@ +'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. Returns the registry key and the auth config. +const regFromURI = (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) { + const authKey = hasAuth(regKey, opts) + // got some auth for this URI + if (authKey) { + return { regKey, authKey } + } + + // 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(/([^/]+|\/)$/, '') + } + return { regKey: false, authKey: null } +} + +// Not only do we want to know if there is auth, but if we are calling `npm +// logout` we want to know what config value specifically provided it. This is +// so we can look up where the config came from to delete it (i.e. user vs +// project) +const hasAuth = (regKey, opts) => { + if (opts[`${regKey}:_authToken`]) { + return '_authToken' + } + if (opts[`${regKey}:_auth`]) { + return '_auth' + } + if (opts[`${regKey}:username`] && opts[`${regKey}:_password`]) { + // 'password' can be inferred to also be present + return 'username' + } + if (opts[`${regKey}:certfile`] && opts[`${regKey}:keyfile`]) { + // 'keyfile' can be inferred to also be present + return 'certfile' + } + return false +} + +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, authKey } = regFromURI(uri, forceAuth || opts) + + // we are only allowed to use what's in forceAuth if specified + if (forceAuth && !regKey) { + return new Auth({ + // if we force auth we don't want to refer back to anything in config + regKey: false, + authKey: null, + 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 { regKey: scopeAuthKey, authKey: _authKey } = regFromURI(registry, opts) + return new Auth({ scopeAuthKey, regKey: scopeAuthKey, authKey: _authKey }) + } + } + + 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, + regKey, + authKey, + token, + auth, + username, + password, + certfile, + keyfile, + }) +} + +class Auth { + constructor ({ + token, + auth, + username, + password, + scopeAuthKey, + certfile, + keyfile, + regKey, + authKey, + }) { + // same as regKey but only present for scoped auth. Should have been named scopeRegKey + this.scopeAuthKey = scopeAuthKey + // `${regKey}:${authKey}` will get you back to the auth config that gave us auth + this.regKey = regKey + this.authKey = authKey + 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..65eea2963b0b4 --- /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 { redact: cleanUrl } = require('@npmcli/redact') + +/* 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/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..bce6e6b1aae0a --- /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.getAuth = getAuth + +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 +} 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..52820a6a206ec --- /dev/null +++ b/node_modules/npm-profile/node_modules/npm-registry-fetch/package.json @@ -0,0 +1,68 @@ +{ + "name": "npm-registry-fetch", + "version": "17.0.0", + "description": "Fetch-based http client for use with npm registry APIs", + "main": "lib", + "files": [ + "bin/", + "lib/" + ], + "scripts": { + "eslint": "eslint", + "lint": "eslint \"**/*.{js,cjs,ts,mjs,jsx,tsx}\"", + "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": { + "@npmcli/redact": "^2.0.0", + "make-fetch-happen": "^13.0.0", + "minipass": "^7.0.2", + "minipass-fetch": "^3.0.0", + "minipass-json-stream": "^1.0.1", + "minizlib": "^2.1.2", + "npm-package-arg": "^11.0.0", + "proc-log": "^4.0.0" + }, + "devDependencies": { + "@npmcli/eslint-config": "^4.0.0", + "@npmcli/template-oss": "4.21.4", + "cacache": "^18.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": "^16.14.0 || >=18.0.0" + }, + "templateOSS": { + "//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.", + "version": "4.21.4", + "publish": "true" + } +} diff --git a/node_modules/npm-profile/package.json b/node_modules/npm-profile/package.json index 3b4b582f8c15f..acdf4d6baf2ee 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.1", + "version": "9.0.2", "description": "Library for updating an npmjs.com profile", "keywords": [], "author": "GitHub Inc.", "license": "ISC", "dependencies": { - "npm-registry-fetch": "^16.0.0", + "npm-registry-fetch": "^17.0.0", "proc-log": "^4.0.0" }, "main": "./lib/index.js", @@ -20,7 +20,7 @@ ], "devDependencies": { "@npmcli/eslint-config": "^4.0.0", - "@npmcli/template-oss": "4.21.3", + "@npmcli/template-oss": "4.21.4", "nock": "^13.2.4", "tap": "^16.0.1" }, @@ -45,7 +45,7 @@ }, "templateOSS": { "//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.", - "version": "4.21.3", + "version": "4.21.4", "publish": true } } diff --git a/node_modules/sprintf-js/bower.json b/node_modules/sprintf-js/bower.json new file mode 100644 index 0000000000000..d90a75989f7b0 --- /dev/null +++ b/node_modules/sprintf-js/bower.json @@ -0,0 +1,14 @@ +{ + "name": "sprintf", + "description": "JavaScript sprintf implementation", + "version": "1.0.3", + "main": "src/sprintf.js", + "license": "BSD-3-Clause-Clear", + "keywords": ["sprintf", "string", "formatting"], + "authors": ["Alexandru Marasteanu (http://alexei.ro/)"], + "homepage": "https://github.com/alexei/sprintf.js", + "repository": { + "type": "git", + "url": "git://github.com/alexei/sprintf.js.git" + } +} diff --git a/node_modules/sprintf-js/demo/angular.html b/node_modules/sprintf-js/demo/angular.html new file mode 100644 index 0000000000000..3559efd763563 --- /dev/null +++ b/node_modules/sprintf-js/demo/angular.html @@ -0,0 +1,20 @@ + + + + + + + + +
{{ "%+010d"|sprintf:-123 }}
+
{{ "%+010d"|vsprintf:[-123] }}
+
{{ "%+010d"|fmt:-123 }}
+
{{ "%+010d"|vfmt:[-123] }}
+
{{ "I've got %2$d apples and %1$d oranges."|fmt:4:2 }}
+
{{ "I've got %(apples)d apples and %(oranges)d oranges."|fmt:{apples: 2, oranges: 4} }}
+ + + + diff --git a/node_modules/sprintf-js/gruntfile.js b/node_modules/sprintf-js/gruntfile.js new file mode 100644 index 0000000000000..246e1c3b9801f --- /dev/null +++ b/node_modules/sprintf-js/gruntfile.js @@ -0,0 +1,36 @@ +module.exports = function(grunt) { + grunt.initConfig({ + pkg: grunt.file.readJSON("package.json"), + + uglify: { + options: { + banner: "/*! <%= pkg.name %> | <%= pkg.author %> | <%= pkg.license %> */\n", + sourceMap: true + }, + build: { + files: [ + { + src: "src/sprintf.js", + dest: "dist/sprintf.min.js" + }, + { + src: "src/angular-sprintf.js", + dest: "dist/angular-sprintf.min.js" + } + ] + } + }, + + watch: { + js: { + files: "src/*.js", + tasks: ["uglify"] + } + } + }) + + grunt.loadNpmTasks("grunt-contrib-uglify") + grunt.loadNpmTasks("grunt-contrib-watch") + + grunt.registerTask("default", ["uglify", "watch"]) +} diff --git a/node_modules/sprintf-js/test/test.js b/node_modules/sprintf-js/test/test.js new file mode 100644 index 0000000000000..6f57b2538c852 --- /dev/null +++ b/node_modules/sprintf-js/test/test.js @@ -0,0 +1,82 @@ +var assert = require("assert"), + sprintfjs = require("../src/sprintf.js"), + sprintf = sprintfjs.sprintf, + vsprintf = sprintfjs.vsprintf + +describe("sprintfjs", function() { + var pi = 3.141592653589793 + + it("should return formated strings for simple placeholders", function() { + assert.equal("%", sprintf("%%")) + assert.equal("10", sprintf("%b", 2)) + assert.equal("A", sprintf("%c", 65)) + assert.equal("2", sprintf("%d", 2)) + assert.equal("2", sprintf("%i", 2)) + assert.equal("2", sprintf("%d", "2")) + assert.equal("2", sprintf("%i", "2")) + assert.equal('{"foo":"bar"}', sprintf("%j", {foo: "bar"})) + assert.equal('["foo","bar"]', sprintf("%j", ["foo", "bar"])) + assert.equal("2e+0", sprintf("%e", 2)) + assert.equal("2", sprintf("%u", 2)) + assert.equal("4294967294", sprintf("%u", -2)) + assert.equal("2.2", sprintf("%f", 2.2)) + assert.equal("3.141592653589793", sprintf("%g", pi)) + assert.equal("10", sprintf("%o", 8)) + assert.equal("%s", sprintf("%s", "%s")) + assert.equal("ff", sprintf("%x", 255)) + assert.equal("FF", sprintf("%X", 255)) + assert.equal("Polly wants a cracker", sprintf("%2$s %3$s a %1$s", "cracker", "Polly", "wants")) + assert.equal("Hello world!", sprintf("Hello %(who)s!", {"who": "world"})) + }) + + it("should return formated strings for complex placeholders", function() { + // sign + assert.equal("2", sprintf("%d", 2)) + assert.equal("-2", sprintf("%d", -2)) + assert.equal("+2", sprintf("%+d", 2)) + assert.equal("-2", sprintf("%+d", -2)) + assert.equal("2", sprintf("%i", 2)) + assert.equal("-2", sprintf("%i", -2)) + assert.equal("+2", sprintf("%+i", 2)) + assert.equal("-2", sprintf("%+i", -2)) + assert.equal("2.2", sprintf("%f", 2.2)) + assert.equal("-2.2", sprintf("%f", -2.2)) + assert.equal("+2.2", sprintf("%+f", 2.2)) + assert.equal("-2.2", sprintf("%+f", -2.2)) + assert.equal("-2.3", sprintf("%+.1f", -2.34)) + assert.equal("-0.0", sprintf("%+.1f", -0.01)) + assert.equal("3.14159", sprintf("%.6g", pi)) + assert.equal("3.14", sprintf("%.3g", pi)) + assert.equal("3", sprintf("%.1g", pi)) + assert.equal("-000000123", sprintf("%+010d", -123)) + assert.equal("______-123", sprintf("%+'_10d", -123)) + assert.equal("-234.34 123.2", sprintf("%f %f", -234.34, 123.2)) + + // padding + assert.equal("-0002", sprintf("%05d", -2)) + assert.equal("-0002", sprintf("%05i", -2)) + assert.equal(" <", sprintf("%5s", "<")) + assert.equal("0000<", sprintf("%05s", "<")) + assert.equal("____<", sprintf("%'_5s", "<")) + assert.equal("> ", sprintf("%-5s", ">")) + assert.equal(">0000", sprintf("%0-5s", ">")) + assert.equal(">____", sprintf("%'_-5s", ">")) + assert.equal("xxxxxx", sprintf("%5s", "xxxxxx")) + assert.equal("1234", sprintf("%02u", 1234)) + assert.equal(" -10.235", sprintf("%8.3f", -10.23456)) + assert.equal("-12.34 xxx", sprintf("%f %s", -12.34, "xxx")) + assert.equal('{\n "foo": "bar"\n}', sprintf("%2j", {foo: "bar"})) + assert.equal('[\n "foo",\n "bar"\n]', sprintf("%2j", ["foo", "bar"])) + + // precision + assert.equal("2.3", sprintf("%.1f", 2.345)) + assert.equal("xxxxx", sprintf("%5.5s", "xxxxxx")) + assert.equal(" x", sprintf("%5.1s", "xxxxxx")) + + }) + + it("should return formated strings for callbacks", function() { + assert.equal("foobar", sprintf("%s", function() { return "foobar" })) + assert.equal(Date.now(), sprintf("%s", Date.now)) // should pass... + }) +}) diff --git a/package-lock.json b/package-lock.json index 8c8db3beee119..01a2fc4881a7a 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.1", + "npm-profile": "^9.0.2", "npm-registry-fetch": "^16.2.1", "npm-user-validate": "^2.0.0", "p-map": "^4.0.0", @@ -9012,12 +9012,40 @@ } }, "node_modules/npm-profile": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/npm-profile/-/npm-profile-9.0.1.tgz", - "integrity": "sha512-9o6dw5eu3tiqX1XFOXjznO73Jzin48Oyo9qAhfaEHXzeZHXpdzgDmzWruWk7uJsu5GMIsigx5hva5rB5NhfSWw==", + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/npm-profile/-/npm-profile-9.0.2.tgz", + "integrity": "sha512-4S/fd/PNyGgjaGolsdUJFsnfEb+AxJzrrZC3I9qbTYZJ3PJy8T46tIWXA4pBoaeiGh2M2GRvK1K/xMQe1Xgbvw==", "inBundle": true, "dependencies": { - "npm-registry-fetch": "^16.0.0", + "npm-registry-fetch": "^17.0.0", + "proc-log": "^4.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm-profile/node_modules/@npmcli/redact": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/redact/-/redact-2.0.0.tgz", + "integrity": "sha512-SEjCPAVHWYUIQR+Yn03kJmrJjZDtJLYpj300m3HV9OTRZNpC5YpbMsM3eTkECyT4aWj8lDr9WeY6TWefpubtYQ==", + "inBundle": true, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm-profile/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==", + "inBundle": true, + "dependencies": { + "@npmcli/redact": "^2.0.0", + "make-fetch-happen": "^13.0.0", + "minipass": "^7.0.2", + "minipass-fetch": "^3.0.0", + "minipass-json-stream": "^1.0.1", + "minizlib": "^2.1.2", + "npm-package-arg": "^11.0.0", "proc-log": "^4.0.0" }, "engines": { diff --git a/package.json b/package.json index ed5d11f7d9a60..79239642853a9 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.1", + "npm-profile": "^9.0.2", "npm-registry-fetch": "^16.2.1", "npm-user-validate": "^2.0.0", "p-map": "^4.0.0",