| Crates.io | hyperstack-sdk |
| lib.rs | hyperstack-sdk |
| version | 0.3.5 |
| created_at | 2026-01-09 05:44:15.839192+00 |
| updated_at | 2026-01-24 05:44:32.529889+00 |
| description | Rust SDK client for connecting to HyperStack streaming servers |
| homepage | |
| repository | https://github.com/HyperTekOrg/hyperstack.git |
| max_upload_size | |
| id | 2031627 |
| size | 151,544 |
Rust client SDK for connecting to HyperStack streaming servers.
[dependencies]
hyperstack-sdk = "0.2"
By default, the SDK uses rustls for TLS. You can switch to native TLS:
[dependencies]
hyperstack-sdk = { version = "0.1", default-features = false, features = ["native-tls"] }
use hyperstack_sdk::prelude::*;
use my_stack::{PumpfunToken, PumpfunTokenEntity};
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let hs = HyperStack::connect("wss://mainnet.hyperstack.xyz").await?;
// List all entities
let tokens = hs.list::<PumpfunTokenEntity>().await;
println!("Found {} tokens", tokens.len());
// Watch for real-time updates (lazy - no .await needed)
let mut stream = hs.watch::<PumpfunTokenEntity>();
while let Some(update) = stream.next().await {
match update {
Update::Upsert { key, data } => println!("Updated {}", key),
Update::Patch { key, data } => println!("Patched {}", key),
Update::Delete { key } => println!("Deleted {}", key),
}
}
Ok(())
}
The prelude module re-exports all commonly needed types including StreamExt, so you don't need separate imports from futures_util.
Streams are lazy - calling watch() returns immediately without subscribing. The subscription happens automatically on first poll. This enables ergonomic method chaining:
use std::collections::HashSet;
let watchlist: HashSet<String> = /* tokens to watch */;
let mut price_alerts = hs
.watch_rich::<PumpfunTokenEntity>()
.filter(move |u| watchlist.contains(u.key()))
.filter_map(|update| match update {
RichUpdate::Updated { before, after, .. } => {
let prev = before.trading.last_trade_price.flatten().unwrap_or(0.0);
let curr = after.trading.last_trade_price.flatten().unwrap_or(0.0);
if prev > 0.0 {
let pct = (curr - prev) / prev * 100.0;
if pct.abs() > 0.1 {
return Some((after.info.name.clone(), pct));
}
}
None
}
_ => None,
});
while let Some((name, pct)) = price_alerts.next().await {
println!("[PRICE] {:?} changed by {:.2}%", name, pct);
}
| Operator | Description |
|---|---|
.filter(predicate) |
Keep only updates matching the predicate |
.filter_map(f) |
Filter and transform in one step |
.map(f) |
Transform each update |
All operators are chainable and return streams that support the same operators.
The Views API provides a unified interface for accessing state, list, and derived views. This is the recommended way to access views as it provides consistent ergonomics across all view types.
use hyperstack_sdk::prelude::*;
use my_stack::{OreRound, OreRoundViews};
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let hs = HyperStack::connect("wss://mainnet.hyperstack.xyz").await?;
// Get a views accessor
let views = hs.views::<OreRoundViews>();
// Access derived view (e.g., "latest" round)
let latest = views.latest().get().await;
println!("Latest round: {:?}", latest);
// Access list view
let all_rounds = views.list().get().await;
println!("Found {} rounds", all_rounds.len());
// Access state view by key
let specific = views.state().get("round_key").await;
// Watch derived view for updates
let mut stream = views.latest().watch();
while let Some(update) = stream.next().await {
println!("Latest round updated: {:?}", update);
}
Ok(())
}
| View Type | Access Pattern | Returns |
|---|---|---|
| State | views.state().get(key) |
Option<T> |
| List | views.list().get() |
Vec<T> |
| Derived Single | views.{name}().get() |
Option<T> |
| Derived Collection | views.{name}().get() |
Vec<T> |
All view types support .watch() for streaming updates.
// Simple connection
let hs = HyperStack::connect("wss://example.com").await?;
// With configuration
let hs = HyperStack::builder()
.url("wss://example.com")
.auto_reconnect(true)
.max_reconnect_attempts(10)
.ping_interval(Duration::from_secs(30))
.initial_data_timeout(Duration::from_secs(5))
.connect()
.await?;
| Method | Returns | Description |
|---|---|---|
get::<E>(key).await |
Option<T> |
Get a single entity by key |
list::<E>().await |
Vec<T> |
Get all entities of type E |
watch::<E>() |
EntityStream<T> |
Stream all updates (lazy) |
watch_key::<E>(key) |
EntityStream<T> |
Stream updates for a specific key (lazy) |
watch_keys::<E>(&[keys]) |
EntityStream<T> |
Stream updates for multiple keys (lazy) |
watch_rich::<E>() |
RichEntityStream<T> |
Stream with before/after values (lazy) |
watch_key_rich::<E>(key) |
RichEntityStream<T> |
Rich stream for specific key (lazy) |
connection_state().await |
ConnectionState |
Get current connection state |
disconnect().await |
() |
Close the connection |
When streaming with watch(), you receive Update<T> variants:
pub enum Update<T> {
Upsert { key: String, data: T }, // Full entity update
Patch { key: String, data: T }, // Partial update (merged)
Delete { key: String }, // Entity removed
}
Helper methods: key(), data(), is_delete(), has_data(), into_data(), into_key(), map(f)
For tracking changes over time, use watch_rich():
pub enum RichUpdate<T> {
Created { key: String, data: T },
Updated { key: String, before: T, after: T, patch: Option<Value> },
Deleted { key: String, last_known: Option<T> },
}
The Updated variant includes patch - the raw JSON of changed fields, useful for checking what specifically changed:
if update.has_patch_field("trading") {
// The trading field was modified
}
Option<Option<T>> FieldsGenerated entity types often have fields typed as Option<Option<T>>. This represents the patch semantics of HyperStack updates:
| Value | Meaning |
|---|---|
None |
Field was not included in this update (no change) |
Some(None) |
Field was explicitly set to null |
Some(Some(value)) |
Field has a concrete value |
This distinction matters for partial updates (patches). When the server sends a patch, only changed fields are included. An absent field means "keep the previous value", while an explicit null means "clear this field".
Option<Option<T>>// Access a nested optional field
let price = token.trading.last_trade_price.flatten().unwrap_or(0.0);
// Check if field was explicitly set (vs absent from patch)
match &token.reserves.current_price_sol {
None => println!("Price not in this update"),
Some(None) => println!("Price explicitly cleared"),
Some(Some(price)) => println!("Price: {}", price),
}
// Compare values in before/after
if before.trading.last_trade_price != after.trading.last_trade_price {
println!("Price changed!");
}
Use the HyperStack CLI to generate a typed Rust SDK from your spec:
# Generate SDK crate
hs sdk create rust settlement-game
# With custom output directory
hs sdk create rust settlement-game --output ./crates/game-sdk
# With custom crate name
hs sdk create rust settlement-game --crate-name game-sdk
This generates a crate with:
generated/settlement-game-stack/
├── Cargo.toml
└── src/
├── lib.rs # Re-exports
├── types.rs # Data structs (with Option<Option<T>> for patchable fields)
└── entity.rs # Entity trait implementations
Add the generated crate to your Cargo.toml:
[dependencies]
hyperstack-sdk = "0.2"
settlement-game-stack = { path = "./generated/settlement-game-stack" }
The SDK automatically reconnects on connection loss with configurable backoff:
let hs = HyperStack::builder()
.url("wss://example.com")
.auto_reconnect(true)
.reconnect_intervals(vec![
Duration::from_secs(1),
Duration::from_secs(2),
Duration::from_secs(5),
Duration::from_secs(10),
])
.max_reconnect_attempts(20)
.connect()
.await?;
let state = hs.connection_state().await;
match state {
ConnectionState::Connected => println!("Connected"),
ConnectionState::Connecting => println!("Connecting..."),
ConnectionState::Reconnecting { attempt } => println!("Reconnecting (attempt {})", attempt),
ConnectionState::Disconnected => println!("Disconnected"),
ConnectionState::Error => println!("Error"),
}
| Mode | View | Description |
|---|---|---|
| State | Entity/state |
Single shared state object |
| List | Entity/list |
All entities, key-value lookups |
| Append | Entity/append |
Append-only event log |
MIT