From 4ef43e2d8c526a8e16a53829db66123b22af7f93 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 1 Nov 2026 18:57:24 +0000 Subject: [PATCH 1/3] Start sweep of vex_consumed.rs and vex_sources.rs Assisted-by: Claude Code:claude-opus-5-5 From 97fee04483a516583f4a74afb1be5e07ab058fe1 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 1 Nov 2026 19:03:52 +0000 Subject: [PATCH 2/3] Sweep vex_consumed.rs and vex_sources.rs vex_consumed.rs: drop the not_installed() wrapper and the cfg(test) npm_identity_fallback forwarder (the reusing variant takes its name), drop banner and bare issue-ref comments, fix the stale and garbled test docs, and drop the write_pkg test wrapper. The two npm_alias_copies_* tests exercise ecosystem_dispatch's every-copy lookup, so they move there. vex_sources.rs: inline fetch_concurrency, recognized_dead_note and build_candidates, drop the note() forwarder for PlanNote literals, make the module-only NOTE_* consts private and group the unattested ones together, and drop banners and bare issue refs. The deleted fetch_concurrency test only re-checked core's api_concurrency. No behavior change. Assisted-by: Claude Code:claude-opus-5-5 --- .../src/commands/vex_consumed.rs | 217 +++---------- .../src/commands/vex_sources.rs | 284 +++++++----------- .../src/ecosystem_dispatch.rs | 125 ++++++++ 3 files changed, 277 insertions(+), 349 deletions(-) diff --git a/crates/socket-patch-cli/src/commands/vex_consumed.rs b/crates/socket-patch-cli/src/commands/vex_consumed.rs index 94af3f22a..6b84f9d5b 100644 --- a/crates/socket-patch-cli/src/commands/vex_consumed.rs +++ b/crates/socket-patch-cli/src/commands/vex_consumed.rs @@ -25,7 +25,7 @@ //! | maven | `///-socket./` (the version the pom or the hosted Gradle wiring pins) in `~/.m2` and every Gradle `files-2.1` holding it (hash dirs expanded), its artifact files matched under the suffixed name | the `` version dir | //! | npm | every `node_modules` copy the crawler finds (pnpm and vlt store copies included), every peer / modifier / registry variant of those in the same `.pnpm` / `.vlt` store, alias installs (`node_modules/` holding the package) in the root's and every workspace member's tree included | — each serves some dependent: ALL must verify | //! | pypi | every copy in the crawler's environment set (the project's venvs when it has any, else the interpreters) | — any may be the one that runs the project: ALL must verify | -//! | gem | every copy in bundler's gem path; under an explicit or deployment `path` the `gem env` homes hold only default gems bundler loads (#1098) | a non-default gem's `gem env` copy when bundler doesn't use system gems; otherwise bundler loads whichever `Gem.path` home it hits first: ALL must verify | +//! | gem | every copy in bundler's gem path; under an explicit or deployment `path` the `gem env` homes hold only default gems bundler loads | a non-default gem's `gem env` copy when bundler doesn't use system gems; otherwise bundler loads whichever `Gem.path` home it hits first: ALL must verify | //! //! composer and nuget have ONE install location shared by every source //! (`vendor/`, the global packages folder — which restore reuses whatever @@ -96,7 +96,7 @@ pub(crate) async fn hosted_consumed_copies( .flatten() .filter_map(|purl| Some((purl.clone(), installed.get(purl)?.clone()))) .collect(); - npm_identity_fallback_reusing(shared.get(&Ecosystem::Npm), &options, &mut all, prior).await; + npm_identity_fallback(shared.get(&Ecosystem::Npm), &options, &mut all, prior).await; let npm: Vec<&String> = shared.get(&Ecosystem::Npm).into_iter().flatten().collect(); for purl in shared.values().flatten() { let mut paths = all.remove(purl).unwrap_or_default(); @@ -115,7 +115,7 @@ pub(crate) async fn hosted_consumed_copies( }, ); } - // An orphaned Bun store entry is no consumed copy (#599); the + // An orphaned Bun store entry is no consumed copy; the // installed lookup dropped its own, this drops the ones the alias // and identity fallbacks' variant expansion added. socket_patch_core::crawlers::npm_crawler::retain_live_store_copies( @@ -142,12 +142,6 @@ pub(crate) async fn hosted_consumed_copies( out } -fn not_installed() -> HostedCopies { - HostedCopies::default() -} - -// ── npm identity fallback ──────────────────────────────────────────────── - /// Last-resort identity lookup for an npm purl the targeted resolver did /// not find. The resolver takes real alias dirs (`node_modules/lp` holding /// `left-pad@1.3.0`) as copies, but not an alias reached through a symlinked @@ -156,23 +150,13 @@ fn not_installed() -> HostedCopies { /// the lockfile basis excuses, so such a copy must still be hash-verified /// ("installed evidence wins"). Resolve every npm purl the targeted lookup /// missed by the installed `package.json` identity instead — the same -/// fallback `vendor` uses before declaring a package missing. -#[cfg(test)] +/// fallback `vendor` uses before declaring a package missing. Answers from +/// `prior`'s crawled packages when it was crawled with `options` (the whole +/// `NpmCrawler::crawl_all` output for them) instead of crawling again. async fn npm_identity_fallback( npm: Option<&Vec>, options: &CrawlerOptions, all: &mut HashMap>, -) { - npm_identity_fallback_reusing(npm, options, all, None).await -} - -/// [`npm_identity_fallback`], answering from `prior`'s crawled packages -/// when it was crawled with `options` (the whole `NpmCrawler::crawl_all` -/// output for them) instead of crawling again. -async fn npm_identity_fallback_reusing( - npm: Option<&Vec>, - options: &CrawlerOptions, - all: &mut HashMap>, prior: Option<&NpmCrawlSnapshot>, ) { let missing: Vec<&String> = npm @@ -186,8 +170,6 @@ async fn npm_identity_fallback_reusing( } } -// ── golang ─────────────────────────────────────────────────────────────── - /// Under `replace M v => patch.socket.dev/gopatch/ ` the build /// fetches and compiles the REPLACEMENT module; the module cache's `M@v` (if /// any) is pristine by construction and never read. The served module keeps @@ -199,12 +181,12 @@ async fn golang_copies( wiring: &HostedWiring, ) -> HostedCopies { let Some((_, module, version)) = purl_parts(purl) else { - return not_installed(); + return HostedCopies::default(); }; let Some((rhs_module, rhs_version)) = golang_replacement(options, &module, &version, wiring).await else { - return not_installed(); + return HostedCopies::default(); }; let target = format!("pkg:golang/{rhs_module}@{rhs_version}"); let crawler = GoCrawler::new(); @@ -224,7 +206,7 @@ async fn golang_copies( }; } } - not_installed() + HostedCopies::default() } /// The replacement `(module, version)` the hosted wiring routes `M@v` to: @@ -262,8 +244,6 @@ async fn golang_replacement( }) } -// ── cargo ──────────────────────────────────────────────────────────────── - /// Cargo extracts every registry's crates into its OWN /// `registry/src/-/` dir, and a locked package builds from the /// dir of the registry its `Cargo.lock` source names — so for a hosted pin @@ -278,7 +258,7 @@ async fn golang_replacement( /// the operator points at, and is taken as given. async fn cargo_copies(options: &CrawlerOptions, purl: &str, wiring: &HostedWiring) -> HostedCopies { let Some((_, name, version)) = purl_parts(purl) else { - return not_installed(); + return HostedCopies::default(); }; let base = format!("pkg:cargo/{name}@{version}"); let pinned = wiring.refs.iter().find_map(|r| match &r.locked_integrity { @@ -299,7 +279,7 @@ async fn cargo_copies(options: &CrawlerOptions, purl: &str, wiring: &HostedWirin }, }; let Some(host) = source.as_deref().and_then(registry_host) else { - return not_installed(); + return HostedCopies::default(); }; let crawler = CargoCrawler::new(); let mut copies: Vec<(PathBuf, PathBuf)> = Vec::new(); @@ -395,8 +375,6 @@ fn cached_crate(src: &Path, name: &str, version: &str) -> Option { ) } -// ── maven ──────────────────────────────────────────────────────────────── - /// The fail-closed hosted pom pins `-socket.`, a version only the Socket repository serves: maven resolves /// `~/.m2/…///`, never the `` dir (whose copy predates @@ -410,13 +388,13 @@ fn cached_crate(src: &Path, name: &str, version: &str) -> Option { /// `-…` — a member-keyed record checks the jar of that name. async fn maven_copies(options: &CrawlerOptions, purl: &str, wiring: &HostedWiring) -> HostedCopies { let Some((_, name, version)) = purl_parts(purl) else { - return not_installed(); + return HostedCopies::default(); }; let Some((group, artifact)) = name.split_once('/') else { - return not_installed(); + return HostedCopies::default(); }; let Some(hex8) = wiring.uuid.get(..8) else { - return not_installed(); + return HostedCopies::default(); }; let suffixed = format!("{version}-socket.{hex8}"); let target = format!("pkg:maven/{group}/{artifact}@{suffixed}"); @@ -441,7 +419,7 @@ async fn maven_copies(options: &CrawlerOptions, purl: &str, wiring: &HostedWirin } } if paths.is_empty() { - return not_installed(); + return HostedCopies::default(); } HostedCopies { paths, @@ -643,8 +621,8 @@ mod tests { let purl = "pkg:npm/left-pad@1.3.0".to_string(); let mut expected = vec![alias.clone(), peers[1].clone()]; expected.sort(); - // The resolver takes a root link only for a purl with no copy - // (#1494), and expands it to the same copies. + // The resolver takes a root link only for a purl with no copy, and + // expands it to the same copies. let mut resolved = installed_copies(&common, &purl) .await .remove(&purl) @@ -668,10 +646,6 @@ mod tests { .unwrap(); } - fn write_pkg(root: &Path, dir: &str, name: &str, version: &str) { - pkg(&root.join(dir), name, version); - } - fn local(root: &Path) -> CrawlerOptions { CrawlerOptions { cwd: root.to_path_buf(), @@ -680,36 +654,31 @@ mod tests { } } - /// An npm alias install (`node_modules/lp` holding left-pad@1.3.0) is a - /// consumed copy of the hosted purl, though the targeted lookup probes - /// only `node_modules/left-pad`. + /// The identity fallback resolves a missed purl by its installed + /// `package.json` identity (the alias `node_modules/lp` holding + /// left-pad@1.3.0), leaves an absent purl out, and keeps a copy the + /// targeted lookup already found. #[tokio::test] async fn npm_identity_fallback_fills_only_misses() { let tmp = tempfile::tempdir().unwrap(); - write_pkg(tmp.path(), "node_modules/lp", "left-pad", "1.3.0"); - write_pkg(tmp.path(), "node_modules/other", "other", "1.0.0"); - let options = CrawlerOptions { - cwd: tmp.path().to_path_buf(), - global: false, - global_prefix: None, - }; + pkg(&tmp.path().join("node_modules/lp"), "left-pad", "1.3.0"); + pkg(&tmp.path().join("node_modules/other"), "other", "1.0.0"); + let options = local(tmp.path()); let purls = vec![ "pkg:npm/left-pad@1.3.0".to_string(), "pkg:npm/absent@2.0.0".to_string(), ]; let mut all: HashMap> = HashMap::new(); - npm_identity_fallback(Some(&purls), &options, &mut all).await; + npm_identity_fallback(Some(&purls), &options, &mut all, None).await; assert_eq!( all.get("pkg:npm/left-pad@1.3.0"), Some(&vec![tmp.path().join("node_modules/lp")]) ); assert!(!all.contains_key("pkg:npm/absent@2.0.0"), "{all:?}"); - // A copy the targeted lookup already found is kept as-is (the alias - // fallback only fills misses). let found = tmp.path().join("node_modules/left-pad"); let mut all = HashMap::from([(purls[0].clone(), vec![found.clone()])]); - npm_identity_fallback(Some(&purls), &options, &mut all).await; + npm_identity_fallback(Some(&purls), &options, &mut all, None).await; assert_eq!(all[&purls[0]], vec![found.clone()]); } @@ -723,16 +692,16 @@ mod tests { async fn npm_identity_fallback_from_the_snapshot_matches_the_crawl() { let tmp = tempfile::tempdir().unwrap(); let root = tmp.path(); - write_pkg(root, "node_modules/aaa", "aaa", "1.0.0"); - write_pkg(root, "node_modules/left-pad", "left-pad", "1.2.0"); - write_pkg(root, "node_modules/zzz", "zzz", "1.0.0"); + pkg(&root.join("node_modules/aaa"), "aaa", "1.0.0"); + pkg(&root.join("node_modules/left-pad"), "left-pad", "1.2.0"); + pkg(&root.join("node_modules/zzz"), "zzz", "1.0.0"); // Two such packages, so an answer drawn from only part of the // snapshot (whatever its directory order) cannot match. for (store, link, name) in [ ("store/a", "node_modules/lp", "left-pad"), ("store/b", "node_modules/odd", "is-odd"), ] { - write_pkg(root, store, name, "1.3.0"); + pkg(&root.join(store), name, "1.3.0"); std::os::unix::fs::symlink(root.join(store), root.join(link)).unwrap(); } let options = local(root); @@ -751,7 +720,7 @@ mod tests { ) .await; // The crawler skips symlinks; the resolver takes the root link - // only because the purl has no other copy (#1494). + // only because the purl has no other copy. assert_eq!( resolved.get(&purls[0]), Some(&vec![root.join("node_modules/lp")]), @@ -767,9 +736,9 @@ mod tests { ); let mut walked: HashMap> = HashMap::new(); - npm_identity_fallback(Some(&purls), &options, &mut walked).await; + npm_identity_fallback(Some(&purls), &options, &mut walked, None).await; let mut reused: HashMap> = HashMap::new(); - npm_identity_fallback_reusing(Some(&purls), &options, &mut reused, Some(&snapshot)).await; + npm_identity_fallback(Some(&purls), &options, &mut reused, Some(&snapshot)).await; assert_eq!(reused, walked); for purl in &purls[..2] { assert_eq!( @@ -781,120 +750,11 @@ mod tests { assert!(!reused.contains_key("pkg:npm/absent@2.0.0"), "{reused:?}"); } - fn npm_scope(root: &Path) -> GlobalArgs { - GlobalArgs { - cwd: root.to_path_buf(), - ecosystems: Some(vec!["npm".to_string()]), - ..GlobalArgs::default() - } - } - fn sorted(mut paths: Vec) -> Vec { paths.sort(); paths } - /// The installed lookup `vex` judges hosted npm purls by takes alias - /// installs as copies beside the own-name one: scoped keys, nested trees - /// and scoped targets count; hidden dirs, other versions and symlinks do - /// not (#856: no second alias walk backs it up). - #[tokio::test] - async fn npm_alias_copies_finds_only_alias_installs() { - let tmp = tempfile::tempdir().unwrap(); - let root = tmp.path(); - let nm = root.join("node_modules"); - pkg(&nm.join("minimist"), "minimist", "1.2.2"); - pkg(&nm.join("mm"), "minimist", "1.2.2"); - pkg(&nm.join("@me/mm"), "minimist", "1.2.2"); - pkg(&nm.join("dep/node_modules/deep"), "minimist", "1.2.2"); - pkg(&nm.join("old"), "minimist", "1.2.8"); - pkg( - &nm.join(".pnpm/minimist@1.2.2/node_modules/x"), - "minimist", - "1.2.2", - ); - pkg(&nm.join("sc"), "@scope/pkg", "1.0.0"); - #[cfg(unix)] - std::os::unix::fs::symlink(nm.join("mm"), nm.join("linked")).unwrap(); - - let purls = vec![ - "pkg:npm/minimist@1.2.2".to_string(), - "pkg:npm/%40scope/pkg@1.0.0".to_string(), - ]; - let found = crate::ecosystem_dispatch::find_manifest_package_copies_reusing( - &purls, - &npm_scope(root), - true, - None, - ) - .await; - assert_eq!( - sorted(found["pkg:npm/minimist@1.2.2"].clone()), - sorted(vec![ - nm.join("@me/mm"), - nm.join("dep/node_modules/deep"), - nm.join("minimist"), - nm.join("mm"), - ]) - ); - assert_eq!(found["pkg:npm/%40scope/pkg@1.0.0"], vec![nm.join("sc")]); - } - - /// A workspace member's alias install is a consumed copy even when the - /// root holds the package under its own name: every importer tree the - /// crawler enumerates is searched, and `--global-prefix` searches the - /// prefix. - #[tokio::test] - async fn npm_alias_copies_walks_every_workspace_members_tree() { - let tmp = tempfile::tempdir().unwrap(); - let root = tmp.path(); - pkg(&root.join("node_modules/left-pad"), "left-pad", "1.3.0"); - pkg( - &root.join("packages/a/node_modules/lp"), - "left-pad", - "1.3.0", - ); - pkg( - &root.join("packages/a/node_modules/dep/node_modules/deep"), - "left-pad", - "1.3.0", - ); - pkg( - &root.join("apps/web/node_modules/@me/lp"), - "left-pad", - "1.3.0", - ); - pkg( - &root.join("apps/web/node_modules/left-pad"), - "left-pad", - "1.3.0", - ); - - let purl = "pkg:npm/left-pad@1.3.0"; - let got = installed_copies(&npm_scope(root), purl).await; - assert_eq!( - sorted(got[purl].clone()), - sorted(vec![ - root.join("apps/web/node_modules/@me/lp"), - root.join("apps/web/node_modules/left-pad"), - root.join("node_modules/left-pad"), - root.join("packages/a/node_modules/dep/node_modules/deep"), - root.join("packages/a/node_modules/lp"), - ]) - ); - - let prefix = root.join("packages/a/node_modules"); - let under_prefix = GlobalArgs { - global_prefix: Some(prefix.clone()), - ..npm_scope(root) - }; - let got = installed_copies(&under_prefix, purl).await; - assert_eq!( - sorted(got[purl].clone()), - vec![prefix.join("dep/node_modules/deep"), prefix.join("lp")] - ); - } - #[cfg(unix)] #[tokio::test] async fn store_variants_of_a_consumed_copy_are_consumed_too() { @@ -934,11 +794,10 @@ mod tests { assert!(with_store_peer_variant_copies(Vec::new()).await.is_empty()); } - /// vlt twin of the `.pnpm` case: every importer entry is a link into - /// the `.vlt` store (the alias `lp` too), so no importer dir is an alias - /// nothing, while the crawler resolves the alias's package from its - /// store copy, which is named after the real package. That store copy - /// is the consumed evidence. + /// vlt: every importer entry is a link into the `.vlt` store (the alias + /// `lp` too). The crawler resolves the alias's package from its store + /// copy, which is named after the real package, and the identity + /// fallback finds the alias link itself. #[cfg(unix)] #[tokio::test] async fn vlt_alias_is_consumed_through_its_store_copy() { @@ -975,7 +834,7 @@ mod tests { ); let mut all: HashMap> = HashMap::new(); - npm_identity_fallback(Some(&purls), &local(root), &mut all).await; + npm_identity_fallback(Some(&purls), &local(root), &mut all, None).await; assert_eq!(all.get(&purls[0]), Some(&vec![nm.join("lp")])); } diff --git a/crates/socket-patch-cli/src/commands/vex_sources.rs b/crates/socket-patch-cli/src/commands/vex_sources.rs index 3d476e868..52f02b99f 100644 --- a/crates/socket-patch-cli/src/commands/vex_sources.rs +++ b/crates/socket-patch-cli/src/commands/vex_sources.rs @@ -75,6 +75,7 @@ use socket_patch_core::api::client::{ build_proxy_fallback_client, get_api_client_with_overrides, hold_back_debug, is_fallback_candidate, ApiClient, }; +use socket_patch_core::ledgers::{Ledgers, Store}; use socket_patch_core::manifest::schema::{PatchManifest, PatchRecord}; use socket_patch_core::patch::redirect::RedirectState; use socket_patch_core::utils::concurrent::{api_concurrency, ordered_concurrent}; @@ -108,17 +109,11 @@ pub(crate) const REDIRECT_UNWIRED: &str = "redirect_unwired"; /// candidate for that package attests. pub(crate) const WIRING_CONFLICT: &str = "wiring_conflict"; -/// Ceiling on concurrent patch-view fetches (one GET per uuid; the view -/// carries blob content, so it is heavy). +/// Ceiling on concurrent patch-view fetches, on top of +/// `SOCKET_API_CONCURRENCY` (one GET per uuid; the view carries blob +/// content, so it is heavy). const FETCH_CONCURRENCY: usize = 10; -/// The in-flight cap for the record fetch: `SOCKET_API_CONCURRENCY` applies -/// here like every other patch-API window, with [`FETCH_CONCURRENCY`] as -/// this window's own ceiling on top (a view is heavy). -fn fetch_concurrency(use_public_proxy: bool) -> usize { - api_concurrency(use_public_proxy).min(FETCH_CONCURRENCY) -} - /// Everything `vex` reads, loaded once by the caller (which owns the /// corrupt-ledger hard errors). pub(crate) struct Sources { @@ -176,17 +171,17 @@ pub(crate) struct PlanNote { } /// A lockfile wires the package to different patches (`wiring_conflict`). -pub(crate) const NOTE_WIRING_CONFLICT: &str = "vex_wiring_conflict"; +const NOTE_WIRING_CONFLICT: &str = "vex_wiring_conflict"; /// A recorded patch is superseded by the lockfile-wired one. -pub(crate) const NOTE_RECORD_SUPERSEDED: &str = "vex_record_superseded"; +const NOTE_RECORD_SUPERSEDED: &str = "vex_record_superseded"; /// A ledger claim died although the lockfiles still mention the patch. -pub(crate) const NOTE_CLAIM_UNWIRED: &str = "vex_claim_unwired"; +const NOTE_CLAIM_UNWIRED: &str = "vex_claim_unwired"; /// `--offline` forbids fetching the records the plan needs. -pub(crate) const NOTE_RECORD_OFFLINE: &str = "vex_record_offline"; +const NOTE_RECORD_OFFLINE: &str = "vex_record_offline"; /// The patch API has no such patch. -pub(crate) const NOTE_RECORD_NOT_FOUND: &str = "vex_record_not_found"; +const NOTE_RECORD_NOT_FOUND: &str = "vex_record_not_found"; /// A record fetch failed (transport, server, paid-only, ...). -pub(crate) const NOTE_RECORD_FETCH_FAILED: &str = "vex_record_fetch_failed"; +const NOTE_RECORD_FETCH_FAILED: &str = "vex_record_fetch_failed"; /// The public proxy served free patches only: the authenticated API refused /// the credentials (`get` / `scan`'s fallback), or a token was set but its /// org could not be resolved, so the run's client is on the proxy. @@ -211,15 +206,27 @@ pub(crate) fn api_auth_fallback_warning( /// Omission tag and note: a hosted Gradle pin is wired, but a lock file /// records a release above its base, which that build resolves instead /// (`vex::Unattested`). -pub(crate) const NOTE_LOCK_ABOVE_BASE: &str = "vex_gradle_lock_above_base"; +const NOTE_LOCK_ABOVE_BASE: &str = "vex_gradle_lock_above_base"; /// Omission tag and note: an npm pin is wired, but its pnpm lock names a /// package that bundles a copy of it, which no wiring reaches /// (`vex::Unattested`, `UnattestedKind::BundledCopy`). -pub(crate) const NOTE_PNPM_BUNDLED_COPY: &str = "vex_pnpm_bundled_copy"; +const NOTE_PNPM_BUNDLED_COPY: &str = "vex_pnpm_bundled_copy"; /// Omission tag and note: an npm pin is wired, but `deno.lock` locks the /// same version, which `deno install` installs without the wiring /// (`vex::Unattested`, `UnattestedKind::DenoLock`). -pub(crate) const NOTE_DENO_LOCK_COPY: &str = "vex_deno_lock_copy"; +const NOTE_DENO_LOCK_COPY: &str = "vex_deno_lock_copy"; + +/// Omission tag and note: the patch is wired only in a root +/// `npm-shrinkwrap.json` with no `package-lock.json` twin, which npm >= 12 +/// never reads (`vex::Unattested`). +const NOTE_NPM_SHRINKWRAP_ONLY: &str = "vex_npm_shrinkwrap_only"; +/// Omission tag and note: a hosted Maven pin sits in a reactor root, where a +/// module's own `` may override it (`vex::Unattested`). +const NOTE_MAVEN_REACTOR_ROOT: &str = "vex_maven_reactor_root"; +/// Omission tag and note: a hosted Maven pin's GA also has an executable +/// classifier copy at another version, which the pin does not reach +/// (`vex::Unattested`). +const NOTE_MAVEN_CLASSIFIER_UNPATCHED: &str = "vex_maven_classifier_unpatched"; /// The omission tag of an [`Unattested`](socket_patch_core::vex::Unattested) /// ref, and the remedy its note ends with. @@ -259,22 +266,6 @@ fn unattested_note(kind: UnattestedKind) -> (&'static str, &'static str) { } } -/// Omission tag and note: the patch is wired only in a root -/// `npm-shrinkwrap.json` with no `package-lock.json` twin, which npm >= 12 -/// never reads (`vex::Unattested`, #899). -pub(crate) const NOTE_NPM_SHRINKWRAP_ONLY: &str = "vex_npm_shrinkwrap_only"; -/// Omission tag and note: a hosted Maven pin sits in a reactor root, where a -/// module's own `` may override it (`vex::Unattested`, #261). -pub(crate) const NOTE_MAVEN_REACTOR_ROOT: &str = "vex_maven_reactor_root"; -/// Omission tag and note: a hosted Maven pin's GA also has an executable -/// classifier copy at another version, which the pin does not reach -/// (`vex::Unattested`). -pub(crate) const NOTE_MAVEN_CLASSIFIER_UNPATCHED: &str = "vex_maven_classifier_unpatched"; - -fn note(code: &'static str, detail: String) -> PlanNote { - PlanNote { code, detail } -} - /// One hosted-basis view purl's wiring (see [`Plan::hosted`]). #[derive(Debug, Clone, PartialEq, Eq)] pub(crate) struct HostedWiring { @@ -356,7 +347,7 @@ pub(crate) async fn plan( assume_live: &[String], api_client: &RunApiClient, ) -> Plan { - // The ledgers' project (#745): liveness and JVM wiring are judged where + // The ledgers' project: liveness and JVM wiring are judged where // the ledger and the discovery that gates it were read. let root_buf = common.project_root(); let root = root_buf.as_path(); @@ -377,7 +368,29 @@ pub(crate) async fn plan( let mut notes = Vec::new(); let mut gated: Vec = Vec::new(); - let mut cands = build_candidates(&manifest, &vendor, redirect.as_ref()); + // One candidate per owner key under the shared owner rule: the manifest + // keys, then the unclaimed vendor-ledger keys, then the redirect-ledger + // keys neither owns, each sorted, with the losing copies' records as + // `alts`. + let mut cands: Vec = Ledgers { + manifest: Some(&manifest), + vendor: Some(&vendor), + redirect: redirect.as_ref(), + } + .owned() + .into_iter() + .map(|o| Cand { + key: o.key.to_string(), + uuid: o.uuid.to_string(), + record: o.record.cloned(), + alts: o.alts.into_iter().cloned().collect(), + manifest_owned: o.store == Store::Manifest, + redirected: o.hosted, + vendor_entry: o.vendor.map(|(_, e)| e.clone()), + discovered: Vec::new(), + lockfile_only: false, + }) + .collect(); let conflicts = wiring_conflicts(&discovery); if !conflicts.is_empty() { // Gate every candidate for a conflicting package BEFORE anything can @@ -408,14 +421,14 @@ pub(crate) async fn plan( for key in keys { gated.push(failed(&key, WIRING_CONFLICT)); } - notes.push(note( - NOTE_WIRING_CONFLICT, - format!( + notes.push(PlanNote { + code: NOTE_WIRING_CONFLICT, + detail: format!( "{pkg}: the lockfiles wire it to different patches ({wiring}); which one the \ build installs cannot be told from the files, so none is attested — make \ the lockfiles agree (re-run `socket-patch scan` or delete the stale lock)" ), - )); + }); } } let superseded = attach_discovered(&mut cands, &discovery, &vendor, &conflicts); @@ -436,26 +449,25 @@ pub(crate) async fn plan( }; let (code, remedy) = unattested_note(u.kind); gated.push(failed(&c.key, code)); - notes.push(note( + notes.push(PlanNote { code, - format!( + detail: format!( "{}: patch {} is wired, but {}; {remedy}", c.key, c.uuid, u.detail ), - )); + }); false }); for (key, old_uuid, wired) in &superseded { - notes.push(note( - NOTE_RECORD_SUPERSEDED, - format!( + notes.push(PlanNote { + code: NOTE_RECORD_SUPERSEDED, + detail: format!( "{key}: the recorded patch {old_uuid} is superseded by the lockfile-wired patch \ {wired}; attesting only what the lockfile wires" ), - )); + }); } - // ── bases + liveness ──────────────────────────────────────────────── let mut liveness = LedgerLiveness::new(root, &discovery, redirect.as_ref()); let mut based: Vec<(Cand, Basis)> = Vec::new(); for mut cand in cands { @@ -469,10 +481,10 @@ pub(crate) async fn plan( { const CODE: &str = "vendor_jvm_shape_unsupported"; gated.push(failed(&cand.key, CODE)); - notes.push(note( - CODE, - format!("{}: cannot establish JVM wiring: {detail}", cand.key), - )); + notes.push(PlanNote { + code: CODE, + detail: format!("{}: cannot establish JVM wiring: {detail}", cand.key), + }); continue; } } @@ -550,7 +562,6 @@ pub(crate) async fn plan( based.push((cand, basis)); } - // ── record resolution ─────────────────────────────────────────────── let mut need_api: Vec = Vec::new(); for (i, (cand, _)) in based.iter_mut().enumerate() { if cand.record.is_some() { @@ -596,7 +607,6 @@ pub(crate) async fn plan( } } - // ── assemble ──────────────────────────────────────────────────────── let mut plan = Plan { view: PatchManifest { patches: HashMap::new(), @@ -688,37 +698,6 @@ fn expected_package(cand: &Cand) -> String { } } -/// One candidate per owner key under the shared owner rule -/// ([`socket_patch_core::ledgers::Ledgers::owned`]): the manifest keys, then -/// the unclaimed vendor-ledger keys, then the redirect-ledger keys neither -/// owns, each sorted, with the losing copies' records as `alts`. -fn build_candidates( - manifest: &PatchManifest, - vendor: &VendorState, - redirect: Option<&RedirectState>, -) -> Vec { - use socket_patch_core::ledgers::{Ledgers, Store}; - Ledgers { - manifest: Some(manifest), - vendor: Some(vendor), - redirect, - } - .owned() - .into_iter() - .map(|o| Cand { - key: o.key.to_string(), - uuid: o.uuid.to_string(), - record: o.record.cloned(), - alts: o.alts.into_iter().cloned().collect(), - manifest_owned: o.store == Store::Manifest, - redirected: o.hosted, - vendor_entry: o.vendor.map(|(_, e)| e.clone()), - discovered: Vec::new(), - lockfile_only: false, - }) - .collect() -} - /// Packages the discovered references wire to MORE than one patch uuid /// (any mix of modes and files), mapped to a human description of the /// disagreeing wiring (` (), …`) for the note. @@ -892,52 +871,41 @@ fn vendored_entry_for(cand: &Cand, vref: &PatchedRef) -> VendorEntry { } } -/// [`recognized_dead_note`] for each dead `(uuid, mode, ledger)` claim of -/// one candidate. +/// One `vex_claim_unwired` note per dead `(uuid, mode, ledger)` claim of one +/// candidate that discovery's authority killed although the lockfiles still +/// MENTION the patch (rule 11): which files, and the extractor's own reason +/// when it gave one (a diagnostic from one of those files naming the uuid). +/// A claim whose uuid discovery does not recognize died on the ledger's own +/// evidence; its skip reason says all there is, so it gets no note. fn dead_claim_notes( discovery: &Discovery, key: &str, claims: &[(&str, WiringMode, &str)], ) -> Vec { - claims - .iter() - .filter_map(|(uuid, mode, ledger)| { - recognized_dead_note(discovery, key, uuid, *mode, ledger) - }) - .map(|detail| note(NOTE_CLAIM_UNWIRED, detail)) - .collect() -} - -/// The human note for a ledger claim discovery's authority killed although -/// the lockfiles still MENTION the patch (rule 11): which files, and the -/// extractor's own reason when it gave one (a diagnostic from one of those -/// files naming the uuid). `None` when discovery does not recognize the -/// uuid — the claim then died on the ledger's own evidence, and the skip -/// reason says all there is. -fn recognized_dead_note( - discovery: &Discovery, - key: &str, - uuid: &str, - mode: WiringMode, - ledger: &str, -) -> Option { - let files = discovery.recognized_files(uuid, mode); - if files.is_empty() { - return None; + let mut notes = Vec::new(); + for &(uuid, mode, ledger) in claims { + let files = discovery.recognized_files(uuid, mode); + if files.is_empty() { + continue; + } + let reason = discovery + .diagnostics + .iter() + .find(|d| files.contains(&d.file.as_path()) && d.detail.contains(uuid)) + .map(|d| format!(" ({}: {})", d.code, d.detail)) + .unwrap_or_default(); + let names: Vec = files.iter().map(|f| f.display().to_string()).collect(); + notes.push(PlanNote { + code: NOTE_CLAIM_UNWIRED, + detail: format!( + "{key}: the {ledger} records patch {uuid}, and {} still mention{} it, but not as \ + wiring the package manager consumes for this package{reason}; not attested", + names.join(", "), + if names.len() == 1 { "s" } else { "" }, + ), + }); } - let reason = discovery - .diagnostics - .iter() - .find(|d| files.contains(&d.file.as_path()) && d.detail.contains(uuid)) - .map(|d| format!(" ({}: {})", d.code, d.detail)) - .unwrap_or_default(); - let names: Vec = files.iter().map(|f| f.display().to_string()).collect(); - Some(format!( - "{key}: the {ledger} records patch {uuid}, and {} still mention{} it, but not as \ - wiring the package manager consumes for this package{reason}; not attested", - names.join(", "), - if names.len() == 1 { "s" } else { "" }, - )) + notes } /// A local record for `uuid` (manifest, then redirect ledger, then vendor @@ -989,15 +957,15 @@ async fn fetch_records( } if common.offline { let n = uuids.len(); - notes.push(note( - NOTE_RECORD_OFFLINE, - format!( + notes.push(PlanNote { + code: NOTE_RECORD_OFFLINE, + detail: format!( "{} no local record (manifest or .socket/vendor ledgers), and --offline forbids \ fetching {} from the patch API", plural(n, "lockfile-wired patch has", "lockfile-wired patches have"), if n == 1 { "it" } else { "them" }, ), - )); + }); return out; } let overrides = common.api_client_overrides(); @@ -1013,7 +981,10 @@ async fn fetch_records( // reports this itself). if built_here && common.json { if let Some(reason) = client.org_unresolved() { - notes.push(note(NOTE_API_AUTH_FALLBACK, reason.to_string())); + notes.push(PlanNote { + code: NOTE_API_AUTH_FALLBACK, + detail: reason.to_string(), + }); } } let mut use_public_proxy = client.uses_public_proxy(); @@ -1034,7 +1005,7 @@ async fn fetch_records( let client = &client; let mut views = std::pin::pin!(ordered_concurrent( pending.iter(), - fetch_concurrency(use_public_proxy), + api_concurrency(use_public_proxy).min(FETCH_CONCURRENCY), |uuid| async move { (uuid, hold_back_debug(client.fetch_patch(uuid)).await) }, )); loop { @@ -1058,18 +1029,18 @@ async fn fetch_records( socket_patch_core::manifest::records::record_from_patch_response(&view), ); } - Ok(None) => notes.push(note( - NOTE_RECORD_NOT_FOUND, - format!("Patch {uuid} was not found by the patch API"), - )), + Ok(None) => notes.push(PlanNote { + code: NOTE_RECORD_NOT_FOUND, + detail: format!("Patch {uuid} was not found by the patch API"), + }), Err(e) if !use_public_proxy && is_fallback_candidate(&e) => { auth_error.get_or_insert_with(|| e.to_string()); auth_refused.push(uuid) } - Err(e) => notes.push(note( - NOTE_RECORD_FETCH_FAILED, - format!("Could not fetch patch {uuid}: {e}"), - )), + Err(e) => notes.push(PlanNote { + code: NOTE_RECORD_FETCH_FAILED, + detail: format!("Could not fetch patch {uuid}: {e}"), + }), } } } @@ -1078,14 +1049,14 @@ async fn fetch_records( } // Same stale-credential recovery (and warning text) as `get` / // `scan`: free patches are still served by the public proxy. - notes.push(note( - NOTE_API_AUTH_FALLBACK, - format!( + notes.push(PlanNote { + code: NOTE_API_AUTH_FALLBACK, + detail: format!( "authenticated API returned {}; falling back to public patch API proxy (free \ patches only).", auth_error.unwrap_or_default() ), - )); + }); client = build_proxy_fallback_client(&overrides); use_public_proxy = true; // The refused views are fetched again: not done yet. @@ -1154,33 +1125,6 @@ mod tests { } } - /// `SOCKET_API_CONCURRENCY=1` means one view at a time here as well. - /// Serial: `SOCKET_*` is process-global. - #[test] - #[serial_test::serial] - fn socket_api_concurrency_paces_the_record_fetch() { - use socket_patch_core::utils::concurrent::API_CONCURRENCY_ENV; - let orig = std::env::var(API_CONCURRENCY_ENV).ok(); - std::env::remove_var(API_CONCURRENCY_ENV); - // A view is heavy, so this window's own ceiling binds by default - // on the authenticated API (whose cap is above it). - assert_eq!(fetch_concurrency(false), FETCH_CONCURRENCY); - assert_eq!(fetch_concurrency(true), 4); - - std::env::set_var(API_CONCURRENCY_ENV, "1"); - assert_eq!(fetch_concurrency(false), 1); - assert_eq!(fetch_concurrency(true), 1); - - // Turned up past this window's own ceiling, the ceiling holds. - std::env::set_var(API_CONCURRENCY_ENV, "32"); - assert_eq!(fetch_concurrency(false), FETCH_CONCURRENCY); - - match orig { - Some(v) => std::env::set_var(API_CONCURRENCY_ENV, v), - None => std::env::remove_var(API_CONCURRENCY_ENV), - } - } - /// The record fetch folds in `pending` order, not in the order the /// server happens to answer: the FIRST refusal in `pending` is the one /// reported in the fallback note, even when it answers last, and the diff --git a/crates/socket-patch-cli/src/ecosystem_dispatch.rs b/crates/socket-patch-cli/src/ecosystem_dispatch.rs index d2761444c..0bba87308 100644 --- a/crates/socket-patch-cli/src/ecosystem_dispatch.rs +++ b/crates/socket-patch-cli/src/ecosystem_dispatch.rs @@ -2260,6 +2260,131 @@ mod tests { assert_eq!(single.get(&purl), Some(&scoped_copy)); } + fn npm_scope(root: &std::path::Path) -> crate::args::GlobalArgs { + crate::args::GlobalArgs { + cwd: root.to_path_buf(), + ecosystems: Some(vec!["npm".to_string()]), + ..crate::args::GlobalArgs::default() + } + } + + fn sorted(mut paths: Vec) -> Vec { + paths.sort(); + paths + } + + fn write_package_json(dir: &std::path::Path, name: &str, version: &str) { + std::fs::create_dir_all(dir).unwrap(); + std::fs::write( + dir.join("package.json"), + format!(r#"{{"name":"{name}","version":"{version}"}}"#), + ) + .unwrap(); + } + + async fn manifest_copies( + common: &crate::args::GlobalArgs, + purl: &str, + ) -> HashMap> { + find_manifest_package_copies_reusing(&[purl.to_string()], common, true, None).await + } + + /// The every-copy lookup `vex` judges hosted npm purls by takes alias + /// installs as copies beside the own-name one: scoped keys, nested trees + /// and scoped targets count; hidden dirs, other versions and symlinks do + /// not. + #[tokio::test] + async fn npm_alias_copies_finds_only_alias_installs() { + let tmp = tempfile::tempdir().unwrap(); + let root = tmp.path(); + let nm = root.join("node_modules"); + write_package_json(&nm.join("minimist"), "minimist", "1.2.2"); + write_package_json(&nm.join("mm"), "minimist", "1.2.2"); + write_package_json(&nm.join("@me/mm"), "minimist", "1.2.2"); + write_package_json(&nm.join("dep/node_modules/deep"), "minimist", "1.2.2"); + write_package_json(&nm.join("old"), "minimist", "1.2.8"); + write_package_json( + &nm.join(".pnpm/minimist@1.2.2/node_modules/x"), + "minimist", + "1.2.2", + ); + write_package_json(&nm.join("sc"), "@scope/pkg", "1.0.0"); + #[cfg(unix)] + std::os::unix::fs::symlink(nm.join("mm"), nm.join("linked")).unwrap(); + + let purls = vec![ + "pkg:npm/minimist@1.2.2".to_string(), + "pkg:npm/%40scope/pkg@1.0.0".to_string(), + ]; + let found = + find_manifest_package_copies_reusing(&purls, &npm_scope(root), true, None).await; + assert_eq!( + sorted(found["pkg:npm/minimist@1.2.2"].clone()), + sorted(vec![ + nm.join("@me/mm"), + nm.join("dep/node_modules/deep"), + nm.join("minimist"), + nm.join("mm"), + ]) + ); + assert_eq!(found["pkg:npm/%40scope/pkg@1.0.0"], vec![nm.join("sc")]); + } + + /// A workspace member's alias install is a consumed copy even when the + /// root holds the package under its own name: every importer tree the + /// crawler enumerates is searched, and `--global-prefix` searches the + /// prefix. + #[tokio::test] + async fn npm_alias_copies_walks_every_workspace_members_tree() { + let tmp = tempfile::tempdir().unwrap(); + let root = tmp.path(); + write_package_json(&root.join("node_modules/left-pad"), "left-pad", "1.3.0"); + write_package_json( + &root.join("packages/a/node_modules/lp"), + "left-pad", + "1.3.0", + ); + write_package_json( + &root.join("packages/a/node_modules/dep/node_modules/deep"), + "left-pad", + "1.3.0", + ); + write_package_json( + &root.join("apps/web/node_modules/@me/lp"), + "left-pad", + "1.3.0", + ); + write_package_json( + &root.join("apps/web/node_modules/left-pad"), + "left-pad", + "1.3.0", + ); + + let purl = "pkg:npm/left-pad@1.3.0"; + let got = manifest_copies(&npm_scope(root), purl).await; + assert_eq!( + sorted(got[purl].clone()), + sorted(vec![ + root.join("apps/web/node_modules/@me/lp"), + root.join("apps/web/node_modules/left-pad"), + root.join("node_modules/left-pad"), + root.join("packages/a/node_modules/dep/node_modules/deep"), + root.join("packages/a/node_modules/lp"), + ]) + ); + + let prefix = root.join("packages/a/node_modules"); + let under_prefix = crate::args::GlobalArgs { + global_prefix: Some(prefix.clone()), + ..npm_scope(root) + }; + let got = manifest_copies(&under_prefix, purl).await; + assert_eq!( + sorted(got[purl].clone()), + vec![prefix.join("dep/node_modules/deep"), prefix.join("lp")] + ); + } + #[tokio::test] async fn find_packages_for_purls_skips_version_mismatch() { // The crawler only matches an installed dir whose version equals the From 14ba988283c739b8fc6e0823d4f77f846aaa9b94 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 1 Nov 2026 19:11:24 +0000 Subject: [PATCH 3/3] Keep two named helpers; tidy moved tests /code-review: fetch_concurrency's test is the only check of this window's own FETCH_CONCURRENCY ceiling, and build_candidates names a step plan() is better off without inlined, so both stay. The moved ecosystem_dispatch tests are renamed after the lookup they test, call it directly, keep the "no second alias walk" reason, and share the package.json writer with write_npm_package. Assisted-by: Claude Code:claude-opus-5-5 --- .../src/commands/vex_sources.rs | 97 +++++++++++++------ .../src/ecosystem_dispatch.rs | 27 ++---- 2 files changed, 79 insertions(+), 45 deletions(-) diff --git a/crates/socket-patch-cli/src/commands/vex_sources.rs b/crates/socket-patch-cli/src/commands/vex_sources.rs index 52f02b99f..25e49273e 100644 --- a/crates/socket-patch-cli/src/commands/vex_sources.rs +++ b/crates/socket-patch-cli/src/commands/vex_sources.rs @@ -75,7 +75,6 @@ use socket_patch_core::api::client::{ build_proxy_fallback_client, get_api_client_with_overrides, hold_back_debug, is_fallback_candidate, ApiClient, }; -use socket_patch_core::ledgers::{Ledgers, Store}; use socket_patch_core::manifest::schema::{PatchManifest, PatchRecord}; use socket_patch_core::patch::redirect::RedirectState; use socket_patch_core::utils::concurrent::{api_concurrency, ordered_concurrent}; @@ -109,11 +108,17 @@ pub(crate) const REDIRECT_UNWIRED: &str = "redirect_unwired"; /// candidate for that package attests. pub(crate) const WIRING_CONFLICT: &str = "wiring_conflict"; -/// Ceiling on concurrent patch-view fetches, on top of -/// `SOCKET_API_CONCURRENCY` (one GET per uuid; the view carries blob -/// content, so it is heavy). +/// Ceiling on concurrent patch-view fetches (one GET per uuid; the view +/// carries blob content, so it is heavy). const FETCH_CONCURRENCY: usize = 10; +/// The in-flight cap for the record fetch: `SOCKET_API_CONCURRENCY` applies +/// here like every other patch-API window, with [`FETCH_CONCURRENCY`] as +/// this window's own ceiling on top (a view is heavy). +fn fetch_concurrency(use_public_proxy: bool) -> usize { + api_concurrency(use_public_proxy).min(FETCH_CONCURRENCY) +} + /// Everything `vex` reads, loaded once by the caller (which owns the /// corrupt-ledger hard errors). pub(crate) struct Sources { @@ -368,29 +373,7 @@ pub(crate) async fn plan( let mut notes = Vec::new(); let mut gated: Vec = Vec::new(); - // One candidate per owner key under the shared owner rule: the manifest - // keys, then the unclaimed vendor-ledger keys, then the redirect-ledger - // keys neither owns, each sorted, with the losing copies' records as - // `alts`. - let mut cands: Vec = Ledgers { - manifest: Some(&manifest), - vendor: Some(&vendor), - redirect: redirect.as_ref(), - } - .owned() - .into_iter() - .map(|o| Cand { - key: o.key.to_string(), - uuid: o.uuid.to_string(), - record: o.record.cloned(), - alts: o.alts.into_iter().cloned().collect(), - manifest_owned: o.store == Store::Manifest, - redirected: o.hosted, - vendor_entry: o.vendor.map(|(_, e)| e.clone()), - discovered: Vec::new(), - lockfile_only: false, - }) - .collect(); + let mut cands = build_candidates(&manifest, &vendor, redirect.as_ref()); let conflicts = wiring_conflicts(&discovery); if !conflicts.is_empty() { // Gate every candidate for a conflicting package BEFORE anything can @@ -698,6 +681,37 @@ fn expected_package(cand: &Cand) -> String { } } +/// One candidate per owner key under the shared owner rule +/// ([`socket_patch_core::ledgers::Ledgers::owned`]): the manifest keys, then +/// the unclaimed vendor-ledger keys, then the redirect-ledger keys neither +/// owns, each sorted, with the losing copies' records as `alts`. +fn build_candidates( + manifest: &PatchManifest, + vendor: &VendorState, + redirect: Option<&RedirectState>, +) -> Vec { + use socket_patch_core::ledgers::{Ledgers, Store}; + Ledgers { + manifest: Some(manifest), + vendor: Some(vendor), + redirect, + } + .owned() + .into_iter() + .map(|o| Cand { + key: o.key.to_string(), + uuid: o.uuid.to_string(), + record: o.record.cloned(), + alts: o.alts.into_iter().cloned().collect(), + manifest_owned: o.store == Store::Manifest, + redirected: o.hosted, + vendor_entry: o.vendor.map(|(_, e)| e.clone()), + discovered: Vec::new(), + lockfile_only: false, + }) + .collect() +} + /// Packages the discovered references wire to MORE than one patch uuid /// (any mix of modes and files), mapped to a human description of the /// disagreeing wiring (` (), …`) for the note. @@ -1005,7 +1019,7 @@ async fn fetch_records( let client = &client; let mut views = std::pin::pin!(ordered_concurrent( pending.iter(), - api_concurrency(use_public_proxy).min(FETCH_CONCURRENCY), + fetch_concurrency(use_public_proxy), |uuid| async move { (uuid, hold_back_debug(client.fetch_patch(uuid)).await) }, )); loop { @@ -1125,6 +1139,33 @@ mod tests { } } + /// `SOCKET_API_CONCURRENCY=1` means one view at a time here as well. + /// Serial: `SOCKET_*` is process-global. + #[test] + #[serial_test::serial] + fn socket_api_concurrency_paces_the_record_fetch() { + use socket_patch_core::utils::concurrent::API_CONCURRENCY_ENV; + let orig = std::env::var(API_CONCURRENCY_ENV).ok(); + std::env::remove_var(API_CONCURRENCY_ENV); + // A view is heavy, so this window's own ceiling binds by default + // on the authenticated API (whose cap is above it). + assert_eq!(fetch_concurrency(false), FETCH_CONCURRENCY); + assert_eq!(fetch_concurrency(true), 4); + + std::env::set_var(API_CONCURRENCY_ENV, "1"); + assert_eq!(fetch_concurrency(false), 1); + assert_eq!(fetch_concurrency(true), 1); + + // Turned up past this window's own ceiling, the ceiling holds. + std::env::set_var(API_CONCURRENCY_ENV, "32"); + assert_eq!(fetch_concurrency(false), FETCH_CONCURRENCY); + + match orig { + Some(v) => std::env::set_var(API_CONCURRENCY_ENV, v), + None => std::env::remove_var(API_CONCURRENCY_ENV), + } + } + /// The record fetch folds in `pending` order, not in the order the /// server happens to answer: the FIRST refusal in `pending` is the one /// reported in the fallback note, even when it answers last, and the diff --git a/crates/socket-patch-cli/src/ecosystem_dispatch.rs b/crates/socket-patch-cli/src/ecosystem_dispatch.rs index 0bba87308..8c0ec1b2a 100644 --- a/crates/socket-patch-cli/src/ecosystem_dispatch.rs +++ b/crates/socket-patch-cli/src/ecosystem_dispatch.rs @@ -2036,16 +2036,12 @@ mod tests { // temp `node_modules`, so a regression in the macro plumbing (wrong // crawler/path method, dropped result, swapped merge) is caught. - use std::io::Write as _; - /// Lay down `node_modules//package.json` under `root` with the /// given version, returning the package directory the crawler should /// resolve the PURL to. fn write_npm_package(root: &std::path::Path, name: &str, version: &str) -> PathBuf { let pkg_dir = root.join("node_modules").join(name); - std::fs::create_dir_all(&pkg_dir).unwrap(); - let mut f = std::fs::File::create(pkg_dir.join("package.json")).unwrap(); - write!(f, r#"{{"name":"{name}","version":"{version}"}}"#).unwrap(); + write_package_json(&pkg_dir, name, version); pkg_dir } @@ -2282,19 +2278,12 @@ mod tests { .unwrap(); } - async fn manifest_copies( - common: &crate::args::GlobalArgs, - purl: &str, - ) -> HashMap> { - find_manifest_package_copies_reusing(&[purl.to_string()], common, true, None).await - } - /// The every-copy lookup `vex` judges hosted npm purls by takes alias /// installs as copies beside the own-name one: scoped keys, nested trees /// and scoped targets count; hidden dirs, other versions and symlinks do - /// not. + /// not. No second alias walk backs this lookup up. #[tokio::test] - async fn npm_alias_copies_finds_only_alias_installs() { + async fn manifest_copies_take_npm_alias_installs_only() { let tmp = tempfile::tempdir().unwrap(); let root = tmp.path(); let nm = root.join("node_modules"); @@ -2335,7 +2324,7 @@ mod tests { /// crawler enumerates is searched, and `--global-prefix` searches the /// prefix. #[tokio::test] - async fn npm_alias_copies_walks_every_workspace_members_tree() { + async fn manifest_copies_walk_every_npm_workspace_members_tree() { let tmp = tempfile::tempdir().unwrap(); let root = tmp.path(); write_package_json(&root.join("node_modules/left-pad"), "left-pad", "1.3.0"); @@ -2361,7 +2350,9 @@ mod tests { ); let purl = "pkg:npm/left-pad@1.3.0"; - let got = manifest_copies(&npm_scope(root), purl).await; + let got = + find_manifest_package_copies_reusing(&[purl.to_string()], &npm_scope(root), true, None) + .await; assert_eq!( sorted(got[purl].clone()), sorted(vec![ @@ -2378,7 +2369,9 @@ mod tests { global_prefix: Some(prefix.clone()), ..npm_scope(root) }; - let got = manifest_copies(&under_prefix, purl).await; + let got = + find_manifest_package_copies_reusing(&[purl.to_string()], &under_prefix, true, None) + .await; assert_eq!( sorted(got[purl].clone()), vec![prefix.join("dep/node_modules/deep"), prefix.join("lp")]