Skip to content

[pull] latest from npm:latest - #14

Open
pull[bot] wants to merge 1021 commits into
Graybar-codespace:latestfrom
npm:latest
Open

[pull] latest from npm:latest#14
pull[bot] wants to merge 1021 commits into
Graybar-codespace:latestfrom
npm:latest

Conversation

@pull

@pull pull Bot commented Oct 19, 2024

Copy link
Copy Markdown

See Commits and Changes for more details.


Created by pull[bot]

Can you help keep this open source service alive? 💖 Please sponsor : )

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The pull request #14 has too many files changed.

We can only review pull requests with up to 300 changed files, and this pull request has 587.

@pull pull Bot added the ⤵️ pull label Oct 19, 2024
@owlstronaut
owlstronaut force-pushed the latest branch 2 times, most recently from 85ec0c9 to 26b6454 Compare March 27, 2025 18:03
Michael Smith and others added 26 commits April 22, 2026 13:05
BREAKING CHANGE: npm no longer registers man pages with the system when installed globally. `man npm-install` will no longer work, but `npm help install` is unaffected.
BREAKING CHANGE: `npm sbom --sbom-format=cyclonedx` now reports the
`name` field from each package's `package.json` instead of the on-disk
directory name. The `name`, `bom-ref`, and `purl` of the root component
and of aliased dependencies may change.

fixes: #9178

---------

Co-authored-by: cyphercodes <cyphercodes@users.noreply.github.com>
BREAKING CHANGE: npm pack and npm publish now error when a package's overrides apply to one or more of its bundled packages (bundledDependencies / bundleDependencies). Defining both fields is still allowed as long as no override actually targets a bundled package. To resolve the error, remove the affected entries from either overrides or the bundle.
BREAKING CHANGE: npm view --json now always returns an array.
When a peerOptional edge conflicts, search descendants for a satisfying
node before fetching from the registry. This prevents extraneous
packages from blocking hoisting of required deps.

This fixes 1/2 or 1/3 of #9249. Before this change a clean install would
resolve `nm/jest-util@30` when resolving the conflict at nm/jest-util
between ts-jest's jest-util@^29||^30, and expect's ^28, which had been
placed at root. `#nodeFromEdge` would create a brand new node, matching
greatest ^30. A subequent install would mark nm/jest-util@30 as
extraneous and prune it.
This tree is valid, but ts-jest's peerOptional jest-util is unsatisfied,
while compatible jest-util are installed and duplicated.

This change reduces duplication and can prevent peerOptionals from
actively installing.

1. Now during initial installs npm will prefer hoisting a dependency to
de-dupe a peerOptional conflict over creating a new extraneous edge.
2. It doesn't solve the problem if there's no compatible version in the
sub-tree. npm will still use `#nodeFromEdge` and install an extraneous
edge.
3. It doesn't fix installs from lockfiles generated before this fix. I
think this is okay, because the trees are techincally valid, just not
optimal.

 I think a better solution to all three issues would be:
* During problemEdge conflict resolution, npm would hoist
nm/jest-util@28 under expect, without replacing it with anything.
ts-jest's peerOptional jest-util would be unsatisfied. This creates the
same tree as npm's second installs that prune extraneous.
* Check for any dependencies that can be hosited. This can run during
the initial install on problemEdge conflict resoultion, and in
pruneIdealTree on any nodes that are removed.
 
I think this solves all three issues. I didn't implement it because I
couldn't find a way to resolve the conflict by leaving a hole in the
tree..
## Description

Fixes #9298.

`buildIdealTree` handles global updates by reading the global
`node_modules` folder directly and adding each entry as a synthetic
top-level dependency. That path did not ignore hidden entries, so a
directory like `.hidden-non-package` was converted to
`.hidden-non-package@*` and rejected by `npm-package-arg`.

This applies the same hidden-entry filter already used by `loadActual`,
so hidden directories and retired scoped package folders are not treated
as installed global packages during `npm up -g`.

## Testing

```sh
node node_modules/tap/bin/run.js workspaces/arborist/test/arborist/build-ideal-tree.js -g "update global ignores hidden"
```

```sh
npm_config_prefix=/tmp/npm-hidden-global-patched.azXmRd node . up -g
```

```sh
node . run eslint -- workspaces/arborist/lib/arborist/build-ideal-tree.js workspaces/arborist/test/arborist/build-ideal-tree.js
```
Exposes a public method on the Arborist class that builds (or reuses) an ideal tree, commits the shrinkwrap metadata, and returns the lockfile contents as a string without writing to disk.

This makes a previously-undocumented sequence (`buildIdealTree()` -> `tree.meta.commit()` -> `String(tree.meta)`) a discoverable, supported API, enabling callers to inspect, diff, or store generated lockfiles without mutating the project's package-lock.json.
…9309)

In continuation of our exploration of using `install-strategy=linked` in
the [Gutenberg
monorepo](WordPress/gutenberg#75814), which
powers the WordPress Block Editor.

When using `install-strategy=linked`, removing a dependency leaves a
dangling symlink at `node_modules/<pkg>` (and
`<workspace>/node_modules/<pkg>` for workspace deps).
The store entry under `node_modules/.store/<pkg>@…` is correctly cleaned
up by `#cleanOrphanedStoreEntries`, but the top-level link pointing into
it is left behind, so `require('<pkg>')` fails with `Cannot find module`
even though the entry still appears in `node_modules/`.

The root cause is the same family of issue as #9106.
`#buildLinkedActualForDiff` builds the synthetic actual tree from the
ideal tree, so any dependency that exists on disk but is no longer in
the ideal tree is never compared, and the diff produces no REMOVE action
for its top-level symlink.

Fixed by extending `#cleanOrphanedStoreEntries` to also collect, per
`node_modules` directory (root and each workspace), the set of valid
top-level link names from the ideal tree, then sweeping each directory
and removing any symlink whose name is not in that set.
The root `node_modules` and every workspace's `node_modules` (via
`idealTree.fsChildren`) are always seeded into the sweep, so the case of
removing the last dependency from the project root or from a workspace
still triggers cleanup, including when the workspace itself is declared
as a root dependency and therefore has its self-link at the root rather
than under its own `node_modules`.

The sweep is restricted to symlinks whose target resolves inside the
project root, so it covers both store links (e.g. `node_modules/eslint
-> .store/...`) and workspace self-links that no longer belong (e.g.
`node_modules/a -> ../packages/a` after `a` is undeclared) without
touching symlinks that point outside the project, such as those created
by `npm link <global-pkg>` without `--save`.
Real directories and npm-managed entries (`.bin`, `.store`,
`.package-lock.json`) are left alone.
The workspace self-link inside its own `node_modules` (e.g.
`packages/a/node_modules/a -> ..`) is in the ideal tree as a non-store
link, so it's preserved.

The sweep also respects the install mode:

- It is skipped entirely for `dryRun` and `packageLockOnly` installs,
both of which short-circuit `#reifyPackages` and must not mutate
`node_modules`.
- For workspace-filtered installs (`npm install -w <ws>
--install-strategy=linked`), the set of `node_modules` directories to
sweep is restricted to the workspaces named in `--workspace`, so dropped
dependencies from the in-scope workspace get cleaned up while
out-of-scope workspaces and the project root are left untouched.

`IsolatedNode`/`IsolatedLink` locations are built with `path.join`,
which uses backslashes on Windows; locations are normalized to forward
slashes inside the sweep so the parser works on both POSIX and Windows.

## Trade-off

This aligns the linked strategy with npm's normal
`node_modules`-is-managed model: any in-project symlink that isn't in
the ideal tree is treated as orphaned, matching what happens today under
the default install strategy.
A consequence is that hand-made or unsaved `npm link` symlinks pointing
to other paths inside the project root (e.g. `node_modules/foo ->
../examples/foo`) are also swept, since npm doesn't currently record
which links it owns and they are indistinguishable from workspace
self-links by target alone.
A more discriminating ownership check (recording managed link names in
the hidden lockfile and only sweeping those) is a worthwhile follow-up
but materially larger than this fix.

## References

Fixes #9308
Related to #9106
## Summary

Closes the per-node duplication gap left by #7992.

A node can have multiple outgoing edges resolving to the same
`name@version` — typically when a package declares both a direct
dependency and an npm alias to the same package, e.g.:

```json
{
  "dependencies": {
    "lodash": "^4.17.21",
    "lodash-aliased": "npm:lodash@^4.17.21"
  }
}
```

`toCyclonedxDependency` and the SPDX relationship loop both map each
edge through `name@version` ID generation without deduplicating, so the
per-node `dependsOn` array (CycloneDX) and `DEPENDENCY_OF` relationships
(SPDX) end up with duplicate entries.

CycloneDX 1.5 requires `dependsOn` items to be unique, so downstream
validators (e.g. Dependency Track) reject the SBOM with:

```
$.dependencies[N].dependsOn: must have only unique items in the array
```

## Changes

- `lib/utils/sbom-cyclonedx.js`: wrap the `dependsOn` array in `[...new
Set(...)]` after mapping edges to refs.
- `lib/utils/sbom-spdx.js`: dedupe per source-node relationships by the
`(spdxElementId, relatedSpdxElement, relationshipType)` triple.
- Test cases added to both `test/lib/utils/sbom-cyclonedx.js` and
`test/lib/utils/sbom-spdx.js` covering the
duplicate-edges-to-same-target scenario, with explicit assertions plus
snapshot updates.

## Test plan

- [x] `node . run test -- test/lib/utils/sbom-cyclonedx.js
test/lib/utils/sbom-spdx.js` — passes
- [x] 100% coverage on both touched files
- [x] Snapshot diff is purely additive (no existing snapshots changed)
- [x] Schema-validation tests in both files still pass for all snapshots
- [x] Reproduced original issue locally with the alias example, ran
patched npm against it, confirmed both CycloneDX `dependsOn` and SPDX
relationships are now deduped

Fixes #9310
<!-- What / Why -->
The --json flag for npm view now always returns an array, even when only
a single version matches. Previously, a single item result would be
unwrapped and returned as a plain object/string, which made programmatic
parsing fragile - consumers had to handle both array and non-array
shapes depending on how many versions matched.

This doc update adds a note to npm-view.md clarifying that the output is
always an array when --json is used, reflecting the behavioral fix.


## References
Related to
npm/statusboard#1074 (comment)
github-actions Bot and others added 30 commits July 8, 2026 15:26
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This PR fixes #9722.

`mock-registry` pinned `@npmcli/arborist@^9.1.2`, which meant the local
workspace version wasn't linked and the dependency was pulled from the
registry instead. `sigstore@^4` got installed at the root node_modules
and `^5` was put into `workspaces/libnpmpublish/node_modules`. The
dev-only `^4` was then excluded when npm was packed.

Fix:
- `mock-registry`: arborist `^9.1.2` ->  `^10.0.0`
- `workspaces/arborist`: validate-npm-package-name `^7.0.2` -> `^8.0.0`
(need to release a patch for arborist)
- Lockfile regenerated via install + dedupe; `node . run dependencies`


The packed tarball now contains `package/node_modules/sigstore` and
`publish --dry-run` from the extracted tarball succeeds.
npm 12.0.0 breaks downstream updaters by returning nested arrays for
`npm view <pkg> versions --json`.

## The bug

On npm 12.0.0, a single array-valued field is wrapped in the outer
results array:

```
$ npm view abbrev versions --json
[["1.0.3","1.0.4", ...]]   # should be ["1.0.3","1.0.4", ...]
```

This happens in `lib/commands/view.js` `#packageOutput`: for a
single-field query it maps to `res.map(m => m[first[0]])`, and when that
field's value is itself an array (e.g. `versions`), it gets
double-wrapped.

## The fix

Return a sole array-valued JSON result directly instead of adding a
second result wrapper. Existing output shapes are preserved:

- scalar and object results still return in an array (`["1.0.0"]`,
`[{...}]`)
- multiple matching versions keep the result boundary (`[[...],[...]]`)
- a single array-valued result is returned directly
(`["1.0.0","1.0.1"]`)

Docs and tests updated to cover flat array, nested array,
object-wrapper, workspace, and multi-match cases.

Co-authored-by: Martin Ruiz <martin.ruiz.mares@gmail.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…9746)

The `bin links adding and removing` test in
`workspaces/arborist/test/arborist/reify.js` reifies `rimraf@2.7.1`
without setting up a mock registry.

This adds `createRegistry(t, true)` so the test uses the mock registry
instead of depending on the real one.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
## Problem

The `build nodejs` jobs in `Release Integration / publish` fail at
`.github/workflows/node-integration.yml`:

```
node . pack --loglevel=silent --json | jq -r .[0].filename
→ jq: error (at <stdin>:9859): Cannot index object with number
→ Process completed with exit code 5
```

As of #9247 (sync json output of pack and publish), `npm pack --json` no
longer outputs an array. `logTar` now buffers `{ [tar.name]: tarball }`,
so the output is an object keyed by package name:

```json
{ "npm": { "filename": "npm-12.0.1.tgz", ... } }
```

The workflow still parsed it with `.[0].filename`, which errors on an
object. npm 12.0.x is the first release carrying this change, so the
release integration only started breaking now.

## Fix

Parse the filename from the object instead of an array index:

```diff
-npmtarball="$(node . pack --loglevel=silent --json | jq -r .[0].filename)"
+npmtarball="$(node . pack --loglevel=silent --json | jq -r 'to_entries[0].value.filename')"
```

Verified locally: returns `npm-12.0.1.tgz`.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
## What / Why

`npm audit` can report that a fix is available through `npm audit fix`
when the highest safe version inside the declared dependency range is
older than the installed version.

Arborist already selects that safe candidate using the advisory range,
but `CanPlaceDep` rejects it because replacement candidates normally
must be newer than the installed version. This causes `npm audit fix` to
complete without applying the advertised remediation.

## How

- Pass the existing audit report from `PlaceDep` into `CanPlaceDep` and
recursive peer placement checks.
- Permit an older candidate only when:
  - the installed node is vulnerable;
  - the candidate is not vulnerable; and
- the candidate passes the existing replacement and peer dependency
checks.
- Preserve existing no-downgrade behavior for ordinary installs and
updates.
- Add synthetic, strictly mocked regressions covering:
  - compatible safe downgrades;
  - non-audit placement;
  - still-vulnerable candidates;
  - peer conflicts;
  - actual tree replacement; and
- metavulnerability removal by pruning a vulnerable transitive
dependency.

This does not change audit reporting or `--force` behavior. Fixes
outside declared dependency ranges still require `npm audit fix
--force`.

## Testing

- Focused Arborist placement and audit tests
- npm command-level audit tests

## References

Fixes #9557  
Fixes #9718

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Installing a package whose peers form a cycle with an already-installed
optional peer could crash with `TypeError: Cannot read properties of
null (reading 'explain')` instead of resolving or reporting a real
conflict.
A minimal trigger: `vite@8.1.4` declares an optional peer on
`@vitejs/devtools`, `@vitejs/devtools` peers back on `vite`, so
installing vite and then adding devtools crashes.

The root cause is in `#loadPeerSet`.
While resolving a package's peer edge through the parent's edge, the
recursive `#nodeFromEdge` call can place a compatible peer that replaces
and detaches the current node from the tree mid-iteration.
The now-invalid edge then reached `#failPeerConflict`, whose
`#explainPeerConflict` calls `node.resolve(edge.name).explain()` on the
detached node.
`resolve()` returns `null` for a node no longer in the tree, so
`.explain()` threw.

A detached node has been superseded by a compatible peer, so there is no
real conflict to report.
The fix adds a guard that stops processing when the node has been
detached, right before `#failPeerConflict`, mirroring the existing
top-of-loop detachment check.
This lets the install complete by keeping the compatible peer that
replaced the node (for the reproduction, `@vitejs/devtools` backs off to
a version that satisfies vite's optional peer range) rather than
crashing or raising a spurious `ERESOLVE`.

## References

Fixes #5222
Closes #4787
)

## Summary

Fixes #9802.

Before the npm 12 stable cut, #9729 restored warn-by-default for unknown
`.npmrc` keys (with `strict-npmrc` to opt into errors). #9733 updated
the `12.0.0-pre.1` changelog bullet to match, but the aggregated stable
`12.0.0` / `@npmcli/config@11.0.0` breaking-change notes (and the config
package's `pre.1` note) still said unknown `.npmrc` configs throw.

That stale line also shipped in the published [v12.0.0 GitHub release
notes](https://github.com/npm/cli/releases/tag/v12.0.0).

This aligns those changelog bullets with the corrected wording:

> unknown CLI flags, abbreviated flags, and single-hyphen multi-char
shorthands now throw instead of warning. (Unknown `.npmrc` configs still
warn by default; opt into erroring with the new `strict-npmrc` config.)

Maintainers may also want to refresh the published `v12.0.0` release
body to match; that cannot be updated via this PR alone.

## References

- #9729 (warn instead of error on unknown `.npmrc` configs)
- #9733 (clarified the `pre.1` changelog note)
## Summary

Ensure `npm owner add` and `npm owner rm` resolve users from the same
registry used for the target package.

The user lookup now receives the package `spec`, allowing
`npm-registry-fetch` to honor scoped registry configuration instead of
falling back to the global registry.

## Testing

Added regression coverage for split global/scoped registry
configurations, including:

- Preventing substitution of the user added as an owner.
- Preventing removal of an unintended existing owner.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 1e1231bc-474e-4a27-8b78-242366f48e19
Make `npm pack` honor `min-release-age-exclude` when resolving packages
from a registry.

Given:

```ini
min-release-age=7
min-release-age-exclude=@myscope/*
```
 `npm pack @myscope/some-package@1.2.3`  incorrectly failed with
 ETARGET  when the package was newer than seven days, despite matching
the exclusion.

Root cause

`min-release-age`  is flattened into the  `before`  option consumed by
 `pacote` . However,  `pacote`  does not interpret
 `min-release-age-exclude` ; callers must remove  before  for matching
packages.

 `npm pack`  performs two manifest resolutions:

1. Directly through `pacote.manifest`
2. Internally through  `libnpmpack` 

Both resolutions received the unmodified `before` option, so the
exclusion was never applied.

Fix

Derive effective options for each package spec using the existing
Arborist release-age helpers:

• Clear `before` when the package matches `min-release-age-exclude` 
• Preserve the cutoff for nonmatching packages
• Pass the same effective options to both manifest resolutions

Using the alias target prevents an excluded alias name from disabling
the release-age policy for an unrelated package.

Test coverage

Added regression coverage confirming that:

• A recently published scoped package matching an exclusion glob can be
packed
• An excluded alias name does not exempt its non-excluded registry
target

The original scenario was also reproduced against a local registry: it
failed with `ETARGET` before this change and successfully produced the
tarball afterward.

References

Fixes #9759

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: dec204b6-ad66-45a5-8228-831e306f6ba6
…tegration (#9822)

## What / Why

The `node-integration` workflow passed `--nodedir` as an npm **CLI
flag** to `npm install`. In npm 12 unknown configs are no longer
accepted, so this now fails with `EUNKNOWNCONFIG`:

```
npm error code EUNKNOWNCONFIG
npm error Unknown cli flag:
npm error   - --nodedir
```

(seen in the Release Integration citgm jobs, e.g. `citgm -
bcrypt@6.0.0`).

`nodedir` is a **node-gyp** option, not an npm config — it only ever
worked via npm's old "accept arbitrary config and re-export as
`npm_config_*`" behavior, which node-gyp itself notes was deprecated in
npm v11
([nodejs/node-gyp#3156](nodejs/node-gyp#3156)).

## Change

Export `npm_package_config_node_gyp_nodedir` instead of using the CLI
flag. This is node-gyp's preferred prefix since npm v11: node-gyp reads
it directly from the environment, and npm does **not** warn on it —
unlike `npm_config_nodedir`, which currently warns and is slated to
error in npm 13.

This also matches how upstream
[nodejs/citgm](https://github.com/nodejs/citgm) supplies `nodedir` (via
env, not a CLI flag).

Applied to both the generated `.github/workflows/node-integration.yml`
and its `scripts/template-oss/node-integration-yml.hbs` template;
`template-oss-apply --lint` passes.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 6e300b82-303b-49ac-ae93-52d984743d5b
## Summary

Add the omitted npm 12 breaking-change note explaining that dependency
lifecycle scripts are blocked by default unless covered by
`allowScripts`, including the approval and rebuild workflow.

## Cause

The change was introduced in
[`5cd5150`](5cd5150).
Although its message described a v12-only default flip, it used `feat:`
instead of `feat!:` and did not include a `BREAKING CHANGE:` footer.
Release Please therefore classified it as a regular feature and omitted
it from the aggregated npm 12 breaking-change notes.

## Release notes

The published [`v12.0.0` GitHub
release](https://github.com/npm/cli/releases/tag/v12.0.0) was corrected
manually with the same breaking-change entry. This PR corrects the
source-controlled changelog used by the npm documentation site.

## Manual correction process

If a breaking change is omitted from release notes in the future:

1. Do not rewrite the merged commit. Add the missing entry under the
released version’s `⚠️ BREAKING CHANGES` section in the root
`CHANGELOG.md` and submit a documentation PR.
2. After the PR merges, the npm documentation repository’s scheduled
**Update CLI** workflow copies the root changelog into the corresponding
CLI documentation page and publishes it. Dispatch that workflow manually
if the docs need to update immediately.
3. Update the existing GitHub release separately because a changelog PR
cannot modify an already-published release. Preserve the complete
current release body before editing it because `gh release edit
--notes-file` replaces the entire body:

   ```bash
gh release view <tag> --repo npm/cli --json body --jq .body > release.md
   # Add the same breaking-change entry to release.md.
   gh release edit <tag> --repo npm/cli --notes-file release.md
   ```

4. Verify that the source changelog, npm documentation page, and GitHub
release contain identical wording.

To prevent the omission, breaking commits must use a conventional-commit
breaking marker such as `feat!:` and include a `BREAKING CHANGE:` footer
describing the user-visible impact.

Fixes #9750
…9836)

## Situation

If the GitHub Actions
[.github/workflows/node-integration.yml](https://github.com/npm/cli/blob/latest/.github/workflows/node-integration.yml)
workflow "nodejs integration" is run, it outputs multiple deprecation
warnings, including the text "Node.js 20 is deprecated." and referring
to the blog article [Deprecation of Node 20 on GitHub Actions
runners](https://github.blog/changelog/2025-09-19-deprecation-of-node-20-on-github-actions-runners/)
dated September 19, 2025.

## Change

Update action usage in
[scripts/template-oss/node-integration-yml.hbs](https://github.com/npm/cli/blob/latest/scripts/template-oss/node-integration-yml.hbs)
to latest versions with `runs.using` `node24`:

| BEFORE | AFTER |
|
------------------------------------------------------------------------------------
|
------------------------------------------------------------------------------------
|
| [actions/cache@v3](https://github.com/actions/cache/tree/v3) |
[actions/cache@v6](https://github.com/actions/cache/tree/v6) |
|
[actions/download-artifact@v4](https://github.com/actions/download-artifact/tree/v4)
|
[actions/download-artifact@v8](https://github.com/actions/download-artifact/tree/v8)
|
|
[actions/github-script@v6](https://github.com/actions/github-script/tree/v6)
|
[actions/github-script@v9](https://github.com/actions/github-script/tree/v9)
|
|
[actions/upload-artifact@v4](https://github.com/actions/upload-artifact/tree/v4)
|
[actions/upload-artifact@v7](https://github.com/actions/upload-artifact/tree/v7)
|

## Verification

Run workflow `node-integration` with:

nodejs: 26.5.1
npm version: 12.0.2

Confirm that there are no longer deprecation warnings output.

## References

- closes #9834
## What / Why

A global install prints "N packages are looking for funding / run `npm
fund` for details", but nothing can be done with it. `npm fund -g` fails
with `EFUNDGLOBAL`, and a bare `npm fund` loads the tree at the local
prefix, so outside a project it prints the directory name and nothing
else.

`packagesFundingMessage` in `lib/utils/reify-output.js` never looked at
`npm.global`, so the hint went out for global installs too. It now
returns early in that case. Making `npm fund -g` work is a separate
feature request; #3112 already splits it out that way.

## Testing

New case in `test/lib/utils/reify-output.js`, mirroring the existing
`fund: false` one: with `global: true` the output no longer mentions
funding. It fails without the change.

## References

Fixes #9863
Fixes #3112
## Summary

- allow explicit directory targets through `npm pack` and `npm publish`
when `allow-directory` is `none` or `root`
- keep dependency fetch policies unchanged and cover the shared library
plus both CLI commands

## Why

`allow-directory` restricts directory dependencies, but the local
package selected for packing or publishing is the command target. The
current flow applies the dependency policy to that target during
manifest or tarball preparation.

Closes #9755.

## Validation

- `node . run test` - passed (123 test files, 100% coverage)
- `node . run test -w libnpmpack` - passed (100% coverage)
- dry-run `pack` and `publish` smokes with `allow-directory=none|root` -
passed

Co-authored-by: ychampion <ychampion@users.noreply.github.com>
## What / Why

`npm uninstall vite@8.2.1` neither removes the package nor complains
about the argument. The spec goes straight through to Arborist as an
`rm` entry, nothing in the tree is named `vite@8.2.1`, so the reify
finishes with "up to date" and `npm ls` still shows `vite`.

`npm update` already validates its own argument list and throws
`EUPDATEARGS` for anything that is not a bare package name. This applies
the same rule to `rm`, so a version, tag or range now fails with
`ERMARGS` and the message names the command to run instead.

## Testing

New case in `workspaces/arborist/test/arborist/build-ideal-tree.js`,
next to the existing rm test and mirroring the update one. It covers an
exact version, a tag, ranges, a scoped name carrying a version, and a
filesystem path, where the suggestion falls back to `<pkg>` because
there is no name to print. It fails without the change.

## References

Fixes #9880
## Summary

- suppress human-readable reify diff lines when JSON output is requested
- preserve detailed diff output for non-JSON `--dry-run` and `--long`
commands
- cover both `--dry-run --json` and `--long --json`

## Background

Detailed dry-run diff output was introduced in npm 10.4.0 by #7133
without accounting for JSON mode. The structured JSON summary already
contains the add, remove, and change details, so the extra text is
redundant and makes stdout invalid JSON.

Related to #8567, with the regression coverage requested during review.

## Testing

- `tap --no-coverage --no-check-coverage test/lib/utils/reify-output.js`
- `eslint lib/utils/reify-output.js test/lib/utils/reify-output.js`

Fixes #8565

Copilot-Session: 48e629f8-eb5a-467c-970b-feb313628c00
## Summary

Updates the bundled `tar` dependency from `7.5.19` to `7.5.22` on the
latest npm 12 branch.

This resolves
[GHSA-r292-9mhp-454m](GHSA-r292-9mhp-454m),
which affects `tar` versions through `7.5.20`. All production dependency
paths now resolve to `tar@7.5.22`.

## Testing

- `node . run dependencies --ignore-scripts`
- `node . ls tar --all --omit=dev`
- `node . audit --omit=dev --json` reports no `tar` vulnerability
- `node . test --ignore-scripts` reports the environment-specific
`EXDEV` failure in `test/bin/windows-shims.js`; all other tests pass

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 73e1dfa8-6bec-47aa-b811-4edf404e9883
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 73e1dfa8-6bec-47aa-b811-4edf404e9883
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 73e1dfa8-6bec-47aa-b811-4edf404e9883
## Summary

Fixes `allowScripts` matching for local file and tarball dependencies,
particularly on Windows.

Arborist stores a resolved Windows file source in this form:

```text
file:C:\project\vendor\pkg.tgz
```

However,  `npm-package-arg` parses an equivalent policy key into:

`saveSpec: file:C:/project/vendor/pkg.tgz`
`fetchSpec: C:\project\vendor\pkg.tgz`

The existing matcher compared `node.resolved` only with `saveSpec` and
`fetchSpec`. Neither comparison could match because one uses different
separators and the other lacks the `file:`  prefix.

## Changes

• Match local file and directory sources against
`file:${parsed.fetchSpec}`, which is the same representation produced by
Arborist's `consistentResolve()`.
• Resolve relative `allowScripts` file keys from the Arborist project
root rather than the process working directory.
• Reuse the Arborist file/directory matcher in `allow-scripts-writer`,
keeping runtime enforcement and policy updates consistent.
• Preserve the existing exact matching behavior for remote URLs.
• Preserve deny-wins behavior when an existing local-file policy entry
is `false`.

The implementation intentionally does not globally normalize path
separators. On POSIX systems, a backslash can be a literal filename
character, so replacing every  `\`  with  `/`  could cause two different
sources to share an approval.

The change also does not modify dependency resolution, lockfile
generation, fetching, linking, or `consistentResolve()`.

## Security considerations

Local file and tarball dependencies continue to require an exact source
identity.

This change does not:

• Match local packages by their self-reported name or version.
• Case-fold paths.
• Match by basename.
• Resolve paths through `realpath()`.
• Treat remote URLs as local file sources.
• Change registry or Git identity matching.

This preserves the manifest-confusion protections in `allowScripts`
while recognizing the exact Windows representation npm already
generates.

## Testing

Added regression coverage for:

• Backslash and forward-slash absolute Windows keys matching the same
local tarball.
• Relative keys resolving from the project root.
• `versionedKeyFor()` producing a key accepted by `isScriptAllowed()`.
• Different local paths remaining unmatched.
• Existing `false` file entries continuing to block approval.
• Windows UNC paths matching through the same `file:${fetchSpec}`
representation.
• POSIX filenames containing literal backslashes remaining distinct.
• Remote URL policies not matching `file:  sources`.
• `npm install-scripts prune` retaining valid local-file entries.

The focused Arborist matcher, policy-writer, and prune tests pass
locally. Native Windows path and UNC behavior is covered by
platform-specific tests and will run in the repository's Windows CI
matrix.

Fixes #9900

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
## Summary

- recognize existing Node.js npm update PRs with branch prefixes such as
`[v22.x]`
- preserve an existing PR title when updating it

## Background

The create-node-pr workflow successfully regenerated and force-pushed
nodejs/node#64884, but then failed because the existing-PR parser
treated `[v22.x] 10.9.9` as the npm version. It attempted to create a
duplicate PR instead of editing the existing one.

This extracts the version following the canonical `deps: upgrade npm to`
text regardless of any title prefix. Existing base-branch filtering
continues to distinguish PRs targeting different Node.js release lines.

Reproduction: https://github.com/npm/cli/actions/runs/32768286326

Copilot-Session: 14a9626a-4d0e-4be3-b06c-b6676ecb3895
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.