interop/build/web/
wasm.rs1use crate::util::RunningProcess;
18use crate::TEST_SERVER_PORT;
19use color_eyre::eyre::Result;
20use std::net::SocketAddr;
21
22async fn find_wasm_file(deploy_path: &std::path::Path) -> Result<std::path::PathBuf> {
23 let wasm_base_path = deploy_path.to_path_buf();
24 let wasm_path = if wasm_base_path.exists() {
25 let mut wasm_dir = tokio::fs::read_dir(wasm_base_path.clone()).await?;
26 let mut maybe_wasm_path = None;
27 while let Some(entry) = wasm_dir.next_entry().await? {
28 log::debug!("wasm dir entry: {entry:?}");
29 if let Some(ext) = entry.path().extension() {
30 if ext == std::ffi::OsStr::new("wasm") {
31 log::debug!("found");
32 maybe_wasm_path = Some(entry);
33 }
34 }
35 }
36 maybe_wasm_path
37 .map(|entry| entry.path())
38 .unwrap_or_else(|| wasm_base_path.join(".not-found"))
39 } else {
40 wasm_base_path.join(".not-found")
41 };
42
43 Ok(wasm_path)
44}
45
46pub(crate) async fn build_wasm() -> Result<()> {
47 use sha2::{Digest, Sha256};
48 use tokio::process::Command;
49
50 let cwd = std::env::current_dir()?;
51
52 if cfg!(feature = "proteus") {
53 let spinner = RunningProcess::new("Building Cryptobox ESM bundle...", false);
54
55 Command::new("bun")
56 .args(["install"])
57 .current_dir(cwd.join("interop/src/build/web/cryptobox-esm"))
58 .stdout(std::process::Stdio::null())
59 .stderr(std::process::Stdio::null())
60 .status()
61 .await?;
62
63 Command::new("bun")
64 .args(["run", "build"])
65 .current_dir(cwd.join("interop/src/build/web/cryptobox-esm"))
66 .stdout(std::process::Stdio::null())
67 .stderr(std::process::Stdio::null())
68 .status()
69 .await?;
70
71 spinner.success("Cryptobox ESM bundle [OK]");
72 }
73
74 let mut spinner = RunningProcess::new("Building WASM bundle...", false);
75
76 let wasm_deploy_path = cwd.join("platforms/web");
77
78 let exe_path = std::env::current_exe()?;
79 let exe_folder = exe_path.parent().unwrap();
80 let wasm_cache_path = exe_folder.join(".wasm.cache");
81 let js_cache_path = exe_folder.join(".js.cache");
82
83 let wasm_path = find_wasm_file(&wasm_deploy_path).await?;
84 let js_path = wasm_deploy_path.join("corecrypto.js");
85
86 std::fs::copy(
87 cwd.join("crypto-ffi/bindings/js/test/index.html"),
88 wasm_deploy_path.join("index.html"),
89 )?;
90
91 if !wasm_path.exists() || !js_path.exists() {
92 spinner.update("WASM: No JS/WASM files found, rebuilding; Please wait...");
93 } else if wasm_cache_path.exists() && js_cache_path.exists() {
94 let wasm_hash = hex::decode(tokio::fs::read_to_string(wasm_cache_path.clone()).await?)?;
95 let js_hash = hex::decode(tokio::fs::read_to_string(js_cache_path.clone()).await?)?;
96
97 let mut hasher = Sha256::new();
98 hasher.update(tokio::fs::read(js_path.clone()).await?);
99 let js_current_hash = hasher.finalize();
100
101 let mut hasher = Sha256::new();
102 hasher.update(tokio::fs::read(wasm_path.clone()).await?);
103 let wasm_current_hash = hasher.finalize();
104
105 if js_current_hash[..] == js_hash && wasm_current_hash[..] == wasm_hash {
106 spinner.success("WASM: builds are identical - skipping build");
107 return Ok(());
108 }
109
110 spinner.update("WASM: Hashes differ, needs rebuild! Please wait...");
111 } else {
112 spinner.update("WASM: No cache file found, rebuilding to get a cache! Please wait...");
113 }
114
115 let mut cargo_args = vec!["make", "wasm"];
116 let mut bun_env = vec![];
117
118 if cfg!(feature = "proteus") {
119 spinner.update(
120 "`proteus` feature enabled. Building `core-crypto` with proteus support & enabling bun env BUILD_PROTEUS=1; Also building ESM bundle for Cryptobox",
121 );
122 cargo_args.push("--features");
123 cargo_args.push("proteus");
124 bun_env.push(("BUILD_PROTEUS", "1"));
125 }
126
127 Command::new("cargo")
128 .args(&cargo_args)
129 .current_dir(cwd.join("crypto-ffi"))
130 .stdout(std::process::Stdio::null())
131 .status()
132 .await?;
133
134 Command::new("bun")
135 .args(["run", "wdio"])
136 .envs(bun_env.clone())
137 .stdout(std::process::Stdio::null())
138 .stderr(std::process::Stdio::null())
139 .status()
140 .await?;
141
142 spinner.update("WASM: Computing new file hashes...");
143
144 let mut hasher = Sha256::new();
145 hasher.update(tokio::fs::read(js_path).await?);
146 let js_current_hash = hex::encode(hasher.finalize());
147
148 let wasm_path = find_wasm_file(&wasm_deploy_path).await?;
149 log::debug!("Found wasm file at {wasm_path:?}");
150
151 let mut hasher = Sha256::new();
152 hasher.update(tokio::fs::read(wasm_path).await?);
153 let wasm_current_hash = hex::encode(hasher.finalize());
154
155 log::debug!("Cache updated; CoreCrypto.wasm[{wasm_current_hash}] | corecrypto.js[{js_current_hash}]");
156 log::debug!("JS Cache Path: {js_cache_path:?} | WASM Cache Path: {wasm_cache_path:?}");
157
158 tokio::fs::write(js_cache_path, js_current_hash).await?;
159 tokio::fs::write(wasm_cache_path, wasm_current_hash).await?;
160
161 spinner.success("WASM bundle [OK]");
162
163 Ok(())
164}
165
166pub(crate) async fn spawn_http_server() -> Result<()> {
167 use warp::Filter as _;
168 let addr = SocketAddr::from(([0, 0, 0, 0], TEST_SERVER_PORT.parse()?));
169 let warp_filter_cc = warp::path("core-crypto").and(warp::fs::dir("platforms/web".to_string()));
170 let warp_filter_cbox =
171 warp::path("cryptobox").and(warp::fs::dir("interop/src/build/web/cryptobox-esm/dist".to_string()));
172
173 warp::serve(warp_filter_cc.or(warp_filter_cbox).boxed())
174 .bind(addr)
175 .await;
176
177 Ok(())
178}