…e manifests (#9343)
### What
When using a proxy/upstream registry (e.g. Azure Artifacts), npm 11
writes lockfile entries for platform-specific optional dependencies
without `version`, `resolved`, or `integrity` fields. Subsequent `npm
ci` fails with `Invalid Version:` errors.
Example broken lockfile entry:
```json
"node_modules/@esbuild/aix-ppc64": {
"optional": true
}
```
### Why
`#fetchManifest()` in `build-ideal-tree.js` fetches manifests for ALL
platform variants of optional dependencies. Proxy registries that
haven't cached packages for non-current platforms return incomplete
metadata (missing `version` field). npm creates a Node with empty
`pkg.version`, and `metaFromNode()` writes `{"optional": true}` to the
lockfile without version.
npm 10 never attempted to resolve non-current-platform variants, so this
only affects npm 11+.
### How
Two-layer defense:
1. **`build-ideal-tree.js` (`#nodeFromSpec`)**: When a registry manifest
lacks a `version` field, treat it as an `EINCOMPLETEMANIFEST` load
failure so that `#pruneFailedOptional()` marks it inert. Only applies to
`spec.registry` specs (not `file:` dependencies which may legitimately
omit version).
2. **`shrinkwrap.js` (`commit()`)**: Skip writing entries where the node
is optional, has no version, and is not the root. This acts as a
defense-in-depth layer.
### Testing
- Unit test added to `workspaces/arborist/test/shrinkwrap.js` —
simulates proxy registry scenario where an optional dep node has no
version
- All existing tests pass (`shrinkwrap.js` 45/45, `build-ideal-tree.js`
114/114)
- Manually verified with Azure Artifacts upstream proxy feed:
- Before fix: 46 broken lockfile entries (optional deps without version)
- After fix: 0 broken entries
- Output matches npm 10 behavior
Closes #9342
---------
Co-authored-by: Michael Smith <owlstronaut@github.com>
(cherry picked from commit 822ce86)
Backport of #9343 to release/v11.