From 1b20037053aab159b27d1547fb62ad877a287fd2 Mon Sep 17 00:00:00 2001 From: Brandon Vandegrift <798832-bmv437@users.noreply.gitlab.com> Date: Wed, 16 Aug 2023 10:25:09 -0400 Subject: [PATCH] Setup for TypeScript type gen for WASM using Tsify - Includes breaking changes to the WASM API surface, since it now accepts objects instead of stringified JSON. --- Cargo.lock | 78 +++++++++- Cargo.toml | 1 + tsify-async/Cargo.toml | 14 ++ tsify-async/src/lib.rs | 30 ++++ veilid-core/Cargo.toml | 3 + veilid-core/src/core_context.rs | 2 +- .../src/crypto/types/byte_array_types.rs | 6 + veilid-core/src/crypto/types/crypto_typed.rs | 2 +- .../src/crypto/types/crypto_typed_group.rs | 4 +- veilid-core/src/crypto/types/mod.rs | 11 ++ veilid-core/src/lib.rs | 3 + veilid-core/src/veilid_api/error.rs | 15 +- .../src/veilid_api/types/aligned_u64.rs | 17 ++- .../src/veilid_api/types/app_message_call.rs | 6 +- veilid-core/src/veilid_api/types/dht/mod.rs | 2 + .../src/veilid_api/types/dht/value_data.rs | 4 +- veilid-core/src/veilid_api/types/fourcc.rs | 13 +- .../src/veilid_api/types/veilid_log.rs | 6 +- .../src/veilid_api/types/veilid_state.rs | 21 +-- veilid-core/src/veilid_config.rs | 51 ++++--- veilid-wasm/Cargo.toml | 2 + veilid-wasm/src/lib.rs | 141 +++++++++--------- 22 files changed, 323 insertions(+), 109 deletions(-) create mode 100644 tsify-async/Cargo.toml create mode 100644 tsify-async/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 2585bef8..015ad59a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2136,6 +2136,19 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "gloo-utils" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037fcb07216cb3a30f7292bd0176b050b7b9a052ba830ef7d5d65f6dc64ba58e" +dependencies = [ + "js-sys", + "serde", + "serde_json", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "gloo-utils" version = "0.2.0" @@ -4250,7 +4263,7 @@ checksum = "ec0f696e21e10fa546b7ffb1c9672c6de8fbc7a81acf59524386d8639bf12737" dependencies = [ "proc-macro2", "quote", - "serde_derive_internals", + "serde_derive_internals 0.26.0", "syn 1.0.109", ] @@ -4358,6 +4371,17 @@ dependencies = [ "serde", ] +[[package]] +name = "serde-wasm-bindgen" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3b143e2833c57ab9ad3ea280d21fd34e285a42837aeb0ee301f4f41890fa00e" +dependencies = [ + "js-sys", + "serde", + "wasm-bindgen", +] + [[package]] name = "serde_cbor" version = "0.11.2" @@ -4390,6 +4414,17 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "serde_derive_internals" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e578a843d40b4189a4d66bba51d7684f57da5bd7c304c64e14bd63efbef49509" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.31", +] + [[package]] name = "serde_json" version = "1.0.105" @@ -5347,6 +5382,41 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" +[[package]] +name = "tsify" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6b26cf145f2f3b9ff84e182c448eaf05468e247f148cf3d2a7d67d78ff023a0" +dependencies = [ + "gloo-utils 0.1.7", + "serde", + "serde-wasm-bindgen", + "serde_json", + "tsify-macros", + "wasm-bindgen", +] + +[[package]] +name = "tsify-async" +version = "0.1.0" +dependencies = [ + "quote", + "serde-wasm-bindgen", + "syn 1.0.109", +] + +[[package]] +name = "tsify-macros" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a94b0f0954b3e59bfc2c246b4c8574390d94a4ad4ad246aaf2fb07d7dfd3b47" +dependencies = [ + "proc-macro2", + "quote", + "serde_derive_internals 0.28.0", + "syn 2.0.31", +] + [[package]] name = "tungstenite" version = "0.11.1" @@ -5614,6 +5684,7 @@ dependencies = [ "send_wrapper 0.6.0", "serde", "serde-big-array", + "serde-wasm-bindgen", "serde_json", "serial_test 2.0.0", "shell-words", @@ -5631,6 +5702,7 @@ dependencies = [ "tracing-subscriber", "tracing-wasm", "trust-dns-resolver", + "tsify", "veilid-bugsalot", "veilid-hashlink", "veilid-igd", @@ -5813,15 +5885,17 @@ dependencies = [ "console_error_panic_hook", "data-encoding", "futures-util", - "gloo-utils", + "gloo-utils 0.2.0", "js-sys", "lazy_static", "send_wrapper 0.6.0", "serde", + "serde-wasm-bindgen", "serde_json", "tracing", "tracing-subscriber", "tracing-wasm", + "tsify", "veilid-core", "wasm-bindgen", "wasm-bindgen-futures", diff --git a/Cargo.toml b/Cargo.toml index 61d53ad7..eaa3bf6f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,7 @@ members = [ "veilid-cli", "veilid-flutter/rust", "veilid-wasm", + "tsify-async" ] exclude = ["./external"] diff --git a/tsify-async/Cargo.toml b/tsify-async/Cargo.toml new file mode 100644 index 00000000..192733fc --- /dev/null +++ b/tsify-async/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "tsify-async" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[lib] +proc-macro = true + +[dependencies] +syn = "1.0" +quote = "1.0" +serde-wasm-bindgen = "0.5.0" diff --git a/tsify-async/src/lib.rs b/tsify-async/src/lib.rs new file mode 100644 index 00000000..4e4f75b7 --- /dev/null +++ b/tsify-async/src/lib.rs @@ -0,0 +1,30 @@ +// Copied from https://github.com/madonoharu/tsify/issues/24#issuecomment-1670228789 +// TODO: I think this had more to do with: +// the trait `From` is not implemented for `wasm_bindgen::JsValue` +// than it does with tsify itself... Maybe rename to something else? + +use proc_macro::TokenStream; +use quote::quote; +use syn; + +#[proc_macro_derive(TsifyAsync)] +pub fn tsify_async_macro_derive(input: TokenStream) -> TokenStream { + // Construct a representation of Rust code as a syntax tree + // that we can manipulate + let ast = syn::parse(input).unwrap(); + + // Build the trait implementation + impl_tsify_async_macro(&ast) +} + +fn impl_tsify_async_macro(ast: &syn::DeriveInput) -> TokenStream { + let name = &ast.ident; + let gen = quote! { + impl From<#name> for JsValue { + fn from(value: #name) -> Self { + serde_wasm_bindgen::to_value(&value).unwrap() + } + } + }; + gen.into() +} diff --git a/veilid-core/Cargo.toml b/veilid-core/Cargo.toml index 941af040..1a1be691 100644 --- a/veilid-core/Cargo.toml +++ b/veilid-core/Cargo.toml @@ -140,6 +140,9 @@ lz4_flex = { version = "0.11.1", default-features = false, features = [ "safe-encode", "safe-decode", ] } +tsify = { version = "0.4.5", features = ["js"] } +wasm-bindgen = "^0" +serde-wasm-bindgen = "0.5.0" # Dependencies for native builds only # Linux, Windows, Mac, iOS, Android diff --git a/veilid-core/src/core_context.rs b/veilid-core/src/core_context.rs index 4624b012..61e84f8a 100644 --- a/veilid-core/src/core_context.rs +++ b/veilid-core/src/core_context.rs @@ -231,7 +231,7 @@ impl VeilidCoreContext { update_callback: UpdateCallback, config_json: String, ) -> VeilidAPIResult { - // Set up config from callback + // Set up config from json trace!("setup config with json"); let mut config = VeilidConfig::new(); config.setup_from_json(config_json, update_callback.clone())?; diff --git a/veilid-core/src/crypto/types/byte_array_types.rs b/veilid-core/src/crypto/types/byte_array_types.rs index 4cdf2300..84995829 100644 --- a/veilid-core/src/crypto/types/byte_array_types.rs +++ b/veilid-core/src/crypto/types/byte_array_types.rs @@ -293,11 +293,17 @@ macro_rules! byte_array_type { byte_array_type!(CryptoKey, CRYPTO_KEY_LENGTH, CRYPTO_KEY_LENGTH_ENCODED); +#[declare] pub type PublicKey = CryptoKey; +#[declare] pub type SecretKey = CryptoKey; +#[declare] pub type HashDigest = CryptoKey; +#[declare] pub type SharedSecret = CryptoKey; +#[declare] pub type RouteId = CryptoKey; +#[declare] pub type CryptoKeyDistance = CryptoKey; byte_array_type!(Signature, SIGNATURE_LENGTH, SIGNATURE_LENGTH_ENCODED); diff --git a/veilid-core/src/crypto/types/crypto_typed.rs b/veilid-core/src/crypto/types/crypto_typed.rs index 740f888c..4a59ae20 100644 --- a/veilid-core/src/crypto/types/crypto_typed.rs +++ b/veilid-core/src/crypto/types/crypto_typed.rs @@ -1,6 +1,6 @@ use super::*; -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Tsify)] pub struct CryptoTyped where K: Clone diff --git a/veilid-core/src/crypto/types/crypto_typed_group.rs b/veilid-core/src/crypto/types/crypto_typed_group.rs index 80f717bc..60a34b2e 100644 --- a/veilid-core/src/crypto/types/crypto_typed_group.rs +++ b/veilid-core/src/crypto/types/crypto_typed_group.rs @@ -1,6 +1,8 @@ use super::*; -#[derive(Clone, Debug, Serialize, Deserialize, PartialOrd, Ord, PartialEq, Eq, Hash, Default)] +#[derive( + Clone, Debug, Serialize, Deserialize, PartialOrd, Ord, PartialEq, Eq, Hash, Default, Tsify, +)] #[serde(from = "Vec>", into = "Vec>")] pub struct CryptoTypedGroup where diff --git a/veilid-core/src/crypto/types/mod.rs b/veilid-core/src/crypto/types/mod.rs index af03453d..a1f2026d 100644 --- a/veilid-core/src/crypto/types/mod.rs +++ b/veilid-core/src/crypto/types/mod.rs @@ -6,6 +6,7 @@ use core::fmt; use core::hash::Hash; /// Cryptography version fourcc code +#[declare] pub type CryptoKind = FourCC; /// Sort best crypto kinds first @@ -51,14 +52,24 @@ pub use crypto_typed::*; pub use crypto_typed_group::*; pub use keypair::*; +#[declare] pub type TypedKey = CryptoTyped; +#[declare] pub type TypedSecret = CryptoTyped; +#[declare] pub type TypedKeyPair = CryptoTyped; +#[declare] pub type TypedSignature = CryptoTyped; +#[declare] pub type TypedSharedSecret = CryptoTyped; +#[declare] pub type TypedKeyGroup = CryptoTypedGroup; +#[declare] pub type TypedSecretGroup = CryptoTypedGroup; +#[declare] pub type TypedKeyPairGroup = CryptoTypedGroup; +#[declare] pub type TypedSignatureGroup = CryptoTypedGroup; +#[declare] pub type TypedSharedSecretGroup = CryptoTypedGroup; diff --git a/veilid-core/src/lib.rs b/veilid-core/src/lib.rs index 63d7db63..1a53fd73 100644 --- a/veilid-core/src/lib.rs +++ b/veilid-core/src/lib.rs @@ -126,4 +126,7 @@ use serde::*; use stop_token::*; use thiserror::Error as ThisError; use tracing::*; +use tsify::*; +use tsify_async::*; use veilid_tools::*; +use wasm_bindgen::prelude::*; diff --git a/veilid-core/src/veilid_api/error.rs b/veilid-core/src/veilid_api/error.rs index 6fcc4861..170a4146 100644 --- a/veilid-core/src/veilid_api/error.rs +++ b/veilid-core/src/veilid_api/error.rs @@ -105,9 +105,21 @@ macro_rules! apibail_already_initialized { } #[derive( - ThisError, Clone, Debug, PartialOrd, PartialEq, Eq, Ord, Serialize, Deserialize, JsonSchema, + ThisError, + Clone, + Debug, + PartialOrd, + PartialEq, + Eq, + Ord, + Serialize, + Deserialize, + JsonSchema, + Tsify, + TsifyAsync, )] #[serde(tag = "kind")] +#[tsify(into_wasm_abi)] pub enum VeilidAPIError { #[error("Not initialized")] NotInitialized, @@ -213,6 +225,7 @@ impl VeilidAPIError { } } +#[declare] pub type VeilidAPIResult = Result; impl From for VeilidAPIError { diff --git a/veilid-core/src/veilid_api/types/aligned_u64.rs b/veilid-core/src/veilid_api/types/aligned_u64.rs index fbad5696..32b5ceb1 100644 --- a/veilid-core/src/veilid_api/types/aligned_u64.rs +++ b/veilid-core/src/veilid_api/types/aligned_u64.rs @@ -6,7 +6,18 @@ use super::*; /// Supports serializing to string for JSON as well, since JSON can't handle 64-bit numbers to Javascript #[derive( - Clone, Default, PartialEq, Eq, PartialOrd, Ord, Copy, Hash, Serialize, Deserialize, JsonSchema, + Clone, + Default, + PartialEq, + Eq, + PartialOrd, + Ord, + Copy, + Hash, + Serialize, + Deserialize, + JsonSchema, + Tsify, )] #[repr(C, align(8))] #[serde(transparent)] @@ -117,13 +128,17 @@ impl AlignedU64 { ///////////////////////////////////////////////////////////////////////////////////////////////////// /// Microseconds since epoch +#[declare] pub type Timestamp = AlignedU64; pub fn get_aligned_timestamp() -> Timestamp { get_timestamp().into() } /// Microseconds duration +#[declare] pub type TimestampDuration = AlignedU64; /// Request/Response matching id +#[declare] pub type OperationId = AlignedU64; /// Number of bytes +#[declare] pub type ByteCount = AlignedU64; diff --git a/veilid-core/src/veilid_api/types/app_message_call.rs b/veilid-core/src/veilid_api/types/app_message_call.rs index 500588be..6959ad17 100644 --- a/veilid-core/src/veilid_api/types/app_message_call.rs +++ b/veilid-core/src/veilid_api/types/app_message_call.rs @@ -1,10 +1,11 @@ use super::*; /// Direct statement blob passed to hosting application for processing -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema, Tsify)] pub struct VeilidAppMessage { #[serde(with = "as_human_opt_string")] #[schemars(with = "Option")] + #[tsify(optional)] sender: Option, #[serde(with = "as_human_base64")] @@ -29,10 +30,11 @@ impl VeilidAppMessage { } /// Direct question blob passed to hosting application for processing to send an eventual AppReply -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema, Tsify)] pub struct VeilidAppCall { #[serde(with = "as_human_opt_string")] #[schemars(with = "Option")] + #[tsify(optional)] sender: Option, #[serde(with = "as_human_base64")] diff --git a/veilid-core/src/veilid_api/types/dht/mod.rs b/veilid-core/src/veilid_api/types/dht/mod.rs index 8b757907..9830042b 100644 --- a/veilid-core/src/veilid_api/types/dht/mod.rs +++ b/veilid-core/src/veilid_api/types/dht/mod.rs @@ -11,6 +11,8 @@ pub use value_data::*; pub use value_subkey_range_set::*; /// Value subkey +#[declare] pub type ValueSubkey = u32; /// Value sequence number +#[declare] pub type ValueSeqNum = u32; diff --git a/veilid-core/src/veilid_api/types/dht/value_data.rs b/veilid-core/src/veilid_api/types/dht/value_data.rs index f5aee32e..6a89388d 100644 --- a/veilid-core/src/veilid_api/types/dht/value_data.rs +++ b/veilid-core/src/veilid_api/types/dht/value_data.rs @@ -1,7 +1,9 @@ use super::*; use veilid_api::VeilidAPIResult; -#[derive(Clone, Default, PartialOrd, PartialEq, Eq, Ord, Serialize, Deserialize, JsonSchema)] +#[derive( + Clone, Default, PartialOrd, PartialEq, Eq, Ord, Serialize, Deserialize, JsonSchema, Tsify, +)] pub struct ValueData { /// An increasing sequence number to time-order the DHT record changes seq: ValueSeqNum, diff --git a/veilid-core/src/veilid_api/types/fourcc.rs b/veilid-core/src/veilid_api/types/fourcc.rs index 5995c1b5..8325a48e 100644 --- a/veilid-core/src/veilid_api/types/fourcc.rs +++ b/veilid-core/src/veilid_api/types/fourcc.rs @@ -2,7 +2,18 @@ use super::*; /// FOURCC code #[derive( - Copy, Default, Clone, Hash, PartialOrd, Ord, PartialEq, Eq, Serialize, Deserialize, JsonSchema, + Copy, + Default, + Clone, + Hash, + PartialOrd, + Ord, + PartialEq, + Eq, + Serialize, + Deserialize, + JsonSchema, + Tsify, )] #[serde(try_from = "String")] #[serde(into = "String")] diff --git a/veilid-core/src/veilid_api/types/veilid_log.rs b/veilid-core/src/veilid_api/types/veilid_log.rs index bf9c5963..7289e281 100644 --- a/veilid-core/src/veilid_api/types/veilid_log.rs +++ b/veilid-core/src/veilid_api/types/veilid_log.rs @@ -2,8 +2,9 @@ use super::*; /// Log level for VeilidCore #[derive( - Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Copy, Serialize, Deserialize, JsonSchema, + Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Copy, Serialize, Deserialize, JsonSchema, Tsify, )] +#[tsify(namespace)] pub enum VeilidLogLevel { Error = 1, Warn = 2, @@ -79,9 +80,10 @@ impl fmt::Display for VeilidLogLevel { } } /// A VeilidCore log message with optional backtrace -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema, Tsify)] pub struct VeilidLog { pub log_level: VeilidLogLevel, pub message: String, + #[tsify(optional)] pub backtrace: Option, } diff --git a/veilid-core/src/veilid_api/types/veilid_state.rs b/veilid-core/src/veilid_api/types/veilid_state.rs index 88ee35b9..496aaeac 100644 --- a/veilid-core/src/veilid_api/types/veilid_state.rs +++ b/veilid-core/src/veilid_api/types/veilid_state.rs @@ -1,7 +1,8 @@ use super::*; /// Attachment abstraction for network 'signal strength' -#[derive(Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize, JsonSchema)] +#[derive(Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize, JsonSchema, Tsify)] +#[tsify(namespace, from_wasm_abi, into_wasm_abi)] pub enum AttachmentState { Detached = 0, Attaching = 1, @@ -47,14 +48,14 @@ impl TryFrom for AttachmentState { } } -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema, Tsify)] pub struct VeilidStateAttachment { pub state: AttachmentState, pub public_internet_ready: bool, pub local_network_ready: bool, } -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema, Tsify)] pub struct PeerTableData { #[schemars(with = "Vec")] pub node_ids: Vec, @@ -62,7 +63,7 @@ pub struct PeerTableData { pub peer_stats: PeerStats, } -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema, Tsify)] pub struct VeilidStateNetwork { pub started: bool, pub bps_down: ByteCount, @@ -70,7 +71,7 @@ pub struct VeilidStateNetwork { pub peers: Vec, } -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema, Tsify)] pub struct VeilidRouteChange { #[schemars(with = "Vec")] pub dead_routes: Vec, @@ -78,12 +79,12 @@ pub struct VeilidRouteChange { pub dead_remote_routes: Vec, } -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema, Tsify)] pub struct VeilidStateConfig { pub config: VeilidConfigInner, } -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema, Tsify)] pub struct VeilidValueChange { #[schemars(with = "String")] pub key: TypedKey, @@ -92,8 +93,9 @@ pub struct VeilidValueChange { pub value: ValueData, } -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema, Tsify, TsifyAsync)] #[serde(tag = "kind")] +#[tsify(into_wasm_abi)] pub enum VeilidUpdate { Log(VeilidLog), AppMessage(VeilidAppMessage), @@ -106,7 +108,8 @@ pub enum VeilidUpdate { Shutdown, } -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema, Tsify, TsifyAsync)] +#[tsify(into_wasm_abi)] pub struct VeilidState { pub attachment: VeilidStateAttachment, pub network: VeilidStateNetwork, diff --git a/veilid-core/src/veilid_config.rs b/veilid-core/src/veilid_config.rs index 91027978..8aee0819 100644 --- a/veilid-core/src/veilid_config.rs +++ b/veilid-core/src/veilid_config.rs @@ -1,7 +1,9 @@ use crate::*; //////////////////////////////////////////////////////////////////////////////////////////////// +#[declare] pub type ConfigCallbackReturn = VeilidAPIResult>; +#[declare] pub type ConfigCallback = Arc ConfigCallbackReturn + Send + Sync>; /// Enable and configure HTTPS access to the Veilid node @@ -14,11 +16,12 @@ pub type ConfigCallback = Arc ConfigCallbackReturn + Send + Sy /// url: 'https://localhost:5150' /// ``` /// -#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] +#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema, Tsify)] pub struct VeilidConfigHTTPS { pub enabled: bool, pub listen_address: String, pub path: String, + #[tsify(optional)] pub url: Option, // Fixed URL is not optional for TLS-based protocols and is dynamically validated } @@ -32,11 +35,12 @@ pub struct VeilidConfigHTTPS { /// url: 'https://localhost:5150' /// ``` /// -#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] +#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema, Tsify)] pub struct VeilidConfigHTTP { pub enabled: bool, pub listen_address: String, pub path: String, + #[tsify(optional)] pub url: Option, } @@ -46,7 +50,7 @@ pub struct VeilidConfigHTTP { /// /// To be implemented... /// -#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] +#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema, Tsify)] pub struct VeilidConfigApplication { pub https: VeilidConfigHTTPS, pub http: VeilidConfigHTTP, @@ -62,11 +66,12 @@ pub struct VeilidConfigApplication { /// public_address: '' /// ``` /// -#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] +#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema, Tsify)] pub struct VeilidConfigUDP { pub enabled: bool, pub socket_pool_size: u32, pub listen_address: String, + #[tsify(optional)] pub public_address: Option, } @@ -80,12 +85,13 @@ pub struct VeilidConfigUDP { /// listen_address: ':5150' /// public_address: '' /// -#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] +#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema, Tsify)] pub struct VeilidConfigTCP { pub connect: bool, pub listen: bool, pub max_connections: u32, pub listen_address: String, + #[tsify(optional)] pub public_address: Option, } @@ -100,7 +106,7 @@ pub struct VeilidConfigTCP { /// path: 'ws' /// url: 'ws://localhost:5150/ws' /// -#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] +#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema, Tsify)] pub struct VeilidConfigWS { pub connect: bool, @@ -108,6 +114,7 @@ pub struct VeilidConfigWS { pub max_connections: u32, pub listen_address: String, pub path: String, + #[tsify(optional)] pub url: Option, } @@ -122,7 +129,7 @@ pub struct VeilidConfigWS { /// path: 'ws' /// url: '' /// -#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] +#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema, Tsify)] pub struct VeilidConfigWSS { pub connect: bool, @@ -130,6 +137,7 @@ pub struct VeilidConfigWSS { pub max_connections: u32, pub listen_address: String, pub path: String, + #[tsify(optional)] pub url: Option, // Fixed URL is not optional for TLS-based protocols and is dynamically validated } @@ -140,7 +148,7 @@ pub struct VeilidConfigWSS { /// All protocols are available by default, and the Veilid node will /// sort out which protocol is used for each peer connection. /// -#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] +#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema, Tsify)] pub struct VeilidConfigProtocol { pub udp: VeilidConfigUDP, @@ -157,7 +165,7 @@ pub struct VeilidConfigProtocol { /// private_key_path: /path/to/private/key /// connection_initial_timeout_ms: 2000 /// -#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] +#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema, Tsify)] pub struct VeilidConfigTLS { pub certificate_path: String, pub private_key_path: String, @@ -166,7 +174,7 @@ pub struct VeilidConfigTLS { /// Configure the Distributed Hash Table (DHT) /// -#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] +#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema, Tsify)] pub struct VeilidConfigDHT { pub max_find_node_count: u32, pub resolve_node_timeout_ms: u32, @@ -191,11 +199,13 @@ pub struct VeilidConfigDHT { /// Configure RPC /// -#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] +#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema, Tsify)] pub struct VeilidConfigRPC { pub concurrency: u32, pub queue_size: u32, + #[tsify(optional)] pub max_timestamp_behind_ms: Option, + #[tsify(optional)] pub max_timestamp_ahead_ms: Option, pub timeout_ms: u32, pub max_route_hop_count: u8, @@ -204,7 +214,7 @@ pub struct VeilidConfigRPC { /// Configure the network routing table /// -#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] +#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema, Tsify)] pub struct VeilidConfigRoutingTable { #[schemars(with = "Vec")] pub node_id: TypedKeyGroup, @@ -220,7 +230,7 @@ pub struct VeilidConfigRoutingTable { // xxx pub enable_local_network: bool, } -#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] +#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema, Tsify)] pub struct VeilidConfigNetwork { pub connection_initial_timeout_ms: u32, pub connection_inactivity_timeout_ms: u32, @@ -231,6 +241,7 @@ pub struct VeilidConfigNetwork { pub client_whitelist_timeout_ms: u32, pub reverse_connection_receipt_time_ms: u32, pub hole_punch_receipt_time_ms: u32, + #[tsify(optional)] pub network_key_password: Option, pub routing_table: VeilidConfigRoutingTable, pub rpc: VeilidConfigRPC, @@ -243,34 +254,36 @@ pub struct VeilidConfigNetwork { pub protocol: VeilidConfigProtocol, } -#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] +#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema, Tsify)] pub struct VeilidConfigTableStore { pub directory: String, pub delete: bool, } -#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] +#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema, Tsify)] pub struct VeilidConfigBlockStore { pub directory: String, pub delete: bool, } -#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] +#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema, Tsify)] pub struct VeilidConfigProtectedStore { pub allow_insecure_fallback: bool, pub always_use_insecure_storage: bool, pub directory: String, pub delete: bool, pub device_encryption_key_password: String, + #[tsify(optional)] pub new_device_encryption_key_password: Option, } -#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] +#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema, Tsify)] pub struct VeilidConfigCapabilities { pub disable: Vec, } -#[derive(Clone, Copy, PartialEq, Eq, Debug, Serialize, Deserialize, JsonSchema)] +#[derive(Clone, Copy, PartialEq, Eq, Debug, Serialize, Deserialize, JsonSchema, Tsify)] +#[tsify(namespace, from_wasm_abi)] pub enum VeilidConfigLogLevel { Off, Error, @@ -357,7 +370,7 @@ impl fmt::Display for VeilidConfigLogLevel { } } -#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] +#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema, Tsify)] pub struct VeilidConfigInner { pub program_name: String, pub namespace: String, diff --git a/veilid-wasm/Cargo.toml b/veilid-wasm/Cargo.toml index 129840f9..06313daf 100644 --- a/veilid-wasm/Cargo.toml +++ b/veilid-wasm/Cargo.toml @@ -34,6 +34,8 @@ send_wrapper = "^0" futures-util = { version = "^0" } data-encoding = { version = "^2" } gloo-utils = { version = "^0", features = ["serde"] } +tsify = { version = "0.4.5", features = ["js"] } +serde-wasm-bindgen = "0.5.0" [dev-dependencies] wasm-bindgen-test = "^0" diff --git a/veilid-wasm/src/lib.rs b/veilid-wasm/src/lib.rs index ed6172e7..76501d54 100644 --- a/veilid-wasm/src/lib.rs +++ b/veilid-wasm/src/lib.rs @@ -19,6 +19,7 @@ use serde::*; use tracing_subscriber::prelude::*; use tracing_subscriber::*; use tracing_wasm::{WASMLayerConfigBuilder, *}; +use tsify::*; use veilid_core::tools::*; use veilid_core::*; use wasm_bindgen::prelude::*; @@ -85,6 +86,7 @@ type APIResult = Result; const APIRESULT_UNDEFINED: APIResult<()> = APIResult::Ok(()); pub fn wrap_api_future_json(future: F) -> Promise +// Result where F: Future> + 'static, T: Serialize + Debug + 'static, @@ -93,6 +95,7 @@ where } pub fn wrap_api_future_plain(future: F) -> Promise +// Result where F: Future> + 'static, JsValue: From, @@ -102,6 +105,7 @@ where } pub fn wrap_api_future_void(future: F) -> Promise +// Result<(), VeilidAPIError> where F: Future> + 'static, { @@ -111,7 +115,7 @@ where ///////////////////////////////////////// // WASM-specific -#[derive(Debug, Deserialize, Serialize)] +#[derive(Debug, Deserialize, Serialize, Tsify)] pub struct VeilidWASMConfigLoggingPerformance { pub enabled: bool, pub level: veilid_core::VeilidConfigLogLevel, @@ -119,24 +123,25 @@ pub struct VeilidWASMConfigLoggingPerformance { pub logs_in_console: bool, } -#[derive(Debug, Deserialize, Serialize)] +#[derive(Debug, Deserialize, Serialize, Tsify)] pub struct VeilidWASMConfigLoggingAPI { pub enabled: bool, pub level: veilid_core::VeilidConfigLogLevel, } -#[derive(Debug, Deserialize, Serialize)] +#[derive(Debug, Deserialize, Serialize, Tsify)] pub struct VeilidWASMConfigLogging { pub performance: VeilidWASMConfigLoggingPerformance, pub api: VeilidWASMConfigLoggingAPI, } -#[derive(Debug, Deserialize, Serialize)] +#[derive(Debug, Deserialize, Serialize, Tsify)] +#[tsify(from_wasm_abi)] pub struct VeilidWASMConfig { pub logging: VeilidWASMConfigLogging, } -#[derive(Debug, Deserialize, Serialize)] +#[derive(Debug, Deserialize, Serialize, Tsify)] pub struct VeilidRouteBlob { pub route_id: veilid_core::RouteId, #[serde(with = "veilid_core::as_human_base64")] @@ -152,13 +157,10 @@ pub fn initialize_veilid_wasm() { static INITIALIZED: AtomicBool = AtomicBool::new(false); #[wasm_bindgen()] -pub fn initialize_veilid_core(platform_config: String) { +pub fn initialize_veilid_core(platform_config: VeilidWASMConfig) { if INITIALIZED.swap(true, Ordering::Relaxed) { return; } - let platform_config: VeilidWASMConfig = veilid_core::deserialize_json(&platform_config) - .expect("failed to deserialize platform config json"); - // Set up subscriber and layers let subscriber = Registry::default(); let mut layers = Vec::new(); @@ -199,9 +201,8 @@ pub fn initialize_veilid_core(platform_config: String) { } #[wasm_bindgen()] -pub fn change_log_level(layer: String, log_level: String) { +pub fn change_log_level(layer: String, log_level: VeilidConfigLogLevel) { let layer = if layer == "all" { "".to_owned() } else { layer }; - let log_level: veilid_core::VeilidConfigLogLevel = deserialize_json(&log_level).unwrap(); let filters = (*FILTERS).borrow(); if layer.is_empty() { // Change all layers @@ -215,65 +216,73 @@ pub fn change_log_level(layer: String, log_level: String) { } } -#[wasm_bindgen()] -pub fn startup_veilid_core(update_callback_js: Function, json_config: String) -> Promise { +#[wasm_bindgen(typescript_custom_section)] +const IUPDATE_VEILID_FUNCTION: &'static str = r#" +type UpdateVeilidFunction = (event: VeilidUpdate) => void; +"#; + +#[wasm_bindgen] +extern "C" { + #[wasm_bindgen(extends = Function, typescript_type = "UpdateVeilidFunction")] + pub type UpdateVeilidFunction; +} + +#[wasm_bindgen] +pub async fn startup_veilid_core( + update_callback_js: UpdateVeilidFunction, + json_config: String, +) -> Result<(), VeilidAPIError> { let update_callback_js = SendWrapper::new(update_callback_js); - wrap_api_future_void(async move { - let update_callback = Arc::new(move |update: VeilidUpdate| { - let _ret = - match Function::call1(&update_callback_js, &JsValue::UNDEFINED, &to_json(update)) { - Ok(v) => v, - Err(e) => { - console_log(&format!("calling update callback failed: {:?}", e)); - return; - } - }; - }); + let update_callback = Arc::new(move |update: VeilidUpdate| { + let _ret = match Function::call1( + &update_callback_js, + &JsValue::UNDEFINED, + &to_jsvalue(update), + ) { + Ok(v) => v, + Err(e) => { + console_log(&format!("calling update callback failed: {:?}", e)); + return; + } + }; + }); - if VEILID_API.borrow().is_some() { - return Err(veilid_core::VeilidAPIError::AlreadyInitialized); - } + if VEILID_API.borrow().is_some() { + return Err(veilid_core::VeilidAPIError::AlreadyInitialized); + } - let veilid_api = veilid_core::api_startup_json(update_callback, json_config).await?; - VEILID_API.replace(Some(veilid_api)); - APIRESULT_UNDEFINED - }) + let veilid_api = veilid_core::api_startup_json(update_callback, json_config).await?; + // veilid_core::api_startup(update_callback, config_callback) + VEILID_API.replace(Some(veilid_api)); + Ok(()) } #[wasm_bindgen()] -pub fn get_veilid_state() -> Promise { - wrap_api_future_json(async move { - let veilid_api = get_veilid_api()?; - let core_state = veilid_api.get_state().await?; - APIResult::Ok(core_state) - }) +pub async fn get_veilid_state() -> Result { + let veilid_api = get_veilid_api()?; + let core_state = veilid_api.get_state().await?; + Ok(core_state) } #[wasm_bindgen()] -pub fn attach() -> Promise { - wrap_api_future_void(async move { - let veilid_api = get_veilid_api()?; - veilid_api.attach().await?; - APIRESULT_UNDEFINED - }) +pub async fn attach() -> Result<(), VeilidAPIError> { + let veilid_api = get_veilid_api()?; + veilid_api.attach().await?; + Ok(()) } #[wasm_bindgen()] -pub fn detach() -> Promise { - wrap_api_future_void(async move { - let veilid_api = get_veilid_api()?; - veilid_api.detach().await?; - APIRESULT_UNDEFINED - }) +pub async fn detach() -> Result<(), VeilidAPIError> { + let veilid_api = get_veilid_api()?; + veilid_api.detach().await?; + Ok(()) } #[wasm_bindgen()] -pub fn shutdown_veilid_core() -> Promise { - wrap_api_future_void(async move { - let veilid_api = take_veilid_api()?; - veilid_api.shutdown().await; - APIRESULT_UNDEFINED - }) +pub async fn shutdown_veilid_core() -> Result<(), VeilidAPIError> { + let veilid_api = take_veilid_api()?; + veilid_api.shutdown().await; + Ok(()) } fn add_routing_context(routing_context: veilid_core::RoutingContext) -> u32 { @@ -287,13 +296,11 @@ fn add_routing_context(routing_context: veilid_core::RoutingContext) -> u32 { } #[wasm_bindgen()] -pub fn routing_context() -> Promise { - wrap_api_future_plain(async move { - let veilid_api = get_veilid_api()?; - let routing_context = veilid_api.routing_context(); - let new_id = add_routing_context(routing_context); - APIResult::Ok(new_id) - }) +pub async fn routing_context() -> Result { + let veilid_api = get_veilid_api()?; + let routing_context = veilid_api.routing_context(); + let new_id = add_routing_context(routing_context); + Ok(new_id) } #[wasm_bindgen()] @@ -1440,12 +1447,10 @@ pub fn now() -> u64 { } #[wasm_bindgen()] -pub fn debug(command: String) -> Promise { - wrap_api_future_plain(async move { - let veilid_api = get_veilid_api()?; - let out = veilid_api.debug(command).await?; - APIResult::Ok(out) - }) +pub async fn debug(command: String) -> Result { + let veilid_api = get_veilid_api()?; + let out = veilid_api.debug(command).await?; + APIResult::Ok(out) } #[wasm_bindgen()]