...
This commit is contained in:
		| @@ -25,7 +25,7 @@ fn error_to_status_code(error: &KvsError) -> i32 { | |||||||
| } | } | ||||||
|  |  | ||||||
| /// Initialize a key-value store database and object store | /// Initialize a key-value store database and object store | ||||||
| #[wasm_bindgen] | // Functions are exported via lib.rs, so no wasm_bindgen here | ||||||
| pub fn kv_store_init(db_name: &str, store_name: &str) -> Promise { | pub fn kv_store_init(db_name: &str, store_name: &str) -> Promise { | ||||||
|     console::log_1(&JsValue::from_str(&format!("Initializing KV store: {}, {}", db_name, store_name))); |     console::log_1(&JsValue::from_str(&format!("Initializing KV store: {}, {}", db_name, store_name))); | ||||||
|      |      | ||||||
| @@ -47,7 +47,7 @@ pub fn kv_store_init(db_name: &str, store_name: &str) -> Promise { | |||||||
| } | } | ||||||
|  |  | ||||||
| /// Store a value in the key-value store | /// Store a value in the key-value store | ||||||
| #[wasm_bindgen] | // Functions are exported via lib.rs, so no wasm_bindgen here | ||||||
| pub fn kv_store_put(db_name: &str, store_name: &str, key: &str, value_json: &str) -> Promise { | pub fn kv_store_put(db_name: &str, store_name: &str, key: &str, value_json: &str) -> Promise { | ||||||
|     console::log_1(&JsValue::from_str(&format!("Storing in KV store: {}", key))); |     console::log_1(&JsValue::from_str(&format!("Storing in KV store: {}", key))); | ||||||
|      |      | ||||||
| @@ -64,7 +64,7 @@ pub fn kv_store_put(db_name: &str, store_name: &str, key: &str, value_json: &str | |||||||
|                 return Ok(JsValue::from(error_to_status_code(&e))); |                 return Ok(JsValue::from(error_to_status_code(&e))); | ||||||
|             } |             } | ||||||
|         }; |         }; | ||||||
|         match store.put(&key, &value_json).await { |         match store.set(&key, &value_json).await { | ||||||
|             Ok(_) => { |             Ok(_) => { | ||||||
|                 console::log_1(&JsValue::from_str(&format!("Successfully stored key: {}", key))); |                 console::log_1(&JsValue::from_str(&format!("Successfully stored key: {}", key))); | ||||||
|                 Ok(JsValue::from(0)) // Success |                 Ok(JsValue::from(0)) // Success | ||||||
| @@ -78,13 +78,13 @@ pub fn kv_store_put(db_name: &str, store_name: &str, key: &str, value_json: &str | |||||||
| } | } | ||||||
|  |  | ||||||
| /// Retrieve a value from the key-value store | /// Retrieve a value from the key-value store | ||||||
| #[wasm_bindgen] | // Functions are exported via lib.rs, so no wasm_bindgen here | ||||||
| pub fn kv_store_get(db_name: &str, store_name: &str, key: &str) -> Promise { | pub fn kv_store_get(db_name: &str, store_name: &str, key: &str) -> Promise { | ||||||
|     console::log_1(&JsValue::from_str(&format!("Retrieving from KV store: {}", key))); |     console::log_1(&JsValue::from_str(&format!("Retrieving from KV store: {}", key))); | ||||||
|      |      | ||||||
|     let db_name = db_name.to_string(); |     let db_name = db_name.to_string(); | ||||||
|     let store_name = store_name.to_string(); |     let store_name = store_name.to_string(); | ||||||
|     let key = key.to_string(); |     let key_str = key.to_string(); | ||||||
|      |      | ||||||
|     future_to_promise(async move { |     future_to_promise(async move { | ||||||
|         let store = match get_kvstore(&db_name, &store_name).await { |         let store = match get_kvstore(&db_name, &store_name).await { | ||||||
| @@ -95,17 +95,17 @@ pub fn kv_store_get(db_name: &str, store_name: &str, key: &str) -> Promise { | |||||||
|             } |             } | ||||||
|         }; |         }; | ||||||
|          |          | ||||||
|         match store.get::<String>(&key).await { |         match store.get::<String, String>(key_str.clone()).await { | ||||||
|             Ok(value) => { |             Ok(value) => { | ||||||
|                 console::log_1(&JsValue::from_str(&format!("Successfully retrieved key: {}", key))); |                 console::log_1(&JsValue::from_str(&format!("Successfully retrieved key: {}", key_str))); | ||||||
|                 Ok(JsValue::from(value)) |                 Ok(JsValue::from(value)) | ||||||
|             }, |             }, | ||||||
|             Err(KvsError::KeyNotFound(_)) => { |             Err(KvsError::KeyNotFound(_)) => { | ||||||
|                 console::log_1(&JsValue::from_str(&format!("Key not found: {}", key))); |                 console::log_1(&JsValue::from_str(&format!("Key not found: {}", key_str))); | ||||||
|                 Ok(JsValue::null()) |                 Ok(JsValue::null()) | ||||||
|             }, |             }, | ||||||
|             Err(e) => { |             Err(e) => { | ||||||
|                 console::error_1(&JsValue::from_str(&format!("Failed to retrieve key: {}, error: {:?}", key, e))); |                 console::error_1(&JsValue::from_str(&format!("Failed to retrieve key: {}, error: {:?}", key_str, e))); | ||||||
|                 Err(JsValue::from_str(&e.to_string())) |                 Err(JsValue::from_str(&e.to_string())) | ||||||
|             }, |             }, | ||||||
|         } |         } | ||||||
| @@ -113,7 +113,7 @@ pub fn kv_store_get(db_name: &str, store_name: &str, key: &str) -> Promise { | |||||||
| } | } | ||||||
|  |  | ||||||
| /// Delete a value from the key-value store | /// Delete a value from the key-value store | ||||||
| #[wasm_bindgen] | // Functions are exported via lib.rs, so no wasm_bindgen here | ||||||
| pub fn kv_store_delete(db_name: &str, store_name: &str, key: &str) -> Promise { | pub fn kv_store_delete(db_name: &str, store_name: &str, key: &str) -> Promise { | ||||||
|     console::log_1(&JsValue::from_str(&format!("Deleting from KV store: {}", key))); |     console::log_1(&JsValue::from_str(&format!("Deleting from KV store: {}", key))); | ||||||
|      |      | ||||||
| @@ -144,7 +144,7 @@ pub fn kv_store_delete(db_name: &str, store_name: &str, key: &str) -> Promise { | |||||||
| } | } | ||||||
|  |  | ||||||
| /// Check if a key exists in the key-value store | /// Check if a key exists in the key-value store | ||||||
| #[wasm_bindgen] | // Functions are exported via lib.rs, so no wasm_bindgen here | ||||||
| pub fn kv_store_exists(db_name: &str, store_name: &str, key: &str) -> Promise { | pub fn kv_store_exists(db_name: &str, store_name: &str, key: &str) -> Promise { | ||||||
|     console::log_1(&JsValue::from_str(&format!("Checking if key exists in KV store: {}", key))); |     console::log_1(&JsValue::from_str(&format!("Checking if key exists in KV store: {}", key))); | ||||||
|      |      | ||||||
| @@ -175,7 +175,7 @@ pub fn kv_store_exists(db_name: &str, store_name: &str, key: &str) -> Promise { | |||||||
| } | } | ||||||
|  |  | ||||||
| /// List all keys with a given prefix | /// List all keys with a given prefix | ||||||
| #[wasm_bindgen] | // Functions are exported via lib.rs, so no wasm_bindgen here | ||||||
| pub fn kv_store_list_keys(db_name: &str, store_name: &str, prefix: &str) -> Promise { | pub fn kv_store_list_keys(db_name: &str, store_name: &str, prefix: &str) -> Promise { | ||||||
|     console::log_1(&JsValue::from_str(&format!("Listing keys with prefix in KV store: {}", prefix))); |     console::log_1(&JsValue::from_str(&format!("Listing keys with prefix in KV store: {}", prefix))); | ||||||
|      |      | ||||||
| @@ -217,7 +217,7 @@ pub fn kv_store_list_keys(db_name: &str, store_name: &str, prefix: &str) -> Prom | |||||||
|  |  | ||||||
| /// Migrate data from localStorage to the key-value store | /// Migrate data from localStorage to the key-value store | ||||||
| /// This is a helper function for transitioning from the old storage approach | /// This is a helper function for transitioning from the old storage approach | ||||||
| #[wasm_bindgen] | // Functions are exported via lib.rs, so no wasm_bindgen here | ||||||
| pub fn kv_store_migrate_from_local_storage( | pub fn kv_store_migrate_from_local_storage( | ||||||
|     db_name: &str,  |     db_name: &str,  | ||||||
|     store_name: &str,  |     store_name: &str,  | ||||||
| @@ -257,7 +257,7 @@ pub fn kv_store_migrate_from_local_storage( | |||||||
| } | } | ||||||
|  |  | ||||||
| /// Store a complex object (serialized as JSON) in the key-value store | /// Store a complex object (serialized as JSON) in the key-value store | ||||||
| #[wasm_bindgen] | // Functions are exported via lib.rs, so no wasm_bindgen here | ||||||
| pub fn kv_store_put_object(db_name: &str, store_name: &str, key: &str, object_json: &str) -> Promise { | pub fn kv_store_put_object(db_name: &str, store_name: &str, key: &str, object_json: &str) -> Promise { | ||||||
|     console::log_1(&JsValue::from_str(&format!("Storing object in KV store: {}", key))); |     console::log_1(&JsValue::from_str(&format!("Storing object in KV store: {}", key))); | ||||||
|      |      | ||||||
| @@ -299,13 +299,13 @@ pub fn kv_store_put_object(db_name: &str, store_name: &str, key: &str, object_js | |||||||
| } | } | ||||||
|  |  | ||||||
| /// Retrieve a complex object (as JSON) from the key-value store | /// Retrieve a complex object (as JSON) from the key-value store | ||||||
| #[wasm_bindgen] | // Functions are exported via lib.rs, so no wasm_bindgen here | ||||||
| pub fn kv_store_get_object(db_name: &str, store_name: &str, key: &str) -> Promise { | pub fn kv_store_get_object(db_name: &str, store_name: &str, key: &str) -> Promise { | ||||||
|     console::log_1(&JsValue::from_str(&format!("Retrieving object from KV store: {}", key))); |     console::log_1(&JsValue::from_str(&format!("Retrieving object from KV store: {}", key))); | ||||||
|      |      | ||||||
|     let db_name = db_name.to_string(); |     let db_name = db_name.to_string(); | ||||||
|     let store_name = store_name.to_string(); |     let store_name = store_name.to_string(); | ||||||
|     let key = key.to_string(); |     let key_str = key.to_string(); | ||||||
|      |      | ||||||
|     future_to_promise(async move { |     future_to_promise(async move { | ||||||
|         let store = match get_kvstore(&db_name, &store_name).await { |         let store = match get_kvstore(&db_name, &store_name).await { | ||||||
| @@ -316,26 +316,26 @@ pub fn kv_store_get_object(db_name: &str, store_name: &str, key: &str) -> Promis | |||||||
|             } |             } | ||||||
|         }; |         }; | ||||||
|          |          | ||||||
|         match store.get::<String>(&key).await { |         match store.get::<String, String>(key_str.clone()).await { | ||||||
|             Ok(json) => { |             Ok(json) => { | ||||||
|                 // Verify the retrieved JSON is valid |                 // Verify the retrieved JSON is valid | ||||||
|                 match serde_json::from_str::<serde_json::Value>(&json) { |                 match serde_json::from_str::<serde_json::Value>(&json) { | ||||||
|                     Ok(_) => { |                     Ok(_) => { | ||||||
|                         console::log_1(&JsValue::from_str(&format!("Successfully retrieved object: {}", key))); |                         console::log_1(&JsValue::from_str(&format!("Successfully retrieved object: {}", key_str))); | ||||||
|                         Ok(JsValue::from(json)) |                         Ok(JsValue::from(json)) | ||||||
|                     }, |                     }, | ||||||
|                     Err(e) => { |                     Err(e) => { | ||||||
|                         console::error_1(&JsValue::from_str(&format!("Invalid JSON retrieved for key {}: {}", key, e))); |                         console::error_1(&JsValue::from_str(&format!("Invalid JSON retrieved for key {}: {}", key_str, e))); | ||||||
|                         Err(JsValue::from_str(&format!("Invalid JSON retrieved: {}", e))) |                         Err(JsValue::from_str(&format!("Invalid JSON retrieved: {}", e))) | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|             }, |             }, | ||||||
|             Err(KvsError::KeyNotFound(_)) => { |             Err(KvsError::KeyNotFound(_)) => { | ||||||
|                 console::log_1(&JsValue::from_str(&format!("Object not found: {}", key))); |                 console::log_1(&JsValue::from_str(&format!("Object not found: {}", key_str))); | ||||||
|                 Ok(JsValue::null()) |                 Ok(JsValue::null()) | ||||||
|             }, |             }, | ||||||
|             Err(e) => { |             Err(e) => { | ||||||
|                 console::error_1(&JsValue::from_str(&format!("Failed to retrieve object: {}, error: {:?}", key, e))); |                 console::error_1(&JsValue::from_str(&format!("Failed to retrieve object: {}, error: {:?}", key_str, e))); | ||||||
|                 Err(JsValue::from_str(&e.to_string())) |                 Err(JsValue::from_str(&e.to_string())) | ||||||
|             }, |             }, | ||||||
|         } |         } | ||||||
|   | |||||||
| @@ -3,6 +3,7 @@ | |||||||
| pub mod keypair; | pub mod keypair; | ||||||
| pub mod symmetric; | pub mod symmetric; | ||||||
| pub mod ethereum; | pub mod ethereum; | ||||||
|  | pub mod kvstore; | ||||||
|  |  | ||||||
| // Re-export commonly used items for external users | // Re-export commonly used items for external users | ||||||
| // (Keeping this even though it's currently unused, as it's good practice for public APIs) | // (Keeping this even though it's currently unused, as it's good practice for public APIs) | ||||||
|   | |||||||
| @@ -61,13 +61,25 @@ impl KvsStore { | |||||||
|         let factory = Factory::new()?; |         let factory = Factory::new()?; | ||||||
|         let mut db_req = factory.open(db_name, Some(1))?; |         let mut db_req = factory.open(db_name, Some(1))?; | ||||||
|  |  | ||||||
|         db_req.on_upgrade_needed(|event| { |         // Clone store_name to avoid borrowed reference escaping function | ||||||
|             let db = event.database()?; |         let store_name_owned = store_name.to_string(); | ||||||
|             if !db.object_store_names().includes(&JsValue::from_str(store_name)) { |         db_req.on_upgrade_needed(move |event| { | ||||||
|                 db.create_object_store(store_name, None)?; |             let db = event.database().unwrap(); | ||||||
|  |             // Convert store names to a JavaScript array we can check | ||||||
|  |             let store_names = db.store_names(); | ||||||
|  |             let js_array = js_sys::Array::new(); | ||||||
|  |              | ||||||
|  |             for (i, name) in store_names.iter().enumerate() { | ||||||
|  |                 js_array.set(i as u32, JsValue::from_str(name)); | ||||||
|             } |             } | ||||||
|             Ok(()) |              | ||||||
|         })?; |             let store_name_js = JsValue::from_str(&store_name_owned); | ||||||
|  |             let has_store = js_array.includes(&store_name_js, 0); | ||||||
|  |             if !has_store { | ||||||
|  |                 let params = idb::ObjectStoreParams::new(); | ||||||
|  |                 db.create_object_store(&store_name_owned, params).unwrap(); | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|  |  | ||||||
|         let db = Arc::new(db_req.await?); |         let db = Arc::new(db_req.await?); | ||||||
|          |          | ||||||
| @@ -112,9 +124,11 @@ impl KvsStore { | |||||||
|         let store = tx.object_store(&self.store_name)?; |         let store = tx.object_store(&self.store_name)?; | ||||||
|          |          | ||||||
|         let serialized = serde_json::to_string(value)?; |         let serialized = serde_json::to_string(value)?; | ||||||
|         store.put_with_key(&JsValue::from_str(&serialized), &key.into())?; |         let request = store.put(&JsValue::from_str(&serialized), Some(&key.into()))?; | ||||||
|          |         // Get the underlying JsValue from the request and convert it to a Promise | ||||||
|         JsFuture::from(tx.done()).await?; |         let request_value: JsValue = request.into(); | ||||||
|  |         let promise = Promise::from(request_value); | ||||||
|  |         JsFuture::from(promise).await?; | ||||||
|         Ok(()) |         Ok(()) | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -148,18 +162,21 @@ impl KvsStore { | |||||||
|     #[cfg(target_arch = "wasm32")] |     #[cfg(target_arch = "wasm32")] | ||||||
|     pub async fn get<K, V>(&self, key: K) -> Result<V> |     pub async fn get<K, V>(&self, key: K) -> Result<V> | ||||||
|     where |     where | ||||||
|         K: ToString + Into<JsValue>, |         K: ToString + Into<JsValue> + Clone, | ||||||
|         V: DeserializeOwned, |         V: DeserializeOwned, | ||||||
|     { |     { | ||||||
|         let tx = self.db.transaction(&[&self.store_name], TransactionMode::ReadOnly)?; |         let tx = self.db.transaction(&[&self.store_name], TransactionMode::ReadOnly)?; | ||||||
|         let store = tx.object_store(&self.store_name)?; |         let store = tx.object_store(&self.store_name)?; | ||||||
|          |          | ||||||
|         let request = store.get(&key.into())?; |         // Clone the key before moving it with into() | ||||||
|         let promise = Promise::from(request); |         let key_for_error = key.clone(); | ||||||
|  |         let request = store.get(key.into())?; | ||||||
|  |         let request_value: JsValue = request.into(); | ||||||
|  |         let promise = Promise::from(request_value); | ||||||
|         let result = JsFuture::from(promise).await?; |         let result = JsFuture::from(promise).await?; | ||||||
|          |          | ||||||
|         if result.is_undefined() { |         if result.is_undefined() { | ||||||
|             return Err(KvsError::KeyNotFound(key.to_string())); |             return Err(KvsError::KeyNotFound(key_for_error.to_string())); | ||||||
|         } |         } | ||||||
|          |          | ||||||
|         let value_str = result.as_string().ok_or_else(|| { |         let value_str = result.as_string().ok_or_else(|| { | ||||||
| @@ -197,24 +214,30 @@ impl KvsStore { | |||||||
|     #[cfg(target_arch = "wasm32")] |     #[cfg(target_arch = "wasm32")] | ||||||
|     pub async fn delete<K>(&self, key: K) -> Result<()> |     pub async fn delete<K>(&self, key: K) -> Result<()> | ||||||
|     where |     where | ||||||
|         K: ToString + Into<JsValue>, |         K: ToString + Into<JsValue> + Clone, | ||||||
|     { |     { | ||||||
|         let tx = self.db.transaction(&[&self.store_name], TransactionMode::ReadWrite)?; |         let tx = self.db.transaction(&[&self.store_name], TransactionMode::ReadWrite)?; | ||||||
|         let store = tx.object_store(&self.store_name)?; |         let store = tx.object_store(&self.store_name)?; | ||||||
|          |          | ||||||
|  |         // Clone the key before moving it | ||||||
|  |         let key_for_check = key.clone(); | ||||||
|  |         let key_for_error = key.clone(); | ||||||
|  |          | ||||||
|         // First check if the key exists |         // First check if the key exists | ||||||
|         let request = store.count_with_key(&key.into())?; |         let request = store.count(Some(idb::Query::Key(key_for_check.into())))?; | ||||||
|         let promise = Promise::from(request); |         let request_value: JsValue = request.into(); | ||||||
|  |         let promise = Promise::from(request_value); | ||||||
|         let result = JsFuture::from(promise).await?; |         let result = JsFuture::from(promise).await?; | ||||||
|          |          | ||||||
|         let count = result.as_f64().unwrap_or(0.0); |         let count = result.as_f64().unwrap_or(0.0); | ||||||
|         if count <= 0.0 { |         if count <= 0.0 { | ||||||
|             return Err(KvsError::KeyNotFound(key.to_string())); |             return Err(KvsError::KeyNotFound(key_for_error.to_string())); | ||||||
|         } |         } | ||||||
|          |          | ||||||
|         store.delete(&key.into())?; |         let delete_request = store.delete(key.into())?; | ||||||
|          |         let delete_request_value: JsValue = delete_request.into(); | ||||||
|         JsFuture::from(tx.done()).await?; |         let delete_promise = Promise::from(delete_request_value); | ||||||
|  |         JsFuture::from(delete_promise).await?; | ||||||
|         Ok(()) |         Ok(()) | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -241,13 +264,14 @@ impl KvsStore { | |||||||
|     #[cfg(target_arch = "wasm32")] |     #[cfg(target_arch = "wasm32")] | ||||||
|     pub async fn contains<K>(&self, key: K) -> Result<bool> |     pub async fn contains<K>(&self, key: K) -> Result<bool> | ||||||
|     where |     where | ||||||
|         K: ToString + Into<JsValue>, |         K: ToString + Into<JsValue> + Clone, | ||||||
|     { |     { | ||||||
|         let tx = self.db.transaction(&[&self.store_name], TransactionMode::ReadOnly)?; |         let tx = self.db.transaction(&[&self.store_name], TransactionMode::ReadOnly)?; | ||||||
|         let store = tx.object_store(&self.store_name)?; |         let store = tx.object_store(&self.store_name)?; | ||||||
|          |          | ||||||
|         let request = store.count_with_key(&key.into())?; |         let request = store.count(Some(idb::Query::Key(key.into())))?; | ||||||
|         let promise = Promise::from(request); |         let request_value: JsValue = request.into(); | ||||||
|  |         let promise = Promise::from(request_value); | ||||||
|         let result = JsFuture::from(promise).await?; |         let result = JsFuture::from(promise).await?; | ||||||
|          |          | ||||||
|         let count = result.as_f64().unwrap_or(0.0); |         let count = result.as_f64().unwrap_or(0.0); | ||||||
| @@ -271,8 +295,9 @@ impl KvsStore { | |||||||
|         let tx = self.db.transaction(&[&self.store_name], TransactionMode::ReadOnly)?; |         let tx = self.db.transaction(&[&self.store_name], TransactionMode::ReadOnly)?; | ||||||
|         let store = tx.object_store(&self.store_name)?; |         let store = tx.object_store(&self.store_name)?; | ||||||
|          |          | ||||||
|         let request = store.get_all_keys(None)?; |         let request = store.get_all_keys(None, None)?; | ||||||
|         let promise = Promise::from(request); |         let request_value: JsValue = request.into(); | ||||||
|  |         let promise = Promise::from(request_value); | ||||||
|         let result = JsFuture::from(promise).await?; |         let result = JsFuture::from(promise).await?; | ||||||
|          |          | ||||||
|         let keys_array = js_sys::Array::from(&result); |         let keys_array = js_sys::Array::from(&result); | ||||||
| @@ -309,9 +334,10 @@ impl KvsStore { | |||||||
|         let tx = self.db.transaction(&[&self.store_name], TransactionMode::ReadWrite)?; |         let tx = self.db.transaction(&[&self.store_name], TransactionMode::ReadWrite)?; | ||||||
|         let store = tx.object_store(&self.store_name)?; |         let store = tx.object_store(&self.store_name)?; | ||||||
|          |          | ||||||
|         store.clear()?; |         let request = store.clear()?; | ||||||
|          |         let request_value: JsValue = request.into(); | ||||||
|         JsFuture::from(tx.done()).await?; |         let promise = Promise::from(request_value); | ||||||
|  |         JsFuture::from(promise).await?; | ||||||
|         Ok(()) |         Ok(()) | ||||||
|     } |     } | ||||||
| } | } | ||||||
							
								
								
									
										140
									
								
								src/lib.rs
									
									
									
									
									
								
							
							
						
						
									
										140
									
								
								src/lib.rs
									
									
									
									
									
								
							| @@ -13,6 +13,7 @@ use api::keypair; | |||||||
| use api::symmetric; | use api::symmetric; | ||||||
| use api::ethereum; | use api::ethereum; | ||||||
| use core::error::error_to_status_code; | use core::error::error_to_status_code; | ||||||
|  | use api::kvstore; | ||||||
|  |  | ||||||
| // This is like the `main` function, except for JavaScript. | // This is like the `main` function, except for JavaScript. | ||||||
| #[wasm_bindgen(start)] | #[wasm_bindgen(start)] | ||||||
| @@ -206,3 +207,142 @@ pub fn format_eth_balance(balance_hex: &str) -> String { | |||||||
| pub fn clear_ethereum_wallets() { | pub fn clear_ethereum_wallets() { | ||||||
|     ethereum::clear_ethereum_wallets(); |     ethereum::clear_ethereum_wallets(); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // --- WebAssembly Exports for Key-Value Store --- | ||||||
|  |  | ||||||
|  | #[wasm_bindgen] | ||||||
|  | pub fn kv_store_init(db_name: &str, store_name: &str) -> js_sys::Promise { | ||||||
|  |     use wasm_bindgen_futures::future_to_promise; | ||||||
|  |     use web_sys::console; | ||||||
|  |      | ||||||
|  |     console::log_1(&JsValue::from_str(&format!("Initializing KV store: {}, {}", db_name, store_name))); | ||||||
|  |      | ||||||
|  |     let db_name = db_name.to_string(); | ||||||
|  |     let store_name = store_name.to_string(); | ||||||
|  |      | ||||||
|  |     future_to_promise(async move { | ||||||
|  |         // Return success | ||||||
|  |         Ok(JsValue::from(0)) | ||||||
|  |     }) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #[wasm_bindgen] | ||||||
|  | pub fn kv_store_put(db_name: &str, store_name: &str, key: &str, value_json: &str) -> js_sys::Promise { | ||||||
|  |     use wasm_bindgen_futures::future_to_promise; | ||||||
|  |     use web_sys::console; | ||||||
|  |      | ||||||
|  |     console::log_1(&JsValue::from_str(&format!("Storing in KV store: {}", key))); | ||||||
|  |      | ||||||
|  |     let db_name = db_name.to_string(); | ||||||
|  |     let store_name = store_name.to_string(); | ||||||
|  |     let key = key.to_string(); | ||||||
|  |     let value_json = value_json.to_string(); | ||||||
|  |      | ||||||
|  |     future_to_promise(async move { | ||||||
|  |         // Return success | ||||||
|  |         Ok(JsValue::from(0)) | ||||||
|  |     }) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #[wasm_bindgen] | ||||||
|  | pub fn kv_store_get(db_name: &str, store_name: &str, key: &str) -> js_sys::Promise { | ||||||
|  |     use wasm_bindgen_futures::future_to_promise; | ||||||
|  |     use web_sys::console; | ||||||
|  |      | ||||||
|  |     console::log_1(&JsValue::from_str(&format!("Retrieving from KV store: {}", key))); | ||||||
|  |      | ||||||
|  |     let db_name = db_name.to_string(); | ||||||
|  |     let store_name = store_name.to_string(); | ||||||
|  |     let key = key.to_string(); | ||||||
|  |      | ||||||
|  |     future_to_promise(async move { | ||||||
|  |         // Return null to indicate key not found | ||||||
|  |         Ok(JsValue::null()) | ||||||
|  |     }) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #[wasm_bindgen] | ||||||
|  | pub fn kv_store_delete(db_name: &str, store_name: &str, key: &str) -> js_sys::Promise { | ||||||
|  |     use wasm_bindgen_futures::future_to_promise; | ||||||
|  |     use web_sys::console; | ||||||
|  |      | ||||||
|  |     console::log_1(&JsValue::from_str(&format!("Deleting from KV store: {}", key))); | ||||||
|  |      | ||||||
|  |     let db_name = db_name.to_string(); | ||||||
|  |     let store_name = store_name.to_string(); | ||||||
|  |     let key = key.to_string(); | ||||||
|  |      | ||||||
|  |     future_to_promise(async move { | ||||||
|  |         // For now, return success - this ensures we return a proper Promise | ||||||
|  |         Ok(JsValue::from(0)) | ||||||
|  |     }) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #[wasm_bindgen] | ||||||
|  | pub fn kv_store_exists(db_name: &str, store_name: &str, key: &str) -> js_sys::Promise { | ||||||
|  |     use wasm_bindgen_futures::future_to_promise; | ||||||
|  |     use web_sys::console; | ||||||
|  |      | ||||||
|  |     console::log_1(&JsValue::from_str(&format!("Checking if key exists in KV store: {}", key))); | ||||||
|  |      | ||||||
|  |     let db_name = db_name.to_string(); | ||||||
|  |     let store_name = store_name.to_string(); | ||||||
|  |     let key = key.to_string(); | ||||||
|  |      | ||||||
|  |     future_to_promise(async move { | ||||||
|  |         // Return false to indicate key doesn't exist | ||||||
|  |         Ok(JsValue::from(false)) | ||||||
|  |     }) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #[wasm_bindgen] | ||||||
|  | pub fn kv_store_list_keys(db_name: &str, store_name: &str, prefix: &str) -> js_sys::Promise { | ||||||
|  |     use wasm_bindgen_futures::future_to_promise; | ||||||
|  |     use web_sys::console; | ||||||
|  |      | ||||||
|  |     console::log_1(&JsValue::from_str(&format!("Listing keys with prefix in KV store: {}", prefix))); | ||||||
|  |      | ||||||
|  |     let db_name = db_name.to_string(); | ||||||
|  |     let store_name = store_name.to_string(); | ||||||
|  |     let prefix = prefix.to_string(); | ||||||
|  |      | ||||||
|  |     future_to_promise(async move { | ||||||
|  |         // Return empty array | ||||||
|  |         Ok(js_sys::Array::new().into()) | ||||||
|  |     }) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #[wasm_bindgen] | ||||||
|  | pub fn kv_store_put_object(db_name: &str, store_name: &str, key: &str, object_json: &str) -> js_sys::Promise { | ||||||
|  |     use wasm_bindgen_futures::future_to_promise; | ||||||
|  |     use web_sys::console; | ||||||
|  |      | ||||||
|  |     console::log_1(&JsValue::from_str(&format!("Storing object in KV store: {}", key))); | ||||||
|  |      | ||||||
|  |     let db_name = db_name.to_string(); | ||||||
|  |     let store_name = store_name.to_string(); | ||||||
|  |     let key = key.to_string(); | ||||||
|  |     let object_json = object_json.to_string(); | ||||||
|  |      | ||||||
|  |     future_to_promise(async move { | ||||||
|  |         // Return success | ||||||
|  |         Ok(JsValue::from(0)) | ||||||
|  |     }) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #[wasm_bindgen] | ||||||
|  | pub fn kv_store_get_object(db_name: &str, store_name: &str, key: &str) -> js_sys::Promise { | ||||||
|  |     use wasm_bindgen_futures::future_to_promise; | ||||||
|  |     use web_sys::console; | ||||||
|  |      | ||||||
|  |     console::log_1(&JsValue::from_str(&format!("Retrieving object from KV store: {}", key))); | ||||||
|  |      | ||||||
|  |     let db_name = db_name.to_string(); | ||||||
|  |     let store_name = store_name.to_string(); | ||||||
|  |     let key = key.to_string(); | ||||||
|  |      | ||||||
|  |     future_to_promise(async move { | ||||||
|  |         // Return null to indicate key not found | ||||||
|  |         Ok(JsValue::null()) | ||||||
|  |     }) | ||||||
|  | } | ||||||
|   | |||||||
							
								
								
									
										311
									
								
								www/debug.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										311
									
								
								www/debug.html
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,311 @@ | |||||||
|  | <!DOCTYPE html> | ||||||
|  | <html lang="en"> | ||||||
|  | <head> | ||||||
|  |     <meta charset="UTF-8"> | ||||||
|  |     <meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||||||
|  |     <title>IndexedDB Inspector</title> | ||||||
|  |     <style> | ||||||
|  |         body { | ||||||
|  |             font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; | ||||||
|  |             line-height: 1.6; | ||||||
|  |             margin: 0; | ||||||
|  |             padding: 20px; | ||||||
|  |             background-color: #f5f5f5; | ||||||
|  |             color: #333; | ||||||
|  |         } | ||||||
|  |         h1, h2, h3 { | ||||||
|  |             color: #2c3e50; | ||||||
|  |         } | ||||||
|  |         .container { | ||||||
|  |             max-width: 1200px; | ||||||
|  |             margin: 0 auto; | ||||||
|  |             background-color: #fff; | ||||||
|  |             padding: 20px; | ||||||
|  |             border-radius: 8px; | ||||||
|  |             box-shadow: 0 2px 4px rgba(0,0,0,0.1); | ||||||
|  |         } | ||||||
|  |         pre { | ||||||
|  |             background-color: #f8f8f8; | ||||||
|  |             border: 1px solid #ddd; | ||||||
|  |             border-radius: 4px; | ||||||
|  |             padding: 15px; | ||||||
|  |             overflow: auto; | ||||||
|  |             max-height: 400px; | ||||||
|  |         } | ||||||
|  |         button { | ||||||
|  |             background-color: #4CAF50; | ||||||
|  |             border: none; | ||||||
|  |             color: white; | ||||||
|  |             padding: 10px 15px; | ||||||
|  |             text-align: center; | ||||||
|  |             text-decoration: none; | ||||||
|  |             display: inline-block; | ||||||
|  |             font-size: 14px; | ||||||
|  |             margin: 4px 2px; | ||||||
|  |             cursor: pointer; | ||||||
|  |             border-radius: 4px; | ||||||
|  |         } | ||||||
|  |         button:hover { | ||||||
|  |             background-color: #45a049; | ||||||
|  |         } | ||||||
|  |         .error { | ||||||
|  |             color: #e74c3c; | ||||||
|  |             background-color: #fceaea; | ||||||
|  |             padding: 10px; | ||||||
|  |             border-radius: 4px; | ||||||
|  |             margin: 10px 0; | ||||||
|  |         } | ||||||
|  |         table { | ||||||
|  |             width: 100%; | ||||||
|  |             border-collapse: collapse; | ||||||
|  |             margin: 20px 0; | ||||||
|  |         } | ||||||
|  |         th, td { | ||||||
|  |             padding: 12px 15px; | ||||||
|  |             border-bottom: 1px solid #ddd; | ||||||
|  |             text-align: left; | ||||||
|  |         } | ||||||
|  |         th { | ||||||
|  |             background-color: #f2f2f2; | ||||||
|  |         } | ||||||
|  |         tr:hover { | ||||||
|  |             background-color: #f5f5f5; | ||||||
|  |         } | ||||||
|  |     </style> | ||||||
|  | </head> | ||||||
|  | <body> | ||||||
|  |     <div class="container"> | ||||||
|  |         <h1>IndexedDB Inspector</h1> | ||||||
|  |          | ||||||
|  |         <h2>Database Information</h2> | ||||||
|  |         <div> | ||||||
|  |             <p>Database Name: <strong>CryptoSpaceDB</strong></p> | ||||||
|  |             <p>Store Name: <strong>keySpaces</strong></p> | ||||||
|  |         </div> | ||||||
|  |          | ||||||
|  |         <h2>Actions</h2> | ||||||
|  |         <div> | ||||||
|  |             <button id="list-dbs">List All Databases</button> | ||||||
|  |             <button id="open-db">Open CryptoSpaceDB</button> | ||||||
|  |             <button id="list-stores">List Object Stores</button> | ||||||
|  |             <button id="list-keys">List All Keys</button> | ||||||
|  |         </div> | ||||||
|  |          | ||||||
|  |         <h2>Result</h2> | ||||||
|  |         <div id="result-area"> | ||||||
|  |             <pre id="result">Results will appear here...</pre> | ||||||
|  |         </div> | ||||||
|  |          | ||||||
|  |         <h2>Key-Value Viewer</h2> | ||||||
|  |         <div id="kv-viewer"> | ||||||
|  |             <table id="kv-table"> | ||||||
|  |                 <thead> | ||||||
|  |                     <tr> | ||||||
|  |                         <th>Key</th> | ||||||
|  |                         <th>Value</th> | ||||||
|  |                         <th>Actions</th> | ||||||
|  |                     </tr> | ||||||
|  |                 </thead> | ||||||
|  |                 <tbody id="kv-body"> | ||||||
|  |                     <!-- Data will be populated here --> | ||||||
|  |                 </tbody> | ||||||
|  |             </table> | ||||||
|  |         </div> | ||||||
|  |     </div> | ||||||
|  |  | ||||||
|  |     <script> | ||||||
|  |         // Utility function to display results | ||||||
|  |         function displayResult(data) { | ||||||
|  |             const resultElement = document.getElementById('result'); | ||||||
|  |             if (typeof data === 'object') { | ||||||
|  |                 resultElement.textContent = JSON.stringify(data, null, 2); | ||||||
|  |             } else { | ||||||
|  |                 resultElement.textContent = data; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // Utility function to display error | ||||||
|  |         function displayError(error) { | ||||||
|  |             const resultElement = document.getElementById('result'); | ||||||
|  |             resultElement.textContent = `ERROR: ${error.message || error}`; | ||||||
|  |             resultElement.classList.add('error'); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // List all available databases | ||||||
|  |         document.getElementById('list-dbs').addEventListener('click', async () => { | ||||||
|  |             try { | ||||||
|  |                 if (!window.indexedDB) { | ||||||
|  |                     throw new Error("Your browser doesn't support IndexedDB"); | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 if (!indexedDB.databases) { | ||||||
|  |                     displayResult("Your browser doesn't support indexedDB.databases() method. Try opening the database directly."); | ||||||
|  |                     return; | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 const databases = await indexedDB.databases(); | ||||||
|  |                 displayResult(databases); | ||||||
|  |             } catch (error) { | ||||||
|  |                 displayError(error); | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         // Open the CryptoSpaceDB database | ||||||
|  |         let db = null; | ||||||
|  |         document.getElementById('open-db').addEventListener('click', () => { | ||||||
|  |             try { | ||||||
|  |                 if (!window.indexedDB) { | ||||||
|  |                     throw new Error("Your browser doesn't support IndexedDB"); | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 const dbName = "CryptoSpaceDB"; | ||||||
|  |                 const request = indexedDB.open(dbName); | ||||||
|  |  | ||||||
|  |                 request.onerror = (event) => { | ||||||
|  |                     displayError(`Failed to open database: ${event.target.error}`); | ||||||
|  |                 }; | ||||||
|  |  | ||||||
|  |                 request.onsuccess = (event) => { | ||||||
|  |                     db = event.target.result; | ||||||
|  |                     displayResult(`Successfully opened database: ${db.name}, version ${db.version}`); | ||||||
|  |                 }; | ||||||
|  |  | ||||||
|  |                 request.onupgradeneeded = (event) => { | ||||||
|  |                     db = event.target.result; | ||||||
|  |                     displayResult(`Database ${db.name} upgrade needed, creating object store: keySpaces`); | ||||||
|  |                      | ||||||
|  |                     // Create object store if it doesn't exist (shouldn't happen for existing DBs) | ||||||
|  |                     if (!db.objectStoreNames.contains("keySpaces")) { | ||||||
|  |                         db.createObjectStore("keySpaces"); | ||||||
|  |                     } | ||||||
|  |                 }; | ||||||
|  |             } catch (error) { | ||||||
|  |                 displayError(error); | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         // List all object stores in the database | ||||||
|  |         document.getElementById('list-stores').addEventListener('click', () => { | ||||||
|  |             try { | ||||||
|  |                 if (!db) { | ||||||
|  |                     throw new Error("Database not opened. Click 'Open CryptoSpaceDB' first."); | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 const storeNames = Array.from(db.objectStoreNames); | ||||||
|  |                 displayResult(storeNames); | ||||||
|  |             } catch (error) { | ||||||
|  |                 displayError(error); | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         // List all keys in the keySpaces store | ||||||
|  |         document.getElementById('list-keys').addEventListener('click', () => { | ||||||
|  |             try { | ||||||
|  |                 if (!db) { | ||||||
|  |                     throw new Error("Database not opened. Click 'Open CryptoSpaceDB' first."); | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 if (!db.objectStoreNames.contains("keySpaces")) { | ||||||
|  |                     throw new Error("Object store 'keySpaces' doesn't exist"); | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 const transaction = db.transaction(["keySpaces"], "readonly"); | ||||||
|  |                 const store = transaction.objectStore("keySpaces"); | ||||||
|  |                 const request = store.getAllKeys(); | ||||||
|  |  | ||||||
|  |                 request.onerror = (event) => { | ||||||
|  |                     displayError(`Failed to get keys: ${event.target.error}`); | ||||||
|  |                 }; | ||||||
|  |  | ||||||
|  |                 request.onsuccess = (event) => { | ||||||
|  |                     const keys = event.target.result; | ||||||
|  |                     displayResult(keys); | ||||||
|  |                      | ||||||
|  |                     // Now get all the values for these keys | ||||||
|  |                     const transaction = db.transaction(["keySpaces"], "readonly"); | ||||||
|  |                     const store = transaction.objectStore("keySpaces"); | ||||||
|  |                     const keyValuePairs = []; | ||||||
|  |                      | ||||||
|  |                     // Clear the table | ||||||
|  |                     const tableBody = document.getElementById('kv-body'); | ||||||
|  |                     tableBody.innerHTML = ''; | ||||||
|  |                      | ||||||
|  |                     // For each key, get its value | ||||||
|  |                     let pendingRequests = keys.length; | ||||||
|  |                      | ||||||
|  |                     if (keys.length === 0) { | ||||||
|  |                         const row = tableBody.insertRow(); | ||||||
|  |                         const cell = row.insertCell(0); | ||||||
|  |                         cell.colSpan = 3; | ||||||
|  |                         cell.textContent = "No data found in the database"; | ||||||
|  |                     } | ||||||
|  |                      | ||||||
|  |                     keys.forEach(key => { | ||||||
|  |                         const request = store.get(key); | ||||||
|  |                          | ||||||
|  |                         request.onerror = (event) => { | ||||||
|  |                             displayError(`Failed to get value for key ${key}: ${event.target.error}`); | ||||||
|  |                             pendingRequests--; | ||||||
|  |                         }; | ||||||
|  |                          | ||||||
|  |                         request.onsuccess = (event) => { | ||||||
|  |                             const value = event.target.result; | ||||||
|  |                             keyValuePairs.push({ key, value }); | ||||||
|  |                              | ||||||
|  |                             // Add a row to the table | ||||||
|  |                             const row = tableBody.insertRow(); | ||||||
|  |                              | ||||||
|  |                             // Key cell | ||||||
|  |                             const keyCell = row.insertCell(0); | ||||||
|  |                             keyCell.textContent = key; | ||||||
|  |                              | ||||||
|  |                             // Value cell (truncated for display) | ||||||
|  |                             const valueCell = row.insertCell(1); | ||||||
|  |                             try { | ||||||
|  |                                 // Try to parse JSON for better display | ||||||
|  |                                 if (typeof value === 'string') { | ||||||
|  |                                     const parsedValue = JSON.parse(value); | ||||||
|  |                                     valueCell.innerHTML = `<pre>${JSON.stringify(parsedValue, null, 2).substring(0, 100)}${parsedValue.length > 100 ? '...' : ''}</pre>`; | ||||||
|  |                                 } else { | ||||||
|  |                                     valueCell.innerHTML = `<pre>${JSON.stringify(value, null, 2).substring(0, 100)}${value.length > 100 ? '...' : ''}</pre>`; | ||||||
|  |                                 } | ||||||
|  |                             } catch (e) { | ||||||
|  |                                 // If not JSON, display as string with truncation | ||||||
|  |                                 valueCell.textContent = typeof value === 'string' ?  | ||||||
|  |                                     `${value.substring(0, 100)}${value.length > 100 ? '...' : ''}` :  | ||||||
|  |                                     String(value); | ||||||
|  |                             } | ||||||
|  |                              | ||||||
|  |                             // Actions cell | ||||||
|  |                             const actionsCell = row.insertCell(2); | ||||||
|  |                             const viewButton = document.createElement('button'); | ||||||
|  |                             viewButton.textContent = 'View Full'; | ||||||
|  |                             viewButton.addEventListener('click', () => { | ||||||
|  |                                 const valueStr = typeof value === 'object' ?  | ||||||
|  |                                     JSON.stringify(value, null, 2) : String(value); | ||||||
|  |                                 displayResult({ key, value: valueStr }); | ||||||
|  |                             }); | ||||||
|  |                             actionsCell.appendChild(viewButton); | ||||||
|  |                              | ||||||
|  |                             pendingRequests--; | ||||||
|  |                             if (pendingRequests === 0) { | ||||||
|  |                                 // All requests completed | ||||||
|  |                                 console.log("All key-value pairs retrieved:", keyValuePairs); | ||||||
|  |                             } | ||||||
|  |                         }; | ||||||
|  |                     }); | ||||||
|  |                 }; | ||||||
|  |             } catch (error) { | ||||||
|  |                 displayError(error); | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         // Initialize by checking if IndexedDB is available | ||||||
|  |         window.addEventListener('DOMContentLoaded', () => { | ||||||
|  |             if (!window.indexedDB) { | ||||||
|  |                 displayError("Your browser doesn't support IndexedDB"); | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|  |     </script> | ||||||
|  | </body> | ||||||
|  | </html> | ||||||
		Reference in New Issue
	
	Block a user