use briefcase_core::models::{DecisionSnapshot, Input, Output};
use std::collections::HashMap;
use wasm_bindgen::prelude::*;
use web_sys::console;
#[cfg(feature = "console_error_panic_hook")]
#[wasm_bindgen(start)]
pub fn main() {
console_error_panic_hook::set_once();
}
macro_rules! log {
( $( $t:tt )* ) => {
console::log_1(&format!( $( $t )* ).into());
}
}
#[wasm_bindgen]
pub fn init() {
#[cfg(feature = "console_error_panic_hook")]
console_error_panic_hook::set_once();
log!("🦀 Briefcase AI WASM module initialized!");
}
#[wasm_bindgen]
pub struct JsDecisionSnapshot {
inner: DecisionSnapshot,
}
#[wasm_bindgen]
impl JsDecisionSnapshot {
#[wasm_bindgen(constructor)]
pub fn new(function_name: &str) -> JsDecisionSnapshot {
JsDecisionSnapshot {
inner: DecisionSnapshot::new(function_name),
}
}
#[wasm_bindgen]
pub fn with_module(mut self, module_name: &str) -> JsDecisionSnapshot {
self.inner = self.inner.with_module(module_name);
self
}
#[wasm_bindgen]
pub fn add_input(
mut self,
name: &str,
value: &JsValue,
data_type: &str,
) -> Result<JsDecisionSnapshot, JsError> {
let json_value: serde_json::Value = serde_wasm_bindgen::from_value(value.clone())
.map_err(|e| JsError::new(&format!("Failed to convert input value: {}", e)))?;
let input = Input::new(name, json_value, data_type);
self.inner = self.inner.add_input(input);
Ok(self)
}
#[wasm_bindgen]
pub fn add_output(
mut self,
name: &str,
value: &JsValue,
data_type: &str,
) -> Result<JsDecisionSnapshot, JsError> {
let json_value: serde_json::Value = serde_wasm_bindgen::from_value(value.clone())
.map_err(|e| JsError::new(&format!("Failed to convert output value: {}", e)))?;
let output = Output::new(name, json_value, data_type);
self.inner = self.inner.add_output(output);
Ok(self)
}
#[wasm_bindgen]
pub fn add_tag(mut self, key: &str, value: &str) -> JsDecisionSnapshot {
self.inner = self.inner.add_tag(key, value);
self
}
#[wasm_bindgen]
pub fn to_json(&self) -> Result<JsValue, JsError> {
serde_wasm_bindgen::to_value(&self.inner)
.map_err(|e| JsError::new(&format!("Failed to serialize decision: {}", e)))
}
}
#[wasm_bindgen]
pub struct JsMemoryStorage {
decisions: HashMap<String, DecisionSnapshot>,
}
impl Default for JsMemoryStorage {
fn default() -> Self {
Self::new()
}
}
#[wasm_bindgen]
impl JsMemoryStorage {
#[wasm_bindgen(constructor)]
pub fn new() -> JsMemoryStorage {
JsMemoryStorage {
decisions: HashMap::new(),
}
}
#[wasm_bindgen]
pub fn save_decision(&mut self, decision: &JsDecisionSnapshot) -> String {
let id = format!("decision_{}", uuid::Uuid::new_v4());
self.decisions.insert(id.clone(), decision.inner.clone());
id
}
#[wasm_bindgen]
pub fn load_decision(&self, decision_id: &str) -> Result<JsDecisionSnapshot, JsError> {
let decision = self
.decisions
.get(decision_id)
.ok_or_else(|| JsError::new(&format!("Decision not found: {}", decision_id)))?;
Ok(JsDecisionSnapshot {
inner: decision.clone(),
})
}
#[wasm_bindgen]
pub fn health_check(&self) -> bool {
true
}
}
#[wasm_bindgen]
pub fn version() -> String {
env!("CARGO_PKG_VERSION").to_string()
}
#[wasm_bindgen]
pub fn test_functionality() -> Result<JsValue, JsError> {
let decision = JsDecisionSnapshot::new("test_function")
.with_module("test_module")
.add_tag("env", "test");
let mut storage = JsMemoryStorage::new();
let decision_id = storage.save_decision(&decision);
let _loaded_decision = storage.load_decision(&decision_id)?;
let result = serde_json::json!({
"decision_id": decision_id,
"storage_health": storage.health_check(),
"version": version(),
});
serde_wasm_bindgen::to_value(&result)
.map_err(|e| JsError::new(&format!("Failed to serialize test result: {}", e)))
}
pub mod client;
pub mod cost;
pub mod drift;
pub mod models;
pub mod sanitization;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_version_returns_package_version() {
let v = version();
assert!(!v.is_empty());
assert!(v.contains('.'), "Version '{}' should contain dots", v);
}
#[test]
fn test_js_decision_snapshot_construction() {
let decision = JsDecisionSnapshot::new("test_fn");
assert_eq!(decision.inner.function_name, "test_fn");
assert!(decision.inner.module_name.is_none());
}
#[test]
fn test_js_decision_snapshot_with_module() {
let decision = JsDecisionSnapshot::new("test_fn").with_module("test_mod");
assert_eq!(decision.inner.module_name, Some("test_mod".to_string()));
}
#[test]
fn test_js_decision_snapshot_add_tag() {
let decision = JsDecisionSnapshot::new("test_fn")
.add_tag("env", "production")
.add_tag("version", "1.0");
assert_eq!(
decision.inner.tags.get("env"),
Some(&"production".to_string())
);
assert_eq!(decision.inner.tags.get("version"), Some(&"1.0".to_string()));
}
#[test]
fn test_js_memory_storage_new() {
let storage = JsMemoryStorage::new();
assert!(storage.health_check());
}
#[test]
fn test_js_memory_storage_save_and_load() {
let decision = JsDecisionSnapshot::new("persist_fn")
.with_module("persist_mod")
.add_tag("key", "value");
let mut storage = JsMemoryStorage::new();
let id = storage.save_decision(&decision);
assert!(id.starts_with("decision_"));
let loaded = storage.load_decision(&id).unwrap();
assert_eq!(loaded.inner.function_name, "persist_fn");
assert_eq!(loaded.inner.module_name, Some("persist_mod".to_string()));
assert_eq!(loaded.inner.tags.get("key"), Some(&"value".to_string()));
}
#[test]
#[cfg(target_arch = "wasm32")]
fn test_js_memory_storage_load_nonexistent() {
let storage = JsMemoryStorage::new();
let result = storage.load_decision("nonexistent_id");
assert!(result.is_err());
}
#[test]
fn test_js_memory_storage_multiple_decisions() {
let mut storage = JsMemoryStorage::new();
let d1 = JsDecisionSnapshot::new("fn_1");
let d2 = JsDecisionSnapshot::new("fn_2");
let d3 = JsDecisionSnapshot::new("fn_3");
let id1 = storage.save_decision(&d1);
let id2 = storage.save_decision(&d2);
let id3 = storage.save_decision(&d3);
assert_ne!(id1, id2);
assert_ne!(id2, id3);
assert_ne!(id1, id3);
assert_eq!(
storage.load_decision(&id1).unwrap().inner.function_name,
"fn_1"
);
assert_eq!(
storage.load_decision(&id2).unwrap().inner.function_name,
"fn_2"
);
assert_eq!(
storage.load_decision(&id3).unwrap().inner.function_name,
"fn_3"
);
}
#[test]
fn test_js_memory_storage_default_trait() {
let storage = JsMemoryStorage::default();
assert!(storage.health_check());
}
}