commit 2e65f6dd94d6e7e9b5bc78fe83efaf320af62179
parent b5ddc8fd1c926a1f04f8e505ee0ac8174e6a390b
Author: triesap <tyson@radroots.org>
Date: Thu, 13 Nov 2025 01:03:28 +0000
workspace: add `tangle-sql-wasm` crate and wasm build pipeline
Diffstat:
8 files changed, 197 insertions(+), 17 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
@@ -1862,6 +1862,29 @@ dependencies = [
]
[[package]]
+name = "radroots-tangle-sql"
+version = "0.1.0"
+dependencies = [
+ "radroots-sql-core",
+ "radroots-tangle-schema",
+ "serde_json",
+]
+
+[[package]]
+name = "radroots-tangle-sql-wasm"
+version = "0.1.0"
+dependencies = [
+ "radroots-sql-core",
+ "radroots-sql-wasm-core",
+ "radroots-tangle-schema",
+ "radroots-tangle-sql",
+ "serde",
+ "serde-wasm-bindgen",
+ "serde_json",
+ "wasm-bindgen",
+]
+
+[[package]]
name = "radroots-trade"
version = "0.1.0"
dependencies = [
diff --git a/Cargo.toml b/Cargo.toml
@@ -14,6 +14,8 @@ members = [
"sql-wasm-core",
"sql-core",
"tangle-schema",
+ "tangle-sql",
+ "tangle-sql-wasm",
"trade",
"types",
]
@@ -40,6 +42,8 @@ radroots-sql-wasm-bridge = { path = "sql-wasm-bridge", version = "0.1.0" }
radroots-sql-wasm-core = { path = "sql-wasm-core", version = "0.1.0" }
radroots-sql-core = { path = "sql-core", version = "0.1.0" }
radroots-tangle-schema = { path = "tangle-schema", version = "0.1.0", default-features = false }
+radroots-tangle-sql = { path = "tangle-sql", version = "0.1.0", default-features = false }
+radroots-tangle-sql-wasm = { path = "tangle-sql-wasm", version = "0.1.0" }
radroots-trade = { path = "trade", version = "0.1.0", default-features = false }
radroots-types = { path = "types", version = "0.1.0", default-features = false }
diff --git a/Makefile b/Makefile
@@ -1,28 +1,44 @@
-.PHONY: all bindings bindings-tangle-schema bindings-types clean help
+.PHONY: all bindings clean help \
+ bindings-tangle-schema bindings-types \
+ build build-tangle-sql-wasm
+
SHELL := /bin/bash
.SHELLFLAGS := -e -o pipefail -c
TS_RS_FEATURE ?= ts-rs
-all: bindings
+BINDINGS_TARGETS := \
+ bindings-tangle-schema \
+ bindings-types
+
+BUILD_TARGETS := \
+ build-tangle-sql-wasm
+
+all: bindings build
+
+bindings: $(BINDINGS_TARGETS)
-bindings: bindings-tangle-schema bindings-types
- @echo "All bindings built successfully."
+build: $(BUILD_TARGETS)
+
+clean:
+ cargo clean
+
+help:
+ @echo "Commands:"
+ @echo " make all"
+ @echo " make bindings"
+ @echo " make build"
+ @echo " make clean"
+ @echo " make help"
+ @printf "%s\n" $(BINDINGS_TARGETS)
+ @printf "%s\n" $(BUILD_TARGETS)
bindings-tangle-schema:
- @echo "Building tangle-schema bindings"
@(cd tangle-schema && cargo test --features $(TS_RS_FEATURE))
@(cd tangle-schema/bindings/ts && yarn build)
bindings-types:
- @echo "Building types bindings"
@(cd types && cargo test --features $(TS_RS_FEATURE))
@(cd types/bindings/ts && yarn build)
-clean:
- cargo clean
-
-help:
- @echo "Usage:"
- @echo " make bindings Build all Rust + TS bindings for all crates"
- @echo " make clean Remove build artifacts"
- @echo " make help Show this help message"
+build-tangle-sql-wasm:
+ wasm-pack build tangle-sql-wasm --release --target web --out-dir ../tangle-sql-wasm/pkg/dist --scope radroots
diff --git a/sql-core/src/lib.rs b/sql-core/src/lib.rs
@@ -5,9 +5,9 @@ extern crate alloc;
pub mod error;
-#[cfg(feature = "web")]
+#[cfg(all(feature = "web", target_arch = "wasm32"))]
mod executor_wasm;
-#[cfg(feature = "web")]
+#[cfg(all(feature = "web", target_arch = "wasm32"))]
pub use executor_wasm::WasmSqlExecutor;
#[cfg(feature = "native")]
@@ -23,7 +23,7 @@ pub use executor_embedded::EmbeddedSqlExecutor;
#[cfg(not(any(feature = "embedded", target_os = "espidf")))]
pub mod utils;
-use error::SqlError;
+pub use error::SqlError;
#[derive(Clone, Copy, Debug)]
pub struct ExecOutcome {
diff --git a/tangle-sql-wasm/Cargo.toml b/tangle-sql-wasm/Cargo.toml
@@ -0,0 +1,20 @@
+[package]
+name = "radroots-tangle-sql-wasm"
+version.workspace = true
+edition.workspace = true
+authors = ["Radroots Authors"]
+rust-version.workspace = true
+license.workspace = true
+
+[lib]
+crate-type = ["cdylib", "rlib"]
+
+[dependencies]
+radroots-sql-core = { workspace = true, features = ["web"] }
+radroots-sql-wasm-core = { workspace = true }
+radroots-tangle-sql = { workspace = true }
+radroots-tangle-schema = { workspace = true }
+serde = { workspace = true, features = ["derive"] }
+serde_json = { workspace = true }
+serde-wasm-bindgen = { workspace = true }
+wasm-bindgen = { workspace = true }
diff --git a/tangle-sql-wasm/pkg/package.json b/tangle-sql-wasm/pkg/package.json
@@ -0,0 +1,16 @@
+{
+ "name": "@radroots/tangle-sql-wasm",
+ "version": "0.1.0",
+ "private": true,
+ "type": "module",
+ "files": [
+ "dist"
+ ],
+ "exports": {
+ ".": {
+ "types": "./dist/radroots_tangle_sql_wasm.d.ts",
+ "import": "./dist/radroots_tangle_sql_wasm.js"
+ }
+ },
+ "sideEffects": false
+}
+\ No newline at end of file
diff --git a/tangle-sql-wasm/src/lib.rs b/tangle-sql-wasm/src/lib.rs
@@ -0,0 +1,60 @@
+#![cfg(target_arch = "wasm32")]
+
+use wasm_bindgen::prelude::*;
+
+use radroots_sql_core::WasmSqlExecutor;
+use radroots_tangle_schema::log_error::{
+ ILogErrorFields, ILogErrorFieldsFilter, ILogErrorFieldsPartial, LogError,
+ LogErrorQueryBindValues,
+};
+use radroots_tangle_sql::log_error;
+
+pub mod utils;
+pub use utils::*;
+
+#[wasm_bindgen(js_name = tangle_log_error_create)]
+pub fn tangle_log_error_create(opts_json: &str) -> Result<JsValue, JsValue> {
+ let payload = radroots_sql_wasm_core::parse_json::<ILogErrorFields>(opts_json)
+ .map_err(radroots_sql_wasm_core::err_js)?;
+ let exec = WasmSqlExecutor::new();
+ let out = log_error::insert(&exec, payload).map_err(radroots_sql_wasm_core::err_js)?;
+ value_to_js(out)
+}
+
+#[wasm_bindgen(js_name = tangle_log_error_find_many)]
+pub fn tangle_log_error_find_many(filter_json: &str) -> Result<JsValue, JsValue> {
+ let filter = parse_optional_json::<ILogErrorFieldsFilter>(filter_json)
+ .map_err(radroots_sql_wasm_core::err_js)?;
+ let exec = WasmSqlExecutor::new();
+ let out =
+ log_error::find_many(&exec, filter.as_ref()).map_err(radroots_sql_wasm_core::err_js)?;
+ value_to_js(out)
+}
+
+#[wasm_bindgen(js_name = tangle_log_error_find_one)]
+pub fn tangle_log_error_find_one(bind_json: &str) -> Result<JsValue, JsValue> {
+ let bind = radroots_sql_wasm_core::parse_json::<LogErrorQueryBindValues>(bind_json)
+ .map_err(radroots_sql_wasm_core::err_js)?;
+ let exec = WasmSqlExecutor::new();
+ let out: Option<LogError> =
+ log_error::find_one(&exec, &bind).map_err(radroots_sql_wasm_core::err_js)?;
+ value_to_js(out)
+}
+
+#[wasm_bindgen(js_name = tangle_log_error_update)]
+pub fn tangle_log_error_update(id: &str, fields_json: &str) -> Result<JsValue, JsValue> {
+ let fields = radroots_sql_wasm_core::parse_json::<ILogErrorFieldsPartial>(fields_json)
+ .map_err(radroots_sql_wasm_core::err_js)?;
+ let exec = WasmSqlExecutor::new();
+ let outcome = log_error::update(&exec, id, fields).map_err(radroots_sql_wasm_core::err_js)?;
+ outcome_to_js(outcome)
+}
+
+#[wasm_bindgen(js_name = tangle_log_error_delete)]
+pub fn tangle_log_error_delete(bind_json: &str) -> Result<JsValue, JsValue> {
+ let bind = radroots_sql_wasm_core::parse_json::<LogErrorQueryBindValues>(bind_json)
+ .map_err(radroots_sql_wasm_core::err_js)?;
+ let exec = WasmSqlExecutor::new();
+ let outcome = log_error::delete(&exec, &bind).map_err(radroots_sql_wasm_core::err_js)?;
+ outcome_to_js(outcome)
+}
diff --git a/tangle-sql-wasm/src/utils.rs b/tangle-sql-wasm/src/utils.rs
@@ -0,0 +1,40 @@
+use radroots_sql_core::error::SqlError;
+use serde::de::DeserializeOwned;
+use serde_json::json;
+use wasm_bindgen::JsValue;
+
+pub fn parse_optional_json<T>(input: &str) -> Result<Option<T>, SqlError>
+where
+ T: DeserializeOwned,
+{
+ let trimmed = input.trim();
+ if trimmed.is_empty() || trimmed == "null" {
+ Ok(None)
+ } else {
+ let value = radroots_sql_wasm_core::parse_json::<T>(trimmed)?;
+ Ok(Some(value))
+ }
+}
+
+fn serialize_to_js_value<T>(value: &T) -> Result<JsValue, JsValue>
+where
+ T: serde::Serialize,
+{
+ serde_wasm_bindgen::to_value(value)
+ .map_err(|e| radroots_sql_wasm_core::err_js(SqlError::SerializationError(e.to_string())))
+}
+
+pub fn outcome_to_js(outcome: radroots_sql_core::ExecOutcome) -> Result<JsValue, JsValue> {
+ let payload = json!({
+ "changes": outcome.changes,
+ "last_insert_id": outcome.last_insert_id,
+ });
+ serialize_to_js_value(&payload)
+}
+
+pub fn value_to_js<T>(value: T) -> Result<JsValue, JsValue>
+where
+ T: serde::Serialize,
+{
+ serialize_to_js_value(&value)
+}