use std::rc::Rc; use std::sync::Arc; use avril_core::{Op, Runtime, RuntimeOptions, Snapshot}; use super::{Settings, WorkerScope, extensions}; use crate::authority::SourceAuthority; use crate::caps::{Capabilities, Initial}; use crate::modules::ModuleGraph; use crate::registry::{Options, PackageStore}; use avril_ext_process::SignalMode; use avril_ext_stdio::Stdio; use avril_host::Host; use avril_host::environment::Environment; #[derive(Debug, thiserror::Error)] #[error("Live operation during snapshot generation: {0}")] struct LiveOp(&'static str); /// Generate main and worker contexts in one artifact, without ambient guest authority. /// Must be the first V8 use in a dedicated generator process; live runtimes are /// disabled in that process so the deterministic generation seed cannot reach guests. pub fn create() -> Result> { avril_core::initialize_snapshot_generator()?; let directory = tempfile::tempdir()?; let root = directory.path(); let host = Host::new()?; let caps = Capabilities::new(Initial::new(false), root.to_owned()); let store = Arc::new(PackageStore::new_with_authority( host.handle(), Options { jsr_registry: "https://jsr.invalid/".parse()?, npm_registry: "https://npm.invalid/".parse()?, cache: root.join("cache"), lock: root.join("avril.lock"), project_root: root.to_owned(), offline: true, frozen: false, update: false, ephemeral_lock: true, vendor: None, }, SourceAuthority::execute(caps.clone()), )?); let settings = Settings { snapshot: None, storage: avril_webstorage::Persistent::new( root.join("storage"), &root.join("avril.conf"), "snapshot", ), caps, environment: Environment::default(), args: Vec::new(), store: store.clone(), runtime: host.handle(), broker: Arc::default(), process_signal: SignalMode::Run, }; let graph = Rc::new(ModuleGraph::open_shared( SourceAuthority::execute(settings.caps.clone()), store, )); let mut layouts = Vec::new(); for worker in [false, true] { let scope = if worker { Some(WorkerScope { control: Arc::default(), inbox: settings.broker.pair()?.1, url: "https://worker.invalid/snapshot.js".into(), name: String::new(), }) } else { None }; let mut extensions = extensions( graph.clone(), settings.clone(), Stdio::new()?, scope, None, None, )?; for extension in &mut extensions { for op in &mut extension.ops { // Only these operations depend solely on target and codec tables. if !matches!(op.name, "op_path_platform" | "op_encoding_label") { let name = op.name; *op = Op::try_raw(name, move |_, _| Err(LiveOp(name))); } } } layouts.push(RuntimeOptions { extensions, module_loader: None }); } Ok(Runtime::create_snapshot(layouts)?) }