A better Rust ATProto crate

edit pass on changelog, improving readme

Orual a7375ddd 8c229615

Changed files
+128 -65
+6 -38
CHANGELOG.md
···
## [0.5.0] - 2025-10-13
-
### Breaking Changes
-
-
**AgentSession trait** (`jacquard`)
-
- Removed `async fn` in favour of `impl Future` return types for better trait object compatibility
-
- Methods now return `impl Future` instead of being marked `async fn`
-
-
**XRPC improvements** (`jacquard-common`)
-
- Simplified response transmutation for typed record retrieval
-
- `Response::transmute()` added for zero-cost response type conversion
-
-
**jacquard-axum**
-
- Removed binary target (`main.rs`), now library-only
-
### Added
**Agent convenience methods** (`jacquard`)
···
- **Basic CRUD**: `create_record()`, `get_record()`, `put_record()`, `delete_record()`
- **Update patterns**: `update_record()` (fetch-modify-put), `update_vec()`, `update_vec_item()`
- **Blob operations**: `upload_blob()`
-
- All methods auto-fill repo from session and collection from type's `Collection::NSID`
-
- Simplified bounds on `update_record` - no HRTB issues, works with all record types
**VecUpdate trait** (`jacquard`)
- `VecUpdate` trait for fetch-modify-put patterns on array-based endpoints
-
- `PreferencesUpdate` implementation for updating user preferences
-
- Enables type-safe updates to preferences, saved feeds, and other array endpoints
-
**Typed record retrieval** (`jacquard-api`, `jacquard-common`)
- Each collection generates `{Type}Record` marker struct implementing `XrpcResp`
- `Collection::Record` associated type points to the marker
- `get_record::<R>()` returns `Response<R::Record>` with zero-copy `.parse()`
···
- `post_with_image.rs`: Uploading images and creating posts with embeds
- `update_preferences.rs`: Using VecUpdate for preferences
- `create_whitewind_post.rs`, `read_whitewind_post.rs`: Third-party lexicons
-
- `read_tangled_repo.rs`: Reading git repo metadata from tangled.sh
- `resolve_did.rs`: Identity resolution examples
- `public_atproto_feed.rs`: Unauthenticated feed access
- `axum_server.rs`: Server-side XRPC handler
-
### Changed
-
-
**Code organization** (`jacquard-lexicon`)
-
- Refactored monolithic `codegen.rs` into focused modules:
-
- `codegen/structs.rs`: Record and object generation
-
- `codegen/xrpc.rs`: XRPC request/response generation
-
- `codegen/types.rs`: Type alias and union generation
-
- `codegen/names.rs`: Identifier sanitization and naming
-
- `codegen/lifetime.rs`: Lifetime propagation logic
-
- `codegen/output.rs`: Module and feature generation
-
- `codegen/utils.rs`: Shared utilities
-
- Improved code navigation and maintainability
**Documentation** (`jacquard`)
-
- Added comprehensive trait-level docs for `AgentSessionExt`
-
- Updated examples to use new convenience methods
-
-
### Fixed
-
-
- `update_record` now works with all record types without lifetime issues
-
- Proper `IdentityResolver` bounds on `AgentSessionExt`
## [0.4.1] - 2025-10-13
···
## [0.5.0] - 2025-10-13
### Added
**Agent convenience methods** (`jacquard`)
···
- **Basic CRUD**: `create_record()`, `get_record()`, `put_record()`, `delete_record()`
- **Update patterns**: `update_record()` (fetch-modify-put), `update_vec()`, `update_vec_item()`
- **Blob operations**: `upload_blob()`
+
- All methods auto-fill repo from session or URI parameter as relevant, and collection from type's `Collection::NSID`
**VecUpdate trait** (`jacquard`)
- `VecUpdate` trait for fetch-modify-put patterns on array-based endpoints
+
- `PreferencesUpdate` implementation for updating Bluesky user preferences
+
- Enables simpler updates to preferences and other 'array of union' types
+
**Typed record retrieval** (`jacquard-api`, `jacquard-common`, `jacquard-lexicon`)
- Each collection generates `{Type}Record` marker struct implementing `XrpcResp`
- `Collection::Record` associated type points to the marker
- `get_record::<R>()` returns `Response<R::Record>` with zero-copy `.parse()`
···
- `post_with_image.rs`: Uploading images and creating posts with embeds
- `update_preferences.rs`: Using VecUpdate for preferences
- `create_whitewind_post.rs`, `read_whitewind_post.rs`: Third-party lexicons
+
- `read_tangled_repo.rs`: Reading git repo metadata from tangled.org
- `resolve_did.rs`: Identity resolution examples
- `public_atproto_feed.rs`: Unauthenticated feed access
- `axum_server.rs`: Server-side XRPC handler
**Documentation** (`jacquard`)
+
- A whole host of examples added, as well as a lengthy explainer of the trait patterns.
## [0.4.1] - 2025-10-13
+102 -15
Cargo.lock
···
"bytes",
"clap",
"http",
-
"jacquard-api",
-
"jacquard-common",
-
"jacquard-derive",
-
"jacquard-identity",
"jacquard-oauth",
"jose-jwk",
"miette",
···
dependencies = [
"bon",
"bytes",
-
"jacquard-common",
-
"jacquard-derive",
"miette",
"serde",
"thiserror 2.0.17",
···
"axum-test",
"bytes",
"jacquard",
-
"jacquard-common",
"miette",
"serde",
"serde_html_form",
···
]
[[package]]
name = "jacquard-derive"
version = "0.5.0"
dependencies = [
"heck 0.5.0",
"itertools",
-
"jacquard-common",
"prettyplease",
"proc-macro2",
"quote",
···
"bytes",
"hickory-resolver",
"http",
-
"jacquard-api",
-
"jacquard-common",
"miette",
"percent-encoding",
"reqwest",
···
"glob",
"heck 0.5.0",
"itertools",
-
"jacquard-api",
-
"jacquard-common",
-
"jacquard-identity",
"kdl",
"miette",
"prettyplease",
···
"dashmap",
"elliptic-curve",
"http",
-
"jacquard-common",
-
"jacquard-identity",
"jose-jwa",
"jose-jwk",
"miette",
···
"bytes",
"clap",
"http",
+
"jacquard-api 0.4.1",
+
"jacquard-common 0.5.0",
+
"jacquard-derive 0.5.0",
+
"jacquard-identity 0.4.1",
"jacquard-oauth",
"jose-jwk",
"miette",
···
dependencies = [
"bon",
"bytes",
+
"jacquard-common 0.5.0",
+
"jacquard-derive 0.5.0",
+
"miette",
+
"serde",
+
"thiserror 2.0.17",
+
]
+
+
[[package]]
+
name = "jacquard-api"
+
version = "0.4.1"
+
source = "git+https://tangled.org/@nonbinary.computer/jacquard#8c229615c802488f3310f1cb35e7b79683289893"
+
dependencies = [
+
"bon",
+
"bytes",
+
"jacquard-common 0.5.0 (git+https://tangled.org/@nonbinary.computer/jacquard)",
+
"jacquard-derive 0.5.0 (git+https://tangled.org/@nonbinary.computer/jacquard)",
"miette",
"serde",
"thiserror 2.0.17",
···
"axum-test",
"bytes",
"jacquard",
+
"jacquard-common 0.5.0",
"miette",
"serde",
"serde_html_form",
···
]
[[package]]
+
name = "jacquard-common"
+
version = "0.5.0"
+
source = "git+https://tangled.org/@nonbinary.computer/jacquard#8c229615c802488f3310f1cb35e7b79683289893"
+
dependencies = [
+
"async-trait",
+
"base64 0.22.1",
+
"bon",
+
"bytes",
+
"chrono",
+
"cid",
+
"http",
+
"ipld-core",
+
"langtag",
+
"miette",
+
"multibase",
+
"multihash",
+
"ouroboros",
+
"rand 0.9.2",
+
"regex",
+
"reqwest",
+
"serde",
+
"serde_html_form",
+
"serde_ipld_dagcbor",
+
"serde_json",
+
"serde_with",
+
"smol_str",
+
"thiserror 2.0.17",
+
"tokio",
+
"trait-variant",
+
"url",
+
]
+
+
[[package]]
name = "jacquard-derive"
version = "0.5.0"
dependencies = [
"heck 0.5.0",
"itertools",
+
"jacquard-common 0.5.0",
+
"prettyplease",
+
"proc-macro2",
+
"quote",
+
"serde",
+
"serde_json",
+
"serde_repr",
+
"serde_with",
+
"syn 2.0.106",
+
]
+
+
[[package]]
+
name = "jacquard-derive"
+
version = "0.5.0"
+
source = "git+https://tangled.org/@nonbinary.computer/jacquard#8c229615c802488f3310f1cb35e7b79683289893"
+
dependencies = [
+
"heck 0.5.0",
+
"itertools",
"prettyplease",
"proc-macro2",
"quote",
···
"bytes",
"hickory-resolver",
"http",
+
"jacquard-api 0.4.1",
+
"jacquard-common 0.5.0",
+
"miette",
+
"percent-encoding",
+
"reqwest",
+
"serde",
+
"serde_html_form",
+
"serde_json",
+
"thiserror 2.0.17",
+
"tokio",
+
"url",
+
"urlencoding",
+
]
+
+
[[package]]
+
name = "jacquard-identity"
+
version = "0.4.1"
+
source = "git+https://tangled.org/@nonbinary.computer/jacquard#8c229615c802488f3310f1cb35e7b79683289893"
+
dependencies = [
+
"async-trait",
+
"bon",
+
"bytes",
+
"http",
+
"jacquard-api 0.4.1 (git+https://tangled.org/@nonbinary.computer/jacquard)",
+
"jacquard-common 0.5.0 (git+https://tangled.org/@nonbinary.computer/jacquard)",
"miette",
"percent-encoding",
"reqwest",
···
"glob",
"heck 0.5.0",
"itertools",
+
"jacquard-api 0.4.1 (git+https://tangled.org/@nonbinary.computer/jacquard)",
+
"jacquard-common 0.5.0 (git+https://tangled.org/@nonbinary.computer/jacquard)",
+
"jacquard-identity 0.4.1 (git+https://tangled.org/@nonbinary.computer/jacquard)",
"kdl",
"miette",
"prettyplease",
···
"dashmap",
"elliptic-curve",
"http",
+
"jacquard-common 0.5.0",
+
"jacquard-identity 0.4.1",
"jose-jwa",
"jose-jwk",
"miette",
+20 -12
README.md
···
# Jacquard
-
A suite of Rust crates for the AT Protocol. [Docs](https://docs.rs/jacquard/latest/jacquard/)
## Goals and Features
···
- Batteries-included, but easily replaceable batteries.
- Easy to extend with custom lexicons using code generation or handwritten api types
- Straightforward OAuth
-
- stateless options (or options where you handle the state) for rolling your own
-
- all the building blocks of the convenient abstractions are available
-
- lexicon Value type for working with unknown atproto data (dag-cbor or json)
-
- order of magnitude less boilerplate than some existing crates
-
- use as much or as little from the crates as you need
## Example
···
## Component crates
-
Jacquard is broken up into several crates for modularity. The correct one to use is generally `jacquard` itself, as it re-exports the others.
- `jacquard`: Main crate [![Crates.io](https://img.shields.io/crates/v/jacquard.svg)](https://crates.io/crates/jacquard) [![Documentation](https://docs.rs/jacquard/badge.svg)](https://docs.rs/jacquard)
- `jacquard-common`: Foundation crate [![Crates.io](https://img.shields.io/crates/v/jacquard-common.svg)](https://crates.io/crates/jacquard-common) [![Documentation](https://docs.rs/jacquard-common/badge.svg)](https://docs.rs/jacquard-common)
-
- `jacquard-api`: Autogenerated API bindings [![Crates.io](https://img.shields.io/crates/v/jacquard-api.svg)](https://crates.io/crates/jacquard-api) [![Documentation](https://docs.rs/jacquard-api/badge.svg)](https://docs.rs/jacquard-api)
- `jacquard-axum`: Axum extractor and other helpers [![Crates.io](https://img.shields.io/crates/v/jacquard-axum.svg)](https://crates.io/crates/jacquard-axum) [![Documentation](https://docs.rs/jacquard-axum/badge.svg)](https://docs.rs/jacquard-axum)
- `jacquard-oauth`: atproto OAuth implementation [![Crates.io](https://img.shields.io/crates/v/jacquard-oauth.svg)](https://crates.io/crates/jacquard-oauth) [![Documentation](https://docs.rs/jacquard-oauth/badge.svg)](https://docs.rs/jacquard-oauth)
- `jacquard-identity`: Identity resolution [![Crates.io](https://img.shields.io/crates/v/jacquard-identity.svg)](https://crates.io/crates/jacquard-identity) [![Documentation](https://docs.rs/jacquard-identity/badge.svg)](https://docs.rs/jacquard-identity)
- `jacquard-lexicon`: Lexicon parsing and code generation [![Crates.io](https://img.shields.io/crates/v/jacquard-lexicon.svg)](https://crates.io/crates/jacquard-lexicon) [![Documentation](https://docs.rs/jacquard-lexicon/badge.svg)](https://docs.rs/jacquard-lexicon)
···
Highlights:
-
- A ton of new lexicons included in `jacquard-api`
-
- `jacquard-axum` Axum extractor and a number of improvements to lifetimes and (de)serialization required to make that work (thanks [@thoth.ptnote.dev](https://tangled.org/@thoth.ptnote.dev) for helping provide feedback and sample code to test against)
-
- `from_data`, `from_raw_data`, `to_data`, and `to_raw_data` to serialize to and deserialize from the loosely typed value data formats (think `serde_json::from_value` and company). Particularly useful for second-stage deserialization of type "unknown" fields in lexicons, such as `PostView.record`.
-
- better API code generation
## Development
···
+
[![Crates.io](https://img.shields.io/crates/v/jacquard.svg)](https://crates.io/crates/jacquard) [![Documentation](https://docs.rs/jacquard/badge.svg)](https://docs.rs/jacquard)
+
# Jacquard
+
A suite of Rust crates intended to make it much easier to get started with atproto development, without sacrificing flexibility or performance.
+
+
[Jacquard is simpler](https://whtwnd.com/nonbinary.computer/3m33efvsylz2s) because it is designed in a way which makes things simple that almost every other atproto library seems to make difficult.
+
+
It is also designed around zero-copy/borrowed deserialization: types like [`Post<'_>`](https://tangled.org/@nonbinary.computer/jacquard/blob/main/crates/jacquard-api/src/app_bsky/feed/post.rs) can borrow data (via the [`CowStr<'_>`](https://docs.rs/jacquard/latest/jacquard/cowstr/enum.CowStr.html) type and a host of other types built on top of it) directly from the response buffer instead of allocating owned copies. Owned versions are themselves mostly inlined or reference-counted pointers and are therefore still quite efficient. The `IntoStatic` trait (which is derivable) makes it easy to get an owned version and avoid worrying about lifetimes.
## Goals and Features
···
- Batteries-included, but easily replaceable batteries.
- Easy to extend with custom lexicons using code generation or handwritten api types
- Straightforward OAuth
+
- Stateless options (or options where you handle the state) for rolling your own
+
- All the building blocks of the convenient abstractions are available
+
- Server-side convenience features
+
- Lexicon Data value type for working with unknown atproto data (dag-cbor or json)
+
- An order of magnitude less boilerplate than some existing crates
+
- Use as much or as little from the crates as you need
+
## Example
···
## Component crates
+
Jacquard is broken up into several crates for modularity. The correct one to use is generally `jacquard` itself, as it re-exports most of the others.
- `jacquard`: Main crate [![Crates.io](https://img.shields.io/crates/v/jacquard.svg)](https://crates.io/crates/jacquard) [![Documentation](https://docs.rs/jacquard/badge.svg)](https://docs.rs/jacquard)
- `jacquard-common`: Foundation crate [![Crates.io](https://img.shields.io/crates/v/jacquard-common.svg)](https://crates.io/crates/jacquard-common) [![Documentation](https://docs.rs/jacquard-common/badge.svg)](https://docs.rs/jacquard-common)
- `jacquard-axum`: Axum extractor and other helpers [![Crates.io](https://img.shields.io/crates/v/jacquard-axum.svg)](https://crates.io/crates/jacquard-axum) [![Documentation](https://docs.rs/jacquard-axum/badge.svg)](https://docs.rs/jacquard-axum)
+
- `jacquard-api`: Autogenerated API bindings [![Crates.io](https://img.shields.io/crates/v/jacquard-api.svg)](https://crates.io/crates/jacquard-api) [![Documentation](https://docs.rs/jacquard-api/badge.svg)](https://docs.rs/jacquard-api)
- `jacquard-oauth`: atproto OAuth implementation [![Crates.io](https://img.shields.io/crates/v/jacquard-oauth.svg)](https://crates.io/crates/jacquard-oauth) [![Documentation](https://docs.rs/jacquard-oauth/badge.svg)](https://docs.rs/jacquard-oauth)
- `jacquard-identity`: Identity resolution [![Crates.io](https://img.shields.io/crates/v/jacquard-identity.svg)](https://crates.io/crates/jacquard-identity) [![Documentation](https://docs.rs/jacquard-identity/badge.svg)](https://docs.rs/jacquard-identity)
- `jacquard-lexicon`: Lexicon parsing and code generation [![Crates.io](https://img.shields.io/crates/v/jacquard-lexicon.svg)](https://crates.io/crates/jacquard-lexicon) [![Documentation](https://docs.rs/jacquard-lexicon/badge.svg)](https://docs.rs/jacquard-lexicon)
···
Highlights:
+
- `AgentSessionExt` trait with a host of convenience methods for working with records and preferences
+
- Improvements to the `Collection` trait, code generation, and addition of the `VecUpdate` trait to enable that
+
- A bunch of examples, both in the docs and in the repository
+
- More lexicons in the generated API bindings.
## Development