Fixed-output derivations for npm dependencies fail with hash mismatches
when rebuilt on different days. The root cause is that npm registry
metadata (packuments) contain volatile fields that change whenever
upstream publishes a new version.
For example, the TypeScript packument changes daily due to nightly
releases, even though the lockfile pins a specific version. The cached
packument includes _rev, time, modified, and the full versions list -
all of which drift over time.
Strip these volatile fields and filter the versions map to only include
versions actually referenced in the lockfile.
Signed-off-by: Jörg Thalheim <joerg@thalheim.io>
npm lockfiles can contain package aliases where the lockfile key differs
from the actual package name (e.g., "string-width-cjs" aliasing
"string-width"). Previously we always used the lockfile key, causing us
to fetch packuments for the wrong package.
Use the package's own "name" field when present, falling back to the
lockfile key. This ensures we fetch the correct packument for aliased
packages, fixing non-deterministic builds where the wrong packument
fetch could succeed or fail depending on network timing.
The current approach parses tarball URLs to extract package names for
packument fetching. This is fragile as it only handles npmjs.org URLs
and requires special-casing other registries.
Use lockfile keys directly instead. The lockfile already contains the
canonical package names in the form "node_modules/@scope/name", so we
can simply strip the prefix rather than parsing URLs.
This handles all registries uniformly and eliminates the URL parsing
code along with its tests.
Add a cacheVersion parameter to fetchNpmDeps and npmDepsCacheVersion to
buildNpmPackage. When set to 2, prefetch-npm-deps will also fetch and
cache packuments (package metadata) in addition to tarballs.
npm can request packuments with two different Accept headers:
- corgiDoc: abbreviated metadata (default)
- fullDoc: full metadata (used for workspaces)
npm's cache policy requires headers to match, so we cache both versions.
This is opt-in via cacheVersion to avoid breaking existing hashes.
Set npmDepsCacheVersion = 2 for projects using npm workspaces.
Also fix cacache index format to properly separate multiple entries
with newlines, and update map_cache() to parse multi-line index files.