You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Came up while reviewing #11622. resolving/npm-resolver/src/pickPackage.ts currently has three near-identical cache-hit paths — in-memory metaCache, preferOffline/pickLowestVersion disk-cache, and the 304-Not-Modified branch — each of which calls maybeUpgradeAbbreviatedMetaForReleaseAge, then persistUpgradedMeta (with a dryRun guard), then ctx.metaCache.set, then pickMatchingVersionFinal. Plus a fourth abbreviated→full upgrade lives inline in the post-fetch (200) path with its own slightly different logic (saves abbreviated to disk first, doesn't forward etag/modified).
Two fast-path catch blocks (version-spec cache and mtime-gated cache) also do the same dance: try pickMatchingVersionFast, swallow ERR_PNPM_MISSING_TIME under strict, fall through.
Proposal
Restructure pickPackage as a tier'd resolver chain. Each tier is a function that returns { meta, pickedPackage } | null:
tryFromInMemoryCache
→ tryFromVersionSpecDiskCache
→ tryFromMtimeGatedDiskCache
→ fetchAndMaybeUpgrade (handles 304 + post-200 upgrade in one place)
All four tiers share a single "ensure meta has `time` if needed (upgrade, persist, set cache)" helper, replacing the current maybeUpgradeAbbreviatedMetaForReleaseAge + persistUpgradedMeta + boilerplate trio at every site.
Wins
Removes 3× duplication of the upgrade-and-persist block.
The function reads top-down as a sequence of fallback attempts.
Risks / non-goals
Touches pre-PR code paths — the version-spec and mtime-gated fast paths exist as micro-optimizations to skip the network when possible. The chain refactor must preserve their short-circuit behavior, including the MISSING_TIME-swallowing fall-through.
The post-fetch upgrade currently saves the abbreviated meta to disk before fetching full (so a failed upgrade still leaves something cached). That behavior should be preserved or explicitly traded off.
The dual-cache layout (ABBREVIATED_META_DIR vs FULL_META_DIR) is unchanged by this refactor; the abbreviated dir continues to hold upgraded-to-full content after an upgrade. Worth a comment in the unified helper.
Background
Came up while reviewing #11622.
resolving/npm-resolver/src/pickPackage.tscurrently has three near-identical cache-hit paths — in-memory metaCache, preferOffline/pickLowestVersion disk-cache, and the 304-Not-Modified branch — each of which callsmaybeUpgradeAbbreviatedMetaForReleaseAge, thenpersistUpgradedMeta(with adryRunguard), thenctx.metaCache.set, thenpickMatchingVersionFinal. Plus a fourth abbreviated→full upgrade lives inline in the post-fetch (200) path with its own slightly different logic (saves abbreviated to disk first, doesn't forward etag/modified).Two fast-path catch blocks (version-spec cache and mtime-gated cache) also do the same dance: try
pickMatchingVersionFast, swallowERR_PNPM_MISSING_TIMEunder strict, fall through.Proposal
Restructure
pickPackageas a tier'd resolver chain. Each tier is a function that returns{ meta, pickedPackage } | null:All four tiers share a single "ensure meta has `time` if needed (upgrade, persist, set cache)" helper, replacing the current
maybeUpgradeAbbreviatedMetaForReleaseAge+persistUpgradedMeta+ boilerplate trio at every site.Wins
shouldRethrowFromFastPathCache) lives once, naturally.Risks / non-goals
ABBREVIATED_META_DIRvsFULL_META_DIR) is unchanged by this refactor; the abbreviated dir continues to hold upgraded-to-full content after an upgrade. Worth a comment in the unified helper.References
minimumReleaseAgeStrict: truethe default, which is what surfaced the duplication-driven bugs.Written by an agent (Claude Code, claude-opus-4-7).