Skip to content

feat(npm): serve the abbreviated packument for install-v1 clients - #959

Merged
ertime037 merged 1 commit into
fix/npm-packument-rebuildfrom
feat/npm-abbreviated-packument
Sep 5, 2026
Merged

feat(npm): serve the abbreviated packument for install-v1 clients#959
ertime037 merged 1 commit into
fix/npm-packument-rebuildfrom
feat/npm-abbreviated-packument

Conversation

@devitway

@devitway devitway commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Closes #957. Stacked on #958 — that is the base branch; GitHub will retarget this to main once #958 lands.

What

npm install asks for application/vnd.npm.install-v1+json and NORA ignored it, serving the full packument to every client. The fields the full document carries are exactly the ones an installer never reads: readme, maintainers, repository, per-version description, scripts, gitHead.

The packument serve path now branches on Accept and projects the document down to npm's abbreviated shape.

Three decisions worth a look:

  • The per-version field list is an allowlist, in one place. Dropping a field an installer consults — os, cpu, engines, peerDependenciesMeta — silently changes what gets resolved and installed. A field gets added there once, not at a call site.
  • Vary: Accept is load-bearing, not decoration. The body now depends on a request header while NORA still marks metadata Cache-Control: public; without Vary, a shared cache can hand the abbreviated document to a client that asked for the full one.
  • Derived locally, never requested upstream. The cache keeps exactly one canonical object per package, so a short document can never displace the full one.

An unparsable body is served unchanged rather than turned into an error — the client asked for a projection of this document, not for a failure.

Measured on a polygon

Real S3 (MinIO), live upstream through the proxy, real npm install:

package full install-v1 versions
lodash 247 652 B 71 989 B −70.9% 117 → 117
express 804 975 B 344 703 B −57.2% 288 → 288

npm install verified against both a proxied package and a hosted one.

A bug the polygon caught

The first pass abbreviated the body on the stale-serve path but left Content-Type: application/json on it — a short document announced as the full one. It only showed up because the polygon instance happened to serve from the stale path. Fixed by routing that branch through the same helper, and test_npm_packument_stale_serve_honours_install_v1 now asserts both the x-nora-stale precondition and the content type, so the degraded path cannot drift from the normal one again.

Tests

Four in spec_conformance_tests:

  • test_npm_packument_abbreviated_on_install_v1_accept — content type, Vary, dropped fields, and every resolution-affecting field asserted present
  • test_npm_packument_full_without_install_v1_accept — no header, full document intact
  • test_npm_packument_stale_serve_honours_install_v1 — the degraded path agrees with itself
  • test_npm_packument_unparsable_body_served_unchanged — a non-packument body is passed through

Full suite: 63 lib + 1788 bin + 6 doc, zero failures. cargo fmt --check and cargo clippy -p nora-registry --all-targets -- -D warnings clean.

Credit

Surfaced by #889, which materializes an install-v1.json alongside the full packument. That half of the idea stands on its own, and the commit carries Co-authored-by: ivaseeq.

`npm install` asks for `application/vnd.npm.install-v1+json` — the abbreviated
packument — and NORA ignored it, serving the full document to every client. The
fields it carries are exactly the ones an installer never reads: readme,
maintainers, repository, per-version description, scripts, gitHead.

The packument serve path now branches on `Accept` and projects the document down
to npm's abbreviated shape. The per-version field list is an allowlist, in one
place: dropping a field an installer consults — `os`, `cpu`, `engines`,
`peerDependenciesMeta` — silently changes what gets resolved, so a field is
added there once rather than at a call site.

`Vary: Accept` goes with it, and it is not decoration. The body now depends on a
request header while NORA still marks metadata `Cache-Control: public`; without
`Vary` a shared cache can hand the abbreviated document to a client that asked
for the full one.

The abbreviation is derived locally rather than requested upstream, so the cache
keeps exactly one canonical object per package and the full document is never
displaced by a short one.

Measured against a live upstream through the proxy, all versions preserved:

    lodash     247652 -> 71989 B    -70.9%   117 versions
    express    804975 -> 344703 B   -57.2%   288 versions

An unparsable body is served unchanged rather than turned into an error: the
client asked for a projection of this document, not for a failure.

Closes #957

Co-authored-by: ivaseeq <126263471+ivaseeq@users.noreply.github.com>
@devitway
devitway requested a review from ertime037 as a code owner September 5, 2026 08:31
@ertime037
ertime037 merged commit d400d70 into fix/npm-packument-rebuild Sep 5, 2026
@ertime037
ertime037 deleted the feat/npm-abbreviated-packument branch September 5, 2026 08:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants