lib

Core libraries for Radroots
git clone https://radroots.dev/git/lib.git
Log | Files | Refs | README

commit 1050e18ed6f7f3aac073274f0048bf03ffe25a64
parent 0740da72976e88a58166aa1bcb62a5913c779cc3
Author: triesap <tyson@radroots.org>
Date:   Sun, 28 Jun 2026 00:14:49 +0000

ts: render constants through dto_bindgen

- Move events constants and kinds onto dto_bindgen TypeScriptModule descriptors.

- Move identity constants onto dto_bindgen constant declarations.

- Remove raw TypeScript source handling from the SDK package output path.

- Add module-backed generated output tests for constants and kinds.

Diffstat:
Mcrates/identity_bindings/Cargo.toml | 1+
Mcrates/identity_bindings/src/lib.rs | 28+++++++++++++++++++++++-----
Mtools/sdk_xtask_import/src/output.rs | 89++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------
3 files changed, 99 insertions(+), 19 deletions(-)

diff --git a/crates/identity_bindings/Cargo.toml b/crates/identity_bindings/Cargo.toml @@ -10,4 +10,5 @@ homepage.workspace = true publish = false [dependencies] +dto_bindgen_backend_ts = { workspace = true } radroots_identity = { workspace = true } diff --git a/crates/identity_bindings/src/lib.rs b/crates/identity_bindings/src/lib.rs @@ -1,13 +1,31 @@ pub use radroots_identity as upstream; +use dto_bindgen_backend_ts::{TypeScriptDeclaration, TypeScriptModule, TypeScriptValue}; use radroots_identity::{ RADROOTS_USERNAME_MAX_LEN, RADROOTS_USERNAME_MIN_LEN, RADROOTS_USERNAME_REGEX, }; -pub fn constants_module() -> String { - format!( - "export const RADROOTS_USERNAME_MIN_LEN = {RADROOTS_USERNAME_MIN_LEN};\nexport const RADROOTS_USERNAME_MAX_LEN = {RADROOTS_USERNAME_MAX_LEN};\nexport const RADROOTS_USERNAME_REGEX = {RADROOTS_USERNAME_REGEX:?};" - ) +pub fn constants_module() -> TypeScriptModule { + TypeScriptModule::new("src/generated/constants.ts") + .with_declaration(TypeScriptDeclaration::constant( + "RADROOTS_USERNAME_MIN_LEN", + None, + usize_value(RADROOTS_USERNAME_MIN_LEN), + )) + .with_declaration(TypeScriptDeclaration::constant( + "RADROOTS_USERNAME_MAX_LEN", + None, + usize_value(RADROOTS_USERNAME_MAX_LEN), + )) + .with_declaration(TypeScriptDeclaration::constant( + "RADROOTS_USERNAME_REGEX", + None, + TypeScriptValue::string(RADROOTS_USERNAME_REGEX), + )) +} + +fn usize_value(value: usize) -> TypeScriptValue { + TypeScriptValue::number(i64::try_from(value).expect("TypeScript constant fits in i64")) } #[cfg(test)] @@ -19,7 +37,7 @@ mod tests { #[test] fn preserves_username_constant_exports() { - let rendered = constants_module(); + let rendered = constants_module().render_source(); assert!(rendered.contains("RADROOTS_USERNAME_MIN_LEN")); assert!(rendered.contains(&RADROOTS_USERNAME_MIN_LEN.to_string())); assert!(rendered.contains("RADROOTS_USERNAME_MAX_LEN")); diff --git a/tools/sdk_xtask_import/src/output.rs b/tools/sdk_xtask_import/src/output.rs @@ -23,21 +23,21 @@ pub struct GeneratedFile { pub enum TsSource { DtoRegistry(DtoTypesModule), - Raw(String), + Module(TypeScriptModule), } impl TsSource { fn render(&self) -> String { match self { Self::DtoRegistry(module) => module.body_ts().to_owned(), - Self::Raw(body) => body.clone(), + Self::Module(module) => module.render_source(), } } fn imports(&self) -> Option<&str> { match self { Self::DtoRegistry(module) => module.imports_ts(), - Self::Raw(_) => None, + Self::Module(_) => None, } } } @@ -92,8 +92,10 @@ pub fn package_outputs() -> Result<Vec<PackageOutput>, String> { spec: spec_by_key("events"), types_ts: Some(TsSource::DtoRegistry(dto_roots::events_types_module()?)), types_imports: Vec::new(), - constants_ts: Some(TsSource::Raw(radroots_events_bindings::constants_module())), - kinds_ts: Some(TsSource::Raw(radroots_events_bindings::kinds_module())), + constants_ts: Some(TsSource::Module( + radroots_events_bindings::constants_module(), + )), + kinds_ts: Some(TsSource::Module(radroots_events_bindings::kinds_module())), }, PackageOutput { spec: spec_by_key("events_indexed"), @@ -108,7 +110,9 @@ pub fn package_outputs() -> Result<Vec<PackageOutput>, String> { spec: spec_by_key("identity"), types_ts: None, types_imports: Vec::new(), - constants_ts: Some(TsSource::Raw(radroots_identity_bindings::constants_module())), + constants_ts: Some(TsSource::Module( + radroots_identity_bindings::constants_module(), + )), kinds_ts: None, }, PackageOutput { @@ -212,26 +216,31 @@ fn render_index(output: &PackageOutput) -> String { mod tests { use super::{PackageOutput, TsSource, package_outputs, render_ts}; use crate::package_matrix::package_specs; - use dto_bindgen_backend_ts::{DtoTypesModule, TypeScriptImport}; + use dto_bindgen_backend_ts::{ + DtoTypesModule, TypeScriptDeclaration, TypeScriptImport, TypeScriptModule, TypeScriptType, + }; const TRADE_BINDINGS_TYPES_TS: &str = include_str!("../../../packages/trade-bindings/src/generated/types.ts"); const REPLICA_DB_SCHEMA_BINDINGS_TYPES_TS: &str = include_str!("../../../packages/replica-db-schema-bindings/src/generated/types.ts"); + const EVENTS_BINDINGS_CONSTANTS_TS: &str = + include_str!("../../../packages/events-bindings/src/generated/constants.ts"); + const EVENTS_BINDINGS_KINDS_TS: &str = + include_str!("../../../packages/events-bindings/src/generated/kinds.ts"); + const IDENTITY_BINDINGS_CONSTANTS_TS: &str = + include_str!("../../../packages/identity-bindings/src/generated/constants.ts"); #[test] fn renders_sdk_header() { - let output = render_ts(&TsSource::Raw("export type A = string;".to_owned()), None); + let output = render_ts(&test_module(), None); assert!(output.starts_with("// @generated by cargo xtask generate ts")); assert!(output.contains("export type A = string;")); } #[test] fn renders_import_prelude_after_header() { - let output = render_ts( - &TsSource::Raw("export type A = string;".to_owned()), - Some("import type { B } from \"b\";\n\n"), - ); + let output = render_ts(&test_module(), Some("import type { B } from \"b\";\n\n")); assert!(output.starts_with( "// @generated by cargo xtask generate ts\n// Do not edit by hand.\nimport type" )); @@ -239,8 +248,8 @@ mod tests { } #[test] - fn renders_raw_sources() { - let output = render_ts(&TsSource::Raw("export type A = string;".to_owned()), None); + fn renders_module_sources() { + let output = render_ts(&test_module(), None); assert_eq!( output, "// @generated by cargo xtask generate ts\n// Do not edit by hand.\nexport type A = string;\n" @@ -339,4 +348,56 @@ mod tests { assert_eq!(types.contents, REPLICA_DB_SCHEMA_BINDINGS_TYPES_TS); } + + #[test] + fn events_constants_and_kinds_use_modules_and_match_checked_in_files() { + let output = package_outputs() + .expect("package outputs") + .into_iter() + .find(|output| output.spec.key == "events") + .expect("events output"); + + assert!(matches!(output.constants_ts, Some(TsSource::Module(_)))); + assert!(matches!(output.kinds_ts, Some(TsSource::Module(_)))); + + let files = output.files(); + let constants = files + .iter() + .find(|file| file.relative_path == "src/generated/constants.ts") + .expect("constants file"); + let kinds = files + .iter() + .find(|file| file.relative_path == "src/generated/kinds.ts") + .expect("kinds file"); + + assert_eq!(constants.contents, EVENTS_BINDINGS_CONSTANTS_TS); + assert_eq!(kinds.contents, EVENTS_BINDINGS_KINDS_TS); + } + + #[test] + fn identity_constants_use_module_and_match_checked_in_file() { + let output = package_outputs() + .expect("package outputs") + .into_iter() + .find(|output| output.spec.key == "identity") + .expect("identity output"); + + assert!(matches!(output.constants_ts, Some(TsSource::Module(_)))); + + let constants = output + .files() + .into_iter() + .find(|file| file.relative_path == "src/generated/constants.ts") + .expect("constants file"); + + assert_eq!(constants.contents, IDENTITY_BINDINGS_CONSTANTS_TS); + } + + fn test_module() -> TsSource { + TsSource::Module( + TypeScriptModule::new("src/generated/test.ts").with_declaration( + TypeScriptDeclaration::type_alias("A", TypeScriptType::String), + ), + ) + } }