11cb0ef41Sopenharmony_ciconst { resolve, dirname, join } = require('path')
21cb0ef41Sopenharmony_ciconst Config = require('@npmcli/config')
31cb0ef41Sopenharmony_ciconst which = require('which')
41cb0ef41Sopenharmony_ciconst fs = require('fs/promises')
51cb0ef41Sopenharmony_ci
61cb0ef41Sopenharmony_ci// Patch the global fs module here at the app level
71cb0ef41Sopenharmony_cirequire('graceful-fs').gracefulify(require('fs'))
81cb0ef41Sopenharmony_ci
91cb0ef41Sopenharmony_ciconst { definitions, flatten, shorthands } = require('@npmcli/config/lib/definitions')
101cb0ef41Sopenharmony_ciconst usage = require('./utils/npm-usage.js')
111cb0ef41Sopenharmony_ciconst LogFile = require('./utils/log-file.js')
121cb0ef41Sopenharmony_ciconst Timers = require('./utils/timers.js')
131cb0ef41Sopenharmony_ciconst Display = require('./utils/display.js')
141cb0ef41Sopenharmony_ciconst log = require('./utils/log-shim')
151cb0ef41Sopenharmony_ciconst replaceInfo = require('./utils/replace-info.js')
161cb0ef41Sopenharmony_ciconst updateNotifier = require('./utils/update-notifier.js')
171cb0ef41Sopenharmony_ciconst pkg = require('../package.json')
181cb0ef41Sopenharmony_ciconst { deref } = require('./utils/cmd-list.js')
191cb0ef41Sopenharmony_ci
201cb0ef41Sopenharmony_ciclass Npm {
211cb0ef41Sopenharmony_ci  static get version () {
221cb0ef41Sopenharmony_ci    return pkg.version
231cb0ef41Sopenharmony_ci  }
241cb0ef41Sopenharmony_ci
251cb0ef41Sopenharmony_ci  static cmd (c) {
261cb0ef41Sopenharmony_ci    const command = deref(c)
271cb0ef41Sopenharmony_ci    if (!command) {
281cb0ef41Sopenharmony_ci      throw Object.assign(new Error(`Unknown command ${c}`), {
291cb0ef41Sopenharmony_ci        code: 'EUNKNOWNCOMMAND',
301cb0ef41Sopenharmony_ci      })
311cb0ef41Sopenharmony_ci    }
321cb0ef41Sopenharmony_ci    return require(`./commands/${command}.js`)
331cb0ef41Sopenharmony_ci  }
341cb0ef41Sopenharmony_ci
351cb0ef41Sopenharmony_ci  updateNotification = null
361cb0ef41Sopenharmony_ci  loadErr = null
371cb0ef41Sopenharmony_ci  argv = []
381cb0ef41Sopenharmony_ci
391cb0ef41Sopenharmony_ci  #command = null
401cb0ef41Sopenharmony_ci  #runId = new Date().toISOString().replace(/[.:]/g, '_')
411cb0ef41Sopenharmony_ci  #loadPromise = null
421cb0ef41Sopenharmony_ci  #title = 'npm'
431cb0ef41Sopenharmony_ci  #argvClean = []
441cb0ef41Sopenharmony_ci  #npmRoot = null
451cb0ef41Sopenharmony_ci  #warnedNonDashArg = false
461cb0ef41Sopenharmony_ci
471cb0ef41Sopenharmony_ci  #chalk = null
481cb0ef41Sopenharmony_ci  #logChalk = null
491cb0ef41Sopenharmony_ci  #noColorChalk = null
501cb0ef41Sopenharmony_ci
511cb0ef41Sopenharmony_ci  #outputBuffer = []
521cb0ef41Sopenharmony_ci  #logFile = new LogFile()
531cb0ef41Sopenharmony_ci  #display = new Display()
541cb0ef41Sopenharmony_ci  #timers = new Timers({
551cb0ef41Sopenharmony_ci    start: 'npm',
561cb0ef41Sopenharmony_ci    listener: (name, ms) => {
571cb0ef41Sopenharmony_ci      const args = ['timing', name, `Completed in ${ms}ms`]
581cb0ef41Sopenharmony_ci      this.#logFile.log(...args)
591cb0ef41Sopenharmony_ci      this.#display.log(...args)
601cb0ef41Sopenharmony_ci    },
611cb0ef41Sopenharmony_ci  })
621cb0ef41Sopenharmony_ci
631cb0ef41Sopenharmony_ci  // all these options are only used by tests in order to make testing more
641cb0ef41Sopenharmony_ci  // closely resemble real world usage. for now, npm has no programmatic API so
651cb0ef41Sopenharmony_ci  // it is ok to add stuff here, but we should not rely on it more than
661cb0ef41Sopenharmony_ci  // necessary. XXX: make these options not necessary by refactoring @npmcli/config
671cb0ef41Sopenharmony_ci  //   - npmRoot: this is where npm looks for docs files and the builtin config
681cb0ef41Sopenharmony_ci  //   - argv: this allows tests to extend argv in the same way the argv would
691cb0ef41Sopenharmony_ci  //     be passed in via a CLI arg.
701cb0ef41Sopenharmony_ci  //   - excludeNpmCwd: this is a hack to get @npmcli/config to stop walking up
711cb0ef41Sopenharmony_ci  //     dirs to set a local prefix when it encounters the `npmRoot`. this
721cb0ef41Sopenharmony_ci  //     allows tests created by tap inside this repo to not set the local
731cb0ef41Sopenharmony_ci  //     prefix to `npmRoot` since that is the first dir it would encounter when
741cb0ef41Sopenharmony_ci  //     doing implicit detection
751cb0ef41Sopenharmony_ci  constructor ({ npmRoot = dirname(__dirname), argv = [], excludeNpmCwd = false } = {}) {
761cb0ef41Sopenharmony_ci    this.#npmRoot = npmRoot
771cb0ef41Sopenharmony_ci    this.config = new Config({
781cb0ef41Sopenharmony_ci      npmPath: this.#npmRoot,
791cb0ef41Sopenharmony_ci      definitions,
801cb0ef41Sopenharmony_ci      flatten,
811cb0ef41Sopenharmony_ci      shorthands,
821cb0ef41Sopenharmony_ci      argv: [...process.argv, ...argv],
831cb0ef41Sopenharmony_ci      excludeNpmCwd,
841cb0ef41Sopenharmony_ci    })
851cb0ef41Sopenharmony_ci  }
861cb0ef41Sopenharmony_ci
871cb0ef41Sopenharmony_ci  get version () {
881cb0ef41Sopenharmony_ci    return this.constructor.version
891cb0ef41Sopenharmony_ci  }
901cb0ef41Sopenharmony_ci
911cb0ef41Sopenharmony_ci  setCmd (cmd) {
921cb0ef41Sopenharmony_ci    const Command = Npm.cmd(cmd)
931cb0ef41Sopenharmony_ci    const command = new Command(this)
941cb0ef41Sopenharmony_ci
951cb0ef41Sopenharmony_ci    // since 'test', 'start', 'stop', etc. commands re-enter this function
961cb0ef41Sopenharmony_ci    // to call the run-script command, we need to only set it one time.
971cb0ef41Sopenharmony_ci    if (!this.#command) {
981cb0ef41Sopenharmony_ci      this.#command = command
991cb0ef41Sopenharmony_ci      process.env.npm_command = this.command
1001cb0ef41Sopenharmony_ci    }
1011cb0ef41Sopenharmony_ci
1021cb0ef41Sopenharmony_ci    return command
1031cb0ef41Sopenharmony_ci  }
1041cb0ef41Sopenharmony_ci
1051cb0ef41Sopenharmony_ci  // Call an npm command
1061cb0ef41Sopenharmony_ci  // TODO: tests are currently the only time the second
1071cb0ef41Sopenharmony_ci  // parameter of args is used. When called via `lib/cli.js` the config is
1081cb0ef41Sopenharmony_ci  // loaded and this.argv is set to the remaining command line args. We should
1091cb0ef41Sopenharmony_ci  // consider testing the CLI the same way it is used and not allow args to be
1101cb0ef41Sopenharmony_ci  // passed in directly.
1111cb0ef41Sopenharmony_ci  async exec (cmd, args = this.argv) {
1121cb0ef41Sopenharmony_ci    const command = this.setCmd(cmd)
1131cb0ef41Sopenharmony_ci
1141cb0ef41Sopenharmony_ci    const timeEnd = this.time(`command:${cmd}`)
1151cb0ef41Sopenharmony_ci
1161cb0ef41Sopenharmony_ci    // this is async but we dont await it, since its ok if it doesnt
1171cb0ef41Sopenharmony_ci    // finish before the command finishes running. it uses command and argv
1181cb0ef41Sopenharmony_ci    // so it must be initiated here, after the command name is set
1191cb0ef41Sopenharmony_ci    // eslint-disable-next-line promise/catch-or-return
1201cb0ef41Sopenharmony_ci    updateNotifier(this).then((msg) => (this.updateNotification = msg))
1211cb0ef41Sopenharmony_ci
1221cb0ef41Sopenharmony_ci    // Options are prefixed by a hyphen-minus (-, \u2d).
1231cb0ef41Sopenharmony_ci    // Other dash-type chars look similar but are invalid.
1241cb0ef41Sopenharmony_ci    if (!this.#warnedNonDashArg) {
1251cb0ef41Sopenharmony_ci      const nonDashArgs = args.filter(a => /^[\u2010-\u2015\u2212\uFE58\uFE63\uFF0D]/.test(a))
1261cb0ef41Sopenharmony_ci      if (nonDashArgs.length) {
1271cb0ef41Sopenharmony_ci        this.#warnedNonDashArg = true
1281cb0ef41Sopenharmony_ci        log.error(
1291cb0ef41Sopenharmony_ci          'arg',
1301cb0ef41Sopenharmony_ci          'Argument starts with non-ascii dash, this is probably invalid:',
1311cb0ef41Sopenharmony_ci          nonDashArgs.join(', ')
1321cb0ef41Sopenharmony_ci        )
1331cb0ef41Sopenharmony_ci      }
1341cb0ef41Sopenharmony_ci    }
1351cb0ef41Sopenharmony_ci
1361cb0ef41Sopenharmony_ci    return command.cmdExec(args).finally(timeEnd)
1371cb0ef41Sopenharmony_ci  }
1381cb0ef41Sopenharmony_ci
1391cb0ef41Sopenharmony_ci  async load () {
1401cb0ef41Sopenharmony_ci    if (!this.#loadPromise) {
1411cb0ef41Sopenharmony_ci      this.#loadPromise = this.time('npm:load', () => this.#load().catch((er) => {
1421cb0ef41Sopenharmony_ci        this.loadErr = er
1431cb0ef41Sopenharmony_ci        throw er
1441cb0ef41Sopenharmony_ci      }))
1451cb0ef41Sopenharmony_ci    }
1461cb0ef41Sopenharmony_ci    return this.#loadPromise
1471cb0ef41Sopenharmony_ci  }
1481cb0ef41Sopenharmony_ci
1491cb0ef41Sopenharmony_ci  get loaded () {
1501cb0ef41Sopenharmony_ci    return this.config.loaded
1511cb0ef41Sopenharmony_ci  }
1521cb0ef41Sopenharmony_ci
1531cb0ef41Sopenharmony_ci  // This gets called at the end of the exit handler and
1541cb0ef41Sopenharmony_ci  // during any tests to cleanup all of our listeners
1551cb0ef41Sopenharmony_ci  // Everything in here should be synchronous
1561cb0ef41Sopenharmony_ci  unload () {
1571cb0ef41Sopenharmony_ci    this.#timers.off()
1581cb0ef41Sopenharmony_ci    this.#display.off()
1591cb0ef41Sopenharmony_ci    this.#logFile.off()
1601cb0ef41Sopenharmony_ci  }
1611cb0ef41Sopenharmony_ci
1621cb0ef41Sopenharmony_ci  time (name, fn) {
1631cb0ef41Sopenharmony_ci    return this.#timers.time(name, fn)
1641cb0ef41Sopenharmony_ci  }
1651cb0ef41Sopenharmony_ci
1661cb0ef41Sopenharmony_ci  writeTimingFile () {
1671cb0ef41Sopenharmony_ci    this.#timers.writeFile({
1681cb0ef41Sopenharmony_ci      id: this.#runId,
1691cb0ef41Sopenharmony_ci      command: this.#argvClean,
1701cb0ef41Sopenharmony_ci      logfiles: this.logFiles,
1711cb0ef41Sopenharmony_ci      version: this.version,
1721cb0ef41Sopenharmony_ci    })
1731cb0ef41Sopenharmony_ci  }
1741cb0ef41Sopenharmony_ci
1751cb0ef41Sopenharmony_ci  get title () {
1761cb0ef41Sopenharmony_ci    return this.#title
1771cb0ef41Sopenharmony_ci  }
1781cb0ef41Sopenharmony_ci
1791cb0ef41Sopenharmony_ci  set title (t) {
1801cb0ef41Sopenharmony_ci    process.title = t
1811cb0ef41Sopenharmony_ci    this.#title = t
1821cb0ef41Sopenharmony_ci  }
1831cb0ef41Sopenharmony_ci
1841cb0ef41Sopenharmony_ci  async #load () {
1851cb0ef41Sopenharmony_ci    await this.time('npm:load:whichnode', async () => {
1861cb0ef41Sopenharmony_ci      // TODO should we throw here?
1871cb0ef41Sopenharmony_ci      const node = await which(process.argv[0]).catch(() => {})
1881cb0ef41Sopenharmony_ci      if (node && node.toUpperCase() !== process.execPath.toUpperCase()) {
1891cb0ef41Sopenharmony_ci        log.verbose('node symlink', node)
1901cb0ef41Sopenharmony_ci        process.execPath = node
1911cb0ef41Sopenharmony_ci        this.config.execPath = node
1921cb0ef41Sopenharmony_ci      }
1931cb0ef41Sopenharmony_ci    })
1941cb0ef41Sopenharmony_ci
1951cb0ef41Sopenharmony_ci    await this.time('npm:load:configload', () => this.config.load())
1961cb0ef41Sopenharmony_ci
1971cb0ef41Sopenharmony_ci    // get createSupportsColor from chalk directly if this lands
1981cb0ef41Sopenharmony_ci    // https://github.com/chalk/chalk/pull/600
1991cb0ef41Sopenharmony_ci    const [{ Chalk }, { createSupportsColor }] = await Promise.all([
2001cb0ef41Sopenharmony_ci      import('chalk'),
2011cb0ef41Sopenharmony_ci      import('supports-color'),
2021cb0ef41Sopenharmony_ci    ])
2031cb0ef41Sopenharmony_ci    this.#noColorChalk = new Chalk({ level: 0 })
2041cb0ef41Sopenharmony_ci    // we get the chalk level based on a null stream meaning chalk will only use
2051cb0ef41Sopenharmony_ci    // what it knows about the environment to get color support since we already
2061cb0ef41Sopenharmony_ci    // determined in our definitions that we want to show colors.
2071cb0ef41Sopenharmony_ci    const level = Math.max(createSupportsColor(null).level, 1)
2081cb0ef41Sopenharmony_ci    this.#chalk = this.color ? new Chalk({ level }) : this.#noColorChalk
2091cb0ef41Sopenharmony_ci    this.#logChalk = this.logColor ? new Chalk({ level }) : this.#noColorChalk
2101cb0ef41Sopenharmony_ci
2111cb0ef41Sopenharmony_ci    // mkdir this separately since the logs dir can be set to
2121cb0ef41Sopenharmony_ci    // a different location. if this fails, then we don't have
2131cb0ef41Sopenharmony_ci    // a cache dir, but we don't want to fail immediately since
2141cb0ef41Sopenharmony_ci    // the command might not need a cache dir (like `npm --version`)
2151cb0ef41Sopenharmony_ci    await this.time('npm:load:mkdirpcache', () =>
2161cb0ef41Sopenharmony_ci      fs.mkdir(this.cache, { recursive: true })
2171cb0ef41Sopenharmony_ci        .catch((e) => log.verbose('cache', `could not create cache: ${e}`)))
2181cb0ef41Sopenharmony_ci
2191cb0ef41Sopenharmony_ci    // it's ok if this fails. user might have specified an invalid dir
2201cb0ef41Sopenharmony_ci    // which we will tell them about at the end
2211cb0ef41Sopenharmony_ci    if (this.config.get('logs-max') > 0) {
2221cb0ef41Sopenharmony_ci      await this.time('npm:load:mkdirplogs', () =>
2231cb0ef41Sopenharmony_ci        fs.mkdir(this.logsDir, { recursive: true })
2241cb0ef41Sopenharmony_ci          .catch((e) => log.verbose('logfile', `could not create logs-dir: ${e}`)))
2251cb0ef41Sopenharmony_ci    }
2261cb0ef41Sopenharmony_ci
2271cb0ef41Sopenharmony_ci    // note: this MUST be shorter than the actual argv length, because it
2281cb0ef41Sopenharmony_ci    // uses the same memory, so node will truncate it if it's too long.
2291cb0ef41Sopenharmony_ci    this.time('npm:load:setTitle', () => {
2301cb0ef41Sopenharmony_ci      const { parsedArgv: { cooked, remain } } = this.config
2311cb0ef41Sopenharmony_ci      this.argv = remain
2321cb0ef41Sopenharmony_ci      // Secrets are mostly in configs, so title is set using only the positional args
2331cb0ef41Sopenharmony_ci      // to keep those from being leaked.
2341cb0ef41Sopenharmony_ci      this.title = ['npm'].concat(replaceInfo(remain)).join(' ').trim()
2351cb0ef41Sopenharmony_ci      // The cooked argv is also logged separately for debugging purposes. It is
2361cb0ef41Sopenharmony_ci      // cleaned as a best effort by replacing known secrets like basic auth
2371cb0ef41Sopenharmony_ci      // password and strings that look like npm tokens. XXX: for this to be
2381cb0ef41Sopenharmony_ci      // safer the config should create a sanitized version of the argv as it
2391cb0ef41Sopenharmony_ci      // has the full context of what each option contains.
2401cb0ef41Sopenharmony_ci      this.#argvClean = replaceInfo(cooked)
2411cb0ef41Sopenharmony_ci      log.verbose('title', this.title)
2421cb0ef41Sopenharmony_ci      log.verbose('argv', this.#argvClean.map(JSON.stringify).join(' '))
2431cb0ef41Sopenharmony_ci    })
2441cb0ef41Sopenharmony_ci
2451cb0ef41Sopenharmony_ci    this.time('npm:load:display', () => {
2461cb0ef41Sopenharmony_ci      this.#display.load({
2471cb0ef41Sopenharmony_ci        // Use logColor since that is based on stderr
2481cb0ef41Sopenharmony_ci        color: this.logColor,
2491cb0ef41Sopenharmony_ci        chalk: this.logChalk,
2501cb0ef41Sopenharmony_ci        progress: this.flatOptions.progress,
2511cb0ef41Sopenharmony_ci        silent: this.silent,
2521cb0ef41Sopenharmony_ci        timing: this.config.get('timing'),
2531cb0ef41Sopenharmony_ci        loglevel: this.config.get('loglevel'),
2541cb0ef41Sopenharmony_ci        unicode: this.config.get('unicode'),
2551cb0ef41Sopenharmony_ci        heading: this.config.get('heading'),
2561cb0ef41Sopenharmony_ci      })
2571cb0ef41Sopenharmony_ci      process.env.COLOR = this.color ? '1' : '0'
2581cb0ef41Sopenharmony_ci    })
2591cb0ef41Sopenharmony_ci
2601cb0ef41Sopenharmony_ci    this.time('npm:load:logFile', () => {
2611cb0ef41Sopenharmony_ci      this.#logFile.load({
2621cb0ef41Sopenharmony_ci        path: this.logPath,
2631cb0ef41Sopenharmony_ci        logsMax: this.config.get('logs-max'),
2641cb0ef41Sopenharmony_ci      })
2651cb0ef41Sopenharmony_ci      log.verbose('logfile', this.#logFile.files[0] || 'no logfile created')
2661cb0ef41Sopenharmony_ci    })
2671cb0ef41Sopenharmony_ci
2681cb0ef41Sopenharmony_ci    this.time('npm:load:timers', () =>
2691cb0ef41Sopenharmony_ci      this.#timers.load({
2701cb0ef41Sopenharmony_ci        path: this.config.get('timing') ? this.logPath : null,
2711cb0ef41Sopenharmony_ci      })
2721cb0ef41Sopenharmony_ci    )
2731cb0ef41Sopenharmony_ci
2741cb0ef41Sopenharmony_ci    this.time('npm:load:configScope', () => {
2751cb0ef41Sopenharmony_ci      const configScope = this.config.get('scope')
2761cb0ef41Sopenharmony_ci      if (configScope && !/^@/.test(configScope)) {
2771cb0ef41Sopenharmony_ci        this.config.set('scope', `@${configScope}`, this.config.find('scope'))
2781cb0ef41Sopenharmony_ci      }
2791cb0ef41Sopenharmony_ci    })
2801cb0ef41Sopenharmony_ci
2811cb0ef41Sopenharmony_ci    if (this.config.get('force')) {
2821cb0ef41Sopenharmony_ci      log.warn('using --force', 'Recommended protections disabled.')
2831cb0ef41Sopenharmony_ci    }
2841cb0ef41Sopenharmony_ci  }
2851cb0ef41Sopenharmony_ci
2861cb0ef41Sopenharmony_ci  get isShellout () {
2871cb0ef41Sopenharmony_ci    return this.#command?.constructor?.isShellout
2881cb0ef41Sopenharmony_ci  }
2891cb0ef41Sopenharmony_ci
2901cb0ef41Sopenharmony_ci  get command () {
2911cb0ef41Sopenharmony_ci    return this.#command?.name
2921cb0ef41Sopenharmony_ci  }
2931cb0ef41Sopenharmony_ci
2941cb0ef41Sopenharmony_ci  get flatOptions () {
2951cb0ef41Sopenharmony_ci    const { flat } = this.config
2961cb0ef41Sopenharmony_ci    flat.nodeVersion = process.version
2971cb0ef41Sopenharmony_ci    flat.npmVersion = pkg.version
2981cb0ef41Sopenharmony_ci    if (this.command) {
2991cb0ef41Sopenharmony_ci      flat.npmCommand = this.command
3001cb0ef41Sopenharmony_ci    }
3011cb0ef41Sopenharmony_ci    return flat
3021cb0ef41Sopenharmony_ci  }
3031cb0ef41Sopenharmony_ci
3041cb0ef41Sopenharmony_ci  // color and logColor are a special derived values that takes into
3051cb0ef41Sopenharmony_ci  // consideration not only the config, but whether or not we are operating
3061cb0ef41Sopenharmony_ci  // in a tty with the associated output (stdout/stderr)
3071cb0ef41Sopenharmony_ci  get color () {
3081cb0ef41Sopenharmony_ci    return this.flatOptions.color
3091cb0ef41Sopenharmony_ci  }
3101cb0ef41Sopenharmony_ci
3111cb0ef41Sopenharmony_ci  get logColor () {
3121cb0ef41Sopenharmony_ci    return this.flatOptions.logColor
3131cb0ef41Sopenharmony_ci  }
3141cb0ef41Sopenharmony_ci
3151cb0ef41Sopenharmony_ci  get noColorChalk () {
3161cb0ef41Sopenharmony_ci    return this.#noColorChalk
3171cb0ef41Sopenharmony_ci  }
3181cb0ef41Sopenharmony_ci
3191cb0ef41Sopenharmony_ci  get chalk () {
3201cb0ef41Sopenharmony_ci    return this.#chalk
3211cb0ef41Sopenharmony_ci  }
3221cb0ef41Sopenharmony_ci
3231cb0ef41Sopenharmony_ci  get logChalk () {
3241cb0ef41Sopenharmony_ci    return this.#logChalk
3251cb0ef41Sopenharmony_ci  }
3261cb0ef41Sopenharmony_ci
3271cb0ef41Sopenharmony_ci  get global () {
3281cb0ef41Sopenharmony_ci    return this.config.get('global') || this.config.get('location') === 'global'
3291cb0ef41Sopenharmony_ci  }
3301cb0ef41Sopenharmony_ci
3311cb0ef41Sopenharmony_ci  get silent () {
3321cb0ef41Sopenharmony_ci    return this.flatOptions.silent
3331cb0ef41Sopenharmony_ci  }
3341cb0ef41Sopenharmony_ci
3351cb0ef41Sopenharmony_ci  get lockfileVersion () {
3361cb0ef41Sopenharmony_ci    return 2
3371cb0ef41Sopenharmony_ci  }
3381cb0ef41Sopenharmony_ci
3391cb0ef41Sopenharmony_ci  get unfinishedTimers () {
3401cb0ef41Sopenharmony_ci    return this.#timers.unfinished
3411cb0ef41Sopenharmony_ci  }
3421cb0ef41Sopenharmony_ci
3431cb0ef41Sopenharmony_ci  get finishedTimers () {
3441cb0ef41Sopenharmony_ci    return this.#timers.finished
3451cb0ef41Sopenharmony_ci  }
3461cb0ef41Sopenharmony_ci
3471cb0ef41Sopenharmony_ci  get started () {
3481cb0ef41Sopenharmony_ci    return this.#timers.started
3491cb0ef41Sopenharmony_ci  }
3501cb0ef41Sopenharmony_ci
3511cb0ef41Sopenharmony_ci  get logFiles () {
3521cb0ef41Sopenharmony_ci    return this.#logFile.files
3531cb0ef41Sopenharmony_ci  }
3541cb0ef41Sopenharmony_ci
3551cb0ef41Sopenharmony_ci  get logsDir () {
3561cb0ef41Sopenharmony_ci    return this.config.get('logs-dir') || join(this.cache, '_logs')
3571cb0ef41Sopenharmony_ci  }
3581cb0ef41Sopenharmony_ci
3591cb0ef41Sopenharmony_ci  get logPath () {
3601cb0ef41Sopenharmony_ci    return resolve(this.logsDir, `${this.#runId}-`)
3611cb0ef41Sopenharmony_ci  }
3621cb0ef41Sopenharmony_ci
3631cb0ef41Sopenharmony_ci  get timingFile () {
3641cb0ef41Sopenharmony_ci    return this.#timers.file
3651cb0ef41Sopenharmony_ci  }
3661cb0ef41Sopenharmony_ci
3671cb0ef41Sopenharmony_ci  get npmRoot () {
3681cb0ef41Sopenharmony_ci    return this.#npmRoot
3691cb0ef41Sopenharmony_ci  }
3701cb0ef41Sopenharmony_ci
3711cb0ef41Sopenharmony_ci  get cache () {
3721cb0ef41Sopenharmony_ci    return this.config.get('cache')
3731cb0ef41Sopenharmony_ci  }
3741cb0ef41Sopenharmony_ci
3751cb0ef41Sopenharmony_ci  set cache (r) {
3761cb0ef41Sopenharmony_ci    this.config.set('cache', r)
3771cb0ef41Sopenharmony_ci  }
3781cb0ef41Sopenharmony_ci
3791cb0ef41Sopenharmony_ci  get globalPrefix () {
3801cb0ef41Sopenharmony_ci    return this.config.globalPrefix
3811cb0ef41Sopenharmony_ci  }
3821cb0ef41Sopenharmony_ci
3831cb0ef41Sopenharmony_ci  set globalPrefix (r) {
3841cb0ef41Sopenharmony_ci    this.config.globalPrefix = r
3851cb0ef41Sopenharmony_ci  }
3861cb0ef41Sopenharmony_ci
3871cb0ef41Sopenharmony_ci  get localPrefix () {
3881cb0ef41Sopenharmony_ci    return this.config.localPrefix
3891cb0ef41Sopenharmony_ci  }
3901cb0ef41Sopenharmony_ci
3911cb0ef41Sopenharmony_ci  set localPrefix (r) {
3921cb0ef41Sopenharmony_ci    this.config.localPrefix = r
3931cb0ef41Sopenharmony_ci  }
3941cb0ef41Sopenharmony_ci
3951cb0ef41Sopenharmony_ci  get localPackage () {
3961cb0ef41Sopenharmony_ci    return this.config.localPackage
3971cb0ef41Sopenharmony_ci  }
3981cb0ef41Sopenharmony_ci
3991cb0ef41Sopenharmony_ci  get globalDir () {
4001cb0ef41Sopenharmony_ci    return process.platform !== 'win32'
4011cb0ef41Sopenharmony_ci      ? resolve(this.globalPrefix, 'lib', 'node_modules')
4021cb0ef41Sopenharmony_ci      : resolve(this.globalPrefix, 'node_modules')
4031cb0ef41Sopenharmony_ci  }
4041cb0ef41Sopenharmony_ci
4051cb0ef41Sopenharmony_ci  get localDir () {
4061cb0ef41Sopenharmony_ci    return resolve(this.localPrefix, 'node_modules')
4071cb0ef41Sopenharmony_ci  }
4081cb0ef41Sopenharmony_ci
4091cb0ef41Sopenharmony_ci  get dir () {
4101cb0ef41Sopenharmony_ci    return this.global ? this.globalDir : this.localDir
4111cb0ef41Sopenharmony_ci  }
4121cb0ef41Sopenharmony_ci
4131cb0ef41Sopenharmony_ci  get globalBin () {
4141cb0ef41Sopenharmony_ci    const b = this.globalPrefix
4151cb0ef41Sopenharmony_ci    return process.platform !== 'win32' ? resolve(b, 'bin') : b
4161cb0ef41Sopenharmony_ci  }
4171cb0ef41Sopenharmony_ci
4181cb0ef41Sopenharmony_ci  get localBin () {
4191cb0ef41Sopenharmony_ci    return resolve(this.dir, '.bin')
4201cb0ef41Sopenharmony_ci  }
4211cb0ef41Sopenharmony_ci
4221cb0ef41Sopenharmony_ci  get bin () {
4231cb0ef41Sopenharmony_ci    return this.global ? this.globalBin : this.localBin
4241cb0ef41Sopenharmony_ci  }
4251cb0ef41Sopenharmony_ci
4261cb0ef41Sopenharmony_ci  get prefix () {
4271cb0ef41Sopenharmony_ci    return this.global ? this.globalPrefix : this.localPrefix
4281cb0ef41Sopenharmony_ci  }
4291cb0ef41Sopenharmony_ci
4301cb0ef41Sopenharmony_ci  set prefix (r) {
4311cb0ef41Sopenharmony_ci    const k = this.global ? 'globalPrefix' : 'localPrefix'
4321cb0ef41Sopenharmony_ci    this[k] = r
4331cb0ef41Sopenharmony_ci  }
4341cb0ef41Sopenharmony_ci
4351cb0ef41Sopenharmony_ci  get usage () {
4361cb0ef41Sopenharmony_ci    return usage(this)
4371cb0ef41Sopenharmony_ci  }
4381cb0ef41Sopenharmony_ci
4391cb0ef41Sopenharmony_ci  // output to stdout in a progress bar compatible way
4401cb0ef41Sopenharmony_ci  output (...msg) {
4411cb0ef41Sopenharmony_ci    log.clearProgress()
4421cb0ef41Sopenharmony_ci    // eslint-disable-next-line no-console
4431cb0ef41Sopenharmony_ci    console.log(...msg.map(Display.clean))
4441cb0ef41Sopenharmony_ci    log.showProgress()
4451cb0ef41Sopenharmony_ci  }
4461cb0ef41Sopenharmony_ci
4471cb0ef41Sopenharmony_ci  outputBuffer (item) {
4481cb0ef41Sopenharmony_ci    this.#outputBuffer.push(item)
4491cb0ef41Sopenharmony_ci  }
4501cb0ef41Sopenharmony_ci
4511cb0ef41Sopenharmony_ci  flushOutput (jsonError) {
4521cb0ef41Sopenharmony_ci    if (!jsonError && !this.#outputBuffer.length) {
4531cb0ef41Sopenharmony_ci      return
4541cb0ef41Sopenharmony_ci    }
4551cb0ef41Sopenharmony_ci
4561cb0ef41Sopenharmony_ci    if (this.config.get('json')) {
4571cb0ef41Sopenharmony_ci      const jsonOutput = this.#outputBuffer.reduce((acc, item) => {
4581cb0ef41Sopenharmony_ci        if (typeof item === 'string') {
4591cb0ef41Sopenharmony_ci          // try to parse it as json in case its a string
4601cb0ef41Sopenharmony_ci          try {
4611cb0ef41Sopenharmony_ci            item = JSON.parse(item)
4621cb0ef41Sopenharmony_ci          } catch {
4631cb0ef41Sopenharmony_ci            return acc
4641cb0ef41Sopenharmony_ci          }
4651cb0ef41Sopenharmony_ci        }
4661cb0ef41Sopenharmony_ci        return { ...acc, ...item }
4671cb0ef41Sopenharmony_ci      }, {})
4681cb0ef41Sopenharmony_ci      this.output(JSON.stringify({ ...jsonOutput, ...jsonError }, null, 2))
4691cb0ef41Sopenharmony_ci    } else {
4701cb0ef41Sopenharmony_ci      for (const item of this.#outputBuffer) {
4711cb0ef41Sopenharmony_ci        this.output(item)
4721cb0ef41Sopenharmony_ci      }
4731cb0ef41Sopenharmony_ci    }
4741cb0ef41Sopenharmony_ci
4751cb0ef41Sopenharmony_ci    this.#outputBuffer.length = 0
4761cb0ef41Sopenharmony_ci  }
4771cb0ef41Sopenharmony_ci
4781cb0ef41Sopenharmony_ci  outputError (...msg) {
4791cb0ef41Sopenharmony_ci    log.clearProgress()
4801cb0ef41Sopenharmony_ci    // eslint-disable-next-line no-console
4811cb0ef41Sopenharmony_ci    console.error(...msg.map(Display.clean))
4821cb0ef41Sopenharmony_ci    log.showProgress()
4831cb0ef41Sopenharmony_ci  }
4841cb0ef41Sopenharmony_ci}
4851cb0ef41Sopenharmony_cimodule.exports = Npm
486