diff --git a/Cargo.toml b/Cargo.toml index e0b5bbdf..9681ddfa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -108,7 +108,7 @@ trailbase-wasm-common = { path = "crates/wasm-runtime-common", version = "0.2.0" trailbase-wasm-runtime-host = { path = "crates/wasm-runtime-host", version = "0.1.0" } ts-rs = { version = "11", features = ["uuid-impl", "serde-json-impl", "indexmap-impl"] } uuid = { version = "1", default-features = false, features = ["std", "v4", "v7", "serde"] } -wasmtime = { version = "41.0.0", features = ["winch"] } +wasmtime = { version = "41.0.0", features = ["winch", "component-model-async"] } wasmtime-wasi = { version = "41.0.0", default-features = false, features = [] } -wasmtime-wasi-http = { version = "41.0.0", features = [] } +wasmtime-wasi-http = { version = "41.0.0", features = ["component-model-async"] } wasmtime-wasi-io = { version = "41.0.0", features = [] } diff --git a/client/testfixture/wasm/wasm_guest_testfixture.wasm b/client/testfixture/wasm/wasm_guest_testfixture.wasm index 819eb8c9..80d9d6c3 100644 Binary files a/client/testfixture/wasm/wasm_guest_testfixture.wasm and b/client/testfixture/wasm/wasm_guest_testfixture.wasm differ diff --git a/crates/wasm-runtime-guest/Cargo.toml b/crates/wasm-runtime-guest/Cargo.toml index aa6712bd..40ee8fcb 100644 --- a/crates/wasm-runtime-guest/Cargo.toml +++ b/crates/wasm-runtime-guest/Cargo.toml @@ -3,6 +3,7 @@ name = "trailbase-wasm" version = "0.5.0" edition = "2024" license = "OSL-3.0" +rust-version = "1.93" description = "WASM runtime for the TrailBase framework" homepage = "https://trailbase.io" repository = "https://github.com/trailbaseio/trailbase" diff --git a/crates/wasm-runtime-guest/src/db.rs b/crates/wasm-runtime-guest/src/db.rs index 2ca881a3..30ad84ef 100644 --- a/crates/wasm-runtime-guest/src/db.rs +++ b/crates/wasm-runtime-guest/src/db.rs @@ -1,9 +1,8 @@ use trailbase_sqlvalue::{Blob, DecodeError, SqlValue}; -use wstd::http::body::IntoBody; -use wstd::http::{Client, Request}; use crate::wit::trailbase::database::sqlite::{ - tx_begin, tx_commit, tx_execute, tx_query, tx_rollback, + execute as call_execute, query as call_query, tx_begin, tx_commit, tx_execute, tx_query, + tx_rollback, }; pub use crate::wit::trailbase::database::sqlite::{TxError, Value}; @@ -62,84 +61,18 @@ pub async fn query( query: impl std::string::ToString, params: impl Into>, ) -> Result>, Error> { - let r = SqliteRequest { - query: query.to_string(), - params: params.into().into_iter().map(to_sql_value).collect(), - }; - let request = Request::builder() - .uri("http://__sqlite/query") - .method("POST") - .body( - serde_json::to_vec(&r) - .map_err(|_| Error::UnexpectedType)? - .into_body(), - ) - .map_err(|err| Error::Other(err.to_string()))?; - - let client = Client::new(); - let (_parts, mut body) = client - .send(request) + return call_query(query.to_string(), params.into()) .await - .map_err(|err| Error::Other(err.to_string()))? - .into_parts(); - - let bytes = body - .bytes() - .await - .map_err(|err| Error::Other(err.to_string()))?; - - return match serde_json::from_slice(&bytes) { - Ok(SqliteResponse::Query { rows }) => Ok( - rows - .into_iter() - .map(|row| { - row - .into_iter() - .map(from_sql_value) - .collect::, _>>() - }) - .collect::, _>>()?, - ), - Ok(_) => Err(Error::UnexpectedType), - Err(err) => Err(Error::Other(err.to_string())), - }; + .map_err(|err| Error::Other(err.to_string())); } pub async fn execute( query: impl std::string::ToString, params: impl Into>, -) -> Result { - let r = SqliteRequest { - query: query.to_string(), - params: params.into().into_iter().map(to_sql_value).collect(), - }; - let request = Request::builder() - .uri("http://__sqlite/execute") - .method("POST") - .body( - serde_json::to_vec(&r) - .map_err(|_| Error::UnexpectedType)? - .into_body(), - ) - .map_err(|err| Error::Other(err.to_string()))?; - - let client = Client::new(); - let (_parts, mut body) = client - .send(request) +) -> Result { + return call_execute(query.to_string(), params.into()) .await - .map_err(|err| Error::Other(err.to_string()))? - .into_parts(); - - let bytes = body - .bytes() - .await - .map_err(|err| Error::Other(err.to_string()))?; - - return match serde_json::from_slice(&bytes) { - Ok(SqliteResponse::Execute { rows_affected }) => Ok(rows_affected), - Ok(_) => Err(Error::UnexpectedType), - Err(err) => Err(Error::Other(err.to_string())), - }; + .map_err(|err| Error::Other(err.to_string())); } // fn from_json_value(value: serde_json::Value) -> Result { @@ -165,15 +98,15 @@ pub async fn execute( // }; // } -fn from_sql_value(value: SqlValue) -> Result { - return match value { - SqlValue::Null => Ok(Value::Null), - SqlValue::Integer(v) => Ok(Value::Integer(v)), - SqlValue::Real(v) => Ok(Value::Real(v)), - SqlValue::Text(v) => Ok(Value::Text(v)), - SqlValue::Blob(v) => Ok(Value::Blob(v.into_bytes()?)), - }; -} +// fn from_sql_value(value: SqlValue) -> Result { +// return match value { +// SqlValue::Null => Ok(Value::Null), +// SqlValue::Integer(v) => Ok(Value::Integer(v)), +// SqlValue::Real(v) => Ok(Value::Real(v)), +// SqlValue::Text(v) => Ok(Value::Text(v)), +// SqlValue::Blob(v) => Ok(Value::Blob(v.into_bytes()?)), +// }; +// } // #[derive(Serialize)] // struct Blob { diff --git a/crates/wasm-runtime-guest/wit/trailbase/component/world.wit b/crates/wasm-runtime-guest/wit/trailbase/component/world.wit index b13aa1a7..a71d00b4 100644 --- a/crates/wasm-runtime-guest/wit/trailbase/component/world.wit +++ b/crates/wasm-runtime-guest/wit/trailbase/component/world.wit @@ -25,7 +25,7 @@ world interfaces { // TrailBase's interfaces: @since(version = 0.1.0) - include trailbase:database/interfaces@0.1.0; + include trailbase:database/interfaces@0.1.1; @since(version = 0.1.0) export init-endpoint; @@ -38,7 +38,7 @@ world interfaces { world init { // TrailBase's interfaces: @since(version = 0.1.0) - include trailbase:database/interfaces@0.1.0; + include trailbase:database/interfaces@0.1.1; @since(version = 0.1.0) export init-endpoint; diff --git a/crates/wasm-runtime-guest/wit/trailbase/database/world.wit b/crates/wasm-runtime-guest/wit/trailbase/database/world.wit index 928f5506..c573fdfd 100644 --- a/crates/wasm-runtime-guest/wit/trailbase/database/world.wit +++ b/crates/wasm-runtime-guest/wit/trailbase/database/world.wit @@ -1,4 +1,4 @@ -package trailbase:database@0.1.0; +package trailbase:database@0.1.1; interface sqlite { // WARNING: Evolving a variant currently breaks the ABI: @@ -15,12 +15,10 @@ interface sqlite { real(f64), } - // NOTE: Ideally, we'd use these but they can currently block guests, w/o a - // better non-blocking event loop. - // @since(version = 0.1.0) - // execute: func(query: string, params: list) -> result; - // @since(version = 0.1.0) - // query: func(query: string, params: list) -> result>, tx-error>; + @since(version = 0.1.1) + execute: async func(query: string, params: list) -> result; + @since(version = 0.1.1) + query: async func(query: string, params: list) -> result>, tx-error>; // However, transactions have to be sync. @since(version = 0.1.0) diff --git a/crates/wasm-runtime-host/Cargo.toml b/crates/wasm-runtime-host/Cargo.toml index b75e2ba3..50f51d01 100644 --- a/crates/wasm-runtime-host/Cargo.toml +++ b/crates/wasm-runtime-host/Cargo.toml @@ -3,6 +3,7 @@ name = "trailbase-wasm-runtime-host" version = "0.1.0" edition = "2024" license = "OSL-3.0" +rust-version = "1.93" description = "WASM runtime for the TrailBase framework" homepage = "https://trailbase.io" exclude = [ diff --git a/crates/wasm-runtime-host/src/host.rs b/crates/wasm-runtime-host/src/host.rs index 121a8268..f621cb44 100644 --- a/crates/wasm-runtime-host/src/host.rs +++ b/crates/wasm-runtime-host/src/host.rs @@ -5,7 +5,7 @@ use std::sync::Arc; use trailbase_sqlite::{Params, Rows}; use trailbase_wasi_keyvalue::WasiKeyValueCtx; use wasmtime::Result; -use wasmtime::component::{HasData, ResourceTable}; +use wasmtime::component::{Accessor, HasData, ResourceTable}; use wasmtime_wasi::{WasiCtx, WasiCtxView, WasiView}; use wasmtime_wasi_http::{WasiHttpCtx, WasiHttpView}; use wasmtime_wasi_io::IoView; @@ -40,6 +40,7 @@ wasmtime::component::bindgen!({ // to return traps from generated functions. imports: { "trailbase:database/sqlite.tx-begin": async, + "trailbase:database/sqlite.execute": async, }, exports: { default: async | store | task_exit, @@ -145,6 +146,52 @@ impl HasData for State { type Data<'a> = &'a mut State; } +impl self::trailbase::database::sqlite::HostWithStore for State { + async fn execute( + accessor: &Accessor, + query: String, + params: Vec, + ) -> Result { + let Some(conn) = accessor.with(|mut a| a.get().shared.conn.clone()) else { + return Err(TxError::Other("missing conn".to_string())); + }; + + let params: Vec<_> = params.into_iter().map(to_sqlite_value).collect(); + + return conn + .execute(query, params) + .await + .map_err(|err| TxError::Other(err.to_string())) + .map(|v| v as u64); + } + + async fn query( + accessor: &Accessor, + query: String, + params: Vec, + ) -> Result>, TxError> { + let Some(conn) = accessor.with(|mut a| a.get().shared.conn.clone()) else { + return Err(TxError::Other("missing conn".to_string())); + }; + + let params: Vec<_> = params.into_iter().map(to_sqlite_value).collect(); + + let rows = conn + .write_query_rows(query, params) + .await + .map_err(|err| TxError::Other(err.to_string()))?; + + let values: Vec<_> = rows + .into_iter() + .map(|trailbase_sqlite::Row(row, _col)| { + return row.into_iter().map(from_sqlite_value).collect::>(); + }) + .collect(); + + return Ok(values); + } +} + impl self::trailbase::database::sqlite::Host for State { fn tx_begin(&mut self) -> impl Future> + Send { async fn begin( diff --git a/crates/wasm-runtime-host/src/lib.rs b/crates/wasm-runtime-host/src/lib.rs index 316fadcc..839db86f 100644 --- a/crates/wasm-runtime-host/src/lib.rs +++ b/crates/wasm-runtime-host/src/lib.rs @@ -426,12 +426,14 @@ fn build_config(cache: Option, use_winch: bool) -> Config { // calling synchronous bindings will panic. config.async_support(true); config.wasm_component_model(true); + config.wasm_component_model_async(true); // config.wasm_backtrace_details(wasmtime::WasmBacktraceDetails::Enable); // Compilation settings. config.cache(cache); - if use_winch { + // FIXME: ASYNC component model doesn't currently support winch; + if false && use_winch { config.strategy(wasmtime::Strategy::Winch); } else { config.strategy(wasmtime::Strategy::Cranelift);