use std::sync::Arc;
const REGISTRY_LIKE_PREFIXES: &[&str] = &["npm:"];
const NON_REGISTRY_SPEC_MARKERS: &[&str] = &[
"workspace:",
"file:",
"link:",
"github:",
"git+",
"git:",
"http://",
"https://",
"catalog:",
];
#[inline]
pub fn is_disabled() -> bool {
std::env::var_os("AUBE_DISABLE_PREFETCH").is_some()
}
pub fn spawn_direct_dep_prefetch(
manifest: &aube_manifest::PackageJson,
cwd: &std::path::Path,
network_mode: aube_registry::NetworkMode,
) {
if is_disabled() {
return;
}
if matches!(network_mode, aube_registry::NetworkMode::Offline) {
return;
}
if has_lockfile(cwd) {
return;
}
let names = collect_registry_dep_names(manifest);
if names.is_empty() {
return;
}
let client = Arc::new(super::super::make_client(cwd).with_network_mode(network_mode));
let cache_dir = super::super::packument_cache_dir();
let count = names.len();
tracing::debug!("prefetch: spawning {count} direct-dep packument GETs");
for name in names {
let client = client.clone();
let cache_dir = cache_dir.clone();
tokio::spawn(async move {
if let Err(e) = client.fetch_packument_cached(&name, &cache_dir).await {
tracing::debug!(name = %name, error = %e, "prefetch fetch failed");
}
});
}
}
fn has_lockfile(cwd: &std::path::Path) -> bool {
const LOCKFILES: &[&str] = &[
"aube-lock.yaml",
"pnpm-lock.yaml",
"bun.lock",
"bun.lockb",
"yarn.lock",
"package-lock.json",
"npm-shrinkwrap.json",
];
LOCKFILES.iter().any(|name| cwd.join(name).exists())
}
fn collect_registry_dep_names(manifest: &aube_manifest::PackageJson) -> Vec<String> {
let mut names: Vec<String> = Vec::new();
let sections = [
&manifest.dependencies,
&manifest.dev_dependencies,
&manifest.optional_dependencies,
&manifest.peer_dependencies,
];
for section in sections {
for (name, spec) in section.iter() {
if !is_registry_spec(spec) {
continue;
}
names.push(name.clone());
}
}
names.sort();
names.dedup();
names
}
fn is_registry_spec(spec: &str) -> bool {
let trimmed = spec.trim();
if trimmed.is_empty() {
return false;
}
for marker in NON_REGISTRY_SPEC_MARKERS {
if trimmed.starts_with(marker) {
return false;
}
}
for prefix in REGISTRY_LIKE_PREFIXES {
if trimmed.starts_with(prefix) {
return false;
}
}
true
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn registry_specs_pass() {
assert!(is_registry_spec("^1.2.3"));
assert!(is_registry_spec("1.2.3"));
assert!(is_registry_spec("~1.0"));
assert!(is_registry_spec("*"));
assert!(is_registry_spec("latest"));
assert!(is_registry_spec(">=1.0 <2.0"));
}
#[test]
fn non_registry_specs_filtered() {
assert!(!is_registry_spec("workspace:*"));
assert!(!is_registry_spec("workspace:^1.0"));
assert!(!is_registry_spec("file:./local"));
assert!(!is_registry_spec("link:../sibling"));
assert!(!is_registry_spec("github:user/repo"));
assert!(!is_registry_spec("git+https://example.com/r.git"));
assert!(!is_registry_spec("https://example.com/pkg.tgz"));
assert!(!is_registry_spec("catalog:default"));
assert!(!is_registry_spec("npm:foo@1.0.0"));
assert!(!is_registry_spec(""));
}
}