//! rUv Neural WASM — WebAssembly bindings for browser-based brain topology visualization. //! //! This crate provides JavaScript-callable functions for creating, analyzing, and //! visualizing brain connectivity graphs directly in the browser. It wraps the //! core `ruv-neural-core` types with `wasm-bindgen` bindings and provides //! lightweight WASM-compatible implementations of graph algorithms. //! //! # Features //! //! - Parse brain graphs from JSON and return JS-compatible objects //! - Compute minimum cut (Stoer-Wagner) on graphs up to 500 nodes //! - Generate topology metrics (density, efficiency, modularity, Fiedler value) //! - Spectral embedding via power iteration (no LAPACK dependency) //! - Decode cognitive state from topology metrics //! - RVF file format load/export //! - Streaming data processor for WebSocket integration //! - Visualization data structures for D3.js / Three.js pub mod graph_wasm; pub mod streaming; pub mod viz_data; use ruv_neural_core::graph::BrainGraph; use ruv_neural_core::rvf::{RvfDataType, RvfFile}; use ruv_neural_core::topology::TopologyMetrics; use wasm_bindgen::prelude::*; use graph_wasm::{wasm_decode, wasm_embed, wasm_mincut, wasm_topology_metrics}; /// Initialize the WASM module. /// /// Called automatically when the module is loaded. Sets up panic hooks /// for better error messages in the browser console. #[wasm_bindgen(start)] pub fn init() { #[cfg(feature = "console_error_panic_hook")] console_error_panic_hook::set_once(); } /// Create a brain graph from JSON data. /// /// Parses a JSON string into a `BrainGraph` and returns it as a JS object. /// /// # Arguments /// * `json_data` - JSON string representing a `BrainGraph`. /// /// # Returns /// A JS object containing the parsed graph data. #[wasm_bindgen] pub fn create_brain_graph(json_data: &str) -> Result { let graph: BrainGraph = serde_json::from_str(json_data).map_err(|e| JsError::new(&e.to_string()))?; serde_wasm_bindgen::to_value(&graph).map_err(|e| JsError::new(&e.to_string())) } /// Compute minimum cut on a brain graph. /// /// Uses a simplified Stoer-Wagner algorithm suitable for graphs with up to /// 500 nodes. Returns the cut value, partitions, and cut edges. /// /// # Arguments /// * `json_graph` - JSON string representing a `BrainGraph`. /// /// # Returns /// A JS object containing the `MincutResult`. #[wasm_bindgen] pub fn compute_mincut(json_graph: &str) -> Result { let graph: BrainGraph = serde_json::from_str(json_graph).map_err(|e| JsError::new(&e.to_string()))?; let result = wasm_mincut(&graph)?; serde_wasm_bindgen::to_value(&result).map_err(|e| JsError::new(&e.to_string())) } /// Compute topology metrics for a brain graph. /// /// Returns density, efficiency, modularity, Fiedler value, entropy, and /// module count. All computations use WASM-compatible algorithms without /// heavy linear algebra dependencies. /// /// # Arguments /// * `json_graph` - JSON string representing a `BrainGraph`. /// /// # Returns /// A JS object containing the `TopologyMetrics`. #[wasm_bindgen] pub fn compute_topology_metrics(json_graph: &str) -> Result { let graph: BrainGraph = serde_json::from_str(json_graph).map_err(|e| JsError::new(&e.to_string()))?; let metrics = wasm_topology_metrics(&graph)?; serde_wasm_bindgen::to_value(&metrics).map_err(|e| JsError::new(&e.to_string())) } /// Generate a spectral embedding from a brain graph. /// /// Uses power iteration on the normalized Laplacian to compute spectral /// coordinates. Returns a flat vector of length `num_nodes * dimension`. /// /// # Arguments /// * `json_graph` - JSON string representing a `BrainGraph`. /// * `dimension` - Number of embedding dimensions. /// /// # Returns /// A JS object containing the `NeuralEmbedding`. #[wasm_bindgen] pub fn embed_graph(json_graph: &str, dimension: usize) -> Result { let graph: BrainGraph = serde_json::from_str(json_graph).map_err(|e| JsError::new(&e.to_string()))?; let embedding = wasm_embed(&graph, dimension)?; serde_wasm_bindgen::to_value(&embedding).map_err(|e| JsError::new(&e.to_string())) } /// Decode cognitive state from topology metrics. /// /// Uses threshold-based heuristics to classify the cognitive state /// from a set of topology metrics. For production use, the trained /// decoder from `ruv-neural-decoder` is recommended. /// /// # Arguments /// * `json_metrics` - JSON string representing `TopologyMetrics`. /// /// # Returns /// A JS object containing the decoded `CognitiveState`. #[wasm_bindgen] pub fn decode_state(json_metrics: &str) -> Result { let metrics: TopologyMetrics = serde_json::from_str(json_metrics).map_err(|e| JsError::new(&e.to_string()))?; let state = wasm_decode(&metrics)?; serde_wasm_bindgen::to_value(&state).map_err(|e| JsError::new(&e.to_string())) } /// Load an RVF (RuVector File) from raw bytes. /// /// Parses the binary RVF header, JSON metadata, and payload, returning /// the complete file structure as a JS object. /// /// # Arguments /// * `data` - Raw bytes of the RVF file. /// /// # Returns /// A JS object containing the parsed `RvfFile`. #[wasm_bindgen] pub fn load_rvf(data: &[u8]) -> Result { let mut cursor = std::io::Cursor::new(data); let rvf = RvfFile::read_from(&mut cursor).map_err(|e| JsError::new(&e.to_string()))?; serde_wasm_bindgen::to_value(&rvf).map_err(|e| JsError::new(&e.to_string())) } /// Export a brain graph as RVF bytes. /// /// Serializes a `BrainGraph` (provided as JSON) into the binary RVF format. /// /// # Arguments /// * `json_graph` - JSON string representing a `BrainGraph`. /// /// # Returns /// A `Vec` containing the RVF binary data. #[wasm_bindgen] pub fn export_rvf(json_graph: &str) -> Result, JsError> { let graph: BrainGraph = serde_json::from_str(json_graph).map_err(|e| JsError::new(&e.to_string()))?; let graph_json = serde_json::to_vec(&graph).map_err(|e| JsError::new(&e.to_string()))?; let mut rvf = RvfFile::new(RvfDataType::BrainGraph); rvf.header.num_entries = 1; rvf.metadata = serde_json::json!({ "num_nodes": graph.num_nodes, "num_edges": graph.edges.len(), "timestamp": graph.timestamp, }); rvf.data = graph_json; let mut buf = Vec::new(); rvf.write_to(&mut buf) .map_err(|e| JsError::new(&e.to_string()))?; Ok(buf) } /// Get the crate version string. #[wasm_bindgen] pub fn version() -> String { env!("CARGO_PKG_VERSION").to_string() } #[cfg(test)] mod tests { use super::*; use ruv_neural_core::brain::Atlas; use ruv_neural_core::graph::{BrainEdge, BrainGraph}; use ruv_neural_core::signal::FrequencyBand; fn sample_graph_json() -> String { let graph = BrainGraph { num_nodes: 3, edges: vec![ BrainEdge { source: 0, target: 1, weight: 0.8, metric: ruv_neural_core::graph::ConnectivityMetric::Coherence, frequency_band: FrequencyBand::Alpha, }, BrainEdge { source: 1, target: 2, weight: 0.5, metric: ruv_neural_core::graph::ConnectivityMetric::Coherence, frequency_band: FrequencyBand::Beta, }, ], timestamp: 1000.0, window_duration_s: 1.0, atlas: Atlas::Custom(3), }; serde_json::to_string(&graph).unwrap() } #[test] fn test_create_brain_graph_parses_valid_json() { let json = sample_graph_json(); let graph: BrainGraph = serde_json::from_str(&json).unwrap(); assert_eq!(graph.num_nodes, 3); assert_eq!(graph.edges.len(), 2); } #[test] fn test_create_brain_graph_rejects_invalid_json() { let result: Result = serde_json::from_str("not valid json"); assert!(result.is_err()); } #[test] fn test_compute_mincut_returns_valid_result() { let json = sample_graph_json(); let graph: BrainGraph = serde_json::from_str(&json).unwrap(); let result = wasm_mincut(&graph).unwrap(); assert!(result.cut_value >= 0.0); assert_eq!(result.num_nodes(), 3); } #[test] fn test_rvf_round_trip() { let json = sample_graph_json(); let graph: BrainGraph = serde_json::from_str(&json).unwrap(); // Export to RVF bytes. let graph_bytes = serde_json::to_vec(&graph).unwrap(); let mut rvf = RvfFile::new(RvfDataType::BrainGraph); rvf.header.num_entries = 1; rvf.metadata = serde_json::json!({"test": true}); rvf.data = graph_bytes; let mut buf = Vec::new(); rvf.write_to(&mut buf).unwrap(); // Read back. let mut cursor = std::io::Cursor::new(&buf); let loaded = RvfFile::read_from(&mut cursor).unwrap(); assert_eq!(loaded.header.data_type, RvfDataType::BrainGraph); assert_eq!(loaded.header.num_entries, 1); // Deserialize the payload back to a BrainGraph. let loaded_graph: BrainGraph = serde_json::from_slice(&loaded.data).unwrap(); assert_eq!(loaded_graph.num_nodes, 3); assert_eq!(loaded_graph.edges.len(), 2); } #[test] fn test_version_returns_string() { let v = version(); assert!(!v.is_empty()); assert!(v.contains('.')); } #[test] fn test_decode_state_from_metrics() { let metrics = TopologyMetrics { global_mincut: 0.5, modularity: 0.6, global_efficiency: 0.2, local_efficiency: 0.3, graph_entropy: 1.5, fiedler_value: 0.3, num_modules: 2, timestamp: 0.0, }; let state = wasm_decode(&metrics).unwrap(); // High modularity + low efficiency + moderate entropy => Rest. assert_eq!( state, ruv_neural_core::topology::CognitiveState::Rest ); } #[test] fn test_embed_graph_produces_correct_dimensions() { let json = sample_graph_json(); let graph: BrainGraph = serde_json::from_str(&json).unwrap(); let embedding = wasm_embed(&graph, 2).unwrap(); assert_eq!(embedding.vector.len(), 6); } }