From 457708699f2e7792484c0b47f1ed20ae93b578c7 Mon Sep 17 00:00:00 2001 From: Willie Ruemmele Date: Thu, 23 May 2024 12:34:08 -0600 Subject: [PATCH] feat: add network diagnostic check (#828) * feat: add network diagnostic check * docs: update ping check message * chore: add custom registry check too * refactor: without undefined, defers npm run * refactor: change conditional * refactor: env, env, or config * chore: update output with env/config, npm ping suggestion --------- Co-authored-by: mshanemc --- package.json | 2 + src/hooks/diagnostics.ts | 62 ++++++++++++++ src/shared/npmCommand.ts | 14 +++- yarn.lock | 170 ++++++++++++++++++++++++++++++++++++++- 4 files changed, 245 insertions(+), 3 deletions(-) create mode 100644 src/hooks/diagnostics.ts diff --git a/package.json b/package.json index 4d34128f..47486abb 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "@oclif/core": "^3.26.6", "@salesforce/core": "^7.3.8", "@salesforce/kit": "^3.1.1", + "@salesforce/plugin-info": "^3.2.7", "@salesforce/sf-plugins-core": "^9.0.10", "got": "^13.0.0", "npm": "^10.7.0", @@ -58,6 +59,7 @@ "oclif": { "commands": "./lib/commands", "hooks": { + "sf-doctor-@salesforce/plugin-trust": "./lib/hooks/diagnostics", "plugins:preinstall:verify:signature": [ "./lib/hooks/verifyInstallSignature.js" ], diff --git a/src/hooks/diagnostics.ts b/src/hooks/diagnostics.ts new file mode 100644 index 00000000..7c550cb1 --- /dev/null +++ b/src/hooks/diagnostics.ts @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2022, salesforce.com, inc. + * All rights reserved. + * Licensed under the BSD 3-Clause license. + * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause + */ +import { SfDoctor } from '@salesforce/plugin-info'; +import { Lifecycle } from '@salesforce/core'; +import { NpmModule } from '../shared/npmCommand.js'; +type HookFunction = (options: { doctor: SfDoctor }) => Promise<[void]>; +export const hook: HookFunction = (options) => Promise.all([registryCheck(options)]); + +const registryCheck = async (options: { doctor: SfDoctor }): Promise => { + const pluginName = '@salesforce/plugin-trust'; + // find npm install + const npm = new NpmModule(''); + const env = process.env.npm_config_registry ?? process.env.NPM_CONFIG_REGISTRY; + if (env) { + options.doctor.addSuggestion(`using npm registry ${env} from environment variable`); + } + + const config = npm.run('config get registry').stdout.trim(); + if (config) { + options.doctor.addSuggestion(`using npm registry ${config} from npm config`); + } + + await Promise.all( + [ + ...new Set([ + // npm and yarn registries + 'https://registry.npmjs.org', + 'https://registry.yarnpkg.com', + env ?? config, + ]), + ] + // incase customRegistry is undefined, prevent printing an extra line + .filter((u) => u) + .map(async (url) => { + try { + const results = npm.ping(url); + + // timeout after 5000ms, error + if (!results || results.time > 5000) { + // to trigger the catch/fail below + throw Error; + } + await Lifecycle.getInstance().emit('Doctor:diagnostic', { + testName: `[${pluginName}] can ping: ${url}`, + status: 'pass', + }); + } catch (e) { + await Lifecycle.getInstance().emit('Doctor:diagnostic', { + testName: `[${pluginName}] can't ping: ${url}`, + status: 'fail', + }); + options.doctor.addSuggestion( + `Cannot ping ${url} - potential network configuration error, check proxies, firewalls, environment variables. Verify this by running 'npm ping ${url}'` + ); + } + }) + ); +}; diff --git a/src/shared/npmCommand.ts b/src/shared/npmCommand.ts index d75cad34..ae5bf9fa 100644 --- a/src/shared/npmCommand.ts +++ b/src/shared/npmCommand.ts @@ -11,7 +11,7 @@ import { createRequire } from 'node:module'; import fs from 'node:fs'; import npmRunPath from 'npm-run-path'; -import shelljs from 'shelljs'; +import shelljs, { ShellString } from 'shelljs'; import { SfError } from '@salesforce/core'; import { sleep, parseJson } from '@salesforce/kit'; import { Ux } from '@salesforce/sf-plugins-core'; @@ -157,6 +157,18 @@ export class NpmModule { }; } + public ping(registry?: string): { registry: string; time: number; details: Record } { + return JSON.parse(NpmCommand.runNpmCmd(`ping ${registry} --json`, { json: true, cliRoot: this.cliRoot })) as { + registry: string; + time: number; + details: Record; + }; + } + + public run(command: string): ShellString { + return NpmCommand.runNpmCmd(command, { cliRoot: this.cliRoot, json: command.includes('--json') }); + } + public show(registry: string): NpmShowResults { const showCmd = NpmCommand.runNpmCmd(`show ${this.module}@${this.version} --json`, { registry, diff --git a/yarn.lock b/yarn.lock index e07cd8f6..b004d512 100644 --- a/yarn.lock +++ b/yarn.lock @@ -941,6 +941,11 @@ "@babel/helper-validator-identifier" "^7.22.20" to-fast-properties "^2.0.0" +"@colors/colors@1.5.0": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9" + integrity sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ== + "@commitlint/cli@^17.1.2": version "17.8.1" resolved "https://registry.npmjs.org/@commitlint/cli/-/cli-17.8.1.tgz" @@ -1244,11 +1249,35 @@ strip-ansi "^6.0.1" wrap-ansi "^6.2.0" +"@inquirer/core@^8.2.1": + version "8.2.1" + resolved "https://registry.yarnpkg.com/@inquirer/core/-/core-8.2.1.tgz#ee92c2bf25f378819f56290f8ed8bfef8c6cc94d" + integrity sha512-TIcuQMn2qrtyYe0j136UpHeYpk7AcR/trKeT/7YY0vRgcS9YSfJuQ2+PudPhSofLLsHNnRYAHScQCcVZrJkMqA== + dependencies: + "@inquirer/figures" "^1.0.2" + "@inquirer/type" "^1.3.2" + "@types/mute-stream" "^0.0.4" + "@types/node" "^20.12.12" + "@types/wrap-ansi" "^3.0.0" + ansi-escapes "^4.3.2" + chalk "^4.1.2" + cli-spinners "^2.9.2" + cli-width "^4.1.0" + mute-stream "^1.0.0" + signal-exit "^4.1.0" + strip-ansi "^6.0.1" + wrap-ansi "^6.2.0" + "@inquirer/figures@^1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@inquirer/figures/-/figures-1.0.1.tgz#d65f0bd0e9511a90b4d3543ee6a3ce7211f29417" integrity sha512-mtup3wVKia3ZwULPHcbs4Mor8Voi+iIXEWD7wCNbIO6lYR62oPCTQyrddi5OMYVXHzeCSoneZwJuS8sBvlEwDw== +"@inquirer/figures@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@inquirer/figures/-/figures-1.0.2.tgz#a6af5e9f9969efb9ed3469130566315c36506b8a" + integrity sha512-4F1MBwVr3c/m4bAUef6LgkvBfSjzwH+OfldgHqcuacWwSUetFebM2wi58WfG9uk1rR98U6GwLed4asLJbwdV5w== + "@inquirer/input@^2.1.1": version "2.1.2" resolved "https://registry.npmjs.org/@inquirer/input/-/input-2.1.2.tgz" @@ -1257,6 +1286,14 @@ "@inquirer/core" "^7.1.2" "@inquirer/type" "^1.2.1" +"@inquirer/input@^2.1.6": + version "2.1.8" + resolved "https://registry.yarnpkg.com/@inquirer/input/-/input-2.1.8.tgz#442cee18f0fce6ef850627c6653c4c4c6e66c1ec" + integrity sha512-W1hsmUArJRGI8kL8+Kl+9wgnm02xPbpKtSIlwoHtRfIn8f/b/9spfNuTWolCVDHh3ZA4LS+Et71d1P6UpdD20A== + dependencies: + "@inquirer/core" "^8.2.1" + "@inquirer/type" "^1.3.2" + "@inquirer/password@^1.1.16": version "1.1.16" resolved "https://registry.npmjs.org/@inquirer/password/-/password-1.1.16.tgz" @@ -1283,6 +1320,11 @@ resolved "https://registry.yarnpkg.com/@inquirer/type/-/type-1.3.1.tgz#afb95ff78f44fff7e8a00e17d5820db6add2a076" integrity sha512-Pe3PFccjPVJV1vtlfVvm9OnlbxqdnP5QcscFEFEnK5quChf1ufZtM0r8mR5ToWHMxZOh0s8o/qp9ANGRTo/DAw== +"@inquirer/type@^1.3.2": + version "1.3.2" + resolved "https://registry.yarnpkg.com/@inquirer/type/-/type-1.3.2.tgz#439b0b50c152c89fd369d2a17eff54869b4d79b8" + integrity sha512-5Frickan9c89QbPkSu6I6y8p+9eR6hZkdPahGmNDsTFX8FHLPAozyzCZMKUeW8FyYwnlCKUjqIEqxY+UctARiw== + "@isaacs/cliui@^8.0.2": version "8.0.2" resolved "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz" @@ -1819,6 +1861,23 @@ debug "^4.3.4" handlebars "^4.7.8" +"@salesforce/plugin-info@^3.2.7": + version "3.2.8" + resolved "https://registry.yarnpkg.com/@salesforce/plugin-info/-/plugin-info-3.2.8.tgz#34d5bc209b7377b4c2fb51e4da875e355da6cd95" + integrity sha512-yYGn+LbKg1UPQ1D8yKwuwjtWZbM4eAPpRlMTuTST3t4d/rfYXdBN9D6iSIgiL9h88LQwkK4T0fjcqVX+W4ZZhQ== + dependencies: + "@inquirer/input" "^2.1.6" + "@oclif/core" "^3.26.5" + "@salesforce/core" "^7.3.5" + "@salesforce/kit" "^3.1.0" + "@salesforce/sf-plugins-core" "^9.0.7" + got "^13.0.0" + marked "^4.3.0" + marked-terminal "^4.2.0" + open "^10.1.0" + proxy-agent "^6.4.0" + semver "^7.6.0" + "@salesforce/plugin-telemetry@^3.3.9": version "3.3.9" resolved "https://registry.yarnpkg.com/@salesforce/plugin-telemetry/-/plugin-telemetry-3.3.9.tgz#9dd576dac713cb67918e17f490ce78c68202e2a0" @@ -1855,6 +1914,19 @@ "@salesforce/ts-types" "^2.0.9" chalk "^5.3.0" +"@salesforce/sf-plugins-core@^9.0.7": + version "9.0.12" + resolved "https://registry.yarnpkg.com/@salesforce/sf-plugins-core/-/sf-plugins-core-9.0.12.tgz#8bcacdb412c3570f66220acd6346636d86f81d62" + integrity sha512-KtZErzUdBrDDONpE6ZbW3V5WfBwUwo07Snttnu4IFs82rbsOkyCG/m02b+mbnGBrwWWy7auZW4e2ciRrmbZGDQ== + dependencies: + "@inquirer/confirm" "^2.0.17" + "@inquirer/password" "^1.1.16" + "@oclif/core" "^3.26.6" + "@salesforce/core" "^7.3.8" + "@salesforce/kit" "^3.1.1" + "@salesforce/ts-types" "^2.0.9" + chalk "^5.3.0" + "@salesforce/telemetry@^6.0.6": version "6.0.7" resolved "https://registry.yarnpkg.com/@salesforce/telemetry/-/telemetry-6.0.7.tgz#79fa4daabf440cbd5a88dfa9c42cf663cf6f3337" @@ -2656,6 +2728,13 @@ dependencies: undici-types "~5.26.4" +"@types/node@^20.12.12": + version "20.12.12" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.12.12.tgz#7cbecdf902085cec634fdb362172dfe12b8f2050" + integrity sha512-eWLDGF/FOSPtAvEqeRAQ4C8LSA7M1I7i0ky1I8U7kD1J5ITyW3AsRhQrKVoWf5pFKZ2kILsEGJhsI9r93PYnOw== + dependencies: + undici-types "~5.26.4" + "@types/normalize-package-data@^2.4.0": version "2.4.3" resolved "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.3.tgz" @@ -2889,7 +2968,7 @@ ansi-colors@4.1.1: resolved "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz" integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== -ansi-escapes@^4.3.2: +ansi-escapes@^4.3.1, ansi-escapes@^4.3.2: version "4.3.2" resolved "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz" integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== @@ -3260,6 +3339,13 @@ builtins@^5.0.0: dependencies: semver "^7.0.0" +bundle-name@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/bundle-name/-/bundle-name-4.1.0.tgz#f3b96b34160d6431a19d7688135af7cfb8797889" + integrity sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q== + dependencies: + run-applescript "^7.0.0" + cacache@^18.0.0, cacache@^18.0.2: version "18.0.2" resolved "https://registry.npmjs.org/cacache/-/cacache-18.0.2.tgz" @@ -3523,6 +3609,15 @@ cli-spinners@^2.9.2: resolved "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz" integrity sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg== +cli-table3@^0.6.0: + version "0.6.5" + resolved "https://registry.yarnpkg.com/cli-table3/-/cli-table3-0.6.5.tgz#013b91351762739c16a9567c21a04632e449bf2f" + integrity sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ== + dependencies: + string-width "^4.2.0" + optionalDependencies: + "@colors/colors" "1.5.0" + cli-width@^4.1.0: version "4.1.0" resolved "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz" @@ -3856,6 +3951,19 @@ deep-is@^0.1.3: resolved "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz" integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== +default-browser-id@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/default-browser-id/-/default-browser-id-5.0.0.tgz#a1d98bf960c15082d8a3fa69e83150ccccc3af26" + integrity sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA== + +default-browser@^5.2.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/default-browser/-/default-browser-5.2.1.tgz#7b7ba61204ff3e425b556869ae6d3e9d9f1712cf" + integrity sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg== + dependencies: + bundle-name "^4.1.0" + default-browser-id "^5.0.0" + default-require-extensions@^3.0.0: version "3.0.1" resolved "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-3.0.1.tgz" @@ -3877,6 +3985,11 @@ define-data-property@^1.0.1, define-data-property@^1.1.1: gopd "^1.0.1" has-property-descriptors "^1.0.0" +define-lazy-prop@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz#dbb19adfb746d7fc6d734a06b72f4a00d021255f" + integrity sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg== + define-properties@^1.1.3, define-properties@^1.1.4, define-properties@^1.2.0: version "1.2.1" resolved "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz" @@ -5413,6 +5526,11 @@ is-docker@^2.0.0: resolved "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz" integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== +is-docker@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-3.0.0.tgz#90093aa3106277d8a77a5910dbae71747e15a200" + integrity sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ== + is-extglob@^2.1.1: version "2.1.1" resolved "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz" @@ -5430,6 +5548,13 @@ is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: dependencies: is-extglob "^2.1.1" +is-inside-container@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-inside-container/-/is-inside-container-1.0.0.tgz#e81fba699662eb31dbdaf26766a61d4814717ea4" + integrity sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA== + dependencies: + is-docker "^3.0.0" + is-lambda@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz" @@ -5559,6 +5684,13 @@ is-wsl@^2.2.0: dependencies: is-docker "^2.0.0" +is-wsl@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-3.1.0.tgz#e1c657e39c10090afcbedec61720f6b924c3cbd2" + integrity sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw== + dependencies: + is-inside-container "^1.0.0" + isarray@0.0.1: version "0.0.1" resolved "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" @@ -6265,6 +6397,18 @@ map-obj@^4.0.0: resolved "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz" integrity sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ== +marked-terminal@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/marked-terminal/-/marked-terminal-4.2.0.tgz#593734a53cf9a4bb01ea961aa579bd21889ce502" + integrity sha512-DQfNRV9svZf0Dm9Cf5x5xaVJ1+XjxQW6XjFJ5HFkVyK52SDpj5PCBzS5X5r2w9nHr3mlB0T5201UMLue9fmhUw== + dependencies: + ansi-escapes "^4.3.1" + cardinal "^2.1.1" + chalk "^4.1.0" + cli-table3 "^0.6.0" + node-emoji "^1.10.0" + supports-hyperlinks "^2.1.0" + marked@^10.0.0: version "10.0.0" resolved "https://registry.npmjs.org/marked/-/marked-10.0.0.tgz" @@ -6608,6 +6752,13 @@ no-case@^3.0.4: lower-case "^2.0.2" tslib "^2.0.3" +node-emoji@^1.10.0: + version "1.11.0" + resolved "https://registry.yarnpkg.com/node-emoji/-/node-emoji-1.11.0.tgz#69a0150e6946e2f115e9d7ea4df7971e2628301c" + integrity sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A== + dependencies: + lodash "^4.17.21" + node-fetch@^2.6.1, node-fetch@^2.6.9: version "2.7.0" resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz" @@ -7013,6 +7164,16 @@ onetime@^5.1.0, onetime@^5.1.2: dependencies: mimic-fn "^2.1.0" +open@^10.1.0: + version "10.1.0" + resolved "https://registry.yarnpkg.com/open/-/open-10.1.0.tgz#a7795e6e5d519abe4286d9937bb24b51122598e1" + integrity sha512-mnkeQ1qP5Ue2wd+aivTD3NHd/lZ96Lu0jgf0pwktLPtx6cTZiH7tyeGRRHs0zX0rbrahXPnXlUnbeXyaBBuIaw== + dependencies: + default-browser "^5.2.1" + define-lazy-prop "^3.0.0" + is-inside-container "^1.0.0" + is-wsl "^3.1.0" + optionator@^0.9.3: version "0.9.3" resolved "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz" @@ -7787,6 +7948,11 @@ rimraf@^3.0.0, rimraf@^3.0.2: dependencies: glob "^7.1.3" +run-applescript@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/run-applescript/-/run-applescript-7.0.0.tgz#e5a553c2bffd620e169d276c1cd8f1b64778fbeb" + integrity sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A== + run-async@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/run-async/-/run-async-3.0.0.tgz" @@ -8371,7 +8537,7 @@ supports-color@^9.4.0: resolved "https://registry.npmjs.org/supports-color/-/supports-color-9.4.0.tgz" integrity sha512-VL+lNrEoIXww1coLPOmiEmK/0sGigko5COxI09KzHc2VJXJsQ37UaQ+8quuxjDeA7+KnLGTWRyOXSLLR2Wb4jw== -supports-hyperlinks@^2.2.0: +supports-hyperlinks@^2.1.0, supports-hyperlinks@^2.2.0: version "2.3.0" resolved "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.3.0.tgz" integrity sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==