+98
CHANGELOG.md
+98
CHANGELOG.md
···
1
1
# Changelog
2
2
3
+
## [0.9.0] - 2025-11-03
4
+
5
+
### Added
6
+
7
+
**Runtime schema validation** (`jacquard-lexicon`)
8
+
- `SchemaValidator` for validating `Data` values against lexicon schemas
9
+
- CID-based validation caching for efficient repeated validation
10
+
- `ValidationResult` with structural and constraint error separation
11
+
- Comprehensive error types: `StructuralError` (type mismatches, missing fields, union errors) and `ConstraintError` (length, grapheme, numeric bounds)
12
+
- `ValidationPath` for precise error location reporting
13
+
- Ref cycle detection with configurable max depth
14
+
- Support for validating partial/malformed data without full deserialization
15
+
16
+
**Value query DSL** (`jacquard-common`)
17
+
- Pattern-based querying of nested `Data` structures
18
+
- `data.query(pattern)` with expressive syntax:
19
+
- `field.nested` - exact path navigation
20
+
- `[..]` - wildcard over collections (array elements or object values)
21
+
- `field..nested` - scoped recursion (find nested within field, expect one)
22
+
- `...field` - global recursion (find all occurrences anywhere)
23
+
- `QueryResult` enum with `Single`, `Multiple`, and `None` variants
24
+
- `QueryMatch` with path tracking for multiple results
25
+
- Iterator support via `.values()`, `.first()`, `.single()`, `.multiple()`
26
+
27
+
**Data value enhancements** (`jacquard-common`)
28
+
- `get_at_path()` for simple path-based field access on `Data` and `RawData`
29
+
- Path syntax: `embed.images[0].alt` for navigating nested structures
30
+
- `type_discriminator()` helper methods for AT Protocol union discrimination
31
+
- Returns `$type` field value for objects with type discriminators
32
+
- Added on `Data`, `Object`, and `RawData` types
33
+
- Collection helper methods: `get()`, `contains_key()`, `len()`, `is_empty()`, `iter()`, `keys()`, `values()`
34
+
- Index operator support: `obj["key"]` and `arr[0]`
35
+
36
+
**Lexicon resolution** (`jacquard-identity`)
37
+
- `LexiconResolver` for fetching lexicon schemas from AT Protocol services
38
+
- Resolves lexicons from PDS instances and lexicon hosts
39
+
- `resolve_lexicon()` fetches and parses lexicon schemas
40
+
- `resolve_lexicon_raw()` fetches raw schema JSON
41
+
- New example: `resolve_lexicon.rs`
42
+
43
+
**Identity resolver caching** (`jacquard-identity`)
44
+
- Optional `cache` feature with configurable in-memory caching
45
+
- `JacquardResolver::with_cache()` constructor for cached resolver
46
+
- Separate TTLs for handle→DID, DID→doc, and lexicon resolution
47
+
48
+
**XRPC client improvements** (`jacquard-common`, `jacquard`, `jacquard-oauth`)
49
+
- `set_options()` and `set_endpoint()` methods on `XrpcClient` trait
50
+
- Default no-op implementations for stateless clients
51
+
- Enables runtime reconfiguration of stateful clients
52
+
- Better support for custom endpoint and option overrides
53
+
54
+
**Lexicon schema generation from Rust types** (`jacquard-derive`, `jacquard-lexicon`)
55
+
- New `#[derive(LexiconSchema)]` macro for generating lexicon schemas from Rust structs
56
+
- New `#[lexicon_union]` attribute macro for lexicon union types (tagged enums)
57
+
- Automatic schema generation for custom lexicons without writing JSON manually
58
+
- Field-level attributes: `ref` for explicit type references, `union` for union fields
59
+
- Fragment support for multi-def lexicons via `fragment = "..."` attribute
60
+
- Generates `LexiconDoc` at compile time for runtime validation
61
+
- Enables type-safe custom lexicon development
62
+
63
+
**Lexicon codegen improvements** (`jacquard-lexicon`, `jacquard-api`)
64
+
- Vendored in an implementation of the typed builder pattern from `bon` to **substantially** improve compile times
65
+
- Feature-gated heavy code generation features so `jacquard-api` and other consumers of the validation capabilities don't pay the `syn` tax as badly.
66
+
- LexiconSchema trait generated implementations for runtime validation
67
+
68
+
**Session store improvements** (`jacquard`)
69
+
- Improved trait bounds for `SessionStore` implementations
70
+
- Better ergonomics for credential session types
71
+
- Memory-based credential session helpers
72
+
73
+
**New crate: `jacquard-lexgen`**
74
+
- Lexicon code generation tooling extracted from `jacquard-lexicon`
75
+
- Separates binary/CLI tools from library code
76
+
- Contains lexicon fetching and code generation binaries
77
+
- `jacquard-lexicon` remains as pure library for lexicon parsing, code generation, and validation
78
+
79
+
**Examples**
80
+
- `app_password_create_post.rs`: App password authentication example
81
+
82
+
### Changed
83
+
84
+
**Feature gating** (`jacquard-identity`)
85
+
- Better conditional compilation for platform-specific features
86
+
- Improved WASM target support
87
+
88
+
**Dependency updates**
89
+
- Updated to latest lexicons from atproto/bluesky
90
+
- Added workspace dependencies: sha2, multihash, dashmap, cid
91
+
- Various minor dependency version updates
92
+
93
+
### Fixed
94
+
95
+
**File auth store** (`jacquard`)
96
+
- Fixed serialization/deserialization bugs in `FileAuthStore` implementation
97
+
98
+
**Packaging** (`jacquard-lexgen`)
99
+
- Added Nix flake apps for lexicon tools
100
+
3
101
## [0.8.0] - 2025-10-23
4
102
5
103
### Breaking Changes
+35
-44
Cargo.lock
+35
-44
Cargo.lock
···
1796
1796
]
1797
1797
1798
1798
[[package]]
1799
-
name = "home"
1800
-
version = "0.5.12"
1801
-
source = "registry+https://github.com/rust-lang/crates.io-index"
1802
-
checksum = "cc627f471c528ff0c4a49e1d5e60450c8f6461dd6d10ba9dcd3a61d3dff7728d"
1803
-
dependencies = [
1804
-
"windows-sys 0.61.2",
1805
-
]
1806
-
1807
-
[[package]]
1808
1799
name = "html5ever"
1809
1800
version = "0.27.0"
1810
1801
source = "registry+https://github.com/rust-lang/crates.io-index"
···
2243
2234
2244
2235
[[package]]
2245
2236
name = "jacquard"
2246
-
version = "0.8.0"
2237
+
version = "0.9.0"
2247
2238
dependencies = [
2248
2239
"bytes",
2249
2240
"clap",
···
2276
2267
2277
2268
[[package]]
2278
2269
name = "jacquard-api"
2279
-
version = "0.8.0"
2270
+
version = "0.9.0"
2280
2271
dependencies = [
2281
2272
"bon",
2282
2273
"bytes",
···
2293
2284
2294
2285
[[package]]
2295
2286
name = "jacquard-axum"
2296
-
version = "0.8.0"
2287
+
version = "0.9.0"
2297
2288
dependencies = [
2298
2289
"axum",
2299
2290
"axum-macros",
···
2323
2314
2324
2315
[[package]]
2325
2316
name = "jacquard-common"
2326
-
version = "0.8.0"
2317
+
version = "0.9.0"
2327
2318
dependencies = [
2328
2319
"base64 0.22.1",
2329
2320
"bon",
···
2367
2358
2368
2359
[[package]]
2369
2360
name = "jacquard-derive"
2370
-
version = "0.8.0"
2361
+
version = "0.9.0"
2371
2362
dependencies = [
2372
2363
"heck 0.5.0",
2373
2364
"inventory",
···
2383
2374
2384
2375
[[package]]
2385
2376
name = "jacquard-identity"
2386
-
version = "0.8.0"
2377
+
version = "0.9.0"
2387
2378
dependencies = [
2388
2379
"bon",
2389
2380
"bytes",
···
2410
2401
2411
2402
[[package]]
2412
2403
name = "jacquard-lexgen"
2413
-
version = "0.8.0"
2404
+
version = "0.9.0"
2414
2405
dependencies = [
2415
2406
"clap",
2416
2407
"clap_complete",
···
2437
2428
2438
2429
[[package]]
2439
2430
name = "jacquard-lexicon"
2440
-
version = "0.8.0"
2431
+
version = "0.9.0"
2441
2432
dependencies = [
2442
2433
"bytes",
2443
2434
"cid",
···
2465
2456
2466
2457
[[package]]
2467
2458
name = "jacquard-oauth"
2468
-
version = "0.8.0"
2459
+
version = "0.9.0"
2469
2460
dependencies = [
2470
2461
"base64 0.22.1",
2471
2462
"bytes",
···
2498
2489
2499
2490
[[package]]
2500
2491
name = "jacquard-repo"
2501
-
version = "0.8.0"
2492
+
version = "0.9.0"
2502
2493
dependencies = [
2503
2494
"anyhow",
2504
2495
"bytes",
···
2776
2767
version = "0.1.1"
2777
2768
source = "registry+https://github.com/rust-lang/crates.io-index"
2778
2769
checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4"
2779
-
2780
-
[[package]]
2781
-
name = "malloc_buf"
2782
-
version = "0.0.6"
2783
-
source = "registry+https://github.com/rust-lang/crates.io-index"
2784
-
checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb"
2785
-
dependencies = [
2786
-
"libc",
2787
-
]
2788
2770
2789
2771
[[package]]
2790
2772
name = "markup5ever"
···
3197
3179
]
3198
3180
3199
3181
[[package]]
3200
-
name = "objc"
3201
-
version = "0.2.7"
3182
+
name = "objc2"
3183
+
version = "0.6.3"
3202
3184
source = "registry+https://github.com/rust-lang/crates.io-index"
3203
-
checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1"
3185
+
checksum = "b7c2599ce0ec54857b29ce62166b0ed9b4f6f1a70ccc9a71165b6154caca8c05"
3204
3186
dependencies = [
3205
-
"malloc_buf",
3187
+
"objc2-encode",
3188
+
]
3189
+
3190
+
[[package]]
3191
+
name = "objc2-encode"
3192
+
version = "4.1.0"
3193
+
source = "registry+https://github.com/rust-lang/crates.io-index"
3194
+
checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33"
3195
+
3196
+
[[package]]
3197
+
name = "objc2-foundation"
3198
+
version = "0.3.2"
3199
+
source = "registry+https://github.com/rust-lang/crates.io-index"
3200
+
checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272"
3201
+
dependencies = [
3202
+
"bitflags",
3203
+
"objc2",
3206
3204
]
3207
3205
3208
3206
[[package]]
···
3782
3780
"rayon",
3783
3781
"rgb",
3784
3782
]
3785
-
3786
-
[[package]]
3787
-
name = "raw-window-handle"
3788
-
version = "0.5.2"
3789
-
source = "registry+https://github.com/rust-lang/crates.io-index"
3790
-
checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9"
3791
3783
3792
3784
[[package]]
3793
3785
name = "rayon"
···
5553
5545
5554
5546
[[package]]
5555
5547
name = "webbrowser"
5556
-
version = "0.8.15"
5548
+
version = "1.0.6"
5557
5549
source = "registry+https://github.com/rust-lang/crates.io-index"
5558
-
checksum = "db67ae75a9405634f5882791678772c94ff5f16a66535aae186e26aa0841fc8b"
5550
+
checksum = "00f1243ef785213e3a32fa0396093424a3a6ea566f9948497e5a2309261a4c97"
5559
5551
dependencies = [
5560
-
"core-foundation 0.9.4",
5561
-
"home",
5552
+
"core-foundation 0.10.1",
5562
5553
"jni",
5563
5554
"log",
5564
5555
"ndk-context",
5565
-
"objc",
5566
-
"raw-window-handle",
5556
+
"objc2",
5557
+
"objc2-foundation",
5567
5558
"url",
5568
5559
"web-sys",
5569
5560
]
+1
-1
Cargo.toml
+1
-1
Cargo.toml
+47
-8
README.md
+47
-8
README.md
···
22
22
- All the building blocks of the convenient abstractions are available
23
23
- Use as much or as little from the crates as you need
24
24
25
-
## 0.8.0 Release Highlights:
25
+
## 0.9.0 Release Highlights:
26
+
27
+
**`#[derive(LexiconSchema)]` + `#[lexicon_union]` macros**
28
+
- Automatic schema generation for custom lexicons from Rust structs
29
+
- Supports all lexicon constraints via attributes (max_length, max_graphemes, min/max, etc.)
30
+
- Generates `LexiconDoc` at compile time for runtime validation
31
+
32
+
**Runtime lexicon data validation**
33
+
- Validation of structural and/or value contraints of data against a lexicon
34
+
- caching for value validations
35
+
- LexiconSchema trait generated implementations for runtime validation
36
+
- detailed validation error results
37
+
38
+
**Lexicon resolver**
39
+
- Fetch lexicons at runtime for addition to schema registry
40
+
41
+
**Query and path DSLs for `Data` and `RawData` value types**
42
+
- Pattern-based querying of nested `Data` structures
43
+
- `data.query(pattern)` with expressive syntax:
44
+
- `field.nested` - exact path navigation
45
+
- `[..]` - wildcard over collections (array elements or object values)
46
+
- `field..nested` - scoped recursion (find nested within field, expect one)
47
+
- `...field` - global recursion (find all occurrences anywhere)
48
+
- `get_at_path()` for simple path-based field access on `Data` and `RawData`
49
+
- Path syntax: `embed.images[0].alt` for navigating nested structures
50
+
- `type_discriminator()` helper methods for AT Protocol union discrimination
51
+
- Collection helper methods: `get()`, `contains_key()`, `len()`, `is_empty()`, `iter()`, `keys()`, `values()`
52
+
- Index trait implemented: `obj["key"]` and `arr[0]`
53
+
54
+
**Caching in identity/lexicon resolver**
55
+
- Basic LRU in-memory cache implementation using `mini-moka`
56
+
- Reduces number of network requests for certain operations
57
+
- Works on both native and WebAssembly
58
+
59
+
**XRPC client improvements**
60
+
- `set_options()` and `set_endpoint()` methods on `XrpcClient` trait
61
+
- Default no-op implementations for stateless clients
62
+
- Enables runtime reconfiguration of stateful clients
63
+
- Better support for custom endpoint and option overrides
64
+
- Fixed bug where setting a custom 'Content-Type' header wouldn't be respected
65
+
66
+
**Major generated API compilation time improvements**
67
+
- Generated code output now includes a typestate builder implementation, similar to the `bon` crate
68
+
- Moves the substantial `syn` tax of generating the builders to code generation time, not compile time.
26
69
27
-
**`jacquard-repo` crate**
28
-
- Complete implementation of the atproto repository spec
29
-
- [Sync v1.1](https://github.com/bluesky-social/proposals/blob/main/0006-sync-iteration/README.md#commit-validation-mst-operation-inversion) commit event support (both proof production and verification), well-validated in testing
30
-
- repository CAR file read/write support
31
-
- CAR file write order compatible with streaming mode from the [sync iteration proposal](https://github.com/bluesky-social/proposals/blob/main/0006-sync-iteration/README.md#streaming-car-processing)
32
-
- Big rewrite of all the errors in the crate, improvements to context and overall structure
33
-
- Made handle parsing a bit more permissive for a common case ('handle.invalid' when someone has a messed up handle), added a method to confirm syntactic validity (the correct way to confirm validity is resolve_handle() from the IdentityResolver trait, then fetching and comparing to the DID document).
70
+
**New `jacquard-lexgen` crate**
71
+
- Moves binaries out of jacquard-lexicon to reduce size further
72
+
- Flake app for `lex-fetch`
34
73
35
74
## Example
36
75
+4
-4
crates/jacquard-api/Cargo.toml
+4
-4
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.8.0"
5
+
version = "0.9.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.8", path = "../jacquard-common" }
21
-
jacquard-derive = { version = "0.8", path = "../jacquard-derive" }
22
-
jacquard-lexicon = { version = "0.8", path = "../jacquard-lexicon", default-features = false }
20
+
jacquard-common = { version = "0.9", path = "../jacquard-common" }
21
+
jacquard-derive = { version = "0.9", path = "../jacquard-derive" }
22
+
jacquard-lexicon = { version = "0.9", path = "../jacquard-lexicon", default-features = false }
23
23
miette.workspace = true
24
24
rustversion = "1.0"
25
25
serde.workspace = true
+5
-5
crates/jacquard-axum/Cargo.toml
+5
-5
crates/jacquard-axum/Cargo.toml
···
1
1
[package]
2
2
name = "jacquard-axum"
3
3
edition.workspace = true
4
-
version = "0.8.0"
4
+
version = "0.9.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.8", path = "../jacquard", default-features = false, features = ["api"] }
26
-
jacquard-common = { version = "0.8", path = "../jacquard-common", features = ["reqwest-client"] }
27
-
jacquard-derive = { version = "0.8", path = "../jacquard-derive" }
28
-
jacquard-identity = { version = "0.8", path = "../jacquard-identity", optional = true }
25
+
jacquard = { version = "0.9", path = "../jacquard", default-features = false, features = ["api"] }
26
+
jacquard-common = { version = "0.9", path = "../jacquard-common", features = ["reqwest-client"] }
27
+
jacquard-derive = { version = "0.9", path = "../jacquard-derive" }
28
+
jacquard-identity = { version = "0.9", path = "../jacquard-identity", optional = true }
29
29
miette.workspace = true
30
30
multibase = { version = "0.9.1", optional = true }
31
31
serde.workspace = true
+1
-1
crates/jacquard-common/Cargo.toml
+1
-1
crates/jacquard-common/Cargo.toml
+11
-1
crates/jacquard-common/src/xrpc.rs
+11
-1
crates/jacquard-common/src/xrpc.rs
···
542
542
path.push_str("/xrpc/");
543
543
path.push_str(<R as XrpcRequest>::NSID);
544
544
url.set_path(&path);
545
+
// Check if extra_headers already contains Content-Type
545
546
546
547
if let XrpcMethod::Query = <R as XrpcRequest>::METHOD {
547
548
let qs = serde_html_form::to_string(&req).map_err(|e| {
···
561
562
562
563
let mut builder = Request::builder().method(method).uri(url.as_str());
563
564
565
+
let has_content_type = opts
566
+
.extra_headers
567
+
.iter()
568
+
.any(|(name, _)| name == CONTENT_TYPE);
569
+
564
570
if let XrpcMethod::Procedure(encoding) = <R as XrpcRequest>::METHOD {
565
-
builder = builder.header(Header::ContentType, encoding);
571
+
// Only set default Content-Type if not provided in extra_headers
572
+
if !has_content_type {
573
+
builder = builder.header(Header::ContentType, encoding);
574
+
}
566
575
}
567
576
let output_encoding = <R::Response as XrpcResp>::ENCODING;
568
577
builder = builder.header(http::header::ACCEPT, output_encoding);
···
1118
1127
builder = builder.header(Header::AtprotoAcceptLabelers, joined);
1119
1128
}
1120
1129
}
1130
+
1121
1131
for (name, value) in &self.opts.extra_headers {
1122
1132
builder = builder.header(name, value);
1123
1133
}
+3
-3
crates/jacquard-derive/Cargo.toml
+3
-3
crates/jacquard-derive/Cargo.toml
···
16
16
17
17
[dependencies]
18
18
heck.workspace = true
19
-
jacquard-lexicon = { version = "0.8", path = "../jacquard-lexicon", features = ["codegen"] }
19
+
jacquard-lexicon = { version = "0.9", path = "../jacquard-lexicon", features = ["codegen"] }
20
20
proc-macro2.workspace = true
21
21
quote.workspace = true
22
22
syn.workspace = true
23
23
24
24
[dev-dependencies]
25
25
inventory = "0.3"
26
-
jacquard-common = { version = "0.8", path = "../jacquard-common" }
27
-
jacquard-lexicon = { version = "0.8", path = "../jacquard-lexicon" }
26
+
jacquard-common = { version = "0.9", path = "../jacquard-common" }
27
+
jacquard-lexicon = { version = "0.9", path = "../jacquard-lexicon" }
28
28
serde.workspace = true
29
29
serde_json.workspace = true
30
30
unicode-segmentation = "1.12"
+39
-13
crates/jacquard-derive/src/lib.rs
+39
-13
crates/jacquard-derive/src/lib.rs
···
84
84
//!
85
85
//! **Type-level attributes** (`#[lexicon(...)]`):
86
86
//! - `nsid = "..."`: The lexicon NSID (required)
87
-
//! - `record`: Mark as a record type (requires `key`)
87
+
//! - `record`: Mark as a record type
88
+
//! - `key = "..."`: Record key type (`"tid"`, `"literal:self"`, or custom) - optional
88
89
//! - `object`: Mark as an object type (default if neither record/procedure/query)
89
-
//! - `key = "..."`: Record key type (`"tid"`, `"literal:self"`, or custom)
90
-
//! - `fragment = "..."`: Fragment name for non-main defs
90
+
//! - `fragment = "..."`: Fragment name for non-main defs (e.g., `fragment = "textSlice"`)
91
91
//!
92
92
//! **Field-level attributes** (`#[lexicon(...)]`):
93
93
//! - `max_length = N`: Max byte length for strings
94
94
//! - `max_graphemes = N`: Max grapheme count for strings
95
95
//! - `min_length = N`, `min_graphemes = N`: Minimum constraints
96
96
//! - `minimum = N`, `maximum = N`: Integer range constraints
97
+
//! - `max_items = N`: Max array length
98
+
//! - `item_max_length = N`, `item_max_graphemes = N`: Constraints on array items
99
+
//! - `ref = "..."`: Explicit type ref (e.g., `ref = "com.atproto.repo.strongRef"` or `ref = "#textSlice"`)
100
+
//! - `union`: Mark field as union type (use with `#[lexicon_union]` enum)
97
101
//!
98
102
//! **Serde integration**: Respects `#[serde(rename)]`, `#[serde(rename_all)]`, and
99
103
//! `#[serde(skip)]`. Defaults to camelCase for field names (lexicon standard).
100
104
//!
101
-
//! **Enums**: Closed unions by default. Add `#[open_union]` for open unions. Variant
102
-
//! refs resolved via `#[nsid = "..."]` > `#[serde(rename = "...")]` > fragment inference.
105
+
//! **Unions**: Use `#[lexicon_union]` attribute macro, not `#[derive(LexiconSchema)]`.
106
+
//! Mark union fields with `#[lexicon(union)]`.
103
107
//!
104
108
//! ```ignore
105
-
//! // Record with constraints
109
+
//! // Record with constraints and fragments
106
110
//! #[derive(LexiconSchema)]
107
111
//! #[lexicon(nsid = "app.bsky.feed.post", record, key = "tid")]
112
+
//! #[serde(rename_all = "camelCase")]
108
113
//! struct Post<'a> {
109
114
//! #[lexicon(max_graphemes = 300, max_length = 3000)]
110
115
//! pub text: CowStr<'a>,
116
+
//!
111
117
//! pub created_at: Datetime, // -> "createdAt" (camelCase)
118
+
//!
119
+
//! #[lexicon(union)]
120
+
//! pub embed: Option<PostEmbed<'a>>,
121
+
//!
122
+
//! #[lexicon(max_items = 8, item_max_length = 640, item_max_graphemes = 64)]
123
+
//! pub tags: Option<Vec<CowStr<'a>>>,
124
+
//!
125
+
//! #[lexicon(ref = "app.bsky.richtext.facet")]
126
+
//! pub facets: Option<Vec<CowStr<'a>>>,
112
127
//! }
113
128
//!
114
-
//! // Closed union
129
+
//! // Fragment (non-main def)
115
130
//! #[derive(LexiconSchema)]
116
-
//! #[lexicon(nsid = "app.bsky.feed.defs")]
117
-
//! enum FeedViewPref {
118
-
//! #[nsid = "app.bsky.feed.defs#feedViewPref"]
119
-
//! Feed,
120
-
//! #[nsid = "app.bsky.feed.defs#threadViewPref"]
121
-
//! Thread,
131
+
//! #[lexicon(nsid = "app.bsky.feed.post", fragment = "textSlice")]
132
+
//! #[serde(rename_all = "camelCase")]
133
+
//! struct TextSlice {
134
+
//! #[lexicon(minimum = 0)]
135
+
//! pub start: i64,
136
+
//! #[lexicon(minimum = 0)]
137
+
//! pub end: i64,
138
+
//! }
139
+
//!
140
+
//! // Union (uses lexicon_union, not LexiconSchema)
141
+
//! #[lexicon_union]
142
+
//! #[serde(tag = "$type")]
143
+
//! enum PostEmbed<'a> {
144
+
//! #[serde(rename = "app.bsky.embed.images")]
145
+
//! Images(CowStr<'a>),
146
+
//! #[serde(rename = "app.bsky.embed.video")]
147
+
//! Video(CowStr<'a>),
122
148
//! }
123
149
//! ```
124
150
+4
-4
crates/jacquard-identity/Cargo.toml
+4
-4
crates/jacquard-identity/Cargo.toml
···
1
1
[package]
2
2
name = "jacquard-identity"
3
3
edition.workspace = true
4
-
version = "0.8.0"
4
+
version = "0.9.0"
5
5
authors.workspace = true
6
6
repository.workspace = true
7
7
keywords.workspace = true
···
22
22
trait-variant.workspace = true
23
23
bon.workspace = true
24
24
bytes.workspace = true
25
-
jacquard-common = { version = "0.8", path = "../jacquard-common", features = ["reqwest-client"] }
26
-
jacquard-api = { version = "0.8", path = "../jacquard-api", default-features = false, features = ["minimal"] }
27
-
jacquard-lexicon = { version = "0.8", path = "../jacquard-lexicon", default-features = false }
25
+
jacquard-common = { version = "0.9", path = "../jacquard-common", features = ["reqwest-client"] }
26
+
jacquard-api = { version = "0.9", path = "../jacquard-api", default-features = false, features = ["minimal"] }
27
+
jacquard-lexicon = { version = "0.9", path = "../jacquard-lexicon", default-features = false }
28
28
percent-encoding.workspace = true
29
29
reqwest.workspace = true
30
30
url.workspace = true
+2
-2
crates/jacquard-identity/src/lib.rs
+2
-2
crates/jacquard-identity/src/lib.rs
···
122
122
{
123
123
mini_moka::sync::Cache::builder()
124
124
.max_capacity(max_capacity)
125
-
.time_to_live(ttl)
125
+
.time_to_idle(ttl)
126
126
.build()
127
127
}
128
128
···
166
166
Arc::new(Mutex::new(
167
167
mini_moka::unsync::Cache::builder()
168
168
.max_capacity(max_capacity)
169
-
.time_to_live(ttl)
169
+
.time_to_idle(ttl)
170
170
.build(),
171
171
))
172
172
}
+5
-5
crates/jacquard-lexgen/Cargo.toml
+5
-5
crates/jacquard-lexgen/Cargo.toml
···
32
32
clap.workspace = true
33
33
glob = "0.3"
34
34
inventory = "0.3"
35
-
jacquard-api = { version = "0.8", path = "../jacquard-api", default-features = false, features = [ "minimal" ] }
36
-
jacquard-common = { version = "0.8", features = [ "reqwest-client" ], path = "../jacquard-common" }
37
-
jacquard-derive = { version = "0.8", path = "../jacquard-derive" }
38
-
jacquard-identity = { version = "0.8", path = "../jacquard-identity", features = ["dns"] }
39
-
jacquard-lexicon = { version = "0.8", path = "../jacquard-lexicon" }
35
+
jacquard-api = { version = "0.9", path = "../jacquard-api", default-features = false, features = [ "minimal" ] }
36
+
jacquard-common = { version = "0.9", features = [ "reqwest-client" ], path = "../jacquard-common" }
37
+
jacquard-derive = { version = "0.9", path = "../jacquard-derive" }
38
+
jacquard-identity = { version = "0.9", path = "../jacquard-identity", features = ["dns"] }
39
+
jacquard-lexicon = { version = "0.9", path = "../jacquard-lexicon" }
40
40
kdl = "6"
41
41
miette = { workspace = true, features = ["fancy"] }
42
42
reqwest = { workspace = true, features = ["json", "http2", "system-proxy", "rustls-tls"] }
+2
-2
crates/jacquard-lexicon/Cargo.toml
+2
-2
crates/jacquard-lexicon/Cargo.toml
···
20
20
dashmap.workspace = true
21
21
heck = { workspace = true, optional = true }
22
22
inventory = "0.3"
23
-
jacquard-common = { version = "0.8", path = "../jacquard-common" }
23
+
jacquard-common = { version = "0.9", path = "../jacquard-common" }
24
24
miette = { workspace = true }
25
25
multihash.workspace = true
26
26
prettyplease = { workspace = true, optional = true }
···
38
38
39
39
[dev-dependencies]
40
40
bytes = { workspace = true }
41
-
jacquard-derive = { version = "0.8", path = "../jacquard-derive" }
41
+
jacquard-derive = { version = "0.9", path = "../jacquard-derive" }
42
42
tempfile = { version = "3.23.0" }
+4
-4
crates/jacquard-oauth/Cargo.toml
+4
-4
crates/jacquard-oauth/Cargo.toml
···
1
1
[package]
2
2
name = "jacquard-oauth"
3
-
version = "0.8.0"
3
+
version = "0.9.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.8", path = "../jacquard-common", features = ["reqwest-client"] }
25
-
jacquard-identity = { version = "0.8", path = "../jacquard-identity" }
24
+
jacquard-common = { version = "0.9", path = "../jacquard-common", features = ["reqwest-client"] }
25
+
jacquard-identity = { version = "0.9", path = "../jacquard-identity" }
26
26
serde = { workspace = true, features = ["derive"] }
27
27
serde_json = { workspace = true }
28
28
url = { workspace = true }
···
45
45
tokio = { workspace = true, default-features = false, features = ["sync"] }
46
46
trait-variant.workspace = true
47
47
n0-future = { workspace = true, optional = true }
48
-
webbrowser = { version = "0.8", optional = true }
48
+
webbrowser = { version = "1", optional = true }
49
49
tracing = { workspace = true, optional = true }
50
50
51
51
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
+2
-2
crates/jacquard-repo/Cargo.toml
+2
-2
crates/jacquard-repo/Cargo.toml
···
16
16
17
17
[dependencies]
18
18
# Internal
19
-
jacquard-common = { path = "../jacquard-common", version = "0.8.0", features = ["crypto-ed25519", "crypto-k256", "crypto-p256"] }
20
-
jacquard-derive = { path = "../jacquard-derive", version = "0.8.0" }
19
+
jacquard-common = { path = "../jacquard-common", version = "0.9.0", features = ["crypto-ed25519", "crypto-k256", "crypto-p256"] }
20
+
jacquard-derive = { path = "../jacquard-derive", version = "0.9.0" }
21
21
22
22
# Serialization
23
23
serde.workspace = true
+6
-6
crates/jacquard/Cargo.toml
+6
-6
crates/jacquard/Cargo.toml
···
127
127
required-features = ["api_bluesky", "loopback"]
128
128
129
129
[dependencies]
130
-
jacquard-api = { version = "0.8", path = "../jacquard-api" }
131
-
jacquard-common = { version = "0.8", path = "../jacquard-common", features = [
130
+
jacquard-api = { version = "0.9", path = "../jacquard-api" }
131
+
jacquard-common = { version = "0.9", path = "../jacquard-common", features = [
132
132
"reqwest-client",
133
133
] }
134
-
jacquard-oauth = { version = "0.8", path = "../jacquard-oauth" }
135
-
jacquard-derive = { version = "0.8", path = "../jacquard-derive", optional = true }
136
-
jacquard-identity = { version = "0.8", path = "../jacquard-identity" }
134
+
jacquard-oauth = { version = "0.9", path = "../jacquard-oauth" }
135
+
jacquard-derive = { version = "0.9", path = "../jacquard-derive", optional = true }
136
+
jacquard-identity = { version = "0.9", path = "../jacquard-identity" }
137
137
138
138
139
139
···
158
158
159
159
160
160
[target.'cfg(not(target_family = "wasm"))'.dependencies]
161
-
jacquard-identity = { version = "0.8", path = "../jacquard-identity", features = ["cache"] }
161
+
jacquard-identity = { version = "0.9", path = "../jacquard-identity", features = ["cache"] }
162
162
reqwest = { workspace = true, features = [
163
163
"http2",
164
164
"system-proxy",