Repository navigation
feat(npm): resolve versions over HTTP, groundwork for embedding aube - #11147
Conversation
…view Version metadata queries (ls-remote, latest resolution) now hit the npm registry directly over HTTP via a built-in packument client, so node/npm no longer need to be installed to resolve npm: tool versions. User-level .npmrc / NPM_CONFIG_* registry, scoped-registry, and auth settings are honored; the config/packument logic is ported from jdx/aube's aube-registry crate, trimmed to read-only metadata fetches. The new npm.use_npm_view setting restores the old `npm view` code path for setups relying on npm-only config (cafile, client certs, token helpers). Fixes stale/failed npm: version resolution when npm is missing (#10563) and removes the npm dependency edge from version queries. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
📝 WalkthroughWalkthroughnpm version metadata resolution now defaults to direct registry HTTP requests, with npm CLI lookup retained behind Changesnpm version resolution
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant User
participant NPMBackend
participant NPM_REGISTRY_CONFIG
participant npmRegistry
User->>NPMBackend: Request npm versions
NPMBackend->>NPMBackend: Check use_npm_view
NPMBackend->>NPM_REGISTRY_CONFIG: Fetch packument
NPM_REGISTRY_CONFIG->>npmRegistry: HTTP request
npmRegistry-->>NPM_REGISTRY_CONFIG: Packument response
NPM_REGISTRY_CONFIG-->>NPMBackend: Version metadata
NPMBackend-->>User: Available versions
Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 3 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit a1b8b90. Configure here.
| } | ||
| if suffix.eq_ignore_ascii_case("registry") { | ||
| out.push(("registry".to_string(), value.clone())); | ||
| } |
There was a problem hiding this comment.
Bare npm auth env ignored
Medium Severity
The built-in registry client’s npm_config_env_entries helper maps registry URLs and //host/:_authToken-style keys from the environment, but not bare _authToken or _auth suffixes (for example npm_config__authToken / NPM_CONFIG__AUTH_TOKEN). Those values still work with npm view, so private-registry auth that relies on a global token env var can succeed in legacy mode and fail on the default HTTP path.
Reviewed by Cursor Bugbot for commit a1b8b90. Configure here.
There was a problem hiding this comment.
Fixed in 05c1ed5. npm_config_env_entries now translates bare _authToken/_auth env vars (both npm_config__authToken and NPM_CONFIG__AUTH_TOKEN spellings) to the default-registry auth, matching how apply already handled them from .npmrc. Added test_env_bare_auth_token + test_env_bare_auth_overrides_npmrc.
This reply was generated by an AI coding assistant.
| pub fn registry_for(&self, name: &str) -> &str { | ||
| package_scope(name) | ||
| .and_then(|scope| self.scoped_registries.get(scope)) | ||
| .unwrap_or(&self.registry) |
There was a problem hiding this comment.
Scoped registry case mismatch
Low Severity
Scoped registry overrides are stored under lowercased scope keys, but registry_for looks up the scope taken verbatim from the package name. A tool like npm:@MyOrg/pkg with @myorg:registry in config resolves to the public default registry instead of the scoped one, unlike npm’s case-insensitive scope matching.
Reviewed by Cursor Bugbot for commit a1b8b90. Configure here.
There was a problem hiding this comment.
Fixed in 05c1ed5. registry_for now lowercases the package scope before the lookup (keys are stored lowercased), so npm:@MyOrg/pkg resolves the @myorg:registry override. Added test_scoped_registry_lookup_is_case_insensitive.
This reply was generated by an AI coding assistant.
| Ok(Arc::new(packument)) | ||
| }) | ||
| .await | ||
| } |
There was a problem hiding this comment.
Packument never refreshes
Medium Severity
The npm backend caches the packument in a OnceCell for the lifetime of the backend instance. When the remote version cache expires or is refreshed, _list_remote_versions runs again but still reuses that first packument instead of hitting the registry, so version lists and dist-tag resolution can stay stale until a new process starts.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit a1b8b90. Configure here.
There was a problem hiding this comment.
Fixed in 05c1ed5. Removed the process-lifetime packument OnceCell. Both _list_remote_versions and latest_stable_version are already wrapped in their own TTL disk caches by the backend layer, so the memo was a redundant third layer that could shadow those TTLs with stale data — each now fetches directly and the disk caches handle dedup across invocations.
This reply was generated by an AI coding assistant.
Greptile SummaryThis PR replaces the
Confidence Score: 4/5The change is safe to merge. The new HTTP path is well-tested and the legacy The implementation is thorough and the unit tests are comprehensive. The two gaps — bare
Important Files Changed
|
There was a problem hiding this comment.
🧹 Nitpick comments (1)
src/backend/npm.rs (1)
562-571: 🚀 Performance & Scalability | 🔵 TrivialNote the bandwidth cost of full packuments for large packages.
The default path fetches the full packument (rather than the abbreviated
vnd.npm.install-v1+jsoncorgi) becausetimeis required — a deliberate and correct choice. For very large packages the full document can be tens of MB, versus the tinynpm view <pkg> versions timeprojection the legacy path returned. This is bounded byfetch_remote_versions_cacheand the per-processOnceCell, so it's fine, but you may want to keep an eye on cold-fetch latency/bandwidth forls-remoteon heavyweight packages, or consider skipping thetimefetch when no release-age filtering is configured.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/backend/npm.rs` around lines 562 - 571, Review the fetch_packument path and monitor cold-fetch latency and bandwidth for large packages, while preserving the full packument request because release-age filtering requires time data. If the surrounding configuration exposes whether release-age filtering is enabled, avoid fetching time/full packuments when it is disabled; otherwise retain the current behavior and caching through fetch_remote_versions_cache and the per-process OnceCell.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@src/backend/npm.rs`:
- Around line 562-571: Review the fetch_packument path and monitor cold-fetch
latency and bandwidth for large packages, while preserving the full packument
request because release-age filtering requires time data. If the surrounding
configuration exposes whether release-age filtering is enabled, avoid fetching
time/full packuments when it is disabled; otherwise retain the current behavior
and caching through fetch_remote_versions_cache and the per-process OnceCell.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Central YAML (base), Organization UI (inherited)
Review profile: CHILL
Plan: Pro Plus
Run ID: 4dcb58f2-9ef7-471d-9301-0f63cec12f0f
📒 Files selected for processing (8)
docs/dev-tools/backends/npm.mde2e/backend/test_backend_missing_depse2e/backend/test_npm_shim_recursion_system_dirschema/mise.jsonsettings.tomlsrc/backend/mod.rssrc/backend/npm.rssrc/backend/npm_registry.rs
Hyperfine Performance
|
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
mise-2026.7.11 x -- echo |
18.6 ± 0.8 | 16.9 | 25.1 | 1.00 |
mise x -- echo |
20.0 ± 1.2 | 18.2 | 34.5 | 1.07 ± 0.08 |
mise env
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
mise-2026.7.11 env |
17.5 ± 0.9 | 15.9 | 22.8 | 1.00 |
mise env |
18.8 ± 0.9 | 17.1 | 23.6 | 1.07 ± 0.08 |
mise hook-env
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
mise-2026.7.11 hook-env |
18.3 ± 0.9 | 16.8 | 22.7 | 1.00 |
mise hook-env |
19.6 ± 0.9 | 17.8 | 23.9 | 1.07 ± 0.07 |
mise ls
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
mise-2026.7.11 ls |
15.6 ± 0.8 | 14.0 | 19.0 | 1.00 |
mise ls |
16.9 ± 0.9 | 15.3 | 20.9 | 1.09 ± 0.08 |
xtasks/test/perf
| Command | mise-2026.7.11 | mise | Variance |
|---|---|---|---|
| install (cached) | 154ms | 163ms | -5% |
| ls (cached) | 57ms | 59ms | -3% |
| bin-paths (cached) | 60ms | 62ms | -3% |
| task-ls (cached) | 123ms | 127ms | -3% |


Summary
Version metadata queries for the
npm:backend (mise ls-remote npm:*, resolvinglatest) now hit the npm registry directly over HTTP through a built-in packument client, instead of shelling out tonpm view. This means node/npm no longer need to be installed to resolvenpm:tool versions — only to actually install packages.User-level
~/.npmrc(orNPM_CONFIG_USERCONFIG) andNPM_CONFIG_*environment variables are honored for the default registry, scoped registries (@scope:registry), and auth (_authToken/_auth/username+_password), matching whatnpm viewresolved before. The full (non-corgi) packument route is used so thetimemap is available forminimum_release_age/ release-date filtering.Direction: embedding aube
This is step one of a larger plan to embed aube's library crates in mise — replacing not just
npm viewbut eventually theaube/npm installsubprocess for the npm backend.The npmrc/packument logic here is ported inline from aube's
aube-registrycrate because the published crate currently can't be a dependency: it declares MSRV 1.93 (mise is pinned to 1.91 by distro packaging, and cargo's MSRV-aware resolver silently falls back to an ancient1.0.0-beta.2), and its workspace features would leak global build side effects into mise via feature unification (reqwest/hickory-dnsflips the default DNS resolver for every client in the binary,flate2/zlib-ngforces a cmake/C build onto cross targets,reqwest/rustlsadds a second TLS stack).aubepkg/aube#1068 fixes all of that upstream: MSRV 1.91 + backend-agnostic TLS/DNS/zlib features selected at the leaves. Once that lands and releases, the follow-up here is to replace this inline module with a real
aube-registry = { version = "...", default-features = false, features = ["native-tls"] }dependency, and then evaluate embedding resolver/store/linker for installs.Escape hatch
A new
npm.use_npm_viewsetting (env:MISE_NPM_USE_NPM_VIEW) restores the previousnpm viewcode path for environments that rely on npm-only configuration the built-in client doesn't model:cafile, client certificates, or an auth token helper. Proxies work either way via the standardHTTP(S)_PROXYenv vars.Why
npm:version resolution when npm isn't installed (#10563)npmdependency edge from version queries inget_dependencies(npm is now only pulled in when it may be the installer, or whenuse_npm_viewis set)npm:lockfile operations and, with the aube embedding, node-free installsBehavior parity
Verified the HTTP path produces byte-identical
ls-remoteoutput (same versions, same semver-ascending order) vs. thenpm viewpath fornpm:prettier,npm:@angular/cli(582 versions, incl. backported patch lines), andnpm:left-pad. Registry versions are strictly semver, so the packument's publish-order document is sorted semver-ascending to match whatnpm view versionsreturned (prefix resolution likenpm:@angular/cli@19depends on it).Tests
src/backend/npm_registry.rscovering npmrc parsing (comments, line continuation,${VAR}expansion, quote stripping), scoped registries, env-var precedence/translation, nerf-dart auth lookup (default-port collapse, prefix fallback, base64 username/password, bare tokens), and packument version ordering + missing-field tolerance.test_backend_missing_deps: now asserts version listing works with npm absent; legacy warning path re-checked underMISE_NPM_USE_NPM_VIEW=1.test_npm_shim_recursion_system_dir: pinned toMISE_NPM_USE_NPM_VIEW=1since it specifically guards thenpm viewshim-recursion path.test_npm*suite passing.🤖 Generated with Claude Code