commit 32cc941b99c79002f34e28b8fcc40b8d7cc11f96
parent 3e65b0b4a4818685e3f6a879366f8f5a183929cc
Author: triesap <tyson@radroots.org>
Date: Sat, 13 Jun 2026 15:09:03 -0700
sdk: remove SDK-owned crates from core workspace
- remove the Rust SDK and wasm binding crates after moving ownership to the SDK repo
- delete the old wasm Makefile surface and generated package manifests
- update rr-rs contracts, docs, Nix apps, and coverage policy for core-only ownership
- keep wasm binding references as SDK-owned consumer surfaces in specs
Diffstat:
53 files changed, 42 insertions(+), 12608 deletions(-)
diff --git a/AGENT_INSTRUCTIONS.md b/AGENT_INSTRUCTIONS.md
@@ -208,6 +208,6 @@ If Agent Mail is active for the task:
- use `.beads/PRIME.md` for the repository coordination conventions
- use the active Beads issue id as the Agent Mail thread id and reservation reason when both tools are active
- reserve files before the first write for coordinated multi-agent work
-- use shared build slots for long-running singleton lanes such as contract, release-preflight, or wasm-build runs
+- use shared build slots for long-running singleton lanes such as contract or release-preflight runs
If Beads or Agent Mail is not active, the repo still follows the same coding and validation standards; only the task-state and coordination backend changes.
diff --git a/Cargo.lock b/Cargo.lock
@@ -619,12 +619,6 @@ dependencies = [
]
[[package]]
-name = "cast"
-version = "0.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
-
-[[package]]
name = "cbc"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2785,16 +2779,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
[[package]]
-name = "minicov"
-version = "0.3.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4869b6a491569605d66d3952bcdf03df789e5b536e5f0cf7758a7f08a55ae24d"
-dependencies = [
- "cc",
- "walkdir",
-]
-
-[[package]]
name = "minimal-lexical"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3075,7 +3059,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
dependencies = [
"autocfg",
- "libm",
]
[[package]]
@@ -3151,12 +3134,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
[[package]]
-name = "oorandom"
-version = "11.1.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e"
-
-[[package]]
name = "opaque-debug"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -4030,18 +4007,6 @@ dependencies = [
]
[[package]]
-name = "radroots_events_codec_wasm"
-version = "0.1.0-alpha.2"
-dependencies = [
- "radroots_core",
- "radroots_events",
- "radroots_events_codec",
- "serde",
- "serde_json",
- "wasm-bindgen",
-]
-
-[[package]]
name = "radroots_events_indexed"
version = "0.1.0-alpha.2"
dependencies = [
@@ -4281,23 +4246,6 @@ dependencies = [
]
[[package]]
-name = "radroots_replica_db_wasm"
-version = "0.1.0-alpha.2"
-dependencies = [
- "js-sys",
- "radroots_replica_db",
- "radroots_replica_db_schema",
- "radroots_replica_sync",
- "radroots_sql_core",
- "radroots_sql_wasm_core",
- "serde",
- "serde-wasm-bindgen",
- "serde_json",
- "wasm-bindgen",
- "wasm-bindgen-test",
-]
-
-[[package]]
name = "radroots_replica_sync"
version = "0.1.0-alpha.2"
dependencies = [
@@ -4317,22 +4265,6 @@ dependencies = [
]
[[package]]
-name = "radroots_replica_sync_wasm"
-version = "0.1.0-alpha.2"
-dependencies = [
- "base64 0.22.1",
- "radroots_events",
- "radroots_replica_sync",
- "radroots_sql_core",
- "radroots_sql_wasm_core",
- "serde",
- "serde-wasm-bindgen",
- "serde_json",
- "uuid",
- "wasm-bindgen",
-]
-
-[[package]]
name = "radroots_runtime"
version = "0.1.0-alpha.2"
dependencies = [
@@ -4386,32 +4318,6 @@ dependencies = [
]
[[package]]
-name = "radroots_sdk"
-version = "0.1.0-alpha.2"
-dependencies = [
- "futures",
- "nostr",
- "radroots_core",
- "radroots_events",
- "radroots_events_codec",
- "radroots_identity",
- "radroots_nostr",
- "radroots_nostr_connect",
- "radroots_nostr_signer",
- "radroots_replica_db",
- "radroots_replica_db_schema",
- "radroots_replica_sync",
- "radroots_sql_core",
- "radroots_trade",
- "reqwest",
- "serde",
- "serde_json",
- "tempfile",
- "tokio",
- "tokio-tungstenite",
-]
-
-[[package]]
name = "radroots_secret_vault"
version = "0.1.0-alpha.2"
dependencies = [
@@ -5078,15 +4984,6 @@ dependencies = [
]
[[package]]
-name = "same-file"
-version = "1.0.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
-dependencies = [
- "winapi-util",
-]
-
-[[package]]
name = "scale-info"
version = "2.11.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -7538,16 +7435,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
[[package]]
-name = "walkdir"
-version = "2.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
-dependencies = [
- "same-file",
- "winapi-util",
-]
-
-[[package]]
name = "want"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -7640,45 +7527,6 @@ dependencies = [
]
[[package]]
-name = "wasm-bindgen-test"
-version = "0.3.64"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6311c867385cc7d5602463b31825d454d0837a3aba7cdb5e56d5201792a3f7fe"
-dependencies = [
- "async-trait",
- "cast",
- "js-sys",
- "libm",
- "minicov",
- "nu-ansi-term",
- "num-traits",
- "oorandom",
- "serde",
- "serde_json",
- "wasm-bindgen",
- "wasm-bindgen-futures",
- "wasm-bindgen-test-macro",
- "wasm-bindgen-test-shared",
-]
-
-[[package]]
-name = "wasm-bindgen-test-macro"
-version = "0.3.64"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "67008cdde4769831958536b0f11b3bdd0380bde882be17fff9c2f34bb4549abd"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.117",
-]
-
-[[package]]
-name = "wasm-bindgen-test-shared"
-version = "0.2.114"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cfe29135b180b72b04c74aa97b2b4a2ef275161eff9a6c7955ea9eaedc7e1d4e"
-
-[[package]]
name = "wasm-encoder"
version = "0.244.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -7792,15 +7640,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
-name = "winapi-util"
-version = "0.1.11"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
-dependencies = [
- "windows-sys 0.61.2",
-]
-
-[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/Cargo.toml b/Cargo.toml
@@ -4,7 +4,6 @@ members = [
"crates/events",
"crates/event_store",
"crates/events_codec",
- "crates/events_codec_wasm",
"crates/events_indexed",
"crates/geocoder",
"crates/identity",
@@ -35,13 +34,10 @@ members = [
"crates/test_fixtures",
"crates/replica_db_schema",
"crates/replica_sync",
- "crates/replica_sync_wasm",
"crates/replica_db",
- "crates/replica_db_wasm",
"crates/runtime_paths",
"crates/runtime_distribution",
"crates/runtime_manager",
- "crates/sdk",
"crates/sp1_guest_trade",
"crates/sp1_host_trade",
"crates/trade",
@@ -78,7 +74,6 @@ radroots_runtime = { path = "crates/runtime", version = "0.1.0-alpha.2", default
radroots_runtime_paths = { path = "crates/runtime_paths", version = "0.1.0-alpha.2", default-features = false }
radroots_runtime_distribution = { path = "crates/runtime_distribution", version = "0.1.0-alpha.2", default-features = false }
radroots_runtime_manager = { path = "crates/runtime_manager", version = "0.1.0-alpha.2", default-features = false }
-radroots_sdk = { path = "crates/sdk", version = "0.1.0-alpha.2", default-features = false }
radroots_log = { path = "crates/log", version = "0.1.0-alpha.2", default-features = false }
radroots_net = { path = "crates/net", version = "0.1.0-alpha.2", default-features = false }
radroots_nostr_runtime = { path = "crates/nostr_runtime", version = "0.1.0-alpha.2", default-features = false }
@@ -99,8 +94,6 @@ radroots_test_fixtures = { path = "crates/test_fixtures", version = "0.1.0-alpha
radroots_replica_db_schema = { path = "crates/replica_db_schema", version = "0.1.0-alpha.2", default-features = false }
radroots_replica_sync = { path = "crates/replica_sync", version = "0.1.0-alpha.2", default-features = false }
radroots_replica_db = { path = "crates/replica_db", version = "0.1.0-alpha.2", default-features = false }
-radroots_replica_db_wasm = { path = "crates/replica_db_wasm", version = "0.1.0-alpha.2" }
-radroots_replica_sync_wasm = { path = "crates/replica_sync_wasm", version = "0.1.0-alpha.2" }
radroots_trade = { path = "crates/trade", version = "0.1.0-alpha.2", default-features = false }
radroots_types = { path = "crates/types", version = "0.1.0-alpha.2", default-features = false }
radroots_protected_store = { path = "crates/protected_store", version = "0.1.0-alpha.2", default-features = false }
diff --git a/Makefile b/Makefile
@@ -1,58 +0,0 @@
-.PHONY: all build clean help \
- clean-hyphenated-wasm-output \
- build-events-codec-wasm build-replica-db-wasm build-replica-sync-wasm
-
-SHELL := /bin/bash
-.SHELLFLAGS := -e -o pipefail -c
-
-EVENTS_CODEC_WASM_PACKAGE := @radroots/radroots_events-codec-wasm
-EVENTS_CODEC_WASM_DESCRIPTION := WebAssembly bindings for radroots_events_codec
-REPLICA_DB_WASM_PACKAGE := @radroots/radroots_replica-db-wasm
-REPLICA_DB_WASM_DESCRIPTION := WebAssembly bindings for radroots_replica_db
-REPLICA_SYNC_WASM_PACKAGE := @radroots/radroots_replica-sync-wasm
-REPLICA_SYNC_WASM_DESCRIPTION := WebAssembly bindings for radroots_replica_sync
-
-HYPHENATED_WASM_OUTPUT_DIRS := \
- crates/events-codec-wasm \
- crates/replica-db-wasm \
- crates/replica-sync-wasm
-
-BUILD_TARGETS := \
- build-events-codec-wasm \
- build-replica-db-wasm \
- build-replica-sync-wasm
-
-all: build
-
-build: clean-hyphenated-wasm-output $(BUILD_TARGETS)
-
-clean:
- cargo clean
-
-help:
- @echo "Commands:"
- @echo " make all"
- @echo " make build"
- @echo " make clean"
- @echo " make help"
- @printf "%s\n" $(BUILD_TARGETS)
-
-clean-hyphenated-wasm-output:
- rm -rf $(HYPHENATED_WASM_OUTPUT_DIRS)
-
-normalize_wasm_package_json = python3 -c 'import json, pathlib, sys; path = pathlib.Path(sys.argv[1]); data = json.loads(path.read_text()); data["name"] = sys.argv[2]; data["description"] = sys.argv[3]; path.write_text(json.dumps(data, indent=2) + "\n")' "$(1)" "$(2)" "$(3)"
-
-build-replica-db-wasm:
- wasm-pack build crates/replica_db_wasm --release --target web \
- --out-dir pkg/dist --scope radroots
- $(call normalize_wasm_package_json,crates/replica_db_wasm/pkg/dist/package.json,$(REPLICA_DB_WASM_PACKAGE),$(REPLICA_DB_WASM_DESCRIPTION))
-
-build-events-codec-wasm:
- wasm-pack build crates/events_codec_wasm --release --target web \
- --out-dir pkg/dist --scope radroots
- $(call normalize_wasm_package_json,crates/events_codec_wasm/pkg/dist/package.json,$(EVENTS_CODEC_WASM_PACKAGE),$(EVENTS_CODEC_WASM_DESCRIPTION))
-
-build-replica-sync-wasm:
- wasm-pack build crates/replica_sync_wasm --release --target web \
- --out-dir pkg/dist --scope radroots
- $(call normalize_wasm_package_json,crates/replica_sync_wasm/pkg/dist/package.json,$(REPLICA_SYNC_WASM_PACKAGE),$(REPLICA_SYNC_WASM_DESCRIPTION))
diff --git a/crates/events_codec/README b/crates/events_codec/README
@@ -40,8 +40,8 @@ The group codecs use bare metadata marker tags such as `private`, `restricted`,
`hidden`, and `closed`, `supported_kinds` declarations, and `code` tags for
invite and join flows. They preserve optional reason content on user management
and moderation events. LiveKit room metadata and live participant state are
-deferred. The companion `radroots_events_codec_wasm` crate exposes
-deterministic JSON tag builders for the same Field and NIP-29 families.
+deferred. SDK-owned wasm bindings expose deterministic JSON tag builders for the
+same Field and NIP-29 families.
## Copyright
diff --git a/crates/events_codec_wasm/Cargo.toml b/crates/events_codec_wasm/Cargo.toml
@@ -1,34 +0,0 @@
-[package]
-name = "radroots_events_codec_wasm"
-publish = false
-version = "0.1.0-alpha.2"
-edition.workspace = true
-authors = ["Tyson Lupul <tyson@radroots.org>"]
-rust-version.workspace = true
-license.workspace = true
-description = "WebAssembly bindings for radroots_events_codec"
-repository.workspace = true
-homepage.workspace = true
-documentation = "https://docs.rs/radroots_events_codec_wasm"
-readme = "README"
-
-[lib]
-crate-type = ["cdylib", "rlib"]
-
-[dependencies]
-radroots_events = { workspace = true, default-features = false, features = [
- "std",
- "serde",
-] }
-radroots_events_codec = { workspace = true, default-features = false, features = [
- "std",
- "serde_json",
-] }
-serde = { workspace = true }
-serde_json = { workspace = true }
-wasm-bindgen = { workspace = true }
-
-[dev-dependencies]
-radroots_core = { workspace = true, default-features = false, features = [
- "std",
-] }
diff --git a/crates/events_codec_wasm/README b/crates/events_codec_wasm/README
@@ -1,24 +0,0 @@
-# radroots_events_codec_wasm
-
-This is the README for `radroots_events_codec_wasm`, which provides WebAssembly
-bindings for `radroots_events_codec` in the `radroots` core libraries.
-
-## Overview
-
- * wasm-bindgen entry points for event content and tag encoding and decoding;
- * specialized tag helpers for farm, list, job, message, plot, and reaction
- event families;
- * JSON and `JsValue` boundaries built around `serde-wasm-bindgen` and base64
- helpers;
- * built as `cdylib` and `rlib` artifacts for wasm consumers.
-
-## Copyright
-
-Except as otherwise noted, all files in the `radroots_events_codec_wasm`
-distribution are
-
- Copyright (c) 2020-2026 Tyson Lupul
-
-For information on usage and redistribution, and for a DISCLAIMER OF ALL
-WARRANTIES, see LICENSE included in the `radroots_events_codec_wasm`
-distribution.
diff --git a/crates/events_codec_wasm/pkg/package.json b/crates/events_codec_wasm/pkg/package.json
@@ -1,20 +0,0 @@
-{
- "name": "@radroots/radroots_events-codec-wasm",
- "description": "WebAssembly bindings for radroots_events_codec",
- "version": "0.1.0",
- "private": true,
- "type": "module",
- "files": [
- "dist"
- ],
- "main": "./dist/radroots_events_codec_wasm.js",
- "types": "./dist/radroots_events_codec_wasm.d.ts",
- "exports": {
- ".": {
- "types": "./dist/radroots_events_codec_wasm.d.ts",
- "import": "./dist/radroots_events_codec_wasm.js",
- "default": "./dist/radroots_events_codec_wasm.js"
- }
- },
- "sideEffects": false
-}
diff --git a/crates/events_codec_wasm/src/lib.rs b/crates/events_codec_wasm/src/lib.rs
@@ -1,1149 +0,0 @@
-#![forbid(unsafe_code)]
-
-use radroots_events::article::RadrootsArticle;
-use radroots_events::calendar::{
- RadrootsCalendar, RadrootsCalendarDateEvent, RadrootsCalendarEventRsvp,
- RadrootsCalendarTimeEvent,
-};
-use radroots_events::comment::RadrootsComment;
-use radroots_events::coop::RadrootsCoop;
-use radroots_events::document::RadrootsDocument;
-use radroots_events::farm::RadrootsFarm;
-use radroots_events::farm_crdt::RadrootsFarmCrdtChange;
-use radroots_events::farm_file::RadrootsFarmFileMetadata;
-use radroots_events::farm_workspace::RadrootsFarmWorkspaceManifest;
-use radroots_events::file_metadata::RadrootsFileMetadata;
-use radroots_events::follow::RadrootsFollow;
-use radroots_events::gift_wrap::RadrootsGiftWrap;
-use radroots_events::group::{
- RadrootsGroupAdmins, RadrootsGroupCreateGroup, RadrootsGroupCreateInvite,
- RadrootsGroupDeleteEvent, RadrootsGroupDeleteGroup, RadrootsGroupEditMetadata,
- RadrootsGroupJoinRequest, RadrootsGroupLeaveRequest, RadrootsGroupMembers,
- RadrootsGroupMetadata, RadrootsGroupPutUser, RadrootsGroupRemoveUser, RadrootsGroupRoles,
-};
-use radroots_events::http_auth::RadrootsHttpAuth;
-use radroots_events::job_feedback::RadrootsJobFeedback;
-use radroots_events::job_request::RadrootsJobRequest;
-use radroots_events::job_result::RadrootsJobResult;
-use radroots_events::list::RadrootsList;
-use radroots_events::list_set::RadrootsListSet;
-use radroots_events::listing::RadrootsListing;
-use radroots_events::message::RadrootsMessage;
-use radroots_events::message_file::RadrootsMessageFile;
-use radroots_events::plot::RadrootsPlot;
-use radroots_events::post::RadrootsPost;
-use radroots_events::reaction::RadrootsReaction;
-use radroots_events::relay_auth::RadrootsRelayAuth;
-use radroots_events::report::RadrootsReport;
-use radroots_events::repost::{RadrootsGenericRepost, RadrootsRepost};
-use radroots_events::seal::RadrootsSeal;
-use radroots_events_codec::article::encode::article_build_tags;
-use radroots_events_codec::calendar::encode::{
- calendar_collection_build_tags, calendar_date_event_build_tags, calendar_time_event_build_tags,
- rsvp_build_tags,
-};
-use radroots_events_codec::comment::encode::comment_build_tags;
-use radroots_events_codec::coop::encode::coop_build_tags;
-use radroots_events_codec::document::encode::document_build_tags;
-use radroots_events_codec::farm::encode::farm_build_tags;
-use radroots_events_codec::farm_crdt::encode::farm_crdt_change_build_tags_with_author;
-use radroots_events_codec::farm_file::encode::farm_file_metadata_build_tags;
-use radroots_events_codec::farm_workspace::encode::farm_workspace_build_tags;
-use radroots_events_codec::file_metadata::encode::file_metadata_build_tags;
-use radroots_events_codec::follow::encode::follow_build_tags;
-use radroots_events_codec::gift_wrap::encode::gift_wrap_build_tags;
-use radroots_events_codec::group::encode::{
- group_admins_build_tags, group_create_group_build_tags, group_create_invite_build_tags,
- group_delete_event_build_tags, group_delete_group_build_tags, group_edit_metadata_build_tags,
- group_join_request_build_tags, group_leave_request_build_tags, group_members_build_tags,
- group_metadata_build_tags, group_put_user_build_tags, group_remove_user_build_tags,
- group_roles_build_tags,
-};
-use radroots_events_codec::http_auth::encode::http_auth_build_tags;
-use radroots_events_codec::job::feedback::encode::job_feedback_build_tags;
-use radroots_events_codec::job::request::encode::job_request_build_tags;
-use radroots_events_codec::job::result::encode::job_result_build_tags;
-use radroots_events_codec::list::encode::list_build_tags;
-use radroots_events_codec::list_set::encode::list_set_build_tags;
-use radroots_events_codec::listing::tags::{
- listing_tags as listing_tags_impl, listing_tags_full as listing_tags_full_impl,
-};
-use radroots_events_codec::message::encode::message_build_tags;
-use radroots_events_codec::message_file::encode::message_file_build_tags;
-use radroots_events_codec::plot::encode::plot_build_tags;
-use radroots_events_codec::post::encode::post_build_tags;
-use radroots_events_codec::reaction::encode::reaction_build_tags;
-use radroots_events_codec::relay_auth::encode::relay_auth_build_tags;
-use radroots_events_codec::report::encode::report_build_tags;
-use radroots_events_codec::repost::encode::{generic_repost_build_tags, repost_build_tags};
-use radroots_events_codec::seal::encode::seal_build_tags;
-use serde::de::DeserializeOwned;
-#[cfg(target_arch = "wasm32")]
-use wasm_bindgen::JsValue;
-#[cfg(target_arch = "wasm32")]
-use wasm_bindgen::prelude::*;
-
-#[cfg(target_arch = "wasm32")]
-type RadrootsJsValue = JsValue;
-
-#[cfg(not(target_arch = "wasm32"))]
-type RadrootsJsValue = String;
-
-fn err_js<E: ToString>(err: E) -> RadrootsJsValue {
- #[cfg(target_arch = "wasm32")]
- {
- JsValue::from_str(&err.to_string())
- }
- #[cfg(not(target_arch = "wasm32"))]
- {
- err.to_string()
- }
-}
-
-fn normalized_payload(input: &str) -> &str {
- if input.is_empty() { "{}" } else { input }
-}
-
-fn parse_json<T: DeserializeOwned>(input: &str) -> Result<T, RadrootsJsValue> {
- serde_json::from_str(normalized_payload(input)).map_err(err_js)
-}
-
-fn tags_to_json(tags: Vec<Vec<String>>) -> Result<String, RadrootsJsValue> {
- serde_json::to_string(&tags).map_err(err_js)
-}
-
-fn build_tags_json<T, E, F>(input: &str, build: F) -> Result<String, RadrootsJsValue>
-where
- T: DeserializeOwned,
- E: ToString,
- F: FnOnce(&T) -> Result<Vec<Vec<String>>, E>,
-{
- let value = parse_json::<T>(input)?;
- let tags = build(&value).map_err(err_js)?;
- tags_to_json(tags)
-}
-
-fn build_tags_json_infallible<T, F>(input: &str, build: F) -> Result<String, RadrootsJsValue>
-where
- T: DeserializeOwned,
- F: FnOnce(&T) -> Vec<Vec<String>>,
-{
- let value = parse_json::<T>(input)?;
- let tags = build(&value);
- tags_to_json(tags)
-}
-
-#[derive(serde::Deserialize)]
-struct FarmCrdtTagsInput {
- change: RadrootsFarmCrdtChange,
- author_pubkey: String,
-}
-
-#[cfg_attr(target_arch = "wasm32", wasm_bindgen(js_name = listing_tags))]
-pub fn listing_tags(listing_json: &str) -> Result<String, RadrootsJsValue> {
- build_tags_json::<RadrootsListing, _, _>(listing_json, listing_tags_impl)
-}
-
-#[cfg_attr(target_arch = "wasm32", wasm_bindgen(js_name = listing_tags_full))]
-pub fn listing_tags_full(listing_json: &str) -> Result<String, RadrootsJsValue> {
- build_tags_json::<RadrootsListing, _, _>(listing_json, listing_tags_full_impl)
-}
-
-#[cfg_attr(target_arch = "wasm32", wasm_bindgen(js_name = post_tags))]
-pub fn post_tags(post_json: &str) -> Result<String, RadrootsJsValue> {
- build_tags_json::<RadrootsPost, _, _>(post_json, post_build_tags)
-}
-
-#[cfg_attr(target_arch = "wasm32", wasm_bindgen(js_name = comment_tags))]
-pub fn comment_tags(comment_json: &str) -> Result<String, RadrootsJsValue> {
- build_tags_json::<RadrootsComment, _, _>(comment_json, comment_build_tags)
-}
-
-#[cfg_attr(target_arch = "wasm32", wasm_bindgen(js_name = article_tags))]
-pub fn article_tags(article_json: &str) -> Result<String, RadrootsJsValue> {
- build_tags_json::<RadrootsArticle, _, _>(article_json, article_build_tags)
-}
-
-#[cfg_attr(target_arch = "wasm32", wasm_bindgen(js_name = file_metadata_tags))]
-pub fn file_metadata_tags(metadata_json: &str) -> Result<String, RadrootsJsValue> {
- build_tags_json::<RadrootsFileMetadata, _, _>(metadata_json, file_metadata_build_tags)
-}
-
-#[cfg_attr(
- target_arch = "wasm32",
- wasm_bindgen(js_name = calendar_date_event_tags)
-)]
-pub fn calendar_date_event_tags(event_json: &str) -> Result<String, RadrootsJsValue> {
- build_tags_json::<RadrootsCalendarDateEvent, _, _>(event_json, calendar_date_event_build_tags)
-}
-
-#[cfg_attr(
- target_arch = "wasm32",
- wasm_bindgen(js_name = calendar_time_event_tags)
-)]
-pub fn calendar_time_event_tags(event_json: &str) -> Result<String, RadrootsJsValue> {
- build_tags_json::<RadrootsCalendarTimeEvent, _, _>(event_json, calendar_time_event_build_tags)
-}
-
-#[cfg_attr(target_arch = "wasm32", wasm_bindgen(js_name = calendar_tags))]
-pub fn calendar_tags(calendar_json: &str) -> Result<String, RadrootsJsValue> {
- build_tags_json::<RadrootsCalendar, _, _>(calendar_json, calendar_collection_build_tags)
-}
-
-#[cfg_attr(
- target_arch = "wasm32",
- wasm_bindgen(js_name = calendar_event_rsvp_tags)
-)]
-pub fn calendar_event_rsvp_tags(rsvp_json: &str) -> Result<String, RadrootsJsValue> {
- build_tags_json::<RadrootsCalendarEventRsvp, _, _>(rsvp_json, rsvp_build_tags)
-}
-
-#[cfg_attr(target_arch = "wasm32", wasm_bindgen(js_name = repost_tags))]
-pub fn repost_tags(repost_json: &str) -> Result<String, RadrootsJsValue> {
- build_tags_json::<RadrootsRepost, _, _>(repost_json, repost_build_tags)
-}
-
-#[cfg_attr(target_arch = "wasm32", wasm_bindgen(js_name = generic_repost_tags))]
-pub fn generic_repost_tags(repost_json: &str) -> Result<String, RadrootsJsValue> {
- build_tags_json::<RadrootsGenericRepost, _, _>(repost_json, generic_repost_build_tags)
-}
-
-#[cfg_attr(target_arch = "wasm32", wasm_bindgen(js_name = report_tags))]
-pub fn report_tags(report_json: &str) -> Result<String, RadrootsJsValue> {
- build_tags_json::<RadrootsReport, _, _>(report_json, report_build_tags)
-}
-
-#[cfg_attr(target_arch = "wasm32", wasm_bindgen(js_name = follow_tags))]
-pub fn follow_tags(follow_json: &str) -> Result<String, RadrootsJsValue> {
- build_tags_json::<RadrootsFollow, _, _>(follow_json, follow_build_tags)
-}
-
-#[cfg_attr(target_arch = "wasm32", wasm_bindgen(js_name = document_tags))]
-pub fn document_tags(document_json: &str) -> Result<String, RadrootsJsValue> {
- build_tags_json::<RadrootsDocument, _, _>(document_json, document_build_tags)
-}
-
-#[cfg_attr(target_arch = "wasm32", wasm_bindgen(js_name = coop_tags))]
-pub fn coop_tags(coop_json: &str) -> Result<String, RadrootsJsValue> {
- build_tags_json::<RadrootsCoop, _, _>(coop_json, coop_build_tags)
-}
-
-#[cfg_attr(target_arch = "wasm32", wasm_bindgen(js_name = farm_tags))]
-pub fn farm_tags(farm_json: &str) -> Result<String, RadrootsJsValue> {
- build_tags_json::<RadrootsFarm, _, _>(farm_json, farm_build_tags)
-}
-
-#[cfg_attr(target_arch = "wasm32", wasm_bindgen(js_name = list_tags))]
-pub fn list_tags(list_json: &str) -> Result<String, RadrootsJsValue> {
- build_tags_json::<RadrootsList, _, _>(list_json, list_build_tags)
-}
-
-#[cfg_attr(target_arch = "wasm32", wasm_bindgen(js_name = list_set_tags))]
-pub fn list_set_tags(list_json: &str) -> Result<String, RadrootsJsValue> {
- build_tags_json::<RadrootsListSet, _, _>(list_json, list_set_build_tags)
-}
-
-#[cfg_attr(target_arch = "wasm32", wasm_bindgen(js_name = plot_tags))]
-pub fn plot_tags(plot_json: &str) -> Result<String, RadrootsJsValue> {
- build_tags_json::<RadrootsPlot, _, _>(plot_json, plot_build_tags)
-}
-
-#[cfg_attr(target_arch = "wasm32", wasm_bindgen(js_name = job_request_tags))]
-pub fn job_request_tags(job_json: &str) -> Result<String, RadrootsJsValue> {
- build_tags_json_infallible::<RadrootsJobRequest, _>(job_json, job_request_build_tags)
-}
-
-#[cfg_attr(target_arch = "wasm32", wasm_bindgen(js_name = job_result_tags))]
-pub fn job_result_tags(job_json: &str) -> Result<String, RadrootsJsValue> {
- build_tags_json_infallible::<RadrootsJobResult, _>(job_json, job_result_build_tags)
-}
-
-#[cfg_attr(target_arch = "wasm32", wasm_bindgen(js_name = job_feedback_tags))]
-pub fn job_feedback_tags(job_json: &str) -> Result<String, RadrootsJsValue> {
- build_tags_json_infallible::<RadrootsJobFeedback, _>(job_json, job_feedback_build_tags)
-}
-
-#[cfg_attr(target_arch = "wasm32", wasm_bindgen(js_name = reaction_tags))]
-pub fn reaction_tags(reaction_json: &str) -> Result<String, RadrootsJsValue> {
- build_tags_json::<RadrootsReaction, _, _>(reaction_json, reaction_build_tags)
-}
-
-#[cfg_attr(target_arch = "wasm32", wasm_bindgen(js_name = message_tags))]
-pub fn message_tags(message_json: &str) -> Result<String, RadrootsJsValue> {
- build_tags_json::<RadrootsMessage, _, _>(message_json, message_build_tags)
-}
-
-#[cfg_attr(target_arch = "wasm32", wasm_bindgen(js_name = message_file_tags))]
-pub fn message_file_tags(message_json: &str) -> Result<String, RadrootsJsValue> {
- build_tags_json::<RadrootsMessageFile, _, _>(message_json, message_file_build_tags)
-}
-
-#[cfg_attr(target_arch = "wasm32", wasm_bindgen(js_name = seal_tags))]
-pub fn seal_tags(seal_json: &str) -> Result<String, RadrootsJsValue> {
- build_tags_json::<RadrootsSeal, _, _>(seal_json, seal_build_tags)
-}
-
-#[cfg_attr(target_arch = "wasm32", wasm_bindgen(js_name = gift_wrap_tags))]
-pub fn gift_wrap_tags(gift_wrap_json: &str) -> Result<String, RadrootsJsValue> {
- build_tags_json::<RadrootsGiftWrap, _, _>(gift_wrap_json, gift_wrap_build_tags)
-}
-
-#[cfg_attr(
- target_arch = "wasm32",
- wasm_bindgen(js_name = farm_workspace_manifest_tags)
-)]
-pub fn farm_workspace_manifest_tags(workspace_json: &str) -> Result<String, RadrootsJsValue> {
- build_tags_json::<RadrootsFarmWorkspaceManifest, _, _>(
- workspace_json,
- farm_workspace_build_tags,
- )
-}
-
-#[cfg_attr(target_arch = "wasm32", wasm_bindgen(js_name = farm_crdt_change_tags))]
-pub fn farm_crdt_change_tags(input_json: &str) -> Result<String, RadrootsJsValue> {
- let input = parse_json::<FarmCrdtTagsInput>(input_json)?;
- let tags = farm_crdt_change_build_tags_with_author(&input.change, Some(&input.author_pubkey))
- .map_err(err_js)?;
- tags_to_json(tags)
-}
-
-#[cfg_attr(target_arch = "wasm32", wasm_bindgen(js_name = farm_file_metadata_tags))]
-pub fn farm_file_metadata_tags(file_json: &str) -> Result<String, RadrootsJsValue> {
- build_tags_json::<RadrootsFarmFileMetadata, _, _>(file_json, farm_file_metadata_build_tags)
-}
-
-#[cfg_attr(target_arch = "wasm32", wasm_bindgen(js_name = relay_auth_tags))]
-pub fn relay_auth_tags(auth_json: &str) -> Result<String, RadrootsJsValue> {
- build_tags_json::<RadrootsRelayAuth, _, _>(auth_json, relay_auth_build_tags)
-}
-
-#[cfg_attr(target_arch = "wasm32", wasm_bindgen(js_name = http_auth_tags))]
-pub fn http_auth_tags(auth_json: &str) -> Result<String, RadrootsJsValue> {
- build_tags_json::<RadrootsHttpAuth, _, _>(auth_json, http_auth_build_tags)
-}
-
-#[cfg_attr(target_arch = "wasm32", wasm_bindgen(js_name = group_put_user_tags))]
-pub fn group_put_user_tags(group_json: &str) -> Result<String, RadrootsJsValue> {
- build_tags_json::<RadrootsGroupPutUser, _, _>(group_json, group_put_user_build_tags)
-}
-
-#[cfg_attr(target_arch = "wasm32", wasm_bindgen(js_name = group_remove_user_tags))]
-pub fn group_remove_user_tags(group_json: &str) -> Result<String, RadrootsJsValue> {
- build_tags_json::<RadrootsGroupRemoveUser, _, _>(group_json, group_remove_user_build_tags)
-}
-
-#[cfg_attr(target_arch = "wasm32", wasm_bindgen(js_name = group_create_group_tags))]
-pub fn group_create_group_tags(group_json: &str) -> Result<String, RadrootsJsValue> {
- build_tags_json::<RadrootsGroupCreateGroup, _, _>(group_json, group_create_group_build_tags)
-}
-
-#[cfg_attr(target_arch = "wasm32", wasm_bindgen(js_name = group_edit_metadata_tags))]
-pub fn group_edit_metadata_tags(group_json: &str) -> Result<String, RadrootsJsValue> {
- build_tags_json::<RadrootsGroupEditMetadata, _, _>(group_json, group_edit_metadata_build_tags)
-}
-
-#[cfg_attr(target_arch = "wasm32", wasm_bindgen(js_name = group_delete_group_tags))]
-pub fn group_delete_group_tags(group_json: &str) -> Result<String, RadrootsJsValue> {
- build_tags_json::<RadrootsGroupDeleteGroup, _, _>(group_json, group_delete_group_build_tags)
-}
-
-#[cfg_attr(target_arch = "wasm32", wasm_bindgen(js_name = group_delete_event_tags))]
-pub fn group_delete_event_tags(group_json: &str) -> Result<String, RadrootsJsValue> {
- build_tags_json::<RadrootsGroupDeleteEvent, _, _>(group_json, group_delete_event_build_tags)
-}
-
-#[cfg_attr(target_arch = "wasm32", wasm_bindgen(js_name = group_create_invite_tags))]
-pub fn group_create_invite_tags(group_json: &str) -> Result<String, RadrootsJsValue> {
- build_tags_json::<RadrootsGroupCreateInvite, _, _>(group_json, group_create_invite_build_tags)
-}
-
-#[cfg_attr(target_arch = "wasm32", wasm_bindgen(js_name = group_join_request_tags))]
-pub fn group_join_request_tags(group_json: &str) -> Result<String, RadrootsJsValue> {
- build_tags_json::<RadrootsGroupJoinRequest, _, _>(group_json, group_join_request_build_tags)
-}
-
-#[cfg_attr(target_arch = "wasm32", wasm_bindgen(js_name = group_leave_request_tags))]
-pub fn group_leave_request_tags(group_json: &str) -> Result<String, RadrootsJsValue> {
- build_tags_json::<RadrootsGroupLeaveRequest, _, _>(group_json, group_leave_request_build_tags)
-}
-
-#[cfg_attr(target_arch = "wasm32", wasm_bindgen(js_name = group_metadata_tags))]
-pub fn group_metadata_tags(group_json: &str) -> Result<String, RadrootsJsValue> {
- build_tags_json::<RadrootsGroupMetadata, _, _>(group_json, group_metadata_build_tags)
-}
-
-#[cfg_attr(target_arch = "wasm32", wasm_bindgen(js_name = group_admins_tags))]
-pub fn group_admins_tags(group_json: &str) -> Result<String, RadrootsJsValue> {
- build_tags_json::<RadrootsGroupAdmins, _, _>(group_json, group_admins_build_tags)
-}
-
-#[cfg_attr(target_arch = "wasm32", wasm_bindgen(js_name = group_members_tags))]
-pub fn group_members_tags(group_json: &str) -> Result<String, RadrootsJsValue> {
- build_tags_json::<RadrootsGroupMembers, _, _>(group_json, group_members_build_tags)
-}
-
-#[cfg_attr(target_arch = "wasm32", wasm_bindgen(js_name = group_roles_tags))]
-pub fn group_roles_tags(group_json: &str) -> Result<String, RadrootsJsValue> {
- build_tags_json::<RadrootsGroupRoles, _, _>(group_json, group_roles_build_tags)
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
- use radroots_core::{
- RadrootsCoreCurrency, RadrootsCoreDecimal, RadrootsCoreMoney, RadrootsCoreQuantity,
- RadrootsCoreQuantityPrice, RadrootsCoreUnit,
- };
- use radroots_events::farm::RadrootsFarmRef;
- use radroots_events::farm_crdt::{
- RADROOTS_FARM_CRDT_CHANGE_SCHEMA, RadrootsCrdtBackend, RadrootsFarmCrdtDocumentKind,
- RadrootsFarmSemanticKind,
- };
- use radroots_events::farm_file::{
- RadrootsFarmFileDimensions, RadrootsFarmFileMetadata, RadrootsFarmFileSource,
- };
- use radroots_events::farm_workspace::{
- RADROOTS_FARM_WORKSPACE_PROTOCOL_VERSION, RADROOTS_FARM_WORKSPACE_SCHEMA,
- RadrootsFarmWorkspaceManifest, RadrootsFarmWorkspaceMediaServer, RadrootsFarmWorkspaceRef,
- RadrootsFarmWorkspaceRelay, RadrootsFarmWorkspaceRelayMode,
- };
- use radroots_events::group::{
- RadrootsGroupAdmins, RadrootsGroupCreateGroup, RadrootsGroupCreateInvite,
- RadrootsGroupDeleteEvent, RadrootsGroupDeleteGroup, RadrootsGroupEditMetadata,
- RadrootsGroupEditableMetadata, RadrootsGroupJoinRequest, RadrootsGroupLeaveRequest,
- RadrootsGroupMembers, RadrootsGroupMetadata, RadrootsGroupPutUser, RadrootsGroupRemoveUser,
- RadrootsGroupRole, RadrootsGroupRoles, RadrootsGroupUserRef,
- };
- use radroots_events::http_auth::RadrootsHttpAuth;
- use radroots_events::job::JobInputType;
- use radroots_events::job_request::{RadrootsJobInput, RadrootsJobParam};
- use radroots_events::kinds::KIND_FARM_FILE_METADATA;
- use radroots_events::listing::{RadrootsListingBin, RadrootsListingProduct};
- use radroots_events::relay_auth::RadrootsRelayAuth;
- use radroots_events::social::{
- RadrootsCalendarDateValue, RadrootsCalendarEventFreeBusy, RadrootsCalendarEventRsvpStatus,
- RadrootsCalendarParticipant, RadrootsReportFileTarget, RadrootsReportType,
- RadrootsSocialFarmAnchor, RadrootsSocialLocation, RadrootsSocialMediaDimensions,
- RadrootsSocialMediaMetadata, RadrootsSocialTarget,
- };
-
- fn sample_listing() -> RadrootsListing {
- let quantity =
- RadrootsCoreQuantity::new(RadrootsCoreDecimal::from(1u32), RadrootsCoreUnit::Each);
- let price = RadrootsCoreQuantityPrice::new(
- RadrootsCoreMoney::new(RadrootsCoreDecimal::from(10u32), RadrootsCoreCurrency::USD),
- quantity.clone(),
- );
-
- RadrootsListing {
- d_tag: "AAAAAAAAAAAAAAAAAAAAAg".parse().expect("listing d tag"),
- published_at: None,
- farm: RadrootsFarmRef {
- pubkey: "farm_pubkey".to_string(),
- d_tag: "AAAAAAAAAAAAAAAAAAAAAA".to_string(),
- },
- product: RadrootsListingProduct {
- key: "sku".to_string(),
- title: "widget".to_string(),
- category: "tools".to_string(),
- summary: None,
- process: None,
- lot: None,
- location: None,
- profile: None,
- year: None,
- },
- primary_bin_id: "bin-1".parse().expect("primary bin id"),
- bins: vec![RadrootsListingBin {
- bin_id: "bin-1".parse().expect("bin id"),
- quantity,
- price_per_canonical_unit: price,
- display_amount: None,
- display_unit: None,
- display_label: None,
- display_price: None,
- display_price_unit: None,
- }],
- resource_area: None,
- plot: None,
- discounts: None,
- inventory_available: None,
- availability: None,
- delivery_method: None,
- location: None,
- images: None,
- }
- }
-
- fn synthetic_pubkey(seed: char) -> String {
- seed.to_string().repeat(64)
- }
-
- fn synthetic_event_id(seed: char) -> String {
- seed.to_string().repeat(64)
- }
-
- fn social_farm_anchor() -> RadrootsSocialFarmAnchor {
- RadrootsSocialFarmAnchor {
- farm: RadrootsFarmRef {
- pubkey: synthetic_pubkey('a'),
- d_tag: "AAAAAAAAAAAAAAAAAAAAAA".to_string(),
- },
- relays: Some(vec!["wss://relay.example.test".to_string()]),
- }
- }
-
- fn event_target(kind: u32, seed: char) -> RadrootsSocialTarget {
- RadrootsSocialTarget::Event {
- id: synthetic_event_id(seed),
- author: Some(synthetic_pubkey('b')),
- event_kind: Some(kind),
- relays: Some(vec!["wss://relay.example.test".to_string()]),
- }
- }
-
- fn address_target(kind: u32, d_tag: &str) -> RadrootsSocialTarget {
- let author = synthetic_pubkey('c');
- RadrootsSocialTarget::Address {
- address: format!("{kind}:{author}:{d_tag}"),
- author: Some(author),
- event_kind: Some(kind),
- relays: Some(vec!["wss://relay2.example.test".to_string()]),
- }
- }
-
- fn social_location() -> RadrootsSocialLocation {
- RadrootsSocialLocation {
- name: Some("field edge".to_string()),
- geohash: Some("c23nb62w20st".to_string()),
- }
- }
-
- fn sample_post() -> RadrootsPost {
- RadrootsPost {
- content: "field update".to_string(),
- farm: Some(social_farm_anchor()),
- address_refs: Some(vec![address_target(30023, "AAAAAAAAAAAAAAAAAAAAAQ")]),
- location: Some(social_location()),
- topics: Some(vec!["soil".to_string(), "market".to_string()]),
- quote_refs: Some(vec![event_target(30023, 'd')]),
- media: Some(vec![RadrootsSocialMediaMetadata {
- url: Some("https://media.example.test/field.jpg".to_string()),
- mime_type: Some("image/jpeg".to_string()),
- sha256: Some(
- "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef".to_string(),
- ),
- original_sha256: None,
- size: Some(4096),
- dimensions: Some(RadrootsSocialMediaDimensions {
- width: 1200,
- height: 800,
- }),
- blurhash: None,
- thumbnails: None,
- image: None,
- summary: Some("field photo".to_string()),
- alt: Some("rows after harvest".to_string()),
- fallback: None,
- magnet: Some("magnet:?xt=urn:btih:abc".to_string()),
- content_hashes: Some(vec!["sha256:field".to_string()]),
- services: Some(vec!["https://media.example.test".to_string()]),
- imeta: None,
- }]),
- }
- }
-
- fn sample_article() -> RadrootsArticle {
- RadrootsArticle {
- d_tag: "AAAAAAAAAAAAAAAAAAAAAg".to_string(),
- title: "soil notes".to_string(),
- content: "# soil notes".to_string(),
- summary: Some("cover crop observations".to_string()),
- image: Some("https://media.example.test/article.jpg".to_string()),
- published_at: Some(1_780_000_000),
- farm: Some(social_farm_anchor()),
- location: Some(social_location()),
- topics: Some(vec!["soil".to_string(), "cover-crops".to_string()]),
- }
- }
-
- fn sample_public_file_metadata() -> RadrootsFileMetadata {
- RadrootsFileMetadata {
- url: "https://media.example.test/public.jpg".to_string(),
- mime_type: "image/jpeg".to_string(),
- sha256: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef".to_string(),
- original_sha256: Some(
- "abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789".to_string(),
- ),
- size: Some(4096),
- dimensions: Some(RadrootsSocialMediaDimensions {
- width: 1200,
- height: 800,
- }),
- blurhash: None,
- thumbnails: None,
- summary: Some("public field photo".to_string()),
- alt: Some("rows after harvest".to_string()),
- fallback: Some("https://media.example.test/fallback.jpg".to_string()),
- magnet: Some("magnet:?xt=urn:btih:abc".to_string()),
- content_hashes: Some(vec!["sha256:field".to_string()]),
- services: Some(vec!["https://media.example.test".to_string()]),
- content: Some("caption".to_string()),
- }
- }
-
- fn sample_calendar_date_event() -> RadrootsCalendarDateEvent {
- RadrootsCalendarDateEvent {
- d_tag: "AAAAAAAAAAAAAAAAAAAAAw".to_string(),
- title: "market day".to_string(),
- start: "2026-06-20".to_string(),
- description: Some("Farm stand pickup window.".to_string()),
- end: Some("2026-06-21".to_string()),
- days: Some(vec![RadrootsCalendarDateValue {
- value: "2026-06-20".to_string(),
- }]),
- location: Some(social_location()),
- summary: Some("weekly pickup".to_string()),
- image: None,
- participants: Some(vec![RadrootsCalendarParticipant {
- pubkey: synthetic_pubkey('e'),
- relay: Some("wss://relay.example.test".to_string()),
- role: Some("host".to_string()),
- }]),
- }
- }
-
- fn sample_calendar_time_event() -> RadrootsCalendarTimeEvent {
- RadrootsCalendarTimeEvent {
- d_tag: "AAAAAAAAAAAAAAAAAAAA-A".to_string(),
- title: "wash pack shift".to_string(),
- start: 1_781_895_600,
- dates: vec![RadrootsCalendarDateValue {
- value: "2026-06-20".to_string(),
- }],
- description: Some("Prepare CSA bins before pickup.".to_string()),
- end: Some(1_781_899_200),
- start_tzid: Some("America/Vancouver".to_string()),
- end_tzid: Some("America/Vancouver".to_string()),
- location: Some(social_location()),
- summary: Some("field crew".to_string()),
- image: None,
- participants: None,
- }
- }
-
- fn sample_calendar() -> RadrootsCalendar {
- RadrootsCalendar {
- d_tag: "AAAAAAAAAAAAAAAAAAAA_A".to_string(),
- title: "farm calendar".to_string(),
- events: vec![address_target(31923, "AAAAAAAAAAAAAAAAAAAA-A")],
- description: Some("Shared schedule for farm operations.".to_string()),
- summary: Some("field schedule".to_string()),
- image: None,
- }
- }
-
- fn sample_calendar_rsvp() -> RadrootsCalendarEventRsvp {
- RadrootsCalendarEventRsvp {
- d_tag: "AAAAAAAAAAAAAAAAAAAAAQ".to_string(),
- event: address_target(31923, "AAAAAAAAAAAAAAAAAAAA-A"),
- event_id: Some(synthetic_event_id('f')),
- status: RadrootsCalendarEventRsvpStatus::Tentative,
- free_busy: Some(RadrootsCalendarEventFreeBusy::Busy),
- note: Some("depends on harvest".to_string()),
- participants: None,
- }
- }
-
- fn sample_comment() -> RadrootsComment {
- RadrootsComment {
- root: event_target(30023, 'a'),
- parent: address_target(30023, "AAAAAAAAAAAAAAAAAAAAAg"),
- content: "great notes".to_string(),
- }
- }
-
- fn sample_reaction() -> RadrootsReaction {
- RadrootsReaction {
- target: address_target(30023, "AAAAAAAAAAAAAAAAAAAAAg"),
- content: String::new(),
- }
- }
-
- fn sample_repost() -> RadrootsRepost {
- RadrootsRepost {
- target: event_target(1, 'b'),
- content: Some("field update".to_string()),
- }
- }
-
- fn sample_generic_repost() -> RadrootsGenericRepost {
- RadrootsGenericRepost {
- target: address_target(30023, "AAAAAAAAAAAAAAAAAAAAAg"),
- target_kind: 30023,
- content: Some("article share".to_string()),
- }
- }
-
- fn sample_report() -> RadrootsReport {
- RadrootsReport {
- reported_pubkey: synthetic_pubkey('b'),
- report_type: RadrootsReportType::Spam,
- event: Some(event_target(1, 'c')),
- file: Some(RadrootsReportFileTarget {
- sha256: Some(
- "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef".to_string(),
- ),
- url: Some("https://media.example.test/bad.jpg".to_string()),
- magnet: None,
- }),
- content: Some("spam report".to_string()),
- }
- }
-
- fn sample_job_request() -> RadrootsJobRequest {
- RadrootsJobRequest {
- kind: 5100,
- inputs: vec![RadrootsJobInput {
- data: "alpha".to_string(),
- input_type: JobInputType::Text,
- relay: None,
- marker: None,
- }],
- output: None,
- params: vec![RadrootsJobParam {
- key: "mode".to_string(),
- value: "fast".to_string(),
- }],
- bid_sat: Some(42),
- relays: vec!["wss://relay.example.com".to_string()],
- providers: vec!["provider-a".to_string()],
- topics: vec!["topic-a".to_string()],
- encrypted: false,
- }
- }
-
- fn sample_workspace_manifest() -> RadrootsFarmWorkspaceManifest {
- RadrootsFarmWorkspaceManifest {
- d_tag: "AAAAAAAAAAAAAAAAAAAAAA".to_string(),
- schema: RADROOTS_FARM_WORKSPACE_SCHEMA.to_string(),
- farm_group_id: "field-group".to_string(),
- name: "Small Regen Farm".to_string(),
- owner_pubkey: "workspace_owner_pubkey".to_string(),
- farm: Some(RadrootsFarmRef {
- pubkey: "farm_pubkey".to_string(),
- d_tag: "AAAAAAAAAAAAAAAAAAAAAQ".to_string(),
- }),
- relays: vec![RadrootsFarmWorkspaceRelay {
- url: "wss://relay.example.invalid/farm/field-group".to_string(),
- mode: RadrootsFarmWorkspaceRelayMode::ReadWrite,
- }],
- media_servers: vec![RadrootsFarmWorkspaceMediaServer {
- url: "https://media.example.invalid/farm/field-group".to_string(),
- service: "RadrootsPrivateMedia".to_string(),
- }],
- supported_kinds: vec![78, 30078, KIND_FARM_FILE_METADATA],
- protocol_version: RADROOTS_FARM_WORKSPACE_PROTOCOL_VERSION.to_string(),
- created_at_ms: 1_780_000_000_000,
- updated_at_ms: None,
- }
- }
-
- fn sample_crdt_change() -> RadrootsFarmCrdtChange {
- RadrootsFarmCrdtChange {
- schema: RADROOTS_FARM_CRDT_CHANGE_SCHEMA.to_string(),
- workspace: RadrootsFarmWorkspaceRef {
- pubkey: "workspace_pubkey".to_string(),
- d_tag: "AAAAAAAAAAAAAAAAAAAAAA".to_string(),
- },
- farm_group_id: "field-group".to_string(),
- document_id: "AAAAAAAAAAAAAAAAAAAAAg".to_string(),
- document_kind: RadrootsFarmCrdtDocumentKind::FarmTask,
- crdt_backend: RadrootsCrdtBackend::Automerge,
- crdt_backend_version: Some("0.x".to_string()),
- actor_id: "actor_abc".to_string(),
- change_hash: "crdt_hash_abc".to_string(),
- dependencies: Vec::new(),
- encoded_change: "abc-DEF_012".to_string(),
- semantic_kind: RadrootsFarmSemanticKind::FarmTaskCreate,
- business_time_ms: 1_780_000_000_000,
- author_member_id: Some("member_abc".to_string()),
- app_version: Some("0.1.0".to_string()),
- }
- }
-
- fn sample_file_metadata() -> RadrootsFarmFileMetadata {
- RadrootsFarmFileMetadata {
- d_tag: "AAAAAAAAAAAAAAAAAAAAAQ".to_string(),
- workspace: RadrootsFarmWorkspaceRef {
- pubkey: "workspace_pubkey".to_string(),
- d_tag: "AAAAAAAAAAAAAAAAAAAAAA".to_string(),
- },
- farm_group_id: "field-group".to_string(),
- owner_document_id: "AAAAAAAAAAAAAAAAAAAAAg".to_string(),
- owner_document_kind: RadrootsFarmCrdtDocumentKind::FarmTask,
- caption: Some("Tomatoes harvested from Patch Y.".to_string()),
- url: "https://media.example.invalid/blob/sha256".to_string(),
- mime_type: "image/jpeg".to_string(),
- sha256: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef".to_string(),
- original_sha256: None,
- size_bytes: Some(123_456),
- dimensions: Some(RadrootsFarmFileDimensions { w: 1600, h: 1200 }),
- blurhash: None,
- thumb: Some(RadrootsFarmFileSource {
- url: "https://media.example.invalid/thumb/sha256".to_string(),
- mime_type: Some("image/jpeg".to_string()),
- dimensions: Some(RadrootsFarmFileDimensions { w: 320, h: 240 }),
- }),
- image: None,
- alt: Some("Harvested tomatoes in a crate".to_string()),
- fallbacks: Vec::new(),
- }
- }
-
- fn sample_group_metadata() -> RadrootsGroupEditableMetadata {
- RadrootsGroupEditableMetadata {
- name: Some("Small Regen Farm".to_string()),
- about: Some("Field app group".to_string()),
- picture: Some("https://media.example.invalid/group.png".to_string()),
- is_private: false,
- is_restricted: true,
- is_closed: false,
- is_hidden: false,
- supported_kinds: Some(vec![78, 30078, KIND_FARM_FILE_METADATA]),
- }
- }
-
- fn sample_group_user(role: &str) -> RadrootsGroupUserRef {
- RadrootsGroupUserRef {
- pubkey: format!("{role}_pubkey"),
- roles: vec![role.to_string()],
- }
- }
-
- fn sample_group_role() -> RadrootsGroupRole {
- RadrootsGroupRole {
- name: "member".to_string(),
- description: Some("can read and write group events".to_string()),
- permissions: vec!["read".to_string(), "write".to_string()],
- }
- }
-
- fn assert_tags_json(value: Result<String, RadrootsJsValue>) {
- let tags = tags_json(value);
- assert!(!tags.is_empty());
- }
-
- fn tags_json(value: Result<String, RadrootsJsValue>) -> Vec<Vec<String>> {
- let json = value.expect("tags json");
- serde_json::from_str(&json).expect("tags")
- }
-
- fn has_tag(tags: &[Vec<String>], key: &str, value: &str) -> bool {
- tags.iter().any(|tag| {
- tag.first().map(|entry| entry.as_str()) == Some(key)
- && tag.get(1).map(|entry| entry.as_str()) == Some(value)
- })
- }
-
- #[test]
- fn bindings_reject_invalid_json() {
- let bindings: [fn(&str) -> Result<String, RadrootsJsValue>; 46] = [
- listing_tags,
- listing_tags_full,
- post_tags,
- comment_tags,
- article_tags,
- file_metadata_tags,
- calendar_date_event_tags,
- calendar_time_event_tags,
- calendar_tags,
- calendar_event_rsvp_tags,
- repost_tags,
- generic_repost_tags,
- report_tags,
- follow_tags,
- document_tags,
- coop_tags,
- farm_tags,
- list_tags,
- list_set_tags,
- plot_tags,
- job_request_tags,
- job_result_tags,
- job_feedback_tags,
- reaction_tags,
- message_tags,
- message_file_tags,
- seal_tags,
- gift_wrap_tags,
- farm_workspace_manifest_tags,
- farm_crdt_change_tags,
- farm_file_metadata_tags,
- relay_auth_tags,
- http_auth_tags,
- group_put_user_tags,
- group_remove_user_tags,
- group_create_group_tags,
- group_edit_metadata_tags,
- group_delete_group_tags,
- group_delete_event_tags,
- group_create_invite_tags,
- group_join_request_tags,
- group_leave_request_tags,
- group_metadata_tags,
- group_admins_tags,
- group_members_tags,
- group_roles_tags,
- ];
-
- for binding in bindings {
- assert!(binding("{").is_err());
- }
- assert!(listing_tags("").is_err());
- }
-
- #[test]
- fn bindings_encode_to_json_when_input_is_valid() {
- let listing_json = serde_json::to_string(&sample_listing()).expect("listing json");
- let listing_tags_json = listing_tags(&listing_json).expect("listing tags");
- let listing_tags: Vec<Vec<String>> =
- serde_json::from_str(&listing_tags_json).expect("listing tags json");
- assert!(!listing_tags.is_empty());
-
- let request_json = serde_json::to_string(&sample_job_request()).expect("request json");
- let request_tags_json = job_request_tags(&request_json).expect("request tags");
- let request_tags: Vec<Vec<String>> =
- serde_json::from_str(&request_tags_json).expect("request tags json");
- assert!(!request_tags.is_empty());
- }
-
- #[test]
- fn social_bindings_encode_to_json_when_input_is_valid() {
- assert_tags_json(post_tags(
- &serde_json::to_string(&sample_post()).expect("post json"),
- ));
- assert_tags_json(comment_tags(
- &serde_json::to_string(&sample_comment()).expect("comment json"),
- ));
- assert_tags_json(article_tags(
- &serde_json::to_string(&sample_article()).expect("article json"),
- ));
- assert_tags_json(file_metadata_tags(
- &serde_json::to_string(&sample_public_file_metadata()).expect("file json"),
- ));
- assert_tags_json(calendar_date_event_tags(
- &serde_json::to_string(&sample_calendar_date_event()).expect("date json"),
- ));
- let time_tags = tags_json(calendar_time_event_tags(
- &serde_json::to_string(&sample_calendar_time_event()).expect("time json"),
- ));
- assert!(has_tag(&time_tags, "D", "2026-06-20"));
- assert_tags_json(calendar_tags(
- &serde_json::to_string(&sample_calendar()).expect("calendar json"),
- ));
- assert_tags_json(calendar_event_rsvp_tags(
- &serde_json::to_string(&sample_calendar_rsvp()).expect("rsvp json"),
- ));
- assert_tags_json(reaction_tags(
- &serde_json::to_string(&sample_reaction()).expect("reaction json"),
- ));
- assert_tags_json(repost_tags(
- &serde_json::to_string(&sample_repost()).expect("repost json"),
- ));
- assert_tags_json(generic_repost_tags(
- &serde_json::to_string(&sample_generic_repost()).expect("generic repost json"),
- ));
- assert_tags_json(report_tags(
- &serde_json::to_string(&sample_report()).expect("report json"),
- ));
- }
-
- #[test]
- fn social_bindings_surface_builder_errors() {
- let mut article = sample_article();
- article.d_tag.clear();
- assert!(article_tags(&serde_json::to_string(&article).expect("article json")).is_err());
-
- let mut comment = sample_comment();
- comment.root = event_target(1, 'a');
- assert!(comment_tags(&serde_json::to_string(&comment).expect("comment json")).is_err());
-
- let mut reaction = sample_reaction();
- reaction.target = RadrootsSocialTarget::External {
- id: "https://example.test/object".to_string(),
- external_kind: "web".to_string(),
- hint: None,
- };
- assert!(reaction_tags(&serde_json::to_string(&reaction).expect("reaction json")).is_err());
-
- let mut rsvp = sample_calendar_rsvp();
- rsvp.event = event_target(31923, 'f');
- assert!(
- calendar_event_rsvp_tags(&serde_json::to_string(&rsvp).expect("rsvp json")).is_err()
- );
-
- let mut report = sample_report();
- report.reported_pubkey.clear();
- assert!(report_tags(&serde_json::to_string(&report).expect("report json")).is_err());
- }
-
- #[test]
- fn field_bindings_encode_to_json_when_input_is_valid() {
- let workspace_json =
- serde_json::to_string(&sample_workspace_manifest()).expect("workspace json");
- assert_tags_json(farm_workspace_manifest_tags(&workspace_json));
-
- let crdt_json = serde_json::json!({
- "change": sample_crdt_change(),
- "author_pubkey": "author_pubkey"
- })
- .to_string();
- assert_tags_json(farm_crdt_change_tags(&crdt_json));
-
- let file_json = serde_json::to_string(&sample_file_metadata()).expect("file json");
- assert_tags_json(farm_file_metadata_tags(&file_json));
-
- let relay_auth_json = serde_json::to_string(&RadrootsRelayAuth {
- relay: "wss://relay.example.invalid/farm/field-group".to_string(),
- challenge: "relay-provided-challenge".to_string(),
- })
- .expect("relay auth json");
- assert_tags_json(relay_auth_tags(&relay_auth_json));
-
- let http_auth_json = serde_json::to_string(&RadrootsHttpAuth {
- url: "https://media.example.invalid/upload".to_string(),
- method: "POST".to_string(),
- payload_sha256: Some(
- "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef".to_string(),
- ),
- })
- .expect("http auth json");
- assert_tags_json(http_auth_tags(&http_auth_json));
- }
-
- #[test]
- fn group_bindings_encode_to_json_when_input_is_valid() {
- let metadata = sample_group_metadata();
- assert_tags_json(group_put_user_tags(
- &serde_json::to_string(&RadrootsGroupPutUser {
- group_id: "field-group".to_string(),
- message: Some("add member".to_string()),
- pubkey: "member_pubkey".to_string(),
- roles: vec!["member".to_string()],
- })
- .expect("put user json"),
- ));
- assert_tags_json(group_remove_user_tags(
- &serde_json::to_string(&RadrootsGroupRemoveUser {
- group_id: "field-group".to_string(),
- message: Some("remove member".to_string()),
- pubkey: "member_pubkey".to_string(),
- })
- .expect("remove user json"),
- ));
- assert_tags_json(group_create_group_tags(
- &serde_json::to_string(&RadrootsGroupCreateGroup {
- group_id: "field-group".to_string(),
- message: Some("create group".to_string()),
- metadata: metadata.clone(),
- })
- .expect("create group json"),
- ));
- assert_tags_json(group_edit_metadata_tags(
- &serde_json::to_string(&RadrootsGroupEditMetadata {
- group_id: "field-group".to_string(),
- message: Some("edit metadata".to_string()),
- metadata: metadata.clone(),
- })
- .expect("edit metadata json"),
- ));
- assert_tags_json(group_delete_group_tags(
- &serde_json::to_string(&RadrootsGroupDeleteGroup {
- group_id: "field-group".to_string(),
- message: Some("delete group".to_string()),
- })
- .expect("delete group json"),
- ));
- assert_tags_json(group_delete_event_tags(
- &serde_json::to_string(&RadrootsGroupDeleteEvent {
- group_id: "field-group".to_string(),
- message: Some("delete event".to_string()),
- event_id: "event_id".to_string(),
- })
- .expect("delete event json"),
- ));
- let invite_tags = tags_json(group_create_invite_tags(
- &serde_json::to_string(&RadrootsGroupCreateInvite {
- group_id: "field-group".to_string(),
- message: Some("join the field group".to_string()),
- code: "invite-code".to_string(),
- })
- .expect("invite json"),
- ));
- assert!(invite_tags.contains(&vec!["code".to_string(), "invite-code".to_string()]));
- assert_tags_json(group_join_request_tags(
- &serde_json::to_string(&RadrootsGroupJoinRequest {
- group_id: "field-group".to_string(),
- message: Some("requesting access".to_string()),
- code: Some("invite-code".to_string()),
- })
- .expect("join json"),
- ));
- assert_tags_json(group_leave_request_tags(
- &serde_json::to_string(&RadrootsGroupLeaveRequest {
- group_id: "field-group".to_string(),
- message: Some("leaving".to_string()),
- })
- .expect("leave json"),
- ));
- let metadata_tags = tags_json(group_metadata_tags(
- &serde_json::to_string(&RadrootsGroupMetadata {
- d_tag: "field-group".to_string(),
- metadata,
- })
- .expect("metadata json"),
- ));
- assert!(metadata_tags.contains(&vec!["restricted".to_string()]));
- assert!(metadata_tags.contains(&vec![
- "supported_kinds".to_string(),
- "78".to_string(),
- "30078".to_string(),
- KIND_FARM_FILE_METADATA.to_string()
- ]));
- assert_tags_json(group_admins_tags(
- &serde_json::to_string(&RadrootsGroupAdmins {
- d_tag: "field-group".to_string(),
- description: Some("group admins".to_string()),
- admins: vec![sample_group_user("admin")],
- })
- .expect("admins json"),
- ));
- assert_tags_json(group_members_tags(
- &serde_json::to_string(&RadrootsGroupMembers {
- d_tag: "field-group".to_string(),
- description: Some("group members".to_string()),
- members: vec![sample_group_user("member")],
- })
- .expect("members json"),
- ));
- assert_tags_json(group_roles_tags(
- &serde_json::to_string(&RadrootsGroupRoles {
- d_tag: "field-group".to_string(),
- description: Some("group roles".to_string()),
- roles: vec![sample_group_role()],
- })
- .expect("roles json"),
- ));
- }
-
- #[test]
- fn listing_bindings_surface_builder_errors() {
- let mut listing_json = serde_json::to_value(sample_listing()).expect("listing value");
- listing_json["bins"] = serde_json::Value::Array(Vec::new());
- let listing_json = serde_json::to_string(&listing_json).expect("listing json");
-
- assert!(listing_tags(&listing_json).is_err());
- assert!(listing_tags_full(&listing_json).is_err());
- }
-}
diff --git a/crates/replica_db_wasm/Cargo.toml b/crates/replica_db_wasm/Cargo.toml
@@ -1,36 +0,0 @@
-[package]
-name = "radroots_replica_db_wasm"
-publish = false
-version = "0.1.0-alpha.2"
-edition.workspace = true
-authors = ["Tyson Lupul <tyson@radroots.org>"]
-rust-version.workspace = true
-license.workspace = true
-description = "WebAssembly bindings for radroots_replica_db"
-repository.workspace = true
-homepage.workspace = true
-documentation = "https://docs.rs/radroots_replica_db_wasm"
-readme = "README"
-
-[lib]
-crate-type = ["cdylib", "rlib"]
-
-[dependencies]
-radroots_sql_core = { workspace = true, features = ["bridge"] }
-radroots_sql_wasm_core = { workspace = true, default-features = false, features = [
- "bridge",
-] }
-radroots_replica_db = { workspace = true }
-radroots_replica_db_schema = { workspace = true }
-radroots_replica_sync = { workspace = true, features = ["std"] }
-js-sys = { workspace = true }
-serde = { workspace = true, features = ["derive"] }
-serde_json = { workspace = true }
-serde-wasm-bindgen = { workspace = true }
-wasm-bindgen = { workspace = true }
-
-[dev-dependencies]
-wasm-bindgen-test = { workspace = true }
-
-[lints.rust]
-unexpected_cfgs = { level = "warn", check-cfg = ['cfg(coverage_nightly)'] }
diff --git a/crates/replica_db_wasm/README b/crates/replica_db_wasm/README
@@ -1,25 +0,0 @@
-# radroots_replica_db_wasm
-
-This is the README for `radroots_replica_db_wasm`, which provides WebAssembly
-bindings for `radroots_replica_db` in the `radroots` core libraries.
-
-## Overview
-
- * a wasm32-only wrapper around the replica database runtime surface;
- * integration with `radroots_sql_wasm_core` for browser and worker SQL
- execution;
- * a small target-specific entry point layer rather than a separate database
- implementation;
- * intended for JavaScript-facing callers that need the replica database APIs
- in wasm builds.
-
-## Copyright
-
-Except as otherwise noted, all files in the `radroots_replica_db_wasm`
-distribution are
-
- Copyright (c) 2020-2026 Tyson Lupul
-
-For information on usage and redistribution, and for a DISCLAIMER OF ALL
-WARRANTIES, see LICENSE included in the `radroots_replica_db_wasm`
-distribution.
diff --git a/crates/replica_db_wasm/pkg/package.json b/crates/replica_db_wasm/pkg/package.json
@@ -1,20 +0,0 @@
-{
- "name": "@radroots/radroots_replica-db-wasm",
- "description": "WebAssembly bindings for radroots_replica_db",
- "version": "0.1.0",
- "private": true,
- "type": "module",
- "files": [
- "dist"
- ],
- "main": "./dist/radroots_replica_db_wasm.js",
- "types": "./dist/radroots_replica_db_wasm.d.ts",
- "exports": {
- ".": {
- "types": "./dist/radroots_replica_db_wasm.d.ts",
- "import": "./dist/radroots_replica_db_wasm.js",
- "default": "./dist/radroots_replica_db_wasm.js"
- }
- },
- "sideEffects": false
-}
diff --git a/crates/replica_db_wasm/src/lib.rs b/crates/replica_db_wasm/src/lib.rs
@@ -1,29 +0,0 @@
-#![cfg(any(target_arch = "wasm32", coverage_nightly))]
-#![forbid(unsafe_code)]
-
-#[cfg(target_arch = "wasm32")]
-mod utils;
-#[cfg(target_arch = "wasm32")]
-mod wasm_impl;
-#[cfg(target_arch = "wasm32")]
-pub use wasm_impl::*;
-
-#[cfg(coverage_nightly)]
-pub fn coverage_branch_probe(input: bool) -> &'static str {
- if input {
- "replica-db-wasm"
- } else {
- "replica-db-wasm"
- }
-}
-
-#[cfg(all(test, coverage_nightly))]
-mod tests {
- use super::coverage_branch_probe;
-
- #[test]
- fn coverage_branch_probe_hits_both_paths() {
- assert_eq!(coverage_branch_probe(true), "replica-db-wasm");
- assert_eq!(coverage_branch_probe(false), "replica-db-wasm");
- }
-}
diff --git a/crates/replica_db_wasm/src/utils.rs b/crates/replica_db_wasm/src/utils.rs
@@ -1,13 +0,0 @@
-use serde::Serialize;
-use wasm_bindgen::prelude::*;
-
-use radroots_sql_core::SqlError;
-
-pub fn value_to_js<T>(value: T) -> Result<JsValue, JsValue>
-where
- T: Serialize,
-{
- let json = serde_json::to_string(&value)
- .map_err(|err| radroots_sql_wasm_core::err_js(SqlError::from(err)))?;
- Ok(JsValue::from_str(&json))
-}
diff --git a/crates/replica_db_wasm/src/wasm_impl.rs b/crates/replica_db_wasm/src/wasm_impl.rs
@@ -1,891 +0,0 @@
-use crate::utils::value_to_js;
-use radroots_replica_db::migrations;
-use radroots_replica_db::{ReplicaDbExportManifestRs, export_manifest};
-use radroots_replica_sync::radroots_replica_sync_status;
-use radroots_sql_core::{
- WasmSqlExecutor, export_lock_begin, export_lock_end, with_export_lock_bypass,
-};
-use radroots_sql_wasm_core::{err_js, parse_json};
-use wasm_bindgen::JsValue;
-use wasm_bindgen::prelude::*;
-
-use radroots_replica_db_schema::farm::{
- IFarmCreate, IFarmDelete, IFarmFindMany, IFarmFindOne, IFarmUpdate,
-};
-
-use radroots_replica_db_schema::farm_gcs_location::{
- IFarmGcsLocationCreate, IFarmGcsLocationDelete, IFarmGcsLocationFindMany,
- IFarmGcsLocationFindOne, IFarmGcsLocationUpdate,
-};
-
-use radroots_replica_db_schema::farm_member::{
- IFarmMemberCreate, IFarmMemberDelete, IFarmMemberFindMany, IFarmMemberFindOne,
- IFarmMemberUpdate,
-};
-
-use radroots_replica_db_schema::farm_member_claim::{
- IFarmMemberClaimCreate, IFarmMemberClaimDelete, IFarmMemberClaimFindMany,
- IFarmMemberClaimFindOne, IFarmMemberClaimUpdate,
-};
-
-use radroots_replica_db_schema::farm_tag::{
- IFarmTagCreate, IFarmTagDelete, IFarmTagFindMany, IFarmTagFindOne, IFarmTagUpdate,
-};
-
-use radroots_replica_db_schema::gcs_location::{
- IGcsLocationCreate, IGcsLocationDelete, IGcsLocationFindMany, IGcsLocationFindOne,
- IGcsLocationUpdate,
-};
-
-use radroots_replica_db_schema::log_error::{
- ILogErrorCreate, ILogErrorDelete, ILogErrorFindMany, ILogErrorFindOne, ILogErrorUpdate,
-};
-
-use radroots_replica_db_schema::media_image::{
- IMediaImageCreate, IMediaImageDelete, IMediaImageFindMany, IMediaImageFindOne,
- IMediaImageUpdate,
-};
-
-use radroots_replica_db_schema::nostr_profile::{
- INostrProfileCreate, INostrProfileDelete, INostrProfileFindMany, INostrProfileFindOne,
- INostrProfileUpdate,
-};
-
-use radroots_replica_db_schema::nostr_event_head::{
- INostrEventHeadCreate, INostrEventHeadDelete, INostrEventHeadFindMany, INostrEventHeadFindOne,
- INostrEventHeadUpdate,
-};
-
-use radroots_replica_db_schema::nostr_relay::{
- INostrRelayCreate, INostrRelayDelete, INostrRelayFindMany, INostrRelayFindOne,
- INostrRelayUpdate,
-};
-
-use radroots_replica_db_schema::trade_product::{
- ITradeProductCreate, ITradeProductDelete, ITradeProductFindMany, ITradeProductFindOne,
- ITradeProductUpdate,
-};
-
-use radroots_replica_db_schema::plot::{
- IPlotCreate, IPlotDelete, IPlotFindMany, IPlotFindOne, IPlotUpdate,
-};
-
-use radroots_replica_db_schema::plot_gcs_location::{
- IPlotGcsLocationCreate, IPlotGcsLocationDelete, IPlotGcsLocationFindMany,
- IPlotGcsLocationFindOne, IPlotGcsLocationUpdate,
-};
-
-use radroots_replica_db_schema::plot_tag::{
- IPlotTagCreate, IPlotTagDelete, IPlotTagFindMany, IPlotTagFindOne, IPlotTagUpdate,
-};
-
-use radroots_replica_db_schema::nostr_profile_relay::INostrProfileRelayRelation;
-
-use radroots_replica_db_schema::trade_product_location::ITradeProductLocationRelation;
-
-use radroots_replica_db_schema::trade_product_media::ITradeProductMediaRelation;
-
-#[wasm_bindgen(js_name = replica_db_run_migrations)]
-pub fn replica_db_run_migrations() -> Result<(), JsValue> {
- let exec = WasmSqlExecutor::new();
- migrations::run_all_up(&exec).map_err(err_js)
-}
-
-#[wasm_bindgen(js_name = replica_db_reset_database)]
-pub fn replica_db_reset_database() -> Result<(), JsValue> {
- let exec = WasmSqlExecutor::new();
- migrations::run_all_down(&exec).map_err(err_js)
-}
-
-#[wasm_bindgen(js_name = replica_db_export_json)]
-pub fn replica_db_export_json() -> Result<JsValue, JsValue> {
- let exec = WasmSqlExecutor::new();
- let dump = radroots_replica_db::backup::export_database_backup(&exec).map_err(err_js)?;
- value_to_js(dump)
-}
-
-#[wasm_bindgen(js_name = replica_db_import_json)]
-pub fn replica_db_import_json(dump_json: &str) -> Result<(), JsValue> {
- let exec = WasmSqlExecutor::new();
- radroots_replica_db::backup::restore_database_backup_json(&exec, dump_json).map_err(err_js)
-}
-
-#[wasm_bindgen(js_name = replica_db_export_begin)]
-pub fn replica_db_export_begin() -> Result<JsValue, JsValue> {
- export_lock_begin().map_err(err_js)?;
- let exec = WasmSqlExecutor::new();
- let result = with_export_lock_bypass(|| export_snapshot(&exec));
- match result {
- Ok(value) => Ok(value),
- Err(err) => {
- export_lock_end();
- Err(err)
- }
- }
-}
-
-#[wasm_bindgen(js_name = replica_db_export_finish)]
-pub fn replica_db_export_finish() -> Result<(), JsValue> {
- export_lock_end();
- Ok(())
-}
-
-fn export_snapshot(exec: &WasmSqlExecutor) -> Result<JsValue, JsValue> {
- let status = radroots_replica_sync_status(exec).map_err(|err| {
- err_js(radroots_sql_core::SqlError::InvalidArgument(
- err.to_string(),
- ))
- })?;
- if status.pending_count > 0 {
- return Err(err_js(radroots_sql_core::SqlError::InvalidArgument(
- format!(
- "replica db export requires synced state (pending {}/{})",
- status.pending_count, status.expected_count
- ),
- )));
- }
- let manifest = export_manifest(exec).map_err(err_js)?;
- export_snapshot_value(manifest)
-}
-
-fn export_snapshot_value(manifest: ReplicaDbExportManifestRs) -> Result<JsValue, JsValue> {
- let bytes_js = radroots_sql_wasm_core::export_bytes();
- export_snapshot_value_with_bytes(manifest, bytes_js)
-}
-
-fn export_snapshot_value_with_bytes(
- manifest: ReplicaDbExportManifestRs,
- bytes_js: JsValue,
-) -> Result<JsValue, JsValue> {
- let manifest_js = serde_wasm_bindgen::to_value(&manifest).map_err(|err| {
- err_js(radroots_sql_core::SqlError::SerializationError(
- err.to_string(),
- ))
- })?;
- let obj = js_sys::Object::new();
- js_sys::Reflect::set(&obj, &JsValue::from_str("manifest_rs"), &manifest_js)
- .map_err(|_| err_js(radroots_sql_core::SqlError::Internal))?;
- js_sys::Reflect::set(&obj, &JsValue::from_str("db_bytes"), &bytes_js)
- .map_err(|_| err_js(radroots_sql_core::SqlError::Internal))?;
- Ok(JsValue::from(obj))
-}
-
-#[cfg(all(test, target_arch = "wasm32"))]
-mod tests {
- use super::export_snapshot_value_with_bytes;
- use js_sys::{Reflect, Uint8Array};
- use wasm_bindgen::JsValue;
-
- #[wasm_bindgen_test::wasm_bindgen_test]
- fn export_snapshot_value_includes_fields() {
- let manifest = radroots_replica_db::ReplicaDbExportManifestRs {
- export_version: "1".to_string(),
- replica_db_version: "0.0.0".to_string(),
- backup_format_version: "0.0.0".to_string(),
- schema_hash: "hash".to_string(),
- schema: Vec::new(),
- migrations: Vec::new(),
- table_counts: Vec::new(),
- };
- let bytes = Uint8Array::new_with_length(2);
- let js =
- export_snapshot_value_with_bytes(manifest, JsValue::from(bytes)).expect("snapshot");
- let manifest_rs =
- Reflect::get(&js, &JsValue::from_str("manifest_rs")).expect("manifest_rs");
- let db_bytes = Reflect::get(&js, &JsValue::from_str("db_bytes")).expect("db_bytes");
- assert!(manifest_rs.is_object());
- assert!(db_bytes.is_object());
- }
-}
-
-#[wasm_bindgen(js_name = replica_db_farm_create)]
-pub fn replica_db_farm_create(opts_json: &str) -> Result<JsValue, JsValue> {
- let opts: IFarmCreate = parse_json(opts_json).map_err(err_js)?;
- let exec = WasmSqlExecutor::new();
- let out = radroots_replica_db::farm::create(&exec, &opts).map_err(|e| err_js(e.err))?;
- value_to_js(out)
-}
-
-#[wasm_bindgen(js_name = replica_db_farm_find_one)]
-pub fn replica_db_farm_find_one(opts_json: &str) -> Result<JsValue, JsValue> {
- let opts: IFarmFindOne = parse_json(opts_json).map_err(err_js)?;
- let exec = WasmSqlExecutor::new();
- let out = radroots_replica_db::farm::find_one(&exec, &opts).map_err(|e| err_js(e.err))?;
- value_to_js(out)
-}
-
-#[wasm_bindgen(js_name = replica_db_farm_find_many)]
-pub fn replica_db_farm_find_many(opts_json: &str) -> Result<JsValue, JsValue> {
- let opts: IFarmFindMany = parse_json(opts_json).map_err(err_js)?;
- let exec = WasmSqlExecutor::new();
- let out = radroots_replica_db::farm::find_many(&exec, &opts).map_err(|e| err_js(e.err))?;
- value_to_js(out)
-}
-
-#[wasm_bindgen(js_name = replica_db_farm_update)]
-pub fn replica_db_farm_update(opts_json: &str) -> Result<JsValue, JsValue> {
- let opts: IFarmUpdate = parse_json(opts_json).map_err(err_js)?;
- let exec = WasmSqlExecutor::new();
- let out = radroots_replica_db::farm::update(&exec, &opts).map_err(|e| err_js(e.err))?;
- value_to_js(out)
-}
-
-#[wasm_bindgen(js_name = replica_db_farm_delete)]
-pub fn replica_db_farm_delete(opts_json: &str) -> Result<JsValue, JsValue> {
- let opts: IFarmDelete = parse_json(opts_json).map_err(err_js)?;
- let exec = WasmSqlExecutor::new();
- let out = radroots_replica_db::farm::delete(&exec, &opts).map_err(|e| err_js(e.err))?;
- value_to_js(out)
-}
-
-#[wasm_bindgen(js_name = replica_db_plot_create)]
-pub fn replica_db_plot_create(opts_json: &str) -> Result<JsValue, JsValue> {
- let opts: IPlotCreate = parse_json(opts_json).map_err(err_js)?;
- let exec = WasmSqlExecutor::new();
- let out = radroots_replica_db::plot::create(&exec, &opts).map_err(|e| err_js(e.err))?;
- value_to_js(out)
-}
-
-#[wasm_bindgen(js_name = replica_db_plot_find_one)]
-pub fn replica_db_plot_find_one(opts_json: &str) -> Result<JsValue, JsValue> {
- let opts: IPlotFindOne = parse_json(opts_json).map_err(err_js)?;
- let exec = WasmSqlExecutor::new();
- let out = radroots_replica_db::plot::find_one(&exec, &opts).map_err(|e| err_js(e.err))?;
- value_to_js(out)
-}
-
-#[wasm_bindgen(js_name = replica_db_plot_find_many)]
-pub fn replica_db_plot_find_many(opts_json: &str) -> Result<JsValue, JsValue> {
- let opts: IPlotFindMany = parse_json(opts_json).map_err(err_js)?;
- let exec = WasmSqlExecutor::new();
- let out = radroots_replica_db::plot::find_many(&exec, &opts).map_err(|e| err_js(e.err))?;
- value_to_js(out)
-}
-
-#[wasm_bindgen(js_name = replica_db_plot_update)]
-pub fn replica_db_plot_update(opts_json: &str) -> Result<JsValue, JsValue> {
- let opts: IPlotUpdate = parse_json(opts_json).map_err(err_js)?;
- let exec = WasmSqlExecutor::new();
- let out = radroots_replica_db::plot::update(&exec, &opts).map_err(|e| err_js(e.err))?;
- value_to_js(out)
-}
-
-#[wasm_bindgen(js_name = replica_db_plot_delete)]
-pub fn replica_db_plot_delete(opts_json: &str) -> Result<JsValue, JsValue> {
- let opts: IPlotDelete = parse_json(opts_json).map_err(err_js)?;
- let exec = WasmSqlExecutor::new();
- let out = radroots_replica_db::plot::delete(&exec, &opts).map_err(|e| err_js(e.err))?;
- value_to_js(out)
-}
-
-#[wasm_bindgen(js_name = replica_db_gcs_location_create)]
-pub fn replica_db_gcs_location_create(opts_json: &str) -> Result<JsValue, JsValue> {
- let opts: IGcsLocationCreate = parse_json(opts_json).map_err(err_js)?;
- let exec = WasmSqlExecutor::new();
- let out = radroots_replica_db::gcs_location::create(&exec, &opts).map_err(|e| err_js(e.err))?;
- value_to_js(out)
-}
-
-#[wasm_bindgen(js_name = replica_db_gcs_location_find_one)]
-pub fn replica_db_gcs_location_find_one(opts_json: &str) -> Result<JsValue, JsValue> {
- let opts: IGcsLocationFindOne = parse_json(opts_json).map_err(err_js)?;
- let exec = WasmSqlExecutor::new();
- let out =
- radroots_replica_db::gcs_location::find_one(&exec, &opts).map_err(|e| err_js(e.err))?;
- value_to_js(out)
-}
-
-#[wasm_bindgen(js_name = replica_db_gcs_location_find_many)]
-pub fn replica_db_gcs_location_find_many(opts_json: &str) -> Result<JsValue, JsValue> {
- let opts: IGcsLocationFindMany = parse_json(opts_json).map_err(err_js)?;
- let exec = WasmSqlExecutor::new();
- let out =
- radroots_replica_db::gcs_location::find_many(&exec, &opts).map_err(|e| err_js(e.err))?;
- value_to_js(out)
-}
-
-#[wasm_bindgen(js_name = replica_db_gcs_location_update)]
-pub fn replica_db_gcs_location_update(opts_json: &str) -> Result<JsValue, JsValue> {
- let opts: IGcsLocationUpdate = parse_json(opts_json).map_err(err_js)?;
- let exec = WasmSqlExecutor::new();
- let out = radroots_replica_db::gcs_location::update(&exec, &opts).map_err(|e| err_js(e.err))?;
- value_to_js(out)
-}
-
-#[wasm_bindgen(js_name = replica_db_gcs_location_delete)]
-pub fn replica_db_gcs_location_delete(opts_json: &str) -> Result<JsValue, JsValue> {
- let opts: IGcsLocationDelete = parse_json(opts_json).map_err(err_js)?;
- let exec = WasmSqlExecutor::new();
- let out = radroots_replica_db::gcs_location::delete(&exec, &opts).map_err(|e| err_js(e.err))?;
- value_to_js(out)
-}
-
-#[wasm_bindgen(js_name = replica_db_farm_gcs_location_create)]
-pub fn replica_db_farm_gcs_location_create(opts_json: &str) -> Result<JsValue, JsValue> {
- let opts: IFarmGcsLocationCreate = parse_json(opts_json).map_err(err_js)?;
- let exec = WasmSqlExecutor::new();
- let out =
- radroots_replica_db::farm_gcs_location::create(&exec, &opts).map_err(|e| err_js(e.err))?;
- value_to_js(out)
-}
-
-#[wasm_bindgen(js_name = replica_db_farm_gcs_location_find_one)]
-pub fn replica_db_farm_gcs_location_find_one(opts_json: &str) -> Result<JsValue, JsValue> {
- let opts: IFarmGcsLocationFindOne = parse_json(opts_json).map_err(err_js)?;
- let exec = WasmSqlExecutor::new();
- let out = radroots_replica_db::farm_gcs_location::find_one(&exec, &opts)
- .map_err(|e| err_js(e.err))?;
- value_to_js(out)
-}
-
-#[wasm_bindgen(js_name = replica_db_farm_gcs_location_find_many)]
-pub fn replica_db_farm_gcs_location_find_many(opts_json: &str) -> Result<JsValue, JsValue> {
- let opts: IFarmGcsLocationFindMany = parse_json(opts_json).map_err(err_js)?;
- let exec = WasmSqlExecutor::new();
- let out = radroots_replica_db::farm_gcs_location::find_many(&exec, &opts)
- .map_err(|e| err_js(e.err))?;
- value_to_js(out)
-}
-
-#[wasm_bindgen(js_name = replica_db_farm_gcs_location_update)]
-pub fn replica_db_farm_gcs_location_update(opts_json: &str) -> Result<JsValue, JsValue> {
- let opts: IFarmGcsLocationUpdate = parse_json(opts_json).map_err(err_js)?;
- let exec = WasmSqlExecutor::new();
- let out =
- radroots_replica_db::farm_gcs_location::update(&exec, &opts).map_err(|e| err_js(e.err))?;
- value_to_js(out)
-}
-
-#[wasm_bindgen(js_name = replica_db_farm_gcs_location_delete)]
-pub fn replica_db_farm_gcs_location_delete(opts_json: &str) -> Result<JsValue, JsValue> {
- let opts: IFarmGcsLocationDelete = parse_json(opts_json).map_err(err_js)?;
- let exec = WasmSqlExecutor::new();
- let out =
- radroots_replica_db::farm_gcs_location::delete(&exec, &opts).map_err(|e| err_js(e.err))?;
- value_to_js(out)
-}
-
-#[wasm_bindgen(js_name = replica_db_plot_gcs_location_create)]
-pub fn replica_db_plot_gcs_location_create(opts_json: &str) -> Result<JsValue, JsValue> {
- let opts: IPlotGcsLocationCreate = parse_json(opts_json).map_err(err_js)?;
- let exec = WasmSqlExecutor::new();
- let out =
- radroots_replica_db::plot_gcs_location::create(&exec, &opts).map_err(|e| err_js(e.err))?;
- value_to_js(out)
-}
-
-#[wasm_bindgen(js_name = replica_db_plot_gcs_location_find_one)]
-pub fn replica_db_plot_gcs_location_find_one(opts_json: &str) -> Result<JsValue, JsValue> {
- let opts: IPlotGcsLocationFindOne = parse_json(opts_json).map_err(err_js)?;
- let exec = WasmSqlExecutor::new();
- let out = radroots_replica_db::plot_gcs_location::find_one(&exec, &opts)
- .map_err(|e| err_js(e.err))?;
- value_to_js(out)
-}
-
-#[wasm_bindgen(js_name = replica_db_plot_gcs_location_find_many)]
-pub fn replica_db_plot_gcs_location_find_many(opts_json: &str) -> Result<JsValue, JsValue> {
- let opts: IPlotGcsLocationFindMany = parse_json(opts_json).map_err(err_js)?;
- let exec = WasmSqlExecutor::new();
- let out = radroots_replica_db::plot_gcs_location::find_many(&exec, &opts)
- .map_err(|e| err_js(e.err))?;
- value_to_js(out)
-}
-
-#[wasm_bindgen(js_name = replica_db_plot_gcs_location_update)]
-pub fn replica_db_plot_gcs_location_update(opts_json: &str) -> Result<JsValue, JsValue> {
- let opts: IPlotGcsLocationUpdate = parse_json(opts_json).map_err(err_js)?;
- let exec = WasmSqlExecutor::new();
- let out =
- radroots_replica_db::plot_gcs_location::update(&exec, &opts).map_err(|e| err_js(e.err))?;
- value_to_js(out)
-}
-
-#[wasm_bindgen(js_name = replica_db_plot_gcs_location_delete)]
-pub fn replica_db_plot_gcs_location_delete(opts_json: &str) -> Result<JsValue, JsValue> {
- let opts: IPlotGcsLocationDelete = parse_json(opts_json).map_err(err_js)?;
- let exec = WasmSqlExecutor::new();
- let out =
- radroots_replica_db::plot_gcs_location::delete(&exec, &opts).map_err(|e| err_js(e.err))?;
- value_to_js(out)
-}
-
-#[wasm_bindgen(js_name = replica_db_farm_tag_create)]
-pub fn replica_db_farm_tag_create(opts_json: &str) -> Result<JsValue, JsValue> {
- let opts: IFarmTagCreate = parse_json(opts_json).map_err(err_js)?;
- let exec = WasmSqlExecutor::new();
- let out = radroots_replica_db::farm_tag::create(&exec, &opts).map_err(|e| err_js(e.err))?;
- value_to_js(out)
-}
-
-#[wasm_bindgen(js_name = replica_db_farm_tag_find_one)]
-pub fn replica_db_farm_tag_find_one(opts_json: &str) -> Result<JsValue, JsValue> {
- let opts: IFarmTagFindOne = parse_json(opts_json).map_err(err_js)?;
- let exec = WasmSqlExecutor::new();
- let out = radroots_replica_db::farm_tag::find_one(&exec, &opts).map_err(|e| err_js(e.err))?;
- value_to_js(out)
-}
-
-#[wasm_bindgen(js_name = replica_db_farm_tag_find_many)]
-pub fn replica_db_farm_tag_find_many(opts_json: &str) -> Result<JsValue, JsValue> {
- let opts: IFarmTagFindMany = parse_json(opts_json).map_err(err_js)?;
- let exec = WasmSqlExecutor::new();
- let out = radroots_replica_db::farm_tag::find_many(&exec, &opts).map_err(|e| err_js(e.err))?;
- value_to_js(out)
-}
-
-#[wasm_bindgen(js_name = replica_db_farm_tag_update)]
-pub fn replica_db_farm_tag_update(opts_json: &str) -> Result<JsValue, JsValue> {
- let opts: IFarmTagUpdate = parse_json(opts_json).map_err(err_js)?;
- let exec = WasmSqlExecutor::new();
- let out = radroots_replica_db::farm_tag::update(&exec, &opts).map_err(|e| err_js(e.err))?;
- value_to_js(out)
-}
-
-#[wasm_bindgen(js_name = replica_db_farm_tag_delete)]
-pub fn replica_db_farm_tag_delete(opts_json: &str) -> Result<JsValue, JsValue> {
- let opts: IFarmTagDelete = parse_json(opts_json).map_err(err_js)?;
- let exec = WasmSqlExecutor::new();
- let out = radroots_replica_db::farm_tag::delete(&exec, &opts).map_err(|e| err_js(e.err))?;
- value_to_js(out)
-}
-
-#[wasm_bindgen(js_name = replica_db_plot_tag_create)]
-pub fn replica_db_plot_tag_create(opts_json: &str) -> Result<JsValue, JsValue> {
- let opts: IPlotTagCreate = parse_json(opts_json).map_err(err_js)?;
- let exec = WasmSqlExecutor::new();
- let out = radroots_replica_db::plot_tag::create(&exec, &opts).map_err(|e| err_js(e.err))?;
- value_to_js(out)
-}
-
-#[wasm_bindgen(js_name = replica_db_plot_tag_find_one)]
-pub fn replica_db_plot_tag_find_one(opts_json: &str) -> Result<JsValue, JsValue> {
- let opts: IPlotTagFindOne = parse_json(opts_json).map_err(err_js)?;
- let exec = WasmSqlExecutor::new();
- let out = radroots_replica_db::plot_tag::find_one(&exec, &opts).map_err(|e| err_js(e.err))?;
- value_to_js(out)
-}
-
-#[wasm_bindgen(js_name = replica_db_plot_tag_find_many)]
-pub fn replica_db_plot_tag_find_many(opts_json: &str) -> Result<JsValue, JsValue> {
- let opts: IPlotTagFindMany = parse_json(opts_json).map_err(err_js)?;
- let exec = WasmSqlExecutor::new();
- let out = radroots_replica_db::plot_tag::find_many(&exec, &opts).map_err(|e| err_js(e.err))?;
- value_to_js(out)
-}
-
-#[wasm_bindgen(js_name = replica_db_plot_tag_update)]
-pub fn replica_db_plot_tag_update(opts_json: &str) -> Result<JsValue, JsValue> {
- let opts: IPlotTagUpdate = parse_json(opts_json).map_err(err_js)?;
- let exec = WasmSqlExecutor::new();
- let out = radroots_replica_db::plot_tag::update(&exec, &opts).map_err(|e| err_js(e.err))?;
- value_to_js(out)
-}
-
-#[wasm_bindgen(js_name = replica_db_plot_tag_delete)]
-pub fn replica_db_plot_tag_delete(opts_json: &str) -> Result<JsValue, JsValue> {
- let opts: IPlotTagDelete = parse_json(opts_json).map_err(err_js)?;
- let exec = WasmSqlExecutor::new();
- let out = radroots_replica_db::plot_tag::delete(&exec, &opts).map_err(|e| err_js(e.err))?;
- value_to_js(out)
-}
-
-#[wasm_bindgen(js_name = replica_db_farm_member_create)]
-pub fn replica_db_farm_member_create(opts_json: &str) -> Result<JsValue, JsValue> {
- let opts: IFarmMemberCreate = parse_json(opts_json).map_err(err_js)?;
- let exec = WasmSqlExecutor::new();
- let out = radroots_replica_db::farm_member::create(&exec, &opts).map_err(|e| err_js(e.err))?;
- value_to_js(out)
-}
-
-#[wasm_bindgen(js_name = replica_db_farm_member_find_one)]
-pub fn replica_db_farm_member_find_one(opts_json: &str) -> Result<JsValue, JsValue> {
- let opts: IFarmMemberFindOne = parse_json(opts_json).map_err(err_js)?;
- let exec = WasmSqlExecutor::new();
- let out =
- radroots_replica_db::farm_member::find_one(&exec, &opts).map_err(|e| err_js(e.err))?;
- value_to_js(out)
-}
-
-#[wasm_bindgen(js_name = replica_db_farm_member_find_many)]
-pub fn replica_db_farm_member_find_many(opts_json: &str) -> Result<JsValue, JsValue> {
- let opts: IFarmMemberFindMany = parse_json(opts_json).map_err(err_js)?;
- let exec = WasmSqlExecutor::new();
- let out =
- radroots_replica_db::farm_member::find_many(&exec, &opts).map_err(|e| err_js(e.err))?;
- value_to_js(out)
-}
-
-#[wasm_bindgen(js_name = replica_db_farm_member_update)]
-pub fn replica_db_farm_member_update(opts_json: &str) -> Result<JsValue, JsValue> {
- let opts: IFarmMemberUpdate = parse_json(opts_json).map_err(err_js)?;
- let exec = WasmSqlExecutor::new();
- let out = radroots_replica_db::farm_member::update(&exec, &opts).map_err(|e| err_js(e.err))?;
- value_to_js(out)
-}
-
-#[wasm_bindgen(js_name = replica_db_farm_member_delete)]
-pub fn replica_db_farm_member_delete(opts_json: &str) -> Result<JsValue, JsValue> {
- let opts: IFarmMemberDelete = parse_json(opts_json).map_err(err_js)?;
- let exec = WasmSqlExecutor::new();
- let out = radroots_replica_db::farm_member::delete(&exec, &opts).map_err(|e| err_js(e.err))?;
- value_to_js(out)
-}
-
-#[wasm_bindgen(js_name = replica_db_farm_member_claim_create)]
-pub fn replica_db_farm_member_claim_create(opts_json: &str) -> Result<JsValue, JsValue> {
- let opts: IFarmMemberClaimCreate = parse_json(opts_json).map_err(err_js)?;
- let exec = WasmSqlExecutor::new();
- let out =
- radroots_replica_db::farm_member_claim::create(&exec, &opts).map_err(|e| err_js(e.err))?;
- value_to_js(out)
-}
-
-#[wasm_bindgen(js_name = replica_db_farm_member_claim_find_one)]
-pub fn replica_db_farm_member_claim_find_one(opts_json: &str) -> Result<JsValue, JsValue> {
- let opts: IFarmMemberClaimFindOne = parse_json(opts_json).map_err(err_js)?;
- let exec = WasmSqlExecutor::new();
- let out = radroots_replica_db::farm_member_claim::find_one(&exec, &opts)
- .map_err(|e| err_js(e.err))?;
- value_to_js(out)
-}
-
-#[wasm_bindgen(js_name = replica_db_farm_member_claim_find_many)]
-pub fn replica_db_farm_member_claim_find_many(opts_json: &str) -> Result<JsValue, JsValue> {
- let opts: IFarmMemberClaimFindMany = parse_json(opts_json).map_err(err_js)?;
- let exec = WasmSqlExecutor::new();
- let out = radroots_replica_db::farm_member_claim::find_many(&exec, &opts)
- .map_err(|e| err_js(e.err))?;
- value_to_js(out)
-}
-
-#[wasm_bindgen(js_name = replica_db_farm_member_claim_update)]
-pub fn replica_db_farm_member_claim_update(opts_json: &str) -> Result<JsValue, JsValue> {
- let opts: IFarmMemberClaimUpdate = parse_json(opts_json).map_err(err_js)?;
- let exec = WasmSqlExecutor::new();
- let out =
- radroots_replica_db::farm_member_claim::update(&exec, &opts).map_err(|e| err_js(e.err))?;
- value_to_js(out)
-}
-
-#[wasm_bindgen(js_name = replica_db_farm_member_claim_delete)]
-pub fn replica_db_farm_member_claim_delete(opts_json: &str) -> Result<JsValue, JsValue> {
- let opts: IFarmMemberClaimDelete = parse_json(opts_json).map_err(err_js)?;
- let exec = WasmSqlExecutor::new();
- let out =
- radroots_replica_db::farm_member_claim::delete(&exec, &opts).map_err(|e| err_js(e.err))?;
- value_to_js(out)
-}
-
-#[wasm_bindgen(js_name = replica_db_log_error_create)]
-pub fn replica_db_log_error_create(opts_json: &str) -> Result<JsValue, JsValue> {
- let opts: ILogErrorCreate = parse_json(opts_json).map_err(err_js)?;
- let exec = WasmSqlExecutor::new();
- let out = radroots_replica_db::log_error::create(&exec, &opts).map_err(|e| err_js(e.err))?;
- value_to_js(out)
-}
-
-#[wasm_bindgen(js_name = replica_db_log_error_find_one)]
-pub fn replica_db_log_error_find_one(opts_json: &str) -> Result<JsValue, JsValue> {
- let opts: ILogErrorFindOne = parse_json(opts_json).map_err(err_js)?;
- let exec = WasmSqlExecutor::new();
- let out = radroots_replica_db::log_error::find_one(&exec, &opts).map_err(|e| err_js(e.err))?;
- value_to_js(out)
-}
-
-#[wasm_bindgen(js_name = replica_db_log_error_find_many)]
-pub fn replica_db_log_error_find_many(opts_json: &str) -> Result<JsValue, JsValue> {
- let opts: ILogErrorFindMany = parse_json(opts_json).map_err(err_js)?;
- let exec = WasmSqlExecutor::new();
- let out = radroots_replica_db::log_error::find_many(&exec, &opts).map_err(|e| err_js(e.err))?;
- value_to_js(out)
-}
-
-#[wasm_bindgen(js_name = replica_db_log_error_update)]
-pub fn replica_db_log_error_update(opts_json: &str) -> Result<JsValue, JsValue> {
- let opts: ILogErrorUpdate = parse_json(opts_json).map_err(err_js)?;
- let exec = WasmSqlExecutor::new();
- let out = radroots_replica_db::log_error::update(&exec, &opts).map_err(|e| err_js(e.err))?;
- value_to_js(out)
-}
-
-#[wasm_bindgen(js_name = replica_db_log_error_delete)]
-pub fn replica_db_log_error_delete(opts_json: &str) -> Result<JsValue, JsValue> {
- let opts: ILogErrorDelete = parse_json(opts_json).map_err(err_js)?;
- let exec = WasmSqlExecutor::new();
- let out = radroots_replica_db::log_error::delete(&exec, &opts).map_err(|e| err_js(e.err))?;
- value_to_js(out)
-}
-
-#[wasm_bindgen(js_name = replica_db_media_image_create)]
-pub fn replica_db_media_image_create(opts_json: &str) -> Result<JsValue, JsValue> {
- let opts: IMediaImageCreate = parse_json(opts_json).map_err(err_js)?;
- let exec = WasmSqlExecutor::new();
- let out = radroots_replica_db::media_image::create(&exec, &opts).map_err(|e| err_js(e.err))?;
- value_to_js(out)
-}
-
-#[wasm_bindgen(js_name = replica_db_media_image_find_one)]
-pub fn replica_db_media_image_find_one(opts_json: &str) -> Result<JsValue, JsValue> {
- let opts: IMediaImageFindOne = parse_json(opts_json).map_err(err_js)?;
- let exec = WasmSqlExecutor::new();
- let out =
- radroots_replica_db::media_image::find_one(&exec, &opts).map_err(|e| err_js(e.err))?;
- value_to_js(out)
-}
-
-#[wasm_bindgen(js_name = replica_db_media_image_find_many)]
-pub fn replica_db_media_image_find_many(opts_json: &str) -> Result<JsValue, JsValue> {
- let opts: IMediaImageFindMany = parse_json(opts_json).map_err(err_js)?;
- let exec = WasmSqlExecutor::new();
- let out =
- radroots_replica_db::media_image::find_many(&exec, &opts).map_err(|e| err_js(e.err))?;
- value_to_js(out)
-}
-
-#[wasm_bindgen(js_name = replica_db_media_image_update)]
-pub fn replica_db_media_image_update(opts_json: &str) -> Result<JsValue, JsValue> {
- let opts: IMediaImageUpdate = parse_json(opts_json).map_err(err_js)?;
- let exec = WasmSqlExecutor::new();
- let out = radroots_replica_db::media_image::update(&exec, &opts).map_err(|e| err_js(e.err))?;
- value_to_js(out)
-}
-
-#[wasm_bindgen(js_name = replica_db_media_image_delete)]
-pub fn replica_db_media_image_delete(opts_json: &str) -> Result<JsValue, JsValue> {
- let opts: IMediaImageDelete = parse_json(opts_json).map_err(err_js)?;
- let exec = WasmSqlExecutor::new();
- let out = radroots_replica_db::media_image::delete(&exec, &opts).map_err(|e| err_js(e.err))?;
- value_to_js(out)
-}
-
-#[wasm_bindgen(js_name = replica_db_nostr_profile_create)]
-pub fn replica_db_nostr_profile_create(opts_json: &str) -> Result<JsValue, JsValue> {
- let opts: INostrProfileCreate = parse_json(opts_json).map_err(err_js)?;
- let exec = WasmSqlExecutor::new();
- let out =
- radroots_replica_db::nostr_profile::create(&exec, &opts).map_err(|e| err_js(e.err))?;
- value_to_js(out)
-}
-
-#[wasm_bindgen(js_name = replica_db_nostr_profile_find_one)]
-pub fn replica_db_nostr_profile_find_one(opts_json: &str) -> Result<JsValue, JsValue> {
- let opts: INostrProfileFindOne = parse_json(opts_json).map_err(err_js)?;
- let exec = WasmSqlExecutor::new();
- let out =
- radroots_replica_db::nostr_profile::find_one(&exec, &opts).map_err(|e| err_js(e.err))?;
- value_to_js(out)
-}
-
-#[wasm_bindgen(js_name = replica_db_nostr_profile_find_many)]
-pub fn replica_db_nostr_profile_find_many(opts_json: &str) -> Result<JsValue, JsValue> {
- let opts: INostrProfileFindMany = parse_json(opts_json).map_err(err_js)?;
- let exec = WasmSqlExecutor::new();
- let out =
- radroots_replica_db::nostr_profile::find_many(&exec, &opts).map_err(|e| err_js(e.err))?;
- value_to_js(out)
-}
-
-#[wasm_bindgen(js_name = replica_db_nostr_profile_update)]
-pub fn replica_db_nostr_profile_update(opts_json: &str) -> Result<JsValue, JsValue> {
- let opts: INostrProfileUpdate = parse_json(opts_json).map_err(err_js)?;
- let exec = WasmSqlExecutor::new();
- let out =
- radroots_replica_db::nostr_profile::update(&exec, &opts).map_err(|e| err_js(e.err))?;
- value_to_js(out)
-}
-
-#[wasm_bindgen(js_name = replica_db_nostr_profile_delete)]
-pub fn replica_db_nostr_profile_delete(opts_json: &str) -> Result<JsValue, JsValue> {
- let opts: INostrProfileDelete = parse_json(opts_json).map_err(err_js)?;
- let exec = WasmSqlExecutor::new();
- let out =
- radroots_replica_db::nostr_profile::delete(&exec, &opts).map_err(|e| err_js(e.err))?;
- value_to_js(out)
-}
-
-#[wasm_bindgen(js_name = replica_db_nostr_event_head_create)]
-pub fn replica_db_nostr_event_head_create(opts_json: &str) -> Result<JsValue, JsValue> {
- let opts: INostrEventHeadCreate = parse_json(opts_json).map_err(err_js)?;
- let exec = WasmSqlExecutor::new();
- let out =
- radroots_replica_db::nostr_event_head::create(&exec, &opts).map_err(|e| err_js(e.err))?;
- value_to_js(out)
-}
-
-#[wasm_bindgen(js_name = replica_db_nostr_event_head_find_one)]
-pub fn replica_db_nostr_event_head_find_one(opts_json: &str) -> Result<JsValue, JsValue> {
- let opts: INostrEventHeadFindOne = parse_json(opts_json).map_err(err_js)?;
- let exec = WasmSqlExecutor::new();
- let out =
- radroots_replica_db::nostr_event_head::find_one(&exec, &opts).map_err(|e| err_js(e.err))?;
- value_to_js(out)
-}
-
-#[wasm_bindgen(js_name = replica_db_nostr_event_head_find_many)]
-pub fn replica_db_nostr_event_head_find_many(opts_json: &str) -> Result<JsValue, JsValue> {
- let opts: INostrEventHeadFindMany = parse_json(opts_json).map_err(err_js)?;
- let exec = WasmSqlExecutor::new();
- let out = radroots_replica_db::nostr_event_head::find_many(&exec, &opts)
- .map_err(|e| err_js(e.err))?;
- value_to_js(out)
-}
-
-#[wasm_bindgen(js_name = replica_db_nostr_event_head_update)]
-pub fn replica_db_nostr_event_head_update(opts_json: &str) -> Result<JsValue, JsValue> {
- let opts: INostrEventHeadUpdate = parse_json(opts_json).map_err(err_js)?;
- let exec = WasmSqlExecutor::new();
- let out =
- radroots_replica_db::nostr_event_head::update(&exec, &opts).map_err(|e| err_js(e.err))?;
- value_to_js(out)
-}
-
-#[wasm_bindgen(js_name = replica_db_nostr_event_head_delete)]
-pub fn replica_db_nostr_event_head_delete(opts_json: &str) -> Result<JsValue, JsValue> {
- let opts: INostrEventHeadDelete = parse_json(opts_json).map_err(err_js)?;
- let exec = WasmSqlExecutor::new();
- let out =
- radroots_replica_db::nostr_event_head::delete(&exec, &opts).map_err(|e| err_js(e.err))?;
- value_to_js(out)
-}
-
-#[wasm_bindgen(js_name = replica_db_nostr_relay_create)]
-pub fn replica_db_nostr_relay_create(opts_json: &str) -> Result<JsValue, JsValue> {
- let opts: INostrRelayCreate = parse_json(opts_json).map_err(err_js)?;
- let exec = WasmSqlExecutor::new();
- let out = radroots_replica_db::nostr_relay::create(&exec, &opts).map_err(|e| err_js(e.err))?;
- value_to_js(out)
-}
-
-#[wasm_bindgen(js_name = replica_db_nostr_relay_find_one)]
-pub fn replica_db_nostr_relay_find_one(opts_json: &str) -> Result<JsValue, JsValue> {
- let opts: INostrRelayFindOne = parse_json(opts_json).map_err(err_js)?;
- let exec = WasmSqlExecutor::new();
- let out =
- radroots_replica_db::nostr_relay::find_one(&exec, &opts).map_err(|e| err_js(e.err))?;
- value_to_js(out)
-}
-
-#[wasm_bindgen(js_name = replica_db_nostr_relay_find_many)]
-pub fn replica_db_nostr_relay_find_many(opts_json: &str) -> Result<JsValue, JsValue> {
- let opts: INostrRelayFindMany = parse_json(opts_json).map_err(err_js)?;
- let exec = WasmSqlExecutor::new();
- let out =
- radroots_replica_db::nostr_relay::find_many(&exec, &opts).map_err(|e| err_js(e.err))?;
- value_to_js(out)
-}
-
-#[wasm_bindgen(js_name = replica_db_nostr_relay_update)]
-pub fn replica_db_nostr_relay_update(opts_json: &str) -> Result<JsValue, JsValue> {
- let opts: INostrRelayUpdate = parse_json(opts_json).map_err(err_js)?;
- let exec = WasmSqlExecutor::new();
- let out = radroots_replica_db::nostr_relay::update(&exec, &opts).map_err(|e| err_js(e.err))?;
- value_to_js(out)
-}
-
-#[wasm_bindgen(js_name = replica_db_nostr_relay_delete)]
-pub fn replica_db_nostr_relay_delete(opts_json: &str) -> Result<JsValue, JsValue> {
- let opts: INostrRelayDelete = parse_json(opts_json).map_err(err_js)?;
- let exec = WasmSqlExecutor::new();
- let out = radroots_replica_db::nostr_relay::delete(&exec, &opts).map_err(|e| err_js(e.err))?;
- value_to_js(out)
-}
-
-#[wasm_bindgen(js_name = replica_db_trade_product_create)]
-pub fn replica_db_trade_product_create(opts_json: &str) -> Result<JsValue, JsValue> {
- let opts: ITradeProductCreate = parse_json(opts_json).map_err(err_js)?;
- let exec = WasmSqlExecutor::new();
- let out =
- radroots_replica_db::trade_product::create(&exec, &opts).map_err(|e| err_js(e.err))?;
- value_to_js(out)
-}
-
-#[wasm_bindgen(js_name = replica_db_trade_product_find_one)]
-pub fn replica_db_trade_product_find_one(opts_json: &str) -> Result<JsValue, JsValue> {
- let opts: ITradeProductFindOne = parse_json(opts_json).map_err(err_js)?;
- let exec = WasmSqlExecutor::new();
- let out =
- radroots_replica_db::trade_product::find_one(&exec, &opts).map_err(|e| err_js(e.err))?;
- value_to_js(out)
-}
-
-#[wasm_bindgen(js_name = replica_db_trade_product_find_many)]
-pub fn replica_db_trade_product_find_many(opts_json: &str) -> Result<JsValue, JsValue> {
- let opts: ITradeProductFindMany = parse_json(opts_json).map_err(err_js)?;
- let exec = WasmSqlExecutor::new();
- let out =
- radroots_replica_db::trade_product::find_many(&exec, &opts).map_err(|e| err_js(e.err))?;
- value_to_js(out)
-}
-
-#[wasm_bindgen(js_name = replica_db_trade_product_update)]
-pub fn replica_db_trade_product_update(opts_json: &str) -> Result<JsValue, JsValue> {
- let opts: ITradeProductUpdate = parse_json(opts_json).map_err(err_js)?;
- let exec = WasmSqlExecutor::new();
- let out =
- radroots_replica_db::trade_product::update(&exec, &opts).map_err(|e| err_js(e.err))?;
- value_to_js(out)
-}
-
-#[wasm_bindgen(js_name = replica_db_trade_product_delete)]
-pub fn replica_db_trade_product_delete(opts_json: &str) -> Result<JsValue, JsValue> {
- let opts: ITradeProductDelete = parse_json(opts_json).map_err(err_js)?;
- let exec = WasmSqlExecutor::new();
- let out =
- radroots_replica_db::trade_product::delete(&exec, &opts).map_err(|e| err_js(e.err))?;
- value_to_js(out)
-}
-
-#[wasm_bindgen(js_name = replica_db_nostr_profile_relay_set)]
-pub fn replica_db_nostr_profile_relay_set(opts_json: &str) -> Result<JsValue, JsValue> {
- let opts: INostrProfileRelayRelation = parse_json(opts_json).map_err(err_js)?;
- let exec = WasmSqlExecutor::new();
- let out =
- radroots_replica_db::nostr_profile_relay::set(&exec, &opts).map_err(|e| err_js(e.err))?;
- value_to_js(out)
-}
-
-#[wasm_bindgen(js_name = replica_db_nostr_profile_relay_unset)]
-pub fn replica_db_nostr_profile_relay_unset(opts_json: &str) -> Result<JsValue, JsValue> {
- let opts: INostrProfileRelayRelation = parse_json(opts_json).map_err(err_js)?;
- let exec = WasmSqlExecutor::new();
- let out =
- radroots_replica_db::nostr_profile_relay::unset(&exec, &opts).map_err(|e| err_js(e.err))?;
- value_to_js(out)
-}
-
-#[wasm_bindgen(js_name = replica_db_trade_product_location_set)]
-pub fn replica_db_trade_product_location_set(opts_json: &str) -> Result<JsValue, JsValue> {
- let opts: ITradeProductLocationRelation = parse_json(opts_json).map_err(err_js)?;
- let exec = WasmSqlExecutor::new();
- let out = radroots_replica_db::trade_product_location::set(&exec, &opts)
- .map_err(|e| err_js(e.err))?;
- value_to_js(out)
-}
-
-#[wasm_bindgen(js_name = replica_db_trade_product_location_unset)]
-pub fn replica_db_trade_product_location_unset(opts_json: &str) -> Result<JsValue, JsValue> {
- let opts: ITradeProductLocationRelation = parse_json(opts_json).map_err(err_js)?;
- let exec = WasmSqlExecutor::new();
- let out = radroots_replica_db::trade_product_location::unset(&exec, &opts)
- .map_err(|e| err_js(e.err))?;
- value_to_js(out)
-}
-
-#[wasm_bindgen(js_name = replica_db_trade_product_media_set)]
-pub fn replica_db_trade_product_media_set(opts_json: &str) -> Result<JsValue, JsValue> {
- let opts: ITradeProductMediaRelation = parse_json(opts_json).map_err(err_js)?;
- let exec = WasmSqlExecutor::new();
- let out =
- radroots_replica_db::trade_product_media::set(&exec, &opts).map_err(|e| err_js(e.err))?;
- value_to_js(out)
-}
-
-#[wasm_bindgen(js_name = replica_db_trade_product_media_unset)]
-pub fn replica_db_trade_product_media_unset(opts_json: &str) -> Result<JsValue, JsValue> {
- let opts: ITradeProductMediaRelation = parse_json(opts_json).map_err(err_js)?;
- let exec = WasmSqlExecutor::new();
- let out =
- radroots_replica_db::trade_product_media::unset(&exec, &opts).map_err(|e| err_js(e.err))?;
- value_to_js(out)
-}
diff --git a/crates/replica_sync_wasm/Cargo.toml b/crates/replica_sync_wasm/Cargo.toml
@@ -1,35 +0,0 @@
-[package]
-name = "radroots_replica_sync_wasm"
-publish = false
-version = "0.1.0-alpha.2"
-edition.workspace = true
-authors = ["Tyson Lupul <tyson@radroots.org>"]
-rust-version.workspace = true
-license.workspace = true
-description = "WebAssembly bindings for radroots_replica_sync"
-repository.workspace = true
-homepage.workspace = true
-documentation = "https://docs.rs/radroots_replica_sync_wasm"
-readme = "README"
-
-[lib]
-crate-type = ["cdylib", "rlib"]
-
-[dependencies]
-base64 = { workspace = true }
-radroots_events = { workspace = true, default-features = false, features = [
- "serde",
-] }
-radroots_sql_core = { workspace = true, features = ["bridge"] }
-radroots_sql_wasm_core = { workspace = true, default-features = false, features = [
- "bridge",
-] }
-radroots_replica_sync = { workspace = true, features = ["std"] }
-serde = { workspace = true, features = ["derive"] }
-serde_json = { workspace = true }
-serde-wasm-bindgen = { workspace = true }
-uuid = { workspace = true, features = ["js"] }
-wasm-bindgen = { workspace = true }
-
-[lints.rust]
-unexpected_cfgs = { level = "warn", check-cfg = ['cfg(coverage_nightly)'] }
diff --git a/crates/replica_sync_wasm/README b/crates/replica_sync_wasm/README
@@ -1,25 +0,0 @@
-# radroots_replica_sync_wasm
-
-This is the README for `radroots_replica_sync_wasm`, which provides WebAssembly
-bindings for `radroots_replica_sync` in the `radroots` core libraries.
-
-## Overview
-
- * wasm32 entry points for full sync and single-event ingest operations;
- * integration with `WasmSqlExecutor` from `radroots_sql_core` for database
- access;
- * UUIDv7 id generation and JSON and base64 boundary handling for JavaScript
- callers;
- * a small target-specific wrapper around the Rust sync crate rather than a
- separate sync engine.
-
-## Copyright
-
-Except as otherwise noted, all files in the `radroots_replica_sync_wasm`
-distribution are
-
- Copyright (c) 2020-2026 Tyson Lupul
-
-For information on usage and redistribution, and for a DISCLAIMER OF ALL
-WARRANTIES, see LICENSE included in the `radroots_replica_sync_wasm`
-distribution.
diff --git a/crates/replica_sync_wasm/pkg/package.json b/crates/replica_sync_wasm/pkg/package.json
@@ -1,20 +0,0 @@
-{
- "name": "@radroots/radroots_replica-sync-wasm",
- "description": "WebAssembly bindings for radroots_replica_sync",
- "version": "0.1.0",
- "private": true,
- "type": "module",
- "files": [
- "dist"
- ],
- "main": "./dist/radroots_replica_sync_wasm.js",
- "types": "./dist/radroots_replica_sync_wasm.d.ts",
- "exports": {
- ".": {
- "types": "./dist/radroots_replica_sync_wasm.d.ts",
- "import": "./dist/radroots_replica_sync_wasm.js",
- "default": "./dist/radroots_replica_sync_wasm.js"
- }
- },
- "sideEffects": false
-}
diff --git a/crates/replica_sync_wasm/src/lib.rs b/crates/replica_sync_wasm/src/lib.rs
@@ -1,124 +0,0 @@
-#![cfg(any(target_arch = "wasm32", coverage_nightly))]
-#![forbid(unsafe_code)]
-
-#[cfg(target_arch = "wasm32")]
-use base64::Engine;
-#[cfg(target_arch = "wasm32")]
-use base64::engine::general_purpose::URL_SAFE_NO_PAD;
-#[cfg(target_arch = "wasm32")]
-use radroots_events::RadrootsNostrEvent;
-#[cfg(target_arch = "wasm32")]
-use radroots_replica_sync::{
- RadrootsReplicaIdFactory, RadrootsReplicaIngestOutcome, RadrootsReplicaSyncRequest,
- radroots_replica_ingest_event_with_factory, radroots_replica_sync_all,
-};
-#[cfg(target_arch = "wasm32")]
-use radroots_sql_core::WasmSqlExecutor;
-#[cfg(target_arch = "wasm32")]
-use serde::Deserialize;
-#[cfg(target_arch = "wasm32")]
-use uuid::Uuid;
-#[cfg(target_arch = "wasm32")]
-use wasm_bindgen::prelude::*;
-
-#[cfg(target_arch = "wasm32")]
-fn err_js<E: ToString>(err: E) -> JsValue {
- JsValue::from_str(&err.to_string())
-}
-
-#[cfg(target_arch = "wasm32")]
-struct WasmIdFactory;
-
-#[cfg(target_arch = "wasm32")]
-impl RadrootsReplicaIdFactory for WasmIdFactory {
- fn new_d_tag(&self) -> String {
- let uuid = Uuid::now_v7();
- URL_SAFE_NO_PAD.encode(uuid.as_bytes())
- }
-}
-
-#[cfg(target_arch = "wasm32")]
-#[derive(Deserialize)]
-struct NostrEventEnvelope {
- id: String,
- #[serde(default)]
- author: Option<String>,
- #[serde(default)]
- pubkey: Option<String>,
- created_at: u32,
- kind: u32,
- tags: Vec<Vec<String>>,
- content: String,
- sig: String,
-}
-
-#[cfg(target_arch = "wasm32")]
-fn parse_request(request_json: &str) -> Result<RadrootsReplicaSyncRequest, JsValue> {
- serde_json::from_str(request_json).map_err(err_js)
-}
-
-#[cfg(target_arch = "wasm32")]
-fn parse_event(event_json: &str) -> Result<RadrootsNostrEvent, JsValue> {
- let envelope: NostrEventEnvelope = serde_json::from_str(event_json).map_err(err_js)?;
- let author = match (envelope.author, envelope.pubkey) {
- (Some(author), Some(pubkey)) if author != pubkey => {
- return Err(JsValue::from_str("author/pubkey mismatch"));
- }
- (Some(author), _) => author,
- (None, Some(pubkey)) => pubkey,
- (None, None) => return Err(JsValue::from_str("missing author/pubkey")),
- };
- Ok(RadrootsNostrEvent {
- id: envelope.id,
- author,
- created_at: envelope.created_at,
- kind: envelope.kind,
- tags: envelope.tags,
- content: envelope.content,
- sig: envelope.sig,
- })
-}
-
-#[cfg(target_arch = "wasm32")]
-#[wasm_bindgen(js_name = replica_sync_sync_all)]
-pub fn replica_sync_sync_all(request_json: &str) -> Result<JsValue, JsValue> {
- let request = parse_request(request_json)?;
- let exec = WasmSqlExecutor::new();
- let bundle = radroots_replica_sync_all(&exec, &request).map_err(err_js)?;
- serde_wasm_bindgen::to_value(&bundle).map_err(err_js)
-}
-
-#[cfg(target_arch = "wasm32")]
-#[wasm_bindgen(js_name = replica_sync_ingest_event)]
-pub fn replica_sync_ingest_event(event_json: &str) -> Result<JsValue, JsValue> {
- let event = parse_event(event_json)?;
- let exec = WasmSqlExecutor::new();
- let factory = WasmIdFactory;
- let outcome =
- radroots_replica_ingest_event_with_factory(&exec, &event, &factory).map_err(err_js)?;
- let value = match outcome {
- RadrootsReplicaIngestOutcome::Applied => "applied",
- RadrootsReplicaIngestOutcome::Skipped => "skipped",
- };
- Ok(JsValue::from_str(value))
-}
-
-#[cfg(coverage_nightly)]
-pub fn coverage_branch_probe(input: bool) -> &'static str {
- if input {
- "replica-sync-wasm"
- } else {
- "replica-sync-wasm"
- }
-}
-
-#[cfg(all(test, coverage_nightly))]
-mod tests {
- use super::coverage_branch_probe;
-
- #[test]
- fn coverage_branch_probe_hits_both_paths() {
- assert_eq!(coverage_branch_probe(true), "replica-sync-wasm");
- assert_eq!(coverage_branch_probe(false), "replica-sync-wasm");
- }
-}
diff --git a/crates/sdk/Cargo.toml b/crates/sdk/Cargo.toml
@@ -1,78 +0,0 @@
-[package]
-name = "radroots_sdk"
-publish = ["crates-io"]
-version.workspace = true
-edition.workspace = true
-authors = ["Tyson Lupul <tyson@radroots.org>"]
-rust-version.workspace = true
-license.workspace = true
-description = "Curated Radroots SDK for profile, farm, listing, and trade event workflows"
-repository.workspace = true
-homepage.workspace = true
-documentation = "https://docs.rs/radroots_sdk"
-readme = "README"
-
-[features]
-default = ["std", "serde", "serde_json", "identity-models"]
-std = ["radroots_events/std", "radroots_events_codec/std", "radroots_trade/std"]
-serde = ["dep:serde", "radroots_events/serde", "radroots_trade/serde"]
-serde_json = [
- "dep:serde_json",
- "serde",
- "nostr",
- "radroots_events_codec/serde_json",
- "radroots_trade/serde_json",
-]
-nostr = ["radroots_events_codec/nostr"]
-identity-models = [
- "dep:radroots_identity",
- "radroots_identity/profile",
- "radroots_identity/std",
-]
-identity-storage = ["identity-models", "std", "radroots_identity/std"]
-signing = ["dep:radroots_nostr", "nostr"]
-relay-client = ["signing", "std", "serde_json", "radroots_nostr/client"]
-radrootsd-client = ["std", "serde_json", "dep:reqwest"]
-signer-adapters = [
- "identity-models",
- "signing",
- "std",
- "dep:radroots_nostr_connect",
- "dep:radroots_nostr_signer",
-]
-
-[dependencies]
-radroots_events = { workspace = true, default-features = false }
-radroots_events_codec = { workspace = true, default-features = false }
-radroots_trade = { workspace = true, default-features = false }
-radroots_identity = { workspace = true, optional = true, default-features = false }
-radroots_nostr = { workspace = true, optional = true, default-features = false }
-radroots_nostr_connect = { workspace = true, optional = true }
-radroots_nostr_signer = { workspace = true, optional = true, default-features = false }
-reqwest = { workspace = true, optional = true, default-features = false, features = [
- "json",
- "rustls-tls",
-] }
-serde = { workspace = true, optional = true, default-features = false, features = [
- "derive",
- "alloc",
-] }
-serde_json = { workspace = true, optional = true, default-features = false, features = [
- "alloc",
-] }
-
-[dev-dependencies]
-futures = { workspace = true }
-nostr = { workspace = true }
-radroots_core = { workspace = true, default-features = false, features = [
- "std",
-] }
-radroots_replica_db = { workspace = true, default-features = false, features = [
- "native",
-] }
-radroots_replica_db_schema = { workspace = true }
-radroots_replica_sync = { workspace = true, features = ["std"] }
-radroots_sql_core = { workspace = true, features = ["native"] }
-tempfile = { workspace = true }
-tokio = { workspace = true, features = ["macros", "rt-multi-thread"] }
-tokio-tungstenite = "0.26.2"
diff --git a/crates/sdk/README b/crates/sdk/README
@@ -1,23 +0,0 @@
-# radroots_sdk
-
-Curated Rad Roots Rust SDK for the public marketplace event contract.
-
-This crate provides the recommended Rust entrypoint for building, parsing, and
-validating Rad Roots profile, farm, listing, and trade events. It is a thin
-facade over the underlying `rr-rs` substrate crates and does not duplicate the
-core event or trade implementations.
-
-The deterministic event contract lives at the crate root:
-
-- `profile`
-- `farm`
-- `listing`
-- `trade`
-
-Optional advanced substrate is explicitly feature-scoped:
-
-- `identity-models`: identity data types without local storage coupling
-- `identity-storage`: encrypted identity-file helpers
-- `signing`: Nostr builder and local signing adapters
-- `relay-client`: relay client and publish adapters
-- `signer-adapters`: NIP-46 and signer-session primitives
diff --git a/crates/sdk/src/adapters/mod.rs b/crates/sdk/src/adapters/mod.rs
@@ -1,8 +0,0 @@
-#[cfg(feature = "radrootsd-client")]
-pub mod radrootsd;
-#[cfg(feature = "relay-client")]
-pub mod relay;
-#[cfg(feature = "signer-adapters")]
-pub mod signer;
-#[cfg(feature = "signing")]
-pub mod signing;
diff --git a/crates/sdk/src/adapters/radrootsd.rs b/crates/sdk/src/adapters/radrootsd.rs
@@ -1,835 +0,0 @@
-use core::fmt;
-use core::time::Duration;
-
-use crate::config::RadrootsdAuth;
-use crate::farm::RadrootsFarm;
-use crate::listing;
-use crate::listing::RadrootsListing;
-use crate::order;
-use crate::profile::{RadrootsProfile, RadrootsProfileType};
-use crate::{RadrootsNostrEvent, RadrootsNostrEventPtr};
-use radroots_events::kinds::KIND_LISTING;
-use reqwest::header::{AUTHORIZATION, CONTENT_TYPE, HeaderMap, HeaderValue};
-use serde::{Deserialize, Serialize, de::DeserializeOwned};
-use serde_json::{Value, json};
-
-#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
-pub struct SdkRadrootsdSignerAuthority {
- pub provider_runtime_id: String,
- pub account_identity_id: String,
- #[serde(default, skip_serializing_if = "Option::is_none")]
- pub provider_signer_session_id: Option<String>,
-}
-
-impl fmt::Debug for SdkRadrootsdSignerAuthority {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- let mut debug = f.debug_struct("SdkRadrootsdSignerAuthority");
- debug.field("provider_runtime_id", &self.provider_runtime_id);
- debug.field("account_identity_id", &self.account_identity_id);
- debug.field(
- "provider_signer_session_id",
- &self
- .provider_signer_session_id
- .as_ref()
- .map(|_| "<redacted>"),
- );
- debug.finish()
- }
-}
-
-#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
-pub enum SdkRadrootsdSignerSessionMode {
- #[serde(alias = "bunker")]
- Bunker,
- #[serde(alias = "nostrconnect")]
- Nostrconnect,
-}
-
-#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
-#[serde(rename_all = "snake_case")]
-pub enum SdkRadrootsdSignerSessionRole {
- InboundLocalSigner,
- OutboundRemoteSigner,
-}
-
-#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
-#[serde(rename_all = "snake_case")]
-pub enum SdkRadrootsdBridgeDeliveryPolicy {
- Any,
- Quorum,
- All,
-}
-
-#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
-#[serde(rename_all = "snake_case")]
-pub enum SdkRadrootsdBridgeJobStatus {
- Accepted,
- Published,
- Failed,
-}
-
-#[derive(Clone, PartialEq, Eq, Serialize)]
-pub struct SdkRadrootsdSignerSessionConnectRequest {
- pub url: String,
- #[serde(default, skip_serializing_if = "Option::is_none")]
- pub client_secret_key: Option<String>,
- #[serde(default, skip_serializing_if = "Option::is_none")]
- pub signer_authority: Option<SdkRadrootsdSignerAuthority>,
-}
-
-impl SdkRadrootsdSignerSessionConnectRequest {
- pub fn bunker(url: impl Into<String>) -> Self {
- Self {
- url: url.into(),
- client_secret_key: None,
- signer_authority: None,
- }
- }
-
- pub fn nostrconnect(url: impl Into<String>, client_secret_key: impl Into<String>) -> Self {
- Self {
- url: url.into(),
- client_secret_key: Some(client_secret_key.into()),
- signer_authority: None,
- }
- }
-
- pub fn with_signer_authority(mut self, signer_authority: SdkRadrootsdSignerAuthority) -> Self {
- self.signer_authority = Some(signer_authority);
- self
- }
-}
-
-impl fmt::Debug for SdkRadrootsdSignerSessionConnectRequest {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- let mut debug = f.debug_struct("SdkRadrootsdSignerSessionConnectRequest");
- debug.field("url", &self.url);
- debug.field(
- "client_secret_key",
- &self.client_secret_key.as_ref().map(|_| "<redacted>"),
- );
- debug.field("signer_authority", &self.signer_authority);
- debug.finish()
- }
-}
-
-#[derive(Clone, Serialize)]
-pub struct SdkRadrootsdProfilePublishRequest {
- pub profile: RadrootsProfile,
- #[serde(default, skip_serializing_if = "Option::is_none")]
- pub profile_type: Option<RadrootsProfileType>,
- pub signer_session_id: String,
- #[serde(default, skip_serializing_if = "Option::is_none")]
- pub signer_authority: Option<SdkRadrootsdSignerAuthority>,
- #[serde(default, skip_serializing_if = "Option::is_none")]
- pub idempotency_key: Option<String>,
-}
-
-impl fmt::Debug for SdkRadrootsdProfilePublishRequest {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- let mut debug = f.debug_struct("SdkRadrootsdProfilePublishRequest");
- debug.field("profile", &self.profile);
- debug.field("profile_type", &self.profile_type);
- debug.field("signer_session_id", &"<redacted>");
- debug.field("signer_authority", &self.signer_authority);
- debug.field("idempotency_key", &self.idempotency_key);
- debug.finish()
- }
-}
-
-#[derive(Clone, Serialize)]
-pub struct SdkRadrootsdFarmPublishRequest {
- pub farm: RadrootsFarm,
- #[serde(default, skip_serializing_if = "Option::is_none")]
- pub kind: Option<u32>,
- pub signer_session_id: String,
- #[serde(default, skip_serializing_if = "Option::is_none")]
- pub signer_authority: Option<SdkRadrootsdSignerAuthority>,
- #[serde(default, skip_serializing_if = "Option::is_none")]
- pub idempotency_key: Option<String>,
-}
-
-impl fmt::Debug for SdkRadrootsdFarmPublishRequest {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- let mut debug = f.debug_struct("SdkRadrootsdFarmPublishRequest");
- debug.field("farm", &self.farm);
- debug.field("kind", &self.kind);
- debug.field("signer_session_id", &"<redacted>");
- debug.field("signer_authority", &self.signer_authority);
- debug.field("idempotency_key", &self.idempotency_key);
- debug.finish()
- }
-}
-
-#[derive(Clone, Serialize)]
-pub struct SdkRadrootsdListingPublishRequest {
- pub listing: RadrootsListing,
- #[serde(default, skip_serializing_if = "Option::is_none")]
- pub kind: Option<u32>,
- pub signer_session_id: String,
- #[serde(default, skip_serializing_if = "Option::is_none")]
- pub signer_authority: Option<SdkRadrootsdSignerAuthority>,
- #[serde(default, skip_serializing_if = "Option::is_none")]
- pub idempotency_key: Option<String>,
-}
-
-impl fmt::Debug for SdkRadrootsdListingPublishRequest {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- let mut debug = f.debug_struct("SdkRadrootsdListingPublishRequest");
- debug.field("listing", &self.listing);
- debug.field("kind", &self.kind);
- debug.field("signer_session_id", &"<redacted>");
- debug.field("signer_authority", &self.signer_authority);
- debug.field("idempotency_key", &self.idempotency_key);
- debug.finish()
- }
-}
-
-#[derive(Clone, PartialEq, Eq, Serialize)]
-pub(crate) struct SdkRadrootsdOrderRequestPublishRequest {
- pub order: order::RadrootsOrderRequest,
- pub listing_event: RadrootsNostrEventPtr,
- pub signer_session_id: String,
- #[serde(default, skip_serializing_if = "Option::is_none")]
- pub signer_authority: Option<SdkRadrootsdSignerAuthority>,
- #[serde(default, skip_serializing_if = "Option::is_none")]
- pub idempotency_key: Option<String>,
-}
-
-impl fmt::Debug for SdkRadrootsdOrderRequestPublishRequest {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- let mut debug = f.debug_struct("SdkRadrootsdOrderRequestPublishRequest");
- debug.field("order", &self.order);
- debug.field("listing_event", &self.listing_event);
- debug.field("signer_session_id", &"<redacted>");
- debug.field("signer_authority", &self.signer_authority);
- debug.field("idempotency_key", &self.idempotency_key);
- debug.finish()
- }
-}
-
-impl SdkRadrootsdListingPublishRequest {
- pub fn from_event(
- event: &RadrootsNostrEvent,
- signer_session_id: impl Into<String>,
- signer_authority: Option<SdkRadrootsdSignerAuthority>,
- idempotency_key: Option<String>,
- ) -> Result<Self, listing::RadrootsListingParseError> {
- if event.kind != KIND_LISTING {
- return Err(listing::RadrootsListingParseError::InvalidKind(event.kind));
- }
- Ok(Self {
- listing: listing::parse_event(event)?,
- kind: Some(event.kind),
- signer_session_id: signer_session_id.into(),
- signer_authority,
- idempotency_key,
- })
- }
-}
-
-#[derive(Clone, Debug, PartialEq, Eq, Deserialize)]
-pub(crate) struct SdkRadrootsdSignerSessionConnectResponse {
- pub session_id: String,
- pub mode: SdkRadrootsdSignerSessionMode,
- pub remote_signer_pubkey: String,
- pub client_pubkey: String,
- pub relays: Vec<String>,
-}
-
-#[derive(Clone, PartialEq, Eq, Deserialize)]
-pub(crate) struct SdkRadrootsdSignerSessionViewResponse {
- pub session_id: String,
- pub role: SdkRadrootsdSignerSessionRole,
- pub client_pubkey: String,
- pub signer_pubkey: String,
- #[serde(default)]
- pub user_pubkey: Option<String>,
- pub relays: Vec<String>,
- pub permissions: Vec<String>,
- #[serde(default)]
- pub name: Option<String>,
- #[serde(default)]
- pub url: Option<String>,
- #[serde(default)]
- pub image: Option<String>,
- pub auth_required: bool,
- pub authorized: bool,
- #[serde(default)]
- pub auth_url: Option<String>,
- #[serde(default)]
- pub expires_in_secs: Option<u64>,
- #[serde(default)]
- pub signer_authority: Option<SdkRadrootsdSignerAuthority>,
-}
-
-impl fmt::Debug for SdkRadrootsdSignerSessionViewResponse {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- let mut debug = f.debug_struct("SdkRadrootsdSignerSessionViewResponse");
- debug.field("session_id", &"<redacted>");
- debug.field("role", &self.role);
- debug.field("client_pubkey", &self.client_pubkey);
- debug.field("signer_pubkey", &self.signer_pubkey);
- debug.field("user_pubkey", &self.user_pubkey);
- debug.field("relays", &self.relays);
- debug.field("permissions", &self.permissions);
- debug.field("name", &self.name);
- debug.field("url", &self.url);
- debug.field("image", &self.image);
- debug.field("auth_required", &self.auth_required);
- debug.field("authorized", &self.authorized);
- debug.field("auth_url", &self.auth_url);
- debug.field("expires_in_secs", &self.expires_in_secs);
- debug.field("signer_authority", &self.signer_authority);
- debug.finish()
- }
-}
-
-#[derive(Clone, Debug, PartialEq, Eq, Deserialize)]
-pub(crate) struct SdkRadrootsdSignerSessionAuthorizeResponse {
- pub authorized: bool,
- pub replayed: bool,
-}
-
-#[derive(Clone, Debug, PartialEq, Eq, Deserialize)]
-pub(crate) struct SdkRadrootsdSignerSessionPublicKeyResponse {
- pub pubkey: String,
-}
-
-#[derive(Clone, Debug, PartialEq, Eq, Deserialize)]
-pub(crate) struct SdkRadrootsdSignerSessionRequireAuthResponse {
- pub required: bool,
-}
-
-#[derive(Clone, Debug, PartialEq, Eq, Deserialize)]
-pub(crate) struct SdkRadrootsdSignerSessionCloseResponse {
- pub closed: bool,
-}
-
-#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
-pub struct SdkRadrootsdBridgePublishResponse {
- pub deduplicated: bool,
- pub job: SdkRadrootsdBridgeJob,
-}
-
-#[derive(Clone, Debug, PartialEq, Eq, Deserialize)]
-pub struct SdkRadrootsdBridgeStatusResponse {
- pub enabled: bool,
- pub ready: bool,
- pub auth_mode: String,
- pub signer_mode: String,
- pub default_signer_mode: String,
- pub supported_signer_modes: Vec<String>,
- pub available_nip46_signer_sessions: usize,
- pub relay_count: usize,
- pub delivery_policy: SdkRadrootsdBridgeDeliveryPolicy,
- #[serde(default)]
- pub delivery_quorum: Option<usize>,
- pub publish_max_attempts: usize,
- pub publish_initial_backoff_millis: u64,
- pub publish_max_backoff_millis: u64,
- pub job_status_retention: usize,
- pub retained_jobs: usize,
- pub retained_idempotency_keys: usize,
- pub accepted_jobs: usize,
- pub published_jobs: usize,
- pub failed_jobs: usize,
- pub recovered_failed_jobs: usize,
- pub methods: Vec<String>,
-}
-
-#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
-pub struct SdkRadrootsdBridgeRelayPublishResult {
- pub relay_url: String,
- pub acknowledged: bool,
- #[serde(default)]
- pub detail: Option<String>,
-}
-
-#[derive(Clone, PartialEq, Eq, Deserialize)]
-pub struct SdkRadrootsdBridgeJob {
- pub job_id: String,
- pub command: String,
- pub status: String,
- pub terminal: bool,
- pub recovered_after_restart: bool,
- pub signer_mode: String,
- #[serde(default)]
- pub signer_session_id: Option<String>,
- pub event_kind: u32,
- #[serde(default)]
- pub event_id: Option<String>,
- #[serde(default)]
- pub event_addr: Option<String>,
- pub relay_count: usize,
- pub acknowledged_relay_count: usize,
-}
-
-impl fmt::Debug for SdkRadrootsdBridgeJob {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- let mut debug = f.debug_struct("SdkRadrootsdBridgeJob");
- debug.field("job_id", &self.job_id);
- debug.field("command", &self.command);
- debug.field("status", &self.status);
- debug.field("terminal", &self.terminal);
- debug.field("recovered_after_restart", &self.recovered_after_restart);
- debug.field("signer_mode", &"<redacted>");
- debug.field(
- "signer_session_id",
- &self.signer_session_id.as_ref().map(|_| "<redacted>"),
- );
- debug.field("event_kind", &self.event_kind);
- debug.field("event_id", &self.event_id);
- debug.field("event_addr", &self.event_addr);
- debug.field("relay_count", &self.relay_count);
- debug.field("acknowledged_relay_count", &self.acknowledged_relay_count);
- debug.finish()
- }
-}
-
-#[derive(Clone, PartialEq, Eq, Deserialize)]
-pub struct SdkRadrootsdBridgeJobView {
- pub job_id: String,
- pub command: String,
- #[serde(default)]
- pub idempotency_key: Option<String>,
- pub status: SdkRadrootsdBridgeJobStatus,
- pub terminal: bool,
- pub recovered_after_restart: bool,
- pub requested_at_unix: u64,
- #[serde(default)]
- pub completed_at_unix: Option<u64>,
- pub signer_mode: String,
- #[serde(default)]
- pub signer_session_id: Option<String>,
- pub event_kind: u32,
- #[serde(default)]
- pub event_id: Option<String>,
- #[serde(default)]
- pub event_addr: Option<String>,
- pub delivery_policy: SdkRadrootsdBridgeDeliveryPolicy,
- #[serde(default)]
- pub delivery_quorum: Option<usize>,
- pub relay_count: usize,
- pub acknowledged_relay_count: usize,
- pub required_acknowledged_relay_count: usize,
- pub attempt_count: usize,
- #[serde(default)]
- pub attempt_summaries: Vec<String>,
- #[serde(default)]
- pub relay_results: Vec<SdkRadrootsdBridgeRelayPublishResult>,
- pub relay_outcome_summary: String,
-}
-
-impl fmt::Debug for SdkRadrootsdBridgeJobView {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- let mut debug = f.debug_struct("SdkRadrootsdBridgeJobView");
- debug.field("job_id", &self.job_id);
- debug.field("command", &self.command);
- debug.field("idempotency_key", &self.idempotency_key);
- debug.field("status", &self.status);
- debug.field("terminal", &self.terminal);
- debug.field("recovered_after_restart", &self.recovered_after_restart);
- debug.field("requested_at_unix", &self.requested_at_unix);
- debug.field("completed_at_unix", &self.completed_at_unix);
- debug.field("signer_mode", &self.signer_mode.as_str());
- debug.field(
- "signer_session_id",
- &self.signer_session_id.as_ref().map(|_| "<redacted>"),
- );
- debug.field("event_kind", &self.event_kind);
- debug.field("event_id", &self.event_id);
- debug.field("event_addr", &self.event_addr);
- debug.field("delivery_policy", &self.delivery_policy);
- debug.field("delivery_quorum", &self.delivery_quorum);
- debug.field("relay_count", &self.relay_count);
- debug.field("acknowledged_relay_count", &self.acknowledged_relay_count);
- debug.field(
- "required_acknowledged_relay_count",
- &self.required_acknowledged_relay_count,
- );
- debug.field("attempt_count", &self.attempt_count);
- debug.field("attempt_summaries", &self.attempt_summaries);
- debug.field("relay_results", &self.relay_results);
- debug.field("relay_outcome_summary", &self.relay_outcome_summary);
- debug.finish()
- }
-}
-
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub enum RadrootsdError {
- InvalidAuthHeader(String),
- Http(String),
- JsonRpc(String),
- MalformedResponse(String),
-}
-
-impl core::fmt::Display for RadrootsdError {
- fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
- match self {
- Self::InvalidAuthHeader(value) => {
- write!(f, "invalid radrootsd bearer token header: {value}")
- }
- Self::Http(value) => write!(f, "{value}"),
- Self::JsonRpc(value) => write!(f, "{value}"),
- Self::MalformedResponse(value) => write!(f, "{value}"),
- }
- }
-}
-
-impl std::error::Error for RadrootsdError {}
-
-#[derive(Debug, Deserialize)]
-struct JsonRpcEnvelope<T> {
- result: Option<T>,
- error: Option<JsonRpcError>,
-}
-
-#[derive(Debug, Deserialize)]
-struct JsonRpcError {
- code: i64,
- message: String,
-}
-
-#[derive(Debug, Serialize)]
-struct SdkRadrootsdSignerSessionParams<'a> {
- session_id: &'a str,
-}
-
-#[derive(Debug, Serialize)]
-struct SdkRadrootsdSignerSessionRequireAuthParams<'a> {
- session_id: &'a str,
- auth_url: &'a str,
-}
-
-#[derive(Debug, Serialize)]
-struct SdkRadrootsdBridgeJobParams<'a> {
- job_id: &'a str,
-}
-
-pub async fn publish_listing(
- endpoint: &str,
- auth: &RadrootsdAuth,
- request: &SdkRadrootsdListingPublishRequest,
- timeout: Duration,
-) -> Result<SdkRadrootsdBridgePublishResponse, RadrootsdError> {
- jsonrpc_call(
- endpoint,
- auth,
- "radroots-sdk-listing-publish",
- "bridge.listing.publish",
- request,
- timeout,
- )
- .await
-}
-
-pub(crate) async fn publish_profile(
- endpoint: &str,
- auth: &RadrootsdAuth,
- request: &SdkRadrootsdProfilePublishRequest,
- timeout: Duration,
-) -> Result<SdkRadrootsdBridgePublishResponse, RadrootsdError> {
- jsonrpc_call(
- endpoint,
- auth,
- "radroots-sdk-profile-publish",
- "bridge.profile.publish",
- request,
- timeout,
- )
- .await
-}
-
-pub(crate) async fn publish_farm(
- endpoint: &str,
- auth: &RadrootsdAuth,
- request: &SdkRadrootsdFarmPublishRequest,
- timeout: Duration,
-) -> Result<SdkRadrootsdBridgePublishResponse, RadrootsdError> {
- jsonrpc_call(
- endpoint,
- auth,
- "radroots-sdk-farm-publish",
- "bridge.farm.publish",
- request,
- timeout,
- )
- .await
-}
-
-pub(crate) async fn publish_order_request(
- endpoint: &str,
- auth: &RadrootsdAuth,
- request: &SdkRadrootsdOrderRequestPublishRequest,
- timeout: Duration,
-) -> Result<SdkRadrootsdBridgePublishResponse, RadrootsdError> {
- jsonrpc_call(
- endpoint,
- auth,
- "radroots-sdk-order-request-publish",
- "bridge.order.request",
- request,
- timeout,
- )
- .await
-}
-
-pub(crate) async fn connect_signer_session(
- endpoint: &str,
- auth: &RadrootsdAuth,
- request: &SdkRadrootsdSignerSessionConnectRequest,
- timeout: Duration,
-) -> Result<SdkRadrootsdSignerSessionConnectResponse, RadrootsdError> {
- jsonrpc_call(
- endpoint,
- auth,
- "radroots-sdk-nip46-connect",
- "nip46.connect",
- request,
- timeout,
- )
- .await
-}
-
-pub(crate) async fn signer_session_status(
- endpoint: &str,
- auth: &RadrootsdAuth,
- session_id: &str,
- timeout: Duration,
-) -> Result<SdkRadrootsdSignerSessionViewResponse, RadrootsdError> {
- jsonrpc_call(
- endpoint,
- auth,
- "radroots-sdk-nip46-session-status",
- "nip46.session.status",
- &SdkRadrootsdSignerSessionParams { session_id },
- timeout,
- )
- .await
-}
-
-pub(crate) async fn list_signer_sessions(
- endpoint: &str,
- auth: &RadrootsdAuth,
- timeout: Duration,
-) -> Result<Vec<SdkRadrootsdSignerSessionViewResponse>, RadrootsdError> {
- jsonrpc_call(
- endpoint,
- auth,
- "radroots-sdk-nip46-session-list",
- "nip46.session.list",
- &json!({}),
- timeout,
- )
- .await
-}
-
-pub(crate) async fn authorize_signer_session(
- endpoint: &str,
- auth: &RadrootsdAuth,
- session_id: &str,
- timeout: Duration,
-) -> Result<SdkRadrootsdSignerSessionAuthorizeResponse, RadrootsdError> {
- jsonrpc_call(
- endpoint,
- auth,
- "radroots-sdk-nip46-session-authorize",
- "nip46.session.authorize",
- &SdkRadrootsdSignerSessionParams { session_id },
- timeout,
- )
- .await
-}
-
-pub(crate) async fn get_signer_session_public_key(
- endpoint: &str,
- auth: &RadrootsdAuth,
- session_id: &str,
- timeout: Duration,
-) -> Result<SdkRadrootsdSignerSessionPublicKeyResponse, RadrootsdError> {
- jsonrpc_call(
- endpoint,
- auth,
- "radroots-sdk-nip46-get-public-key",
- "nip46.get_public_key",
- &SdkRadrootsdSignerSessionParams { session_id },
- timeout,
- )
- .await
-}
-
-pub(crate) async fn require_signer_session_auth(
- endpoint: &str,
- auth: &RadrootsdAuth,
- session_id: &str,
- auth_url: &str,
- timeout: Duration,
-) -> Result<SdkRadrootsdSignerSessionRequireAuthResponse, RadrootsdError> {
- jsonrpc_call(
- endpoint,
- auth,
- "radroots-sdk-nip46-session-require-auth",
- "nip46.session.require_auth",
- &SdkRadrootsdSignerSessionRequireAuthParams {
- session_id,
- auth_url,
- },
- timeout,
- )
- .await
-}
-
-pub(crate) async fn close_signer_session(
- endpoint: &str,
- auth: &RadrootsdAuth,
- session_id: &str,
- timeout: Duration,
-) -> Result<SdkRadrootsdSignerSessionCloseResponse, RadrootsdError> {
- jsonrpc_call(
- endpoint,
- auth,
- "radroots-sdk-nip46-session-close",
- "nip46.session.close",
- &SdkRadrootsdSignerSessionParams { session_id },
- timeout,
- )
- .await
-}
-
-pub(crate) async fn bridge_status(
- endpoint: &str,
- auth: &RadrootsdAuth,
- timeout: Duration,
-) -> Result<SdkRadrootsdBridgeStatusResponse, RadrootsdError> {
- jsonrpc_call(
- endpoint,
- auth,
- "radroots-sdk-bridge-status",
- "bridge.status",
- &json!({}),
- timeout,
- )
- .await
-}
-
-pub(crate) async fn bridge_job_status(
- endpoint: &str,
- auth: &RadrootsdAuth,
- job_id: &str,
- timeout: Duration,
-) -> Result<SdkRadrootsdBridgeJobView, RadrootsdError> {
- jsonrpc_call(
- endpoint,
- auth,
- "radroots-sdk-bridge-job-status",
- "bridge.job.status",
- &SdkRadrootsdBridgeJobParams { job_id },
- timeout,
- )
- .await
-}
-
-pub(crate) async fn list_bridge_jobs(
- endpoint: &str,
- auth: &RadrootsdAuth,
- timeout: Duration,
-) -> Result<Vec<SdkRadrootsdBridgeJobView>, RadrootsdError> {
- jsonrpc_call(
- endpoint,
- auth,
- "radroots-sdk-bridge-job-list",
- "bridge.job.list",
- &json!({}),
- timeout,
- )
- .await
-}
-
-fn auth_headers(auth: &RadrootsdAuth) -> Result<HeaderMap, RadrootsdError> {
- let mut headers = HeaderMap::new();
- match auth {
- RadrootsdAuth::None => Ok(headers),
- RadrootsdAuth::BearerToken(token) => {
- let value = HeaderValue::from_str(format!("Bearer {token}").as_str())
- .map_err(|err| RadrootsdError::InvalidAuthHeader(err.to_string()))?;
- headers.insert(AUTHORIZATION, value);
- Ok(headers)
- }
- }
-}
-
-pub fn bridge_listing_publish_request_json(
- request: &SdkRadrootsdListingPublishRequest,
-) -> Result<Value, RadrootsdError> {
- serde_json::to_value(request).map_err(|err| {
- RadrootsdError::MalformedResponse(format!(
- "serialize radrootsd listing publish request: {err}"
- ))
- })
-}
-
-async fn jsonrpc_call<P, R>(
- endpoint: &str,
- auth: &RadrootsdAuth,
- request_id: &str,
- method: &str,
- params: &P,
- timeout: Duration,
-) -> Result<R, RadrootsdError>
-where
- P: Serialize + ?Sized,
- R: DeserializeOwned,
-{
- let client = reqwest::Client::builder()
- .timeout(timeout)
- .build()
- .map_err(|err| RadrootsdError::Http(format!("build radrootsd client: {err}")))?;
- let mut request_builder = client
- .post(endpoint)
- .headers(auth_headers(auth)?)
- .json(&json!({
- "jsonrpc": "2.0",
- "id": request_id,
- "method": method,
- "params": params,
- }));
-
- request_builder = request_builder.header(CONTENT_TYPE, "application/json");
-
- let response = request_builder
- .send()
- .await
- .map_err(|err| RadrootsdError::Http(format!("send radrootsd {method} request: {err}")))?;
- let status = response.status();
- let body = response
- .text()
- .await
- .map_err(|err| RadrootsdError::Http(format!("read radrootsd response body: {err}")))?;
-
- if !status.is_success() {
- return Err(RadrootsdError::Http(format!(
- "radrootsd returned http {}: {}",
- status.as_u16(),
- body
- )));
- }
-
- let envelope: JsonRpcEnvelope<R> = serde_json::from_str(body.as_str()).map_err(|err| {
- RadrootsdError::MalformedResponse(format!("decode radrootsd {method} response: {err}"))
- })?;
- match (envelope.result, envelope.error) {
- (Some(result), None) => Ok(result),
- (None, Some(error)) => Err(RadrootsdError::JsonRpc(format!(
- "radrootsd {method} failed {}: {}",
- error.code, error.message
- ))),
- (Some(_), Some(error)) => Err(RadrootsdError::MalformedResponse(format!(
- "radrootsd {method} returned result and error: {} {}",
- error.code, error.message
- ))),
- (None, None) => Err(RadrootsdError::MalformedResponse(format!(
- "radrootsd {method} returned neither result nor error"
- ))),
- }
-}
diff --git a/crates/sdk/src/adapters/relay.rs b/crates/sdk/src/adapters/relay.rs
@@ -1,96 +0,0 @@
-use core::time::Duration;
-
-use crate::adapters::signing::SignedNostrEvent;
-use crate::identity::RadrootsIdentity;
-use radroots_nostr::prelude::{
- RadrootsNostrClient, RadrootsNostrClientOptions, RadrootsNostrError, RadrootsNostrEventId,
- RadrootsNostrOutput,
-};
-
-pub type RelayClient = RadrootsNostrClient;
-pub type RelayClientOptions = RadrootsNostrClientOptions;
-pub type RelayError = RadrootsNostrError;
-pub type RelayEventId = RadrootsNostrEventId;
-pub type RelayOutput<T> = RadrootsNostrOutput<T>;
-
-pub fn signerless_client() -> RelayClient {
- RelayClient::new_signerless()
-}
-
-pub fn signerless_client_with_options(
- options: RelayClientOptions,
-) -> Result<RelayClient, RelayError> {
- RelayClient::new_signerless_with_options(options)
-}
-
-pub fn client_from_identity(identity: &RadrootsIdentity) -> RelayClient {
- RelayClient::from_identity(identity)
-}
-
-pub async fn configure_write_relays(
- client: &RelayClient,
- relay_urls: &[String],
- connect_timeout: Duration,
-) -> Result<(), RelayError> {
- for relay_url in relay_urls {
- client.add_write_relay(relay_url).await?;
- }
- client.connect().await;
- client.wait_for_connection(connect_timeout).await;
- Ok(())
-}
-
-pub async fn connected_client_from_identity(
- identity: &RadrootsIdentity,
- relay_urls: &[String],
- connect_timeout: Duration,
-) -> Result<RelayClient, RelayError> {
- let client = client_from_identity(identity);
- configure_write_relays(&client, relay_urls, connect_timeout).await?;
- Ok(client)
-}
-
-pub async fn connected_relay_urls(client: &RelayClient) -> Vec<String> {
- let mut relay_urls = client
- .relays()
- .await
- .into_values()
- .filter(|relay| relay.is_connected())
- .map(|relay| relay.url().to_string())
- .collect::<Vec<_>>();
- relay_urls.sort();
- relay_urls
-}
-
-pub async fn publish_signed_event(
- client: &RelayClient,
- event: &SignedNostrEvent,
-) -> Result<RelayOutput<RelayEventId>, RelayError> {
- client.send_event(event).await
-}
-
-#[cfg(test)]
-mod tests {
- use super::{client_from_identity, signerless_client, signerless_client_with_options};
- use crate::identity::RadrootsIdentity;
- use tokio::runtime::Runtime;
-
- #[test]
- fn client_constructors_build_without_runtime_net() {
- let identity = RadrootsIdentity::generate();
- let _client = client_from_identity(&identity);
- let _signerless = signerless_client();
- let _signerless_with_options =
- signerless_client_with_options(super::RelayClientOptions::new())
- .expect("signerless client with options");
- }
-
- #[test]
- fn signerless_client_has_no_signer() {
- let runtime = Runtime::new().expect("tokio runtime");
- runtime.block_on(async {
- let client = signerless_client();
- assert!(!client.has_signer().await);
- });
- }
-}
diff --git a/crates/sdk/src/adapters/signer.rs b/crates/sdk/src/adapters/signer.rs
@@ -1,24 +0,0 @@
-pub use radroots_nostr_connect::prelude::{
- RADROOTS_NOSTR_CONNECT_PENDING_CONNECTION_ERROR, RADROOTS_NOSTR_CONNECT_RPC_KIND,
- RadrootsNostrConnectBunkerUri, RadrootsNostrConnectClientMetadata,
- RadrootsNostrConnectClientUri, RadrootsNostrConnectError, RadrootsNostrConnectMethod,
- RadrootsNostrConnectPendingConnectionPollOutcome, RadrootsNostrConnectPermission,
- RadrootsNostrConnectPermissions, RadrootsNostrConnectRemoteSessionCapability,
- RadrootsNostrConnectRequest, RadrootsNostrConnectRequestMessage, RadrootsNostrConnectResponse,
- RadrootsNostrConnectResponseEnvelope, RadrootsNostrConnectUri,
-};
-pub use radroots_nostr_signer::prelude::{
- RadrootsNostrEmbeddedSignerBackend, RadrootsNostrLocalSignerAvailability,
- RadrootsNostrLocalSignerCapability, RadrootsNostrRemoteSessionSignerCapability,
- RadrootsNostrSignerBackend, RadrootsNostrSignerBackendCapabilities,
- RadrootsNostrSignerCapability, RadrootsNostrSignerConnectEvaluation,
- RadrootsNostrSignerConnectProposal, RadrootsNostrSignerError,
- RadrootsNostrSignerHandledRequest, RadrootsNostrSignerHandledRequestOutcome,
- RadrootsNostrSignerManager, RadrootsNostrSignerNip46Codec,
- RadrootsNostrSignerNip46ConnectDecision, RadrootsNostrSignerNip46Handler,
- RadrootsNostrSignerNip46Policy, RadrootsNostrSignerNip46Signer,
- RadrootsNostrSignerPublishTransition, RadrootsNostrSignerRequestAction,
- RadrootsNostrSignerRequestEvaluation, RadrootsNostrSignerRequestResponseHint,
- RadrootsNostrSignerSessionLookup, connect_response_outcome, handled_request_for_action,
- response_from_hint,
-};
diff --git a/crates/sdk/src/adapters/signing.rs b/crates/sdk/src/adapters/signing.rs
@@ -1,63 +0,0 @@
-use crate::WireEventParts;
-use crate::identity::RadrootsIdentity;
-use radroots_nostr::prelude::{RadrootsNostrError, radroots_nostr_build_event};
-
-pub type SignedNostrEvent = radroots_nostr::prelude::RadrootsNostrEvent;
-pub type NostrEventBuilder = radroots_nostr::prelude::RadrootsNostrEventBuilder;
-pub type SigningError = RadrootsNostrError;
-
-pub fn event_builder_from_parts(parts: WireEventParts) -> Result<NostrEventBuilder, SigningError> {
- radroots_nostr_build_event(parts.kind, parts.content, parts.tags)
-}
-
-pub fn sign_parts_with_identity(
- identity: &RadrootsIdentity,
- parts: WireEventParts,
-) -> Result<SignedNostrEvent, SigningError> {
- let builder = event_builder_from_parts(parts)?;
- sign_builder_with_identity(identity, builder)
-}
-
-pub fn sign_builder_with_identity(
- identity: &RadrootsIdentity,
- builder: NostrEventBuilder,
-) -> Result<SignedNostrEvent, SigningError> {
- builder.sign_with_keys(identity.keys()).map_err(Into::into)
-}
-
-#[cfg(test)]
-mod tests {
- use super::{event_builder_from_parts, sign_parts_with_identity};
- use crate::{WireEventParts, identity::RadrootsIdentity};
-
- #[test]
- fn event_builder_from_parts_preserves_kind_and_content() {
- let builder = event_builder_from_parts(WireEventParts {
- kind: 30402,
- content: "hello".into(),
- tags: vec![vec!["x".into(), "y".into()]],
- })
- .expect("builder");
- let identity = RadrootsIdentity::generate();
- let event = builder.build(identity.keys().public_key());
-
- assert_eq!(u16::from(event.kind), 30402);
- assert_eq!(event.content, "hello");
- }
-
- #[test]
- fn sign_parts_with_identity_signs_event() {
- let identity = RadrootsIdentity::generate();
- let event = sign_parts_with_identity(
- &identity,
- WireEventParts {
- kind: 30402,
- content: "hello".into(),
- tags: vec![],
- },
- )
- .expect("signed event");
-
- assert_eq!(event.pubkey.to_hex(), identity.public_key_hex());
- }
-}
diff --git a/crates/sdk/src/client.rs b/crates/sdk/src/client.rs
@@ -1,2711 +0,0 @@
-#[cfg(not(feature = "std"))]
-use alloc::{string::String, vec::Vec};
-use core::fmt;
-#[cfg(feature = "std")]
-use std::{string::String, vec::Vec};
-
-#[cfg(feature = "radrootsd-client")]
-use crate::adapters::radrootsd;
-#[cfg(all(
- feature = "identity-models",
- feature = "relay-client",
- feature = "signing"
-))]
-use crate::adapters::{relay, signing};
-use crate::config::SignerConfig;
-use crate::config::{RadrootsSdkConfig, SdkConfigError, SdkTransportMode};
-#[cfg(all(
- feature = "identity-models",
- feature = "relay-client",
- feature = "signing"
-))]
-use crate::identity::RadrootsIdentity;
-use crate::{
- NostrTags, RadrootsNostrEvent, RadrootsNostrEventPtr, RadrootsProfile, RadrootsProfileType,
- TradeListingValidateResult, WireEventParts, farm, listing, order, profile,
-};
-#[cfg(any(
- feature = "radrootsd-client",
- all(
- feature = "identity-models",
- feature = "relay-client",
- feature = "signing"
- )
-))]
-use core::time::Duration;
-use radroots_events::ids::RadrootsEventId;
-#[cfg(feature = "radrootsd-client")]
-use radroots_events::kinds::{KIND_FARM, KIND_LISTING};
-
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub struct SdkPublishReceipt {
- pub transport: SdkTransportMode,
- pub event_kind: Option<u32>,
- pub event_id: Option<String>,
- pub transport_receipt: SdkTransportReceipt,
-}
-
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub enum SdkTransportReceipt {
- RelayDirect(SdkRelayPublishReceipt),
- Radrootsd(SdkRadrootsdPublishReceipt),
-}
-
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub struct SdkRelayPublishReceipt {
- pub event: RadrootsNostrEvent,
- pub event_id: String,
- pub event_kind: u32,
- pub created_at: u32,
- pub signature: String,
- pub target_relays: Vec<String>,
- pub connected_relays: Vec<String>,
- pub acknowledged_relays: Vec<String>,
- pub failed_relays: Vec<SdkRelayFailure>,
-}
-
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub struct SdkRelayFailure {
- pub relay_url: String,
- pub error: String,
-}
-
-#[derive(Clone, PartialEq, Eq, Default)]
-pub struct SdkRadrootsdPublishReceipt {
- pub accepted: bool,
- pub deduplicated: bool,
- pub job_id: Option<String>,
- pub status: Option<String>,
- pub signer_mode: Option<String>,
- pub signer_session_id: Option<String>,
- pub event_addr: Option<String>,
- pub relay_count: Option<usize>,
- pub acknowledged_relay_count: Option<usize>,
-}
-
-impl fmt::Debug for SdkRadrootsdPublishReceipt {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- let mut debug = f.debug_struct("SdkRadrootsdPublishReceipt");
- debug.field("accepted", &self.accepted);
- debug.field("deduplicated", &self.deduplicated);
- debug.field("job_id", &self.job_id);
- debug.field("status", &self.status);
- debug.field(
- "signer_mode",
- &self.signer_mode.as_ref().map(|_| "<redacted>"),
- );
- debug.field(
- "signer_session_id",
- &self.signer_session_id.as_ref().map(|_| "<redacted>"),
- );
- debug.field("event_addr", &self.event_addr);
- debug.field("relay_count", &self.relay_count);
- debug.field("acknowledged_relay_count", &self.acknowledged_relay_count);
- debug.finish()
- }
-}
-
-#[cfg(feature = "radrootsd-client")]
-impl SdkRadrootsdPublishReceipt {
- pub fn job(&self) -> Option<SdkRadrootsdBridgeJobRef> {
- self.job_id
- .as_ref()
- .map(|job_id| SdkRadrootsdBridgeJobRef::new(job_id.clone()))
- }
-}
-
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub enum SdkPublishError {
- Config(SdkConfigError),
- Encode(String),
- UnsupportedTransport {
- transport: SdkTransportMode,
- operation: &'static str,
- },
- UnsupportedSignerMode {
- transport: SdkTransportMode,
- signer: SignerConfig,
- required: SignerConfig,
- operation: &'static str,
- },
- Relay(String),
- RelaySetup {
- transport: SdkTransportMode,
- operation: &'static str,
- target_relays: Vec<String>,
- error: String,
- },
- RelayNotAcknowledged {
- transport: SdkTransportMode,
- failed_relays: Vec<SdkRelayFailure>,
- },
- Radrootsd(String),
-}
-
-impl From<SdkConfigError> for SdkPublishError {
- fn from(value: SdkConfigError) -> Self {
- Self::Config(value)
- }
-}
-
-impl core::fmt::Display for SdkPublishError {
- fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
- match self {
- Self::Config(err) => write!(f, "{err}"),
- Self::Encode(message) => write!(f, "{message}"),
- Self::UnsupportedTransport {
- transport,
- operation,
- } => {
- write!(
- f,
- "{operation} requires a different sdk transport mode than {transport:?}"
- )
- }
- Self::UnsupportedSignerMode {
- transport,
- signer,
- required,
- operation,
- } => write!(
- f,
- "{operation} requires signer mode `{required}` for {transport:?} transport, got `{signer}`"
- ),
- Self::Relay(message) => write!(f, "{message}"),
- Self::RelaySetup {
- transport,
- operation,
- target_relays,
- error,
- } => {
- if target_relays.is_empty() {
- write!(
- f,
- "{operation} failed to prepare {transport:?} relay publish: {error}"
- )
- } else {
- let relays = target_relays.join(", ");
- write!(
- f,
- "{operation} failed to prepare {transport:?} relay publish for {relays}: {error}"
- )
- }
- }
- Self::RelayNotAcknowledged {
- transport,
- failed_relays,
- } => {
- if failed_relays.is_empty() {
- write!(f, "{transport:?} publish was not acknowledged by any relay")
- } else {
- let summary = failed_relays
- .iter()
- .map(|failure| format!("{}: {}", failure.relay_url, failure.error))
- .collect::<Vec<_>>()
- .join(", ");
- write!(
- f,
- "{transport:?} publish was not acknowledged by any relay: {summary}"
- )
- }
- }
- Self::Radrootsd(message) => write!(f, "{message}"),
- }
- }
-}
-
-#[cfg(feature = "std")]
-impl std::error::Error for SdkPublishError {}
-
-#[cfg(feature = "radrootsd-client")]
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub enum SdkRadrootsdSessionError {
- Config(SdkConfigError),
- UnsupportedTransport {
- transport: SdkTransportMode,
- operation: &'static str,
- },
- Radrootsd(String),
-}
-
-#[cfg(feature = "radrootsd-client")]
-impl From<SdkConfigError> for SdkRadrootsdSessionError {
- fn from(value: SdkConfigError) -> Self {
- Self::Config(value)
- }
-}
-
-#[cfg(feature = "radrootsd-client")]
-impl fmt::Display for SdkRadrootsdSessionError {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- match self {
- Self::Config(err) => write!(f, "{err}"),
- Self::UnsupportedTransport {
- transport,
- operation,
- } => {
- write!(
- f,
- "{operation} requires a different sdk transport mode than {transport:?}"
- )
- }
- Self::Radrootsd(message) => write!(f, "{message}"),
- }
- }
-}
-
-#[cfg(all(feature = "radrootsd-client", feature = "std"))]
-impl std::error::Error for SdkRadrootsdSessionError {}
-
-#[cfg(feature = "radrootsd-client")]
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub enum SdkRadrootsdBridgeError {
- Config(SdkConfigError),
- UnsupportedTransport {
- transport: SdkTransportMode,
- operation: &'static str,
- },
- Radrootsd(String),
-}
-
-#[cfg(feature = "radrootsd-client")]
-impl From<SdkConfigError> for SdkRadrootsdBridgeError {
- fn from(value: SdkConfigError) -> Self {
- Self::Config(value)
- }
-}
-
-#[cfg(feature = "radrootsd-client")]
-impl fmt::Display for SdkRadrootsdBridgeError {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- match self {
- Self::Config(err) => write!(f, "{err}"),
- Self::UnsupportedTransport {
- transport,
- operation,
- } => write!(
- f,
- "{operation} requires a different sdk transport mode than {transport:?}"
- ),
- Self::Radrootsd(message) => write!(f, "{message}"),
- }
- }
-}
-
-#[cfg(all(feature = "radrootsd-client", feature = "std"))]
-impl std::error::Error for SdkRadrootsdBridgeError {}
-
-#[cfg(feature = "radrootsd-client")]
-#[derive(Clone, PartialEq, Eq)]
-pub struct SdkRadrootsdSignerSessionRef {
- session_id: String,
-}
-
-#[cfg(feature = "radrootsd-client")]
-impl fmt::Debug for SdkRadrootsdSignerSessionRef {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.debug_struct("SdkRadrootsdSignerSessionRef")
- .field("session_id", &"<redacted>")
- .finish()
- }
-}
-
-#[cfg(feature = "radrootsd-client")]
-impl SdkRadrootsdSignerSessionRef {
- pub fn from_session_id(session_id: impl Into<String>) -> Self {
- Self {
- session_id: session_id.into(),
- }
- }
-
- pub fn session_id(&self) -> &str {
- self.session_id.as_str()
- }
-}
-
-#[cfg(feature = "radrootsd-client")]
-#[derive(Clone, Debug, PartialEq, Eq)]
-pub struct SdkRadrootsdBridgeJobRef {
- job_id: String,
-}
-
-#[cfg(feature = "radrootsd-client")]
-impl SdkRadrootsdBridgeJobRef {
- pub fn new(job_id: impl Into<String>) -> Self {
- Self {
- job_id: job_id.into(),
- }
- }
-
- pub fn job_id(&self) -> &str {
- self.job_id.as_str()
- }
-}
-
-#[cfg(feature = "radrootsd-client")]
-#[derive(Clone, Debug, PartialEq, Eq)]
-pub struct SdkRadrootsdBridgeStatus {
- pub enabled: bool,
- pub ready: bool,
- pub auth_mode: String,
- pub signer_mode: String,
- pub default_signer_mode: String,
- pub supported_signer_modes: Vec<String>,
- pub available_nip46_signer_sessions: usize,
- pub relay_count: usize,
- pub delivery_policy: radrootsd::SdkRadrootsdBridgeDeliveryPolicy,
- pub delivery_quorum: Option<usize>,
- pub publish_max_attempts: usize,
- pub publish_initial_backoff_millis: u64,
- pub publish_max_backoff_millis: u64,
- pub job_status_retention: usize,
- pub retained_jobs: usize,
- pub retained_idempotency_keys: usize,
- pub accepted_jobs: usize,
- pub published_jobs: usize,
- pub failed_jobs: usize,
- pub recovered_failed_jobs: usize,
- pub methods: Vec<String>,
-}
-
-#[cfg(feature = "radrootsd-client")]
-#[derive(Clone, Debug, PartialEq, Eq)]
-pub struct SdkRadrootsdBridgeJobView {
- job: SdkRadrootsdBridgeJobRef,
- pub command: String,
- pub idempotency_key: Option<String>,
- pub status: radrootsd::SdkRadrootsdBridgeJobStatus,
- pub terminal: bool,
- pub recovered_after_restart: bool,
- pub requested_at_unix: u64,
- pub completed_at_unix: Option<u64>,
- pub signer_mode: String,
- pub signer_session_id: Option<String>,
- pub event_kind: u32,
- pub event_id: Option<String>,
- pub event_addr: Option<String>,
- pub delivery_policy: radrootsd::SdkRadrootsdBridgeDeliveryPolicy,
- pub delivery_quorum: Option<usize>,
- pub relay_count: usize,
- pub acknowledged_relay_count: usize,
- pub required_acknowledged_relay_count: usize,
- pub attempt_count: usize,
- pub attempt_summaries: Vec<String>,
- pub relay_results: Vec<radrootsd::SdkRadrootsdBridgeRelayPublishResult>,
- pub relay_outcome_summary: String,
-}
-
-#[cfg(feature = "radrootsd-client")]
-impl SdkRadrootsdBridgeJobView {
- pub fn job(&self) -> &SdkRadrootsdBridgeJobRef {
- &self.job
- }
-}
-
-#[cfg(feature = "radrootsd-client")]
-impl From<radrootsd::SdkRadrootsdBridgeStatusResponse> for SdkRadrootsdBridgeStatus {
- fn from(value: radrootsd::SdkRadrootsdBridgeStatusResponse) -> Self {
- Self {
- enabled: value.enabled,
- ready: value.ready,
- auth_mode: value.auth_mode,
- signer_mode: value.signer_mode,
- default_signer_mode: value.default_signer_mode,
- supported_signer_modes: value.supported_signer_modes,
- available_nip46_signer_sessions: value.available_nip46_signer_sessions,
- relay_count: value.relay_count,
- delivery_policy: value.delivery_policy,
- delivery_quorum: value.delivery_quorum,
- publish_max_attempts: value.publish_max_attempts,
- publish_initial_backoff_millis: value.publish_initial_backoff_millis,
- publish_max_backoff_millis: value.publish_max_backoff_millis,
- job_status_retention: value.job_status_retention,
- retained_jobs: value.retained_jobs,
- retained_idempotency_keys: value.retained_idempotency_keys,
- accepted_jobs: value.accepted_jobs,
- published_jobs: value.published_jobs,
- failed_jobs: value.failed_jobs,
- recovered_failed_jobs: value.recovered_failed_jobs,
- methods: value.methods,
- }
- }
-}
-
-#[cfg(feature = "radrootsd-client")]
-impl From<radrootsd::SdkRadrootsdBridgeJobView> for SdkRadrootsdBridgeJobView {
- fn from(value: radrootsd::SdkRadrootsdBridgeJobView) -> Self {
- Self {
- job: SdkRadrootsdBridgeJobRef::new(value.job_id),
- command: value.command,
- idempotency_key: value.idempotency_key,
- status: value.status,
- terminal: value.terminal,
- recovered_after_restart: value.recovered_after_restart,
- requested_at_unix: value.requested_at_unix,
- completed_at_unix: value.completed_at_unix,
- signer_mode: value.signer_mode,
- signer_session_id: value.signer_session_id,
- event_kind: value.event_kind,
- event_id: value.event_id,
- event_addr: value.event_addr,
- delivery_policy: value.delivery_policy,
- delivery_quorum: value.delivery_quorum,
- relay_count: value.relay_count,
- acknowledged_relay_count: value.acknowledged_relay_count,
- required_acknowledged_relay_count: value.required_acknowledged_relay_count,
- attempt_count: value.attempt_count,
- attempt_summaries: value.attempt_summaries,
- relay_results: value.relay_results,
- relay_outcome_summary: value.relay_outcome_summary,
- }
- }
-}
-
-#[cfg(feature = "radrootsd-client")]
-#[derive(Clone, PartialEq, Eq)]
-pub struct SdkRadrootsdSignerSessionHandle {
- session: SdkRadrootsdSignerSessionRef,
- mode: radrootsd::SdkRadrootsdSignerSessionMode,
- remote_signer_pubkey: String,
- client_pubkey: String,
- relays: Vec<String>,
-}
-
-#[cfg(feature = "radrootsd-client")]
-#[derive(Clone, PartialEq, Eq)]
-pub struct SdkRadrootsdSignerSessionView {
- session: SdkRadrootsdSignerSessionRef,
- pub role: radrootsd::SdkRadrootsdSignerSessionRole,
- pub client_pubkey: String,
- pub signer_pubkey: String,
- pub user_pubkey: Option<String>,
- pub relays: Vec<String>,
- pub permissions: Vec<String>,
- pub name: Option<String>,
- pub url: Option<String>,
- pub image: Option<String>,
- pub auth_required: bool,
- pub authorized: bool,
- pub auth_url: Option<String>,
- pub expires_in_secs: Option<u64>,
- pub signer_authority: Option<radrootsd::SdkRadrootsdSignerAuthority>,
-}
-
-#[cfg(feature = "radrootsd-client")]
-impl fmt::Debug for SdkRadrootsdSignerSessionView {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- let mut debug = f.debug_struct("SdkRadrootsdSignerSessionView");
- debug.field("session", &self.session);
- debug.field("role", &self.role);
- debug.field("client_pubkey", &self.client_pubkey);
- debug.field("signer_pubkey", &self.signer_pubkey);
- debug.field("user_pubkey", &self.user_pubkey);
- debug.field("relays", &self.relays);
- debug.field("permissions", &self.permissions);
- debug.field("name", &self.name);
- debug.field("url", &self.url);
- debug.field("image", &self.image);
- debug.field("auth_required", &self.auth_required);
- debug.field("authorized", &self.authorized);
- debug.field("auth_url", &self.auth_url);
- debug.field("expires_in_secs", &self.expires_in_secs);
- debug.field("signer_authority", &self.signer_authority);
- debug.finish()
- }
-}
-
-#[cfg(feature = "radrootsd-client")]
-impl SdkRadrootsdSignerSessionView {
- pub fn session(&self) -> &SdkRadrootsdSignerSessionRef {
- &self.session
- }
-}
-
-#[cfg(feature = "radrootsd-client")]
-#[derive(Clone, Debug, PartialEq, Eq)]
-pub struct SdkRadrootsdSignerSessionAuthorizeResult {
- pub authorized: bool,
- pub replayed: bool,
-}
-
-#[cfg(feature = "radrootsd-client")]
-#[derive(Clone, Debug, PartialEq, Eq)]
-pub struct SdkRadrootsdSignerSessionPublicKeyResult {
- pub pubkey: String,
-}
-
-#[cfg(feature = "radrootsd-client")]
-#[derive(Clone, Debug, PartialEq, Eq)]
-pub struct SdkRadrootsdSignerSessionRequireAuthResult {
- pub required: bool,
-}
-
-#[cfg(feature = "radrootsd-client")]
-#[derive(Clone, Debug, PartialEq, Eq)]
-pub struct SdkRadrootsdSignerSessionCloseResult {
- pub closed: bool,
-}
-
-#[cfg(feature = "radrootsd-client")]
-impl From<radrootsd::SdkRadrootsdSignerSessionViewResponse> for SdkRadrootsdSignerSessionView {
- fn from(value: radrootsd::SdkRadrootsdSignerSessionViewResponse) -> Self {
- Self {
- session: SdkRadrootsdSignerSessionRef {
- session_id: value.session_id,
- },
- role: value.role,
- client_pubkey: value.client_pubkey,
- signer_pubkey: value.signer_pubkey,
- user_pubkey: value.user_pubkey,
- relays: value.relays,
- permissions: value.permissions,
- name: value.name,
- url: value.url,
- image: value.image,
- auth_required: value.auth_required,
- authorized: value.authorized,
- auth_url: value.auth_url,
- expires_in_secs: value.expires_in_secs,
- signer_authority: value.signer_authority,
- }
- }
-}
-
-#[cfg(feature = "radrootsd-client")]
-impl From<radrootsd::SdkRadrootsdSignerSessionAuthorizeResponse>
- for SdkRadrootsdSignerSessionAuthorizeResult
-{
- fn from(value: radrootsd::SdkRadrootsdSignerSessionAuthorizeResponse) -> Self {
- Self {
- authorized: value.authorized,
- replayed: value.replayed,
- }
- }
-}
-
-#[cfg(feature = "radrootsd-client")]
-impl From<radrootsd::SdkRadrootsdSignerSessionPublicKeyResponse>
- for SdkRadrootsdSignerSessionPublicKeyResult
-{
- fn from(value: radrootsd::SdkRadrootsdSignerSessionPublicKeyResponse) -> Self {
- Self {
- pubkey: value.pubkey,
- }
- }
-}
-
-#[cfg(feature = "radrootsd-client")]
-impl From<radrootsd::SdkRadrootsdSignerSessionRequireAuthResponse>
- for SdkRadrootsdSignerSessionRequireAuthResult
-{
- fn from(value: radrootsd::SdkRadrootsdSignerSessionRequireAuthResponse) -> Self {
- Self {
- required: value.required,
- }
- }
-}
-
-#[cfg(feature = "radrootsd-client")]
-impl From<radrootsd::SdkRadrootsdSignerSessionCloseResponse>
- for SdkRadrootsdSignerSessionCloseResult
-{
- fn from(value: radrootsd::SdkRadrootsdSignerSessionCloseResponse) -> Self {
- Self {
- closed: value.closed,
- }
- }
-}
-
-#[cfg(feature = "radrootsd-client")]
-impl fmt::Debug for SdkRadrootsdSignerSessionHandle {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- let mut debug = f.debug_struct("SdkRadrootsdSignerSessionHandle");
- debug.field("session", &self.session);
- debug.field("mode", &self.mode);
- debug.field("remote_signer_pubkey", &self.remote_signer_pubkey);
- debug.field("client_pubkey", &self.client_pubkey);
- debug.field("relays", &self.relays);
- debug.finish()
- }
-}
-
-#[cfg(feature = "radrootsd-client")]
-impl SdkRadrootsdSignerSessionHandle {
- pub fn session(&self) -> &SdkRadrootsdSignerSessionRef {
- &self.session
- }
-
- pub fn mode(&self) -> radrootsd::SdkRadrootsdSignerSessionMode {
- self.mode
- }
-
- pub fn remote_signer_pubkey(&self) -> &str {
- self.remote_signer_pubkey.as_str()
- }
-
- pub fn client_pubkey(&self) -> &str {
- self.client_pubkey.as_str()
- }
-
- pub fn relays(&self) -> &[String] {
- self.relays.as_slice()
- }
-}
-
-#[cfg(feature = "radrootsd-client")]
-impl From<radrootsd::SdkRadrootsdSignerSessionConnectResponse> for SdkRadrootsdSignerSessionHandle {
- fn from(value: radrootsd::SdkRadrootsdSignerSessionConnectResponse) -> Self {
- Self {
- session: SdkRadrootsdSignerSessionRef {
- session_id: value.session_id,
- },
- mode: value.mode,
- remote_signer_pubkey: value.remote_signer_pubkey,
- client_pubkey: value.client_pubkey,
- relays: value.relays,
- }
- }
-}
-
-#[cfg(feature = "radrootsd-client")]
-#[derive(Clone, PartialEq, Eq)]
-pub struct SdkRadrootsdProfilePublishOptions {
- session: SdkRadrootsdSignerSessionRef,
- idempotency_key: Option<String>,
- signer_authority: Option<radrootsd::SdkRadrootsdSignerAuthority>,
-}
-
-#[cfg(feature = "radrootsd-client")]
-impl SdkRadrootsdProfilePublishOptions {
- pub fn from_signer_session(session: &SdkRadrootsdSignerSessionHandle) -> Self {
- Self {
- session: session.session().clone(),
- idempotency_key: None,
- signer_authority: None,
- }
- }
-
- pub fn from_signer_session_ref(session: &SdkRadrootsdSignerSessionRef) -> Self {
- Self {
- session: session.clone(),
- idempotency_key: None,
- signer_authority: None,
- }
- }
-
- pub fn with_idempotency_key(mut self, idempotency_key: impl Into<String>) -> Self {
- self.idempotency_key = Some(idempotency_key.into());
- self
- }
-
- pub fn with_signer_authority(
- mut self,
- signer_authority: radrootsd::SdkRadrootsdSignerAuthority,
- ) -> Self {
- self.signer_authority = Some(signer_authority);
- self
- }
-
- pub fn session(&self) -> &SdkRadrootsdSignerSessionRef {
- &self.session
- }
-
- pub fn idempotency_key(&self) -> Option<&str> {
- self.idempotency_key.as_deref()
- }
-
- pub fn signer_authority(&self) -> Option<&radrootsd::SdkRadrootsdSignerAuthority> {
- self.signer_authority.as_ref()
- }
-}
-
-#[cfg(feature = "radrootsd-client")]
-impl fmt::Debug for SdkRadrootsdProfilePublishOptions {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- let mut debug = f.debug_struct("SdkRadrootsdProfilePublishOptions");
- debug.field("session", &self.session);
- debug.field("idempotency_key", &self.idempotency_key);
- debug.field("signer_authority", &self.signer_authority);
- debug.finish()
- }
-}
-
-#[cfg(feature = "radrootsd-client")]
-#[derive(Clone, PartialEq, Eq)]
-pub struct SdkRadrootsdFarmPublishOptions {
- session: SdkRadrootsdSignerSessionRef,
- idempotency_key: Option<String>,
- signer_authority: Option<radrootsd::SdkRadrootsdSignerAuthority>,
-}
-
-#[cfg(feature = "radrootsd-client")]
-impl SdkRadrootsdFarmPublishOptions {
- pub fn from_signer_session(session: &SdkRadrootsdSignerSessionHandle) -> Self {
- Self {
- session: session.session().clone(),
- idempotency_key: None,
- signer_authority: None,
- }
- }
-
- pub fn from_signer_session_ref(session: &SdkRadrootsdSignerSessionRef) -> Self {
- Self {
- session: session.clone(),
- idempotency_key: None,
- signer_authority: None,
- }
- }
-
- pub fn with_idempotency_key(mut self, idempotency_key: impl Into<String>) -> Self {
- self.idempotency_key = Some(idempotency_key.into());
- self
- }
-
- pub fn with_signer_authority(
- mut self,
- signer_authority: radrootsd::SdkRadrootsdSignerAuthority,
- ) -> Self {
- self.signer_authority = Some(signer_authority);
- self
- }
-
- pub fn session(&self) -> &SdkRadrootsdSignerSessionRef {
- &self.session
- }
-
- pub fn idempotency_key(&self) -> Option<&str> {
- self.idempotency_key.as_deref()
- }
-
- pub fn signer_authority(&self) -> Option<&radrootsd::SdkRadrootsdSignerAuthority> {
- self.signer_authority.as_ref()
- }
-}
-
-#[cfg(feature = "radrootsd-client")]
-impl fmt::Debug for SdkRadrootsdFarmPublishOptions {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- let mut debug = f.debug_struct("SdkRadrootsdFarmPublishOptions");
- debug.field("session", &self.session);
- debug.field("idempotency_key", &self.idempotency_key);
- debug.field("signer_authority", &self.signer_authority);
- debug.finish()
- }
-}
-
-#[cfg(feature = "radrootsd-client")]
-#[derive(Clone, PartialEq, Eq)]
-pub struct SdkRadrootsdListingPublishOptions {
- session: SdkRadrootsdSignerSessionRef,
- idempotency_key: Option<String>,
- signer_authority: Option<radrootsd::SdkRadrootsdSignerAuthority>,
-}
-
-#[cfg(feature = "radrootsd-client")]
-impl SdkRadrootsdListingPublishOptions {
- pub fn from_signer_session(session: &SdkRadrootsdSignerSessionHandle) -> Self {
- Self {
- session: session.session().clone(),
- idempotency_key: None,
- signer_authority: None,
- }
- }
-
- pub fn from_signer_session_ref(session: &SdkRadrootsdSignerSessionRef) -> Self {
- Self {
- session: session.clone(),
- idempotency_key: None,
- signer_authority: None,
- }
- }
-
- pub fn with_idempotency_key(mut self, idempotency_key: impl Into<String>) -> Self {
- self.idempotency_key = Some(idempotency_key.into());
- self
- }
-
- pub fn with_signer_authority(
- mut self,
- signer_authority: radrootsd::SdkRadrootsdSignerAuthority,
- ) -> Self {
- self.signer_authority = Some(signer_authority);
- self
- }
-
- pub fn session(&self) -> &SdkRadrootsdSignerSessionRef {
- &self.session
- }
-
- pub fn idempotency_key(&self) -> Option<&str> {
- self.idempotency_key.as_deref()
- }
-
- pub fn signer_authority(&self) -> Option<&radrootsd::SdkRadrootsdSignerAuthority> {
- self.signer_authority.as_ref()
- }
-}
-
-#[cfg(feature = "radrootsd-client")]
-impl fmt::Debug for SdkRadrootsdListingPublishOptions {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- let mut debug = f.debug_struct("SdkRadrootsdListingPublishOptions");
- debug.field("session", &self.session);
- debug.field("idempotency_key", &self.idempotency_key);
- debug.field("signer_authority", &self.signer_authority);
- debug.finish()
- }
-}
-
-#[cfg(feature = "radrootsd-client")]
-#[derive(Clone, PartialEq, Eq)]
-pub struct SdkRadrootsdOrderRequestPublishOptions {
- session: SdkRadrootsdSignerSessionRef,
- idempotency_key: Option<String>,
- signer_authority: Option<radrootsd::SdkRadrootsdSignerAuthority>,
-}
-
-#[cfg(feature = "radrootsd-client")]
-impl SdkRadrootsdOrderRequestPublishOptions {
- pub fn from_signer_session(session: &SdkRadrootsdSignerSessionHandle) -> Self {
- Self {
- session: session.session().clone(),
- idempotency_key: None,
- signer_authority: None,
- }
- }
-
- pub fn from_signer_session_ref(session: &SdkRadrootsdSignerSessionRef) -> Self {
- Self {
- session: session.clone(),
- idempotency_key: None,
- signer_authority: None,
- }
- }
-
- pub fn with_idempotency_key(mut self, idempotency_key: impl Into<String>) -> Self {
- self.idempotency_key = Some(idempotency_key.into());
- self
- }
-
- pub fn with_signer_authority(
- mut self,
- signer_authority: radrootsd::SdkRadrootsdSignerAuthority,
- ) -> Self {
- self.signer_authority = Some(signer_authority);
- self
- }
-
- pub fn session(&self) -> &SdkRadrootsdSignerSessionRef {
- &self.session
- }
-
- pub fn idempotency_key(&self) -> Option<&str> {
- self.idempotency_key.as_deref()
- }
-
- pub fn signer_authority(&self) -> Option<&radrootsd::SdkRadrootsdSignerAuthority> {
- self.signer_authority.as_ref()
- }
-}
-
-#[cfg(feature = "radrootsd-client")]
-impl fmt::Debug for SdkRadrootsdOrderRequestPublishOptions {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- let mut debug = f.debug_struct("SdkRadrootsdOrderRequestPublishOptions");
- debug.field("session", &self.session);
- debug.field("idempotency_key", &self.idempotency_key);
- debug.field("signer_authority", &self.signer_authority);
- debug.finish()
- }
-}
-
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub struct RadrootsSdkClient {
- config: RadrootsSdkConfig,
- resolved_transport_target: SdkResolvedTransportTarget,
-}
-
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub enum SdkResolvedTransportTarget {
- RelayDirect { relay_urls: Vec<String> },
- Radrootsd { endpoint: String },
-}
-
-impl RadrootsSdkClient {
- pub fn from_config(config: RadrootsSdkConfig) -> Result<Self, SdkConfigError> {
- let resolved_transport_target = match config.transport {
- SdkTransportMode::RelayDirect => SdkResolvedTransportTarget::RelayDirect {
- relay_urls: config.resolved_relay_urls()?,
- },
- SdkTransportMode::Radrootsd => SdkResolvedTransportTarget::Radrootsd {
- endpoint: config.resolved_radrootsd_endpoint()?,
- },
- };
- Ok(Self {
- config,
- resolved_transport_target,
- })
- }
-
- pub fn config(&self) -> &RadrootsSdkConfig {
- &self.config
- }
-
- pub fn transport(&self) -> SdkTransportMode {
- self.config.transport
- }
-
- pub fn signer(&self) -> SignerConfig {
- self.config.signer
- }
-
- pub fn resolved_transport_target(&self) -> &SdkResolvedTransportTarget {
- &self.resolved_transport_target
- }
-
- pub fn profile(&self) -> ProfileClient<'_> {
- ProfileClient { client: self }
- }
-
- pub fn farm(&self) -> FarmClient<'_> {
- FarmClient { client: self }
- }
-
- pub fn listing(&self) -> ListingClient<'_> {
- ListingClient { client: self }
- }
-
- pub fn order(&self) -> TradeClient<'_> {
- TradeClient { client: self }
- }
-
- #[cfg(feature = "radrootsd-client")]
- pub fn radrootsd(&self) -> RadrootsdClient<'_> {
- RadrootsdClient { client: self }
- }
-
- #[cfg(any(
- feature = "radrootsd-client",
- all(
- feature = "identity-models",
- feature = "relay-client",
- feature = "signing"
- )
- ))]
- fn require_signer_mode(
- &self,
- required: SignerConfig,
- operation: &'static str,
- ) -> Result<(), SdkPublishError> {
- let signer = self.signer();
- if signer == required {
- return Ok(());
- }
- Err(SdkPublishError::UnsupportedSignerMode {
- transport: self.transport(),
- signer,
- required,
- operation,
- })
- }
-
- #[cfg(all(
- feature = "identity-models",
- feature = "relay-client",
- feature = "signing"
- ))]
- async fn publish_parts_via_relay_with_identity(
- &self,
- identity: &RadrootsIdentity,
- parts: WireEventParts,
- operation: &'static str,
- ) -> Result<SdkPublishReceipt, SdkPublishError> {
- if self.transport() != SdkTransportMode::RelayDirect {
- return Err(SdkPublishError::UnsupportedTransport {
- transport: self.transport(),
- operation,
- });
- }
- self.require_signer_mode(SignerConfig::LocalIdentity, operation)?;
-
- let relay_urls = match &self.resolved_transport_target {
- SdkResolvedTransportTarget::RelayDirect { relay_urls } => relay_urls.clone(),
- SdkResolvedTransportTarget::Radrootsd { .. } => {
- return Err(SdkPublishError::UnsupportedTransport {
- transport: self.transport(),
- operation,
- });
- }
- };
- let client = relay::connected_client_from_identity(
- identity,
- &relay_urls,
- Duration::from_millis(self.config.network.timeout_ms),
- )
- .await
- .map_err(|err| SdkPublishError::RelaySetup {
- transport: SdkTransportMode::RelayDirect,
- operation,
- target_relays: relay_urls.clone(),
- error: err.to_string(),
- })?;
- let connected_relays = relay::connected_relay_urls(&client).await;
- if connected_relays.is_empty() {
- return Err(SdkPublishError::RelaySetup {
- transport: SdkTransportMode::RelayDirect,
- operation,
- target_relays: relay_urls,
- error: "no relay connection was established".to_owned(),
- });
- }
- let signed_event = signing::sign_parts_with_identity(identity, parts)
- .map_err(|err| SdkPublishError::Relay(err.to_string()))?;
- let output = relay::publish_signed_event(&client, &signed_event)
- .await
- .map_err(|err| SdkPublishError::RelaySetup {
- transport: SdkTransportMode::RelayDirect,
- operation,
- target_relays: relay_urls.clone(),
- error: err.to_string(),
- })?;
- sdk_publish_receipt_from_relay_output(signed_event, relay_urls, connected_relays, output)
- }
-
- #[cfg(feature = "radrootsd-client")]
- async fn publish_listing_via_radrootsd(
- &self,
- request: &radrootsd::SdkRadrootsdListingPublishRequest,
- ) -> Result<SdkPublishReceipt, SdkPublishError> {
- if self.transport() != SdkTransportMode::Radrootsd {
- return Err(SdkPublishError::UnsupportedTransport {
- transport: self.transport(),
- operation: "listing.publish_via_radrootsd",
- });
- }
- self.require_signer_mode(SignerConfig::Nip46, "listing.publish_via_radrootsd")?;
-
- let endpoint = match &self.resolved_transport_target {
- SdkResolvedTransportTarget::Radrootsd { endpoint } => endpoint.as_str(),
- SdkResolvedTransportTarget::RelayDirect { .. } => {
- return Err(SdkPublishError::UnsupportedTransport {
- transport: self.transport(),
- operation: "listing.publish_via_radrootsd",
- });
- }
- };
- let response = radrootsd::publish_listing(
- endpoint,
- &self.config.radrootsd.auth,
- request,
- Duration::from_millis(self.config.network.timeout_ms),
- )
- .await
- .map_err(|err| SdkPublishError::Radrootsd(err.to_string()))?;
- Ok(sdk_publish_receipt_from_radrootsd_bridge_response(response))
- }
-
- #[cfg(feature = "radrootsd-client")]
- async fn publish_profile_via_radrootsd(
- &self,
- request: &radrootsd::SdkRadrootsdProfilePublishRequest,
- ) -> Result<SdkPublishReceipt, SdkPublishError> {
- if self.transport() != SdkTransportMode::Radrootsd {
- return Err(SdkPublishError::UnsupportedTransport {
- transport: self.transport(),
- operation: "profile.publish_via_radrootsd",
- });
- }
- self.require_signer_mode(SignerConfig::Nip46, "profile.publish_via_radrootsd")?;
-
- let endpoint = match &self.resolved_transport_target {
- SdkResolvedTransportTarget::Radrootsd { endpoint } => endpoint.as_str(),
- SdkResolvedTransportTarget::RelayDirect { .. } => {
- return Err(SdkPublishError::UnsupportedTransport {
- transport: self.transport(),
- operation: "profile.publish_via_radrootsd",
- });
- }
- };
- let response = radrootsd::publish_profile(
- endpoint,
- &self.config.radrootsd.auth,
- request,
- Duration::from_millis(self.config.network.timeout_ms),
- )
- .await
- .map_err(|err| SdkPublishError::Radrootsd(err.to_string()))?;
- Ok(sdk_publish_receipt_from_radrootsd_bridge_response(response))
- }
-
- #[cfg(feature = "radrootsd-client")]
- async fn publish_farm_via_radrootsd(
- &self,
- request: &radrootsd::SdkRadrootsdFarmPublishRequest,
- ) -> Result<SdkPublishReceipt, SdkPublishError> {
- if self.transport() != SdkTransportMode::Radrootsd {
- return Err(SdkPublishError::UnsupportedTransport {
- transport: self.transport(),
- operation: "farm.publish_via_radrootsd",
- });
- }
- self.require_signer_mode(SignerConfig::Nip46, "farm.publish_via_radrootsd")?;
-
- let endpoint = match &self.resolved_transport_target {
- SdkResolvedTransportTarget::Radrootsd { endpoint } => endpoint.as_str(),
- SdkResolvedTransportTarget::RelayDirect { .. } => {
- return Err(SdkPublishError::UnsupportedTransport {
- transport: self.transport(),
- operation: "farm.publish_via_radrootsd",
- });
- }
- };
- let response = radrootsd::publish_farm(
- endpoint,
- &self.config.radrootsd.auth,
- request,
- Duration::from_millis(self.config.network.timeout_ms),
- )
- .await
- .map_err(|err| SdkPublishError::Radrootsd(err.to_string()))?;
- Ok(sdk_publish_receipt_from_radrootsd_bridge_response(response))
- }
-
- #[cfg(feature = "radrootsd-client")]
- async fn publish_order_request_via_radrootsd(
- &self,
- request: &radrootsd::SdkRadrootsdOrderRequestPublishRequest,
- ) -> Result<SdkPublishReceipt, SdkPublishError> {
- if self.transport() != SdkTransportMode::Radrootsd {
- return Err(SdkPublishError::UnsupportedTransport {
- transport: self.transport(),
- operation: "order.publish_order_request_via_radrootsd",
- });
- }
- self.require_signer_mode(
- SignerConfig::Nip46,
- "order.publish_order_request_via_radrootsd",
- )?;
-
- let endpoint = match &self.resolved_transport_target {
- SdkResolvedTransportTarget::Radrootsd { endpoint } => endpoint.as_str(),
- SdkResolvedTransportTarget::RelayDirect { .. } => {
- return Err(SdkPublishError::UnsupportedTransport {
- transport: self.transport(),
- operation: "order.publish_order_request_via_radrootsd",
- });
- }
- };
- let response = radrootsd::publish_order_request(
- endpoint,
- &self.config.radrootsd.auth,
- request,
- Duration::from_millis(self.config.network.timeout_ms),
- )
- .await
- .map_err(|err| SdkPublishError::Radrootsd(err.to_string()))?;
- Ok(sdk_publish_receipt_from_radrootsd_bridge_response(response))
- }
-
- #[cfg(feature = "radrootsd-client")]
- async fn connect_radrootsd_signer_session(
- &self,
- request: &radrootsd::SdkRadrootsdSignerSessionConnectRequest,
- ) -> Result<SdkRadrootsdSignerSessionHandle, SdkRadrootsdSessionError> {
- if self.transport() != SdkTransportMode::Radrootsd {
- return Err(SdkRadrootsdSessionError::UnsupportedTransport {
- transport: self.transport(),
- operation: "radrootsd.signer_sessions.connect",
- });
- }
-
- let endpoint = self.require_radrootsd_endpoint("radrootsd.signer_sessions.connect")?;
- let response = radrootsd::connect_signer_session(
- endpoint,
- &self.config.radrootsd.auth,
- request,
- Duration::from_millis(self.config.network.timeout_ms),
- )
- .await
- .map_err(|err| SdkRadrootsdSessionError::Radrootsd(err.to_string()))?;
- Ok(response.into())
- }
-
- #[cfg(feature = "radrootsd-client")]
- fn require_radrootsd_endpoint(
- &self,
- operation: &'static str,
- ) -> Result<&str, SdkRadrootsdSessionError> {
- match &self.resolved_transport_target {
- SdkResolvedTransportTarget::Radrootsd { endpoint } => Ok(endpoint.as_str()),
- SdkResolvedTransportTarget::RelayDirect { .. } => {
- Err(SdkRadrootsdSessionError::UnsupportedTransport {
- transport: self.transport(),
- operation,
- })
- }
- }
- }
-
- #[cfg(feature = "radrootsd-client")]
- async fn radrootsd_signer_session_status(
- &self,
- session: &SdkRadrootsdSignerSessionRef,
- ) -> Result<SdkRadrootsdSignerSessionView, SdkRadrootsdSessionError> {
- if self.transport() != SdkTransportMode::Radrootsd {
- return Err(SdkRadrootsdSessionError::UnsupportedTransport {
- transport: self.transport(),
- operation: "radrootsd.signer_sessions.status",
- });
- }
-
- let response = radrootsd::signer_session_status(
- self.require_radrootsd_endpoint("radrootsd.signer_sessions.status")?,
- &self.config.radrootsd.auth,
- session.session_id(),
- Duration::from_millis(self.config.network.timeout_ms),
- )
- .await
- .map_err(|err| SdkRadrootsdSessionError::Radrootsd(err.to_string()))?;
- Ok(response.into())
- }
-
- #[cfg(feature = "radrootsd-client")]
- async fn radrootsd_list_signer_sessions(
- &self,
- ) -> Result<Vec<SdkRadrootsdSignerSessionView>, SdkRadrootsdSessionError> {
- if self.transport() != SdkTransportMode::Radrootsd {
- return Err(SdkRadrootsdSessionError::UnsupportedTransport {
- transport: self.transport(),
- operation: "radrootsd.signer_sessions.list",
- });
- }
-
- let response = radrootsd::list_signer_sessions(
- self.require_radrootsd_endpoint("radrootsd.signer_sessions.list")?,
- &self.config.radrootsd.auth,
- Duration::from_millis(self.config.network.timeout_ms),
- )
- .await
- .map_err(|err| SdkRadrootsdSessionError::Radrootsd(err.to_string()))?;
- Ok(response.into_iter().map(Into::into).collect())
- }
-
- #[cfg(feature = "radrootsd-client")]
- async fn authorize_radrootsd_signer_session(
- &self,
- session: &SdkRadrootsdSignerSessionRef,
- ) -> Result<SdkRadrootsdSignerSessionAuthorizeResult, SdkRadrootsdSessionError> {
- if self.transport() != SdkTransportMode::Radrootsd {
- return Err(SdkRadrootsdSessionError::UnsupportedTransport {
- transport: self.transport(),
- operation: "radrootsd.signer_sessions.authorize",
- });
- }
-
- let response = radrootsd::authorize_signer_session(
- self.require_radrootsd_endpoint("radrootsd.signer_sessions.authorize")?,
- &self.config.radrootsd.auth,
- session.session_id(),
- Duration::from_millis(self.config.network.timeout_ms),
- )
- .await
- .map_err(|err| SdkRadrootsdSessionError::Radrootsd(err.to_string()))?;
- Ok(response.into())
- }
-
- #[cfg(feature = "radrootsd-client")]
- async fn get_radrootsd_signer_session_public_key(
- &self,
- session: &SdkRadrootsdSignerSessionRef,
- ) -> Result<SdkRadrootsdSignerSessionPublicKeyResult, SdkRadrootsdSessionError> {
- if self.transport() != SdkTransportMode::Radrootsd {
- return Err(SdkRadrootsdSessionError::UnsupportedTransport {
- transport: self.transport(),
- operation: "radrootsd.signer_sessions.get_public_key",
- });
- }
-
- let response = radrootsd::get_signer_session_public_key(
- self.require_radrootsd_endpoint("radrootsd.signer_sessions.get_public_key")?,
- &self.config.radrootsd.auth,
- session.session_id(),
- Duration::from_millis(self.config.network.timeout_ms),
- )
- .await
- .map_err(|err| SdkRadrootsdSessionError::Radrootsd(err.to_string()))?;
- Ok(response.into())
- }
-
- #[cfg(feature = "radrootsd-client")]
- async fn require_radrootsd_signer_session_auth(
- &self,
- session: &SdkRadrootsdSignerSessionRef,
- auth_url: &str,
- ) -> Result<SdkRadrootsdSignerSessionRequireAuthResult, SdkRadrootsdSessionError> {
- if self.transport() != SdkTransportMode::Radrootsd {
- return Err(SdkRadrootsdSessionError::UnsupportedTransport {
- transport: self.transport(),
- operation: "radrootsd.signer_sessions.require_auth",
- });
- }
-
- let response = radrootsd::require_signer_session_auth(
- self.require_radrootsd_endpoint("radrootsd.signer_sessions.require_auth")?,
- &self.config.radrootsd.auth,
- session.session_id(),
- auth_url,
- Duration::from_millis(self.config.network.timeout_ms),
- )
- .await
- .map_err(|err| SdkRadrootsdSessionError::Radrootsd(err.to_string()))?;
- Ok(response.into())
- }
-
- #[cfg(feature = "radrootsd-client")]
- async fn close_radrootsd_signer_session(
- &self,
- session: &SdkRadrootsdSignerSessionRef,
- ) -> Result<SdkRadrootsdSignerSessionCloseResult, SdkRadrootsdSessionError> {
- if self.transport() != SdkTransportMode::Radrootsd {
- return Err(SdkRadrootsdSessionError::UnsupportedTransport {
- transport: self.transport(),
- operation: "radrootsd.signer_sessions.close",
- });
- }
-
- let response = radrootsd::close_signer_session(
- self.require_radrootsd_endpoint("radrootsd.signer_sessions.close")?,
- &self.config.radrootsd.auth,
- session.session_id(),
- Duration::from_millis(self.config.network.timeout_ms),
- )
- .await
- .map_err(|err| SdkRadrootsdSessionError::Radrootsd(err.to_string()))?;
- Ok(response.into())
- }
-
- #[cfg(feature = "radrootsd-client")]
- fn require_radrootsd_bridge_endpoint(
- &self,
- operation: &'static str,
- ) -> Result<&str, SdkRadrootsdBridgeError> {
- match &self.resolved_transport_target {
- SdkResolvedTransportTarget::Radrootsd { endpoint } => Ok(endpoint.as_str()),
- SdkResolvedTransportTarget::RelayDirect { .. } => {
- Err(SdkRadrootsdBridgeError::UnsupportedTransport {
- transport: self.transport(),
- operation,
- })
- }
- }
- }
-
- #[cfg(feature = "radrootsd-client")]
- async fn radrootsd_bridge_status(
- &self,
- ) -> Result<SdkRadrootsdBridgeStatus, SdkRadrootsdBridgeError> {
- if self.transport() != SdkTransportMode::Radrootsd {
- return Err(SdkRadrootsdBridgeError::UnsupportedTransport {
- transport: self.transport(),
- operation: "radrootsd.bridge.status",
- });
- }
-
- let response = radrootsd::bridge_status(
- self.require_radrootsd_bridge_endpoint("radrootsd.bridge.status")?,
- &self.config.radrootsd.auth,
- Duration::from_millis(self.config.network.timeout_ms),
- )
- .await
- .map_err(|err| SdkRadrootsdBridgeError::Radrootsd(err.to_string()))?;
- Ok(response.into())
- }
-
- #[cfg(feature = "radrootsd-client")]
- async fn radrootsd_bridge_job_status(
- &self,
- job: &SdkRadrootsdBridgeJobRef,
- ) -> Result<SdkRadrootsdBridgeJobView, SdkRadrootsdBridgeError> {
- if self.transport() != SdkTransportMode::Radrootsd {
- return Err(SdkRadrootsdBridgeError::UnsupportedTransport {
- transport: self.transport(),
- operation: "radrootsd.bridge.job",
- });
- }
-
- let response = radrootsd::bridge_job_status(
- self.require_radrootsd_bridge_endpoint("radrootsd.bridge.job")?,
- &self.config.radrootsd.auth,
- job.job_id(),
- Duration::from_millis(self.config.network.timeout_ms),
- )
- .await
- .map_err(|err| SdkRadrootsdBridgeError::Radrootsd(err.to_string()))?;
- Ok(response.into())
- }
-
- #[cfg(feature = "radrootsd-client")]
- async fn radrootsd_bridge_jobs(
- &self,
- ) -> Result<Vec<SdkRadrootsdBridgeJobView>, SdkRadrootsdBridgeError> {
- if self.transport() != SdkTransportMode::Radrootsd {
- return Err(SdkRadrootsdBridgeError::UnsupportedTransport {
- transport: self.transport(),
- operation: "radrootsd.bridge.jobs",
- });
- }
-
- let response = radrootsd::list_bridge_jobs(
- self.require_radrootsd_bridge_endpoint("radrootsd.bridge.jobs")?,
- &self.config.radrootsd.auth,
- Duration::from_millis(self.config.network.timeout_ms),
- )
- .await
- .map_err(|err| SdkRadrootsdBridgeError::Radrootsd(err.to_string()))?;
- Ok(response.into_iter().map(Into::into).collect())
- }
-}
-
-#[cfg(feature = "radrootsd-client")]
-#[derive(Debug, Clone, Copy)]
-pub struct RadrootsdClient<'a> {
- client: &'a RadrootsSdkClient,
-}
-
-#[cfg(feature = "radrootsd-client")]
-impl<'a> RadrootsdClient<'a> {
- pub fn sdk(&self) -> &'a RadrootsSdkClient {
- self.client
- }
-
- pub fn transport(&self) -> SdkTransportMode {
- self.client.transport()
- }
-
- pub fn signer(&self) -> SignerConfig {
- self.client.signer()
- }
-
- pub fn signer_sessions(&self) -> RadrootsdSignerSessionClient<'a> {
- RadrootsdSignerSessionClient {
- client: self.client,
- }
- }
-
- pub fn bridge(&self) -> RadrootsdBridgeClient<'a> {
- RadrootsdBridgeClient {
- client: self.client,
- }
- }
-}
-
-#[cfg(feature = "radrootsd-client")]
-#[derive(Debug, Clone, Copy)]
-pub struct RadrootsdSignerSessionClient<'a> {
- client: &'a RadrootsSdkClient,
-}
-
-#[cfg(feature = "radrootsd-client")]
-impl<'a> RadrootsdSignerSessionClient<'a> {
- pub fn sdk(&self) -> &'a RadrootsSdkClient {
- self.client
- }
-
- pub fn transport(&self) -> SdkTransportMode {
- self.client.transport()
- }
-
- pub fn signer(&self) -> SignerConfig {
- self.client.signer()
- }
-
- pub async fn connect(
- &self,
- request: &radrootsd::SdkRadrootsdSignerSessionConnectRequest,
- ) -> Result<SdkRadrootsdSignerSessionHandle, SdkRadrootsdSessionError> {
- self.client.connect_radrootsd_signer_session(request).await
- }
-
- pub async fn connect_bunker(
- &self,
- url: impl Into<String>,
- ) -> Result<SdkRadrootsdSignerSessionHandle, SdkRadrootsdSessionError> {
- let request = radrootsd::SdkRadrootsdSignerSessionConnectRequest::bunker(url);
- self.connect(&request).await
- }
-
- pub async fn connect_nostrconnect(
- &self,
- url: impl Into<String>,
- client_secret_key: impl Into<String>,
- ) -> Result<SdkRadrootsdSignerSessionHandle, SdkRadrootsdSessionError> {
- let request = radrootsd::SdkRadrootsdSignerSessionConnectRequest::nostrconnect(
- url,
- client_secret_key,
- );
- self.connect(&request).await
- }
-
- pub async fn status(
- &self,
- session: &SdkRadrootsdSignerSessionRef,
- ) -> Result<SdkRadrootsdSignerSessionView, SdkRadrootsdSessionError> {
- self.client.radrootsd_signer_session_status(session).await
- }
-
- pub async fn list(
- &self,
- ) -> Result<Vec<SdkRadrootsdSignerSessionView>, SdkRadrootsdSessionError> {
- self.client.radrootsd_list_signer_sessions().await
- }
-
- pub async fn authorize(
- &self,
- session: &SdkRadrootsdSignerSessionRef,
- ) -> Result<SdkRadrootsdSignerSessionAuthorizeResult, SdkRadrootsdSessionError> {
- self.client
- .authorize_radrootsd_signer_session(session)
- .await
- }
-
- pub async fn get_public_key(
- &self,
- session: &SdkRadrootsdSignerSessionRef,
- ) -> Result<SdkRadrootsdSignerSessionPublicKeyResult, SdkRadrootsdSessionError> {
- self.client
- .get_radrootsd_signer_session_public_key(session)
- .await
- }
-
- pub async fn require_auth(
- &self,
- session: &SdkRadrootsdSignerSessionRef,
- auth_url: impl AsRef<str>,
- ) -> Result<SdkRadrootsdSignerSessionRequireAuthResult, SdkRadrootsdSessionError> {
- self.client
- .require_radrootsd_signer_session_auth(session, auth_url.as_ref())
- .await
- }
-
- pub async fn close(
- &self,
- session: &SdkRadrootsdSignerSessionRef,
- ) -> Result<SdkRadrootsdSignerSessionCloseResult, SdkRadrootsdSessionError> {
- self.client.close_radrootsd_signer_session(session).await
- }
-}
-
-#[cfg(feature = "radrootsd-client")]
-#[derive(Debug, Clone, Copy)]
-pub struct RadrootsdBridgeClient<'a> {
- client: &'a RadrootsSdkClient,
-}
-
-#[cfg(feature = "radrootsd-client")]
-impl<'a> RadrootsdBridgeClient<'a> {
- pub fn sdk(&self) -> &'a RadrootsSdkClient {
- self.client
- }
-
- pub fn transport(&self) -> SdkTransportMode {
- self.client.transport()
- }
-
- pub fn signer(&self) -> SignerConfig {
- self.client.signer()
- }
-
- pub async fn status(&self) -> Result<SdkRadrootsdBridgeStatus, SdkRadrootsdBridgeError> {
- self.client.radrootsd_bridge_status().await
- }
-
- pub async fn job(
- &self,
- job: &SdkRadrootsdBridgeJobRef,
- ) -> Result<SdkRadrootsdBridgeJobView, SdkRadrootsdBridgeError> {
- self.client.radrootsd_bridge_job_status(job).await
- }
-
- pub async fn jobs(&self) -> Result<Vec<SdkRadrootsdBridgeJobView>, SdkRadrootsdBridgeError> {
- self.client.radrootsd_bridge_jobs().await
- }
-}
-
-#[derive(Debug, Clone, Copy)]
-pub struct ProfileClient<'a> {
- client: &'a RadrootsSdkClient,
-}
-
-impl<'a> ProfileClient<'a> {
- pub fn sdk(&self) -> &'a RadrootsSdkClient {
- self.client
- }
-
- pub fn transport(&self) -> SdkTransportMode {
- self.client.transport()
- }
-
- pub fn signer(&self) -> SignerConfig {
- self.client.signer()
- }
-
- #[cfg(feature = "serde_json")]
- pub fn build_draft(
- &self,
- profile_value: &RadrootsProfile,
- profile_type: Option<RadrootsProfileType>,
- ) -> Result<WireEventParts, profile::ProfileEncodeError> {
- profile::build_draft(profile_value, profile_type)
- }
-
- #[cfg(all(
- feature = "identity-models",
- feature = "relay-client",
- feature = "signing"
- ))]
- pub async fn publish_with_identity(
- &self,
- identity: &RadrootsIdentity,
- profile_value: &RadrootsProfile,
- profile_type: Option<RadrootsProfileType>,
- ) -> Result<SdkPublishReceipt, SdkPublishError> {
- let parts = profile::build_draft(profile_value, profile_type)
- .map_err(|err| SdkPublishError::Encode(err.to_string()))?;
- self.client
- .publish_parts_via_relay_with_identity(identity, parts, "profile.publish_with_identity")
- .await
- }
-
- #[cfg(all(
- feature = "identity-models",
- feature = "relay-client",
- feature = "signing"
- ))]
- pub async fn publish_draft_with_identity(
- &self,
- identity: &RadrootsIdentity,
- draft: WireEventParts,
- ) -> Result<SdkPublishReceipt, SdkPublishError> {
- self.client
- .publish_parts_via_relay_with_identity(
- identity,
- draft,
- "profile.publish_draft_with_identity",
- )
- .await
- }
-
- #[cfg(feature = "radrootsd-client")]
- pub async fn publish_profile_via_radrootsd(
- &self,
- profile_value: &RadrootsProfile,
- profile_type: Option<RadrootsProfileType>,
- session: &SdkRadrootsdSignerSessionHandle,
- ) -> Result<SdkPublishReceipt, SdkPublishError> {
- self.publish_profile_via_radrootsd_with_options(
- profile_value,
- profile_type,
- &SdkRadrootsdProfilePublishOptions::from_signer_session(session),
- )
- .await
- }
-
- #[cfg(feature = "radrootsd-client")]
- pub async fn publish_profile_via_radrootsd_with_options(
- &self,
- profile_value: &RadrootsProfile,
- profile_type: Option<RadrootsProfileType>,
- options: &SdkRadrootsdProfilePublishOptions,
- ) -> Result<SdkPublishReceipt, SdkPublishError> {
- let request = radrootsd::SdkRadrootsdProfilePublishRequest {
- profile: profile_value.clone(),
- profile_type,
- signer_session_id: options.session().session_id().to_owned(),
- signer_authority: options.signer_authority().cloned(),
- idempotency_key: options.idempotency_key().map(str::to_owned),
- };
- self.client.publish_profile_via_radrootsd(&request).await
- }
-}
-
-#[derive(Debug, Clone, Copy)]
-pub struct FarmClient<'a> {
- client: &'a RadrootsSdkClient,
-}
-
-impl<'a> FarmClient<'a> {
- pub fn sdk(&self) -> &'a RadrootsSdkClient {
- self.client
- }
-
- pub fn transport(&self) -> SdkTransportMode {
- self.client.transport()
- }
-
- pub fn signer(&self) -> SignerConfig {
- self.client.signer()
- }
-
- #[cfg(feature = "serde_json")]
- pub fn build_draft(
- &self,
- farm_value: &farm::RadrootsFarm,
- ) -> Result<WireEventParts, farm::EventEncodeError> {
- farm::build_draft(farm_value)
- }
-
- #[cfg(all(
- feature = "identity-models",
- feature = "relay-client",
- feature = "signing"
- ))]
- pub async fn publish_with_identity(
- &self,
- identity: &RadrootsIdentity,
- farm_value: &farm::RadrootsFarm,
- ) -> Result<SdkPublishReceipt, SdkPublishError> {
- let parts = farm::build_draft(farm_value)
- .map_err(|err| SdkPublishError::Encode(err.to_string()))?;
- self.client
- .publish_parts_via_relay_with_identity(identity, parts, "farm.publish_with_identity")
- .await
- }
-
- #[cfg(all(
- feature = "identity-models",
- feature = "relay-client",
- feature = "signing"
- ))]
- pub async fn publish_draft_with_identity(
- &self,
- identity: &RadrootsIdentity,
- draft: WireEventParts,
- ) -> Result<SdkPublishReceipt, SdkPublishError> {
- self.client
- .publish_parts_via_relay_with_identity(
- identity,
- draft,
- "farm.publish_draft_with_identity",
- )
- .await
- }
-
- #[cfg(feature = "radrootsd-client")]
- pub async fn publish_farm_via_radrootsd(
- &self,
- farm_value: &farm::RadrootsFarm,
- session: &SdkRadrootsdSignerSessionHandle,
- ) -> Result<SdkPublishReceipt, SdkPublishError> {
- self.publish_farm_via_radrootsd_with_options(
- farm_value,
- &SdkRadrootsdFarmPublishOptions::from_signer_session(session),
- )
- .await
- }
-
- #[cfg(feature = "radrootsd-client")]
- pub async fn publish_farm_via_radrootsd_with_options(
- &self,
- farm_value: &farm::RadrootsFarm,
- options: &SdkRadrootsdFarmPublishOptions,
- ) -> Result<SdkPublishReceipt, SdkPublishError> {
- let request = radrootsd::SdkRadrootsdFarmPublishRequest {
- farm: farm_value.clone(),
- kind: Some(KIND_FARM),
- signer_session_id: options.session().session_id().to_owned(),
- signer_authority: options.signer_authority().cloned(),
- idempotency_key: options.idempotency_key().map(str::to_owned),
- };
- self.client.publish_farm_via_radrootsd(&request).await
- }
-}
-
-#[derive(Debug, Clone, Copy)]
-pub struct ListingClient<'a> {
- client: &'a RadrootsSdkClient,
-}
-
-impl<'a> ListingClient<'a> {
- pub fn sdk(&self) -> &'a RadrootsSdkClient {
- self.client
- }
-
- pub fn transport(&self) -> SdkTransportMode {
- self.client.transport()
- }
-
- pub fn signer(&self) -> SignerConfig {
- self.client.signer()
- }
-
- pub fn build_tags(
- &self,
- listing_value: &listing::RadrootsListing,
- ) -> Result<NostrTags, listing::EventEncodeError> {
- listing::build_tags(listing_value)
- }
-
- #[cfg(feature = "serde_json")]
- pub fn build_draft(
- &self,
- listing_value: &listing::RadrootsListing,
- ) -> Result<listing::RadrootsListingDraft, listing::EventEncodeError> {
- listing::build_draft(listing_value)
- }
-
- #[cfg(feature = "serde_json")]
- pub fn parse_event(
- &self,
- event: &RadrootsNostrEvent,
- ) -> Result<listing::RadrootsListing, listing::RadrootsListingParseError> {
- listing::parse_event(event)
- }
-
- #[cfg(all(
- feature = "identity-models",
- feature = "relay-client",
- feature = "signing"
- ))]
- pub async fn publish_with_identity(
- &self,
- identity: &RadrootsIdentity,
- listing_value: &listing::RadrootsListing,
- ) -> Result<SdkPublishReceipt, SdkPublishError> {
- let parts = listing::build_draft(listing_value)
- .map_err(|err| SdkPublishError::Encode(err.to_string()))?
- .into_wire_parts();
- self.client
- .publish_parts_via_relay_with_identity(identity, parts, "listing.publish_with_identity")
- .await
- }
-
- #[cfg(all(
- feature = "identity-models",
- feature = "relay-client",
- feature = "signing"
- ))]
- pub async fn publish_draft_with_identity(
- &self,
- identity: &RadrootsIdentity,
- draft: listing::RadrootsListingDraft,
- ) -> Result<SdkPublishReceipt, SdkPublishError> {
- self.client
- .publish_parts_via_relay_with_identity(
- identity,
- draft.into_wire_parts(),
- "listing.publish_draft_with_identity",
- )
- .await
- }
-
- #[cfg(feature = "radrootsd-client")]
- pub async fn publish_listing_via_radrootsd(
- &self,
- listing_value: &listing::RadrootsListing,
- session: &SdkRadrootsdSignerSessionHandle,
- ) -> Result<SdkPublishReceipt, SdkPublishError> {
- self.publish_listing_via_radrootsd_with_options(
- listing_value,
- &SdkRadrootsdListingPublishOptions::from_signer_session(session),
- )
- .await
- }
-
- #[cfg(feature = "radrootsd-client")]
- pub async fn publish_listing_via_radrootsd_with_options(
- &self,
- listing_value: &listing::RadrootsListing,
- options: &SdkRadrootsdListingPublishOptions,
- ) -> Result<SdkPublishReceipt, SdkPublishError> {
- let request = radrootsd::SdkRadrootsdListingPublishRequest {
- listing: listing_value.clone(),
- kind: Some(KIND_LISTING),
- signer_session_id: options.session().session_id().to_owned(),
- signer_authority: options.signer_authority().cloned(),
- idempotency_key: options.idempotency_key().map(str::to_owned),
- };
- self.client.publish_listing_via_radrootsd(&request).await
- }
-
- #[cfg(feature = "radrootsd-client")]
- pub async fn publish_draft_via_radrootsd(
- &self,
- draft: listing::RadrootsListingDraft,
- session: &SdkRadrootsdSignerSessionHandle,
- ) -> Result<SdkPublishReceipt, SdkPublishError> {
- self.publish_draft_via_radrootsd_with_options(
- draft,
- &SdkRadrootsdListingPublishOptions::from_signer_session(session),
- )
- .await
- }
-
- #[cfg(feature = "radrootsd-client")]
- pub async fn publish_draft_via_radrootsd_with_options(
- &self,
- draft: listing::RadrootsListingDraft,
- options: &SdkRadrootsdListingPublishOptions,
- ) -> Result<SdkPublishReceipt, SdkPublishError> {
- let parts = draft.into_wire_parts();
- let event = RadrootsNostrEvent {
- id: String::new(),
- author: String::new(),
- created_at: 0,
- kind: parts.kind,
- tags: parts.tags,
- content: parts.content,
- sig: String::new(),
- };
- let request = radrootsd::SdkRadrootsdListingPublishRequest::from_event(
- &event,
- options.session().session_id().to_owned(),
- options.signer_authority().cloned(),
- options.idempotency_key().map(str::to_owned),
- )
- .map_err(|err| SdkPublishError::Encode(err.to_string()))?;
- self.client.publish_listing_via_radrootsd(&request).await
- }
-}
-
-#[derive(Debug, Clone, Copy)]
-pub struct TradeClient<'a> {
- client: &'a RadrootsSdkClient,
-}
-
-impl<'a> TradeClient<'a> {
- pub fn sdk(&self) -> &'a RadrootsSdkClient {
- self.client
- }
-
- pub fn transport(&self) -> SdkTransportMode {
- self.client.transport()
- }
-
- pub fn signer(&self) -> SignerConfig {
- self.client.signer()
- }
-
- #[cfg(feature = "serde_json")]
- pub fn parse_listing_address(
- &self,
- listing_addr: &str,
- ) -> Result<order::RadrootsOrderListingAddress, order::RadrootsOrderListingAddressError> {
- order::parse_listing_address(listing_addr)
- }
-
- #[cfg(feature = "serde_json")]
- pub fn validate_listing_event(
- &self,
- event: &RadrootsNostrEvent,
- ) -> Result<TradeListingValidateResult, order::RadrootsTradeValidationListingError> {
- order::validate_listing_event(event)
- }
-
- #[cfg(feature = "serde_json")]
- pub fn build_order_request_draft(
- &self,
- listing_event: &RadrootsNostrEventPtr,
- payload: &order::RadrootsOrderRequest,
- ) -> Result<order::RadrootsOrderRequestDraft, order::EventEncodeError> {
- order::build_order_request_draft(listing_event, payload)
- }
-
- #[cfg(feature = "serde_json")]
- pub fn build_order_decision_draft(
- &self,
- root_event_id: &RadrootsEventId,
- prev_event_id: &RadrootsEventId,
- payload: &order::RadrootsOrderDecision,
- ) -> Result<order::RadrootsOrderDecisionDraft, order::EventEncodeError> {
- order::build_order_decision_draft(root_event_id, prev_event_id, payload)
- }
-
- #[cfg(feature = "serde_json")]
- pub fn build_order_revision_proposal_draft(
- &self,
- root_event_id: &RadrootsEventId,
- prev_event_id: &RadrootsEventId,
- payload: &order::RadrootsOrderRevisionProposal,
- ) -> Result<order::RadrootsOrderRevisionProposalDraft, order::EventEncodeError> {
- order::build_order_revision_proposal_draft(root_event_id, prev_event_id, payload)
- }
-
- #[cfg(feature = "serde_json")]
- pub fn build_order_revision_decision_draft(
- &self,
- root_event_id: &RadrootsEventId,
- prev_event_id: &RadrootsEventId,
- payload: &order::RadrootsOrderRevisionDecision,
- ) -> Result<order::RadrootsOrderRevisionDecisionDraft, order::EventEncodeError> {
- order::build_order_revision_decision_draft(root_event_id, prev_event_id, payload)
- }
-
- #[cfg(feature = "serde_json")]
- pub fn build_fulfillment_update_draft(
- &self,
- root_event_id: &RadrootsEventId,
- prev_event_id: &RadrootsEventId,
- payload: &order::RadrootsOrderFulfillmentUpdate,
- ) -> Result<order::RadrootsOrderFulfillmentUpdateDraft, order::EventEncodeError> {
- order::build_fulfillment_update_draft(root_event_id, prev_event_id, payload)
- }
-
- #[cfg(feature = "serde_json")]
- pub fn build_order_cancellation_draft(
- &self,
- root_event_id: &RadrootsEventId,
- prev_event_id: &RadrootsEventId,
- payload: &order::RadrootsOrderCancellation,
- ) -> Result<order::RadrootsOrderCancellationDraft, order::EventEncodeError> {
- order::build_order_cancellation_draft(root_event_id, prev_event_id, payload)
- }
-
- #[cfg(feature = "serde_json")]
- pub fn build_buyer_receipt_draft(
- &self,
- root_event_id: &RadrootsEventId,
- prev_event_id: &RadrootsEventId,
- payload: &order::RadrootsOrderReceipt,
- ) -> Result<order::RadrootsOrderReceiptDraft, order::EventEncodeError> {
- order::build_buyer_receipt_draft(root_event_id, prev_event_id, payload)
- }
-
- #[cfg(feature = "serde_json")]
- pub fn parse_order_request(
- &self,
- event: &RadrootsNostrEvent,
- ) -> Result<
- order::RadrootsOrderEnvelope<order::RadrootsOrderRequest>,
- order::RadrootsOrderEnvelopeParseError,
- > {
- order::parse_order_request(event)
- }
-
- #[cfg(feature = "serde_json")]
- pub fn parse_order_decision(
- &self,
- event: &RadrootsNostrEvent,
- ) -> Result<
- order::RadrootsOrderEnvelope<order::RadrootsOrderDecision>,
- order::RadrootsOrderEnvelopeParseError,
- > {
- order::parse_order_decision(event)
- }
-
- #[cfg(feature = "serde_json")]
- pub fn parse_order_revision_proposal(
- &self,
- event: &RadrootsNostrEvent,
- ) -> Result<
- order::RadrootsOrderEnvelope<order::RadrootsOrderRevisionProposal>,
- order::RadrootsOrderEnvelopeParseError,
- > {
- order::parse_order_revision_proposal(event)
- }
-
- #[cfg(feature = "serde_json")]
- pub fn parse_order_revision_decision(
- &self,
- event: &RadrootsNostrEvent,
- ) -> Result<
- order::RadrootsOrderEnvelope<order::RadrootsOrderRevisionDecision>,
- order::RadrootsOrderEnvelopeParseError,
- > {
- order::parse_order_revision_decision(event)
- }
-
- #[cfg(feature = "serde_json")]
- pub fn parse_fulfillment_update(
- &self,
- event: &RadrootsNostrEvent,
- ) -> Result<
- order::RadrootsOrderEnvelope<order::RadrootsOrderFulfillmentUpdate>,
- order::RadrootsOrderEnvelopeParseError,
- > {
- order::parse_fulfillment_update(event)
- }
-
- #[cfg(feature = "serde_json")]
- pub fn parse_order_cancellation(
- &self,
- event: &RadrootsNostrEvent,
- ) -> Result<
- order::RadrootsOrderEnvelope<order::RadrootsOrderCancellation>,
- order::RadrootsOrderEnvelopeParseError,
- > {
- order::parse_order_cancellation(event)
- }
-
- #[cfg(feature = "serde_json")]
- pub fn parse_buyer_receipt(
- &self,
- event: &RadrootsNostrEvent,
- ) -> Result<
- order::RadrootsOrderEnvelope<order::RadrootsOrderReceipt>,
- order::RadrootsOrderEnvelopeParseError,
- > {
- order::parse_buyer_receipt(event)
- }
-
- #[cfg(all(
- feature = "identity-models",
- feature = "relay-client",
- feature = "signing"
- ))]
- pub async fn publish_order_request_with_identity(
- &self,
- identity: &RadrootsIdentity,
- listing_event: &RadrootsNostrEventPtr,
- payload: &order::RadrootsOrderRequest,
- ) -> Result<SdkPublishReceipt, SdkPublishError> {
- let draft = order::build_order_request_draft(listing_event, payload)
- .map_err(|err| SdkPublishError::Encode(err.to_string()))?;
- self.client
- .publish_parts_via_relay_with_identity(
- identity,
- draft.into_wire_parts(),
- "order.publish_order_request_with_identity",
- )
- .await
- }
-
- #[cfg(all(
- feature = "identity-models",
- feature = "relay-client",
- feature = "signing"
- ))]
- pub async fn publish_order_revision_proposal_with_identity(
- &self,
- identity: &RadrootsIdentity,
- root_event_id: &RadrootsEventId,
- prev_event_id: &RadrootsEventId,
- payload: &order::RadrootsOrderRevisionProposal,
- ) -> Result<SdkPublishReceipt, SdkPublishError> {
- let draft =
- order::build_order_revision_proposal_draft(root_event_id, prev_event_id, payload)
- .map_err(|err| SdkPublishError::Encode(err.to_string()))?;
- self.client
- .publish_parts_via_relay_with_identity(
- identity,
- draft.into_wire_parts(),
- "order.publish_order_revision_proposal_with_identity",
- )
- .await
- }
-
- #[cfg(all(
- feature = "identity-models",
- feature = "relay-client",
- feature = "signing"
- ))]
- pub async fn publish_order_revision_decision_with_identity(
- &self,
- identity: &RadrootsIdentity,
- root_event_id: &RadrootsEventId,
- prev_event_id: &RadrootsEventId,
- payload: &order::RadrootsOrderRevisionDecision,
- ) -> Result<SdkPublishReceipt, SdkPublishError> {
- let draft =
- order::build_order_revision_decision_draft(root_event_id, prev_event_id, payload)
- .map_err(|err| SdkPublishError::Encode(err.to_string()))?;
- self.client
- .publish_parts_via_relay_with_identity(
- identity,
- draft.into_wire_parts(),
- "order.publish_order_revision_decision_with_identity",
- )
- .await
- }
-
- #[cfg(all(
- feature = "identity-models",
- feature = "relay-client",
- feature = "signing"
- ))]
- pub async fn publish_order_decision_with_identity(
- &self,
- identity: &RadrootsIdentity,
- root_event_id: &RadrootsEventId,
- prev_event_id: &RadrootsEventId,
- payload: &order::RadrootsOrderDecision,
- ) -> Result<SdkPublishReceipt, SdkPublishError> {
- let draft = order::build_order_decision_draft(root_event_id, prev_event_id, payload)
- .map_err(|err| SdkPublishError::Encode(err.to_string()))?;
- self.client
- .publish_parts_via_relay_with_identity(
- identity,
- draft.into_wire_parts(),
- "order.publish_order_decision_with_identity",
- )
- .await
- }
-
- #[cfg(all(
- feature = "identity-models",
- feature = "relay-client",
- feature = "signing"
- ))]
- pub async fn publish_fulfillment_update_with_identity(
- &self,
- identity: &RadrootsIdentity,
- root_event_id: &RadrootsEventId,
- prev_event_id: &RadrootsEventId,
- payload: &order::RadrootsOrderFulfillmentUpdate,
- ) -> Result<SdkPublishReceipt, SdkPublishError> {
- let draft = order::build_fulfillment_update_draft(root_event_id, prev_event_id, payload)
- .map_err(|err| SdkPublishError::Encode(err.to_string()))?;
- self.client
- .publish_parts_via_relay_with_identity(
- identity,
- draft.into_wire_parts(),
- "order.publish_fulfillment_update_with_identity",
- )
- .await
- }
-
- #[cfg(all(
- feature = "identity-models",
- feature = "relay-client",
- feature = "signing"
- ))]
- pub async fn publish_order_revision_proposal_draft_with_identity(
- &self,
- identity: &RadrootsIdentity,
- draft: order::RadrootsOrderRevisionProposalDraft,
- ) -> Result<SdkPublishReceipt, SdkPublishError> {
- self.client
- .publish_parts_via_relay_with_identity(
- identity,
- draft.into_wire_parts(),
- "order.publish_order_revision_proposal_draft_with_identity",
- )
- .await
- }
-
- #[cfg(all(
- feature = "identity-models",
- feature = "relay-client",
- feature = "signing"
- ))]
- pub async fn publish_order_revision_decision_draft_with_identity(
- &self,
- identity: &RadrootsIdentity,
- draft: order::RadrootsOrderRevisionDecisionDraft,
- ) -> Result<SdkPublishReceipt, SdkPublishError> {
- self.client
- .publish_parts_via_relay_with_identity(
- identity,
- draft.into_wire_parts(),
- "order.publish_order_revision_decision_draft_with_identity",
- )
- .await
- }
-
- #[cfg(all(
- feature = "identity-models",
- feature = "relay-client",
- feature = "signing"
- ))]
- pub async fn publish_order_cancellation_with_identity(
- &self,
- identity: &RadrootsIdentity,
- root_event_id: &RadrootsEventId,
- prev_event_id: &RadrootsEventId,
- payload: &order::RadrootsOrderCancellation,
- ) -> Result<SdkPublishReceipt, SdkPublishError> {
- let draft = order::build_order_cancellation_draft(root_event_id, prev_event_id, payload)
- .map_err(|err| SdkPublishError::Encode(err.to_string()))?;
- self.client
- .publish_parts_via_relay_with_identity(
- identity,
- draft.into_wire_parts(),
- "order.publish_order_cancellation_with_identity",
- )
- .await
- }
-
- #[cfg(all(
- feature = "identity-models",
- feature = "relay-client",
- feature = "signing"
- ))]
- pub async fn publish_buyer_receipt_with_identity(
- &self,
- identity: &RadrootsIdentity,
- root_event_id: &RadrootsEventId,
- prev_event_id: &RadrootsEventId,
- payload: &order::RadrootsOrderReceipt,
- ) -> Result<SdkPublishReceipt, SdkPublishError> {
- let draft = order::build_buyer_receipt_draft(root_event_id, prev_event_id, payload)
- .map_err(|err| SdkPublishError::Encode(err.to_string()))?;
- self.client
- .publish_parts_via_relay_with_identity(
- identity,
- draft.into_wire_parts(),
- "order.publish_buyer_receipt_with_identity",
- )
- .await
- }
-
- #[cfg(all(
- feature = "identity-models",
- feature = "relay-client",
- feature = "signing"
- ))]
- pub async fn publish_order_request_draft_with_identity(
- &self,
- identity: &RadrootsIdentity,
- draft: order::RadrootsOrderRequestDraft,
- ) -> Result<SdkPublishReceipt, SdkPublishError> {
- self.client
- .publish_parts_via_relay_with_identity(
- identity,
- draft.into_wire_parts(),
- "order.publish_order_request_draft_with_identity",
- )
- .await
- }
-
- #[cfg(all(
- feature = "identity-models",
- feature = "relay-client",
- feature = "signing"
- ))]
- pub async fn publish_order_decision_draft_with_identity(
- &self,
- identity: &RadrootsIdentity,
- draft: order::RadrootsOrderDecisionDraft,
- ) -> Result<SdkPublishReceipt, SdkPublishError> {
- self.client
- .publish_parts_via_relay_with_identity(
- identity,
- draft.into_wire_parts(),
- "order.publish_order_decision_draft_with_identity",
- )
- .await
- }
-
- #[cfg(all(
- feature = "identity-models",
- feature = "relay-client",
- feature = "signing"
- ))]
- pub async fn publish_fulfillment_update_draft_with_identity(
- &self,
- identity: &RadrootsIdentity,
- draft: order::RadrootsOrderFulfillmentUpdateDraft,
- ) -> Result<SdkPublishReceipt, SdkPublishError> {
- self.client
- .publish_parts_via_relay_with_identity(
- identity,
- draft.into_wire_parts(),
- "order.publish_fulfillment_update_draft_with_identity",
- )
- .await
- }
-
- #[cfg(all(
- feature = "identity-models",
- feature = "relay-client",
- feature = "signing"
- ))]
- pub async fn publish_order_cancellation_draft_with_identity(
- &self,
- identity: &RadrootsIdentity,
- draft: order::RadrootsOrderCancellationDraft,
- ) -> Result<SdkPublishReceipt, SdkPublishError> {
- self.client
- .publish_parts_via_relay_with_identity(
- identity,
- draft.into_wire_parts(),
- "order.publish_order_cancellation_draft_with_identity",
- )
- .await
- }
-
- #[cfg(all(
- feature = "identity-models",
- feature = "relay-client",
- feature = "signing"
- ))]
- pub async fn publish_buyer_receipt_draft_with_identity(
- &self,
- identity: &RadrootsIdentity,
- draft: order::RadrootsOrderReceiptDraft,
- ) -> Result<SdkPublishReceipt, SdkPublishError> {
- self.client
- .publish_parts_via_relay_with_identity(
- identity,
- draft.into_wire_parts(),
- "order.publish_buyer_receipt_draft_with_identity",
- )
- .await
- }
-
- #[cfg(feature = "radrootsd-client")]
- pub async fn publish_order_request_via_radrootsd(
- &self,
- order: &order::RadrootsOrderRequest,
- listing_event: &RadrootsNostrEventPtr,
- session: &SdkRadrootsdSignerSessionHandle,
- ) -> Result<SdkPublishReceipt, SdkPublishError> {
- self.publish_order_request_via_radrootsd_with_options(
- order,
- listing_event,
- &SdkRadrootsdOrderRequestPublishOptions::from_signer_session(session),
- )
- .await
- }
-
- #[cfg(feature = "radrootsd-client")]
- pub async fn publish_order_request_via_radrootsd_with_options(
- &self,
- order: &order::RadrootsOrderRequest,
- listing_event: &RadrootsNostrEventPtr,
- options: &SdkRadrootsdOrderRequestPublishOptions,
- ) -> Result<SdkPublishReceipt, SdkPublishError> {
- let request = radrootsd::SdkRadrootsdOrderRequestPublishRequest {
- order: order.clone(),
- listing_event: listing_event.clone(),
- signer_session_id: options.session().session_id().to_owned(),
- signer_authority: options.signer_authority().cloned(),
- idempotency_key: options.idempotency_key().map(str::to_owned),
- };
- self.client
- .publish_order_request_via_radrootsd(&request)
- .await
- }
-}
-
-#[cfg(all(
- feature = "identity-models",
- feature = "relay-client",
- feature = "signing"
-))]
-fn sdk_publish_receipt_from_relay_output(
- signed_event: signing::SignedNostrEvent,
- target_relays: Vec<String>,
- connected_relays: Vec<String>,
- output: relay::RelayOutput<relay::RelayEventId>,
-) -> Result<SdkPublishReceipt, SdkPublishError> {
- let event = sdk_event_from_signed_event(&signed_event);
- let event_id = event.id.clone();
- let event_kind = event.kind;
- let created_at = event.created_at;
- let signature = event.sig.clone();
- let target_relays = sorted_unique_strings(target_relays);
- let connected_relays = sorted_unique_strings(connected_relays);
- let mut acknowledged_relays = output
- .success
- .into_iter()
- .map(|relay| relay.to_string())
- .collect::<Vec<_>>();
- acknowledged_relays = sorted_unique_strings(acknowledged_relays);
-
- let mut failed_relays = output
- .failed
- .into_iter()
- .map(|(relay_url, error)| SdkRelayFailure {
- relay_url: relay_url.to_string(),
- error,
- })
- .collect::<Vec<_>>();
- failed_relays.sort_by(|left, right| left.relay_url.cmp(&right.relay_url));
-
- if acknowledged_relays.is_empty() {
- return Err(SdkPublishError::RelayNotAcknowledged {
- transport: SdkTransportMode::RelayDirect,
- failed_relays,
- });
- }
-
- Ok(SdkPublishReceipt {
- transport: SdkTransportMode::RelayDirect,
- event_kind: Some(event_kind),
- event_id: Some(event_id.clone()),
- transport_receipt: SdkTransportReceipt::RelayDirect(SdkRelayPublishReceipt {
- event,
- event_id,
- event_kind,
- created_at,
- signature,
- target_relays,
- connected_relays,
- acknowledged_relays,
- failed_relays,
- }),
- })
-}
-
-#[cfg(all(
- feature = "identity-models",
- feature = "relay-client",
- feature = "signing"
-))]
-fn sdk_event_from_signed_event(event: &signing::SignedNostrEvent) -> RadrootsNostrEvent {
- RadrootsNostrEvent {
- id: event.id.to_string(),
- author: event.pubkey.to_string(),
- created_at: u32::try_from(event.created_at.as_secs()).unwrap_or(u32::MAX),
- kind: event.kind.as_u16() as u32,
- tags: event
- .tags
- .iter()
- .map(|tag| tag.as_slice().to_vec())
- .collect(),
- content: event.content.clone(),
- sig: event.sig.to_string(),
- }
-}
-
-#[cfg(all(
- feature = "identity-models",
- feature = "relay-client",
- feature = "signing"
-))]
-fn sorted_unique_strings(mut values: Vec<String>) -> Vec<String> {
- values.sort();
- values.dedup();
- values
-}
-
-#[cfg(feature = "radrootsd-client")]
-fn sdk_publish_receipt_from_radrootsd_bridge_response(
- response: radrootsd::SdkRadrootsdBridgePublishResponse,
-) -> SdkPublishReceipt {
- let job = response.job;
- SdkPublishReceipt {
- transport: SdkTransportMode::Radrootsd,
- event_kind: Some(job.event_kind),
- event_id: job.event_id.clone(),
- transport_receipt: SdkTransportReceipt::Radrootsd(SdkRadrootsdPublishReceipt {
- accepted: true,
- deduplicated: response.deduplicated,
- job_id: Some(job.job_id),
- status: Some(job.status),
- signer_mode: Some(job.signer_mode),
- signer_session_id: job.signer_session_id,
- event_addr: job.event_addr,
- relay_count: Some(job.relay_count),
- acknowledged_relay_count: Some(job.acknowledged_relay_count),
- }),
- }
-}
-
-#[cfg(all(
- test,
- feature = "identity-models",
- feature = "relay-client",
- feature = "signing"
-))]
-mod tests {
- use super::{
- SdkPublishError, SdkRelayFailure, SdkTransportMode, sdk_publish_receipt_from_relay_output,
- };
- use crate::WireEventParts;
- use crate::adapters::relay::RelayOutput;
- use crate::adapters::signing::sign_parts_with_identity;
- use crate::identity::RadrootsIdentity;
- use radroots_nostr::prelude::RadrootsNostrEventId;
- use std::collections::{HashMap, HashSet};
-
- #[test]
- fn relay_output_maps_to_normalized_publish_receipt() {
- let identity = RadrootsIdentity::generate();
- let signed_event = sign_parts_with_identity(
- &identity,
- WireEventParts {
- kind: 30402,
- content: "listing".to_owned(),
- tags: vec![vec!["d".to_owned(), "AAAAAAAAAAAAAAAAAAAAAg".to_owned()]],
- },
- )
- .expect("signed event");
- let event_id = signed_event.id.to_string();
- let event_created_at = u32::try_from(signed_event.created_at.as_secs()).unwrap();
- let event_signature = signed_event.sig.to_string();
- let output = RelayOutput {
- val: RadrootsNostrEventId::parse(event_id.as_str()).expect("event id"),
- success: HashSet::from([
- nostr::RelayUrl::parse("ws://127.0.0.1:8080").expect("relay a"),
- nostr::RelayUrl::parse("ws://127.0.0.1:8081").expect("relay b"),
- ]),
- failed: HashMap::from([(
- nostr::RelayUrl::parse("ws://127.0.0.1:8082").expect("relay c"),
- "timeout".to_owned(),
- )]),
- };
-
- let receipt = sdk_publish_receipt_from_relay_output(
- signed_event,
- vec![
- "ws://127.0.0.1:8081".to_owned(),
- "ws://127.0.0.1:8080".to_owned(),
- ],
- vec!["ws://127.0.0.1:8080".to_owned()],
- output,
- )
- .expect("receipt");
-
- assert_eq!(receipt.transport, SdkTransportMode::RelayDirect);
- assert_eq!(receipt.event_kind, Some(30402));
- assert_eq!(receipt.event_id, Some(event_id.clone()));
- let relay_receipt = match receipt.transport_receipt {
- super::SdkTransportReceipt::RelayDirect(relay_receipt) => relay_receipt,
- super::SdkTransportReceipt::Radrootsd(_) => panic!("unexpected radrootsd receipt"),
- };
- assert_eq!(relay_receipt.event.id, event_id);
- assert_eq!(relay_receipt.event_id, relay_receipt.event.id);
- assert_eq!(relay_receipt.event_kind, 30402);
- assert_eq!(relay_receipt.created_at, event_created_at);
- assert_eq!(relay_receipt.signature, event_signature);
- assert_eq!(
- relay_receipt.target_relays,
- vec![
- "ws://127.0.0.1:8080".to_owned(),
- "ws://127.0.0.1:8081".to_owned(),
- ]
- );
- assert_eq!(
- relay_receipt.connected_relays,
- vec!["ws://127.0.0.1:8080".to_owned()]
- );
- }
-
- #[test]
- fn relay_output_without_acknowledgement_is_rejected() {
- let identity = RadrootsIdentity::generate();
- let signed_event = sign_parts_with_identity(
- &identity,
- WireEventParts {
- kind: 30402,
- content: "listing".to_owned(),
- tags: vec![],
- },
- )
- .expect("signed event");
- let output = RelayOutput {
- val: RadrootsNostrEventId::parse(signed_event.id.to_string().as_str())
- .expect("event id"),
- success: HashSet::new(),
- failed: HashMap::from([(
- nostr::RelayUrl::parse("ws://127.0.0.1:8082").expect("relay c"),
- "blocked".to_owned(),
- )]),
- };
-
- let error = sdk_publish_receipt_from_relay_output(signed_event, vec![], vec![], output)
- .expect_err("error");
-
- assert_eq!(
- error,
- SdkPublishError::RelayNotAcknowledged {
- transport: SdkTransportMode::RelayDirect,
- failed_relays: vec![SdkRelayFailure {
- relay_url: "ws://127.0.0.1:8082".to_owned(),
- error: "blocked".to_owned(),
- }],
- }
- );
- }
-}
diff --git a/crates/sdk/src/config.rs b/crates/sdk/src/config.rs
@@ -1,388 +0,0 @@
-#[cfg(not(feature = "std"))]
-use alloc::{string::String, vec::Vec};
-use core::fmt;
-#[cfg(feature = "std")]
-use std::{env, string::String, vec::Vec};
-
-pub const RADROOTS_SDK_PRODUCTION_RELAY_URL: &str = "wss://radroots.org";
-pub const RADROOTS_SDK_STAGING_RELAY_URL: &str = "wss://staging.radroots.org";
-pub const RADROOTS_SDK_LOCAL_RELAY_URL: &str = "ws://127.0.0.1:8080";
-
-pub const RADROOTS_SDK_PRODUCTION_RADROOTSD_ENDPOINT: &str = "https://rpc.radroots.org/jsonrpc";
-pub const RADROOTS_SDK_STAGING_RADROOTSD_ENDPOINT: &str =
- "https://rpc.staging.radroots.org/jsonrpc";
-pub const RADROOTS_SDK_LOCAL_RADROOTSD_ENDPOINT: &str = "http://127.0.0.1:7070";
-
-pub const RADROOTS_SDK_DEFAULT_TIMEOUT_MS: u64 = 10_000;
-
-#[cfg(feature = "std")]
-const LOCAL_RELAY_SCHEME_ENV: &str = "NOSTR_RS_RELAY_PUBLIC_SCHEME";
-#[cfg(feature = "std")]
-const LOCAL_RELAY_HOST_ENV: &str = "NOSTR_RS_RELAY_PUBLIC_HOST";
-#[cfg(feature = "std")]
-const LOCAL_RELAY_PORT_ENV: &str = "NOSTR_RS_RELAY_PUBLIC_PORT";
-#[cfg(feature = "std")]
-const LOCAL_RADROOTSD_ENDPOINT_ENV: &str = "RADROOTSD_RPC_URL";
-#[cfg(feature = "std")]
-const LOCAL_RADROOTSD_HOST_ENV: &str = "RADROOTSD_RPC_HOST";
-#[cfg(feature = "std")]
-const LOCAL_RADROOTSD_PORT_ENV: &str = "RADROOTSD_RPC_PORT";
-
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub struct RadrootsSdkConfig {
- pub environment: SdkEnvironment,
- pub transport: SdkTransportMode,
- pub relay: RelayConfig,
- pub radrootsd: RadrootsdConfig,
- pub signer: SignerConfig,
- pub network: NetworkConfig,
-}
-
-impl RadrootsSdkConfig {
- pub fn production() -> Self {
- Self::for_environment(SdkEnvironment::Production)
- }
-
- pub fn staging() -> Self {
- Self::for_environment(SdkEnvironment::Staging)
- }
-
- pub fn local() -> Self {
- Self::for_environment(SdkEnvironment::Local)
- }
-
- pub fn custom() -> Self {
- Self::for_environment(SdkEnvironment::Custom)
- }
-
- pub fn for_environment(environment: SdkEnvironment) -> Self {
- Self {
- environment,
- transport: SdkTransportMode::RelayDirect,
- relay: RelayConfig::default(),
- radrootsd: RadrootsdConfig::default(),
- signer: SignerConfig::default(),
- network: NetworkConfig::default(),
- }
- }
-
- pub fn resolved_relay_urls(&self) -> Result<Vec<String>, SdkConfigError> {
- self.relay.resolved_urls(self.environment)
- }
-
- pub fn resolved_radrootsd_endpoint(&self) -> Result<String, SdkConfigError> {
- self.radrootsd.resolved_endpoint(self.environment)
- }
-}
-
-impl Default for RadrootsSdkConfig {
- fn default() -> Self {
- Self::production()
- }
-}
-
-#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-pub enum SdkEnvironment {
- Production,
- Staging,
- Local,
- Custom,
-}
-
-impl SdkEnvironment {
- pub fn default_relay_urls(self) -> Option<Vec<String>> {
- match self {
- Self::Production => Some(vec![RADROOTS_SDK_PRODUCTION_RELAY_URL.to_owned()]),
- Self::Staging => Some(vec![RADROOTS_SDK_STAGING_RELAY_URL.to_owned()]),
- Self::Local => Some(vec![RADROOTS_SDK_LOCAL_RELAY_URL.to_owned()]),
- Self::Custom => None,
- }
- }
-
- pub fn default_radrootsd_endpoint(self) -> Option<&'static str> {
- match self {
- Self::Production => Some(RADROOTS_SDK_PRODUCTION_RADROOTSD_ENDPOINT),
- Self::Staging => Some(RADROOTS_SDK_STAGING_RADROOTSD_ENDPOINT),
- Self::Local => Some(RADROOTS_SDK_LOCAL_RADROOTSD_ENDPOINT),
- Self::Custom => None,
- }
- }
-}
-
-#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-pub enum SdkTransportMode {
- RelayDirect,
- Radrootsd,
-}
-
-#[derive(Debug, Clone, PartialEq, Eq, Default)]
-pub struct RelayConfig {
- pub urls: Vec<String>,
-}
-
-impl RelayConfig {
- pub fn resolved_urls(
- &self,
- environment: SdkEnvironment,
- ) -> Result<Vec<String>, SdkConfigError> {
- if self.urls.is_empty() {
- if environment == SdkEnvironment::Local {
- #[cfg(feature = "std")]
- if let Some(local_url) = resolve_local_relay_url_from_env() {
- return Ok(vec![normalize_relay_url(local_url.as_str())?]);
- }
- }
- return environment
- .default_relay_urls()
- .ok_or(SdkConfigError::MissingCustomRelayUrls);
- }
-
- normalize_relay_urls(&self.urls)
- }
-}
-
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub struct RadrootsdConfig {
- pub endpoint: Option<String>,
- pub auth: RadrootsdAuth,
-}
-
-impl RadrootsdConfig {
- pub fn resolved_endpoint(&self, environment: SdkEnvironment) -> Result<String, SdkConfigError> {
- match self.endpoint.as_deref() {
- Some(endpoint) => normalize_radrootsd_endpoint(endpoint),
- None => {
- if environment == SdkEnvironment::Local {
- #[cfg(feature = "std")]
- if let Some(endpoint) = resolve_local_radrootsd_endpoint_from_env() {
- return normalize_radrootsd_endpoint(endpoint.as_str());
- }
- }
-
- environment
- .default_radrootsd_endpoint()
- .map(str::to_owned)
- .ok_or(SdkConfigError::MissingCustomRadrootsdEndpoint)
- }
- }
- }
-}
-
-impl Default for RadrootsdConfig {
- fn default() -> Self {
- Self {
- endpoint: None,
- auth: RadrootsdAuth::default(),
- }
- }
-}
-
-#[derive(Clone, PartialEq, Eq, Default)]
-pub enum RadrootsdAuth {
- #[default]
- None,
- BearerToken(String),
-}
-
-impl fmt::Debug for RadrootsdAuth {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- match self {
- Self::None => f.write_str("None"),
- Self::BearerToken(_) => f.write_str("BearerToken(\"<redacted>\")"),
- }
- }
-}
-
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
-pub enum SignerConfig {
- #[default]
- DraftOnly,
- LocalIdentity,
- Nip46,
-}
-
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub struct NetworkConfig {
- pub timeout_ms: u64,
-}
-
-impl Default for NetworkConfig {
- fn default() -> Self {
- Self {
- timeout_ms: RADROOTS_SDK_DEFAULT_TIMEOUT_MS,
- }
- }
-}
-
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub enum SdkConfigError {
- MissingCustomRelayUrls,
- MissingCustomRadrootsdEndpoint,
- EmptyRelayUrl,
- InvalidRelayUrl(String),
- EmptyRadrootsdEndpoint,
- InvalidRadrootsdEndpoint(String),
-}
-
-impl fmt::Display for SdkConfigError {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- match self {
- Self::MissingCustomRelayUrls => {
- f.write_str("custom sdk environment requires explicit relay urls")
- }
- Self::MissingCustomRadrootsdEndpoint => {
- f.write_str("custom sdk environment requires an explicit radrootsd endpoint")
- }
- Self::EmptyRelayUrl => f.write_str("relay url must not be empty"),
- Self::InvalidRelayUrl(value) => {
- write!(f, "relay url must use ws or wss, got `{value}`")
- }
- Self::EmptyRadrootsdEndpoint => f.write_str("radrootsd endpoint must not be empty"),
- Self::InvalidRadrootsdEndpoint(value) => {
- write!(
- f,
- "radrootsd endpoint must use http or https, got `{value}`"
- )
- }
- }
- }
-}
-
-#[cfg(feature = "std")]
-impl std::error::Error for SdkConfigError {}
-
-impl fmt::Display for SignerConfig {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- match self {
- Self::DraftOnly => f.write_str("draft_only"),
- Self::LocalIdentity => f.write_str("local_identity"),
- Self::Nip46 => f.write_str("nip46"),
- }
- }
-}
-
-fn normalize_relay_urls(values: &[String]) -> Result<Vec<String>, SdkConfigError> {
- let mut normalized = Vec::new();
- for value in values {
- let relay = normalize_relay_url(value.as_str())?;
- if !normalized.iter().any(|existing| existing == &relay) {
- normalized.push(relay);
- }
- }
- Ok(normalized)
-}
-
-fn normalize_relay_url(value: &str) -> Result<String, SdkConfigError> {
- let trimmed = value.trim();
- if trimmed.is_empty() {
- return Err(SdkConfigError::EmptyRelayUrl);
- }
-
- let rest = if let Some(rest) = trimmed.strip_prefix("ws://") {
- rest
- } else if let Some(rest) = trimmed.strip_prefix("wss://") {
- rest
- } else {
- return Err(SdkConfigError::InvalidRelayUrl(trimmed.to_owned()));
- };
-
- if relay_authority_is_invalid(rest) {
- return Err(SdkConfigError::InvalidRelayUrl(trimmed.to_owned()));
- }
-
- Ok(trimmed.to_owned())
-}
-
-fn relay_authority_is_invalid(rest: &str) -> bool {
- let authority_end = rest
- .char_indices()
- .find(|(_, ch)| matches!(ch, '/' | '?' | '#'))
- .map(|(index, _)| index)
- .unwrap_or(rest.len());
- let authority = &rest[..authority_end];
-
- if authority.is_empty() || authority.chars().any(char::is_whitespace) {
- return true;
- }
- if authority.contains('@') {
- return true;
- }
-
- if let Some(after_open) = authority.strip_prefix('[') {
- let Some(close_index) = after_open.find(']') else {
- return true;
- };
- let host = &after_open[..close_index];
- let after_host = &after_open[close_index + 1..];
- if host.is_empty() {
- return true;
- }
- return relay_port_suffix_is_invalid(after_host);
- }
-
- let colon_count = authority.bytes().filter(|byte| *byte == b':').count();
- match colon_count {
- 0 => false,
- 1 => {
- let (host, port) = authority
- .split_once(':')
- .expect("one colon in relay authority");
- host.is_empty() || relay_port_is_invalid(port)
- }
- _ => true,
- }
-}
-
-fn relay_port_suffix_is_invalid(after_host: &str) -> bool {
- if after_host.is_empty() {
- return false;
- }
- let Some(port) = after_host.strip_prefix(':') else {
- return true;
- };
- relay_port_is_invalid(port)
-}
-
-fn relay_port_is_invalid(port: &str) -> bool {
- port.is_empty() || !port.bytes().all(|byte| byte.is_ascii_digit())
-}
-
-fn normalize_radrootsd_endpoint(value: &str) -> Result<String, SdkConfigError> {
- let trimmed = value.trim();
- if trimmed.is_empty() {
- return Err(SdkConfigError::EmptyRadrootsdEndpoint);
- }
- if !(trimmed.starts_with("http://") || trimmed.starts_with("https://")) {
- return Err(SdkConfigError::InvalidRadrootsdEndpoint(trimmed.to_owned()));
- }
- Ok(trimmed.to_owned())
-}
-
-#[cfg(feature = "std")]
-fn resolve_local_relay_url_from_env() -> Option<String> {
- let scheme = read_trimmed_env(LOCAL_RELAY_SCHEME_ENV)?;
- let host = read_trimmed_env(LOCAL_RELAY_HOST_ENV)?;
- let port = read_trimmed_env(LOCAL_RELAY_PORT_ENV)?;
- Some(format!("{scheme}://{host}:{port}"))
-}
-
-#[cfg(feature = "std")]
-fn resolve_local_radrootsd_endpoint_from_env() -> Option<String> {
- if let Some(endpoint) = read_trimmed_env(LOCAL_RADROOTSD_ENDPOINT_ENV) {
- return Some(endpoint);
- }
-
- let host = read_trimmed_env(LOCAL_RADROOTSD_HOST_ENV)?;
- let port = read_trimmed_env(LOCAL_RADROOTSD_PORT_ENV)?;
- Some(format!("http://{host}:{port}"))
-}
-
-#[cfg(feature = "std")]
-fn read_trimmed_env(key: &str) -> Option<String> {
- let value = env::var(key).ok()?;
- let trimmed = value.trim();
- if trimmed.is_empty() {
- return None;
- }
- Some(trimmed.to_owned())
-}
diff --git a/crates/sdk/src/farm.rs b/crates/sdk/src/farm.rs
@@ -1,9 +0,0 @@
-pub use radroots_events::farm::*;
-pub use radroots_events_codec::error::EventEncodeError;
-
-use crate::WireEventParts;
-
-#[cfg(feature = "serde_json")]
-pub fn build_draft(farm: &RadrootsFarm) -> Result<WireEventParts, EventEncodeError> {
- radroots_events_codec::farm::encode::to_wire_parts(farm)
-}
diff --git a/crates/sdk/src/identity.rs b/crates/sdk/src/identity.rs
@@ -1,33 +0,0 @@
-pub use radroots_identity::{
- DEFAULT_IDENTITY_PATH, IdentityError, RADROOTS_USERNAME_MAX_LEN, RADROOTS_USERNAME_MIN_LEN,
- RADROOTS_USERNAME_REGEX, RadrootsIdentity, RadrootsIdentityFile, RadrootsIdentityId,
- RadrootsIdentityProfile, RadrootsIdentityPublic, RadrootsIdentitySecretKeyFormat,
- radroots_username_is_valid, radroots_username_normalize,
-};
-
-#[cfg(feature = "identity-storage")]
-pub use radroots_identity::{
- RADROOTS_ENCRYPTED_IDENTITY_DEFAULT_KEY_SLOT, RADROOTS_ENCRYPTED_IDENTITY_KEY_SUFFIX,
- RadrootsEncryptedIdentityFile, encrypted_identity_wrapping_key_path, load_encrypted_identity,
- load_encrypted_identity_with_key_slot, load_identity_profile, rotate_encrypted_identity,
- rotate_encrypted_identity_with_key_slot, store_encrypted_identity,
- store_encrypted_identity_with_key_slot, store_identity_profile,
-};
-
-#[cfg(all(feature = "identity-models", feature = "identity-storage"))]
-#[cfg(test)]
-mod tests {
- use super::{RadrootsEncryptedIdentityFile, RadrootsIdentity};
-
- #[test]
- fn encrypted_identity_file_round_trips() {
- let temp = tempfile::tempdir().expect("tempdir");
- let file = RadrootsEncryptedIdentityFile::new(temp.path().join("identity.enc.json"));
- let identity = RadrootsIdentity::generate();
-
- file.store(&identity).expect("store identity");
- let loaded = file.load().expect("load identity");
-
- assert_eq!(loaded.public_key_hex(), identity.public_key_hex());
- }
-}
diff --git a/crates/sdk/src/lib.rs b/crates/sdk/src/lib.rs
@@ -1,72 +0,0 @@
-#![cfg_attr(not(feature = "std"), no_std)]
-#![forbid(unsafe_code)]
-
-#[cfg(not(feature = "std"))]
-extern crate alloc;
-
-#[cfg(not(feature = "std"))]
-use alloc::{string::String, vec::Vec};
-#[cfg(feature = "std")]
-use std::{string::String, vec::Vec};
-
-#[cfg(any(
- feature = "radrootsd-client",
- feature = "signing",
- feature = "relay-client",
- feature = "signer-adapters"
-))]
-pub mod adapters;
-pub mod client;
-pub mod config;
-pub mod farm;
-#[cfg(feature = "identity-models")]
-pub mod identity;
-pub mod listing;
-pub mod order;
-pub mod profile;
-
-#[cfg(feature = "radrootsd-client")]
-pub use crate::adapters::radrootsd::{
- SdkRadrootsdBridgeDeliveryPolicy, SdkRadrootsdBridgeJobStatus,
- SdkRadrootsdBridgeRelayPublishResult, SdkRadrootsdSignerAuthority,
- SdkRadrootsdSignerSessionConnectRequest, SdkRadrootsdSignerSessionMode,
- SdkRadrootsdSignerSessionRole,
-};
-pub use crate::client::{
- FarmClient, ListingClient, ProfileClient, RadrootsSdkClient, SdkPublishError,
- SdkPublishReceipt, SdkRadrootsdPublishReceipt, SdkRelayFailure, SdkRelayPublishReceipt,
- SdkResolvedTransportTarget, SdkTransportReceipt, TradeClient,
-};
-#[cfg(feature = "radrootsd-client")]
-pub use crate::client::{
- RadrootsdBridgeClient, RadrootsdClient, RadrootsdSignerSessionClient, SdkRadrootsdBridgeError,
- SdkRadrootsdBridgeJobRef, SdkRadrootsdBridgeJobView, SdkRadrootsdBridgeStatus,
- SdkRadrootsdFarmPublishOptions, SdkRadrootsdListingPublishOptions,
- SdkRadrootsdOrderRequestPublishOptions, SdkRadrootsdProfilePublishOptions,
- SdkRadrootsdSessionError, SdkRadrootsdSignerSessionAuthorizeResult,
- SdkRadrootsdSignerSessionCloseResult, SdkRadrootsdSignerSessionHandle,
- SdkRadrootsdSignerSessionPublicKeyResult, SdkRadrootsdSignerSessionRef,
- SdkRadrootsdSignerSessionRequireAuthResult, SdkRadrootsdSignerSessionView,
-};
-pub use crate::config::{
- NetworkConfig, RADROOTS_SDK_LOCAL_RADROOTSD_ENDPOINT, RADROOTS_SDK_LOCAL_RELAY_URL,
- RADROOTS_SDK_PRODUCTION_RADROOTSD_ENDPOINT, RADROOTS_SDK_PRODUCTION_RELAY_URL,
- RADROOTS_SDK_STAGING_RADROOTSD_ENDPOINT, RADROOTS_SDK_STAGING_RELAY_URL, RadrootsSdkConfig,
- RadrootsdAuth, RadrootsdConfig, RelayConfig, SdkConfigError, SdkEnvironment, SdkTransportMode,
- SignerConfig,
-};
-pub use radroots_events::{
- RadrootsNostrEvent, RadrootsNostrEventPtr, RadrootsNostrEventRef,
- draft::{RadrootsFrozenEventDraft, RadrootsSignedNostrEvent},
- farm::RadrootsFarm,
- listing::RadrootsListing,
- profile::{RadrootsProfile, RadrootsProfileType},
-};
-#[cfg(feature = "serde_json")]
-pub use radroots_events_codec::order::{
- RadrootsOrderEnvelopeParseError, RadrootsOrderListingAddress, RadrootsOrderListingAddressError,
-};
-pub use radroots_events_codec::wire::WireEventParts;
-pub use radroots_trade::listing::validation::RadrootsTradeListing as TradeListingValidateResult;
-
-pub type NostrTags = Vec<Vec<String>>;
diff --git a/crates/sdk/src/listing.rs b/crates/sdk/src/listing.rs
@@ -1,40 +0,0 @@
-pub use radroots_events::listing::*;
-pub use radroots_events::order::RadrootsListingParseError;
-pub use radroots_events::trade_validation::RadrootsTradeValidationListingError;
-pub use radroots_events_codec::error::EventEncodeError;
-pub use radroots_trade::listing::validation::RadrootsTradeListing as TradeListingValidateResult;
-
-use crate::{NostrTags, RadrootsNostrEvent, WireEventParts};
-
-#[derive(Debug, Clone)]
-pub struct RadrootsListingDraft {
- parts: WireEventParts,
-}
-
-impl RadrootsListingDraft {
- pub fn as_wire_parts(&self) -> &WireEventParts {
- &self.parts
- }
-
- pub fn into_wire_parts(self) -> WireEventParts {
- self.parts
- }
-}
-
-pub fn build_tags(listing: &RadrootsListing) -> Result<NostrTags, EventEncodeError> {
- radroots_events_codec::listing::encode::listing_build_tags(listing)
-}
-
-#[cfg(feature = "serde_json")]
-pub fn build_draft(listing: &RadrootsListing) -> Result<RadrootsListingDraft, EventEncodeError> {
- Ok(RadrootsListingDraft {
- parts: radroots_events_codec::listing::encode::to_wire_parts(listing)?,
- })
-}
-
-#[cfg(feature = "serde_json")]
-pub fn parse_event(
- event: &RadrootsNostrEvent,
-) -> Result<RadrootsListing, RadrootsListingParseError> {
- radroots_trade::listing::parse_listing_event(event)
-}
diff --git a/crates/sdk/src/order.rs b/crates/sdk/src/order.rs
@@ -1,281 +0,0 @@
-pub use radroots_events::order::*;
-pub use radroots_events::trade_validation::*;
-pub use radroots_events_codec::error::EventEncodeError;
-#[cfg(feature = "serde_json")]
-pub use radroots_events_codec::order::{
- RadrootsOrderEnvelopeParseError, RadrootsOrderEventContext, RadrootsOrderListingAddress,
- RadrootsOrderListingAddressError,
-};
-pub use radroots_trade::listing::validation::RadrootsTradeListing as TradeListingValidateResult;
-
-use crate::{RadrootsNostrEvent, RadrootsNostrEventPtr, WireEventParts};
-use radroots_events::ids::RadrootsEventId;
-
-#[derive(Debug, Clone)]
-pub struct RadrootsOrderRequestDraft {
- parts: WireEventParts,
-}
-
-#[derive(Debug, Clone)]
-pub struct RadrootsOrderDecisionDraft {
- parts: WireEventParts,
-}
-
-#[derive(Debug, Clone)]
-pub struct RadrootsOrderRevisionProposalDraft {
- parts: WireEventParts,
-}
-
-#[derive(Debug, Clone)]
-pub struct RadrootsOrderRevisionDecisionDraft {
- parts: WireEventParts,
-}
-
-#[derive(Debug, Clone)]
-pub struct RadrootsOrderFulfillmentUpdateDraft {
- parts: WireEventParts,
-}
-
-#[derive(Debug, Clone)]
-pub struct RadrootsOrderCancellationDraft {
- parts: WireEventParts,
-}
-
-#[derive(Debug, Clone)]
-pub struct RadrootsOrderReceiptDraft {
- parts: WireEventParts,
-}
-
-impl RadrootsOrderRequestDraft {
- pub fn as_wire_parts(&self) -> &WireEventParts {
- &self.parts
- }
-
- pub fn into_wire_parts(self) -> WireEventParts {
- self.parts
- }
-}
-
-impl RadrootsOrderDecisionDraft {
- pub fn as_wire_parts(&self) -> &WireEventParts {
- &self.parts
- }
-
- pub fn into_wire_parts(self) -> WireEventParts {
- self.parts
- }
-}
-
-impl RadrootsOrderRevisionProposalDraft {
- pub fn as_wire_parts(&self) -> &WireEventParts {
- &self.parts
- }
-
- pub fn into_wire_parts(self) -> WireEventParts {
- self.parts
- }
-}
-
-impl RadrootsOrderRevisionDecisionDraft {
- pub fn as_wire_parts(&self) -> &WireEventParts {
- &self.parts
- }
-
- pub fn into_wire_parts(self) -> WireEventParts {
- self.parts
- }
-}
-
-impl RadrootsOrderFulfillmentUpdateDraft {
- pub fn as_wire_parts(&self) -> &WireEventParts {
- &self.parts
- }
-
- pub fn into_wire_parts(self) -> WireEventParts {
- self.parts
- }
-}
-
-impl RadrootsOrderCancellationDraft {
- pub fn as_wire_parts(&self) -> &WireEventParts {
- &self.parts
- }
-
- pub fn into_wire_parts(self) -> WireEventParts {
- self.parts
- }
-}
-
-impl RadrootsOrderReceiptDraft {
- pub fn as_wire_parts(&self) -> &WireEventParts {
- &self.parts
- }
-
- pub fn into_wire_parts(self) -> WireEventParts {
- self.parts
- }
-}
-
-#[cfg(feature = "serde_json")]
-pub fn build_order_request_draft(
- listing_event: &RadrootsNostrEventPtr,
- payload: &RadrootsOrderRequest,
-) -> Result<RadrootsOrderRequestDraft, EventEncodeError> {
- Ok(RadrootsOrderRequestDraft {
- parts: radroots_events_codec::order::order_request_event_build(listing_event, payload)?,
- })
-}
-
-#[cfg(feature = "serde_json")]
-pub fn build_order_decision_draft(
- root_event_id: &RadrootsEventId,
- prev_event_id: &RadrootsEventId,
- payload: &RadrootsOrderDecision,
-) -> Result<RadrootsOrderDecisionDraft, EventEncodeError> {
- Ok(RadrootsOrderDecisionDraft {
- parts: radroots_events_codec::order::order_decision_event_build(
- root_event_id,
- prev_event_id,
- payload,
- )?,
- })
-}
-
-#[cfg(feature = "serde_json")]
-pub fn build_order_revision_proposal_draft(
- root_event_id: &RadrootsEventId,
- prev_event_id: &RadrootsEventId,
- payload: &RadrootsOrderRevisionProposal,
-) -> Result<RadrootsOrderRevisionProposalDraft, EventEncodeError> {
- Ok(RadrootsOrderRevisionProposalDraft {
- parts: radroots_events_codec::order::order_revision_proposal_event_build(
- root_event_id,
- prev_event_id,
- payload,
- )?,
- })
-}
-
-#[cfg(feature = "serde_json")]
-pub fn build_order_revision_decision_draft(
- root_event_id: &RadrootsEventId,
- prev_event_id: &RadrootsEventId,
- payload: &RadrootsOrderRevisionDecision,
-) -> Result<RadrootsOrderRevisionDecisionDraft, EventEncodeError> {
- Ok(RadrootsOrderRevisionDecisionDraft {
- parts: radroots_events_codec::order::order_revision_decision_event_build(
- root_event_id,
- prev_event_id,
- payload,
- )?,
- })
-}
-
-#[cfg(feature = "serde_json")]
-pub fn build_fulfillment_update_draft(
- root_event_id: &RadrootsEventId,
- prev_event_id: &RadrootsEventId,
- payload: &RadrootsOrderFulfillmentUpdate,
-) -> Result<RadrootsOrderFulfillmentUpdateDraft, EventEncodeError> {
- Ok(RadrootsOrderFulfillmentUpdateDraft {
- parts: radroots_events_codec::order::order_fulfillment_update_event_build(
- root_event_id,
- prev_event_id,
- payload,
- )?,
- })
-}
-
-#[cfg(feature = "serde_json")]
-pub fn build_order_cancellation_draft(
- root_event_id: &RadrootsEventId,
- prev_event_id: &RadrootsEventId,
- payload: &RadrootsOrderCancellation,
-) -> Result<RadrootsOrderCancellationDraft, EventEncodeError> {
- Ok(RadrootsOrderCancellationDraft {
- parts: radroots_events_codec::order::order_cancellation_event_build(
- root_event_id,
- prev_event_id,
- payload,
- )?,
- })
-}
-
-#[cfg(feature = "serde_json")]
-pub fn build_buyer_receipt_draft(
- root_event_id: &RadrootsEventId,
- prev_event_id: &RadrootsEventId,
- payload: &RadrootsOrderReceipt,
-) -> Result<RadrootsOrderReceiptDraft, EventEncodeError> {
- Ok(RadrootsOrderReceiptDraft {
- parts: radroots_events_codec::order::order_receipt_event_build(
- root_event_id,
- prev_event_id,
- payload,
- )?,
- })
-}
-
-#[cfg(feature = "serde_json")]
-pub fn parse_order_request(
- event: &RadrootsNostrEvent,
-) -> Result<RadrootsOrderEnvelope<RadrootsOrderRequest>, RadrootsOrderEnvelopeParseError> {
- radroots_events_codec::order::order_request_from_event(event)
-}
-
-#[cfg(feature = "serde_json")]
-pub fn parse_order_decision(
- event: &RadrootsNostrEvent,
-) -> Result<RadrootsOrderEnvelope<RadrootsOrderDecision>, RadrootsOrderEnvelopeParseError> {
- radroots_events_codec::order::order_decision_from_event(event)
-}
-
-#[cfg(feature = "serde_json")]
-pub fn parse_order_revision_proposal(
- event: &RadrootsNostrEvent,
-) -> Result<RadrootsOrderEnvelope<RadrootsOrderRevisionProposal>, RadrootsOrderEnvelopeParseError> {
- radroots_events_codec::order::order_revision_proposal_from_event(event)
-}
-
-#[cfg(feature = "serde_json")]
-pub fn parse_order_revision_decision(
- event: &RadrootsNostrEvent,
-) -> Result<RadrootsOrderEnvelope<RadrootsOrderRevisionDecision>, RadrootsOrderEnvelopeParseError> {
- radroots_events_codec::order::order_revision_decision_from_event(event)
-}
-
-#[cfg(feature = "serde_json")]
-pub fn parse_fulfillment_update(
- event: &RadrootsNostrEvent,
-) -> Result<RadrootsOrderEnvelope<RadrootsOrderFulfillmentUpdate>, RadrootsOrderEnvelopeParseError>
-{
- radroots_events_codec::order::order_fulfillment_update_from_event(event)
-}
-
-#[cfg(feature = "serde_json")]
-pub fn parse_order_cancellation(
- event: &RadrootsNostrEvent,
-) -> Result<RadrootsOrderEnvelope<RadrootsOrderCancellation>, RadrootsOrderEnvelopeParseError> {
- radroots_events_codec::order::order_cancellation_from_event(event)
-}
-
-#[cfg(feature = "serde_json")]
-pub fn parse_buyer_receipt(
- event: &RadrootsNostrEvent,
-) -> Result<RadrootsOrderEnvelope<RadrootsOrderReceipt>, RadrootsOrderEnvelopeParseError> {
- radroots_events_codec::order::order_receipt_from_event(event)
-}
-
-#[cfg(feature = "serde_json")]
-pub fn parse_listing_address(
- listing_addr: &str,
-) -> Result<RadrootsOrderListingAddress, RadrootsOrderListingAddressError> {
- RadrootsOrderListingAddress::parse(listing_addr)
-}
-
-#[cfg(feature = "serde_json")]
-pub fn validate_listing_event(
- event: &RadrootsNostrEvent,
-) -> Result<TradeListingValidateResult, RadrootsTradeValidationListingError> {
- radroots_trade::listing::validation::validate_listing_event(event)
-}
diff --git a/crates/sdk/src/profile.rs b/crates/sdk/src/profile.rs
@@ -1,12 +0,0 @@
-pub use radroots_events::profile::{RadrootsProfile, RadrootsProfileType};
-pub use radroots_events_codec::profile::error::ProfileEncodeError;
-
-use crate::WireEventParts;
-
-#[cfg(feature = "serde_json")]
-pub fn build_draft(
- profile: &RadrootsProfile,
- profile_type: Option<RadrootsProfileType>,
-) -> Result<WireEventParts, ProfileEncodeError> {
- radroots_events_codec::profile::encode::to_wire_parts_with_profile_type(profile, profile_type)
-}
diff --git a/crates/sdk/tests/client.rs b/crates/sdk/tests/client.rs
@@ -1,906 +0,0 @@
-use radroots_core::{
- RadrootsCoreCurrency, RadrootsCoreDecimal, RadrootsCoreMoney, RadrootsCoreQuantity,
- RadrootsCoreQuantityPrice, RadrootsCoreUnit,
-};
-use radroots_events::farm::{RadrootsFarm, RadrootsFarmRef};
-use radroots_events::ids::{RadrootsEventId, RadrootsPublicKey};
-use radroots_events::kinds::{
- KIND_FARM, KIND_LISTING, KIND_ORDER_CANCELLATION, KIND_ORDER_DECISION,
- KIND_ORDER_FULFILLMENT_UPDATE, KIND_ORDER_RECEIPT, KIND_ORDER_REQUEST,
- KIND_ORDER_REVISION_DECISION, KIND_ORDER_REVISION_PROPOSAL, KIND_PROFILE,
-};
-use radroots_events::listing::{
- RadrootsListing, RadrootsListingAvailability, RadrootsListingBin,
- RadrootsListingDeliveryMethod, RadrootsListingLocation, RadrootsListingProduct,
- RadrootsListingStatus,
-};
-use radroots_events::order::{
- RadrootsOrderCancellation, RadrootsOrderDecision, RadrootsOrderDecisionOutcome,
- RadrootsOrderEconomicItem, RadrootsOrderEconomics, RadrootsOrderFulfillmentState,
- RadrootsOrderFulfillmentUpdate, RadrootsOrderInventoryCommitment, RadrootsOrderItem,
- RadrootsOrderPricingBasis, RadrootsOrderReceipt, RadrootsOrderRequest,
- RadrootsOrderRevisionDecision, RadrootsOrderRevisionOutcome, RadrootsOrderRevisionProposal,
-};
-use radroots_events::profile::{RadrootsProfile, RadrootsProfileType};
-use radroots_sdk::{
- RADROOTS_SDK_PRODUCTION_RELAY_URL, RadrootsNostrEvent, RadrootsNostrEventPtr,
- RadrootsSdkClient, RadrootsSdkConfig, RelayConfig, SdkConfigError, SdkEnvironment,
- SdkPublishError, SdkRadrootsdPublishReceipt, SdkRelayFailure, SdkResolvedTransportTarget,
- SdkTransportMode, SignerConfig, WireEventParts,
-};
-
-fn sample_farm() -> RadrootsFarm {
- RadrootsFarm {
- d_tag: "AAAAAAAAAAAAAAAAAAAAAA".into(),
- name: "North Farm".into(),
- about: Some("Organic coffee".into()),
- website: None,
- picture: None,
- banner: None,
- location: None,
- tags: Some(vec!["coffee".into()]),
- }
-}
-
-fn sample_listing() -> RadrootsListing {
- RadrootsListing {
- d_tag: "AAAAAAAAAAAAAAAAAAAAAg".parse().expect("listing d tag"),
- published_at: None,
- farm: RadrootsFarmRef {
- pubkey: "seller".into(),
- d_tag: "AAAAAAAAAAAAAAAAAAAAAA".into(),
- },
- product: RadrootsListingProduct {
- key: "coffee".into(),
- title: "Coffee".into(),
- category: "coffee".into(),
- summary: Some("Single origin coffee".into()),
- process: None,
- lot: None,
- location: None,
- profile: None,
- year: None,
- },
- primary_bin_id: "bin-1".parse().expect("primary bin id"),
- bins: vec![RadrootsListingBin {
- bin_id: "bin-1".parse().expect("bin id"),
- quantity: RadrootsCoreQuantity::new(
- RadrootsCoreDecimal::from(1000u32),
- RadrootsCoreUnit::MassG,
- ),
- price_per_canonical_unit: RadrootsCoreQuantityPrice {
- amount: RadrootsCoreMoney::new(
- RadrootsCoreDecimal::from(20u32),
- RadrootsCoreCurrency::USD,
- ),
- quantity: RadrootsCoreQuantity::new(
- RadrootsCoreDecimal::from(1u32),
- RadrootsCoreUnit::MassG,
- ),
- },
- display_amount: None,
- display_unit: None,
- display_label: None,
- display_price: None,
- display_price_unit: None,
- }],
- resource_area: None,
- plot: None,
- discounts: None,
- inventory_available: Some(RadrootsCoreDecimal::from(5u32)),
- availability: Some(RadrootsListingAvailability::Status {
- status: RadrootsListingStatus::Active,
- }),
- delivery_method: Some(RadrootsListingDeliveryMethod::Pickup),
- location: Some(RadrootsListingLocation {
- primary: "North Farm".into(),
- city: None,
- region: None,
- country: None,
- lat: None,
- lng: None,
- geohash: None,
- }),
- images: None,
- }
-}
-
-fn sample_profile() -> RadrootsProfile {
- RadrootsProfile {
- name: "north-farm".into(),
- display_name: Some("North Farm".into()),
- nip05: None,
- about: Some("Farm profile".into()),
- website: None,
- picture: None,
- banner: None,
- lud06: None,
- lud16: None,
- bot: None,
- }
-}
-
-fn decimal(raw: &str) -> RadrootsCoreDecimal {
- raw.parse().expect("decimal")
-}
-
-fn usd(raw: &str) -> RadrootsCoreMoney {
- RadrootsCoreMoney::new(decimal(raw), RadrootsCoreCurrency::USD)
-}
-
-fn listing_event_ptr() -> RadrootsNostrEventPtr {
- RadrootsNostrEventPtr {
- id: event_id_wire('a'),
- relays: Some("wss://listing.relay.example".into()),
- }
-}
-
-fn public_key(value: String) -> RadrootsPublicKey {
- value.parse().expect("public key")
-}
-
-fn event_id(character: char) -> RadrootsEventId {
- core::iter::repeat_n(character, 64)
- .collect::<String>()
- .parse()
- .expect("event id")
-}
-
-fn event_id_wire(character: char) -> String {
- event_id(character).into_string()
-}
-
-fn sample_order_request(buyer_pubkey: String, seller_pubkey: String) -> RadrootsOrderRequest {
- let buyer_pubkey = public_key(buyer_pubkey);
- let seller_pubkey = public_key(seller_pubkey);
- RadrootsOrderRequest {
- order_id: "order-1".parse().expect("order id"),
- listing_addr: format!("{KIND_LISTING}:{seller_pubkey}:AAAAAAAAAAAAAAAAAAAAAg")
- .parse()
- .expect("listing address"),
- buyer_pubkey,
- seller_pubkey,
- items: vec![RadrootsOrderItem {
- bin_id: "bin-1".parse().expect("bin id"),
- bin_count: 2,
- }],
- economics: RadrootsOrderEconomics {
- quote_id: "quote-1".parse().expect("quote id"),
- quote_version: 1,
- pricing_basis: RadrootsOrderPricingBasis::ListingEvent,
- currency: RadrootsCoreCurrency::USD,
- items: vec![RadrootsOrderEconomicItem {
- bin_id: "bin-1".parse().expect("bin id"),
- bin_count: 2,
- quantity_amount: decimal("1"),
- quantity_unit: RadrootsCoreUnit::Each,
- unit_price_amount: decimal("5"),
- unit_price_currency: RadrootsCoreCurrency::USD,
- line_subtotal: usd("10"),
- }],
- discounts: Vec::new(),
- adjustments: Vec::new(),
- subtotal: usd("10"),
- discount_total: usd("0"),
- adjustment_total: usd("0"),
- total: usd("10"),
- },
- }
-}
-
-fn sample_order_decision(buyer_pubkey: String, seller_pubkey: String) -> RadrootsOrderDecision {
- let buyer_pubkey = public_key(buyer_pubkey);
- let seller_pubkey = public_key(seller_pubkey);
- RadrootsOrderDecision {
- order_id: "order-1".parse().expect("order id"),
- listing_addr: format!("{KIND_LISTING}:{seller_pubkey}:AAAAAAAAAAAAAAAAAAAAAg")
- .parse()
- .expect("listing address"),
- buyer_pubkey,
- seller_pubkey,
- decision: RadrootsOrderDecisionOutcome::Accepted {
- inventory_commitments: vec![RadrootsOrderInventoryCommitment {
- bin_id: "bin-1".parse().expect("bin id"),
- bin_count: 2,
- }],
- },
- }
-}
-
-fn sample_order_revision_proposal(
- buyer_pubkey: String,
- seller_pubkey: String,
- root_event_id: String,
- prev_event_id: String,
-) -> RadrootsOrderRevisionProposal {
- let buyer_pubkey = public_key(buyer_pubkey);
- let seller_pubkey = public_key(seller_pubkey);
- RadrootsOrderRevisionProposal {
- revision_id: "revision-1".parse().expect("revision id"),
- order_id: "order-1".parse().expect("order id"),
- listing_addr: format!("{KIND_LISTING}:{seller_pubkey}:AAAAAAAAAAAAAAAAAAAAAg")
- .parse()
- .expect("listing address"),
- buyer_pubkey,
- seller_pubkey,
- root_event_id: root_event_id.parse().expect("root event id"),
- prev_event_id: prev_event_id.parse().expect("previous event id"),
- items: vec![RadrootsOrderItem {
- bin_id: "bin-1".parse().expect("bin id"),
- bin_count: 3,
- }],
- economics: RadrootsOrderEconomics {
- quote_id: "revision-quote-1".parse().expect("revision quote id"),
- quote_version: 2,
- pricing_basis: RadrootsOrderPricingBasis::ListingEvent,
- currency: RadrootsCoreCurrency::USD,
- items: vec![RadrootsOrderEconomicItem {
- bin_id: "bin-1".parse().expect("bin id"),
- bin_count: 3,
- quantity_amount: decimal("1"),
- quantity_unit: RadrootsCoreUnit::Each,
- unit_price_amount: decimal("5"),
- unit_price_currency: RadrootsCoreCurrency::USD,
- line_subtotal: usd("15"),
- }],
- discounts: Vec::new(),
- adjustments: Vec::new(),
- subtotal: usd("15"),
- discount_total: usd("0"),
- adjustment_total: usd("0"),
- total: usd("15"),
- },
- reason: "update count".into(),
- }
-}
-
-fn sample_order_revision_decision(
- proposal: &RadrootsOrderRevisionProposal,
- decision: RadrootsOrderRevisionOutcome,
-) -> RadrootsOrderRevisionDecision {
- RadrootsOrderRevisionDecision {
- revision_id: proposal.revision_id.clone(),
- order_id: proposal.order_id.clone(),
- listing_addr: proposal.listing_addr.clone(),
- buyer_pubkey: proposal.buyer_pubkey.clone(),
- seller_pubkey: proposal.seller_pubkey.clone(),
- root_event_id: proposal.root_event_id.clone(),
- prev_event_id: event_id('3'),
- decision,
- }
-}
-
-fn sample_fulfillment_update(
- buyer_pubkey: String,
- seller_pubkey: String,
-) -> RadrootsOrderFulfillmentUpdate {
- let buyer_pubkey = public_key(buyer_pubkey);
- let seller_pubkey = public_key(seller_pubkey);
- RadrootsOrderFulfillmentUpdate {
- order_id: "order-1".parse().expect("order id"),
- listing_addr: format!("{KIND_LISTING}:{seller_pubkey}:AAAAAAAAAAAAAAAAAAAAAg")
- .parse()
- .expect("listing address"),
- buyer_pubkey,
- seller_pubkey,
- status: RadrootsOrderFulfillmentState::ReadyForPickup,
- }
-}
-
-fn sample_order_cancellation(
- buyer_pubkey: String,
- seller_pubkey: String,
-) -> RadrootsOrderCancellation {
- let buyer_pubkey = public_key(buyer_pubkey);
- let seller_pubkey = public_key(seller_pubkey);
- RadrootsOrderCancellation {
- order_id: "order-1".parse().expect("order id"),
- listing_addr: format!("{KIND_LISTING}:{seller_pubkey}:AAAAAAAAAAAAAAAAAAAAAg")
- .parse()
- .expect("listing address"),
- buyer_pubkey,
- seller_pubkey,
- reason: "schedule changed".into(),
- }
-}
-
-fn sample_buyer_receipt(buyer_pubkey: String, seller_pubkey: String) -> RadrootsOrderReceipt {
- let buyer_pubkey = public_key(buyer_pubkey);
- let seller_pubkey = public_key(seller_pubkey);
- RadrootsOrderReceipt {
- order_id: "order-1".parse().expect("order id"),
- listing_addr: format!("{KIND_LISTING}:{seller_pubkey}:AAAAAAAAAAAAAAAAAAAAAg")
- .parse()
- .expect("listing address"),
- buyer_pubkey,
- seller_pubkey,
- received: true,
- issue: None,
- received_at: 1_785_000_000,
- }
-}
-
-fn event_from_parts(
- id: &str,
- author: &str,
- created_at: u32,
- parts: WireEventParts,
-) -> RadrootsNostrEvent {
- RadrootsNostrEvent {
- id: id.into(),
- author: author.into(),
- created_at,
- kind: parts.kind,
- tags: parts.tags,
- content: parts.content,
- sig: String::new(),
- }
-}
-
-#[test]
-fn client_default_config_uses_production_relay_direct() {
- let client = RadrootsSdkClient::from_config(RadrootsSdkConfig::default()).expect("sdk client");
-
- assert_eq!(client.transport(), SdkTransportMode::RelayDirect);
- assert_eq!(
- client.resolved_transport_target(),
- &SdkResolvedTransportTarget::RelayDirect {
- relay_urls: vec![RADROOTS_SDK_PRODUCTION_RELAY_URL.to_string()],
- }
- );
-}
-
-#[test]
-fn client_rejects_invalid_config_on_construction() {
- let mut config = RadrootsSdkConfig::custom();
- config.transport = SdkTransportMode::RelayDirect;
- config.relay = RelayConfig {
- urls: vec!["https://radroots.org".into()],
- };
-
- let error = RadrootsSdkClient::from_config(config).expect_err("invalid config");
- assert_eq!(
- error,
- SdkConfigError::InvalidRelayUrl("https://radroots.org".into())
- );
-}
-
-#[test]
-fn client_rejects_invalid_radrootsd_config_on_construction() {
- let mut missing = RadrootsSdkConfig::custom();
- missing.transport = SdkTransportMode::Radrootsd;
-
- assert_eq!(
- RadrootsSdkClient::from_config(missing).expect_err("missing radrootsd endpoint"),
- SdkConfigError::MissingCustomRadrootsdEndpoint
- );
-
- let mut invalid = RadrootsSdkConfig::custom();
- invalid.transport = SdkTransportMode::Radrootsd;
- invalid.radrootsd.endpoint = Some("wss://rpc.bad".into());
-
- assert_eq!(
- RadrootsSdkClient::from_config(invalid).expect_err("invalid radrootsd endpoint"),
- SdkConfigError::InvalidRadrootsdEndpoint("wss://rpc.bad".into())
- );
-}
-
-#[test]
-fn client_allows_custom_relay_without_radrootsd_endpoint() {
- let mut config = RadrootsSdkConfig::custom();
- config.transport = SdkTransportMode::RelayDirect;
- config.relay = RelayConfig {
- urls: vec!["wss://radroots.org".into()],
- };
-
- let client = RadrootsSdkClient::from_config(config).expect("relay-only sdk client");
- assert_eq!(
- client.resolved_transport_target(),
- &SdkResolvedTransportTarget::RelayDirect {
- relay_urls: vec!["wss://radroots.org".to_string()],
- }
- );
-}
-
-#[test]
-fn client_allows_custom_radrootsd_without_relay_urls() {
- let endpoint = "https://custom.radroots.org/jsonrpc";
- let mut config = RadrootsSdkConfig::custom();
- config.transport = SdkTransportMode::Radrootsd;
- config.radrootsd.endpoint = Some(endpoint.into());
-
- let client = RadrootsSdkClient::from_config(config).expect("radrootsd-only sdk client");
- assert_eq!(
- client.resolved_transport_target(),
- &SdkResolvedTransportTarget::Radrootsd {
- endpoint: endpoint.to_string(),
- }
- );
-}
-
-#[test]
-fn namespace_clients_reflect_explicit_transport_mode() {
- let mut config = RadrootsSdkConfig::for_environment(SdkEnvironment::Production);
- config.transport = SdkTransportMode::Radrootsd;
- config.signer = SignerConfig::LocalIdentity;
-
- let client = RadrootsSdkClient::from_config(config).expect("sdk client");
-
- assert_eq!(client.transport(), SdkTransportMode::Radrootsd);
- assert_eq!(client.profile().transport(), SdkTransportMode::Radrootsd);
- assert_eq!(client.farm().transport(), SdkTransportMode::Radrootsd);
- assert_eq!(client.listing().transport(), SdkTransportMode::Radrootsd);
- assert_eq!(client.order().transport(), SdkTransportMode::Radrootsd);
- #[cfg(feature = "radrootsd-client")]
- assert_eq!(client.radrootsd().transport(), SdkTransportMode::Radrootsd);
- assert_eq!(client.signer(), SignerConfig::LocalIdentity);
- assert_eq!(client.profile().signer(), SignerConfig::LocalIdentity);
- assert_eq!(client.farm().signer(), SignerConfig::LocalIdentity);
- assert_eq!(client.listing().signer(), SignerConfig::LocalIdentity);
- assert_eq!(client.order().signer(), SignerConfig::LocalIdentity);
- #[cfg(feature = "radrootsd-client")]
- assert_eq!(client.radrootsd().signer(), SignerConfig::LocalIdentity);
-}
-
-#[test]
-fn namespace_clients_expose_parent_sdk_and_draft_facades() {
- let client =
- RadrootsSdkClient::from_config(RadrootsSdkConfig::production()).expect("sdk client");
- let profile = client.profile();
- let farm = client.farm();
- let listing = client.listing();
- let order = client.order();
-
- assert_eq!(client.config().environment, SdkEnvironment::Production);
- assert!(std::ptr::eq(profile.sdk(), &client));
- assert!(std::ptr::eq(farm.sdk(), &client));
- assert!(std::ptr::eq(listing.sdk(), &client));
- assert!(std::ptr::eq(order.sdk(), &client));
-
- let profile_draft = profile
- .build_draft(&sample_profile(), Some(RadrootsProfileType::Farm))
- .expect("profile draft");
- assert_eq!(profile_draft.kind, KIND_PROFILE);
-
- let farm_draft = farm.build_draft(&sample_farm()).expect("farm draft");
- assert_eq!(farm_draft.kind, KIND_FARM);
-
- let listing_draft = listing
- .build_draft(&sample_listing())
- .expect("listing draft");
- assert_eq!(listing_draft.as_wire_parts().kind, KIND_LISTING);
- assert_eq!(listing_draft.into_wire_parts().kind, KIND_LISTING);
-
- let mut invalid_listing = sample_listing();
- invalid_listing.farm.pubkey.clear();
- assert!(listing.build_draft(&invalid_listing).is_err());
-}
-
-#[test]
-fn listing_and_order_clients_wrap_existing_sdk_facades() {
- let client = RadrootsSdkClient::from_config(RadrootsSdkConfig::local()).expect("sdk client");
- let listing_value = sample_listing();
- let buyer_pubkey = "b".repeat(64);
- let seller_pubkey = "a".repeat(64);
-
- let tags = client
- .listing()
- .build_tags(&listing_value)
- .expect("listing tags");
- assert!(!tags.is_empty());
-
- let draft = client
- .listing()
- .build_draft(&listing_value)
- .expect("listing draft");
- assert_eq!(draft.as_wire_parts().kind, KIND_LISTING);
-
- let event = RadrootsNostrEvent {
- id: "listing-1".into(),
- author: "seller".into(),
- created_at: 1,
- kind: draft.as_wire_parts().kind,
- tags: draft.as_wire_parts().tags.clone(),
- content: draft.as_wire_parts().content.clone(),
- sig: String::new(),
- };
- let parsed = client
- .listing()
- .parse_event(&event)
- .expect("parsed listing");
- assert_eq!(parsed.d_tag, listing_value.d_tag);
-
- let validated = client
- .order()
- .validate_listing_event(&event)
- .expect("validated listing");
- assert_eq!(validated.listing_id, listing_value.d_tag);
-
- let listing_addr = format!("{KIND_LISTING}:{seller_pubkey}:{}", listing_value.d_tag);
- let payload = sample_order_request(buyer_pubkey.clone(), seller_pubkey.clone());
- let envelope = client
- .order()
- .build_order_request_draft(&listing_event_ptr(), &payload)
- .expect("order draft");
- assert_eq!(envelope.as_wire_parts().kind, KIND_ORDER_REQUEST);
- let envelope_event = RadrootsNostrEvent {
- id: "order-event-1".into(),
- author: buyer_pubkey,
- created_at: 2,
- kind: envelope.as_wire_parts().kind,
- tags: envelope.as_wire_parts().tags.clone(),
- content: envelope.as_wire_parts().content.clone(),
- sig: String::new(),
- };
- assert_eq!(
- client
- .order()
- .parse_order_request(&envelope_event)
- .expect("order envelope")
- .payload
- .order_id,
- payload.order_id
- );
- let parsed_addr = client
- .order()
- .parse_listing_address(&listing_addr)
- .expect("listing address");
- assert_eq!(parsed_addr.listing_id, listing_value.d_tag);
-}
-
-#[test]
-fn order_facades_round_trip_all_draft_types() {
- let client =
- RadrootsSdkClient::from_config(RadrootsSdkConfig::production()).expect("sdk client");
- let order_client = client.order();
- let buyer_pubkey = "b".repeat(64);
- let seller_pubkey = "a".repeat(64);
- let root_event_id = event_id('1');
- let decision_event_id = event_id('2');
- let proposal_event_id = event_id('3');
- let fulfillment_event_id = event_id('4');
-
- let order_request = sample_order_request(buyer_pubkey.clone(), seller_pubkey.clone());
- let order_draft = order_client
- .build_order_request_draft(&listing_event_ptr(), &order_request)
- .expect("order request draft");
- assert_eq!(order_draft.as_wire_parts().kind, KIND_ORDER_REQUEST);
- let order_event = event_from_parts(
- root_event_id.as_str(),
- &buyer_pubkey,
- 1,
- order_draft.clone().into_wire_parts(),
- );
- let order_envelope = order_client
- .parse_order_request(&order_event)
- .expect("order request envelope");
- assert_eq!(order_envelope.payload.economics.total, usd("10"));
-
- let decision = sample_order_decision(buyer_pubkey.clone(), seller_pubkey.clone());
- let decision_draft = order_client
- .build_order_decision_draft(&root_event_id, &root_event_id, &decision)
- .expect("order decision draft");
- assert_eq!(decision_draft.as_wire_parts().kind, KIND_ORDER_DECISION);
- let decision_event = event_from_parts(
- decision_event_id.as_str(),
- &seller_pubkey,
- 2,
- decision_draft.clone().into_wire_parts(),
- );
- assert_eq!(
- order_client
- .parse_order_decision(&decision_event)
- .expect("order decision envelope")
- .payload
- .decision,
- decision.decision
- );
-
- let proposal = sample_order_revision_proposal(
- buyer_pubkey.clone(),
- seller_pubkey.clone(),
- root_event_id.to_string(),
- decision_event_id.to_string(),
- );
- let proposal_draft = order_client
- .build_order_revision_proposal_draft(&root_event_id, &decision_event_id, &proposal)
- .expect("revision proposal draft");
- assert_eq!(
- proposal_draft.as_wire_parts().kind,
- KIND_ORDER_REVISION_PROPOSAL
- );
- let proposal_event = event_from_parts(
- proposal_event_id.as_str(),
- &seller_pubkey,
- 3,
- proposal_draft.clone().into_wire_parts(),
- );
- assert_eq!(
- order_client
- .parse_order_revision_proposal(&proposal_event)
- .expect("revision proposal envelope")
- .payload
- .economics
- .total,
- usd("15")
- );
-
- let revision_decision =
- sample_order_revision_decision(&proposal, RadrootsOrderRevisionOutcome::Accepted);
- let revision_decision_draft = order_client
- .build_order_revision_decision_draft(
- &root_event_id,
- &revision_decision.prev_event_id,
- &revision_decision,
- )
- .expect("revision decision draft");
- assert_eq!(
- revision_decision_draft.as_wire_parts().kind,
- KIND_ORDER_REVISION_DECISION
- );
- let revision_decision_event = event_from_parts(
- "order-revision-decision-event-1",
- &buyer_pubkey,
- 4,
- revision_decision_draft.clone().into_wire_parts(),
- );
- assert_eq!(
- order_client
- .parse_order_revision_decision(&revision_decision_event)
- .expect("revision decision envelope")
- .payload
- .revision_id,
- revision_decision.revision_id
- );
-
- let fulfillment = sample_fulfillment_update(buyer_pubkey.clone(), seller_pubkey.clone());
- let fulfillment_draft = order_client
- .build_fulfillment_update_draft(&root_event_id, &decision_event_id, &fulfillment)
- .expect("fulfillment draft");
- assert_eq!(
- fulfillment_draft.as_wire_parts().kind,
- KIND_ORDER_FULFILLMENT_UPDATE
- );
- let fulfillment_event = event_from_parts(
- fulfillment_event_id.as_str(),
- &seller_pubkey,
- 5,
- fulfillment_draft.clone().into_wire_parts(),
- );
- assert_eq!(
- order_client
- .parse_fulfillment_update(&fulfillment_event)
- .expect("fulfillment envelope")
- .payload
- .status,
- fulfillment.status
- );
-
- let cancellation = sample_order_cancellation(buyer_pubkey.clone(), seller_pubkey.clone());
- let cancellation_draft = order_client
- .build_order_cancellation_draft(&root_event_id, &decision_event_id, &cancellation)
- .expect("cancellation draft");
- assert_eq!(
- cancellation_draft.as_wire_parts().kind,
- KIND_ORDER_CANCELLATION
- );
- let cancellation_event = event_from_parts(
- "order-cancellation-event-1",
- &buyer_pubkey,
- 6,
- cancellation_draft.clone().into_wire_parts(),
- );
- assert_eq!(
- order_client
- .parse_order_cancellation(&cancellation_event)
- .expect("cancellation envelope")
- .payload
- .reason,
- cancellation.reason
- );
-
- let receipt = sample_buyer_receipt(buyer_pubkey.clone(), seller_pubkey.clone());
- let receipt_draft = order_client
- .build_buyer_receipt_draft(&root_event_id, &fulfillment_event_id, &receipt)
- .expect("receipt draft");
- assert_eq!(receipt_draft.as_wire_parts().kind, KIND_ORDER_RECEIPT);
- let receipt_event = event_from_parts(
- "receipt-event-1",
- &buyer_pubkey,
- 7,
- receipt_draft.clone().into_wire_parts(),
- );
- assert!(
- order_client
- .parse_buyer_receipt(&receipt_event)
- .expect("receipt envelope")
- .payload
- .received
- );
-}
-
-#[test]
-fn order_draft_facades_return_encoder_errors() {
- let client =
- RadrootsSdkClient::from_config(RadrootsSdkConfig::production()).expect("sdk client");
- let order = client.order();
- let buyer_pubkey = "b".repeat(64);
- let seller_pubkey = "a".repeat(64);
- let root_event_id = event_id('1');
- let decision_event_id = event_id('2');
-
- let mut invalid_order = sample_order_request(buyer_pubkey.clone(), seller_pubkey.clone());
- invalid_order.items.clear();
- assert!(
- order
- .build_order_request_draft(&listing_event_ptr(), &invalid_order)
- .is_err()
- );
-
- let mut invalid_decision = sample_order_decision(buyer_pubkey.clone(), seller_pubkey.clone());
- invalid_decision.decision = RadrootsOrderDecisionOutcome::Accepted {
- inventory_commitments: Vec::new(),
- };
- assert!(
- order
- .build_order_decision_draft(&root_event_id, &root_event_id, &invalid_decision)
- .is_err()
- );
-
- let proposal = sample_order_revision_proposal(
- buyer_pubkey.clone(),
- seller_pubkey.clone(),
- root_event_id.to_string(),
- decision_event_id.to_string(),
- );
- let different_root_event_id = event_id('d');
- assert!(
- order
- .build_order_revision_proposal_draft(
- &different_root_event_id,
- &decision_event_id,
- &proposal,
- )
- .is_err()
- );
-
- let revision_decision =
- sample_order_revision_decision(&proposal, RadrootsOrderRevisionOutcome::Accepted);
- let different_prev_event_id = event_id('e');
- assert!(
- order
- .build_order_revision_decision_draft(
- &root_event_id,
- &different_prev_event_id,
- &revision_decision,
- )
- .is_err()
- );
-
- let mut fulfillment = sample_fulfillment_update(buyer_pubkey.clone(), seller_pubkey.clone());
- fulfillment.status = RadrootsOrderFulfillmentState::AcceptedNotFulfilled;
- assert!(
- order
- .build_fulfillment_update_draft(&root_event_id, &decision_event_id, &fulfillment)
- .is_err()
- );
-
- let mut cancellation = sample_order_cancellation(buyer_pubkey.clone(), seller_pubkey.clone());
- cancellation.reason.clear();
- assert!(
- order
- .build_order_cancellation_draft(&root_event_id, &decision_event_id, &cancellation)
- .is_err()
- );
-
- let mut receipt = sample_buyer_receipt(buyer_pubkey, seller_pubkey);
- receipt.received = false;
- assert!(
- order
- .build_buyer_receipt_draft(&root_event_id, &decision_event_id, &receipt)
- .is_err()
- );
-}
-
-#[test]
-fn publish_receipts_and_errors_format_public_details() {
- let receipt = SdkRadrootsdPublishReceipt {
- accepted: true,
- deduplicated: true,
- job_id: Some("job-1".into()),
- status: Some("accepted".into()),
- signer_mode: Some("secret-mode".into()),
- signer_session_id: Some("secret-session".into()),
- event_addr: Some("3432:pubkey:order-1".into()),
- relay_count: Some(2),
- acknowledged_relay_count: Some(1),
- };
- let debug = format!("{receipt:?}");
-
- assert!(debug.contains("SdkRadrootsdPublishReceipt"));
- assert!(debug.contains("<redacted>"));
- assert!(!debug.contains("secret-mode"));
- assert!(!debug.contains("secret-session"));
-
- let relay_failure = SdkRelayFailure {
- relay_url: "wss://relay.example".into(),
- error: "closed".into(),
- };
- let formatted = [
- SdkPublishError::from(SdkConfigError::EmptyRelayUrl).to_string(),
- SdkPublishError::Encode("encode failed".into()).to_string(),
- SdkPublishError::UnsupportedTransport {
- transport: SdkTransportMode::Radrootsd,
- operation: "order.publish",
- }
- .to_string(),
- SdkPublishError::UnsupportedSignerMode {
- transport: SdkTransportMode::RelayDirect,
- signer: SignerConfig::DraftOnly,
- required: SignerConfig::LocalIdentity,
- operation: "order.publish",
- }
- .to_string(),
- SdkPublishError::Relay("relay failed".into()).to_string(),
- SdkPublishError::RelaySetup {
- transport: SdkTransportMode::RelayDirect,
- operation: "order.publish",
- target_relays: Vec::new(),
- error: "setup failed".into(),
- }
- .to_string(),
- SdkPublishError::RelaySetup {
- transport: SdkTransportMode::RelayDirect,
- operation: "order.publish",
- target_relays: vec!["wss://relay.example".into()],
- error: "setup failed".into(),
- }
- .to_string(),
- SdkPublishError::RelayNotAcknowledged {
- transport: SdkTransportMode::RelayDirect,
- failed_relays: Vec::new(),
- }
- .to_string(),
- SdkPublishError::RelayNotAcknowledged {
- transport: SdkTransportMode::RelayDirect,
- failed_relays: vec![relay_failure],
- }
- .to_string(),
- SdkPublishError::Radrootsd("radrootsd failed".into()).to_string(),
- ];
-
- assert!(
- formatted
- .iter()
- .any(|message| message == "relay url must not be empty")
- );
- assert!(formatted.iter().any(|message| message == "encode failed"));
- assert!(
- formatted
- .iter()
- .any(|message| message.contains("requires signer mode `local_identity`"))
- );
- assert!(formatted.iter().any(|message| {
- message.contains("failed to prepare RelayDirect relay publish for wss://relay.example")
- }));
- assert!(
- formatted
- .iter()
- .any(|message| message.contains("wss://relay.example: closed"))
- );
- assert!(
- formatted
- .iter()
- .any(|message| message == "radrootsd failed")
- );
-}
-
-#[test]
-fn farm_client_wraps_existing_farm_facade() {
- let client =
- RadrootsSdkClient::from_config(RadrootsSdkConfig::production()).expect("sdk client");
- let farm = sample_farm();
-
- let draft = client.farm().build_draft(&farm).expect("farm draft");
- assert!(!draft.tags.is_empty());
-}
diff --git a/crates/sdk/tests/config.rs b/crates/sdk/tests/config.rs
@@ -1,562 +0,0 @@
-use radroots_sdk::{
- NetworkConfig, RADROOTS_SDK_LOCAL_RADROOTSD_ENDPOINT, RADROOTS_SDK_LOCAL_RELAY_URL,
- RADROOTS_SDK_PRODUCTION_RADROOTSD_ENDPOINT, RADROOTS_SDK_PRODUCTION_RELAY_URL,
- RADROOTS_SDK_STAGING_RADROOTSD_ENDPOINT, RADROOTS_SDK_STAGING_RELAY_URL, RadrootsSdkConfig,
- RadrootsdAuth, RelayConfig, SdkConfigError, SdkEnvironment, SdkTransportMode, SignerConfig,
-};
-use std::{
- ffi::OsString,
- sync::{Mutex, OnceLock},
-};
-
-const LOCAL_SDK_ENV_KEYS: &[&str] = &[
- "NOSTR_RS_RELAY_PUBLIC_SCHEME",
- "NOSTR_RS_RELAY_PUBLIC_HOST",
- "NOSTR_RS_RELAY_PUBLIC_PORT",
- "RADROOTSD_RPC_URL",
- "RADROOTSD_RPC_HOST",
- "RADROOTSD_RPC_PORT",
-];
-
-fn sdk_env_lock() -> &'static Mutex<()> {
- static LOCK: OnceLock<Mutex<()>> = OnceLock::new();
- LOCK.get_or_init(|| Mutex::new(()))
-}
-
-struct LocalSdkEnvRestore {
- saved: Vec<(&'static str, Option<OsString>)>,
-}
-
-impl LocalSdkEnvRestore {
- fn apply(pairs: &[(&str, &str)]) -> Self {
- let saved = LOCAL_SDK_ENV_KEYS
- .iter()
- .map(|key| (*key, std::env::var_os(key)))
- .collect::<Vec<_>>();
-
- for key in LOCAL_SDK_ENV_KEYS {
- unsafe {
- std::env::remove_var(key);
- }
- }
- for (key, value) in pairs {
- assert!(
- LOCAL_SDK_ENV_KEYS.contains(key),
- "unexpected local sdk env key `{key}`"
- );
- unsafe {
- std::env::set_var(key, value);
- }
- }
-
- Self { saved }
- }
-}
-
-impl Drop for LocalSdkEnvRestore {
- fn drop(&mut self) {
- for (key, original) in self.saved.drain(..) {
- match original {
- Some(value) => unsafe {
- std::env::set_var(key, value);
- },
- None => unsafe {
- std::env::remove_var(key);
- },
- }
- }
- }
-}
-
-struct EnvKeyRestore {
- key: &'static str,
- saved: Option<OsString>,
-}
-
-impl EnvKeyRestore {
- fn capture(key: &'static str) -> Self {
- Self {
- key,
- saved: std::env::var_os(key),
- }
- }
-}
-
-impl Drop for EnvKeyRestore {
- fn drop(&mut self) {
- match &self.saved {
- Some(value) => unsafe {
- std::env::set_var(self.key, value);
- },
- None => unsafe {
- std::env::remove_var(self.key);
- },
- }
- }
-}
-
-fn with_local_sdk_env<F>(pairs: &[(&str, &str)], test: F)
-where
- F: FnOnce(),
-{
- let _guard = sdk_env_lock().lock().expect("sdk env lock");
- let _env_restore = LocalSdkEnvRestore::apply(pairs);
-
- test();
-}
-
-#[test]
-fn local_sdk_env_restore_preserves_original_os_string_values() {
- let _guard = sdk_env_lock().lock().expect("sdk env lock");
- let key = "NOSTR_RS_RELAY_PUBLIC_HOST";
- let _restore_key = EnvKeyRestore::capture(key);
- let original = OsString::from("relay.before.example");
-
- unsafe {
- std::env::set_var(key, &original);
- }
-
- {
- let _env_restore = LocalSdkEnvRestore::apply(&[("RADROOTSD_RPC_PORT", "18080")]);
-
- assert_eq!(std::env::var_os(key), None);
- }
-
- assert_eq!(std::env::var_os(key), Some(original));
-}
-
-#[test]
-fn env_key_restore_restores_existing_value() {
- let _guard = sdk_env_lock().lock().expect("sdk env lock");
- let key = "NOSTR_RS_RELAY_PUBLIC_HOST";
- let _restore_outer = EnvKeyRestore::capture(key);
- let original = OsString::from("relay.before.example");
- let changed = OsString::from("relay.changed.example");
-
- unsafe {
- std::env::set_var(key, &original);
- }
-
- {
- let _restore_inner = EnvKeyRestore::capture(key);
-
- unsafe {
- std::env::set_var(key, &changed);
- }
- }
-
- assert_eq!(std::env::var_os(key), Some(original));
-}
-
-#[cfg(unix)]
-#[test]
-fn local_sdk_env_restore_preserves_non_unicode_original_values() {
- use std::os::unix::ffi::OsStringExt;
-
- let _guard = sdk_env_lock().lock().expect("sdk env lock");
- let key = "NOSTR_RS_RELAY_PUBLIC_HOST";
- let _restore_key = EnvKeyRestore::capture(key);
- let original = OsString::from_vec(vec![b'r', b'e', b'l', b'a', b'y', 0x80]);
-
- unsafe {
- std::env::set_var(key, &original);
- }
-
- {
- let _env_restore = LocalSdkEnvRestore::apply(&[("RADROOTSD_RPC_PORT", "18080")]);
-
- assert_eq!(std::env::var_os(key), None);
- }
-
- assert_eq!(std::env::var_os(key), Some(original));
-}
-
-#[test]
-fn default_config_uses_production_relay_direct_draft_only() {
- let config = RadrootsSdkConfig::default();
-
- assert_eq!(config.environment, SdkEnvironment::Production);
- assert_eq!(config.transport, SdkTransportMode::RelayDirect);
- assert_eq!(config.signer, SignerConfig::DraftOnly);
- assert_eq!(config.network, NetworkConfig::default());
- assert_eq!(config.radrootsd.auth, RadrootsdAuth::None);
-}
-
-#[test]
-fn production_environment_resolves_radroots_org_defaults() {
- let config = RadrootsSdkConfig::production();
-
- assert_eq!(
- config.resolved_relay_urls().expect("relay defaults"),
- vec![RADROOTS_SDK_PRODUCTION_RELAY_URL.to_owned()]
- );
- assert_eq!(
- config
- .resolved_radrootsd_endpoint()
- .expect("radrootsd endpoint"),
- RADROOTS_SDK_PRODUCTION_RADROOTSD_ENDPOINT
- );
-}
-
-#[test]
-fn staging_environment_resolves_staging_defaults() {
- let config = RadrootsSdkConfig::staging();
-
- assert_eq!(
- config.resolved_relay_urls().expect("relay defaults"),
- vec![RADROOTS_SDK_STAGING_RELAY_URL.to_owned()]
- );
- assert_eq!(
- config
- .resolved_radrootsd_endpoint()
- .expect("radrootsd endpoint"),
- RADROOTS_SDK_STAGING_RADROOTSD_ENDPOINT
- );
-}
-
-#[test]
-fn local_environment_resolves_localhost_defaults() {
- with_local_sdk_env(&[], || {
- let config = RadrootsSdkConfig::local();
-
- assert_eq!(
- config.resolved_relay_urls().expect("relay defaults"),
- vec![RADROOTS_SDK_LOCAL_RELAY_URL.to_owned()]
- );
- assert_eq!(
- config
- .resolved_radrootsd_endpoint()
- .expect("radrootsd endpoint"),
- RADROOTS_SDK_LOCAL_RADROOTSD_ENDPOINT
- );
- });
-}
-
-#[test]
-fn local_environment_prefers_root_env_contract_when_present() {
- with_local_sdk_env(
- &[
- ("NOSTR_RS_RELAY_PUBLIC_SCHEME", "ws"),
- ("NOSTR_RS_RELAY_PUBLIC_HOST", "127.0.0.1"),
- ("NOSTR_RS_RELAY_PUBLIC_PORT", "18080"),
- ("RADROOTSD_RPC_URL", "http://127.0.0.1:17070/jsonrpc"),
- ],
- || {
- let config = RadrootsSdkConfig::local();
-
- assert_eq!(
- config.resolved_relay_urls().expect("relay defaults"),
- vec!["ws://127.0.0.1:18080".to_owned()]
- );
- assert_eq!(
- config
- .resolved_radrootsd_endpoint()
- .expect("radrootsd endpoint"),
- "http://127.0.0.1:17070/jsonrpc"
- );
- },
- );
-}
-
-#[test]
-fn local_environment_ignores_partial_or_blank_env_contracts() {
- with_local_sdk_env(
- &[
- ("NOSTR_RS_RELAY_PUBLIC_SCHEME", "ws"),
- ("NOSTR_RS_RELAY_PUBLIC_HOST", " "),
- ("NOSTR_RS_RELAY_PUBLIC_PORT", "18080"),
- ("RADROOTSD_RPC_HOST", "127.0.0.1"),
- ],
- || {
- let config = RadrootsSdkConfig::local();
-
- assert_eq!(
- config.resolved_relay_urls().expect("relay defaults"),
- vec![RADROOTS_SDK_LOCAL_RELAY_URL.to_owned()]
- );
- assert_eq!(
- config
- .resolved_radrootsd_endpoint()
- .expect("radrootsd endpoint"),
- RADROOTS_SDK_LOCAL_RADROOTSD_ENDPOINT
- );
- },
- );
-}
-
-#[test]
-fn local_environment_handles_invalid_and_missing_relay_port_env() {
- with_local_sdk_env(
- &[
- ("NOSTR_RS_RELAY_PUBLIC_SCHEME", "http"),
- ("NOSTR_RS_RELAY_PUBLIC_HOST", "127.0.0.1"),
- ("NOSTR_RS_RELAY_PUBLIC_PORT", "18080"),
- ],
- || {
- let config = RadrootsSdkConfig::local();
-
- assert_eq!(
- config.resolved_relay_urls().expect_err("invalid relay env"),
- SdkConfigError::InvalidRelayUrl("http://127.0.0.1:18080".to_owned())
- );
- },
- );
-
- with_local_sdk_env(
- &[
- ("NOSTR_RS_RELAY_PUBLIC_SCHEME", "ws"),
- ("NOSTR_RS_RELAY_PUBLIC_HOST", "127.0.0.1"),
- ],
- || {
- let config = RadrootsSdkConfig::local();
-
- assert_eq!(
- config.resolved_relay_urls().expect("relay defaults"),
- vec![RADROOTS_SDK_LOCAL_RELAY_URL.to_owned()]
- );
- },
- );
-}
-
-#[test]
-fn local_environment_builds_radrootsd_endpoint_from_host_port_env() {
- with_local_sdk_env(
- &[
- ("RADROOTSD_RPC_HOST", "127.0.0.1"),
- ("RADROOTSD_RPC_PORT", "17070"),
- ],
- || {
- let config = RadrootsSdkConfig::local();
-
- assert_eq!(
- config
- .resolved_radrootsd_endpoint()
- .expect("host port endpoint"),
- "http://127.0.0.1:17070"
- );
- },
- );
-}
-
-#[test]
-fn explicit_coordinates_override_environment_defaults_exactly() {
- let mut config = RadrootsSdkConfig::production();
- config.relay.urls = vec![
- " wss://relay.custom.one ".to_owned(),
- "wss://relay.custom.one".to_owned(),
- "ws://relay.custom.two".to_owned(),
- ];
- config.radrootsd.endpoint = Some(" https://rpc.custom.radroots.org ".to_owned());
-
- assert_eq!(
- config.resolved_relay_urls().expect("relay overrides"),
- vec![
- "wss://relay.custom.one".to_owned(),
- "ws://relay.custom.two".to_owned(),
- ]
- );
- assert_eq!(
- config
- .resolved_radrootsd_endpoint()
- .expect("endpoint override"),
- "https://rpc.custom.radroots.org"
- );
-}
-
-#[test]
-fn custom_environment_requires_explicit_coordinates() {
- let config = RadrootsSdkConfig::custom();
-
- assert_eq!(
- config
- .resolved_relay_urls()
- .expect_err("custom relay error"),
- SdkConfigError::MissingCustomRelayUrls
- );
- assert_eq!(
- config
- .resolved_radrootsd_endpoint()
- .expect_err("custom radrootsd error"),
- SdkConfigError::MissingCustomRadrootsdEndpoint
- );
-}
-
-#[test]
-fn custom_environment_accepts_explicit_coordinates() {
- let mut config = RadrootsSdkConfig::custom();
- config.relay.urls = vec!["wss://relay.custom.radroots.org".to_owned()];
- config.radrootsd.endpoint = Some("https://rpc.custom.radroots.org".to_owned());
-
- assert_eq!(
- config.resolved_relay_urls().expect("custom relay"),
- vec!["wss://relay.custom.radroots.org".to_owned()]
- );
- assert_eq!(
- config
- .resolved_radrootsd_endpoint()
- .expect("custom endpoint"),
- "https://rpc.custom.radroots.org"
- );
-}
-
-#[test]
-fn empty_coordinate_values_fail_loudly() {
- let mut config = RadrootsSdkConfig::production();
- config.relay = RelayConfig {
- urls: vec![" ".to_owned()],
- };
- config.radrootsd.endpoint = Some(" ".to_owned());
-
- assert_eq!(
- config.resolved_relay_urls().expect_err("empty relay"),
- SdkConfigError::EmptyRelayUrl
- );
- assert_eq!(
- config
- .resolved_radrootsd_endpoint()
- .expect_err("empty radrootsd endpoint"),
- SdkConfigError::EmptyRadrootsdEndpoint
- );
-}
-
-#[test]
-fn invalid_coordinate_schemes_fail_loudly() {
- let mut config = RadrootsSdkConfig::production();
- config.relay.urls = vec!["https://relay.bad".to_owned()];
- config.radrootsd.endpoint = Some("wss://rpc.bad".to_owned());
-
- assert_eq!(
- config
- .resolved_relay_urls()
- .expect_err("relay scheme error"),
- SdkConfigError::InvalidRelayUrl("https://relay.bad".to_owned())
- );
- assert_eq!(
- config
- .resolved_radrootsd_endpoint()
- .expect_err("endpoint scheme error"),
- SdkConfigError::InvalidRadrootsdEndpoint("wss://rpc.bad".to_owned())
- );
-}
-
-#[test]
-fn invalid_relay_authorities_fail_loudly() {
- let invalid_relays = [
- "wss://",
- "wss:///relay",
- "ws://:8080",
- "wss://relay.example:",
- "wss://relay example",
- "wss://user@relay.example",
- "wss://relay.example:abc",
- "wss://2001:db8::1",
- ];
-
- for relay_url in invalid_relays {
- let mut config = RadrootsSdkConfig::production();
- config.relay.urls = vec![relay_url.to_owned()];
-
- assert_eq!(
- config
- .resolved_relay_urls()
- .expect_err("relay authority error"),
- SdkConfigError::InvalidRelayUrl(relay_url.to_owned())
- );
- }
-}
-
-#[test]
-fn invalid_bracketed_relay_authorities_fail_loudly() {
- let invalid_relays = [
- "wss://[2001:db8::1",
- "wss://[]:443",
- "wss://[2001:db8::1]suffix",
- "wss://[2001:db8::1]:abc",
- ];
-
- for relay_url in invalid_relays {
- let mut config = RadrootsSdkConfig::production();
- config.relay.urls = vec![relay_url.to_owned()];
-
- assert_eq!(
- config
- .resolved_relay_urls()
- .expect_err("bracketed relay authority error"),
- SdkConfigError::InvalidRelayUrl(relay_url.to_owned())
- );
- }
-}
-
-#[test]
-fn valid_relay_authorities_still_resolve() {
- let mut config = RadrootsSdkConfig::production();
- config.relay.urls = vec![
- " wss://relay.example/nostr ".to_owned(),
- "ws://127.0.0.1:8080".to_owned(),
- "wss://[2001:db8::1]/relay".to_owned(),
- "wss://[2001:db8::1]:443/relay".to_owned(),
- ];
-
- assert_eq!(
- config.resolved_relay_urls().expect("valid relays"),
- vec![
- "wss://relay.example/nostr".to_owned(),
- "ws://127.0.0.1:8080".to_owned(),
- "wss://[2001:db8::1]/relay".to_owned(),
- "wss://[2001:db8::1]:443/relay".to_owned()
- ]
- );
-}
-
-#[test]
-fn signer_modes_format_as_config_tokens() {
- assert_eq!(SignerConfig::DraftOnly.to_string(), "draft_only");
- assert_eq!(SignerConfig::LocalIdentity.to_string(), "local_identity");
- assert_eq!(SignerConfig::Nip46.to_string(), "nip46");
-}
-
-#[test]
-fn config_errors_format_operator_facing_messages() {
- let formatted = [
- SdkConfigError::MissingCustomRelayUrls.to_string(),
- SdkConfigError::MissingCustomRadrootsdEndpoint.to_string(),
- SdkConfigError::EmptyRelayUrl.to_string(),
- SdkConfigError::InvalidRelayUrl("http://relay.example".into()).to_string(),
- SdkConfigError::EmptyRadrootsdEndpoint.to_string(),
- SdkConfigError::InvalidRadrootsdEndpoint("ws://rpc.example".into()).to_string(),
- ];
-
- assert_eq!(
- formatted,
- [
- "custom sdk environment requires explicit relay urls",
- "custom sdk environment requires an explicit radrootsd endpoint",
- "relay url must not be empty",
- "relay url must use ws or wss, got `http://relay.example`",
- "radrootsd endpoint must not be empty",
- "radrootsd endpoint must use http or https, got `ws://rpc.example`",
- ]
- );
-}
-
-#[test]
-fn radrootsd_auth_debug_formats_none_and_redacts_bearer_tokens() {
- assert_eq!(format!("{:?}", RadrootsdAuth::None), "None");
-
- let bearer = RadrootsdAuth::BearerToken("sdk-secret-token".to_owned());
- let debug = format!("{bearer:?}");
-
- assert!(!debug.contains("sdk-secret-token"));
- assert_eq!(debug, "BearerToken(\"<redacted>\")");
-}
-
-#[test]
-fn sdk_config_debug_redacts_bearer_tokens() {
- let mut config = RadrootsSdkConfig::production();
- config.radrootsd.auth = RadrootsdAuth::BearerToken("sdk-secret-token".to_owned());
-
- let debug = format!("{config:?}");
-
- assert!(!debug.contains("sdk-secret-token"));
- assert!(debug.contains("BearerToken(\"<redacted>\")"));
-}
diff --git a/crates/sdk/tests/facade.rs b/crates/sdk/tests/facade.rs
@@ -1,269 +0,0 @@
-use radroots_core::{
- RadrootsCoreCurrency, RadrootsCoreDecimal, RadrootsCoreMoney, RadrootsCoreQuantity,
- RadrootsCoreQuantityPrice, RadrootsCoreUnit,
-};
-use radroots_events::farm::{RadrootsFarm, RadrootsFarmRef};
-use radroots_events::ids::RadrootsPublicKey;
-use radroots_events::kinds::{KIND_FARM, KIND_LISTING, KIND_ORDER_REQUEST, KIND_PROFILE};
-use radroots_events::listing::{
- RadrootsListing, RadrootsListingAvailability, RadrootsListingBin,
- RadrootsListingDeliveryMethod, RadrootsListingLocation, RadrootsListingProduct,
- RadrootsListingStatus,
-};
-use radroots_events::order::{
- RadrootsOrderEconomicItem, RadrootsOrderEconomics, RadrootsOrderItem,
- RadrootsOrderPricingBasis, RadrootsOrderRequest,
-};
-use radroots_events::profile::{RadrootsProfile, RadrootsProfileType};
-use radroots_sdk::{RadrootsNostrEvent, RadrootsNostrEventPtr, farm, listing, order, profile};
-
-fn sample_profile() -> RadrootsProfile {
- RadrootsProfile {
- name: "North Farm".into(),
- display_name: Some("North Farm".into()),
- nip05: None,
- about: Some("Organic coffee".into()),
- website: Some("https://example.com".into()),
- picture: None,
- banner: None,
- lud06: None,
- lud16: None,
- bot: None,
- }
-}
-
-fn sample_farm() -> RadrootsFarm {
- RadrootsFarm {
- d_tag: "AAAAAAAAAAAAAAAAAAAAAA".into(),
- name: "North Farm".into(),
- about: Some("Organic coffee".into()),
- website: None,
- picture: None,
- banner: None,
- location: None,
- tags: Some(vec!["coffee".into()]),
- }
-}
-
-fn sample_listing() -> RadrootsListing {
- RadrootsListing {
- d_tag: "AAAAAAAAAAAAAAAAAAAAAg".parse().expect("listing d tag"),
- published_at: None,
- farm: RadrootsFarmRef {
- pubkey: "seller".into(),
- d_tag: "AAAAAAAAAAAAAAAAAAAAAA".into(),
- },
- product: RadrootsListingProduct {
- key: "coffee".into(),
- title: "Coffee".into(),
- category: "coffee".into(),
- summary: Some("Single origin coffee".into()),
- process: None,
- lot: None,
- location: None,
- profile: None,
- year: None,
- },
- primary_bin_id: "bin-1".parse().expect("primary bin id"),
- bins: vec![RadrootsListingBin {
- bin_id: "bin-1".parse().expect("bin id"),
- quantity: RadrootsCoreQuantity::new(
- RadrootsCoreDecimal::from(1000u32),
- RadrootsCoreUnit::MassG,
- ),
- price_per_canonical_unit: RadrootsCoreQuantityPrice {
- amount: RadrootsCoreMoney::new(
- RadrootsCoreDecimal::from(20u32),
- RadrootsCoreCurrency::USD,
- ),
- quantity: RadrootsCoreQuantity::new(
- RadrootsCoreDecimal::from(1u32),
- RadrootsCoreUnit::MassG,
- ),
- },
- display_amount: None,
- display_unit: None,
- display_label: None,
- display_price: None,
- display_price_unit: None,
- }],
- resource_area: None,
- plot: None,
- discounts: None,
- inventory_available: Some(RadrootsCoreDecimal::from(5u32)),
- availability: Some(RadrootsListingAvailability::Status {
- status: RadrootsListingStatus::Active,
- }),
- delivery_method: Some(RadrootsListingDeliveryMethod::Pickup),
- location: Some(RadrootsListingLocation {
- primary: "North Farm".into(),
- city: None,
- region: None,
- country: None,
- lat: None,
- lng: None,
- geohash: None,
- }),
- images: None,
- }
-}
-
-fn listing_event(listing_value: &RadrootsListing) -> RadrootsNostrEvent {
- let parts = listing::build_draft(listing_value).expect("listing draft");
- RadrootsNostrEvent {
- id: "event-1".into(),
- author: "seller".into(),
- created_at: 1,
- kind: parts.as_wire_parts().kind,
- tags: parts.as_wire_parts().tags.clone(),
- content: parts.as_wire_parts().content.clone(),
- sig: String::new(),
- }
-}
-
-fn listing_event_ptr() -> RadrootsNostrEventPtr {
- RadrootsNostrEventPtr {
- id: core::iter::repeat_n('a', 64).collect(),
- relays: Some("wss://listing.relay.example".into()),
- }
-}
-
-fn public_key(character: char) -> RadrootsPublicKey {
- core::iter::repeat_n(character, 64)
- .collect::<String>()
- .parse()
- .expect("public key")
-}
-
-fn sample_order_request() -> RadrootsOrderRequest {
- let seller_pubkey = public_key('a');
-
- RadrootsOrderRequest {
- order_id: "order-1".parse().expect("order id"),
- listing_addr: format!("{KIND_LISTING}:{seller_pubkey}:AAAAAAAAAAAAAAAAAAAAAg")
- .parse()
- .expect("listing address"),
- buyer_pubkey: public_key('b'),
- seller_pubkey,
- items: vec![RadrootsOrderItem {
- bin_id: "bin-1".parse().expect("bin id"),
- bin_count: 2,
- }],
- economics: RadrootsOrderEconomics {
- quote_id: "quote-1".parse().expect("quote id"),
- quote_version: 1,
- pricing_basis: RadrootsOrderPricingBasis::ListingEvent,
- currency: RadrootsCoreCurrency::USD,
- items: vec![RadrootsOrderEconomicItem {
- bin_id: "bin-1".parse().expect("bin id"),
- bin_count: 2,
- quantity_amount: RadrootsCoreDecimal::from(1u32),
- quantity_unit: RadrootsCoreUnit::Each,
- unit_price_amount: RadrootsCoreDecimal::from(5u32),
- unit_price_currency: RadrootsCoreCurrency::USD,
- line_subtotal: RadrootsCoreMoney::new(
- RadrootsCoreDecimal::from(10u32),
- RadrootsCoreCurrency::USD,
- ),
- }],
- discounts: Vec::new(),
- adjustments: Vec::new(),
- subtotal: RadrootsCoreMoney::new(
- RadrootsCoreDecimal::from(10u32),
- RadrootsCoreCurrency::USD,
- ),
- discount_total: RadrootsCoreMoney::new(
- RadrootsCoreDecimal::from(0u32),
- RadrootsCoreCurrency::USD,
- ),
- adjustment_total: RadrootsCoreMoney::new(
- RadrootsCoreDecimal::from(0u32),
- RadrootsCoreCurrency::USD,
- ),
- total: RadrootsCoreMoney::new(
- RadrootsCoreDecimal::from(10u32),
- RadrootsCoreCurrency::USD,
- ),
- },
- }
-}
-
-#[test]
-fn profile_build_draft_wraps_profile_encoder() {
- let parts =
- profile::build_draft(&sample_profile(), Some(RadrootsProfileType::Farm)).expect("profile");
-
- assert_eq!(parts.kind, KIND_PROFILE);
- assert!(parts.tags.iter().any(|tag| {
- tag.first().map(|value| value.as_str()) == Some("t")
- && tag.get(1).map(|value| value.as_str()) == Some("radroots:type:farm")
- }));
-}
-
-#[test]
-fn farm_build_draft_wraps_farm_encoder() {
- let parts = farm::build_draft(&sample_farm()).expect("farm");
-
- assert_eq!(parts.kind, KIND_FARM);
- assert!(
- parts
- .tags
- .iter()
- .any(|tag| tag.first().map(|value| value.as_str()) == Some("d"))
- );
-}
-
-#[test]
-fn listing_facade_wraps_build_parse_and_validate() {
- let listing_value = sample_listing();
- let tags = listing::build_tags(&listing_value).expect("listing tags");
- assert!(!tags.is_empty());
-
- let event = listing_event(&listing_value);
- let parsed = listing::parse_event(&event).expect("parsed listing");
- assert_eq!(parsed.d_tag, listing_value.d_tag);
-
- let validated = order::validate_listing_event(&event).expect("validated listing");
- assert_eq!(validated.listing_id, listing_value.d_tag);
- assert_eq!(event.kind, KIND_LISTING);
-}
-
-#[test]
-fn listing_parse_rejects_non_listing_kind() {
- let listing_value = sample_listing();
- let mut event = listing_event(&listing_value);
- event.kind = KIND_PROFILE;
-
- assert_eq!(
- listing::parse_event(&event).expect_err("listing kind error"),
- listing::RadrootsListingParseError::InvalidKind(KIND_PROFILE)
- );
-}
-
-#[test]
-fn order_facade_wraps_build_parse_and_address_ops() {
- let listing_value = sample_listing();
- let seller_pubkey = "a".repeat(64);
- let listing_addr = format!("{KIND_LISTING}:{seller_pubkey}:{}", listing_value.d_tag);
- let payload = sample_order_request();
- let parts =
- order::build_order_request_draft(&listing_event_ptr(), &payload).expect("order draft");
-
- assert_eq!(parts.as_wire_parts().kind, KIND_ORDER_REQUEST);
-
- let parsed_addr = order::parse_listing_address(&listing_addr).expect("listing address");
- assert_eq!(parsed_addr.listing_id, listing_value.d_tag);
-
- let event = RadrootsNostrEvent {
- id: core::iter::repeat_n('b', 64).collect(),
- author: payload.buyer_pubkey.to_string(),
- created_at: 2,
- kind: parts.as_wire_parts().kind,
- tags: parts.as_wire_parts().tags.clone(),
- content: parts.as_wire_parts().content.clone(),
- sig: String::new(),
- };
- let envelope = order::parse_order_request(&event).expect("order envelope");
- assert_eq!(envelope.payload.order_id, payload.order_id);
- assert_eq!(envelope.payload.listing_addr, listing_addr);
-}
diff --git a/crates/sdk/tests/radrootsd.rs b/crates/sdk/tests/radrootsd.rs
@@ -1,1952 +0,0 @@
-#![cfg(feature = "radrootsd-client")]
-
-use radroots_core::{
- RadrootsCoreCurrency, RadrootsCoreDecimal, RadrootsCoreMoney, RadrootsCoreQuantity,
- RadrootsCoreQuantityPrice, RadrootsCoreUnit,
-};
-use radroots_events::farm::{RadrootsFarm, RadrootsFarmLocation, RadrootsFarmRef};
-use radroots_events::ids::RadrootsPublicKey;
-use radroots_events::kinds::{
- KIND_FARM, KIND_LISTING, KIND_LISTING_DRAFT, KIND_ORDER_REQUEST, KIND_PROFILE,
-};
-use radroots_sdk::adapters::radrootsd::{
- SdkRadrootsdBridgeJob, SdkRadrootsdBridgePublishResponse, SdkRadrootsdListingPublishRequest,
- SdkRadrootsdSignerAuthority, SdkRadrootsdSignerSessionConnectRequest,
- SdkRadrootsdSignerSessionMode,
-};
-use radroots_sdk::listing::{
- RadrootsListing, RadrootsListingAvailability, RadrootsListingBin,
- RadrootsListingDeliveryMethod, RadrootsListingLocation, RadrootsListingParseError,
- RadrootsListingProduct, RadrootsListingStatus,
-};
-use radroots_sdk::order::{
- RadrootsOrderEconomicItem, RadrootsOrderEconomicLine, RadrootsOrderEconomics,
- RadrootsOrderItem, RadrootsOrderPricingBasis, RadrootsOrderRequest,
-};
-use radroots_sdk::{
- RadrootsNostrEvent, RadrootsNostrEventPtr, RadrootsProfile, RadrootsProfileType,
- RadrootsSdkClient, RadrootsSdkConfig, RadrootsdAuth, RadrootsdConfig, SdkConfigError,
- SdkEnvironment, SdkPublishError, SdkRadrootsdBridgeDeliveryPolicy, SdkRadrootsdBridgeError,
- SdkRadrootsdBridgeJobStatus, SdkRadrootsdFarmPublishOptions, SdkRadrootsdListingPublishOptions,
- SdkRadrootsdOrderRequestPublishOptions, SdkRadrootsdProfilePublishOptions,
- SdkRadrootsdPublishReceipt, SdkRadrootsdSessionError, SdkRadrootsdSignerSessionHandle,
- SdkRadrootsdSignerSessionRole, SdkRadrootsdSignerSessionView, SdkTransportMode,
- SdkTransportReceipt, SignerConfig,
-};
-use serde_json::{Value, json};
-use std::collections::VecDeque;
-use tokio::io::{AsyncReadExt, AsyncWriteExt};
-use tokio::net::TcpListener;
-use tokio::sync::{mpsc, oneshot};
-
-type TestResult<T> = Result<T, Box<dyn std::error::Error + Send + Sync>>;
-
-struct JsonRpcServer {
- endpoint: String,
- shutdown_tx: Option<oneshot::Sender<()>>,
-}
-
-impl JsonRpcServer {
- async fn spawn(
- expected_auth: Option<&str>,
- response_body: Value,
- ) -> TestResult<(Self, oneshot::Receiver<Value>)> {
- let listener = TcpListener::bind("127.0.0.1:0").await?;
- let addr = listener.local_addr()?;
- let endpoint = format!("http://{addr}/jsonrpc");
- let (shutdown_tx, mut shutdown_rx) = oneshot::channel();
- let (request_tx, request_rx) = oneshot::channel();
- let expected_auth = expected_auth.map(str::to_owned);
- let response_text = response_body.to_string();
-
- tokio::spawn(async move {
- loop {
- tokio::select! {
- _ = &mut shutdown_rx => break,
- accept = listener.accept() => {
- let Ok((mut stream, _)) = accept else {
- break;
- };
- let mut buffer = Vec::new();
- let mut chunk = [0_u8; 4096];
- let header_end = loop {
- let Ok(read) = stream.read(&mut chunk).await else {
- return;
- };
- if read == 0 {
- return;
- }
- buffer.extend_from_slice(&chunk[..read]);
- if let Some(index) = find_headers_end(&buffer) {
- break index;
- }
- };
-
- let headers = String::from_utf8_lossy(&buffer[..header_end]).into_owned();
- let content_length = parse_content_length(headers.as_str()).unwrap_or(0);
- let body_start = header_end + 4;
- while buffer.len().saturating_sub(body_start) < content_length {
- let Ok(read) = stream.read(&mut chunk).await else {
- return;
- };
- if read == 0 {
- break;
- }
- buffer.extend_from_slice(&chunk[..read]);
- }
-
- if let Some(expected_auth) = expected_auth.as_deref() {
- let actual_auth = parse_authorization(headers.as_str());
- if actual_auth.as_deref() != Some(expected_auth) {
- let _ = write_http_response(
- &mut stream,
- 401,
- json!({
- "jsonrpc": "2.0",
- "id": "sdk-test",
- "error": {
- "code": -32001,
- "message": format!(
- "unexpected authorization header: {:?}",
- actual_auth
- ),
- }
- })
- .to_string()
- .as_str(),
- )
- .await;
- return;
- }
- }
-
- let body = &buffer[body_start..body_start + content_length];
- let Ok(request_json) = serde_json::from_slice::<Value>(body) else {
- return;
- };
- let _ = request_tx.send(request_json);
- let _ = write_http_response(&mut stream, 200, response_text.as_str()).await;
- break;
- }
- }
- }
- });
-
- Ok((
- Self {
- endpoint,
- shutdown_tx: Some(shutdown_tx),
- },
- request_rx,
- ))
- }
-
- fn endpoint(&self) -> &str {
- self.endpoint.as_str()
- }
-}
-
-struct JsonRpcSequenceServer {
- endpoint: String,
- shutdown_tx: Option<oneshot::Sender<()>>,
-}
-
-impl JsonRpcSequenceServer {
- async fn spawn(
- expected_auth: Option<&str>,
- response_bodies: Vec<Value>,
- ) -> TestResult<(Self, mpsc::UnboundedReceiver<Value>)> {
- let listener = TcpListener::bind("127.0.0.1:0").await?;
- let addr = listener.local_addr()?;
- let endpoint = format!("http://{addr}/jsonrpc");
- let (shutdown_tx, mut shutdown_rx) = oneshot::channel();
- let (request_tx, request_rx) = mpsc::unbounded_channel();
- let expected_auth = expected_auth.map(str::to_owned);
- let mut response_texts = response_bodies
- .into_iter()
- .map(|value| value.to_string())
- .collect::<VecDeque<_>>();
-
- tokio::spawn(async move {
- loop {
- if response_texts.is_empty() {
- break;
- }
-
- tokio::select! {
- _ = &mut shutdown_rx => break,
- accept = listener.accept() => {
- let Ok((mut stream, _)) = accept else {
- break;
- };
- let mut buffer = Vec::new();
- let mut chunk = [0_u8; 4096];
- let header_end = loop {
- let Ok(read) = stream.read(&mut chunk).await else {
- return;
- };
- if read == 0 {
- return;
- }
- buffer.extend_from_slice(&chunk[..read]);
- if let Some(index) = find_headers_end(&buffer) {
- break index;
- }
- };
-
- let headers = String::from_utf8_lossy(&buffer[..header_end]).into_owned();
- let content_length = parse_content_length(headers.as_str()).unwrap_or(0);
- let body_start = header_end + 4;
- while buffer.len().saturating_sub(body_start) < content_length {
- let Ok(read) = stream.read(&mut chunk).await else {
- return;
- };
- if read == 0 {
- break;
- }
- buffer.extend_from_slice(&chunk[..read]);
- }
-
- if let Some(expected_auth) = expected_auth.as_deref() {
- let actual_auth = parse_authorization(headers.as_str());
- if actual_auth.as_deref() != Some(expected_auth) {
- let _ = write_http_response(
- &mut stream,
- 401,
- json!({
- "jsonrpc": "2.0",
- "id": "sdk-test",
- "error": {
- "code": -32001,
- "message": format!(
- "unexpected authorization header: {:?}",
- actual_auth
- ),
- }
- })
- .to_string()
- .as_str(),
- )
- .await;
- return;
- }
- }
-
- let body = &buffer[body_start..body_start + content_length];
- let Ok(request_json) = serde_json::from_slice::<Value>(body) else {
- return;
- };
- let _ = request_tx.send(request_json);
- let Some(response_text) = response_texts.pop_front() else {
- return;
- };
- let _ = write_http_response(&mut stream, 200, response_text.as_str()).await;
- }
- }
- }
- });
-
- Ok((
- Self {
- endpoint,
- shutdown_tx: Some(shutdown_tx),
- },
- request_rx,
- ))
- }
-
- fn endpoint(&self) -> &str {
- self.endpoint.as_str()
- }
-}
-
-impl Drop for JsonRpcSequenceServer {
- fn drop(&mut self) {
- if let Some(shutdown_tx) = self.shutdown_tx.take() {
- let _ = shutdown_tx.send(());
- }
- }
-}
-
-impl Drop for JsonRpcServer {
- fn drop(&mut self) {
- if let Some(shutdown_tx) = self.shutdown_tx.take() {
- let _ = shutdown_tx.send(());
- }
- }
-}
-
-fn find_headers_end(buffer: &[u8]) -> Option<usize> {
- buffer.windows(4).position(|window| window == b"\r\n\r\n")
-}
-
-fn parse_content_length(headers: &str) -> Option<usize> {
- headers.lines().find_map(|line| {
- let (name, value) = line.split_once(':')?;
- if !name.eq_ignore_ascii_case("content-length") {
- return None;
- }
- value.trim().parse().ok()
- })
-}
-
-fn parse_authorization(headers: &str) -> Option<String> {
- headers.lines().find_map(|line| {
- let (name, value) = line.split_once(':')?;
- if !name.eq_ignore_ascii_case("authorization") {
- return None;
- }
- Some(value.trim().to_owned())
- })
-}
-
-async fn write_http_response(
- stream: &mut tokio::net::TcpStream,
- status: u16,
- body: &str,
-) -> Result<(), std::io::Error> {
- let status_text = match status {
- 200 => "OK",
- 401 => "Unauthorized",
- _ => "Internal Server Error",
- };
- let response = format!(
- "HTTP/1.1 {status} {status_text}\r\ncontent-type: application/json\r\ncontent-length: {}\r\nconnection: close\r\n\r\n{}",
- body.len(),
- body
- );
- stream.write_all(response.as_bytes()).await
-}
-
-fn sample_listing() -> RadrootsListing {
- RadrootsListing {
- d_tag: "AAAAAAAAAAAAAAAAAAAAAg".parse().expect("listing d tag"),
- published_at: None,
- farm: RadrootsFarmRef {
- pubkey: "seller".into(),
- d_tag: "AAAAAAAAAAAAAAAAAAAAAA".into(),
- },
- product: RadrootsListingProduct {
- key: "coffee".into(),
- title: "Coffee".into(),
- category: "coffee".into(),
- summary: Some("Single origin coffee".into()),
- process: None,
- lot: None,
- location: None,
- profile: None,
- year: None,
- },
- primary_bin_id: "bin-1".parse().expect("primary bin id"),
- bins: vec![RadrootsListingBin {
- bin_id: "bin-1".parse().expect("bin id"),
- quantity: RadrootsCoreQuantity::new(
- RadrootsCoreDecimal::from(1000u32),
- RadrootsCoreUnit::MassG,
- ),
- price_per_canonical_unit: RadrootsCoreQuantityPrice {
- amount: RadrootsCoreMoney::new(
- RadrootsCoreDecimal::from(20u32),
- RadrootsCoreCurrency::USD,
- ),
- quantity: RadrootsCoreQuantity::new(
- RadrootsCoreDecimal::from(1u32),
- RadrootsCoreUnit::MassG,
- ),
- },
- display_amount: None,
- display_unit: None,
- display_label: None,
- display_price: None,
- display_price_unit: None,
- }],
- resource_area: None,
- plot: None,
- discounts: None,
- inventory_available: Some(RadrootsCoreDecimal::from(5u32)),
- availability: Some(RadrootsListingAvailability::Status {
- status: RadrootsListingStatus::Active,
- }),
- delivery_method: Some(RadrootsListingDeliveryMethod::Pickup),
- location: Some(RadrootsListingLocation {
- primary: "North Farm".into(),
- city: None,
- region: None,
- country: None,
- lat: None,
- lng: None,
- geohash: None,
- }),
- images: None,
- }
-}
-
-fn sample_profile() -> RadrootsProfile {
- RadrootsProfile {
- name: "North Farm".into(),
- display_name: Some("North Farm".into()),
- nip05: None,
- about: Some("Coffee farm".into()),
- website: Some("https://example.invalid/north-farm".into()),
- picture: None,
- banner: None,
- lud06: None,
- lud16: None,
- bot: None,
- }
-}
-
-fn sample_farm() -> RadrootsFarm {
- RadrootsFarm {
- d_tag: "AAAAAAAAAAAAAAAAAAAAAA".into(),
- name: "North Farm".into(),
- about: Some("Coffee farm".into()),
- website: Some("https://example.invalid/north-farm".into()),
- picture: None,
- banner: None,
- location: Some(RadrootsFarmLocation {
- primary: Some("North Farm".into()),
- city: Some("San Francisco".into()),
- region: Some("CA".into()),
- country: Some("US".into()),
- gcs: None,
- }),
- tags: Some(vec!["coffee".into()]),
- }
-}
-
-fn sample_order_request_economics() -> RadrootsOrderEconomics {
- RadrootsOrderEconomics {
- quote_id: "quote-1".parse().expect("quote id"),
- quote_version: 1,
- pricing_basis: RadrootsOrderPricingBasis::ListingEvent,
- currency: RadrootsCoreCurrency::USD,
- items: vec![RadrootsOrderEconomicItem {
- bin_id: "bin-1".parse().expect("bin id"),
- bin_count: 2,
- quantity_amount: RadrootsCoreDecimal::from(1u32),
- quantity_unit: RadrootsCoreUnit::MassG,
- unit_price_amount: RadrootsCoreDecimal::from(20u32),
- unit_price_currency: RadrootsCoreCurrency::USD,
- line_subtotal: RadrootsCoreMoney::new(
- RadrootsCoreDecimal::from(40u32),
- RadrootsCoreCurrency::USD,
- ),
- }],
- discounts: Vec::<RadrootsOrderEconomicLine>::new(),
- adjustments: Vec::<RadrootsOrderEconomicLine>::new(),
- subtotal: RadrootsCoreMoney::new(
- RadrootsCoreDecimal::from(40u32),
- RadrootsCoreCurrency::USD,
- ),
- discount_total: RadrootsCoreMoney::new(
- RadrootsCoreDecimal::from(0u32),
- RadrootsCoreCurrency::USD,
- ),
- adjustment_total: RadrootsCoreMoney::new(
- RadrootsCoreDecimal::from(0u32),
- RadrootsCoreCurrency::USD,
- ),
- total: RadrootsCoreMoney::new(RadrootsCoreDecimal::from(40u32), RadrootsCoreCurrency::USD),
- }
-}
-
-fn sample_order_request() -> RadrootsOrderRequest {
- let seller_pubkey: RadrootsPublicKey = "a".repeat(64).parse().expect("seller public key");
-
- RadrootsOrderRequest {
- order_id: "order-1".parse().expect("order id"),
- listing_addr: format!("{KIND_LISTING}:{seller_pubkey}:AAAAAAAAAAAAAAAAAAAAAg")
- .parse()
- .expect("listing address"),
- buyer_pubkey: "b".repeat(64).parse().expect("buyer public key"),
- seller_pubkey,
- items: vec![RadrootsOrderItem {
- bin_id: "bin-1".parse().expect("bin id"),
- bin_count: 2,
- }],
- economics: sample_order_request_economics(),
- }
-}
-
-fn listing_event_ptr_with_relays(relays: Option<&str>) -> RadrootsNostrEventPtr {
- RadrootsNostrEventPtr {
- id: "a".repeat(64),
- relays: relays.map(str::to_owned),
- }
-}
-
-fn sdk_event(
- author: &str,
- created_at: u32,
- draft: radroots_sdk::listing::RadrootsListingDraft,
-) -> RadrootsNostrEvent {
- let parts = draft.into_wire_parts();
- RadrootsNostrEvent {
- id: "event-1".to_owned(),
- author: author.to_owned(),
- created_at,
- kind: parts.kind,
- tags: parts.tags,
- content: parts.content,
- sig: "f".repeat(128),
- }
-}
-
-fn radrootsd_test_client(endpoint: &str) -> Result<RadrootsSdkClient, SdkConfigError> {
- let mut config = RadrootsSdkConfig::for_environment(SdkEnvironment::Production);
- config.transport = SdkTransportMode::Radrootsd;
- config.signer = SignerConfig::Nip46;
- config.radrootsd = RadrootsdConfig {
- endpoint: Some(endpoint.to_owned()),
- auth: RadrootsdAuth::BearerToken("sdk-secret".to_owned()),
- };
- RadrootsSdkClient::from_config(config)
-}
-
-fn sample_session_view_json(session_id: &str) -> Value {
- json!({
- "session_id": session_id,
- "role": "outbound_remote_signer",
- "client_pubkey": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
- "signer_pubkey": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
- "user_pubkey": "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc",
- "relays": ["wss://radroots.org"],
- "permissions": ["sign_event:30402"],
- "name": "Radroots Signer",
- "url": "https://radroots.org/signers/demo",
- "image": "https://radroots.org/signers/demo.png",
- "auth_required": false,
- "authorized": true,
- "auth_url": null,
- "expires_in_secs": 120,
- "signer_authority": {
- "provider_runtime_id": "runtime-1",
- "account_identity_id": "identity-1",
- "provider_signer_session_id": "provider-session-123"
- }
- })
-}
-
-fn sample_bridge_status_json() -> Value {
- json!({
- "enabled": true,
- "ready": true,
- "auth_mode": "bearer_token",
- "signer_mode": "selectable_per_request",
- "default_signer_mode": "embedded_service_identity",
- "supported_signer_modes": ["embedded_service_identity", "nip46_session"],
- "available_nip46_signer_sessions": 2,
- "relay_count": 1,
- "delivery_policy": "quorum",
- "delivery_quorum": 1,
- "publish_max_attempts": 3,
- "publish_initial_backoff_millis": 250,
- "publish_max_backoff_millis": 4000,
- "job_status_retention": 64,
- "retained_jobs": 4,
- "retained_idempotency_keys": 2,
- "accepted_jobs": 1,
- "published_jobs": 2,
- "failed_jobs": 1,
- "recovered_failed_jobs": 0,
- "methods": ["bridge.status", "bridge.job.status", "bridge.job.list", "bridge.listing.publish"]
- })
-}
-
-fn sample_bridge_job_json(job_id: &str) -> Value {
- sample_bridge_job_json_for(job_id, "bridge.listing.publish", 30402)
-}
-
-fn sample_bridge_job_json_for(job_id: &str, command: &str, event_kind: u32) -> Value {
- json!({
- "job_id": job_id,
- "command": command,
- "idempotency_key": "idem-bridge-1",
- "status": "published",
- "terminal": true,
- "recovered_after_restart": false,
- "requested_at_unix": 1720000000u64,
- "completed_at_unix": 1720000001u64,
- "signer_mode": "nip46_session",
- "signer_session_id": "session-123",
- "event_kind": event_kind,
- "event_id": "event-bridge-1",
- "event_addr": "30402:seller:listing-bridge-1",
- "delivery_policy": "quorum",
- "delivery_quorum": 1,
- "relay_count": 2,
- "acknowledged_relay_count": 1,
- "required_acknowledged_relay_count": 1,
- "attempt_count": 1,
- "attempt_summaries": ["attempt 1: 1/2 relays acknowledged"],
- "relay_results": [
- {
- "relay_url": "wss://radroots.org",
- "acknowledged": true,
- "detail": null
- },
- {
- "relay_url": "wss://backup.radroots.org",
- "acknowledged": false,
- "detail": "timeout"
- }
- ],
- "relay_outcome_summary": "quorum satisfied with 1/2 relay acknowledgements"
- })
-}
-
-async fn connected_bunker_session_handle(
- session_id: &str,
-) -> TestResult<SdkRadrootsdSignerSessionHandle> {
- let (server, _) = JsonRpcServer::spawn(
- Some("Bearer sdk-secret"),
- json!({
- "jsonrpc": "2.0",
- "id": "radroots-sdk-nip46-connect",
- "result": {
- "session_id": session_id,
- "mode": "Bunker",
- "remote_signer_pubkey": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
- "client_pubkey": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
- "relays": ["wss://radroots.org"]
- }
- }),
- )
- .await?;
- let client = radrootsd_test_client(server.endpoint())?;
- client
- .radrootsd()
- .signer_sessions()
- .connect_bunker(
- "bunker://aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa?relay=wss%3A%2F%2Fradroots.org&secret=shared-secret",
- )
- .await
- .map_err(Into::into)
-}
-
-#[test]
-fn radrootsd_debug_redacts_signer_session_values() {
- let signer_authority = SdkRadrootsdSignerAuthority {
- provider_runtime_id: "runtime-1".to_owned(),
- account_identity_id: "identity-1".to_owned(),
- provider_signer_session_id: Some("provider-session-123".to_owned()),
- };
- let request = SdkRadrootsdListingPublishRequest {
- listing: sample_listing(),
- kind: Some(30402),
- signer_session_id: "session-123".to_owned(),
- signer_authority: Some(signer_authority),
- idempotency_key: Some("idem-1".to_owned()),
- };
- let job = SdkRadrootsdBridgeJob {
- job_id: "job-1".to_owned(),
- command: "bridge.listing.publish".to_owned(),
- status: "published".to_owned(),
- terminal: true,
- recovered_after_restart: false,
- signer_mode: "nip46_session:session-123".to_owned(),
- signer_session_id: Some("session-123".to_owned()),
- event_kind: 30402,
- event_id: Some("event-1".to_owned()),
- event_addr: Some("30402:seller:listing-1".to_owned()),
- relay_count: 1,
- acknowledged_relay_count: 1,
- };
- let response = SdkRadrootsdBridgePublishResponse {
- deduplicated: false,
- job,
- };
- let receipt = SdkRadrootsdPublishReceipt {
- accepted: true,
- deduplicated: false,
- job_id: Some("job-1".to_owned()),
- status: Some("published".to_owned()),
- signer_mode: Some("nip46_session:session-123".to_owned()),
- signer_session_id: Some("session-123".to_owned()),
- event_addr: Some("30402:seller:listing-1".to_owned()),
- relay_count: Some(1),
- acknowledged_relay_count: Some(1),
- };
-
- let request_debug = format!("{request:?}");
- let response_debug = format!("{response:?}");
- let receipt_debug = format!("{receipt:?}");
-
- assert!(!request_debug.contains("session-123"));
- assert!(!request_debug.contains("provider-session-123"));
- assert!(request_debug.contains("<redacted>"));
-
- assert!(!response_debug.contains("session-123"));
- assert!(response_debug.contains("<redacted>"));
-
- assert!(!receipt_debug.contains("session-123"));
- assert!(receipt_debug.contains("<redacted>"));
-
- let connect_request = SdkRadrootsdSignerSessionConnectRequest::nostrconnect(
- "nostrconnect://bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb?relay=wss%3A%2F%2Fradroots.org&secret=shared-secret",
- "client-secret-key",
- )
- .with_signer_authority(SdkRadrootsdSignerAuthority {
- provider_runtime_id: "runtime-1".to_owned(),
- account_identity_id: "identity-1".to_owned(),
- provider_signer_session_id: Some("provider-session-123".to_owned()),
- });
- let connect_request_debug = format!("{connect_request:?}");
- assert!(!connect_request_debug.contains("client-secret-key"));
- assert!(!connect_request_debug.contains("provider-session-123"));
- assert!(connect_request_debug.contains("<redacted>"));
-}
-
-#[tokio::test]
-async fn radrootsd_signer_session_connect_returns_opaque_handle() -> TestResult<()> {
- let (server, request_rx) = JsonRpcServer::spawn(
- Some("Bearer sdk-secret"),
- json!({
- "jsonrpc": "2.0",
- "id": "radroots-sdk-nip46-connect",
- "result": {
- "session_id": "session-123",
- "mode": "Nostrconnect",
- "remote_signer_pubkey": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
- "client_pubkey": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
- "relays": ["wss://radroots.org"]
- }
- }),
- )
- .await?;
-
- let mut config = RadrootsSdkConfig::for_environment(SdkEnvironment::Production);
- config.transport = SdkTransportMode::Radrootsd;
- config.signer = SignerConfig::Nip46;
- config.radrootsd = RadrootsdConfig {
- endpoint: Some(server.endpoint().to_owned()),
- auth: RadrootsdAuth::BearerToken("sdk-secret".to_owned()),
- };
- let client = RadrootsSdkClient::from_config(config)?;
- let request = SdkRadrootsdSignerSessionConnectRequest::nostrconnect(
- "nostrconnect://bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb?relay=wss%3A%2F%2Fradroots.org&secret=shared-secret",
- "client-secret-key",
- );
-
- let handle: SdkRadrootsdSignerSessionHandle = client
- .radrootsd()
- .signer_sessions()
- .connect(&request)
- .await?;
- let request_json = request_rx.await?;
-
- assert_eq!(request_json["method"], "nip46.connect");
- assert_eq!(
- request_json["params"]["url"],
- "nostrconnect://bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb?relay=wss%3A%2F%2Fradroots.org&secret=shared-secret"
- );
- assert_eq!(
- request_json["params"]["client_secret_key"],
- "client-secret-key"
- );
- assert_eq!(handle.mode(), SdkRadrootsdSignerSessionMode::Nostrconnect);
- assert_eq!(
- handle.remote_signer_pubkey(),
- "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
- );
- assert_eq!(
- handle.client_pubkey(),
- "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
- );
- assert_eq!(handle.relays(), &["wss://radroots.org".to_owned()]);
-
- let handle_debug = format!("{handle:?}");
- assert!(!handle_debug.contains("session-123"));
- assert!(handle_debug.contains("<redacted>"));
-
- let options = SdkRadrootsdListingPublishOptions::from_signer_session(&handle);
- let options_debug = format!("{options:?}");
- assert!(!options_debug.contains("session-123"));
- assert!(options_debug.contains("<redacted>"));
-
- Ok(())
-}
-
-#[tokio::test]
-async fn radrootsd_signer_session_connect_bunker_supports_bunker_mode() -> TestResult<()> {
- let (server, request_rx) = JsonRpcServer::spawn(
- Some("Bearer sdk-secret"),
- json!({
- "jsonrpc": "2.0",
- "id": "radroots-sdk-nip46-connect",
- "result": {
- "session_id": "session-bunker",
- "mode": "Bunker",
- "remote_signer_pubkey": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
- "client_pubkey": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
- "relays": ["wss://radroots.org"]
- }
- }),
- )
- .await?;
-
- let client = radrootsd_test_client(server.endpoint())?;
- let handle: SdkRadrootsdSignerSessionHandle = client
- .radrootsd()
- .signer_sessions()
- .connect_bunker(
- "bunker://aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa?relay=wss%3A%2F%2Fradroots.org&secret=shared-secret",
- )
- .await?;
- let request_json = request_rx.await?;
-
- assert_eq!(request_json["method"], "nip46.connect");
- assert_eq!(
- request_json["params"]["url"],
- "bunker://aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa?relay=wss%3A%2F%2Fradroots.org&secret=shared-secret"
- );
- assert!(request_json["params"]["client_secret_key"].is_null());
- assert_eq!(handle.mode(), SdkRadrootsdSignerSessionMode::Bunker);
-
- Ok(())
-}
-
-#[tokio::test]
-async fn radrootsd_signer_session_status_returns_typed_view() -> TestResult<()> {
- let (connect_server, _) = JsonRpcServer::spawn(
- Some("Bearer sdk-secret"),
- json!({
- "jsonrpc": "2.0",
- "id": "radroots-sdk-nip46-connect",
- "result": {
- "session_id": "session-123",
- "mode": "Nostrconnect",
- "remote_signer_pubkey": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
- "client_pubkey": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
- "relays": ["wss://radroots.org"]
- }
- }),
- )
- .await?;
- let connect_client = radrootsd_test_client(connect_server.endpoint())?;
- let handle: SdkRadrootsdSignerSessionHandle = connect_client
- .radrootsd()
- .signer_sessions()
- .connect_nostrconnect(
- "nostrconnect://bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb?relay=wss%3A%2F%2Fradroots.org&secret=shared-secret",
- "client-secret-key",
- )
- .await?;
-
- let (status_server, request_rx) = JsonRpcServer::spawn(
- Some("Bearer sdk-secret"),
- json!({
- "jsonrpc": "2.0",
- "id": "radroots-sdk-nip46-session-status",
- "result": sample_session_view_json("session-123")
- }),
- )
- .await?;
- let status_client = radrootsd_test_client(status_server.endpoint())?;
- let session: SdkRadrootsdSignerSessionView = status_client
- .radrootsd()
- .signer_sessions()
- .status(handle.session())
- .await?;
- let request_json = request_rx.await?;
-
- assert_eq!(request_json["method"], "nip46.session.status");
- assert_eq!(request_json["params"]["session_id"], "session-123");
- assert_eq!(session.session(), handle.session());
- assert_eq!(
- session.role,
- SdkRadrootsdSignerSessionRole::OutboundRemoteSigner
- );
- assert_eq!(
- session.client_pubkey,
- "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
- );
- assert_eq!(
- session.signer_pubkey,
- "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
- );
- assert_eq!(
- session.user_pubkey.as_deref(),
- Some("cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc")
- );
- assert_eq!(session.relays, vec!["wss://radroots.org".to_owned()]);
- assert_eq!(session.permissions, vec!["sign_event:30402".to_owned()]);
- assert_eq!(session.name.as_deref(), Some("Radroots Signer"));
- assert_eq!(
- session.url.as_deref(),
- Some("https://radroots.org/signers/demo")
- );
- assert_eq!(
- session.image.as_deref(),
- Some("https://radroots.org/signers/demo.png")
- );
- assert!(session.authorized);
- assert!(!session.auth_required);
- assert_eq!(session.expires_in_secs, Some(120));
- assert_eq!(
- session
- .signer_authority
- .as_ref()
- .map(|value| value.provider_runtime_id.as_str()),
- Some("runtime-1")
- );
-
- let debug = format!("{session:?}");
- assert!(!debug.contains("session-123"));
- assert!(debug.contains("<redacted>"));
-
- Ok(())
-}
-
-#[tokio::test]
-async fn radrootsd_signer_session_list_returns_typed_views() -> TestResult<()> {
- let (server, request_rx) = JsonRpcServer::spawn(
- Some("Bearer sdk-secret"),
- json!({
- "jsonrpc": "2.0",
- "id": "radroots-sdk-nip46-session-list",
- "result": [
- sample_session_view_json("session-123"),
- sample_session_view_json("session-456")
- ]
- }),
- )
- .await?;
- let client = radrootsd_test_client(server.endpoint())?;
- let sessions: Vec<SdkRadrootsdSignerSessionView> =
- client.radrootsd().signer_sessions().list().await?;
- let request_json = request_rx.await?;
-
- assert_eq!(request_json["method"], "nip46.session.list");
- assert_eq!(sessions.len(), 2);
- assert_eq!(
- sessions[0].role,
- SdkRadrootsdSignerSessionRole::OutboundRemoteSigner
- );
- let debug = format!("{:?}", sessions[0].session());
- assert!(!debug.contains("session-123"));
- assert!(debug.contains("<redacted>"));
-
- Ok(())
-}
-
-#[tokio::test]
-async fn radrootsd_signer_session_authorize_returns_typed_result() -> TestResult<()> {
- let (connect_server, _) = JsonRpcServer::spawn(
- Some("Bearer sdk-secret"),
- json!({
- "jsonrpc": "2.0",
- "id": "radroots-sdk-nip46-connect",
- "result": {
- "session_id": "session-123",
- "mode": "Bunker",
- "remote_signer_pubkey": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
- "client_pubkey": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
- "relays": ["wss://radroots.org"]
- }
- }),
- )
- .await?;
- let connect_client = radrootsd_test_client(connect_server.endpoint())?;
- let handle: SdkRadrootsdSignerSessionHandle = connect_client
- .radrootsd()
- .signer_sessions()
- .connect_bunker(
- "bunker://aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa?relay=wss%3A%2F%2Fradroots.org&secret=shared-secret",
- )
- .await?;
-
- let (server, request_rx) = JsonRpcServer::spawn(
- Some("Bearer sdk-secret"),
- json!({
- "jsonrpc": "2.0",
- "id": "radroots-sdk-nip46-session-authorize",
- "result": {
- "authorized": true,
- "replayed": true
- }
- }),
- )
- .await?;
- let client = radrootsd_test_client(server.endpoint())?;
- let result = client
- .radrootsd()
- .signer_sessions()
- .authorize(handle.session())
- .await?;
- let request_json = request_rx.await?;
-
- assert_eq!(request_json["method"], "nip46.session.authorize");
- assert_eq!(request_json["params"]["session_id"], "session-123");
- assert!(result.authorized);
- assert!(result.replayed);
-
- Ok(())
-}
-
-#[tokio::test]
-async fn radrootsd_signer_session_get_public_key_returns_typed_result() -> TestResult<()> {
- let (connect_server, _) = JsonRpcServer::spawn(
- Some("Bearer sdk-secret"),
- json!({
- "jsonrpc": "2.0",
- "id": "radroots-sdk-nip46-connect",
- "result": {
- "session_id": "session-123",
- "mode": "Bunker",
- "remote_signer_pubkey": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
- "client_pubkey": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
- "relays": ["wss://radroots.org"]
- }
- }),
- )
- .await?;
- let connect_client = radrootsd_test_client(connect_server.endpoint())?;
- let handle: SdkRadrootsdSignerSessionHandle = connect_client
- .radrootsd()
- .signer_sessions()
- .connect_bunker(
- "bunker://aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa?relay=wss%3A%2F%2Fradroots.org&secret=shared-secret",
- )
- .await?;
-
- let (server, request_rx) = JsonRpcServer::spawn(
- Some("Bearer sdk-secret"),
- json!({
- "jsonrpc": "2.0",
- "id": "radroots-sdk-nip46-get-public-key",
- "result": {
- "pubkey": "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"
- }
- }),
- )
- .await?;
- let client = radrootsd_test_client(server.endpoint())?;
- let result = client
- .radrootsd()
- .signer_sessions()
- .get_public_key(handle.session())
- .await?;
- let request_json = request_rx.await?;
-
- assert_eq!(request_json["method"], "nip46.get_public_key");
- assert_eq!(request_json["params"]["session_id"], "session-123");
- assert_eq!(
- result.pubkey,
- "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"
- );
-
- Ok(())
-}
-
-#[tokio::test]
-async fn radrootsd_signer_session_require_auth_returns_typed_result() -> TestResult<()> {
- let (connect_server, _) = JsonRpcServer::spawn(
- Some("Bearer sdk-secret"),
- json!({
- "jsonrpc": "2.0",
- "id": "radroots-sdk-nip46-connect",
- "result": {
- "session_id": "session-123",
- "mode": "Bunker",
- "remote_signer_pubkey": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
- "client_pubkey": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
- "relays": ["wss://radroots.org"]
- }
- }),
- )
- .await?;
- let connect_client = radrootsd_test_client(connect_server.endpoint())?;
- let handle: SdkRadrootsdSignerSessionHandle = connect_client
- .radrootsd()
- .signer_sessions()
- .connect_bunker(
- "bunker://aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa?relay=wss%3A%2F%2Fradroots.org&secret=shared-secret",
- )
- .await?;
-
- let (server, request_rx) = JsonRpcServer::spawn(
- Some("Bearer sdk-secret"),
- json!({
- "jsonrpc": "2.0",
- "id": "radroots-sdk-nip46-session-require-auth",
- "result": {
- "required": true
- }
- }),
- )
- .await?;
- let client = radrootsd_test_client(server.endpoint())?;
- let result = client
- .radrootsd()
- .signer_sessions()
- .require_auth(handle.session(), "https://radroots.org/auth")
- .await?;
- let request_json = request_rx.await?;
-
- assert_eq!(request_json["method"], "nip46.session.require_auth");
- assert_eq!(request_json["params"]["session_id"], "session-123");
- assert_eq!(
- request_json["params"]["auth_url"],
- "https://radroots.org/auth"
- );
- assert!(result.required);
-
- Ok(())
-}
-
-#[tokio::test]
-async fn radrootsd_signer_session_close_returns_typed_result() -> TestResult<()> {
- let (connect_server, _) = JsonRpcServer::spawn(
- Some("Bearer sdk-secret"),
- json!({
- "jsonrpc": "2.0",
- "id": "radroots-sdk-nip46-connect",
- "result": {
- "session_id": "session-123",
- "mode": "Bunker",
- "remote_signer_pubkey": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
- "client_pubkey": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
- "relays": ["wss://radroots.org"]
- }
- }),
- )
- .await?;
- let connect_client = radrootsd_test_client(connect_server.endpoint())?;
- let handle: SdkRadrootsdSignerSessionHandle = connect_client
- .radrootsd()
- .signer_sessions()
- .connect_bunker(
- "bunker://aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa?relay=wss%3A%2F%2Fradroots.org&secret=shared-secret",
- )
- .await?;
-
- let (server, request_rx) = JsonRpcServer::spawn(
- Some("Bearer sdk-secret"),
- json!({
- "jsonrpc": "2.0",
- "id": "radroots-sdk-nip46-session-close",
- "result": {
- "closed": true
- }
- }),
- )
- .await?;
- let client = radrootsd_test_client(server.endpoint())?;
- let result = client
- .radrootsd()
- .signer_sessions()
- .close(handle.session())
- .await?;
- let request_json = request_rx.await?;
-
- assert_eq!(request_json["method"], "nip46.session.close");
- assert_eq!(request_json["params"]["session_id"], "session-123");
- assert!(result.closed);
-
- Ok(())
-}
-
-#[tokio::test]
-async fn radrootsd_signer_session_connect_rejects_relay_transport_mode() -> TestResult<()> {
- let client = RadrootsSdkClient::from_config(RadrootsSdkConfig::production())?;
- let request = SdkRadrootsdSignerSessionConnectRequest::bunker(
- "bunker://aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa?relay=wss%3A%2F%2Fradroots.org&secret=shared-secret",
- );
-
- let error = client
- .radrootsd()
- .signer_sessions()
- .connect(&request)
- .await
- .expect_err("unsupported transport");
-
- assert!(matches!(
- error,
- SdkRadrootsdSessionError::UnsupportedTransport {
- transport: SdkTransportMode::RelayDirect,
- operation: "radrootsd.signer_sessions.connect",
- }
- ));
-
- Ok(())
-}
-
-#[tokio::test]
-async fn radrootsd_listing_publish_accepts_sdk_built_draft() -> TestResult<()> {
- let (server, request_rx) = JsonRpcServer::spawn(
- Some("Bearer sdk-secret"),
- json!({
- "jsonrpc": "2.0",
- "id": "radroots-sdk-listing-publish",
- "result": {
- "deduplicated": false,
- "job": {
- "job_id": "job-1",
- "command": "bridge.listing.publish",
- "status": "published",
- "terminal": true,
- "recovered_after_restart": false,
- "signer_mode": "nip46_session:session-123",
- "signer_session_id": "session-123",
- "event_kind": 30402,
- "event_id": "event-1",
- "event_addr": "30402:seller:listing-1",
- "relay_count": 1,
- "acknowledged_relay_count": 1
- }
- }
- }),
- )
- .await?;
-
- let handle = connected_bunker_session_handle("session-123").await?;
- let client = radrootsd_test_client(server.endpoint())?;
- let draft = client.listing().build_draft(&sample_listing())?;
- let options = SdkRadrootsdListingPublishOptions::from_signer_session(&handle)
- .with_idempotency_key("idem-1");
-
- let receipt = client
- .listing()
- .publish_draft_via_radrootsd_with_options(draft, &options)
- .await?;
- let request_json = request_rx.await?;
-
- assert_eq!(request_json["method"], "bridge.listing.publish");
- assert_eq!(request_json["params"]["signer_session_id"], "session-123");
- assert_eq!(request_json["params"]["idempotency_key"], "idem-1");
- assert_eq!(request_json["params"]["kind"], 30402);
- assert_eq!(
- request_json["params"]["listing"]["d_tag"],
- "AAAAAAAAAAAAAAAAAAAAAg"
- );
-
- assert_eq!(receipt.transport, SdkTransportMode::Radrootsd);
- assert_eq!(receipt.event_kind, Some(30402));
- assert_eq!(receipt.event_id, Some("event-1".to_owned()));
- match receipt.transport_receipt {
- SdkTransportReceipt::Radrootsd(rpc_receipt) => {
- assert!(rpc_receipt.accepted);
- assert!(!rpc_receipt.deduplicated);
- assert_eq!(rpc_receipt.job_id.as_deref(), Some("job-1"));
- assert_eq!(rpc_receipt.status.as_deref(), Some("published"));
- assert_eq!(
- rpc_receipt.signer_session_id.as_deref(),
- Some("session-123")
- );
- assert_eq!(
- rpc_receipt.event_addr.as_deref(),
- Some("30402:seller:listing-1")
- );
- assert_eq!(rpc_receipt.relay_count, Some(1));
- assert_eq!(rpc_receipt.acknowledged_relay_count, Some(1));
- }
- SdkTransportReceipt::RelayDirect(_) => panic!("unexpected relay receipt"),
- }
-
- Ok(())
-}
-
-#[tokio::test]
-async fn radrootsd_listing_publish_accepts_typed_listing_value() -> TestResult<()> {
- let (server, request_rx) = JsonRpcServer::spawn(
- Some("Bearer sdk-secret"),
- json!({
- "jsonrpc": "2.0",
- "id": "radroots-sdk-listing-publish",
- "result": {
- "deduplicated": false,
- "job": {
- "job_id": "job-2",
- "command": "bridge.listing.publish",
- "status": "published",
- "terminal": true,
- "recovered_after_restart": false,
- "signer_mode": "nip46_session:session-456",
- "signer_session_id": "session-456",
- "event_kind": 30402,
- "event_id": "event-2",
- "event_addr": "30402:seller:listing-2",
- "relay_count": 1,
- "acknowledged_relay_count": 1
- }
- }
- }),
- )
- .await?;
-
- let handle = connected_bunker_session_handle("session-456").await?;
- let client = radrootsd_test_client(server.endpoint())?;
-
- let receipt = client
- .listing()
- .publish_listing_via_radrootsd(&sample_listing(), &handle)
- .await?;
- let request_json = request_rx.await?;
-
- assert_eq!(request_json["method"], "bridge.listing.publish");
- assert_eq!(request_json["params"]["signer_session_id"], "session-456");
- assert!(request_json["params"]["idempotency_key"].is_null());
- assert_eq!(request_json["params"]["kind"], 30402);
- assert_eq!(
- request_json["params"]["listing"]["d_tag"],
- "AAAAAAAAAAAAAAAAAAAAAg"
- );
-
- assert_eq!(receipt.transport, SdkTransportMode::Radrootsd);
- assert_eq!(receipt.event_kind, Some(30402));
- assert_eq!(receipt.event_id, Some("event-2".to_owned()));
-
- Ok(())
-}
-
-#[tokio::test]
-async fn radrootsd_profile_publish_accepts_typed_profile_value() -> TestResult<()> {
- let (server, request_rx) = JsonRpcServer::spawn(
- Some("Bearer sdk-secret"),
- json!({
- "jsonrpc": "2.0",
- "id": "radroots-sdk-profile-publish",
- "result": {
- "deduplicated": false,
- "job": {
- "job_id": "job-profile-1",
- "command": "bridge.profile.publish",
- "status": "published",
- "terminal": true,
- "recovered_after_restart": false,
- "signer_mode": "nip46_session:session-profile-1",
- "signer_session_id": "session-profile-1",
- "event_kind": 0,
- "event_id": "event-profile-1",
- "relay_count": 1,
- "acknowledged_relay_count": 1
- }
- }
- }),
- )
- .await?;
-
- let handle = connected_bunker_session_handle("session-profile-1").await?;
- let client = radrootsd_test_client(server.endpoint())?;
- let options = SdkRadrootsdProfilePublishOptions::from_signer_session(&handle)
- .with_idempotency_key("profile-idem-1")
- .with_signer_authority(SdkRadrootsdSignerAuthority {
- provider_runtime_id: "runtime-profile".to_owned(),
- account_identity_id: "identity-profile".to_owned(),
- provider_signer_session_id: Some("provider-session-profile".to_owned()),
- });
-
- let receipt = client
- .profile()
- .publish_profile_via_radrootsd_with_options(
- &sample_profile(),
- Some(RadrootsProfileType::Farm),
- &options,
- )
- .await?;
- let request_json = request_rx.await?;
-
- assert_eq!(request_json["method"], "bridge.profile.publish");
- assert_eq!(
- request_json["params"]["signer_session_id"],
- "session-profile-1"
- );
- assert_eq!(request_json["params"]["profile_type"], "farm");
- assert_eq!(request_json["params"]["profile"]["name"], "North Farm");
- assert_eq!(request_json["params"]["idempotency_key"], "profile-idem-1");
- assert_eq!(
- request_json["params"]["signer_authority"]["provider_runtime_id"],
- "runtime-profile"
- );
- assert_eq!(receipt.event_kind, Some(KIND_PROFILE));
- assert_eq!(receipt.event_id, Some("event-profile-1".to_owned()));
-
- Ok(())
-}
-
-#[tokio::test]
-async fn radrootsd_farm_publish_accepts_typed_farm_value() -> TestResult<()> {
- let (server, request_rx) = JsonRpcServer::spawn(
- Some("Bearer sdk-secret"),
- json!({
- "jsonrpc": "2.0",
- "id": "radroots-sdk-farm-publish",
- "result": {
- "deduplicated": false,
- "job": {
- "job_id": "job-farm-1",
- "command": "bridge.farm.publish",
- "status": "published",
- "terminal": true,
- "recovered_after_restart": false,
- "signer_mode": "nip46_session:session-farm-1",
- "signer_session_id": "session-farm-1",
- "event_kind": 30340,
- "event_id": "event-farm-1",
- "event_addr": "30340:seller:AAAAAAAAAAAAAAAAAAAAAA",
- "relay_count": 1,
- "acknowledged_relay_count": 1
- }
- }
- }),
- )
- .await?;
-
- let handle = connected_bunker_session_handle("session-farm-1").await?;
- let client = radrootsd_test_client(server.endpoint())?;
- let options = SdkRadrootsdFarmPublishOptions::from_signer_session(&handle)
- .with_idempotency_key("farm-idem-1");
-
- let receipt = client
- .farm()
- .publish_farm_via_radrootsd_with_options(&sample_farm(), &options)
- .await?;
- let request_json = request_rx.await?;
-
- assert_eq!(request_json["method"], "bridge.farm.publish");
- assert_eq!(
- request_json["params"]["signer_session_id"],
- "session-farm-1"
- );
- assert_eq!(request_json["params"]["kind"], KIND_FARM);
- assert_eq!(
- request_json["params"]["farm"]["d_tag"],
- "AAAAAAAAAAAAAAAAAAAAAA"
- );
- assert_eq!(request_json["params"]["idempotency_key"], "farm-idem-1");
- assert_eq!(receipt.event_kind, Some(KIND_FARM));
- assert_eq!(receipt.event_id, Some("event-farm-1".to_owned()));
- match receipt.transport_receipt {
- SdkTransportReceipt::Radrootsd(receipt) => {
- assert_eq!(
- receipt.event_addr,
- Some("30340:seller:AAAAAAAAAAAAAAAAAAAAAA".to_owned())
- );
- }
- SdkTransportReceipt::RelayDirect(_) => panic!("unexpected relay receipt"),
- }
-
- Ok(())
-}
-
-#[tokio::test]
-async fn radrootsd_listing_publish_with_options_forwards_typed_continuity_metadata()
--> TestResult<()> {
- let (server, request_rx) = JsonRpcServer::spawn(
- Some("Bearer sdk-secret"),
- json!({
- "jsonrpc": "2.0",
- "id": "radroots-sdk-listing-publish",
- "result": {
- "deduplicated": false,
- "job": {
- "job_id": "job-3",
- "command": "bridge.listing.publish",
- "status": "published",
- "terminal": true,
- "recovered_after_restart": false,
- "signer_mode": "nip46_session:session-789",
- "signer_session_id": "session-789",
- "event_kind": 30402,
- "event_id": "event-3",
- "event_addr": "30402:seller:listing-3",
- "relay_count": 1,
- "acknowledged_relay_count": 1
- }
- }
- }),
- )
- .await?;
-
- let handle = connected_bunker_session_handle("session-789").await?;
- let client = radrootsd_test_client(server.endpoint())?;
- let options = SdkRadrootsdListingPublishOptions::from_signer_session(&handle)
- .with_idempotency_key("idem-3")
- .with_signer_authority(SdkRadrootsdSignerAuthority {
- provider_runtime_id: "runtime-1".to_owned(),
- account_identity_id: "identity-1".to_owned(),
- provider_signer_session_id: Some("provider-session-123".to_owned()),
- });
-
- let receipt = client
- .listing()
- .publish_listing_via_radrootsd_with_options(&sample_listing(), &options)
- .await?;
- let request_json = request_rx.await?;
-
- assert_eq!(request_json["method"], "bridge.listing.publish");
- assert_eq!(request_json["params"]["signer_session_id"], "session-789");
- assert_eq!(request_json["params"]["idempotency_key"], "idem-3");
- assert_eq!(
- request_json["params"]["signer_authority"]["provider_runtime_id"],
- "runtime-1"
- );
- assert_eq!(
- request_json["params"]["signer_authority"]["account_identity_id"],
- "identity-1"
- );
- assert_eq!(
- request_json["params"]["signer_authority"]["provider_signer_session_id"],
- "provider-session-123"
- );
- assert_eq!(receipt.event_id, Some("event-3".to_owned()));
-
- Ok(())
-}
-
-#[tokio::test]
-async fn radrootsd_listing_publish_rejects_draft_only_signer_mode() -> TestResult<()> {
- let mut config = RadrootsSdkConfig::for_environment(SdkEnvironment::Production);
- config.transport = SdkTransportMode::Radrootsd;
- config.signer = SignerConfig::DraftOnly;
- let client = RadrootsSdkClient::from_config(config)?;
- let handle = connected_bunker_session_handle("session-123").await?;
-
- let error = client
- .listing()
- .publish_listing_via_radrootsd(&sample_listing(), &handle)
- .await
- .expect_err("unsupported signer mode");
-
- assert!(matches!(
- error,
- SdkPublishError::UnsupportedSignerMode {
- transport: SdkTransportMode::Radrootsd,
- signer: SignerConfig::DraftOnly,
- required: SignerConfig::Nip46,
- operation: "listing.publish_via_radrootsd",
- }
- ));
-
- Ok(())
-}
-
-#[tokio::test]
-async fn radrootsd_listing_publish_rejects_local_identity_signer_mode() -> TestResult<()> {
- let mut config = RadrootsSdkConfig::for_environment(SdkEnvironment::Production);
- config.transport = SdkTransportMode::Radrootsd;
- config.signer = SignerConfig::LocalIdentity;
- let client = RadrootsSdkClient::from_config(config)?;
- let handle = connected_bunker_session_handle("session-123").await?;
-
- let error = client
- .listing()
- .publish_listing_via_radrootsd(&sample_listing(), &handle)
- .await
- .expect_err("unsupported signer mode");
-
- assert!(matches!(
- error,
- SdkPublishError::UnsupportedSignerMode {
- transport: SdkTransportMode::Radrootsd,
- signer: SignerConfig::LocalIdentity,
- required: SignerConfig::Nip46,
- operation: "listing.publish_via_radrootsd",
- }
- ));
-
- Ok(())
-}
-
-#[tokio::test]
-async fn radrootsd_listing_publish_rejects_relay_transport_mode() -> TestResult<()> {
- let client = RadrootsSdkClient::from_config(RadrootsSdkConfig::production())?;
- let handle = connected_bunker_session_handle("session-123").await?;
-
- let error = client
- .listing()
- .publish_listing_via_radrootsd(&sample_listing(), &handle)
- .await
- .expect_err("unsupported transport");
-
- assert!(matches!(
- error,
- SdkPublishError::UnsupportedTransport {
- transport: SdkTransportMode::RelayDirect,
- operation: "listing.publish_via_radrootsd",
- }
- ));
-
- Ok(())
-}
-
-#[tokio::test]
-async fn radrootsd_order_request_publish_accepts_session_handle() -> TestResult<()> {
- let (server, request_rx) = JsonRpcServer::spawn(
- Some("Bearer sdk-secret"),
- json!({
- "jsonrpc": "2.0",
- "id": "radroots-sdk-order-request-publish",
- "result": {
- "deduplicated": false,
- "job": {
- "job_id": "job-order-1",
- "command": "bridge.order.request",
- "status": "published",
- "terminal": true,
- "recovered_after_restart": false,
- "signer_mode": "nip46_session:session-order-1",
- "signer_session_id": "session-order-1",
- "event_kind": KIND_ORDER_REQUEST,
- "event_id": "event-order-1",
- "event_addr": format!("{KIND_LISTING}:{}:AAAAAAAAAAAAAAAAAAAAAg", "a".repeat(64)),
- "relay_count": 1,
- "acknowledged_relay_count": 1
- }
- }
- }),
- )
- .await?;
-
- let handle = connected_bunker_session_handle("session-order-1").await?;
- let client = radrootsd_test_client(server.endpoint())?;
- let options = SdkRadrootsdOrderRequestPublishOptions::from_signer_session(&handle)
- .with_idempotency_key("idem-order-1")
- .with_signer_authority(SdkRadrootsdSignerAuthority {
- provider_runtime_id: "runtime-1".to_owned(),
- account_identity_id: "identity-1".to_owned(),
- provider_signer_session_id: Some("provider-session-order-1".to_owned()),
- });
-
- let receipt = client
- .order()
- .publish_order_request_via_radrootsd_with_options(
- &sample_order_request(),
- &listing_event_ptr_with_relays(Some("wss://radroots.org")),
- &options,
- )
- .await?;
- let request_json = request_rx.await?;
-
- assert_eq!(request_json["method"], "bridge.order.request");
- assert_eq!(
- request_json["params"]["signer_session_id"],
- "session-order-1"
- );
- assert_eq!(request_json["params"]["idempotency_key"], "idem-order-1");
- assert_eq!(request_json["params"]["order"]["order_id"], "order-1");
- assert_eq!(
- request_json["params"]["listing_event"]["id"],
- "a".repeat(64)
- );
- assert_eq!(
- request_json["params"]["listing_event"]["relays"],
- "wss://radroots.org"
- );
- assert_eq!(
- request_json["params"]["signer_authority"]["provider_runtime_id"],
- "runtime-1"
- );
- assert_eq!(
- request_json["params"]["signer_authority"]["provider_signer_session_id"],
- "provider-session-order-1"
- );
- assert_eq!(receipt.event_kind, Some(KIND_ORDER_REQUEST));
- assert_eq!(receipt.event_id, Some("event-order-1".to_owned()));
-
- Ok(())
-}
-
-#[tokio::test]
-async fn radrootsd_sdk_workflow_chains_session_listing_order_and_bridge_job() -> TestResult<()> {
- let (server, mut request_rx) = JsonRpcSequenceServer::spawn(
- Some("Bearer sdk-secret"),
- vec![
- json!({
- "jsonrpc": "2.0",
- "id": "radroots-sdk-nip46-connect",
- "result": {
- "session_id": "session-workflow-1",
- "mode": "Bunker",
- "remote_signer_pubkey": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
- "client_pubkey": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
- "relays": ["wss://radroots.org"]
- }
- }),
- json!({
- "jsonrpc": "2.0",
- "id": "radroots-sdk-listing-publish",
- "result": {
- "deduplicated": false,
- "job": {
- "job_id": "job-workflow-listing",
- "command": "bridge.listing.publish",
- "status": "published",
- "terminal": true,
- "recovered_after_restart": false,
- "signer_mode": "nip46_session:session-workflow-1",
- "signer_session_id": "session-workflow-1",
- "event_kind": 30402,
- "event_id": "event-workflow-listing",
- "event_addr": "30402:seller:listing-workflow-1",
- "relay_count": 1,
- "acknowledged_relay_count": 1
- }
- }
- }),
- json!({
- "jsonrpc": "2.0",
- "id": "radroots-sdk-order-request-publish",
- "result": {
- "deduplicated": false,
- "job": {
- "job_id": "job-workflow-order",
- "command": "bridge.order.request",
- "status": "published",
- "terminal": true,
- "recovered_after_restart": false,
- "signer_mode": "nip46_session:session-workflow-1",
- "signer_session_id": "session-workflow-1",
- "event_kind": KIND_ORDER_REQUEST,
- "event_id": "event-workflow-order",
- "event_addr": format!("{KIND_LISTING}:{}:AAAAAAAAAAAAAAAAAAAAAg", "a".repeat(64)),
- "relay_count": 1,
- "acknowledged_relay_count": 1
- }
- }
- }),
- json!({
- "jsonrpc": "2.0",
- "id": "radroots-sdk-bridge-job-status",
- "result": sample_bridge_job_json_for(
- "job-workflow-order",
- "bridge.order.request",
- KIND_ORDER_REQUEST,
- )
- }),
- ],
- )
- .await?;
-
- let client = radrootsd_test_client(server.endpoint())?;
- let handle = client
- .radrootsd()
- .signer_sessions()
- .connect_bunker(
- "bunker://aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa?relay=wss%3A%2F%2Fradroots.org&secret=shared-secret",
- )
- .await?;
- assert_eq!(handle.mode(), SdkRadrootsdSignerSessionMode::Bunker);
-
- let connect_request = request_rx.recv().await.expect("connect request");
- assert_eq!(connect_request["method"], "nip46.connect");
-
- let listing_receipt = client
- .listing()
- .publish_listing_via_radrootsd(&sample_listing(), &handle)
- .await?;
- let listing_request = request_rx.recv().await.expect("listing publish request");
- assert_eq!(listing_request["method"], "bridge.listing.publish");
- assert_eq!(
- listing_request["params"]["signer_session_id"],
- "session-workflow-1"
- );
-
- let order_receipt = client
- .order()
- .publish_order_request_via_radrootsd(
- &sample_order_request(),
- &listing_event_ptr_with_relays(Some("wss://radroots.org")),
- &handle,
- )
- .await?;
- let order_request = request_rx.recv().await.expect("order publish request");
- assert_eq!(order_request["method"], "bridge.order.request");
- assert_eq!(
- order_request["params"]["signer_session_id"],
- "session-workflow-1"
- );
- assert_eq!(order_request["params"]["order"]["order_id"], "order-1");
- assert_eq!(
- order_request["params"]["listing_event"]["id"],
- "a".repeat(64)
- );
-
- let order_job = match &order_receipt.transport_receipt {
- SdkTransportReceipt::Radrootsd(receipt) => receipt.job(),
- SdkTransportReceipt::RelayDirect(_) => None,
- }
- .expect("order publish receipt should expose a bridge job ref");
-
- let job_view = client.radrootsd().bridge().job(&order_job).await?;
- let job_request = request_rx.recv().await.expect("bridge job request");
- assert_eq!(job_request["method"], "bridge.job.status");
- assert_eq!(job_request["params"]["job_id"], "job-workflow-order");
-
- assert_eq!(listing_receipt.event_kind, Some(30402));
- assert_eq!(order_receipt.event_kind, Some(KIND_ORDER_REQUEST));
- assert_eq!(job_view.job().job_id(), "job-workflow-order");
- assert_eq!(job_view.command, "bridge.order.request");
- assert_eq!(job_view.status, SdkRadrootsdBridgeJobStatus::Published);
-
- Ok(())
-}
-
-#[tokio::test]
-async fn radrootsd_bridge_status_returns_typed_status() -> TestResult<()> {
- let (server, request_rx) = JsonRpcServer::spawn(
- Some("Bearer sdk-secret"),
- json!({
- "jsonrpc": "2.0",
- "id": "radroots-sdk-bridge-status",
- "result": sample_bridge_status_json()
- }),
- )
- .await?;
- let client = radrootsd_test_client(server.endpoint())?;
- let status = client.radrootsd().bridge().status().await?;
- let request_json = request_rx.await?;
-
- assert_eq!(request_json["method"], "bridge.status");
- assert!(status.enabled);
- assert!(status.ready);
- assert_eq!(
- status.delivery_policy,
- SdkRadrootsdBridgeDeliveryPolicy::Quorum
- );
- assert_eq!(status.delivery_quorum, Some(1));
- assert_eq!(status.available_nip46_signer_sessions, 2);
- assert!(
- status
- .methods
- .contains(&"bridge.listing.publish".to_owned())
- );
-
- Ok(())
-}
-
-#[tokio::test]
-async fn radrootsd_bridge_job_status_accepts_typed_job_ref_from_publish_receipt() -> TestResult<()>
-{
- let (publish_server, publish_request_rx) = JsonRpcServer::spawn(
- Some("Bearer sdk-secret"),
- json!({
- "jsonrpc": "2.0",
- "id": "radroots-sdk-listing-publish",
- "result": {
- "deduplicated": false,
- "job": {
- "job_id": "job-bridge-1",
- "command": "bridge.listing.publish",
- "status": "published",
- "terminal": true,
- "recovered_after_restart": false,
- "signer_mode": "nip46_session:session-123",
- "signer_session_id": "session-123",
- "event_kind": 30402,
- "event_id": "event-bridge-1",
- "event_addr": "30402:seller:listing-bridge-1",
- "relay_count": 1,
- "acknowledged_relay_count": 1
- }
- }
- }),
- )
- .await?;
- let handle = connected_bunker_session_handle("session-123").await?;
- let publish_client = radrootsd_test_client(publish_server.endpoint())?;
- let publish_receipt = publish_client
- .listing()
- .publish_listing_via_radrootsd(&sample_listing(), &handle)
- .await?;
- let publish_request_json = publish_request_rx.await?;
- assert_eq!(publish_request_json["method"], "bridge.listing.publish");
-
- let job = match &publish_receipt.transport_receipt {
- SdkTransportReceipt::Radrootsd(receipt) => receipt.job(),
- SdkTransportReceipt::RelayDirect(_) => None,
- }
- .expect("publish receipt should expose a bridge job ref");
-
- let (job_server, request_rx) = JsonRpcServer::spawn(
- Some("Bearer sdk-secret"),
- json!({
- "jsonrpc": "2.0",
- "id": "radroots-sdk-bridge-job-status",
- "result": sample_bridge_job_json("job-bridge-1")
- }),
- )
- .await?;
- let job_client = radrootsd_test_client(job_server.endpoint())?;
- let job_view = job_client.radrootsd().bridge().job(&job).await?;
- let request_json = request_rx.await?;
-
- assert_eq!(request_json["method"], "bridge.job.status");
- assert_eq!(request_json["params"]["job_id"], "job-bridge-1");
- assert_eq!(job_view.job().job_id(), "job-bridge-1");
- assert_eq!(job_view.status, SdkRadrootsdBridgeJobStatus::Published);
- assert_eq!(
- job_view.delivery_policy,
- SdkRadrootsdBridgeDeliveryPolicy::Quorum
- );
- assert_eq!(job_view.attempt_count, 1);
- assert_eq!(job_view.relay_results.len(), 2);
- assert_eq!(job_view.relay_results[0].relay_url, "wss://radroots.org");
- assert!(job_view.relay_results[0].acknowledged);
-
- Ok(())
-}
-
-#[tokio::test]
-async fn radrootsd_bridge_job_list_returns_typed_views() -> TestResult<()> {
- let (server, request_rx) = JsonRpcServer::spawn(
- Some("Bearer sdk-secret"),
- json!({
- "jsonrpc": "2.0",
- "id": "radroots-sdk-bridge-job-list",
- "result": [
- sample_bridge_job_json("job-bridge-1"),
- sample_bridge_job_json("job-bridge-2")
- ]
- }),
- )
- .await?;
- let client = radrootsd_test_client(server.endpoint())?;
- let jobs = client.radrootsd().bridge().jobs().await?;
- let request_json = request_rx.await?;
-
- assert_eq!(request_json["method"], "bridge.job.list");
- assert_eq!(jobs.len(), 2);
- assert_eq!(jobs[0].job().job_id(), "job-bridge-1");
- assert_eq!(jobs[1].job().job_id(), "job-bridge-2");
- assert_eq!(jobs[0].status, SdkRadrootsdBridgeJobStatus::Published);
-
- Ok(())
-}
-
-#[tokio::test]
-async fn radrootsd_bridge_status_rejects_relay_transport_mode() -> TestResult<()> {
- let client = RadrootsSdkClient::from_config(RadrootsSdkConfig::production())?;
- let error = client
- .radrootsd()
- .bridge()
- .status()
- .await
- .expect_err("unsupported transport");
-
- assert!(matches!(
- error,
- SdkRadrootsdBridgeError::UnsupportedTransport {
- transport: SdkTransportMode::RelayDirect,
- operation: "radrootsd.bridge.status",
- }
- ));
-
- Ok(())
-}
-
-#[test]
-fn radrootsd_listing_request_from_event_rejects_listing_draft_kind() -> TestResult<()> {
- let draft = radroots_sdk::listing::build_draft(&sample_listing())?;
- let mut event = sdk_event("seller", 1_720_000_000, draft);
- event.kind = KIND_LISTING_DRAFT;
-
- assert!(matches!(
- SdkRadrootsdListingPublishRequest::from_event(&event, "session-123", None, None),
- Err(RadrootsListingParseError::InvalidKind(KIND_LISTING_DRAFT))
- ));
-
- Ok(())
-}
diff --git a/crates/sdk/tests/relay_direct.rs b/crates/sdk/tests/relay_direct.rs
@@ -1,1406 +0,0 @@
-#![cfg(all(
- feature = "identity-models",
- feature = "relay-client",
- feature = "signing"
-))]
-
-use futures::{SinkExt, StreamExt};
-use nostr::{ClientMessage, JsonUtil, RelayMessage};
-use radroots_core::{
- RadrootsCoreCurrency, RadrootsCoreDecimal, RadrootsCoreMoney, RadrootsCoreQuantity,
- RadrootsCoreQuantityPrice, RadrootsCoreUnit,
-};
-use radroots_events::ids::{RadrootsEventId, RadrootsPublicKey};
-use radroots_sdk::farm::{RadrootsFarm, RadrootsFarmLocation, RadrootsFarmRef};
-use radroots_sdk::identity::RadrootsIdentity;
-use radroots_sdk::listing::{
- RadrootsListing, RadrootsListingAvailability, RadrootsListingBin,
- RadrootsListingDeliveryMethod, RadrootsListingLocation, RadrootsListingProduct,
- RadrootsListingStatus,
-};
-use radroots_sdk::order::{
- RadrootsOrderCancellation, RadrootsOrderDecision, RadrootsOrderDecisionOutcome,
- RadrootsOrderEconomicItem, RadrootsOrderEconomics, RadrootsOrderFulfillmentState,
- RadrootsOrderFulfillmentUpdate, RadrootsOrderInventoryCommitment, RadrootsOrderItem,
- RadrootsOrderPricingBasis, RadrootsOrderReceipt, RadrootsOrderRequest,
- RadrootsOrderRevisionDecision, RadrootsOrderRevisionOutcome, RadrootsOrderRevisionProposal,
-};
-use radroots_sdk::profile::{RadrootsProfile, RadrootsProfileType};
-use radroots_sdk::{
- RadrootsNostrEventPtr, RadrootsSdkClient, RadrootsSdkConfig, RelayConfig, SdkEnvironment,
- SdkPublishError, SdkTransportMode, SdkTransportReceipt, SignerConfig,
-};
-use tokio::net::TcpListener;
-use tokio::sync::oneshot;
-use tokio_tungstenite::tungstenite::Message;
-
-type TestResult<T> = Result<T, Box<dyn std::error::Error + Send + Sync>>;
-
-struct AckRelay {
- url: String,
- shutdown_tx: Option<oneshot::Sender<()>>,
-}
-
-impl AckRelay {
- async fn spawn() -> TestResult<Self> {
- let listener = TcpListener::bind("127.0.0.1:0").await?;
- let addr = listener.local_addr()?;
- let url = format!("ws://{addr}");
- let (shutdown_tx, mut shutdown_rx) = oneshot::channel();
-
- tokio::spawn(async move {
- loop {
- tokio::select! {
- _ = &mut shutdown_rx => break,
- accept = listener.accept() => {
- let Ok((stream, _)) = accept else {
- break;
- };
- tokio::spawn(async move {
- let Ok(websocket) = tokio_tungstenite::accept_async(stream).await else {
- return;
- };
- let (mut writer, mut reader) = websocket.split();
- while let Some(message) = reader.next().await {
- let Ok(message) = message else {
- break;
- };
- let Message::Text(text) = message else {
- continue;
- };
- let Ok(client_message) = ClientMessage::from_json(text.as_str()) else {
- continue;
- };
- if let ClientMessage::Event(event) = client_message {
- let relay_message =
- RelayMessage::ok(event.id, true, "").as_json();
- if writer
- .send(Message::Text(relay_message.into()))
- .await
- .is_err()
- {
- break;
- }
- }
- }
- });
- }
- }
- }
- });
-
- Ok(Self {
- url,
- shutdown_tx: Some(shutdown_tx),
- })
- }
-
- fn url(&self) -> &str {
- self.url.as_str()
- }
-}
-
-impl Drop for AckRelay {
- fn drop(&mut self) {
- if let Some(shutdown_tx) = self.shutdown_tx.take() {
- let _ = shutdown_tx.send(());
- }
- }
-}
-
-fn sample_listing() -> RadrootsListing {
- RadrootsListing {
- d_tag: "AAAAAAAAAAAAAAAAAAAAAg".parse().expect("listing d tag"),
- published_at: None,
- farm: RadrootsFarmRef {
- pubkey: "seller".into(),
- d_tag: "AAAAAAAAAAAAAAAAAAAAAA".into(),
- },
- product: RadrootsListingProduct {
- key: "coffee".into(),
- title: "Coffee".into(),
- category: "coffee".into(),
- summary: Some("Single origin coffee".into()),
- process: None,
- lot: None,
- location: None,
- profile: None,
- year: None,
- },
- primary_bin_id: "bin-1".parse().expect("primary bin id"),
- bins: vec![RadrootsListingBin {
- bin_id: "bin-1".parse().expect("bin id"),
- quantity: RadrootsCoreQuantity::new(
- RadrootsCoreDecimal::from(1000u32),
- RadrootsCoreUnit::MassG,
- ),
- price_per_canonical_unit: RadrootsCoreQuantityPrice {
- amount: RadrootsCoreMoney::new(
- RadrootsCoreDecimal::from(20u32),
- RadrootsCoreCurrency::USD,
- ),
- quantity: RadrootsCoreQuantity::new(
- RadrootsCoreDecimal::from(1u32),
- RadrootsCoreUnit::MassG,
- ),
- },
- display_amount: None,
- display_unit: None,
- display_label: None,
- display_price: None,
- display_price_unit: None,
- }],
- resource_area: None,
- plot: None,
- discounts: None,
- inventory_available: Some(RadrootsCoreDecimal::from(5u32)),
- availability: Some(RadrootsListingAvailability::Status {
- status: RadrootsListingStatus::Active,
- }),
- delivery_method: Some(RadrootsListingDeliveryMethod::Pickup),
- location: Some(RadrootsListingLocation {
- primary: "North Farm".into(),
- city: None,
- region: None,
- country: None,
- lat: None,
- lng: None,
- geohash: None,
- }),
- images: None,
- }
-}
-
-fn sample_profile() -> RadrootsProfile {
- RadrootsProfile {
- name: "north-farm".into(),
- display_name: Some("North Farm".into()),
- nip05: None,
- about: Some("Farm profile".into()),
- website: None,
- picture: None,
- banner: None,
- lud06: None,
- lud16: None,
- bot: None,
- }
-}
-
-fn sample_farm() -> RadrootsFarm {
- RadrootsFarm {
- d_tag: "AAAAAAAAAAAAAAAAAAAAAA".into(),
- name: "North Farm".into(),
- about: Some("Vegetable farm".into()),
- website: None,
- picture: None,
- banner: None,
- location: Some(RadrootsFarmLocation {
- primary: Some("North Road".into()),
- city: None,
- region: None,
- country: Some("US".into()),
- gcs: None,
- }),
- tags: Some(vec!["vegetables".into()]),
- }
-}
-
-fn decimal(raw: &str) -> RadrootsCoreDecimal {
- raw.parse().expect("decimal")
-}
-
-fn usd(raw: &str) -> RadrootsCoreMoney {
- RadrootsCoreMoney::new(decimal(raw), RadrootsCoreCurrency::USD)
-}
-
-fn listing_event_ptr() -> RadrootsNostrEventPtr {
- RadrootsNostrEventPtr {
- id: event_id_wire('a'),
- relays: Some("wss://listing.relay.example".into()),
- }
-}
-
-fn public_key(value: String) -> RadrootsPublicKey {
- value.parse().expect("public key")
-}
-
-fn event_id(character: char) -> RadrootsEventId {
- core::iter::repeat_n(character, 64)
- .collect::<String>()
- .parse()
- .expect("event id")
-}
-
-fn event_id_wire(character: char) -> String {
- event_id(character).into_string()
-}
-
-fn sample_order_request(buyer_pubkey: String, seller_pubkey: String) -> RadrootsOrderRequest {
- let buyer_pubkey = public_key(buyer_pubkey);
- let seller_pubkey = public_key(seller_pubkey);
- RadrootsOrderRequest {
- order_id: "order-1".parse().expect("order id"),
- listing_addr: format!("30402:{seller_pubkey}:AAAAAAAAAAAAAAAAAAAAAg")
- .parse()
- .expect("listing address"),
- buyer_pubkey,
- seller_pubkey,
- items: vec![RadrootsOrderItem {
- bin_id: "bin-1".parse().expect("bin id"),
- bin_count: 2,
- }],
- economics: RadrootsOrderEconomics {
- quote_id: "quote-1".parse().expect("quote id"),
- quote_version: 1,
- pricing_basis: RadrootsOrderPricingBasis::ListingEvent,
- currency: RadrootsCoreCurrency::USD,
- items: vec![RadrootsOrderEconomicItem {
- bin_id: "bin-1".parse().expect("bin id"),
- bin_count: 2,
- quantity_amount: decimal("1"),
- quantity_unit: RadrootsCoreUnit::Each,
- unit_price_amount: decimal("5"),
- unit_price_currency: RadrootsCoreCurrency::USD,
- line_subtotal: usd("10"),
- }],
- discounts: Vec::new(),
- adjustments: Vec::new(),
- subtotal: usd("10"),
- discount_total: usd("0"),
- adjustment_total: usd("0"),
- total: usd("10"),
- },
- }
-}
-
-fn sample_order_decision(buyer_pubkey: String, seller_pubkey: String) -> RadrootsOrderDecision {
- let buyer_pubkey = public_key(buyer_pubkey);
- let seller_pubkey = public_key(seller_pubkey);
- RadrootsOrderDecision {
- order_id: "order-1".parse().expect("order id"),
- listing_addr: format!("30402:{seller_pubkey}:AAAAAAAAAAAAAAAAAAAAAg")
- .parse()
- .expect("listing address"),
- buyer_pubkey,
- seller_pubkey,
- decision: RadrootsOrderDecisionOutcome::Accepted {
- inventory_commitments: vec![RadrootsOrderInventoryCommitment {
- bin_id: "bin-1".parse().expect("bin id"),
- bin_count: 2,
- }],
- },
- }
-}
-
-fn sample_order_revision_proposal(
- buyer_pubkey: String,
- seller_pubkey: String,
- root_event_id: String,
- prev_event_id: String,
-) -> RadrootsOrderRevisionProposal {
- let buyer_pubkey = public_key(buyer_pubkey);
- let seller_pubkey = public_key(seller_pubkey);
- RadrootsOrderRevisionProposal {
- revision_id: "revision-1".parse().expect("revision id"),
- order_id: "order-1".parse().expect("order id"),
- listing_addr: format!("30402:{seller_pubkey}:AAAAAAAAAAAAAAAAAAAAAg")
- .parse()
- .expect("listing address"),
- buyer_pubkey,
- seller_pubkey,
- root_event_id: root_event_id.parse().expect("root event id"),
- prev_event_id: prev_event_id.parse().expect("previous event id"),
- items: vec![RadrootsOrderItem {
- bin_id: "bin-1".parse().expect("bin id"),
- bin_count: 3,
- }],
- economics: RadrootsOrderEconomics {
- quote_id: "revision-quote-1".parse().expect("revision quote id"),
- quote_version: 2,
- pricing_basis: RadrootsOrderPricingBasis::ListingEvent,
- currency: RadrootsCoreCurrency::USD,
- items: vec![RadrootsOrderEconomicItem {
- bin_id: "bin-1".parse().expect("bin id"),
- bin_count: 3,
- quantity_amount: decimal("1"),
- quantity_unit: RadrootsCoreUnit::Each,
- unit_price_amount: decimal("5"),
- unit_price_currency: RadrootsCoreCurrency::USD,
- line_subtotal: usd("15"),
- }],
- discounts: Vec::new(),
- adjustments: Vec::new(),
- subtotal: usd("15"),
- discount_total: usd("0"),
- adjustment_total: usd("0"),
- total: usd("15"),
- },
- reason: "update count".into(),
- }
-}
-
-fn sample_order_revision_decision(
- proposal: &RadrootsOrderRevisionProposal,
- decision: RadrootsOrderRevisionOutcome,
-) -> RadrootsOrderRevisionDecision {
- RadrootsOrderRevisionDecision {
- revision_id: proposal.revision_id.clone(),
- order_id: proposal.order_id.clone(),
- listing_addr: proposal.listing_addr.clone(),
- buyer_pubkey: proposal.buyer_pubkey.clone(),
- seller_pubkey: proposal.seller_pubkey.clone(),
- root_event_id: proposal.root_event_id.clone(),
- prev_event_id: event_id('3'),
- decision,
- }
-}
-
-fn sample_fulfillment_update(
- buyer_pubkey: String,
- seller_pubkey: String,
-) -> RadrootsOrderFulfillmentUpdate {
- let buyer_pubkey = public_key(buyer_pubkey);
- let seller_pubkey = public_key(seller_pubkey);
- RadrootsOrderFulfillmentUpdate {
- order_id: "order-1".parse().expect("order id"),
- listing_addr: format!("30402:{seller_pubkey}:AAAAAAAAAAAAAAAAAAAAAg")
- .parse()
- .expect("listing address"),
- buyer_pubkey,
- seller_pubkey,
- status: RadrootsOrderFulfillmentState::ReadyForPickup,
- }
-}
-
-fn sample_order_cancellation(
- buyer_pubkey: String,
- seller_pubkey: String,
-) -> RadrootsOrderCancellation {
- let buyer_pubkey = public_key(buyer_pubkey);
- let seller_pubkey = public_key(seller_pubkey);
- RadrootsOrderCancellation {
- order_id: "order-1".parse().expect("order id"),
- listing_addr: format!("30402:{seller_pubkey}:AAAAAAAAAAAAAAAAAAAAAg")
- .parse()
- .expect("listing address"),
- buyer_pubkey,
- seller_pubkey,
- reason: "schedule changed".into(),
- }
-}
-
-fn sample_buyer_receipt(buyer_pubkey: String, seller_pubkey: String) -> RadrootsOrderReceipt {
- let buyer_pubkey = public_key(buyer_pubkey);
- let seller_pubkey = public_key(seller_pubkey);
- RadrootsOrderReceipt {
- order_id: "order-1".parse().expect("order id"),
- listing_addr: format!("30402:{seller_pubkey}:AAAAAAAAAAAAAAAAAAAAAg")
- .parse()
- .expect("listing address"),
- buyer_pubkey,
- seller_pubkey,
- received: true,
- issue: None,
- received_at: 1_785_000_000,
- }
-}
-
-#[tokio::test]
-async fn relay_direct_farm_publish_accepts_sdk_built_draft() -> TestResult<()> {
- let relay = AckRelay::spawn().await?;
- let identity = RadrootsIdentity::generate();
- let mut config = RadrootsSdkConfig::for_environment(SdkEnvironment::Custom);
- config.transport = SdkTransportMode::RelayDirect;
- config.signer = SignerConfig::LocalIdentity;
- config.relay = RelayConfig {
- urls: vec![relay.url().to_owned()],
- };
- let client = RadrootsSdkClient::from_config(config)?;
- let draft = client.farm().build_draft(&sample_farm())?;
-
- let receipt = client
- .farm()
- .publish_draft_with_identity(&identity, draft)
- .await?;
-
- assert_eq!(receipt.transport, SdkTransportMode::RelayDirect);
- assert_eq!(receipt.event_kind, Some(30340));
- assert!(receipt.event_id.is_some());
- match receipt.transport_receipt {
- SdkTransportReceipt::RelayDirect(relay_receipt) => {
- assert_eq!(
- receipt.event_id.as_deref(),
- Some(relay_receipt.event_id.as_str())
- );
- assert_eq!(relay_receipt.event.kind, 30340);
- assert_eq!(relay_receipt.event.author, identity.public_key_hex());
- assert_eq!(
- relay_receipt.event.tags,
- vec![
- vec!["d".to_owned(), "AAAAAAAAAAAAAAAAAAAAAA".to_owned()],
- vec!["t".to_owned(), "vegetables".to_owned()]
- ]
- );
- assert_eq!(relay_receipt.target_relays, vec![relay.url().to_owned()]);
- assert_eq!(relay_receipt.connected_relays, vec![relay.url().to_owned()]);
- assert_eq!(
- relay_receipt.acknowledged_relays,
- vec![relay.url().to_owned()]
- );
- assert!(relay_receipt.failed_relays.is_empty());
- }
- SdkTransportReceipt::Radrootsd(_) => panic!("unexpected radrootsd receipt"),
- }
-
- Ok(())
-}
-
-#[tokio::test]
-async fn relay_direct_order_request_publish_accepts_sdk_built_draft() -> TestResult<()> {
- let relay = AckRelay::spawn().await?;
- let buyer_identity = RadrootsIdentity::generate();
- let seller_identity = RadrootsIdentity::generate();
- let listing_event = listing_event_ptr();
- let payload = sample_order_request(
- buyer_identity.public_key_hex(),
- seller_identity.public_key_hex(),
- );
- let mut config = RadrootsSdkConfig::for_environment(SdkEnvironment::Custom);
- config.transport = SdkTransportMode::RelayDirect;
- config.signer = SignerConfig::LocalIdentity;
- config.relay = RelayConfig {
- urls: vec![relay.url().to_owned()],
- };
- let client = RadrootsSdkClient::from_config(config)?;
- let draft = client
- .order()
- .build_order_request_draft(&listing_event, &payload)?;
- assert_eq!(draft.as_wire_parts().kind, 3422);
-
- let receipt = client
- .order()
- .publish_order_request_draft_with_identity(&buyer_identity, draft)
- .await?;
-
- assert_eq!(receipt.transport, SdkTransportMode::RelayDirect);
- assert_eq!(receipt.event_kind, Some(3422));
- assert!(receipt.event_id.is_some());
- match receipt.transport_receipt {
- SdkTransportReceipt::RelayDirect(relay_receipt) => {
- assert_eq!(
- receipt.event_id.as_deref(),
- Some(relay_receipt.event_id.as_str())
- );
- assert_eq!(receipt.event_kind, Some(relay_receipt.event_kind));
- assert_eq!(relay_receipt.event.kind, 3422);
- assert_eq!(relay_receipt.event_id, relay_receipt.event.id);
- assert_eq!(relay_receipt.signature, relay_receipt.event.sig);
- assert_eq!(relay_receipt.created_at, relay_receipt.event.created_at);
- assert_eq!(relay_receipt.event.author, buyer_identity.public_key_hex());
- assert!(
- relay_receipt
- .event
- .tags
- .contains(&vec!["p".to_owned(), seller_identity.public_key_hex()])
- );
- assert!(
- relay_receipt
- .event
- .tags
- .contains(&vec!["a".to_owned(), payload.listing_addr.to_string()])
- );
- assert!(
- relay_receipt
- .event
- .tags
- .contains(&vec!["d".to_owned(), payload.order_id.to_string()])
- );
- assert!(relay_receipt.event.tags.contains(&vec![
- "listing_event".to_owned(),
- listing_event.id.clone(),
- listing_event.relays.clone().expect("listing relay")
- ]));
- assert_eq!(relay_receipt.target_relays, vec![relay.url().to_owned()]);
- assert_eq!(relay_receipt.connected_relays, vec![relay.url().to_owned()]);
- assert_eq!(
- relay_receipt.acknowledged_relays,
- vec![relay.url().to_owned()]
- );
- assert!(relay_receipt.failed_relays.is_empty());
- let envelope = client
- .order()
- .parse_order_request(&relay_receipt.event)
- .expect("order request");
- assert_eq!(envelope.order_id, payload.order_id);
- assert_eq!(envelope.listing_addr, payload.listing_addr);
- assert_eq!(envelope.payload.economics.quote_id, "quote-1");
- assert_eq!(envelope.payload.economics.total, usd("10"));
- }
- SdkTransportReceipt::Radrootsd(_) => panic!("unexpected radrootsd receipt"),
- }
-
- Ok(())
-}
-
-#[tokio::test]
-async fn relay_direct_order_decision_publish_accepts_sdk_built_draft() -> TestResult<()> {
- let relay = AckRelay::spawn().await?;
- let buyer_identity = RadrootsIdentity::generate();
- let seller_identity = RadrootsIdentity::generate();
- let root_event_id = event_id('1');
- let payload = sample_order_decision(
- buyer_identity.public_key_hex(),
- seller_identity.public_key_hex(),
- );
- let mut config = RadrootsSdkConfig::for_environment(SdkEnvironment::Custom);
- config.transport = SdkTransportMode::RelayDirect;
- config.signer = SignerConfig::LocalIdentity;
- config.relay = RelayConfig {
- urls: vec![relay.url().to_owned()],
- };
- let client = RadrootsSdkClient::from_config(config)?;
- let draft =
- client
- .order()
- .build_order_decision_draft(&root_event_id, &root_event_id, &payload)?;
- assert_eq!(draft.as_wire_parts().kind, 3423);
-
- let receipt = client
- .order()
- .publish_order_decision_draft_with_identity(&seller_identity, draft)
- .await?;
-
- assert_eq!(receipt.transport, SdkTransportMode::RelayDirect);
- assert_eq!(receipt.event_kind, Some(3423));
- assert!(receipt.event_id.is_some());
- match receipt.transport_receipt {
- SdkTransportReceipt::RelayDirect(relay_receipt) => {
- assert_eq!(
- receipt.event_id.as_deref(),
- Some(relay_receipt.event_id.as_str())
- );
- assert_eq!(receipt.event_kind, Some(relay_receipt.event_kind));
- assert_eq!(relay_receipt.event.kind, 3423);
- assert_eq!(relay_receipt.event.author, seller_identity.public_key_hex());
- assert!(
- relay_receipt
- .event
- .tags
- .contains(&vec!["p".to_owned(), buyer_identity.public_key_hex()])
- );
- assert!(
- relay_receipt
- .event
- .tags
- .contains(&vec!["a".to_owned(), payload.listing_addr.to_string()])
- );
- assert!(
- relay_receipt
- .event
- .tags
- .contains(&vec!["d".to_owned(), payload.order_id.to_string()])
- );
- assert!(
- relay_receipt
- .event
- .tags
- .contains(&vec!["e_root".to_owned(), root_event_id.to_string()])
- );
- assert!(
- relay_receipt
- .event
- .tags
- .contains(&vec!["e_prev".to_owned(), root_event_id.to_string()])
- );
- assert_eq!(relay_receipt.target_relays, vec![relay.url().to_owned()]);
- assert_eq!(relay_receipt.connected_relays, vec![relay.url().to_owned()]);
- assert_eq!(
- relay_receipt.acknowledged_relays,
- vec![relay.url().to_owned()]
- );
- assert!(relay_receipt.failed_relays.is_empty());
- let envelope = client
- .order()
- .parse_order_decision(&relay_receipt.event)
- .expect("order decision");
- assert_eq!(envelope.order_id, payload.order_id);
- assert_eq!(envelope.listing_addr, payload.listing_addr);
- assert_eq!(envelope.payload.decision, payload.decision);
- }
- SdkTransportReceipt::Radrootsd(_) => panic!("unexpected radrootsd receipt"),
- }
-
- Ok(())
-}
-
-#[tokio::test]
-async fn relay_direct_order_revision_publish_accepts_sdk_built_payloads() -> TestResult<()> {
- let relay = AckRelay::spawn().await?;
- let buyer_identity = RadrootsIdentity::generate();
- let seller_identity = RadrootsIdentity::generate();
- let buyer_pubkey = buyer_identity.public_key_hex();
- let seller_pubkey = seller_identity.public_key_hex();
- let root_event_id = event_id('1');
- let decision_event_id = event_id('2');
- let proposal = sample_order_revision_proposal(
- buyer_pubkey.clone(),
- seller_pubkey.clone(),
- root_event_id.to_string(),
- decision_event_id.to_string(),
- );
- let decision =
- sample_order_revision_decision(&proposal, RadrootsOrderRevisionOutcome::Accepted);
- let mut config = RadrootsSdkConfig::for_environment(SdkEnvironment::Custom);
- config.transport = SdkTransportMode::RelayDirect;
- config.signer = SignerConfig::LocalIdentity;
- config.relay = RelayConfig {
- urls: vec![relay.url().to_owned()],
- };
- let client = RadrootsSdkClient::from_config(config)?;
-
- let proposal_receipt = client
- .order()
- .publish_order_revision_proposal_with_identity(
- &seller_identity,
- &root_event_id,
- &decision_event_id,
- &proposal,
- )
- .await?;
- let decision_receipt = client
- .order()
- .publish_order_revision_decision_with_identity(
- &buyer_identity,
- &root_event_id,
- &decision.prev_event_id,
- &decision,
- )
- .await?;
-
- assert_eq!(proposal_receipt.event_kind, Some(3424));
- assert_eq!(decision_receipt.event_kind, Some(3425));
-
- match proposal_receipt.transport_receipt {
- SdkTransportReceipt::RelayDirect(relay_receipt) => {
- assert_eq!(relay_receipt.event.kind, 3424);
- assert_eq!(relay_receipt.event.author, seller_pubkey);
- assert!(
- relay_receipt
- .event
- .tags
- .contains(&vec!["p".to_owned(), buyer_pubkey.clone()])
- );
- assert!(
- relay_receipt
- .event
- .tags
- .contains(&vec!["e_root".to_owned(), root_event_id.to_string()])
- );
- assert!(
- relay_receipt
- .event
- .tags
- .contains(&vec!["e_prev".to_owned(), decision_event_id.to_string()])
- );
- let envelope = client
- .order()
- .parse_order_revision_proposal(&relay_receipt.event)
- .expect("order revision proposal");
- assert_eq!(envelope.order_id, proposal.order_id);
- assert_eq!(envelope.listing_addr, proposal.listing_addr);
- assert_eq!(envelope.payload.revision_id, "revision-1");
- assert_eq!(envelope.payload.economics.total, usd("15"));
- assert_eq!(envelope.payload.reason, "update count");
- }
- SdkTransportReceipt::Radrootsd(_) => panic!("unexpected radrootsd receipt"),
- }
-
- match decision_receipt.transport_receipt {
- SdkTransportReceipt::RelayDirect(relay_receipt) => {
- assert_eq!(relay_receipt.event.kind, 3425);
- assert_eq!(relay_receipt.event.author, buyer_pubkey);
- assert!(
- relay_receipt
- .event
- .tags
- .contains(&vec!["p".to_owned(), seller_pubkey])
- );
- assert!(
- relay_receipt
- .event
- .tags
- .contains(&vec!["e_root".to_owned(), root_event_id.to_string()])
- );
- assert!(
- relay_receipt
- .event
- .tags
- .contains(&vec!["e_prev".to_owned(), event_id_wire('3')])
- );
- let envelope = client
- .order()
- .parse_order_revision_decision(&relay_receipt.event)
- .expect("order revision decision");
- assert_eq!(envelope.order_id, decision.order_id);
- assert_eq!(envelope.listing_addr, decision.listing_addr);
- assert_eq!(envelope.payload.revision_id, decision.revision_id);
- assert_eq!(
- envelope.payload.decision,
- RadrootsOrderRevisionOutcome::Accepted
- );
- }
- SdkTransportReceipt::Radrootsd(_) => panic!("unexpected radrootsd receipt"),
- }
-
- Ok(())
-}
-
-#[tokio::test]
-async fn relay_direct_order_lifecycle_publish_accepts_sdk_built_payloads() -> TestResult<()> {
- let relay = AckRelay::spawn().await?;
- let buyer_identity = RadrootsIdentity::generate();
- let seller_identity = RadrootsIdentity::generate();
- let buyer_pubkey = buyer_identity.public_key_hex();
- let seller_pubkey = seller_identity.public_key_hex();
- let root_event_id = event_id('1');
- let decision_event_id = event_id('2');
- let fulfillment_event_id = event_id('4');
- let fulfillment = sample_fulfillment_update(buyer_pubkey.clone(), seller_pubkey.clone());
- let cancellation = sample_order_cancellation(buyer_pubkey.clone(), seller_pubkey.clone());
- let receipt = sample_buyer_receipt(buyer_pubkey.clone(), seller_pubkey.clone());
- let mut config = RadrootsSdkConfig::for_environment(SdkEnvironment::Custom);
- config.transport = SdkTransportMode::RelayDirect;
- config.signer = SignerConfig::LocalIdentity;
- config.relay = RelayConfig {
- urls: vec![relay.url().to_owned()],
- };
- let client = RadrootsSdkClient::from_config(config)?;
-
- let fulfillment_receipt = client
- .order()
- .publish_fulfillment_update_with_identity(
- &seller_identity,
- &root_event_id,
- &decision_event_id,
- &fulfillment,
- )
- .await?;
- let cancellation_receipt = client
- .order()
- .publish_order_cancellation_with_identity(
- &buyer_identity,
- &root_event_id,
- &root_event_id,
- &cancellation,
- )
- .await?;
- let buyer_receipt = client
- .order()
- .publish_buyer_receipt_with_identity(
- &buyer_identity,
- &root_event_id,
- &fulfillment_event_id,
- &receipt,
- )
- .await?;
-
- assert_eq!(fulfillment_receipt.event_kind, Some(3433));
- assert_eq!(cancellation_receipt.event_kind, Some(3432));
- assert_eq!(buyer_receipt.event_kind, Some(3434));
-
- match fulfillment_receipt.transport_receipt {
- SdkTransportReceipt::RelayDirect(relay_receipt) => {
- assert_eq!(relay_receipt.event.kind, 3433);
- assert_eq!(relay_receipt.event.author, seller_pubkey);
- assert!(
- relay_receipt
- .event
- .tags
- .contains(&vec!["p".to_owned(), buyer_pubkey.clone()])
- );
- assert!(
- relay_receipt
- .event
- .tags
- .contains(&vec!["e_root".to_owned(), root_event_id.to_string()])
- );
- assert!(
- relay_receipt
- .event
- .tags
- .contains(&vec!["e_prev".to_owned(), decision_event_id.to_string()])
- );
- let envelope = client
- .order()
- .parse_fulfillment_update(&relay_receipt.event)
- .expect("active fulfillment update");
- assert_eq!(envelope.order_id, fulfillment.order_id);
- assert_eq!(envelope.listing_addr, fulfillment.listing_addr);
- assert_eq!(envelope.payload.status, fulfillment.status);
- }
- SdkTransportReceipt::Radrootsd(_) => panic!("unexpected radrootsd receipt"),
- }
-
- match cancellation_receipt.transport_receipt {
- SdkTransportReceipt::RelayDirect(relay_receipt) => {
- assert_eq!(relay_receipt.event.kind, 3432);
- assert_eq!(relay_receipt.event.author, buyer_pubkey);
- assert!(
- relay_receipt
- .event
- .tags
- .contains(&vec!["p".to_owned(), seller_pubkey.clone()])
- );
- assert!(
- relay_receipt
- .event
- .tags
- .contains(&vec!["e_root".to_owned(), root_event_id.to_string()])
- );
- assert!(
- relay_receipt
- .event
- .tags
- .contains(&vec!["e_prev".to_owned(), root_event_id.to_string()])
- );
- let envelope = client
- .order()
- .parse_order_cancellation(&relay_receipt.event)
- .expect("order cancellation");
- assert_eq!(envelope.order_id, cancellation.order_id);
- assert_eq!(envelope.listing_addr, cancellation.listing_addr);
- assert_eq!(envelope.payload.reason, cancellation.reason);
- }
- SdkTransportReceipt::Radrootsd(_) => panic!("unexpected radrootsd receipt"),
- }
-
- match buyer_receipt.transport_receipt {
- SdkTransportReceipt::RelayDirect(relay_receipt) => {
- assert_eq!(relay_receipt.event.kind, 3434);
- assert_eq!(relay_receipt.event.author, buyer_pubkey);
- assert!(
- relay_receipt
- .event
- .tags
- .contains(&vec!["p".to_owned(), seller_pubkey])
- );
- assert!(
- relay_receipt
- .event
- .tags
- .contains(&vec!["e_root".to_owned(), root_event_id.to_string()])
- );
- assert!(
- relay_receipt
- .event
- .tags
- .contains(&vec!["e_prev".to_owned(), fulfillment_event_id.to_string()])
- );
- let envelope = client
- .order()
- .parse_buyer_receipt(&relay_receipt.event)
- .expect("active buyer receipt");
- assert_eq!(envelope.order_id, receipt.order_id);
- assert_eq!(envelope.listing_addr, receipt.listing_addr);
- assert_eq!(envelope.payload.received, receipt.received);
- }
- SdkTransportReceipt::Radrootsd(_) => panic!("unexpected radrootsd receipt"),
- }
-
- Ok(())
-}
-
-#[tokio::test]
-async fn relay_direct_order_decision_publish_builds_and_publishes_payload() -> TestResult<()> {
- let relay = AckRelay::spawn().await?;
- let buyer_identity = RadrootsIdentity::generate();
- let seller_identity = RadrootsIdentity::generate();
- let payload = sample_order_decision(
- buyer_identity.public_key_hex(),
- seller_identity.public_key_hex(),
- );
- let mut config = RadrootsSdkConfig::for_environment(SdkEnvironment::Custom);
- config.transport = SdkTransportMode::RelayDirect;
- config.signer = SignerConfig::LocalIdentity;
- config.relay = RelayConfig {
- urls: vec![relay.url().to_owned()],
- };
- let client = RadrootsSdkClient::from_config(config)?;
- let root_event_id = event_id('1');
-
- let receipt = client
- .order()
- .publish_order_decision_with_identity(
- &seller_identity,
- &root_event_id,
- &root_event_id,
- &payload,
- )
- .await?;
-
- assert_eq!(receipt.transport, SdkTransportMode::RelayDirect);
- assert_eq!(receipt.event_kind, Some(3423));
-
- Ok(())
-}
-
-#[tokio::test]
-async fn relay_direct_order_request_publish_builds_and_publishes_payload() -> TestResult<()> {
- let relay = AckRelay::spawn().await?;
- let buyer_identity = RadrootsIdentity::generate();
- let seller_identity = RadrootsIdentity::generate();
- let payload = sample_order_request(
- buyer_identity.public_key_hex(),
- seller_identity.public_key_hex(),
- );
- let mut config = RadrootsSdkConfig::for_environment(SdkEnvironment::Custom);
- config.transport = SdkTransportMode::RelayDirect;
- config.signer = SignerConfig::LocalIdentity;
- config.relay = RelayConfig {
- urls: vec![relay.url().to_owned()],
- };
- let client = RadrootsSdkClient::from_config(config)?;
-
- let receipt = client
- .order()
- .publish_order_request_with_identity(&buyer_identity, &listing_event_ptr(), &payload)
- .await?;
-
- assert_eq!(receipt.transport, SdkTransportMode::RelayDirect);
- assert_eq!(receipt.event_kind, Some(3422));
-
- Ok(())
-}
-
-#[tokio::test]
-async fn relay_direct_order_request_publish_rejects_radrootsd_transport_mode() -> TestResult<()> {
- let buyer_identity = RadrootsIdentity::generate();
- let seller_identity = RadrootsIdentity::generate();
- let payload = sample_order_request(
- buyer_identity.public_key_hex(),
- seller_identity.public_key_hex(),
- );
- let mut config = RadrootsSdkConfig::production();
- config.transport = SdkTransportMode::Radrootsd;
- config.signer = SignerConfig::LocalIdentity;
- let client = RadrootsSdkClient::from_config(config)?;
-
- let error = client
- .order()
- .publish_order_request_with_identity(&buyer_identity, &listing_event_ptr(), &payload)
- .await
- .expect_err("unsupported transport");
-
- assert!(matches!(
- error,
- SdkPublishError::UnsupportedTransport {
- transport: SdkTransportMode::Radrootsd,
- operation: "order.publish_order_request_with_identity",
- }
- ));
-
- Ok(())
-}
-
-#[tokio::test]
-async fn relay_direct_order_request_publish_rejects_draft_only_signer_mode() -> TestResult<()> {
- let relay = AckRelay::spawn().await?;
- let buyer_identity = RadrootsIdentity::generate();
- let seller_identity = RadrootsIdentity::generate();
- let payload = sample_order_request(
- buyer_identity.public_key_hex(),
- seller_identity.public_key_hex(),
- );
- let mut config = RadrootsSdkConfig::for_environment(SdkEnvironment::Custom);
- config.transport = SdkTransportMode::RelayDirect;
- config.signer = SignerConfig::DraftOnly;
- config.relay = RelayConfig {
- urls: vec![relay.url().to_owned()],
- };
- let client = RadrootsSdkClient::from_config(config)?;
-
- let error = client
- .order()
- .publish_order_request_with_identity(&buyer_identity, &listing_event_ptr(), &payload)
- .await
- .expect_err("unsupported signer mode");
-
- assert!(matches!(
- error,
- SdkPublishError::UnsupportedSignerMode {
- transport: SdkTransportMode::RelayDirect,
- signer: SignerConfig::DraftOnly,
- required: SignerConfig::LocalIdentity,
- operation: "order.publish_order_request_with_identity",
- }
- ));
-
- Ok(())
-}
-
-#[tokio::test]
-async fn relay_direct_order_request_publish_rejects_invalid_economics() -> TestResult<()> {
- let buyer_identity = RadrootsIdentity::generate();
- let seller_identity = RadrootsIdentity::generate();
- let mut payload = sample_order_request(
- buyer_identity.public_key_hex(),
- seller_identity.public_key_hex(),
- );
- payload.economics.items[0].bin_count = 1;
- let mut config = RadrootsSdkConfig::for_environment(SdkEnvironment::Custom);
- config.transport = SdkTransportMode::RelayDirect;
- config.signer = SignerConfig::LocalIdentity;
- config.relay = RelayConfig {
- urls: vec!["ws://127.0.0.1:9".to_owned()],
- };
- let client = RadrootsSdkClient::from_config(config)?;
-
- let error = client
- .order()
- .publish_order_request_with_identity(&buyer_identity, &listing_event_ptr(), &payload)
- .await
- .expect_err("invalid economics");
-
- assert!(matches!(error, SdkPublishError::Encode(_)));
-
- Ok(())
-}
-
-#[tokio::test]
-async fn relay_direct_order_request_publish_reports_setup_error_detail() -> TestResult<()> {
- let buyer_identity = RadrootsIdentity::generate();
- let seller_identity = RadrootsIdentity::generate();
- let payload = sample_order_request(
- buyer_identity.public_key_hex(),
- seller_identity.public_key_hex(),
- );
- let mut config = RadrootsSdkConfig::for_environment(SdkEnvironment::Custom);
- config.transport = SdkTransportMode::RelayDirect;
- config.signer = SignerConfig::LocalIdentity;
- config.network.timeout_ms = 10;
- config.relay = RelayConfig {
- urls: vec!["ws://127.0.0.1:9".to_owned()],
- };
- let client = RadrootsSdkClient::from_config(config)?;
-
- let error = client
- .order()
- .publish_order_request_with_identity(&buyer_identity, &listing_event_ptr(), &payload)
- .await
- .expect_err("relay setup error");
-
- assert!(matches!(
- error,
- SdkPublishError::RelaySetup {
- transport: SdkTransportMode::RelayDirect,
- operation: "order.publish_order_request_with_identity",
- target_relays,
- error: _,
- } if target_relays == vec!["ws://127.0.0.1:9".to_owned()]
- ));
-
- Ok(())
-}
-
-#[tokio::test]
-async fn relay_direct_farm_publish_rejects_radrootsd_transport_mode() -> TestResult<()> {
- let identity = RadrootsIdentity::generate();
- let mut config = RadrootsSdkConfig::production();
- config.transport = SdkTransportMode::Radrootsd;
- config.signer = SignerConfig::LocalIdentity;
- let client = RadrootsSdkClient::from_config(config)?;
-
- let error = client
- .farm()
- .publish_with_identity(&identity, &sample_farm())
- .await
- .expect_err("unsupported transport");
-
- assert!(matches!(
- error,
- SdkPublishError::UnsupportedTransport {
- transport: SdkTransportMode::Radrootsd,
- operation: "farm.publish_with_identity",
- }
- ));
-
- Ok(())
-}
-
-#[tokio::test]
-async fn relay_direct_farm_publish_rejects_draft_only_signer_mode() -> TestResult<()> {
- let relay = AckRelay::spawn().await?;
- let identity = RadrootsIdentity::generate();
- let mut config = RadrootsSdkConfig::for_environment(SdkEnvironment::Custom);
- config.transport = SdkTransportMode::RelayDirect;
- config.signer = SignerConfig::DraftOnly;
- config.relay = RelayConfig {
- urls: vec![relay.url().to_owned()],
- };
- let client = RadrootsSdkClient::from_config(config)?;
-
- let error = client
- .farm()
- .publish_with_identity(&identity, &sample_farm())
- .await
- .expect_err("unsupported signer mode");
-
- assert!(matches!(
- error,
- SdkPublishError::UnsupportedSignerMode {
- transport: SdkTransportMode::RelayDirect,
- signer: SignerConfig::DraftOnly,
- required: SignerConfig::LocalIdentity,
- operation: "farm.publish_with_identity",
- }
- ));
-
- Ok(())
-}
-
-#[tokio::test]
-async fn relay_direct_profile_publish_accepts_sdk_built_draft() -> TestResult<()> {
- let relay = AckRelay::spawn().await?;
- let identity = RadrootsIdentity::generate();
- let mut config = RadrootsSdkConfig::for_environment(SdkEnvironment::Custom);
- config.transport = SdkTransportMode::RelayDirect;
- config.signer = SignerConfig::LocalIdentity;
- config.relay = RelayConfig {
- urls: vec![relay.url().to_owned()],
- };
- let client = RadrootsSdkClient::from_config(config)?;
- let draft = client
- .profile()
- .build_draft(&sample_profile(), Some(RadrootsProfileType::Farm))?;
-
- let receipt = client
- .profile()
- .publish_draft_with_identity(&identity, draft)
- .await?;
-
- assert_eq!(receipt.transport, SdkTransportMode::RelayDirect);
- assert_eq!(receipt.event_kind, Some(0));
- assert!(receipt.event_id.is_some());
- match receipt.transport_receipt {
- SdkTransportReceipt::RelayDirect(relay_receipt) => {
- assert_eq!(
- receipt.event_id.as_deref(),
- Some(relay_receipt.event_id.as_str())
- );
- assert_eq!(relay_receipt.event.kind, 0);
- assert_eq!(relay_receipt.event.author, identity.public_key_hex());
- assert_eq!(
- relay_receipt.event.tags,
- vec![vec!["t".to_owned(), "radroots:type:farm".to_owned()]]
- );
- assert_eq!(relay_receipt.target_relays, vec![relay.url().to_owned()]);
- assert_eq!(relay_receipt.connected_relays, vec![relay.url().to_owned()]);
- assert_eq!(
- relay_receipt.acknowledged_relays,
- vec![relay.url().to_owned()]
- );
- assert!(relay_receipt.failed_relays.is_empty());
- }
- SdkTransportReceipt::Radrootsd(_) => panic!("unexpected radrootsd receipt"),
- }
-
- Ok(())
-}
-
-#[tokio::test]
-async fn relay_direct_profile_publish_rejects_radrootsd_transport_mode() -> TestResult<()> {
- let identity = RadrootsIdentity::generate();
- let mut config = RadrootsSdkConfig::production();
- config.transport = SdkTransportMode::Radrootsd;
- config.signer = SignerConfig::LocalIdentity;
- let client = RadrootsSdkClient::from_config(config)?;
-
- let error = client
- .profile()
- .publish_with_identity(
- &identity,
- &sample_profile(),
- Some(RadrootsProfileType::Farm),
- )
- .await
- .expect_err("unsupported transport");
-
- assert!(matches!(
- error,
- SdkPublishError::UnsupportedTransport {
- transport: SdkTransportMode::Radrootsd,
- operation: "profile.publish_with_identity",
- }
- ));
-
- Ok(())
-}
-
-#[tokio::test]
-async fn relay_direct_profile_publish_rejects_draft_only_signer_mode() -> TestResult<()> {
- let relay = AckRelay::spawn().await?;
- let identity = RadrootsIdentity::generate();
- let mut config = RadrootsSdkConfig::for_environment(SdkEnvironment::Custom);
- config.transport = SdkTransportMode::RelayDirect;
- config.signer = SignerConfig::DraftOnly;
- config.relay = RelayConfig {
- urls: vec![relay.url().to_owned()],
- };
- let client = RadrootsSdkClient::from_config(config)?;
-
- let error = client
- .profile()
- .publish_with_identity(
- &identity,
- &sample_profile(),
- Some(RadrootsProfileType::Farm),
- )
- .await
- .expect_err("unsupported signer mode");
-
- assert!(matches!(
- error,
- SdkPublishError::UnsupportedSignerMode {
- transport: SdkTransportMode::RelayDirect,
- signer: SignerConfig::DraftOnly,
- required: SignerConfig::LocalIdentity,
- operation: "profile.publish_with_identity",
- }
- ));
-
- Ok(())
-}
-
-#[tokio::test]
-async fn relay_direct_listing_publish_accepts_sdk_built_draft() -> TestResult<()> {
- let relay = AckRelay::spawn().await?;
- let identity = RadrootsIdentity::generate();
- let mut config = RadrootsSdkConfig::for_environment(SdkEnvironment::Custom);
- config.transport = SdkTransportMode::RelayDirect;
- config.signer = SignerConfig::LocalIdentity;
- config.relay = RelayConfig {
- urls: vec![relay.url().to_owned()],
- };
- let client = RadrootsSdkClient::from_config(config)?;
- let draft = client.listing().build_draft(&sample_listing())?;
-
- let receipt = client
- .listing()
- .publish_draft_with_identity(&identity, draft)
- .await?;
-
- assert_eq!(receipt.transport, SdkTransportMode::RelayDirect);
- assert_eq!(receipt.event_kind, Some(30402));
- assert!(receipt.event_id.is_some());
- match receipt.transport_receipt {
- SdkTransportReceipt::RelayDirect(relay_receipt) => {
- assert_eq!(
- receipt.event_id.as_deref(),
- Some(relay_receipt.event_id.as_str())
- );
- assert_eq!(receipt.event_kind, Some(relay_receipt.event_kind));
- assert_eq!(relay_receipt.event.kind, 30402);
- assert_eq!(relay_receipt.event_id, relay_receipt.event.id);
- assert_eq!(relay_receipt.signature, relay_receipt.event.sig);
- assert_eq!(relay_receipt.created_at, relay_receipt.event.created_at);
- assert_eq!(relay_receipt.event.author, identity.public_key_hex());
- assert_eq!(relay_receipt.target_relays, vec![relay.url().to_owned()]);
- assert_eq!(relay_receipt.connected_relays, vec![relay.url().to_owned()]);
- assert_eq!(
- relay_receipt.acknowledged_relays,
- vec![relay.url().to_owned()]
- );
- assert!(relay_receipt.failed_relays.is_empty());
- }
- SdkTransportReceipt::Radrootsd(_) => panic!("unexpected radrootsd receipt"),
- }
-
- Ok(())
-}
-
-#[tokio::test]
-async fn relay_direct_publish_rejects_radrootsd_transport_mode() -> TestResult<()> {
- let identity = RadrootsIdentity::generate();
- let mut config = RadrootsSdkConfig::production();
- config.transport = SdkTransportMode::Radrootsd;
- config.signer = SignerConfig::LocalIdentity;
- let client = RadrootsSdkClient::from_config(config)?;
-
- let error = client
- .listing()
- .publish_with_identity(&identity, &sample_listing())
- .await
- .expect_err("unsupported transport");
-
- assert!(matches!(
- error,
- SdkPublishError::UnsupportedTransport {
- transport: SdkTransportMode::Radrootsd,
- operation: "listing.publish_with_identity",
- }
- ));
-
- Ok(())
-}
-
-#[tokio::test]
-async fn relay_direct_publish_rejects_draft_only_signer_mode() -> TestResult<()> {
- let relay = AckRelay::spawn().await?;
- let identity = RadrootsIdentity::generate();
- let mut config = RadrootsSdkConfig::for_environment(SdkEnvironment::Custom);
- config.transport = SdkTransportMode::RelayDirect;
- config.signer = SignerConfig::DraftOnly;
- config.relay = RelayConfig {
- urls: vec![relay.url().to_owned()],
- };
- let client = RadrootsSdkClient::from_config(config)?;
-
- let error = client
- .listing()
- .publish_with_identity(&identity, &sample_listing())
- .await
- .expect_err("unsupported signer mode");
-
- assert!(matches!(
- error,
- SdkPublishError::UnsupportedSignerMode {
- transport: SdkTransportMode::RelayDirect,
- signer: SignerConfig::DraftOnly,
- required: SignerConfig::LocalIdentity,
- operation: "listing.publish_with_identity",
- }
- ));
-
- Ok(())
-}
-
-#[tokio::test]
-async fn relay_direct_publish_rejects_nip46_signer_mode() -> TestResult<()> {
- let relay = AckRelay::spawn().await?;
- let identity = RadrootsIdentity::generate();
- let mut config = RadrootsSdkConfig::for_environment(SdkEnvironment::Custom);
- config.transport = SdkTransportMode::RelayDirect;
- config.signer = SignerConfig::Nip46;
- config.relay = RelayConfig {
- urls: vec![relay.url().to_owned()],
- };
- let client = RadrootsSdkClient::from_config(config)?;
-
- let error = client
- .listing()
- .publish_with_identity(&identity, &sample_listing())
- .await
- .expect_err("unsupported signer mode");
-
- assert!(matches!(
- error,
- SdkPublishError::UnsupportedSignerMode {
- transport: SdkTransportMode::RelayDirect,
- signer: SignerConfig::Nip46,
- required: SignerConfig::LocalIdentity,
- operation: "listing.publish_with_identity",
- }
- ));
-
- Ok(())
-}
diff --git a/crates/sdk/tests/replica_ingest.rs b/crates/sdk/tests/replica_ingest.rs
@@ -1,85 +0,0 @@
-use radroots_replica_db::ReplicaSql;
-use radroots_replica_db_schema::farm::IFarmFindMany;
-use radroots_replica_sync::{RadrootsReplicaIngestOutcome, radroots_replica_ingest_event};
-use radroots_sdk::{RadrootsFarm, RadrootsNostrEvent, farm};
-use radroots_sql_core::{SqlExecutor, SqliteExecutor};
-use tempfile::{TempDir, tempdir};
-
-fn seller_pubkey() -> String {
- "a".repeat(64)
-}
-
-fn sdk_event(
- id: u64,
- author: &str,
- created_at: u32,
- kind: u32,
- content: String,
- tags: Vec<Vec<String>>,
-) -> RadrootsNostrEvent {
- RadrootsNostrEvent {
- id: format!("{id:064x}"),
- author: author.to_owned(),
- created_at,
- kind,
- tags,
- content,
- sig: "f".repeat(128),
- }
-}
-
-fn sample_farm() -> RadrootsFarm {
- RadrootsFarm {
- d_tag: "AAAAAAAAAAAAAAAAAAAAAA".into(),
- name: "North Farm".into(),
- about: Some("Organic coffee".into()),
- website: None,
- picture: None,
- banner: None,
- location: None,
- tags: Some(vec!["coffee".into()]),
- }
-}
-
-fn open_replica() -> (TempDir, ReplicaSql<SqliteExecutor>) {
- let dir = tempdir().expect("tempdir");
- let db_path = dir.path().join("replica.sqlite");
- let executor = SqliteExecutor::open(&db_path).expect("open sqlite");
- executor
- .exec("PRAGMA foreign_keys = ON;", "[]")
- .expect("enable foreign keys");
- let replica = ReplicaSql::new(executor);
- replica.migrate_up().expect("migrate");
- (dir, replica)
-}
-
-fn ingest_farm(replica: &ReplicaSql<SqliteExecutor>) -> RadrootsNostrEvent {
- let farm_value = sample_farm();
- let author = seller_pubkey();
- let parts = farm::build_draft(&farm_value).expect("farm draft");
- let event = sdk_event(
- 1,
- &author,
- 1_720_000_000,
- parts.kind,
- parts.content,
- parts.tags,
- );
- let outcome = radroots_replica_ingest_event(replica.executor(), &event).expect("ingest farm");
- assert_eq!(outcome, RadrootsReplicaIngestOutcome::Applied);
- event
-}
-
-#[test]
-fn sdk_farm_draft_ingests_into_replica_projection() {
- let (_dir, replica) = open_replica();
- let event = ingest_farm(&replica);
- let farms = replica
- .farm_find_many(&IFarmFindMany { filter: None })
- .expect("query farms")
- .results;
- assert_eq!(farms.len(), 1);
- assert_eq!(farms[0].d_tag, sample_farm().d_tag);
- assert_eq!(farms[0].name, sample_farm().name);
- assert_eq!(farms[0].pubkey, event.author);
-}
diff --git a/crates/xtask/src/contract.rs b/crates/xtask/src/contract.rs
@@ -3612,9 +3612,6 @@ fn validate_contract_bundle_with_release_policy_override(
if bundle.manifest.surface.algorithm_crates.is_empty() {
return Err("contract surface.algorithm_crates must not be empty".to_string());
}
- if bundle.manifest.surface.wasm_crates.is_empty() {
- return Err("contract surface.wasm_crates must not be empty".to_string());
- }
validate_export_mappings(bundle)?;
if bundle.version.contract.version.trim().is_empty() {
return Err("version.contract.version is required".to_string());
@@ -3983,9 +3980,6 @@ pub fn validate_contract_bundle(bundle: &ContractBundle) -> Result<(), String> {
if bundle.manifest.surface.algorithm_crates.is_empty() {
return Err("contract surface.algorithm_crates must not be empty".to_string());
}
- if bundle.manifest.surface.wasm_crates.is_empty() {
- return Err("contract surface.wasm_crates must not be empty".to_string());
- }
validate_export_mappings(bundle)?;
if bundle.version.contract.version.trim().is_empty() {
return Err("version.contract.version is required".to_string());
@@ -5581,9 +5575,6 @@ edition = "2024"
assert_bundle_error("surface.algorithm_crates must not be empty", |bundle| {
bundle.manifest.surface.algorithm_crates.clear();
});
- assert_bundle_error("surface.wasm_crates must not be empty", |bundle| {
- bundle.manifest.surface.wasm_crates.clear();
- });
assert_bundle_error(
"at least one language export mapping is required",
|bundle| {
diff --git a/crates/xtask/src/phase1_1.rs b/crates/xtask/src/phase1_1.rs
@@ -31,7 +31,6 @@ pub fn validate_invariants(root: &Path) -> Result<(), String> {
PathBuf::from("crates/events/src"),
PathBuf::from("crates/events_codec/src"),
PathBuf::from("crates/trade/src"),
- PathBuf::from("crates/sdk/src"),
],
&[
"RadrootsTradeMessageType",
@@ -55,7 +54,6 @@ pub fn validate_invariants(root: &Path) -> Result<(), String> {
"public_trade",
"events::trade::",
"events_codec::trade::",
- "radroots_sdk::trade::",
"trade_order_economics_digest",
"trade_revision",
"trade_lifecycle",
diff --git a/docs/nix.md b/docs/nix.md
@@ -63,7 +63,6 @@ The shells provide:
- Rust `1.92.0` with `wasm32-unknown-unknown`
- pinned nightly cargo for coverage from `rust-toolchain-coverage.toml`
-- `wasm-pack`
- `cargo-llvm-cov`
- `pkg-config`
- `clang` and `libclang`
@@ -84,7 +83,6 @@ nix run .#fmt
nix run .#check
nix run .#contract
nix run .#coverage-report
-nix run .#wasm-builds
nix run .#release-preflight
```
@@ -98,7 +96,6 @@ nix run .#release-preflight
Repo-aware flows stay behind `nix run` apps because they need a real checkout:
- coverage refresh and release preflight produce repo-local artifacts derived from measured per-crate gate reports
-- wasm packaging writes package output directories
- publish commands read runtime tokens and the live checkout state
## First Verification
diff --git a/nix/apps.nix b/nix/apps.nix
@@ -99,10 +99,4 @@ in
pathPrefix = coveragePath;
};
- wasm-builds = mkRepoApp {
- name = "wasm-builds";
- description = "Build the wasm packages defined by the workspace makefile";
- runtimeInputs = common.runtimeInputs.wasm;
- command = common.wasmBuildsCommand;
- };
}
diff --git a/nix/common.nix b/nix/common.nix
@@ -18,7 +18,6 @@ let
lib.fileset.unions [
../Cargo.toml
../Cargo.lock
- ../Makefile
../README
../rust-toolchain.toml
../spec
@@ -85,12 +84,7 @@ let
toolchains.coverage
cargoLlvmCov
];
- wasmRuntimeInputs = stableRuntimeInputs ++ [
- pkgs.wasm-pack
- ];
- releaseRuntimeInputs = coverageRuntimeInputs ++ [
- pkgs.wasm-pack
- ];
+ releaseRuntimeInputs = coverageRuntimeInputs;
sdkContractCrates = [
"xtask"
"radroots_core"
@@ -100,7 +94,6 @@ let
"radroots_identity"
"radroots_replica_db_schema"
"radroots_events_codec"
- "radroots_events_codec_wasm"
"radroots_nostr_connect"
"radroots_nostr_signer"
];
@@ -186,9 +179,6 @@ let
cargo test -q ${sdkContractCargoArgs}
cargo run -q -p xtask -- sdk validate
'';
- wasmBuildsCommand = ''
- make build
- '';
releasePreflightCommand = ''
./scripts/ci/release_preflight.sh
'';
@@ -313,7 +303,6 @@ in
sdkContractCargoArgs
sharedEnv
version
- wasmBuildsCommand
xtaskPackage
;
@@ -324,6 +313,5 @@ in
stable = stableRuntimeInputs;
coverage = coverageRuntimeInputs;
release = releaseRuntimeInputs;
- wasm = wasmRuntimeInputs;
};
}
diff --git a/policy/coverage/policy.toml b/policy/coverage/policy.toml
@@ -52,7 +52,6 @@ crates = [
"radroots_runtime_paths",
"radroots_runtime_distribution",
"radroots_runtime_manager",
- "radroots_sdk",
"radroots_identity",
"radroots_secret_vault",
"radroots_trade",
diff --git a/spec/RCLD.md b/spec/RCLD.md
@@ -147,7 +147,7 @@ Examples:
- operation implemented in `radroots_events_codec`
- type defined in `radroots_events`
-- deterministic helper exposed via `radroots_events_codec_wasm`
+- deterministic helper exposed by SDK-owned wasm bindings
## Tier 1 Domains And Operations
@@ -572,7 +572,7 @@ model_crates = [
"radroots_identity",
]
algorithm_crates = ["radroots_events_codec"]
-wasm_crates = ["radroots_events_codec_wasm"]
+wasm_crates = []
```
This keeps current workspace knowledge available without making it the public contract unit.
diff --git a/spec/README.md b/spec/README.md
@@ -1,14 +1,13 @@
-# radroots_sdk
+# radroots_core_contract
-Curated SDK contract for the Rad Roots cross-language SDK.
+Core wire, event, codec, and replica contract for Rad Roots SDK consumers.
## Purpose
-This directory defines the `radroots_sdk` contract used to align Rust,
-TypeScript, Python, Swift, and Kotlin surfaces. It defines the public
-interoperability boundary for external integrators, keeps Rust as the canonical
-source for exported models and transforms, and enforces deterministic,
-machine-verifiable governance for contract changes and releases.
+This directory defines the rr-rs core contract consumed by the first-class
+Rad Roots SDK repository. It keeps Rust event models, wire codecs, replica core
+semantics, conformance vectors, and release governance deterministic and
+machine-verifiable.
## Contract Surface
@@ -16,20 +15,18 @@ SDK contract metadata is defined in `spec/manifest.toml` and currently includes:
- model crates: `radroots_core`, `radroots_events`, `radroots_trade`, `radroots_identity`
- algorithm crate: `radroots_events_codec`
-- wasm crate: `radroots_events_codec_wasm`
-The curated public Rust entrypoint is `radroots_sdk`.
-The crate list above records implementation provenance for the contract surface;
-it is not a promise that every listed crate is a first-class end-user SDK
-package.
+The first-class Rust SDK and WebAssembly package surfaces are owned by the SDK
+repository. The crate list above records rr-rs implementation provenance for the
+core contract surface; it is not a promise that every listed crate is a
+first-class end-user SDK package.
Public SDK exports are intentionally narrower than the full Rust workspace.
## Field Event Substrate
Field-oriented farming operations are represented in the public Rust substrate
-through `radroots_events`, `radroots_events_codec`, and
-`radroots_events_codec_wasm`.
+through `radroots_events` and `radroots_events_codec`.
The substrate includes workspace manifests, CRDT change envelopes, farm file
metadata, NIP-42 relay auth, NIP-98 HTTP auth, and the supported NIP-29 group
@@ -49,7 +46,7 @@ matching conformance vectors and language export mappings.
## Public Social Event Substrate
Public social events are represented as event and codec substrate in
-`radroots_events`, `radroots_events_codec`, and `radroots_events_codec_wasm`.
+`radroots_events` and `radroots_events_codec`.
The active social-event contract is defined in `spec/social-events.md`. It covers
ordinary posts, comments, reactions, articles, public generic file metadata,
@@ -59,17 +56,15 @@ NIP-65 relay lists through `RadrootsList`.
The social surface is substrate-first. MVP social tag builders for posts,
comments, reactions, articles, generic public file metadata, calendar date
events, and calendar time events are promoted into curated SDK operation
-metadata after their Rust models, codecs, wasm helpers, and deterministic
-conformance vectors exist. Production-v1 repost, report, calendar collection,
-and RSVP behavior remains available through event and codec APIs by default and
-is covered by conformance vectors.
+metadata after their Rust models, codecs, SDK-owned wasm helpers, and
+deterministic conformance vectors exist. Production-v1 repost, report, calendar
+collection, and RSVP behavior remains available through event and codec APIs by
+default and is covered by conformance vectors.
## Rust Crate Tiers
The public Rust story is tiered explicitly.
-- Curated SDK entrypoint:
- - `radroots_sdk`
- Advanced substrate crates:
- `radroots_core`
- `radroots_events`
@@ -99,14 +94,11 @@ The public Rust story is tiered explicitly.
- `radroots_event_store`
- `radroots_outbox`
- `radroots_relay_transport`
- - `radroots_events_codec_wasm`
- `radroots_net`
- `radroots_nostr_runtime`
- `radroots_nostr_ndb`
- `radroots_sql_wasm_bridge`
- `radroots_sql_wasm_core`
- - `radroots_replica_db_wasm`
- - `radroots_replica_sync_wasm`
- `radroots_simplex_chat_proto`
- `radroots_simplex_smp_proto`
@@ -161,9 +153,10 @@ Internal replica crate family:
- `radroots_replica_db_schema`
- `radroots_replica_db`
-- `radroots_replica_db_wasm`
- `radroots_replica_sync`
-- `radroots_replica_sync_wasm`
+
+SDK-owned wasm bindings for replica storage and sync are recorded in
+`spec/replica.toml`.
## Governance
diff --git a/spec/exports/ts.toml b/spec/exports/ts.toml
@@ -7,7 +7,6 @@ repository = "sdk-typescript"
"radroots_events" = "@radroots/sdk"
"radroots_trade" = "@radroots/sdk"
"radroots_identity" = "@radroots/sdk"
-"radroots_events_codec_wasm" = "@radroots/sdk"
[artifacts]
models_dir = "src/generated"
diff --git a/spec/manifest.toml b/spec/manifest.toml
@@ -1,5 +1,5 @@
[contract]
-name = "radroots_sdk"
+name = "radroots_core_contract"
version = "0.1.0-alpha.2"
source = "rust"
@@ -11,14 +11,14 @@ model_crates = [
"radroots_identity",
]
algorithm_crates = ["radroots_events_codec"]
-wasm_crates = ["radroots_events_codec_wasm"]
+wasm_crates = []
-[sdk]
+[consumer_sdk]
rust_package = "radroots_sdk"
public_surface = "operation_first"
website_ingest_contract = true
-[sdk.rust_story]
+[consumer_sdk.rust_story]
entrypoints = ["radroots_sdk"]
advanced_substrate = [
"radroots_core",
@@ -51,14 +51,11 @@ deferred_publication = [
"radroots_event_store",
"radroots_outbox",
"radroots_relay_transport",
- "radroots_events_codec_wasm",
"radroots_net",
"radroots_nostr_runtime",
"radroots_nostr_ndb",
"radroots_sql_wasm_bridge",
"radroots_sql_wasm_core",
- "radroots_replica_db_wasm",
- "radroots_replica_sync_wasm",
"radroots_simplex_chat_proto",
"radroots_simplex_smp_proto",
"radroots_sp1_guest_trade",
@@ -68,9 +65,9 @@ deferred_publication = [
[surface.internal_replica_crates]
schema = "radroots_replica_db_schema"
storage = "radroots_replica_db"
-storage_wasm = "radroots_replica_db_wasm"
+storage_wasm_binding_crate = "radroots_replica_db_wasm"
sync = "radroots_replica_sync"
-sync_wasm = "radroots_replica_sync_wasm"
+sync_wasm_binding_crate = "radroots_replica_sync_wasm"
[export.ts]
packages = ["@radroots/sdk"]
diff --git a/spec/operations.toml b/spec/operations.toml
@@ -1,5 +1,5 @@
[contract]
-name = "radroots_sdk"
+name = "radroots_core_contract"
version = "0.1.0-alpha.2"
source = "rust"
@@ -53,9 +53,9 @@ model_crates = [
"radroots_identity",
]
algorithm_crates = ["radroots_events_codec"]
-wasm_crates = ["radroots_events_codec_wasm"]
+wasm_crates = []
-[sdk]
+[consumer_sdk]
rust_package = "radroots_sdk"
primary_domains = [
"profile",
diff --git a/spec/replica.toml b/spec/replica.toml
@@ -6,9 +6,11 @@ purpose = "offline-first local replica state and deterministic sync"
[crate_family]
schema = "radroots_replica_db_schema"
storage = "radroots_replica_db"
-storage_wasm = "radroots_replica_db_wasm"
sync = "radroots_replica_sync"
-sync_wasm = "radroots_replica_sync_wasm"
+
+[sdk_wasm_bindings]
+storage = "radroots_replica_db_wasm"
+sync = "radroots_replica_sync_wasm"
[policy]
transport_agnostic_sync_core = true
diff --git a/spec/social-events.md b/spec/social-events.md
@@ -2,7 +2,7 @@
Status: active implementation contract
-Scope: public Radroots social Nostr event models, codecs, wasm builders, and deterministic
+Scope: public Radroots social Nostr event models, codecs, SDK-owned wasm builders, and deterministic
conformance vectors in this repository.
## Purpose
@@ -13,7 +13,7 @@ services, and private Field business documents outside this repository's event-c
The target implementation is standards-first and Radroots-named. Event models live in
`radroots_events`, canonical encode/decode behavior lives in `radroots_events_codec`, optional JSON
-to tags helpers live in `radroots_events_codec_wasm`, and deterministic fixtures live under
+to tags helpers are provided by SDK-owned wasm bindings, and deterministic fixtures live under
`spec/conformance`.
## Implementation Inventory
@@ -116,11 +116,11 @@ helpers, and conformance vectors exist. Production-v1 repost, report, calendar c
behavior remains substrate-visible by default unless a consumer proves that it should be promoted
into the curated operation surface.
-`radroots_events_codec_wasm` exposes the canonical JSON-to-tags helper names `post_tags`,
+The SDK-owned events codec wasm binding exposes the canonical JSON-to-tags helper names `post_tags`,
`comment_tags`, `reaction_tags`, `article_tags`, `file_metadata_tags`,
`calendar_date_event_tags`, `calendar_time_event_tags`, `calendar_tags`,
`calendar_event_rsvp_tags`, `repost_tags`, `generic_repost_tags`, and `report_tags` for the public
-social substrate. The same wasm crate exposes `farm_workspace_manifest_tags`,
+social substrate. The same SDK-owned wasm binding exposes `farm_workspace_manifest_tags`,
`farm_crdt_change_tags`, `farm_file_metadata_tags`, `relay_auth_tags`, and `http_auth_tags` for the
field event substrate.