diff --git a/deps/npm/docs/content/commands/npm-adduser.md b/deps/npm/docs/content/commands/npm-adduser.md index 9f7caeb9cdc90c..700aecb2255b27 100644 --- a/deps/npm/docs/content/commands/npm-adduser.md +++ b/deps/npm/docs/content/commands/npm-adduser.md @@ -93,13 +93,12 @@ npm init --scope=@foo --yes #### `auth-type` * Default: "legacy" -* Type: "legacy", "webauthn", "sso", "saml", or "oauth" -* DEPRECATED: The SSO/SAML/OAuth methods are deprecated and will be removed in - a future version of npm in favor of web-based login. +* Type: "legacy", "web", "sso", "saml", "oauth", or "webauthn" -What authentication strategy to use with `adduser`/`login`. +NOTE: auth-type values "sso", "saml", "oauth", and "webauthn" will be +removed in a future version. -Pass `webauthn` to use a web-based login. +What authentication strategy to use with `login`. diff --git a/deps/npm/docs/content/commands/npm-audit.md b/deps/npm/docs/content/commands/npm-audit.md index 9d09a4107fdc7d..206a33f53a688c 100644 --- a/deps/npm/docs/content/commands/npm-audit.md +++ b/deps/npm/docs/content/commands/npm-audit.md @@ -11,7 +11,7 @@ description: Run a security audit ```bash -npm audit [fix] +npm audit [fix|signatures] ``` @@ -41,6 +41,17 @@ vulnerability is found. It may be useful in CI environments to include the will cause the command to fail. This option does not filter the report output, it simply changes the command's failure threshold. +### Audit Signatures + +This command can also audit the integrity values of the packages in your +tree against any signatures present in the registry they were downloaded +from. npm will attempt to download the keys from `/-/npm/v1/keys` on +each the registry used to download any given package. It will then +check the `dist.signatures` object in the package itself, and verify the +`sig` present there using the `keyid` there, matching it with a key +returned from the registry. The command for this is `npm audit +signatures` + ### Audit Endpoints There are two audit endpoints that npm may use to fetch vulnerability diff --git a/deps/npm/docs/content/commands/npm.md b/deps/npm/docs/content/commands/npm.md index aaf295dfde230d..18a68d03cd44ff 100644 --- a/deps/npm/docs/content/commands/npm.md +++ b/deps/npm/docs/content/commands/npm.md @@ -102,7 +102,7 @@ following help topics: done via [`npm install`](/commands/npm-install) * adduser: Create an account or log in. When you do this, npm will store - credentials in the user config file config file. + credentials in the user config file. * publish: Use the [`npm publish`](/commands/npm-publish) command to upload your code to the registry. diff --git a/deps/npm/docs/content/configuring-npm/package-json.md b/deps/npm/docs/content/configuring-npm/package-json.md index 64081350af271f..826f10ce825317 100644 --- a/deps/npm/docs/content/configuring-npm/package-json.md +++ b/deps/npm/docs/content/configuring-npm/package-json.md @@ -124,7 +124,7 @@ IDs](https://spdx.org/licenses/). Ideally you should pick one that is If your package is licensed under multiple common licenses, use an [SPDX license expression syntax version 2.0 -string](https://www.npmjs.com/package/spdx), like this: +string](https://spdx.dev/specifications/), like this: ```json { diff --git a/deps/npm/docs/content/using-npm/config.md b/deps/npm/docs/content/using-npm/config.md index 3fb431402669f5..ffbf9be055719b 100644 --- a/deps/npm/docs/content/using-npm/config.md +++ b/deps/npm/docs/content/using-npm/config.md @@ -215,6 +215,19 @@ exit code. +#### `auth-type` + +* Default: "legacy" +* Type: "legacy", "web", "sso", "saml", "oauth", or "webauthn" + +NOTE: auth-type values "sso", "saml", "oauth", and "webauthn" will be +removed in a future version. + +What authentication strategy to use with `login`. + + + + #### `before` * Default: null @@ -1905,20 +1918,6 @@ When set to `dev` or `development`, this is an alias for `--include=dev`. -#### `auth-type` - -* Default: "legacy" -* Type: "legacy", "webauthn", "sso", "saml", or "oauth" -* DEPRECATED: The SSO/SAML/OAuth methods are deprecated and will be removed in - a future version of npm in favor of web-based login. - -What authentication strategy to use with `adduser`/`login`. - -Pass `webauthn` to use a web-based login. - - - - #### `cache-max` * Default: Infinity diff --git a/deps/npm/docs/content/using-npm/scripts.md b/deps/npm/docs/content/using-npm/scripts.md index 381c0a71812f09..2e0a5d1a95e14d 100644 --- a/deps/npm/docs/content/using-npm/scripts.md +++ b/deps/npm/docs/content/using-npm/scripts.md @@ -39,7 +39,7 @@ There are some special life cycle scripts that happen only in certain situations. These scripts happen in addition to the `pre`, `post`, and `` scripts. -* `prepare`, `prepublish`, `prepublishOnly`, `prepack`, `postpack` +* `prepare`, `prepublish`, `prepublishOnly`, `prepack`, `postpack`, `dependencies` **prepare** (since `npm@4.0.0`) * Runs any time before the package is packed, i.e. during `npm publish` @@ -71,6 +71,10 @@ situations. These scripts happen in addition to the `pre`, `post`, **postpack** * Runs AFTER the tarball has been generated but before it is moved to its final destination (if at all, publish does not save the tarball locally) +**dependencies** +* Runs AFTER any operations that modify the `node_modules` directory IF changes occurred. +* Does NOT run in global mode + #### Prepare and Prepublish **Deprecation Note: prepublish** @@ -96,6 +100,10 @@ The advantage of doing these things at `prepublish` time is that they can be don * You don't need to rely on your users having `curl` or `wget` or other system tools on the target machines. +#### Dependencies + +The `dependencies` script is run any time an `npm` command causes changes to the `node_modules` directory. It is run AFTER the changes have been applied and the `package.json` and `package-lock.json` files have been updated. + ### Life Cycle Operation Order #### [`npm cache add`](/commands/npm-cache) diff --git a/deps/npm/docs/content/using-npm/workspaces.md b/deps/npm/docs/content/using-npm/workspaces.md index 26d6a5d7551dcf..82491cd74af823 100644 --- a/deps/npm/docs/content/using-npm/workspaces.md +++ b/deps/npm/docs/content/using-npm/workspaces.md @@ -57,7 +57,7 @@ structure of files and folders: ``` . +-- node_modules -| `-- packages/a -> ../packages/a +| `-- a -> ../packages/a +-- package-lock.json +-- package.json `-- packages @@ -112,15 +112,15 @@ respect the provided `workspace` configuration. Given the [specifities of how Node.js handles module resolution](https://nodejs.org/dist/latest-v14.x/docs/api/modules.html#modules_all_together) it's possible to consume any defined workspace by its declared `package.json` `name`. Continuing from the example defined -above, let's also create a Node.js script that will require the `workspace-a` +above, let's also create a Node.js script that will require the workspace `a` example module, e.g: ``` -// ./workspace-a/index.js +// ./packages/a/index.js module.exports = 'a' // ./lib/index.js -const moduleA = require('workspace-a') +const moduleA = require('a') console.log(moduleA) // -> a ``` diff --git a/deps/npm/docs/output/commands/npm-adduser.html b/deps/npm/docs/output/commands/npm-adduser.html index 3b8eaa3159dda3..d084a5e1f2deaf 100644 --- a/deps/npm/docs/output/commands/npm-adduser.html +++ b/deps/npm/docs/output/commands/npm-adduser.html @@ -207,12 +207,11 @@

scope

auth-type

  • Default: "legacy"
  • -
  • Type: "legacy", "webauthn", "sso", "saml", or "oauth"
  • -
  • DEPRECATED: The SSO/SAML/OAuth methods are deprecated and will be removed in -a future version of npm in favor of web-based login.
  • +
  • Type: "legacy", "web", "sso", "saml", "oauth", or "webauthn"
-

What authentication strategy to use with adduser/login.

-

Pass webauthn to use a web-based login.

+

NOTE: auth-type values "sso", "saml", "oauth", and "webauthn" will be +removed in a future version.

+

What authentication strategy to use with login.

diff --git a/deps/npm/docs/output/commands/npm-audit.html b/deps/npm/docs/output/commands/npm-audit.html index af627aff7701a1..ee315493a27d03 100644 --- a/deps/npm/docs/output/commands/npm-audit.html +++ b/deps/npm/docs/output/commands/npm-audit.html @@ -142,14 +142,14 @@

npm-audit

Table of contents

- +

Synopsis

-
npm audit [fix]
+
npm audit [fix|signatures]
 
@@ -170,6 +170,14 @@

Description

--audit-level parameter to specify the minimum vulnerability level that will cause the command to fail. This option does not filter the report output, it simply changes the command's failure threshold.

+

Audit Signatures

+

This command can also audit the integrity values of the packages in your +tree against any signatures present in the registry they were downloaded +from. npm will attempt to download the keys from /-/npm/v1/keys on +each the registry used to download any given package. It will then +check the dist.signatures object in the package itself, and verify the +sig present there using the keyid there, matching it with a key +returned from the registry. The command for this is npm audit signatures

Audit Endpoints

There are two audit endpoints that npm may use to fetch vulnerability information: the Bulk Advisory endpoint and the Quick Audit endpoint.

diff --git a/deps/npm/docs/output/commands/npm-ls.html b/deps/npm/docs/output/commands/npm-ls.html index 07deb2d490fc1f..4483459262c500 100644 --- a/deps/npm/docs/output/commands/npm-ls.html +++ b/deps/npm/docs/output/commands/npm-ls.html @@ -166,7 +166,7 @@

Description

the results to only the paths to the packages named. Note that nested packages will also show the paths to the specified packages. For example, running npm ls promzard in npm's source tree will show:

-
npm@8.13.2 /path/to/npm
+
npm@8.14.0 /path/to/npm
 └─┬ init-package-json@0.0.4
   └── promzard@0.1.5
 
diff --git a/deps/npm/docs/output/commands/npm.html b/deps/npm/docs/output/commands/npm.html index 9a0446af631e4f..cbe5fbee3e1cbb 100644 --- a/deps/npm/docs/output/commands/npm.html +++ b/deps/npm/docs/output/commands/npm.html @@ -149,7 +149,7 @@

Table of contents

Version

-

8.13.2

+

8.14.0

Description

npm is the package manager for the Node JavaScript platform. It puts modules in place so that node can find them, and manages dependency @@ -219,7 +219,7 @@

Developer Usage

done via npm install
  • adduser: Create an account or log in. When you do this, npm will store -credentials in the user config file config file.
  • +credentials in the user config file.
  • publish: Use the npm publish command to upload your code to the registry.
  • diff --git a/deps/npm/docs/output/configuring-npm/package-json.html b/deps/npm/docs/output/configuring-npm/package-json.html index a3bcd299c5bbe6..665dad43671cb9 100644 --- a/deps/npm/docs/output/configuring-npm/package-json.html +++ b/deps/npm/docs/output/configuring-npm/package-json.html @@ -231,7 +231,7 @@

    license

    You can check the full list of SPDX license IDs. Ideally you should pick one that is OSI approved.

    -

    If your package is licensed under multiple common licenses, use an SPDX +

    If your package is licensed under multiple common licenses, use an SPDX license expression syntax version 2.0 string, like this:

    {
    diff --git a/deps/npm/docs/output/using-npm/config.html b/deps/npm/docs/output/using-npm/config.html
    index 42789d96b7922e..900c08b3623d73 100644
    --- a/deps/npm/docs/output/using-npm/config.html
    +++ b/deps/npm/docs/output/using-npm/config.html
    @@ -142,7 +142,7 @@ 

    config

    Table of contents

    -
    +

    Description

    @@ -321,6 +321,16 @@

    audit-level

    exit code.

    +

    auth-type

    +
      +
    • Default: "legacy"
    • +
    • Type: "legacy", "web", "sso", "saml", "oauth", or "webauthn"
    • +
    +

    NOTE: auth-type values "sso", "saml", "oauth", and "webauthn" will be +removed in a future version.

    +

    What authentication strategy to use with login.

    + +

    before

    • Default: null
    • @@ -1673,17 +1683,6 @@

      also

      When set to dev or development, this is an alias for --include=dev.

      -

      auth-type

      -
        -
      • Default: "legacy"
      • -
      • Type: "legacy", "webauthn", "sso", "saml", or "oauth"
      • -
      • DEPRECATED: The SSO/SAML/OAuth methods are deprecated and will be removed in -a future version of npm in favor of web-based login.
      • -
      -

      What authentication strategy to use with adduser/login.

      -

      Pass webauthn to use a web-based login.

      - -

      cache-max

      • Default: Infinity
      • diff --git a/deps/npm/docs/output/using-npm/scripts.html b/deps/npm/docs/output/using-npm/scripts.html index 635f0c0acbda82..9571e73ac63f07 100644 --- a/deps/npm/docs/output/using-npm/scripts.html +++ b/deps/npm/docs/output/using-npm/scripts.html @@ -142,7 +142,7 @@

        scripts

        Table of contents

        - +

        Description

        @@ -172,7 +172,7 @@

        Life Cycle Scripts

        situations. These scripts happen in addition to the pre<event>, post<event>, and <event> scripts.

          -
        • prepare, prepublish, prepublishOnly, prepack, postpack
        • +
        • prepare, prepublish, prepublishOnly, prepack, postpack, dependencies

        prepare (since npm@4.0.0)

          @@ -221,6 +221,11 @@

          Life Cycle Scripts

          • Runs AFTER the tarball has been generated but before it is moved to its final destination (if at all, publish does not save the tarball locally)
          +

          dependencies

          +
            +
          • Runs AFTER any operations that modify the node_modules directory IF changes occurred.
          • +
          • Does NOT run in global mode
          • +

          Prepare and Prepublish

          Deprecation Note: prepublish

          Since npm@1.1.71, the npm CLI has run the prepublish script for both npm publish and npm install, because it's a convenient way to prepare a package for use (some common use cases are described in the section below). It has also turned out to be, in practice, very confusing. As of npm@4.0.0, a new event has been introduced, prepare, that preserves this existing behavior. A new event, prepublishOnly has been added as a transitional strategy to allow users to avoid the confusing behavior of existing npm versions and only run on npm publish (for instance, running the tests one last time to ensure they're in good shape).

          @@ -241,6 +246,8 @@

          Prepare and Prepublish

        • You don't need to rely on your users having curl or wget or other system tools on the target machines.
        +

        Dependencies

        +

        The dependencies script is run any time an npm command causes changes to the node_modules directory. It is run AFTER the changes have been applied and the package.json and package-lock.json files have been updated.

        Life Cycle Operation Order

        npm cache add

          diff --git a/deps/npm/docs/output/using-npm/workspaces.html b/deps/npm/docs/output/using-npm/workspaces.html index a1613b782f7c46..06928563acc1fd 100644 --- a/deps/npm/docs/output/using-npm/workspaces.html +++ b/deps/npm/docs/output/using-npm/workspaces.html @@ -184,7 +184,7 @@

          Defining workspaces

          structure of files and folders:

          .
           +-- node_modules
          -|  `-- packages/a -> ../packages/a
          +|  `-- a -> ../packages/a
           +-- package-lock.json
           +-- package.json
           `-- packages
          @@ -223,13 +223,13 @@ 

          Adding dependencies to a workspaceUsing workspaces

          Given the specifities of how Node.js handles module resolution it's possible to consume any defined workspace by its declared package.json name. Continuing from the example defined -above, let's also create a Node.js script that will require the workspace-a +above, let's also create a Node.js script that will require the workspace a example module, e.g:

          -
          // ./workspace-a/index.js
          +
          // ./packages/a/index.js
           module.exports = 'a'
           
           // ./lib/index.js
          -const moduleA = require('workspace-a')
          +const moduleA = require('a')
           console.log(moduleA) // -> a
           

          When running it with:

          diff --git a/deps/npm/lib/commands/adduser.js b/deps/npm/lib/commands/adduser.js index cf467b7a73a5e8..5e23f40dc59680 100644 --- a/deps/npm/lib/commands/adduser.js +++ b/deps/npm/lib/commands/adduser.js @@ -3,6 +3,7 @@ const replaceInfo = require('../utils/replace-info.js') const BaseCommand = require('../base-command.js') const authTypes = { legacy: require('../auth/legacy.js'), + web: require('../auth/legacy.js'), webauthn: require('../auth/legacy.js'), oauth: require('../auth/oauth.js'), saml: require('../auth/saml.js'), @@ -28,6 +29,10 @@ class AddUser extends BaseCommand { log.disableProgress() + log.warn('adduser', + '`adduser` will be split into `login` and `register in a future version.' + + ' `adduser` will become an alias of `register`.' + + ' `login` (currently an alias) will become its own command.') log.notice('', `Log in on ${replaceInfo(registry)}`) const { message, newCreds } = await auth(this.npm, { diff --git a/deps/npm/lib/commands/audit.js b/deps/npm/lib/commands/audit.js index 08d011d8318751..779bc22fc6aaf7 100644 --- a/deps/npm/lib/commands/audit.js +++ b/deps/npm/lib/commands/audit.js @@ -1,8 +1,336 @@ const Arborist = require('@npmcli/arborist') const auditReport = require('npm-audit-report') -const reifyFinish = require('../utils/reify-finish.js') -const auditError = require('../utils/audit-error.js') +const fetch = require('npm-registry-fetch') +const localeCompare = require('@isaacs/string-locale-compare')('en') +const npa = require('npm-package-arg') +const pacote = require('pacote') +const pMap = require('p-map') + const ArboristWorkspaceCmd = require('../arborist-cmd.js') +const auditError = require('../utils/audit-error.js') +const log = require('../utils/log-shim.js') +const reifyFinish = require('../utils/reify-finish.js') + +const sortAlphabetically = (a, b) => localeCompare(a.name, b.name) + +class VerifySignatures { + constructor (tree, filterSet, npm, opts) { + this.tree = tree + this.filterSet = filterSet + this.npm = npm + this.opts = opts + this.keys = new Map() + this.invalid = [] + this.missing = [] + this.checkedPackages = new Set() + this.auditedWithKeysCount = 0 + this.verifiedCount = 0 + this.output = [] + this.exitCode = 0 + } + + async run () { + const start = process.hrtime.bigint() + + // Find all deps in tree + const { edges, registries } = this.getEdgesOut(this.tree.inventory.values(), this.filterSet) + if (edges.size === 0) { + throw new Error('found no installed dependencies to audit') + } + + await Promise.all([...registries].map(registry => this.setKeys({ registry }))) + + const progress = log.newItem('verifying registry signatures', edges.size) + const mapper = async (edge) => { + progress.completeWork(1) + await this.getVerifiedInfo(edge) + } + await pMap(edges, mapper, { concurrency: 20, stopOnError: true }) + + // Didn't find any dependencies that could be verified, e.g. only local + // deps, missing version, not on a registry etc. + if (!this.auditedWithKeysCount) { + throw new Error('found no dependencies to audit that where installed from ' + + 'a supported registry') + } + + const invalid = this.invalid.sort(sortAlphabetically) + const missing = this.missing.sort(sortAlphabetically) + + const hasNoInvalidOrMissing = invalid.length === 0 && missing.length === 0 + + if (!hasNoInvalidOrMissing) { + this.exitCode = 1 + } + + if (this.npm.config.get('json')) { + this.appendOutput(JSON.stringify({ + invalid: this.makeJSON(invalid), + missing: this.makeJSON(missing), + }, null, 2)) + return + } + const end = process.hrtime.bigint() + const elapsed = end - start + + const auditedPlural = this.auditedWithKeysCount > 1 ? 's' : '' + const timing = `audited ${this.auditedWithKeysCount} package${auditedPlural} in ` + + `${Math.floor(Number(elapsed) / 1e9)}s` + this.appendOutput(`${timing}\n`) + + if (this.verifiedCount) { + const verifiedBold = this.npm.chalk.bold('verified') + const msg = this.verifiedCount === 1 ? + `${this.verifiedCount} package has a ${verifiedBold} registry signature\n` : + `${this.verifiedCount} packages have ${verifiedBold} registry signatures\n` + this.appendOutput(msg) + } + + if (missing.length) { + const missingClr = this.npm.chalk.bold(this.npm.chalk.red('missing')) + const msg = missing.length === 1 ? + `package has a ${missingClr} registry signature` : + `packages have ${missingClr} registry signatures` + this.appendOutput( + `${missing.length} ${msg} but the registry is ` + + `providing signing keys:\n` + ) + this.appendOutput(this.humanOutput(missing)) + } + + if (invalid.length) { + const invalidClr = this.npm.chalk.bold(this.npm.chalk.red('invalid')) + const msg = invalid.length === 1 ? + `${invalid.length} package has an ${invalidClr} registry signature:\n` : + `${invalid.length} packages have ${invalidClr} registry signatures:\n` + this.appendOutput( + `${missing.length ? '\n' : ''}${msg}` + ) + this.appendOutput(this.humanOutput(invalid)) + const tamperMsg = invalid.length === 1 ? + `\nSomeone might have tampered with this package since it was ` + + `published on the registry!\n` : + `\nSomeone might have tampered with these packages since they where ` + + `published on the registry!\n` + this.appendOutput(tamperMsg) + } + } + + appendOutput (...args) { + this.output.push(...args.flat()) + } + + report () { + return { report: this.output.join('\n'), exitCode: this.exitCode } + } + + getEdgesOut (nodes, filterSet) { + const edges = new Set() + const registries = new Set() + for (const node of nodes) { + for (const edge of node.edgesOut.values()) { + const filteredOut = + edge.from + && filterSet + && filterSet.size > 0 + && !filterSet.has(edge.from.target) + + if (!filteredOut) { + const spec = this.getEdgeSpec(edge) + if (spec) { + // Prefetch and cache public keys from used registries + registries.add(this.getSpecRegistry(spec)) + } + edges.add(edge) + } + } + } + return { edges, registries } + } + + async setKeys ({ registry }) { + const keys = await fetch.json('/-/npm/v1/keys', { + ...this.npm.flatOptions, + registry, + }).then(({ keys }) => keys.map((key) => ({ + ...key, + pemkey: `-----BEGIN PUBLIC KEY-----\n${key.key}\n-----END PUBLIC KEY-----`, + }))).catch(err => { + if (err.code === 'E404') { + return null + } else { + throw err + } + }) + if (keys) { + this.keys.set(registry, keys) + } + } + + getEdgeType (edge) { + return edge.optional ? 'optionalDependencies' + : edge.peer ? 'peerDependencies' + : edge.dev ? 'devDependencies' + : 'dependencies' + } + + getEdgeSpec (edge) { + let name = edge.name + try { + name = npa(edge.spec).subSpec.name + } catch (_) { + } + try { + return npa(`${name}@${edge.spec}`) + } catch (_) { + // Skip packages with invalid spec + } + } + + buildRegistryConfig (registry) { + const keys = this.keys.get(registry) || [] + const parsedRegistry = new URL(registry) + const regKey = `//${parsedRegistry.host}${parsedRegistry.pathname}` + return { + [`${regKey}:_keys`]: keys, + } + } + + getSpecRegistry (spec) { + return fetch.pickRegistry(spec, this.npm.flatOptions) + } + + getValidPackageInfo (edge) { + const type = this.getEdgeType(edge) + // Skip potentially optional packages that are not on disk, as these could + // be omitted during install + if (edge.error === 'MISSING' && type !== 'dependencies') { + return + } + + const spec = this.getEdgeSpec(edge) + // Skip invalid version requirements + if (!spec) { + return + } + const node = edge.to || edge + const { version } = node.package || {} + + if (node.isWorkspace || // Skip local workspaces packages + !version || // Skip packages that don't have a installed version, e.g. optonal dependencies + !spec.registry) { // Skip if not from registry, e.g. git package + return + } + + for (const omitType of this.npm.config.get('omit')) { + if (node[omitType]) { + return + } + } + + return { + name: spec.name, + version, + type, + location: node.location, + registry: this.getSpecRegistry(spec), + } + } + + async verifySignatures (name, version, registry) { + const { + _integrity: integrity, + _signatures, + _resolved: resolved, + } = await pacote.manifest(`${name}@${version}`, { + verifySignatures: true, + ...this.buildRegistryConfig(registry), + ...this.npm.flatOptions, + }) + const signatures = _signatures || [] + return { + integrity, + signatures, + resolved, + } + } + + async getVerifiedInfo (edge) { + const info = this.getValidPackageInfo(edge) + if (!info) { + return + } + const { name, version, location, registry, type } = info + if (this.checkedPackages.has(location)) { + // we already did or are doing this one + return + } + this.checkedPackages.add(location) + + // We only "audit" or verify the signature, or the presence of it, on + // packages whose registry returns signing keys + const keys = this.keys.get(registry) || [] + if (keys.length) { + this.auditedWithKeysCount += 1 + } + + try { + const { integrity, signatures, resolved } = await this.verifySignatures( + name, version, registry + ) + + // Currently we only care about missing signatures on registries that provide a public key + // We could make this configurable in the future with a strict/paranoid mode + if (signatures.length) { + this.verifiedCount += 1 + } else if (keys.length) { + this.missing.push({ + name, + version, + location, + resolved, + integrity, + registry, + }) + } + } catch (e) { + if (e.code === 'EINTEGRITYSIGNATURE') { + const { signature, keyid, integrity, resolved } = e + this.invalid.push({ + name, + type, + version, + resolved, + location, + integrity, + registry, + signature, + keyid, + }) + } else { + throw e + } + } + } + + humanOutput (list) { + return list.map(v => + `${this.npm.chalk.red(`${v.name}@${v.version}`)} (${v.registry})` + ).join('\n') + } + + makeJSON (deps) { + return deps.map(d => ({ + name: d.name, + version: d.version, + location: d.location, + resolved: d.resolved, + integrity: d.integrity, + signature: d.signature, + keyid: d.keyid, + })) + } +} class Audit extends ArboristWorkspaceCmd { static description = 'Run a security audit' @@ -19,7 +347,7 @@ class Audit extends ArboristWorkspaceCmd { ...super.params, ] - static usage = ['[fix]'] + static usage = ['[fix|signatures]'] async completion (opts) { const argv = opts.conf.argv.remain @@ -32,11 +360,21 @@ class Audit extends ArboristWorkspaceCmd { case 'fix': return [] default: - throw new Error(argv[2] + ' not recognized') + throw Object.assign(new Error(argv[2] + ' not recognized'), { + code: 'EUSAGE', + }) } } async exec (args) { + if (args[0] === 'signatures') { + await this.auditSignatures() + } else { + await this.auditAdvisories(args) + } + } + + async auditAdvisories (args) { const reporter = this.npm.config.get('json') ? 'json' : 'detail' const opts = { ...this.npm.flatOptions, @@ -59,6 +397,44 @@ class Audit extends ArboristWorkspaceCmd { this.npm.output(result.report) } } + + async auditSignatures () { + if (this.npm.global) { + throw Object.assign( + new Error('`npm audit signatures` does not support global packages'), { + code: 'EAUDITGLOBAL', + } + ) + } + + log.verbose('loading installed dependencies') + const opts = { + ...this.npm.flatOptions, + path: this.npm.prefix, + workspaces: this.workspaceNames, + } + + const arb = new Arborist(opts) + const tree = await arb.loadActual() + let filterSet = new Set() + if (opts.workspaces && opts.workspaces.length) { + filterSet = + arb.workspaceDependencySet( + tree, + opts.workspaces, + this.npm.flatOptions.includeWorkspaceRoot + ) + } else if (!this.npm.flatOptions.workspacesEnabled) { + filterSet = + arb.excludeWorkspacesDependencySet(tree) + } + + const verify = new VerifySignatures(tree, filterSet, this.npm, { ...opts }) + await verify.run() + const result = verify.report() + process.exitCode = process.exitCode || result.exitCode + this.npm.output(result.report) + } } module.exports = Audit diff --git a/deps/npm/lib/package-url-cmd.js b/deps/npm/lib/package-url-cmd.js index b897272149d027..4254dde4517ba3 100644 --- a/deps/npm/lib/package-url-cmd.js +++ b/deps/npm/lib/package-url-cmd.js @@ -40,6 +40,9 @@ class PackageUrlCommand extends BaseCommand { } async execWorkspaces (args, filters) { + if (args && args.length) { + return this.exec(args) + } await this.setWorkspaces(filters) return this.exec(this.workspacePaths) } diff --git a/deps/npm/lib/utils/config/definitions.js b/deps/npm/lib/utils/config/definitions.js index 6b35e7d4d05b4c..665ed1efe5e61c 100644 --- a/deps/npm/lib/utils/config/definitions.js +++ b/deps/npm/lib/utils/config/definitions.js @@ -3,6 +3,7 @@ module.exports = definitions const Definition = require('./definition.js') +const log = require('../log-shim') const { version: npmVersion } = require('../../../package.json') const ciDetect = require('@npmcli/ci-detect') const ciName = ciDetect() @@ -238,17 +239,24 @@ define('audit-level', { define('auth-type', { default: 'legacy', - type: ['legacy', 'webauthn', 'sso', 'saml', 'oauth'], - deprecated: ` - The SSO/SAML/OAuth methods are deprecated and will be removed in - a future version of npm in favor of web-based login. - `, + type: ['legacy', 'web', 'sso', 'saml', 'oauth', 'webauthn'], + // deprecation in description rather than field, because not every value + // is deprecated description: ` - What authentication strategy to use with \`adduser\`/\`login\`. + NOTE: auth-type values "sso", "saml", "oauth", and "webauthn" will be + removed in a future version. - Pass \`webauthn\` to use a web-based login. + What authentication strategy to use with \`login\`. `, - flatten, + flatten (key, obj, flatOptions) { + flatOptions.authType = obj[key] + if (obj[key] === 'sso') { + // no need to deprecate saml/oauth here, as sso-type will be set by these in + // lib/auth/ and is deprecated already + log.warn('config', + '--auth-type=sso is will be removed in a future version.') + } + }, }) define('before', { diff --git a/deps/npm/man/man1/npm-access.1 b/deps/npm/man/man1/npm-access.1 index 0529f88ac9e172..2c3f67180c1c89 100644 --- a/deps/npm/man/man1/npm-access.1 +++ b/deps/npm/man/man1/npm-access.1 @@ -1,4 +1,4 @@ -.TH "NPM\-ACCESS" "1" "June 2022" "" "" +.TH "NPM\-ACCESS" "1" "July 2022" "" "" .SH "NAME" \fBnpm-access\fR \- Set access level on published packages .SS Synopsis diff --git a/deps/npm/man/man1/npm-adduser.1 b/deps/npm/man/man1/npm-adduser.1 index de87ef4df4a199..fc27cef4516a28 100644 --- a/deps/npm/man/man1/npm-adduser.1 +++ b/deps/npm/man/man1/npm-adduser.1 @@ -1,4 +1,4 @@ -.TH "NPM\-ADDUSER" "1" "June 2022" "" "" +.TH "NPM\-ADDUSER" "1" "July 2022" "" "" .SH "NAME" \fBnpm-adduser\fR \- Add a registry user account .SS Synopsis @@ -82,16 +82,14 @@ npm init \-\-scope=@foo \-\-yes .IP \(bu 2 Default: "legacy" .IP \(bu 2 -Type: "legacy", "webauthn", "sso", "saml", or "oauth" -.IP \(bu 2 -DEPRECATED: The SSO/SAML/OAuth methods are deprecated and will be removed in -a future version of npm in favor of web\-based login\. +Type: "legacy", "web", "sso", "saml", "oauth", or "webauthn" .RE .P -What authentication strategy to use with \fBadduser\fP/\fBlogin\fP\|\. +NOTE: auth\-type values "sso", "saml", "oauth", and "webauthn" will be +removed in a future version\. .P -Pass \fBwebauthn\fP to use a web\-based login\. +What authentication strategy to use with \fBlogin\fP\|\. .SS See Also .RS 0 .IP \(bu 2 diff --git a/deps/npm/man/man1/npm-audit.1 b/deps/npm/man/man1/npm-audit.1 index 2f2d0b394b6e71..63ef87d0a7126a 100644 --- a/deps/npm/man/man1/npm-audit.1 +++ b/deps/npm/man/man1/npm-audit.1 @@ -1,11 +1,11 @@ -.TH "NPM\-AUDIT" "1" "June 2022" "" "" +.TH "NPM\-AUDIT" "1" "July 2022" "" "" .SH "NAME" \fBnpm-audit\fR \- Run a security audit .SS Synopsis .P .RS 2 .nf -npm audit [fix] +npm audit [fix|signatures] .fi .RE .SS Description @@ -29,6 +29,16 @@ vulnerability is found\. It may be useful in CI environments to include the \fB\-\-audit\-level\fP parameter to specify the minimum vulnerability level that will cause the command to fail\. This option does not filter the report output, it simply changes the command's failure threshold\. +.SS Audit Signatures +.P +This command can also audit the integrity values of the packages in your +tree against any signatures present in the registry they were downloaded +from\. npm will attempt to download the keys from \fB/\-/npm/v1/keys\fP on +each the registry used to download any given package\. It will then +check the \fBdist\.signatures\fP object in the package itself, and verify the +\fBsig\fP present there using the \fBkeyid\fP there, matching it with a key +returned from the registry\. The command for this is \fBnpm audit +signatures\fP .SS Audit Endpoints .P There are two audit endpoints that npm may use to fetch vulnerability diff --git a/deps/npm/man/man1/npm-bin.1 b/deps/npm/man/man1/npm-bin.1 index a053d7bc882e8e..a3316780613917 100644 --- a/deps/npm/man/man1/npm-bin.1 +++ b/deps/npm/man/man1/npm-bin.1 @@ -1,4 +1,4 @@ -.TH "NPM\-BIN" "1" "June 2022" "" "" +.TH "NPM\-BIN" "1" "July 2022" "" "" .SH "NAME" \fBnpm-bin\fR \- Display npm bin folder .SS Synopsis diff --git a/deps/npm/man/man1/npm-bugs.1 b/deps/npm/man/man1/npm-bugs.1 index bee4cd4d01e139..d401d152d4fa9c 100644 --- a/deps/npm/man/man1/npm-bugs.1 +++ b/deps/npm/man/man1/npm-bugs.1 @@ -1,4 +1,4 @@ -.TH "NPM\-BUGS" "1" "June 2022" "" "" +.TH "NPM\-BUGS" "1" "July 2022" "" "" .SH "NAME" \fBnpm-bugs\fR \- Report bugs for a package in a web browser .SS Synopsis diff --git a/deps/npm/man/man1/npm-cache.1 b/deps/npm/man/man1/npm-cache.1 index 30b4947bd0b24b..379fdccedd94b7 100644 --- a/deps/npm/man/man1/npm-cache.1 +++ b/deps/npm/man/man1/npm-cache.1 @@ -1,4 +1,4 @@ -.TH "NPM\-CACHE" "1" "June 2022" "" "" +.TH "NPM\-CACHE" "1" "July 2022" "" "" .SH "NAME" \fBnpm-cache\fR \- Manipulates packages cache .SS Synopsis diff --git a/deps/npm/man/man1/npm-ci.1 b/deps/npm/man/man1/npm-ci.1 index 5c92d5d9ba7059..316da3a61189ce 100644 --- a/deps/npm/man/man1/npm-ci.1 +++ b/deps/npm/man/man1/npm-ci.1 @@ -1,4 +1,4 @@ -.TH "NPM\-CI" "1" "June 2022" "" "" +.TH "NPM\-CI" "1" "July 2022" "" "" .SH "NAME" \fBnpm-ci\fR \- Clean install a project .SS Synopsis diff --git a/deps/npm/man/man1/npm-completion.1 b/deps/npm/man/man1/npm-completion.1 index 7b6be088ca14cd..6fedc9f5a4cab2 100644 --- a/deps/npm/man/man1/npm-completion.1 +++ b/deps/npm/man/man1/npm-completion.1 @@ -1,4 +1,4 @@ -.TH "NPM\-COMPLETION" "1" "June 2022" "" "" +.TH "NPM\-COMPLETION" "1" "July 2022" "" "" .SH "NAME" \fBnpm-completion\fR \- Tab Completion for npm .SS Synopsis diff --git a/deps/npm/man/man1/npm-config.1 b/deps/npm/man/man1/npm-config.1 index 2cd7faf69bb333..1898ee6cdc630e 100644 --- a/deps/npm/man/man1/npm-config.1 +++ b/deps/npm/man/man1/npm-config.1 @@ -1,4 +1,4 @@ -.TH "NPM\-CONFIG" "1" "June 2022" "" "" +.TH "NPM\-CONFIG" "1" "July 2022" "" "" .SH "NAME" \fBnpm-config\fR \- Manage the npm configuration files .SS Synopsis diff --git a/deps/npm/man/man1/npm-dedupe.1 b/deps/npm/man/man1/npm-dedupe.1 index 3f235b273eb79e..cdae87193198e6 100644 --- a/deps/npm/man/man1/npm-dedupe.1 +++ b/deps/npm/man/man1/npm-dedupe.1 @@ -1,4 +1,4 @@ -.TH "NPM\-DEDUPE" "1" "June 2022" "" "" +.TH "NPM\-DEDUPE" "1" "July 2022" "" "" .SH "NAME" \fBnpm-dedupe\fR \- Reduce duplication in the package tree .SS Synopsis diff --git a/deps/npm/man/man1/npm-deprecate.1 b/deps/npm/man/man1/npm-deprecate.1 index 7f9fe808f5013c..ba9c1d083e9bf1 100644 --- a/deps/npm/man/man1/npm-deprecate.1 +++ b/deps/npm/man/man1/npm-deprecate.1 @@ -1,4 +1,4 @@ -.TH "NPM\-DEPRECATE" "1" "June 2022" "" "" +.TH "NPM\-DEPRECATE" "1" "July 2022" "" "" .SH "NAME" \fBnpm-deprecate\fR \- Deprecate a version of a package .SS Synopsis diff --git a/deps/npm/man/man1/npm-diff.1 b/deps/npm/man/man1/npm-diff.1 index 753d2863e87106..6d5f0eb2c64e00 100644 --- a/deps/npm/man/man1/npm-diff.1 +++ b/deps/npm/man/man1/npm-diff.1 @@ -1,4 +1,4 @@ -.TH "NPM\-DIFF" "1" "June 2022" "" "" +.TH "NPM\-DIFF" "1" "July 2022" "" "" .SH "NAME" \fBnpm-diff\fR \- The registry diff command .SS Synopsis diff --git a/deps/npm/man/man1/npm-dist-tag.1 b/deps/npm/man/man1/npm-dist-tag.1 index 7334c059d4d7f5..b96b7c26e4f7d2 100644 --- a/deps/npm/man/man1/npm-dist-tag.1 +++ b/deps/npm/man/man1/npm-dist-tag.1 @@ -1,4 +1,4 @@ -.TH "NPM\-DIST\-TAG" "1" "June 2022" "" "" +.TH "NPM\-DIST\-TAG" "1" "July 2022" "" "" .SH "NAME" \fBnpm-dist-tag\fR \- Modify package distribution tags .SS Synopsis diff --git a/deps/npm/man/man1/npm-docs.1 b/deps/npm/man/man1/npm-docs.1 index 55ad03256ba8aa..d15191dd13ab94 100644 --- a/deps/npm/man/man1/npm-docs.1 +++ b/deps/npm/man/man1/npm-docs.1 @@ -1,4 +1,4 @@ -.TH "NPM\-DOCS" "1" "June 2022" "" "" +.TH "NPM\-DOCS" "1" "July 2022" "" "" .SH "NAME" \fBnpm-docs\fR \- Open documentation for a package in a web browser .SS Synopsis diff --git a/deps/npm/man/man1/npm-doctor.1 b/deps/npm/man/man1/npm-doctor.1 index a21a8eef1bee7e..aa4affb8908d37 100644 --- a/deps/npm/man/man1/npm-doctor.1 +++ b/deps/npm/man/man1/npm-doctor.1 @@ -1,4 +1,4 @@ -.TH "NPM\-DOCTOR" "1" "June 2022" "" "" +.TH "NPM\-DOCTOR" "1" "July 2022" "" "" .SH "NAME" \fBnpm-doctor\fR \- Check your npm environment .SS Synopsis diff --git a/deps/npm/man/man1/npm-edit.1 b/deps/npm/man/man1/npm-edit.1 index 5c95bb10538b65..7b0725b669c44d 100644 --- a/deps/npm/man/man1/npm-edit.1 +++ b/deps/npm/man/man1/npm-edit.1 @@ -1,4 +1,4 @@ -.TH "NPM\-EDIT" "1" "June 2022" "" "" +.TH "NPM\-EDIT" "1" "July 2022" "" "" .SH "NAME" \fBnpm-edit\fR \- Edit an installed package .SS Synopsis diff --git a/deps/npm/man/man1/npm-exec.1 b/deps/npm/man/man1/npm-exec.1 index 6403191e1c6741..f29a7bcb7d77b4 100644 --- a/deps/npm/man/man1/npm-exec.1 +++ b/deps/npm/man/man1/npm-exec.1 @@ -1,4 +1,4 @@ -.TH "NPM\-EXEC" "1" "June 2022" "" "" +.TH "NPM\-EXEC" "1" "July 2022" "" "" .SH "NAME" \fBnpm-exec\fR \- Run a command from a local or remote npm package .SS Synopsis diff --git a/deps/npm/man/man1/npm-explain.1 b/deps/npm/man/man1/npm-explain.1 index fc7c2f2343b3d0..0a0dd0a14991c4 100644 --- a/deps/npm/man/man1/npm-explain.1 +++ b/deps/npm/man/man1/npm-explain.1 @@ -1,4 +1,4 @@ -.TH "NPM\-EXPLAIN" "1" "June 2022" "" "" +.TH "NPM\-EXPLAIN" "1" "July 2022" "" "" .SH "NAME" \fBnpm-explain\fR \- Explain installed packages .SS Synopsis diff --git a/deps/npm/man/man1/npm-explore.1 b/deps/npm/man/man1/npm-explore.1 index f57776e2df6c5a..e46d75eec6a2b7 100644 --- a/deps/npm/man/man1/npm-explore.1 +++ b/deps/npm/man/man1/npm-explore.1 @@ -1,4 +1,4 @@ -.TH "NPM\-EXPLORE" "1" "June 2022" "" "" +.TH "NPM\-EXPLORE" "1" "July 2022" "" "" .SH "NAME" \fBnpm-explore\fR \- Browse an installed package .SS Synopsis diff --git a/deps/npm/man/man1/npm-find-dupes.1 b/deps/npm/man/man1/npm-find-dupes.1 index 32e036cd73ad95..6c5aa7e1b43131 100644 --- a/deps/npm/man/man1/npm-find-dupes.1 +++ b/deps/npm/man/man1/npm-find-dupes.1 @@ -1,4 +1,4 @@ -.TH "NPM\-FIND\-DUPES" "1" "June 2022" "" "" +.TH "NPM\-FIND\-DUPES" "1" "July 2022" "" "" .SH "NAME" \fBnpm-find-dupes\fR \- Find duplication in the package tree .SS Synopsis diff --git a/deps/npm/man/man1/npm-fund.1 b/deps/npm/man/man1/npm-fund.1 index 3f48fda912c551..06612675a4a0da 100644 --- a/deps/npm/man/man1/npm-fund.1 +++ b/deps/npm/man/man1/npm-fund.1 @@ -1,4 +1,4 @@ -.TH "NPM\-FUND" "1" "June 2022" "" "" +.TH "NPM\-FUND" "1" "July 2022" "" "" .SH "NAME" \fBnpm-fund\fR \- Retrieve funding information .SS Synopsis diff --git a/deps/npm/man/man1/npm-help-search.1 b/deps/npm/man/man1/npm-help-search.1 index e9b7f3e34eecab..69b13814f1af26 100644 --- a/deps/npm/man/man1/npm-help-search.1 +++ b/deps/npm/man/man1/npm-help-search.1 @@ -1,4 +1,4 @@ -.TH "NPM\-HELP\-SEARCH" "1" "June 2022" "" "" +.TH "NPM\-HELP\-SEARCH" "1" "July 2022" "" "" .SH "NAME" \fBnpm-help-search\fR \- Search npm help documentation .SS Synopsis diff --git a/deps/npm/man/man1/npm-help.1 b/deps/npm/man/man1/npm-help.1 index 1c22611f8e8059..7c541c114df917 100644 --- a/deps/npm/man/man1/npm-help.1 +++ b/deps/npm/man/man1/npm-help.1 @@ -1,4 +1,4 @@ -.TH "NPM\-HELP" "1" "June 2022" "" "" +.TH "NPM\-HELP" "1" "July 2022" "" "" .SH "NAME" \fBnpm-help\fR \- Get help on npm .SS Synopsis diff --git a/deps/npm/man/man1/npm-hook.1 b/deps/npm/man/man1/npm-hook.1 index b90d873b0944d5..0eee4018b4eac4 100644 --- a/deps/npm/man/man1/npm-hook.1 +++ b/deps/npm/man/man1/npm-hook.1 @@ -1,4 +1,4 @@ -.TH "NPM\-HOOK" "1" "June 2022" "" "" +.TH "NPM\-HOOK" "1" "July 2022" "" "" .SH "NAME" \fBnpm-hook\fR \- Manage registry hooks .SS Synopsis diff --git a/deps/npm/man/man1/npm-init.1 b/deps/npm/man/man1/npm-init.1 index c49b0c7ac4cd47..50334564f99085 100644 --- a/deps/npm/man/man1/npm-init.1 +++ b/deps/npm/man/man1/npm-init.1 @@ -1,4 +1,4 @@ -.TH "NPM\-INIT" "1" "June 2022" "" "" +.TH "NPM\-INIT" "1" "July 2022" "" "" .SH "NAME" \fBnpm-init\fR \- Create a package\.json file .SS Synopsis diff --git a/deps/npm/man/man1/npm-install-ci-test.1 b/deps/npm/man/man1/npm-install-ci-test.1 index 8f153e1366e240..59fb33b10bdafc 100644 --- a/deps/npm/man/man1/npm-install-ci-test.1 +++ b/deps/npm/man/man1/npm-install-ci-test.1 @@ -1,4 +1,4 @@ -.TH "NPM\-INSTALL\-CI\-TEST" "1" "June 2022" "" "" +.TH "NPM\-INSTALL\-CI\-TEST" "1" "July 2022" "" "" .SH "NAME" \fBnpm-install-ci-test\fR \- Install a project with a clean slate and run tests .SS Synopsis diff --git a/deps/npm/man/man1/npm-install-test.1 b/deps/npm/man/man1/npm-install-test.1 index 02f9603e2597f8..0cd44530fd7e0b 100644 --- a/deps/npm/man/man1/npm-install-test.1 +++ b/deps/npm/man/man1/npm-install-test.1 @@ -1,4 +1,4 @@ -.TH "NPM\-INSTALL\-TEST" "1" "June 2022" "" "" +.TH "NPM\-INSTALL\-TEST" "1" "July 2022" "" "" .SH "NAME" \fBnpm-install-test\fR \- Install package(s) and run tests .SS Synopsis diff --git a/deps/npm/man/man1/npm-install.1 b/deps/npm/man/man1/npm-install.1 index f99ad884255373..43adfbe9e4b5f0 100644 --- a/deps/npm/man/man1/npm-install.1 +++ b/deps/npm/man/man1/npm-install.1 @@ -1,4 +1,4 @@ -.TH "NPM\-INSTALL" "1" "June 2022" "" "" +.TH "NPM\-INSTALL" "1" "July 2022" "" "" .SH "NAME" \fBnpm-install\fR \- Install a package .SS Synopsis diff --git a/deps/npm/man/man1/npm-link.1 b/deps/npm/man/man1/npm-link.1 index e138490e1b3301..8943060964e156 100644 --- a/deps/npm/man/man1/npm-link.1 +++ b/deps/npm/man/man1/npm-link.1 @@ -1,4 +1,4 @@ -.TH "NPM\-LINK" "1" "June 2022" "" "" +.TH "NPM\-LINK" "1" "July 2022" "" "" .SH "NAME" \fBnpm-link\fR \- Symlink a package folder .SS Synopsis diff --git a/deps/npm/man/man1/npm-logout.1 b/deps/npm/man/man1/npm-logout.1 index 4f37c3f7b0faac..1e0d35f9005bb8 100644 --- a/deps/npm/man/man1/npm-logout.1 +++ b/deps/npm/man/man1/npm-logout.1 @@ -1,4 +1,4 @@ -.TH "NPM\-LOGOUT" "1" "June 2022" "" "" +.TH "NPM\-LOGOUT" "1" "July 2022" "" "" .SH "NAME" \fBnpm-logout\fR \- Log out of the registry .SS Synopsis diff --git a/deps/npm/man/man1/npm-ls.1 b/deps/npm/man/man1/npm-ls.1 index 5a78c46a6e6da4..ad4473f2d9660a 100644 --- a/deps/npm/man/man1/npm-ls.1 +++ b/deps/npm/man/man1/npm-ls.1 @@ -1,4 +1,4 @@ -.TH "NPM\-LS" "1" "June 2022" "" "" +.TH "NPM\-LS" "1" "July 2022" "" "" .SH "NAME" \fBnpm-ls\fR \- List installed packages .SS Synopsis @@ -26,7 +26,7 @@ example, running \fBnpm ls promzard\fP in npm's source tree will show: .P .RS 2 .nf -npm@8\.13\.2 /path/to/npm +npm@8\.14\.0 /path/to/npm └─┬ init\-package\-json@0\.0\.4 └── promzard@0\.1\.5 .fi diff --git a/deps/npm/man/man1/npm-org.1 b/deps/npm/man/man1/npm-org.1 index b8b606c61d8145..9545a27ced04f9 100644 --- a/deps/npm/man/man1/npm-org.1 +++ b/deps/npm/man/man1/npm-org.1 @@ -1,4 +1,4 @@ -.TH "NPM\-ORG" "1" "June 2022" "" "" +.TH "NPM\-ORG" "1" "July 2022" "" "" .SH "NAME" \fBnpm-org\fR \- Manage orgs .SS Synopsis diff --git a/deps/npm/man/man1/npm-outdated.1 b/deps/npm/man/man1/npm-outdated.1 index 80ec0928a2baa4..b18a2bd064b137 100644 --- a/deps/npm/man/man1/npm-outdated.1 +++ b/deps/npm/man/man1/npm-outdated.1 @@ -1,4 +1,4 @@ -.TH "NPM\-OUTDATED" "1" "June 2022" "" "" +.TH "NPM\-OUTDATED" "1" "July 2022" "" "" .SH "NAME" \fBnpm-outdated\fR \- Check for outdated packages .SS Synopsis diff --git a/deps/npm/man/man1/npm-owner.1 b/deps/npm/man/man1/npm-owner.1 index 85ab4077b21bce..ebdf9f719c8412 100644 --- a/deps/npm/man/man1/npm-owner.1 +++ b/deps/npm/man/man1/npm-owner.1 @@ -1,4 +1,4 @@ -.TH "NPM\-OWNER" "1" "June 2022" "" "" +.TH "NPM\-OWNER" "1" "July 2022" "" "" .SH "NAME" \fBnpm-owner\fR \- Manage package owners .SS Synopsis diff --git a/deps/npm/man/man1/npm-pack.1 b/deps/npm/man/man1/npm-pack.1 index a46bf5c752648f..f89580c2090606 100644 --- a/deps/npm/man/man1/npm-pack.1 +++ b/deps/npm/man/man1/npm-pack.1 @@ -1,4 +1,4 @@ -.TH "NPM\-PACK" "1" "June 2022" "" "" +.TH "NPM\-PACK" "1" "July 2022" "" "" .SH "NAME" \fBnpm-pack\fR \- Create a tarball from a package .SS Synopsis diff --git a/deps/npm/man/man1/npm-ping.1 b/deps/npm/man/man1/npm-ping.1 index 643504f3d9a638..c60ce739dfea26 100644 --- a/deps/npm/man/man1/npm-ping.1 +++ b/deps/npm/man/man1/npm-ping.1 @@ -1,4 +1,4 @@ -.TH "NPM\-PING" "1" "June 2022" "" "" +.TH "NPM\-PING" "1" "July 2022" "" "" .SH "NAME" \fBnpm-ping\fR \- Ping npm registry .SS Synopsis diff --git a/deps/npm/man/man1/npm-pkg.1 b/deps/npm/man/man1/npm-pkg.1 index 7ffaf09ccac98c..9bffc812fc6f84 100644 --- a/deps/npm/man/man1/npm-pkg.1 +++ b/deps/npm/man/man1/npm-pkg.1 @@ -1,4 +1,4 @@ -.TH "NPM\-PKG" "1" "June 2022" "" "" +.TH "NPM\-PKG" "1" "July 2022" "" "" .SH "NAME" \fBnpm-pkg\fR \- Manages your package\.json .SS Synopsis diff --git a/deps/npm/man/man1/npm-prefix.1 b/deps/npm/man/man1/npm-prefix.1 index b209abac6c74a1..fcc9f57bcfbb13 100644 --- a/deps/npm/man/man1/npm-prefix.1 +++ b/deps/npm/man/man1/npm-prefix.1 @@ -1,4 +1,4 @@ -.TH "NPM\-PREFIX" "1" "June 2022" "" "" +.TH "NPM\-PREFIX" "1" "July 2022" "" "" .SH "NAME" \fBnpm-prefix\fR \- Display prefix .SS Synopsis diff --git a/deps/npm/man/man1/npm-profile.1 b/deps/npm/man/man1/npm-profile.1 index ffdfbb5af52aab..285f4e118be6ea 100644 --- a/deps/npm/man/man1/npm-profile.1 +++ b/deps/npm/man/man1/npm-profile.1 @@ -1,4 +1,4 @@ -.TH "NPM\-PROFILE" "1" "June 2022" "" "" +.TH "NPM\-PROFILE" "1" "July 2022" "" "" .SH "NAME" \fBnpm-profile\fR \- Change settings on your registry profile .SS Synopsis diff --git a/deps/npm/man/man1/npm-prune.1 b/deps/npm/man/man1/npm-prune.1 index 1f91bd1f2352ae..0a3c631fb57dc1 100644 --- a/deps/npm/man/man1/npm-prune.1 +++ b/deps/npm/man/man1/npm-prune.1 @@ -1,4 +1,4 @@ -.TH "NPM\-PRUNE" "1" "June 2022" "" "" +.TH "NPM\-PRUNE" "1" "July 2022" "" "" .SH "NAME" \fBnpm-prune\fR \- Remove extraneous packages .SS Synopsis diff --git a/deps/npm/man/man1/npm-publish.1 b/deps/npm/man/man1/npm-publish.1 index d64a144447f7b2..664e5664ba95d7 100644 --- a/deps/npm/man/man1/npm-publish.1 +++ b/deps/npm/man/man1/npm-publish.1 @@ -1,4 +1,4 @@ -.TH "NPM\-PUBLISH" "1" "June 2022" "" "" +.TH "NPM\-PUBLISH" "1" "July 2022" "" "" .SH "NAME" \fBnpm-publish\fR \- Publish a package .SS Synopsis diff --git a/deps/npm/man/man1/npm-rebuild.1 b/deps/npm/man/man1/npm-rebuild.1 index f4e4c56118d292..a59bfb49bf9c17 100644 --- a/deps/npm/man/man1/npm-rebuild.1 +++ b/deps/npm/man/man1/npm-rebuild.1 @@ -1,4 +1,4 @@ -.TH "NPM\-REBUILD" "1" "June 2022" "" "" +.TH "NPM\-REBUILD" "1" "July 2022" "" "" .SH "NAME" \fBnpm-rebuild\fR \- Rebuild a package .SS Synopsis diff --git a/deps/npm/man/man1/npm-repo.1 b/deps/npm/man/man1/npm-repo.1 index 453434dbb85818..a56585cfe18840 100644 --- a/deps/npm/man/man1/npm-repo.1 +++ b/deps/npm/man/man1/npm-repo.1 @@ -1,4 +1,4 @@ -.TH "NPM\-REPO" "1" "June 2022" "" "" +.TH "NPM\-REPO" "1" "July 2022" "" "" .SH "NAME" \fBnpm-repo\fR \- Open package repository page in the browser .SS Synopsis diff --git a/deps/npm/man/man1/npm-restart.1 b/deps/npm/man/man1/npm-restart.1 index 460a94350f1bd9..99da68ba9ae4e5 100644 --- a/deps/npm/man/man1/npm-restart.1 +++ b/deps/npm/man/man1/npm-restart.1 @@ -1,4 +1,4 @@ -.TH "NPM\-RESTART" "1" "June 2022" "" "" +.TH "NPM\-RESTART" "1" "July 2022" "" "" .SH "NAME" \fBnpm-restart\fR \- Restart a package .SS Synopsis diff --git a/deps/npm/man/man1/npm-root.1 b/deps/npm/man/man1/npm-root.1 index 3cda79f406879d..093aacaf1d78c0 100644 --- a/deps/npm/man/man1/npm-root.1 +++ b/deps/npm/man/man1/npm-root.1 @@ -1,4 +1,4 @@ -.TH "NPM\-ROOT" "1" "June 2022" "" "" +.TH "NPM\-ROOT" "1" "July 2022" "" "" .SH "NAME" \fBnpm-root\fR \- Display npm root .SS Synopsis diff --git a/deps/npm/man/man1/npm-run-script.1 b/deps/npm/man/man1/npm-run-script.1 index c9c2925ff121ed..6c056ca9f6be3c 100644 --- a/deps/npm/man/man1/npm-run-script.1 +++ b/deps/npm/man/man1/npm-run-script.1 @@ -1,4 +1,4 @@ -.TH "NPM\-RUN\-SCRIPT" "1" "June 2022" "" "" +.TH "NPM\-RUN\-SCRIPT" "1" "July 2022" "" "" .SH "NAME" \fBnpm-run-script\fR \- Run arbitrary package scripts .SS Synopsis diff --git a/deps/npm/man/man1/npm-search.1 b/deps/npm/man/man1/npm-search.1 index 19bebf6c25d2d4..7ecd371aa95962 100644 --- a/deps/npm/man/man1/npm-search.1 +++ b/deps/npm/man/man1/npm-search.1 @@ -1,4 +1,4 @@ -.TH "NPM\-SEARCH" "1" "June 2022" "" "" +.TH "NPM\-SEARCH" "1" "July 2022" "" "" .SH "NAME" \fBnpm-search\fR \- Search for packages .SS Synopsis diff --git a/deps/npm/man/man1/npm-set-script.1 b/deps/npm/man/man1/npm-set-script.1 index d6c6c36f2141bf..66197695fa93a1 100644 --- a/deps/npm/man/man1/npm-set-script.1 +++ b/deps/npm/man/man1/npm-set-script.1 @@ -1,4 +1,4 @@ -.TH "NPM\-SET\-SCRIPT" "1" "June 2022" "" "" +.TH "NPM\-SET\-SCRIPT" "1" "July 2022" "" "" .SH "NAME" \fBnpm-set-script\fR \- Set tasks in the scripts section of package\.json .SS Synopsis diff --git a/deps/npm/man/man1/npm-shrinkwrap.1 b/deps/npm/man/man1/npm-shrinkwrap.1 index bcfcae2fcc878c..702c2d18a3ba28 100644 --- a/deps/npm/man/man1/npm-shrinkwrap.1 +++ b/deps/npm/man/man1/npm-shrinkwrap.1 @@ -1,4 +1,4 @@ -.TH "NPM\-SHRINKWRAP" "1" "June 2022" "" "" +.TH "NPM\-SHRINKWRAP" "1" "July 2022" "" "" .SH "NAME" \fBnpm-shrinkwrap\fR \- Lock down dependency versions for publication .SS Synopsis diff --git a/deps/npm/man/man1/npm-star.1 b/deps/npm/man/man1/npm-star.1 index 2e91f78bdc9ca8..1abbc968298f33 100644 --- a/deps/npm/man/man1/npm-star.1 +++ b/deps/npm/man/man1/npm-star.1 @@ -1,4 +1,4 @@ -.TH "NPM\-STAR" "1" "June 2022" "" "" +.TH "NPM\-STAR" "1" "July 2022" "" "" .SH "NAME" \fBnpm-star\fR \- Mark your favorite packages .SS Synopsis diff --git a/deps/npm/man/man1/npm-stars.1 b/deps/npm/man/man1/npm-stars.1 index 91fcb6560b064c..f4c52978e0ae45 100644 --- a/deps/npm/man/man1/npm-stars.1 +++ b/deps/npm/man/man1/npm-stars.1 @@ -1,4 +1,4 @@ -.TH "NPM\-STARS" "1" "June 2022" "" "" +.TH "NPM\-STARS" "1" "July 2022" "" "" .SH "NAME" \fBnpm-stars\fR \- View packages marked as favorites .SS Synopsis diff --git a/deps/npm/man/man1/npm-start.1 b/deps/npm/man/man1/npm-start.1 index af45de89f3f5d3..5176b3b3dc12e1 100644 --- a/deps/npm/man/man1/npm-start.1 +++ b/deps/npm/man/man1/npm-start.1 @@ -1,4 +1,4 @@ -.TH "NPM\-START" "1" "June 2022" "" "" +.TH "NPM\-START" "1" "July 2022" "" "" .SH "NAME" \fBnpm-start\fR \- Start a package .SS Synopsis diff --git a/deps/npm/man/man1/npm-stop.1 b/deps/npm/man/man1/npm-stop.1 index f1159699a3bbca..da3d1e3a1f0523 100644 --- a/deps/npm/man/man1/npm-stop.1 +++ b/deps/npm/man/man1/npm-stop.1 @@ -1,4 +1,4 @@ -.TH "NPM\-STOP" "1" "June 2022" "" "" +.TH "NPM\-STOP" "1" "July 2022" "" "" .SH "NAME" \fBnpm-stop\fR \- Stop a package .SS Synopsis diff --git a/deps/npm/man/man1/npm-team.1 b/deps/npm/man/man1/npm-team.1 index b56e3fc7862911..2b16d2db0ea322 100644 --- a/deps/npm/man/man1/npm-team.1 +++ b/deps/npm/man/man1/npm-team.1 @@ -1,4 +1,4 @@ -.TH "NPM\-TEAM" "1" "June 2022" "" "" +.TH "NPM\-TEAM" "1" "July 2022" "" "" .SH "NAME" \fBnpm-team\fR \- Manage organization teams and team memberships .SS Synopsis diff --git a/deps/npm/man/man1/npm-test.1 b/deps/npm/man/man1/npm-test.1 index 8dd0f2f6828b00..f67c2752d7fcd0 100644 --- a/deps/npm/man/man1/npm-test.1 +++ b/deps/npm/man/man1/npm-test.1 @@ -1,4 +1,4 @@ -.TH "NPM\-TEST" "1" "June 2022" "" "" +.TH "NPM\-TEST" "1" "July 2022" "" "" .SH "NAME" \fBnpm-test\fR \- Test a package .SS Synopsis diff --git a/deps/npm/man/man1/npm-token.1 b/deps/npm/man/man1/npm-token.1 index 0292cf80135184..f16b421f0f1852 100644 --- a/deps/npm/man/man1/npm-token.1 +++ b/deps/npm/man/man1/npm-token.1 @@ -1,4 +1,4 @@ -.TH "NPM\-TOKEN" "1" "June 2022" "" "" +.TH "NPM\-TOKEN" "1" "July 2022" "" "" .SH "NAME" \fBnpm-token\fR \- Manage your authentication tokens .SS Synopsis diff --git a/deps/npm/man/man1/npm-uninstall.1 b/deps/npm/man/man1/npm-uninstall.1 index 7ffd372951c792..654a0ae681f401 100644 --- a/deps/npm/man/man1/npm-uninstall.1 +++ b/deps/npm/man/man1/npm-uninstall.1 @@ -1,4 +1,4 @@ -.TH "NPM\-UNINSTALL" "1" "June 2022" "" "" +.TH "NPM\-UNINSTALL" "1" "July 2022" "" "" .SH "NAME" \fBnpm-uninstall\fR \- Remove a package .SS Synopsis diff --git a/deps/npm/man/man1/npm-unpublish.1 b/deps/npm/man/man1/npm-unpublish.1 index 57ba85fb432522..6577e11e837765 100644 --- a/deps/npm/man/man1/npm-unpublish.1 +++ b/deps/npm/man/man1/npm-unpublish.1 @@ -1,4 +1,4 @@ -.TH "NPM\-UNPUBLISH" "1" "June 2022" "" "" +.TH "NPM\-UNPUBLISH" "1" "July 2022" "" "" .SH "NAME" \fBnpm-unpublish\fR \- Remove a package from the registry .SS Synopsis diff --git a/deps/npm/man/man1/npm-unstar.1 b/deps/npm/man/man1/npm-unstar.1 index b3ece049f9bb53..43600931c9f8df 100644 --- a/deps/npm/man/man1/npm-unstar.1 +++ b/deps/npm/man/man1/npm-unstar.1 @@ -1,4 +1,4 @@ -.TH "NPM\-UNSTAR" "1" "June 2022" "" "" +.TH "NPM\-UNSTAR" "1" "July 2022" "" "" .SH "NAME" \fBnpm-unstar\fR \- Remove an item from your favorite packages .SS Synopsis diff --git a/deps/npm/man/man1/npm-update.1 b/deps/npm/man/man1/npm-update.1 index df54155942bbf6..54bf4184fbf6f6 100644 --- a/deps/npm/man/man1/npm-update.1 +++ b/deps/npm/man/man1/npm-update.1 @@ -1,4 +1,4 @@ -.TH "NPM\-UPDATE" "1" "June 2022" "" "" +.TH "NPM\-UPDATE" "1" "July 2022" "" "" .SH "NAME" \fBnpm-update\fR \- Update packages .SS Synopsis diff --git a/deps/npm/man/man1/npm-version.1 b/deps/npm/man/man1/npm-version.1 index 49717643237826..40c157adf971e0 100644 --- a/deps/npm/man/man1/npm-version.1 +++ b/deps/npm/man/man1/npm-version.1 @@ -1,4 +1,4 @@ -.TH "NPM\-VERSION" "1" "June 2022" "" "" +.TH "NPM\-VERSION" "1" "July 2022" "" "" .SH "NAME" \fBnpm-version\fR \- Bump a package version .SS Synopsis diff --git a/deps/npm/man/man1/npm-view.1 b/deps/npm/man/man1/npm-view.1 index 7f83d89bf67b74..b3d9488a9f9be2 100644 --- a/deps/npm/man/man1/npm-view.1 +++ b/deps/npm/man/man1/npm-view.1 @@ -1,4 +1,4 @@ -.TH "NPM\-VIEW" "1" "June 2022" "" "" +.TH "NPM\-VIEW" "1" "July 2022" "" "" .SH "NAME" \fBnpm-view\fR \- View registry info .SS Synopsis diff --git a/deps/npm/man/man1/npm-whoami.1 b/deps/npm/man/man1/npm-whoami.1 index 726153e012e6d6..27a7bbb4ce87e6 100644 --- a/deps/npm/man/man1/npm-whoami.1 +++ b/deps/npm/man/man1/npm-whoami.1 @@ -1,4 +1,4 @@ -.TH "NPM\-WHOAMI" "1" "June 2022" "" "" +.TH "NPM\-WHOAMI" "1" "July 2022" "" "" .SH "NAME" \fBnpm-whoami\fR \- Display npm username .SS Synopsis diff --git a/deps/npm/man/man1/npm.1 b/deps/npm/man/man1/npm.1 index 1e2c15ebae15a8..b9f54eec1ba16d 100644 --- a/deps/npm/man/man1/npm.1 +++ b/deps/npm/man/man1/npm.1 @@ -1,10 +1,10 @@ -.TH "NPM" "1" "June 2022" "" "" +.TH "NPM" "1" "July 2022" "" "" .SH "NAME" \fBnpm\fR \- javascript package manager .SS Synopsis .SS Version .P -8\.13\.2 +8\.14\.0 .SS Description .P npm is the package manager for the Node JavaScript platform\. It puts @@ -97,7 +97,7 @@ done via npm help \fBinstall\fP .IP \(bu 2 adduser: Create an account or log in\. When you do this, npm will store -credentials in the user config file config file\. +credentials in the user config file\. .IP \(bu 2 publish: Use the npm help \fBpublish\fP command to upload your diff --git a/deps/npm/man/man1/npx.1 b/deps/npm/man/man1/npx.1 index 1f8c292bc545d8..47eefc4dd25796 100644 --- a/deps/npm/man/man1/npx.1 +++ b/deps/npm/man/man1/npx.1 @@ -1,4 +1,4 @@ -.TH "NPX" "1" "June 2022" "" "" +.TH "NPX" "1" "July 2022" "" "" .SH "NAME" \fBnpx\fR \- Run a command from a local or remote npm package .SS Synopsis diff --git a/deps/npm/man/man5/folders.5 b/deps/npm/man/man5/folders.5 index 925373df6d425e..3dda65825fa13c 100644 --- a/deps/npm/man/man5/folders.5 +++ b/deps/npm/man/man5/folders.5 @@ -1,4 +1,4 @@ -.TH "FOLDERS" "5" "June 2022" "" "" +.TH "FOLDERS" "5" "July 2022" "" "" .SH "NAME" \fBfolders\fR \- Folder Structures Used by npm .SS Description diff --git a/deps/npm/man/man5/install.5 b/deps/npm/man/man5/install.5 index e587d17aafef20..7d8c5693027cc2 100644 --- a/deps/npm/man/man5/install.5 +++ b/deps/npm/man/man5/install.5 @@ -1,4 +1,4 @@ -.TH "INSTALL" "5" "June 2022" "" "" +.TH "INSTALL" "5" "July 2022" "" "" .SH "NAME" \fBinstall\fR \- Download and install node and npm .SS Description diff --git a/deps/npm/man/man5/npm-shrinkwrap-json.5 b/deps/npm/man/man5/npm-shrinkwrap-json.5 index 783976fb6c52b0..931d1ae49f01c3 100644 --- a/deps/npm/man/man5/npm-shrinkwrap-json.5 +++ b/deps/npm/man/man5/npm-shrinkwrap-json.5 @@ -1,4 +1,4 @@ -.TH "NPM\-SHRINKWRAP\.JSON" "5" "June 2022" "" "" +.TH "NPM\-SHRINKWRAP\.JSON" "5" "July 2022" "" "" .SH "NAME" \fBnpm-shrinkwrap.json\fR \- A publishable lockfile .SS Description diff --git a/deps/npm/man/man5/npmrc.5 b/deps/npm/man/man5/npmrc.5 index cce0b9201c7d8e..a4d2ee239f8543 100644 --- a/deps/npm/man/man5/npmrc.5 +++ b/deps/npm/man/man5/npmrc.5 @@ -1,4 +1,4 @@ -.TH "NPMRC" "5" "June 2022" "" "" +.TH "NPMRC" "5" "July 2022" "" "" .SH "NAME" \fBnpmrc\fR \- The npm config files .SS Description diff --git a/deps/npm/man/man5/package-json.5 b/deps/npm/man/man5/package-json.5 index f6477ebc015c05..78bfc39af2e95d 100644 --- a/deps/npm/man/man5/package-json.5 +++ b/deps/npm/man/man5/package-json.5 @@ -1,4 +1,4 @@ -.TH "PACKAGE\.JSON" "5" "June 2022" "" "" +.TH "PACKAGE\.JSON" "5" "July 2022" "" "" .SH "NAME" \fBpackage.json\fR \- Specifics of npm's package\.json handling .SS Description @@ -132,7 +132,7 @@ OSI \fIhttps://opensource\.org/licenses/alphabetical\fR approved\. .P If your package is licensed under multiple common licenses, use an SPDX license expression syntax version 2\.0 -string \fIhttps://www\.npmjs\.com/package/spdx\fR, like this: +string \fIhttps://spdx\.dev/specifications/\fR, like this: .P .RS 2 .nf diff --git a/deps/npm/man/man5/package-lock-json.5 b/deps/npm/man/man5/package-lock-json.5 index 9e17a85f750876..ad4fa609649f72 100644 --- a/deps/npm/man/man5/package-lock-json.5 +++ b/deps/npm/man/man5/package-lock-json.5 @@ -1,4 +1,4 @@ -.TH "PACKAGE\-LOCK\.JSON" "5" "June 2022" "" "" +.TH "PACKAGE\-LOCK\.JSON" "5" "July 2022" "" "" .SH "NAME" \fBpackage-lock.json\fR \- A manifestation of the manifest .SS Description diff --git a/deps/npm/man/man7/config.7 b/deps/npm/man/man7/config.7 index a64db0ae281ea8..1674743956ff6b 100644 --- a/deps/npm/man/man7/config.7 +++ b/deps/npm/man/man7/config.7 @@ -1,4 +1,4 @@ -.TH "CONFIG" "7" "June 2022" "" "" +.TH "CONFIG" "7" "July 2022" "" "" .SH "NAME" \fBconfig\fR \- More than you probably want to know about npm configuration .SS Description @@ -247,6 +247,19 @@ Type: null, "info", "low", "moderate", "high", "critical", or "none" .P The minimum level of vulnerability for \fBnpm audit\fP to exit with a non\-zero exit code\. +.SS \fBauth\-type\fP +.RS 0 +.IP \(bu 2 +Default: "legacy" +.IP \(bu 2 +Type: "legacy", "web", "sso", "saml", "oauth", or "webauthn" + +.RE +.P +NOTE: auth\-type values "sso", "saml", "oauth", and "webauthn" will be +removed in a future version\. +.P +What authentication strategy to use with \fBlogin\fP\|\. .SS \fBbefore\fP .RS 0 .IP \(bu 2 @@ -1992,21 +2005,6 @@ DEPRECATED: Please use \-\-include=dev instead\. .RE .P When set to \fBdev\fP or \fBdevelopment\fP, this is an alias for \fB\-\-include=dev\fP\|\. -.SS \fBauth\-type\fP -.RS 0 -.IP \(bu 2 -Default: "legacy" -.IP \(bu 2 -Type: "legacy", "webauthn", "sso", "saml", or "oauth" -.IP \(bu 2 -DEPRECATED: The SSO/SAML/OAuth methods are deprecated and will be removed in -a future version of npm in favor of web\-based login\. - -.RE -.P -What authentication strategy to use with \fBadduser\fP/\fBlogin\fP\|\. -.P -Pass \fBwebauthn\fP to use a web\-based login\. .SS \fBcache\-max\fP .RS 0 .IP \(bu 2 diff --git a/deps/npm/man/man7/developers.7 b/deps/npm/man/man7/developers.7 index e5c163c68062e8..595fc387ca48c5 100644 --- a/deps/npm/man/man7/developers.7 +++ b/deps/npm/man/man7/developers.7 @@ -1,4 +1,4 @@ -.TH "DEVELOPERS" "7" "June 2022" "" "" +.TH "DEVELOPERS" "7" "July 2022" "" "" .SH "NAME" \fBdevelopers\fR \- Developer Guide .SS Description diff --git a/deps/npm/man/man7/logging.7 b/deps/npm/man/man7/logging.7 index 821edc2b03add2..9f637cff885945 100644 --- a/deps/npm/man/man7/logging.7 +++ b/deps/npm/man/man7/logging.7 @@ -1,4 +1,4 @@ -.TH "LOGGING" "7" "June 2022" "" "" +.TH "LOGGING" "7" "July 2022" "" "" .SH "NAME" \fBLogging\fR \- Why, What & How We Log .SS Description diff --git a/deps/npm/man/man7/orgs.7 b/deps/npm/man/man7/orgs.7 index c713fb501c7c3d..cb718d7476aa8a 100644 --- a/deps/npm/man/man7/orgs.7 +++ b/deps/npm/man/man7/orgs.7 @@ -1,4 +1,4 @@ -.TH "ORGS" "7" "June 2022" "" "" +.TH "ORGS" "7" "July 2022" "" "" .SH "NAME" \fBorgs\fR \- Working with Teams & Orgs .SS Description diff --git a/deps/npm/man/man7/package-spec.7 b/deps/npm/man/man7/package-spec.7 index f86a00686b0c3f..01da091d16b6f0 100644 --- a/deps/npm/man/man7/package-spec.7 +++ b/deps/npm/man/man7/package-spec.7 @@ -1,4 +1,4 @@ -.TH "PACKAGE\-SPEC" "7" "June 2022" "" "" +.TH "PACKAGE\-SPEC" "7" "July 2022" "" "" .SH "NAME" \fBpackage-spec\fR \- Package name specifier .SS Description diff --git a/deps/npm/man/man7/registry.7 b/deps/npm/man/man7/registry.7 index 2452816cff3ae6..0afd375ae74660 100644 --- a/deps/npm/man/man7/registry.7 +++ b/deps/npm/man/man7/registry.7 @@ -1,4 +1,4 @@ -.TH "REGISTRY" "7" "June 2022" "" "" +.TH "REGISTRY" "7" "July 2022" "" "" .SH "NAME" \fBregistry\fR \- The JavaScript Package Registry .SS Description diff --git a/deps/npm/man/man7/removal.7 b/deps/npm/man/man7/removal.7 index 5896b1f07a4c01..e30f3afbd7889d 100644 --- a/deps/npm/man/man7/removal.7 +++ b/deps/npm/man/man7/removal.7 @@ -1,4 +1,4 @@ -.TH "REMOVAL" "7" "June 2022" "" "" +.TH "REMOVAL" "7" "July 2022" "" "" .SH "NAME" \fBremoval\fR \- Cleaning the Slate .SS Synopsis diff --git a/deps/npm/man/man7/scope.7 b/deps/npm/man/man7/scope.7 index e519a2b4323913..825b9298f063c7 100644 --- a/deps/npm/man/man7/scope.7 +++ b/deps/npm/man/man7/scope.7 @@ -1,4 +1,4 @@ -.TH "SCOPE" "7" "June 2022" "" "" +.TH "SCOPE" "7" "July 2022" "" "" .SH "NAME" \fBscope\fR \- Scoped packages .SS Description diff --git a/deps/npm/man/man7/scripts.7 b/deps/npm/man/man7/scripts.7 index 945cb86e9052e8..87a6d72492cff3 100644 --- a/deps/npm/man/man7/scripts.7 +++ b/deps/npm/man/man7/scripts.7 @@ -1,4 +1,4 @@ -.TH "SCRIPTS" "7" "June 2022" "" "" +.TH "SCRIPTS" "7" "July 2022" "" "" .SH "NAME" \fBscripts\fR \- How npm handles the "scripts" field .SS Description @@ -37,7 +37,7 @@ situations\. These scripts happen in addition to the \fBpre\fP, \fBpost\fP scripts\. .RS 0 .IP \(bu 2 -\fBprepare\fP, \fBprepublish\fP, \fBprepublishOnly\fP, \fBprepack\fP, \fBpostpack\fP +\fBprepare\fP, \fBprepublish\fP, \fBprepublishOnly\fP, \fBprepack\fP, \fBpostpack\fP, \fBdependencies\fP .RE .P @@ -94,6 +94,15 @@ NOTE: "\fBnpm run pack\fP" is NOT the same as "\fBnpm pack\fP"\. "\fBnpm run pac .IP \(bu 2 Runs AFTER the tarball has been generated but before it is moved to its final destination (if at all, publish does not save the tarball locally) +.RE +.P +\fBdependencies\fR +.RS 0 +.IP \(bu 2 +Runs AFTER any operations that modify the \fBnode_modules\fP directory IF changes occurred\. +.IP \(bu 2 +Does NOT run in global mode + .RE .SS Prepare and Prepublish .P @@ -129,6 +138,9 @@ You don't need to rely on your users having \fBcurl\fP or \fBwget\fP or other system tools on the target machines\. .RE +.SS Dependencies +.P +The \fBdependencies\fP script is run any time an \fBnpm\fP command causes changes to the \fBnode_modules\fP directory\. It is run AFTER the changes have been applied and the \fBpackage\.json\fP and \fBpackage\-lock\.json\fP files have been updated\. .SS Life Cycle Operation Order .SS npm help \fBcache add\fP .RS 0 diff --git a/deps/npm/man/man7/workspaces.7 b/deps/npm/man/man7/workspaces.7 index 16460caa951cb6..0bc4488b44e5f2 100644 --- a/deps/npm/man/man7/workspaces.7 +++ b/deps/npm/man/man7/workspaces.7 @@ -1,4 +1,4 @@ -.TH "WORKSPACES" "7" "June 2022" "" "" +.TH "WORKSPACES" "7" "July 2022" "" "" .SH "NAME" \fBworkspaces\fR \- Working with workspaces .SS Description @@ -58,7 +58,7 @@ structure of files and folders: .nf \|\. +\-\- node_modules -| `\-\- packages/a \-> \.\./packages/a +| `\-\- a \-> \.\./packages/a +\-\- package\-lock\.json +\-\- package\.json `\-\- packages @@ -117,16 +117,16 @@ respect the provided \fBworkspace\fP configuration\. .P Given the specifities of how Node\.js handles module resolution \fIhttps://nodejs\.org/dist/latest\-v14\.x/docs/api/modules\.html#modules_all_together\fR it's possible to consume any defined workspace by its declared \fBpackage\.json\fP \fBname\fP\|\. Continuing from the example defined -above, let's also create a Node\.js script that will require the \fBworkspace\-a\fP +above, let's also create a Node\.js script that will require the workspace \fBa\fP example module, e\.g: .P .RS 2 .nf -// \./workspace\-a/index\.js +// \./packages/a/index\.js module\.exports = 'a' // \./lib/index\.js -const moduleA = require('workspace\-a') +const moduleA = require('a') console\.log(moduleA) // \-> a .fi .RE diff --git a/deps/npm/node_modules/@npmcli/arborist/lib/arborist/reify.js b/deps/npm/node_modules/@npmcli/arborist/lib/arborist/reify.js index 4932c17d03667b..faf016c7040109 100644 --- a/deps/npm/node_modules/@npmcli/arborist/lib/arborist/reify.js +++ b/deps/npm/node_modules/@npmcli/arborist/lib/arborist/reify.js @@ -22,6 +22,7 @@ const moveFile = require('@npmcli/move-file') const rimraf = promisify(require('rimraf')) const PackageJson = require('@npmcli/package-json') const packageContents = require('@npmcli/installed-package-contents') +const runScript = require('@npmcli/run-script') const { checkEngine, checkPlatform } = require('npm-install-checks') const _force = Symbol.for('force') @@ -1516,6 +1517,30 @@ module.exports = cls => class Reifier extends cls { if (!this[_global]) { await this.actualTree.meta.save() + const ignoreScripts = !!this.options.ignoreScripts + // if we aren't doing a dry run or ignoring scripts and we actually made changes to the dep + // tree, then run the dependencies scripts + if (!this[_dryRun] && !ignoreScripts && this.diff && this.diff.children.length) { + const { path, package: pkg } = this.actualTree.target + const stdio = this.options.foregroundScripts ? 'inherit' : 'pipe' + const { scripts = {} } = pkg + for (const event of ['predependencies', 'dependencies', 'postdependencies']) { + if (Object.prototype.hasOwnProperty.call(scripts, event)) { + const timer = `reify:run:${event}` + process.emit('time', timer) + log.info('run', pkg._id, event, scripts[event]) + await runScript({ + event, + path, + pkg, + stdioString: true, + stdio, + scriptShell: this.options.scriptShell, + }) + process.emit('timeEnd', timer) + } + } + } } } } diff --git a/deps/npm/node_modules/@npmcli/arborist/package.json b/deps/npm/node_modules/@npmcli/arborist/package.json index bfb9b4273e8834..328cdf0146c141 100644 --- a/deps/npm/node_modules/@npmcli/arborist/package.json +++ b/deps/npm/node_modules/@npmcli/arborist/package.json @@ -1,6 +1,6 @@ { "name": "@npmcli/arborist", - "version": "5.2.3", + "version": "5.3.0", "description": "Manage node_modules trees", "dependencies": { "@isaacs/string-locale-compare": "^1.1.0", diff --git a/deps/npm/node_modules/@npmcli/run-script/lib/escape.js b/deps/npm/node_modules/@npmcli/run-script/lib/escape.js index 3c574371bcf94e..303100d337eb7f 100644 --- a/deps/npm/node_modules/@npmcli/run-script/lib/escape.js +++ b/deps/npm/node_modules/@npmcli/run-script/lib/escape.js @@ -68,7 +68,7 @@ const sh = (input) => { // disabling the no-control-regex rule for this line as we very specifically _do_ want to // replace those characters if they somehow exist at this point, which is highly unlikely // eslint-disable-next-line no-control-regex -const filename = (input) => input.replace(/[<>:"/\\|?*\x00-\x31]/g, '') +const filename = (input) => input.replace(/[<>:"/\\|?*\x00-\x1F]/g, '') module.exports = { cmd, diff --git a/deps/npm/node_modules/@npmcli/run-script/lib/make-spawn-args.js b/deps/npm/node_modules/@npmcli/run-script/lib/make-spawn-args.js index 47f73463011be0..f2253d7cc607b1 100644 --- a/deps/npm/node_modules/@npmcli/run-script/lib/make-spawn-args.js +++ b/deps/npm/node_modules/@npmcli/run-script/lib/make-spawn-args.js @@ -1,12 +1,19 @@ /* eslint camelcase: "off" */ const isWindows = require('./is-windows.js') const setPATH = require('./set-path.js') -const { chmodSync: chmod, unlinkSync: unlink, writeFileSync: writeFile } = require('fs') +const { unlinkSync: unlink, writeFileSync: writeFile } = require('fs') const { tmpdir } = require('os') -const { isAbsolute, resolve } = require('path') +const { resolve } = require('path') const which = require('which') const npm_config_node_gyp = require.resolve('node-gyp/bin/node-gyp.js') const escape = require('./escape.js') +const { randomBytes } = require('crypto') + +const translateWinPathToPosix = (path) => { + return path + .replace(/^([A-z]):/, '/$1') + .replace(/\\/g, '/') +} const makeSpawnArgs = options => { const { @@ -30,7 +37,7 @@ const makeSpawnArgs = options => { npm_config_node_gyp, }) - const fileName = escape.filename(`${event}-${Date.now()}`) + const fileName = escape.filename(`${event}-${randomBytes(4).toString('hex')}`) let scriptFile let script = '' @@ -69,24 +76,17 @@ const makeSpawnArgs = options => { script += ` ${args.map((arg) => escape.cmd(arg, doubleEscape)).join(' ')}` } } else { - const shebang = isAbsolute(scriptShell) - ? `#!${scriptShell}` - : `#!/usr/bin/env ${scriptShell}` scriptFile = resolve(tmpdir(), `${fileName}.sh`) - script += `${shebang}\n` - script += cmd + script = cmd if (args.length) { script += ` ${args.map((arg) => escape.sh(arg)).join(' ')}` } } writeFile(scriptFile, script) - if (!isCmd) { - chmod(scriptFile, '0775') - } const spawnArgs = isCmd ? ['/d', '/s', '/c', escape.cmd(scriptFile)] - : ['-c', escape.sh(scriptFile)] + : [isWindows ? translateWinPathToPosix(scriptFile) : scriptFile] const spawnOpts = { env: spawnEnv, diff --git a/deps/npm/node_modules/@npmcli/run-script/package.json b/deps/npm/node_modules/@npmcli/run-script/package.json index 1ce162dd8d19a5..c096f39421bf11 100644 --- a/deps/npm/node_modules/@npmcli/run-script/package.json +++ b/deps/npm/node_modules/@npmcli/run-script/package.json @@ -1,6 +1,6 @@ { "name": "@npmcli/run-script", - "version": "4.1.5", + "version": "4.1.7", "description": "Run a lifecycle script for a package (descendant of npm-lifecycle)", "author": "GitHub Inc.", "license": "ISC", diff --git a/deps/npm/node_modules/just-diff-apply/index.d.ts b/deps/npm/node_modules/just-diff-apply/index.d.ts index 9fc05257af0aab..7547b722f484fb 100644 --- a/deps/npm/node_modules/just-diff-apply/index.d.ts +++ b/deps/npm/node_modules/just-diff-apply/index.d.ts @@ -1,6 +1,6 @@ // Definitions by: Eddie Atkinson -type Operation = "add" | "replace" | "remove"; +type Operation = "add" | "replace" | "remove" | "move"; type DiffOps = Array<{ op: Operation; diff --git a/deps/npm/node_modules/just-diff-apply/index.mjs b/deps/npm/node_modules/just-diff-apply/index.mjs index 045830507cd177..adc5f46ed51df7 100644 --- a/deps/npm/node_modules/just-diff-apply/index.mjs +++ b/deps/npm/node_modules/just-diff-apply/index.mjs @@ -42,6 +42,7 @@ var REMOVE = 'remove'; var REPLACE = 'replace'; var ADD = 'add'; +var MOVE = 'move'; function diffApply(obj, diff, pathConverter) { if (!obj || typeof obj != 'object') { @@ -57,23 +58,40 @@ function diffApply(obj, diff, pathConverter) { var thisDiff = diff[i]; var subObject = obj; var thisOp = thisDiff.op; - var thisPath = thisDiff.path; - if (pathConverter) { - thisPath = pathConverter(thisPath); - if (!Array.isArray(thisPath)) { - throw new Error('pathConverter must return an array'); + + var thisPath = transformPath(pathConverter, thisDiff.path); + var thisFromPath = thisDiff.from && transformPath(pathConverter, thisDiff.from); + var toPath, toPathCopy, lastToProp, subToObject, valueToMove; + + if (thisFromPath) { + // MOVE only, "fromPath" is effectively path and "path" is toPath + toPath = thisPath; + thisPath = thisFromPath; + + toPathCopy = toPath.slice(); + lastToProp = toPathCopy.pop(); + prototypeCheck(lastToProp); + if (lastToProp == null) { + return false; } - } else { - if (!Array.isArray(thisPath)) { - throw new Error('diff path must be an array, consider supplying a path converter'); + + var thisToProp; + while (((thisToProp = toPathCopy.shift())) != null) { + prototypeCheck(thisToProp); + if (!(thisToProp in subToObject)) { + subToObject[thisToProp] = {}; + } + subToObject = subToObject[thisToProp]; } } + var pathCopy = thisPath.slice(); var lastProp = pathCopy.pop(); prototypeCheck(lastProp); if (lastProp == null) { return false; } + var thisProp; while (((thisProp = pathCopy.shift())) != null) { prototypeCheck(thisProp); @@ -82,21 +100,50 @@ function diffApply(obj, diff, pathConverter) { } subObject = subObject[thisProp]; } - if (thisOp === REMOVE || thisOp === REPLACE) { + if (thisOp === REMOVE || thisOp === REPLACE || thisOp === MOVE) { + var path = thisOp === MOVE ? thisDiff.from : thisDiff.path; if (!subObject.hasOwnProperty(lastProp)) { - throw new Error(['expected to find property', thisDiff.path, 'in object', obj].join(' ')); + throw new Error(['expected to find property', path, 'in object', obj].join(' ')); } } - if (thisOp === REMOVE) { + if (thisOp === REMOVE || thisOp === MOVE) { + if (thisOp === MOVE) { + valueToMove = subObject[lastProp]; + } Array.isArray(subObject) ? subObject.splice(lastProp, 1) : delete subObject[lastProp]; } if (thisOp === REPLACE || thisOp === ADD) { subObject[lastProp] = thisDiff.value; } + + if (thisOp === MOVE) { + subObject[lastToProp] = valueToMove; + } } return subObject; } +function transformPath(pathConverter, thisPath) { + if(pathConverter) { + thisPath = pathConverter(thisPath); + if(!Array.isArray(thisPath)) { + throw new Error([ + 'pathConverter must return an array, returned:', + thisPath, + ].join(' ')); + } + } else { + if(!Array.isArray(thisPath)) { + throw new Error([ + 'diff path', + thisPath, + 'must be an array, consider supplying a path converter'] + .join(' ')); + } + } + return thisPath; +} + function jsonPatchPathConverter(stringPath) { return stringPath.split('/').slice(1); } diff --git a/deps/npm/node_modules/just-diff-apply/package.json b/deps/npm/node_modules/just-diff-apply/package.json index b8e5012ff83b15..b2f80b73a19c6f 100644 --- a/deps/npm/node_modules/just-diff-apply/package.json +++ b/deps/npm/node_modules/just-diff-apply/package.json @@ -1,6 +1,6 @@ { "name": "just-diff-apply", - "version": "5.2.0", + "version": "5.3.1", "description": "Apply a diff to an object. Optionally supports jsonPatch protocol", "main": "index.js", "module": "index.mjs", diff --git a/deps/npm/node_modules/just-diff/index.mjs b/deps/npm/node_modules/just-diff/index.mjs index 8da5b5cea8dab2..a0c5834475fea6 100644 --- a/deps/npm/node_modules/just-diff/index.mjs +++ b/deps/npm/node_modules/just-diff/index.mjs @@ -124,9 +124,13 @@ function diff(obj1, obj2, pathConverter) { } } - return diffs.remove.reverse().concat(diffs.replace).concat(diffs.add); + return diffs; } - return getDiff(obj1, obj2, [], {remove: [], replace: [], add: []}); + const finalDiffs = getDiff(obj1, obj2, [], {remove: [], replace: [], add: []}); + return finalDiffs.remove + .reverse() + .concat(finalDiffs.replace) + .concat(finalDiffs.add); } function pushReplace(path, basePath, key, diffs, pathConverter, obj2) { diff --git a/deps/npm/node_modules/just-diff/package.json b/deps/npm/node_modules/just-diff/package.json index 5a4bb5f129c831..9c6a8bbe2f94b3 100644 --- a/deps/npm/node_modules/just-diff/package.json +++ b/deps/npm/node_modules/just-diff/package.json @@ -1,6 +1,6 @@ { "name": "just-diff", - "version": "5.0.2", + "version": "5.0.3", "description": "Return an object representing the diffs between two objects. Supports jsonPatch protocol", "main": "index.js", "module": "index.mjs", diff --git a/deps/npm/node_modules/lru-cache/index.d.ts b/deps/npm/node_modules/lru-cache/index.d.ts new file mode 100644 index 00000000000000..b9375a8b96a716 --- /dev/null +++ b/deps/npm/node_modules/lru-cache/index.d.ts @@ -0,0 +1,593 @@ +// Type definitions for lru-cache 7.10.0 +// Project: https://github.com/isaacs/node-lru-cache +// Based initially on @types/lru-cache +// https://github.com/DefinitelyTyped/DefinitelyTyped +// used under the terms of the MIT License, shown below. +// +// DefinitelyTyped license: +// ------ +// MIT License +// +// Copyright (c) Microsoft Corporation. +// +// 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 +// ------ +// +// Changes by Isaac Z. Schlueter released under the terms found in the +// LICENSE file within this project. + +/// +//tslint:disable:member-access +declare class LRUCache implements Iterable<[K, V]> { + constructor(options: LRUCache.Options) + + /** + * Number of items in the cache. + * Alias for `cache.size` + * + * @deprecated since 7.0 use `cache.size` instead + */ + public readonly length: number + + public readonly max: number + public readonly maxSize: number + public readonly sizeCalculation: + | LRUCache.SizeCalculator + | undefined + public readonly dispose: LRUCache.Disposer + /** + * @since 7.4.0 + */ + public readonly disposeAfter: LRUCache.Disposer | null + public readonly noDisposeOnSet: boolean + public readonly ttl: number + public readonly ttlResolution: number + public readonly ttlAutopurge: boolean + public readonly allowStale: boolean + public readonly updateAgeOnGet: boolean + /** + * @since 7.11.0 + */ + public readonly noDeleteOnStaleGet: boolean + /** + * @since 7.6.0 + */ + public readonly fetchMethod: LRUCache.Fetcher | null + + /** + * The total number of items held in the cache at the current moment. + */ + public readonly size: number + + /** + * The total size of items in cache when using size tracking. + */ + public readonly calculatedSize: number + + /** + * Add a value to the cache. + */ + public set( + key: K, + value: V, + options?: LRUCache.SetOptions + ): this + + /** + * Return a value from the cache. + * Will update the recency of the cache entry found. + * If the key is not found, `get()` will return `undefined`. + * This can be confusing when setting values specifically to `undefined`, + * as in `cache.set(key, undefined)`. Use `cache.has()` to determine + * whether a key is present in the cache at all. + */ + // tslint:disable-next-line:no-unnecessary-generics + public get( + key: K, + options?: LRUCache.GetOptions + ): T | undefined + + /** + * Like `get()` but doesn't update recency or delete stale items. + * Returns `undefined` if the item is stale, unless `allowStale` is set + * either on the cache or in the options object. + */ + // tslint:disable-next-line:no-unnecessary-generics + public peek( + key: K, + options?: LRUCache.PeekOptions + ): T | undefined + + /** + * Check if a key is in the cache, without updating the recency of use. + * Will return false if the item is stale, even though it is technically + * in the cache. + * Will not update item age unless `updateAgeOnHas` is set in the options + * or constructor. + */ + public has(key: K, options?: LRUCache.HasOptions): boolean + + /** + * Deletes a key out of the cache. + * Returns true if the key was deleted, false otherwise. + */ + public delete(key: K): boolean + + /** + * Clear the cache entirely, throwing away all values. + */ + public clear(): void + + /** + * Delete any stale entries. Returns true if anything was removed, false + * otherwise. + */ + public purgeStale(): boolean + + /** + * Find a value for which the supplied fn method returns a truthy value, + * similar to Array.find(). fn is called as fn(value, key, cache). + */ + // tslint:disable-next-line:no-unnecessary-generics + public find( + callbackFn: ( + value: V, + key: K, + cache: this + ) => boolean | undefined | void, + options?: LRUCache.GetOptions + ): T + + /** + * Call the supplied function on each item in the cache, in order from + * most recently used to least recently used. fn is called as + * fn(value, key, cache). Does not update age or recenty of use. + */ + public forEach( + callbackFn: (this: T, value: V, key: K, cache: this) => void, + thisArg?: T + ): void + + /** + * The same as `cache.forEach(...)` but items are iterated over in reverse + * order. (ie, less recently used items are iterated over first.) + */ + public rforEach( + callbackFn: (this: T, value: V, key: K, cache: this) => void, + thisArg?: T + ): void + + /** + * Return a generator yielding the keys in the cache, + * in order from most recently used to least recently used. + */ + public keys(): Generator + + /** + * Inverse order version of `cache.keys()` + * Return a generator yielding the keys in the cache, + * in order from least recently used to most recently used. + */ + public rkeys(): Generator + + /** + * Return a generator yielding the values in the cache, + * in order from most recently used to least recently used. + */ + public values(): Generator + + /** + * Inverse order version of `cache.values()` + * Return a generator yielding the values in the cache, + * in order from least recently used to most recently used. + */ + public rvalues(): Generator + + /** + * Return a generator yielding `[key, value]` pairs, + * in order from most recently used to least recently used. + */ + public entries(): Generator<[K, V]> + + /** + * Inverse order version of `cache.entries()` + * Return a generator yielding `[key, value]` pairs, + * in order from least recently used to most recently used. + */ + public rentries(): Generator<[K, V]> + + /** + * Iterating over the cache itself yields the same results as + * `cache.entries()` + */ + public [Symbol.iterator](): Iterator<[K, V]> + + /** + * Return an array of [key, entry] objects which can be passed to + * cache.load() + */ + public dump(): Array<[K, LRUCache.Entry]> + + /** + * Reset the cache and load in the items in entries in the order listed. + * Note that the shape of the resulting cache may be different if the + * same options are not used in both caches. + */ + public load( + cacheEntries: ReadonlyArray<[K, LRUCache.Entry]> + ): void + + /** + * Evict the least recently used item, returning its value or `undefined` + * if cache is empty. + */ + public pop(): V | undefined + + /** + * Deletes a key out of the cache. + * + * @deprecated since 7.0 use delete() instead + */ + public del(key: K): boolean + + /** + * Clear the cache entirely, throwing away all values. + * + * @deprecated since 7.0 use clear() instead + */ + public reset(): void + + /** + * Manually iterates over the entire cache proactively pruning old entries. + * + * @deprecated since 7.0 use purgeStale() instead + */ + public prune(): boolean + + /** + * since: 7.6.0 + */ + // tslint:disable-next-line:no-unnecessary-generics + public fetch( + key: K, + options?: LRUCache.FetchOptions + ): Promise + + /** + * since: 7.6.0 + */ + public getRemainingTTL(key: K): number +} + +declare namespace LRUCache { + type LRUMilliseconds = number + type DisposeReason = 'evict' | 'set' | 'delete' + + type SizeCalculator = (value: V, key: K) => number + type Disposer = ( + value: V, + key: K, + reason: DisposeReason + ) => void + type Fetcher = ( + key: K, + staleValue: V, + options: FetcherOptions + ) => Promise | V | void | undefined + + interface DeprecatedOptions { + /** + * alias for ttl + * + * @deprecated since 7.0 use options.ttl instead + */ + maxAge?: number + + /** + * alias for sizeCalculation + * + * @deprecated since 7.0 use options.sizeCalculation instead + */ + length?: SizeCalculator + + /** + * alias for allowStale + * + * @deprecated since 7.0 use options.allowStale instead + */ + stale?: boolean + } + + interface LimitedByCount { + /** + * The number of most recently used items to keep. + * Note that we may store fewer items than this if maxSize is hit. + */ + max: number + } + + interface LimitedBySize { + /** + * If you wish to track item size, you must provide a maxSize + * note that we still will only keep up to max *actual items*, + * if max is set, so size tracking may cause fewer than max items + * to be stored. At the extreme, a single item of maxSize size + * will cause everything else in the cache to be dropped when it + * is added. Use with caution! + * Note also that size tracking can negatively impact performance, + * though for most cases, only minimally. + */ + maxSize: number + + /** + * Function to calculate size of items. Useful if storing strings or + * buffers or other items where memory size depends on the object itself. + * Also note that oversized items do NOT immediately get dropped from + * the cache, though they will cause faster turnover in the storage. + */ + sizeCalculation?: SizeCalculator + } + + interface LimitedByTTL { + /** + * Max time in milliseconds for items to live in cache before they are + * considered stale. Note that stale items are NOT preemptively removed + * by default, and MAY live in the cache, contributing to its LRU max, + * long after they have expired. + * + * Also, as this cache is optimized for LRU/MRU operations, some of + * the staleness/TTL checks will reduce performance, as they will incur + * overhead by deleting items. + * + * Must be an integer number of ms, defaults to 0, which means "no TTL" + */ + ttl: number + + /** + * Boolean flag to tell the cache to not update the TTL when + * setting a new value for an existing key (ie, when updating a value + * rather than inserting a new value). Note that the TTL value is + * _always_ set (if provided) when adding a new entry into the cache. + * + * @default false + * @since 7.4.0 + */ + noUpdateTTL?: boolean + + /** + * Minimum amount of time in ms in which to check for staleness. + * Defaults to 1, which means that the current time is checked + * at most once per millisecond. + * + * Set to 0 to check the current time every time staleness is tested. + * (This reduces performance, and is theoretically unnecessary.) + * + * Setting this to a higher value will improve performance somewhat + * while using ttl tracking, albeit at the expense of keeping stale + * items around a bit longer than intended. + * + * @default 1 + * @since 7.1.0 + */ + ttlResolution?: number + + /** + * Preemptively remove stale items from the cache. + * Note that this may significantly degrade performance, + * especially if the cache is storing a large number of items. + * It is almost always best to just leave the stale items in + * the cache, and let them fall out as new items are added. + * + * Note that this means that allowStale is a bit pointless, + * as stale items will be deleted almost as soon as they expire. + * + * Use with caution! + * + * @default false + * @since 7.1.0 + */ + ttlAutopurge?: boolean + + /** + * Return stale items from cache.get() before disposing of them. + * Return stale values from cache.fetch() while performing a call + * to the `fetchMethod` in the background. + * + * @default false + */ + allowStale?: boolean + + /** + * Update the age of items on cache.get(), renewing their TTL + * + * @default false + */ + updateAgeOnGet?: boolean + + /** + * Do not delete stale items when they are retrieved with cache.get() + * Note that the get() return value will still be `undefined` unless + * allowStale is true. + * + * @default false + * @since 7.11.0 + */ + noDeleteOnStaleGet?: boolean + + /** + * Update the age of items on cache.has(), renewing their TTL + * + * @default false + */ + updateAgeOnHas?: boolean + } + + type SafetyBounds = + | LimitedByCount + | LimitedBySize + | LimitedByTTL + + // options shared by all three of the limiting scenarios + interface SharedOptions { + /** + * Function that is called on items when they are dropped from the cache. + * This can be handy if you want to close file descriptors or do other + * cleanup tasks when items are no longer accessible. Called with `key, + * value`. It's called before actually removing the item from the + * internal cache, so it is *NOT* safe to re-add them. + * Use `disposeAfter` if you wish to dispose items after they have been + * full removed, when it is safe to add them back to the cache. + */ + dispose?: Disposer + + /** + * The same as dispose, but called *after* the entry is completely + * removed and the cache is once again in a clean state. It is safe to + * add an item right back into the cache at this point. + * However, note that it is *very* easy to inadvertently create infinite + * recursion this way. + * + * @since 7.3.0 + */ + disposeAfter?: Disposer + + /** + * Set to true to suppress calling the dispose() function if the entry + * key is still accessible within the cache. + * This may be overridden by passing an options object to cache.set(). + * + * @default false + */ + noDisposeOnSet?: boolean + + /** + * `fetchMethod` Function that is used to make background asynchronous + * fetches. Called with `fetchMethod(key, staleValue)`. May return a + * Promise. + * + * If `fetchMethod` is not provided, then `cache.fetch(key)` is + * equivalent to `Promise.resolve(cache.get(key))`. + * + * @since 7.6.0 + */ + fetchMethod?: LRUCache.Fetcher + + /** + * Set to true to suppress the deletion of stale data when a + * `fetchMethod` throws an error or returns a rejected promise + * + * @default false + * @since 7.10.0 + */ + noDeleteOnFetchRejection?: boolean + + /** + * Set to any value in the constructor or fetch() options to + * pass arbitrary data to the fetch() method in the options.context + * field. + * + * @since 7.12.0 + */ + fetchContext?: any + } + + type Options = SharedOptions & + DeprecatedOptions & + SafetyBounds + + /** + * options which override the options set in the LRUCache constructor + * when making `cache.set()` calls. + */ + interface SetOptions { + /** + * A value for the size of the entry, prevents calls to + * `sizeCalculation` function. + */ + size?: number + sizeCalculation?: SizeCalculator + ttl?: number + start?: number + noDisposeOnSet?: boolean + noUpdateTTL?: boolean + } + + /** + * options which override the options set in the LRUCAche constructor + * when making `cache.has()` calls. + */ + interface HasOptions { + updateAgeOnHas?: boolean + } + + /** + * options which override the options set in the LRUCache constructor + * when making `cache.get()` calls. + */ + interface GetOptions { + allowStale?: boolean + updateAgeOnGet?: boolean + noDeleteOnStaleGet?: boolean + } + + /** + * options which override the options set in the LRUCache constructor + * when making `cache.peek()` calls. + */ + interface PeekOptions { + allowStale?: boolean + } + + interface FetcherFetchOptions { + allowStale?: boolean + updateAgeOnGet?: boolean + noDeleteOnStaleGet?: boolean + size?: number + sizeCalculation?: SizeCalculator + ttl?: number + noDisposeOnSet?: boolean + noUpdateTTL?: boolean + noDeleteOnFetchRejection?: boolean + } + + /** + * options which override the options set in the LRUCache constructor + * when making `cache.fetch()` calls. + * This is the union of GetOptions and SetOptions, plus the + * `noDeleteOnFetchRejection` and `fetchContext` fields. + */ + interface FetchOptions extends FetcherFetchOptions { + fetchContext?: any + } + + interface FetcherOptions { + signal: AbortSignal + options: FetcherFetchOptions + context: any + } + + interface Entry { + value: V + ttl?: number + size?: number + start?: number + } +} + +export = LRUCache diff --git a/deps/npm/node_modules/lru-cache/index.js b/deps/npm/node_modules/lru-cache/index.js index fb1a076fa3ae8e..479ffc8656b703 100644 --- a/deps/npm/node_modules/lru-cache/index.js +++ b/deps/npm/node_modules/lru-cache/index.js @@ -1,5 +1,9 @@ -const perf = typeof performance === 'object' && performance && - typeof performance.now === 'function' ? performance : Date +const perf = + typeof performance === 'object' && + performance && + typeof performance.now === 'function' + ? performance + : Date const hasAbortController = typeof AbortController === 'function' @@ -7,20 +11,30 @@ const hasAbortController = typeof AbortController === 'function' // this doesn't have nearly all the checks and whatnot that // actual AbortController/Signal has, but it's enough for // our purposes, and if used properly, behaves the same. -const AC = hasAbortController ? AbortController : Object.assign( - class AbortController { - constructor () { this.signal = new AC.AbortSignal } - abort () { - this.signal.dispatchEvent('abort') - } - }, - { - AbortSignal: class AbortSignal { - constructor () { +const AC = hasAbortController + ? AbortController + : class AbortController { + constructor() { + this.signal = new AS() + } + abort() { + this.signal.dispatchEvent('abort') + } + } + +const hasAbortSignal = typeof AbortSignal === 'function' +// Some polyfills put this on the AC class, not global +const hasACAbortSignal = typeof AC.AbortSignal === 'function' +const AS = hasAbortSignal + ? AbortSignal + : hasACAbortSignal + ? AC.AbortController + : class AbortSignal { + constructor() { this.aborted = false this._listeners = [] } - dispatchEvent (type) { + dispatchEvent(type) { if (type === 'abort') { this.aborted = true const e = { type, target: this } @@ -28,20 +42,18 @@ const AC = hasAbortController ? AbortController : Object.assign( this._listeners.forEach(f => f(e), this) } } - onabort () {} - addEventListener (ev, fn) { + onabort() {} + addEventListener(ev, fn) { if (ev === 'abort') { this._listeners.push(fn) } } - removeEventListener (ev, fn) { + removeEventListener(ev, fn) { if (ev === 'abort') { this._listeners = this._listeners.filter(f => f !== fn) } } } - } -) const warned = new Set() const deprecatedOption = (opt, instead) => { @@ -69,10 +81,10 @@ const deprecatedProperty = (field, instead) => { const emitWarning = (...a) => { typeof process === 'object' && - process && - typeof process.emitWarning === 'function' - ? process.emitWarning(...a) - : console.error(...a) + process && + typeof process.emitWarning === 'function' + ? process.emitWarning(...a) + : console.error(...a) } const shouldWarn = code => !warned.has(code) @@ -93,22 +105,28 @@ const isPosInt = n => n && n === Math.floor(n) && n > 0 && isFinite(n) * zeroes at init time is brutal when you get that big. * But why not be complete? * Maybe in the future, these limits will have expanded. */ -const getUintArray = max => !isPosInt(max) ? null -: max <= Math.pow(2, 8) ? Uint8Array -: max <= Math.pow(2, 16) ? Uint16Array -: max <= Math.pow(2, 32) ? Uint32Array -: max <= Number.MAX_SAFE_INTEGER ? ZeroArray -: null +const getUintArray = max => + !isPosInt(max) + ? null + : max <= Math.pow(2, 8) + ? Uint8Array + : max <= Math.pow(2, 16) + ? Uint16Array + : max <= Math.pow(2, 32) + ? Uint32Array + : max <= Number.MAX_SAFE_INTEGER + ? ZeroArray + : null class ZeroArray extends Array { - constructor (size) { + constructor(size) { super(size) this.fill(0) } } class Stack { - constructor (max) { + constructor(max) { if (max === 0) { return [] } @@ -116,16 +134,16 @@ class Stack { this.heap = new UintArray(max) this.length = 0 } - push (n) { + push(n) { this.heap[this.length++] = n } - pop () { + pop() { return this.heap[--this.length] } } class LRUCache { - constructor (options = {}) { + constructor(options = {}) { const { max = 0, ttl, @@ -141,15 +159,15 @@ class LRUCache { maxSize = 0, sizeCalculation, fetchMethod, + fetchContext, + noDeleteOnFetchRejection, + noDeleteOnStaleGet, } = options // deprecated options, don't trigger a warning for getting them if // the thing being passed in is another LRUCache we're copying. - const { - length, - maxAge, - stale, - } = options instanceof LRUCache ? {} : options + const { length, maxAge, stale } = + options instanceof LRUCache ? {} : options if (max !== 0 && !isPosInt(max)) { throw new TypeError('max option must be a nonnegative integer') @@ -165,7 +183,9 @@ class LRUCache { this.sizeCalculation = sizeCalculation || length if (this.sizeCalculation) { if (!this.maxSize) { - throw new TypeError('cannot set sizeCalculation without setting maxSize') + throw new TypeError( + 'cannot set sizeCalculation without setting maxSize' + ) } if (typeof this.sizeCalculation !== 'function') { throw new TypeError('sizeCalculation set to non-function') @@ -174,7 +194,16 @@ class LRUCache { this.fetchMethod = fetchMethod || null if (this.fetchMethod && typeof this.fetchMethod !== 'function') { - throw new TypeError('fetchMethod must be a function if specified') + throw new TypeError( + 'fetchMethod must be a function if specified' + ) + } + + this.fetchContext = fetchContext + if (!this.fetchMethod && fetchContext !== undefined) { + throw new TypeError( + 'cannot set fetchContext without fetchMethod' + ) } this.keyMap = new Map() @@ -200,37 +229,48 @@ class LRUCache { } this.noDisposeOnSet = !!noDisposeOnSet this.noUpdateTTL = !!noUpdateTTL + this.noDeleteOnFetchRejection = !!noDeleteOnFetchRejection if (this.maxSize !== 0) { if (!isPosInt(this.maxSize)) { - throw new TypeError('maxSize must be a positive integer if specified') + throw new TypeError( + 'maxSize must be a positive integer if specified' + ) } this.initializeSizeTracking() } this.allowStale = !!allowStale || !!stale + this.noDeleteOnStaleGet = !!noDeleteOnStaleGet this.updateAgeOnGet = !!updateAgeOnGet this.updateAgeOnHas = !!updateAgeOnHas - this.ttlResolution = isPosInt(ttlResolution) || ttlResolution === 0 - ? ttlResolution : 1 + this.ttlResolution = + isPosInt(ttlResolution) || ttlResolution === 0 + ? ttlResolution + : 1 this.ttlAutopurge = !!ttlAutopurge this.ttl = ttl || maxAge || 0 if (this.ttl) { if (!isPosInt(this.ttl)) { - throw new TypeError('ttl must be a positive integer if specified') + throw new TypeError( + 'ttl must be a positive integer if specified' + ) } this.initializeTTLTracking() } // do not allow completely unbounded caches if (this.max === 0 && this.ttl === 0 && this.maxSize === 0) { - throw new TypeError('At least one of max, maxSize, or ttl is required') + throw new TypeError( + 'At least one of max, maxSize, or ttl is required' + ) } if (!this.ttlAutopurge && !this.max && !this.maxSize) { const code = 'LRU_CACHE_UNBOUNDED' if (shouldWarn(code)) { warned.add(code) - const msg = 'TTL caching without ttlAutopurge, max, or maxSize can ' + + const msg = + 'TTL caching without ttlAutopurge, max, or maxSize can ' + 'result in unbounded memory consumption.' emitWarning(msg, 'UnboundedCacheWarning', code, LRUCache) } @@ -247,16 +287,16 @@ class LRUCache { } } - getRemainingTTL (key) { + getRemainingTTL(key) { return this.has(key, { updateAgeOnHas: false }) ? Infinity : 0 } - initializeTTLTracking () { + initializeTTLTracking() { this.ttls = new ZeroArray(this.max) this.starts = new ZeroArray(this.max) - this.setItemTTL = (index, ttl) => { - this.starts[index] = ttl !== 0 ? perf.now() : 0 + this.setItemTTL = (index, ttl, start = perf.now()) => { + this.starts[index] = ttl !== 0 ? start : 0 this.ttls[index] = ttl if (ttl !== 0 && this.ttlAutopurge) { const t = setTimeout(() => { @@ -271,7 +311,7 @@ class LRUCache { } } - this.updateItemAge = (index) => { + this.updateItemAge = index => { this.starts[index] = this.ttls[index] !== 0 ? perf.now() : 0 } @@ -282,7 +322,10 @@ class LRUCache { const n = perf.now() if (this.ttlResolution > 0) { cachedNow = n - const t = setTimeout(() => cachedNow = 0, this.ttlResolution) + const t = setTimeout( + () => (cachedNow = 0), + this.ttlResolution + ) /* istanbul ignore else - not available on all platforms */ if (t.unref) { t.unref() @@ -291,28 +334,38 @@ class LRUCache { return n } - this.getRemainingTTL = (key) => { + this.getRemainingTTL = key => { const index = this.keyMap.get(key) if (index === undefined) { return 0 } - return this.ttls[index] === 0 || this.starts[index] === 0 ? Infinity - : ((this.starts[index] + this.ttls[index]) - (cachedNow || getNow())) + return this.ttls[index] === 0 || this.starts[index] === 0 + ? Infinity + : this.starts[index] + + this.ttls[index] - + (cachedNow || getNow()) } - this.isStale = (index) => { - return this.ttls[index] !== 0 && this.starts[index] !== 0 && - ((cachedNow || getNow()) - this.starts[index] > this.ttls[index]) + this.isStale = index => { + return ( + this.ttls[index] !== 0 && + this.starts[index] !== 0 && + (cachedNow || getNow()) - this.starts[index] > + this.ttls[index] + ) } } - updateItemAge (index) {} - setItemTTL (index, ttl) {} - isStale (index) { return false } + updateItemAge(index) {} + setItemTTL(index, ttl, start) {} + isStale(index) { + return false + } - initializeSizeTracking () { + initializeSizeTracking() { this.calculatedSize = 0 this.sizes = new ZeroArray(this.max) - this.removeItemSize = index => this.calculatedSize -= this.sizes[index] + this.removeItemSize = index => + (this.calculatedSize -= this.sizes[index]) this.requireSize = (k, v, size, sizeCalculation) => { if (!isPosInt(size)) { if (sizeCalculation) { @@ -321,10 +374,14 @@ class LRUCache { } size = sizeCalculation(v, k) if (!isPosInt(size)) { - throw new TypeError('sizeCalculation return invalid (expect positive integer)') + throw new TypeError( + 'sizeCalculation return invalid (expect positive integer)' + ) } } else { - throw new TypeError('invalid size value (must be positive integer)') + throw new TypeError( + 'invalid size value (must be positive integer)' + ) } } return size @@ -338,15 +395,17 @@ class LRUCache { this.calculatedSize += this.sizes[index] } } - removeItemSize (index) {} - addItemSize (index, v, k, size) {} - requireSize (k, v, size, sizeCalculation) { + removeItemSize(index) {} + addItemSize(index, v, k, size) {} + requireSize(k, v, size, sizeCalculation) { if (size || sizeCalculation) { - throw new TypeError('cannot set size without setting maxSize on cache') + throw new TypeError( + 'cannot set size without setting maxSize on cache' + ) } } - *indexes ({ allowStale = this.allowStale } = {}) { + *indexes({ allowStale = this.allowStale } = {}) { if (this.size) { for (let i = this.tail; true; ) { if (!this.isValidIndex(i)) { @@ -364,7 +423,7 @@ class LRUCache { } } - *rindexes ({ allowStale = this.allowStale } = {}) { + *rindexes({ allowStale = this.allowStale } = {}) { if (this.size) { for (let i = this.head; true; ) { if (!this.isValidIndex(i)) { @@ -382,48 +441,48 @@ class LRUCache { } } - isValidIndex (index) { + isValidIndex(index) { return this.keyMap.get(this.keyList[index]) === index } - *entries () { + *entries() { for (const i of this.indexes()) { yield [this.keyList[i], this.valList[i]] } } - *rentries () { + *rentries() { for (const i of this.rindexes()) { yield [this.keyList[i], this.valList[i]] } } - *keys () { + *keys() { for (const i of this.indexes()) { yield this.keyList[i] } } - *rkeys () { + *rkeys() { for (const i of this.rindexes()) { yield this.keyList[i] } } - *values () { + *values() { for (const i of this.indexes()) { yield this.valList[i] } } - *rvalues () { + *rvalues() { for (const i of this.rindexes()) { yield this.valList[i] } } - [Symbol.iterator] () { + [Symbol.iterator]() { return this.entries() } - find (fn, getOptions = {}) { + find(fn, getOptions = {}) { for (const i of this.indexes()) { if (fn(this.valList[i], this.keyList[i], this)) { return this.get(this.keyList[i], getOptions) @@ -431,24 +490,24 @@ class LRUCache { } } - forEach (fn, thisp = this) { + forEach(fn, thisp = this) { for (const i of this.indexes()) { fn.call(thisp, this.valList[i], this.keyList[i], this) } } - rforEach (fn, thisp = this) { + rforEach(fn, thisp = this) { for (const i of this.rindexes()) { fn.call(thisp, this.valList[i], this.keyList[i], this) } } - get prune () { + get prune() { deprecatedMethod('prune', 'purgeStale') return this.purgeStale } - purgeStale () { + purgeStale() { let deleted = false for (const i of this.rindexes({ allowStale: true })) { if (this.isStale(i)) { @@ -459,14 +518,19 @@ class LRUCache { return deleted } - dump () { + dump() { const arr = [] - for (const i of this.indexes()) { + for (const i of this.indexes({ allowStale: true })) { const key = this.keyList[i] - const value = this.valList[i] + const v = this.valList[i] + const value = this.isBackgroundFetch(v) ? v.__staleWhileFetching : v const entry = { value } if (this.ttls) { entry.ttl = this.ttls[i] + // always dump the start relative to a portable timestamp + // it's ok for this to be a bit slow, it's a rare operation. + const age = perf.now() - this.starts[i] + entry.start = Math.floor(Date.now() - age) } if (this.sizes) { entry.size = this.sizes[i] @@ -476,22 +540,34 @@ class LRUCache { return arr } - load (arr) { + load(arr) { this.clear() for (const [key, entry] of arr) { + if (entry.start) { + // entry.start is a portable timestamp, but we may be using + // node's performance.now(), so calculate the offset. + // it's ok for this to be a bit slow, it's a rare operation. + const age = Date.now() - entry.start + entry.start = perf.now() - age + } this.set(key, entry.value, entry) } } - dispose (v, k, reason) {} + dispose(v, k, reason) {} - set (k, v, { - ttl = this.ttl, - noDisposeOnSet = this.noDisposeOnSet, - size = 0, - sizeCalculation = this.sizeCalculation, - noUpdateTTL = this.noUpdateTTL, - } = {}) { + set( + k, + v, + { + ttl = this.ttl, + start, + noDisposeOnSet = this.noDisposeOnSet, + size = 0, + sizeCalculation = this.sizeCalculation, + noUpdateTTL = this.noUpdateTTL, + } = {} + ) { size = this.requireSize(k, v, size, sizeCalculation) let index = this.size === 0 ? undefined : this.keyMap.get(k) if (index === undefined) { @@ -503,7 +579,7 @@ class LRUCache { this.next[this.tail] = index this.prev[index] = this.tail this.tail = index - this.size ++ + this.size++ this.addItemSize(index, v, k, size) noUpdateTTL = false } else { @@ -530,7 +606,7 @@ class LRUCache { this.initializeTTLTracking() } if (!noUpdateTTL) { - this.setItemTTL(index, ttl) + this.setItemTTL(index, ttl, start) } if (this.disposeAfter) { while (this.disposed.length) { @@ -540,7 +616,7 @@ class LRUCache { return this } - newIndex () { + newIndex() { if (this.size === 0) { return this.tail } @@ -554,7 +630,7 @@ class LRUCache { return this.initialFill++ } - pop () { + pop() { if (this.size) { const val = this.valList[this.head] this.evict(true) @@ -562,7 +638,7 @@ class LRUCache { } } - evict (free) { + evict(free) { const head = this.head const k = this.keyList[head] const v = this.valList[head] @@ -583,11 +659,11 @@ class LRUCache { } this.head = this.next[head] this.keyMap.delete(k) - this.size -- + this.size-- return head } - has (k, { updateAgeOnHas = this.updateAgeOnHas } = {}) { + has(k, { updateAgeOnHas = this.updateAgeOnHas } = {}) { const index = this.keyMap.get(k) if (index !== undefined) { if (!this.isStale(index)) { @@ -601,14 +677,14 @@ class LRUCache { } // like get(), but without any LRU updating or TTL expiration - peek (k, { allowStale = this.allowStale } = {}) { + peek(k, { allowStale = this.allowStale } = {}) { const index = this.keyMap.get(k) if (index !== undefined && (allowStale || !this.isStale(index))) { return this.valList[index] } } - backgroundFetch (k, index, options) { + backgroundFetch(k, index, options, context) { const v = index === undefined ? undefined : this.valList[index] if (this.isBackgroundFetch(v)) { return v @@ -617,15 +693,36 @@ class LRUCache { const fetchOpts = { signal: ac.signal, options, + context, } - const p = Promise.resolve(this.fetchMethod(k, v, fetchOpts)).then(v => { + const cb = v => { if (!ac.signal.aborted) { this.set(k, v, fetchOpts.options) } return v - }) + } + const eb = er => { + if (this.valList[index] === p) { + const del = + !options.noDeleteOnFetchRejection || + p.__staleWhileFetching === undefined + if (del) { + this.delete(k) + } else { + // still replace the *promise* with the stale value, + // since we are done with the promise at this point. + this.valList[index] = p.__staleWhileFetching + } + } + if (p.__returned === p) { + throw er + } + } + const pcall = res => res(this.fetchMethod(k, v, fetchOpts)) + const p = new Promise(pcall).then(cb, eb) p.__abortController = ac p.__staleWhileFetching = v + p.__returned = null if (index === undefined) { this.set(k, p, fetchOpts.options) index = this.keyMap.get(k) @@ -635,44 +732,66 @@ class LRUCache { return p } - isBackgroundFetch (p) { - return p && typeof p === 'object' && typeof p.then === 'function' && - Object.prototype.hasOwnProperty.call(p, '__staleWhileFetching') + isBackgroundFetch(p) { + return ( + p && + typeof p === 'object' && + typeof p.then === 'function' && + Object.prototype.hasOwnProperty.call( + p, + '__staleWhileFetching' + ) && + Object.prototype.hasOwnProperty.call(p, '__returned') && + (p.__returned === p || p.__returned === null) + ) } // this takes the union of get() and set() opts, because it does both - async fetch (k, { - allowStale = this.allowStale, - updateAgeOnGet = this.updateAgeOnGet, - ttl = this.ttl, - noDisposeOnSet = this.noDisposeOnSet, - size = 0, - sizeCalculation = this.sizeCalculation, - noUpdateTTL = this.noUpdateTTL, - } = {}) { + async fetch( + k, + { + // get options + allowStale = this.allowStale, + updateAgeOnGet = this.updateAgeOnGet, + noDeleteOnStaleGet = this.noDeleteOnStaleGet, + // set options + ttl = this.ttl, + noDisposeOnSet = this.noDisposeOnSet, + size = 0, + sizeCalculation = this.sizeCalculation, + noUpdateTTL = this.noUpdateTTL, + // fetch exclusive options + noDeleteOnFetchRejection = this.noDeleteOnFetchRejection, + fetchContext = this.fetchContext, + } = {} + ) { if (!this.fetchMethod) { - return this.get(k, {allowStale, updateAgeOnGet}) + return this.get(k, { allowStale, updateAgeOnGet, noDeleteOnStaleGet }) } const options = { allowStale, updateAgeOnGet, + noDeleteOnStaleGet, ttl, noDisposeOnSet, size, sizeCalculation, noUpdateTTL, + noDeleteOnFetchRejection, } let index = this.keyMap.get(k) if (index === undefined) { - return this.backgroundFetch(k, index, options) + const p = this.backgroundFetch(k, index, options, fetchContext) + return (p.__returned = p) } else { // in cache, maybe already fetching const v = this.valList[index] if (this.isBackgroundFetch(v)) { return allowStale && v.__staleWhileFetching !== undefined - ? v.__staleWhileFetching : v + ? v.__staleWhileFetching + : (v.__returned = v) } if (!this.isStale(index)) { @@ -685,16 +804,21 @@ class LRUCache { // ok, it is stale, and not already fetching // refresh the cache. - const p = this.backgroundFetch(k, index, options) + const p = this.backgroundFetch(k, index, options, fetchContext) return allowStale && p.__staleWhileFetching !== undefined - ? p.__staleWhileFetching : p + ? p.__staleWhileFetching + : (p.__returned = p) } } - get (k, { - allowStale = this.allowStale, - updateAgeOnGet = this.updateAgeOnGet, - } = {}) { + get( + k, + { + allowStale = this.allowStale, + updateAgeOnGet = this.updateAgeOnGet, + noDeleteOnStaleGet = this.noDeleteOnStaleGet, + } = {} + ) { const index = this.keyMap.get(k) if (index !== undefined) { const value = this.valList[index] @@ -702,7 +826,9 @@ class LRUCache { if (this.isStale(index)) { // delete only if not an in-flight background fetch if (!fetching) { - this.delete(k) + if (!noDeleteOnStaleGet) { + this.delete(k) + } return allowStale ? value : undefined } else { return allowStale ? value.__staleWhileFetching : undefined @@ -723,12 +849,12 @@ class LRUCache { } } - connect (p, n) { + connect(p, n) { this.prev[n] = p this.next[p] = n } - moveToTail (index) { + moveToTail(index) { // if tail already, nothing to do // if head, move head to next[index] // else @@ -748,12 +874,12 @@ class LRUCache { } } - get del () { + get del() { deprecatedMethod('del', 'delete') return this.delete } - delete (k) { + delete(k) { let deleted = false if (this.size !== 0) { const index = this.keyMap.get(k) @@ -783,7 +909,7 @@ class LRUCache { this.next[this.prev[index]] = this.next[index] this.prev[this.next[index]] = this.prev[index] } - this.size -- + this.size-- this.free.push(index) } } @@ -796,7 +922,7 @@ class LRUCache { return deleted } - clear () { + clear() { for (const index of this.rindexes({ allowStale: true })) { const v = this.valList[index] if (this.isBackgroundFetch(v)) { @@ -833,19 +959,22 @@ class LRUCache { } } - get reset () { + get reset() { deprecatedMethod('reset', 'clear') return this.clear } - get length () { + get length() { deprecatedProperty('length', 'size') return this.size } - static get AbortController () { + static get AbortController() { return AC } + static get AbortSignal() { + return AS + } } module.exports = LRUCache diff --git a/deps/npm/node_modules/lru-cache/package.json b/deps/npm/node_modules/lru-cache/package.json index 5364b09d2002c1..c023ce6c49aca2 100644 --- a/deps/npm/node_modules/lru-cache/package.json +++ b/deps/npm/node_modules/lru-cache/package.json @@ -1,7 +1,7 @@ { "name": "lru-cache", "description": "A cache object that deletes the least-recently-used items.", - "version": "7.9.0", + "version": "7.12.0", "author": "Isaac Z. Schlueter ", "keywords": [ "mru", @@ -10,34 +10,60 @@ ], "scripts": { "build": "", + "size": "size-limit", "test": "tap", "snap": "tap", - "size": "size-limit", "preversion": "npm test", "postversion": "npm publish", - "prepublishOnly": "git push origin --follow-tags" + "prepublishOnly": "git push origin --follow-tags", + "format": "prettier --write ." }, "main": "index.js", "repository": "git://github.com/isaacs/node-lru-cache.git", "devDependencies": { "@size-limit/preset-small-lib": "^7.0.8", + "@types/node": "^17.0.31", + "@types/tap": "^15.0.6", "benchmark": "^2.1.4", + "c8": "^7.11.2", "clock-mock": "^1.0.4", + "eslint-config-prettier": "^8.5.0", + "prettier": "^2.6.2", "size-limit": "^7.0.8", - "tap": "^15.1.6" + "tap": "^16.0.1", + "ts-node": "^10.7.0", + "tslib": "^2.4.0", + "typescript": "^4.6.4" }, "license": "ISC", "files": [ - "index.js" + "index.js", + "index.d.ts" ], "engines": { "node": ">=12" }, + "prettier": { + "semi": false, + "printWidth": 70, + "tabWidth": 2, + "useTabs": false, + "singleQuote": true, + "jsxSingleQuote": false, + "bracketSameLine": true, + "arrowParens": "avoid", + "endOfLine": "lf" + }, "tap": { - "coverage-map": "map.js", + "nyc-arg": [ + "--include=index.js" + ], "node-arg": [ - "--expose-gc" - ] + "--expose-gc", + "--require", + "ts-node/register" + ], + "ts": false }, "size-limit": [ { diff --git a/deps/npm/node_modules/minipass/LICENSE b/deps/npm/node_modules/minipass/LICENSE index 20a47625409237..bf1dece2e1f122 100644 --- a/deps/npm/node_modules/minipass/LICENSE +++ b/deps/npm/node_modules/minipass/LICENSE @@ -1,6 +1,6 @@ The ISC License -Copyright (c) npm, Inc. and Contributors +Copyright (c) 2017-2022 npm, Inc., Isaac Z. Schlueter, and Contributors Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above diff --git a/deps/npm/node_modules/minipass/index.d.ts b/deps/npm/node_modules/minipass/index.d.ts new file mode 100644 index 00000000000000..edbef548248014 --- /dev/null +++ b/deps/npm/node_modules/minipass/index.d.ts @@ -0,0 +1,149 @@ +/// +import { EventEmitter } from 'events' +import { Stream } from 'stream' + +export type Encoding = BufferEncoding | 'buffer' | null + +interface Writable extends EventEmitter { + end(): any + write(chunk: any, ...args: any[]): any +} + +interface Readable extends EventEmitter { + pause(): any + resume(): any + pipe(): any +} + +interface Pipe { + src: Minipass + dest: Writable + opts: PipeOptions +} + +type DualIterable = Iterable & AsyncIterable + +type ContiguousData = Buffer | ArrayBufferLike | ArrayBufferView | string + +type BufferOrString = Buffer | string + +export default class Minipass< + RType extends any = Buffer, + WType extends any = RType extends BufferOrString ? ContiguousData : RType + > + extends Stream + implements DualIterable +{ + static isStream(stream: any): stream is Readable | Writable + + readonly bufferLength: number + readonly flowing: boolean + readonly writable: boolean + readonly readable: boolean + readonly paused: boolean + readonly emittedEnd: boolean + readonly destroyed: boolean + + /** + * Not technically private or readonly, but not safe to mutate. + */ + private readonly buffer: RType[] + private readonly pipes: Pipe[] + + /** + * Technically writable, but mutating it can change the type, + * so is not safe to do in TypeScript. + */ + readonly objectMode: boolean + async: boolean + + /** + * Note: encoding is not actually read-only, and setEncoding(enc) + * exists. However, this type definition will insist that TypeScript + * programs declare the type of a Minipass stream up front, and if + * that type is string, then an encoding MUST be set in the ctor. If + * the type is Buffer, then the encoding must be missing, or set to + * 'buffer' or null. If the type is anything else, then objectMode + * must be set in the constructor options. So there is effectively + * no allowed way that a TS program can set the encoding after + * construction, as doing so will destroy any hope of type safety. + * TypeScript does not provide many options for changing the type of + * an object at run-time, which is what changing the encoding does. + */ + readonly encoding: Encoding + // setEncoding(encoding: Encoding): void + + // Options required if not reading buffers + constructor( + ...args: RType extends Buffer + ? [] | [Options] + : [Options] + ) + + write(chunk: WType, cb?: () => void): boolean + write(chunk: WType, encoding?: Encoding, cb?: () => void): boolean + read(size?: number): RType + end(cb?: () => void): this + end(chunk: any, cb?: () => void): this + end(chunk: any, encoding?: Encoding, cb?: () => void): this + pause(): void + resume(): void + promise(): Promise + collect(): Promise + + concat(): RType extends BufferOrString ? Promise : never + destroy(er?: any): void + pipe(dest: W, opts?: PipeOptions): W + unpipe(dest: W): void + + /** + * alias for on() + */ + addEventHandler(event: string, listener: (...args: any[]) => any): this + + on(event: string, listener: (...args: any[]) => any): this + on(event: 'data', listener: (chunk: RType) => any): this + on(event: 'error', listener: (error: any) => any): this + on( + event: + | 'readable' + | 'drain' + | 'resume' + | 'end' + | 'prefinish' + | 'finish' + | 'close', + listener: () => any + ): this + + [Symbol.iterator](): Iterator + [Symbol.asyncIterator](): AsyncIterator +} + +interface StringOptions { + encoding: BufferEncoding + objectMode?: boolean + async?: boolean +} + +interface BufferOptions { + encoding?: null | 'buffer' + objectMode?: boolean + async?: boolean +} + +interface ObjectModeOptions { + objectMode: true + async?: boolean +} + +export declare interface PipeOptions { + end?: boolean + proxyErrors?: boolean +} + +export declare type Options = T extends string + ? StringOptions + : T extends Buffer + ? BufferOptions + : ObjectModeOptions diff --git a/deps/npm/node_modules/minipass/index.js b/deps/npm/node_modules/minipass/index.js index 1835dd9bcf5121..e8797aab6cc276 100644 --- a/deps/npm/node_modules/minipass/index.js +++ b/deps/npm/node_modules/minipass/index.js @@ -5,7 +5,6 @@ const proc = typeof process === 'object' && process ? process : { } const EE = require('events') const Stream = require('stream') -const Yallist = require('yallist') const SD = require('string_decoder').StringDecoder const EOF = Symbol('EOF') @@ -27,6 +26,12 @@ const BUFFERPUSH = Symbol('bufferPush') const BUFFERSHIFT = Symbol('bufferShift') const OBJECTMODE = Symbol('objectMode') const DESTROYED = Symbol('destroyed') +const EMITDATA = Symbol('emitData') +const EMITEND = Symbol('emitEnd') +const EMITEND2 = Symbol('emitEnd2') +const ASYNC = Symbol('async') + +const defer = fn => Promise.resolve().then(fn) // TODO remove when Node v8 support drops const doIter = global._MP_NO_ITERATOR_SYMBOLS_ !== '1' @@ -51,14 +56,46 @@ const isArrayBuffer = b => b instanceof ArrayBuffer || const isArrayBufferView = b => !Buffer.isBuffer(b) && ArrayBuffer.isView(b) +class Pipe { + constructor (src, dest, opts) { + this.src = src + this.dest = dest + this.opts = opts + this.ondrain = () => src[RESUME]() + dest.on('drain', this.ondrain) + } + unpipe () { + this.dest.removeListener('drain', this.ondrain) + } + // istanbul ignore next - only here for the prototype + proxyErrors () {} + end () { + this.unpipe() + if (this.opts.end) + this.dest.end() + } +} + +class PipeProxyErrors extends Pipe { + unpipe () { + this.src.removeListener('error', this.proxyErrors) + super.unpipe() + } + constructor (src, dest, opts) { + super(src, dest, opts) + this.proxyErrors = er => dest.emit('error', er) + src.on('error', this.proxyErrors) + } +} + module.exports = class Minipass extends Stream { constructor (options) { super() this[FLOWING] = false // whether we're explicitly paused this[PAUSED] = false - this.pipes = new Yallist() - this.buffer = new Yallist() + this.pipes = [] + this.buffer = [] this[OBJECTMODE] = options && options.objectMode || false if (this[OBJECTMODE]) this[ENCODING] = null @@ -66,6 +103,7 @@ module.exports = class Minipass extends Stream { this[ENCODING] = options && options.encoding || null if (this[ENCODING] === 'buffer') this[ENCODING] = null + this[ASYNC] = options && !!options.async || false this[DECODER] = this[ENCODING] ? new SD(this[ENCODING]) : null this[EOF] = false this[EMITTED_END] = false @@ -105,6 +143,9 @@ module.exports = class Minipass extends Stream { get objectMode () { return this[OBJECTMODE] } set objectMode (om) { this[OBJECTMODE] = this[OBJECTMODE] || !!om } + get ['async'] () { return this[ASYNC] } + set ['async'] (a) { this[ASYNC] = this[ASYNC] || !!a } + write (chunk, encoding, cb) { if (this[EOF]) throw new Error('write after end') @@ -123,6 +164,8 @@ module.exports = class Minipass extends Stream { if (!encoding) encoding = 'utf8' + const fn = this[ASYNC] ? defer : f => f() + // convert array buffers and typed array views into buffers // at some point in the future, we may want to do the opposite! // leave strings and buffers as-is @@ -137,19 +180,40 @@ module.exports = class Minipass extends Stream { this.objectMode = true } - // this ensures at this point that the chunk is a buffer or string + // handle object mode up front, since it's simpler + // this yields better performance, fewer checks later. + if (this[OBJECTMODE]) { + /* istanbul ignore if - maybe impossible? */ + if (this.flowing && this[BUFFERLENGTH] !== 0) + this[FLUSH](true) + + if (this.flowing) + this.emit('data', chunk) + else + this[BUFFERPUSH](chunk) + + if (this[BUFFERLENGTH] !== 0) + this.emit('readable') + + if (cb) + fn(cb) + + return this.flowing + } + + // at this point the chunk is a buffer or string // don't buffer it up or send it to the decoder - if (!this.objectMode && !chunk.length) { + if (!chunk.length) { if (this[BUFFERLENGTH] !== 0) this.emit('readable') if (cb) - cb() + fn(cb) return this.flowing } // fast-path writing strings of same encoding to a stream with // an empty buffer, skipping the buffer/decoder dance - if (typeof chunk === 'string' && !this[OBJECTMODE] && + if (typeof chunk === 'string' && // unless it is a string already ready for us to use !(encoding === this[ENCODING] && !this[DECODER].lastNeed)) { chunk = Buffer.from(chunk, encoding) @@ -158,27 +222,20 @@ module.exports = class Minipass extends Stream { if (Buffer.isBuffer(chunk) && this[ENCODING]) chunk = this[DECODER].write(chunk) - if (this.flowing) { - // if we somehow have something in the buffer, but we think we're - // flowing, then we need to flush all that out first, or we get - // chunks coming in out of order. Can't emit 'drain' here though, - // because we're mid-write, so that'd be bad. - if (this[BUFFERLENGTH] !== 0) - this[FLUSH](true) + // Note: flushing CAN potentially switch us into not-flowing mode + if (this.flowing && this[BUFFERLENGTH] !== 0) + this[FLUSH](true) - // if we are still flowing after flushing the buffer we can emit the - // chunk otherwise we have to buffer it. - this.flowing - ? this.emit('data', chunk) - : this[BUFFERPUSH](chunk) - } else + if (this.flowing) + this.emit('data', chunk) + else this[BUFFERPUSH](chunk) if (this[BUFFERLENGTH] !== 0) this.emit('readable') if (cb) - cb() + fn(cb) return this.flowing } @@ -187,35 +244,31 @@ module.exports = class Minipass extends Stream { if (this[DESTROYED]) return null - try { - if (this[BUFFERLENGTH] === 0 || n === 0 || n > this[BUFFERLENGTH]) - return null + if (this[BUFFERLENGTH] === 0 || n === 0 || n > this[BUFFERLENGTH]) { + this[MAYBE_EMIT_END]() + return null + } - if (this[OBJECTMODE]) - n = null - - if (this.buffer.length > 1 && !this[OBJECTMODE]) { - if (this.encoding) - this.buffer = new Yallist([ - Array.from(this.buffer).join('') - ]) - else - this.buffer = new Yallist([ - Buffer.concat(Array.from(this.buffer), this[BUFFERLENGTH]) - ]) - } + if (this[OBJECTMODE]) + n = null - return this[READ](n || null, this.buffer.head.value) - } finally { - this[MAYBE_EMIT_END]() + if (this.buffer.length > 1 && !this[OBJECTMODE]) { + if (this.encoding) + this.buffer = [this.buffer.join('')] + else + this.buffer = [Buffer.concat(this.buffer, this[BUFFERLENGTH])] } + + const ret = this[READ](n || null, this.buffer[0]) + this[MAYBE_EMIT_END]() + return ret } [READ] (n, chunk) { if (n === chunk.length || n === null) this[BUFFERSHIFT]() else { - this.buffer.head.value = chunk.slice(n) + this.buffer[0] = chunk.slice(n) chunk = chunk.slice(0, n) this[BUFFERLENGTH] -= n } @@ -291,7 +344,7 @@ module.exports = class Minipass extends Stream { this[BUFFERLENGTH] += 1 else this[BUFFERLENGTH] += chunk.length - return this.buffer.push(chunk) + this.buffer.push(chunk) } [BUFFERSHIFT] () { @@ -299,7 +352,7 @@ module.exports = class Minipass extends Stream { if (this[OBJECTMODE]) this[BUFFERLENGTH] -= 1 else - this[BUFFERLENGTH] -= this.buffer.head.value.length + this[BUFFERLENGTH] -= this.buffer[0].length } return this.buffer.shift() } @@ -325,35 +378,52 @@ module.exports = class Minipass extends Stream { opts.end = false else opts.end = opts.end !== false + opts.proxyErrors = !!opts.proxyErrors - const p = { dest: dest, opts: opts, ondrain: _ => this[RESUME]() } - this.pipes.push(p) - - dest.on('drain', p.ondrain) - this[RESUME]() // piping an ended stream ends immediately - if (ended && p.opts.end) - p.dest.end() + if (ended) { + if (opts.end) + dest.end() + } else { + this.pipes.push(!opts.proxyErrors ? new Pipe(this, dest, opts) + : new PipeProxyErrors(this, dest, opts)) + if (this[ASYNC]) + defer(() => this[RESUME]()) + else + this[RESUME]() + } + return dest } + unpipe (dest) { + const p = this.pipes.find(p => p.dest === dest) + if (p) { + this.pipes.splice(this.pipes.indexOf(p), 1) + p.unpipe() + } + } + addListener (ev, fn) { return this.on(ev, fn) } on (ev, fn) { - try { - return super.on(ev, fn) - } finally { - if (ev === 'data' && !this.pipes.length && !this.flowing) - this[RESUME]() - else if (isEndish(ev) && this[EMITTED_END]) { - super.emit(ev) - this.removeAllListeners(ev) - } else if (ev === 'error' && this[EMITTED_ERROR]) { + const ret = super.on(ev, fn) + if (ev === 'data' && !this.pipes.length && !this.flowing) + this[RESUME]() + else if (ev === 'readable' && this[BUFFERLENGTH] !== 0) + super.emit('readable') + else if (isEndish(ev) && this[EMITTED_END]) { + super.emit(ev) + this.removeAllListeners(ev) + } else if (ev === 'error' && this[EMITTED_ERROR]) { + if (this[ASYNC]) + defer(() => fn.call(this, this[EMITTED_ERROR])) + else fn.call(this, this[EMITTED_ERROR]) - } } + return ret } get emittedEnd () { @@ -376,65 +446,84 @@ module.exports = class Minipass extends Stream { } } - emit (ev, data) { + emit (ev, data, ...extra) { // error and close are only events allowed after calling destroy() if (ev !== 'error' && ev !== 'close' && ev !== DESTROYED && this[DESTROYED]) return else if (ev === 'data') { - if (!data) - return - - if (this.pipes.length) - this.pipes.forEach(p => - p.dest.write(data) === false && this.pause()) + return !data ? false + : this[ASYNC] ? defer(() => this[EMITDATA](data)) + : this[EMITDATA](data) } else if (ev === 'end') { - // only actual end gets this treatment - if (this[EMITTED_END] === true) - return - - this[EMITTED_END] = true - this.readable = false - - if (this[DECODER]) { - data = this[DECODER].end() - if (data) { - this.pipes.forEach(p => p.dest.write(data)) - super.emit('data', data) - } - } - - this.pipes.forEach(p => { - p.dest.removeListener('drain', p.ondrain) - if (p.opts.end) - p.dest.end() - }) + return this[EMITEND]() } else if (ev === 'close') { this[CLOSED] = true // don't emit close before 'end' and 'finish' if (!this[EMITTED_END] && !this[DESTROYED]) return + const ret = super.emit('close') + this.removeAllListeners('close') + return ret } else if (ev === 'error') { this[EMITTED_ERROR] = data + const ret = super.emit('error', data) + this[MAYBE_EMIT_END]() + return ret + } else if (ev === 'resume') { + const ret = super.emit('resume') + this[MAYBE_EMIT_END]() + return ret + } else if (ev === 'finish' || ev === 'prefinish') { + const ret = super.emit(ev) + this.removeAllListeners(ev) + return ret } - // TODO: replace with a spread operator when Node v4 support drops - const args = new Array(arguments.length) - args[0] = ev - args[1] = data - if (arguments.length > 2) { - for (let i = 2; i < arguments.length; i++) { - args[i] = arguments[i] + // Some other unknown event + const ret = super.emit(ev, data, ...extra) + this[MAYBE_EMIT_END]() + return ret + } + + [EMITDATA] (data) { + for (const p of this.pipes) { + if (p.dest.write(data) === false) + this.pause() + } + const ret = super.emit('data', data) + this[MAYBE_EMIT_END]() + return ret + } + + [EMITEND] () { + if (this[EMITTED_END]) + return + + this[EMITTED_END] = true + this.readable = false + if (this[ASYNC]) + defer(() => this[EMITEND2]()) + else + this[EMITEND2]() + } + + [EMITEND2] () { + if (this[DECODER]) { + const data = this[DECODER].end() + if (data) { + for (const p of this.pipes) { + p.dest.write(data) + } + super.emit('data', data) } } - try { - return super.emit.apply(this, args) - } finally { - if (!isEndish(ev)) - this[MAYBE_EMIT_END]() - else - this.removeAllListeners(ev) + for (const p of this.pipes) { + p.end() } + const ret = super.emit('end') + this.removeAllListeners('end') + return ret } // const all = await stream.collect() @@ -536,7 +625,7 @@ module.exports = class Minipass extends Stream { this[DESTROYED] = true // throw away all buffered data, it's never coming out - this.buffer = new Yallist() + this.buffer.length = 0 this[BUFFERLENGTH] = 0 if (typeof this.close === 'function' && !this[CLOSED]) diff --git a/deps/npm/node_modules/minipass/package.json b/deps/npm/node_modules/minipass/package.json index 1728e5108c4c20..47c90a4fcf4a71 100644 --- a/deps/npm/node_modules/minipass/package.json +++ b/deps/npm/node_modules/minipass/package.json @@ -1,15 +1,19 @@ { "name": "minipass", - "version": "3.1.6", + "version": "3.3.4", "description": "minimal implementation of a PassThrough stream", "main": "index.js", "dependencies": { "yallist": "^4.0.0" }, "devDependencies": { + "@types/node": "^17.0.41", "end-of-stream": "^1.4.0", - "tap": "^15.0.9", - "through2": "^2.0.3" + "prettier": "^2.6.2", + "tap": "^16.2.0", + "through2": "^2.0.3", + "ts-node": "^10.8.1", + "typescript": "^4.7.3" }, "scripts": { "test": "tap", @@ -28,6 +32,7 @@ "author": "Isaac Z. Schlueter (http://blog.izs.me/)", "license": "ISC", "files": [ + "index.d.ts", "index.js" ], "tap": { @@ -35,5 +40,16 @@ }, "engines": { "node": ">=8" + }, + "prettier": { + "semi": false, + "printWidth": 80, + "tabWidth": 2, + "useTabs": false, + "singleQuote": true, + "jsxSingleQuote": false, + "bracketSameLine": true, + "arrowParens": "avoid", + "endOfLine": "lf" } } diff --git a/deps/npm/node_modules/npm-package-arg/lib/npa.js b/deps/npm/node_modules/npm-package-arg/lib/npa.js index cc1eddaec74b4e..61fee0783ecc7e 100644 --- a/deps/npm/node_modules/npm-package-arg/lib/npa.js +++ b/deps/npm/node_modules/npm-package-arg/lib/npa.js @@ -9,6 +9,7 @@ const semver = require('semver') const path = global.FAKE_WINDOWS ? require('path').win32 : require('path') const validatePackageName = require('validate-npm-package-name') const { homedir } = require('os') +const log = require('proc-log') const isWindows = process.platform === 'win32' || global.FAKE_WINDOWS const hasSlashes = isWindows ? /\\|[/]/ : /[/]/ @@ -120,6 +121,7 @@ function Result (opts) { } this.gitRange = opts.gitRange this.gitCommittish = opts.gitCommittish + this.gitSubdir = opts.gitSubdir this.hosted = opts.hosted } @@ -155,11 +157,45 @@ Result.prototype.toJSON = function () { } function setGitCommittish (res, committish) { - if (committish != null && committish.length >= 7 && committish.slice(0, 7) === 'semver:') { - res.gitRange = decodeURIComponent(committish.slice(7)) + if (!committish) { res.gitCommittish = null - } else { - res.gitCommittish = committish === '' ? null : committish + return res + } + + // for each :: separated item: + for (const part of committish.split('::')) { + // if the item has no : the n it is a commit-ish + if (!part.includes(':')) { + if (res.gitRange) { + throw new Error('cannot override existing semver range with a committish') + } + if (res.gitCommittish) { + throw new Error('cannot override existing committish with a second committish') + } + res.gitCommittish = part + continue + } + // split on name:value + const [name, value] = part.split(':') + // if name is semver do semver lookup of ref or tag + if (name === 'semver') { + if (res.gitCommittish) { + throw new Error('cannot override existing committish with a semver range') + } + if (res.gitRange) { + throw new Error('cannot override existing semver range with a second semver range') + } + res.gitRange = decodeURIComponent(value) + continue + } + if (name === 'path') { + if (res.gitSubdir) { + throw new Error('cannot override existing path with a second path') + } + res.gitSubdir = `/${value}` + continue + } + log.warn('npm-package-arg', `ignoring unknown key "${name}"`) } return res diff --git a/deps/npm/node_modules/npm-package-arg/package.json b/deps/npm/node_modules/npm-package-arg/package.json index 457f1d8a2f83d3..b9729db5d4f14e 100644 --- a/deps/npm/node_modules/npm-package-arg/package.json +++ b/deps/npm/node_modules/npm-package-arg/package.json @@ -1,6 +1,6 @@ { "name": "npm-package-arg", - "version": "9.0.2", + "version": "9.1.0", "description": "Parse the things that can be arguments to `npm install`", "main": "./lib/npa.js", "directories": { @@ -12,12 +12,13 @@ ], "dependencies": { "hosted-git-info": "^5.0.0", + "proc-log": "^2.0.1", "semver": "^7.3.5", "validate-npm-package-name": "^4.0.0" }, "devDependencies": { "@npmcli/eslint-config": "^3.0.1", - "@npmcli/template-oss": "3.2.1", + "@npmcli/template-oss": "3.5.0", "tap": "^16.0.1" }, "scripts": { @@ -52,6 +53,6 @@ }, "templateOSS": { "//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.", - "version": "3.2.1" + "version": "3.5.0" } } diff --git a/deps/npm/node_modules/npm-profile/lib/index.js b/deps/npm/node_modules/npm-profile/lib/index.js index eb97c621dd18dc..e4ac5622e0d5c6 100644 --- a/deps/npm/node_modules/npm-profile/lib/index.js +++ b/deps/npm/node_modules/npm-profile/lib/index.js @@ -292,4 +292,5 @@ module.exports = { listTokens, removeToken, createToken, + webAuthCheckLogin, } diff --git a/deps/npm/node_modules/npm-profile/package.json b/deps/npm/node_modules/npm-profile/package.json index 4f9da95d73fe81..bd4ebd31761159 100644 --- a/deps/npm/node_modules/npm-profile/package.json +++ b/deps/npm/node_modules/npm-profile/package.json @@ -1,6 +1,6 @@ { "name": "npm-profile", - "version": "6.1.0", + "version": "6.2.0", "description": "Library for updating an npmjs.com profile", "keywords": [], "author": "GitHub Inc.", @@ -20,7 +20,7 @@ ], "devDependencies": { "@npmcli/eslint-config": "^3.0.1", - "@npmcli/template-oss": "3.4.2", + "@npmcli/template-oss": "3.5.0", "nock": "^13.2.4", "tap": "^16.0.1" }, @@ -44,6 +44,6 @@ }, "templateOSS": { "//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.", - "version": "3.4.2" + "version": "3.5.0" } } diff --git a/deps/npm/node_modules/npm-registry-fetch/lib/index.js b/deps/npm/node_modules/npm-registry-fetch/lib/index.js index 6c834aa4af5ef0..c788febc33afb4 100644 --- a/deps/npm/node_modules/npm-registry-fetch/lib/index.js +++ b/deps/npm/node_modules/npm-registry-fetch/lib/index.js @@ -213,6 +213,10 @@ function getHeaders (uri, auth, opts) { 'user-agent': opts.userAgent, }, opts.headers || {}) + if (opts.authType) { + headers['npm-auth-type'] = opts.authType + } + if (opts.scope) { headers['npm-scope'] = opts.scope } diff --git a/deps/npm/node_modules/npm-registry-fetch/package.json b/deps/npm/node_modules/npm-registry-fetch/package.json index 0ce12c633637a6..5f19697c3b19d7 100644 --- a/deps/npm/node_modules/npm-registry-fetch/package.json +++ b/deps/npm/node_modules/npm-registry-fetch/package.json @@ -1,6 +1,6 @@ { "name": "npm-registry-fetch", - "version": "13.1.1", + "version": "13.2.0", "description": "Fetch-based http client for use with npm registry APIs", "main": "lib", "files": [ @@ -44,7 +44,7 @@ }, "devDependencies": { "@npmcli/eslint-config": "^3.0.1", - "@npmcli/template-oss": "3.3.2", + "@npmcli/template-oss": "3.5.0", "cacache": "^16.0.2", "nock": "^13.2.4", "require-inject": "^1.4.4", @@ -60,6 +60,6 @@ }, "templateOSS": { "//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.", - "version": "3.3.2" + "version": "3.5.0" } } diff --git a/deps/npm/package.json b/deps/npm/package.json index 95afa528fa144f..80e7a4fb0f6795 100644 --- a/deps/npm/package.json +++ b/deps/npm/package.json @@ -1,5 +1,5 @@ { - "version": "8.13.2", + "version": "8.14.0", "name": "npm", "description": "a package manager for JavaScript", "workspaces": [ @@ -62,7 +62,7 @@ "@npmcli/fs": "^2.1.0", "@npmcli/map-workspaces": "^2.0.3", "@npmcli/package-json": "^2.0.0", - "@npmcli/run-script": "^4.1.5", + "@npmcli/run-script": "^4.1.7", "abbrev": "~1.1.1", "archy": "~1.0.0", "cacache": "^16.1.1", @@ -100,13 +100,14 @@ "nopt": "^5.0.0", "npm-audit-report": "^3.0.0", "npm-install-checks": "^5.0.0", - "npm-package-arg": "^9.0.2", + "npm-package-arg": "^9.1.0", "npm-pick-manifest": "^7.0.1", - "npm-profile": "^6.1.0", - "npm-registry-fetch": "^13.1.1", + "npm-profile": "^6.2.0", + "npm-registry-fetch": "^13.2.0", "npm-user-validate": "^1.0.1", "npmlog": "^6.0.2", "opener": "^1.5.2", + "p-map": "^4.0.0", "pacote": "^13.6.1", "parse-conflict-json": "^2.0.2", "proc-log": "^2.0.1", @@ -179,6 +180,7 @@ "npm-user-validate", "npmlog", "opener", + "p-map", "pacote", "parse-conflict-json", "proc-log", @@ -207,6 +209,7 @@ "tap": "^16.0.1" }, "scripts": { + "dependencies": "node scripts/bundle-and-gitignore-deps.js", "dumpconf": "env | grep npm | sort | uniq", "preversion": "bash scripts/update-authors.sh && git add AUTHORS && git commit -m \"chore: update AUTHORS\" || true", "licenses": "licensee --production --errors-only", diff --git a/deps/npm/tap-snapshots/test/lib/commands/adduser.js.test.cjs b/deps/npm/tap-snapshots/test/lib/commands/adduser.js.test.cjs new file mode 100644 index 00000000000000..ba27a6a781ab05 --- /dev/null +++ b/deps/npm/tap-snapshots/test/lib/commands/adduser.js.test.cjs @@ -0,0 +1,17 @@ +/* IMPORTANT + * This snapshot file is auto-generated, but designed for humans. + * It should be checked into source control and tracked carefully. + * Re-generate by setting TAP_SNAPSHOT=1 and running tests. + * Make sure to inspect the output below. Do not ignore changes! + */ +'use strict' +exports[`test/lib/commands/adduser.js TAP auth-type sso warning > warning 1`] = ` +Object { + "warn": Array [ + Array [ + "config", + "--auth-type=sso is will be removed in a future version.", + ], + ], +} +` diff --git a/deps/npm/tap-snapshots/test/lib/commands/audit.js.test.cjs b/deps/npm/tap-snapshots/test/lib/commands/audit.js.test.cjs index c3680933e6a793..3e7658c14bb195 100644 --- a/deps/npm/tap-snapshots/test/lib/commands/audit.js.test.cjs +++ b/deps/npm/tap-snapshots/test/lib/commands/audit.js.test.cjs @@ -41,6 +41,233 @@ added 1 package, and audited 2 packages in xxx found 0 vulnerabilities ` +exports[`test/lib/commands/audit.js TAP audit signatures ignores optional dependencies > must match snapshot 1`] = ` +audited 1 package in xxx + +1 package has a verified registry signature + +` + +exports[`test/lib/commands/audit.js TAP audit signatures json output with invalid and missing signatures > must match snapshot 1`] = ` +{ + "invalid": [ + { + "name": "kms-demo", + "version": "1.0.0", + "location": "node_modules/kms-demo", + "resolved": "https://registry.npmjs.org/kms-demo/-/kms-demo-1.0.0.tgz", + "integrity": "sha512-QqZ7VJ/8xPkS9s2IWB7Shj3qTJdcRyeXKbPQnsZjsPEwvutGv0EGeVchPcauoiDFJlGbZMFq5GDCurAGNSghJQ==", + "signature": "bogus", + "keyid": "SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA" + } + ], + "missing": [ + { + "name": "async", + "version": "1.1.1", + "location": "node_modules/async", + "resolved": "https://registry.npmjs.org/async/-/async-1.1.1.tgz" + } + ] +} +` + +exports[`test/lib/commands/audit.js TAP audit signatures json output with invalid signatures > must match snapshot 1`] = ` +{ + "invalid": [ + { + "name": "kms-demo", + "version": "1.0.0", + "location": "node_modules/kms-demo", + "resolved": "https://registry.npmjs.org/kms-demo/-/kms-demo-1.0.0.tgz", + "integrity": "sha512-QqZ7VJ/8xPkS9s2IWB7Shj3qTJdcRyeXKbPQnsZjsPEwvutGv0EGeVchPcauoiDFJlGbZMFq5GDCurAGNSghJQ==", + "signature": "bogus", + "keyid": "SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA" + } + ], + "missing": [] +} +` + +exports[`test/lib/commands/audit.js TAP audit signatures json output with valid signatures > must match snapshot 1`] = ` +{ + "invalid": [], + "missing": [] +} +` + +exports[`test/lib/commands/audit.js TAP audit signatures multiple registries with keys and signatures > must match snapshot 1`] = ` +audited 2 packages in xxx + +2 packages have verified registry signatures + +` + +exports[`test/lib/commands/audit.js TAP audit signatures omit dev dependencies with missing signature > must match snapshot 1`] = ` +audited 1 package in xxx + +1 package has a verified registry signature + +` + +exports[`test/lib/commands/audit.js TAP audit signatures output details about missing signatures > must match snapshot 1`] = ` +audited 1 package in xxx + +1 package has a missing registry signature but the registry is providing signing keys: + +kms-demo@1.0.0 (https://registry.npmjs.org/) +` + +exports[`test/lib/commands/audit.js TAP audit signatures third-party registry with invalid signatures errors > must match snapshot 1`] = ` +audited 1 package in xxx + +1 package has an invalid registry signature: + +@npmcli/arborist@1.0.14 (https://verdaccio-clone.org) + +Someone might have tampered with this package since it was published on the registry! + +` + +exports[`test/lib/commands/audit.js TAP audit signatures third-party registry with keys and missing signatures errors > must match snapshot 1`] = ` +audited 1 package in xxx + +1 package has a missing registry signature but the registry is providing signing keys: + +@npmcli/arborist@1.0.14 (https://verdaccio-clone.org) +` + +exports[`test/lib/commands/audit.js TAP audit signatures third-party registry with keys and signatures > must match snapshot 1`] = ` +audited 1 package in xxx + +1 package has a verified registry signature + +` + +exports[`test/lib/commands/audit.js TAP audit signatures with both invalid and missing signatures > must match snapshot 1`] = ` +audited 2 packages in xxx + +1 package has a missing registry signature but the registry is providing signing keys: + +async@1.1.1 (https://registry.npmjs.org/) + +1 package has an invalid registry signature: + +kms-demo@1.0.0 (https://registry.npmjs.org/) + +Someone might have tampered with this package since it was published on the registry! + +` + +exports[`test/lib/commands/audit.js TAP audit signatures with bundled and peer deps and no signatures > must match snapshot 1`] = ` +audited 1 package in xxx + +1 package has a verified registry signature + +` + +exports[`test/lib/commands/audit.js TAP audit signatures with invalid signatures > must match snapshot 1`] = ` +audited 1 package in xxx + +1 package has an invalid registry signature: + +kms-demo@1.0.0 (https://registry.npmjs.org/) + +Someone might have tampered with this package since it was published on the registry! + +` + +exports[`test/lib/commands/audit.js TAP audit signatures with invalid signtaures and color output enabled > must match snapshot 1`] = ` +audited 1 package in xxx + +1 package has an invalid registry signature: + +kms-demo@1.0.0 (https://registry.npmjs.org/) + +Someone might have tampered with this package since it was published on the registry! + +` + +exports[`test/lib/commands/audit.js TAP audit signatures with keys but missing signature > must match snapshot 1`] = ` +audited 1 package in xxx + +1 package has a missing registry signature but the registry is providing signing keys: + +kms-demo@1.0.0 (https://registry.npmjs.org/) +` + +exports[`test/lib/commands/audit.js TAP audit signatures with multiple invalid signatures > must match snapshot 1`] = ` +audited 2 packages in xxx + +2 packages have invalid registry signatures: + +async@1.1.1 (https://registry.npmjs.org/) +kms-demo@1.0.0 (https://registry.npmjs.org/) + +Someone might have tampered with these packages since they where published on the registry! + +` + +exports[`test/lib/commands/audit.js TAP audit signatures with multiple missing signatures > must match snapshot 1`] = ` +audited 2 packages in xxx + +2 packages have missing registry signatures but the registry is providing signing keys: + +async@1.1.1 (https://registry.npmjs.org/) +kms-demo@1.0.0 (https://registry.npmjs.org/) +` + +exports[`test/lib/commands/audit.js TAP audit signatures with multiple valid signatures and one invalid > must match snapshot 1`] = ` +audited 3 packages in xxx + +2 packages have verified registry signatures + +1 package has an invalid registry signature: + +node-fetch@1.6.0 (https://registry.npmjs.org/) + +Someone might have tampered with this package since it was published on the registry! + +` + +exports[`test/lib/commands/audit.js TAP audit signatures with valid and missing signatures > must match snapshot 1`] = ` +audited 2 packages in xxx + +1 package has a verified registry signature + +1 package has a missing registry signature but the registry is providing signing keys: + +async@1.1.1 (https://registry.npmjs.org/) +` + +exports[`test/lib/commands/audit.js TAP audit signatures with valid signatures > must match snapshot 1`] = ` +audited 1 package in xxx + +1 package has a verified registry signature + +` + +exports[`test/lib/commands/audit.js TAP audit signatures with valid signatures using alias > must match snapshot 1`] = ` +audited 1 package in xxx + +1 package has a verified registry signature + +` + +exports[`test/lib/commands/audit.js TAP audit signatures workspaces verifies registry deps and ignores local workspace deps > must match snapshot 1`] = ` +audited 3 packages in xxx + +3 packages have verified registry signatures + +` + +exports[`test/lib/commands/audit.js TAP audit signatures workspaces verifies registry deps when filtering by workspace name > must match snapshot 1`] = ` +audited 2 packages in xxx + +2 packages have verified registry signatures + +` + exports[`test/lib/commands/audit.js TAP fallback audit > must match snapshot 1`] = ` # npm audit report diff --git a/deps/npm/tap-snapshots/test/lib/load-all-commands.js.test.cjs b/deps/npm/tap-snapshots/test/lib/load-all-commands.js.test.cjs index 57dd6126660cdc..b697dfbb796c68 100644 --- a/deps/npm/tap-snapshots/test/lib/load-all-commands.js.test.cjs +++ b/deps/npm/tap-snapshots/test/lib/load-all-commands.js.test.cjs @@ -33,7 +33,7 @@ npm adduser Options: [--registry ] [--scope <@scope>] -[--auth-type ] +[--auth-type ] aliases: login, add-user @@ -44,7 +44,7 @@ exports[`test/lib/load-all-commands.js TAP load each command audit > must match Run a security audit Usage: -npm audit [fix] +npm audit [fix|signatures] Options: [--audit-level ] [--dry-run] [-f|--force] @@ -499,7 +499,7 @@ npm adduser Options: [--registry ] [--scope <@scope>] -[--auth-type ] +[--auth-type ] aliases: login, add-user diff --git a/deps/npm/tap-snapshots/test/lib/npm.js.test.cjs b/deps/npm/tap-snapshots/test/lib/npm.js.test.cjs index 5ae34e868771d6..b2ba45b2d615cb 100644 --- a/deps/npm/tap-snapshots/test/lib/npm.js.test.cjs +++ b/deps/npm/tap-snapshots/test/lib/npm.js.test.cjs @@ -190,7 +190,7 @@ All commands: Options: [--registry ] [--scope <@scope>] - [--auth-type ] + [--auth-type ] aliases: login, add-user @@ -199,7 +199,7 @@ All commands: audit Run a security audit Usage: - npm audit [fix] + npm audit [fix|signatures] Options: [--audit-level ] [--dry-run] [-f|--force] @@ -577,7 +577,7 @@ All commands: Options: [--registry ] [--scope <@scope>] - [--auth-type ] + [--auth-type ] aliases: login, add-user diff --git a/deps/npm/tap-snapshots/test/lib/utils/config/definitions.js.test.cjs b/deps/npm/tap-snapshots/test/lib/utils/config/definitions.js.test.cjs index 19909d8c5b35d6..04d304a2254d99 100644 --- a/deps/npm/tap-snapshots/test/lib/utils/config/definitions.js.test.cjs +++ b/deps/npm/tap-snapshots/test/lib/utils/config/definitions.js.test.cjs @@ -253,13 +253,12 @@ exports[`test/lib/utils/config/definitions.js TAP > config description for auth- #### \`auth-type\` * Default: "legacy" -* Type: "legacy", "webauthn", "sso", "saml", or "oauth" -* DEPRECATED: The SSO/SAML/OAuth methods are deprecated and will be removed in - a future version of npm in favor of web-based login. +* Type: "legacy", "web", "sso", "saml", "oauth", or "webauthn" -What authentication strategy to use with \`adduser\`/\`login\`. +NOTE: auth-type values "sso", "saml", "oauth", and "webauthn" will be +removed in a future version. -Pass \`webauthn\` to use a web-based login. +What authentication strategy to use with \`login\`. ` exports[`test/lib/utils/config/definitions.js TAP > config description for before 1`] = ` diff --git a/deps/npm/tap-snapshots/test/lib/utils/config/describe-all.js.test.cjs b/deps/npm/tap-snapshots/test/lib/utils/config/describe-all.js.test.cjs index 9d95aa9528bd89..a291af6dedfae8 100644 --- a/deps/npm/tap-snapshots/test/lib/utils/config/describe-all.js.test.cjs +++ b/deps/npm/tap-snapshots/test/lib/utils/config/describe-all.js.test.cjs @@ -88,6 +88,19 @@ exit code. +#### \`auth-type\` + +* Default: "legacy" +* Type: "legacy", "web", "sso", "saml", "oauth", or "webauthn" + +NOTE: auth-type values "sso", "saml", "oauth", and "webauthn" will be +removed in a future version. + +What authentication strategy to use with \`login\`. + + + + #### \`before\` * Default: null @@ -1778,20 +1791,6 @@ When set to \`dev\` or \`development\`, this is an alias for \`--include=dev\`. -#### \`auth-type\` - -* Default: "legacy" -* Type: "legacy", "webauthn", "sso", "saml", or "oauth" -* DEPRECATED: The SSO/SAML/OAuth methods are deprecated and will be removed in - a future version of npm in favor of web-based login. - -What authentication strategy to use with \`adduser\`/\`login\`. - -Pass \`webauthn\` to use a web-based login. - - - - #### \`cache-max\` * Default: Infinity diff --git a/deps/npm/test/lib/commands/adduser.js b/deps/npm/test/lib/commands/adduser.js index ca07199b2c9fbd..7c7079394f132d 100644 --- a/deps/npm/test/lib/commands/adduser.js +++ b/deps/npm/test/lib/commands/adduser.js @@ -75,6 +75,15 @@ t.test('bad auth type', async t => { }) }) +t.test('auth-type sso warning', async t => { + const { logs } = await loadMockNpm(t, { + config: { + 'auth-type': 'sso', + }, + }) + t.matchSnapshot({ warn: logs.warn }, 'warning') +}) + t.test('scoped login', async t => { const stdin = new stream.PassThrough() stdin.write('test-user\n') diff --git a/deps/npm/test/lib/commands/audit.js b/deps/npm/test/lib/commands/audit.js index da6de4774e6b8d..b6c6c77a2b40aa 100644 --- a/deps/npm/test/lib/commands/audit.js +++ b/deps/npm/test/lib/commands/audit.js @@ -1,14 +1,15 @@ +const fs = require('fs') +const zlib = require('zlib') +const path = require('path') const t = require('tap') const { load: loadMockNpm } = require('../../fixtures/mock-npm') const MockRegistry = require('../../fixtures/mock-registry.js') -const zlib = require('zlib') -const gzip = zlib.gzipSync + const gunzip = zlib.gunzipSync -const path = require('path') -const fs = require('fs') +const gzip = zlib.gzipSync -t.cleanSnapshot = str => str.replace(/packages in [0-9]+[a-z]+/g, 'packages in xxx') +t.cleanSnapshot = str => str.replace(/package(s)? in [0-9]+[a-z]+/g, 'package$1 in xxx') const tree = { 'package.json': JSON.stringify({ @@ -236,3 +237,1456 @@ t.test('completion', async t => { }) }) }) + +t.test('audit signatures', async t => { + const VALID_REGISTRY_KEYS = { + keys: [{ + expires: null, + keyid: 'SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA', + keytype: 'ecdsa-sha2-nistp256', + scheme: 'ecdsa-sha2-nistp256', + key: 'MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE1Olb3zMAFFxXKHiIkQO5cJ3Yhl5i6UPp+' + + 'IhuteBJbuHcA5UogKo0EWtlWwW6KSaKoTNEYL7JlCQiVnkhBktUgg==', + }], + } + + const MISMATCHING_REGISTRY_KEYS = { + keys: [{ + expires: null, + keyid: 'SHA256:2l3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA', + keytype: 'ecdsa-sha2-nistp256', + scheme: 'ecdsa-sha2-nistp256', + key: 'MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE1Olb3zMAFFxXKHiIkQO5cJ3Yhl5i6UPp+' + + 'IhuteBJbuHcA5UogKo0EWtlWwW6KSaKoTNEYL7JlCQiVnkhBktUgg==', + }], + } + + const EXPIRED_REGISTRY_KEYS = { + keys: [{ + expires: '2021-01-11T15:45:42.144Z', + keyid: 'SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA', + keytype: 'ecdsa-sha2-nistp256', + scheme: 'ecdsa-sha2-nistp256', + key: 'MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE1Olb3zMAFFxXKHiIkQO5cJ3Yhl5i6UPp+' + + 'IhuteBJbuHcA5UogKo0EWtlWwW6KSaKoTNEYL7JlCQiVnkhBktUgg==', + }], + } + + const installWithValidSigs = { + 'package.json': JSON.stringify({ + name: 'test-dep', + version: '1.0.0', + dependencies: { + 'kms-demo': '1.0.0', + }, + }), + node_modules: { + 'kms-demo': { + 'package.json': JSON.stringify({ + name: 'kms-demo', + version: '1.0.0', + }), + }, + }, + 'package-lock.json': JSON.stringify({ + name: 'test-dep', + version: '1.0.0', + lockfileVersion: 2, + requires: true, + packages: { + '': { + name: 'scratch', + version: '1.0.0', + dependencies: { + 'kms-demo': '^1.0.0', + }, + }, + 'node_modules/kms-demo': { + version: '1.0.0', + }, + }, + dependencies: { + 'kms-demo': { + version: '1.0.0', + }, + }, + }), + } + + const installWithAlias = { + 'package.json': JSON.stringify({ + name: 'test-dep', + version: '1.0.0', + dependencies: { + get: 'npm:node-fetch@^1.0.0', + }, + }), + node_modules: { + get: { + 'package.json': JSON.stringify({ + name: 'node-fetch', + version: '1.7.1', + }), + }, + }, + 'package-lock.json': JSON.stringify({ + name: 'test-dep', + version: '1.0.0', + lockfileVersion: 2, + requires: true, + packages: { + '': { + name: 'test-dep', + version: '1.0.0', + dependencies: { + get: 'npm:node-fetch@^1.0.0', + }, + }, + 'node_modules/demo': { + name: 'node-fetch', + version: '1.7.1', + }, + }, + dependencies: { + get: { + version: 'npm:node-fetch@1.7.1', + }, + }, + }), + } + + const noInstall = { + 'package.json': JSON.stringify({ + name: 'test-dep', + version: '1.0.0', + dependencies: { + 'kms-demo': '1.0.0', + }, + }), + 'package-lock.json': JSON.stringify({ + name: 'test-dep', + version: '1.0.0', + lockfileVersion: 2, + requires: true, + packages: { + '': { + name: 'scratch', + version: '1.0.0', + dependencies: { + 'kms-demo': '^1.0.0', + }, + }, + 'node_modules/kms-demo': { + version: '1.0.0', + }, + }, + dependencies: { + 'kms-demo': { + version: '1.0.0', + }, + }, + }), + } + + const workspaceInstall = { + 'package.json': JSON.stringify({ + name: 'workspaces-project', + version: '1.0.0', + workspaces: ['packages/*'], + dependencies: { + 'kms-demo': '^1.0.0', + }, + }), + node_modules: { + a: t.fixture('symlink', '../packages/a'), + b: t.fixture('symlink', '../packages/b'), + c: t.fixture('symlink', '../packages/c'), + 'kms-demo': { + 'package.json': JSON.stringify({ + name: 'kms-demo', + version: '1.0.0', + }), + }, + async: { + 'package.json': JSON.stringify({ + name: 'async', + version: '2.5.0', + }), + }, + 'light-cycle': { + 'package.json': JSON.stringify({ + name: 'light-cycle', + version: '1.4.2', + }), + }, + }, + packages: { + a: { + 'package.json': JSON.stringify({ + name: 'a', + version: '1.0.0', + dependencies: { + b: '^1.0.0', + async: '^2.0.0', + }, + }), + }, + b: { + 'package.json': JSON.stringify({ + name: 'b', + version: '1.0.0', + dependencies: { + 'light-cycle': '^1.0.0', + }, + }), + }, + c: { + 'package.json': JSON.stringify({ + name: 'c', + version: '1.0.0', + }), + }, + }, + } + + const installWithMultipleDeps = { + 'package.json': JSON.stringify({ + name: 'test-dep', + version: '1.0.0', + dependencies: { + 'kms-demo': '^1.0.0', + }, + devDependencies: { + async: '~1.1.0', + }, + }), + node_modules: { + 'kms-demo': { + 'package.json': JSON.stringify({ + name: 'kms-demo', + version: '1.0.0', + }), + }, + async: { + 'package.json': JSON.stringify({ + name: 'async', + version: '1.1.1', + dependencies: { + 'kms-demo': '^1.0.0', + }, + }), + }, + }, + 'package-lock.json': JSON.stringify({ + name: 'test-dep', + version: '1.0.0', + lockfileVersion: 2, + requires: true, + packages: { + '': { + name: 'scratch', + version: '1.0.0', + dependencies: { + 'kms-demo': '^1.0.0', + }, + devDependencies: { + async: '~1.0.0', + }, + }, + 'node_modules/kms-demo': { + version: '1.0.0', + }, + 'node_modules/async': { + version: '1.1.1', + }, + }, + dependencies: { + 'kms-demo': { + version: '1.0.0', + }, + async: { + version: '1.1.1', + dependencies: { + 'kms-demo': '^1.0.0', + }, + }, + }, + }), + } + + const installWithPeerDeps = { + 'package.json': JSON.stringify({ + name: 'test-dep', + version: '1.0.0', + peerDependencies: { + 'kms-demo': '^1.0.0', + }, + }), + node_modules: { + 'kms-demo': { + 'package.json': JSON.stringify({ + name: 'kms-demo', + version: '1.0.0', + }), + }, + }, + 'package-lock.json': JSON.stringify({ + name: 'test-dep', + version: '1.0.0', + lockfileVersion: 2, + requires: true, + packages: { + '': { + name: 'scratch', + version: '1.0.0', + peerDependencies: { + 'kms-demo': '^1.0.0', + }, + }, + 'node_modules/kms-demo': { + version: '1.0.0', + }, + }, + dependencies: { + 'kms-demo': { + version: '1.0.0', + }, + }, + }), + } + + const installWithOptionalDeps = { + 'package.json': JSON.stringify({ + name: 'test-dep', + version: '1.0.0', + dependencies: { + 'kms-demo': '^1.0.0', + }, + optionalDependencies: { + lorem: '^1.0.0', + }, + }, null, 2), + node_modules: { + 'kms-demo': { + 'package.json': JSON.stringify({ + name: 'kms-demo', + version: '1.0.0', + }), + }, + }, + 'package-lock.json': JSON.stringify({ + name: 'test-dep', + version: '1.0.0', + lockfileVersion: 2, + requires: true, + packages: { + '': { + name: 'scratch', + version: '1.0.0', + dependencies: { + 'kms-demo': '^1.0.0', + }, + optionalDependencies: { + lorem: '^1.0.0', + }, + }, + 'node_modules/kms-demo': { + version: '1.0.0', + }, + }, + dependencies: { + 'kms-demo': { + version: '1.0.0', + }, + }, + }), + } + + const installWithMultipleRegistries = { + 'package.json': JSON.stringify({ + name: 'test-dep', + version: '1.0.0', + dependencies: { + '@npmcli/arborist': '^1.0.0', + 'kms-demo': '^1.0.0', + }, + }), + node_modules: { + '@npmcli/arborist': { + 'package.json': JSON.stringify({ + name: '@npmcli/arborist', + version: '1.0.14', + }), + }, + 'kms-demo': { + 'package.json': JSON.stringify({ + name: 'kms-demo', + version: '1.0.0', + }), + }, + }, + 'package-lock.json': JSON.stringify({ + name: 'test-dep', + version: '1.0.0', + lockfileVersion: 2, + requires: true, + packages: { + '': { + name: 'test-dep', + version: '1.0.0', + dependencies: { + '@npmcli/arborist': '^1.0.0', + 'kms-demo': '^1.0.0', + }, + }, + 'node_modules/@npmcli/arborist': { + version: '1.0.14', + }, + 'node_modules/kms-demo': { + version: '1.0.0', + }, + }, + dependencies: { + '@npmcli/arborist': { + version: '1.0.14', + }, + 'kms-demo': { + version: '1.0.0', + }, + }, + }), + } + + const installWithThirdPartyRegistry = { + 'package.json': JSON.stringify({ + name: 'test-dep', + version: '1.0.0', + dependencies: { + '@npmcli/arborist': '^1.0.0', + }, + }), + node_modules: { + '@npmcli/arborist': { + 'package.json': JSON.stringify({ + name: '@npmcli/arborist', + version: '1.0.14', + }), + }, + }, + 'package-lock.json': JSON.stringify({ + name: 'test-dep', + version: '1.0.0', + lockfileVersion: 2, + requires: true, + packages: { + '': { + name: 'test-dep', + version: '1.0.0', + dependencies: { + '@npmcli/arborist': '^1.0.0', + }, + }, + 'node_modules/@npmcli/arborist': { + version: '1.0.14', + }, + }, + dependencies: { + '@npmcli/arborist': { + version: '1.0.14', + }, + }, + }), + } + + async function manifestWithValidSigs ({ registry }) { + const manifest = registry.manifest({ + name: 'kms-demo', + packuments: [{ + version: '1.0.0', + dist: { + tarball: 'https://registry.npmjs.org/kms-demo/-/kms-demo-1.0.0.tgz', + integrity: 'sha512-QqZ7VJ/8xPkS9s2IWB7Shj3qTJdcRyeXKbPQnsZjsPEwvutGv0EGeVchPca' + + 'uoiDFJlGbZMFq5GDCurAGNSghJQ==', + signatures: [ + { + keyid: 'SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA', + sig: 'MEUCIDrLNspFeU5NZ6d55ycVBZIMXnPJi/XnI1Y2dlJvK8P1AiEAnXjn1IOMUd+U7YfPH' + + '+FNjwfLq+jCwfH8uaxocq+mpPk=', + }, + ], + }, + }], + }) + await registry.package({ manifest }) + } + + async function manifestWithInvalidSigs ({ registry, name = 'kms-demo', version = '1.0.0' }) { + const manifest = registry.manifest({ + name, + packuments: [{ + version, + dist: { + tarball: `https://registry.npmjs.org/${name}/-/${name}-${version}.tgz`, + integrity: 'sha512-QqZ7VJ/8xPkS9s2IWB7Shj3qTJdcRyeXKbPQnsZjsPEwvutGv0EGeVchPca' + + 'uoiDFJlGbZMFq5GDCurAGNSghJQ==', + signatures: [ + { + keyid: 'SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA', + sig: 'bogus', + }, + ], + }, + }], + }) + await registry.package({ manifest }) + } + + async function manifestWithoutSigs ({ registry, name = 'kms-demo', version = '1.0.0' }) { + const manifest = registry.manifest({ + name, + packuments: [{ + version, + }], + }) + await registry.package({ manifest }) + } + + t.test('with valid signatures', async t => { + const { npm, joinedOutput } = await loadMockNpm(t, { + prefixDir: installWithValidSigs, + }) + const registry = new MockRegistry({ tap: t, registry: npm.config.get('registry') }) + await manifestWithValidSigs({ registry }) + registry.nock.get('/-/npm/v1/keys').reply(200, VALID_REGISTRY_KEYS) + + await npm.exec('audit', ['signatures']) + + t.equal(process.exitCode, 0, 'should exit successfully') + process.exitCode = 0 + t.match(joinedOutput(), /audited 1 package/) + t.matchSnapshot(joinedOutput()) + }) + + t.test('with valid signatures using alias', async t => { + const { npm, joinedOutput } = await loadMockNpm(t, { + prefixDir: installWithAlias, + }) + const registry = new MockRegistry({ tap: t, registry: npm.config.get('registry') }) + const manifest = registry.manifest({ + name: 'node-fetch', + packuments: [{ + version: '1.7.1', + dist: { + tarball: 'https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.1.tgz', + integrity: 'sha512-j8XsFGCLw79vWXkZtMSmmLaOk9z5SQ9bV/tkbZVCqvgwzrjAGq6' + + '6igobLofHtF63NvMTp2WjytpsNTGKa+XRIQ==', + signatures: [ + { + keyid: 'SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA', + sig: 'MEYCIQDEn2XrrMXlRm+wh2tOIUyb0Km3ZujfT+6Mf61OXGK9zQIhANnPauUwx3' + + 'N9RcQYQakDpOmLvYzNkySh7fmzmvyhk21j', + }, + ], + }, + }], + }) + await registry.package({ manifest }) + registry.nock.get('/-/npm/v1/keys').reply(200, VALID_REGISTRY_KEYS) + + await npm.exec('audit', ['signatures']) + + t.equal(process.exitCode, 0, 'should exit successfully') + process.exitCode = 0 + t.match(joinedOutput(), /audited 1 package/) + t.matchSnapshot(joinedOutput()) + }) + + t.test('with multiple valid signatures and one invalid', async t => { + const { npm, joinedOutput } = await loadMockNpm(t, { + prefixDir: { + 'package.json': JSON.stringify({ + name: 'test-dep', + version: '1.0.0', + dependencies: { + 'kms-demo': '^1.0.0', + 'node-fetch': '^1.6.0', + }, + devDependencies: { + async: '~2.1.0', + }, + }), + node_modules: { + 'kms-demo': { + 'package.json': JSON.stringify({ + name: 'kms-demo', + version: '1.0.0', + }), + }, + async: { + 'package.json': JSON.stringify({ + name: 'async', + version: '2.5.0', + }), + }, + 'node-fetch': { + 'package.json': JSON.stringify({ + name: 'node-fetch', + version: '1.6.0', + }), + }, + }, + 'package-lock.json': JSON.stringify({ + name: 'test-dep', + version: '1.0.0', + lockfileVersion: 2, + requires: true, + packages: { + '': { + name: 'test-dep', + version: '1.0.0', + dependencies: { + 'kms-demo': '^1.0.0', + 'node-fetch': '^1.6.0', + }, + devDependencies: { + async: '~2.1.0', + }, + }, + 'node_modules/kms-demo': { + version: '1.0.0', + }, + 'node_modules/async': { + version: '2.5.0', + }, + 'node_modules/node-fetch': { + version: '1.6.0', + }, + }, + dependencies: { + 'kms-demo': { + version: '1.0.0', + }, + 'node-fetch': { + version: '1.6.0', + }, + async: { + version: '2.5.0', + }, + }, + }), + }, + }) + const registry = new MockRegistry({ tap: t, registry: npm.config.get('registry') }) + await manifestWithValidSigs({ registry }) + const asyncManifest = registry.manifest({ + name: 'async', + packuments: [{ + version: '2.5.0', + dist: { + tarball: 'https://registry.npmjs.org/async/-/async-2.5.0.tgz', + integrity: 'sha512-e+lJAJeNWuPCNyxZKOBdaJGyLGHugXVQtrAwtuAe2vhxTYxFT' + + 'KE73p8JuTmdH0qdQZtDvI4dhJwjZc5zsfIsYw==', + signatures: [ + { + keyid: 'SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA', + sig: 'MEUCIQCM8cX2U3IVZKKhzQx1w5AlNSDUI+fVf4857K1qT0NTNgIgdT4qwEl' + + '/kg2vU1uIWUI0bGikRvVHCHlRs1rgjPMpRFA=', + }, + ], + }, + }], + }) + await registry.package({ manifest: asyncManifest }) + await manifestWithInvalidSigs({ registry, name: 'node-fetch', version: '1.6.0' }) + registry.nock.get('/-/npm/v1/keys').reply(200, VALID_REGISTRY_KEYS) + + await npm.exec('audit', ['signatures']) + + t.equal(process.exitCode, 1, 'should exit with error') + process.exitCode = 0 + t.match(joinedOutput(), /audited 3 packages/) + t.match(joinedOutput(), /2 packages have verified registry signatures/) + t.match(joinedOutput(), /1 package has an invalid registry signature/) + t.matchSnapshot(joinedOutput()) + }) + + t.test('with bundled and peer deps and no signatures', async t => { + const { npm, joinedOutput } = await loadMockNpm(t, { + prefixDir: installWithPeerDeps, + }) + const registry = new MockRegistry({ tap: t, registry: npm.config.get('registry') }) + await manifestWithValidSigs({ registry }) + registry.nock.get('/-/npm/v1/keys').reply(200, VALID_REGISTRY_KEYS) + + await npm.exec('audit', ['signatures']) + + t.equal(process.exitCode, 0, 'should exit successfully') + process.exitCode = 0 + t.match(joinedOutput(), /audited 1 package/) + t.matchSnapshot(joinedOutput()) + }) + + t.test('with invalid signatures', async t => { + const { npm, joinedOutput } = await loadMockNpm(t, { + prefixDir: installWithValidSigs, + }) + const registry = new MockRegistry({ tap: t, registry: npm.config.get('registry') }) + await manifestWithInvalidSigs({ registry }) + registry.nock.get('/-/npm/v1/keys').reply(200, VALID_REGISTRY_KEYS) + + await npm.exec('audit', ['signatures']) + + t.equal(process.exitCode, 1, 'should exit with error') + process.exitCode = 0 + t.match(joinedOutput(), /invalid registry signature/) + t.match(joinedOutput(), /kms-demo@1.0.0/) + t.matchSnapshot(joinedOutput()) + }) + + t.test('with valid and missing signatures', async t => { + const { npm, joinedOutput } = await loadMockNpm(t, { + prefixDir: installWithMultipleDeps, + }) + const registry = new MockRegistry({ tap: t, registry: npm.config.get('registry') }) + await manifestWithValidSigs({ registry }) + await manifestWithoutSigs({ registry, name: 'async', version: '1.1.1' }) + registry.nock.get('/-/npm/v1/keys').reply(200, VALID_REGISTRY_KEYS) + + await npm.exec('audit', ['signatures']) + + t.equal(process.exitCode, 1, 'should exit with error') + process.exitCode = 0 + t.match(joinedOutput(), /audited 2 packages/) + t.match(joinedOutput(), /verified registry signature/) + t.match(joinedOutput(), /missing registry signature/) + t.matchSnapshot(joinedOutput()) + }) + + t.test('with both invalid and missing signatures', async t => { + const { npm, joinedOutput } = await loadMockNpm(t, { + prefixDir: installWithMultipleDeps, + }) + const registry = new MockRegistry({ tap: t, registry: npm.config.get('registry') }) + await manifestWithInvalidSigs({ registry }) + await manifestWithoutSigs({ registry, name: 'async', version: '1.1.1' }) + registry.nock.get('/-/npm/v1/keys').reply(200, VALID_REGISTRY_KEYS) + + await npm.exec('audit', ['signatures']) + + t.equal(process.exitCode, 1, 'should exit with error') + process.exitCode = 0 + t.match(joinedOutput(), /audited 2 packages/) + t.match(joinedOutput(), /invalid/) + t.match(joinedOutput(), /missing/) + t.matchSnapshot(joinedOutput()) + }) + + t.test('with multiple invalid signatures', async t => { + const { npm, joinedOutput } = await loadMockNpm(t, { + prefixDir: installWithMultipleDeps, + }) + const registry = new MockRegistry({ tap: t, registry: npm.config.get('registry') }) + await manifestWithInvalidSigs({ registry, name: 'kms-demo', version: '1.0.0' }) + await manifestWithInvalidSigs({ registry, name: 'async', version: '1.1.1' }) + registry.nock.get('/-/npm/v1/keys').reply(200, VALID_REGISTRY_KEYS) + + await npm.exec('audit', ['signatures']) + + t.equal(process.exitCode, 1, 'should exit with error') + process.exitCode = 0 + t.matchSnapshot(joinedOutput()) + }) + + t.test('with multiple missing signatures', async t => { + const { npm, joinedOutput } = await loadMockNpm(t, { + prefixDir: installWithMultipleDeps, + }) + const registry = new MockRegistry({ tap: t, registry: npm.config.get('registry') }) + await manifestWithoutSigs({ registry, name: 'kms-demo', version: '1.0.0' }) + await manifestWithoutSigs({ registry, name: 'async', version: '1.1.1' }) + registry.nock.get('/-/npm/v1/keys').reply(200, VALID_REGISTRY_KEYS) + + await npm.exec('audit', ['signatures']) + + t.equal(process.exitCode, 1, 'should exit with error') + process.exitCode = 0 + t.matchSnapshot(joinedOutput()) + }) + + t.test('with signatures but no public keys', async t => { + const { npm } = await loadMockNpm(t, { + prefixDir: installWithValidSigs, + }) + const registry = new MockRegistry({ tap: t, registry: npm.config.get('registry') }) + await manifestWithValidSigs({ registry }) + registry.nock.get('/-/npm/v1/keys').reply(404) + + await t.rejects( + npm.exec('audit', ['signatures']), + /no corresponding public key can be found/, + 'should throw with error' + ) + }) + + t.test('with signatures but the public keys are expired', async t => { + const { npm } = await loadMockNpm(t, { + prefixDir: installWithValidSigs, + }) + const registry = new MockRegistry({ tap: t, registry: npm.config.get('registry') }) + await manifestWithValidSigs({ registry }) + registry.nock.get('/-/npm/v1/keys').reply(200, EXPIRED_REGISTRY_KEYS) + + await t.rejects( + npm.exec('audit', ['signatures']), + /the corresponding public key has expired/, + 'should throw with error' + ) + }) + + t.test('with signatures but the public keyid does not match', async t => { + const { npm } = await loadMockNpm(t, { + prefixDir: installWithValidSigs, + }) + const registry = new MockRegistry({ tap: t, registry: npm.config.get('registry') }) + await manifestWithValidSigs({ registry }) + registry.nock.get('/-/npm/v1/keys').reply(200, MISMATCHING_REGISTRY_KEYS) + + await t.rejects( + npm.exec('audit', ['signatures']), + /no corresponding public key can be found/, + 'should throw with error' + ) + }) + + t.test('with keys but missing signature', async t => { + const { npm, joinedOutput } = await loadMockNpm(t, { + prefixDir: installWithValidSigs, + }) + const registry = new MockRegistry({ tap: t, registry: npm.config.get('registry') }) + await manifestWithoutSigs({ registry }) + registry.nock.get('/-/npm/v1/keys').reply(200, VALID_REGISTRY_KEYS) + + await npm.exec('audit', ['signatures']) + + t.equal(process.exitCode, 1, 'should exit with error') + process.exitCode = 0 + t.match( + joinedOutput(), + /registry is providing signing keys/ + ) + t.matchSnapshot(joinedOutput()) + }) + + t.test('output details about missing signatures', async t => { + const { npm, joinedOutput } = await loadMockNpm(t, { + prefixDir: installWithValidSigs, + }) + const registry = new MockRegistry({ tap: t, registry: npm.config.get('registry') }) + await manifestWithoutSigs({ registry }) + registry.nock.get('/-/npm/v1/keys').reply(200, VALID_REGISTRY_KEYS) + + await npm.exec('audit', ['signatures']) + + t.equal(process.exitCode, 1, 'should exit with error') + process.exitCode = 0 + t.match( + joinedOutput(), + /kms-demo/ + ) + t.matchSnapshot(joinedOutput()) + }) + + t.test('json output with valid signatures', async t => { + const { npm, joinedOutput } = await loadMockNpm(t, { + prefixDir: installWithValidSigs, + config: { + json: true, + }, + }) + const registry = new MockRegistry({ tap: t, registry: npm.config.get('registry') }) + await manifestWithValidSigs({ registry }) + registry.nock.get('/-/npm/v1/keys').reply(200, VALID_REGISTRY_KEYS) + + await npm.exec('audit', ['signatures']) + + t.equal(process.exitCode, 0, 'should exit successfully') + process.exitCode = 0 + t.match(joinedOutput(), JSON.stringify({ invalid: [], missing: [] }, null, 2)) + t.matchSnapshot(joinedOutput()) + }) + + t.test('json output with invalid signatures', async t => { + const { npm, joinedOutput } = await loadMockNpm(t, { + prefixDir: installWithValidSigs, + config: { + json: true, + }, + }) + const registry = new MockRegistry({ tap: t, registry: npm.config.get('registry') }) + await manifestWithInvalidSigs({ registry }) + registry.nock.get('/-/npm/v1/keys').reply(200, VALID_REGISTRY_KEYS) + + await npm.exec('audit', ['signatures']) + + t.equal(process.exitCode, 1, 'should exit with error') + process.exitCode = 0 + t.matchSnapshot(joinedOutput()) + }) + + t.test('json output with invalid and missing signatures', async t => { + const { npm, joinedOutput } = await loadMockNpm(t, { + prefixDir: installWithMultipleDeps, + config: { + json: true, + }, + }) + const registry = new MockRegistry({ tap: t, registry: npm.config.get('registry') }) + await manifestWithInvalidSigs({ registry }) + await manifestWithoutSigs({ registry, name: 'async', version: '1.1.1' }) + registry.nock.get('/-/npm/v1/keys').reply(200, VALID_REGISTRY_KEYS) + + await npm.exec('audit', ['signatures']) + + t.equal(process.exitCode, 1, 'should exit with error') + process.exitCode = 0 + t.matchSnapshot(joinedOutput()) + }) + + t.test('omit dev dependencies with missing signature', async t => { + const { npm, joinedOutput } = await loadMockNpm(t, { + prefixDir: installWithMultipleDeps, + config: { + omit: ['dev'], + }, + }) + const registry = new MockRegistry({ tap: t, registry: npm.config.get('registry') }) + await manifestWithValidSigs({ registry }) + registry.nock.get('/-/npm/v1/keys').reply(200, VALID_REGISTRY_KEYS) + + await npm.exec('audit', ['signatures']) + + t.equal(process.exitCode, 0, 'should exit successfully') + process.exitCode = 0 + t.match(joinedOutput(), /audited 1 package/) + t.matchSnapshot(joinedOutput()) + }) + + t.test('third-party registry without keys does not verify', async t => { + const registryUrl = 'https://verdaccio-clone2.org' + const { npm } = await loadMockNpm(t, { + prefixDir: installWithThirdPartyRegistry, + config: { + '@npmcli:registry': registryUrl, + }, + }) + const registry = new MockRegistry({ tap: t, registry: registryUrl }) + const manifest = registry.manifest({ + name: '@npmcli/arborist', + packuments: [{ + version: '1.0.14', + dist: { + tarball: 'https://registry.npmjs.org/@npmcli/arborist/-/@npmcli/arborist-1.0.14.tgz', + integrity: 'sha512-caa8hv5rW9VpQKk6tyNRvSaVDySVjo9GkI7Wj/wcsFyxPm3tYrE' + + 'sFyTjSnJH8HCIfEGVQNjqqKXaXLFVp7UBag==', + }, + }], + }) + await registry.package({ manifest }) + registry.nock.get('/-/npm/v1/keys').reply(404) + + await t.rejects( + npm.exec('audit', ['signatures']), + /found no dependencies to audit that where installed from a supported registry/ + ) + }) + + t.test('third-party registry with keys and signatures', async t => { + const registryUrl = 'https://verdaccio-clone.org' + const { npm, joinedOutput } = await loadMockNpm(t, { + prefixDir: installWithThirdPartyRegistry, + config: { + '@npmcli:registry': registryUrl, + }, + }) + const registry = new MockRegistry({ tap: t, registry: registryUrl }) + + const manifest = registry.manifest({ + name: '@npmcli/arborist', + packuments: [{ + version: '1.0.14', + dist: { + tarball: 'https://registry.npmjs.org/@npmcli/arborist/-/@npmcli/arborist-1.0.14.tgz', + integrity: 'sha512-caa8hv5rW9VpQKk6tyNRvSaVDySVjo9GkI7Wj/wcsFyxPm3tYrE' + + 'sFyTjSnJH8HCIfEGVQNjqqKXaXLFVp7UBag==', + signatures: [ + { + keyid: 'SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA', + sig: 'MEUCIAvNpR3G0j7WOPUuVMhE0ZdM8PnDNcsoeFD8Iwz9YWIMAiEAn8cicDC2' + + 'Sf9MFQydqTv6S5XYsAh9Af1sig1nApNI11M=', + }, + ], + }, + }], + }) + await registry.package({ manifest }) + registry.nock.get('/-/npm/v1/keys') + .reply(200, { + keys: [{ + expires: null, + keyid: 'SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA', + keytype: 'ecdsa-sha2-nistp256', + scheme: 'ecdsa-sha2-nistp256', + key: 'MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE1Olb3zMAFFxXKHiIkQO5cJ3Yhl5i6UPp+' + + 'IhuteBJbuHcA5UogKo0EWtlWwW6KSaKoTNEYL7JlCQiVnkhBktUgg==', + }], + }) + + await npm.exec('audit', ['signatures']) + + t.equal(process.exitCode, 0, 'should exit successfully') + process.exitCode = 0 + t.match(joinedOutput(), /audited 1 package/) + t.matchSnapshot(joinedOutput()) + }) + + t.test('third-party registry with invalid signatures errors', async t => { + const registryUrl = 'https://verdaccio-clone.org' + const { npm, joinedOutput } = await loadMockNpm(t, { + prefixDir: installWithThirdPartyRegistry, + config: { + '@npmcli:registry': registryUrl, + }, + }) + const registry = new MockRegistry({ tap: t, registry: registryUrl }) + + const manifest = registry.manifest({ + name: '@npmcli/arborist', + packuments: [{ + version: '1.0.14', + dist: { + tarball: 'https://registry.npmjs.org/@npmcli/arborist/-/@npmcli/arborist-1.0.14.tgz', + integrity: 'sha512-caa8hv5rW9VpQKk6tyNRvSaVDySVjo9GkI7Wj/wcsFyxPm3tYrE' + + 'sFyTjSnJH8HCIfEGVQNjqqKXaXLFVp7UBag==', + signatures: [ + { + keyid: 'SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA', + sig: 'bogus', + }, + ], + }, + }], + }) + await registry.package({ manifest }) + registry.nock.get('/-/npm/v1/keys') + .reply(200, { + keys: [{ + expires: null, + keyid: 'SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA', + keytype: 'ecdsa-sha2-nistp256', + scheme: 'ecdsa-sha2-nistp256', + key: 'MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE1Olb3zMAFFxXKHiIkQO5cJ3Yhl5i6UPp+' + + 'IhuteBJbuHcA5UogKo0EWtlWwW6KSaKoTNEYL7JlCQiVnkhBktUgg==', + }], + }) + + await npm.exec('audit', ['signatures']) + + t.equal(process.exitCode, 1, 'should exit with error') + process.exitCode = 0 + t.match(joinedOutput(), /https:\/\/verdaccio-clone.org/) + t.matchSnapshot(joinedOutput()) + }) + + t.test('third-party registry with keys and missing signatures errors', async t => { + const registryUrl = 'https://verdaccio-clone.org' + const { npm, joinedOutput } = await loadMockNpm(t, { + prefixDir: installWithThirdPartyRegistry, + config: { + '@npmcli:registry': registryUrl, + }, + }) + const registry = new MockRegistry({ tap: t, registry: registryUrl }) + + const manifest = registry.manifest({ + name: '@npmcli/arborist', + packuments: [{ + version: '1.0.14', + dist: { + tarball: 'https://registry.npmjs.org/@npmcli/arborist/-/@npmcli/arborist-1.0.14.tgz', + integrity: 'sha512-caa8hv5rW9VpQKk6tyNRvSaVDySVjo9GkI7Wj/wcsFyxPm3tYrE' + + 'sFyTjSnJH8HCIfEGVQNjqqKXaXLFVp7UBag==', + }, + }], + }) + await registry.package({ manifest }) + registry.nock.get('/-/npm/v1/keys') + .reply(200, { + keys: [{ + expires: null, + keyid: 'SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA', + keytype: 'ecdsa-sha2-nistp256', + scheme: 'ecdsa-sha2-nistp256', + key: 'MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE1Olb3zMAFFxXKHiIkQO5cJ3Yhl5i6UPp+' + + 'IhuteBJbuHcA5UogKo0EWtlWwW6KSaKoTNEYL7JlCQiVnkhBktUgg==', + }], + }) + + await npm.exec('audit', ['signatures']) + + t.equal(process.exitCode, 1, 'should exit with error') + process.exitCode = 0 + t.match(joinedOutput(), /1 package has a missing registry signature/) + t.matchSnapshot(joinedOutput()) + }) + + t.test('multiple registries with keys and signatures', async t => { + const registryUrl = 'https://verdaccio-clone.org' + const { npm, joinedOutput } = await loadMockNpm(t, { + prefixDir: installWithMultipleRegistries, + config: { + '@npmcli:registry': registryUrl, + }, + }) + const registry = new MockRegistry({ tap: t, registry: npm.config.get('registry') }) + const thirdPartyRegistry = new MockRegistry({ + tap: t, + registry: registryUrl, + }) + await manifestWithValidSigs({ registry }) + registry.nock.get('/-/npm/v1/keys').reply(200, VALID_REGISTRY_KEYS) + + const manifest = thirdPartyRegistry.manifest({ + name: '@npmcli/arborist', + packuments: [{ + version: '1.0.14', + dist: { + tarball: 'https://registry.npmjs.org/@npmcli/arborist/-/@npmcli/arborist-1.0.14.tgz', + integrity: 'sha512-caa8hv5rW9VpQKk6tyNRvSaVDySVjo9GkI7Wj/wcsFyxPm3tYrE' + + 'sFyTjSnJH8HCIfEGVQNjqqKXaXLFVp7UBag==', + signatures: [ + { + keyid: 'SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA', + sig: 'MEUCIAvNpR3G0j7WOPUuVMhE0ZdM8PnDNcsoeFD8Iwz9YWIMAiEAn8cicDC2' + + 'Sf9MFQydqTv6S5XYsAh9Af1sig1nApNI11M=', + }, + ], + }, + }], + }) + await thirdPartyRegistry.package({ manifest }) + thirdPartyRegistry.nock.get('/-/npm/v1/keys') + .reply(200, { + keys: [{ + expires: null, + keyid: 'SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA', + keytype: 'ecdsa-sha2-nistp256', + scheme: 'ecdsa-sha2-nistp256', + key: 'MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE1Olb3zMAFFxXKHiIkQO5cJ3Yhl5i6UPp+' + + 'IhuteBJbuHcA5UogKo0EWtlWwW6KSaKoTNEYL7JlCQiVnkhBktUgg==', + }], + }) + + await npm.exec('audit', ['signatures']) + + t.equal(process.exitCode, 0, 'should exit successfully') + process.exitCode = 0 + t.match(joinedOutput(), /audited 2 packages/) + t.matchSnapshot(joinedOutput()) + }) + + t.test('errors with an empty install', async t => { + const { npm } = await loadMockNpm(t, { + prefixDir: { + 'package.json': JSON.stringify({ + name: 'test-dep', + version: '1.0.0', + }), + }, + }) + + await t.rejects( + npm.exec('audit', ['signatures']), + /found no installed dependencies to audit/ + ) + }) + + t.test('errors when the keys endpoint errors', async t => { + const { npm } = await loadMockNpm(t, { + prefixDir: installWithMultipleDeps, + }) + const registry = new MockRegistry({ tap: t, registry: npm.config.get('registry') }) + registry.nock.get('/-/npm/v1/keys') + .reply(500, { error: 'keys broke' }) + + await t.rejects( + npm.exec('audit', ['signatures']), + /keys broke/ + ) + }) + + t.test('ignores optional dependencies', async t => { + const { npm, joinedOutput } = await loadMockNpm(t, { + prefixDir: installWithOptionalDeps, + }) + + const registry = new MockRegistry({ tap: t, registry: npm.config.get('registry') }) + await manifestWithValidSigs({ registry }) + registry.nock.get('/-/npm/v1/keys').reply(200, VALID_REGISTRY_KEYS) + + await npm.exec('audit', ['signatures']) + + t.equal(process.exitCode, 0, 'should exit successfully') + process.exitCode = 0 + t.match(joinedOutput(), /audited 1 package/) + t.matchSnapshot(joinedOutput()) + }) + + t.test('errors when no installed dependencies', async t => { + const { npm } = await loadMockNpm(t, { + prefixDir: noInstall, + }) + const registry = new MockRegistry({ tap: t, registry: npm.config.get('registry') }) + registry.nock.get('/-/npm/v1/keys').reply(200, VALID_REGISTRY_KEYS) + + await t.rejects( + npm.exec('audit', ['signatures']), + /found no dependencies to audit that where installed from a supported registry/ + ) + }) + + t.test('should skip missing non-prod deps', async t => { + const { npm } = await loadMockNpm(t, { + prefixDir: { + 'package.json': JSON.stringify({ + name: 'delta', + version: '1.0.0', + devDependencies: { + chai: '^1.0.0', + }, + }, null, 2), + node_modules: {}, + }, + }) + const registry = new MockRegistry({ tap: t, registry: npm.config.get('registry') }) + registry.nock.get('/-/npm/v1/keys').reply(200, VALID_REGISTRY_KEYS) + + await t.rejects( + npm.exec('audit', ['signatures']), + /found no dependencies to audit that where installed from a supported registry/ + ) + }) + + t.test('should skip invalid pkg ranges', async t => { + const { npm } = await loadMockNpm(t, { + prefixDir: { + 'package.json': JSON.stringify({ + name: 'delta', + version: '1.0.0', + dependencies: { + cat: '>=^2', + }, + }, null, 2), + node_modules: { + cat: { + 'package.json': JSON.stringify({ + name: 'cat', + version: '1.0.0', + }, null, 2), + }, + }, + }, + }) + + await t.rejects( + npm.exec('audit', ['signatures']), + /found no dependencies to audit that where installed from a supported registry/ + ) + }) + + t.test('should skip git specs', async t => { + const { npm } = await loadMockNpm(t, { + prefixDir: { + 'package.json': JSON.stringify({ + name: 'delta', + version: '1.0.0', + dependencies: { + cat: 'github:username/foo', + }, + }, null, 2), + node_modules: { + cat: { + 'package.json': JSON.stringify({ + name: 'cat', + version: '1.0.0', + }, null, 2), + }, + }, + }, + }) + + const registry = new MockRegistry({ tap: t, registry: npm.config.get('registry') }) + registry.nock.get('/-/npm/v1/keys').reply(200, VALID_REGISTRY_KEYS) + + await t.rejects( + npm.exec('audit', ['signatures']), + /found no dependencies to audit that where installed from a supported registry/ + ) + }) + + t.test('errors for global packages', async t => { + const { npm } = await loadMockNpm(t, { + config: { global: true }, + }) + + await t.rejects( + npm.exec('audit', ['signatures']), + /`npm audit signatures` does not support global packages/, + { code: 'ECIGLOBAL' } + ) + }) + + t.test('with invalid signtaures and color output enabled', async t => { + const { npm, joinedOutput } = await loadMockNpm(t, { + prefixDir: installWithValidSigs, + config: { color: 'always' }, + }) + const registry = new MockRegistry({ tap: t, registry: npm.config.get('registry') }) + await manifestWithInvalidSigs({ registry }) + registry.nock.get('/-/npm/v1/keys').reply(200, VALID_REGISTRY_KEYS) + + await npm.exec('audit', ['signatures']) + + t.equal(process.exitCode, 1, 'should exit with error') + process.exitCode = 0 + t.match( + joinedOutput(), + // eslint-disable-next-line no-control-regex + /\u001b\[1m\u001b\[31minvalid\u001b\[39m\u001b\[22m registry signature/ + ) + t.matchSnapshot(joinedOutput()) + }) + + t.test('workspaces', async t => { + t.test('verifies registry deps and ignores local workspace deps', async t => { + const { npm, joinedOutput } = await loadMockNpm(t, { + prefixDir: workspaceInstall, + }) + const registry = new MockRegistry({ tap: t, registry: npm.config.get('registry') }) + await manifestWithValidSigs({ registry }) + const asyncManifest = registry.manifest({ + name: 'async', + packuments: [{ + version: '2.5.0', + dist: { + tarball: 'https://registry.npmjs.org/async/-/async-2.5.0.tgz', + integrity: 'sha512-e+lJAJeNWuPCNyxZKOBdaJGyLGHugXVQtrAwtuAe2vhxTYxFT' + + 'KE73p8JuTmdH0qdQZtDvI4dhJwjZc5zsfIsYw==', + signatures: [ + { + keyid: 'SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA', + sig: 'MEUCIQCM8cX2U3IVZKKhzQx1w5AlNSDUI+fVf4857K1qT0NTNgIgdT4qwEl' + + '/kg2vU1uIWUI0bGikRvVHCHlRs1rgjPMpRFA=', + }, + ], + }, + }], + }) + const lightCycleManifest = registry.manifest({ + name: 'light-cycle', + packuments: [{ + version: '1.4.2', + dist: { + tarball: 'https://registry.npmjs.org/light-cycle/-/light-cycle-1.4.2.tgz', + integrity: 'sha512-badZ3KMUaGwQfVcHjXTXSecYSXxT6f99bT+kVzBqmO10U1UNlE' + + 'thJ1XAok97E4gfDRTA2JJ3r0IeMPtKf0EJMw==', + signatures: [ + { + keyid: 'SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA', + sig: 'MEUCIQDXjoxQz4MzPqaIuy2RJmBlcFp0UD3h9EhKZxxEz9IYZAIgLO0znG5' + + 'aGciTAg4u8fE0/UXBU4gU7JcvTZGxW2BmKGw=', + }, + ], + }, + }], + }) + await registry.package({ manifest: asyncManifest }) + await registry.package({ manifest: lightCycleManifest }) + registry.nock.get('/-/npm/v1/keys').reply(200, VALID_REGISTRY_KEYS) + + await npm.exec('audit', ['signatures']) + + t.equal(process.exitCode, 0, 'should exit successfully') + process.exitCode = 0 + t.match(joinedOutput(), /audited 3 packages/) + t.matchSnapshot(joinedOutput()) + }) + + t.test('verifies registry deps when filtering by workspace name', async t => { + const { npm, joinedOutput } = await loadMockNpm(t, { + prefixDir: workspaceInstall, + config: { workspace: ['./packages/a'] }, + }) + const registry = new MockRegistry({ tap: t, registry: npm.config.get('registry') }) + const asyncManifest = registry.manifest({ + name: 'async', + packuments: [{ + version: '2.5.0', + dist: { + tarball: 'https://registry.npmjs.org/async/-/async-2.5.0.tgz', + integrity: 'sha512-e+lJAJeNWuPCNyxZKOBdaJGyLGHugXVQtrAwtuAe2vhxTYxFT' + + 'KE73p8JuTmdH0qdQZtDvI4dhJwjZc5zsfIsYw==', + signatures: [ + { + keyid: 'SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA', + sig: 'MEUCIQCM8cX2U3IVZKKhzQx1w5AlNSDUI+fVf4857K1qT0NTNgIgdT4qwEl' + + '/kg2vU1uIWUI0bGikRvVHCHlRs1rgjPMpRFA=', + }, + ], + }, + }], + }) + const lightCycleManifest = registry.manifest({ + name: 'light-cycle', + packuments: [{ + version: '1.4.2', + dist: { + tarball: 'https://registry.npmjs.org/light-cycle/-/light-cycle-1.4.2.tgz', + integrity: 'sha512-badZ3KMUaGwQfVcHjXTXSecYSXxT6f99bT+kVzBqmO10U1UNlE' + + 'thJ1XAok97E4gfDRTA2JJ3r0IeMPtKf0EJMw==', + signatures: [ + { + keyid: 'SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA', + sig: 'MEUCIQDXjoxQz4MzPqaIuy2RJmBlcFp0UD3h9EhKZxxEz9IYZAIgLO0znG5' + + 'aGciTAg4u8fE0/UXBU4gU7JcvTZGxW2BmKGw=', + }, + ], + }, + }], + }) + await registry.package({ manifest: asyncManifest }) + await registry.package({ manifest: lightCycleManifest }) + registry.nock.get('/-/npm/v1/keys').reply(200, VALID_REGISTRY_KEYS) + + await npm.exec('audit', ['signatures']) + + t.equal(process.exitCode, 0, 'should exit successfully') + process.exitCode = 0 + t.match(joinedOutput(), /audited 2 packages/) + t.matchSnapshot(joinedOutput()) + }) + + // TODO: This should verify kms-demo, but doesn't because arborist filters + // workspace deps even if they're also root deps + t.test('verifies registry dep if workspaces is disabled', async t => { + const { npm } = await loadMockNpm(t, { + prefixDir: workspaceInstall, + config: { workspaces: false }, + }) + + await t.rejects( + npm.exec('audit', ['signatures']), + /found no installed dependencies to audit/ + ) + }) + }) +}) diff --git a/deps/npm/test/lib/commands/repo.js b/deps/npm/test/lib/commands/repo.js index e06a2894417bc2..4c983f1353f007 100644 --- a/deps/npm/test/lib/commands/repo.js +++ b/deps/npm/test/lib/commands/repo.js @@ -311,4 +311,12 @@ t.test('workspaces', async t => { ) t.match({}, opened, 'opened no repo urls') }) + + t.test('package arg and workspace', async (t) => { + npm.config.set('workspace', ['workspace-a']) + await npm.exec('repo', ['.']) + t.match({ + 'https://github.com/npm/workspaces-test': 1, + }, opened, 'opened url for package arg, not workspace') + }) })