Microservice to bring 2FA to self hosted PDSes

Adding an auth module with all the scopes, handles, and did checking goodies that can be used as middleware layers in axum inspired from my attribute macros elsewhere.

example

```
// Multi-community endpoint
.route("/xrpc/community.shared.moderation.report",
post(report).layer(from_fn_with_state(
with_rules(AuthRules::All(vec![
AuthRules::HandleEndsWithAny(vec![
".blacksky.team".into(),
".bsky.team".into(),
".mod.social".into(),
]),
AuthRules::ScopeEquals("atproto".into()),
]), &state),
auth_middleware
)))
```

authored by Clinton Bowen and committed by tangled.org a49ee039 2e39f1e7

+279
AUTH.md
··· 1 + # PDS Gatekeeper Authentication Middleware 2 + 3 + This document describes the authentication middleware system for pds-gatekeeper, which provides flexible authorization rules based on DIDs, handles, and OAuth scopes. 4 + 5 + ## Overview 6 + 7 + The auth middleware validates incoming requests by: 8 + 9 + 1. **Extracting** the DID and scopes from a JWT Bearer token 10 + 2. **Resolving** the DID to a handle using jacquard-identity 11 + 3. **Validating** against configured authorization rules 12 + 4. **Returning** appropriate HTTP errors (401/403) on failure 13 + 14 + ## Quick Start 15 + 16 + ```rust 17 + use axum::middleware::from_fn_with_state; 18 + use crate::auth::{auth_middleware, handle_ends_with, scope_equals, with_rules, AuthRules}; 19 + 20 + let app = Router::new() 21 + // Simple: require handle from specific domain 22 + .route("/xrpc/community.blacksky.feed.get", 23 + get(handler).layer(from_fn_with_state( 24 + handle_ends_with(".blacksky.team", &state), 25 + auth_middleware 26 + ))) 27 + 28 + // Simple: require specific OAuth scope 29 + .route("/xrpc/com.atproto.repo.createRecord", 30 + post(handler).layer(from_fn_with_state( 31 + scope_equals("repo:app.bsky.feed.post", &state), 32 + auth_middleware 33 + ))) 34 + 35 + .with_state(state); 36 + ``` 37 + 38 + ## ATProto OAuth Scopes Reference 39 + 40 + | Scope | Description | 41 + |-------|-------------| 42 + | `atproto` | Base scope, required for all OAuth clients | 43 + | `transition:generic` | Full repository access (equivalent to app passwords) | 44 + | `repo:<collection>` | Access to specific collection (e.g., `repo:app.bsky.feed.post`) | 45 + | `identity:handle` | Permits handle changes | 46 + | `identity:*` | Full DID document control | 47 + | `account:email` | Read email addresses | 48 + | `account:repo?action=manage` | Import repository data | 49 + | `blob:*/*` | Upload any blob type | 50 + | `blob?accept=image/*` | Upload only images | 51 + 52 + See [Marvin's Guide to OAuth Scopes](https://marvins-guide.leaflet.pub/3mbfvey7sok26) for complete details. 53 + 54 + ## Helper Functions 55 + 56 + ### Identity Helpers 57 + 58 + | Function | Description | 59 + |----------|-------------| 60 + | `handle_ends_with(suffix, state)` | Handle must end with suffix | 61 + | `handle_ends_with_any(suffixes, state)` | Handle must end with any suffix (OR) | 62 + | `did_equals(did, state)` | DID must match exactly | 63 + | `did_equals_any(dids, state)` | DID must match any value (OR) | 64 + 65 + ### Scope Helpers 66 + 67 + | Function | Description | 68 + |----------|-------------| 69 + | `scope_equals(scope, state)` | Must have specific scope | 70 + | `scope_any(scopes, state)` | Must have any of the scopes (OR) | 71 + | `scope_all(scopes, state)` | Must have all scopes (AND) | 72 + 73 + ### Combined Helpers (Identity + Scope) 74 + 75 + | Function | Description | 76 + |----------|-------------| 77 + | `handle_ends_with_and_scope(suffix, scope, state)` | Handle suffix AND scope | 78 + | `handle_ends_with_and_scopes(suffix, scopes, state)` | Handle suffix AND all scopes | 79 + | `did_with_scope(did, scope, state)` | DID match AND scope | 80 + | `did_with_scopes(did, scopes, state)` | DID match AND all scopes | 81 + 82 + ### Custom Rules 83 + 84 + For complex authorization logic, use `with_rules()`: 85 + 86 + ```rust 87 + with_rules(AuthRules::Any(vec![ 88 + AuthRules::DidEquals("did:plc:rnpkyqnmsw4ipey6eotbdnnf".into()), 89 + AuthRules::All(vec![ 90 + AuthRules::HandleEndsWith(".mod.team".into()), 91 + AuthRules::ScopeEquals("account:email".into()), 92 + ]), 93 + ]), &state) 94 + ``` 95 + 96 + ## Realistic PDS Endpoint Examples 97 + 98 + ### Admin Endpoints 99 + 100 + Based on `com.atproto.admin.*` endpoints from the ATProto PDS: 101 + 102 + ```rust 103 + // com.atproto.admin.deleteAccount 104 + // Admin-only: specific DID with full access scope 105 + .route("/xrpc/com.atproto.admin.deleteAccount", 106 + post(delete_account).layer(from_fn_with_state( 107 + did_with_scope("did:plc:rnpkyqnmsw4ipey6eotbdnnf", "transition:generic", &state), 108 + auth_middleware 109 + ))) 110 + 111 + // com.atproto.admin.getAccountInfo 112 + // Either admin DID OR (moderator handle + account scope) 113 + .route("/xrpc/com.atproto.admin.getAccountInfo", 114 + get(get_account_info).layer(from_fn_with_state( 115 + with_rules(AuthRules::Any(vec![ 116 + AuthRules::DidEquals("did:plc:rnpkyqnmsw4ipey6eotbdnnf".into()), 117 + AuthRules::All(vec![ 118 + AuthRules::HandleEndsWith(".mod.team".into()), 119 + AuthRules::ScopeEquals("account:email".into()), 120 + ]), 121 + ]), &state), 122 + auth_middleware 123 + ))) 124 + 125 + // com.atproto.admin.updateAccountEmail 126 + // Admin DID with account management scope 127 + .route("/xrpc/com.atproto.admin.updateAccountEmail", 128 + post(update_email).layer(from_fn_with_state( 129 + did_with_scopes( 130 + "did:plc:rnpkyqnmsw4ipey6eotbdnnf", 131 + ["account:email", "account:repo?action=manage"], 132 + &state 133 + ), 134 + auth_middleware 135 + ))) 136 + 137 + // com.atproto.admin.updateAccountHandle 138 + // Admin with identity control 139 + .route("/xrpc/com.atproto.admin.updateAccountHandle", 140 + post(update_handle).layer(from_fn_with_state( 141 + did_with_scope("did:plc:rnpkyqnmsw4ipey6eotbdnnf", "identity:*", &state), 142 + auth_middleware 143 + ))) 144 + ``` 145 + 146 + ### Repository Endpoints 147 + 148 + ```rust 149 + // com.atproto.repo.createRecord 150 + // Scoped write access to specific collection 151 + .route("/xrpc/com.atproto.repo.createRecord", 152 + post(create_record).layer(from_fn_with_state( 153 + scope_equals("repo:app.bsky.feed.post", &state), 154 + auth_middleware 155 + ))) 156 + 157 + // com.atproto.repo.putRecord 158 + // Either specific collection scope OR full access 159 + .route("/xrpc/com.atproto.repo.putRecord", 160 + post(put_record).layer(from_fn_with_state( 161 + scope_any(["repo:app.bsky.feed.post", "transition:generic"], &state), 162 + auth_middleware 163 + ))) 164 + 165 + // com.atproto.repo.uploadBlob 166 + // Blob upload with media type restriction (scope-based) 167 + .route("/xrpc/com.atproto.repo.uploadBlob", 168 + post(upload_blob).layer(from_fn_with_state( 169 + scope_any(["blob:*/*", "blob?accept=image/*", "transition:generic"], &state), 170 + auth_middleware 171 + ))) 172 + ``` 173 + 174 + ### Community/Custom Endpoints 175 + 176 + ```rust 177 + // Community feed generator - restricted to team members with full access 178 + .route("/xrpc/community.blacksky.feed.generator", 179 + post(generator).layer(from_fn_with_state( 180 + handle_ends_with_and_scope(".blacksky.team", "transition:generic", &state), 181 + auth_middleware 182 + ))) 183 + 184 + // Multi-community endpoint 185 + .route("/xrpc/community.shared.moderation.report", 186 + post(report).layer(from_fn_with_state( 187 + with_rules(AuthRules::All(vec![ 188 + AuthRules::HandleEndsWithAny(vec![ 189 + ".blacksky.team".into(), 190 + ".bsky.team".into(), 191 + ".mod.social".into(), 192 + ]), 193 + AuthRules::ScopeEquals("atproto".into()), 194 + ]), &state), 195 + auth_middleware 196 + ))) 197 + 198 + // VIP access - specific DIDs only 199 + .route("/xrpc/community.blacksky.vip.access", 200 + get(vip_handler).layer(from_fn_with_state( 201 + did_equals_any([ 202 + "did:plc:rnpkyqnmsw4ipey6eotbdnnf", 203 + "did:plc:abc123def456ghi789jklmno", 204 + "did:plc:xyz987uvw654rst321qponml", 205 + ], &state), 206 + auth_middleware 207 + ))) 208 + ``` 209 + 210 + ## Building Complex Authorization Rules 211 + 212 + The `AuthRules` enum supports arbitrary nesting: 213 + 214 + ```rust 215 + // Complex: Admin OR (Team member with write scope) OR (Moderator with read-only) 216 + let rules = AuthRules::Any(vec![ 217 + // Admin bypass 218 + AuthRules::DidEquals("did:plc:rnpkyqnmsw4ipey6eotbdnnf".into()), 219 + 220 + // Team member with write access 221 + AuthRules::All(vec![ 222 + AuthRules::HandleEndsWith(".blacksky.team".into()), 223 + AuthRules::ScopeEquals("transition:generic".into()), 224 + ]), 225 + 226 + // Moderator with limited scope 227 + AuthRules::All(vec![ 228 + AuthRules::HandleEndsWith(".mod.team".into()), 229 + AuthRules::ScopeEqualsAny(vec![ 230 + "account:email".into(), 231 + "atproto".into(), 232 + ]), 233 + ]), 234 + ]); 235 + ``` 236 + 237 + ## Error Responses 238 + 239 + | Status | Error Code | Description | 240 + |--------|------------|-------------| 241 + | `401` | `AuthRequired` | No Authorization header provided | 242 + | `401` | `InvalidToken` | JWT validation failed (expired, invalid signature, malformed) | 243 + | `403` | `AccessDenied` | Valid authentication but authorization rules rejected | 244 + | `500` | `ResolutionError` | Failed to resolve DID to handle | 245 + 246 + Response format: 247 + ```json 248 + { 249 + "error": "AccessDenied", 250 + "message": "Access denied by authorization rules" 251 + } 252 + ``` 253 + 254 + ## JWT Token Format 255 + 256 + The middleware expects JWT tokens with these claims: 257 + 258 + ```json 259 + { 260 + "sub": "did:plc:rnpkyqnmsw4ipey6eotbdnnf", 261 + "scope": "atproto transition:generic repo:app.bsky.feed.post", 262 + "iat": 1704067200, 263 + "exp": 1704153600 264 + } 265 + ``` 266 + 267 + - `sub` (required): The user's DID 268 + - `scope` (optional): Space-separated OAuth scopes per [RFC 6749](https://tools.ietf.org/html/rfc6749) 269 + 270 + ## Handle Resolution 271 + 272 + DIDs are resolved to handles using the jacquard-identity `PublicResolver`: 273 + 274 + 1. Check the `HandleCache` for a cached result 275 + 2. If miss, resolve the DID document via PLC directory 276 + 3. Extract handle from `alsoKnownAs` field (format: `at://handle.example.com`) 277 + 4. Cache the result (1 hour TTL default) 278 + 279 + This allows rules like `HandleEndsWith(".blacksky.team")` to work even though the JWT only contains the DID.
+759 -605
Cargo.lock
··· 22 22 ] 23 23 24 24 [[package]] 25 - name = "addr2line" 26 - version = "0.24.2" 27 - source = "registry+https://github.com/rust-lang/crates.io-index" 28 - checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" 29 - dependencies = [ 30 - "gimli", 31 - ] 32 - 33 - [[package]] 34 25 name = "adler2" 35 26 version = "2.0.1" 36 27 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 50 41 51 42 [[package]] 52 43 name = "aho-corasick" 53 - version = "1.1.3" 44 + version = "1.1.4" 54 45 source = "registry+https://github.com/rust-lang/crates.io-index" 55 - checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" 46 + checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" 56 47 dependencies = [ 57 48 "memchr", 58 49 ] ··· 80 71 81 72 [[package]] 82 73 name = "anyhow" 83 - version = "1.0.99" 74 + version = "1.0.100" 75 + source = "registry+https://github.com/rust-lang/crates.io-index" 76 + checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" 77 + 78 + [[package]] 79 + name = "ar_archive_writer" 80 + version = "0.2.0" 84 81 source = "registry+https://github.com/rust-lang/crates.io-index" 85 - checksum = "b0674a1ddeecb70197781e945de4b3b8ffb61fa939a5597bcf48503737663100" 82 + checksum = "f0c269894b6fe5e9d7ada0cf69b5bf847ff35bc25fc271f08e1d080fce80339a" 83 + dependencies = [ 84 + "object", 85 + ] 86 86 87 87 [[package]] 88 88 name = "async-compression" 89 - version = "0.4.27" 89 + version = "0.4.36" 90 90 source = "registry+https://github.com/rust-lang/crates.io-index" 91 - checksum = "ddb939d66e4ae03cee6091612804ba446b12878410cfa17f785f4dd67d4014e8" 91 + checksum = "98ec5f6c2f8bc326c994cb9e241cc257ddaba9afa8555a43cffbb5dd86efaa37" 92 92 dependencies = [ 93 - "flate2", 93 + "compression-codecs", 94 + "compression-core", 94 95 "futures-core", 95 - "memchr", 96 96 "pin-project-lite", 97 97 "tokio", 98 - "zstd", 99 - "zstd-safe", 100 98 ] 101 99 102 100 [[package]] ··· 107 105 dependencies = [ 108 106 "proc-macro2", 109 107 "quote", 110 - "syn 2.0.105", 108 + "syn 2.0.112", 111 109 ] 112 110 113 111 [[package]] ··· 120 118 ] 121 119 122 120 [[package]] 121 + name = "atomic-polyfill" 122 + version = "1.0.3" 123 + source = "registry+https://github.com/rust-lang/crates.io-index" 124 + checksum = "8cf2bce30dfe09ef0bfaef228b9d414faaf7e563035494d7fe092dba54b300f4" 125 + dependencies = [ 126 + "critical-section", 127 + ] 128 + 129 + [[package]] 123 130 name = "atomic-waker" 124 131 version = "1.1.2" 125 132 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 133 140 134 141 [[package]] 135 142 name = "aws-lc-rs" 136 - version = "1.13.3" 143 + version = "1.15.2" 137 144 source = "registry+https://github.com/rust-lang/crates.io-index" 138 - checksum = "5c953fe1ba023e6b7730c0d4b031d06f267f23a46167dcbd40316644b10a17ba" 145 + checksum = "6a88aab2464f1f25453baa7a07c84c5b7684e274054ba06817f382357f77a288" 139 146 dependencies = [ 140 147 "aws-lc-sys", 141 148 "untrusted 0.7.1", ··· 144 151 145 152 [[package]] 146 153 name = "aws-lc-sys" 147 - version = "0.30.0" 154 + version = "0.35.0" 148 155 source = "registry+https://github.com/rust-lang/crates.io-index" 149 - checksum = "dbfd150b5dbdb988bcc8fb1fe787eb6b7ee6180ca24da683b61ea5405f3d43ff" 156 + checksum = "b45afffdee1e7c9126814751f88dddc747f41d91da16c9551a0f1e8a11e788a1" 150 157 dependencies = [ 151 - "bindgen", 152 158 "cc", 153 159 "cmake", 154 160 "dunce", ··· 157 163 158 164 [[package]] 159 165 name = "axum" 160 - version = "0.8.4" 166 + version = "0.8.8" 161 167 source = "registry+https://github.com/rust-lang/crates.io-index" 162 - checksum = "021e862c184ae977658b36c4500f7feac3221ca5da43e3f25bd04ab6c79a29b5" 168 + checksum = "8b52af3cb4058c895d37317bb27508dccc8e5f2d39454016b297bf4a400597b8" 163 169 dependencies = [ 164 170 "axum-core", 165 171 "axum-macros", ··· 177 183 "mime", 178 184 "percent-encoding", 179 185 "pin-project-lite", 180 - "rustversion", 181 - "serde", 186 + "serde_core", 182 187 "serde_json", 183 188 "serde_path_to_error", 184 189 "serde_urlencoded", ··· 192 197 193 198 [[package]] 194 199 name = "axum-core" 195 - version = "0.5.2" 200 + version = "0.5.6" 196 201 source = "registry+https://github.com/rust-lang/crates.io-index" 197 - checksum = "68464cd0412f486726fb3373129ef5d2993f90c34bc2bc1c1e9943b2f4fc7ca6" 202 + checksum = "08c78f31d7b1291f7ee735c1c6780ccde7785daae9a9206026862dab7d8792d1" 198 203 dependencies = [ 199 204 "bytes", 200 205 "futures-core", ··· 203 208 "http-body-util", 204 209 "mime", 205 210 "pin-project-lite", 206 - "rustversion", 207 211 "sync_wrapper", 208 212 "tower-layer", 209 213 "tower-service", ··· 218 222 dependencies = [ 219 223 "proc-macro2", 220 224 "quote", 221 - "syn 2.0.105", 225 + "syn 2.0.112", 222 226 ] 223 227 224 228 [[package]] ··· 230 234 "axum", 231 235 "handlebars", 232 236 "serde", 233 - "thiserror 2.0.14", 234 - ] 235 - 236 - [[package]] 237 - name = "backtrace" 238 - version = "0.3.75" 239 - source = "registry+https://github.com/rust-lang/crates.io-index" 240 - checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" 241 - dependencies = [ 242 - "addr2line", 243 - "cfg-if", 244 - "libc", 245 - "miniz_oxide", 246 - "object", 247 - "rustc-demangle", 248 - "windows-targets 0.52.6", 237 + "thiserror 2.0.17", 249 238 ] 250 239 251 240 [[package]] ··· 278 267 279 268 [[package]] 280 269 name = "base64ct" 281 - version = "1.8.0" 270 + version = "1.8.1" 282 271 source = "registry+https://github.com/rust-lang/crates.io-index" 283 - checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba" 284 - 285 - [[package]] 286 - name = "bindgen" 287 - version = "0.69.5" 288 - source = "registry+https://github.com/rust-lang/crates.io-index" 289 - checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" 290 - dependencies = [ 291 - "bitflags", 292 - "cexpr", 293 - "clang-sys", 294 - "itertools", 295 - "lazy_static", 296 - "lazycell", 297 - "log", 298 - "prettyplease", 299 - "proc-macro2", 300 - "quote", 301 - "regex", 302 - "rustc-hash 1.1.0", 303 - "shlex", 304 - "syn 2.0.105", 305 - "which", 306 - ] 272 + checksum = "0e050f626429857a27ddccb31e0aca21356bfa709c04041aefddac081a8f068a" 307 273 308 274 [[package]] 309 275 name = "bitflags" 310 - version = "2.9.1" 276 + version = "2.10.0" 311 277 source = "registry+https://github.com/rust-lang/crates.io-index" 312 - checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" 278 + checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" 313 279 dependencies = [ 314 - "serde", 280 + "serde_core", 315 281 ] 316 282 317 283 [[package]] ··· 345 311 "proc-macro2", 346 312 "quote", 347 313 "rustversion", 348 - "syn 2.0.105", 314 + "syn 2.0.112", 349 315 ] 350 316 351 317 [[package]] ··· 359 325 360 326 [[package]] 361 327 name = "bstr" 362 - version = "1.12.0" 328 + version = "1.12.1" 363 329 source = "registry+https://github.com/rust-lang/crates.io-index" 364 - checksum = "234113d19d0d7d613b40e86fb654acf958910802bcceab913a4f9e7cda03b1a4" 330 + checksum = "63044e1ae8e69f3b5a92c736ca6269b8d12fa7efe39bf34ddb06d102cf0e2cab" 365 331 dependencies = [ 366 332 "memchr", 367 333 "serde", ··· 393 359 394 360 [[package]] 395 361 name = "bumpalo" 396 - version = "3.19.0" 362 + version = "3.19.1" 397 363 source = "registry+https://github.com/rust-lang/crates.io-index" 398 - checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" 364 + checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" 399 365 400 366 [[package]] 401 367 name = "byteorder" ··· 405 371 406 372 [[package]] 407 373 name = "bytes" 408 - version = "1.10.1" 374 + version = "1.11.0" 409 375 source = "registry+https://github.com/rust-lang/crates.io-index" 410 - checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" 376 + checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" 411 377 dependencies = [ 412 378 "serde", 413 379 ] ··· 423 389 424 390 [[package]] 425 391 name = "cc" 426 - version = "1.2.32" 392 + version = "1.2.51" 427 393 source = "registry+https://github.com/rust-lang/crates.io-index" 428 - checksum = "2352e5597e9c544d5e6d9c95190d5d27738ade584fa8db0a16e130e5c2b5296e" 394 + checksum = "7a0aeaff4ff1a90589618835a598e545176939b97874f7abc7851caa0618f203" 429 395 dependencies = [ 396 + "find-msvc-tools", 430 397 "jobserver", 431 398 "libc", 432 399 "shlex", ··· 442 409 ] 443 410 444 411 [[package]] 445 - name = "cexpr" 446 - version = "0.6.0" 447 - source = "registry+https://github.com/rust-lang/crates.io-index" 448 - checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" 449 - dependencies = [ 450 - "nom 7.1.3", 451 - ] 452 - 453 - [[package]] 454 412 name = "cfg-if" 455 - version = "1.0.1" 413 + version = "1.0.4" 456 414 source = "registry+https://github.com/rust-lang/crates.io-index" 457 - checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" 415 + checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" 458 416 459 417 [[package]] 460 418 name = "cfg_aliases" ··· 473 431 "num-traits", 474 432 "serde", 475 433 "wasm-bindgen", 476 - "windows-link 0.2.1", 434 + "windows-link", 477 435 ] 478 436 479 437 [[package]] ··· 538 496 ] 539 497 540 498 [[package]] 541 - name = "clang-sys" 542 - version = "1.8.1" 499 + name = "cmake" 500 + version = "0.1.57" 501 + source = "registry+https://github.com/rust-lang/crates.io-index" 502 + checksum = "75443c44cd6b379beb8c5b45d85d0773baf31cce901fe7bb252f4eff3008ef7d" 503 + dependencies = [ 504 + "cc", 505 + ] 506 + 507 + [[package]] 508 + name = "cobs" 509 + version = "0.3.0" 543 510 source = "registry+https://github.com/rust-lang/crates.io-index" 544 - checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" 511 + checksum = "0fa961b519f0b462e3a3b4a34b64d119eeaca1d59af726fe450bbba07a9fc0a1" 545 512 dependencies = [ 546 - "glob", 547 - "libc", 548 - "libloading", 513 + "thiserror 2.0.17", 549 514 ] 550 515 551 516 [[package]] 552 - name = "cmake" 553 - version = "0.1.54" 517 + name = "compression-codecs" 518 + version = "0.4.35" 554 519 source = "registry+https://github.com/rust-lang/crates.io-index" 555 - checksum = "e7caa3f9de89ddbe2c607f4101924c5abec803763ae9534e4f4d7d8f84aa81f0" 520 + checksum = "b0f7ac3e5b97fdce45e8922fb05cae2c37f7bbd63d30dd94821dacfd8f3f2bf2" 556 521 dependencies = [ 557 - "cc", 522 + "compression-core", 523 + "flate2", 524 + "memchr", 525 + "zstd", 526 + "zstd-safe", 558 527 ] 528 + 529 + [[package]] 530 + name = "compression-core" 531 + version = "0.4.31" 532 + source = "registry+https://github.com/rust-lang/crates.io-index" 533 + checksum = "75984efb6ed102a0d42db99afb6c1948f0380d1d91808d5529916e6c08b49d8d" 559 534 560 535 [[package]] 561 536 name = "concurrent-queue" ··· 579 554 checksum = "2f421161cb492475f1661ddc9815a745a1c894592070661180fdec3d4872e9c3" 580 555 581 556 [[package]] 557 + name = "cordyceps" 558 + version = "0.3.4" 559 + source = "registry+https://github.com/rust-lang/crates.io-index" 560 + checksum = "688d7fbb8092b8de775ef2536f36c8c31f2bc4006ece2e8d8ad2d17d00ce0a2a" 561 + dependencies = [ 562 + "loom", 563 + "tracing", 564 + ] 565 + 566 + [[package]] 582 567 name = "core-foundation" 583 568 version = "0.9.4" 584 569 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 614 599 615 600 [[package]] 616 601 name = "crc" 617 - version = "3.3.0" 602 + version = "3.4.0" 618 603 source = "registry+https://github.com/rust-lang/crates.io-index" 619 - checksum = "9710d3b3739c2e349eb44fe848ad0b7c8cb1e42bd87ee49371df2f7acaf3e675" 604 + checksum = "5eb8a2a1cd12ab0d987a5d5e825195d372001a4094a0376319d5a0ad71c1ba0d" 620 605 dependencies = [ 621 606 "crc-catalog", 622 607 ] ··· 635 620 dependencies = [ 636 621 "cfg-if", 637 622 ] 623 + 624 + [[package]] 625 + name = "critical-section" 626 + version = "1.2.0" 627 + source = "registry+https://github.com/rust-lang/crates.io-index" 628 + checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" 638 629 639 630 [[package]] 640 631 name = "crossbeam-queue" ··· 671 662 672 663 [[package]] 673 664 name = "crypto-common" 674 - version = "0.1.6" 665 + version = "0.1.7" 675 666 source = "registry+https://github.com/rust-lang/crates.io-index" 676 - checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" 667 + checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" 677 668 dependencies = [ 678 669 "generic-array", 679 670 "typenum", ··· 710 701 "proc-macro2", 711 702 "quote", 712 703 "strsim", 713 - "syn 2.0.105", 704 + "syn 2.0.112", 714 705 ] 715 706 716 707 [[package]] ··· 724 715 "proc-macro2", 725 716 "quote", 726 717 "strsim", 727 - "syn 2.0.105", 718 + "syn 2.0.112", 728 719 ] 729 720 730 721 [[package]] ··· 735 726 dependencies = [ 736 727 "darling_core 0.20.11", 737 728 "quote", 738 - "syn 2.0.105", 729 + "syn 2.0.112", 739 730 ] 740 731 741 732 [[package]] ··· 746 737 dependencies = [ 747 738 "darling_core 0.21.3", 748 739 "quote", 749 - "syn 2.0.105", 740 + "syn 2.0.112", 750 741 ] 751 742 752 743 [[package]] ··· 786 777 checksum = "8d162beedaa69905488a8da94f5ac3edb4dd4788b732fadb7bd120b2625c1976" 787 778 dependencies = [ 788 779 "data-encoding", 789 - "syn 2.0.105", 780 + "syn 2.0.112", 790 781 ] 791 782 792 783 [[package]] ··· 828 819 "darling 0.20.11", 829 820 "proc-macro2", 830 821 "quote", 831 - "syn 2.0.105", 822 + "syn 2.0.112", 832 823 ] 833 824 834 825 [[package]] ··· 838 829 checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" 839 830 dependencies = [ 840 831 "derive_builder_core", 841 - "syn 2.0.105", 832 + "syn 2.0.112", 842 833 ] 834 + 835 + [[package]] 836 + name = "derive_more" 837 + version = "1.0.0" 838 + source = "registry+https://github.com/rust-lang/crates.io-index" 839 + checksum = "4a9b99b9cbbe49445b21764dc0625032a89b145a2642e67603e1c936f5458d05" 840 + dependencies = [ 841 + "derive_more-impl", 842 + ] 843 + 844 + [[package]] 845 + name = "derive_more-impl" 846 + version = "1.0.0" 847 + source = "registry+https://github.com/rust-lang/crates.io-index" 848 + checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" 849 + dependencies = [ 850 + "proc-macro2", 851 + "quote", 852 + "syn 2.0.112", 853 + "unicode-xid", 854 + ] 855 + 856 + [[package]] 857 + name = "diatomic-waker" 858 + version = "0.2.3" 859 + source = "registry+https://github.com/rust-lang/crates.io-index" 860 + checksum = "ab03c107fafeb3ee9f5925686dbb7a73bc76e3932abb0d2b365cb64b169cf04c" 843 861 844 862 [[package]] 845 863 name = "digest" ··· 861 879 dependencies = [ 862 880 "proc-macro2", 863 881 "quote", 864 - "syn 2.0.105", 882 + "syn 2.0.112", 865 883 ] 866 884 867 885 [[package]] ··· 942 960 checksum = "e079f19b08ca6239f47f8ba8509c11cf3ea30095831f7fed61441475edd8c449" 943 961 944 962 [[package]] 963 + name = "embedded-io" 964 + version = "0.4.0" 965 + source = "registry+https://github.com/rust-lang/crates.io-index" 966 + checksum = "ef1a6892d9eef45c8fa6b9e0086428a2cca8491aca8f787c534a3d6d0bcb3ced" 967 + 968 + [[package]] 969 + name = "embedded-io" 970 + version = "0.6.1" 971 + source = "registry+https://github.com/rust-lang/crates.io-index" 972 + checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" 973 + 974 + [[package]] 945 975 name = "encoding_rs" 946 976 version = "0.8.35" 947 977 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 958 988 959 989 [[package]] 960 990 name = "errno" 961 - version = "0.3.13" 991 + version = "0.3.14" 962 992 source = "registry+https://github.com/rust-lang/crates.io-index" 963 - checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad" 993 + checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" 964 994 dependencies = [ 965 995 "libc", 966 996 "windows-sys 0.59.0", ··· 1005 1035 ] 1006 1036 1007 1037 [[package]] 1038 + name = "find-msvc-tools" 1039 + version = "0.1.6" 1040 + source = "registry+https://github.com/rust-lang/crates.io-index" 1041 + checksum = "645cbb3a84e60b7531617d5ae4e57f7e27308f6445f5abf653209ea76dec8dff" 1042 + 1043 + [[package]] 1008 1044 name = "flate2" 1009 1045 version = "1.1.5" 1010 1046 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1022 1058 dependencies = [ 1023 1059 "futures-core", 1024 1060 "futures-sink", 1025 - "spin", 1061 + "spin 0.9.8", 1026 1062 ] 1027 1063 1028 1064 [[package]] ··· 1038 1074 checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" 1039 1075 1040 1076 [[package]] 1077 + name = "foldhash" 1078 + version = "0.2.0" 1079 + source = "registry+https://github.com/rust-lang/crates.io-index" 1080 + checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" 1081 + 1082 + [[package]] 1041 1083 name = "foreign-types" 1042 1084 version = "0.3.2" 1043 1085 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1054 1096 1055 1097 [[package]] 1056 1098 name = "form_urlencoded" 1057 - version = "1.2.1" 1099 + version = "1.2.2" 1058 1100 source = "registry+https://github.com/rust-lang/crates.io-index" 1059 - checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" 1101 + checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" 1060 1102 dependencies = [ 1061 1103 "percent-encoding", 1062 1104 ] ··· 1078 1120 checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" 1079 1121 1080 1122 [[package]] 1123 + name = "futures-buffered" 1124 + version = "0.2.12" 1125 + source = "registry+https://github.com/rust-lang/crates.io-index" 1126 + checksum = "a8e0e1f38ec07ba4abbde21eed377082f17ccb988be9d988a5adbf4bafc118fd" 1127 + dependencies = [ 1128 + "cordyceps", 1129 + "diatomic-waker", 1130 + "futures-core", 1131 + "pin-project-lite", 1132 + "spin 0.10.0", 1133 + ] 1134 + 1135 + [[package]] 1081 1136 name = "futures-channel" 1082 1137 version = "0.3.31" 1083 1138 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1122 1177 checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" 1123 1178 1124 1179 [[package]] 1180 + name = "futures-lite" 1181 + version = "2.6.1" 1182 + source = "registry+https://github.com/rust-lang/crates.io-index" 1183 + checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad" 1184 + dependencies = [ 1185 + "fastrand", 1186 + "futures-core", 1187 + "futures-io", 1188 + "parking", 1189 + "pin-project-lite", 1190 + ] 1191 + 1192 + [[package]] 1125 1193 name = "futures-macro" 1126 1194 version = "0.3.31" 1127 1195 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1129 1197 dependencies = [ 1130 1198 "proc-macro2", 1131 1199 "quote", 1132 - "syn 2.0.105", 1200 + "syn 2.0.112", 1133 1201 ] 1134 1202 1135 1203 [[package]] ··· 1168 1236 ] 1169 1237 1170 1238 [[package]] 1239 + name = "generator" 1240 + version = "0.8.8" 1241 + source = "registry+https://github.com/rust-lang/crates.io-index" 1242 + checksum = "52f04ae4152da20c76fe800fa48659201d5cf627c5149ca0b707b69d7eef6cf9" 1243 + dependencies = [ 1244 + "cc", 1245 + "cfg-if", 1246 + "libc", 1247 + "log", 1248 + "rustversion", 1249 + "windows-link", 1250 + "windows-result", 1251 + ] 1252 + 1253 + [[package]] 1171 1254 name = "generic-array" 1172 1255 version = "0.14.7" 1173 1256 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1206 1289 ] 1207 1290 1208 1291 [[package]] 1209 - name = "gimli" 1210 - version = "0.31.1" 1211 - source = "registry+https://github.com/rust-lang/crates.io-index" 1212 - checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" 1213 - 1214 - [[package]] 1215 - name = "glob" 1216 - version = "0.3.3" 1217 - source = "registry+https://github.com/rust-lang/crates.io-index" 1218 - checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" 1219 - 1220 - [[package]] 1221 1292 name = "globset" 1222 - version = "0.4.16" 1293 + version = "0.4.18" 1223 1294 source = "registry+https://github.com/rust-lang/crates.io-index" 1224 - checksum = "54a1028dfc5f5df5da8a56a73e6c153c9a9708ec57232470703592a3f18e49f5" 1295 + checksum = "52dfc19153a48bde0cbd630453615c8151bce3a5adfac7a0aebfbf0a1e1f57e3" 1225 1296 dependencies = [ 1226 1297 "aho-corasick", 1227 1298 "bstr", 1228 1299 "log", 1229 - "regex-automata 0.4.13", 1230 - "regex-syntax 0.8.5", 1300 + "regex-automata", 1301 + "regex-syntax", 1231 1302 ] 1232 1303 1233 1304 [[package]] 1234 1305 name = "governor" 1235 - version = "0.10.1" 1306 + version = "0.10.4" 1236 1307 source = "registry+https://github.com/rust-lang/crates.io-index" 1237 - checksum = "444405bbb1a762387aa22dd569429533b54a1d8759d35d3b64cb39b0293eaa19" 1308 + checksum = "9efcab3c1958580ff1f25a2a41be1668f7603d849bb63af523b208a3cc1223b8" 1238 1309 dependencies = [ 1239 1310 "cfg-if", 1240 1311 "dashmap", ··· 1242 1313 "futures-timer", 1243 1314 "futures-util", 1244 1315 "getrandom 0.3.4", 1245 - "hashbrown 0.15.5", 1316 + "hashbrown 0.16.1", 1246 1317 "nonzero_ext", 1247 1318 "parking_lot", 1248 1319 "portable-atomic", ··· 1276 1347 "futures-core", 1277 1348 "futures-sink", 1278 1349 "http", 1279 - "indexmap 2.10.0", 1350 + "indexmap 2.12.1", 1280 1351 "slab", 1281 1352 "tokio", 1282 1353 "tokio-util", ··· 1285 1356 1286 1357 [[package]] 1287 1358 name = "half" 1288 - version = "2.6.0" 1359 + version = "2.7.1" 1289 1360 source = "registry+https://github.com/rust-lang/crates.io-index" 1290 - checksum = "459196ed295495a68f7d7fe1d84f6c4b7ff0e21fe3017b2f283c6fac3ad803c9" 1361 + checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" 1291 1362 dependencies = [ 1292 1363 "cfg-if", 1293 1364 "crunchy", 1365 + "zerocopy", 1294 1366 ] 1295 1367 1296 1368 [[package]] 1297 1369 name = "handlebars" 1298 - version = "6.3.2" 1370 + version = "6.4.0" 1299 1371 source = "registry+https://github.com/rust-lang/crates.io-index" 1300 - checksum = "759e2d5aea3287cb1190c8ec394f42866cb5bf74fcbf213f354e3c856ea26098" 1372 + checksum = "9b3f9296c208515b87bd915a2f5d1163d4b3f863ba83337d7713cf478055948e" 1301 1373 dependencies = [ 1302 1374 "derive_builder", 1303 1375 "log", ··· 1307 1379 "rust-embed", 1308 1380 "serde", 1309 1381 "serde_json", 1310 - "thiserror 2.0.14", 1382 + "thiserror 2.0.17", 1383 + ] 1384 + 1385 + [[package]] 1386 + name = "hash32" 1387 + version = "0.2.1" 1388 + source = "registry+https://github.com/rust-lang/crates.io-index" 1389 + checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67" 1390 + dependencies = [ 1391 + "byteorder", 1311 1392 ] 1312 1393 1313 1394 [[package]] ··· 1334 1415 dependencies = [ 1335 1416 "allocator-api2", 1336 1417 "equivalent", 1337 - "foldhash", 1418 + "foldhash 0.1.5", 1419 + ] 1420 + 1421 + [[package]] 1422 + name = "hashbrown" 1423 + version = "0.16.1" 1424 + source = "registry+https://github.com/rust-lang/crates.io-index" 1425 + checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" 1426 + dependencies = [ 1427 + "allocator-api2", 1428 + "equivalent", 1429 + "foldhash 0.2.0", 1338 1430 ] 1339 1431 1340 1432 [[package]] ··· 1347 1439 ] 1348 1440 1349 1441 [[package]] 1442 + name = "heapless" 1443 + version = "0.7.17" 1444 + source = "registry+https://github.com/rust-lang/crates.io-index" 1445 + checksum = "cdc6457c0eb62c71aac4bc17216026d8410337c4126773b9c5daba343f17964f" 1446 + dependencies = [ 1447 + "atomic-polyfill", 1448 + "hash32", 1449 + "rustc_version", 1450 + "serde", 1451 + "spin 0.9.8", 1452 + "stable_deref_trait", 1453 + ] 1454 + 1455 + [[package]] 1350 1456 name = "heck" 1351 1457 version = "0.4.1" 1352 1458 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1390 1496 1391 1497 [[package]] 1392 1498 name = "home" 1393 - version = "0.5.11" 1499 + version = "0.5.12" 1394 1500 source = "registry+https://github.com/rust-lang/crates.io-index" 1395 - checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" 1501 + checksum = "cc627f471c528ff0c4a49e1d5e60450c8f6461dd6d10ba9dcd3a61d3dff7728d" 1396 1502 dependencies = [ 1397 - "windows-sys 0.59.0", 1503 + "windows-sys 0.61.2", 1398 1504 ] 1399 1505 1400 1506 [[package]] ··· 1408 1514 1409 1515 [[package]] 1410 1516 name = "http" 1411 - version = "1.3.1" 1517 + version = "1.4.0" 1412 1518 source = "registry+https://github.com/rust-lang/crates.io-index" 1413 - checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" 1519 + checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" 1414 1520 dependencies = [ 1415 1521 "bytes", 1416 - "fnv", 1417 1522 "itoa", 1418 1523 ] 1419 1524 ··· 1454 1559 1455 1560 [[package]] 1456 1561 name = "hyper" 1457 - version = "1.6.0" 1562 + version = "1.8.1" 1458 1563 source = "registry+https://github.com/rust-lang/crates.io-index" 1459 - checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80" 1564 + checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11" 1460 1565 dependencies = [ 1566 + "atomic-waker", 1461 1567 "bytes", 1462 1568 "futures-channel", 1463 - "futures-util", 1569 + "futures-core", 1464 1570 "h2", 1465 1571 "http", 1466 1572 "http-body", ··· 1468 1574 "httpdate", 1469 1575 "itoa", 1470 1576 "pin-project-lite", 1577 + "pin-utils", 1471 1578 "smallvec", 1472 1579 "tokio", 1473 1580 "want", ··· 1487 1594 "tokio", 1488 1595 "tokio-rustls", 1489 1596 "tower-service", 1490 - "webpki-roots 1.0.2", 1597 + "webpki-roots 1.0.5", 1491 1598 ] 1492 1599 1493 1600 [[package]] ··· 1505 1612 1506 1613 [[package]] 1507 1614 name = "hyper-util" 1508 - version = "0.1.16" 1615 + version = "0.1.19" 1509 1616 source = "registry+https://github.com/rust-lang/crates.io-index" 1510 - checksum = "8d9b05277c7e8da2c93a568989bb6207bef0112e8d17df7a6eda4a3cf143bc5e" 1617 + checksum = "727805d60e7938b76b826a6ef209eb70eaa1812794f9424d4a4e2d740662df5f" 1511 1618 dependencies = [ 1512 1619 "base64", 1513 1620 "bytes", ··· 1531 1638 1532 1639 [[package]] 1533 1640 name = "iana-time-zone" 1534 - version = "0.1.63" 1641 + version = "0.1.64" 1535 1642 source = "registry+https://github.com/rust-lang/crates.io-index" 1536 - checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8" 1643 + checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb" 1537 1644 dependencies = [ 1538 1645 "android_system_properties", 1539 1646 "core-foundation-sys", ··· 1555 1662 1556 1663 [[package]] 1557 1664 name = "icu_collections" 1558 - version = "2.0.0" 1665 + version = "2.1.1" 1559 1666 source = "registry+https://github.com/rust-lang/crates.io-index" 1560 - checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" 1667 + checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" 1561 1668 dependencies = [ 1562 1669 "displaydoc", 1563 1670 "potential_utf", ··· 1568 1675 1569 1676 [[package]] 1570 1677 name = "icu_locale_core" 1571 - version = "2.0.0" 1678 + version = "2.1.1" 1572 1679 source = "registry+https://github.com/rust-lang/crates.io-index" 1573 - checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" 1680 + checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" 1574 1681 dependencies = [ 1575 1682 "displaydoc", 1576 1683 "litemap", ··· 1581 1688 1582 1689 [[package]] 1583 1690 name = "icu_normalizer" 1584 - version = "2.0.0" 1691 + version = "2.1.1" 1585 1692 source = "registry+https://github.com/rust-lang/crates.io-index" 1586 - checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" 1693 + checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" 1587 1694 dependencies = [ 1588 - "displaydoc", 1589 1695 "icu_collections", 1590 1696 "icu_normalizer_data", 1591 1697 "icu_properties", ··· 1596 1702 1597 1703 [[package]] 1598 1704 name = "icu_normalizer_data" 1599 - version = "2.0.0" 1705 + version = "2.1.1" 1600 1706 source = "registry+https://github.com/rust-lang/crates.io-index" 1601 - checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" 1707 + checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" 1602 1708 1603 1709 [[package]] 1604 1710 name = "icu_properties" 1605 - version = "2.0.1" 1711 + version = "2.1.2" 1606 1712 source = "registry+https://github.com/rust-lang/crates.io-index" 1607 - checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" 1713 + checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" 1608 1714 dependencies = [ 1609 - "displaydoc", 1610 1715 "icu_collections", 1611 1716 "icu_locale_core", 1612 1717 "icu_properties_data", 1613 1718 "icu_provider", 1614 - "potential_utf", 1615 1719 "zerotrie", 1616 1720 "zerovec", 1617 1721 ] 1618 1722 1619 1723 [[package]] 1620 1724 name = "icu_properties_data" 1621 - version = "2.0.1" 1725 + version = "2.1.2" 1622 1726 source = "registry+https://github.com/rust-lang/crates.io-index" 1623 - checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" 1727 + checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" 1624 1728 1625 1729 [[package]] 1626 1730 name = "icu_provider" 1627 - version = "2.0.0" 1731 + version = "2.1.1" 1628 1732 source = "registry+https://github.com/rust-lang/crates.io-index" 1629 - checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" 1733 + checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" 1630 1734 dependencies = [ 1631 1735 "displaydoc", 1632 1736 "icu_locale_core", 1633 - "stable_deref_trait", 1634 - "tinystr", 1635 1737 "writeable", 1636 1738 "yoke", 1637 1739 "zerofrom", ··· 1647 1749 1648 1750 [[package]] 1649 1751 name = "idna" 1650 - version = "1.0.3" 1752 + version = "1.1.0" 1651 1753 source = "registry+https://github.com/rust-lang/crates.io-index" 1652 - checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" 1754 + checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" 1653 1755 dependencies = [ 1654 1756 "idna_adapter", 1655 1757 "smallvec", ··· 1679 1781 1680 1782 [[package]] 1681 1783 name = "indexmap" 1682 - version = "2.10.0" 1784 + version = "2.12.1" 1683 1785 source = "registry+https://github.com/rust-lang/crates.io-index" 1684 - checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" 1786 + checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2" 1685 1787 dependencies = [ 1686 1788 "equivalent", 1687 - "hashbrown 0.15.5", 1789 + "hashbrown 0.16.1", 1688 1790 "serde", 1791 + "serde_core", 1689 1792 ] 1690 1793 1691 1794 [[package]] ··· 1716 1819 ] 1717 1820 1718 1821 [[package]] 1719 - name = "io-uring" 1720 - version = "0.7.9" 1721 - source = "registry+https://github.com/rust-lang/crates.io-index" 1722 - checksum = "d93587f37623a1a17d94ef2bc9ada592f5465fe7732084ab7beefabe5c77c0c4" 1723 - dependencies = [ 1724 - "bitflags", 1725 - "cfg-if", 1726 - "libc", 1727 - ] 1728 - 1729 - [[package]] 1730 1822 name = "ipld-core" 1731 1823 version = "0.4.2" 1732 1824 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1745 1837 1746 1838 [[package]] 1747 1839 name = "iri-string" 1748 - version = "0.7.9" 1840 + version = "0.7.10" 1749 1841 source = "registry+https://github.com/rust-lang/crates.io-index" 1750 - checksum = "4f867b9d1d896b67beb18518eda36fdb77a32ea590de864f1325b294a6d14397" 1842 + checksum = "c91338f0783edbd6195decb37bae672fd3b165faffb89bf7b9e6942f8b1a731a" 1751 1843 dependencies = [ 1752 1844 "memchr", 1753 1845 "serde", 1754 1846 ] 1755 1847 1756 1848 [[package]] 1757 - name = "itertools" 1758 - version = "0.12.1" 1759 - source = "registry+https://github.com/rust-lang/crates.io-index" 1760 - checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" 1761 - dependencies = [ 1762 - "either", 1763 - ] 1764 - 1765 - [[package]] 1766 1849 name = "itoa" 1767 - version = "1.0.15" 1850 + version = "1.0.17" 1768 1851 source = "registry+https://github.com/rust-lang/crates.io-index" 1769 - checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" 1852 + checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" 1770 1853 1771 1854 [[package]] 1772 1855 name = "jacquard-api" 1773 - version = "0.9.2" 1856 + version = "0.9.5" 1774 1857 source = "registry+https://github.com/rust-lang/crates.io-index" 1775 - checksum = "bbbfd6e2b10fa1731f4d4e40c8f791956b0d4f804fb3efef891afec903f20597" 1858 + checksum = "4979fb1848c1dd7ac8fd12745bc71f56f6da61374407d5f9b06005467a954e5a" 1776 1859 dependencies = [ 1777 1860 "bon", 1778 1861 "bytes", ··· 1782 1865 "miette", 1783 1866 "rustversion", 1784 1867 "serde", 1868 + "serde_bytes", 1785 1869 "serde_ipld_dagcbor", 1786 - "thiserror 2.0.14", 1870 + "thiserror 2.0.17", 1787 1871 "unicode-segmentation", 1788 1872 ] 1789 1873 1790 1874 [[package]] 1791 1875 name = "jacquard-common" 1792 - version = "0.9.2" 1876 + version = "0.9.5" 1793 1877 source = "registry+https://github.com/rust-lang/crates.io-index" 1794 - checksum = "df86cb117d9f1c2b0251ba67c3f0e3f963fd22abc6cf8de0e02a7fc846c288ca" 1878 + checksum = "1751921e0bdae5e0077afade6161545e9ef7698306c868f800916e99ecbcaae9" 1795 1879 dependencies = [ 1796 1880 "base64", 1797 1881 "bon", ··· 1809 1893 "multihash", 1810 1894 "ouroboros", 1811 1895 "p256", 1896 + "postcard", 1812 1897 "rand 0.9.2", 1813 1898 "regex", 1814 1899 "regex-lite", 1815 1900 "reqwest", 1816 1901 "serde", 1902 + "serde_bytes", 1817 1903 "serde_html_form", 1818 1904 "serde_ipld_dagcbor", 1819 1905 "serde_json", 1820 1906 "signature", 1821 1907 "smol_str", 1822 - "thiserror 2.0.14", 1908 + "thiserror 2.0.17", 1823 1909 "tokio", 1824 1910 "tokio-util", 1825 1911 "trait-variant", ··· 1828 1914 1829 1915 [[package]] 1830 1916 name = "jacquard-derive" 1831 - version = "0.9.2" 1917 + version = "0.9.5" 1832 1918 source = "registry+https://github.com/rust-lang/crates.io-index" 1833 - checksum = "42ca61a69dc7aa8fb2d7163416514ff7df5d79f2e8b22e269f4610afa85572fe" 1919 + checksum = "9c8d73dfee07943fdab93569ed1c28b06c6921ed891c08b415c4a323ff67e593" 1834 1920 dependencies = [ 1835 1921 "heck 0.5.0", 1836 1922 "jacquard-lexicon", 1837 1923 "proc-macro2", 1838 1924 "quote", 1839 - "syn 2.0.105", 1925 + "syn 2.0.112", 1840 1926 ] 1841 1927 1842 1928 [[package]] 1843 1929 name = "jacquard-identity" 1844 - version = "0.9.2" 1930 + version = "0.9.5" 1845 1931 source = "registry+https://github.com/rust-lang/crates.io-index" 1846 - checksum = "1ef714cacebfca486558a9f8e205daf466bfba0466c4d0c450fd6d0252400a53" 1932 + checksum = "e7aaefa819fa4213cf59f180dba932f018a7cd0599582fd38474ee2a38c16cf2" 1847 1933 dependencies = [ 1848 1934 "bon", 1849 1935 "bytes", ··· 1852 1938 "jacquard-common", 1853 1939 "jacquard-lexicon", 1854 1940 "miette", 1941 + "n0-future", 1855 1942 "percent-encoding", 1856 1943 "reqwest", 1857 1944 "serde", 1858 1945 "serde_html_form", 1859 1946 "serde_json", 1860 - "thiserror 2.0.14", 1947 + "thiserror 2.0.17", 1861 1948 "tokio", 1862 1949 "trait-variant", 1863 1950 "url", ··· 1866 1953 1867 1954 [[package]] 1868 1955 name = "jacquard-lexicon" 1869 - version = "0.9.2" 1956 + version = "0.9.5" 1870 1957 source = "registry+https://github.com/rust-lang/crates.io-index" 1871 - checksum = "de87f2c938faea1b1f1b32d5b9e0c870e7b5bb5efbf96e3692ae2d8f6b2beb7a" 1958 + checksum = "8411aff546569b0a1e0ef669bed2380cec1c00d48f02f3fcd57a71545321b3d8" 1872 1959 dependencies = [ 1873 1960 "cid", 1874 1961 "dashmap", ··· 1886 1973 "serde_repr", 1887 1974 "serde_with", 1888 1975 "sha2", 1889 - "syn 2.0.105", 1890 - "thiserror 2.0.14", 1976 + "syn 2.0.112", 1977 + "thiserror 2.0.17", 1891 1978 "unicode-segmentation", 1892 1979 ] 1893 1980 1894 1981 [[package]] 1895 1982 name = "jobserver" 1896 - version = "0.1.33" 1983 + version = "0.1.34" 1897 1984 source = "registry+https://github.com/rust-lang/crates.io-index" 1898 - checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a" 1985 + checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" 1899 1986 dependencies = [ 1900 1987 "getrandom 0.3.4", 1901 1988 "libc", ··· 1914 2001 "regex", 1915 2002 "serde", 1916 2003 "serde_json", 1917 - "thiserror 2.0.14", 2004 + "thiserror 2.0.17", 1918 2005 "time", 1919 2006 ] 1920 2007 1921 2008 [[package]] 1922 2009 name = "js-sys" 1923 - version = "0.3.77" 2010 + version = "0.3.83" 1924 2011 source = "registry+https://github.com/rust-lang/crates.io-index" 1925 - checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" 2012 + checksum = "464a3709c7f55f1f721e5389aa6ea4e3bc6aba669353300af094b29ffbdde1d8" 1926 2013 dependencies = [ 1927 2014 "once_cell", 1928 2015 "wasm-bindgen", ··· 1979 2066 source = "registry+https://github.com/rust-lang/crates.io-index" 1980 2067 checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" 1981 2068 dependencies = [ 1982 - "spin", 2069 + "spin 0.9.8", 1983 2070 ] 1984 2071 1985 2072 [[package]] 1986 - name = "lazycell" 1987 - version = "1.3.0" 1988 - source = "registry+https://github.com/rust-lang/crates.io-index" 1989 - checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" 1990 - 1991 - [[package]] 1992 2073 name = "lettre" 1993 - version = "0.11.18" 2074 + version = "0.11.19" 1994 2075 source = "registry+https://github.com/rust-lang/crates.io-index" 1995 - checksum = "5cb54db6ff7a89efac87dba5baeac57bb9ccd726b49a9b6f21fb92b3966aaf56" 2076 + checksum = "9e13e10e8818f8b2a60f52cb127041d388b89f3a96a62be9ceaffa22262fef7f" 1996 2077 dependencies = [ 1997 2078 "async-trait", 1998 2079 "base64", ··· 2013 2094 "tokio", 2014 2095 "tokio-rustls", 2015 2096 "url", 2016 - "webpki-roots 1.0.2", 2097 + "webpki-roots 1.0.5", 2017 2098 ] 2018 2099 2019 2100 [[package]] 2020 2101 name = "libc" 2021 - version = "0.2.175" 2022 - source = "registry+https://github.com/rust-lang/crates.io-index" 2023 - checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" 2024 - 2025 - [[package]] 2026 - name = "libloading" 2027 - version = "0.8.8" 2102 + version = "0.2.178" 2028 2103 source = "registry+https://github.com/rust-lang/crates.io-index" 2029 - checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667" 2030 - dependencies = [ 2031 - "cfg-if", 2032 - "windows-targets 0.52.6", 2033 - ] 2104 + checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091" 2034 2105 2035 2106 [[package]] 2036 2107 name = "libm" ··· 2040 2111 2041 2112 [[package]] 2042 2113 name = "libredox" 2043 - version = "0.1.9" 2114 + version = "0.1.12" 2044 2115 source = "registry+https://github.com/rust-lang/crates.io-index" 2045 - checksum = "391290121bad3d37fbddad76d8f5d1c1c314cfc646d143d7e07a3086ddff0ce3" 2116 + checksum = "3d0b95e02c851351f877147b7deea7b1afb1df71b63aa5f8270716e0c5720616" 2046 2117 dependencies = [ 2047 2118 "bitflags", 2048 2119 "libc", 2049 - "redox_syscall", 2120 + "redox_syscall 0.7.0", 2050 2121 ] 2051 2122 2052 2123 [[package]] ··· 2059 2130 "pkg-config", 2060 2131 "vcpkg", 2061 2132 ] 2062 - 2063 - [[package]] 2064 - name = "linux-raw-sys" 2065 - version = "0.4.15" 2066 - source = "registry+https://github.com/rust-lang/crates.io-index" 2067 - checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" 2068 2133 2069 2134 [[package]] 2070 2135 name = "litemap" 2071 - version = "0.8.0" 2136 + version = "0.8.1" 2072 2137 source = "registry+https://github.com/rust-lang/crates.io-index" 2073 - checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" 2138 + checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" 2074 2139 2075 2140 [[package]] 2076 2141 name = "lock_api" 2077 - version = "0.4.13" 2142 + version = "0.4.14" 2078 2143 source = "registry+https://github.com/rust-lang/crates.io-index" 2079 - checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765" 2144 + checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" 2080 2145 dependencies = [ 2081 - "autocfg", 2082 2146 "scopeguard", 2083 2147 ] 2084 2148 2085 2149 [[package]] 2086 2150 name = "log" 2087 - version = "0.4.27" 2151 + version = "0.4.29" 2088 2152 source = "registry+https://github.com/rust-lang/crates.io-index" 2089 - checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" 2153 + checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" 2154 + 2155 + [[package]] 2156 + name = "loom" 2157 + version = "0.7.2" 2158 + source = "registry+https://github.com/rust-lang/crates.io-index" 2159 + checksum = "419e0dc8046cb947daa77eb95ae174acfbddb7673b4151f56d1eed8e93fbfaca" 2160 + dependencies = [ 2161 + "cfg-if", 2162 + "generator", 2163 + "scoped-tls", 2164 + "tracing", 2165 + "tracing-subscriber", 2166 + ] 2090 2167 2091 2168 [[package]] 2092 2169 name = "lru-slab" ··· 2107 2184 2108 2185 [[package]] 2109 2186 name = "matchers" 2110 - version = "0.1.0" 2187 + version = "0.2.0" 2111 2188 source = "registry+https://github.com/rust-lang/crates.io-index" 2112 - checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" 2189 + checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9" 2113 2190 dependencies = [ 2114 - "regex-automata 0.1.10", 2191 + "regex-automata", 2115 2192 ] 2116 2193 2117 2194 [[package]] ··· 2132 2209 2133 2210 [[package]] 2134 2211 name = "memchr" 2135 - version = "2.7.5" 2212 + version = "2.7.6" 2136 2213 source = "registry+https://github.com/rust-lang/crates.io-index" 2137 - checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" 2214 + checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" 2138 2215 2139 2216 [[package]] 2140 2217 name = "miette" ··· 2155 2232 dependencies = [ 2156 2233 "proc-macro2", 2157 2234 "quote", 2158 - "syn 2.0.105", 2235 + "syn 2.0.112", 2159 2236 ] 2160 2237 2161 2238 [[package]] ··· 2182 2259 2183 2260 [[package]] 2184 2261 name = "mio" 2185 - version = "1.0.4" 2262 + version = "1.1.1" 2186 2263 source = "registry+https://github.com/rust-lang/crates.io-index" 2187 - checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" 2264 + checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" 2188 2265 dependencies = [ 2189 2266 "libc", 2190 2267 "wasi", 2191 - "windows-sys 0.59.0", 2268 + "windows-sys 0.61.2", 2192 2269 ] 2193 2270 2194 2271 [[package]] ··· 2215 2292 ] 2216 2293 2217 2294 [[package]] 2295 + name = "n0-future" 2296 + version = "0.1.3" 2297 + source = "registry+https://github.com/rust-lang/crates.io-index" 2298 + checksum = "7bb0e5d99e681ab3c938842b96fcb41bf8a7bb4bfdb11ccbd653a7e83e06c794" 2299 + dependencies = [ 2300 + "cfg_aliases", 2301 + "derive_more", 2302 + "futures-buffered", 2303 + "futures-lite", 2304 + "futures-util", 2305 + "js-sys", 2306 + "pin-project", 2307 + "send_wrapper", 2308 + "tokio", 2309 + "tokio-util", 2310 + "wasm-bindgen", 2311 + "wasm-bindgen-futures", 2312 + "web-time", 2313 + ] 2314 + 2315 + [[package]] 2218 2316 name = "nom" 2219 2317 version = "7.1.3" 2220 2318 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2247 2345 2248 2346 [[package]] 2249 2347 name = "nu-ansi-term" 2250 - version = "0.46.0" 2348 + version = "0.50.3" 2251 2349 source = "registry+https://github.com/rust-lang/crates.io-index" 2252 - checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" 2350 + checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" 2253 2351 dependencies = [ 2254 - "overload", 2255 - "winapi", 2352 + "windows-sys 0.59.0", 2256 2353 ] 2257 2354 2258 2355 [[package]] 2259 2356 name = "num-bigint-dig" 2260 - version = "0.8.4" 2357 + version = "0.8.6" 2261 2358 source = "registry+https://github.com/rust-lang/crates.io-index" 2262 - checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151" 2359 + checksum = "e661dda6640fad38e827a6d4a310ff4763082116fe217f279885c97f511bb0b7" 2263 2360 dependencies = [ 2264 - "byteorder", 2265 2361 "lazy_static", 2266 2362 "libm", 2267 2363 "num-integer", ··· 2325 2421 2326 2422 [[package]] 2327 2423 name = "object" 2328 - version = "0.36.7" 2424 + version = "0.32.2" 2329 2425 source = "registry+https://github.com/rust-lang/crates.io-index" 2330 - checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" 2426 + checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" 2331 2427 dependencies = [ 2332 2428 "memchr", 2333 2429 ] ··· 2361 2457 dependencies = [ 2362 2458 "proc-macro2", 2363 2459 "quote", 2364 - "syn 2.0.105", 2460 + "syn 2.0.112", 2365 2461 ] 2366 2462 2367 2463 [[package]] ··· 2397 2493 "proc-macro2", 2398 2494 "proc-macro2-diagnostics", 2399 2495 "quote", 2400 - "syn 2.0.105", 2496 + "syn 2.0.112", 2401 2497 ] 2402 2498 2403 2499 [[package]] 2404 - name = "overload" 2405 - version = "0.1.1" 2406 - source = "registry+https://github.com/rust-lang/crates.io-index" 2407 - checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" 2408 - 2409 - [[package]] 2410 2500 name = "p256" 2411 2501 version = "0.13.2" 2412 2502 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2426 2516 2427 2517 [[package]] 2428 2518 name = "parking_lot" 2429 - version = "0.12.4" 2519 + version = "0.12.5" 2430 2520 source = "registry+https://github.com/rust-lang/crates.io-index" 2431 - checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13" 2521 + checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" 2432 2522 dependencies = [ 2433 2523 "lock_api", 2434 2524 "parking_lot_core", ··· 2436 2526 2437 2527 [[package]] 2438 2528 name = "parking_lot_core" 2439 - version = "0.9.11" 2529 + version = "0.9.12" 2440 2530 source = "registry+https://github.com/rust-lang/crates.io-index" 2441 - checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" 2531 + checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" 2442 2532 dependencies = [ 2443 2533 "cfg-if", 2444 2534 "libc", 2445 - "redox_syscall", 2535 + "redox_syscall 0.5.18", 2446 2536 "smallvec", 2447 - "windows-targets 0.52.6", 2537 + "windows-link", 2448 2538 ] 2449 2539 2450 2540 [[package]] ··· 2477 2567 "axum", 2478 2568 "axum-template", 2479 2569 "chrono", 2570 + "dashmap", 2480 2571 "dotenvy", 2481 2572 "handlebars", 2482 2573 "hex", ··· 2498 2589 "sha2", 2499 2590 "sqlx", 2500 2591 "tokio", 2592 + "tower", 2501 2593 "tower-http", 2502 2594 "tower_governor", 2503 2595 "tracing", ··· 2516 2608 2517 2609 [[package]] 2518 2610 name = "percent-encoding" 2519 - version = "2.3.1" 2611 + version = "2.3.2" 2520 2612 source = "registry+https://github.com/rust-lang/crates.io-index" 2521 - checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" 2613 + checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" 2522 2614 2523 2615 [[package]] 2524 2616 name = "pest" 2525 - version = "2.8.1" 2617 + version = "2.8.4" 2526 2618 source = "registry+https://github.com/rust-lang/crates.io-index" 2527 - checksum = "1db05f56d34358a8b1066f67cbb203ee3e7ed2ba674a6263a1d5ec6db2204323" 2619 + checksum = "cbcfd20a6d4eeba40179f05735784ad32bdaef05ce8e8af05f180d45bb3e7e22" 2528 2620 dependencies = [ 2529 2621 "memchr", 2530 - "thiserror 2.0.14", 2531 2622 "ucd-trie", 2532 2623 ] 2533 2624 2534 2625 [[package]] 2535 2626 name = "pest_derive" 2536 - version = "2.8.1" 2627 + version = "2.8.4" 2537 2628 source = "registry+https://github.com/rust-lang/crates.io-index" 2538 - checksum = "bb056d9e8ea77922845ec74a1c4e8fb17e7c218cc4fc11a15c5d25e189aa40bc" 2629 + checksum = "51f72981ade67b1ca6adc26ec221be9f463f2b5839c7508998daa17c23d94d7f" 2539 2630 dependencies = [ 2540 2631 "pest", 2541 2632 "pest_generator", ··· 2543 2634 2544 2635 [[package]] 2545 2636 name = "pest_generator" 2546 - version = "2.8.1" 2637 + version = "2.8.4" 2547 2638 source = "registry+https://github.com/rust-lang/crates.io-index" 2548 - checksum = "87e404e638f781eb3202dc82db6760c8ae8a1eeef7fb3fa8264b2ef280504966" 2639 + checksum = "dee9efd8cdb50d719a80088b76f81aec7c41ed6d522ee750178f83883d271625" 2549 2640 dependencies = [ 2550 2641 "pest", 2551 2642 "pest_meta", 2552 2643 "proc-macro2", 2553 2644 "quote", 2554 - "syn 2.0.105", 2645 + "syn 2.0.112", 2555 2646 ] 2556 2647 2557 2648 [[package]] 2558 2649 name = "pest_meta" 2559 - version = "2.8.1" 2650 + version = "2.8.4" 2560 2651 source = "registry+https://github.com/rust-lang/crates.io-index" 2561 - checksum = "edd1101f170f5903fde0914f899bb503d9ff5271d7ba76bbb70bea63690cc0d5" 2652 + checksum = "bf1d70880e76bdc13ba52eafa6239ce793d85c8e43896507e43dd8984ff05b82" 2562 2653 dependencies = [ 2563 2654 "pest", 2564 2655 "sha2", ··· 2581 2672 dependencies = [ 2582 2673 "proc-macro2", 2583 2674 "quote", 2584 - "syn 2.0.105", 2675 + "syn 2.0.112", 2585 2676 ] 2586 2677 2587 2678 [[package]] ··· 2625 2716 2626 2717 [[package]] 2627 2718 name = "portable-atomic" 2628 - version = "1.11.1" 2719 + version = "1.13.0" 2629 2720 source = "registry+https://github.com/rust-lang/crates.io-index" 2630 - checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" 2721 + checksum = "f89776e4d69bb58bc6993e99ffa1d11f228b839984854c7daeb5d37f87cbe950" 2722 + 2723 + [[package]] 2724 + name = "postcard" 2725 + version = "1.1.3" 2726 + source = "registry+https://github.com/rust-lang/crates.io-index" 2727 + checksum = "6764c3b5dd454e283a30e6dfe78e9b31096d9e32036b5d1eaac7a6119ccb9a24" 2728 + dependencies = [ 2729 + "cobs", 2730 + "embedded-io 0.4.0", 2731 + "embedded-io 0.6.1", 2732 + "heapless", 2733 + "serde", 2734 + ] 2631 2735 2632 2736 [[package]] 2633 2737 name = "potential_utf" 2634 - version = "0.1.2" 2738 + version = "0.1.4" 2635 2739 source = "registry+https://github.com/rust-lang/crates.io-index" 2636 - checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585" 2740 + checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" 2637 2741 dependencies = [ 2638 2742 "zerovec", 2639 2743 ] ··· 2655 2759 2656 2760 [[package]] 2657 2761 name = "prettyplease" 2658 - version = "0.2.35" 2762 + version = "0.2.37" 2659 2763 source = "registry+https://github.com/rust-lang/crates.io-index" 2660 - checksum = "061c1221631e079b26479d25bbf2275bfe5917ae8419cd7e34f13bfc2aa7539a" 2764 + checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" 2661 2765 dependencies = [ 2662 2766 "proc-macro2", 2663 - "syn 2.0.105", 2767 + "syn 2.0.112", 2664 2768 ] 2665 2769 2666 2770 [[package]] ··· 2698 2802 2699 2803 [[package]] 2700 2804 name = "proc-macro2" 2701 - version = "1.0.97" 2805 + version = "1.0.104" 2702 2806 source = "registry+https://github.com/rust-lang/crates.io-index" 2703 - checksum = "d61789d7719defeb74ea5fe81f2fdfdbd28a803847077cecce2ff14e1472f6f1" 2807 + checksum = "9695f8df41bb4f3d222c95a67532365f569318332d03d5f3f67f37b20e6ebdf0" 2704 2808 dependencies = [ 2705 2809 "unicode-ident", 2706 2810 ] ··· 2713 2817 dependencies = [ 2714 2818 "proc-macro2", 2715 2819 "quote", 2716 - "syn 2.0.105", 2820 + "syn 2.0.112", 2717 2821 "version_check", 2718 2822 "yansi", 2719 2823 ] 2720 2824 2721 2825 [[package]] 2722 2826 name = "psm" 2723 - version = "0.1.26" 2827 + version = "0.1.28" 2724 2828 source = "registry+https://github.com/rust-lang/crates.io-index" 2725 - checksum = "6e944464ec8536cd1beb0bbfd96987eb5e3b72f2ecdafdc5c769a37f1fa2ae1f" 2829 + checksum = "d11f2fedc3b7dafdc2851bc52f277377c5473d378859be234bc7ebb593144d01" 2726 2830 dependencies = [ 2831 + "ar_archive_writer", 2727 2832 "cc", 2728 2833 ] 2729 2834 ··· 2753 2858 "pin-project-lite", 2754 2859 "quinn-proto", 2755 2860 "quinn-udp", 2756 - "rustc-hash 2.1.1", 2861 + "rustc-hash", 2757 2862 "rustls", 2758 2863 "socket2", 2759 - "thiserror 2.0.14", 2864 + "thiserror 2.0.17", 2760 2865 "tokio", 2761 2866 "tracing", 2762 2867 "web-time", ··· 2773 2878 "lru-slab", 2774 2879 "rand 0.9.2", 2775 2880 "ring", 2776 - "rustc-hash 2.1.1", 2881 + "rustc-hash", 2777 2882 "rustls", 2778 2883 "rustls-pki-types", 2779 2884 "slab", 2780 - "thiserror 2.0.14", 2885 + "thiserror 2.0.17", 2781 2886 "tinyvec", 2782 2887 "tracing", 2783 2888 "web-time", ··· 2799 2904 2800 2905 [[package]] 2801 2906 name = "quote" 2802 - version = "1.0.40" 2907 + version = "1.0.42" 2803 2908 source = "registry+https://github.com/rust-lang/crates.io-index" 2804 - checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" 2909 + checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" 2805 2910 dependencies = [ 2806 2911 "proc-macro2", 2807 2912 ] ··· 2885 2990 2886 2991 [[package]] 2887 2992 name = "raw-cpuid" 2888 - version = "11.5.0" 2993 + version = "11.6.0" 2994 + source = "registry+https://github.com/rust-lang/crates.io-index" 2995 + checksum = "498cd0dc59d73224351ee52a95fee0f1a617a2eae0e7d9d720cc622c73a54186" 2996 + dependencies = [ 2997 + "bitflags", 2998 + ] 2999 + 3000 + [[package]] 3001 + name = "redox_syscall" 3002 + version = "0.5.18" 2889 3003 source = "registry+https://github.com/rust-lang/crates.io-index" 2890 - checksum = "c6df7ab838ed27997ba19a4664507e6f82b41fe6e20be42929332156e5e85146" 3004 + checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" 2891 3005 dependencies = [ 2892 3006 "bitflags", 2893 3007 ] 2894 3008 2895 3009 [[package]] 2896 3010 name = "redox_syscall" 2897 - version = "0.5.17" 3011 + version = "0.7.0" 2898 3012 source = "registry+https://github.com/rust-lang/crates.io-index" 2899 - checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77" 3013 + checksum = "49f3fe0889e69e2ae9e41f4d6c4c0181701d00e4697b356fb1f74173a5e0ee27" 2900 3014 dependencies = [ 2901 3015 "bitflags", 2902 3016 ] ··· 2918 3032 dependencies = [ 2919 3033 "proc-macro2", 2920 3034 "quote", 2921 - "syn 2.0.105", 3035 + "syn 2.0.112", 2922 3036 ] 2923 3037 2924 3038 [[package]] ··· 2929 3043 dependencies = [ 2930 3044 "aho-corasick", 2931 3045 "memchr", 2932 - "regex-automata 0.4.13", 2933 - "regex-syntax 0.8.5", 2934 - ] 2935 - 2936 - [[package]] 2937 - name = "regex-automata" 2938 - version = "0.1.10" 2939 - source = "registry+https://github.com/rust-lang/crates.io-index" 2940 - checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" 2941 - dependencies = [ 2942 - "regex-syntax 0.6.29", 3046 + "regex-automata", 3047 + "regex-syntax", 2943 3048 ] 2944 3049 2945 3050 [[package]] ··· 2950 3055 dependencies = [ 2951 3056 "aho-corasick", 2952 3057 "memchr", 2953 - "regex-syntax 0.8.5", 3058 + "regex-syntax", 2954 3059 ] 2955 3060 2956 3061 [[package]] ··· 2961 3066 2962 3067 [[package]] 2963 3068 name = "regex-syntax" 2964 - version = "0.6.29" 3069 + version = "0.8.8" 2965 3070 source = "registry+https://github.com/rust-lang/crates.io-index" 2966 - checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" 2967 - 2968 - [[package]] 2969 - name = "regex-syntax" 2970 - version = "0.8.5" 2971 - source = "registry+https://github.com/rust-lang/crates.io-index" 2972 - checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" 3071 + checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" 2973 3072 2974 3073 [[package]] 2975 3074 name = "reqwest" 2976 - version = "0.12.24" 3075 + version = "0.12.28" 2977 3076 source = "registry+https://github.com/rust-lang/crates.io-index" 2978 - checksum = "9d0946410b9f7b082a427e4ef5c8ff541a88b357bc6c637c40db3a68ac70a36f" 3077 + checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" 2979 3078 dependencies = [ 2980 - "async-compression", 2981 3079 "base64", 2982 3080 "bytes", 2983 3081 "encoding_rs", 2984 3082 "futures-core", 2985 - "futures-util", 2986 3083 "h2", 2987 3084 "http", 2988 3085 "http-body", ··· 3004 3101 "sync_wrapper", 3005 3102 "tokio", 3006 3103 "tokio-rustls", 3007 - "tokio-util", 3008 3104 "tower", 3009 3105 "tower-http", 3010 3106 "tower-service", 3011 3107 "url", 3012 3108 "wasm-bindgen", 3013 3109 "wasm-bindgen-futures", 3014 - "wasm-streams", 3015 3110 "web-sys", 3016 - "webpki-roots 1.0.2", 3111 + "webpki-roots 1.0.5", 3017 3112 ] 3018 3113 3019 3114 [[package]] ··· 3042 3137 3043 3138 [[package]] 3044 3139 name = "rsa" 3045 - version = "0.9.8" 3140 + version = "0.9.9" 3046 3141 source = "registry+https://github.com/rust-lang/crates.io-index" 3047 - checksum = "78928ac1ed176a5ca1d17e578a1825f3d81ca54cf41053a592584b020cfd691b" 3142 + checksum = "40a0376c50d0358279d9d643e4bf7b7be212f1f4ff1da9070a7b54d22ef75c88" 3048 3143 dependencies = [ 3049 3144 "const-oid", 3050 3145 "digest", ··· 3062 3157 3063 3158 [[package]] 3064 3159 name = "rust-embed" 3065 - version = "8.7.2" 3160 + version = "8.9.0" 3066 3161 source = "registry+https://github.com/rust-lang/crates.io-index" 3067 - checksum = "025908b8682a26ba8d12f6f2d66b987584a4a87bc024abc5bbc12553a8cd178a" 3162 + checksum = "947d7f3fad52b283d261c4c99a084937e2fe492248cb9a68a8435a861b8798ca" 3068 3163 dependencies = [ 3069 3164 "rust-embed-impl", 3070 3165 "rust-embed-utils", ··· 3073 3168 3074 3169 [[package]] 3075 3170 name = "rust-embed-impl" 3076 - version = "8.7.2" 3171 + version = "8.9.0" 3077 3172 source = "registry+https://github.com/rust-lang/crates.io-index" 3078 - checksum = "6065f1a4392b71819ec1ea1df1120673418bf386f50de1d6f54204d836d4349c" 3173 + checksum = "5fa2c8c9e8711e10f9c4fd2d64317ef13feaab820a4c51541f1a8c8e2e851ab2" 3079 3174 dependencies = [ 3080 3175 "proc-macro2", 3081 3176 "quote", 3082 3177 "rust-embed-utils", 3083 - "syn 2.0.105", 3178 + "syn 2.0.112", 3084 3179 "walkdir", 3085 3180 ] 3086 3181 3087 3182 [[package]] 3088 3183 name = "rust-embed-utils" 3089 - version = "8.7.2" 3184 + version = "8.9.0" 3090 3185 source = "registry+https://github.com/rust-lang/crates.io-index" 3091 - checksum = "f6cc0c81648b20b70c491ff8cce00c1c3b223bb8ed2b5d41f0e54c6c4c0a3594" 3186 + checksum = "60b161f275cb337fe0a44d924a5f4df0ed69c2c39519858f931ce61c779d3475" 3092 3187 dependencies = [ 3093 3188 "globset", 3094 3189 "sha2", ··· 3096 3191 ] 3097 3192 3098 3193 [[package]] 3099 - name = "rustc-demangle" 3100 - version = "0.1.26" 3101 - source = "registry+https://github.com/rust-lang/crates.io-index" 3102 - checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" 3103 - 3104 - [[package]] 3105 - name = "rustc-hash" 3106 - version = "1.1.0" 3107 - source = "registry+https://github.com/rust-lang/crates.io-index" 3108 - checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" 3109 - 3110 - [[package]] 3111 3194 name = "rustc-hash" 3112 3195 version = "2.1.1" 3113 3196 source = "registry+https://github.com/rust-lang/crates.io-index" 3114 3197 checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" 3115 3198 3116 3199 [[package]] 3117 - name = "rustix" 3118 - version = "0.38.44" 3200 + name = "rustc_version" 3201 + version = "0.4.1" 3119 3202 source = "registry+https://github.com/rust-lang/crates.io-index" 3120 - checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" 3203 + checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" 3121 3204 dependencies = [ 3122 - "bitflags", 3123 - "errno", 3124 - "libc", 3125 - "linux-raw-sys", 3126 - "windows-sys 0.59.0", 3205 + "semver", 3127 3206 ] 3128 3207 3129 3208 [[package]] 3130 3209 name = "rustls" 3131 - version = "0.23.31" 3210 + version = "0.23.35" 3132 3211 source = "registry+https://github.com/rust-lang/crates.io-index" 3133 - checksum = "c0ebcbd2f03de0fc1122ad9bb24b127a5a6cd51d72604a3f3c50ac459762b6cc" 3212 + checksum = "533f54bc6a7d4f647e46ad909549eda97bf5afc1585190ef692b4286b198bd8f" 3134 3213 dependencies = [ 3135 3214 "aws-lc-rs", 3136 3215 "log", ··· 3144 3223 3145 3224 [[package]] 3146 3225 name = "rustls-pki-types" 3147 - version = "1.12.0" 3226 + version = "1.13.2" 3148 3227 source = "registry+https://github.com/rust-lang/crates.io-index" 3149 - checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79" 3228 + checksum = "21e6f2ab2928ca4291b86736a8bd920a277a399bba1589409d72154ff87c1282" 3150 3229 dependencies = [ 3151 3230 "web-time", 3152 3231 "zeroize", ··· 3154 3233 3155 3234 [[package]] 3156 3235 name = "rustls-webpki" 3157 - version = "0.103.4" 3236 + version = "0.103.8" 3158 3237 source = "registry+https://github.com/rust-lang/crates.io-index" 3159 - checksum = "0a17884ae0c1b773f1ccd2bd4a8c72f16da897310a98b0e84bf349ad5ead92fc" 3238 + checksum = "2ffdfa2f5286e2247234e03f680868ac2815974dc39e00ea15adc445d0aafe52" 3160 3239 dependencies = [ 3161 3240 "aws-lc-rs", 3162 3241 "ring", ··· 3172 3251 3173 3252 [[package]] 3174 3253 name = "ryu" 3175 - version = "1.0.20" 3254 + version = "1.0.22" 3176 3255 source = "registry+https://github.com/rust-lang/crates.io-index" 3177 - checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" 3256 + checksum = "a50f4cf475b65d88e057964e0e9bb1f0aa9bbb2036dc65c64596b42932536984" 3178 3257 3179 3258 [[package]] 3180 3259 name = "salsa20" ··· 3208 3287 3209 3288 [[package]] 3210 3289 name = "schemars" 3211 - version = "1.1.0" 3290 + version = "1.2.0" 3212 3291 source = "registry+https://github.com/rust-lang/crates.io-index" 3213 - checksum = "9558e172d4e8533736ba97870c4b2cd63f84b382a3d6eb063da41b91cce17289" 3292 + checksum = "54e910108742c57a770f492731f99be216a52fadd361b06c8fb59d74ccc267d2" 3214 3293 dependencies = [ 3215 3294 "dyn-clone", 3216 3295 "ref-cast", ··· 3219 3298 ] 3220 3299 3221 3300 [[package]] 3301 + name = "scoped-tls" 3302 + version = "1.0.1" 3303 + source = "registry+https://github.com/rust-lang/crates.io-index" 3304 + checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" 3305 + 3306 + [[package]] 3222 3307 name = "scopeguard" 3223 3308 version = "1.2.0" 3224 3309 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 3269 3354 ] 3270 3355 3271 3356 [[package]] 3357 + name = "semver" 3358 + version = "1.0.27" 3359 + source = "registry+https://github.com/rust-lang/crates.io-index" 3360 + checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" 3361 + 3362 + [[package]] 3363 + name = "send_wrapper" 3364 + version = "0.6.0" 3365 + source = "registry+https://github.com/rust-lang/crates.io-index" 3366 + checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" 3367 + 3368 + [[package]] 3272 3369 name = "serde" 3273 3370 version = "1.0.228" 3274 3371 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 3305 3402 dependencies = [ 3306 3403 "proc-macro2", 3307 3404 "quote", 3308 - "syn 2.0.105", 3405 + "syn 2.0.112", 3309 3406 ] 3310 3407 3311 3408 [[package]] ··· 3315 3412 checksum = "b2f2d7ff8a2140333718bb329f5c40fc5f0865b84c426183ce14c97d2ab8154f" 3316 3413 dependencies = [ 3317 3414 "form_urlencoded", 3318 - "indexmap 2.10.0", 3415 + "indexmap 2.12.1", 3319 3416 "itoa", 3320 3417 "ryu", 3321 3418 "serde_core", ··· 3335 3432 3336 3433 [[package]] 3337 3434 name = "serde_json" 3338 - version = "1.0.145" 3435 + version = "1.0.148" 3339 3436 source = "registry+https://github.com/rust-lang/crates.io-index" 3340 - checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" 3437 + checksum = "3084b546a1dd6289475996f182a22aba973866ea8e8b02c51d9f46b1336a22da" 3341 3438 dependencies = [ 3342 - "indexmap 2.10.0", 3439 + "indexmap 2.12.1", 3343 3440 "itoa", 3344 3441 "memchr", 3345 - "ryu", 3346 3442 "serde", 3347 3443 "serde_core", 3444 + "zmij", 3348 3445 ] 3349 3446 3350 3447 [[package]] 3351 3448 name = "serde_path_to_error" 3352 - version = "0.1.17" 3449 + version = "0.1.20" 3353 3450 source = "registry+https://github.com/rust-lang/crates.io-index" 3354 - checksum = "59fab13f937fa393d08645bf3a84bdfe86e296747b506ada67bb15f10f218b2a" 3451 + checksum = "10a9ff822e371bb5403e391ecd83e182e0e77ba7f6fe0160b795797109d1b457" 3355 3452 dependencies = [ 3356 3453 "itoa", 3357 3454 "serde", 3455 + "serde_core", 3358 3456 ] 3359 3457 3360 3458 [[package]] ··· 3365 3463 dependencies = [ 3366 3464 "proc-macro2", 3367 3465 "quote", 3368 - "syn 2.0.105", 3466 + "syn 2.0.112", 3369 3467 ] 3370 3468 3371 3469 [[package]] ··· 3382 3480 3383 3481 [[package]] 3384 3482 name = "serde_with" 3385 - version = "3.16.0" 3483 + version = "3.16.1" 3386 3484 source = "registry+https://github.com/rust-lang/crates.io-index" 3387 - checksum = "10574371d41b0d9b2cff89418eda27da52bcaff2cc8741db26382a77c29131f1" 3485 + checksum = "4fa237f2807440d238e0364a218270b98f767a00d3dada77b1c53ae88940e2e7" 3388 3486 dependencies = [ 3389 3487 "base64", 3390 3488 "chrono", 3391 3489 "hex", 3392 3490 "indexmap 1.9.3", 3393 - "indexmap 2.10.0", 3491 + "indexmap 2.12.1", 3394 3492 "schemars 0.9.0", 3395 - "schemars 1.1.0", 3493 + "schemars 1.2.0", 3396 3494 "serde_core", 3397 3495 "serde_json", 3398 3496 "serde_with_macros", ··· 3401 3499 3402 3500 [[package]] 3403 3501 name = "serde_with_macros" 3404 - version = "3.16.0" 3502 + version = "3.16.1" 3405 3503 source = "registry+https://github.com/rust-lang/crates.io-index" 3406 - checksum = "08a72d8216842fdd57820dc78d840bef99248e35fb2554ff923319e60f2d686b" 3504 + checksum = "52a8e3ca0ca629121f70ab50f95249e5a6f925cc0f6ffe8256c45b728875706c" 3407 3505 dependencies = [ 3408 3506 "darling 0.21.3", 3409 3507 "proc-macro2", 3410 3508 "quote", 3411 - "syn 2.0.105", 3509 + "syn 2.0.112", 3412 3510 ] 3413 3511 3414 3512 [[package]] ··· 3450 3548 3451 3549 [[package]] 3452 3550 name = "signal-hook-registry" 3453 - version = "1.4.6" 3551 + version = "1.4.8" 3454 3552 source = "registry+https://github.com/rust-lang/crates.io-index" 3455 - checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b" 3553 + checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" 3456 3554 dependencies = [ 3555 + "errno", 3457 3556 "libc", 3458 3557 ] 3459 3558 ··· 3469 3568 3470 3569 [[package]] 3471 3570 name = "simd-adler32" 3472 - version = "0.3.7" 3571 + version = "0.3.8" 3473 3572 source = "registry+https://github.com/rust-lang/crates.io-index" 3474 - checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" 3573 + checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" 3475 3574 3476 3575 [[package]] 3477 3576 name = "slab" ··· 3500 3599 3501 3600 [[package]] 3502 3601 name = "socket2" 3503 - version = "0.6.0" 3602 + version = "0.6.1" 3504 3603 source = "registry+https://github.com/rust-lang/crates.io-index" 3505 - checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807" 3604 + checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881" 3506 3605 dependencies = [ 3507 3606 "libc", 3508 - "windows-sys 0.59.0", 3607 + "windows-sys 0.60.2", 3509 3608 ] 3510 3609 3511 3610 [[package]] ··· 3518 3617 ] 3519 3618 3520 3619 [[package]] 3620 + name = "spin" 3621 + version = "0.10.0" 3622 + source = "registry+https://github.com/rust-lang/crates.io-index" 3623 + checksum = "d5fe4ccb98d9c292d56fec89a5e07da7fc4cf0dc11e156b41793132775d3e591" 3624 + 3625 + [[package]] 3521 3626 name = "spinning_top" 3522 3627 version = "0.3.0" 3523 3628 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 3568 3673 "futures-util", 3569 3674 "hashbrown 0.15.5", 3570 3675 "hashlink", 3571 - "indexmap 2.10.0", 3676 + "indexmap 2.12.1", 3572 3677 "log", 3573 3678 "memchr", 3574 3679 "once_cell", ··· 3578 3683 "serde_json", 3579 3684 "sha2", 3580 3685 "smallvec", 3581 - "thiserror 2.0.14", 3686 + "thiserror 2.0.17", 3582 3687 "tokio", 3583 3688 "tokio-stream", 3584 3689 "tracing", ··· 3596 3701 "quote", 3597 3702 "sqlx-core", 3598 3703 "sqlx-macros-core", 3599 - "syn 2.0.105", 3704 + "syn 2.0.112", 3600 3705 ] 3601 3706 3602 3707 [[package]] ··· 3619 3724 "sqlx-mysql", 3620 3725 "sqlx-postgres", 3621 3726 "sqlx-sqlite", 3622 - "syn 2.0.105", 3727 + "syn 2.0.112", 3623 3728 "tokio", 3624 3729 "url", 3625 3730 ] ··· 3662 3767 "smallvec", 3663 3768 "sqlx-core", 3664 3769 "stringprep", 3665 - "thiserror 2.0.14", 3770 + "thiserror 2.0.17", 3666 3771 "tracing", 3667 3772 "whoami", 3668 3773 ] ··· 3700 3805 "smallvec", 3701 3806 "sqlx-core", 3702 3807 "stringprep", 3703 - "thiserror 2.0.14", 3808 + "thiserror 2.0.17", 3704 3809 "tracing", 3705 3810 "whoami", 3706 3811 ] ··· 3725 3830 "serde", 3726 3831 "serde_urlencoded", 3727 3832 "sqlx-core", 3728 - "thiserror 2.0.14", 3833 + "thiserror 2.0.17", 3729 3834 "tracing", 3730 3835 "url", 3731 3836 ] 3732 3837 3733 3838 [[package]] 3734 3839 name = "stable_deref_trait" 3735 - version = "1.2.0" 3840 + version = "1.2.1" 3736 3841 source = "registry+https://github.com/rust-lang/crates.io-index" 3737 - checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" 3842 + checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" 3738 3843 3739 3844 [[package]] 3740 3845 name = "stacker" 3741 - version = "0.1.21" 3846 + version = "0.1.22" 3742 3847 source = "registry+https://github.com/rust-lang/crates.io-index" 3743 - checksum = "cddb07e32ddb770749da91081d8d0ac3a16f1a569a18b20348cd371f5dead06b" 3848 + checksum = "e1f8b29fb42aafcea4edeeb6b2f2d7ecd0d969c48b4cf0d2e64aafc471dd6e59" 3744 3849 dependencies = [ 3745 3850 "cc", 3746 3851 "cfg-if", ··· 3765 3870 "quote", 3766 3871 "serde", 3767 3872 "sha2", 3768 - "syn 2.0.105", 3873 + "syn 2.0.112", 3769 3874 "thiserror 1.0.69", 3770 3875 ] 3771 3876 ··· 3811 3916 3812 3917 [[package]] 3813 3918 name = "syn" 3814 - version = "2.0.105" 3919 + version = "2.0.112" 3815 3920 source = "registry+https://github.com/rust-lang/crates.io-index" 3816 - checksum = "7bc3fcb250e53458e712715cf74285c1f889686520d79294a9ef3bd7aa1fc619" 3921 + checksum = "21f182278bf2d2bcb3c88b1b08a37df029d71ce3d3ae26168e3c653b213b99d4" 3817 3922 dependencies = [ 3818 3923 "proc-macro2", 3819 3924 "quote", ··· 3837 3942 dependencies = [ 3838 3943 "proc-macro2", 3839 3944 "quote", 3840 - "syn 2.0.105", 3945 + "syn 2.0.112", 3841 3946 ] 3842 3947 3843 3948 [[package]] ··· 3872 3977 3873 3978 [[package]] 3874 3979 name = "thiserror" 3875 - version = "2.0.14" 3980 + version = "2.0.17" 3876 3981 source = "registry+https://github.com/rust-lang/crates.io-index" 3877 - checksum = "0b0949c3a6c842cbde3f1686d6eea5a010516deb7085f79db747562d4102f41e" 3982 + checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" 3878 3983 dependencies = [ 3879 - "thiserror-impl 2.0.14", 3984 + "thiserror-impl 2.0.17", 3880 3985 ] 3881 3986 3882 3987 [[package]] ··· 3887 3992 dependencies = [ 3888 3993 "proc-macro2", 3889 3994 "quote", 3890 - "syn 2.0.105", 3995 + "syn 2.0.112", 3891 3996 ] 3892 3997 3893 3998 [[package]] 3894 3999 name = "thiserror-impl" 3895 - version = "2.0.14" 4000 + version = "2.0.17" 3896 4001 source = "registry+https://github.com/rust-lang/crates.io-index" 3897 - checksum = "cc5b44b4ab9c2fdd0e0512e6bece8388e214c0749f5862b114cc5b7a25daf227" 4002 + checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" 3898 4003 dependencies = [ 3899 4004 "proc-macro2", 3900 4005 "quote", 3901 - "syn 2.0.105", 4006 + "syn 2.0.112", 3902 4007 ] 3903 4008 3904 4009 [[package]] ··· 3943 4048 3944 4049 [[package]] 3945 4050 name = "tinystr" 3946 - version = "0.8.1" 4051 + version = "0.8.2" 3947 4052 source = "registry+https://github.com/rust-lang/crates.io-index" 3948 - checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" 4053 + checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" 3949 4054 dependencies = [ 3950 4055 "displaydoc", 3951 4056 "zerovec", ··· 3953 4058 3954 4059 [[package]] 3955 4060 name = "tinyvec" 3956 - version = "1.9.0" 4061 + version = "1.10.0" 3957 4062 source = "registry+https://github.com/rust-lang/crates.io-index" 3958 - checksum = "09b3661f17e86524eccd4371ab0429194e0d7c008abb45f7a7495b1719463c71" 4063 + checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" 3959 4064 dependencies = [ 3960 4065 "tinyvec_macros", 3961 4066 ] ··· 3968 4073 3969 4074 [[package]] 3970 4075 name = "tokio" 3971 - version = "1.47.1" 4076 + version = "1.48.0" 3972 4077 source = "registry+https://github.com/rust-lang/crates.io-index" 3973 - checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038" 4078 + checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408" 3974 4079 dependencies = [ 3975 - "backtrace", 3976 4080 "bytes", 3977 - "io-uring", 3978 4081 "libc", 3979 4082 "mio", 3980 4083 "pin-project-lite", 3981 4084 "signal-hook-registry", 3982 - "slab", 3983 4085 "socket2", 3984 4086 "tokio-macros", 3985 - "windows-sys 0.59.0", 4087 + "windows-sys 0.61.2", 3986 4088 ] 3987 4089 3988 4090 [[package]] 3989 4091 name = "tokio-macros" 3990 - version = "2.5.0" 4092 + version = "2.6.0" 3991 4093 source = "registry+https://github.com/rust-lang/crates.io-index" 3992 - checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" 4094 + checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" 3993 4095 dependencies = [ 3994 4096 "proc-macro2", 3995 4097 "quote", 3996 - "syn 2.0.105", 4098 + "syn 2.0.112", 3997 4099 ] 3998 4100 3999 4101 [[package]] 4000 4102 name = "tokio-rustls" 4001 - version = "0.26.2" 4103 + version = "0.26.4" 4002 4104 source = "registry+https://github.com/rust-lang/crates.io-index" 4003 - checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b" 4105 + checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" 4004 4106 dependencies = [ 4005 4107 "rustls", 4006 4108 "tokio", ··· 4026 4128 "bytes", 4027 4129 "futures-core", 4028 4130 "futures-sink", 4131 + "futures-util", 4029 4132 "pin-project-lite", 4030 4133 "tokio", 4031 4134 ] 4032 4135 4033 4136 [[package]] 4034 4137 name = "tonic" 4035 - version = "0.14.1" 4138 + version = "0.14.2" 4036 4139 source = "registry+https://github.com/rust-lang/crates.io-index" 4037 - checksum = "67ac5a8627ada0968acec063a4746bf79588aa03ccb66db2f75d7dce26722a40" 4140 + checksum = "eb7613188ce9f7df5bfe185db26c5814347d110db17920415cf2fbcad85e7203" 4038 4141 dependencies = [ 4039 4142 "async-trait", 4040 4143 "axum", ··· 4067 4170 dependencies = [ 4068 4171 "futures-core", 4069 4172 "futures-util", 4070 - "indexmap 2.10.0", 4173 + "indexmap 2.12.1", 4071 4174 "pin-project-lite", 4072 4175 "slab", 4073 4176 "sync_wrapper", ··· 4080 4183 4081 4184 [[package]] 4082 4185 name = "tower-http" 4083 - version = "0.6.6" 4186 + version = "0.6.8" 4084 4187 source = "registry+https://github.com/rust-lang/crates.io-index" 4085 - checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" 4188 + checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" 4086 4189 dependencies = [ 4087 4190 "async-compression", 4088 4191 "bitflags", ··· 4091 4194 "futures-util", 4092 4195 "http", 4093 4196 "http-body", 4197 + "http-body-util", 4094 4198 "iri-string", 4095 4199 "pin-project-lite", 4096 4200 "tokio", ··· 4123 4227 "governor", 4124 4228 "http", 4125 4229 "pin-project", 4126 - "thiserror 2.0.14", 4230 + "thiserror 2.0.17", 4127 4231 "tonic", 4128 4232 "tower", 4129 4233 "tracing", ··· 4131 4235 4132 4236 [[package]] 4133 4237 name = "tracing" 4134 - version = "0.1.41" 4238 + version = "0.1.44" 4135 4239 source = "registry+https://github.com/rust-lang/crates.io-index" 4136 - checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" 4240 + checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" 4137 4241 dependencies = [ 4138 4242 "log", 4139 4243 "pin-project-lite", ··· 4143 4247 4144 4248 [[package]] 4145 4249 name = "tracing-attributes" 4146 - version = "0.1.30" 4250 + version = "0.1.31" 4147 4251 source = "registry+https://github.com/rust-lang/crates.io-index" 4148 - checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" 4252 + checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" 4149 4253 dependencies = [ 4150 4254 "proc-macro2", 4151 4255 "quote", 4152 - "syn 2.0.105", 4256 + "syn 2.0.112", 4153 4257 ] 4154 4258 4155 4259 [[package]] 4156 4260 name = "tracing-core" 4157 - version = "0.1.34" 4261 + version = "0.1.36" 4158 4262 source = "registry+https://github.com/rust-lang/crates.io-index" 4159 - checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" 4263 + checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" 4160 4264 dependencies = [ 4161 4265 "once_cell", 4162 4266 "valuable", ··· 4175 4279 4176 4280 [[package]] 4177 4281 name = "tracing-subscriber" 4178 - version = "0.3.19" 4282 + version = "0.3.22" 4179 4283 source = "registry+https://github.com/rust-lang/crates.io-index" 4180 - checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" 4284 + checksum = "2f30143827ddab0d256fd843b7a66d164e9f271cfa0dde49142c5ca0ca291f1e" 4181 4285 dependencies = [ 4182 4286 "matchers", 4183 4287 "nu-ansi-term", 4184 4288 "once_cell", 4185 - "regex", 4289 + "regex-automata", 4186 4290 "sharded-slab", 4187 4291 "smallvec", 4188 4292 "thread_local", ··· 4199 4303 dependencies = [ 4200 4304 "proc-macro2", 4201 4305 "quote", 4202 - "syn 2.0.105", 4306 + "syn 2.0.112", 4203 4307 ] 4204 4308 4205 4309 [[package]] ··· 4210 4314 4211 4315 [[package]] 4212 4316 name = "typenum" 4213 - version = "1.18.0" 4317 + version = "1.19.0" 4214 4318 source = "registry+https://github.com/rust-lang/crates.io-index" 4215 - checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" 4319 + checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" 4216 4320 4217 4321 [[package]] 4218 4322 name = "ucd-trie" ··· 4228 4332 4229 4333 [[package]] 4230 4334 name = "unicode-ident" 4231 - version = "1.0.18" 4335 + version = "1.0.22" 4232 4336 source = "registry+https://github.com/rust-lang/crates.io-index" 4233 - checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" 4337 + checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" 4234 4338 4235 4339 [[package]] 4236 4340 name = "unicode-normalization" 4237 - version = "0.1.24" 4341 + version = "0.1.25" 4238 4342 source = "registry+https://github.com/rust-lang/crates.io-index" 4239 - checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" 4343 + checksum = "5fd4f6878c9cb28d874b009da9e8d183b5abc80117c40bbd187a1fde336be6e8" 4240 4344 dependencies = [ 4241 4345 "tinyvec", 4242 4346 ] 4243 4347 4244 4348 [[package]] 4245 4349 name = "unicode-properties" 4246 - version = "0.1.3" 4350 + version = "0.1.4" 4247 4351 source = "registry+https://github.com/rust-lang/crates.io-index" 4248 - checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0" 4352 + checksum = "7df058c713841ad818f1dc5d3fd88063241cc61f49f5fbea4b951e8cf5a8d71d" 4249 4353 4250 4354 [[package]] 4251 4355 name = "unicode-segmentation" ··· 4260 4364 checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" 4261 4365 4262 4366 [[package]] 4367 + name = "unicode-xid" 4368 + version = "0.2.6" 4369 + source = "registry+https://github.com/rust-lang/crates.io-index" 4370 + checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" 4371 + 4372 + [[package]] 4263 4373 name = "unsigned-varint" 4264 4374 version = "0.8.0" 4265 4375 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 4279 4389 4280 4390 [[package]] 4281 4391 name = "url" 4282 - version = "2.5.4" 4392 + version = "2.5.7" 4283 4393 source = "registry+https://github.com/rust-lang/crates.io-index" 4284 - checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" 4394 + checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b" 4285 4395 dependencies = [ 4286 4396 "form_urlencoded", 4287 4397 "idna", ··· 4367 4477 4368 4478 [[package]] 4369 4479 name = "wasm-bindgen" 4370 - version = "0.2.100" 4480 + version = "0.2.106" 4371 4481 source = "registry+https://github.com/rust-lang/crates.io-index" 4372 - checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" 4482 + checksum = "0d759f433fa64a2d763d1340820e46e111a7a5ab75f993d1852d70b03dbb80fd" 4373 4483 dependencies = [ 4374 4484 "cfg-if", 4375 4485 "once_cell", 4376 4486 "rustversion", 4377 4487 "wasm-bindgen-macro", 4378 - ] 4379 - 4380 - [[package]] 4381 - name = "wasm-bindgen-backend" 4382 - version = "0.2.100" 4383 - source = "registry+https://github.com/rust-lang/crates.io-index" 4384 - checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" 4385 - dependencies = [ 4386 - "bumpalo", 4387 - "log", 4388 - "proc-macro2", 4389 - "quote", 4390 - "syn 2.0.105", 4391 4488 "wasm-bindgen-shared", 4392 4489 ] 4393 4490 4394 4491 [[package]] 4395 4492 name = "wasm-bindgen-futures" 4396 - version = "0.4.50" 4493 + version = "0.4.56" 4397 4494 source = "registry+https://github.com/rust-lang/crates.io-index" 4398 - checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" 4495 + checksum = "836d9622d604feee9e5de25ac10e3ea5f2d65b41eac0d9ce72eb5deae707ce7c" 4399 4496 dependencies = [ 4400 4497 "cfg-if", 4401 4498 "js-sys", ··· 4406 4503 4407 4504 [[package]] 4408 4505 name = "wasm-bindgen-macro" 4409 - version = "0.2.100" 4506 + version = "0.2.106" 4410 4507 source = "registry+https://github.com/rust-lang/crates.io-index" 4411 - checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" 4508 + checksum = "48cb0d2638f8baedbc542ed444afc0644a29166f1595371af4fecf8ce1e7eeb3" 4412 4509 dependencies = [ 4413 4510 "quote", 4414 4511 "wasm-bindgen-macro-support", ··· 4416 4513 4417 4514 [[package]] 4418 4515 name = "wasm-bindgen-macro-support" 4419 - version = "0.2.100" 4516 + version = "0.2.106" 4420 4517 source = "registry+https://github.com/rust-lang/crates.io-index" 4421 - checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" 4518 + checksum = "cefb59d5cd5f92d9dcf80e4683949f15ca4b511f4ac0a6e14d4e1ac60c6ecd40" 4422 4519 dependencies = [ 4520 + "bumpalo", 4423 4521 "proc-macro2", 4424 4522 "quote", 4425 - "syn 2.0.105", 4426 - "wasm-bindgen-backend", 4523 + "syn 2.0.112", 4427 4524 "wasm-bindgen-shared", 4428 4525 ] 4429 4526 4430 4527 [[package]] 4431 4528 name = "wasm-bindgen-shared" 4432 - version = "0.2.100" 4529 + version = "0.2.106" 4433 4530 source = "registry+https://github.com/rust-lang/crates.io-index" 4434 - checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" 4531 + checksum = "cbc538057e648b67f72a982e708d485b2efa771e1ac05fec311f9f63e5800db4" 4435 4532 dependencies = [ 4436 4533 "unicode-ident", 4437 4534 ] 4438 4535 4439 4536 [[package]] 4440 - name = "wasm-streams" 4441 - version = "0.4.2" 4442 - source = "registry+https://github.com/rust-lang/crates.io-index" 4443 - checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65" 4444 - dependencies = [ 4445 - "futures-util", 4446 - "js-sys", 4447 - "wasm-bindgen", 4448 - "wasm-bindgen-futures", 4449 - "web-sys", 4450 - ] 4451 - 4452 - [[package]] 4453 4537 name = "web-sys" 4454 - version = "0.3.77" 4538 + version = "0.3.83" 4455 4539 source = "registry+https://github.com/rust-lang/crates.io-index" 4456 - checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" 4540 + checksum = "9b32828d774c412041098d182a8b38b16ea816958e07cf40eec2bc080ae137ac" 4457 4541 dependencies = [ 4458 4542 "js-sys", 4459 4543 "wasm-bindgen", ··· 4475 4559 source = "registry+https://github.com/rust-lang/crates.io-index" 4476 4560 checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" 4477 4561 dependencies = [ 4478 - "webpki-roots 1.0.2", 4562 + "webpki-roots 1.0.5", 4479 4563 ] 4480 4564 4481 4565 [[package]] 4482 4566 name = "webpki-roots" 4483 - version = "1.0.2" 4567 + version = "1.0.5" 4484 4568 source = "registry+https://github.com/rust-lang/crates.io-index" 4485 - checksum = "7e8983c3ab33d6fb807cfcdad2491c4ea8cbc8ed839181c7dfd9c67c83e261b2" 4569 + checksum = "12bed680863276c63889429bfd6cab3b99943659923822de1c8a39c49e4d722c" 4486 4570 dependencies = [ 4487 4571 "rustls-pki-types", 4488 - ] 4489 - 4490 - [[package]] 4491 - name = "which" 4492 - version = "4.4.2" 4493 - source = "registry+https://github.com/rust-lang/crates.io-index" 4494 - checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" 4495 - dependencies = [ 4496 - "either", 4497 - "home", 4498 - "once_cell", 4499 - "rustix", 4500 4572 ] 4501 4573 4502 4574 [[package]] ··· 4527 4599 4528 4600 [[package]] 4529 4601 name = "winapi-util" 4530 - version = "0.1.9" 4602 + version = "0.1.11" 4531 4603 source = "registry+https://github.com/rust-lang/crates.io-index" 4532 - checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" 4604 + checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" 4533 4605 dependencies = [ 4534 - "windows-sys 0.59.0", 4606 + "windows-sys 0.48.0", 4535 4607 ] 4536 4608 4537 4609 [[package]] ··· 4542 4614 4543 4615 [[package]] 4544 4616 name = "windows-core" 4545 - version = "0.61.2" 4617 + version = "0.62.2" 4546 4618 source = "registry+https://github.com/rust-lang/crates.io-index" 4547 - checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" 4619 + checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" 4548 4620 dependencies = [ 4549 4621 "windows-implement", 4550 4622 "windows-interface", 4551 - "windows-link 0.1.3", 4623 + "windows-link", 4552 4624 "windows-result", 4553 4625 "windows-strings", 4554 4626 ] 4555 4627 4556 4628 [[package]] 4557 4629 name = "windows-implement" 4558 - version = "0.60.0" 4630 + version = "0.60.2" 4559 4631 source = "registry+https://github.com/rust-lang/crates.io-index" 4560 - checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" 4632 + checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" 4561 4633 dependencies = [ 4562 4634 "proc-macro2", 4563 4635 "quote", 4564 - "syn 2.0.105", 4636 + "syn 2.0.112", 4565 4637 ] 4566 4638 4567 4639 [[package]] 4568 4640 name = "windows-interface" 4569 - version = "0.59.1" 4641 + version = "0.59.3" 4570 4642 source = "registry+https://github.com/rust-lang/crates.io-index" 4571 - checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" 4643 + checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" 4572 4644 dependencies = [ 4573 4645 "proc-macro2", 4574 4646 "quote", 4575 - "syn 2.0.105", 4647 + "syn 2.0.112", 4576 4648 ] 4577 4649 4578 4650 [[package]] 4579 4651 name = "windows-link" 4580 - version = "0.1.3" 4581 - source = "registry+https://github.com/rust-lang/crates.io-index" 4582 - checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" 4583 - 4584 - [[package]] 4585 - name = "windows-link" 4586 4652 version = "0.2.1" 4587 4653 source = "registry+https://github.com/rust-lang/crates.io-index" 4588 4654 checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" 4589 4655 4590 4656 [[package]] 4591 4657 name = "windows-registry" 4592 - version = "0.5.3" 4658 + version = "0.6.1" 4593 4659 source = "registry+https://github.com/rust-lang/crates.io-index" 4594 - checksum = "5b8a9ed28765efc97bbc954883f4e6796c33a06546ebafacbabee9696967499e" 4660 + checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720" 4595 4661 dependencies = [ 4596 - "windows-link 0.1.3", 4662 + "windows-link", 4597 4663 "windows-result", 4598 4664 "windows-strings", 4599 4665 ] 4600 4666 4601 4667 [[package]] 4602 4668 name = "windows-result" 4603 - version = "0.3.4" 4669 + version = "0.4.1" 4604 4670 source = "registry+https://github.com/rust-lang/crates.io-index" 4605 - checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" 4671 + checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" 4606 4672 dependencies = [ 4607 - "windows-link 0.1.3", 4673 + "windows-link", 4608 4674 ] 4609 4675 4610 4676 [[package]] 4611 4677 name = "windows-strings" 4612 - version = "0.4.2" 4678 + version = "0.5.1" 4613 4679 source = "registry+https://github.com/rust-lang/crates.io-index" 4614 - checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" 4680 + checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" 4615 4681 dependencies = [ 4616 - "windows-link 0.1.3", 4682 + "windows-link", 4617 4683 ] 4618 4684 4619 4685 [[package]] ··· 4644 4710 ] 4645 4711 4646 4712 [[package]] 4713 + name = "windows-sys" 4714 + version = "0.60.2" 4715 + source = "registry+https://github.com/rust-lang/crates.io-index" 4716 + checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" 4717 + dependencies = [ 4718 + "windows-targets 0.53.5", 4719 + ] 4720 + 4721 + [[package]] 4722 + name = "windows-sys" 4723 + version = "0.61.2" 4724 + source = "registry+https://github.com/rust-lang/crates.io-index" 4725 + checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" 4726 + dependencies = [ 4727 + "windows-link", 4728 + ] 4729 + 4730 + [[package]] 4647 4731 name = "windows-targets" 4648 4732 version = "0.48.5" 4649 4733 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 4667 4751 "windows_aarch64_gnullvm 0.52.6", 4668 4752 "windows_aarch64_msvc 0.52.6", 4669 4753 "windows_i686_gnu 0.52.6", 4670 - "windows_i686_gnullvm", 4754 + "windows_i686_gnullvm 0.52.6", 4671 4755 "windows_i686_msvc 0.52.6", 4672 4756 "windows_x86_64_gnu 0.52.6", 4673 4757 "windows_x86_64_gnullvm 0.52.6", ··· 4675 4759 ] 4676 4760 4677 4761 [[package]] 4762 + name = "windows-targets" 4763 + version = "0.53.5" 4764 + source = "registry+https://github.com/rust-lang/crates.io-index" 4765 + checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" 4766 + dependencies = [ 4767 + "windows-link", 4768 + "windows_aarch64_gnullvm 0.53.1", 4769 + "windows_aarch64_msvc 0.53.1", 4770 + "windows_i686_gnu 0.53.1", 4771 + "windows_i686_gnullvm 0.53.1", 4772 + "windows_i686_msvc 0.53.1", 4773 + "windows_x86_64_gnu 0.53.1", 4774 + "windows_x86_64_gnullvm 0.53.1", 4775 + "windows_x86_64_msvc 0.53.1", 4776 + ] 4777 + 4778 + [[package]] 4678 4779 name = "windows_aarch64_gnullvm" 4679 4780 version = "0.48.5" 4680 4781 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 4687 4788 checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 4688 4789 4689 4790 [[package]] 4791 + name = "windows_aarch64_gnullvm" 4792 + version = "0.53.1" 4793 + source = "registry+https://github.com/rust-lang/crates.io-index" 4794 + checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" 4795 + 4796 + [[package]] 4690 4797 name = "windows_aarch64_msvc" 4691 4798 version = "0.48.5" 4692 4799 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 4699 4806 checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 4700 4807 4701 4808 [[package]] 4809 + name = "windows_aarch64_msvc" 4810 + version = "0.53.1" 4811 + source = "registry+https://github.com/rust-lang/crates.io-index" 4812 + checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" 4813 + 4814 + [[package]] 4702 4815 name = "windows_i686_gnu" 4703 4816 version = "0.48.5" 4704 4817 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 4711 4824 checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 4712 4825 4713 4826 [[package]] 4827 + name = "windows_i686_gnu" 4828 + version = "0.53.1" 4829 + source = "registry+https://github.com/rust-lang/crates.io-index" 4830 + checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" 4831 + 4832 + [[package]] 4714 4833 name = "windows_i686_gnullvm" 4715 4834 version = "0.52.6" 4716 4835 source = "registry+https://github.com/rust-lang/crates.io-index" 4717 4836 checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 4837 + 4838 + [[package]] 4839 + name = "windows_i686_gnullvm" 4840 + version = "0.53.1" 4841 + source = "registry+https://github.com/rust-lang/crates.io-index" 4842 + checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" 4718 4843 4719 4844 [[package]] 4720 4845 name = "windows_i686_msvc" ··· 4729 4854 checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 4730 4855 4731 4856 [[package]] 4857 + name = "windows_i686_msvc" 4858 + version = "0.53.1" 4859 + source = "registry+https://github.com/rust-lang/crates.io-index" 4860 + checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" 4861 + 4862 + [[package]] 4732 4863 name = "windows_x86_64_gnu" 4733 4864 version = "0.48.5" 4734 4865 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 4741 4872 checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 4742 4873 4743 4874 [[package]] 4875 + name = "windows_x86_64_gnu" 4876 + version = "0.53.1" 4877 + source = "registry+https://github.com/rust-lang/crates.io-index" 4878 + checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" 4879 + 4880 + [[package]] 4744 4881 name = "windows_x86_64_gnullvm" 4745 4882 version = "0.48.5" 4746 4883 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 4753 4890 checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 4754 4891 4755 4892 [[package]] 4893 + name = "windows_x86_64_gnullvm" 4894 + version = "0.53.1" 4895 + source = "registry+https://github.com/rust-lang/crates.io-index" 4896 + checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" 4897 + 4898 + [[package]] 4756 4899 name = "windows_x86_64_msvc" 4757 4900 version = "0.48.5" 4758 4901 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 4765 4908 checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 4766 4909 4767 4910 [[package]] 4911 + name = "windows_x86_64_msvc" 4912 + version = "0.53.1" 4913 + source = "registry+https://github.com/rust-lang/crates.io-index" 4914 + checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" 4915 + 4916 + [[package]] 4768 4917 name = "wit-bindgen" 4769 4918 version = "0.46.0" 4770 4919 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 4772 4921 4773 4922 [[package]] 4774 4923 name = "writeable" 4775 - version = "0.6.1" 4924 + version = "0.6.2" 4776 4925 source = "registry+https://github.com/rust-lang/crates.io-index" 4777 - checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" 4926 + checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" 4778 4927 4779 4928 [[package]] 4780 4929 name = "yansi" ··· 4784 4933 4785 4934 [[package]] 4786 4935 name = "yoke" 4787 - version = "0.8.0" 4936 + version = "0.8.1" 4788 4937 source = "registry+https://github.com/rust-lang/crates.io-index" 4789 - checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" 4938 + checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" 4790 4939 dependencies = [ 4791 - "serde", 4792 4940 "stable_deref_trait", 4793 4941 "yoke-derive", 4794 4942 "zerofrom", ··· 4796 4944 4797 4945 [[package]] 4798 4946 name = "yoke-derive" 4799 - version = "0.8.0" 4947 + version = "0.8.1" 4800 4948 source = "registry+https://github.com/rust-lang/crates.io-index" 4801 - checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" 4949 + checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" 4802 4950 dependencies = [ 4803 4951 "proc-macro2", 4804 4952 "quote", 4805 - "syn 2.0.105", 4953 + "syn 2.0.112", 4806 4954 "synstructure", 4807 4955 ] 4808 4956 4809 4957 [[package]] 4810 4958 name = "zerocopy" 4811 - version = "0.8.26" 4959 + version = "0.8.31" 4812 4960 source = "registry+https://github.com/rust-lang/crates.io-index" 4813 - checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f" 4961 + checksum = "fd74ec98b9250adb3ca554bdde269adf631549f51d8a8f8f0a10b50f1cb298c3" 4814 4962 dependencies = [ 4815 4963 "zerocopy-derive", 4816 4964 ] 4817 4965 4818 4966 [[package]] 4819 4967 name = "zerocopy-derive" 4820 - version = "0.8.26" 4968 + version = "0.8.31" 4821 4969 source = "registry+https://github.com/rust-lang/crates.io-index" 4822 - checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" 4970 + checksum = "d8a8d209fdf45cf5138cbb5a506f6b52522a25afccc534d1475dad8e31105c6a" 4823 4971 dependencies = [ 4824 4972 "proc-macro2", 4825 4973 "quote", 4826 - "syn 2.0.105", 4974 + "syn 2.0.112", 4827 4975 ] 4828 4976 4829 4977 [[package]] ··· 4843 4991 dependencies = [ 4844 4992 "proc-macro2", 4845 4993 "quote", 4846 - "syn 2.0.105", 4994 + "syn 2.0.112", 4847 4995 "synstructure", 4848 4996 ] 4849 4997 4850 4998 [[package]] 4851 4999 name = "zeroize" 4852 - version = "1.8.1" 5000 + version = "1.8.2" 4853 5001 source = "registry+https://github.com/rust-lang/crates.io-index" 4854 - checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" 5002 + checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" 4855 5003 dependencies = [ 4856 5004 "zeroize_derive", 4857 5005 ] 4858 5006 4859 5007 [[package]] 4860 5008 name = "zeroize_derive" 4861 - version = "1.4.2" 5009 + version = "1.4.3" 4862 5010 source = "registry+https://github.com/rust-lang/crates.io-index" 4863 - checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" 5011 + checksum = "85a5b4158499876c763cb03bc4e49185d3cccbabb15b33c627f7884f43db852e" 4864 5012 dependencies = [ 4865 5013 "proc-macro2", 4866 5014 "quote", 4867 - "syn 2.0.105", 5015 + "syn 2.0.112", 4868 5016 ] 4869 5017 4870 5018 [[package]] 4871 5019 name = "zerotrie" 4872 - version = "0.2.2" 5020 + version = "0.2.3" 4873 5021 source = "registry+https://github.com/rust-lang/crates.io-index" 4874 - checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" 5022 + checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" 4875 5023 dependencies = [ 4876 5024 "displaydoc", 4877 5025 "yoke", ··· 4880 5028 4881 5029 [[package]] 4882 5030 name = "zerovec" 4883 - version = "0.11.4" 5031 + version = "0.11.5" 4884 5032 source = "registry+https://github.com/rust-lang/crates.io-index" 4885 - checksum = "e7aa2bd55086f1ab526693ecbe444205da57e25f4489879da80635a46d90e73b" 5033 + checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" 4886 5034 dependencies = [ 4887 5035 "yoke", 4888 5036 "zerofrom", ··· 4891 5039 4892 5040 [[package]] 4893 5041 name = "zerovec-derive" 4894 - version = "0.11.1" 5042 + version = "0.11.2" 4895 5043 source = "registry+https://github.com/rust-lang/crates.io-index" 4896 - checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" 5044 + checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" 4897 5045 dependencies = [ 4898 5046 "proc-macro2", 4899 5047 "quote", 4900 - "syn 2.0.105", 5048 + "syn 2.0.112", 4901 5049 ] 4902 5050 4903 5051 [[package]] 5052 + name = "zmij" 5053 + version = "1.0.8" 5054 + source = "registry+https://github.com/rust-lang/crates.io-index" 5055 + checksum = "317f17ff091ac4515f17cc7a190d2769a8c9a96d227de5d64b500b01cda8f2cd" 5056 + 5057 + [[package]] 4904 5058 name = "zstd" 4905 5059 version = "0.13.3" 4906 5060 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 4920 5074 4921 5075 [[package]] 4922 5076 name = "zstd-sys" 4923 - version = "2.0.15+zstd.1.5.7" 5077 + version = "2.0.16+zstd.1.5.7" 4924 5078 source = "registry+https://github.com/rust-lang/crates.io-index" 4925 - checksum = "eb81183ddd97d0c74cedf1d50d85c8d08c1b8b68ee863bdee9e706eedba1a237" 5079 + checksum = "91e19ebc2adc8f83e43039e79776e3fda8ca919132d68a1fed6a5faca2683748" 4926 5080 dependencies = [ 4927 5081 "cc", 4928 5082 "pkg-config",
+11 -9
Cargo.toml
··· 5 5 license = "MIT" 6 6 7 7 [dependencies] 8 - axum = { version = "0.8.4", features = ["macros", "json"] } 9 - tokio = { version = "1.47.1", features = ["rt-multi-thread", "macros", "signal"] } 8 + axum = { version = "0.8.8", features = ["macros", "json"] } 9 + tokio = { version = "1.48.0", features = ["rt-multi-thread", "macros", "signal"] } 10 10 sqlx = { version = "0.8.6", features = ["runtime-tokio-rustls", "sqlite", "migrate", "chrono"] } 11 11 dotenvy = "0.15.7" 12 12 serde = { version = "1.0", features = ["derive"] } 13 13 serde_json = "1.0" 14 14 tracing = "0.1" 15 15 tracing-subscriber = { version = "0.3", features = ["env-filter", "fmt"] } 16 - hyper-util = { version = "0.1.16", features = ["client", "client-legacy"] } 16 + hyper-util = { version = "0.1.19", features = ["client", "client-legacy"] } 17 17 tower-http = { version = "0.6", features = ["cors", "compression-zstd"] } 18 18 tower_governor = { version = "0.8.0", features = ["axum", "tracing"] } 19 19 hex = "0.4" 20 20 jwt-compact = { version = "0.8.0", features = ["es256k"] } 21 21 scrypt = "0.11" 22 22 #Leaveing these two cause I think it is needed by the email crate for ssl 23 - aws-lc-rs = "1.13.0" 23 + aws-lc-rs = "1.15.2" 24 24 rustls = { version = "0.23", default-features = false, features = ["tls12", "std", "logging", "aws_lc_rs"] } 25 25 lettre = { version = "0.11", default-features = false, features = ["builder", "webpki-roots", "rustls", "aws-lc-rs", "smtp-transport", "tokio1", "tokio1-rustls"] } 26 - handlebars = { version = "6.3.2", features = ["rust-embed"] } 27 - rust-embed = "8.7.2" 26 + handlebars = { version = "6.4.0", features = ["rust-embed"] } 27 + rust-embed = "8.9.0" 28 28 axum-template = { version = "3.0.0", features = ["handlebars"] } 29 29 rand = "0.9.2" 30 - anyhow = "1.0.99" 30 + anyhow = "1.0.100" 31 31 chrono = { version = "0.4.42", features = ["default", "serde"] } 32 32 sha2 = "0.10" 33 - jacquard-common = "0.9.2" 34 - jacquard-identity = "0.9.2" 33 + jacquard-common = "0.9.5" 34 + jacquard-identity = "0.9.5" 35 35 multibase = "0.9.2" 36 36 reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls"] } 37 37 urlencoding = "2.1" 38 38 html-escape = "0.2.13" 39 39 josekit = "0.10.3" 40 + dashmap = "6.1" 41 + tower = "0.5"
+101
src/auth/cache.rs
··· 1 + use dashmap::DashMap; 2 + use std::sync::Arc; 3 + use std::time::{Duration, Instant}; 4 + 5 + #[derive(Clone, Debug)] 6 + struct CachedHandle { 7 + handle: String, 8 + cached_at: Instant, 9 + } 10 + 11 + /// A thread-safe cache for DID-to-handle resolutions with TTL expiration. 12 + #[derive(Clone)] 13 + pub struct HandleCache { 14 + cache: Arc<DashMap<String, CachedHandle>>, 15 + ttl: Duration, 16 + } 17 + 18 + impl Default for HandleCache { 19 + fn default() -> Self { 20 + Self::new() 21 + } 22 + } 23 + 24 + impl HandleCache { 25 + /// Creates a new HandleCache with a default TTL of 1 hour. 26 + pub fn new() -> Self { 27 + Self::with_ttl(Duration::from_secs(3600)) 28 + } 29 + 30 + /// Creates a new HandleCache with a custom TTL. 31 + pub fn with_ttl(ttl: Duration) -> Self { 32 + Self { 33 + cache: Arc::new(DashMap::new()), 34 + ttl, 35 + } 36 + } 37 + 38 + /// Gets a cached handle for the given DID, if it exists and hasn't expired. 39 + pub fn get(&self, did: &str) -> Option<String> { 40 + let entry = self.cache.get(did)?; 41 + if entry.cached_at.elapsed() > self.ttl { 42 + drop(entry); 43 + self.cache.remove(did); 44 + return None; 45 + } 46 + Some(entry.handle.clone()) 47 + } 48 + 49 + /// Inserts a DID-to-handle mapping into the cache. 50 + pub fn insert(&self, did: String, handle: String) { 51 + self.cache.insert( 52 + did, 53 + CachedHandle { 54 + handle, 55 + cached_at: Instant::now(), 56 + }, 57 + ); 58 + } 59 + 60 + /// Removes expired entries from the cache. 61 + /// Call this periodically to prevent memory growth. 62 + pub fn cleanup(&self) { 63 + self.cache.retain(|_, v| v.cached_at.elapsed() <= self.ttl); 64 + } 65 + 66 + /// Returns the number of entries in the cache. 67 + pub fn len(&self) -> usize { 68 + self.cache.len() 69 + } 70 + 71 + /// Returns true if the cache is empty. 72 + pub fn is_empty(&self) -> bool { 73 + self.cache.is_empty() 74 + } 75 + } 76 + 77 + #[cfg(test)] 78 + mod tests { 79 + use super::*; 80 + 81 + #[test] 82 + fn test_cache_insert_and_get() { 83 + let cache = HandleCache::new(); 84 + cache.insert("did:plc:test".into(), "test.handle.com".into()); 85 + assert_eq!(cache.get("did:plc:test"), Some("test.handle.com".into())); 86 + } 87 + 88 + #[test] 89 + fn test_cache_miss() { 90 + let cache = HandleCache::new(); 91 + assert_eq!(cache.get("did:plc:nonexistent"), None); 92 + } 93 + 94 + #[test] 95 + fn test_cache_expiration() { 96 + let cache = HandleCache::with_ttl(Duration::from_millis(1)); 97 + cache.insert("did:plc:test".into(), "test.handle.com".into()); 98 + std::thread::sleep(Duration::from_millis(10)); 99 + assert_eq!(cache.get("did:plc:test"), None); 100 + } 101 + }
+456
src/auth/middleware.rs
··· 1 + use super::{AuthRules, HandleCache, SessionData}; 2 + use crate::helpers::json_error_response; 3 + use crate::AppState; 4 + use axum::extract::{Request, State}; 5 + use axum::http::{HeaderMap, StatusCode}; 6 + use axum::middleware::Next; 7 + use axum::response::{IntoResponse, Response}; 8 + use jacquard_identity::resolver::IdentityResolver; 9 + use jacquard_identity::PublicResolver; 10 + use jwt_compact::alg::{Hs256, Hs256Key}; 11 + use jwt_compact::{AlgorithmExt, Claims, Token, UntrustedToken, ValidationError}; 12 + use serde::{Deserialize, Serialize}; 13 + use std::env; 14 + use std::sync::Arc; 15 + use tracing::log; 16 + 17 + #[derive(Clone, Copy, Debug, PartialEq, Eq)] 18 + pub enum AuthScheme { 19 + Bearer, 20 + DPoP, 21 + } 22 + 23 + #[derive(Serialize, Deserialize)] 24 + pub struct TokenClaims { 25 + pub sub: String, 26 + /// OAuth scopes as space-separated string (per OAuth 2.0 spec) 27 + #[serde(default)] 28 + pub scope: Option<String>, 29 + } 30 + 31 + /// State passed to the auth middleware containing both AppState and auth rules. 32 + #[derive(Clone)] 33 + pub struct AuthMiddlewareState { 34 + pub app_state: AppState, 35 + pub rules: AuthRules, 36 + } 37 + 38 + /// Core middleware function that validates authentication and applies auth rules. 39 + /// 40 + /// Use this with `axum::middleware::from_fn_with_state`: 41 + /// ```ignore 42 + /// use axum::middleware::from_fn_with_state; 43 + /// 44 + /// let mw_state = AuthMiddlewareState { 45 + /// app_state: state.clone(), 46 + /// rules: AuthRules::HandleEndsWith(".blacksky.team".into()), 47 + /// }; 48 + /// 49 + /// .route("/protected", get(handler).layer(from_fn_with_state(mw_state, auth_middleware))) 50 + /// ``` 51 + pub async fn auth_middleware( 52 + State(mw_state): State<AuthMiddlewareState>, 53 + req: Request, 54 + next: Next, 55 + ) -> Response { 56 + let AuthMiddlewareState { app_state, rules } = mw_state; 57 + 58 + // 1. Extract DID and scopes from JWT (Bearer token) 59 + let extracted = match extract_auth_from_request(req.headers()) { 60 + Ok(Some(auth)) => auth, 61 + Ok(None) => { 62 + return json_error_response(StatusCode::UNAUTHORIZED, "AuthRequired", "Authentication required") 63 + .unwrap_or_else(|_| StatusCode::UNAUTHORIZED.into_response()); 64 + } 65 + Err(e) => { 66 + log::error!("Token extraction error: {}", e); 67 + return json_error_response(StatusCode::UNAUTHORIZED, "InvalidToken", &e) 68 + .unwrap_or_else(|_| StatusCode::UNAUTHORIZED.into_response()); 69 + } 70 + }; 71 + 72 + // 2. Resolve DID to handle (check cache first) 73 + let handle = match resolve_did_to_handle(&app_state.resolver, &app_state.handle_cache, &extracted.did).await { 74 + Ok(handle) => handle, 75 + Err(e) => { 76 + log::error!("Failed to resolve DID {} to handle: {}", extracted.did, e); 77 + return json_error_response( 78 + StatusCode::INTERNAL_SERVER_ERROR, 79 + "ResolutionError", 80 + "Failed to resolve identity", 81 + ) 82 + .unwrap_or_else(|_| StatusCode::INTERNAL_SERVER_ERROR.into_response()); 83 + } 84 + }; 85 + 86 + // 3. Build session data and validate rules 87 + let session = SessionData { 88 + did: extracted.did, 89 + handle, 90 + scopes: extracted.scopes, 91 + }; 92 + 93 + if !rules.validate(&session) { 94 + return json_error_response(StatusCode::FORBIDDEN, "AccessDenied", "Access denied by authorization rules") 95 + .unwrap_or_else(|_| StatusCode::FORBIDDEN.into_response()); 96 + } 97 + 98 + // 4. Pass through on success 99 + next.run(req).await 100 + } 101 + 102 + /// Extracted authentication data from JWT 103 + struct ExtractedAuth { 104 + did: String, 105 + scopes: Vec<String>, 106 + } 107 + 108 + /// Extracts the DID and scopes from the Authorization header (Bearer JWT). 109 + fn extract_auth_from_request(headers: &HeaderMap) -> Result<Option<ExtractedAuth>, String> { 110 + let auth = extract_auth(headers)?; 111 + 112 + match auth { 113 + None => Ok(None), 114 + Some((scheme, token_str)) => { 115 + match scheme { 116 + AuthScheme::Bearer => { 117 + let token = UntrustedToken::new(&token_str) 118 + .map_err(|_| "Invalid token format".to_string())?; 119 + 120 + let _claims: Claims<TokenClaims> = token 121 + .deserialize_claims_unchecked() 122 + .map_err(|_| "Failed to parse token claims".to_string())?; 123 + 124 + let key = Hs256Key::new( 125 + env::var("PDS_JWT_SECRET") 126 + .map_err(|_| "PDS_JWT_SECRET not configured".to_string())?, 127 + ); 128 + 129 + let validated: Token<TokenClaims> = Hs256 130 + .validator(&key) 131 + .validate(&token) 132 + .map_err(|e: ValidationError| format!("Token validation failed: {:?}", e))?; 133 + 134 + let custom = &validated.claims().custom; 135 + 136 + // Parse scopes from space-separated string (OAuth 2.0 spec) 137 + let scopes: Vec<String> = custom.scope 138 + .as_ref() 139 + .map(|s| s.split_whitespace().map(|s| s.to_string()).collect()) 140 + .unwrap_or_default(); 141 + 142 + Ok(Some(ExtractedAuth { 143 + did: custom.sub.clone(), 144 + scopes, 145 + })) 146 + } 147 + AuthScheme::DPoP => { 148 + // DPoP tokens are not validated here; pass through without auth data 149 + Ok(None) 150 + } 151 + } 152 + } 153 + } 154 + } 155 + 156 + /// Extracts the authentication scheme and token from the Authorization header. 157 + fn extract_auth(headers: &HeaderMap) -> Result<Option<(AuthScheme, String)>, String> { 158 + match headers.get(axum::http::header::AUTHORIZATION) { 159 + None => Ok(None), 160 + Some(hv) => { 161 + let s = hv 162 + .to_str() 163 + .map_err(|_| "Authorization header is not valid UTF-8".to_string())?; 164 + 165 + let mut parts = s.splitn(2, ' '); 166 + match (parts.next(), parts.next()) { 167 + (Some("Bearer"), Some(tok)) if !tok.is_empty() => { 168 + Ok(Some((AuthScheme::Bearer, tok.to_string()))) 169 + } 170 + (Some("DPoP"), Some(tok)) if !tok.is_empty() => { 171 + Ok(Some((AuthScheme::DPoP, tok.to_string()))) 172 + } 173 + _ => Err( 174 + "Authorization header must be in format 'Bearer <token>' or 'DPoP <token>'" 175 + .to_string(), 176 + ), 177 + } 178 + } 179 + } 180 + } 181 + 182 + /// Resolves a DID to its handle using the PublicResolver, with caching. 183 + async fn resolve_did_to_handle( 184 + resolver: &Arc<PublicResolver>, 185 + cache: &HandleCache, 186 + did: &str, 187 + ) -> Result<String, String> { 188 + // Check cache first 189 + if let Some(handle) = cache.get(did) { 190 + return Ok(handle); 191 + } 192 + 193 + // Parse the DID 194 + let did_parsed = jacquard_common::types::did::Did::new(did) 195 + .map_err(|e| format!("Invalid DID: {:?}", e))?; 196 + 197 + // Resolve the DID document 198 + let did_doc_response = resolver 199 + .resolve_did_doc(&did_parsed) 200 + .await 201 + .map_err(|e| format!("DID resolution failed: {:?}", e))?; 202 + 203 + let doc = did_doc_response 204 + .parse() 205 + .map_err(|e| format!("Failed to parse DID document: {:?}", e))?; 206 + 207 + // Extract handle from alsoKnownAs field 208 + // Format is typically: ["at://handle.example.com"] 209 + let handle: String = doc 210 + .also_known_as 211 + .as_ref() 212 + .and_then(|aka| { 213 + aka.iter() 214 + .find(|uri| uri.starts_with("at://")) 215 + .map(|uri| uri.strip_prefix("at://").unwrap_or(uri.as_ref()).to_string()) 216 + }) 217 + .ok_or_else(|| "No ATProto handle found in DID document".to_string())?; 218 + 219 + // Cache the result 220 + cache.insert(did.to_string(), handle.clone()); 221 + 222 + Ok(handle) 223 + } 224 + 225 + // ============================================================================ 226 + // Helper Functions for Creating Middleware State 227 + // ============================================================================ 228 + 229 + /// Creates an `AuthMiddlewareState` for requiring the handle to end with a specific suffix. 230 + /// 231 + /// # Example 232 + /// ```ignore 233 + /// use axum::middleware::from_fn_with_state; 234 + /// use crate::auth::{auth_middleware, handle_ends_with}; 235 + /// 236 + /// .route("/protected", get(handler).layer( 237 + /// from_fn_with_state(handle_ends_with(".blacksky.team", &state), auth_middleware) 238 + /// )) 239 + /// ``` 240 + pub fn handle_ends_with(suffix: impl Into<String>, state: &AppState) -> AuthMiddlewareState { 241 + AuthMiddlewareState { 242 + app_state: state.clone(), 243 + rules: AuthRules::HandleEndsWith(suffix.into()), 244 + } 245 + } 246 + 247 + /// Creates an `AuthMiddlewareState` for requiring the handle to end with any of the specified suffixes. 248 + pub fn handle_ends_with_any<I, T>(suffixes: I, state: &AppState) -> AuthMiddlewareState 249 + where 250 + I: IntoIterator<Item = T>, 251 + T: Into<String>, 252 + { 253 + AuthMiddlewareState { 254 + app_state: state.clone(), 255 + rules: AuthRules::HandleEndsWithAny(suffixes.into_iter().map(|s| s.into()).collect()), 256 + } 257 + } 258 + 259 + /// Creates an `AuthMiddlewareState` for requiring the DID to equal a specific value. 260 + pub fn did_equals(did: impl Into<String>, state: &AppState) -> AuthMiddlewareState { 261 + AuthMiddlewareState { 262 + app_state: state.clone(), 263 + rules: AuthRules::DidEquals(did.into()), 264 + } 265 + } 266 + 267 + /// Creates an `AuthMiddlewareState` for requiring the DID to be one of the specified values. 268 + pub fn did_equals_any<I, T>(dids: I, state: &AppState) -> AuthMiddlewareState 269 + where 270 + I: IntoIterator<Item = T>, 271 + T: Into<String>, 272 + { 273 + AuthMiddlewareState { 274 + app_state: state.clone(), 275 + rules: AuthRules::DidEqualsAny(dids.into_iter().map(|d| d.into()).collect()), 276 + } 277 + } 278 + 279 + /// Creates an `AuthMiddlewareState` with custom auth rules. 280 + pub fn with_rules(rules: AuthRules, state: &AppState) -> AuthMiddlewareState { 281 + AuthMiddlewareState { 282 + app_state: state.clone(), 283 + rules, 284 + } 285 + } 286 + 287 + // ============================================================================ 288 + // Scope Helper Functions 289 + // ============================================================================ 290 + 291 + /// Creates an `AuthMiddlewareState` requiring a specific OAuth scope. 292 + /// 293 + /// # Example 294 + /// ```ignore 295 + /// .route("/xrpc/com.atproto.repo.createRecord", 296 + /// post(handler).layer(from_fn_with_state( 297 + /// scope_equals("repo:app.bsky.feed.post", &state), 298 + /// auth_middleware 299 + /// ))) 300 + /// ``` 301 + pub fn scope_equals(scope: impl Into<String>, state: &AppState) -> AuthMiddlewareState { 302 + AuthMiddlewareState { 303 + app_state: state.clone(), 304 + rules: AuthRules::ScopeEquals(scope.into()), 305 + } 306 + } 307 + 308 + /// Creates an `AuthMiddlewareState` requiring ANY of the specified scopes (OR logic). 309 + /// 310 + /// # Example 311 + /// ```ignore 312 + /// .route("/xrpc/com.atproto.repo.putRecord", 313 + /// post(handler).layer(from_fn_with_state( 314 + /// scope_any(["repo:app.bsky.feed.post", "transition:generic"], &state), 315 + /// auth_middleware 316 + /// ))) 317 + /// ``` 318 + pub fn scope_any<I, T>(scopes: I, state: &AppState) -> AuthMiddlewareState 319 + where 320 + I: IntoIterator<Item = T>, 321 + T: Into<String>, 322 + { 323 + AuthMiddlewareState { 324 + app_state: state.clone(), 325 + rules: AuthRules::ScopeEqualsAny(scopes.into_iter().map(|s| s.into()).collect()), 326 + } 327 + } 328 + 329 + /// Creates an `AuthMiddlewareState` requiring ALL of the specified scopes (AND logic). 330 + /// 331 + /// # Example 332 + /// ```ignore 333 + /// .route("/xrpc/com.atproto.admin.updateAccount", 334 + /// post(handler).layer(from_fn_with_state( 335 + /// scope_all(["account:email", "account:repo?action=manage"], &state), 336 + /// auth_middleware 337 + /// ))) 338 + /// ``` 339 + pub fn scope_all<I, T>(scopes: I, state: &AppState) -> AuthMiddlewareState 340 + where 341 + I: IntoIterator<Item = T>, 342 + T: Into<String>, 343 + { 344 + AuthMiddlewareState { 345 + app_state: state.clone(), 346 + rules: AuthRules::ScopeEqualsAll(scopes.into_iter().map(|s| s.into()).collect()), 347 + } 348 + } 349 + 350 + // ============================================================================ 351 + // Combined Rule Helpers (Identity + Scope) 352 + // ============================================================================ 353 + 354 + /// Creates an `AuthMiddlewareState` requiring handle to end with suffix AND have a specific scope. 355 + /// 356 + /// # Example 357 + /// ```ignore 358 + /// .route("/xrpc/community.blacksky.feed.generator", 359 + /// post(handler).layer(from_fn_with_state( 360 + /// handle_ends_with_and_scope(".blacksky.team", "transition:generic", &state), 361 + /// auth_middleware 362 + /// ))) 363 + /// ``` 364 + pub fn handle_ends_with_and_scope( 365 + suffix: impl Into<String>, 366 + scope: impl Into<String>, 367 + state: &AppState, 368 + ) -> AuthMiddlewareState { 369 + AuthMiddlewareState { 370 + app_state: state.clone(), 371 + rules: AuthRules::All(vec![ 372 + AuthRules::HandleEndsWith(suffix.into()), 373 + AuthRules::ScopeEquals(scope.into()), 374 + ]), 375 + } 376 + } 377 + 378 + /// Creates an `AuthMiddlewareState` requiring handle to end with suffix AND have ALL specified scopes. 379 + /// 380 + /// # Example 381 + /// ```ignore 382 + /// .route("/xrpc/community.blacksky.admin.manage", 383 + /// post(handler).layer(from_fn_with_state( 384 + /// handle_ends_with_and_scopes(".blacksky.team", ["transition:generic", "identity:*"], &state), 385 + /// auth_middleware 386 + /// ))) 387 + /// ``` 388 + pub fn handle_ends_with_and_scopes<I, T>( 389 + suffix: impl Into<String>, 390 + scopes: I, 391 + state: &AppState, 392 + ) -> AuthMiddlewareState 393 + where 394 + I: IntoIterator<Item = T>, 395 + T: Into<String>, 396 + { 397 + AuthMiddlewareState { 398 + app_state: state.clone(), 399 + rules: AuthRules::All(vec![ 400 + AuthRules::HandleEndsWith(suffix.into()), 401 + AuthRules::ScopeEqualsAll(scopes.into_iter().map(|s| s.into()).collect()), 402 + ]), 403 + } 404 + } 405 + 406 + /// Creates an `AuthMiddlewareState` requiring DID to equal value AND have a specific scope. 407 + /// 408 + /// # Example 409 + /// ```ignore 410 + /// .route("/xrpc/com.atproto.admin.deleteAccount", 411 + /// post(handler).layer(from_fn_with_state( 412 + /// did_with_scope("did:plc:rnpkyqnmsw4ipey6eotbdnnf", "transition:generic", &state), 413 + /// auth_middleware 414 + /// ))) 415 + /// ``` 416 + pub fn did_with_scope( 417 + did: impl Into<String>, 418 + scope: impl Into<String>, 419 + state: &AppState, 420 + ) -> AuthMiddlewareState { 421 + AuthMiddlewareState { 422 + app_state: state.clone(), 423 + rules: AuthRules::All(vec![ 424 + AuthRules::DidEquals(did.into()), 425 + AuthRules::ScopeEquals(scope.into()), 426 + ]), 427 + } 428 + } 429 + 430 + /// Creates an `AuthMiddlewareState` requiring DID to equal value AND have ALL specified scopes. 431 + /// 432 + /// # Example 433 + /// ```ignore 434 + /// .route("/xrpc/com.atproto.admin.fullAccess", 435 + /// post(handler).layer(from_fn_with_state( 436 + /// did_with_scopes("did:plc:rnpkyqnmsw4ipey6eotbdnnf", ["transition:generic", "identity:*"], &state), 437 + /// auth_middleware 438 + /// ))) 439 + /// ``` 440 + pub fn did_with_scopes<I, T>( 441 + did: impl Into<String>, 442 + scopes: I, 443 + state: &AppState, 444 + ) -> AuthMiddlewareState 445 + where 446 + I: IntoIterator<Item = T>, 447 + T: Into<String>, 448 + { 449 + AuthMiddlewareState { 450 + app_state: state.clone(), 451 + rules: AuthRules::All(vec![ 452 + AuthRules::DidEquals(did.into()), 453 + AuthRules::ScopeEqualsAll(scopes.into_iter().map(|s| s.into()).collect()), 454 + ]), 455 + } 456 + }
+16
src/auth/mod.rs
··· 1 + mod cache; 2 + mod middleware; 3 + mod rules; 4 + 5 + pub use cache::HandleCache; 6 + pub use middleware::{ 7 + // Core middleware 8 + auth_middleware, with_rules, AuthMiddlewareState, 9 + // Identity helpers 10 + did_equals, did_equals_any, handle_ends_with, handle_ends_with_any, 11 + // Scope helpers 12 + scope_all, scope_any, scope_equals, 13 + // Combined helpers (identity + scope) 14 + did_with_scope, did_with_scopes, handle_ends_with_and_scope, handle_ends_with_and_scopes, 15 + }; 16 + pub use rules::{AuthRules, SessionData};
+694
src/auth/rules.rs
··· 1 + /// Authentication rules that can be validated against session data 2 + #[derive(Debug, Clone, PartialEq)] 3 + pub enum AuthRules { 4 + /// Handle must end with the specified suffix 5 + HandleEndsWith(String), 6 + /// Handle must end with any of the specified suffixes (OR logic) 7 + HandleEndsWithAny(Vec<String>), 8 + /// DID must exactly match the specified value 9 + DidEquals(String), 10 + /// DID must match any of the specified values (OR logic) 11 + DidEqualsAny(Vec<String>), 12 + /// Session must have the specified OAuth scope 13 + ScopeEquals(String), 14 + /// Session must have ANY of the specified scopes (OR logic) 15 + ScopeEqualsAny(Vec<String>), 16 + /// Session must have ALL of the specified scopes (AND logic) 17 + ScopeEqualsAll(Vec<String>), 18 + /// All nested rules must be satisfied (AND logic) 19 + All(Vec<AuthRules>), 20 + /// At least one nested rule must be satisfied (OR logic) 21 + Any(Vec<AuthRules>), 22 + } 23 + 24 + /// Session data used for authentication validation 25 + #[derive(Debug, Clone)] 26 + pub struct SessionData { 27 + /// The user's DID 28 + pub did: String, 29 + /// The user's handle 30 + pub handle: String, 31 + /// OAuth 2.0 scopes granted to this session 32 + pub scopes: Vec<String>, 33 + } 34 + 35 + impl AuthRules { 36 + /// Validates if the given session data meets the authentication requirements 37 + pub fn validate(&self, session_data: &SessionData) -> bool { 38 + match self { 39 + AuthRules::HandleEndsWith(suffix) => session_data.handle.ends_with(suffix), 40 + AuthRules::HandleEndsWithAny(suffixes) => { 41 + suffixes.iter().any(|s| session_data.handle.ends_with(s)) 42 + } 43 + AuthRules::DidEquals(did) => session_data.did == *did, 44 + AuthRules::DidEqualsAny(dids) => dids.iter().any(|d| session_data.did == *d), 45 + AuthRules::ScopeEquals(scope) => has_scope(&session_data.scopes, scope), 46 + AuthRules::ScopeEqualsAny(scopes) => has_any_scope(&session_data.scopes, scopes), 47 + AuthRules::ScopeEqualsAll(scopes) => has_all_scopes(&session_data.scopes, scopes), 48 + AuthRules::All(rules) => rules.iter().all(|r| r.validate(session_data)), 49 + AuthRules::Any(rules) => rules.iter().any(|r| r.validate(session_data)), 50 + } 51 + } 52 + } 53 + 54 + /// Checks if the session has a specific scope 55 + pub fn has_scope(scopes: &[String], required_scope: &str) -> bool { 56 + scopes.iter().any(|s| s == required_scope) 57 + } 58 + 59 + /// Checks if the session has ANY of the required scopes (OR logic) 60 + pub fn has_any_scope(scopes: &[String], required_scopes: &[String]) -> bool { 61 + required_scopes.iter().any(|req| has_scope(scopes, req)) 62 + } 63 + 64 + /// Checks if the session has ALL of the required scopes (AND logic) 65 + pub fn has_all_scopes(scopes: &[String], required_scopes: &[String]) -> bool { 66 + required_scopes.iter().all(|req| has_scope(scopes, req)) 67 + } 68 + 69 + #[cfg(test)] 70 + mod tests { 71 + use super::*; 72 + 73 + fn test_session(did: &str, handle: &str, scopes: Vec<&str>) -> SessionData { 74 + SessionData { 75 + did: did.to_string(), 76 + handle: handle.to_string(), 77 + scopes: scopes.into_iter().map(|s| s.to_string()).collect(), 78 + } 79 + } 80 + 81 + #[test] 82 + fn test_handle_ends_with() { 83 + let rules = AuthRules::HandleEndsWith(".blacksky.team".into()); 84 + 85 + let valid = test_session("did:plc:123", "alice.blacksky.team", vec!["atproto"]); 86 + assert!(rules.validate(&valid)); 87 + 88 + let invalid = test_session("did:plc:123", "alice.bsky.social", vec!["atproto"]); 89 + assert!(!rules.validate(&invalid)); 90 + } 91 + 92 + #[test] 93 + fn test_handle_ends_with_any() { 94 + let rules = AuthRules::HandleEndsWithAny(vec![".blacksky.team".into(), ".bsky.team".into()]); 95 + 96 + let valid1 = test_session("did:plc:123", "alice.blacksky.team", vec!["atproto"]); 97 + assert!(rules.validate(&valid1)); 98 + 99 + let valid2 = test_session("did:plc:123", "bob.bsky.team", vec!["atproto"]); 100 + assert!(rules.validate(&valid2)); 101 + 102 + let invalid = test_session("did:plc:123", "charlie.bsky.social", vec!["atproto"]); 103 + assert!(!rules.validate(&invalid)); 104 + } 105 + 106 + #[test] 107 + fn test_did_equals() { 108 + let rules = AuthRules::DidEquals("did:plc:alice".into()); 109 + 110 + let valid = test_session("did:plc:alice", "alice.bsky.social", vec!["atproto"]); 111 + assert!(rules.validate(&valid)); 112 + 113 + let invalid = test_session("did:plc:bob", "bob.bsky.social", vec!["atproto"]); 114 + assert!(!rules.validate(&invalid)); 115 + } 116 + 117 + #[test] 118 + fn test_any_combinator() { 119 + let rules = AuthRules::Any(vec![ 120 + AuthRules::DidEquals("did:plc:admin".into()), 121 + AuthRules::HandleEndsWith(".blacksky.team".into()), 122 + ]); 123 + 124 + // First condition met 125 + let valid1 = test_session("did:plc:admin", "admin.bsky.social", vec!["atproto"]); 126 + assert!(rules.validate(&valid1)); 127 + 128 + // Second condition met 129 + let valid2 = test_session("did:plc:user", "user.blacksky.team", vec!["atproto"]); 130 + assert!(rules.validate(&valid2)); 131 + 132 + // Neither condition met 133 + let invalid = test_session("did:plc:user", "user.bsky.social", vec!["atproto"]); 134 + assert!(!rules.validate(&invalid)); 135 + } 136 + 137 + #[test] 138 + fn test_all_combinator() { 139 + let rules = AuthRules::All(vec![ 140 + AuthRules::HandleEndsWith(".blacksky.team".into()), 141 + AuthRules::DidEqualsAny(vec!["did:plc:alice".into(), "did:plc:bob".into()]), 142 + ]); 143 + 144 + // Both conditions met 145 + let valid = test_session("did:plc:alice", "alice.blacksky.team", vec!["atproto"]); 146 + assert!(rules.validate(&valid)); 147 + 148 + // Handle wrong 149 + let invalid1 = test_session("did:plc:alice", "alice.bsky.social", vec!["atproto"]); 150 + assert!(!rules.validate(&invalid1)); 151 + 152 + // DID wrong 153 + let invalid2 = test_session("did:plc:charlie", "charlie.blacksky.team", vec!["atproto"]); 154 + assert!(!rules.validate(&invalid2)); 155 + } 156 + 157 + // ======================================================================== 158 + // Scope Tests 159 + // ======================================================================== 160 + 161 + #[test] 162 + fn test_scope_equals() { 163 + let rules = AuthRules::ScopeEquals("transition:generic".into()); 164 + 165 + let valid = test_session("did:plc:123", "alice.bsky.social", vec!["atproto", "transition:generic"]); 166 + assert!(rules.validate(&valid)); 167 + 168 + let invalid = test_session("did:plc:123", "alice.bsky.social", vec!["atproto"]); 169 + assert!(!rules.validate(&invalid)); 170 + } 171 + 172 + #[test] 173 + fn test_scope_any() { 174 + let rules = AuthRules::ScopeEqualsAny(vec![ 175 + "transition:generic".into(), 176 + "repo:app.bsky.feed.post".into(), 177 + ]); 178 + 179 + // Has first scope 180 + let valid1 = test_session("did:plc:123", "alice.bsky.social", vec!["atproto", "transition:generic"]); 181 + assert!(rules.validate(&valid1)); 182 + 183 + // Has second scope 184 + let valid2 = test_session("did:plc:123", "alice.bsky.social", vec!["atproto", "repo:app.bsky.feed.post"]); 185 + assert!(rules.validate(&valid2)); 186 + 187 + // Has neither 188 + let invalid = test_session("did:plc:123", "alice.bsky.social", vec!["atproto"]); 189 + assert!(!rules.validate(&invalid)); 190 + } 191 + 192 + #[test] 193 + fn test_scope_all() { 194 + let rules = AuthRules::ScopeEqualsAll(vec![ 195 + "atproto".into(), 196 + "transition:generic".into(), 197 + ]); 198 + 199 + // Has both scopes 200 + let valid = test_session("did:plc:123", "alice.bsky.social", vec!["atproto", "transition:generic"]); 201 + assert!(rules.validate(&valid)); 202 + 203 + // Missing one scope 204 + let invalid = test_session("did:plc:123", "alice.bsky.social", vec!["atproto"]); 205 + assert!(!rules.validate(&invalid)); 206 + } 207 + 208 + // ======================================================================== 209 + // Combined Rules Tests (Identity + Scope) 210 + // ======================================================================== 211 + 212 + #[test] 213 + fn test_handle_ends_with_and_scope() { 214 + let rules = AuthRules::All(vec![ 215 + AuthRules::HandleEndsWith(".blacksky.team".into()), 216 + AuthRules::ScopeEquals("transition:generic".into()), 217 + ]); 218 + 219 + // Both conditions met 220 + let valid = test_session( 221 + "did:plc:123", 222 + "alice.blacksky.team", 223 + vec!["atproto", "transition:generic"], 224 + ); 225 + assert!(rules.validate(&valid)); 226 + 227 + // Handle correct, scope wrong 228 + let invalid1 = test_session("did:plc:123", "alice.blacksky.team", vec!["atproto"]); 229 + assert!(!rules.validate(&invalid1)); 230 + 231 + // Scope correct, handle wrong 232 + let invalid2 = test_session( 233 + "did:plc:123", 234 + "alice.bsky.social", 235 + vec!["atproto", "transition:generic"], 236 + ); 237 + assert!(!rules.validate(&invalid2)); 238 + } 239 + 240 + #[test] 241 + fn test_did_with_scope() { 242 + let rules = AuthRules::All(vec![ 243 + AuthRules::DidEquals("did:plc:rnpkyqnmsw4ipey6eotbdnnf".into()), 244 + AuthRules::ScopeEquals("transition:generic".into()), 245 + ]); 246 + 247 + // Both conditions met 248 + let valid = test_session( 249 + "did:plc:rnpkyqnmsw4ipey6eotbdnnf", 250 + "admin.bsky.social", 251 + vec!["atproto", "transition:generic"], 252 + ); 253 + assert!(rules.validate(&valid)); 254 + 255 + // DID correct, scope wrong 256 + let invalid1 = test_session( 257 + "did:plc:rnpkyqnmsw4ipey6eotbdnnf", 258 + "admin.bsky.social", 259 + vec!["atproto"], 260 + ); 261 + assert!(!rules.validate(&invalid1)); 262 + 263 + // Scope correct, DID wrong 264 + let invalid2 = test_session( 265 + "did:plc:wrongdid", 266 + "admin.bsky.social", 267 + vec!["atproto", "transition:generic"], 268 + ); 269 + assert!(!rules.validate(&invalid2)); 270 + } 271 + 272 + #[test] 273 + fn test_complex_admin_or_moderator_rule() { 274 + // Admin DID OR (moderator handle + scope) 275 + let rules = AuthRules::Any(vec![ 276 + AuthRules::DidEquals("did:plc:rnpkyqnmsw4ipey6eotbdnnf".into()), 277 + AuthRules::All(vec![ 278 + AuthRules::HandleEndsWith(".mod.team".into()), 279 + AuthRules::ScopeEquals("account:email".into()), 280 + ]), 281 + ]); 282 + 283 + // Admin DID (doesn't need scope) 284 + let admin = test_session("did:plc:rnpkyqnmsw4ipey6eotbdnnf", "admin.bsky.social", vec!["atproto"]); 285 + assert!(rules.validate(&admin)); 286 + 287 + // Moderator with correct handle and scope 288 + let mod_valid = test_session( 289 + "did:plc:somemod", 290 + "alice.mod.team", 291 + vec!["atproto", "account:email"], 292 + ); 293 + assert!(rules.validate(&mod_valid)); 294 + 295 + // Moderator handle but missing scope 296 + let mod_no_scope = test_session("did:plc:somemod", "alice.mod.team", vec!["atproto"]); 297 + assert!(!rules.validate(&mod_no_scope)); 298 + 299 + // Has scope but not moderator handle 300 + let not_mod = test_session( 301 + "did:plc:someuser", 302 + "alice.bsky.social", 303 + vec!["atproto", "account:email"], 304 + ); 305 + assert!(!rules.validate(&not_mod)); 306 + } 307 + 308 + // ======================================================================== 309 + // Additional Coverage Tests 310 + // ======================================================================== 311 + 312 + #[test] 313 + fn test_did_equals_any() { 314 + let rules = AuthRules::DidEqualsAny(vec![ 315 + "did:plc:alice123456789012345678".into(), 316 + "did:plc:bob12345678901234567890".into(), 317 + "did:plc:charlie12345678901234567".into(), 318 + ]); 319 + 320 + // First DID matches 321 + let valid1 = test_session("did:plc:alice123456789012345678", "alice.bsky.social", vec!["atproto"]); 322 + assert!(rules.validate(&valid1)); 323 + 324 + // Second DID matches 325 + let valid2 = test_session("did:plc:bob12345678901234567890", "bob.bsky.social", vec!["atproto"]); 326 + assert!(rules.validate(&valid2)); 327 + 328 + // Third DID matches 329 + let valid3 = test_session("did:plc:charlie12345678901234567", "charlie.bsky.social", vec!["atproto"]); 330 + assert!(rules.validate(&valid3)); 331 + 332 + // DID not in list 333 + let invalid = test_session("did:plc:unknown1234567890123456", "unknown.bsky.social", vec!["atproto"]); 334 + assert!(!rules.validate(&invalid)); 335 + 336 + // Partial match should fail (prefix) 337 + let partial = test_session("did:plc:alice", "alice.bsky.social", vec!["atproto"]); 338 + assert!(!rules.validate(&partial)); 339 + } 340 + 341 + // ======================================================================== 342 + // Empty Combinator Edge Cases 343 + // ======================================================================== 344 + 345 + #[test] 346 + fn test_empty_any_returns_false() { 347 + // Any with empty rules should return false (no rule can be satisfied) 348 + let rules = AuthRules::Any(vec![]); 349 + let session = test_session("did:plc:test12345678901234567", "test.bsky.social", vec!["atproto"]); 350 + assert!(!rules.validate(&session)); 351 + } 352 + 353 + #[test] 354 + fn test_empty_all_returns_true() { 355 + // All with empty rules should return true (vacuous truth - all zero rules are satisfied) 356 + let rules = AuthRules::All(vec![]); 357 + let session = test_session("did:plc:test12345678901234567", "test.bsky.social", vec!["atproto"]); 358 + assert!(rules.validate(&session)); 359 + } 360 + 361 + // ======================================================================== 362 + // Handle Edge Cases 363 + // ======================================================================== 364 + 365 + #[test] 366 + fn test_handle_exact_suffix_match() { 367 + // Handle that IS exactly the suffix should still match 368 + let rules = AuthRules::HandleEndsWith(".blacksky.team".into()); 369 + 370 + // Handle is exactly the suffix 371 + let exact = test_session("did:plc:test12345678901234567", ".blacksky.team", vec!["atproto"]); 372 + assert!(rules.validate(&exact)); 373 + 374 + // Normal case - handle with prefix 375 + let normal = test_session("did:plc:test12345678901234567", "alice.blacksky.team", vec!["atproto"]); 376 + assert!(rules.validate(&normal)); 377 + 378 + // Empty handle should not match 379 + let empty = test_session("did:plc:test12345678901234567", "", vec!["atproto"]); 380 + assert!(!rules.validate(&empty)); 381 + } 382 + 383 + // ======================================================================== 384 + // Deeply Nested Combinators 385 + // ======================================================================== 386 + 387 + #[test] 388 + fn test_deeply_nested_rules() { 389 + // Complex nested rule: Any(All(Any(...), ...), All(...)) 390 + // Scenario: (Admin OR VIP) AND (has scope OR team member) 391 + // OR 392 + // Specific moderator DID 393 + let rules = AuthRules::Any(vec![ 394 + // Branch 1: Complex nested condition 395 + AuthRules::All(vec![ 396 + // Must be admin or VIP 397 + AuthRules::Any(vec![ 398 + AuthRules::DidEquals("did:plc:admin123456789012345".into()), 399 + AuthRules::HandleEndsWith(".vip.social".into()), 400 + ]), 401 + // AND must have scope or be team member 402 + AuthRules::Any(vec![ 403 + AuthRules::ScopeEquals("transition:generic".into()), 404 + AuthRules::HandleEndsWith(".team.internal".into()), 405 + ]), 406 + ]), 407 + // Branch 2: Specific moderator bypass 408 + AuthRules::DidEquals("did:plc:moderator12345678901".into()), 409 + ]); 410 + 411 + // Admin with required scope - should pass via Branch 1 412 + let admin_with_scope = test_session( 413 + "did:plc:admin123456789012345", 414 + "admin.bsky.social", 415 + vec!["atproto", "transition:generic"], 416 + ); 417 + assert!(rules.validate(&admin_with_scope)); 418 + 419 + // VIP with required scope - should pass via Branch 1 420 + let vip_with_scope = test_session( 421 + "did:plc:somevip1234567890123", 422 + "alice.vip.social", 423 + vec!["atproto", "transition:generic"], 424 + ); 425 + assert!(rules.validate(&vip_with_scope)); 426 + 427 + // Moderator bypass - should pass via Branch 2 428 + let moderator = test_session( 429 + "did:plc:moderator12345678901", 430 + "mod.bsky.social", 431 + vec!["atproto"], 432 + ); 433 + assert!(rules.validate(&moderator)); 434 + 435 + // Admin without scope and not team member - should fail 436 + let admin_no_scope = test_session( 437 + "did:plc:admin123456789012345", 438 + "admin.bsky.social", 439 + vec!["atproto"], 440 + ); 441 + assert!(!rules.validate(&admin_no_scope)); 442 + 443 + // Random user - should fail 444 + let random = test_session( 445 + "did:plc:random12345678901234", 446 + "random.bsky.social", 447 + vec!["atproto", "transition:generic"], 448 + ); 449 + assert!(!rules.validate(&random)); 450 + } 451 + 452 + // ======================================================================== 453 + // ATProto Scope Specification Tests 454 + // ======================================================================== 455 + 456 + #[test] 457 + fn test_scope_with_query_params() { 458 + // ATProto scopes can have query parameters 459 + // These are treated as literal strings (exact matching) 460 + 461 + // blob scope with accept parameter 462 + let blob_rules = AuthRules::ScopeEquals("blob?accept=image/*".into()); 463 + let has_blob = test_session( 464 + "did:plc:test12345678901234567", 465 + "test.bsky.social", 466 + vec!["atproto", "blob?accept=image/*"], 467 + ); 468 + assert!(blob_rules.validate(&has_blob)); 469 + 470 + // Different query param should not match 471 + let wrong_blob = test_session( 472 + "did:plc:test12345678901234567", 473 + "test.bsky.social", 474 + vec!["atproto", "blob?accept=video/*"], 475 + ); 476 + assert!(!blob_rules.validate(&wrong_blob)); 477 + 478 + // account:repo with action parameter 479 + let account_rules = AuthRules::ScopeEquals("account:repo?action=manage".into()); 480 + let has_account = test_session( 481 + "did:plc:test12345678901234567", 482 + "test.bsky.social", 483 + vec!["atproto", "account:repo?action=manage"], 484 + ); 485 + assert!(account_rules.validate(&has_account)); 486 + 487 + // Multiple query params 488 + let multi_param_rules = AuthRules::ScopeEquals("blob?accept=image/*&accept=video/*".into()); 489 + let has_multi = test_session( 490 + "did:plc:test12345678901234567", 491 + "test.bsky.social", 492 + vec!["atproto", "blob?accept=image/*&accept=video/*"], 493 + ); 494 + assert!(multi_param_rules.validate(&has_multi)); 495 + } 496 + 497 + #[test] 498 + fn test_scope_exact_matching_no_wildcards() { 499 + // ATProto spec: Wildcards do NOT work for collections 500 + // repo:app.bsky.feed.post should NOT match repo:app.bsky.feed.* 501 + // This test verifies our exact matching is correct 502 + 503 + let rules = AuthRules::ScopeEquals("repo:app.bsky.feed.post".into()); 504 + 505 + // Exact match works 506 + let exact = test_session( 507 + "did:plc:test12345678901234567", 508 + "test.bsky.social", 509 + vec!["atproto", "repo:app.bsky.feed.post"], 510 + ); 511 + assert!(rules.validate(&exact)); 512 + 513 + // Wildcard scope in JWT should NOT satisfy specific requirement 514 + // (user has repo:app.bsky.feed.* but endpoint requires repo:app.bsky.feed.post) 515 + let wildcard = test_session( 516 + "did:plc:test12345678901234567", 517 + "test.bsky.social", 518 + vec!["atproto", "repo:app.bsky.feed.*"], 519 + ); 520 + assert!(!rules.validate(&wildcard)); 521 + 522 + // Different collection should not match 523 + let different = test_session( 524 + "did:plc:test12345678901234567", 525 + "test.bsky.social", 526 + vec!["atproto", "repo:app.bsky.feed.like"], 527 + ); 528 + assert!(!rules.validate(&different)); 529 + 530 + // Prefix should not match 531 + let prefix = test_session( 532 + "did:plc:test12345678901234567", 533 + "test.bsky.social", 534 + vec!["atproto", "repo:app.bsky.feed"], 535 + ); 536 + assert!(!rules.validate(&prefix)); 537 + 538 + // repo:* should not match specific collection (exact matching) 539 + let full_wildcard = test_session( 540 + "did:plc:test12345678901234567", 541 + "test.bsky.social", 542 + vec!["atproto", "repo:*"], 543 + ); 544 + assert!(!rules.validate(&full_wildcard)); 545 + } 546 + 547 + #[test] 548 + fn test_scope_case_sensitivity() { 549 + // OAuth scopes are case-sensitive per RFC 6749 550 + 551 + let rules = AuthRules::ScopeEquals("atproto".into()); 552 + 553 + // Exact case matches 554 + let exact = test_session("did:plc:test12345678901234567", "test.bsky.social", vec!["atproto"]); 555 + assert!(rules.validate(&exact)); 556 + 557 + // UPPERCASE should NOT match 558 + let upper = test_session("did:plc:test12345678901234567", "test.bsky.social", vec!["ATPROTO"]); 559 + assert!(!rules.validate(&upper)); 560 + 561 + // Mixed case should NOT match 562 + let mixed = test_session("did:plc:test12345678901234567", "test.bsky.social", vec!["AtProto"]); 563 + assert!(!rules.validate(&mixed)); 564 + 565 + // Test with namespaced scope 566 + let ns_rules = AuthRules::ScopeEquals("transition:generic".into()); 567 + let ns_upper = test_session( 568 + "did:plc:test12345678901234567", 569 + "test.bsky.social", 570 + vec!["TRANSITION:GENERIC"], 571 + ); 572 + assert!(!ns_rules.validate(&ns_upper)); 573 + } 574 + 575 + #[test] 576 + fn test_scope_with_wildcards_exact_match() { 577 + // Wildcard scopes like blob:*/* and identity:* are stored as literal strings 578 + // The middleware does exact matching, so JWT must contain the exact string 579 + 580 + // blob:*/* - full blob wildcard 581 + let blob_rules = AuthRules::ScopeEquals("blob:*/*".into()); 582 + let has_blob_wildcard = test_session( 583 + "did:plc:test12345678901234567", 584 + "test.bsky.social", 585 + vec!["atproto", "blob:*/*"], 586 + ); 587 + assert!(blob_rules.validate(&has_blob_wildcard)); 588 + 589 + // Specific blob type should NOT match blob:*/* requirement 590 + let has_specific_blob = test_session( 591 + "did:plc:test12345678901234567", 592 + "test.bsky.social", 593 + vec!["atproto", "blob:image/png"], 594 + ); 595 + assert!(!blob_rules.validate(&has_specific_blob)); 596 + 597 + // identity:* - full identity wildcard 598 + let identity_rules = AuthRules::ScopeEquals("identity:*".into()); 599 + let has_identity_wildcard = test_session( 600 + "did:plc:test12345678901234567", 601 + "test.bsky.social", 602 + vec!["atproto", "identity:*"], 603 + ); 604 + assert!(identity_rules.validate(&has_identity_wildcard)); 605 + 606 + // Specific identity scope should NOT match identity:* requirement 607 + let has_specific_identity = test_session( 608 + "did:plc:test12345678901234567", 609 + "test.bsky.social", 610 + vec!["atproto", "identity:handle"], 611 + ); 612 + assert!(!identity_rules.validate(&has_specific_identity)); 613 + } 614 + 615 + // ======================================================================== 616 + // Scope Helper Function Tests 617 + // ======================================================================== 618 + 619 + #[test] 620 + fn test_has_scope_helper() { 621 + let scopes: Vec<String> = vec![ 622 + "atproto".to_string(), 623 + "transition:generic".to_string(), 624 + "repo:app.bsky.feed.post".to_string(), 625 + ]; 626 + 627 + // Present scopes 628 + assert!(has_scope(&scopes, "atproto")); 629 + assert!(has_scope(&scopes, "transition:generic")); 630 + assert!(has_scope(&scopes, "repo:app.bsky.feed.post")); 631 + 632 + // Absent scopes 633 + assert!(!has_scope(&scopes, "identity:*")); 634 + assert!(!has_scope(&scopes, "")); 635 + assert!(!has_scope(&scopes, "ATPROTO")); // Case sensitive 636 + 637 + // Empty scopes list 638 + let empty: Vec<String> = vec![]; 639 + assert!(!has_scope(&empty, "atproto")); 640 + } 641 + 642 + #[test] 643 + fn test_has_any_scope_helper() { 644 + let scopes: Vec<String> = vec![ 645 + "atproto".to_string(), 646 + "repo:app.bsky.feed.post".to_string(), 647 + ]; 648 + 649 + // Has one of the required scopes 650 + let required1 = vec!["transition:generic".to_string(), "atproto".to_string()]; 651 + assert!(has_any_scope(&scopes, &required1)); 652 + 653 + // Has none of the required scopes 654 + let required2 = vec!["transition:generic".to_string(), "identity:*".to_string()]; 655 + assert!(!has_any_scope(&scopes, &required2)); 656 + 657 + // Empty required list - should return false (no scope to match) 658 + let empty_required: Vec<String> = vec![]; 659 + assert!(!has_any_scope(&scopes, &empty_required)); 660 + 661 + // Empty scopes list 662 + let empty_scopes: Vec<String> = vec![]; 663 + assert!(!has_any_scope(&empty_scopes, &required1)); 664 + } 665 + 666 + #[test] 667 + fn test_has_all_scopes_helper() { 668 + let scopes: Vec<String> = vec![ 669 + "atproto".to_string(), 670 + "transition:generic".to_string(), 671 + "repo:app.bsky.feed.post".to_string(), 672 + ]; 673 + 674 + // Has all required scopes 675 + let required1 = vec!["atproto".to_string(), "transition:generic".to_string()]; 676 + assert!(has_all_scopes(&scopes, &required1)); 677 + 678 + // Missing one required scope 679 + let required2 = vec!["atproto".to_string(), "identity:*".to_string()]; 680 + assert!(!has_all_scopes(&scopes, &required2)); 681 + 682 + // Empty required list - should return true (vacuously all zero required are present) 683 + let empty_required: Vec<String> = vec![]; 684 + assert!(has_all_scopes(&scopes, &empty_required)); 685 + 686 + // Empty scopes list with requirements 687 + let empty_scopes: Vec<String> = vec![]; 688 + assert!(!has_all_scopes(&empty_scopes, &required1)); 689 + 690 + // Single scope requirement 691 + let single = vec!["atproto".to_string()]; 692 + assert!(has_all_scopes(&scopes, &single)); 693 + } 694 + }
+3
src/main.rs
··· 37 37 use tracing::log; 38 38 use tracing_subscriber::{EnvFilter, fmt, prelude::*}; 39 39 40 + mod auth; 40 41 mod gate; 41 42 pub mod helpers; 42 43 mod middleware; ··· 153 154 mailer: AsyncSmtpTransport<Tokio1Executor>, 154 155 template_engine: Engine<Handlebars<'static>>, 155 156 resolver: Arc<PublicResolver>, 157 + handle_cache: auth::HandleCache, 156 158 app_config: AppConfig, 157 159 } 158 160 ··· 278 280 mailer, 279 281 template_engine: Engine::from(hbs), 280 282 resolver: Arc::new(resolver), 283 + handle_cache: auth::HandleCache::new(), 281 284 app_config: AppConfig::new(), 282 285 }; 283 286