feat(npm): the virtual read's fetch options 1/3 (S31 plan: 12/19)

The stack

Step 12 is three MRs. They must merge in this order: part 3's Resolve calls part 1's virtualFetchOptions, and its composed suite asserts envelopes through part 2's mapping, so part 3 does not compile on main without both. Parts 1 and 2 are independent of each other; the stack is linear only because each MR targets the one before it, which keeps every diff to its own files.

Order MR Part Contents LOC
1 !2325 (merged) 1/3 the read's remote.FetchOptions 114
2 !2326 (merged) 2/3 the error mapping and its taxonomy 1574
3 !2255 (merged) 3/3 the resolution facade and the composed suite 1762

3450 LOC across the three. 3442 of those were the whole of what !2255 (merged) carried before the split, and nothing was added or dropped in the cut: part 3's tree reproduced the reviewed head file for file. The other 8 are part 1's review round (11dd3cd2b), which parts 2 and 3 pick up when they rebase onto this branch's new head.

What this MR does

The request-global remote.FetchOptions one kind=virtual npm read runs its winner's Fetch under: the body-kind class and the size cap every position of one read shares, with the Transform deliberately left unset.

Part 1 of the three this step was split into. It is the smallest piece and has no dependency on the other two.

The two decisions in 25 lines

A tarball takes the blob class. npmremote.TarballFetchOptions leaves MaxBodyBytes unset, which remote.FetchOptions reads as the blob class's own configured cap. Carrying npm.max_remote_packument_size there instead would clamp a tarball to a metadata-class number. Every other kind is a metadata document, an undefined one included: the metadata class is the fail-small one, and Target.Validate rejects an undefined kind before a read reaches here.

No fill-time rewriter is set here, and that is the load-bearing half. virtual.Request.Fetch is request-global: virtual.Resolver.fetchWinner copies it and hands the one Transform to whichever position wins. A single request-global rewriter built on position 1's base would rewrite position 2's fill inside the shared flight, and position 2's npm_remote_metadata_files row would land carrying position 1's tarball URLs; a later standalone read of position 2's repository then serves that row verbatim, and clients pull position 1's tarball for position 2's package.

A single-remote fixture cannot see any of that, which is why TestVirtualFetchOptions_CarryNoTransform asserts the absence directly. The rewriter is built where the position's own identity is still in scope, in the per-position fetcher the upstream assembly (Step 11, merged) already owns.

Reachability

virtualFetchOptions is unexported and has no production caller until part 3 lands the facade that calls it. Its test is what exercises it here, which is also why unused does not fire.

Reviewable LOC

114 reviewable LOC (virtual_fetch_options.go 25, virtual_fetch_options_test.go 89), measured at 11dd3cd2b against origin/main at e0de46676. Under the 500 ceiling; no justification needed.

Spec coverage

Spec: docs/specs/S31-npm-virtual.md Plan: docs/plans/2026-08-21-s31-npm-virtual.md, Step 12

# Claim Tests
SEC-7, SEC-8 No new outbound surface; streaming bounds are the upstream's TestVirtualFetchOptions_SelectsTheSizeCapClassPerTarget
P-1, SEC-5 The re-point is per position, not request-global TestVirtualFetchOptions_CarryNoTransform; the fill-time half is part 3's TestVirtualResolution_FillRewritesOntoTheWinningPositionsOwnBase

e2e scenarios

No scenario in docs/testing/e2e/npm.md is added or affected. That catalog carries no virtual section, and no route reaches this code, so no end-to-end flow can exercise it. The virtual npm scenarios land with the serve surface (Steps 13, 15 to 17); the round trip is Step 19.

Conformance

No format-handler behaviour changes: internal/handlers/{oci,maven,npm}/ is untouched and no route is registered, so the npm conformance harness has nothing new to exercise.

Related to #893 (closed)

Edited by Dzmitry (Dima) Meshcharakou

Merge request reports

Loading
Loading