11cb0ef41Sopenharmony_ciconst cacache = require('cacache')
21cb0ef41Sopenharmony_ciconst fs = require('fs')
31cb0ef41Sopenharmony_ciconst fetch = require('make-fetch-happen')
41cb0ef41Sopenharmony_ciconst Table = require('cli-table3')
51cb0ef41Sopenharmony_ciconst which = require('which')
61cb0ef41Sopenharmony_ciconst pacote = require('pacote')
71cb0ef41Sopenharmony_ciconst { resolve } = require('path')
81cb0ef41Sopenharmony_ciconst semver = require('semver')
91cb0ef41Sopenharmony_ciconst { promisify } = require('util')
101cb0ef41Sopenharmony_ciconst log = require('../utils/log-shim.js')
111cb0ef41Sopenharmony_ciconst ping = require('../utils/ping.js')
121cb0ef41Sopenharmony_ciconst { defaults } = require('@npmcli/config/lib/definitions')
131cb0ef41Sopenharmony_ciconst lstat = promisify(fs.lstat)
141cb0ef41Sopenharmony_ciconst readdir = promisify(fs.readdir)
151cb0ef41Sopenharmony_ciconst access = promisify(fs.access)
161cb0ef41Sopenharmony_ciconst { R_OK, W_OK, X_OK } = fs.constants
171cb0ef41Sopenharmony_ci
181cb0ef41Sopenharmony_ciconst maskLabel = mask => {
191cb0ef41Sopenharmony_ci  const label = []
201cb0ef41Sopenharmony_ci  if (mask & R_OK) {
211cb0ef41Sopenharmony_ci    label.push('readable')
221cb0ef41Sopenharmony_ci  }
231cb0ef41Sopenharmony_ci
241cb0ef41Sopenharmony_ci  if (mask & W_OK) {
251cb0ef41Sopenharmony_ci    label.push('writable')
261cb0ef41Sopenharmony_ci  }
271cb0ef41Sopenharmony_ci
281cb0ef41Sopenharmony_ci  if (mask & X_OK) {
291cb0ef41Sopenharmony_ci    label.push('executable')
301cb0ef41Sopenharmony_ci  }
311cb0ef41Sopenharmony_ci
321cb0ef41Sopenharmony_ci  return label.join(', ')
331cb0ef41Sopenharmony_ci}
341cb0ef41Sopenharmony_ci
351cb0ef41Sopenharmony_ciconst subcommands = [
361cb0ef41Sopenharmony_ci  {
371cb0ef41Sopenharmony_ci    groups: ['ping', 'registry'],
381cb0ef41Sopenharmony_ci    title: 'npm ping',
391cb0ef41Sopenharmony_ci    cmd: 'checkPing',
401cb0ef41Sopenharmony_ci  }, {
411cb0ef41Sopenharmony_ci    groups: ['versions'],
421cb0ef41Sopenharmony_ci    title: 'npm -v',
431cb0ef41Sopenharmony_ci    cmd: 'getLatestNpmVersion',
441cb0ef41Sopenharmony_ci  }, {
451cb0ef41Sopenharmony_ci    groups: ['versions'],
461cb0ef41Sopenharmony_ci    title: 'node -v',
471cb0ef41Sopenharmony_ci    cmd: 'getLatestNodejsVersion',
481cb0ef41Sopenharmony_ci  }, {
491cb0ef41Sopenharmony_ci    groups: ['registry'],
501cb0ef41Sopenharmony_ci    title: 'npm config get registry',
511cb0ef41Sopenharmony_ci    cmd: 'checkNpmRegistry',
521cb0ef41Sopenharmony_ci  }, {
531cb0ef41Sopenharmony_ci    groups: ['environment'],
541cb0ef41Sopenharmony_ci    title: 'git executable in PATH',
551cb0ef41Sopenharmony_ci    cmd: 'getGitPath',
561cb0ef41Sopenharmony_ci  }, {
571cb0ef41Sopenharmony_ci    groups: ['environment'],
581cb0ef41Sopenharmony_ci    title: 'global bin folder in PATH',
591cb0ef41Sopenharmony_ci    cmd: 'getBinPath',
601cb0ef41Sopenharmony_ci  }, {
611cb0ef41Sopenharmony_ci    groups: ['permissions', 'cache'],
621cb0ef41Sopenharmony_ci    title: 'Perms check on cached files',
631cb0ef41Sopenharmony_ci    cmd: 'checkCachePermission',
641cb0ef41Sopenharmony_ci    windows: false,
651cb0ef41Sopenharmony_ci  }, {
661cb0ef41Sopenharmony_ci    groups: ['permissions'],
671cb0ef41Sopenharmony_ci    title: 'Perms check on local node_modules',
681cb0ef41Sopenharmony_ci    cmd: 'checkLocalModulesPermission',
691cb0ef41Sopenharmony_ci    windows: false,
701cb0ef41Sopenharmony_ci  }, {
711cb0ef41Sopenharmony_ci    groups: ['permissions'],
721cb0ef41Sopenharmony_ci    title: 'Perms check on global node_modules',
731cb0ef41Sopenharmony_ci    cmd: 'checkGlobalModulesPermission',
741cb0ef41Sopenharmony_ci    windows: false,
751cb0ef41Sopenharmony_ci  }, {
761cb0ef41Sopenharmony_ci    groups: ['permissions'],
771cb0ef41Sopenharmony_ci    title: 'Perms check on local bin folder',
781cb0ef41Sopenharmony_ci    cmd: 'checkLocalBinPermission',
791cb0ef41Sopenharmony_ci    windows: false,
801cb0ef41Sopenharmony_ci  }, {
811cb0ef41Sopenharmony_ci    groups: ['permissions'],
821cb0ef41Sopenharmony_ci    title: 'Perms check on global bin folder',
831cb0ef41Sopenharmony_ci    cmd: 'checkGlobalBinPermission',
841cb0ef41Sopenharmony_ci    windows: false,
851cb0ef41Sopenharmony_ci  }, {
861cb0ef41Sopenharmony_ci    groups: ['cache'],
871cb0ef41Sopenharmony_ci    title: 'Verify cache contents',
881cb0ef41Sopenharmony_ci    cmd: 'verifyCachedFiles',
891cb0ef41Sopenharmony_ci    windows: false,
901cb0ef41Sopenharmony_ci  },
911cb0ef41Sopenharmony_ci  // TODO:
921cb0ef41Sopenharmony_ci  // group === 'dependencies'?
931cb0ef41Sopenharmony_ci  //   - ensure arborist.loadActual() runs without errors and no invalid edges
941cb0ef41Sopenharmony_ci  //   - ensure package-lock.json matches loadActual()
951cb0ef41Sopenharmony_ci  //   - verify loadActual without hidden lock file matches hidden lockfile
961cb0ef41Sopenharmony_ci  // group === '???'
971cb0ef41Sopenharmony_ci  //   - verify all local packages have bins linked
981cb0ef41Sopenharmony_ci  // What is the fix for these?
991cb0ef41Sopenharmony_ci]
1001cb0ef41Sopenharmony_ciconst BaseCommand = require('../base-command.js')
1011cb0ef41Sopenharmony_ciclass Doctor extends BaseCommand {
1021cb0ef41Sopenharmony_ci  static description = 'Check the health of your npm environment'
1031cb0ef41Sopenharmony_ci  static name = 'doctor'
1041cb0ef41Sopenharmony_ci  static params = ['registry']
1051cb0ef41Sopenharmony_ci  static ignoreImplicitWorkspace = false
1061cb0ef41Sopenharmony_ci  static usage = [`[${subcommands.flatMap(s => s.groups)
1071cb0ef41Sopenharmony_ci    .filter((value, index, self) => self.indexOf(value) === index)
1081cb0ef41Sopenharmony_ci    .join('] [')}]`]
1091cb0ef41Sopenharmony_ci
1101cb0ef41Sopenharmony_ci  static subcommands = subcommands
1111cb0ef41Sopenharmony_ci
1121cb0ef41Sopenharmony_ci  // minimum width of check column, enough for the word `Check`
1131cb0ef41Sopenharmony_ci  #checkWidth = 5
1141cb0ef41Sopenharmony_ci
1151cb0ef41Sopenharmony_ci  async exec (args) {
1161cb0ef41Sopenharmony_ci    log.info('Running checkup')
1171cb0ef41Sopenharmony_ci    let allOk = true
1181cb0ef41Sopenharmony_ci
1191cb0ef41Sopenharmony_ci    const actions = this.actions(args)
1201cb0ef41Sopenharmony_ci    this.#checkWidth = actions.reduce((length, item) =>
1211cb0ef41Sopenharmony_ci      Math.max(item.title.length, length), this.#checkWidth)
1221cb0ef41Sopenharmony_ci
1231cb0ef41Sopenharmony_ci    if (!this.npm.silent) {
1241cb0ef41Sopenharmony_ci      this.output(['Check', 'Value', 'Recommendation/Notes'].map(h => this.npm.chalk.underline(h)))
1251cb0ef41Sopenharmony_ci    }
1261cb0ef41Sopenharmony_ci    // Do the actual work
1271cb0ef41Sopenharmony_ci    for (const { title, cmd } of actions) {
1281cb0ef41Sopenharmony_ci      const item = [title]
1291cb0ef41Sopenharmony_ci      try {
1301cb0ef41Sopenharmony_ci        item.push(true, await this[cmd]())
1311cb0ef41Sopenharmony_ci      } catch (err) {
1321cb0ef41Sopenharmony_ci        item.push(false, err)
1331cb0ef41Sopenharmony_ci      }
1341cb0ef41Sopenharmony_ci      if (!item[1]) {
1351cb0ef41Sopenharmony_ci        allOk = false
1361cb0ef41Sopenharmony_ci        item[0] = this.npm.chalk.red(item[0])
1371cb0ef41Sopenharmony_ci        item[1] = this.npm.chalk.red('not ok')
1381cb0ef41Sopenharmony_ci        item[2] = this.npm.chalk.magenta(String(item[2]))
1391cb0ef41Sopenharmony_ci      } else {
1401cb0ef41Sopenharmony_ci        item[1] = this.npm.chalk.green('ok')
1411cb0ef41Sopenharmony_ci      }
1421cb0ef41Sopenharmony_ci      if (!this.npm.silent) {
1431cb0ef41Sopenharmony_ci        this.output(item)
1441cb0ef41Sopenharmony_ci      }
1451cb0ef41Sopenharmony_ci    }
1461cb0ef41Sopenharmony_ci
1471cb0ef41Sopenharmony_ci    if (!allOk) {
1481cb0ef41Sopenharmony_ci      if (this.npm.silent) {
1491cb0ef41Sopenharmony_ci        /* eslint-disable-next-line max-len */
1501cb0ef41Sopenharmony_ci        throw new Error('Some problems found. Check logs or disable silent mode for recommendations.')
1511cb0ef41Sopenharmony_ci      } else {
1521cb0ef41Sopenharmony_ci        throw new Error('Some problems found. See above for recommendations.')
1531cb0ef41Sopenharmony_ci      }
1541cb0ef41Sopenharmony_ci    }
1551cb0ef41Sopenharmony_ci  }
1561cb0ef41Sopenharmony_ci
1571cb0ef41Sopenharmony_ci  async checkPing () {
1581cb0ef41Sopenharmony_ci    const tracker = log.newItem('checkPing', 1)
1591cb0ef41Sopenharmony_ci    tracker.info('checkPing', 'Pinging registry')
1601cb0ef41Sopenharmony_ci    try {
1611cb0ef41Sopenharmony_ci      await ping({ ...this.npm.flatOptions, retry: false })
1621cb0ef41Sopenharmony_ci      return ''
1631cb0ef41Sopenharmony_ci    } catch (er) {
1641cb0ef41Sopenharmony_ci      if (/^E\d{3}$/.test(er.code || '')) {
1651cb0ef41Sopenharmony_ci        throw er.code.slice(1) + ' ' + er.message
1661cb0ef41Sopenharmony_ci      } else {
1671cb0ef41Sopenharmony_ci        throw er.message
1681cb0ef41Sopenharmony_ci      }
1691cb0ef41Sopenharmony_ci    } finally {
1701cb0ef41Sopenharmony_ci      tracker.finish()
1711cb0ef41Sopenharmony_ci    }
1721cb0ef41Sopenharmony_ci  }
1731cb0ef41Sopenharmony_ci
1741cb0ef41Sopenharmony_ci  async getLatestNpmVersion () {
1751cb0ef41Sopenharmony_ci    const tracker = log.newItem('getLatestNpmVersion', 1)
1761cb0ef41Sopenharmony_ci    tracker.info('getLatestNpmVersion', 'Getting npm package information')
1771cb0ef41Sopenharmony_ci    try {
1781cb0ef41Sopenharmony_ci      const latest = (await pacote.manifest('npm@latest', this.npm.flatOptions)).version
1791cb0ef41Sopenharmony_ci      if (semver.gte(this.npm.version, latest)) {
1801cb0ef41Sopenharmony_ci        return `current: v${this.npm.version}, latest: v${latest}`
1811cb0ef41Sopenharmony_ci      } else {
1821cb0ef41Sopenharmony_ci        throw `Use npm v${latest}`
1831cb0ef41Sopenharmony_ci      }
1841cb0ef41Sopenharmony_ci    } finally {
1851cb0ef41Sopenharmony_ci      tracker.finish()
1861cb0ef41Sopenharmony_ci    }
1871cb0ef41Sopenharmony_ci  }
1881cb0ef41Sopenharmony_ci
1891cb0ef41Sopenharmony_ci  async getLatestNodejsVersion () {
1901cb0ef41Sopenharmony_ci    // XXX get the latest in the current major as well
1911cb0ef41Sopenharmony_ci    const current = process.version
1921cb0ef41Sopenharmony_ci    const currentRange = `^${current}`
1931cb0ef41Sopenharmony_ci    const url = 'https://nodejs.org/dist/index.json'
1941cb0ef41Sopenharmony_ci    const tracker = log.newItem('getLatestNodejsVersion', 1)
1951cb0ef41Sopenharmony_ci    tracker.info('getLatestNodejsVersion', 'Getting Node.js release information')
1961cb0ef41Sopenharmony_ci    try {
1971cb0ef41Sopenharmony_ci      const res = await fetch(url, { method: 'GET', ...this.npm.flatOptions })
1981cb0ef41Sopenharmony_ci      const data = await res.json()
1991cb0ef41Sopenharmony_ci      let maxCurrent = '0.0.0'
2001cb0ef41Sopenharmony_ci      let maxLTS = '0.0.0'
2011cb0ef41Sopenharmony_ci      for (const { lts, version } of data) {
2021cb0ef41Sopenharmony_ci        if (lts && semver.gt(version, maxLTS)) {
2031cb0ef41Sopenharmony_ci          maxLTS = version
2041cb0ef41Sopenharmony_ci        }
2051cb0ef41Sopenharmony_ci
2061cb0ef41Sopenharmony_ci        if (semver.satisfies(version, currentRange) && semver.gt(version, maxCurrent)) {
2071cb0ef41Sopenharmony_ci          maxCurrent = version
2081cb0ef41Sopenharmony_ci        }
2091cb0ef41Sopenharmony_ci      }
2101cb0ef41Sopenharmony_ci      const recommended = semver.gt(maxCurrent, maxLTS) ? maxCurrent : maxLTS
2111cb0ef41Sopenharmony_ci      if (semver.gte(process.version, recommended)) {
2121cb0ef41Sopenharmony_ci        return `current: ${current}, recommended: ${recommended}`
2131cb0ef41Sopenharmony_ci      } else {
2141cb0ef41Sopenharmony_ci        throw `Use node ${recommended} (current: ${current})`
2151cb0ef41Sopenharmony_ci      }
2161cb0ef41Sopenharmony_ci    } finally {
2171cb0ef41Sopenharmony_ci      tracker.finish()
2181cb0ef41Sopenharmony_ci    }
2191cb0ef41Sopenharmony_ci  }
2201cb0ef41Sopenharmony_ci
2211cb0ef41Sopenharmony_ci  async getBinPath (dir) {
2221cb0ef41Sopenharmony_ci    const tracker = log.newItem('getBinPath', 1)
2231cb0ef41Sopenharmony_ci    tracker.info('getBinPath', 'Finding npm global bin in your PATH')
2241cb0ef41Sopenharmony_ci    if (!process.env.PATH.includes(this.npm.globalBin)) {
2251cb0ef41Sopenharmony_ci      throw new Error(`Add ${this.npm.globalBin} to your $PATH`)
2261cb0ef41Sopenharmony_ci    }
2271cb0ef41Sopenharmony_ci    return this.npm.globalBin
2281cb0ef41Sopenharmony_ci  }
2291cb0ef41Sopenharmony_ci
2301cb0ef41Sopenharmony_ci  async checkCachePermission () {
2311cb0ef41Sopenharmony_ci    return this.checkFilesPermission(this.npm.cache, true, R_OK)
2321cb0ef41Sopenharmony_ci  }
2331cb0ef41Sopenharmony_ci
2341cb0ef41Sopenharmony_ci  async checkLocalModulesPermission () {
2351cb0ef41Sopenharmony_ci    return this.checkFilesPermission(this.npm.localDir, true, R_OK | W_OK, true)
2361cb0ef41Sopenharmony_ci  }
2371cb0ef41Sopenharmony_ci
2381cb0ef41Sopenharmony_ci  async checkGlobalModulesPermission () {
2391cb0ef41Sopenharmony_ci    return this.checkFilesPermission(this.npm.globalDir, false, R_OK)
2401cb0ef41Sopenharmony_ci  }
2411cb0ef41Sopenharmony_ci
2421cb0ef41Sopenharmony_ci  async checkLocalBinPermission () {
2431cb0ef41Sopenharmony_ci    return this.checkFilesPermission(this.npm.localBin, false, R_OK | W_OK | X_OK, true)
2441cb0ef41Sopenharmony_ci  }
2451cb0ef41Sopenharmony_ci
2461cb0ef41Sopenharmony_ci  async checkGlobalBinPermission () {
2471cb0ef41Sopenharmony_ci    return this.checkFilesPermission(this.npm.globalBin, false, X_OK)
2481cb0ef41Sopenharmony_ci  }
2491cb0ef41Sopenharmony_ci
2501cb0ef41Sopenharmony_ci  async checkFilesPermission (root, shouldOwn, mask, missingOk) {
2511cb0ef41Sopenharmony_ci    let ok = true
2521cb0ef41Sopenharmony_ci
2531cb0ef41Sopenharmony_ci    const tracker = log.newItem(root, 1)
2541cb0ef41Sopenharmony_ci
2551cb0ef41Sopenharmony_ci    try {
2561cb0ef41Sopenharmony_ci      const uid = process.getuid()
2571cb0ef41Sopenharmony_ci      const gid = process.getgid()
2581cb0ef41Sopenharmony_ci      const files = new Set([root])
2591cb0ef41Sopenharmony_ci      for (const f of files) {
2601cb0ef41Sopenharmony_ci        tracker.silly('checkFilesPermission', f.slice(root.length + 1))
2611cb0ef41Sopenharmony_ci        const st = await lstat(f).catch(er => {
2621cb0ef41Sopenharmony_ci          // if it can't be missing, or if it can and the error wasn't that it was missing
2631cb0ef41Sopenharmony_ci          if (!missingOk || er.code !== 'ENOENT') {
2641cb0ef41Sopenharmony_ci            ok = false
2651cb0ef41Sopenharmony_ci            tracker.warn('checkFilesPermission', 'error getting info for ' + f)
2661cb0ef41Sopenharmony_ci          }
2671cb0ef41Sopenharmony_ci        })
2681cb0ef41Sopenharmony_ci
2691cb0ef41Sopenharmony_ci        tracker.completeWork(1)
2701cb0ef41Sopenharmony_ci
2711cb0ef41Sopenharmony_ci        if (!st) {
2721cb0ef41Sopenharmony_ci          continue
2731cb0ef41Sopenharmony_ci        }
2741cb0ef41Sopenharmony_ci
2751cb0ef41Sopenharmony_ci        if (shouldOwn && (uid !== st.uid || gid !== st.gid)) {
2761cb0ef41Sopenharmony_ci          tracker.warn('checkFilesPermission', 'should be owner of ' + f)
2771cb0ef41Sopenharmony_ci          ok = false
2781cb0ef41Sopenharmony_ci        }
2791cb0ef41Sopenharmony_ci
2801cb0ef41Sopenharmony_ci        if (!st.isDirectory() && !st.isFile()) {
2811cb0ef41Sopenharmony_ci          continue
2821cb0ef41Sopenharmony_ci        }
2831cb0ef41Sopenharmony_ci
2841cb0ef41Sopenharmony_ci        try {
2851cb0ef41Sopenharmony_ci          await access(f, mask)
2861cb0ef41Sopenharmony_ci        } catch (er) {
2871cb0ef41Sopenharmony_ci          ok = false
2881cb0ef41Sopenharmony_ci          const msg = `Missing permissions on ${f} (expect: ${maskLabel(mask)})`
2891cb0ef41Sopenharmony_ci          tracker.error('checkFilesPermission', msg)
2901cb0ef41Sopenharmony_ci          continue
2911cb0ef41Sopenharmony_ci        }
2921cb0ef41Sopenharmony_ci
2931cb0ef41Sopenharmony_ci        if (st.isDirectory()) {
2941cb0ef41Sopenharmony_ci          const entries = await readdir(f).catch(er => {
2951cb0ef41Sopenharmony_ci            ok = false
2961cb0ef41Sopenharmony_ci            tracker.warn('checkFilesPermission', 'error reading directory ' + f)
2971cb0ef41Sopenharmony_ci            return []
2981cb0ef41Sopenharmony_ci          })
2991cb0ef41Sopenharmony_ci          for (const entry of entries) {
3001cb0ef41Sopenharmony_ci            files.add(resolve(f, entry))
3011cb0ef41Sopenharmony_ci          }
3021cb0ef41Sopenharmony_ci        }
3031cb0ef41Sopenharmony_ci      }
3041cb0ef41Sopenharmony_ci    } finally {
3051cb0ef41Sopenharmony_ci      tracker.finish()
3061cb0ef41Sopenharmony_ci      if (!ok) {
3071cb0ef41Sopenharmony_ci        throw (
3081cb0ef41Sopenharmony_ci          `Check the permissions of files in ${root}` +
3091cb0ef41Sopenharmony_ci          (shouldOwn ? ' (should be owned by current user)' : '')
3101cb0ef41Sopenharmony_ci        )
3111cb0ef41Sopenharmony_ci      } else {
3121cb0ef41Sopenharmony_ci        return ''
3131cb0ef41Sopenharmony_ci      }
3141cb0ef41Sopenharmony_ci    }
3151cb0ef41Sopenharmony_ci  }
3161cb0ef41Sopenharmony_ci
3171cb0ef41Sopenharmony_ci  async getGitPath () {
3181cb0ef41Sopenharmony_ci    const tracker = log.newItem('getGitPath', 1)
3191cb0ef41Sopenharmony_ci    tracker.info('getGitPath', 'Finding git in your PATH')
3201cb0ef41Sopenharmony_ci    try {
3211cb0ef41Sopenharmony_ci      return await which('git').catch(er => {
3221cb0ef41Sopenharmony_ci        tracker.warn(er)
3231cb0ef41Sopenharmony_ci        throw new Error("Install git and ensure it's in your PATH.")
3241cb0ef41Sopenharmony_ci      })
3251cb0ef41Sopenharmony_ci    } finally {
3261cb0ef41Sopenharmony_ci      tracker.finish()
3271cb0ef41Sopenharmony_ci    }
3281cb0ef41Sopenharmony_ci  }
3291cb0ef41Sopenharmony_ci
3301cb0ef41Sopenharmony_ci  async verifyCachedFiles () {
3311cb0ef41Sopenharmony_ci    const tracker = log.newItem('verifyCachedFiles', 1)
3321cb0ef41Sopenharmony_ci    tracker.info('verifyCachedFiles', 'Verifying the npm cache')
3331cb0ef41Sopenharmony_ci    try {
3341cb0ef41Sopenharmony_ci      const stats = await cacache.verify(this.npm.flatOptions.cache)
3351cb0ef41Sopenharmony_ci      const { badContentCount, reclaimedCount, missingContent, reclaimedSize } = stats
3361cb0ef41Sopenharmony_ci      if (badContentCount || reclaimedCount || missingContent) {
3371cb0ef41Sopenharmony_ci        if (badContentCount) {
3381cb0ef41Sopenharmony_ci          tracker.warn('verifyCachedFiles', `Corrupted content removed: ${badContentCount}`)
3391cb0ef41Sopenharmony_ci        }
3401cb0ef41Sopenharmony_ci
3411cb0ef41Sopenharmony_ci        if (reclaimedCount) {
3421cb0ef41Sopenharmony_ci          tracker.warn(
3431cb0ef41Sopenharmony_ci            'verifyCachedFiles',
3441cb0ef41Sopenharmony_ci            `Content garbage-collected: ${reclaimedCount} (${reclaimedSize} bytes)`
3451cb0ef41Sopenharmony_ci          )
3461cb0ef41Sopenharmony_ci        }
3471cb0ef41Sopenharmony_ci
3481cb0ef41Sopenharmony_ci        if (missingContent) {
3491cb0ef41Sopenharmony_ci          tracker.warn('verifyCachedFiles', `Missing content: ${missingContent}`)
3501cb0ef41Sopenharmony_ci        }
3511cb0ef41Sopenharmony_ci
3521cb0ef41Sopenharmony_ci        tracker.warn('verifyCachedFiles', 'Cache issues have been fixed')
3531cb0ef41Sopenharmony_ci      }
3541cb0ef41Sopenharmony_ci      tracker.info(
3551cb0ef41Sopenharmony_ci        'verifyCachedFiles',
3561cb0ef41Sopenharmony_ci        `Verification complete. Stats: ${JSON.stringify(stats, null, 2)}`
3571cb0ef41Sopenharmony_ci      )
3581cb0ef41Sopenharmony_ci      return `verified ${stats.verifiedContent} tarballs`
3591cb0ef41Sopenharmony_ci    } finally {
3601cb0ef41Sopenharmony_ci      tracker.finish()
3611cb0ef41Sopenharmony_ci    }
3621cb0ef41Sopenharmony_ci  }
3631cb0ef41Sopenharmony_ci
3641cb0ef41Sopenharmony_ci  async checkNpmRegistry () {
3651cb0ef41Sopenharmony_ci    if (this.npm.flatOptions.registry !== defaults.registry) {
3661cb0ef41Sopenharmony_ci      throw `Try \`npm config set registry=${defaults.registry}\``
3671cb0ef41Sopenharmony_ci    } else {
3681cb0ef41Sopenharmony_ci      return `using default registry (${defaults.registry})`
3691cb0ef41Sopenharmony_ci    }
3701cb0ef41Sopenharmony_ci  }
3711cb0ef41Sopenharmony_ci
3721cb0ef41Sopenharmony_ci  output (row) {
3731cb0ef41Sopenharmony_ci    const t = new Table({
3741cb0ef41Sopenharmony_ci      chars: {
3751cb0ef41Sopenharmony_ci        top: '',
3761cb0ef41Sopenharmony_ci        'top-mid': '',
3771cb0ef41Sopenharmony_ci        'top-left': '',
3781cb0ef41Sopenharmony_ci        'top-right': '',
3791cb0ef41Sopenharmony_ci        bottom: '',
3801cb0ef41Sopenharmony_ci        'bottom-mid': '',
3811cb0ef41Sopenharmony_ci        'bottom-left': '',
3821cb0ef41Sopenharmony_ci        'bottom-right': '',
3831cb0ef41Sopenharmony_ci        left: '',
3841cb0ef41Sopenharmony_ci        'left-mid': '',
3851cb0ef41Sopenharmony_ci        mid: '',
3861cb0ef41Sopenharmony_ci        'mid-mid': '',
3871cb0ef41Sopenharmony_ci        right: '',
3881cb0ef41Sopenharmony_ci        'right-mid': '',
3891cb0ef41Sopenharmony_ci        middle: '  ',
3901cb0ef41Sopenharmony_ci      },
3911cb0ef41Sopenharmony_ci      style: {
3921cb0ef41Sopenharmony_ci        'padding-left': 0,
3931cb0ef41Sopenharmony_ci        'padding-right': 0,
3941cb0ef41Sopenharmony_ci        // setting border here is not necessary visually since we've already
3951cb0ef41Sopenharmony_ci        // zeroed out all the chars above, but without it cli-table3 will wrap
3961cb0ef41Sopenharmony_ci        // some of the separator spaces with ansi codes which show up in
3971cb0ef41Sopenharmony_ci        // snapshots.
3981cb0ef41Sopenharmony_ci        border: 0,
3991cb0ef41Sopenharmony_ci      },
4001cb0ef41Sopenharmony_ci      colWidths: [this.#checkWidth, 6],
4011cb0ef41Sopenharmony_ci    })
4021cb0ef41Sopenharmony_ci    t.push(row)
4031cb0ef41Sopenharmony_ci    this.npm.output(t.toString())
4041cb0ef41Sopenharmony_ci  }
4051cb0ef41Sopenharmony_ci
4061cb0ef41Sopenharmony_ci  actions (params) {
4071cb0ef41Sopenharmony_ci    return this.constructor.subcommands.filter(subcmd => {
4081cb0ef41Sopenharmony_ci      if (process.platform === 'win32' && subcmd.windows === false) {
4091cb0ef41Sopenharmony_ci        return false
4101cb0ef41Sopenharmony_ci      }
4111cb0ef41Sopenharmony_ci      if (params.length) {
4121cb0ef41Sopenharmony_ci        return params.some(param => subcmd.groups.includes(param))
4131cb0ef41Sopenharmony_ci      }
4141cb0ef41Sopenharmony_ci      return true
4151cb0ef41Sopenharmony_ci    })
4161cb0ef41Sopenharmony_ci  }
4171cb0ef41Sopenharmony_ci}
4181cb0ef41Sopenharmony_ci
4191cb0ef41Sopenharmony_cimodule.exports = Doctor
420