(wasm) Cleanup and refactoring, add TableDB transactions, copy over docs for JSDoc.
This commit is contained in:
		| @@ -64,6 +64,7 @@ pub struct VeilidStateAttachment { | ||||
| #[cfg_attr(target_arch = "wasm32", derive(Tsify))] | ||||
| pub struct PeerTableData { | ||||
|     #[schemars(with = "Vec<String>")] | ||||
|     #[cfg_attr(target_arch = "wasm32", tsify(type = "string[]"))] | ||||
|     pub node_ids: Vec<TypedKey>, | ||||
|     pub peer_address: String, | ||||
|     pub peer_stats: PeerStats, | ||||
| @@ -82,8 +83,10 @@ pub struct VeilidStateNetwork { | ||||
| #[cfg_attr(target_arch = "wasm32", derive(Tsify))] | ||||
| pub struct VeilidRouteChange { | ||||
|     #[schemars(with = "Vec<String>")] | ||||
|     #[cfg_attr(target_arch = "wasm32", tsify(type = "string"))] | ||||
|     pub dead_routes: Vec<RouteId>, | ||||
|     #[schemars(with = "Vec<String>")] | ||||
|     #[cfg_attr(target_arch = "wasm32", tsify(type = "string"))] | ||||
|     pub dead_remote_routes: Vec<RouteId>, | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -65,6 +65,17 @@ fn take_veilid_api() -> Result<veilid_core::VeilidAPI, veilid_core::VeilidAPIErr | ||||
|         .ok_or(veilid_core::VeilidAPIError::NotInitialized) | ||||
| } | ||||
|  | ||||
| // Marshalling helpers | ||||
| pub fn unmarshall(b64: String) -> Vec<u8> { | ||||
|     data_encoding::BASE64URL_NOPAD | ||||
|         .decode(b64.as_bytes()) | ||||
|         .unwrap() | ||||
| } | ||||
|  | ||||
| pub fn marshall(data: &Vec<u8>) -> String { | ||||
|     data_encoding::BASE64URL_NOPAD.encode(data) | ||||
| } | ||||
|  | ||||
| // JSON Helpers for WASM | ||||
| pub fn to_json<T: Serialize + Debug>(val: T) -> JsValue { | ||||
|     JsValue::from_str(&serialize_json(val)) | ||||
|   | ||||
| @@ -66,10 +66,16 @@ impl VeilidClient { | ||||
|             .expect("failed to initalize WASM platform"); | ||||
|     } | ||||
|  | ||||
|     /// Initialize a Veilid node, with the configuration in JSON format | ||||
|     /// | ||||
|     /// Must be called only once at the start of an application | ||||
|     /// | ||||
|     /// @param {UpdateVeilidFunction} update_callback_js - called when internal state of the Veilid node changes, for example, when app-level messages are received, when private routes die and need to be reallocated, or when routing table states change | ||||
|     /// @param {string} json_config - called at startup to supply a JSON configuration object. | ||||
|     pub async fn startupCore( | ||||
|         update_callback_js: UpdateVeilidFunction, | ||||
|         json_config: String, | ||||
|     ) -> Result<(), VeilidAPIError> { | ||||
|     ) -> APIResult<()> { | ||||
|         let update_callback_js = SendWrapper::new(update_callback_js); | ||||
|         let update_callback = Arc::new(move |update: VeilidUpdate| { | ||||
|             let _ret = match Function::call1( | ||||
| @@ -86,12 +92,12 @@ impl VeilidClient { | ||||
|         }); | ||||
|  | ||||
|         if VEILID_API.borrow().is_some() { | ||||
|             return Err(veilid_core::VeilidAPIError::AlreadyInitialized); | ||||
|             return APIResult::Err(veilid_core::VeilidAPIError::AlreadyInitialized); | ||||
|         } | ||||
|  | ||||
|         let veilid_api = veilid_core::api_startup_json(update_callback, json_config).await?; | ||||
|         VEILID_API.replace(Some(veilid_api)); | ||||
|         Ok(()) | ||||
|         APIRESULT_UNDEFINED | ||||
|     } | ||||
|  | ||||
|     // TODO: can we refine the TS type of `layer`? | ||||
| @@ -110,36 +116,42 @@ impl VeilidClient { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub async fn shutdownCore() -> Result<(), VeilidAPIError> { | ||||
|     /// Shut down Veilid and terminate the API. | ||||
|     pub async fn shutdownCore() -> APIResult<()> { | ||||
|         let veilid_api = take_veilid_api()?; | ||||
|         veilid_api.shutdown().await; | ||||
|         Ok(()) | ||||
|         APIRESULT_UNDEFINED | ||||
|     } | ||||
|  | ||||
|     pub async fn getState() -> Result<VeilidState, VeilidAPIError> { | ||||
|     /// Get a full copy of the current state of Veilid. | ||||
|     pub async fn getState() -> APIResult<VeilidState> { | ||||
|         let veilid_api = get_veilid_api()?; | ||||
|         let core_state = veilid_api.get_state().await?; | ||||
|         Ok(core_state) | ||||
|         APIResult::Ok(core_state) | ||||
|     } | ||||
|  | ||||
|     pub async fn attach() -> Result<(), VeilidAPIError> { | ||||
|     /// Connect to the network. | ||||
|     pub async fn attach() -> APIResult<()> { | ||||
|         let veilid_api = get_veilid_api()?; | ||||
|         veilid_api.attach().await?; | ||||
|         Ok(()) | ||||
|         APIRESULT_UNDEFINED | ||||
|     } | ||||
|  | ||||
|     pub async fn detach() -> Result<(), VeilidAPIError> { | ||||
|     /// Disconnect from the network. | ||||
|     pub async fn detach() -> APIResult<()> { | ||||
|         let veilid_api = get_veilid_api()?; | ||||
|         veilid_api.detach().await?; | ||||
|         Ok(()) | ||||
|         APIRESULT_UNDEFINED | ||||
|     } | ||||
|  | ||||
|     pub async fn debug(command: String) -> Result<String, VeilidAPIError> { | ||||
|     /// Execute an 'internal debug command'. | ||||
|     pub async fn debug(command: String) -> APIResult<String> { | ||||
|         let veilid_api = get_veilid_api()?; | ||||
|         let out = veilid_api.debug(command).await?; | ||||
|         APIResult::Ok(out) | ||||
|     } | ||||
|  | ||||
|     /// Return the cargo package version of veilid-core, in object format. | ||||
|     pub fn version() -> VeilidVersion { | ||||
|         let (major, minor, patch) = veilid_core::veilid_version(); | ||||
|         let vv = super::VeilidVersion { | ||||
| @@ -149,4 +161,9 @@ impl VeilidClient { | ||||
|         }; | ||||
|         vv | ||||
|     } | ||||
|  | ||||
|     /// Return the cargo package version of veilid-core, in string format. | ||||
|     pub fn versionString() -> String { | ||||
|         veilid_core::veilid_version_string() | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -7,43 +7,45 @@ extern "C" { | ||||
|     pub type ValidCryptoKinds; | ||||
| } | ||||
|  | ||||
| #[wasm_bindgen(js_class = veilidCrypto)] | ||||
| #[wasm_bindgen(js_name = veilidCrypto)] | ||||
| pub struct VeilidCrypto {} | ||||
|  | ||||
| // Since this implementation doesn't contain a `new` fn that's marked as a constructor, | ||||
| // and none of the member fns take a &self arg, | ||||
| // this is just a namespace/class of static functions. | ||||
| #[wasm_bindgen(js_class = veilidCrypto)] | ||||
| impl VeilidCrypto { | ||||
|     pub fn validCryptoKinds() -> ValidCryptoKinds { | ||||
|     pub fn validCryptoKinds() -> StringArray { | ||||
|         let res = veilid_core::VALID_CRYPTO_KINDS | ||||
|             .iter() | ||||
|             .map(|k| (*k).to_string()); | ||||
|         res.map(JsValue::from) | ||||
|             .collect::<js_sys::Array>() | ||||
|             .unchecked_into::<ValidCryptoKinds>() | ||||
|             .map(|k| (*k).to_string()) | ||||
|             .collect(); | ||||
|         into_unchecked_string_array(res) | ||||
|     } | ||||
|  | ||||
|     pub fn bestCryptoKind() -> String { | ||||
|         veilid_core::best_crypto_kind().to_string() | ||||
|     } | ||||
|  | ||||
|     pub fn cachedDh(kind: String, key: String, secret: String) -> VeilidAPIResult<String> { | ||||
|     pub fn cachedDh(kind: String, key: String, secret: String) -> APIResult<String> { | ||||
|         let kind: veilid_core::CryptoKind = veilid_core::FourCC::from_str(&kind)?; | ||||
|         let key: veilid_core::PublicKey = veilid_core::PublicKey::from_str(&key)?; | ||||
|         let secret: veilid_core::SecretKey = veilid_core::SecretKey::from_str(&secret)?; | ||||
|  | ||||
|         let veilid_api = get_veilid_api()?; | ||||
|         let crypto = veilid_api.crypto()?; | ||||
|         let csv = crypto.get(kind).ok_or_else(|| { | ||||
|         let crypto_system = crypto.get(kind).ok_or_else(|| { | ||||
|             veilid_core::VeilidAPIError::invalid_argument( | ||||
|                 "crypto_cached_dh", | ||||
|                 "kind", | ||||
|                 kind.to_string(), | ||||
|             ) | ||||
|         })?; | ||||
|         let out = csv.cached_dh(&key, &secret)?; | ||||
|         let out = crypto_system.cached_dh(&key, &secret)?; | ||||
|         APIResult::Ok(out.to_string()) | ||||
|     } | ||||
|  | ||||
|     pub fn computeDh(kind: String, key: String, secret: String) -> VeilidAPIResult<String> { | ||||
|     pub fn computeDh(kind: String, key: String, secret: String) -> APIResult<String> { | ||||
|         let kind: veilid_core::CryptoKind = veilid_core::FourCC::from_str(&kind)?; | ||||
|  | ||||
|         let key: veilid_core::PublicKey = veilid_core::PublicKey::from_str(&key)?; | ||||
| @@ -51,51 +53,51 @@ impl VeilidCrypto { | ||||
|  | ||||
|         let veilid_api = get_veilid_api()?; | ||||
|         let crypto = veilid_api.crypto()?; | ||||
|         let csv = crypto.get(kind).ok_or_else(|| { | ||||
|         let crypto_system = crypto.get(kind).ok_or_else(|| { | ||||
|             veilid_core::VeilidAPIError::invalid_argument( | ||||
|                 "crypto_compute_dh", | ||||
|                 "kind", | ||||
|                 kind.to_string(), | ||||
|             ) | ||||
|         })?; | ||||
|         let out = csv.compute_dh(&key, &secret)?; | ||||
|         let out = crypto_system.compute_dh(&key, &secret)?; | ||||
|         APIResult::Ok(out.to_string()) | ||||
|     } | ||||
|  | ||||
|     pub fn randomBytes(kind: String, len: u32) -> VeilidAPIResult<String> { | ||||
|     pub fn randomBytes(kind: String, len: u32) -> APIResult<String> { | ||||
|         let kind: veilid_core::CryptoKind = veilid_core::FourCC::from_str(&kind)?; | ||||
|  | ||||
|         let veilid_api = get_veilid_api()?; | ||||
|         let crypto = veilid_api.crypto()?; | ||||
|         let csv = crypto.get(kind).ok_or_else(|| { | ||||
|         let crypto_system = crypto.get(kind).ok_or_else(|| { | ||||
|             veilid_core::VeilidAPIError::invalid_argument( | ||||
|                 "crypto_random_bytes", | ||||
|                 "kind", | ||||
|                 kind.to_string(), | ||||
|             ) | ||||
|         })?; | ||||
|         let out = csv.random_bytes(len); | ||||
|         let out = crypto_system.random_bytes(len); | ||||
|         let out = data_encoding::BASE64URL_NOPAD.encode(&out); | ||||
|         APIResult::Ok(out) | ||||
|     } | ||||
|  | ||||
|     pub fn defaultSaltLength(kind: String) -> VeilidAPIResult<u32> { | ||||
|     pub fn defaultSaltLength(kind: String) -> APIResult<u32> { | ||||
|         let kind: veilid_core::CryptoKind = veilid_core::FourCC::from_str(&kind)?; | ||||
|  | ||||
|         let veilid_api = get_veilid_api()?; | ||||
|         let crypto = veilid_api.crypto()?; | ||||
|         let csv = crypto.get(kind).ok_or_else(|| { | ||||
|         let crypto_system = crypto.get(kind).ok_or_else(|| { | ||||
|             veilid_core::VeilidAPIError::invalid_argument( | ||||
|                 "crypto_default_salt_length", | ||||
|                 "kind", | ||||
|                 kind.to_string(), | ||||
|             ) | ||||
|         })?; | ||||
|         let out = csv.default_salt_length(); | ||||
|         let out = crypto_system.default_salt_length(); | ||||
|         APIResult::Ok(out) | ||||
|     } | ||||
|  | ||||
|     pub fn hashPassword(kind: String, password: String, salt: String) -> VeilidAPIResult<String> { | ||||
|     pub fn hashPassword(kind: String, password: String, salt: String) -> APIResult<String> { | ||||
|         let kind: veilid_core::CryptoKind = veilid_core::FourCC::from_str(&kind)?; | ||||
|         let password: Vec<u8> = data_encoding::BASE64URL_NOPAD | ||||
|             .decode(password.as_bytes()) | ||||
| @@ -106,14 +108,14 @@ impl VeilidCrypto { | ||||
|  | ||||
|         let veilid_api = get_veilid_api()?; | ||||
|         let crypto = veilid_api.crypto()?; | ||||
|         let csv = crypto.get(kind).ok_or_else(|| { | ||||
|         let crypto_system = crypto.get(kind).ok_or_else(|| { | ||||
|             veilid_core::VeilidAPIError::invalid_argument( | ||||
|                 "crypto_hash_password", | ||||
|                 "kind", | ||||
|                 kind.to_string(), | ||||
|             ) | ||||
|         })?; | ||||
|         let out = csv.hash_password(&password, &salt)?; | ||||
|         let out = crypto_system.hash_password(&password, &salt)?; | ||||
|         APIResult::Ok(out) | ||||
|     } | ||||
|  | ||||
| @@ -121,7 +123,7 @@ impl VeilidCrypto { | ||||
|         kind: String, | ||||
|         password: String, | ||||
|         password_hash: String, | ||||
|     ) -> VeilidAPIResult<bool> { | ||||
|     ) -> APIResult<bool> { | ||||
|         let kind: veilid_core::CryptoKind = veilid_core::FourCC::from_str(&kind)?; | ||||
|         let password: Vec<u8> = data_encoding::BASE64URL_NOPAD | ||||
|             .decode(password.as_bytes()) | ||||
| @@ -129,22 +131,18 @@ impl VeilidCrypto { | ||||
|  | ||||
|         let veilid_api = get_veilid_api()?; | ||||
|         let crypto = veilid_api.crypto()?; | ||||
|         let csv = crypto.get(kind).ok_or_else(|| { | ||||
|         let crypto_system = crypto.get(kind).ok_or_else(|| { | ||||
|             veilid_core::VeilidAPIError::invalid_argument( | ||||
|                 "crypto_verify_password", | ||||
|                 "kind", | ||||
|                 kind.to_string(), | ||||
|             ) | ||||
|         })?; | ||||
|         let out = csv.verify_password(&password, &password_hash)?; | ||||
|         let out = crypto_system.verify_password(&password, &password_hash)?; | ||||
|         APIResult::Ok(out) | ||||
|     } | ||||
|  | ||||
|     pub fn deriveSharedSecret( | ||||
|         kind: String, | ||||
|         password: String, | ||||
|         salt: String, | ||||
|     ) -> VeilidAPIResult<String> { | ||||
|     pub fn deriveSharedSecret(kind: String, password: String, salt: String) -> APIResult<String> { | ||||
|         let kind: veilid_core::CryptoKind = veilid_core::FourCC::from_str(&kind)?; | ||||
|         let password: Vec<u8> = data_encoding::BASE64URL_NOPAD | ||||
|             .decode(password.as_bytes()) | ||||
| @@ -155,66 +153,66 @@ impl VeilidCrypto { | ||||
|  | ||||
|         let veilid_api = get_veilid_api()?; | ||||
|         let crypto = veilid_api.crypto()?; | ||||
|         let csv = crypto.get(kind).ok_or_else(|| { | ||||
|         let crypto_system = crypto.get(kind).ok_or_else(|| { | ||||
|             veilid_core::VeilidAPIError::invalid_argument( | ||||
|                 "crypto_derive_shared_secret", | ||||
|                 "kind", | ||||
|                 kind.to_string(), | ||||
|             ) | ||||
|         })?; | ||||
|         let out = csv.derive_shared_secret(&password, &salt)?; | ||||
|         let out = crypto_system.derive_shared_secret(&password, &salt)?; | ||||
|         APIResult::Ok(out.to_string()) | ||||
|     } | ||||
|  | ||||
|     pub fn randomNonce(kind: String) -> VeilidAPIResult<String> { | ||||
|     pub fn randomNonce(kind: String) -> APIResult<String> { | ||||
|         let kind: veilid_core::CryptoKind = veilid_core::FourCC::from_str(&kind)?; | ||||
|  | ||||
|         let veilid_api = get_veilid_api()?; | ||||
|         let crypto = veilid_api.crypto()?; | ||||
|         let csv = crypto.get(kind).ok_or_else(|| { | ||||
|         let crypto_system = crypto.get(kind).ok_or_else(|| { | ||||
|             veilid_core::VeilidAPIError::invalid_argument( | ||||
|                 "crypto_random_nonce", | ||||
|                 "kind", | ||||
|                 kind.to_string(), | ||||
|             ) | ||||
|         })?; | ||||
|         let out = csv.random_nonce(); | ||||
|         let out = crypto_system.random_nonce(); | ||||
|         APIResult::Ok(out.to_string()) | ||||
|     } | ||||
|  | ||||
|     pub fn randomSharedSecret(kind: String) -> VeilidAPIResult<String> { | ||||
|     pub fn randomSharedSecret(kind: String) -> APIResult<String> { | ||||
|         let kind: veilid_core::CryptoKind = veilid_core::FourCC::from_str(&kind)?; | ||||
|  | ||||
|         let veilid_api = get_veilid_api()?; | ||||
|         let crypto = veilid_api.crypto()?; | ||||
|         let csv = crypto.get(kind).ok_or_else(|| { | ||||
|         let crypto_system = crypto.get(kind).ok_or_else(|| { | ||||
|             veilid_core::VeilidAPIError::invalid_argument( | ||||
|                 "crypto_random_shared_secret", | ||||
|                 "kind", | ||||
|                 kind.to_string(), | ||||
|             ) | ||||
|         })?; | ||||
|         let out = csv.random_shared_secret(); | ||||
|         let out = crypto_system.random_shared_secret(); | ||||
|         APIResult::Ok(out.to_string()) | ||||
|     } | ||||
|  | ||||
|     pub fn generateKeyPair(kind: String) -> VeilidAPIResult<KeyPair> { | ||||
|     pub fn generateKeyPair(kind: String) -> APIResult<KeyPair> { | ||||
|         let kind: veilid_core::CryptoKind = veilid_core::FourCC::from_str(&kind)?; | ||||
|  | ||||
|         let veilid_api = get_veilid_api()?; | ||||
|         let crypto = veilid_api.crypto()?; | ||||
|         let csv = crypto.get(kind).ok_or_else(|| { | ||||
|         let crypto_system = crypto.get(kind).ok_or_else(|| { | ||||
|             veilid_core::VeilidAPIError::invalid_argument( | ||||
|                 "crypto_generate_key_pair", | ||||
|                 "kind", | ||||
|                 kind.to_string(), | ||||
|             ) | ||||
|         })?; | ||||
|         let out = csv.generate_keypair(); | ||||
|         let out = crypto_system.generate_keypair(); | ||||
|         APIResult::Ok(out) | ||||
|     } | ||||
|  | ||||
|     pub fn generateHash(kind: String, data: String) -> VeilidAPIResult<String> { | ||||
|     pub fn generateHash(kind: String, data: String) -> APIResult<String> { | ||||
|         let kind: veilid_core::CryptoKind = veilid_core::FourCC::from_str(&kind)?; | ||||
|  | ||||
|         let data: Vec<u8> = data_encoding::BASE64URL_NOPAD | ||||
| @@ -223,18 +221,18 @@ impl VeilidCrypto { | ||||
|  | ||||
|         let veilid_api = get_veilid_api()?; | ||||
|         let crypto = veilid_api.crypto()?; | ||||
|         let csv = crypto.get(kind).ok_or_else(|| { | ||||
|         let crypto_system = crypto.get(kind).ok_or_else(|| { | ||||
|             veilid_core::VeilidAPIError::invalid_argument( | ||||
|                 "crypto_generate_hash", | ||||
|                 "kind", | ||||
|                 kind.to_string(), | ||||
|             ) | ||||
|         })?; | ||||
|         let out = csv.generate_hash(&data); | ||||
|         let out = crypto_system.generate_hash(&data); | ||||
|         APIResult::Ok(out.to_string()) | ||||
|     } | ||||
|  | ||||
|     pub fn validateKeyPair(kind: String, key: String, secret: String) -> VeilidAPIResult<bool> { | ||||
|     pub fn validateKeyPair(kind: String, key: String, secret: String) -> APIResult<bool> { | ||||
|         let kind: veilid_core::CryptoKind = veilid_core::FourCC::from_str(&kind)?; | ||||
|  | ||||
|         let key: veilid_core::PublicKey = veilid_core::PublicKey::from_str(&key)?; | ||||
| @@ -242,18 +240,18 @@ impl VeilidCrypto { | ||||
|  | ||||
|         let veilid_api = get_veilid_api()?; | ||||
|         let crypto = veilid_api.crypto()?; | ||||
|         let csv = crypto.get(kind).ok_or_else(|| { | ||||
|         let crypto_system = crypto.get(kind).ok_or_else(|| { | ||||
|             veilid_core::VeilidAPIError::invalid_argument( | ||||
|                 "crypto_validate_key_pair", | ||||
|                 "kind", | ||||
|                 kind.to_string(), | ||||
|             ) | ||||
|         })?; | ||||
|         let out = csv.validate_keypair(&key, &secret); | ||||
|         let out = crypto_system.validate_keypair(&key, &secret); | ||||
|         APIResult::Ok(out) | ||||
|     } | ||||
|  | ||||
|     pub fn validateHash(kind: String, data: String, hash: String) -> VeilidAPIResult<bool> { | ||||
|     pub fn validateHash(kind: String, data: String, hash: String) -> APIResult<bool> { | ||||
|         let kind: veilid_core::CryptoKind = veilid_core::FourCC::from_str(&kind)?; | ||||
|  | ||||
|         let data: Vec<u8> = data_encoding::BASE64URL_NOPAD | ||||
| @@ -264,18 +262,18 @@ impl VeilidCrypto { | ||||
|  | ||||
|         let veilid_api = get_veilid_api()?; | ||||
|         let crypto = veilid_api.crypto()?; | ||||
|         let csv = crypto.get(kind).ok_or_else(|| { | ||||
|         let crypto_system = crypto.get(kind).ok_or_else(|| { | ||||
|             veilid_core::VeilidAPIError::invalid_argument( | ||||
|                 "crypto_validate_hash", | ||||
|                 "kind", | ||||
|                 kind.to_string(), | ||||
|             ) | ||||
|         })?; | ||||
|         let out = csv.validate_hash(&data, &hash); | ||||
|         let out = crypto_system.validate_hash(&data, &hash); | ||||
|         APIResult::Ok(out) | ||||
|     } | ||||
|  | ||||
|     pub fn distance(kind: String, key1: String, key2: String) -> VeilidAPIResult<String> { | ||||
|     pub fn distance(kind: String, key1: String, key2: String) -> APIResult<String> { | ||||
|         let kind: veilid_core::CryptoKind = veilid_core::FourCC::from_str(&kind)?; | ||||
|  | ||||
|         let key1: veilid_core::CryptoKey = veilid_core::CryptoKey::from_str(&key1)?; | ||||
| @@ -283,23 +281,18 @@ impl VeilidCrypto { | ||||
|  | ||||
|         let veilid_api = get_veilid_api()?; | ||||
|         let crypto = veilid_api.crypto()?; | ||||
|         let csv = crypto.get(kind).ok_or_else(|| { | ||||
|         let crypto_system = crypto.get(kind).ok_or_else(|| { | ||||
|             veilid_core::VeilidAPIError::invalid_argument( | ||||
|                 "crypto_distance", | ||||
|                 "kind", | ||||
|                 kind.to_string(), | ||||
|             ) | ||||
|         })?; | ||||
|         let out = csv.distance(&key1, &key2); | ||||
|         let out = crypto_system.distance(&key1, &key2); | ||||
|         APIResult::Ok(out.to_string()) | ||||
|     } | ||||
|  | ||||
|     pub fn sign( | ||||
|         kind: String, | ||||
|         key: String, | ||||
|         secret: String, | ||||
|         data: String, | ||||
|     ) -> VeilidAPIResult<String> { | ||||
|     pub fn sign(kind: String, key: String, secret: String, data: String) -> APIResult<String> { | ||||
|         let kind: veilid_core::CryptoKind = veilid_core::FourCC::from_str(&kind)?; | ||||
|  | ||||
|         let key: veilid_core::PublicKey = veilid_core::PublicKey::from_str(&key)?; | ||||
| @@ -311,19 +304,14 @@ impl VeilidCrypto { | ||||
|  | ||||
|         let veilid_api = get_veilid_api()?; | ||||
|         let crypto = veilid_api.crypto()?; | ||||
|         let csv = crypto.get(kind).ok_or_else(|| { | ||||
|         let crypto_system = crypto.get(kind).ok_or_else(|| { | ||||
|             veilid_core::VeilidAPIError::invalid_argument("crypto_sign", "kind", kind.to_string()) | ||||
|         })?; | ||||
|         let out = csv.sign(&key, &secret, &data)?; | ||||
|         let out = crypto_system.sign(&key, &secret, &data)?; | ||||
|         APIResult::Ok(out.to_string()) | ||||
|     } | ||||
|  | ||||
|     pub fn verify( | ||||
|         kind: String, | ||||
|         key: String, | ||||
|         data: String, | ||||
|         signature: String, | ||||
|     ) -> VeilidAPIResult<()> { | ||||
|     pub fn verify(kind: String, key: String, data: String, signature: String) -> APIResult<()> { | ||||
|         let kind: veilid_core::CryptoKind = veilid_core::FourCC::from_str(&kind)?; | ||||
|  | ||||
|         let key: veilid_core::PublicKey = veilid_core::PublicKey::from_str(&key)?; | ||||
| @@ -334,26 +322,26 @@ impl VeilidCrypto { | ||||
|  | ||||
|         let veilid_api = get_veilid_api()?; | ||||
|         let crypto = veilid_api.crypto()?; | ||||
|         let csv = crypto.get(kind).ok_or_else(|| { | ||||
|         let crypto_system = crypto.get(kind).ok_or_else(|| { | ||||
|             veilid_core::VeilidAPIError::invalid_argument("crypto_verify", "kind", kind.to_string()) | ||||
|         })?; | ||||
|         csv.verify(&key, &data, &signature)?; | ||||
|         crypto_system.verify(&key, &data, &signature)?; | ||||
|         APIRESULT_UNDEFINED | ||||
|     } | ||||
|  | ||||
|     pub fn aeadOverhead(kind: String) -> VeilidAPIResult<usize> { | ||||
|     pub fn aeadOverhead(kind: String) -> APIResult<usize> { | ||||
|         let kind: veilid_core::CryptoKind = veilid_core::FourCC::from_str(&kind)?; | ||||
|  | ||||
|         let veilid_api = get_veilid_api()?; | ||||
|         let crypto = veilid_api.crypto()?; | ||||
|         let csv = crypto.get(kind).ok_or_else(|| { | ||||
|         let crypto_system = crypto.get(kind).ok_or_else(|| { | ||||
|             veilid_core::VeilidAPIError::invalid_argument( | ||||
|                 "crypto_aead_overhead", | ||||
|                 "kind", | ||||
|                 kind.to_string(), | ||||
|             ) | ||||
|         })?; | ||||
|         let out = csv.aead_overhead(); | ||||
|         let out = crypto_system.aead_overhead(); | ||||
|         APIResult::Ok(out) | ||||
|     } | ||||
|  | ||||
| @@ -363,7 +351,7 @@ impl VeilidCrypto { | ||||
|         nonce: String, | ||||
|         shared_secret: String, | ||||
|         associated_data: Option<String>, | ||||
|     ) -> VeilidAPIResult<String> { | ||||
|     ) -> APIResult<String> { | ||||
|         let kind: veilid_core::CryptoKind = veilid_core::FourCC::from_str(&kind)?; | ||||
|  | ||||
|         let body: Vec<u8> = data_encoding::BASE64URL_NOPAD | ||||
| @@ -383,14 +371,14 @@ impl VeilidCrypto { | ||||
|  | ||||
|         let veilid_api = get_veilid_api()?; | ||||
|         let crypto = veilid_api.crypto()?; | ||||
|         let csv = crypto.get(kind).ok_or_else(|| { | ||||
|         let crypto_system = crypto.get(kind).ok_or_else(|| { | ||||
|             veilid_core::VeilidAPIError::invalid_argument( | ||||
|                 "crypto_decrypt_aead", | ||||
|                 "kind", | ||||
|                 kind.to_string(), | ||||
|             ) | ||||
|         })?; | ||||
|         let out = csv.decrypt_aead( | ||||
|         let out = crypto_system.decrypt_aead( | ||||
|             &body, | ||||
|             &nonce, | ||||
|             &shared_secret, | ||||
| @@ -409,7 +397,7 @@ impl VeilidCrypto { | ||||
|         nonce: String, | ||||
|         shared_secret: String, | ||||
|         associated_data: Option<String>, | ||||
|     ) -> VeilidAPIResult<String> { | ||||
|     ) -> APIResult<String> { | ||||
|         let kind: veilid_core::CryptoKind = veilid_core::FourCC::from_str(&kind)?; | ||||
|  | ||||
|         let body: Vec<u8> = data_encoding::BASE64URL_NOPAD | ||||
| @@ -429,14 +417,14 @@ impl VeilidCrypto { | ||||
|  | ||||
|         let veilid_api = get_veilid_api()?; | ||||
|         let crypto = veilid_api.crypto()?; | ||||
|         let csv = crypto.get(kind).ok_or_else(|| { | ||||
|         let crypto_system = crypto.get(kind).ok_or_else(|| { | ||||
|             veilid_core::VeilidAPIError::invalid_argument( | ||||
|                 "crypto_encrypt_aead", | ||||
|                 "kind", | ||||
|                 kind.to_string(), | ||||
|             ) | ||||
|         })?; | ||||
|         let out = csv.encrypt_aead( | ||||
|         let out = crypto_system.encrypt_aead( | ||||
|             &body, | ||||
|             &nonce, | ||||
|             &shared_secret, | ||||
| @@ -454,7 +442,7 @@ impl VeilidCrypto { | ||||
|         body: String, | ||||
|         nonce: String, | ||||
|         shared_secret: String, | ||||
|     ) -> VeilidAPIResult<String> { | ||||
|     ) -> APIResult<String> { | ||||
|         let kind: veilid_core::CryptoKind = veilid_core::FourCC::from_str(&kind)?; | ||||
|  | ||||
|         let mut body: Vec<u8> = data_encoding::BASE64URL_NOPAD | ||||
| @@ -468,14 +456,14 @@ impl VeilidCrypto { | ||||
|  | ||||
|         let veilid_api = get_veilid_api()?; | ||||
|         let crypto = veilid_api.crypto()?; | ||||
|         let csv = crypto.get(kind).ok_or_else(|| { | ||||
|         let crypto_system = crypto.get(kind).ok_or_else(|| { | ||||
|             veilid_core::VeilidAPIError::invalid_argument( | ||||
|                 "crypto_crypt_no_auth", | ||||
|                 "kind", | ||||
|                 kind.to_string(), | ||||
|             ) | ||||
|         })?; | ||||
|         csv.crypt_in_place_no_auth(&mut body, &nonce, &shared_secret); | ||||
|         crypto_system.crypt_in_place_no_auth(&mut body, &nonce, &shared_secret); | ||||
|         let out = data_encoding::BASE64URL_NOPAD.encode(&body); | ||||
|         APIResult::Ok(out) | ||||
|     } | ||||
|   | ||||
| @@ -8,46 +8,71 @@ pub struct VeilidRoutingContext { | ||||
|  | ||||
| #[wasm_bindgen()] | ||||
| impl VeilidRoutingContext { | ||||
|     /// Don't use this constructor directly. | ||||
|     /// Use one of the `VeilidRoutingContext.create___()` factory methods instead. | ||||
|     /// @deprecated | ||||
|     #[wasm_bindgen(constructor)] | ||||
|     pub fn new(id: u32) -> Self { | ||||
|         Self { id } | ||||
|     } | ||||
|  | ||||
|     // Factories | ||||
|     pub fn createWithoutPrivacy() -> VeilidAPIResult<VeilidRoutingContext> { | ||||
|     // -------------------------------- | ||||
|     // Constructor factories | ||||
|     // -------------------------------- | ||||
|  | ||||
|     /// Get a new RoutingContext object to use to send messages over the Veilid network. | ||||
|     pub fn createWithoutPrivacy() -> APIResult<VeilidRoutingContext> { | ||||
|         let veilid_api = get_veilid_api()?; | ||||
|         let routing_context = veilid_api.routing_context(); | ||||
|         let id = add_routing_context(routing_context); | ||||
|         Ok(VeilidRoutingContext { id }) | ||||
|     } | ||||
|  | ||||
|     pub async fn createWithPrivacy() -> VeilidAPIResult<VeilidRoutingContext> { | ||||
|     /// Turn on sender privacy, enabling the use of safety routes. | ||||
|     /// | ||||
|     /// Default values for hop count, stability and sequencing preferences are used. | ||||
|     /// | ||||
|     /// Hop count default is dependent on config, but is set to 1 extra hop. | ||||
|     /// Stability default is to choose 'low latency' routes, preferring them over long-term reliability. | ||||
|     /// Sequencing default is to have no preference for ordered vs unordered message delivery | ||||
|     /// To modify these defaults, use `VeilidRoutingContext.createWithCustomPrivacy()`. | ||||
|     pub fn createWithPrivacy() -> APIResult<VeilidRoutingContext> { | ||||
|         let veilid_api = get_veilid_api()?; | ||||
|         let routing_context = veilid_api.routing_context().with_privacy()?; | ||||
|         let id = add_routing_context(routing_context); | ||||
|         Ok(VeilidRoutingContext { id }) | ||||
|     } | ||||
|  | ||||
|     pub async fn createWithCustomPrivacy( | ||||
|         safetySelection: SafetySelection, | ||||
|     ) -> VeilidAPIResult<VeilidRoutingContext> { | ||||
|     /// Turn on privacy using a custom `SafetySelection` | ||||
|     pub fn createWithCustomPrivacy( | ||||
|         safety_selection: SafetySelection, | ||||
|     ) -> APIResult<VeilidRoutingContext> { | ||||
|         let veilid_api = get_veilid_api()?; | ||||
|         let routing_context = veilid_api | ||||
|             .routing_context() | ||||
|             .with_custom_privacy(safetySelection)?; | ||||
|             .with_custom_privacy(safety_selection)?; | ||||
|         let id = add_routing_context(routing_context); | ||||
|         Ok(VeilidRoutingContext { id }) | ||||
|     } | ||||
|  | ||||
|     pub fn createWithSequencing(sequencing: Sequencing) -> VeilidAPIResult<VeilidRoutingContext> { | ||||
|     /// Use a specified `Sequencing` preference, with or without privacy. | ||||
|     pub fn createWithSequencing(sequencing: Sequencing) -> APIResult<VeilidRoutingContext> { | ||||
|         let veilid_api = get_veilid_api()?; | ||||
|         let routing_context = veilid_api.routing_context().with_sequencing(sequencing); | ||||
|         let id = add_routing_context(routing_context); | ||||
|         Ok(VeilidRoutingContext { id }) | ||||
|     } | ||||
|  | ||||
|     // -------------------------------- | ||||
|     // Static methods | ||||
|     pub async fn newPrivateRoute() -> VeilidAPIResult<VeilidRouteBlob> { | ||||
|     // -------------------------------- | ||||
|  | ||||
|     /// Allocate a new private route set with default cryptography and network options. | ||||
|     /// Returns a route id and a publishable 'blob' with the route encrypted with each crypto kind. | ||||
|     /// Those nodes importing the blob will have their choice of which crypto kind to use. | ||||
|     /// | ||||
|     /// Returns a route id and 'blob' that can be published over some means (DHT or otherwise) to be imported by another Veilid node. | ||||
|     pub async fn newPrivateRoute() -> APIResult<VeilidRouteBlob> { | ||||
|         let veilid_api = get_veilid_api()?; | ||||
|  | ||||
|         let (route_id, blob) = veilid_api.new_private_route().await?; | ||||
| @@ -56,10 +81,15 @@ impl VeilidRoutingContext { | ||||
|         APIResult::Ok(route_blob) | ||||
|     } | ||||
|  | ||||
|     /// Allocate a new private route and specify a specific cryptosystem, stability and sequencing preference. | ||||
|     /// Returns a route id and a publishable 'blob' with the route encrypted with each crypto kind. | ||||
|     /// Those nodes importing the blob will have their choice of which crypto kind to use. | ||||
|     /// | ||||
|     /// Returns a route id and 'blob' that can be published over some means (DHT or otherwise) to be imported by another Veilid node. | ||||
|     pub async fn newCustomPrivateRoute( | ||||
|         stability: Stability, | ||||
|         sequencing: Sequencing, | ||||
|     ) -> VeilidAPIResult<VeilidRouteBlob> { | ||||
|     ) -> APIResult<VeilidRouteBlob> { | ||||
|         let veilid_api = get_veilid_api()?; | ||||
|  | ||||
|         let (route_id, blob) = veilid_api | ||||
| @@ -70,19 +100,26 @@ impl VeilidRoutingContext { | ||||
|         APIResult::Ok(route_blob) | ||||
|     } | ||||
|  | ||||
|     pub async fn releasePrivateRoute(routeId: String) -> VeilidAPIResult<()> { | ||||
|         let route_id: veilid_core::RouteId = veilid_core::deserialize_json(&routeId).unwrap(); | ||||
|     /// Release either a locally allocated or remotely imported private route. | ||||
|     /// | ||||
|     /// This will deactivate the route and free its resources and it can no longer be sent to or received from. | ||||
|     pub fn releasePrivateRoute(route_id: String) -> APIResult<()> { | ||||
|         let route_id: veilid_core::RouteId = veilid_core::deserialize_json(&route_id).unwrap(); | ||||
|         let veilid_api = get_veilid_api()?; | ||||
|         veilid_api.release_private_route(route_id)?; | ||||
|         APIRESULT_UNDEFINED | ||||
|     } | ||||
|  | ||||
|     pub async fn appCallReply(callId: String, message: String) -> VeilidAPIResult<()> { | ||||
|         let call_id = match callId.parse() { | ||||
|     /// Respond to an AppCall received over a VeilidUpdate::AppCall. | ||||
|     /// | ||||
|     /// * `call_id` - specifies which call to reply to, and it comes from a VeilidUpdate::AppCall, specifically the VeilidAppCall::id() value. | ||||
|     /// * `message` - is an answer blob to be returned by the remote node's RoutingContext::app_call() function, and may be up to 32768 bytes | ||||
|     pub async fn appCallReply(call_id: String, message: String) -> APIResult<()> { | ||||
|         let call_id = match call_id.parse() { | ||||
|             Ok(v) => v, | ||||
|             Err(e) => { | ||||
|                 return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument( | ||||
|                     e, "call_id", callId, | ||||
|                     e, "call_id", call_id, | ||||
|                 )) | ||||
|             } | ||||
|         }; | ||||
| @@ -93,15 +130,26 @@ impl VeilidRoutingContext { | ||||
|         APIRESULT_UNDEFINED | ||||
|     } | ||||
|  | ||||
|     // -------------------------------- | ||||
|     // Instance methods | ||||
|     pub async fn appMessage(&self, target_string: String, message: String) -> VeilidAPIResult<()> { | ||||
|         let routing_context = { | ||||
|     // -------------------------------- | ||||
|     fn getRoutingContext(&self) -> APIResult<RoutingContext> { | ||||
|         let rc = (*ROUTING_CONTEXTS).borrow(); | ||||
|         let Some(routing_context) = rc.get(&self.id) else { | ||||
|                 return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument("routing_context_app_message", "id", self.id)); | ||||
|             }; | ||||
|             routing_context.clone() | ||||
|             return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument("getRoutingContext", "id", self.id)); | ||||
|         }; | ||||
|         APIResult::Ok(routing_context.clone()) | ||||
|     } | ||||
|  | ||||
|     /// App-level unidirectional message that does not expect any value to be returned. | ||||
|     /// | ||||
|     /// Veilid apps may use this for arbitrary message passing. | ||||
|     /// | ||||
|     /// @param {string} target - can be either a direct node id or a private route. | ||||
|     /// @param {string} message - an arbitrary message blob of up to `32768` bytes. | ||||
|     #[wasm_bindgen(skip_jsdoc)] | ||||
|     pub async fn appMessage(&self, target_string: String, message: String) -> APIResult<()> { | ||||
|         let routing_context = self.getRoutingContext()?; | ||||
|  | ||||
|         let veilid_api = get_veilid_api()?; | ||||
|         let target = veilid_api.parse_as_target(target_string).await?; | ||||
| @@ -111,42 +159,43 @@ impl VeilidRoutingContext { | ||||
|         APIRESULT_UNDEFINED | ||||
|     } | ||||
|  | ||||
|     pub async fn appCall(&self, target_string: String, request: String) -> VeilidAPIResult<String> { | ||||
|         let routing_context = { | ||||
|             let rc = (*ROUTING_CONTEXTS).borrow(); | ||||
|             let Some(routing_context) = rc.get(&self.id) else { | ||||
|                 return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument("routing_context_app_call", "id", self.id)); | ||||
|             }; | ||||
|             routing_context.clone() | ||||
|         }; | ||||
|     /// App-level bidirectional call that expects a response to be returned. | ||||
|     /// | ||||
|     /// Veilid apps may use this for arbitrary message passing. | ||||
|     /// | ||||
|     /// @param {string} target_string - can be either a direct node id or a private route, base64Url encoded. | ||||
|     /// @param {string} message - an arbitrary message blob of up to `32768` bytes, base64Url encoded. | ||||
|     /// @returns an answer blob of up to `32768` bytes, base64Url encoded. | ||||
|     #[wasm_bindgen(skip_jsdoc)] | ||||
|     pub async fn appCall(&self, target_string: String, request: String) -> APIResult<String> { | ||||
|         let request: Vec<u8> = data_encoding::BASE64URL_NOPAD | ||||
|             .decode(&request.as_bytes()) | ||||
|             .unwrap(); | ||||
|         let routing_context = self.getRoutingContext()?; | ||||
|  | ||||
|         let veilid_api = get_veilid_api()?; | ||||
|         let target = veilid_api.parse_as_target(target_string).await?; | ||||
|         let answer = routing_context | ||||
|             .app_call(target, request.into_bytes()) | ||||
|             .await?; | ||||
|         // let answer = data_encoding::BASE64URL_NOPAD.encode(&answer); | ||||
|         let answer = String::from_utf8_lossy(&answer).into_owned(); | ||||
|         let answer = routing_context.app_call(target, request).await?; | ||||
|         let answer = data_encoding::BASE64URL_NOPAD.encode(&answer); | ||||
|         APIResult::Ok(answer) | ||||
|     } | ||||
|  | ||||
|     /// DHT Records Creates a new DHT record a specified crypto kind and schema | ||||
|     /// | ||||
|     /// The record is considered 'open' after the create operation succeeds. | ||||
|     /// | ||||
|     /// @returns the newly allocated DHT record's key if successful. | ||||
|     pub async fn createDhtRecord( | ||||
|         &self, | ||||
|         schema: DHTSchema, | ||||
|         kind: String, | ||||
|     ) -> VeilidAPIResult<DHTRecordDescriptor> { | ||||
|     ) -> APIResult<DHTRecordDescriptor> { | ||||
|         let crypto_kind = if kind.is_empty() { | ||||
|             None | ||||
|         } else { | ||||
|             Some(veilid_core::FourCC::from_str(&kind)?) | ||||
|         }; | ||||
|         let routing_context = { | ||||
|             let rc = (*ROUTING_CONTEXTS).borrow(); | ||||
|             let Some(routing_context) = rc.get(&self.id) else { | ||||
|                 return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument("routing_context_create_dht_record", "id", self.id)); | ||||
|             }; | ||||
|             routing_context.clone() | ||||
|         }; | ||||
|         let routing_context = self.getRoutingContext()?; | ||||
|  | ||||
|         let dht_record_descriptor = routing_context | ||||
|             .create_dht_record(schema, crypto_kind) | ||||
| @@ -154,94 +203,88 @@ impl VeilidRoutingContext { | ||||
|         APIResult::Ok(dht_record_descriptor) | ||||
|     } | ||||
|  | ||||
|     /// @param {string} writer - Stringified key pair in the form of `key:secret`. | ||||
|     /// Opens a DHT record at a specific key. | ||||
|     /// | ||||
|     /// Associates a secret if one is provided to provide writer capability. Records may only be opened or created. To re-open with a different routing context, first close the value. | ||||
|     /// | ||||
|     /// @returns the DHT record descriptor for the opened record if successful. | ||||
|     /// @param {string} writer - Stringified key pair, in the form of `key:secret` where `key` and `secret` are base64Url encoded. | ||||
|     /// @param {string} key - key of the DHT record. | ||||
|     #[wasm_bindgen(skip_jsdoc)] | ||||
|     pub async fn openDhtRecord( | ||||
|         &self, | ||||
|         key: String, | ||||
|         writer: Option<String>, | ||||
|     ) -> VeilidAPIResult<DHTRecordDescriptor> { | ||||
|     ) -> APIResult<DHTRecordDescriptor> { | ||||
|         let key = TypedKey::from_str(&key).unwrap(); | ||||
|         let writer = match writer { | ||||
|             Some(writer) => Some(KeyPair::from_str(&writer).unwrap()), | ||||
|             _ => None, | ||||
|         }; | ||||
|  | ||||
|         let routing_context = { | ||||
|             let rc = (*ROUTING_CONTEXTS).borrow(); | ||||
|             let Some(routing_context) = rc.get(&self.id) else { | ||||
|                 return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument("routing_context_open_dht_record", "id", self.id)); | ||||
|             }; | ||||
|             routing_context.clone() | ||||
|         }; | ||||
|         let routing_context = self.getRoutingContext()?; | ||||
|         let dht_record_descriptor = routing_context.open_dht_record(key, writer).await?; | ||||
|         APIResult::Ok(dht_record_descriptor) | ||||
|     } | ||||
|  | ||||
|     pub async fn closeDhtRecord(&self, key: String) -> VeilidAPIResult<()> { | ||||
|     /// Closes a DHT record at a specific key that was opened with create_dht_record or open_dht_record. | ||||
|     /// | ||||
|     /// Closing a record allows you to re-open it with a different routing context | ||||
|     pub async fn closeDhtRecord(&self, key: String) -> APIResult<()> { | ||||
|         let key = TypedKey::from_str(&key).unwrap(); | ||||
|         let routing_context = { | ||||
|             let rc = (*ROUTING_CONTEXTS).borrow(); | ||||
|             let Some(routing_context) = rc.get(&self.id) else { | ||||
|                 return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument("routing_context_close_dht_record", "id", self.id)); | ||||
|             }; | ||||
|             routing_context.clone() | ||||
|         }; | ||||
|         let routing_context = self.getRoutingContext()?; | ||||
|         routing_context.close_dht_record(key).await?; | ||||
|         APIRESULT_UNDEFINED | ||||
|     } | ||||
|  | ||||
|     pub async fn deleteDhtRecord(&self, key: String) -> VeilidAPIResult<()> { | ||||
|     /// Deletes a DHT record at a specific key | ||||
|     /// | ||||
|     /// If the record is opened, it must be closed before it is deleted. | ||||
|     /// Deleting a record does not delete it from the network, but will remove the storage of the record locally, | ||||
|     /// and will prevent its value from being refreshed on the network by this node. | ||||
|     pub async fn deleteDhtRecord(&self, key: String) -> APIResult<()> { | ||||
|         let key = TypedKey::from_str(&key).unwrap(); | ||||
|         let routing_context = { | ||||
|             let rc = (*ROUTING_CONTEXTS).borrow(); | ||||
|             let Some(routing_context) = rc.get(&self.id) else { | ||||
|                 return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument("routing_context_delete_dht_record", "id", self.id)); | ||||
|             }; | ||||
|             routing_context.clone() | ||||
|         }; | ||||
|         let routing_context = self.getRoutingContext()?; | ||||
|         routing_context.delete_dht_record(key).await?; | ||||
|         APIRESULT_UNDEFINED | ||||
|     } | ||||
|  | ||||
|     /// Gets the latest value of a subkey. | ||||
|     /// | ||||
|     /// May pull the latest value from the network, but by settings 'force_refresh' you can force a network data refresh. | ||||
|     /// | ||||
|     /// Returns `undefined` if the value subkey has not yet been set. | ||||
|     /// Returns base64Url encoded `data` if the value subkey has valid data. | ||||
|     pub async fn getDhtValue( | ||||
|         &self, | ||||
|         key: String, | ||||
|         subKey: u32, | ||||
|         forceRefresh: bool, | ||||
|     ) -> VeilidAPIResult<Option<ValueData>> { | ||||
|     ) -> APIResult<Option<ValueData>> { | ||||
|         let key = TypedKey::from_str(&key).unwrap(); | ||||
|         let routing_context = { | ||||
|             let rc = (*ROUTING_CONTEXTS).borrow(); | ||||
|             let Some(routing_context) = rc.get(&self.id) else { | ||||
|                     return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument("routing_context_get_dht_value", "id", self.id)); | ||||
|                 }; | ||||
|             routing_context.clone() | ||||
|         }; | ||||
|         let routing_context = self.getRoutingContext()?; | ||||
|         let res = routing_context | ||||
|             .get_dht_value(key, subKey, forceRefresh) | ||||
|             .await?; | ||||
|         APIResult::Ok(res) | ||||
|     } | ||||
|  | ||||
|     /// @param {string} data - Base64Url (no padding) encoded data string | ||||
|     /// Pushes a changed subkey value to the network | ||||
|     /// | ||||
|     /// Returns `undefined` if the value was successfully put. | ||||
|     /// Returns base64Url encoded `data` if the value put was older than the one available on the network. | ||||
|     pub async fn setDhtValue( | ||||
|         &self, | ||||
|         key: String, | ||||
|         subKey: u32, | ||||
|         data: String, | ||||
|     ) -> VeilidAPIResult<Option<ValueData>> { | ||||
|     ) -> APIResult<Option<ValueData>> { | ||||
|         let key = TypedKey::from_str(&key).unwrap(); | ||||
|         let data: Vec<u8> = data_encoding::BASE64URL_NOPAD | ||||
|             .decode(&data.as_bytes()) | ||||
|             .unwrap(); | ||||
|  | ||||
|         let routing_context = { | ||||
|             let rc = (*ROUTING_CONTEXTS).borrow(); | ||||
|             let Some(routing_context) = rc.get(&self.id) else { | ||||
|                     return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument("routing_context_set_dht_value", "id", self.id)); | ||||
|                 }; | ||||
|             routing_context.clone() | ||||
|         }; | ||||
|         let routing_context = self.getRoutingContext()?; | ||||
|         let res = routing_context.set_dht_value(key, subKey, data).await?; | ||||
|         APIResult::Ok(res) | ||||
|     } | ||||
| @@ -252,7 +295,7 @@ impl VeilidRoutingContext { | ||||
|     //     subKeys: ValueSubkeyRangeSet, | ||||
|     //     expiration: Timestamp, | ||||
|     //     count: u32, | ||||
|     // ) -> VeilidAPIResult<String> { | ||||
|     // ) -> APIResult<String> { | ||||
|     //     let key: veilid_core::TypedKey = veilid_core::deserialize_json(&key).unwrap(); | ||||
|     //     let subkeys: veilid_core::ValueSubkeyRangeSet = | ||||
|     //         veilid_core::deserialize_json(&subkeys).unwrap(); | ||||
|   | ||||
| @@ -10,16 +10,28 @@ pub struct VeilidTableDB { | ||||
|  | ||||
| #[wasm_bindgen()] | ||||
| impl VeilidTableDB { | ||||
|     /// If the column count is greater than an existing TableDB's column count, | ||||
|     /// the database will be upgraded to add the missing columns. | ||||
|     #[wasm_bindgen(constructor)] | ||||
|     pub fn new(tableName: String, columnCount: u32) -> VeilidTableDB { | ||||
|         VeilidTableDB { | ||||
|     pub fn new(tableName: String, columnCount: u32) -> Self { | ||||
|         Self { | ||||
|             id: 0, | ||||
|             tableName, | ||||
|             columnCount, | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub async fn openTable(&mut self) -> VeilidAPIResult<u32> { | ||||
|     fn getTableDB(&self) -> APIResult<TableDB> { | ||||
|         let table_dbs = (*TABLE_DBS).borrow(); | ||||
|         let Some(table_db) = table_dbs.get(&self.id) else { | ||||
|             return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument("getTableDB", "id", self.id)); | ||||
|         }; | ||||
|         APIResult::Ok(table_db.clone()) | ||||
|     } | ||||
|  | ||||
|     /// Get or create the TableDB database table. | ||||
|     /// This is called automatically when performing actions on the TableDB. | ||||
|     pub async fn openTable(&mut self) -> APIResult<u32> { | ||||
|         let veilid_api = get_veilid_api()?; | ||||
|         let tstore = veilid_api.table_store()?; | ||||
|         let table_db = tstore | ||||
| @@ -31,6 +43,7 @@ impl VeilidTableDB { | ||||
|         APIResult::Ok(new_id) | ||||
|     } | ||||
|  | ||||
|     /// Release the TableDB instance from memory. | ||||
|     pub fn releaseTable(&mut self) -> bool { | ||||
|         let mut tdbs = (*TABLE_DBS).borrow_mut(); | ||||
|         let status = tdbs.remove(&self.id); | ||||
| @@ -41,7 +54,8 @@ impl VeilidTableDB { | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     pub async fn deleteTable(&mut self) -> VeilidAPIResult<bool> { | ||||
|     /// Delete this TableDB. | ||||
|     pub async fn deleteTable(&mut self) -> APIResult<bool> { | ||||
|         self.releaseTable(); | ||||
|  | ||||
|         let veilid_api = get_veilid_api()?; | ||||
| @@ -59,116 +73,125 @@ impl VeilidTableDB { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub async fn load(&mut self, columnId: u32, key: String) -> VeilidAPIResult<Option<String>> { | ||||
|     /// Read a key from a column in the TableDB immediately. | ||||
|     pub async fn load(&mut self, columnId: u32, key: String) -> APIResult<Option<String>> { | ||||
|         self.ensureOpen().await; | ||||
|         let key = unmarshall(key); | ||||
|         let table_db = self.getTableDB()?; | ||||
|  | ||||
|         let table_db = { | ||||
|             let table_dbs = (*TABLE_DBS).borrow(); | ||||
|             let Some(table_db) = table_dbs.get(&self.id) else { | ||||
|                 return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument("table_db_load", "id", self.id)); | ||||
|             }; | ||||
|             table_db.clone() | ||||
|         }; | ||||
|  | ||||
|         let out = table_db.load(columnId, key.as_bytes()).await?.unwrap(); | ||||
|         let out = Some(str::from_utf8(&out).unwrap().to_owned()); | ||||
|         // let out = serde_wasm_bindgen::to_value(&out) | ||||
|         //     .expect("Could not parse using serde_wasm_bindgen"); | ||||
|         let out = table_db.load(columnId, &key).await?.unwrap(); | ||||
|         let out = Some(marshall(&out)); | ||||
|         APIResult::Ok(out) | ||||
|     } | ||||
|  | ||||
|     pub async fn store( | ||||
|         &mut self, | ||||
|         columnId: u32, | ||||
|         key: String, | ||||
|         value: String, | ||||
|     ) -> VeilidAPIResult<()> { | ||||
|     /// Store a key with a value in a column in the TableDB. | ||||
|     /// Performs a single transaction immediately. | ||||
|     pub async fn store(&mut self, columnId: u32, key: String, value: String) -> APIResult<()> { | ||||
|         self.ensureOpen().await; | ||||
|         let key = unmarshall(key); | ||||
|         let value = unmarshall(value); | ||||
|         let table_db = self.getTableDB()?; | ||||
|  | ||||
|         let table_db = { | ||||
|             let table_dbs = (*TABLE_DBS).borrow(); | ||||
|             let Some(table_db) = table_dbs.get(&self.id) else { | ||||
|                 return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument("table_db_store", "id", self.id)); | ||||
|             }; | ||||
|             table_db.clone() | ||||
|         }; | ||||
|  | ||||
|         table_db | ||||
|             .store(columnId, key.as_bytes(), value.as_bytes()) | ||||
|             .await?; | ||||
|         table_db.store(columnId, &key, &value).await?; | ||||
|         APIRESULT_UNDEFINED | ||||
|     } | ||||
|  | ||||
|     pub async fn delete(&mut self, columnId: u32, key: String) -> VeilidAPIResult<Option<String>> { | ||||
|     /// Delete key with from a column in the TableDB. | ||||
|     pub async fn delete(&mut self, columnId: u32, key: String) -> APIResult<Option<String>> { | ||||
|         self.ensureOpen().await; | ||||
|         let key = unmarshall(key); | ||||
|         let table_db = self.getTableDB()?; | ||||
|  | ||||
|         let table_db = { | ||||
|             let table_dbs = (*TABLE_DBS).borrow(); | ||||
|             let Some(table_db) = table_dbs.get(&self.id) else { | ||||
|                 return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument("table_db_delete", "id", self.id)); | ||||
|             }; | ||||
|             table_db.clone() | ||||
|         }; | ||||
|  | ||||
|         // TODO: will crash when trying to .unwrap() of None (trying to delete key that doesn't exist) | ||||
|         let out = table_db.delete(columnId, key.as_bytes()).await?.unwrap(); | ||||
|         let out = Some(str::from_utf8(&out).unwrap().to_owned()); | ||||
|         let out = table_db.delete(columnId, &key).await?.unwrap(); | ||||
|         let out = Some(marshall(&out)); | ||||
|         APIResult::Ok(out) | ||||
|     } | ||||
|  | ||||
|     // TODO try and figure out how to result a String[], maybe Box<[String]>? | ||||
|     pub async fn getKeys(&mut self, columnId: u32) -> VeilidAPIResult<JsValue> { | ||||
|     /// Get the list of keys in a column of the TableDB. | ||||
|     /// | ||||
|     /// Returns an array of base64Url encoded keys. | ||||
|     pub async fn getKeys(&mut self, columnId: u32) -> APIResult<StringArray> { | ||||
|         self.ensureOpen().await; | ||||
|  | ||||
|         let table_db = { | ||||
|             let table_dbs = (*TABLE_DBS).borrow(); | ||||
|             let Some(table_db) = table_dbs.get(&self.id) else { | ||||
|                 return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument("table_db_store", "id", self.id)); | ||||
|             }; | ||||
|             table_db.clone() | ||||
|         }; | ||||
|         let table_db = self.getTableDB()?; | ||||
|  | ||||
|         let keys = table_db.clone().get_keys(columnId).await?; | ||||
|         let out: Vec<String> = keys | ||||
|             .into_iter() | ||||
|             .map(|k| str::from_utf8(&k).unwrap().to_owned()) | ||||
|             .collect(); | ||||
|         let out = | ||||
|             serde_wasm_bindgen::to_value(&out).expect("Could not parse using serde_wasm_bindgen"); | ||||
|         let out: Vec<String> = keys.into_iter().map(|k| marshall(&k)).collect(); | ||||
|         let out = into_unchecked_string_array(out); | ||||
|  | ||||
|         APIResult::Ok(out) | ||||
|     } | ||||
|  | ||||
|     pub async fn transact(&mut self) -> u32 { | ||||
|     /// Start a TableDB write transaction. | ||||
|     /// The transaction object must be committed or rolled back before dropping. | ||||
|     pub async fn createTransaction(&mut self) -> APIResult<VeilidTableDBTransaction> { | ||||
|         self.ensureOpen().await; | ||||
|         let table_db = self.getTableDB()?; | ||||
|  | ||||
|         let table_dbs = (*TABLE_DBS).borrow(); | ||||
|         let Some(table_db) = table_dbs.get(&self.id) else { | ||||
|             return 0; | ||||
|         }; | ||||
|         let tdbt = table_db.clone().transact(); | ||||
|         let tdbtid = add_table_db_transaction(tdbt); | ||||
|         return tdbtid; | ||||
|         let transaction = table_db.transact(); | ||||
|         let transaction_id = add_table_db_transaction(transaction); | ||||
|         APIResult::Ok(VeilidTableDBTransaction { id: transaction_id }) | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[wasm_bindgen] | ||||
| pub struct VeilidTableDBTransaction { | ||||
|     id: u32, | ||||
| } | ||||
|  | ||||
| #[wasm_bindgen] | ||||
| impl VeilidTableDBTransaction { | ||||
|     /// Don't use this constructor directly. | ||||
|     /// Use `.createTransaction()` on an instance of `VeilidTableDB` instead. | ||||
|     /// @deprecated | ||||
|     #[wasm_bindgen(constructor)] | ||||
|     pub fn new(id: u32) -> Self { | ||||
|         Self { id } | ||||
|     } | ||||
|  | ||||
|     // TODO: placeholders for transaction functions | ||||
|     // pub async fn releaseTransaction(&mut self) { | ||||
|     //     self.ensureOpen().await; | ||||
|     // } | ||||
|     fn getTransaction(&self) -> APIResult<TableDBTransaction> { | ||||
|         let transactions = (*TABLE_DB_TRANSACTIONS).borrow(); | ||||
|         let Some(transaction) = transactions.get(&self.id) else { | ||||
|             return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument("getTransaction", "id", &self.id)); | ||||
|         }; | ||||
|         APIResult::Ok(transaction.clone()) | ||||
|     } | ||||
|  | ||||
|     // pub async fn commitTransaction(&mut self) { | ||||
|     //     self.ensureOpen().await; | ||||
|     // } | ||||
|     /// Releases the transaction from memory. | ||||
|     pub fn releaseTransaction(&mut self) -> bool { | ||||
|         let mut transactions = (*TABLE_DB_TRANSACTIONS).borrow_mut(); | ||||
|         self.id = 0; | ||||
|         if transactions.remove(&self.id).is_none() { | ||||
|             return false; | ||||
|         } | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     // pub async fn rollbackTransaction(&mut self) { | ||||
|     //     self.ensureOpen().await; | ||||
|     // } | ||||
|     /// Commit the transaction. Performs all actions atomically. | ||||
|     pub async fn commit(&self) -> APIResult<()> { | ||||
|         let transaction = self.getTransaction()?; | ||||
|         transaction.commit().await | ||||
|     } | ||||
|  | ||||
|     // pub async fn storeTransaction(&mut self, tableId: u32, key: String, value: String) { | ||||
|     //     self.ensureOpen().await; | ||||
|     // } | ||||
|     /// Rollback the transaction. Does nothing to the TableDB. | ||||
|     pub fn rollback(&self) -> APIResult<()> { | ||||
|         let transaction = self.getTransaction()?; | ||||
|         transaction.rollback(); | ||||
|         APIRESULT_UNDEFINED | ||||
|     } | ||||
|  | ||||
|     // pub async fn deleteTransaction(&mut self) { | ||||
|     //     self.ensureOpen().await; | ||||
|     // } | ||||
|     /// Store a key with a value in a column in the TableDB. | ||||
|     /// Does not modify TableDB until `.commit()` is called. | ||||
|     pub fn store(&self, col: u32, key: String, value: String) -> APIResult<()> { | ||||
|         let key = unmarshall(key); | ||||
|         let value = unmarshall(value); | ||||
|         let transaction = self.getTransaction()?; | ||||
|         transaction.store(col, &key, &value) | ||||
|     } | ||||
|  | ||||
|     /// Delete key with from a column in the TableDB | ||||
|     pub fn deleteKey(&self, col: u32, key: String) -> APIResult<()> { | ||||
|         let key = unmarshall(key); | ||||
|         let transaction = self.getTransaction()?; | ||||
|         transaction.delete(col, &key) | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,3 +1,5 @@ | ||||
| use super::*; | ||||
|  | ||||
| cfg_if::cfg_if! { | ||||
|   if #[cfg(target_arch = "wasm32")] { | ||||
|       pub use tsify::*; | ||||
| @@ -19,3 +21,18 @@ cfg_if::cfg_if! { | ||||
|   } | ||||
| } | ||||
| pub(crate) use from_impl_to_jsvalue; | ||||
|  | ||||
| #[wasm_bindgen] | ||||
| extern "C" { | ||||
|     #[wasm_bindgen(typescript_type = "string[]")] | ||||
|     pub type StringArray; | ||||
| } | ||||
|  | ||||
| /// Convert a `Vec<String>` into a `js_sys::Array` with the type of `string[]` | ||||
| pub(crate) fn into_unchecked_string_array(items: Vec<String>) -> StringArray { | ||||
|     items | ||||
|         .iter() | ||||
|         .map(JsValue::from) | ||||
|         .collect::<js_sys::Array>() | ||||
|         .unchecked_into::<StringArray>() // TODO: can I do this a better way? | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user