From e134ed11a946c3fe935c5beddb70934e47de89ee Mon Sep 17 00:00:00 2001 From: nym21 Date: Fri, 19 Dec 2025 15:48:32 +0100 Subject: [PATCH] global: snapshot --- Cargo.lock | 1 + crates/brk_binder/Cargo.toml | 1 + crates/brk_binder/DESIGN.md | 163 ++++++++----- crates/brk_binder/src/generator/javascript.rs | 229 +++++++++--------- .../src/stateful/states/supply.rs | 3 + crates/brk_types/src/addressstats.rs | 2 +- crates/brk_types/src/anyaddressindex.rs | 1 + crates/brk_types/src/bitcoin.rs | 1 + crates/brk_types/src/blkposition.rs | 1 + crates/brk_types/src/date.rs | 1 + crates/brk_types/src/dollars.rs | 1 + crates/brk_types/src/format.rs | 1 + crates/brk_types/src/health.rs | 2 +- crates/brk_types/src/indexinfo.rs | 2 +- crates/brk_types/src/metriccount.rs | 6 +- crates/brk_types/src/ohlc.rs | 7 + crates/brk_types/src/pagination.rs | 1 + crates/brk_types/src/rewardstats.rs | 3 + crates/brk_types/src/stored_bool.rs | 1 + crates/brk_types/src/stored_f32.rs | 1 + crates/brk_types/src/stored_f64.rs | 1 + crates/brk_types/src/stored_u32.rs | 1 + crates/brk_types/src/stored_u64.rs | 1 + crates/brk_types/src/treenode.rs | 6 +- crates/brk_types/src/tx.rs | 2 +- crates/brk_types/src/txidvout.rs | 3 + crates/brk_types/src/txin.rs | 2 +- crates/brk_types/src/txout.rs | 2 +- crates/brk_types/src/txstatus.rs | 2 +- crates/brk_types/src/weight.rs | 1 + 30 files changed, 265 insertions(+), 184 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 270d34709..48c3b091b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -569,6 +569,7 @@ dependencies = [ "brk_query", "brk_types", "schemars", + "serde_json", "vecdb", ] diff --git a/crates/brk_binder/Cargo.toml b/crates/brk_binder/Cargo.toml index 7d3746345..564e724af 100644 --- a/crates/brk_binder/Cargo.toml +++ b/crates/brk_binder/Cargo.toml @@ -12,4 +12,5 @@ build = "build.rs" brk_query = { workspace = true } brk_types = { workspace = true } schemars = { workspace = true } +serde_json = { workspace = true } vecdb = { workspace = true } diff --git a/crates/brk_binder/DESIGN.md b/crates/brk_binder/DESIGN.md index d6b7c75db..24d95b985 100644 --- a/crates/brk_binder/DESIGN.md +++ b/crates/brk_binder/DESIGN.md @@ -2,7 +2,7 @@ ## Goal -Generate typed API clients for **Rust, TypeScript, and Python** with: +Generate typed API clients for **Rust, JavaScript, and Python** with: - **Discoverability**: Full IDE autocomplete for 20k+ metrics - **Ease of use**: Fluent API with `.fetch()` on each metric node @@ -13,17 +13,22 @@ Generate typed API clients for **Rust, TypeScript, and Python** with: 1. **`js.rs`**: Generates compressed metric catalogs for JS (constants only, no HTTP client) 2. **`tree.rs`**: (kept for reference, not compiled) Brainstorming output for pattern extraction 3. **`generator/`**: Module structure for client generation - - `types.rs`: Intermediate representation (`ClientMetadata`, `MetricInfo`, `IndexPattern`) + - `types.rs`: Intermediate representation (`ClientMetadata`, `MetricInfo`, `IndexPattern`, `schema_to_jsdoc`) - `rust.rs`: Rust client generation (stub) - - `typescript.rs`: TypeScript client generation (stub) + - `javascript.rs`: JavaScript + JSDoc client generation ✅ IMPLEMENTED - `python.rs`: Python client generation (stub) +### What's Working + +- **JS + JSDoc generator**: Generates `client.js` with full JSDoc type annotations +- **schemars integration**: JSON schemas embedded in `MetricLeafWithSchema` for type info +- **Tree navigation**: `client.tree.blocks.difficulty.fetch()` pattern + ### What's Missing -- HTTP client integration (`.fetch()` methods) -- OpenAPI as input source -- Rust client using `brk_types` instead of generating types -- Typed response types per metric +- OpenAPI integration for non-metric endpoints +- Python client implementation +- Rust client implementation ## Target Architecture @@ -39,10 +44,10 @@ Generate typed API clients for **Rust, TypeScript, and Python** with: └─────────────────────────────────────────────────────────────┘ ``` -### Output: Fluent Client (Option B) +### Output: Fluent Client -```typescript -// TypeScript +```javascript +// JavaScript (with JSDoc for IDE support) const client = new BrkClient("http://localhost:3000"); const data = await client.tree.supply.active.by_date.fetch(); // ^^^^ autocomplete all the way down @@ -66,12 +71,28 @@ let data = client.tree.supply.active.by_date.fetch().await?; Each tree leaf becomes a "smart node" holding a client reference: -```typescript -// TypeScript -class MetricNode { - constructor(private client: BrkClient, private path: string) {} - async fetch(): Promise { - return this.client.get(this.path); +```javascript +// JavaScript + JSDoc +/** + * Metric node with fetch capability + * @template T + */ +class MetricNode { + /** + * @param {BrkClientBase} client + * @param {string} path + */ + constructor(client, path) { + this._client = client; + this._path = path; + } + + /** + * Fetch the metric value + * @returns {Promise} + */ + async fetch() { + return this._client.get(this._path); } } ``` @@ -208,7 +229,7 @@ This enables fully typed client generation. ### The Solution -Changed `TreeNode::Leaf(String)` to `TreeNode::Leaf(MetricLeaf)` where: +Changed `TreeNode::Leaf(String)` to `TreeNode::Leaf(MetricLeafWithSchema)` where: ```rust #[derive(Debug, Clone, Serialize, PartialEq, Eq, JsonSchema)] @@ -217,29 +238,45 @@ pub struct MetricLeaf { pub value_type: String, pub indexes: BTreeSet, } + +#[derive(Debug, Clone, Serialize, JsonSchema)] +pub struct MetricLeafWithSchema { + #[serde(flatten)] + pub leaf: MetricLeaf, + #[serde(skip)] + pub schema: serde_json::Value, // JSON Schema from schemars +} ``` #### Implementation **brk_types/src/treenode.rs**: - Added `MetricLeaf` struct with `name`, `value_type`, and `indexes` -- Added `merge_indexes()` method to union indexes when flattening tree -- Updated `TreeNode` enum to use `Leaf(MetricLeaf)` -- Updated merge logic to handle index merging +- Added `MetricLeafWithSchema` wrapper with JSON schema +- Helper methods: `name()`, `value_type()`, `indexes()`, `is_same_metric()`, `merge_indexes()` +- Updated `TreeNode` enum to use `Leaf(MetricLeafWithSchema)` **brk_traversable/src/lib.rs**: -- Added `make_leaf()` helper that creates `MetricLeaf` with proper fields -- Updated all `Traversable::to_tree_node()` implementations +- Added `make_leaf()` helper that creates `MetricLeafWithSchema` with schema from schemars +- Updated all `Traversable::to_tree_node()` implementations with `JsonSchema` bounds +- Schema generated via `schemars::SchemaGenerator::default().into_root_schema_for::()` + +**vecdb** (schemars feature): +- Added optional `schemars` dependency +- Added `AnySchemaVec` trait with blanket impl for `TypedVec where T: JsonSchema` ### Result -The catalog tree now includes full type information at each leaf: +The catalog tree now includes full type information and JSON schema at each leaf: ```rust -TreeNode::Leaf(MetricLeaf { - name: "difficulty".to_string(), - value_type: "StoredF64".to_string(), - indexes: btreeset![Index::Height, Index::Date], +TreeNode::Leaf(MetricLeafWithSchema { + leaf: MetricLeaf { + name: "difficulty".to_string(), + value_type: "StoredF64".to_string(), + indexes: btreeset![Index::Height, Index::Date], + }, + schema: json!({ "type": "number" }), // schemars-generated }) ``` @@ -273,32 +310,46 @@ When trees are merged/simplified, indexes are unioned together. - [x] **vecdb**: Add `short_type_name()` helper in `traits/printable.rs` - [x] **vecdb**: Add `value_type_to_string()` to `AnyVec` trait - [x] **vecdb**: Implement in all vec variants (eager, lazy, raw, compressed, macros) -- [x] **brk_types**: Enhance `TreeNode::Leaf` to include `MetricLeaf` with name, value_type, indexes -- [x] **brk_traversable**: Update all `to_tree_node()` implementations to populate `MetricLeaf` +- [x] **vecdb**: Add optional `schemars` feature with `AnySchemaVec` trait +- [x] **brk_types**: Enhance `TreeNode::Leaf` to include `MetricLeafWithSchema` +- [x] **brk_types**: Add `JsonSchema` derives to all value types +- [x] **brk_traversable**: Update all `to_tree_node()` implementations with schemars integration - [x] **brk_query**: Export `Vecs` publicly for client generation -- [x] **brk_binder**: Set up generator module structure (types, rust, typescript, python stubs) +- [x] **brk_binder**: Set up generator module structure - [x] **brk**: Verify compilation -### Phase 1: Client Foundation +### Phase 1: JavaScript Client ✅ COMPLETE -- [ ] Define `MetricNode` struct/class for each language -- [ ] Define `BrkClient` with base HTTP functionality -- [ ] Implement `ClientMetadata::from_vecs()` to extract metadata from `brk_query::Vecs` -- [ ] Client holds reference, nodes borrow it +- [x] Define `MetricNode` class with JSDoc generics +- [x] Define `BrkClient` with base HTTP functionality +- [x] Implement `ClientMetadata::from_vecs()` to extract metadata +- [x] Generate `client.js` with full JSDoc type annotations +- [x] Use `schema_to_jsdoc()` to convert JSON schemas to JSDoc types +- [x] Tree navigation: `client.tree.category.metric.fetch()` -### Phase 2: Type-Aware Generation +### Phase 2: OpenAPI Integration (NEXT) -- [ ] Create type mapping: `value_type_string → Rust type / TS type / Python type` -- [ ] Generate typed `MetricNode` with correct `T` per metric -- [ ] For Rust: import from `brk_types` instead of generating +- [ ] Add `openapiv3` crate dependency +- [ ] Parse OpenAPI spec from aide (brk_server generates this) +- [ ] Extract non-metric endpoint definitions (health, info, catalog, etc.) +- [ ] Generate methods for each endpoint with proper types +- [ ] Merge with tree-based metric access -### Phase 3: OpenAPI Integration +### Phase 3: Python Client -- [ ] Parse OpenAPI spec with `openapiv3` crate -- [ ] Extract non-metric endpoint definitions -- [ ] Generate methods for health, info, catalog, etc. +- [ ] Define `MetricNode` class with type hints +- [ ] Define `BrkClient` with httpx/aiohttp +- [ ] Generate typed methods from OpenAPI +- [ ] Generate tree navigation -### Phase 4: Polish +### Phase 4: Rust Client + +- [ ] Define `MetricNode` struct using `brk_types` +- [ ] Define `BrkClient` with reqwest +- [ ] Import types from `brk_types` instead of generating +- [ ] Generate tree navigation with proper lifetimes + +### Phase 5: Polish - [ ] Error types per language - [ ] Documentation generation @@ -315,22 +366,26 @@ crates/brk_binder/ │ ├── tree.rs # Pattern extraction (reference only, not compiled) │ └── generator/ │ ├── mod.rs -│ ├── types.rs # ClientMetadata, MetricInfo, IndexPattern -│ ├── rust.rs # Rust client generation -│ ├── typescript.rs -│ └── python.rs +│ ├── types.rs # ClientMetadata, MetricInfo, IndexPattern, schema_to_jsdoc +│ ├── javascript.rs # JavaScript + JSDoc client generation ✅ +│ ├── python.rs # Python client generation (stub) +│ └── rust.rs # Rust client generation (stub) ├── Cargo.toml ├── README.md └── DESIGN.md # This file ``` -## Dependencies (Proposed) +## Dependencies ```toml [dependencies] -openapiv3 = "2" # OpenAPI parsing -serde = { version = "1", features = ["derive"] } -serde_json = "1" -serde_yaml = "0.9" # If parsing YAML specs -tera = "1" # Optional: templating +brk_query = { workspace = true } +brk_types = { workspace = true } +schemars = { workspace = true } +serde_json = { workspace = true } +vecdb = { workspace = true } + +# For OpenAPI integration (Phase 2): +# openapiv3 = "2" # OpenAPI parsing +# serde_yaml = "0.9" # If parsing YAML specs ``` diff --git a/crates/brk_binder/src/generator/javascript.rs b/crates/brk_binder/src/generator/javascript.rs index c6af2bd66..023adc73e 100644 --- a/crates/brk_binder/src/generator/javascript.rs +++ b/crates/brk_binder/src/generator/javascript.rs @@ -1,95 +1,75 @@ -use std::collections::BTreeMap; use std::fmt::Write as FmtWrite; use std::fs; use std::io; use std::path::Path; -use brk_types::{MetricLeaf, TreeNode}; +use brk_types::TreeNode; -use super::{to_camel_case, ClientMetadata, IndexPattern}; +use super::{schema_to_jsdoc, to_camel_case, ClientMetadata, IndexPattern}; -/// Generate TypeScript client from metadata -pub fn generate_typescript_client(metadata: &ClientMetadata, output_dir: &Path) -> io::Result<()> { +/// Generate JavaScript + JSDoc client from metadata +pub fn generate_javascript_client(metadata: &ClientMetadata, output_dir: &Path) -> io::Result<()> { let mut output = String::new(); // Header - writeln!(output, "// Auto-generated BRK TypeScript client").unwrap(); + writeln!(output, "// Auto-generated BRK JavaScript client").unwrap(); writeln!(output, "// Do not edit manually\n").unwrap(); - // Generate pattern interfaces for index groupings - generate_pattern_interfaces(&mut output, &metadata.patterns); - - // Generate value type aliases - generate_value_types(&mut output, metadata); + // Generate pattern JSDoc typedefs for index groupings + generate_pattern_typedefs(&mut output, &metadata.patterns); // Generate the base client class generate_base_client(&mut output); - // Generate tree types from catalog - generate_tree_types(&mut output, &metadata.catalog); + // Generate tree JSDoc typedefs from catalog + generate_tree_typedefs(&mut output, &metadata.catalog); // Generate the main client class with tree generate_main_client(&mut output, &metadata.catalog); - fs::write(output_dir.join("client.ts"), output)?; + fs::write(output_dir.join("client.js"), output)?; Ok(()) } -/// Generate TypeScript interfaces for common index patterns -fn generate_pattern_interfaces(output: &mut String, patterns: &[IndexPattern]) { - writeln!(output, "// Index pattern interfaces").unwrap(); +/// Generate JSDoc typedefs for common index patterns +fn generate_pattern_typedefs(output: &mut String, patterns: &[IndexPattern]) { + writeln!(output, "// Index pattern typedefs").unwrap(); writeln!(output, "// Reusable patterns for metrics with the same index groupings\n").unwrap(); for pattern in patterns { let pattern_name = pattern_to_name(pattern); - writeln!(output, "export interface {} {{", pattern_name).unwrap(); + writeln!(output, "/**").unwrap(); + writeln!(output, " * @template T").unwrap(); + writeln!(output, " * @typedef {{Object}} {}", pattern_name).unwrap(); for index in &pattern.indexes { let field_name = to_camel_case(&index.serialize_long()); - writeln!(output, " {}: MetricNode;", field_name).unwrap(); + writeln!(output, " * @property {{MetricNode}} {}", field_name).unwrap(); } - writeln!(output, "}}\n").unwrap(); + writeln!(output, " */\n").unwrap(); } } -/// Generate TypeScript type aliases for value types -fn generate_value_types(output: &mut String, metadata: &ClientMetadata) { - writeln!(output, "// Value type aliases").unwrap(); - writeln!(output, "// Maps Rust types to TypeScript types\n").unwrap(); - - // Collect unique value types - let mut value_types: Vec<&str> = metadata - .metrics - .values() - .map(|m| m.value_type.as_str()) - .collect(); - value_types.sort(); - value_types.dedup(); - - for vt in value_types { - let ts_type = rust_type_to_ts(vt); - writeln!(output, "export type {} = {};", vt, ts_type).unwrap(); - } - writeln!(output).unwrap(); -} - /// Generate the base BrkClient class with HTTP functionality fn generate_base_client(output: &mut String) { writeln!( output, - r#"// Base HTTP client -export interface BrkClientOptions {{ - baseUrl: string; - timeout?: number; -}} + r#"/** + * @typedef {{Object}} BrkClientOptions + * @property {{string}} baseUrl - The base URL for the API + * @property {{number}} [timeout] - Request timeout in ms (default: 30000) + */ -export class BrkClientBase {{ - private baseUrl: string; - private timeout: number; - - constructor(options: BrkClientOptions | string) {{ +/** + * Base HTTP client + */ +class BrkClientBase {{ + /** + * @param {{BrkClientOptions|string}} options + */ + constructor(options) {{ if (typeof options === 'string') {{ this.baseUrl = options.replace(/\/$/, ''); this.timeout = 30000; @@ -99,7 +79,12 @@ export class BrkClientBase {{ }} }} - async get(path: string): Promise {{ + /** + * @template T + * @param {{string}} path + * @returns {{Promise}} + */ + async get(path) {{ const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), this.timeout); @@ -120,23 +105,45 @@ export class BrkClientBase {{ }} }} -export class BrkError extends Error {{ - constructor(message: string, public statusCode?: number) {{ +/** + * Error class for BRK API errors + */ +class BrkError extends Error {{ + /** + * @param {{string}} message + * @param {{number}} [statusCode] + */ + constructor(message, statusCode) {{ super(message); this.name = 'BrkError'; + this.statusCode = statusCode; }} }} -// Metric node with fetch capability -export class MetricNode {{ - constructor(private client: BrkClientBase, private path: string) {{}} - - async fetch(): Promise {{ - return this.client.get(this.path); +/** + * Metric node with fetch capability + * @template T + */ +class MetricNode {{ + /** + * @param {{BrkClientBase}} client + * @param {{string}} path + */ + constructor(client, path) {{ + this._client = client; + this._path = path; }} - toString(): string {{ - return this.path; + /** + * Fetch the metric value + * @returns {{Promise}} + */ + async fetch() {{ + return this._client.get(this._path); + }} + + toString() {{ + return this._path; }} }} @@ -145,45 +152,47 @@ export class MetricNode {{ .unwrap(); } -/// Generate TypeScript types for the catalog tree -fn generate_tree_types(output: &mut String, catalog: &TreeNode) { - writeln!(output, "// Catalog tree types\n").unwrap(); - generate_node_type(output, "CatalogTree", catalog, ""); +/// Generate JSDoc typedefs for the catalog tree +fn generate_tree_typedefs(output: &mut String, catalog: &TreeNode) { + writeln!(output, "// Catalog tree typedefs\n").unwrap(); + generate_node_typedef(output, "CatalogTree", catalog, ""); } -/// Recursively generate type for a tree node -fn generate_node_type(output: &mut String, name: &str, node: &TreeNode, path: &str) { +/// Recursively generate typedef for a tree node +fn generate_node_typedef(output: &mut String, name: &str, node: &TreeNode, path: &str) { match node { - TreeNode::Leaf(leaf) => { + TreeNode::Leaf(_leaf) => { // Leaf nodes are MetricNode - // No separate interface needed, handled inline + // No separate typedef needed, handled inline } TreeNode::Branch(children) => { - writeln!(output, "export interface {} {{", name).unwrap(); + writeln!(output, "/**").unwrap(); + writeln!(output, " * @typedef {{Object}} {}", name).unwrap(); for (child_name, child_node) in children { let field_name = to_camel_case(child_name); - let child_path = if path.is_empty() { - format!("/{}", child_name) - } else { - format!("{}/{}", path, child_name) - }; match child_node { TreeNode::Leaf(leaf) => { - let value_type = &leaf.value_type; - writeln!(output, " {}: MetricNode<{}>;", field_name, value_type).unwrap(); + let js_type = schema_to_jsdoc(&leaf.schema); + writeln!( + output, + " * @property {{MetricNode<{}>}} {}", + js_type, field_name + ) + .unwrap(); } TreeNode::Branch(_) => { let child_type_name = format!("{}_{}", name, to_pascal_case(child_name)); - writeln!(output, " {}: {};", field_name, child_type_name).unwrap(); + writeln!(output, " * @property {{{}}} {}", child_type_name, field_name) + .unwrap(); } } } - writeln!(output, "}}\n").unwrap(); + writeln!(output, " */\n").unwrap(); - // Generate child types + // Generate child typedefs for (child_name, child_node) in children { if let TreeNode::Branch(_) = child_node { let child_type_name = format!("{}_{}", name, to_pascal_case(child_name)); @@ -192,7 +201,7 @@ fn generate_node_type(output: &mut String, name: &str, node: &TreeNode, path: &s } else { format!("{}/{}", path, child_name) }; - generate_node_type(output, &child_type_name, child_node, &child_path); + generate_node_typedef(output, &child_type_name, child_node, &child_path); } } } @@ -201,21 +210,34 @@ fn generate_node_type(output: &mut String, name: &str, node: &TreeNode, path: &s /// Generate the main client class with initialized tree fn generate_main_client(output: &mut String, catalog: &TreeNode) { - writeln!(output, "// Main client class with catalog tree").unwrap(); - writeln!(output, "export class BrkClient extends BrkClientBase {{").unwrap(); - writeln!(output, " readonly tree: CatalogTree;\n").unwrap(); - writeln!(output, " constructor(options: BrkClientOptions | string) {{").unwrap(); + writeln!(output, "/**").unwrap(); + writeln!(output, " * Main BRK client with catalog tree").unwrap(); + writeln!(output, " * @extends BrkClientBase").unwrap(); + writeln!(output, " */").unwrap(); + writeln!(output, "class BrkClient extends BrkClientBase {{").unwrap(); + writeln!(output, " /**").unwrap(); + writeln!(output, " * @param {{BrkClientOptions|string}} options").unwrap(); + writeln!(output, " */").unwrap(); + writeln!(output, " constructor(options) {{").unwrap(); writeln!(output, " super(options);").unwrap(); + writeln!(output, " /** @type {{CatalogTree}} */").unwrap(); writeln!(output, " this.tree = this._buildTree();").unwrap(); writeln!(output, " }}\n").unwrap(); // Generate _buildTree method - writeln!(output, " private _buildTree(): CatalogTree {{").unwrap(); + writeln!(output, " /**").unwrap(); + writeln!(output, " * @private").unwrap(); + writeln!(output, " * @returns {{CatalogTree}}").unwrap(); + writeln!(output, " */").unwrap(); + writeln!(output, " _buildTree() {{").unwrap(); writeln!(output, " return {{").unwrap(); generate_tree_initializer(output, catalog, "", 3); writeln!(output, " }};").unwrap(); writeln!(output, " }}").unwrap(); - writeln!(output, "}}").unwrap(); + writeln!(output, "}}\n").unwrap(); + + // Export for ES modules + writeln!(output, "export {{ BrkClient, BrkClientBase, BrkError, MetricNode }};").unwrap(); } /// Generate the tree initializer code @@ -234,7 +256,7 @@ fn generate_tree_initializer(output: &mut String, node: &TreeNode, path: &str, i let comma = if i < children.len() - 1 { "," } else { "" }; match child_node { - TreeNode::Leaf(leaf) => { + TreeNode::Leaf(_) => { writeln!( output, "{}{}: new MetricNode(this, '{}'){}", @@ -252,7 +274,7 @@ fn generate_tree_initializer(output: &mut String, node: &TreeNode, path: &str, i } } -/// Convert pattern to a TypeScript interface name +/// Convert pattern to a JSDoc typedef name fn pattern_to_name(pattern: &IndexPattern) -> String { let index_names: Vec = pattern .indexes @@ -262,33 +284,6 @@ fn pattern_to_name(pattern: &IndexPattern) -> String { format!("Pattern_{}", index_names.join("_")) } -/// Convert Rust type name to TypeScript type -fn rust_type_to_ts(rust_type: &str) -> &'static str { - match rust_type { - // Numeric types - "f32" | "f64" | "StoredF32" | "StoredF64" => "number", - "u8" | "u16" | "u32" | "u64" | "i8" | "i16" | "i32" | "i64" => "number", - "usize" | "isize" => "number", - - // Boolean - "bool" => "boolean", - - // String types - "String" | "str" => "string", - - // Bitcoin types (typically numeric or string representations) - "Sats" | "SatsPerVbyte" | "WU" | "VBytes" => "number", - "Height" | "Timestamp" => "number", - "Blockhash" | "Txid" => "string", - - // Arrays/Vecs become arrays - _ if rust_type.starts_with("Vec<") => "unknown[]", - - // Default to unknown for unmapped types - _ => "unknown", - } -} - /// Convert string to PascalCase fn to_pascal_case(s: &str) -> String { s.split('_') diff --git a/crates/brk_computer/src/stateful/states/supply.rs b/crates/brk_computer/src/stateful/states/supply.rs index c82c41eee..31322b679 100644 --- a/crates/brk_computer/src/stateful/states/supply.rs +++ b/crates/brk_computer/src/stateful/states/supply.rs @@ -5,9 +5,12 @@ use schemars::JsonSchema; use serde::Serialize; use vecdb::{Bytes, Formattable}; +/// Current supply state tracking UTXO count and total value #[derive(Debug, Default, Clone, Serialize, JsonSchema)] pub struct SupplyState { + /// Number of unspent transaction outputs pub utxo_count: u64, + /// Total value in satoshis pub value: Sats, } diff --git a/crates/brk_types/src/addressstats.rs b/crates/brk_types/src/addressstats.rs index 5a000ee9b..9308c7bfd 100644 --- a/crates/brk_types/src/addressstats.rs +++ b/crates/brk_types/src/addressstats.rs @@ -2,8 +2,8 @@ use crate::{Address, AddressChainStats, AddressMempoolStats}; use schemars::JsonSchema; use serde::Serialize; -#[derive(Debug, Serialize, JsonSchema)] /// Address information compatible with mempool.space API format +#[derive(Debug, Serialize, JsonSchema)] pub struct AddressStats { /// Bitcoin address string #[schemars( diff --git a/crates/brk_types/src/anyaddressindex.rs b/crates/brk_types/src/anyaddressindex.rs index 7aac9a463..3b2bae74c 100644 --- a/crates/brk_types/src/anyaddressindex.rs +++ b/crates/brk_types/src/anyaddressindex.rs @@ -6,6 +6,7 @@ use crate::{EmptyAddressIndex, LoadedAddressIndex, TypeIndex}; const MIN_EMPTY_INDEX: u32 = u32::MAX - 4_000_000_000; +/// Unified index for any address type (loaded or empty) #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Bytes, JsonSchema)] pub struct AnyAddressIndex(TypeIndex); diff --git a/crates/brk_types/src/bitcoin.rs b/crates/brk_types/src/bitcoin.rs index 8f3df9713..f7a58aee2 100644 --- a/crates/brk_types/src/bitcoin.rs +++ b/crates/brk_types/src/bitcoin.rs @@ -9,6 +9,7 @@ use vecdb::{CheckedSub, Formattable, Pco}; use super::{Sats, StoredF64}; +/// Bitcoin amount as floating point (1 BTC = 100,000,000 satoshis) #[derive(Debug, Default, Clone, Copy, Serialize, Pco, JsonSchema)] pub struct Bitcoin(f64); diff --git a/crates/brk_types/src/blkposition.rs b/crates/brk_types/src/blkposition.rs index f04da9345..4fa08ab02 100644 --- a/crates/brk_types/src/blkposition.rs +++ b/crates/brk_types/src/blkposition.rs @@ -4,6 +4,7 @@ use schemars::JsonSchema; use serde::Serialize; use vecdb::{Formattable, Pco}; +/// Position within a .blk file, encoding file index and byte offset #[derive(Debug, Clone, Copy, Serialize, Pco, JsonSchema)] pub struct BlkPosition(u64); diff --git a/crates/brk_types/src/date.rs b/crates/brk_types/src/date.rs index f185609be..872959fe0 100644 --- a/crates/brk_types/src/date.rs +++ b/crates/brk_types/src/date.rs @@ -7,6 +7,7 @@ use crate::ONE_DAY_IN_SEC_F64; use super::{DateIndex, Timestamp}; +/// Date in YYYYMMDD format stored as u32 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Pco, JsonSchema)] pub struct Date(u32); diff --git a/crates/brk_types/src/dollars.rs b/crates/brk_types/src/dollars.rs index 04ce0836c..18e75a959 100644 --- a/crates/brk_types/src/dollars.rs +++ b/crates/brk_types/src/dollars.rs @@ -14,6 +14,7 @@ use crate::{Low, Open}; use super::{Bitcoin, Cents, Close, High, Sats, StoredF32, StoredF64}; +/// US Dollar amount as floating point #[derive(Debug, Default, Clone, Copy, Deref, Serialize, Deserialize, Pco, JsonSchema)] pub struct Dollars(f64); diff --git a/crates/brk_types/src/format.rs b/crates/brk_types/src/format.rs index 482ec45c7..58bb5ce70 100644 --- a/crates/brk_types/src/format.rs +++ b/crates/brk_types/src/format.rs @@ -1,6 +1,7 @@ use schemars::JsonSchema; use serde::Deserialize; +/// Output format for API responses #[allow(clippy::upper_case_acronyms)] #[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Deserialize, JsonSchema)] #[serde(rename_all = "lowercase")] diff --git a/crates/brk_types/src/health.rs b/crates/brk_types/src/health.rs index 3306601da..ed699d2b4 100644 --- a/crates/brk_types/src/health.rs +++ b/crates/brk_types/src/health.rs @@ -1,8 +1,8 @@ use schemars::JsonSchema; use serde::Serialize; -#[derive(Debug, Serialize, JsonSchema)] /// Server health status +#[derive(Debug, Serialize, JsonSchema)] pub struct Health { pub status: &'static str, pub service: &'static str, diff --git a/crates/brk_types/src/indexinfo.rs b/crates/brk_types/src/indexinfo.rs index 4c202f799..65db2b586 100644 --- a/crates/brk_types/src/indexinfo.rs +++ b/crates/brk_types/src/indexinfo.rs @@ -3,8 +3,8 @@ use serde::Serialize; use super::Index; -#[derive(Clone, Copy, Serialize, JsonSchema)] /// Information about an available index and its query aliases +#[derive(Clone, Copy, Serialize, JsonSchema)] pub struct IndexInfo { /// The canonical index name pub index: Index, diff --git a/crates/brk_types/src/metriccount.rs b/crates/brk_types/src/metriccount.rs index c55e7081e..0dab7d735 100644 --- a/crates/brk_types/src/metriccount.rs +++ b/crates/brk_types/src/metriccount.rs @@ -1,13 +1,13 @@ use schemars::JsonSchema; use serde::Serialize; -#[derive(Debug, Serialize, JsonSchema)] /// Metric count statistics - distinct metrics and total metric-index combinations +#[derive(Debug, Serialize, JsonSchema)] pub struct MetricCount { - #[schemars(example = 3141)] /// Number of unique metrics available (e.g., realized_price, market_cap) + #[schemars(example = 3141)] pub distinct_metrics: usize, - #[schemars(example = 21000)] /// Total number of metric-index combinations across all timeframes + #[schemars(example = 21000)] pub total_endpoints: usize, } diff --git a/crates/brk_types/src/ohlc.rs b/crates/brk_types/src/ohlc.rs index d90fbdde5..84b1455b4 100644 --- a/crates/brk_types/src/ohlc.rs +++ b/crates/brk_types/src/ohlc.rs @@ -13,6 +13,7 @@ use crate::StoredF64; use super::{Cents, Dollars, Sats}; +/// OHLC (Open, High, Low, Close) data in cents #[derive(Debug, Default, Clone, JsonSchema)] #[repr(C)] pub struct OHLCCents { @@ -99,6 +100,7 @@ impl Bytes for OHLCCents { } } +/// OHLC (Open, High, Low, Close) data in dollars #[derive(Debug, Default, Clone, Copy, JsonSchema)] #[repr(C)] pub struct OHLCDollars { @@ -211,6 +213,7 @@ impl Bytes for OHLCDollars { } } +/// OHLC (Open, High, Low, Close) data in satoshis #[derive(Debug, Default, Clone, Copy, JsonSchema)] #[repr(C)] pub struct OHLCSats { @@ -304,6 +307,7 @@ impl Bytes for OHLCSats { } } +/// Opening price value for a time period #[derive( Debug, Default, @@ -433,6 +437,7 @@ where } } +/// Highest price value for a time period #[derive( Debug, Default, @@ -562,6 +567,7 @@ where } } +/// Lowest price value for a time period #[derive( Debug, Default, @@ -691,6 +697,7 @@ where } } +/// Closing price value for a time period #[derive( Debug, Default, diff --git a/crates/brk_types/src/pagination.rs b/crates/brk_types/src/pagination.rs index f60d62be9..e4bfca15e 100644 --- a/crates/brk_types/src/pagination.rs +++ b/crates/brk_types/src/pagination.rs @@ -1,6 +1,7 @@ use schemars::JsonSchema; use serde::{Deserialize, Serialize}; +/// Pagination parameters for paginated API endpoints #[derive(Debug, Default, Serialize, Deserialize, JsonSchema)] pub struct Pagination { /// Pagination index diff --git a/crates/brk_types/src/rewardstats.rs b/crates/brk_types/src/rewardstats.rs index 9a614f6ce..41dc4b711 100644 --- a/crates/brk_types/src/rewardstats.rs +++ b/crates/brk_types/src/rewardstats.rs @@ -3,10 +3,13 @@ use serde::Serialize; use super::{Height, Sats}; +/// Block reward statistics over a range of blocks #[derive(Debug, Clone, Serialize, JsonSchema)] #[serde(rename_all = "camelCase")] pub struct RewardStats { + /// First block in the range pub start_block: Height, + /// Last block in the range pub end_block: Height, #[serde(serialize_with = "sats_as_string")] pub total_reward: Sats, diff --git a/crates/brk_types/src/stored_bool.rs b/crates/brk_types/src/stored_bool.rs index 503975748..a28102c7a 100644 --- a/crates/brk_types/src/stored_bool.rs +++ b/crates/brk_types/src/stored_bool.rs @@ -3,6 +3,7 @@ use schemars::JsonSchema; use serde::Serialize; use vecdb::{Formattable, Pco, PrintableIndex}; +/// Fixed-size boolean value optimized for on-disk storage (stored as u16) #[derive(Debug, Deref, Clone, Default, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Pco, JsonSchema)] pub struct StoredBool(u16); diff --git a/crates/brk_types/src/stored_f32.rs b/crates/brk_types/src/stored_f32.rs index 9300b8449..141aff00b 100644 --- a/crates/brk_types/src/stored_f32.rs +++ b/crates/brk_types/src/stored_f32.rs @@ -15,6 +15,7 @@ use crate::{Close, StoredU32}; use super::{Dollars, StoredF64}; +/// Stored 32-bit floating point value #[derive(Debug, Deref, Default, Clone, Copy, Serialize, Pco, JsonSchema)] pub struct StoredF32(f32); diff --git a/crates/brk_types/src/stored_f64.rs b/crates/brk_types/src/stored_f64.rs index d485f2208..088f9bcf5 100644 --- a/crates/brk_types/src/stored_f64.rs +++ b/crates/brk_types/src/stored_f64.rs @@ -12,6 +12,7 @@ use vecdb::{CheckedSub, Formattable, Pco, PrintableIndex}; use crate::{Bitcoin, Dollars}; +/// Fixed-size 64-bit floating point value optimized for on-disk storage #[derive(Debug, Deref, Default, Clone, Copy, Serialize, Pco, JsonSchema)] pub struct StoredF64(f64); diff --git a/crates/brk_types/src/stored_u32.rs b/crates/brk_types/src/stored_u32.rs index 41ddfee4f..48a6f3b7d 100644 --- a/crates/brk_types/src/stored_u32.rs +++ b/crates/brk_types/src/stored_u32.rs @@ -11,6 +11,7 @@ use super::{ P2WSHAddressIndex, UnknownOutputIndex, }; +/// Fixed-size 32-bit unsigned integer optimized for on-disk storage #[derive(Debug, Deref, Clone, Default, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Pco, JsonSchema)] pub struct StoredU32(u32); diff --git a/crates/brk_types/src/stored_u64.rs b/crates/brk_types/src/stored_u64.rs index 7b0021eb1..1fa6f7b43 100644 --- a/crates/brk_types/src/stored_u64.rs +++ b/crates/brk_types/src/stored_u64.rs @@ -12,6 +12,7 @@ use super::{ UnknownOutputIndex, YearIndex, }; +/// Fixed-size 64-bit unsigned integer optimized for on-disk storage #[derive(Debug, Default, Deref, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Pco, JsonSchema)] pub struct StoredU64(u64); diff --git a/crates/brk_types/src/treenode.rs b/crates/brk_types/src/treenode.rs index f57d913ca..380ffc410 100644 --- a/crates/brk_types/src/treenode.rs +++ b/crates/brk_types/src/treenode.rs @@ -35,7 +35,7 @@ impl MetricLeaf { } /// MetricLeaf with JSON Schema for client generation -#[derive(Debug, Clone, Serialize)] +#[derive(Debug, Clone, Serialize, JsonSchema)] pub struct MetricLeafWithSchema { /// The core metric metadata #[serde(flatten)] @@ -84,9 +84,9 @@ impl PartialEq for MetricLeafWithSchema { impl Eq for MetricLeafWithSchema {} -#[derive(Debug, Clone, Serialize, PartialEq, Eq)] -#[serde(untagged)] /// Hierarchical tree node for organizing metrics into categories +#[derive(Debug, Clone, Serialize, PartialEq, Eq, JsonSchema)] +#[serde(untagged)] pub enum TreeNode { /// Branch node containing subcategories Branch(BTreeMap), diff --git a/crates/brk_types/src/tx.rs b/crates/brk_types/src/tx.rs index e5a803295..e06fd82a0 100644 --- a/crates/brk_types/src/tx.rs +++ b/crates/brk_types/src/tx.rs @@ -3,8 +3,8 @@ use schemars::JsonSchema; use serde::Serialize; use vecdb::CheckedSub; -#[derive(Debug, Clone, Serialize, JsonSchema)] /// Transaction information compatible with mempool.space API format +#[derive(Debug, Clone, Serialize, JsonSchema)] pub struct Transaction { #[schemars(example = TxIndex::new(0))] pub index: Option, diff --git a/crates/brk_types/src/txidvout.rs b/crates/brk_types/src/txidvout.rs index c8c343c13..5d50758de 100644 --- a/crates/brk_types/src/txidvout.rs +++ b/crates/brk_types/src/txidvout.rs @@ -3,8 +3,11 @@ use serde::Deserialize; use crate::{Txid, Vout}; +/// Transaction output reference (txid + output index) #[derive(Deserialize, JsonSchema)] pub struct TxidVout { + /// Transaction ID pub txid: Txid, + /// Output index pub vout: Vout, } diff --git a/crates/brk_types/src/txin.rs b/crates/brk_types/src/txin.rs index 955c0c1bd..8cd017737 100644 --- a/crates/brk_types/src/txin.rs +++ b/crates/brk_types/src/txin.rs @@ -3,8 +3,8 @@ use bitcoin::{Script, ScriptBuf}; use schemars::JsonSchema; use serde::{Serialize, Serializer, ser::SerializeStruct}; -#[derive(Debug, Clone, JsonSchema)] /// Transaction input +#[derive(Debug, Clone, JsonSchema)] pub struct TxIn { /// Transaction ID of the output being spent #[schemars(example = "0000000000000000000000000000000000000000000000000000000000000000")] diff --git a/crates/brk_types/src/txout.rs b/crates/brk_types/src/txout.rs index 6a2fc9ae3..1bb6920a8 100644 --- a/crates/brk_types/src/txout.rs +++ b/crates/brk_types/src/txout.rs @@ -3,8 +3,8 @@ use bitcoin::ScriptBuf; use schemars::JsonSchema; use serde::{Serialize, Serializer, ser::SerializeStruct}; -#[derive(Debug, Clone, JsonSchema)] /// Transaction output +#[derive(Debug, Clone, JsonSchema)] pub struct TxOut { /// Script pubkey (locking script) #[serde( diff --git a/crates/brk_types/src/txstatus.rs b/crates/brk_types/src/txstatus.rs index 1fc3cb2d6..cda145100 100644 --- a/crates/brk_types/src/txstatus.rs +++ b/crates/brk_types/src/txstatus.rs @@ -3,8 +3,8 @@ use serde::Serialize; use crate::{BlockHash, Height, Timestamp}; -#[derive(Debug, Clone, Serialize, JsonSchema)] /// Transaction confirmation status +#[derive(Debug, Clone, Serialize, JsonSchema)] pub struct TxStatus { /// Whether the transaction is confirmed #[schemars(example = true)] diff --git a/crates/brk_types/src/weight.rs b/crates/brk_types/src/weight.rs index 06dbdd153..d8627e8ed 100644 --- a/crates/brk_types/src/weight.rs +++ b/crates/brk_types/src/weight.rs @@ -5,6 +5,7 @@ use schemars::JsonSchema; use serde::Serialize; use vecdb::{Formattable, Pco}; +/// Transaction or block weight in weight units (WU) #[derive(Debug, Deref, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Pco, JsonSchema)] pub struct Weight(u64);