Merge branch 'tsify-typescript-wasm' into 'main'
[WASM] New APIs: Enhanced JS interop and TypeScript type generation See merge request veilid/veilid!130
This commit is contained in:
commit
d6c3d8fddd
70
Cargo.lock
generated
70
Cargo.lock
generated
@ -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,32 @@ 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-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 +5675,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 +5693,7 @@ dependencies = [
|
||||
"tracing-subscriber",
|
||||
"tracing-wasm",
|
||||
"trust-dns-resolver",
|
||||
"tsify",
|
||||
"veilid-bugsalot",
|
||||
"veilid-hashlink",
|
||||
"veilid-igd",
|
||||
@ -5813,15 +5876,18 @@ dependencies = [
|
||||
"console_error_panic_hook",
|
||||
"data-encoding",
|
||||
"futures-util",
|
||||
"gloo-utils",
|
||||
"gloo-utils 0.2.0",
|
||||
"js-sys",
|
||||
"lazy_static",
|
||||
"parking_lot 0.12.1",
|
||||
"send_wrapper 0.6.0",
|
||||
"serde",
|
||||
"serde-wasm-bindgen",
|
||||
"serde_json",
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
"tracing-wasm",
|
||||
"tsify",
|
||||
"veilid-core",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
|
@ -199,6 +199,8 @@ wasm-bindgen = "0.2.87"
|
||||
js-sys = "0.3.64"
|
||||
wasm-bindgen-futures = "0.4.37"
|
||||
send_wrapper = { version = "0.6.0", features = ["futures"] }
|
||||
tsify = { version = "0.4.5", features = ["js"] }
|
||||
serde-wasm-bindgen = "0.5.0"
|
||||
|
||||
# Network
|
||||
ws_stream_wasm = "0.7.4"
|
||||
|
@ -231,7 +231,7 @@ impl VeilidCoreContext {
|
||||
update_callback: UpdateCallback,
|
||||
config_json: String,
|
||||
) -> VeilidAPIResult<VeilidCoreContext> {
|
||||
// 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())?;
|
||||
|
@ -78,6 +78,7 @@ where
|
||||
macro_rules! byte_array_type {
|
||||
($name:ident, $size:expr, $encoded_size:expr) => {
|
||||
#[derive(Clone, Copy, Hash)]
|
||||
#[cfg_attr(target_arch = "wasm32", derive(Tsify), tsify(into_wasm_abi))]
|
||||
pub struct $name {
|
||||
pub bytes: [u8; $size],
|
||||
}
|
||||
@ -293,11 +294,17 @@ macro_rules! byte_array_type {
|
||||
|
||||
byte_array_type!(CryptoKey, CRYPTO_KEY_LENGTH, CRYPTO_KEY_LENGTH_ENCODED);
|
||||
|
||||
#[cfg_attr(target_arch = "wasm32", declare)]
|
||||
pub type PublicKey = CryptoKey;
|
||||
#[cfg_attr(target_arch = "wasm32", declare)]
|
||||
pub type SecretKey = CryptoKey;
|
||||
#[cfg_attr(target_arch = "wasm32", declare)]
|
||||
pub type HashDigest = CryptoKey;
|
||||
#[cfg_attr(target_arch = "wasm32", declare)]
|
||||
pub type SharedSecret = CryptoKey;
|
||||
#[cfg_attr(target_arch = "wasm32", declare)]
|
||||
pub type RouteId = CryptoKey;
|
||||
#[cfg_attr(target_arch = "wasm32", declare)]
|
||||
pub type CryptoKeyDistance = CryptoKey;
|
||||
|
||||
byte_array_type!(Signature, SIGNATURE_LENGTH, SIGNATURE_LENGTH_ENCODED);
|
||||
|
@ -1,6 +1,7 @@
|
||||
use super::*;
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
#[cfg_attr(target_arch = "wasm32", derive(Tsify), tsify(into_wasm_abi))]
|
||||
pub struct CryptoTyped<K>
|
||||
where
|
||||
K: Clone
|
||||
|
@ -1,7 +1,9 @@
|
||||
use super::*;
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialOrd, Ord, PartialEq, Eq, Hash, Default)]
|
||||
#[cfg_attr(target_arch = "wasm32", derive(Tsify))]
|
||||
#[serde(from = "Vec<CryptoTyped<K>>", into = "Vec<CryptoTyped<K>>")]
|
||||
// TODO: figure out hot to TS type this as `string`, since it's converted to string via the JSON API.
|
||||
pub struct CryptoTypedGroup<K = PublicKey>
|
||||
where
|
||||
K: Clone
|
||||
|
@ -1,10 +1,18 @@
|
||||
use super::*;
|
||||
|
||||
#[derive(Clone, Copy, Default, PartialOrd, Ord, PartialEq, Eq, Hash)]
|
||||
#[cfg_attr(
|
||||
target_arch = "wasm32",
|
||||
derive(Tsify),
|
||||
tsify(from_wasm_abi, into_wasm_abi)
|
||||
)]
|
||||
pub struct KeyPair {
|
||||
#[cfg_attr(target_arch = "wasm32", tsify(type = "string"))]
|
||||
pub key: PublicKey,
|
||||
#[cfg_attr(target_arch = "wasm32", tsify(type = "string"))]
|
||||
pub secret: SecretKey,
|
||||
}
|
||||
from_impl_to_jsvalue!(KeyPair);
|
||||
|
||||
impl KeyPair {
|
||||
pub fn new(key: PublicKey, secret: SecretKey) -> Self {
|
||||
|
@ -6,6 +6,7 @@ use core::fmt;
|
||||
use core::hash::Hash;
|
||||
|
||||
/// Cryptography version fourcc code
|
||||
#[cfg_attr(target_arch = "wasm32", 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::*;
|
||||
|
||||
#[cfg_attr(target_arch = "wasm32", declare)]
|
||||
pub type TypedKey = CryptoTyped<PublicKey>;
|
||||
#[cfg_attr(target_arch = "wasm32", declare)]
|
||||
pub type TypedSecret = CryptoTyped<SecretKey>;
|
||||
#[cfg_attr(target_arch = "wasm32", declare)]
|
||||
pub type TypedKeyPair = CryptoTyped<KeyPair>;
|
||||
#[cfg_attr(target_arch = "wasm32", declare)]
|
||||
pub type TypedSignature = CryptoTyped<Signature>;
|
||||
#[cfg_attr(target_arch = "wasm32", declare)]
|
||||
pub type TypedSharedSecret = CryptoTyped<SharedSecret>;
|
||||
|
||||
#[cfg_attr(target_arch = "wasm32", declare)]
|
||||
pub type TypedKeyGroup = CryptoTypedGroup<PublicKey>;
|
||||
#[cfg_attr(target_arch = "wasm32", declare)]
|
||||
pub type TypedSecretGroup = CryptoTypedGroup<SecretKey>;
|
||||
#[cfg_attr(target_arch = "wasm32", declare)]
|
||||
pub type TypedKeyPairGroup = CryptoTypedGroup<KeyPair>;
|
||||
#[cfg_attr(target_arch = "wasm32", declare)]
|
||||
pub type TypedSignatureGroup = CryptoTypedGroup<Signature>;
|
||||
#[cfg_attr(target_arch = "wasm32", declare)]
|
||||
pub type TypedSharedSecretGroup = CryptoTypedGroup<SharedSecret>;
|
||||
|
@ -56,6 +56,7 @@ mod table_store;
|
||||
mod veilid_api;
|
||||
mod veilid_config;
|
||||
mod veilid_layer_filter;
|
||||
mod wasm_helpers;
|
||||
|
||||
pub use self::api_tracing_layer::ApiTracingLayer;
|
||||
pub use self::core_context::{api_startup, api_startup_json, UpdateCallback};
|
||||
@ -127,3 +128,4 @@ use stop_token::*;
|
||||
use thiserror::Error as ThisError;
|
||||
use tracing::*;
|
||||
use veilid_tools::*;
|
||||
use wasm_helpers::*;
|
||||
|
2
veilid-core/src/network_manager/wasm/.cargo/config.toml
Normal file
2
veilid-core/src/network_manager/wasm/.cargo/config.toml
Normal file
@ -0,0 +1,2 @@
|
||||
[build]
|
||||
target = "wasm32-unknown-unknown"
|
@ -107,6 +107,7 @@ macro_rules! apibail_already_initialized {
|
||||
#[derive(
|
||||
ThisError, Clone, Debug, PartialOrd, PartialEq, Eq, Ord, Serialize, Deserialize, JsonSchema,
|
||||
)]
|
||||
#[cfg_attr(target_arch = "wasm32", derive(Tsify), tsify(into_wasm_abi))]
|
||||
#[serde(tag = "kind")]
|
||||
pub enum VeilidAPIError {
|
||||
#[error("Not initialized")]
|
||||
@ -145,6 +146,7 @@ pub enum VeilidAPIError {
|
||||
#[error("Generic: {message}")]
|
||||
Generic { message: String },
|
||||
}
|
||||
from_impl_to_jsvalue!(VeilidAPIError);
|
||||
|
||||
impl VeilidAPIError {
|
||||
pub fn not_initialized() -> Self {
|
||||
@ -213,6 +215,7 @@ impl VeilidAPIError {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(target_arch = "wasm32", declare)]
|
||||
pub type VeilidAPIResult<T> = Result<T, VeilidAPIError>;
|
||||
|
||||
impl From<std::io::Error> for VeilidAPIError {
|
||||
|
@ -8,11 +8,13 @@ use super::*;
|
||||
#[derive(
|
||||
Clone, Default, PartialEq, Eq, PartialOrd, Ord, Copy, Hash, Serialize, Deserialize, JsonSchema,
|
||||
)]
|
||||
#[cfg_attr(target_arch = "wasm32", derive(Tsify))]
|
||||
#[repr(C, align(8))]
|
||||
#[serde(transparent)]
|
||||
pub struct AlignedU64(
|
||||
#[serde(with = "as_human_string")]
|
||||
#[schemars(with = "String")]
|
||||
#[cfg_attr(target_arch = "wasm32", tsify(type = "string"))]
|
||||
u64,
|
||||
);
|
||||
|
||||
@ -117,13 +119,17 @@ impl AlignedU64 {
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Microseconds since epoch
|
||||
#[cfg_attr(target_arch = "wasm32", declare)]
|
||||
pub type Timestamp = AlignedU64;
|
||||
pub fn get_aligned_timestamp() -> Timestamp {
|
||||
get_timestamp().into()
|
||||
}
|
||||
/// Microseconds duration
|
||||
#[cfg_attr(target_arch = "wasm32", declare)]
|
||||
pub type TimestampDuration = AlignedU64;
|
||||
/// Request/Response matching id
|
||||
#[cfg_attr(target_arch = "wasm32", declare)]
|
||||
pub type OperationId = AlignedU64;
|
||||
/// Number of bytes
|
||||
#[cfg_attr(target_arch = "wasm32", declare)]
|
||||
pub type ByteCount = AlignedU64;
|
||||
|
@ -2,13 +2,16 @@ use super::*;
|
||||
|
||||
/// Direct statement blob passed to hosting application for processing
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
||||
#[cfg_attr(target_arch = "wasm32", derive(Tsify))]
|
||||
pub struct VeilidAppMessage {
|
||||
#[serde(with = "as_human_opt_string")]
|
||||
#[schemars(with = "Option<String>")]
|
||||
#[cfg_attr(target_arch = "wasm32", tsify(optional, type = "string"))]
|
||||
sender: Option<TypedKey>,
|
||||
|
||||
#[serde(with = "as_human_base64")]
|
||||
#[schemars(with = "String")]
|
||||
#[cfg_attr(target_arch = "wasm32", tsify(type = "string"))]
|
||||
message: Vec<u8>,
|
||||
}
|
||||
|
||||
@ -30,9 +33,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)]
|
||||
#[cfg_attr(target_arch = "wasm32", derive(Tsify))]
|
||||
pub struct VeilidAppCall {
|
||||
#[serde(with = "as_human_opt_string")]
|
||||
#[schemars(with = "Option<String>")]
|
||||
#[cfg_attr(target_arch = "wasm32", tsify(optional))]
|
||||
sender: Option<TypedKey>,
|
||||
|
||||
#[serde(with = "as_human_base64")]
|
||||
|
@ -2,20 +2,29 @@ use super::*;
|
||||
|
||||
/// DHT Record Descriptor
|
||||
#[derive(Debug, Clone, PartialOrd, Ord, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
||||
#[cfg_attr(
|
||||
target_arch = "wasm32",
|
||||
derive(Tsify),
|
||||
tsify(from_wasm_abi, into_wasm_abi)
|
||||
)]
|
||||
pub struct DHTRecordDescriptor {
|
||||
/// DHT Key = Hash(ownerKeyKind) of: [ ownerKeyValue, schema ]
|
||||
#[schemars(with = "String")]
|
||||
#[cfg_attr(target_arch = "wasm32", tsify(type = "string"))]
|
||||
key: TypedKey,
|
||||
/// The public key of the owner
|
||||
#[schemars(with = "String")]
|
||||
#[cfg_attr(target_arch = "wasm32", tsify(type = "string"))]
|
||||
owner: PublicKey,
|
||||
/// If this key is being created: Some(the secret key of the owner)
|
||||
/// If this key is just being opened: None
|
||||
#[schemars(with = "Option<String>")]
|
||||
#[cfg_attr(target_arch = "wasm32", tsify(optional, type = "string"))]
|
||||
owner_secret: Option<SecretKey>,
|
||||
/// The schema in use associated with the key
|
||||
schema: DHTSchema,
|
||||
}
|
||||
from_impl_to_jsvalue!(DHTRecordDescriptor);
|
||||
|
||||
impl DHTRecordDescriptor {
|
||||
pub fn new(
|
||||
|
@ -11,6 +11,8 @@ pub use value_data::*;
|
||||
pub use value_subkey_range_set::*;
|
||||
|
||||
/// Value subkey
|
||||
#[cfg_attr(target_arch = "wasm32", declare)]
|
||||
pub type ValueSubkey = u32;
|
||||
/// Value sequence number
|
||||
#[cfg_attr(target_arch = "wasm32", declare)]
|
||||
pub type ValueSeqNum = u32;
|
||||
|
@ -2,6 +2,7 @@ use super::*;
|
||||
|
||||
/// Default DHT Schema (DFLT)
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Ord, PartialOrd, Serialize, Deserialize, JsonSchema)]
|
||||
#[cfg_attr(target_arch = "wasm32", derive(Tsify), tsify(from_wasm_abi))]
|
||||
pub struct DHTSchemaDFLT {
|
||||
/// Owner subkey count
|
||||
pub o_cnt: u16,
|
||||
|
@ -9,6 +9,7 @@ pub use smpl::*;
|
||||
/// Enum over all the supported DHT Schemas
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Ord, PartialOrd, Serialize, Deserialize, JsonSchema)]
|
||||
#[serde(tag = "kind")]
|
||||
#[cfg_attr(target_arch = "wasm32", derive(Tsify), tsify(from_wasm_abi))]
|
||||
pub enum DHTSchema {
|
||||
DFLT(DHTSchemaDFLT),
|
||||
SMPL(DHTSchemaSMPL),
|
||||
|
@ -2,9 +2,11 @@ use super::*;
|
||||
|
||||
/// Simple DHT Schema (SMPL) Member
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, JsonSchema)]
|
||||
#[cfg_attr(target_arch = "wasm32", derive(Tsify), tsify(from_wasm_abi))]
|
||||
pub struct DHTSchemaSMPLMember {
|
||||
/// Member key
|
||||
#[schemars(with = "String")]
|
||||
#[cfg_attr(target_arch = "wasm32", tsify(type = "string"))]
|
||||
pub m_key: PublicKey,
|
||||
/// Member subkey count
|
||||
pub m_cnt: u16,
|
||||
@ -12,6 +14,7 @@ pub struct DHTSchemaSMPLMember {
|
||||
|
||||
/// Simple DHT Schema (SMPL)
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Ord, PartialOrd, Serialize, Deserialize, JsonSchema)]
|
||||
#[cfg_attr(target_arch = "wasm32", derive(Tsify), tsify(from_wasm_abi))]
|
||||
pub struct DHTSchemaSMPL {
|
||||
/// Owner subkey count
|
||||
pub o_cnt: u16,
|
||||
|
@ -2,6 +2,7 @@ use super::*;
|
||||
use veilid_api::VeilidAPIResult;
|
||||
|
||||
#[derive(Clone, Default, PartialOrd, PartialEq, Eq, Ord, Serialize, Deserialize, JsonSchema)]
|
||||
#[cfg_attr(target_arch = "wasm32", derive(Tsify), tsify(into_wasm_abi))]
|
||||
pub struct ValueData {
|
||||
/// An increasing sequence number to time-order the DHT record changes
|
||||
seq: ValueSeqNum,
|
||||
@ -9,12 +10,16 @@ pub struct ValueData {
|
||||
/// The contents of a DHT Record
|
||||
#[serde(with = "as_human_base64")]
|
||||
#[schemars(with = "String")]
|
||||
#[cfg_attr(target_arch = "wasm32", tsify(type = "string"))]
|
||||
data: Vec<u8>,
|
||||
|
||||
/// The public identity key of the writer of the data
|
||||
#[schemars(with = "String")]
|
||||
#[cfg_attr(target_arch = "wasm32", tsify(type = "string"))]
|
||||
writer: PublicKey,
|
||||
}
|
||||
from_impl_to_jsvalue!(ValueData);
|
||||
|
||||
impl ValueData {
|
||||
pub const MAX_LEN: usize = 32768;
|
||||
|
||||
|
@ -4,6 +4,7 @@ use super::*;
|
||||
#[derive(
|
||||
Copy, Default, Clone, Hash, PartialOrd, Ord, PartialEq, Eq, Serialize, Deserialize, JsonSchema,
|
||||
)]
|
||||
#[cfg_attr(target_arch = "wasm32", derive(Tsify))]
|
||||
#[serde(try_from = "String")]
|
||||
#[serde(into = "String")]
|
||||
pub struct FourCC(pub [u8; 4]);
|
||||
|
@ -4,6 +4,7 @@ use super::*;
|
||||
#[derive(
|
||||
Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, JsonSchema,
|
||||
)]
|
||||
#[cfg_attr(target_arch = "wasm32", derive(Tsify), tsify(from_wasm_abi, namespace))]
|
||||
pub enum Sequencing {
|
||||
NoPreference = 0,
|
||||
PreferOrdered = 1,
|
||||
@ -20,6 +21,7 @@ impl Default for Sequencing {
|
||||
#[derive(
|
||||
Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, JsonSchema,
|
||||
)]
|
||||
#[cfg_attr(target_arch = "wasm32", derive(Tsify), tsify(from_wasm_abi, namespace))]
|
||||
pub enum Stability {
|
||||
LowLatency = 0,
|
||||
Reliable = 1,
|
||||
@ -35,6 +37,7 @@ impl Default for Stability {
|
||||
#[derive(
|
||||
Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, JsonSchema,
|
||||
)]
|
||||
#[cfg_attr(target_arch = "wasm32", derive(Tsify), tsify(from_wasm_abi, namespace))]
|
||||
pub enum SafetySelection {
|
||||
/// Don't use a safety route, only specify the sequencing preference
|
||||
Unsafe(Sequencing),
|
||||
@ -61,9 +64,11 @@ impl Default for SafetySelection {
|
||||
#[derive(
|
||||
Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, JsonSchema,
|
||||
)]
|
||||
#[cfg_attr(target_arch = "wasm32", derive(Tsify))]
|
||||
pub struct SafetySpec {
|
||||
/// preferred safety route set id if it still exists
|
||||
#[schemars(with = "Option<String>")]
|
||||
#[cfg_attr(target_arch = "wasm32", tsify(optional, type = "string"))]
|
||||
pub preferred_route: Option<RouteId>,
|
||||
/// must be greater than 0
|
||||
pub hop_count: usize,
|
||||
|
@ -1,6 +1,7 @@
|
||||
use super::*;
|
||||
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
||||
#[cfg_attr(target_arch = "wasm32", derive(Tsify))]
|
||||
pub struct LatencyStats {
|
||||
pub fastest: TimestampDuration, // fastest latency in the ROLLING_LATENCIES_SIZE last latencies
|
||||
pub average: TimestampDuration, // average latency over the ROLLING_LATENCIES_SIZE last latencies
|
||||
@ -8,6 +9,7 @@ pub struct LatencyStats {
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
||||
#[cfg_attr(target_arch = "wasm32", derive(Tsify))]
|
||||
pub struct TransferStats {
|
||||
pub total: ByteCount, // total amount transferred ever
|
||||
pub maximum: ByteCount, // maximum rate over the ROLLING_TRANSFERS_SIZE last amounts
|
||||
@ -16,12 +18,14 @@ pub struct TransferStats {
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
||||
#[cfg_attr(target_arch = "wasm32", derive(Tsify))]
|
||||
pub struct TransferStatsDownUp {
|
||||
pub down: TransferStats,
|
||||
pub up: TransferStats,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
||||
#[cfg_attr(target_arch = "wasm32", derive(Tsify))]
|
||||
pub struct RPCStats {
|
||||
pub messages_sent: u32, // number of rpcs that have been sent in the total_time range
|
||||
pub messages_rcvd: u32, // number of rpcs that have been received in the total_time range
|
||||
@ -34,6 +38,7 @@ pub struct RPCStats {
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
||||
#[cfg_attr(target_arch = "wasm32", derive(Tsify))]
|
||||
pub struct PeerStats {
|
||||
pub time_added: Timestamp, // when the peer was added to the routing table
|
||||
pub rpc_stats: RPCStats, // information about RPCs
|
||||
|
@ -4,6 +4,7 @@ use super::*;
|
||||
#[derive(
|
||||
Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Copy, Serialize, Deserialize, JsonSchema,
|
||||
)]
|
||||
#[cfg_attr(target_arch = "wasm32", derive(Tsify), tsify(namespace))]
|
||||
pub enum VeilidLogLevel {
|
||||
Error = 1,
|
||||
Warn = 2,
|
||||
@ -80,8 +81,10 @@ impl fmt::Display for VeilidLogLevel {
|
||||
}
|
||||
/// A VeilidCore log message with optional backtrace
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
||||
#[cfg_attr(target_arch = "wasm32", derive(Tsify))]
|
||||
pub struct VeilidLog {
|
||||
pub log_level: VeilidLogLevel,
|
||||
pub message: String,
|
||||
#[cfg_attr(target_arch = "wasm32", tsify(optional))]
|
||||
pub backtrace: Option<String>,
|
||||
}
|
||||
|
@ -2,6 +2,11 @@ use super::*;
|
||||
|
||||
/// Attachment abstraction for network 'signal strength'
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize, JsonSchema)]
|
||||
#[cfg_attr(
|
||||
target_arch = "wasm32",
|
||||
derive(Tsify),
|
||||
tsify(namespace, from_wasm_abi, into_wasm_abi)
|
||||
)]
|
||||
pub enum AttachmentState {
|
||||
Detached = 0,
|
||||
Attaching = 1,
|
||||
@ -48,6 +53,7 @@ impl TryFrom<String> for AttachmentState {
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
||||
#[cfg_attr(target_arch = "wasm32", derive(Tsify))]
|
||||
pub struct VeilidStateAttachment {
|
||||
pub state: AttachmentState,
|
||||
pub public_internet_ready: bool,
|
||||
@ -55,14 +61,17 @@ pub struct VeilidStateAttachment {
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
||||
#[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,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
||||
#[cfg_attr(target_arch = "wasm32", derive(Tsify))]
|
||||
pub struct VeilidStateNetwork {
|
||||
pub started: bool,
|
||||
pub bps_down: ByteCount,
|
||||
@ -71,21 +80,27 @@ pub struct VeilidStateNetwork {
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
||||
#[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>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
||||
#[cfg_attr(target_arch = "wasm32", derive(Tsify))]
|
||||
pub struct VeilidStateConfig {
|
||||
pub config: VeilidConfigInner,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
||||
#[cfg_attr(target_arch = "wasm32", derive(Tsify))]
|
||||
pub struct VeilidValueChange {
|
||||
#[schemars(with = "String")]
|
||||
#[cfg_attr(target_arch = "wasm32", tsify(type = "string"))]
|
||||
pub key: TypedKey,
|
||||
pub subkeys: Vec<ValueSubkey>,
|
||||
pub count: u32,
|
||||
@ -93,6 +108,7 @@ pub struct VeilidValueChange {
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
||||
#[cfg_attr(target_arch = "wasm32", derive(Tsify), tsify(into_wasm_abi))]
|
||||
#[serde(tag = "kind")]
|
||||
pub enum VeilidUpdate {
|
||||
Log(VeilidLog),
|
||||
@ -105,10 +121,13 @@ pub enum VeilidUpdate {
|
||||
ValueChange(VeilidValueChange),
|
||||
Shutdown,
|
||||
}
|
||||
from_impl_to_jsvalue!(VeilidUpdate);
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
||||
#[cfg_attr(target_arch = "wasm32", derive(Tsify), tsify(into_wasm_abi))]
|
||||
pub struct VeilidState {
|
||||
pub attachment: VeilidStateAttachment,
|
||||
pub network: VeilidStateNetwork,
|
||||
pub config: VeilidStateConfig,
|
||||
}
|
||||
from_impl_to_jsvalue!(VeilidState);
|
||||
|
@ -1,7 +1,9 @@
|
||||
use crate::*;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#[cfg_attr(target_arch = "wasm32", declare)]
|
||||
pub type ConfigCallbackReturn = VeilidAPIResult<Box<dyn core::any::Any + Send>>;
|
||||
#[cfg_attr(target_arch = "wasm32", declare)]
|
||||
pub type ConfigCallback = Arc<dyn Fn(String) -> ConfigCallbackReturn + Send + Sync>;
|
||||
|
||||
/// Enable and configure HTTPS access to the Veilid node
|
||||
@ -15,10 +17,12 @@ pub type ConfigCallback = Arc<dyn Fn(String) -> ConfigCallbackReturn + Send + Sy
|
||||
/// ```
|
||||
///
|
||||
#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
||||
#[cfg_attr(target_arch = "wasm32", derive(Tsify))]
|
||||
pub struct VeilidConfigHTTPS {
|
||||
pub enabled: bool,
|
||||
pub listen_address: String,
|
||||
pub path: String,
|
||||
#[cfg_attr(target_arch = "wasm32", tsify(optional))]
|
||||
pub url: Option<String>, // Fixed URL is not optional for TLS-based protocols and is dynamically validated
|
||||
}
|
||||
|
||||
@ -33,10 +37,12 @@ pub struct VeilidConfigHTTPS {
|
||||
/// ```
|
||||
///
|
||||
#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
||||
#[cfg_attr(target_arch = "wasm32", derive(Tsify))]
|
||||
pub struct VeilidConfigHTTP {
|
||||
pub enabled: bool,
|
||||
pub listen_address: String,
|
||||
pub path: String,
|
||||
#[cfg_attr(target_arch = "wasm32", tsify(optional))]
|
||||
pub url: Option<String>,
|
||||
}
|
||||
|
||||
@ -47,6 +53,7 @@ pub struct VeilidConfigHTTP {
|
||||
/// To be implemented...
|
||||
///
|
||||
#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
||||
#[cfg_attr(target_arch = "wasm32", derive(Tsify))]
|
||||
pub struct VeilidConfigApplication {
|
||||
pub https: VeilidConfigHTTPS,
|
||||
pub http: VeilidConfigHTTP,
|
||||
@ -63,10 +70,12 @@ pub struct VeilidConfigApplication {
|
||||
/// ```
|
||||
///
|
||||
#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
||||
#[cfg_attr(target_arch = "wasm32", derive(Tsify))]
|
||||
pub struct VeilidConfigUDP {
|
||||
pub enabled: bool,
|
||||
pub socket_pool_size: u32,
|
||||
pub listen_address: String,
|
||||
#[cfg_attr(target_arch = "wasm32", tsify(optional))]
|
||||
pub public_address: Option<String>,
|
||||
}
|
||||
|
||||
@ -81,11 +90,13 @@ pub struct VeilidConfigUDP {
|
||||
/// public_address: ''
|
||||
///
|
||||
#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
||||
#[cfg_attr(target_arch = "wasm32", derive(Tsify))]
|
||||
pub struct VeilidConfigTCP {
|
||||
pub connect: bool,
|
||||
pub listen: bool,
|
||||
pub max_connections: u32,
|
||||
pub listen_address: String,
|
||||
#[cfg_attr(target_arch = "wasm32", tsify(optional))]
|
||||
pub public_address: Option<String>,
|
||||
}
|
||||
|
||||
@ -101,6 +112,7 @@ pub struct VeilidConfigTCP {
|
||||
/// url: 'ws://localhost:5150/ws'
|
||||
///
|
||||
#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
||||
#[cfg_attr(target_arch = "wasm32", derive(Tsify))]
|
||||
|
||||
pub struct VeilidConfigWS {
|
||||
pub connect: bool,
|
||||
@ -108,6 +120,7 @@ pub struct VeilidConfigWS {
|
||||
pub max_connections: u32,
|
||||
pub listen_address: String,
|
||||
pub path: String,
|
||||
#[cfg_attr(target_arch = "wasm32", tsify(optional))]
|
||||
pub url: Option<String>,
|
||||
}
|
||||
|
||||
@ -123,6 +136,7 @@ pub struct VeilidConfigWS {
|
||||
/// url: ''
|
||||
///
|
||||
#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
||||
#[cfg_attr(target_arch = "wasm32", derive(Tsify))]
|
||||
|
||||
pub struct VeilidConfigWSS {
|
||||
pub connect: bool,
|
||||
@ -130,6 +144,7 @@ pub struct VeilidConfigWSS {
|
||||
pub max_connections: u32,
|
||||
pub listen_address: String,
|
||||
pub path: String,
|
||||
#[cfg_attr(target_arch = "wasm32", tsify(optional))]
|
||||
pub url: Option<String>, // Fixed URL is not optional for TLS-based protocols and is dynamically validated
|
||||
}
|
||||
|
||||
@ -141,6 +156,7 @@ pub struct VeilidConfigWSS {
|
||||
/// sort out which protocol is used for each peer connection.
|
||||
///
|
||||
#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
||||
#[cfg_attr(target_arch = "wasm32", derive(Tsify))]
|
||||
|
||||
pub struct VeilidConfigProtocol {
|
||||
pub udp: VeilidConfigUDP,
|
||||
@ -158,6 +174,7 @@ pub struct VeilidConfigProtocol {
|
||||
/// connection_initial_timeout_ms: 2000
|
||||
///
|
||||
#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
||||
#[cfg_attr(target_arch = "wasm32", derive(Tsify))]
|
||||
pub struct VeilidConfigTLS {
|
||||
pub certificate_path: String,
|
||||
pub private_key_path: String,
|
||||
@ -167,6 +184,7 @@ pub struct VeilidConfigTLS {
|
||||
/// Configure the Distributed Hash Table (DHT)
|
||||
///
|
||||
#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
||||
#[cfg_attr(target_arch = "wasm32", derive(Tsify))]
|
||||
pub struct VeilidConfigDHT {
|
||||
pub max_find_node_count: u32,
|
||||
pub resolve_node_timeout_ms: u32,
|
||||
@ -192,10 +210,13 @@ pub struct VeilidConfigDHT {
|
||||
/// Configure RPC
|
||||
///
|
||||
#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
||||
#[cfg_attr(target_arch = "wasm32", derive(Tsify))]
|
||||
pub struct VeilidConfigRPC {
|
||||
pub concurrency: u32,
|
||||
pub queue_size: u32,
|
||||
#[cfg_attr(target_arch = "wasm32", tsify(optional))]
|
||||
pub max_timestamp_behind_ms: Option<u32>,
|
||||
#[cfg_attr(target_arch = "wasm32", tsify(optional))]
|
||||
pub max_timestamp_ahead_ms: Option<u32>,
|
||||
pub timeout_ms: u32,
|
||||
pub max_route_hop_count: u8,
|
||||
@ -205,6 +226,7 @@ pub struct VeilidConfigRPC {
|
||||
/// Configure the network routing table
|
||||
///
|
||||
#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
||||
#[cfg_attr(target_arch = "wasm32", derive(Tsify))]
|
||||
pub struct VeilidConfigRoutingTable {
|
||||
#[schemars(with = "Vec<String>")]
|
||||
pub node_id: TypedKeyGroup,
|
||||
@ -221,6 +243,7 @@ pub struct VeilidConfigRoutingTable {
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
||||
#[cfg_attr(target_arch = "wasm32", derive(Tsify))]
|
||||
pub struct VeilidConfigNetwork {
|
||||
pub connection_initial_timeout_ms: u32,
|
||||
pub connection_inactivity_timeout_ms: u32,
|
||||
@ -231,6 +254,7 @@ pub struct VeilidConfigNetwork {
|
||||
pub client_whitelist_timeout_ms: u32,
|
||||
pub reverse_connection_receipt_time_ms: u32,
|
||||
pub hole_punch_receipt_time_ms: u32,
|
||||
#[cfg_attr(target_arch = "wasm32", tsify(optional))]
|
||||
pub network_key_password: Option<String>,
|
||||
pub routing_table: VeilidConfigRoutingTable,
|
||||
pub rpc: VeilidConfigRPC,
|
||||
@ -244,33 +268,40 @@ pub struct VeilidConfigNetwork {
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
||||
#[cfg_attr(target_arch = "wasm32", derive(Tsify))]
|
||||
pub struct VeilidConfigTableStore {
|
||||
pub directory: String,
|
||||
pub delete: bool,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
||||
#[cfg_attr(target_arch = "wasm32", derive(Tsify))]
|
||||
pub struct VeilidConfigBlockStore {
|
||||
pub directory: String,
|
||||
pub delete: bool,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
||||
#[cfg_attr(target_arch = "wasm32", derive(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,
|
||||
#[cfg_attr(target_arch = "wasm32", tsify(optional))]
|
||||
pub new_device_encryption_key_password: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
||||
#[cfg_attr(target_arch = "wasm32", derive(Tsify))]
|
||||
pub struct VeilidConfigCapabilities {
|
||||
pub disable: Vec<FourCC>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Serialize, Deserialize, JsonSchema)]
|
||||
#[cfg_attr(target_arch = "wasm32", derive(Tsify))]
|
||||
#[cfg_attr(target_arch = "wasm32", tsify(namespace, from_wasm_abi))]
|
||||
pub enum VeilidConfigLogLevel {
|
||||
Off,
|
||||
Error,
|
||||
@ -358,6 +389,7 @@ impl fmt::Display for VeilidConfigLogLevel {
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
||||
#[cfg_attr(target_arch = "wasm32", derive(Tsify))]
|
||||
pub struct VeilidConfigInner {
|
||||
pub program_name: String,
|
||||
pub namespace: String,
|
||||
|
21
veilid-core/src/wasm_helpers.rs
Normal file
21
veilid-core/src/wasm_helpers.rs
Normal file
@ -0,0 +1,21 @@
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(target_arch = "wasm32")] {
|
||||
pub use tsify::*;
|
||||
pub use wasm_bindgen::prelude::*;
|
||||
|
||||
macro_rules! from_impl_to_jsvalue {
|
||||
($name: ident) => {
|
||||
impl From<$name> for JsValue {
|
||||
fn from(value: $name) -> Self {
|
||||
serde_wasm_bindgen::to_value(&value).unwrap()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
macro_rules! from_impl_to_jsvalue {
|
||||
($name: ident) => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
pub(crate) use from_impl_to_jsvalue;
|
2
veilid-wasm/.cargo/config.toml
Normal file
2
veilid-wasm/.cargo/config.toml
Normal file
@ -0,0 +1,2 @@
|
||||
[build]
|
||||
target = "wasm32-unknown-unknown"
|
@ -34,6 +34,9 @@ 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"
|
||||
parking_lot = "0.12.1"
|
||||
|
6
veilid-wasm/README.md
Normal file
6
veilid-wasm/README.md
Normal file
@ -0,0 +1,6 @@
|
||||
# veilid-wasm
|
||||
|
||||
## Notes
|
||||
|
||||
- [`wasm_bindgen`](https://rustwasm.github.io/wasm-bindgen/) is used to generate interop code between JavaScript and Rust, as well as basic TypeScript types.
|
||||
- [`tsify`](https://github.com/madonoharu/tsify) is used to export TypeScript types along-side [`wasm_bindgen`](https://rustwasm.github.io/wasm-bindgen/) and [`serde_wasm_bindgen`](https://github.com/cloudflare/serde-wasm-bindgen), and enables serialization/deserialization.
|
@ -19,11 +19,20 @@ 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::*;
|
||||
use wasm_bindgen_futures::*;
|
||||
|
||||
pub mod veilid_client_js;
|
||||
pub mod veilid_crypto_js;
|
||||
pub mod veilid_routing_context_js;
|
||||
pub mod veilid_table_db_js;
|
||||
|
||||
mod wasm_helpers;
|
||||
use wasm_helpers::*;
|
||||
|
||||
// Allocator
|
||||
extern crate wee_alloc;
|
||||
#[global_allocator]
|
||||
@ -56,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))
|
||||
@ -112,6 +132,7 @@ where
|
||||
// WASM-specific
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
#[cfg_attr(target_arch = "wasm32", derive(Tsify))]
|
||||
pub struct VeilidWASMConfigLoggingPerformance {
|
||||
pub enabled: bool,
|
||||
pub level: veilid_core::VeilidConfigLogLevel,
|
||||
@ -120,28 +141,38 @@ pub struct VeilidWASMConfigLoggingPerformance {
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
#[cfg_attr(target_arch = "wasm32", derive(Tsify))]
|
||||
pub struct VeilidWASMConfigLoggingAPI {
|
||||
pub enabled: bool,
|
||||
pub level: veilid_core::VeilidConfigLogLevel,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
#[cfg_attr(target_arch = "wasm32", derive(Tsify))]
|
||||
pub struct VeilidWASMConfigLogging {
|
||||
pub performance: VeilidWASMConfigLoggingPerformance,
|
||||
pub api: VeilidWASMConfigLoggingAPI,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
#[cfg_attr(target_arch = "wasm32", derive(Tsify), tsify(from_wasm_abi))]
|
||||
pub struct VeilidWASMConfig {
|
||||
pub logging: VeilidWASMConfigLogging,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
#[cfg_attr(
|
||||
target_arch = "wasm32",
|
||||
derive(Tsify),
|
||||
tsify(from_wasm_abi, into_wasm_abi)
|
||||
)]
|
||||
pub struct VeilidRouteBlob {
|
||||
pub route_id: veilid_core::RouteId,
|
||||
#[serde(with = "veilid_core::as_human_base64")]
|
||||
#[cfg_attr(target_arch = "wasm32", tsify(type = "string"))]
|
||||
pub blob: Vec<u8>,
|
||||
}
|
||||
from_impl_to_jsvalue!(VeilidRouteBlob);
|
||||
|
||||
// WASM Bindings
|
||||
|
||||
@ -215,6 +246,11 @@ pub fn change_log_level(layer: String, log_level: String) {
|
||||
}
|
||||
}
|
||||
|
||||
#[wasm_bindgen(typescript_custom_section)]
|
||||
const IUPDATE_VEILID_FUNCTION: &'static str = r#"
|
||||
type UpdateVeilidFunction = (event: VeilidUpdate) => void;
|
||||
"#;
|
||||
|
||||
#[wasm_bindgen()]
|
||||
pub fn startup_veilid_core(update_callback_js: Function, json_config: String) -> Promise {
|
||||
let update_callback_js = SendWrapper::new(update_callback_js);
|
||||
@ -378,7 +414,7 @@ pub fn routing_context_app_message(id: u32, target_string: String, message: Stri
|
||||
let routing_context = {
|
||||
let rc = (*ROUTING_CONTEXTS).borrow();
|
||||
let Some(routing_context) = rc.get(&id) else {
|
||||
return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument("routing_context_app_call", "id", id));
|
||||
return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument("routing_context_app_message", "id", id));
|
||||
};
|
||||
routing_context.clone()
|
||||
};
|
||||
@ -1454,6 +1490,8 @@ pub fn veilid_version_string() -> String {
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[cfg_attr(target_arch = "wasm32", derive(Tsify))]
|
||||
#[tsify(into_wasm_abi)]
|
||||
pub struct VeilidVersion {
|
||||
pub major: u32,
|
||||
pub minor: u32,
|
||||
|
169
veilid-wasm/src/veilid_client_js.rs
Normal file
169
veilid-wasm/src/veilid_client_js.rs
Normal file
@ -0,0 +1,169 @@
|
||||
#![allow(non_snake_case)]
|
||||
use super::*;
|
||||
|
||||
#[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(js_name = veilidClient)]
|
||||
pub struct VeilidClient {}
|
||||
|
||||
// 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 = veilidClient)]
|
||||
impl VeilidClient {
|
||||
pub async fn initializeCore(platformConfig: VeilidWASMConfig) {
|
||||
if INITIALIZED.swap(true, Ordering::Relaxed) {
|
||||
return;
|
||||
}
|
||||
console_error_panic_hook::set_once();
|
||||
|
||||
// Set up subscriber and layers
|
||||
let subscriber = Registry::default();
|
||||
let mut layers = Vec::new();
|
||||
let mut filters = (*FILTERS).borrow_mut();
|
||||
|
||||
// Performance logger
|
||||
if platformConfig.logging.performance.enabled {
|
||||
let filter =
|
||||
veilid_core::VeilidLayerFilter::new(platformConfig.logging.performance.level, None);
|
||||
let layer = WASMLayer::new(
|
||||
WASMLayerConfigBuilder::new()
|
||||
.set_report_logs_in_timings(platformConfig.logging.performance.logs_in_timings)
|
||||
.set_console_config(if platformConfig.logging.performance.logs_in_console {
|
||||
ConsoleConfig::ReportWithConsoleColor
|
||||
} else {
|
||||
ConsoleConfig::NoReporting
|
||||
})
|
||||
.build(),
|
||||
)
|
||||
.with_filter(filter.clone());
|
||||
filters.insert("performance", filter);
|
||||
layers.push(layer.boxed());
|
||||
};
|
||||
|
||||
// API logger
|
||||
if platformConfig.logging.api.enabled {
|
||||
let filter =
|
||||
veilid_core::VeilidLayerFilter::new(platformConfig.logging.api.level, None);
|
||||
let layer = veilid_core::ApiTracingLayer::get().with_filter(filter.clone());
|
||||
filters.insert("api", filter);
|
||||
layers.push(layer.boxed());
|
||||
}
|
||||
|
||||
let subscriber = subscriber.with(layers);
|
||||
subscriber
|
||||
.try_init()
|
||||
.map_err(|e| format!("failed to initialize logging: {}", e))
|
||||
.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,
|
||||
) -> APIResult<()> {
|
||||
let update_callback_js = SendWrapper::new(update_callback_js);
|
||||
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 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));
|
||||
APIRESULT_UNDEFINED
|
||||
}
|
||||
|
||||
// TODO: can we refine the TS type of `layer`?
|
||||
pub fn changeLogLevel(layer: String, log_level: VeilidConfigLogLevel) {
|
||||
let layer = if layer == "all" { "".to_owned() } else { layer };
|
||||
let filters = (*FILTERS).borrow();
|
||||
if layer.is_empty() {
|
||||
// Change all layers
|
||||
for f in filters.values() {
|
||||
f.set_max_level(log_level);
|
||||
}
|
||||
} else {
|
||||
// Change a specific layer
|
||||
let f = filters.get(layer.as_str()).unwrap();
|
||||
f.set_max_level(log_level);
|
||||
}
|
||||
}
|
||||
|
||||
/// Shut down Veilid and terminate the API.
|
||||
pub async fn shutdownCore() -> APIResult<()> {
|
||||
let veilid_api = take_veilid_api()?;
|
||||
veilid_api.shutdown().await;
|
||||
APIRESULT_UNDEFINED
|
||||
}
|
||||
|
||||
/// 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?;
|
||||
APIResult::Ok(core_state)
|
||||
}
|
||||
|
||||
/// Connect to the network.
|
||||
pub async fn attach() -> APIResult<()> {
|
||||
let veilid_api = get_veilid_api()?;
|
||||
veilid_api.attach().await?;
|
||||
APIRESULT_UNDEFINED
|
||||
}
|
||||
|
||||
/// Disconnect from the network.
|
||||
pub async fn detach() -> APIResult<()> {
|
||||
let veilid_api = get_veilid_api()?;
|
||||
veilid_api.detach().await?;
|
||||
APIRESULT_UNDEFINED
|
||||
}
|
||||
|
||||
/// 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 {
|
||||
major,
|
||||
minor,
|
||||
patch,
|
||||
};
|
||||
vv
|
||||
}
|
||||
|
||||
/// Return the cargo package version of veilid-core, in string format.
|
||||
pub fn versionString() -> String {
|
||||
veilid_core::veilid_version_string()
|
||||
}
|
||||
}
|
470
veilid-wasm/src/veilid_crypto_js.rs
Normal file
470
veilid-wasm/src/veilid_crypto_js.rs
Normal file
@ -0,0 +1,470 @@
|
||||
#![allow(non_snake_case)]
|
||||
use super::*;
|
||||
|
||||
#[wasm_bindgen]
|
||||
extern "C" {
|
||||
#[wasm_bindgen(typescript_type = "string[]")]
|
||||
pub type ValidCryptoKinds;
|
||||
}
|
||||
|
||||
#[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() -> StringArray {
|
||||
let res = veilid_core::VALID_CRYPTO_KINDS
|
||||
.iter()
|
||||
.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) -> 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 crypto_system = crypto.get(kind).ok_or_else(|| {
|
||||
veilid_core::VeilidAPIError::invalid_argument(
|
||||
"crypto_cached_dh",
|
||||
"kind",
|
||||
kind.to_string(),
|
||||
)
|
||||
})?;
|
||||
let out = crypto_system.cached_dh(&key, &secret)?;
|
||||
APIResult::Ok(out.to_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)?;
|
||||
let secret: veilid_core::SecretKey = veilid_core::SecretKey::from_str(&secret)?;
|
||||
|
||||
let veilid_api = get_veilid_api()?;
|
||||
let crypto = veilid_api.crypto()?;
|
||||
let crypto_system = crypto.get(kind).ok_or_else(|| {
|
||||
veilid_core::VeilidAPIError::invalid_argument(
|
||||
"crypto_compute_dh",
|
||||
"kind",
|
||||
kind.to_string(),
|
||||
)
|
||||
})?;
|
||||
let out = crypto_system.compute_dh(&key, &secret)?;
|
||||
APIResult::Ok(out.to_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 crypto_system = crypto.get(kind).ok_or_else(|| {
|
||||
veilid_core::VeilidAPIError::invalid_argument(
|
||||
"crypto_random_bytes",
|
||||
"kind",
|
||||
kind.to_string(),
|
||||
)
|
||||
})?;
|
||||
let out = crypto_system.random_bytes(len);
|
||||
let out = data_encoding::BASE64URL_NOPAD.encode(&out);
|
||||
APIResult::Ok(out)
|
||||
}
|
||||
|
||||
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 crypto_system = crypto.get(kind).ok_or_else(|| {
|
||||
veilid_core::VeilidAPIError::invalid_argument(
|
||||
"crypto_default_salt_length",
|
||||
"kind",
|
||||
kind.to_string(),
|
||||
)
|
||||
})?;
|
||||
let out = crypto_system.default_salt_length();
|
||||
APIResult::Ok(out)
|
||||
}
|
||||
|
||||
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())
|
||||
.unwrap();
|
||||
let salt: Vec<u8> = data_encoding::BASE64URL_NOPAD
|
||||
.decode(salt.as_bytes())
|
||||
.unwrap();
|
||||
|
||||
let veilid_api = get_veilid_api()?;
|
||||
let crypto = veilid_api.crypto()?;
|
||||
let crypto_system = crypto.get(kind).ok_or_else(|| {
|
||||
veilid_core::VeilidAPIError::invalid_argument(
|
||||
"crypto_hash_password",
|
||||
"kind",
|
||||
kind.to_string(),
|
||||
)
|
||||
})?;
|
||||
let out = crypto_system.hash_password(&password, &salt)?;
|
||||
APIResult::Ok(out)
|
||||
}
|
||||
|
||||
pub fn verifyPassword(
|
||||
kind: String,
|
||||
password: String,
|
||||
password_hash: String,
|
||||
) -> 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())
|
||||
.unwrap();
|
||||
|
||||
let veilid_api = get_veilid_api()?;
|
||||
let crypto = veilid_api.crypto()?;
|
||||
let crypto_system = crypto.get(kind).ok_or_else(|| {
|
||||
veilid_core::VeilidAPIError::invalid_argument(
|
||||
"crypto_verify_password",
|
||||
"kind",
|
||||
kind.to_string(),
|
||||
)
|
||||
})?;
|
||||
let out = crypto_system.verify_password(&password, &password_hash)?;
|
||||
APIResult::Ok(out)
|
||||
}
|
||||
|
||||
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())
|
||||
.unwrap();
|
||||
let salt: Vec<u8> = data_encoding::BASE64URL_NOPAD
|
||||
.decode(salt.as_bytes())
|
||||
.unwrap();
|
||||
|
||||
let veilid_api = get_veilid_api()?;
|
||||
let crypto = veilid_api.crypto()?;
|
||||
let crypto_system = crypto.get(kind).ok_or_else(|| {
|
||||
veilid_core::VeilidAPIError::invalid_argument(
|
||||
"crypto_derive_shared_secret",
|
||||
"kind",
|
||||
kind.to_string(),
|
||||
)
|
||||
})?;
|
||||
let out = crypto_system.derive_shared_secret(&password, &salt)?;
|
||||
APIResult::Ok(out.to_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 crypto_system = crypto.get(kind).ok_or_else(|| {
|
||||
veilid_core::VeilidAPIError::invalid_argument(
|
||||
"crypto_random_nonce",
|
||||
"kind",
|
||||
kind.to_string(),
|
||||
)
|
||||
})?;
|
||||
let out = crypto_system.random_nonce();
|
||||
APIResult::Ok(out.to_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 crypto_system = crypto.get(kind).ok_or_else(|| {
|
||||
veilid_core::VeilidAPIError::invalid_argument(
|
||||
"crypto_random_shared_secret",
|
||||
"kind",
|
||||
kind.to_string(),
|
||||
)
|
||||
})?;
|
||||
let out = crypto_system.random_shared_secret();
|
||||
APIResult::Ok(out.to_string())
|
||||
}
|
||||
|
||||
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 crypto_system = crypto.get(kind).ok_or_else(|| {
|
||||
veilid_core::VeilidAPIError::invalid_argument(
|
||||
"crypto_generate_key_pair",
|
||||
"kind",
|
||||
kind.to_string(),
|
||||
)
|
||||
})?;
|
||||
let out = crypto_system.generate_keypair();
|
||||
APIResult::Ok(out)
|
||||
}
|
||||
|
||||
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
|
||||
.decode(data.as_bytes())
|
||||
.unwrap();
|
||||
|
||||
let veilid_api = get_veilid_api()?;
|
||||
let crypto = veilid_api.crypto()?;
|
||||
let crypto_system = crypto.get(kind).ok_or_else(|| {
|
||||
veilid_core::VeilidAPIError::invalid_argument(
|
||||
"crypto_generate_hash",
|
||||
"kind",
|
||||
kind.to_string(),
|
||||
)
|
||||
})?;
|
||||
let out = crypto_system.generate_hash(&data);
|
||||
APIResult::Ok(out.to_string())
|
||||
}
|
||||
|
||||
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)?;
|
||||
let secret: veilid_core::SecretKey = veilid_core::SecretKey::from_str(&secret)?;
|
||||
|
||||
let veilid_api = get_veilid_api()?;
|
||||
let crypto = veilid_api.crypto()?;
|
||||
let crypto_system = crypto.get(kind).ok_or_else(|| {
|
||||
veilid_core::VeilidAPIError::invalid_argument(
|
||||
"crypto_validate_key_pair",
|
||||
"kind",
|
||||
kind.to_string(),
|
||||
)
|
||||
})?;
|
||||
let out = crypto_system.validate_keypair(&key, &secret);
|
||||
APIResult::Ok(out)
|
||||
}
|
||||
|
||||
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
|
||||
.decode(data.as_bytes())
|
||||
.unwrap();
|
||||
|
||||
let hash: veilid_core::HashDigest = veilid_core::HashDigest::from_str(&hash)?;
|
||||
|
||||
let veilid_api = get_veilid_api()?;
|
||||
let crypto = veilid_api.crypto()?;
|
||||
let crypto_system = crypto.get(kind).ok_or_else(|| {
|
||||
veilid_core::VeilidAPIError::invalid_argument(
|
||||
"crypto_validate_hash",
|
||||
"kind",
|
||||
kind.to_string(),
|
||||
)
|
||||
})?;
|
||||
let out = crypto_system.validate_hash(&data, &hash);
|
||||
APIResult::Ok(out)
|
||||
}
|
||||
|
||||
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)?;
|
||||
let key2: veilid_core::CryptoKey = veilid_core::CryptoKey::from_str(&key2)?;
|
||||
|
||||
let veilid_api = get_veilid_api()?;
|
||||
let crypto = veilid_api.crypto()?;
|
||||
let crypto_system = crypto.get(kind).ok_or_else(|| {
|
||||
veilid_core::VeilidAPIError::invalid_argument(
|
||||
"crypto_distance",
|
||||
"kind",
|
||||
kind.to_string(),
|
||||
)
|
||||
})?;
|
||||
let out = crypto_system.distance(&key1, &key2);
|
||||
APIResult::Ok(out.to_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)?;
|
||||
let secret: veilid_core::SecretKey = veilid_core::SecretKey::from_str(&secret)?;
|
||||
|
||||
let data: Vec<u8> = data_encoding::BASE64URL_NOPAD
|
||||
.decode(data.as_bytes())
|
||||
.unwrap();
|
||||
|
||||
let veilid_api = get_veilid_api()?;
|
||||
let crypto = veilid_api.crypto()?;
|
||||
let crypto_system = crypto.get(kind).ok_or_else(|| {
|
||||
veilid_core::VeilidAPIError::invalid_argument("crypto_sign", "kind", kind.to_string())
|
||||
})?;
|
||||
let out = crypto_system.sign(&key, &secret, &data)?;
|
||||
APIResult::Ok(out.to_string())
|
||||
}
|
||||
|
||||
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)?;
|
||||
let data: Vec<u8> = data_encoding::BASE64URL_NOPAD
|
||||
.decode(data.as_bytes())
|
||||
.unwrap();
|
||||
let signature: veilid_core::Signature = veilid_core::Signature::from_str(&signature)?;
|
||||
|
||||
let veilid_api = get_veilid_api()?;
|
||||
let crypto = veilid_api.crypto()?;
|
||||
let crypto_system = crypto.get(kind).ok_or_else(|| {
|
||||
veilid_core::VeilidAPIError::invalid_argument("crypto_verify", "kind", kind.to_string())
|
||||
})?;
|
||||
crypto_system.verify(&key, &data, &signature)?;
|
||||
APIRESULT_UNDEFINED
|
||||
}
|
||||
|
||||
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 crypto_system = crypto.get(kind).ok_or_else(|| {
|
||||
veilid_core::VeilidAPIError::invalid_argument(
|
||||
"crypto_aead_overhead",
|
||||
"kind",
|
||||
kind.to_string(),
|
||||
)
|
||||
})?;
|
||||
let out = crypto_system.aead_overhead();
|
||||
APIResult::Ok(out)
|
||||
}
|
||||
|
||||
pub fn decryptAead(
|
||||
kind: String,
|
||||
body: String,
|
||||
nonce: String,
|
||||
shared_secret: String,
|
||||
associated_data: Option<String>,
|
||||
) -> APIResult<String> {
|
||||
let kind: veilid_core::CryptoKind = veilid_core::FourCC::from_str(&kind)?;
|
||||
|
||||
let body: Vec<u8> = data_encoding::BASE64URL_NOPAD
|
||||
.decode(body.as_bytes())
|
||||
.unwrap();
|
||||
|
||||
let nonce: veilid_core::Nonce = veilid_core::Nonce::from_str(&nonce)?;
|
||||
|
||||
let shared_secret: veilid_core::SharedSecret =
|
||||
veilid_core::SharedSecret::from_str(&shared_secret)?;
|
||||
|
||||
let associated_data: Option<Vec<u8>> = associated_data.map(|ad| {
|
||||
data_encoding::BASE64URL_NOPAD
|
||||
.decode(ad.as_bytes())
|
||||
.unwrap()
|
||||
});
|
||||
|
||||
let veilid_api = get_veilid_api()?;
|
||||
let crypto = veilid_api.crypto()?;
|
||||
let crypto_system = crypto.get(kind).ok_or_else(|| {
|
||||
veilid_core::VeilidAPIError::invalid_argument(
|
||||
"crypto_decrypt_aead",
|
||||
"kind",
|
||||
kind.to_string(),
|
||||
)
|
||||
})?;
|
||||
let out = crypto_system.decrypt_aead(
|
||||
&body,
|
||||
&nonce,
|
||||
&shared_secret,
|
||||
match &associated_data {
|
||||
Some(ad) => Some(ad.as_slice()),
|
||||
None => None,
|
||||
},
|
||||
)?;
|
||||
let out = data_encoding::BASE64URL_NOPAD.encode(&out);
|
||||
APIResult::Ok(out)
|
||||
}
|
||||
|
||||
pub fn encryptAead(
|
||||
kind: String,
|
||||
body: String,
|
||||
nonce: String,
|
||||
shared_secret: String,
|
||||
associated_data: Option<String>,
|
||||
) -> APIResult<String> {
|
||||
let kind: veilid_core::CryptoKind = veilid_core::FourCC::from_str(&kind)?;
|
||||
|
||||
let body: Vec<u8> = data_encoding::BASE64URL_NOPAD
|
||||
.decode(body.as_bytes())
|
||||
.unwrap();
|
||||
|
||||
let nonce: veilid_core::Nonce = veilid_core::Nonce::from_str(&nonce).unwrap();
|
||||
|
||||
let shared_secret: veilid_core::SharedSecret =
|
||||
veilid_core::SharedSecret::from_str(&shared_secret).unwrap();
|
||||
|
||||
let associated_data: Option<Vec<u8>> = associated_data.map(|ad| {
|
||||
data_encoding::BASE64URL_NOPAD
|
||||
.decode(ad.as_bytes())
|
||||
.unwrap()
|
||||
});
|
||||
|
||||
let veilid_api = get_veilid_api()?;
|
||||
let crypto = veilid_api.crypto()?;
|
||||
let crypto_system = crypto.get(kind).ok_or_else(|| {
|
||||
veilid_core::VeilidAPIError::invalid_argument(
|
||||
"crypto_encrypt_aead",
|
||||
"kind",
|
||||
kind.to_string(),
|
||||
)
|
||||
})?;
|
||||
let out = crypto_system.encrypt_aead(
|
||||
&body,
|
||||
&nonce,
|
||||
&shared_secret,
|
||||
match &associated_data {
|
||||
Some(ad) => Some(ad.as_slice()),
|
||||
None => None,
|
||||
},
|
||||
)?;
|
||||
let out = data_encoding::BASE64URL_NOPAD.encode(&out);
|
||||
APIResult::Ok(out)
|
||||
}
|
||||
|
||||
pub fn cryptNoAuth(
|
||||
kind: String,
|
||||
body: String,
|
||||
nonce: String,
|
||||
shared_secret: String,
|
||||
) -> APIResult<String> {
|
||||
let kind: veilid_core::CryptoKind = veilid_core::FourCC::from_str(&kind)?;
|
||||
|
||||
let mut body: Vec<u8> = data_encoding::BASE64URL_NOPAD
|
||||
.decode(body.as_bytes())
|
||||
.unwrap();
|
||||
|
||||
let nonce: veilid_core::Nonce = veilid_core::Nonce::from_str(&nonce).unwrap();
|
||||
|
||||
let shared_secret: veilid_core::SharedSecret =
|
||||
veilid_core::SharedSecret::from_str(&shared_secret).unwrap();
|
||||
|
||||
let veilid_api = get_veilid_api()?;
|
||||
let crypto = veilid_api.crypto()?;
|
||||
let crypto_system = crypto.get(kind).ok_or_else(|| {
|
||||
veilid_core::VeilidAPIError::invalid_argument(
|
||||
"crypto_crypt_no_auth",
|
||||
"kind",
|
||||
kind.to_string(),
|
||||
)
|
||||
})?;
|
||||
crypto_system.crypt_in_place_no_auth(&mut body, &nonce, &shared_secret);
|
||||
let out = data_encoding::BASE64URL_NOPAD.encode(&body);
|
||||
APIResult::Ok(out)
|
||||
}
|
||||
}
|
333
veilid-wasm/src/veilid_routing_context_js.rs
Normal file
333
veilid-wasm/src/veilid_routing_context_js.rs
Normal file
@ -0,0 +1,333 @@
|
||||
#![allow(non_snake_case)]
|
||||
use super::*;
|
||||
|
||||
#[wasm_bindgen()]
|
||||
pub struct VeilidRoutingContext {
|
||||
inner_routing_context: Option<RoutingContext>,
|
||||
}
|
||||
|
||||
#[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() -> Self {
|
||||
Self {
|
||||
inner_routing_context: None,
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------------------
|
||||
// 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();
|
||||
Ok(VeilidRoutingContext {
|
||||
inner_routing_context: Some(routing_context),
|
||||
})
|
||||
}
|
||||
|
||||
/// 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()?;
|
||||
Ok(VeilidRoutingContext {
|
||||
inner_routing_context: Some(routing_context),
|
||||
})
|
||||
}
|
||||
|
||||
/// 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(safety_selection)?;
|
||||
Ok(VeilidRoutingContext {
|
||||
inner_routing_context: Some(routing_context),
|
||||
})
|
||||
}
|
||||
|
||||
/// 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);
|
||||
Ok(VeilidRoutingContext {
|
||||
inner_routing_context: Some(routing_context),
|
||||
})
|
||||
}
|
||||
|
||||
// --------------------------------
|
||||
// Static methods
|
||||
// --------------------------------
|
||||
|
||||
/// 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?;
|
||||
|
||||
let route_blob = VeilidRouteBlob { route_id, blob };
|
||||
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,
|
||||
) -> APIResult<VeilidRouteBlob> {
|
||||
let veilid_api = get_veilid_api()?;
|
||||
|
||||
let (route_id, blob) = veilid_api
|
||||
.new_custom_private_route(&veilid_core::VALID_CRYPTO_KINDS, stability, sequencing)
|
||||
.await?;
|
||||
|
||||
let route_blob = VeilidRouteBlob { route_id, blob };
|
||||
APIResult::Ok(route_blob)
|
||||
}
|
||||
|
||||
/// 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
|
||||
}
|
||||
|
||||
/// 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 message = unmarshall(message);
|
||||
let call_id = match call_id.parse() {
|
||||
Ok(v) => v,
|
||||
Err(e) => {
|
||||
return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument(
|
||||
e, "call_id", call_id,
|
||||
))
|
||||
}
|
||||
};
|
||||
let veilid_api = get_veilid_api()?;
|
||||
veilid_api.app_call_reply(call_id, message).await?;
|
||||
APIRESULT_UNDEFINED
|
||||
}
|
||||
|
||||
// --------------------------------
|
||||
// Instance methods
|
||||
// --------------------------------
|
||||
fn getRoutingContext(&self) -> APIResult<RoutingContext> {
|
||||
let Some(routing_context) = &self.inner_routing_context else {
|
||||
return APIResult::Err(veilid_core::VeilidAPIError::generic("Unable to getRoutingContext instance. inner_routing_context is None."));
|
||||
};
|
||||
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 message = unmarshall(message);
|
||||
|
||||
let veilid_api = get_veilid_api()?;
|
||||
let target = veilid_api.parse_as_target(target_string).await?;
|
||||
routing_context.app_message(target, message).await?;
|
||||
APIRESULT_UNDEFINED
|
||||
}
|
||||
|
||||
/// 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> = unmarshall(request);
|
||||
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).await?;
|
||||
let answer = marshall(&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,
|
||||
) -> APIResult<DHTRecordDescriptor> {
|
||||
let crypto_kind = if kind.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(veilid_core::FourCC::from_str(&kind)?)
|
||||
};
|
||||
let routing_context = self.getRoutingContext()?;
|
||||
|
||||
let dht_record_descriptor = routing_context
|
||||
.create_dht_record(schema, crypto_kind)
|
||||
.await?;
|
||||
APIResult::Ok(dht_record_descriptor)
|
||||
}
|
||||
|
||||
/// 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>,
|
||||
) -> 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 = self.getRoutingContext()?;
|
||||
let dht_record_descriptor = routing_context.open_dht_record(key, writer).await?;
|
||||
APIResult::Ok(dht_record_descriptor)
|
||||
}
|
||||
|
||||
/// 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 = self.getRoutingContext()?;
|
||||
routing_context.close_dht_record(key).await?;
|
||||
APIRESULT_UNDEFINED
|
||||
}
|
||||
|
||||
/// 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 = 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,
|
||||
) -> APIResult<Option<ValueData>> {
|
||||
let key = TypedKey::from_str(&key).unwrap();
|
||||
let routing_context = self.getRoutingContext()?;
|
||||
let res = routing_context
|
||||
.get_dht_value(key, subKey, forceRefresh)
|
||||
.await?;
|
||||
APIResult::Ok(res)
|
||||
}
|
||||
|
||||
/// 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,
|
||||
) -> 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 = self.getRoutingContext()?;
|
||||
let res = routing_context.set_dht_value(key, subKey, data).await?;
|
||||
APIResult::Ok(res)
|
||||
}
|
||||
|
||||
// pub async fn watchDhtValues(
|
||||
// &self,
|
||||
// key: String,
|
||||
// subKeys: ValueSubkeyRangeSet,
|
||||
// expiration: Timestamp,
|
||||
// count: u32,
|
||||
// ) -> APIResult<String> {
|
||||
// let key: veilid_core::TypedKey = veilid_core::deserialize_json(&key).unwrap();
|
||||
// let subkeys: veilid_core::ValueSubkeyRangeSet =
|
||||
// veilid_core::deserialize_json(&subkeys).unwrap();
|
||||
// let expiration = veilid_core::Timestamp::from_str(&expiration).unwrap();
|
||||
|
||||
// let routing_context = {
|
||||
// let rc = (*ROUTING_CONTEXTS).borrow();
|
||||
// let Some(routing_context) = rc.get(&id) else {
|
||||
// return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument("routing_context_watch_dht_values", "id", self.id));
|
||||
// };
|
||||
// routing_context.clone()
|
||||
// };
|
||||
// let res = routing_context
|
||||
// .watch_dht_values(key, subkeys, expiration, count)
|
||||
// .await?;
|
||||
// APIResult::Ok(res.to_string())
|
||||
// }
|
||||
|
||||
// pub async fn cancelDhtWatch(id: u32, key: String, subkeys: String) -> Promise {
|
||||
// let key: veilid_core::TypedKey = veilid_core::deserialize_json(&key).unwrap();
|
||||
// let subkeys: veilid_core::ValueSubkeyRangeSet =
|
||||
// veilid_core::deserialize_json(&subkeys).unwrap();
|
||||
|
||||
// let routing_context = {
|
||||
// let rc = (*ROUTING_CONTEXTS).borrow();
|
||||
// let Some(routing_context) = rc.get(&id) else {
|
||||
// return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument("routing_context_cancel_dht_watch", "id", self.id));
|
||||
// };
|
||||
// routing_context.clone()
|
||||
// };
|
||||
// let res = routing_context.cancel_dht_watch(key, subkeys).await?;
|
||||
// APIResult::Ok(res)
|
||||
// }
|
||||
}
|
176
veilid-wasm/src/veilid_table_db_js.rs
Normal file
176
veilid-wasm/src/veilid_table_db_js.rs
Normal file
@ -0,0 +1,176 @@
|
||||
#![allow(non_snake_case)]
|
||||
use super::*;
|
||||
|
||||
#[wasm_bindgen()]
|
||||
pub struct VeilidTableDB {
|
||||
inner_table_db: Option<TableDB>,
|
||||
tableName: String,
|
||||
columnCount: u32,
|
||||
}
|
||||
|
||||
#[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) -> Self {
|
||||
Self {
|
||||
inner_table_db: None,
|
||||
tableName,
|
||||
columnCount,
|
||||
}
|
||||
}
|
||||
|
||||
fn getTableDB(&self) -> APIResult<TableDB> {
|
||||
let Some(table_db) = &self.inner_table_db else {
|
||||
return APIResult::Err(veilid_core::VeilidAPIError::generic("Unable to getTableDB instance. Ensure you've called openTable()."));
|
||||
};
|
||||
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<()> {
|
||||
let veilid_api = get_veilid_api()?;
|
||||
let tstore = veilid_api.table_store()?;
|
||||
let table_db = tstore
|
||||
.open(&self.tableName, self.columnCount)
|
||||
.await
|
||||
.map_err(veilid_core::VeilidAPIError::generic)?;
|
||||
self.inner_table_db = Some(table_db);
|
||||
APIRESULT_UNDEFINED
|
||||
}
|
||||
|
||||
/// Delete this TableDB.
|
||||
pub async fn deleteTable(&mut self) -> APIResult<bool> {
|
||||
self.inner_table_db = None;
|
||||
|
||||
let veilid_api = get_veilid_api()?;
|
||||
let tstore = veilid_api.table_store()?;
|
||||
let deleted = tstore
|
||||
.delete(&self.tableName)
|
||||
.await
|
||||
.map_err(veilid_core::VeilidAPIError::generic)?;
|
||||
APIResult::Ok(deleted)
|
||||
}
|
||||
|
||||
async fn ensureOpen(&mut self) {
|
||||
if self.inner_table_db.is_none() {
|
||||
let _ = self.openTable().await;
|
||||
}
|
||||
}
|
||||
|
||||
/// 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 out = table_db.load(columnId, &key).await?.unwrap();
|
||||
let out = Some(marshall(&out));
|
||||
APIResult::Ok(out)
|
||||
}
|
||||
|
||||
/// 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()?;
|
||||
|
||||
table_db.store(columnId, &key, &value).await?;
|
||||
APIRESULT_UNDEFINED
|
||||
}
|
||||
|
||||
/// 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 out = table_db.delete(columnId, &key).await?.unwrap();
|
||||
let out = Some(marshall(&out));
|
||||
APIResult::Ok(out)
|
||||
}
|
||||
|
||||
/// 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 = self.getTableDB()?;
|
||||
|
||||
let keys = table_db.clone().get_keys(columnId).await?;
|
||||
let out: Vec<String> = keys.into_iter().map(|k| marshall(&k)).collect();
|
||||
let out = into_unchecked_string_array(out);
|
||||
|
||||
APIResult::Ok(out)
|
||||
}
|
||||
|
||||
/// 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 transaction = table_db.transact();
|
||||
APIResult::Ok(VeilidTableDBTransaction {
|
||||
inner_transaction: Some(transaction),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub struct VeilidTableDBTransaction {
|
||||
inner_transaction: Option<TableDBTransaction>,
|
||||
}
|
||||
|
||||
#[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() -> Self {
|
||||
Self {
|
||||
inner_transaction: None,
|
||||
}
|
||||
}
|
||||
|
||||
fn getTransaction(&self) -> APIResult<TableDBTransaction> {
|
||||
let Some(transaction) = &self.inner_transaction else {
|
||||
return APIResult::Err(veilid_core::VeilidAPIError::generic("Unable to getTransaction instance. inner_transaction is None."));
|
||||
};
|
||||
APIResult::Ok(transaction.clone())
|
||||
}
|
||||
|
||||
/// Commit the transaction. Performs all actions atomically.
|
||||
pub async fn commit(&self) -> APIResult<()> {
|
||||
let transaction = self.getTransaction()?;
|
||||
transaction.commit().await
|
||||
}
|
||||
|
||||
/// Rollback the transaction. Does nothing to the TableDB.
|
||||
pub fn rollback(&self) -> APIResult<()> {
|
||||
let transaction = self.getTransaction()?;
|
||||
transaction.rollback();
|
||||
APIRESULT_UNDEFINED
|
||||
}
|
||||
|
||||
/// 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)
|
||||
}
|
||||
}
|
38
veilid-wasm/src/wasm_helpers.rs
Normal file
38
veilid-wasm/src/wasm_helpers.rs
Normal file
@ -0,0 +1,38 @@
|
||||
use super::*;
|
||||
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(target_arch = "wasm32")] {
|
||||
pub use tsify::*;
|
||||
pub use wasm_bindgen::prelude::*;
|
||||
|
||||
macro_rules! from_impl_to_jsvalue {
|
||||
($name: ident) => {
|
||||
impl From<$name> for JsValue {
|
||||
fn from(value: $name) -> Self {
|
||||
serde_wasm_bindgen::to_value(&value).unwrap()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
macro_rules! from_impl_to_jsvalue {
|
||||
($name: ident) => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
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?
|
||||
}
|
@ -1,182 +1,218 @@
|
||||
//! Test suite for the Web and headless browsers.
|
||||
|
||||
//XXXXXXXXXXXXXXX
|
||||
//XXX DOES NOT WORK.
|
||||
//! These tests only work with WASM_BINDGEN_USE_NO_MODULE=true env var,
|
||||
//! as otherwise there's no way to access the generated wasm bindings from inside JS.
|
||||
|
||||
#![cfg(target_arch = "wasm32")]
|
||||
|
||||
extern crate alloc;
|
||||
extern crate wasm_bindgen_test;
|
||||
use core::sync::atomic::AtomicBool;
|
||||
use js_sys::*;
|
||||
use parking_lot::Once;
|
||||
use veilid_wasm::*;
|
||||
use wasm_bindgen::*;
|
||||
use wasm_bindgen_futures::*;
|
||||
use wasm_bindgen_test::*;
|
||||
|
||||
wasm_bindgen_test_configure!(run_in_browser);
|
||||
|
||||
static SETUP_ONCE: AtomicBool = AtomicBool::new(false);
|
||||
static SETUP_ONCE: Once = Once::new();
|
||||
pub fn setup() -> () {
|
||||
if SETUP_ONCE
|
||||
.compare_exchange(false, true, Ordering::Relaxed, Ordering::Relaxed)
|
||||
.is_ok()
|
||||
{
|
||||
console_log("setup()");
|
||||
SETUP_ONCE.call_once(|| {
|
||||
console_log!("setup()");
|
||||
console_error_panic_hook::set_once();
|
||||
wasm_logger::init(wasm_logger::Config::new(Level::Trace));
|
||||
init_callbacks();
|
||||
}
|
||||
})
|
||||
}
|
||||
// xxx needs updating to new keys and veilid_api object
|
||||
|
||||
fn init_callbacks() {
|
||||
assert_eq!(js_sys::eval(r#"
|
||||
window.sleep = (milliseconds) => { return new Promise(resolve => setTimeout(resolve, milliseconds)) };
|
||||
window.stateChangeCallback = async (stateChange) => { console.log("State change: " + stateChange); };
|
||||
window.configCallback = (configKey) => {
|
||||
switch(configKey) {
|
||||
case "namespace": return "";
|
||||
case "capabilities.disable": return [];
|
||||
case "tablestore.directory": return "";
|
||||
case "network.routing_table.node_id": return [];
|
||||
case "network.routing_table.node_id_secret": return [];
|
||||
case "network.routing_table.bootstrap": return [];
|
||||
case "network.routing_table.limit_over_attached": return 64;
|
||||
case "network.routing_table.limit_fully_attached": return 32;
|
||||
case "network.routing_table.limit_attached_strong": return 16;
|
||||
case "network.routing_table.limit_attached_good": return 8;
|
||||
case "network.routing_table.limit_attached_weak": return 4;
|
||||
case "network.rpc.concurrency": return 2;
|
||||
case "network.rpc.queue_size": return 128;
|
||||
case "network.rpc.max_timestamp_behind": return 10000000;
|
||||
case "network.rpc.max_timestamp_ahead": return 10000000;
|
||||
case "network.rpc.timeout": return 10000000;
|
||||
case "network.rpc.max_route_hop_count": return 4;
|
||||
case "network.rpc.default_route_hop_count": return 1;
|
||||
case "network.dht.max_find_node_count": return 20;
|
||||
case "network.dht.resolve_node_timeout": return 10000;
|
||||
case "network.dht.resolve_node_count": return 1;
|
||||
case "network.dht.resolve_node_fanout": return 4;
|
||||
case "network.dht.get_value_timeout": return 10000;
|
||||
case "network.dht.get_value_count": return 3;
|
||||
case "network.dht.get_value_fanout": return 4;
|
||||
case "network.dht.set_value_timeout": return 10000;
|
||||
case "network.dht.set_value_count": return 5;
|
||||
case "network.dht.set_value_fanout": return 4;
|
||||
case "network.dht.min_peer_count": return 20;
|
||||
case "network.dht.min_peer_refresh_time": return 2000000;
|
||||
case "network.dht.validate_dial_info_receipt_time": return 5000000;
|
||||
case "network.upnp": return false;
|
||||
case "network.detect_address_changes": return true;
|
||||
case "network.address_filter": return true;
|
||||
case "network.restricted_nat_retries": return 3;
|
||||
case "network.tls.certificate_path": return "";
|
||||
case "network.tls.private_key_path": return "";
|
||||
case "network.application.path": return "/app";
|
||||
case "network.application.https.enabled": return false;
|
||||
case "network.application.https.listen_address": return "";
|
||||
case "network.application.http.enabled": return false;
|
||||
case "network.application.http.listen_address": return "";
|
||||
case "network.protocol.udp.enabled": return false;
|
||||
case "network.protocol.udp.socket_pool_size": return 0;
|
||||
case "network.protocol.udp.listen_address": return "";
|
||||
case "network.protocol.udp.public_address": return "";
|
||||
case "network.protocol.tcp.connect": return false;
|
||||
case "network.protocol.tcp.listen": return false;
|
||||
case "network.protocol.tcp.max_connections": return 32;
|
||||
case "network.protocol.tcp.listen_address": return "";
|
||||
case "network.protocol.tcp.public_address": return "";
|
||||
case "network.protocol.ws.connect": return true;
|
||||
case "network.protocol.ws.listen": return false;
|
||||
case "network.protocol.ws.max_connections": return 16;
|
||||
case "network.protocol.ws.listen_address": return "";
|
||||
case "network.protocol.ws.path": return "/ws";
|
||||
case "network.protocol.ws.public_address": return "";
|
||||
case "network.protocol.wss.connect": return true;
|
||||
case "network.protocol.wss.listen": return false;
|
||||
case "network.protocol.wss.max_connections": return 16;
|
||||
case "network.protocol.wss.listen_address": return "";
|
||||
case "network.protocol.wss.path": return "/ws";
|
||||
case "network.protocol.wss.public_address": return "";
|
||||
default:
|
||||
console.log("config key '" + key +"' doesn't exist"); break;
|
||||
}
|
||||
window.stateChangeCallback = async (stateChange) => {
|
||||
delete stateChange.peers; // makes logs less verbose
|
||||
console.log("State change: ", JSON.stringify(stateChange, null, 2));
|
||||
};
|
||||
window.veilidCoreInitConfig = {
|
||||
logging: {
|
||||
api: {
|
||||
enabled: true,
|
||||
level: 'Info',
|
||||
},
|
||||
performance: {
|
||||
enabled: false,
|
||||
level: 'Info',
|
||||
logs_in_timings: false,
|
||||
logs_in_console: false,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
window.veilidCoreStartupConfig = {
|
||||
program_name: 'veilid-wasm-test',
|
||||
namespace: '',
|
||||
capabilities: {
|
||||
disable: [],
|
||||
},
|
||||
protected_store: {
|
||||
allow_insecure_fallback: true,
|
||||
always_use_insecure_storage: true,
|
||||
directory: '',
|
||||
delete: false,
|
||||
device_encryption_key_password: 'some-user-secret-value',
|
||||
// "new_device_encryption_key_password": "an-updated-user-secret-value"
|
||||
},
|
||||
table_store: {
|
||||
directory: '',
|
||||
delete: false,
|
||||
},
|
||||
block_store: {
|
||||
directory: '',
|
||||
delete: false,
|
||||
},
|
||||
network: {
|
||||
connection_initial_timeout_ms: 2000,
|
||||
connection_inactivity_timeout_ms: 60000,
|
||||
max_connections_per_ip4: 32,
|
||||
max_connections_per_ip6_prefix: 32,
|
||||
max_connections_per_ip6_prefix_size: 56,
|
||||
max_connection_frequency_per_min: 128,
|
||||
client_whitelist_timeout_ms: 300000,
|
||||
reverse_connection_receipt_time_ms: 5000,
|
||||
hole_punch_receipt_time_ms: 5000,
|
||||
network_key_password: '',
|
||||
disable_capabilites: [],
|
||||
routing_table: {
|
||||
node_id: [],
|
||||
node_id_secret: [],
|
||||
bootstrap: [
|
||||
'ws://bootstrap.veilid.net:5150/ws',
|
||||
],
|
||||
limit_over_attached: 64,
|
||||
limit_fully_attached: 32,
|
||||
limit_attached_strong: 16,
|
||||
limit_attached_good: 8,
|
||||
limit_attached_weak: 4,
|
||||
},
|
||||
rpc: {
|
||||
concurrency: 0,
|
||||
queue_size: 1024,
|
||||
max_timestamp_behind_ms: 10000,
|
||||
max_timestamp_ahead_ms: 10000,
|
||||
timeout_ms: 5000,
|
||||
max_route_hop_count: 4,
|
||||
default_route_hop_count: 1,
|
||||
},
|
||||
dht: {
|
||||
max_find_node_count: 20,
|
||||
resolve_node_timeout_ms: 10000,
|
||||
resolve_node_count: 1,
|
||||
resolve_node_fanout: 4,
|
||||
get_value_timeout_ms: 10000,
|
||||
get_value_count: 3,
|
||||
get_value_fanout: 4,
|
||||
set_value_timeout_ms: 10000,
|
||||
set_value_count: 5,
|
||||
set_value_fanout: 4,
|
||||
min_peer_count: 20,
|
||||
min_peer_refresh_time_ms: 60000,
|
||||
validate_dial_info_receipt_time_ms: 2000,
|
||||
local_subkey_cache_size: 128,
|
||||
local_max_subkey_cache_memory_mb: 256,
|
||||
remote_subkey_cache_size: 1024,
|
||||
remote_max_records: 65536,
|
||||
remote_max_subkey_cache_memory_mb: 256,
|
||||
remote_max_storage_space_mb: 0,
|
||||
},
|
||||
upnp: true,
|
||||
detect_address_changes: true,
|
||||
restricted_nat_retries: 0,
|
||||
tls: {
|
||||
certificate_path: '',
|
||||
private_key_path: '',
|
||||
connection_initial_timeout_ms: 2000,
|
||||
},
|
||||
application: {
|
||||
https: {
|
||||
enabled: false,
|
||||
listen_address: ':5150',
|
||||
path: 'app',
|
||||
},
|
||||
http: {
|
||||
enabled: false,
|
||||
listen_address: ':5150',
|
||||
path: 'app',
|
||||
},
|
||||
},
|
||||
protocol: {
|
||||
udp: {
|
||||
enabled: false,
|
||||
socket_pool_size: 0,
|
||||
listen_address: '',
|
||||
},
|
||||
tcp: {
|
||||
connect: false,
|
||||
listen: false,
|
||||
max_connections: 32,
|
||||
listen_address: '',
|
||||
},
|
||||
ws: {
|
||||
connect: true,
|
||||
listen: true,
|
||||
max_connections: 16,
|
||||
listen_address: ':5150',
|
||||
path: 'ws',
|
||||
},
|
||||
wss: {
|
||||
connect: true,
|
||||
listen: false,
|
||||
max_connections: 16,
|
||||
listen_address: '',
|
||||
path: 'ws',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
true
|
||||
"#).expect("failed to eval"), JsValue::TRUE);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
fn test_construct() {
|
||||
setup();
|
||||
|
||||
assert_eq!(
|
||||
js_sys::eval(
|
||||
r#"
|
||||
let vc = new VeilidCore();
|
||||
true
|
||||
"#
|
||||
)
|
||||
.expect("failed to eval"),
|
||||
JsValue::TRUE
|
||||
);
|
||||
}
|
||||
|
||||
#[wasm_bindgen_test(async)]
|
||||
async fn test_startup_shutdown() {
|
||||
setup();
|
||||
|
||||
assert_eq!(
|
||||
JsFuture::from(
|
||||
js_sys::eval(
|
||||
r#"
|
||||
(async function() {
|
||||
let vc = new VeilidCore();
|
||||
await vc.startup(window.stateChangeCallback, window.configCallback);
|
||||
await vc.shutdown();
|
||||
return true;
|
||||
})().then(v => {
|
||||
console.log("finished: " + v);
|
||||
return v;
|
||||
});
|
||||
"#
|
||||
)
|
||||
.expect("failed to eval")
|
||||
/// Helper for converting an eval Promise result into a JsValue
|
||||
async fn eval_promise(source: &str) -> JsValue {
|
||||
JsFuture::from(
|
||||
eval(source)
|
||||
.expect("Failed to eval")
|
||||
.dyn_into::<Promise>()
|
||||
.unwrap()
|
||||
)
|
||||
.await,
|
||||
Ok(JsValue::TRUE)
|
||||
);
|
||||
.unwrap(),
|
||||
)
|
||||
.await
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
#[wasm_bindgen_test(async)]
|
||||
async fn test_attach_detach() {
|
||||
// ----------------------------------------------------------------
|
||||
|
||||
// TODO: now that veilidClient uses a single instance of VeilidAPI,
|
||||
// subsequent tests fail because veilidCore has already been initialized.
|
||||
#[wasm_bindgen_test()]
|
||||
async fn test_kitchen_sink() {
|
||||
setup();
|
||||
|
||||
assert_eq!(
|
||||
JsFuture::from(
|
||||
js_sys::eval(
|
||||
r#"
|
||||
(async function() {
|
||||
let vc = new VeilidCore();
|
||||
await vc.startup(window.stateChangeCallback, window.configCallback);
|
||||
await vc.attach();
|
||||
await window.sleep(1000);
|
||||
await vc.detach();
|
||||
await vc.shutdown();
|
||||
return true;
|
||||
})().then(v => {
|
||||
console.log("finished: " + v);
|
||||
return v;
|
||||
});
|
||||
"#
|
||||
)
|
||||
.expect("failed to eval")
|
||||
.dyn_into::<Promise>()
|
||||
.unwrap()
|
||||
)
|
||||
.await,
|
||||
Ok(JsValue::TRUE)
|
||||
);
|
||||
let res = eval_promise(
|
||||
r#"
|
||||
(async function () {
|
||||
const { veilidClient } = wasm_bindgen; // only accessible in no_module mode.
|
||||
veilidClient.initializeCore(window.veilidCoreInitConfig);
|
||||
await veilidClient.startupCore(window.stateChangeCallback, JSON.stringify(window.veilidCoreStartupConfig));
|
||||
|
||||
console.log(veilidClient.versionString());
|
||||
await veilidClient.attach();
|
||||
|
||||
await sleep(10000);
|
||||
await veilidClient.detach();
|
||||
await veilidClient.shutdownCore();
|
||||
|
||||
return true;
|
||||
})();
|
||||
"#,
|
||||
).await;
|
||||
|
||||
assert_eq!(res, JsValue::TRUE);
|
||||
}
|
||||
|
@ -37,7 +37,7 @@ if [[ "$1" == "release" ]]; then
|
||||
|
||||
cargo build --target wasm32-unknown-unknown --release
|
||||
mkdir -p $OUTPUTDIR
|
||||
wasm-bindgen --out-dir $OUTPUTDIR --target web $INPUTDIR/veilid_wasm.wasm
|
||||
wasm-bindgen --out-dir $OUTPUTDIR --target web --weak-refs $INPUTDIR/veilid_wasm.wasm
|
||||
wasm-strip $OUTPUTDIR/veilid_wasm_bg.wasm
|
||||
else
|
||||
OUTPUTDIR=../target/wasm32-unknown-unknown/debug/pkg
|
||||
@ -45,7 +45,7 @@ else
|
||||
|
||||
RUSTFLAGS="-O -g $RUSTFLAGS" cargo build --target wasm32-unknown-unknown
|
||||
mkdir -p $OUTPUTDIR
|
||||
wasm-bindgen --out-dir $OUTPUTDIR --target web --keep-debug --debug $INPUTDIR/veilid_wasm.wasm
|
||||
wasm-bindgen --out-dir $OUTPUTDIR --target web --weak-refs --keep-debug --debug $INPUTDIR/veilid_wasm.wasm
|
||||
./wasm-sourcemap.py $OUTPUTDIR/veilid_wasm_bg.wasm -o $OUTPUTDIR/veilid_wasm_bg.wasm.map --dwarfdump $DWARFDUMP
|
||||
# wasm-strip $OUTPUTDIR/veilid_wasm_bg.wasm
|
||||
fi
|
||||
|
4
veilid-wasm/wasm_test.sh
Executable file
4
veilid-wasm/wasm_test.sh
Executable file
@ -0,0 +1,4 @@
|
||||
#!/bin/bash
|
||||
set -eo pipefail
|
||||
|
||||
WASM_BINDGEN_USE_NO_MODULE=true wasm-pack test --firefox "$@"
|
Loading…
Reference in New Issue
Block a user