commit 8f306d2f61397891e37ebd04bd00fa8245ec5471
parent 6785fe03f0cdd518c2ab5aec2ee752f9fba22eea
Author: triesap <tyson@radroots.org>
Date: Thu, 11 Jun 2026 14:09:03 -0700
refactor: align core bindings
Diffstat:
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}$";