use std::path::PathBuf;
use std::sync::LazyLock as Lazy;
use aube_registry::NetworkMode;
use aube_registry::client::RegistryClient;
use aube_registry::config::NpmConfig;
use eyre::Result;
use crate::backend::VersionInfo;
use crate::backend::npm::is_semver_prerelease;
use crate::config::Settings;
static CLIENT: Lazy<RegistryClient> = Lazy::new(|| {
crate::backend::aube_host::init();
let config = NpmConfig::load(&meta_dir());
let settings = Settings::get();
let mode = if settings.offline() {
NetworkMode::Offline
} else if settings.prefer_offline() {
NetworkMode::PreferOffline
} else {
NetworkMode::Online
};
let _ = crate::file::create_dir_all(meta_dir());
RegistryClient::from_config(config).with_network_mode(mode)
});
fn meta_dir() -> PathBuf {
crate::dirs::CACHE.join("npm-meta")
}
async fn fetch_packument(name: &str) -> Result<aube_registry::Packument> {
Ok(CLIENT
.fetch_packument_with_time_cached(name, &meta_dir())
.await?)
}
pub async fn list_versions(name: &str) -> Result<Vec<VersionInfo>> {
let packument = fetch_packument(name).await?;
Ok(sort_versions(packument.versions.keys())
.into_iter()
.map(|version| VersionInfo {
version: version.clone(),
created_at: packument.time.get(version).cloned(),
prerelease: is_semver_prerelease(version),
..Default::default()
})
.collect())
}
fn sort_versions<'a>(versions: impl Iterator<Item = &'a String>) -> Vec<&'a String> {
let mut versions = versions
.map(|version| (version, semver::Version::parse(version).ok()))
.collect::<Vec<_>>();
versions.sort_by(|(_, a), (_, b)| match (a, b) {
(Some(a), Some(b)) => a.cmp_precedence(b),
(Some(_), None) => std::cmp::Ordering::Less,
(None, Some(_)) => std::cmp::Ordering::Greater,
(None, None) => std::cmp::Ordering::Equal,
});
versions.into_iter().map(|(version, _)| version).collect()
}
pub async fn latest_dist_tag(name: &str) -> Result<Option<String>> {
let packument = fetch_packument(name).await?;
Ok(packument.dist_tags.get("latest").cloned())
}
#[cfg(test)]
mod tests {
use super::sort_versions;
#[test]
fn sort_versions_supports_npm_safe_integer_components() {
let versions = [
"0.52.0",
"0.0.8999999999999999",
"0.1.0",
"0.0.8888888888",
"0.0.899999999",
"0.0.9007199254740991",
]
.map(String::from);
let sorted = sort_versions(versions.iter())
.into_iter()
.map(String::as_str)
.collect::<Vec<_>>();
assert_eq!(
sorted,
[
"0.0.899999999",
"0.0.8888888888",
"0.0.8999999999999999",
"0.0.9007199254740991",
"0.1.0",
"0.52.0",
]
);
}
}