//! SigSocket connection wrapper for WASM //! //! This module provides a WASM-bindgen compatible wrapper around the //! SigSocket client that can be used from JavaScript in the browser extension. use wasm_bindgen::prelude::*; use sigsocket_client::{SigSocketClient, SignResponse}; use crate::sigsocket::handler::JavaScriptSignHandler; /// WASM-bindgen wrapper for SigSocket client /// /// This provides a clean JavaScript API for the browser extension to: /// - Connect to SigSocket servers /// - Send responses to sign requests /// - Manage connection state #[wasm_bindgen] pub struct SigSocketConnection { client: Option, connected: bool, } #[wasm_bindgen] impl SigSocketConnection { /// Create a new SigSocket connection #[wasm_bindgen(constructor)] pub fn new() -> Self { Self { client: None, connected: false, } } /// Connect to a SigSocket server /// /// # Arguments /// * `server_url` - WebSocket server URL (e.g., "ws://localhost:8080/ws") /// * `public_key_hex` - Client's public key as hex string /// /// # Returns /// * `Ok(())` - Successfully connected /// * `Err(error)` - Connection failed #[wasm_bindgen] pub async fn connect(&mut self, server_url: &str, public_key_hex: &str) -> Result<(), JsValue> { web_sys::console::log_1(&format!("SigSocketConnection::connect called with URL: {}", server_url).into()); web_sys::console::log_1(&format!("Public key (first 16 chars): {}", &public_key_hex[..16]).into()); // Decode public key from hex let public_key = hex::decode(public_key_hex) .map_err(|e| JsValue::from_str(&format!("Invalid public key hex: {}", e)))?; web_sys::console::log_1(&"Creating SigSocketClient...".into()); // Create client let mut client = SigSocketClient::new(server_url, public_key) .map_err(|e| JsValue::from_str(&format!("Failed to create client: {}", e)))?; web_sys::console::log_1(&"SigSocketClient created, attempting connection...".into()); // Set up JavaScript handler client.set_sign_handler(JavaScriptSignHandler); // Connect to server web_sys::console::log_1(&"Calling client.connect()...".into()); client.connect().await .map_err(|e| { web_sys::console::error_1(&format!("Client connection failed: {}", e).into()); JsValue::from_str(&format!("Failed to connect: {}", e)) })?; web_sys::console::log_1(&"Client connection successful!".into()); self.client = Some(client); self.connected = true; web_sys::console::log_1(&"SigSocketConnection state updated to connected".into()); // Notify JavaScript of connection state change super::handler::on_connection_state_changed(true); Ok(()) } /// Send a response to a sign request /// /// This should be called by the extension after the user has approved /// a sign request and the message has been signed. /// /// # Arguments /// * `request_id` - ID of the original request /// * `message_base64` - Original message (base64-encoded) /// * `signature_hex` - Signature as hex string /// /// # Returns /// * `Ok(())` - Response sent successfully /// * `Err(error)` - Failed to send response #[wasm_bindgen] pub async fn send_response(&self, request_id: &str, message_base64: &str, signature_hex: &str) -> Result<(), JsValue> { let client = self.client.as_ref() .ok_or_else(|| JsValue::from_str("Not connected"))?; // Decode signature from hex let signature = hex::decode(signature_hex) .map_err(|e| JsValue::from_str(&format!("Invalid signature hex: {}", e)))?; // Create response let response = SignResponse::new(request_id, message_base64, base64::Engine::encode(&base64::engine::general_purpose::STANDARD, &signature)); // Send response client.send_sign_response(&response).await .map_err(|e| JsValue::from_str(&format!("Failed to send response: {}", e)))?; Ok(()) } /// Send a rejection for a sign request /// /// This should be called when the user rejects a sign request. /// /// # Arguments /// * `request_id` - ID of the request to reject /// * `reason` - Reason for rejection (optional) /// /// # Returns /// * `Ok(())` - Rejection sent successfully /// * `Err(error)` - Failed to send rejection #[wasm_bindgen] pub async fn send_rejection(&self, request_id: &str, reason: &str) -> Result<(), JsValue> { // For now, we'll just log the rejection // In a full implementation, the server might support rejection messages web_sys::console::log_1(&format!("Sign request {} rejected: {}", request_id, reason).into()); // TODO: If the server supports rejection messages, send them here // For now, we just ignore the request (timeout on server side) Ok(()) } /// Disconnect from the SigSocket server #[wasm_bindgen] pub fn disconnect(&mut self) { if let Some(_client) = self.client.take() { // Note: We can't await in a non-async function, so we'll just drop the client // The Drop implementation should handle cleanup self.connected = false; // Notify JavaScript of connection state change super::handler::on_connection_state_changed(false); } } /// Check if connected to the server #[wasm_bindgen] pub fn is_connected(&self) -> bool { // Check if we have a client and if it reports as connected if let Some(ref client) = self.client { client.is_connected() } else { false } } } impl Default for SigSocketConnection { fn default() -> Self { Self::new() } }