A better Rust ATProto crate
102
fork

Configure Feed

Select the types of activity you want to include in your feed.

changelog and version bump

Orual 8546eeba 0b5d4cbe

+136 -26
+110
CHANGELOG.md
··· 1 1 # Changelog 2 2 3 + ## [0.6.0] - 2025-10-18 4 + 5 + ### Added 6 + 7 + **HTTP streaming support** (`jacquard-common`, `jacquard`) 8 + - `HttpClientExt` trait for streaming HTTP requests/responses 9 + - `send_http_streaming()` for streaming response bodies 10 + - `send_http_bidirectional()` for streaming both request and response 11 + - `StreamingResponse` wrapper type with parts + `ByteStream` 12 + - `XrpcResponseStream<R>` for typed XRPC streaming responses 13 + - `ByteStream` / `ByteSink` platform-agnostic stream wrappers (uses n0-future) 14 + - `StreamError` concrete error type with kind enum (Transport, Closed, Protocol) 15 + - Native support via reqwest's `bytes_stream()` and `Body::wrap_stream()` 16 + - WASM compatibility via n0-future (no Send bounds required) 17 + 18 + 19 + **WebSocket subscription support** (`jacquard-common`) 20 + - Full XRPC WebSocket subscription infrastructure 21 + - `SubscriptionResp` trait for defining subscription message/error types 22 + - `XrpcSubscription` trait for subscription parameters 23 + - `SubscriptionStream<S>` typed wrapper with automatic message decoding 24 + - `SubscriptionClient` stateful trait + `TungsteniteSubscriptionClient` implementation 25 + - `SubscriptionExt` for stateless subscription calls 26 + - Support for both JSON and DAG-CBOR message encodings 27 + - Custom path support via `CUSTOM_PATH` constant for non-XRPC endpoints 28 + - WebSocket integration into `Agent` struct (agents can now subscribe) 29 + - `into_stream()`, `into_raw_data_stream()`, `into_data_stream()` methods for different deserialization modes 30 + 31 + **Framed DAG-CBOR message decoding** (`jacquard-common`, `jacquard-api`, `jacquard-lexicon`) 32 + - Two-stage deserialization for AT Protocol event streams (header + body) 33 + - `EventHeader` struct and `parse_event_header()` function 34 + - `decode_framed()` methods generated for all DAG-CBOR subscription message enums 35 + - `decode_message()` override in `SubscriptionResp` trait for custom decoding 36 + - `UnknownEventType` variant in `DecodeError` for unknown discriminators 37 + - Fixes "TrailingData" errors when consuming subscribeRepos and subscribeLabels 38 + 39 + **Jetstream support** (`jacquard-common`) 40 + - Full typed support for Jetstream JSON firehose 41 + - `JetstreamMessage` enum with `Commit`, `Identity`, `Account` variants 42 + - `JetstreamCommit`, `JetstreamIdentity`, `JetstreamAccount` detail structs 43 + - `CommitOperation` enum for create/update/delete operations 44 + - `JetstreamParams` with filtering options (collections, DIDs, cursor, compression) 45 + - Uses proper AT Protocol types (`Did`, `Handle`, `Datetime`, `Data`) 46 + 47 + **Zstd compression** (`jacquard-common`) 48 + - Optional `zstd` feature for Jetstream message decompression 49 + - Automatic detection and decompression of zstd-compressed binary frames 50 + - Includes official Bluesky Jetstream zstd dictionary 51 + - Transparent fallback to uncompressed when zstd unavailable 52 + - Works across all JSON stream methods (`into_stream()`, `into_raw_data_stream()`, `into_data_stream()`) 53 + 54 + **Typed AT URI wrapper** (`jacquard-common`, `jacquard-api`, `jacquard-lexicon`) 55 + - `AtUri<'a>` newtype wrapper for `at://` URIs with proper validation 56 + - Generated `fetch_uri()` method on all record types for fetching by AT URI 57 + - `AtUri::from_parts()` constructor for building URIs from components 58 + - Proper Display and FromStr implementations 59 + 60 + **Memory-based credential session** (`jacquard`) 61 + - `MemoryCredentialSession` for in-memory session storage 62 + - Useful for short-lived applications or testing 63 + - No file I/O required 64 + 65 + **Collection record fetching improvements** (`jacquard-api`, `jacquard-lexicon`) 66 + - Generated `fetch_record()` convenience method on all record types 67 + - Fetches owned record without turbofish syntax: `Post::fetch_record(agent, uri).await` 68 + - Simplifies common pattern of fetching + converting to owned 69 + 70 + **Axum improvements** (`jacquard-axum`) 71 + - `XrpcError` now implements `IntoResponse` for better error handling 72 + - Proper typed error responses without manual conversion 73 + - Better integration with Axum's response system 74 + 75 + **Examples** 76 + - `subscribe_repos.rs`: Subscribe to PDS firehose with typed DAG-CBOR messages 77 + - `subscribe_jetstream.rs`: Subscribe to Jetstream with typed JSON messages and optional compression 78 + - `stream_get_blob.rs`: Download blobs using HTTP streaming 79 + - `app_password_example.rs`: App password authentication example 80 + 81 + **CID deserialization improvements** (`jacquard-common`) 82 + - Fixed `Cid` type to properly deserialize CBOR tag 42 via `IpldCid::deserialize` 83 + - Separate handling for JSON (string) vs CBOR (tag 42) formats 84 + - `CidLink` correctly delegates to `Cid` for both formats 85 + 86 + ### Changed 87 + 88 + **Default features** (`jacquard-common`) 89 + - Added `zstd` to default features for better Jetstream experience 90 + - Jetstream compression enabled by default when using the full feature set 91 + 92 + **Generated code** (`jacquard-lexicon`, `jacquard-api`) 93 + - All DAG-CBOR subscriptions (subscribeRepos, subscribeLabels) now use framed decoding 94 + - Generated `decode_framed()` implementations match on event type discriminator 95 + - Override `decode_message()` in trait impls to use framed decoding 96 + - All record types now have `fetch_uri()` and `fetch_record()` methods generated 97 + 98 + **Dependencies** (`jacquard-axum`) 99 + - Disabled default features for `jacquard` dependency to reduce bloat 100 + 101 + ### Fixed 102 + 103 + **Blob upload** (`jacquard`) 104 + - Fixed `upload_blob()` authentication issues 105 + - Properly authenticates while allowing custom Content-Type headers 106 + 107 + **XRPC client** (`jacquard-common`, `jacquard-oauth`, `jacquard`) 108 + - Added `send_with_options()` method for per-request option overrides 109 + - Stateful clients can now override options while preserving internal auth 110 + 111 + 112 + --- 3 113 4 114 ## `jacquard-api` [0.5.5], `jacquard-lexicon` [0.5.4] - 2025-10-16 5 115
+1 -1
Cargo.toml
··· 5 5 6 6 [workspace.package] 7 7 edition = "2024" 8 - version = "0.5.4" 8 + version = "0.6.0" 9 9 authors = ["Orual <orual@nonbinary.computer>"] 10 10 #repository = "https://github.com/rsform/jacquard" 11 11 repository = "https://tangled.org/@nonbinary.computer/jacquard"
+3 -3
crates/jacquard-api/Cargo.toml
··· 2 2 name = "jacquard-api" 3 3 description = "Generated AT Protocol API bindings for Jacquard" 4 4 edition.workspace = true 5 - version = "0.5.5" 5 + version = "0.6.0" 6 6 authors.workspace = true 7 7 repository.workspace = true 8 8 keywords.workspace = true ··· 17 17 [dependencies] 18 18 bon.workspace = true 19 19 bytes = { workspace = true, features = ["serde"] } 20 - jacquard-common = { version = "0.5", path = "../jacquard-common" } 21 - jacquard-derive = { version = "0.5", path = "../jacquard-derive" } 20 + jacquard-common = { version = "0.6", path = "../jacquard-common" } 21 + jacquard-derive = { version = "0.6", path = "../jacquard-derive" } 22 22 miette.workspace = true 23 23 serde.workspace = true 24 24 serde_ipld_dagcbor.workspace = true
+5 -5
crates/jacquard-axum/Cargo.toml
··· 1 1 [package] 2 2 name = "jacquard-axum" 3 3 edition.workspace = true 4 - version = "0.5.2" 4 + version = "0.6.0" 5 5 authors.workspace = true 6 6 repository.workspace = true 7 7 keywords.workspace = true ··· 22 22 [dependencies] 23 23 axum = "0.8.6" 24 24 bytes.workspace = true 25 - jacquard = { version = "0.5", path = "../jacquard", default-features = false, features = ["api"] } 26 - jacquard-common = { version = "0.5", path = "../jacquard-common", features = ["reqwest-client"] } 27 - jacquard-derive = { version = "0.5.2", path = "../jacquard-derive" } 28 - jacquard-identity = { version = "0.5", path = "../jacquard-identity", optional = true } 25 + jacquard = { version = "0.6", path = "../jacquard", default-features = false, features = ["api"] } 26 + jacquard-common = { version = "0.6", path = "../jacquard-common", features = ["reqwest-client"] } 27 + jacquard-derive = { version = "0.6", path = "../jacquard-derive" } 28 + jacquard-identity = { version = "0.6", path = "../jacquard-identity", optional = true } 29 29 miette.workspace = true 30 30 multibase = { version = "0.9.1", optional = true } 31 31 serde.workspace = true
+3 -3
crates/jacquard-common/Cargo.toml
··· 2 2 name = "jacquard-common" 3 3 description = "Core AT Protocol types and utilities for Jacquard" 4 4 edition.workspace = true 5 - version = "0.5.4" 5 + version = "0.6.0" 6 6 authors.workspace = true 7 7 repository.workspace = true 8 8 keywords.workspace = true ··· 12 12 license.workspace = true 13 13 14 14 [features] 15 - default = ["service-auth", "reqwest-client", "crypto", "websocket", "zstd"] 15 + default = ["service-auth", "reqwest-client", "crypto"] 16 16 crypto = [] 17 17 crypto-ed25519 = ["crypto", "dep:ed25519-dalek"] 18 18 crypto-k256 = ["crypto", "dep:k256", "k256/ecdsa"] ··· 92 92 futures-lite = "2.6" 93 93 94 94 [package.metadata.docs.rs] 95 - features = [ "crypto-k256", "crypto-k256", "crypto-p256"] 95 + features = [ "crypto-k256", "crypto-k256", "crypto-p256", "websocket", "zstd", "service-auth", "reqwest-client", "crypto"]
+1 -1
crates/jacquard-derive/Cargo.toml
··· 20 20 syn.workspace = true 21 21 22 22 [dev-dependencies] 23 - jacquard-common = { version = "0.5", path = "../jacquard-common" } 23 + jacquard-common = { version = "0.6", path = "../jacquard-common" } 24 24 serde.workspace = true 25 25 serde_json.workspace = true
+3 -3
crates/jacquard-identity/Cargo.toml
··· 1 1 [package] 2 2 name = "jacquard-identity" 3 3 edition.workspace = true 4 - version = "0.5.3" 4 + version = "0.6.0" 5 5 authors.workspace = true 6 6 repository.workspace = true 7 7 keywords.workspace = true ··· 21 21 trait-variant.workspace = true 22 22 bon.workspace = true 23 23 bytes.workspace = true 24 - jacquard-common = { version = "0.5", path = "../jacquard-common", features = ["reqwest-client"] } 25 - jacquard-api = { version = "0.5", path = "../jacquard-api", default-features = false, features = ["minimal"] } 24 + jacquard-common = { version = "0.6", path = "../jacquard-common", features = ["reqwest-client"] } 25 + jacquard-api = { version = "0.6", path = "../jacquard-api", default-features = false, features = ["minimal"] } 26 26 percent-encoding.workspace = true 27 27 reqwest.workspace = true 28 28 url.workspace = true
+2 -2
crates/jacquard-lexicon/Cargo.toml
··· 26 26 heck.workspace = true 27 27 #itertools.workspace = true 28 28 jacquard-api = { version = "0.5", git = "https://tangled.org/@nonbinary.computer/jacquard" } 29 - jacquard-common = { version = "0.5", features = [ "reqwest-client" ], git = "https://tangled.org/@nonbinary.computer/jacquard" } 30 - jacquard-identity = { version = "0.5", git = "https://tangled.org/@nonbinary.computer/jacquard" } 29 + jacquard-common = { version = "0.6", features = [ "reqwest-client" ], git = "https://tangled.org/@nonbinary.computer/jacquard" } 30 + jacquard-identity = { version = "0.6", git = "https://tangled.org/@nonbinary.computer/jacquard" } 31 31 kdl = "6" 32 32 miette = { workspace = true, features = ["fancy"] } 33 33 prettyplease.workspace = true
+3 -3
crates/jacquard-oauth/Cargo.toml
··· 1 1 [package] 2 2 name = "jacquard-oauth" 3 - version = "0.5.4" 3 + version = "0.6.0" 4 4 edition.workspace = true 5 5 description = "AT Protocol OAuth 2.1 core types and helpers for Jacquard" 6 6 authors.workspace = true ··· 21 21 streaming = ["jacquard-common/streaming", "dep:n0-future"] 22 22 23 23 [dependencies] 24 - jacquard-common = { version = "0.5", path = "../jacquard-common", features = ["reqwest-client"] } 25 - jacquard-identity = { version = "0.5", path = "../jacquard-identity" } 24 + jacquard-common = { version = "0.6", path = "../jacquard-common", features = ["reqwest-client"] } 25 + jacquard-identity = { version = "0.6", path = "../jacquard-identity" } 26 26 serde = { workspace = true, features = ["derive"] } 27 27 serde_json = { workspace = true } 28 28 url = { workspace = true }
+5 -5
crates/jacquard/Cargo.toml
··· 122 122 123 123 124 124 [dependencies] 125 - jacquard-api = { version = "0.5", path = "../jacquard-api" } 126 - jacquard-common = { version = "0.5", path = "../jacquard-common", features = [ 125 + jacquard-api = { version = "0.6", path = "../jacquard-api" } 126 + jacquard-common = { version = "0.6", path = "../jacquard-common", features = [ 127 127 "reqwest-client", 128 128 ] } 129 - jacquard-oauth = { version = "0.5", path = "../jacquard-oauth" } 130 - jacquard-derive = { version = "0.5", path = "../jacquard-derive", optional = true } 131 - jacquard-identity = { version = "0.5", path = "../jacquard-identity" } 129 + jacquard-oauth = { version = "0.6", path = "../jacquard-oauth" } 130 + jacquard-derive = { version = "0.6", path = "../jacquard-derive", optional = true } 131 + jacquard-identity = { version = "0.6", path = "../jacquard-identity" } 132 132 133 133 bon.workspace = true 134 134 trait-variant.workspace = true