sdk

Radroots SDK and bindings
git clone https://radroots.dev/git/sdk.git
Log | Files | Refs | README

commit 8f306d2f61397891e37ebd04bd00fa8245ec5471
parent 6785fe03f0cdd518c2ab5aec2ee752f9fba22eea
Author: triesap <tyson@radroots.org>
Date:   Thu, 11 Jun 2026 14:09:03 -0700

refactor: align core bindings

Diffstat:
MCargo.lock | 4++++
Mcrates/core_bindings/Cargo.toml | 1+
Mcrates/core_bindings/src/lib.rs | 117+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----
Dcrates/core_bindings/src/typescript/types.ts | 25-------------------------
Mcrates/events_indexed_bindings/Cargo.toml | 1+
Mcrates/events_indexed_bindings/src/lib.rs | 78+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------
Mcrates/identity_bindings/Cargo.toml | 1+
Mcrates/identity_bindings/src/lib.rs | 23++++++++++++++++++-----
Dcrates/identity_bindings/src/typescript/constants.ts | 3---
Mcrates/types_bindings/Cargo.toml | 1+
Mcrates/types_bindings/src/lib.rs | 35++++++++++++++++++++++++++++++-----
Dcrates/types_bindings/src/typescript/types.ts | 9---------
Mcrates/xtask/src/output.rs | 20++++++++++++++------
Mpackages/core-bindings/src/generated/types.ts | 4++--
Mpackages/identity-bindings/src/generated/constants.ts | 4+++-
15 files changed, 250 insertions(+), 76 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock @@ -581,6 +581,7 @@ name = "radroots_core_bindings" version = "0.1.0" dependencies = [ "radroots_core", + "radroots_sdk_binding_model", ] [[package]] @@ -617,6 +618,7 @@ name = "radroots_events_indexed_bindings" version = "0.1.0" dependencies = [ "radroots_events_indexed", + "radroots_sdk_binding_model", ] [[package]] @@ -638,6 +640,7 @@ name = "radroots_identity_bindings" version = "0.1.0" dependencies = [ "radroots_identity", + "radroots_sdk_binding_model", ] [[package]] @@ -733,6 +736,7 @@ dependencies = [ name = "radroots_types_bindings" version = "0.1.0" dependencies = [ + "radroots_sdk_binding_model", "radroots_types", ] diff --git a/crates/core_bindings/Cargo.toml b/crates/core_bindings/Cargo.toml @@ -9,4 +9,5 @@ homepage.workspace = true publish = false [dependencies] +radroots_sdk_binding_model = { path = "../binding_model" } radroots_core = { workspace = true } diff --git a/crates/core_bindings/src/lib.rs b/crates/core_bindings/src/lib.rs @@ -1,15 +1,122 @@ pub use radroots_core as upstream; -pub const TYPES_TS: &str = include_str!("typescript/types.ts"); +use radroots_sdk_binding_model as ts; + +pub fn types_module() -> ts::TsModule { + ts::module(vec![ + ts::type_alias("RadrootsCoreCurrency", ts::string()), + ts::type_alias("RadrootsCoreDecimal", ts::string()), + ts::type_alias( + "RadrootsCoreDiscount", + ts::object(vec![ + ts::field("scope", ts::reference("RadrootsCoreDiscountScope")), + ts::field("threshold", ts::reference("RadrootsCoreDiscountThreshold")), + ts::field("value", ts::reference("RadrootsCoreDiscountValue")), + ]), + ), + ts::type_alias( + "RadrootsCoreDiscountScope", + ts::union(vec![ + ts::string_literal("bin"), + ts::string_literal("order_total"), + ]), + ), + ts::type_alias( + "RadrootsCoreDiscountThreshold", + ts::union(vec![ + ts::object(vec![ + ts::field("kind", ts::string_literal("bin_count")), + ts::field( + "amount", + ts::object(vec![ + ts::field("bin_id", ts::string()), + ts::field("min", ts::number()), + ]), + ), + ]), + ts::object(vec![ + ts::field("kind", ts::string_literal("order_quantity")), + ts::field( + "amount", + ts::object(vec![ts::field( + "min", + ts::reference("RadrootsCoreQuantity"), + )]), + ), + ]), + ]), + ), + ts::type_alias( + "RadrootsCoreDiscountValue", + ts::union(vec![ + ts::object(vec![ + ts::field("kind", ts::string_literal("money_per_bin")), + ts::field("amount", ts::reference("RadrootsCoreMoney")), + ]), + ts::object(vec![ + ts::field("kind", ts::string_literal("percent")), + ts::field("amount", ts::reference("RadrootsCorePercent")), + ]), + ]), + ), + ts::type_alias( + "RadrootsCoreMoney", + ts::object(vec![ + ts::field("amount", ts::string()), + ts::field("currency", ts::string()), + ]), + ), + ts::type_alias( + "RadrootsCorePercent", + ts::object(vec![ts::field("value", ts::string())]), + ), + ts::type_alias( + "RadrootsCoreQuantity", + ts::object(vec![ + ts::field("amount", ts::string()), + ts::field("unit", ts::reference("RadrootsCoreUnit")), + ts::field("label", ts::nullable(ts::string())), + ]), + ), + ts::type_alias( + "RadrootsCoreQuantityPrice", + ts::object(vec![ + ts::field("amount", ts::reference("RadrootsCoreMoney")), + ts::field("quantity", ts::reference("RadrootsCoreQuantity")), + ]), + ), + ts::type_alias( + "RadrootsCoreUnit", + ts::union(vec![ + ts::string_literal("each"), + ts::string_literal("kg"), + ts::string_literal("g"), + ts::string_literal("oz"), + ts::string_literal("lb"), + ts::string_literal("l"), + ts::string_literal("ml"), + ]), + ), + ts::type_alias( + "RadrootsCoreUnitDimension", + ts::union(vec![ + ts::string_literal("count"), + ts::string_literal("mass"), + ts::string_literal("volume"), + ]), + ), + ]) +} #[cfg(test)] mod tests { - use super::TYPES_TS; + use super::types_module; #[test] fn preserves_core_type_exports() { - assert!(TYPES_TS.contains("export type RadrootsCoreMoney")); - assert!(TYPES_TS.contains("export type RadrootsCoreQuantityPrice")); - assert!(TYPES_TS.contains("\"each\"")); + let rendered = types_module().render(); + assert!(rendered.contains("export type RadrootsCoreMoney")); + assert!(rendered.contains("export type RadrootsCoreQuantityPrice")); + assert!(rendered.contains("\"each\"")); } } diff --git a/crates/core_bindings/src/typescript/types.ts b/crates/core_bindings/src/typescript/types.ts @@ -1,25 +0,0 @@ -// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. - -export type RadrootsCoreCurrency = string; - -export type RadrootsCoreDecimal = string; - -export type RadrootsCoreDiscount = { scope: RadrootsCoreDiscountScope, threshold: RadrootsCoreDiscountThreshold, value: RadrootsCoreDiscountValue, }; - -export type RadrootsCoreDiscountScope = "bin" | "order_total"; - -export type RadrootsCoreDiscountThreshold = { "kind": "bin_count", "amount": { bin_id: string, min: number, } } | { "kind": "order_quantity", "amount": { min: RadrootsCoreQuantity, } }; - -export type RadrootsCoreDiscountValue = { "kind": "money_per_bin", "amount": RadrootsCoreMoney } | { "kind": "percent", "amount": RadrootsCorePercent }; - -export type RadrootsCoreMoney = { amount: string, currency: string, }; - -export type RadrootsCorePercent = { value: string, }; - -export type RadrootsCoreQuantity = { amount: string, unit: RadrootsCoreUnit, label: string | null, }; - -export type RadrootsCoreQuantityPrice = { amount: RadrootsCoreMoney, quantity: RadrootsCoreQuantity, }; - -export type RadrootsCoreUnit = "each" | "kg" | "g" | "oz" | "lb" | "l" | "ml"; - -export type RadrootsCoreUnitDimension = "count" | "mass" | "volume"; diff --git a/crates/events_indexed_bindings/Cargo.toml b/crates/events_indexed_bindings/Cargo.toml @@ -9,4 +9,5 @@ homepage.workspace = true publish = false [dependencies] +radroots_sdk_binding_model = { path = "../binding_model" } radroots_events_indexed = { workspace = true } diff --git a/crates/events_indexed_bindings/src/lib.rs b/crates/events_indexed_bindings/src/lib.rs @@ -1,26 +1,74 @@ pub use radroots_events_indexed as upstream; -pub const TYPES_TS: &str = r#"export type RadrootsEventsIndexedShardId = string; +use radroots_sdk_binding_model as ts; -export type RadrootsEventsIndexedIdRange = { start: string, end: string, }; - -export type RadrootsEventsIndexedShardMetadata = { file: string, count: number, first_id: string, last_id: string, first_published_at: number, last_published_at: number, sha256: string, }; - -export type RadrootsEventsIndexedManifest = { country: string, total: number, shard_size: number, first_published_at: number, last_published_at: number, shards: Array<RadrootsEventsIndexedShardMetadata>, }; - -export type RadrootsEventsIndexedShardCheckpoint = { shard_id: RadrootsEventsIndexedShardId, last_created_at: number, last_event_id: string | null, cursor: string | null, }; - -export type RadrootsEventsIndexedIndexCheckpoint = { generated_at: number, shards: Array<RadrootsEventsIndexedShardCheckpoint>, }; -"#; +pub fn types_module() -> ts::TsModule { + ts::module(vec![ + ts::type_alias("RadrootsEventsIndexedShardId", ts::string()), + ts::type_alias( + "RadrootsEventsIndexedIdRange", + ts::object(vec![ + ts::field("start", ts::string()), + ts::field("end", ts::string()), + ]), + ), + ts::type_alias( + "RadrootsEventsIndexedShardMetadata", + ts::object(vec![ + ts::field("file", ts::string()), + ts::field("count", ts::number()), + ts::field("first_id", ts::string()), + ts::field("last_id", ts::string()), + ts::field("first_published_at", ts::number()), + ts::field("last_published_at", ts::number()), + ts::field("sha256", ts::string()), + ]), + ), + ts::type_alias( + "RadrootsEventsIndexedManifest", + ts::object(vec![ + ts::field("country", ts::string()), + ts::field("total", ts::number()), + ts::field("shard_size", ts::number()), + ts::field("first_published_at", ts::number()), + ts::field("last_published_at", ts::number()), + ts::field( + "shards", + ts::array(ts::reference("RadrootsEventsIndexedShardMetadata")), + ), + ]), + ), + ts::type_alias( + "RadrootsEventsIndexedShardCheckpoint", + ts::object(vec![ + ts::field("shard_id", ts::reference("RadrootsEventsIndexedShardId")), + ts::field("last_created_at", ts::number()), + ts::field("last_event_id", ts::nullable(ts::string())), + ts::field("cursor", ts::nullable(ts::string())), + ]), + ), + ts::type_alias( + "RadrootsEventsIndexedIndexCheckpoint", + ts::object(vec![ + ts::field("generated_at", ts::number()), + ts::field( + "shards", + ts::array(ts::reference("RadrootsEventsIndexedShardCheckpoint")), + ), + ]), + ), + ]) +} #[cfg(test)] mod tests { - use super::TYPES_TS; + use super::types_module; #[test] fn exports_indexed_manifest_and_checkpoint_types() { - assert!(TYPES_TS.contains("export type RadrootsEventsIndexedManifest")); - assert!(TYPES_TS.contains("export type RadrootsEventsIndexedIndexCheckpoint")); - assert!(TYPES_TS.contains("export type RadrootsEventsIndexedShardId = string")); + let rendered = types_module().render(); + assert!(rendered.contains("export type RadrootsEventsIndexedManifest")); + assert!(rendered.contains("export type RadrootsEventsIndexedIndexCheckpoint")); + assert!(rendered.contains("export type RadrootsEventsIndexedShardId = string")); } } diff --git a/crates/identity_bindings/Cargo.toml b/crates/identity_bindings/Cargo.toml @@ -9,4 +9,5 @@ homepage.workspace = true publish = false [dependencies] +radroots_sdk_binding_model = { path = "../binding_model" } radroots_identity = { workspace = true } diff --git a/crates/identity_bindings/src/lib.rs b/crates/identity_bindings/src/lib.rs @@ -1,15 +1,28 @@ pub use radroots_identity as upstream; -pub const CONSTANTS_TS: &str = include_str!("typescript/constants.ts"); +use radroots_sdk_binding_model::{self as ts, TsValue}; + +pub fn constants_module() -> ts::TsModule { + ts::module(vec![ + ts::const_number("RADROOTS_USERNAME_MIN_LEN", 3), + ts::const_number("RADROOTS_USERNAME_MAX_LEN", 30), + ts::const_decl( + "RADROOTS_USERNAME_REGEX", + None, + TsValue::String(r#"^(?!.*\.\.)(?!\.)(?!.*\.$)[a-z0-9._-]{3,30}$"#.to_owned()), + ), + ]) +} #[cfg(test)] mod tests { - use super::CONSTANTS_TS; + use super::constants_module; #[test] fn preserves_username_constant_exports() { - assert!(CONSTANTS_TS.contains("RADROOTS_USERNAME_MIN_LEN")); - assert!(CONSTANTS_TS.contains("RADROOTS_USERNAME_MAX_LEN")); - assert!(CONSTANTS_TS.contains("RADROOTS_USERNAME_REGEX")); + let rendered = constants_module().render(); + assert!(rendered.contains("RADROOTS_USERNAME_MIN_LEN")); + assert!(rendered.contains("RADROOTS_USERNAME_MAX_LEN")); + assert!(rendered.contains("RADROOTS_USERNAME_REGEX")); } } diff --git a/crates/identity_bindings/src/typescript/constants.ts b/crates/identity_bindings/src/typescript/constants.ts @@ -1,3 +0,0 @@ -export const RADROOTS_USERNAME_MIN_LEN = 3; -export const RADROOTS_USERNAME_MAX_LEN = 30; -export const RADROOTS_USERNAME_REGEX = "^(?!.*\.\.)(?!\.)(?!.*\.$)[a-z0-9._-]{3,30}$"; diff --git a/crates/types_bindings/Cargo.toml b/crates/types_bindings/Cargo.toml @@ -9,4 +9,5 @@ homepage.workspace = true publish = false [dependencies] +radroots_sdk_binding_model = { path = "../binding_model" } radroots_types = { workspace = true } diff --git a/crates/types_bindings/src/lib.rs b/crates/types_bindings/src/lib.rs @@ -1,15 +1,40 @@ pub use radroots_types as upstream; -pub const TYPES_TS: &str = include_str!("typescript/types.ts"); +use radroots_sdk_binding_model as ts; + +pub fn types_module() -> ts::TsModule { + ts::module(vec![ + ts::type_alias_params( + "IError", + &["T"], + ts::object(vec![ts::field("err", ts::reference("T"))]), + ), + ts::type_alias_params( + "IResult", + &["T"], + ts::object(vec![ts::field("result", ts::reference("T"))]), + ), + ts::type_alias_params( + "IResultList", + &["T"], + ts::object(vec![ts::field("results", ts::array(ts::reference("T")))]), + ), + ts::type_alias( + "IResultPass", + ts::object(vec![ts::field("pass", ts::boolean())]), + ), + ]) +} #[cfg(test)] mod tests { - use super::TYPES_TS; + use super::types_module; #[test] fn preserves_result_wrapper_exports() { - assert!(TYPES_TS.contains("export type IError")); - assert!(TYPES_TS.contains("export type IResultList")); - assert!(TYPES_TS.contains("export type IResultPass")); + let rendered = types_module().render(); + assert!(rendered.contains("export type IError")); + assert!(rendered.contains("export type IResultList")); + assert!(rendered.contains("export type IResultPass")); } } diff --git a/crates/types_bindings/src/typescript/types.ts b/crates/types_bindings/src/typescript/types.ts @@ -1,9 +0,0 @@ -// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. - -export type IError<T> = { err: T, }; - -export type IResult<T> = { result: T, }; - -export type IResultList<T> = { results: Array<T>, }; - -export type IResultPass = { pass: boolean, }; diff --git a/crates/xtask/src/output.rs b/crates/xtask/src/output.rs @@ -78,7 +78,7 @@ pub fn package_outputs() -> Vec<PackageOutput> { vec![ PackageOutput { spec: spec_by_key("core"), - types_ts: Some(TsSource::text(radroots_core_bindings::TYPES_TS)), + types_ts: Some(TsSource::Module(radroots_core_bindings::types_module())), types_imports_ts: None, constants_ts: None, kinds_ts: None, @@ -92,7 +92,9 @@ pub fn package_outputs() -> Vec<PackageOutput> { }, PackageOutput { spec: spec_by_key("events_indexed"), - types_ts: Some(TsSource::text(radroots_events_indexed_bindings::TYPES_TS)), + types_ts: Some(TsSource::Module( + radroots_events_indexed_bindings::types_module(), + )), types_imports_ts: None, constants_ts: None, kinds_ts: None, @@ -101,7 +103,9 @@ pub fn package_outputs() -> Vec<PackageOutput> { spec: spec_by_key("identity"), types_ts: None, types_imports_ts: None, - constants_ts: Some(TsSource::text(radroots_identity_bindings::CONSTANTS_TS)), + constants_ts: Some(TsSource::Module( + radroots_identity_bindings::constants_module(), + )), kinds_ts: None, }, PackageOutput { @@ -122,7 +126,7 @@ pub fn package_outputs() -> Vec<PackageOutput> { }, PackageOutput { spec: spec_by_key("types"), - types_ts: Some(TsSource::text(radroots_types_bindings::TYPES_TS)), + types_ts: Some(TsSource::Module(radroots_types_bindings::types_module())), types_imports_ts: None, constants_ts: None, kinds_ts: None, @@ -141,7 +145,11 @@ fn spec_by_key(key: &str) -> PackageSpec { fn render_ts(source: &TsSource, imports: Option<&str>) -> String { let body = source.render(); let imports = imports.unwrap_or(""); - format!("{}{}{}", generated_header(), imports, body.trim_start()) + let mut rendered = format!("{}{}{}", generated_header(), imports, body.trim_start()); + if !rendered.ends_with('\n') { + rendered.push('\n'); + } + rendered } const EVENTS_TYPES_IMPORTS_TS: &str = r#"import type { @@ -251,7 +259,7 @@ mod tests { ); assert_eq!( output, - "// @generated by cargo xtask generate ts\n// Do not edit by hand.\nexport type A = string;" + "// @generated by cargo xtask generate ts\n// Do not edit by hand.\nexport type A = string;\n" ); } diff --git a/packages/core-bindings/src/generated/types.ts b/packages/core-bindings/src/generated/types.ts @@ -8,9 +8,9 @@ export type RadrootsCoreDiscount = { scope: RadrootsCoreDiscountScope, threshold export type RadrootsCoreDiscountScope = "bin" | "order_total"; -export type RadrootsCoreDiscountThreshold = { "kind": "bin_count", "amount": { bin_id: string, min: number, } } | { "kind": "order_quantity", "amount": { min: RadrootsCoreQuantity, } }; +export type RadrootsCoreDiscountThreshold = { kind: "bin_count", amount: { bin_id: string, min: number, }, } | { kind: "order_quantity", amount: { min: RadrootsCoreQuantity, }, }; -export type RadrootsCoreDiscountValue = { "kind": "money_per_bin", "amount": RadrootsCoreMoney } | { "kind": "percent", "amount": RadrootsCorePercent }; +export type RadrootsCoreDiscountValue = { kind: "money_per_bin", amount: RadrootsCoreMoney, } | { kind: "percent", amount: RadrootsCorePercent, }; export type RadrootsCoreMoney = { amount: string, currency: string, }; diff --git a/packages/identity-bindings/src/generated/constants.ts b/packages/identity-bindings/src/generated/constants.ts @@ -1,5 +1,7 @@ // @generated by cargo xtask generate ts // Do not edit by hand. export const RADROOTS_USERNAME_MIN_LEN = 3; + export const RADROOTS_USERNAME_MAX_LEN = 30; -export const RADROOTS_USERNAME_REGEX = "^(?!.*\.\.)(?!\.)(?!.*\.$)[a-z0-9._-]{3,30}$"; + +export const RADROOTS_USERNAME_REGEX = "^(?!.*\\.\\.)(?!\\.)(?!.*\\.$)[a-z0-9._-]{3,30}$";