use std::path::{Path, PathBuf};
pub(crate) const OPTIMIZE_ENV_JS: &str = include_str!("assets/optimize-env.mjs");
const STAMP_FILE: &str = "optimize-env-stamp.json";
const REPORT_FILE: &str = "optimize-env-report.json";
fn stamp_key() -> String {
format!(
"{}:{}",
env!("CARGO_PKG_VERSION"),
blake3::hash(OPTIMIZE_ENV_JS.as_bytes()).to_hex()
)
}
const LOCKFILE_CANDIDATES: &[&str] = &[
"node_modules/.pnpm/lock.yaml",
"node_modules/.package-lock.json",
"node_modules/.yarn-state.yml",
"node_modules/.yarn-integrity",
"package-lock.json",
"pnpm-lock.yaml",
"yarn.lock",
"bun.lock",
"bun.lockb",
"deno.lock",
".pnp.cjs",
".pnp.js",
".rush/temp/shrinkwrap-deps.json",
];
fn lockfile_stamp(root: &Path) -> String {
let mut dir = Some(root);
while let Some(d) = dir {
let mut hasher = blake3::Hasher::new();
let mut found = false;
for name in LOCKFILE_CANDIDATES {
let p = d.join(name);
if let Ok(bytes) = std::fs::read(&p) {
found = true;
hasher.update(name.as_bytes());
hasher.update(&[0]);
hasher.update(&bytes);
hasher.update(&[0]);
}
}
if found {
return hasher.finalize().to_hex().to_string();
}
dir = d.parent();
}
"none".to_string()
}
fn file_digest(path: &Path) -> Option<String> {
std::fs::read(path)
.ok()
.map(|b| blake3::hash(&b).to_hex().to_string())
}
#[derive(Debug)]
pub(crate) struct SeededEnv {
name: String,
metadata_path: PathBuf,
}
fn stamp_path(root: &Path) -> PathBuf {
oj_cache::cache_root(root).join(STAMP_FILE)
}
pub(crate) fn stamp_is_warm(root: &Path) -> bool {
let Ok(raw) = std::fs::read_to_string(stamp_path(root)) else {
return false;
};
let Ok(stamp) = serde_json::from_str::<serde_json::Value>(&raw) else {
return false;
};
if stamp.get("key").and_then(|v| v.as_str()) != Some(stamp_key().as_str()) {
return false;
}
if stamp.get("lockstamp").and_then(|v| v.as_str()) != Some(lockfile_stamp(root).as_str()) {
return false;
}
let Some(envs) = stamp.get("envs").and_then(|v| v.as_array()) else {
return false;
};
envs.iter().all(|e| {
let path = e.get("metadataPath").and_then(|v| v.as_str());
let hash = e.get("hash").and_then(|v| v.as_str());
match (path, hash) {
(Some(p), Some(h)) => file_digest(Path::new(p)).as_deref() == Some(h),
_ => false,
}
})
}
pub(crate) fn write_stamp(root: &Path, seeded: &[SeededEnv]) {
let mut envs = Vec::new();
for s in seeded {
let Some(hash) = file_digest(&s.metadata_path) else {
return;
};
envs.push(serde_json::json!({
"name": s.name,
"metadataPath": s.metadata_path.to_string_lossy(),
"hash": hash,
}));
}
let stamp = serde_json::json!({
"key": stamp_key(),
"lockstamp": lockfile_stamp(root),
"envs": envs,
});
let dir = oj_cache::cache_root(root);
let _ = std::fs::create_dir_all(&dir);
let tmp = dir.join(format!("{STAMP_FILE}.{}.tmp", std::process::id()));
if std::fs::write(&tmp, stamp.to_string()).is_ok() {
let _ = std::fs::rename(&tmp, stamp_path(root));
}
}
fn parse_report(raw: &str) -> Option<Vec<SeededEnv>> {
let v: serde_json::Value = serde_json::from_str(raw).ok()?;
if v.get("failed").and_then(|f| f.as_bool()) == Some(true) {
return None;
}
let seeded = v.get("seeded")?.as_array()?;
let mut out = Vec::new();
for s in seeded {
out.push(SeededEnv {
name: s.get("name")?.as_str()?.to_string(),
metadata_path: PathBuf::from(s.get("metadataPath")?.as_str()?),
});
}
Some(out)
}
fn preseed_timeout() -> std::time::Duration {
let secs = std::env::var("OJ_PRESEED_TIMEOUT")
.ok()
.and_then(|v| v.trim().parse::<u64>().ok())
.filter(|s| *s > 0)
.unwrap_or(300);
std::time::Duration::from_secs(secs)
}
async fn run_child(root: &Path, env_mode: &str) -> anyhow::Result<Vec<SeededEnv>> {
let cache = oj_cache::cache_root(root);
std::fs::create_dir_all(&cache)?;
let script = cache.join("optimize-env.mjs");
if std::fs::read(&script).ok().as_deref() != Some(OPTIMIZE_ENV_JS.as_bytes()) {
let tmp = cache.join(format!("optimize-env-{}.tmp.mjs", std::process::id()));
std::fs::write(&tmp, OPTIMIZE_ENV_JS)?;
std::fs::rename(&tmp, &script)?;
}
let report = cache.join(REPORT_FILE);
let _ = std::fs::remove_file(&report);
let exe = std::env::current_exe()?;
let mut child = tokio::process::Command::new(exe)
.arg("start-script")
.arg(&script)
.arg("--root")
.arg(root)
.current_dir(root)
.stdin(std::process::Stdio::piped())
.kill_on_drop(true)
.spawn()?;
{
use tokio::io::AsyncWriteExt;
let env: Vec<(String, String)> = vec![
("OJ_APP_ROOT".into(), root.to_string_lossy().into_owned()),
("OJ_ENV_MODE".into(), env_mode.to_string()),
("OJ_PRESEED_REPORT".into(), report.to_string_lossy().into_owned()),
];
let mut stdin = child.stdin.take().expect("piped stdin");
stdin.write_all(serde_json::to_string(&env)?.as_bytes()).await?;
}
let status = match tokio::time::timeout(preseed_timeout(), child.wait()).await {
Ok(status) => status?,
Err(_) => {
let _ = child.kill().await;
anyhow::bail!(
"did not finish within {}s (raise OJ_PRESEED_TIMEOUT for slower machines)",
preseed_timeout().as_secs()
);
}
};
if !status.success() {
anyhow::bail!("child exited with {status}");
}
let raw = std::fs::read_to_string(&report)
.map_err(|e| anyhow::anyhow!("child wrote no report: {e}"))?;
parse_report(&raw).ok_or_else(|| anyhow::anyhow!("child reported an incomplete seed"))
}
pub(crate) async fn preseed_server_deps(root: &Path, env_mode: &str) {
if std::env::var("OJ_NO_DEPS_PRESEED").is_ok_and(|v| !v.is_empty() && v != "0") {
return;
}
if !crate::plugins::extraction_ran_fresh() && stamp_is_warm(root) {
crate::boot_phase("deps preseed skipped (warm)");
return;
}
crate::boot_phase("deps preseed begin");
println!(" deps: pre-optimizing server environments in a one-shot child");
match run_child(root, env_mode).await {
Ok(seeded) => {
write_stamp(root, &seeded);
crate::boot_phase("deps preseed done");
}
Err(e) => {
eprintln!(
"oj: deps pre-optimization incomplete ({e}); the plugin host optimizes in-process this session"
);
crate::boot_phase("deps preseed failed");
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn stamp_warm_requires_key_lockstamp_and_metadata() {
let dir = tempfile::tempdir().unwrap();
let root = dir.path();
std::fs::write(root.join("package-lock.json"), b"{\"v\":1}").unwrap();
let deps = root.join("node_modules/.vite/deps_ssr");
std::fs::create_dir_all(&deps).unwrap();
let meta = deps.join("_metadata.json");
std::fs::write(&meta, b"{\"hash\":\"abc\"}").unwrap();
assert!(!stamp_is_warm(root));
write_stamp(
root,
&[SeededEnv { name: "ssr".into(), metadata_path: meta.clone() }],
);
assert!(stamp_is_warm(root));
std::fs::write(root.join("package-lock.json"), b"{\"v\":2}").unwrap();
assert!(!stamp_is_warm(root));
std::fs::write(root.join("package-lock.json"), b"{\"v\":1}").unwrap();
assert!(stamp_is_warm(root));
std::fs::write(&meta, b"{\"hash\":\"other\"}").unwrap();
assert!(!stamp_is_warm(root));
std::fs::remove_file(&meta).unwrap();
assert!(!stamp_is_warm(root));
}
#[test]
fn empty_seed_stamps_warm_until_lockfile_moves() {
let dir = tempfile::tempdir().unwrap();
let root = dir.path();
std::fs::write(root.join("yarn.lock"), b"a").unwrap();
write_stamp(root, &[]);
assert!(stamp_is_warm(root));
std::fs::write(root.join("yarn.lock"), b"b").unwrap();
assert!(!stamp_is_warm(root));
}
#[test]
fn stamp_key_mismatch_is_cold() {
let dir = tempfile::tempdir().unwrap();
let root = dir.path();
write_stamp(root, &[]);
let p = stamp_path(root);
let mut v: serde_json::Value =
serde_json::from_str(&std::fs::read_to_string(&p).unwrap()).unwrap();
v["key"] = serde_json::json!("other-version:hash");
std::fs::write(&p, v.to_string()).unwrap();
assert!(!stamp_is_warm(root));
}
#[test]
fn lockfile_stamp_walks_up_and_tracks_content() {
let dir = tempfile::tempdir().unwrap();
let ws = dir.path();
let app = ws.join("apps/web");
std::fs::create_dir_all(&app).unwrap();
std::fs::write(ws.join("pnpm-lock.yaml"), b"one").unwrap();
let a = lockfile_stamp(&app);
assert_ne!(a, "none");
std::fs::write(ws.join("pnpm-lock.yaml"), b"two").unwrap();
let b = lockfile_stamp(&app);
assert_ne!(a, b);
let bare = tempfile::tempdir().unwrap();
assert_eq!(lockfile_stamp(bare.path()), "none");
}
#[test]
fn report_parsing_rejects_failed_or_malformed() {
assert!(parse_report("{\"seeded\":[],\"failed\":true}").is_none());
assert!(parse_report("not json").is_none());
assert!(parse_report("{\"seeded\":[{\"name\":\"ssr\"}],\"failed\":false}").is_none());
let ok = parse_report(
"{\"seeded\":[{\"name\":\"ssr\",\"metadataPath\":\"/x/_metadata.json\"}],\"failed\":false}",
)
.unwrap();
assert_eq!(ok.len(), 1);
assert_eq!(ok[0].name, "ssr");
}
}