this repo has no description

Initial idea

lewis.moe 9c326604

+17
.env.example
···
··· 1 + SERVER_HOST=127.0.0.1 2 + SERVER_PORT=3000 3 + 4 + DATABASE_URL=postgres://postgres:postgres@localhost:5432/pds 5 + 6 + OBJECT_STORAGE_ENDPOINT= 7 + OBJECT_STORAGE_REGION=us-east-1 8 + OBJECT_STORAGE_BUCKET=pds-blobs 9 + OBJECT_STORAGE_ACCESS_KEY= 10 + OBJECT_STORAGE_SECRET_KEY= 11 + 12 + # Set to 'true' for MinIO or other services that need path-style addressing 13 + OBJECT_STORAGE_FORCE_PATH_STYLE=false 14 + 15 + JWT_SECRET=your-super-secret-jwt-key-please-change-me 16 + PDS_HOSTNAME=localhost:3000 # The public-facing hostname of the PDS 17 + PLC_URL=plc.directory
+6
.gitignore
···
··· 1 + /target 2 + src_old 3 + .sqlx 4 + 5 + .env 6 +
+5620
Cargo.lock
···
··· 1 + # This file is automatically @generated by Cargo. 2 + # It is not intended for manual editing. 3 + version = 4 4 + 5 + [[package]] 6 + name = "abnf" 7 + version = "0.13.0" 8 + source = "registry+https://github.com/rust-lang/crates.io-index" 9 + checksum = "087113bd50d9adce24850eed5d0476c7d199d532fce8fab5173650331e09033a" 10 + dependencies = [ 11 + "abnf-core", 12 + "nom", 13 + ] 14 + 15 + [[package]] 16 + name = "abnf-core" 17 + version = "0.5.0" 18 + source = "registry+https://github.com/rust-lang/crates.io-index" 19 + checksum = "c44e09c43ae1c368fb91a03a566472d0087c26cf7e1b9e8e289c14ede681dd7d" 20 + dependencies = [ 21 + "nom", 22 + ] 23 + 24 + [[package]] 25 + name = "adler2" 26 + version = "2.0.1" 27 + source = "registry+https://github.com/rust-lang/crates.io-index" 28 + checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" 29 + 30 + [[package]] 31 + name = "adler32" 32 + version = "1.2.0" 33 + source = "registry+https://github.com/rust-lang/crates.io-index" 34 + checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" 35 + 36 + [[package]] 37 + name = "aho-corasick" 38 + version = "1.1.4" 39 + source = "registry+https://github.com/rust-lang/crates.io-index" 40 + checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" 41 + dependencies = [ 42 + "memchr", 43 + ] 44 + 45 + [[package]] 46 + name = "aliasable" 47 + version = "0.1.3" 48 + source = "registry+https://github.com/rust-lang/crates.io-index" 49 + checksum = "250f629c0161ad8107cf89319e990051fae62832fd343083bea452d93e2205fd" 50 + 51 + [[package]] 52 + name = "alloc-no-stdlib" 53 + version = "2.0.4" 54 + source = "registry+https://github.com/rust-lang/crates.io-index" 55 + checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" 56 + 57 + [[package]] 58 + name = "alloc-stdlib" 59 + version = "0.2.2" 60 + source = "registry+https://github.com/rust-lang/crates.io-index" 61 + checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" 62 + dependencies = [ 63 + "alloc-no-stdlib", 64 + ] 65 + 66 + [[package]] 67 + name = "allocator-api2" 68 + version = "0.2.21" 69 + source = "registry+https://github.com/rust-lang/crates.io-index" 70 + checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" 71 + 72 + [[package]] 73 + name = "android_system_properties" 74 + version = "0.1.5" 75 + source = "registry+https://github.com/rust-lang/crates.io-index" 76 + checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" 77 + dependencies = [ 78 + "libc", 79 + ] 80 + 81 + [[package]] 82 + name = "anyhow" 83 + version = "1.0.100" 84 + source = "registry+https://github.com/rust-lang/crates.io-index" 85 + checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" 86 + 87 + [[package]] 88 + name = "ascii" 89 + version = "1.1.0" 90 + source = "registry+https://github.com/rust-lang/crates.io-index" 91 + checksum = "d92bec98840b8f03a5ff5413de5293bfcd8bf96467cf5452609f939ec6f5de16" 92 + 93 + [[package]] 94 + name = "async-compression" 95 + version = "0.4.34" 96 + source = "registry+https://github.com/rust-lang/crates.io-index" 97 + checksum = "0e86f6d3dc9dc4352edeea6b8e499e13e3f5dc3b964d7ca5fd411415a3498473" 98 + dependencies = [ 99 + "compression-codecs", 100 + "compression-core", 101 + "futures-core", 102 + "pin-project-lite", 103 + "tokio", 104 + ] 105 + 106 + [[package]] 107 + name = "async-trait" 108 + version = "0.1.89" 109 + source = "registry+https://github.com/rust-lang/crates.io-index" 110 + checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" 111 + dependencies = [ 112 + "proc-macro2", 113 + "quote", 114 + "syn 2.0.111", 115 + ] 116 + 117 + [[package]] 118 + name = "atoi" 119 + version = "2.0.0" 120 + source = "registry+https://github.com/rust-lang/crates.io-index" 121 + checksum = "f28d99ec8bfea296261ca1af174f24225171fea9664ba9003cbebee704810528" 122 + dependencies = [ 123 + "num-traits", 124 + ] 125 + 126 + [[package]] 127 + name = "atomic-waker" 128 + version = "1.1.2" 129 + source = "registry+https://github.com/rust-lang/crates.io-index" 130 + checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" 131 + 132 + [[package]] 133 + name = "autocfg" 134 + version = "1.5.0" 135 + source = "registry+https://github.com/rust-lang/crates.io-index" 136 + checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" 137 + 138 + [[package]] 139 + name = "axum" 140 + version = "0.8.7" 141 + source = "registry+https://github.com/rust-lang/crates.io-index" 142 + checksum = "5b098575ebe77cb6d14fc7f32749631a6e44edbef6b796f89b020e99ba20d425" 143 + dependencies = [ 144 + "axum-core", 145 + "bytes", 146 + "form_urlencoded", 147 + "futures-util", 148 + "http", 149 + "http-body", 150 + "http-body-util", 151 + "hyper", 152 + "hyper-util", 153 + "itoa", 154 + "matchit", 155 + "memchr", 156 + "mime", 157 + "percent-encoding", 158 + "pin-project-lite", 159 + "serde_core", 160 + "serde_json", 161 + "serde_path_to_error", 162 + "serde_urlencoded", 163 + "sync_wrapper", 164 + "tokio", 165 + "tower", 166 + "tower-layer", 167 + "tower-service", 168 + "tracing", 169 + ] 170 + 171 + [[package]] 172 + name = "axum-core" 173 + version = "0.5.5" 174 + source = "registry+https://github.com/rust-lang/crates.io-index" 175 + checksum = "59446ce19cd142f8833f856eb31f3eb097812d1479ab224f54d72428ca21ea22" 176 + dependencies = [ 177 + "bytes", 178 + "futures-core", 179 + "http", 180 + "http-body", 181 + "http-body-util", 182 + "mime", 183 + "pin-project-lite", 184 + "sync_wrapper", 185 + "tower-layer", 186 + "tower-service", 187 + "tracing", 188 + ] 189 + 190 + [[package]] 191 + name = "base-x" 192 + version = "0.2.11" 193 + source = "registry+https://github.com/rust-lang/crates.io-index" 194 + checksum = "4cbbc9d0964165b47557570cce6c952866c2678457aca742aafc9fb771d30270" 195 + 196 + [[package]] 197 + name = "base16ct" 198 + version = "0.2.0" 199 + source = "registry+https://github.com/rust-lang/crates.io-index" 200 + checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" 201 + 202 + [[package]] 203 + name = "base256emoji" 204 + version = "1.0.2" 205 + source = "registry+https://github.com/rust-lang/crates.io-index" 206 + checksum = "b5e9430d9a245a77c92176e649af6e275f20839a48389859d1661e9a128d077c" 207 + dependencies = [ 208 + "const-str", 209 + "match-lookup", 210 + ] 211 + 212 + [[package]] 213 + name = "base64" 214 + version = "0.13.1" 215 + source = "registry+https://github.com/rust-lang/crates.io-index" 216 + checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" 217 + 218 + [[package]] 219 + name = "base64" 220 + version = "0.22.1" 221 + source = "registry+https://github.com/rust-lang/crates.io-index" 222 + checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" 223 + 224 + [[package]] 225 + name = "base64ct" 226 + version = "1.8.0" 227 + source = "registry+https://github.com/rust-lang/crates.io-index" 228 + checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba" 229 + 230 + [[package]] 231 + name = "bcrypt" 232 + version = "0.17.1" 233 + source = "registry+https://github.com/rust-lang/crates.io-index" 234 + checksum = "abaf6da45c74385272ddf00e1ac074c7d8a6c1a1dda376902bd6a427522a8b2c" 235 + dependencies = [ 236 + "base64 0.22.1", 237 + "blowfish", 238 + "getrandom 0.3.4", 239 + "subtle", 240 + "zeroize", 241 + ] 242 + 243 + [[package]] 244 + name = "bitflags" 245 + version = "2.10.0" 246 + source = "registry+https://github.com/rust-lang/crates.io-index" 247 + checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" 248 + dependencies = [ 249 + "serde_core", 250 + ] 251 + 252 + [[package]] 253 + name = "block-buffer" 254 + version = "0.10.4" 255 + source = "registry+https://github.com/rust-lang/crates.io-index" 256 + checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" 257 + dependencies = [ 258 + "generic-array", 259 + ] 260 + 261 + [[package]] 262 + name = "blowfish" 263 + version = "0.9.1" 264 + source = "registry+https://github.com/rust-lang/crates.io-index" 265 + checksum = "e412e2cd0f2b2d93e02543ceae7917b3c70331573df19ee046bcbc35e45e87d7" 266 + dependencies = [ 267 + "byteorder", 268 + "cipher", 269 + ] 270 + 271 + [[package]] 272 + name = "bon" 273 + version = "3.8.1" 274 + source = "registry+https://github.com/rust-lang/crates.io-index" 275 + checksum = "ebeb9aaf9329dff6ceb65c689ca3db33dbf15f324909c60e4e5eef5701ce31b1" 276 + dependencies = [ 277 + "bon-macros", 278 + "rustversion", 279 + ] 280 + 281 + [[package]] 282 + name = "bon-macros" 283 + version = "3.8.1" 284 + source = "registry+https://github.com/rust-lang/crates.io-index" 285 + checksum = "77e9d642a7e3a318e37c2c9427b5a6a48aa1ad55dcd986f3034ab2239045a645" 286 + dependencies = [ 287 + "darling", 288 + "ident_case", 289 + "prettyplease", 290 + "proc-macro2", 291 + "quote", 292 + "rustversion", 293 + "syn 2.0.111", 294 + ] 295 + 296 + [[package]] 297 + name = "borsh" 298 + version = "1.6.0" 299 + source = "registry+https://github.com/rust-lang/crates.io-index" 300 + checksum = "d1da5ab77c1437701eeff7c88d968729e7766172279eab0676857b3d63af7a6f" 301 + dependencies = [ 302 + "cfg_aliases", 303 + ] 304 + 305 + [[package]] 306 + name = "brotli" 307 + version = "3.5.0" 308 + source = "registry+https://github.com/rust-lang/crates.io-index" 309 + checksum = "d640d25bc63c50fb1f0b545ffd80207d2e10a4c965530809b40ba3386825c391" 310 + dependencies = [ 311 + "alloc-no-stdlib", 312 + "alloc-stdlib", 313 + "brotli-decompressor", 314 + ] 315 + 316 + [[package]] 317 + name = "brotli-decompressor" 318 + version = "2.5.1" 319 + source = "registry+https://github.com/rust-lang/crates.io-index" 320 + checksum = "4e2e4afe60d7dd600fdd3de8d0f08c2b7ec039712e3b6137ff98b7004e82de4f" 321 + dependencies = [ 322 + "alloc-no-stdlib", 323 + "alloc-stdlib", 324 + ] 325 + 326 + [[package]] 327 + name = "bspds" 328 + version = "0.1.0" 329 + dependencies = [ 330 + "anyhow", 331 + "axum", 332 + "bcrypt", 333 + "bytes", 334 + "chrono", 335 + "cid", 336 + "dotenvy", 337 + "jacquard", 338 + "jacquard-axum", 339 + "jacquard-repo", 340 + "jsonwebtoken", 341 + "multihash", 342 + "reqwest", 343 + "serde", 344 + "serde_ipld_dagcbor", 345 + "serde_json", 346 + "sha2", 347 + "sqlx", 348 + "tokio", 349 + "tracing", 350 + "tracing-subscriber", 351 + "uuid", 352 + ] 353 + 354 + [[package]] 355 + name = "btree-range-map" 356 + version = "0.7.2" 357 + source = "registry+https://github.com/rust-lang/crates.io-index" 358 + checksum = "1be5c9672446d3800bcbcaabaeba121fe22f1fb25700c4562b22faf76d377c33" 359 + dependencies = [ 360 + "btree-slab", 361 + "cc-traits", 362 + "range-traits", 363 + "serde", 364 + "slab", 365 + ] 366 + 367 + [[package]] 368 + name = "btree-slab" 369 + version = "0.6.1" 370 + source = "registry+https://github.com/rust-lang/crates.io-index" 371 + checksum = "7a2b56d3029f075c4fa892428a098425b86cef5c89ae54073137ece416aef13c" 372 + dependencies = [ 373 + "cc-traits", 374 + "slab", 375 + "smallvec", 376 + ] 377 + 378 + [[package]] 379 + name = "buf_redux" 380 + version = "0.8.4" 381 + source = "registry+https://github.com/rust-lang/crates.io-index" 382 + checksum = "b953a6887648bb07a535631f2bc00fbdb2a2216f135552cb3f534ed136b9c07f" 383 + dependencies = [ 384 + "memchr", 385 + "safemem", 386 + ] 387 + 388 + [[package]] 389 + name = "bumpalo" 390 + version = "3.19.0" 391 + source = "registry+https://github.com/rust-lang/crates.io-index" 392 + checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" 393 + 394 + [[package]] 395 + name = "bytecount" 396 + version = "0.6.9" 397 + source = "registry+https://github.com/rust-lang/crates.io-index" 398 + checksum = "175812e0be2bccb6abe50bb8d566126198344f707e304f45c648fd8f2cc0365e" 399 + 400 + [[package]] 401 + name = "byteorder" 402 + version = "1.5.0" 403 + source = "registry+https://github.com/rust-lang/crates.io-index" 404 + checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" 405 + 406 + [[package]] 407 + name = "bytes" 408 + version = "1.11.0" 409 + source = "registry+https://github.com/rust-lang/crates.io-index" 410 + checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" 411 + dependencies = [ 412 + "serde", 413 + ] 414 + 415 + [[package]] 416 + name = "camino" 417 + version = "1.2.1" 418 + source = "registry+https://github.com/rust-lang/crates.io-index" 419 + checksum = "276a59bf2b2c967788139340c9f0c5b12d7fd6630315c15c217e559de85d2609" 420 + dependencies = [ 421 + "serde_core", 422 + ] 423 + 424 + [[package]] 425 + name = "cargo-platform" 426 + version = "0.1.9" 427 + source = "registry+https://github.com/rust-lang/crates.io-index" 428 + checksum = "e35af189006b9c0f00a064685c727031e3ed2d8020f7ba284d78cc2671bd36ea" 429 + dependencies = [ 430 + "serde", 431 + ] 432 + 433 + [[package]] 434 + name = "cargo_metadata" 435 + version = "0.14.2" 436 + source = "registry+https://github.com/rust-lang/crates.io-index" 437 + checksum = "4acbb09d9ee8e23699b9634375c72795d095bf268439da88562cf9b501f181fa" 438 + dependencies = [ 439 + "camino", 440 + "cargo-platform", 441 + "semver", 442 + "serde", 443 + "serde_json", 444 + ] 445 + 446 + [[package]] 447 + name = "cbor4ii" 448 + version = "0.2.14" 449 + source = "registry+https://github.com/rust-lang/crates.io-index" 450 + checksum = "b544cf8c89359205f4f990d0e6f3828db42df85b5dac95d09157a250eb0749c4" 451 + dependencies = [ 452 + "serde", 453 + ] 454 + 455 + [[package]] 456 + name = "cc" 457 + version = "1.2.48" 458 + source = "registry+https://github.com/rust-lang/crates.io-index" 459 + checksum = "c481bdbf0ed3b892f6f806287d72acd515b352a4ec27a208489b8c1bc839633a" 460 + dependencies = [ 461 + "find-msvc-tools", 462 + "shlex", 463 + ] 464 + 465 + [[package]] 466 + name = "cc-traits" 467 + version = "2.0.0" 468 + source = "registry+https://github.com/rust-lang/crates.io-index" 469 + checksum = "060303ef31ef4a522737e1b1ab68c67916f2a787bb2f4f54f383279adba962b5" 470 + dependencies = [ 471 + "slab", 472 + ] 473 + 474 + [[package]] 475 + name = "cesu8" 476 + version = "1.1.0" 477 + source = "registry+https://github.com/rust-lang/crates.io-index" 478 + checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" 479 + 480 + [[package]] 481 + name = "cfg-if" 482 + version = "1.0.4" 483 + source = "registry+https://github.com/rust-lang/crates.io-index" 484 + checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" 485 + 486 + [[package]] 487 + name = "cfg_aliases" 488 + version = "0.2.1" 489 + source = "registry+https://github.com/rust-lang/crates.io-index" 490 + checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" 491 + 492 + [[package]] 493 + name = "chrono" 494 + version = "0.4.42" 495 + source = "registry+https://github.com/rust-lang/crates.io-index" 496 + checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2" 497 + dependencies = [ 498 + "iana-time-zone", 499 + "js-sys", 500 + "num-traits", 501 + "serde", 502 + "wasm-bindgen", 503 + "windows-link 0.2.1", 504 + ] 505 + 506 + [[package]] 507 + name = "chunked_transfer" 508 + version = "1.5.0" 509 + source = "registry+https://github.com/rust-lang/crates.io-index" 510 + checksum = "6e4de3bc4ea267985becf712dc6d9eed8b04c953b3fcfb339ebc87acd9804901" 511 + 512 + [[package]] 513 + name = "ciborium" 514 + version = "0.2.2" 515 + source = "registry+https://github.com/rust-lang/crates.io-index" 516 + checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" 517 + dependencies = [ 518 + "ciborium-io", 519 + "ciborium-ll", 520 + "serde", 521 + ] 522 + 523 + [[package]] 524 + name = "ciborium-io" 525 + version = "0.2.2" 526 + source = "registry+https://github.com/rust-lang/crates.io-index" 527 + checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" 528 + 529 + [[package]] 530 + name = "ciborium-ll" 531 + version = "0.2.2" 532 + source = "registry+https://github.com/rust-lang/crates.io-index" 533 + checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" 534 + dependencies = [ 535 + "ciborium-io", 536 + "half", 537 + ] 538 + 539 + [[package]] 540 + name = "cid" 541 + version = "0.11.1" 542 + source = "registry+https://github.com/rust-lang/crates.io-index" 543 + checksum = "3147d8272e8fa0ccd29ce51194dd98f79ddfb8191ba9e3409884e751798acf3a" 544 + dependencies = [ 545 + "core2", 546 + "multibase", 547 + "multihash", 548 + "serde", 549 + "serde_bytes", 550 + "unsigned-varint 0.8.0", 551 + ] 552 + 553 + [[package]] 554 + name = "cipher" 555 + version = "0.4.4" 556 + source = "registry+https://github.com/rust-lang/crates.io-index" 557 + checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" 558 + dependencies = [ 559 + "crypto-common", 560 + "inout", 561 + ] 562 + 563 + [[package]] 564 + name = "combine" 565 + version = "4.6.7" 566 + source = "registry+https://github.com/rust-lang/crates.io-index" 567 + checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" 568 + dependencies = [ 569 + "bytes", 570 + "memchr", 571 + ] 572 + 573 + [[package]] 574 + name = "compression-codecs" 575 + version = "0.4.33" 576 + source = "registry+https://github.com/rust-lang/crates.io-index" 577 + checksum = "302266479cb963552d11bd042013a58ef1adc56768016c8b82b4199488f2d4ad" 578 + dependencies = [ 579 + "compression-core", 580 + "flate2", 581 + "memchr", 582 + ] 583 + 584 + [[package]] 585 + name = "compression-core" 586 + version = "0.4.31" 587 + source = "registry+https://github.com/rust-lang/crates.io-index" 588 + checksum = "75984efb6ed102a0d42db99afb6c1948f0380d1d91808d5529916e6c08b49d8d" 589 + 590 + [[package]] 591 + name = "concurrent-queue" 592 + version = "2.5.0" 593 + source = "registry+https://github.com/rust-lang/crates.io-index" 594 + checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" 595 + dependencies = [ 596 + "crossbeam-utils", 597 + ] 598 + 599 + [[package]] 600 + name = "const-oid" 601 + version = "0.9.6" 602 + source = "registry+https://github.com/rust-lang/crates.io-index" 603 + checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" 604 + 605 + [[package]] 606 + name = "const-str" 607 + version = "0.4.3" 608 + source = "registry+https://github.com/rust-lang/crates.io-index" 609 + checksum = "2f421161cb492475f1661ddc9815a745a1c894592070661180fdec3d4872e9c3" 610 + 611 + [[package]] 612 + name = "cordyceps" 613 + version = "0.3.4" 614 + source = "registry+https://github.com/rust-lang/crates.io-index" 615 + checksum = "688d7fbb8092b8de775ef2536f36c8c31f2bc4006ece2e8d8ad2d17d00ce0a2a" 616 + dependencies = [ 617 + "loom", 618 + "tracing", 619 + ] 620 + 621 + [[package]] 622 + name = "core-foundation" 623 + version = "0.9.4" 624 + source = "registry+https://github.com/rust-lang/crates.io-index" 625 + checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" 626 + dependencies = [ 627 + "core-foundation-sys", 628 + "libc", 629 + ] 630 + 631 + [[package]] 632 + name = "core-foundation" 633 + version = "0.10.1" 634 + source = "registry+https://github.com/rust-lang/crates.io-index" 635 + checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" 636 + dependencies = [ 637 + "core-foundation-sys", 638 + "libc", 639 + ] 640 + 641 + [[package]] 642 + name = "core-foundation-sys" 643 + version = "0.8.7" 644 + source = "registry+https://github.com/rust-lang/crates.io-index" 645 + checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" 646 + 647 + [[package]] 648 + name = "core2" 649 + version = "0.4.0" 650 + source = "registry+https://github.com/rust-lang/crates.io-index" 651 + checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505" 652 + dependencies = [ 653 + "memchr", 654 + ] 655 + 656 + [[package]] 657 + name = "cpufeatures" 658 + version = "0.2.17" 659 + source = "registry+https://github.com/rust-lang/crates.io-index" 660 + checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" 661 + dependencies = [ 662 + "libc", 663 + ] 664 + 665 + [[package]] 666 + name = "crc" 667 + version = "3.4.0" 668 + source = "registry+https://github.com/rust-lang/crates.io-index" 669 + checksum = "5eb8a2a1cd12ab0d987a5d5e825195d372001a4094a0376319d5a0ad71c1ba0d" 670 + dependencies = [ 671 + "crc-catalog", 672 + ] 673 + 674 + [[package]] 675 + name = "crc-catalog" 676 + version = "2.4.0" 677 + source = "registry+https://github.com/rust-lang/crates.io-index" 678 + checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" 679 + 680 + [[package]] 681 + name = "crc32fast" 682 + version = "1.5.0" 683 + source = "registry+https://github.com/rust-lang/crates.io-index" 684 + checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" 685 + dependencies = [ 686 + "cfg-if", 687 + ] 688 + 689 + [[package]] 690 + name = "crossbeam-channel" 691 + version = "0.5.15" 692 + source = "registry+https://github.com/rust-lang/crates.io-index" 693 + checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2" 694 + dependencies = [ 695 + "crossbeam-utils", 696 + ] 697 + 698 + [[package]] 699 + name = "crossbeam-queue" 700 + version = "0.3.12" 701 + source = "registry+https://github.com/rust-lang/crates.io-index" 702 + checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115" 703 + dependencies = [ 704 + "crossbeam-utils", 705 + ] 706 + 707 + [[package]] 708 + name = "crossbeam-utils" 709 + version = "0.8.21" 710 + source = "registry+https://github.com/rust-lang/crates.io-index" 711 + checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" 712 + 713 + [[package]] 714 + name = "crunchy" 715 + version = "0.2.4" 716 + source = "registry+https://github.com/rust-lang/crates.io-index" 717 + checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" 718 + 719 + [[package]] 720 + name = "crypto-bigint" 721 + version = "0.5.5" 722 + source = "registry+https://github.com/rust-lang/crates.io-index" 723 + checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" 724 + dependencies = [ 725 + "generic-array", 726 + "rand_core 0.6.4", 727 + "subtle", 728 + "zeroize", 729 + ] 730 + 731 + [[package]] 732 + name = "crypto-common" 733 + version = "0.1.6" 734 + source = "registry+https://github.com/rust-lang/crates.io-index" 735 + checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" 736 + dependencies = [ 737 + "generic-array", 738 + "typenum", 739 + ] 740 + 741 + [[package]] 742 + name = "curve25519-dalek" 743 + version = "4.1.3" 744 + source = "registry+https://github.com/rust-lang/crates.io-index" 745 + checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" 746 + dependencies = [ 747 + "cfg-if", 748 + "cpufeatures", 749 + "curve25519-dalek-derive", 750 + "digest", 751 + "fiat-crypto", 752 + "rustc_version", 753 + "subtle", 754 + "zeroize", 755 + ] 756 + 757 + [[package]] 758 + name = "curve25519-dalek-derive" 759 + version = "0.1.1" 760 + source = "registry+https://github.com/rust-lang/crates.io-index" 761 + checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" 762 + dependencies = [ 763 + "proc-macro2", 764 + "quote", 765 + "syn 2.0.111", 766 + ] 767 + 768 + [[package]] 769 + name = "darling" 770 + version = "0.21.3" 771 + source = "registry+https://github.com/rust-lang/crates.io-index" 772 + checksum = "9cdf337090841a411e2a7f3deb9187445851f91b309c0c0a29e05f74a00a48c0" 773 + dependencies = [ 774 + "darling_core", 775 + "darling_macro", 776 + ] 777 + 778 + [[package]] 779 + name = "darling_core" 780 + version = "0.21.3" 781 + source = "registry+https://github.com/rust-lang/crates.io-index" 782 + checksum = "1247195ecd7e3c85f83c8d2a366e4210d588e802133e1e355180a9870b517ea4" 783 + dependencies = [ 784 + "fnv", 785 + "ident_case", 786 + "proc-macro2", 787 + "quote", 788 + "strsim", 789 + "syn 2.0.111", 790 + ] 791 + 792 + [[package]] 793 + name = "darling_macro" 794 + version = "0.21.3" 795 + source = "registry+https://github.com/rust-lang/crates.io-index" 796 + checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" 797 + dependencies = [ 798 + "darling_core", 799 + "quote", 800 + "syn 2.0.111", 801 + ] 802 + 803 + [[package]] 804 + name = "dashmap" 805 + version = "5.5.3" 806 + source = "registry+https://github.com/rust-lang/crates.io-index" 807 + checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" 808 + dependencies = [ 809 + "cfg-if", 810 + "hashbrown 0.14.5", 811 + "lock_api", 812 + "once_cell", 813 + "parking_lot_core", 814 + ] 815 + 816 + [[package]] 817 + name = "dashmap" 818 + version = "6.1.0" 819 + source = "registry+https://github.com/rust-lang/crates.io-index" 820 + checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf" 821 + dependencies = [ 822 + "cfg-if", 823 + "crossbeam-utils", 824 + "hashbrown 0.14.5", 825 + "lock_api", 826 + "once_cell", 827 + "parking_lot_core", 828 + ] 829 + 830 + [[package]] 831 + name = "data-encoding" 832 + version = "2.9.0" 833 + source = "registry+https://github.com/rust-lang/crates.io-index" 834 + checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476" 835 + 836 + [[package]] 837 + name = "data-encoding-macro" 838 + version = "0.1.18" 839 + source = "registry+https://github.com/rust-lang/crates.io-index" 840 + checksum = "47ce6c96ea0102f01122a185683611bd5ac8d99e62bc59dd12e6bda344ee673d" 841 + dependencies = [ 842 + "data-encoding", 843 + "data-encoding-macro-internal", 844 + ] 845 + 846 + [[package]] 847 + name = "data-encoding-macro-internal" 848 + version = "0.1.16" 849 + source = "registry+https://github.com/rust-lang/crates.io-index" 850 + checksum = "8d162beedaa69905488a8da94f5ac3edb4dd4788b732fadb7bd120b2625c1976" 851 + dependencies = [ 852 + "data-encoding", 853 + "syn 2.0.111", 854 + ] 855 + 856 + [[package]] 857 + name = "deflate" 858 + version = "1.0.0" 859 + source = "registry+https://github.com/rust-lang/crates.io-index" 860 + checksum = "c86f7e25f518f4b81808a2cf1c50996a61f5c2eb394b2393bd87f2a4780a432f" 861 + dependencies = [ 862 + "adler32", 863 + "gzip-header", 864 + ] 865 + 866 + [[package]] 867 + name = "der" 868 + version = "0.7.10" 869 + source = "registry+https://github.com/rust-lang/crates.io-index" 870 + checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" 871 + dependencies = [ 872 + "const-oid", 873 + "pem-rfc7468", 874 + "zeroize", 875 + ] 876 + 877 + [[package]] 878 + name = "deranged" 879 + version = "0.5.5" 880 + source = "registry+https://github.com/rust-lang/crates.io-index" 881 + checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587" 882 + dependencies = [ 883 + "powerfmt", 884 + "serde_core", 885 + ] 886 + 887 + [[package]] 888 + name = "derive_more" 889 + version = "1.0.0" 890 + source = "registry+https://github.com/rust-lang/crates.io-index" 891 + checksum = "4a9b99b9cbbe49445b21764dc0625032a89b145a2642e67603e1c936f5458d05" 892 + dependencies = [ 893 + "derive_more-impl", 894 + ] 895 + 896 + [[package]] 897 + name = "derive_more-impl" 898 + version = "1.0.0" 899 + source = "registry+https://github.com/rust-lang/crates.io-index" 900 + checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" 901 + dependencies = [ 902 + "proc-macro2", 903 + "quote", 904 + "syn 2.0.111", 905 + "unicode-xid", 906 + ] 907 + 908 + [[package]] 909 + name = "diatomic-waker" 910 + version = "0.2.3" 911 + source = "registry+https://github.com/rust-lang/crates.io-index" 912 + checksum = "ab03c107fafeb3ee9f5925686dbb7a73bc76e3932abb0d2b365cb64b169cf04c" 913 + 914 + [[package]] 915 + name = "digest" 916 + version = "0.10.7" 917 + source = "registry+https://github.com/rust-lang/crates.io-index" 918 + checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" 919 + dependencies = [ 920 + "block-buffer", 921 + "const-oid", 922 + "crypto-common", 923 + "subtle", 924 + ] 925 + 926 + [[package]] 927 + name = "displaydoc" 928 + version = "0.2.5" 929 + source = "registry+https://github.com/rust-lang/crates.io-index" 930 + checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" 931 + dependencies = [ 932 + "proc-macro2", 933 + "quote", 934 + "syn 2.0.111", 935 + ] 936 + 937 + [[package]] 938 + name = "dotenvy" 939 + version = "0.15.7" 940 + source = "registry+https://github.com/rust-lang/crates.io-index" 941 + checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" 942 + 943 + [[package]] 944 + name = "dyn-clone" 945 + version = "1.0.20" 946 + source = "registry+https://github.com/rust-lang/crates.io-index" 947 + checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" 948 + 949 + [[package]] 950 + name = "ecdsa" 951 + version = "0.16.9" 952 + source = "registry+https://github.com/rust-lang/crates.io-index" 953 + checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" 954 + dependencies = [ 955 + "der", 956 + "digest", 957 + "elliptic-curve", 958 + "rfc6979", 959 + "signature", 960 + "spki", 961 + ] 962 + 963 + [[package]] 964 + name = "ed25519" 965 + version = "2.2.3" 966 + source = "registry+https://github.com/rust-lang/crates.io-index" 967 + checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" 968 + dependencies = [ 969 + "pkcs8", 970 + "signature", 971 + ] 972 + 973 + [[package]] 974 + name = "ed25519-dalek" 975 + version = "2.2.0" 976 + source = "registry+https://github.com/rust-lang/crates.io-index" 977 + checksum = "70e796c081cee67dc755e1a36a0a172b897fab85fc3f6bc48307991f64e4eca9" 978 + dependencies = [ 979 + "curve25519-dalek", 980 + "ed25519", 981 + "rand_core 0.6.4", 982 + "serde", 983 + "sha2", 984 + "subtle", 985 + "zeroize", 986 + ] 987 + 988 + [[package]] 989 + name = "either" 990 + version = "1.15.0" 991 + source = "registry+https://github.com/rust-lang/crates.io-index" 992 + checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" 993 + dependencies = [ 994 + "serde", 995 + ] 996 + 997 + [[package]] 998 + name = "elliptic-curve" 999 + version = "0.13.8" 1000 + source = "registry+https://github.com/rust-lang/crates.io-index" 1001 + checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" 1002 + dependencies = [ 1003 + "base16ct", 1004 + "crypto-bigint", 1005 + "digest", 1006 + "ff", 1007 + "generic-array", 1008 + "group", 1009 + "hkdf", 1010 + "pem-rfc7468", 1011 + "pkcs8", 1012 + "rand_core 0.6.4", 1013 + "sec1", 1014 + "subtle", 1015 + "zeroize", 1016 + ] 1017 + 1018 + [[package]] 1019 + name = "encoding_rs" 1020 + version = "0.8.35" 1021 + source = "registry+https://github.com/rust-lang/crates.io-index" 1022 + checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" 1023 + dependencies = [ 1024 + "cfg-if", 1025 + ] 1026 + 1027 + [[package]] 1028 + name = "enum-as-inner" 1029 + version = "0.6.1" 1030 + source = "registry+https://github.com/rust-lang/crates.io-index" 1031 + checksum = "a1e6a265c649f3f5979b601d26f1d05ada116434c87741c9493cb56218f76cbc" 1032 + dependencies = [ 1033 + "heck 0.5.0", 1034 + "proc-macro2", 1035 + "quote", 1036 + "syn 2.0.111", 1037 + ] 1038 + 1039 + [[package]] 1040 + name = "equivalent" 1041 + version = "1.0.2" 1042 + source = "registry+https://github.com/rust-lang/crates.io-index" 1043 + checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" 1044 + 1045 + [[package]] 1046 + name = "errno" 1047 + version = "0.3.14" 1048 + source = "registry+https://github.com/rust-lang/crates.io-index" 1049 + checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" 1050 + dependencies = [ 1051 + "libc", 1052 + "windows-sys 0.61.2", 1053 + ] 1054 + 1055 + [[package]] 1056 + name = "error-chain" 1057 + version = "0.12.4" 1058 + source = "registry+https://github.com/rust-lang/crates.io-index" 1059 + checksum = "2d2f06b9cac1506ece98fe3231e3cc9c4410ec3d5b1f24ae1c8946f0742cdefc" 1060 + dependencies = [ 1061 + "version_check", 1062 + ] 1063 + 1064 + [[package]] 1065 + name = "etcetera" 1066 + version = "0.8.0" 1067 + source = "registry+https://github.com/rust-lang/crates.io-index" 1068 + checksum = "136d1b5283a1ab77bd9257427ffd09d8667ced0570b6f938942bc7568ed5b943" 1069 + dependencies = [ 1070 + "cfg-if", 1071 + "home", 1072 + "windows-sys 0.48.0", 1073 + ] 1074 + 1075 + [[package]] 1076 + name = "event-listener" 1077 + version = "5.4.1" 1078 + source = "registry+https://github.com/rust-lang/crates.io-index" 1079 + checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" 1080 + dependencies = [ 1081 + "concurrent-queue", 1082 + "parking", 1083 + "pin-project-lite", 1084 + ] 1085 + 1086 + [[package]] 1087 + name = "fastrand" 1088 + version = "2.3.0" 1089 + source = "registry+https://github.com/rust-lang/crates.io-index" 1090 + checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" 1091 + 1092 + [[package]] 1093 + name = "ff" 1094 + version = "0.13.1" 1095 + source = "registry+https://github.com/rust-lang/crates.io-index" 1096 + checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" 1097 + dependencies = [ 1098 + "rand_core 0.6.4", 1099 + "subtle", 1100 + ] 1101 + 1102 + [[package]] 1103 + name = "fiat-crypto" 1104 + version = "0.2.9" 1105 + source = "registry+https://github.com/rust-lang/crates.io-index" 1106 + checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" 1107 + 1108 + [[package]] 1109 + name = "filetime" 1110 + version = "0.2.26" 1111 + source = "registry+https://github.com/rust-lang/crates.io-index" 1112 + checksum = "bc0505cd1b6fa6580283f6bdf70a73fcf4aba1184038c90902b92b3dd0df63ed" 1113 + dependencies = [ 1114 + "cfg-if", 1115 + "libc", 1116 + "libredox", 1117 + "windows-sys 0.60.2", 1118 + ] 1119 + 1120 + [[package]] 1121 + name = "find-msvc-tools" 1122 + version = "0.1.5" 1123 + source = "registry+https://github.com/rust-lang/crates.io-index" 1124 + checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844" 1125 + 1126 + [[package]] 1127 + name = "flate2" 1128 + version = "1.1.5" 1129 + source = "registry+https://github.com/rust-lang/crates.io-index" 1130 + checksum = "bfe33edd8e85a12a67454e37f8c75e730830d83e313556ab9ebf9ee7fbeb3bfb" 1131 + dependencies = [ 1132 + "crc32fast", 1133 + "miniz_oxide", 1134 + ] 1135 + 1136 + [[package]] 1137 + name = "flume" 1138 + version = "0.11.1" 1139 + source = "registry+https://github.com/rust-lang/crates.io-index" 1140 + checksum = "da0e4dd2a88388a1f4ccc7c9ce104604dab68d9f408dc34cd45823d5a9069095" 1141 + dependencies = [ 1142 + "futures-core", 1143 + "futures-sink", 1144 + "spin 0.9.8", 1145 + ] 1146 + 1147 + [[package]] 1148 + name = "fnv" 1149 + version = "1.0.7" 1150 + source = "registry+https://github.com/rust-lang/crates.io-index" 1151 + checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 1152 + 1153 + [[package]] 1154 + name = "foldhash" 1155 + version = "0.1.5" 1156 + source = "registry+https://github.com/rust-lang/crates.io-index" 1157 + checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" 1158 + 1159 + [[package]] 1160 + name = "foreign-types" 1161 + version = "0.3.2" 1162 + source = "registry+https://github.com/rust-lang/crates.io-index" 1163 + checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" 1164 + dependencies = [ 1165 + "foreign-types-shared", 1166 + ] 1167 + 1168 + [[package]] 1169 + name = "foreign-types-shared" 1170 + version = "0.1.1" 1171 + source = "registry+https://github.com/rust-lang/crates.io-index" 1172 + checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" 1173 + 1174 + [[package]] 1175 + name = "form_urlencoded" 1176 + version = "1.2.2" 1177 + source = "registry+https://github.com/rust-lang/crates.io-index" 1178 + checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" 1179 + dependencies = [ 1180 + "percent-encoding", 1181 + ] 1182 + 1183 + [[package]] 1184 + name = "futf" 1185 + version = "0.1.5" 1186 + source = "registry+https://github.com/rust-lang/crates.io-index" 1187 + checksum = "df420e2e84819663797d1ec6544b13c5be84629e7bb00dc960d6917db2987843" 1188 + dependencies = [ 1189 + "mac", 1190 + "new_debug_unreachable", 1191 + ] 1192 + 1193 + [[package]] 1194 + name = "futures" 1195 + version = "0.3.31" 1196 + source = "registry+https://github.com/rust-lang/crates.io-index" 1197 + checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" 1198 + dependencies = [ 1199 + "futures-channel", 1200 + "futures-core", 1201 + "futures-executor", 1202 + "futures-io", 1203 + "futures-sink", 1204 + "futures-task", 1205 + "futures-util", 1206 + ] 1207 + 1208 + [[package]] 1209 + name = "futures-buffered" 1210 + version = "0.2.12" 1211 + source = "registry+https://github.com/rust-lang/crates.io-index" 1212 + checksum = "a8e0e1f38ec07ba4abbde21eed377082f17ccb988be9d988a5adbf4bafc118fd" 1213 + dependencies = [ 1214 + "cordyceps", 1215 + "diatomic-waker", 1216 + "futures-core", 1217 + "pin-project-lite", 1218 + "spin 0.10.0", 1219 + ] 1220 + 1221 + [[package]] 1222 + name = "futures-channel" 1223 + version = "0.3.31" 1224 + source = "registry+https://github.com/rust-lang/crates.io-index" 1225 + checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" 1226 + dependencies = [ 1227 + "futures-core", 1228 + "futures-sink", 1229 + ] 1230 + 1231 + [[package]] 1232 + name = "futures-core" 1233 + version = "0.3.31" 1234 + source = "registry+https://github.com/rust-lang/crates.io-index" 1235 + checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" 1236 + 1237 + [[package]] 1238 + name = "futures-executor" 1239 + version = "0.3.31" 1240 + source = "registry+https://github.com/rust-lang/crates.io-index" 1241 + checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" 1242 + dependencies = [ 1243 + "futures-core", 1244 + "futures-task", 1245 + "futures-util", 1246 + ] 1247 + 1248 + [[package]] 1249 + name = "futures-intrusive" 1250 + version = "0.5.0" 1251 + source = "registry+https://github.com/rust-lang/crates.io-index" 1252 + checksum = "1d930c203dd0b6ff06e0201a4a2fe9149b43c684fd4420555b26d21b1a02956f" 1253 + dependencies = [ 1254 + "futures-core", 1255 + "lock_api", 1256 + "parking_lot", 1257 + ] 1258 + 1259 + [[package]] 1260 + name = "futures-io" 1261 + version = "0.3.31" 1262 + source = "registry+https://github.com/rust-lang/crates.io-index" 1263 + checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" 1264 + 1265 + [[package]] 1266 + name = "futures-lite" 1267 + version = "2.6.1" 1268 + source = "registry+https://github.com/rust-lang/crates.io-index" 1269 + checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad" 1270 + dependencies = [ 1271 + "fastrand", 1272 + "futures-core", 1273 + "futures-io", 1274 + "parking", 1275 + "pin-project-lite", 1276 + ] 1277 + 1278 + [[package]] 1279 + name = "futures-macro" 1280 + version = "0.3.31" 1281 + source = "registry+https://github.com/rust-lang/crates.io-index" 1282 + checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" 1283 + dependencies = [ 1284 + "proc-macro2", 1285 + "quote", 1286 + "syn 2.0.111", 1287 + ] 1288 + 1289 + [[package]] 1290 + name = "futures-sink" 1291 + version = "0.3.31" 1292 + source = "registry+https://github.com/rust-lang/crates.io-index" 1293 + checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" 1294 + 1295 + [[package]] 1296 + name = "futures-task" 1297 + version = "0.3.31" 1298 + source = "registry+https://github.com/rust-lang/crates.io-index" 1299 + checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" 1300 + 1301 + [[package]] 1302 + name = "futures-util" 1303 + version = "0.3.31" 1304 + source = "registry+https://github.com/rust-lang/crates.io-index" 1305 + checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" 1306 + dependencies = [ 1307 + "futures-channel", 1308 + "futures-core", 1309 + "futures-io", 1310 + "futures-macro", 1311 + "futures-sink", 1312 + "futures-task", 1313 + "memchr", 1314 + "pin-project-lite", 1315 + "pin-utils", 1316 + "slab", 1317 + ] 1318 + 1319 + [[package]] 1320 + name = "generator" 1321 + version = "0.8.7" 1322 + source = "registry+https://github.com/rust-lang/crates.io-index" 1323 + checksum = "605183a538e3e2a9c1038635cc5c2d194e2ee8fd0d1b66b8349fad7dbacce5a2" 1324 + dependencies = [ 1325 + "cc", 1326 + "cfg-if", 1327 + "libc", 1328 + "log", 1329 + "rustversion", 1330 + "windows", 1331 + ] 1332 + 1333 + [[package]] 1334 + name = "generic-array" 1335 + version = "0.14.9" 1336 + source = "registry+https://github.com/rust-lang/crates.io-index" 1337 + checksum = "4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2" 1338 + dependencies = [ 1339 + "typenum", 1340 + "version_check", 1341 + "zeroize", 1342 + ] 1343 + 1344 + [[package]] 1345 + name = "getrandom" 1346 + version = "0.2.16" 1347 + source = "registry+https://github.com/rust-lang/crates.io-index" 1348 + checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" 1349 + dependencies = [ 1350 + "cfg-if", 1351 + "js-sys", 1352 + "libc", 1353 + "wasi", 1354 + "wasm-bindgen", 1355 + ] 1356 + 1357 + [[package]] 1358 + name = "getrandom" 1359 + version = "0.3.4" 1360 + source = "registry+https://github.com/rust-lang/crates.io-index" 1361 + checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" 1362 + dependencies = [ 1363 + "cfg-if", 1364 + "js-sys", 1365 + "libc", 1366 + "r-efi", 1367 + "wasip2", 1368 + "wasm-bindgen", 1369 + ] 1370 + 1371 + [[package]] 1372 + name = "glob" 1373 + version = "0.3.3" 1374 + source = "registry+https://github.com/rust-lang/crates.io-index" 1375 + checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" 1376 + 1377 + [[package]] 1378 + name = "gloo-storage" 1379 + version = "0.3.0" 1380 + source = "registry+https://github.com/rust-lang/crates.io-index" 1381 + checksum = "fbc8031e8c92758af912f9bc08fbbadd3c6f3cfcbf6b64cdf3d6a81f0139277a" 1382 + dependencies = [ 1383 + "gloo-utils", 1384 + "js-sys", 1385 + "serde", 1386 + "serde_json", 1387 + "thiserror 1.0.69", 1388 + "wasm-bindgen", 1389 + "web-sys", 1390 + ] 1391 + 1392 + [[package]] 1393 + name = "gloo-utils" 1394 + version = "0.2.0" 1395 + source = "registry+https://github.com/rust-lang/crates.io-index" 1396 + checksum = "0b5555354113b18c547c1d3a98fbf7fb32a9ff4f6fa112ce823a21641a0ba3aa" 1397 + dependencies = [ 1398 + "js-sys", 1399 + "serde", 1400 + "serde_json", 1401 + "wasm-bindgen", 1402 + "web-sys", 1403 + ] 1404 + 1405 + [[package]] 1406 + name = "group" 1407 + version = "0.13.0" 1408 + source = "registry+https://github.com/rust-lang/crates.io-index" 1409 + checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" 1410 + dependencies = [ 1411 + "ff", 1412 + "rand_core 0.6.4", 1413 + "subtle", 1414 + ] 1415 + 1416 + [[package]] 1417 + name = "gzip-header" 1418 + version = "1.0.0" 1419 + source = "registry+https://github.com/rust-lang/crates.io-index" 1420 + checksum = "95cc527b92e6029a62960ad99aa8a6660faa4555fe5f731aab13aa6a921795a2" 1421 + dependencies = [ 1422 + "crc32fast", 1423 + ] 1424 + 1425 + [[package]] 1426 + name = "h2" 1427 + version = "0.4.12" 1428 + source = "registry+https://github.com/rust-lang/crates.io-index" 1429 + checksum = "f3c0b69cfcb4e1b9f1bf2f53f95f766e4661169728ec61cd3fe5a0166f2d1386" 1430 + dependencies = [ 1431 + "atomic-waker", 1432 + "bytes", 1433 + "fnv", 1434 + "futures-core", 1435 + "futures-sink", 1436 + "http", 1437 + "indexmap 2.12.1", 1438 + "slab", 1439 + "tokio", 1440 + "tokio-util", 1441 + "tracing", 1442 + ] 1443 + 1444 + [[package]] 1445 + name = "half" 1446 + version = "2.7.1" 1447 + source = "registry+https://github.com/rust-lang/crates.io-index" 1448 + checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" 1449 + dependencies = [ 1450 + "cfg-if", 1451 + "crunchy", 1452 + "zerocopy", 1453 + ] 1454 + 1455 + [[package]] 1456 + name = "hashbrown" 1457 + version = "0.12.3" 1458 + source = "registry+https://github.com/rust-lang/crates.io-index" 1459 + checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" 1460 + 1461 + [[package]] 1462 + name = "hashbrown" 1463 + version = "0.14.5" 1464 + source = "registry+https://github.com/rust-lang/crates.io-index" 1465 + checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" 1466 + 1467 + [[package]] 1468 + name = "hashbrown" 1469 + version = "0.15.5" 1470 + source = "registry+https://github.com/rust-lang/crates.io-index" 1471 + checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" 1472 + dependencies = [ 1473 + "allocator-api2", 1474 + "equivalent", 1475 + "foldhash", 1476 + ] 1477 + 1478 + [[package]] 1479 + name = "hashbrown" 1480 + version = "0.16.1" 1481 + source = "registry+https://github.com/rust-lang/crates.io-index" 1482 + checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" 1483 + 1484 + [[package]] 1485 + name = "hashlink" 1486 + version = "0.10.0" 1487 + source = "registry+https://github.com/rust-lang/crates.io-index" 1488 + checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" 1489 + dependencies = [ 1490 + "hashbrown 0.15.5", 1491 + ] 1492 + 1493 + [[package]] 1494 + name = "heck" 1495 + version = "0.4.1" 1496 + source = "registry+https://github.com/rust-lang/crates.io-index" 1497 + checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" 1498 + 1499 + [[package]] 1500 + name = "heck" 1501 + version = "0.5.0" 1502 + source = "registry+https://github.com/rust-lang/crates.io-index" 1503 + checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" 1504 + 1505 + [[package]] 1506 + name = "hermit-abi" 1507 + version = "0.5.2" 1508 + source = "registry+https://github.com/rust-lang/crates.io-index" 1509 + checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" 1510 + 1511 + [[package]] 1512 + name = "hex" 1513 + version = "0.4.3" 1514 + source = "registry+https://github.com/rust-lang/crates.io-index" 1515 + checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" 1516 + 1517 + [[package]] 1518 + name = "hex_fmt" 1519 + version = "0.3.0" 1520 + source = "registry+https://github.com/rust-lang/crates.io-index" 1521 + checksum = "b07f60793ff0a4d9cef0f18e63b5357e06209987153a64648c972c1e5aff336f" 1522 + 1523 + [[package]] 1524 + name = "hickory-proto" 1525 + version = "0.24.4" 1526 + source = "registry+https://github.com/rust-lang/crates.io-index" 1527 + checksum = "92652067c9ce6f66ce53cc38d1169daa36e6e7eb7dd3b63b5103bd9d97117248" 1528 + dependencies = [ 1529 + "async-trait", 1530 + "cfg-if", 1531 + "data-encoding", 1532 + "enum-as-inner", 1533 + "futures-channel", 1534 + "futures-io", 1535 + "futures-util", 1536 + "idna", 1537 + "ipnet", 1538 + "once_cell", 1539 + "rand 0.8.5", 1540 + "thiserror 1.0.69", 1541 + "tinyvec", 1542 + "tokio", 1543 + "tracing", 1544 + "url", 1545 + ] 1546 + 1547 + [[package]] 1548 + name = "hickory-resolver" 1549 + version = "0.24.4" 1550 + source = "registry+https://github.com/rust-lang/crates.io-index" 1551 + checksum = "cbb117a1ca520e111743ab2f6688eddee69db4e0ea242545a604dce8a66fd22e" 1552 + dependencies = [ 1553 + "cfg-if", 1554 + "futures-util", 1555 + "hickory-proto", 1556 + "ipconfig", 1557 + "lru-cache", 1558 + "once_cell", 1559 + "parking_lot", 1560 + "rand 0.8.5", 1561 + "resolv-conf", 1562 + "smallvec", 1563 + "thiserror 1.0.69", 1564 + "tokio", 1565 + "tracing", 1566 + ] 1567 + 1568 + [[package]] 1569 + name = "hkdf" 1570 + version = "0.12.4" 1571 + source = "registry+https://github.com/rust-lang/crates.io-index" 1572 + checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" 1573 + dependencies = [ 1574 + "hmac", 1575 + ] 1576 + 1577 + [[package]] 1578 + name = "hmac" 1579 + version = "0.12.1" 1580 + source = "registry+https://github.com/rust-lang/crates.io-index" 1581 + checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" 1582 + dependencies = [ 1583 + "digest", 1584 + ] 1585 + 1586 + [[package]] 1587 + name = "home" 1588 + version = "0.5.12" 1589 + source = "registry+https://github.com/rust-lang/crates.io-index" 1590 + checksum = "cc627f471c528ff0c4a49e1d5e60450c8f6461dd6d10ba9dcd3a61d3dff7728d" 1591 + dependencies = [ 1592 + "windows-sys 0.61.2", 1593 + ] 1594 + 1595 + [[package]] 1596 + name = "html5ever" 1597 + version = "0.27.0" 1598 + source = "registry+https://github.com/rust-lang/crates.io-index" 1599 + checksum = "c13771afe0e6e846f1e67d038d4cb29998a6779f93c809212e4e9c32efd244d4" 1600 + dependencies = [ 1601 + "log", 1602 + "mac", 1603 + "markup5ever", 1604 + "proc-macro2", 1605 + "quote", 1606 + "syn 2.0.111", 1607 + ] 1608 + 1609 + [[package]] 1610 + name = "http" 1611 + version = "1.4.0" 1612 + source = "registry+https://github.com/rust-lang/crates.io-index" 1613 + checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" 1614 + dependencies = [ 1615 + "bytes", 1616 + "itoa", 1617 + ] 1618 + 1619 + [[package]] 1620 + name = "http-body" 1621 + version = "1.0.1" 1622 + source = "registry+https://github.com/rust-lang/crates.io-index" 1623 + checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" 1624 + dependencies = [ 1625 + "bytes", 1626 + "http", 1627 + ] 1628 + 1629 + [[package]] 1630 + name = "http-body-util" 1631 + version = "0.1.3" 1632 + source = "registry+https://github.com/rust-lang/crates.io-index" 1633 + checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" 1634 + dependencies = [ 1635 + "bytes", 1636 + "futures-core", 1637 + "http", 1638 + "http-body", 1639 + "pin-project-lite", 1640 + ] 1641 + 1642 + [[package]] 1643 + name = "httparse" 1644 + version = "1.10.1" 1645 + source = "registry+https://github.com/rust-lang/crates.io-index" 1646 + checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" 1647 + 1648 + [[package]] 1649 + name = "httpdate" 1650 + version = "1.0.3" 1651 + source = "registry+https://github.com/rust-lang/crates.io-index" 1652 + checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" 1653 + 1654 + [[package]] 1655 + name = "hyper" 1656 + version = "1.8.1" 1657 + source = "registry+https://github.com/rust-lang/crates.io-index" 1658 + checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11" 1659 + dependencies = [ 1660 + "atomic-waker", 1661 + "bytes", 1662 + "futures-channel", 1663 + "futures-core", 1664 + "h2", 1665 + "http", 1666 + "http-body", 1667 + "httparse", 1668 + "httpdate", 1669 + "itoa", 1670 + "pin-project-lite", 1671 + "pin-utils", 1672 + "smallvec", 1673 + "tokio", 1674 + "want", 1675 + ] 1676 + 1677 + [[package]] 1678 + name = "hyper-rustls" 1679 + version = "0.27.7" 1680 + source = "registry+https://github.com/rust-lang/crates.io-index" 1681 + checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" 1682 + dependencies = [ 1683 + "http", 1684 + "hyper", 1685 + "hyper-util", 1686 + "rustls", 1687 + "rustls-pki-types", 1688 + "tokio", 1689 + "tokio-rustls", 1690 + "tower-service", 1691 + "webpki-roots 1.0.4", 1692 + ] 1693 + 1694 + [[package]] 1695 + name = "hyper-tls" 1696 + version = "0.6.0" 1697 + source = "registry+https://github.com/rust-lang/crates.io-index" 1698 + checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" 1699 + dependencies = [ 1700 + "bytes", 1701 + "http-body-util", 1702 + "hyper", 1703 + "hyper-util", 1704 + "native-tls", 1705 + "tokio", 1706 + "tokio-native-tls", 1707 + "tower-service", 1708 + ] 1709 + 1710 + [[package]] 1711 + name = "hyper-util" 1712 + version = "0.1.18" 1713 + source = "registry+https://github.com/rust-lang/crates.io-index" 1714 + checksum = "52e9a2a24dc5c6821e71a7030e1e14b7b632acac55c40e9d2e082c621261bb56" 1715 + dependencies = [ 1716 + "base64 0.22.1", 1717 + "bytes", 1718 + "futures-channel", 1719 + "futures-core", 1720 + "futures-util", 1721 + "http", 1722 + "http-body", 1723 + "hyper", 1724 + "ipnet", 1725 + "libc", 1726 + "percent-encoding", 1727 + "pin-project-lite", 1728 + "socket2 0.6.1", 1729 + "system-configuration", 1730 + "tokio", 1731 + "tower-service", 1732 + "tracing", 1733 + "windows-registry", 1734 + ] 1735 + 1736 + [[package]] 1737 + name = "iana-time-zone" 1738 + version = "0.1.64" 1739 + source = "registry+https://github.com/rust-lang/crates.io-index" 1740 + checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb" 1741 + dependencies = [ 1742 + "android_system_properties", 1743 + "core-foundation-sys", 1744 + "iana-time-zone-haiku", 1745 + "js-sys", 1746 + "log", 1747 + "wasm-bindgen", 1748 + "windows-core 0.62.2", 1749 + ] 1750 + 1751 + [[package]] 1752 + name = "iana-time-zone-haiku" 1753 + version = "0.1.2" 1754 + source = "registry+https://github.com/rust-lang/crates.io-index" 1755 + checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" 1756 + dependencies = [ 1757 + "cc", 1758 + ] 1759 + 1760 + [[package]] 1761 + name = "icu_collections" 1762 + version = "2.1.1" 1763 + source = "registry+https://github.com/rust-lang/crates.io-index" 1764 + checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" 1765 + dependencies = [ 1766 + "displaydoc", 1767 + "potential_utf", 1768 + "yoke", 1769 + "zerofrom", 1770 + "zerovec", 1771 + ] 1772 + 1773 + [[package]] 1774 + name = "icu_locale_core" 1775 + version = "2.1.1" 1776 + source = "registry+https://github.com/rust-lang/crates.io-index" 1777 + checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" 1778 + dependencies = [ 1779 + "displaydoc", 1780 + "litemap", 1781 + "tinystr", 1782 + "writeable", 1783 + "zerovec", 1784 + ] 1785 + 1786 + [[package]] 1787 + name = "icu_normalizer" 1788 + version = "2.1.1" 1789 + source = "registry+https://github.com/rust-lang/crates.io-index" 1790 + checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" 1791 + dependencies = [ 1792 + "icu_collections", 1793 + "icu_normalizer_data", 1794 + "icu_properties", 1795 + "icu_provider", 1796 + "smallvec", 1797 + "zerovec", 1798 + ] 1799 + 1800 + [[package]] 1801 + name = "icu_normalizer_data" 1802 + version = "2.1.1" 1803 + source = "registry+https://github.com/rust-lang/crates.io-index" 1804 + checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" 1805 + 1806 + [[package]] 1807 + name = "icu_properties" 1808 + version = "2.1.1" 1809 + source = "registry+https://github.com/rust-lang/crates.io-index" 1810 + checksum = "e93fcd3157766c0c8da2f8cff6ce651a31f0810eaa1c51ec363ef790bbb5fb99" 1811 + dependencies = [ 1812 + "icu_collections", 1813 + "icu_locale_core", 1814 + "icu_properties_data", 1815 + "icu_provider", 1816 + "zerotrie", 1817 + "zerovec", 1818 + ] 1819 + 1820 + [[package]] 1821 + name = "icu_properties_data" 1822 + version = "2.1.1" 1823 + source = "registry+https://github.com/rust-lang/crates.io-index" 1824 + checksum = "02845b3647bb045f1100ecd6480ff52f34c35f82d9880e029d329c21d1054899" 1825 + 1826 + [[package]] 1827 + name = "icu_provider" 1828 + version = "2.1.1" 1829 + source = "registry+https://github.com/rust-lang/crates.io-index" 1830 + checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" 1831 + dependencies = [ 1832 + "displaydoc", 1833 + "icu_locale_core", 1834 + "writeable", 1835 + "yoke", 1836 + "zerofrom", 1837 + "zerotrie", 1838 + "zerovec", 1839 + ] 1840 + 1841 + [[package]] 1842 + name = "ident_case" 1843 + version = "1.0.1" 1844 + source = "registry+https://github.com/rust-lang/crates.io-index" 1845 + checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" 1846 + 1847 + [[package]] 1848 + name = "idna" 1849 + version = "1.1.0" 1850 + source = "registry+https://github.com/rust-lang/crates.io-index" 1851 + checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" 1852 + dependencies = [ 1853 + "idna_adapter", 1854 + "smallvec", 1855 + "utf8_iter", 1856 + ] 1857 + 1858 + [[package]] 1859 + name = "idna_adapter" 1860 + version = "1.2.1" 1861 + source = "registry+https://github.com/rust-lang/crates.io-index" 1862 + checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" 1863 + dependencies = [ 1864 + "icu_normalizer", 1865 + "icu_properties", 1866 + ] 1867 + 1868 + [[package]] 1869 + name = "indexmap" 1870 + version = "1.9.3" 1871 + source = "registry+https://github.com/rust-lang/crates.io-index" 1872 + checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" 1873 + dependencies = [ 1874 + "autocfg", 1875 + "hashbrown 0.12.3", 1876 + "serde", 1877 + ] 1878 + 1879 + [[package]] 1880 + name = "indexmap" 1881 + version = "2.12.1" 1882 + source = "registry+https://github.com/rust-lang/crates.io-index" 1883 + checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2" 1884 + dependencies = [ 1885 + "equivalent", 1886 + "hashbrown 0.16.1", 1887 + "serde", 1888 + "serde_core", 1889 + ] 1890 + 1891 + [[package]] 1892 + name = "indoc" 1893 + version = "2.0.7" 1894 + source = "registry+https://github.com/rust-lang/crates.io-index" 1895 + checksum = "79cf5c93f93228cf8efb3ba362535fb11199ac548a09ce117c9b1adc3030d706" 1896 + dependencies = [ 1897 + "rustversion", 1898 + ] 1899 + 1900 + [[package]] 1901 + name = "inout" 1902 + version = "0.1.4" 1903 + source = "registry+https://github.com/rust-lang/crates.io-index" 1904 + checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" 1905 + dependencies = [ 1906 + "generic-array", 1907 + ] 1908 + 1909 + [[package]] 1910 + name = "inventory" 1911 + version = "0.3.21" 1912 + source = "registry+https://github.com/rust-lang/crates.io-index" 1913 + checksum = "bc61209c082fbeb19919bee74b176221b27223e27b65d781eb91af24eb1fb46e" 1914 + dependencies = [ 1915 + "rustversion", 1916 + ] 1917 + 1918 + [[package]] 1919 + name = "ipconfig" 1920 + version = "0.3.2" 1921 + source = "registry+https://github.com/rust-lang/crates.io-index" 1922 + checksum = "b58db92f96b720de98181bbbe63c831e87005ab460c1bf306eb2622b4707997f" 1923 + dependencies = [ 1924 + "socket2 0.5.10", 1925 + "widestring", 1926 + "windows-sys 0.48.0", 1927 + "winreg", 1928 + ] 1929 + 1930 + [[package]] 1931 + name = "ipld-core" 1932 + version = "0.4.2" 1933 + source = "registry+https://github.com/rust-lang/crates.io-index" 1934 + checksum = "104718b1cc124d92a6d01ca9c9258a7df311405debb3408c445a36452f9bf8db" 1935 + dependencies = [ 1936 + "cid", 1937 + "serde", 1938 + "serde_bytes", 1939 + ] 1940 + 1941 + [[package]] 1942 + name = "ipnet" 1943 + version = "2.11.0" 1944 + source = "registry+https://github.com/rust-lang/crates.io-index" 1945 + checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" 1946 + 1947 + [[package]] 1948 + name = "iri-string" 1949 + version = "0.7.9" 1950 + source = "registry+https://github.com/rust-lang/crates.io-index" 1951 + checksum = "4f867b9d1d896b67beb18518eda36fdb77a32ea590de864f1325b294a6d14397" 1952 + dependencies = [ 1953 + "memchr", 1954 + "serde", 1955 + ] 1956 + 1957 + [[package]] 1958 + name = "iroh-car" 1959 + version = "0.5.1" 1960 + source = "registry+https://github.com/rust-lang/crates.io-index" 1961 + checksum = "cb7f8cd4cb9aa083fba8b52e921764252d0b4dcb1cd6d120b809dbfe1106e81a" 1962 + dependencies = [ 1963 + "anyhow", 1964 + "cid", 1965 + "futures", 1966 + "serde", 1967 + "serde_ipld_dagcbor", 1968 + "thiserror 1.0.69", 1969 + "tokio", 1970 + "unsigned-varint 0.7.2", 1971 + ] 1972 + 1973 + [[package]] 1974 + name = "itoa" 1975 + version = "1.0.15" 1976 + source = "registry+https://github.com/rust-lang/crates.io-index" 1977 + checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" 1978 + 1979 + [[package]] 1980 + name = "jacquard" 1981 + version = "0.9.3" 1982 + source = "registry+https://github.com/rust-lang/crates.io-index" 1983 + checksum = "c19864761c8d69d23201fd19dd021cddf1fb7acbebb6e6b50e2b1776ec982768" 1984 + dependencies = [ 1985 + "bytes", 1986 + "getrandom 0.2.16", 1987 + "gloo-storage", 1988 + "http", 1989 + "jacquard-api", 1990 + "jacquard-common", 1991 + "jacquard-derive", 1992 + "jacquard-identity", 1993 + "jacquard-oauth", 1994 + "jose-jwk", 1995 + "miette", 1996 + "regex", 1997 + "regex-lite", 1998 + "reqwest", 1999 + "serde", 2000 + "serde_html_form", 2001 + "serde_json", 2002 + "smol_str", 2003 + "thiserror 2.0.17", 2004 + "tokio", 2005 + "trait-variant", 2006 + "url", 2007 + "webpage", 2008 + ] 2009 + 2010 + [[package]] 2011 + name = "jacquard-api" 2012 + version = "0.9.2" 2013 + source = "registry+https://github.com/rust-lang/crates.io-index" 2014 + checksum = "bbbfd6e2b10fa1731f4d4e40c8f791956b0d4f804fb3efef891afec903f20597" 2015 + dependencies = [ 2016 + "bon", 2017 + "bytes", 2018 + "jacquard-common", 2019 + "jacquard-derive", 2020 + "jacquard-lexicon", 2021 + "miette", 2022 + "rustversion", 2023 + "serde", 2024 + "serde_ipld_dagcbor", 2025 + "thiserror 2.0.17", 2026 + "unicode-segmentation", 2027 + ] 2028 + 2029 + [[package]] 2030 + name = "jacquard-axum" 2031 + version = "0.9.2" 2032 + source = "registry+https://github.com/rust-lang/crates.io-index" 2033 + checksum = "ee1b58111f0a2a08ee18525ac661a0448fdc84d4ecb423d64aaaf88074460380" 2034 + dependencies = [ 2035 + "axum", 2036 + "bytes", 2037 + "jacquard", 2038 + "jacquard-common", 2039 + "jacquard-derive", 2040 + "jacquard-identity", 2041 + "miette", 2042 + "multibase", 2043 + "serde", 2044 + "serde_html_form", 2045 + "serde_json", 2046 + "thiserror 2.0.17", 2047 + "tokio", 2048 + "tower-http", 2049 + "tracing", 2050 + ] 2051 + 2052 + [[package]] 2053 + name = "jacquard-common" 2054 + version = "0.9.2" 2055 + source = "registry+https://github.com/rust-lang/crates.io-index" 2056 + checksum = "df86cb117d9f1c2b0251ba67c3f0e3f963fd22abc6cf8de0e02a7fc846c288ca" 2057 + dependencies = [ 2058 + "base64 0.22.1", 2059 + "bon", 2060 + "bytes", 2061 + "chrono", 2062 + "cid", 2063 + "ed25519-dalek", 2064 + "getrandom 0.2.16", 2065 + "getrandom 0.3.4", 2066 + "http", 2067 + "ipld-core", 2068 + "k256", 2069 + "langtag", 2070 + "miette", 2071 + "multibase", 2072 + "multihash", 2073 + "ouroboros", 2074 + "p256", 2075 + "rand 0.9.2", 2076 + "regex", 2077 + "regex-lite", 2078 + "reqwest", 2079 + "serde", 2080 + "serde_html_form", 2081 + "serde_ipld_dagcbor", 2082 + "serde_json", 2083 + "signature", 2084 + "smol_str", 2085 + "thiserror 2.0.17", 2086 + "tokio", 2087 + "tokio-util", 2088 + "trait-variant", 2089 + "url", 2090 + ] 2091 + 2092 + [[package]] 2093 + name = "jacquard-derive" 2094 + version = "0.9.2" 2095 + source = "registry+https://github.com/rust-lang/crates.io-index" 2096 + checksum = "42ca61a69dc7aa8fb2d7163416514ff7df5d79f2e8b22e269f4610afa85572fe" 2097 + dependencies = [ 2098 + "heck 0.5.0", 2099 + "jacquard-lexicon", 2100 + "proc-macro2", 2101 + "quote", 2102 + "syn 2.0.111", 2103 + ] 2104 + 2105 + [[package]] 2106 + name = "jacquard-identity" 2107 + version = "0.9.2" 2108 + source = "registry+https://github.com/rust-lang/crates.io-index" 2109 + checksum = "1ef714cacebfca486558a9f8e205daf466bfba0466c4d0c450fd6d0252400a53" 2110 + dependencies = [ 2111 + "bon", 2112 + "bytes", 2113 + "hickory-resolver", 2114 + "http", 2115 + "jacquard-api", 2116 + "jacquard-common", 2117 + "jacquard-lexicon", 2118 + "miette", 2119 + "mini-moka", 2120 + "percent-encoding", 2121 + "reqwest", 2122 + "serde", 2123 + "serde_html_form", 2124 + "serde_json", 2125 + "thiserror 2.0.17", 2126 + "tokio", 2127 + "trait-variant", 2128 + "url", 2129 + "urlencoding", 2130 + ] 2131 + 2132 + [[package]] 2133 + name = "jacquard-lexicon" 2134 + version = "0.9.2" 2135 + source = "registry+https://github.com/rust-lang/crates.io-index" 2136 + checksum = "de87f2c938faea1b1f1b32d5b9e0c870e7b5bb5efbf96e3692ae2d8f6b2beb7a" 2137 + dependencies = [ 2138 + "cid", 2139 + "dashmap 6.1.0", 2140 + "heck 0.5.0", 2141 + "inventory", 2142 + "jacquard-common", 2143 + "miette", 2144 + "multihash", 2145 + "prettyplease", 2146 + "proc-macro2", 2147 + "quote", 2148 + "serde", 2149 + "serde_ipld_dagcbor", 2150 + "serde_json", 2151 + "serde_repr", 2152 + "serde_with", 2153 + "sha2", 2154 + "syn 2.0.111", 2155 + "thiserror 2.0.17", 2156 + "unicode-segmentation", 2157 + ] 2158 + 2159 + [[package]] 2160 + name = "jacquard-oauth" 2161 + version = "0.9.2" 2162 + source = "registry+https://github.com/rust-lang/crates.io-index" 2163 + checksum = "aafe9b4b2160cb57cd48d02d84d2c09706853d098e053baacb06a59fcd59a898" 2164 + dependencies = [ 2165 + "base64 0.22.1", 2166 + "bytes", 2167 + "chrono", 2168 + "dashmap 6.1.0", 2169 + "elliptic-curve", 2170 + "http", 2171 + "jacquard-common", 2172 + "jacquard-identity", 2173 + "jose-jwa", 2174 + "jose-jwk", 2175 + "miette", 2176 + "p256", 2177 + "rand 0.8.5", 2178 + "rouille", 2179 + "serde", 2180 + "serde_html_form", 2181 + "serde_json", 2182 + "sha2", 2183 + "smol_str", 2184 + "thiserror 2.0.17", 2185 + "tokio", 2186 + "trait-variant", 2187 + "url", 2188 + "webbrowser", 2189 + ] 2190 + 2191 + [[package]] 2192 + name = "jacquard-repo" 2193 + version = "0.9.2" 2194 + source = "registry+https://github.com/rust-lang/crates.io-index" 2195 + checksum = "2ccdadfea11df142fbfb11cf7e5c1f7b8c0548758dedb0ce0eac182777f91f18" 2196 + dependencies = [ 2197 + "bytes", 2198 + "cid", 2199 + "ed25519-dalek", 2200 + "iroh-car", 2201 + "jacquard-common", 2202 + "jacquard-derive", 2203 + "k256", 2204 + "miette", 2205 + "multihash", 2206 + "n0-future", 2207 + "p256", 2208 + "serde", 2209 + "serde_bytes", 2210 + "serde_ipld_dagcbor", 2211 + "sha2", 2212 + "smol_str", 2213 + "thiserror 2.0.17", 2214 + "tokio", 2215 + "trait-variant", 2216 + ] 2217 + 2218 + [[package]] 2219 + name = "jni" 2220 + version = "0.21.1" 2221 + source = "registry+https://github.com/rust-lang/crates.io-index" 2222 + checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" 2223 + dependencies = [ 2224 + "cesu8", 2225 + "cfg-if", 2226 + "combine", 2227 + "jni-sys", 2228 + "log", 2229 + "thiserror 1.0.69", 2230 + "walkdir", 2231 + "windows-sys 0.45.0", 2232 + ] 2233 + 2234 + [[package]] 2235 + name = "jni-sys" 2236 + version = "0.3.0" 2237 + source = "registry+https://github.com/rust-lang/crates.io-index" 2238 + checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" 2239 + 2240 + [[package]] 2241 + name = "jose-b64" 2242 + version = "0.1.2" 2243 + source = "registry+https://github.com/rust-lang/crates.io-index" 2244 + checksum = "bec69375368709666b21c76965ce67549f2d2db7605f1f8707d17c9656801b56" 2245 + dependencies = [ 2246 + "base64ct", 2247 + "serde", 2248 + "subtle", 2249 + "zeroize", 2250 + ] 2251 + 2252 + [[package]] 2253 + name = "jose-jwa" 2254 + version = "0.1.2" 2255 + source = "registry+https://github.com/rust-lang/crates.io-index" 2256 + checksum = "9ab78e053fe886a351d67cf0d194c000f9d0dcb92906eb34d853d7e758a4b3a7" 2257 + dependencies = [ 2258 + "serde", 2259 + ] 2260 + 2261 + [[package]] 2262 + name = "jose-jwk" 2263 + version = "0.1.2" 2264 + source = "registry+https://github.com/rust-lang/crates.io-index" 2265 + checksum = "280fa263807fe0782ecb6f2baadc28dffc04e00558a58e33bfdb801d11fd58e7" 2266 + dependencies = [ 2267 + "jose-b64", 2268 + "jose-jwa", 2269 + "p256", 2270 + "p384", 2271 + "rsa", 2272 + "serde", 2273 + "zeroize", 2274 + ] 2275 + 2276 + [[package]] 2277 + name = "js-sys" 2278 + version = "0.3.83" 2279 + source = "registry+https://github.com/rust-lang/crates.io-index" 2280 + checksum = "464a3709c7f55f1f721e5389aa6ea4e3bc6aba669353300af094b29ffbdde1d8" 2281 + dependencies = [ 2282 + "once_cell", 2283 + "wasm-bindgen", 2284 + ] 2285 + 2286 + [[package]] 2287 + name = "jsonwebtoken" 2288 + version = "10.2.0" 2289 + source = "registry+https://github.com/rust-lang/crates.io-index" 2290 + checksum = "c76e1c7d7df3e34443b3621b459b066a7b79644f059fc8b2db7070c825fd417e" 2291 + dependencies = [ 2292 + "base64 0.22.1", 2293 + "ed25519-dalek", 2294 + "getrandom 0.2.16", 2295 + "hmac", 2296 + "js-sys", 2297 + "p256", 2298 + "p384", 2299 + "pem", 2300 + "rand 0.8.5", 2301 + "rsa", 2302 + "serde", 2303 + "serde_json", 2304 + "sha2", 2305 + "signature", 2306 + "simple_asn1", 2307 + ] 2308 + 2309 + [[package]] 2310 + name = "k256" 2311 + version = "0.13.4" 2312 + source = "registry+https://github.com/rust-lang/crates.io-index" 2313 + checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b" 2314 + dependencies = [ 2315 + "cfg-if", 2316 + "ecdsa", 2317 + "elliptic-curve", 2318 + "once_cell", 2319 + "sha2", 2320 + "signature", 2321 + ] 2322 + 2323 + [[package]] 2324 + name = "langtag" 2325 + version = "0.4.0" 2326 + source = "registry+https://github.com/rust-lang/crates.io-index" 2327 + checksum = "9ecb4c689a30e48ebeaa14237f34037e300dd072e6ad21a9ec72e810ff3c6600" 2328 + dependencies = [ 2329 + "serde", 2330 + "static-regular-grammar", 2331 + "thiserror 1.0.69", 2332 + ] 2333 + 2334 + [[package]] 2335 + name = "lazy_static" 2336 + version = "1.5.0" 2337 + source = "registry+https://github.com/rust-lang/crates.io-index" 2338 + checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" 2339 + dependencies = [ 2340 + "spin 0.9.8", 2341 + ] 2342 + 2343 + [[package]] 2344 + name = "libc" 2345 + version = "0.2.178" 2346 + source = "registry+https://github.com/rust-lang/crates.io-index" 2347 + checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091" 2348 + 2349 + [[package]] 2350 + name = "libm" 2351 + version = "0.2.15" 2352 + source = "registry+https://github.com/rust-lang/crates.io-index" 2353 + checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" 2354 + 2355 + [[package]] 2356 + name = "libredox" 2357 + version = "0.1.10" 2358 + source = "registry+https://github.com/rust-lang/crates.io-index" 2359 + checksum = "416f7e718bdb06000964960ffa43b4335ad4012ae8b99060261aa4a8088d5ccb" 2360 + dependencies = [ 2361 + "bitflags", 2362 + "libc", 2363 + "redox_syscall", 2364 + ] 2365 + 2366 + [[package]] 2367 + name = "libsqlite3-sys" 2368 + version = "0.30.1" 2369 + source = "registry+https://github.com/rust-lang/crates.io-index" 2370 + checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149" 2371 + dependencies = [ 2372 + "pkg-config", 2373 + "vcpkg", 2374 + ] 2375 + 2376 + [[package]] 2377 + name = "linked-hash-map" 2378 + version = "0.5.6" 2379 + source = "registry+https://github.com/rust-lang/crates.io-index" 2380 + checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" 2381 + 2382 + [[package]] 2383 + name = "linux-raw-sys" 2384 + version = "0.11.0" 2385 + source = "registry+https://github.com/rust-lang/crates.io-index" 2386 + checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" 2387 + 2388 + [[package]] 2389 + name = "litemap" 2390 + version = "0.8.1" 2391 + source = "registry+https://github.com/rust-lang/crates.io-index" 2392 + checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" 2393 + 2394 + [[package]] 2395 + name = "lock_api" 2396 + version = "0.4.14" 2397 + source = "registry+https://github.com/rust-lang/crates.io-index" 2398 + checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" 2399 + dependencies = [ 2400 + "scopeguard", 2401 + ] 2402 + 2403 + [[package]] 2404 + name = "log" 2405 + version = "0.4.28" 2406 + source = "registry+https://github.com/rust-lang/crates.io-index" 2407 + checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" 2408 + 2409 + [[package]] 2410 + name = "loom" 2411 + version = "0.7.2" 2412 + source = "registry+https://github.com/rust-lang/crates.io-index" 2413 + checksum = "419e0dc8046cb947daa77eb95ae174acfbddb7673b4151f56d1eed8e93fbfaca" 2414 + dependencies = [ 2415 + "cfg-if", 2416 + "generator", 2417 + "scoped-tls", 2418 + "tracing", 2419 + "tracing-subscriber", 2420 + ] 2421 + 2422 + [[package]] 2423 + name = "lru-cache" 2424 + version = "0.1.2" 2425 + source = "registry+https://github.com/rust-lang/crates.io-index" 2426 + checksum = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" 2427 + dependencies = [ 2428 + "linked-hash-map", 2429 + ] 2430 + 2431 + [[package]] 2432 + name = "lru-slab" 2433 + version = "0.1.2" 2434 + source = "registry+https://github.com/rust-lang/crates.io-index" 2435 + checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" 2436 + 2437 + [[package]] 2438 + name = "mac" 2439 + version = "0.1.1" 2440 + source = "registry+https://github.com/rust-lang/crates.io-index" 2441 + checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" 2442 + 2443 + [[package]] 2444 + name = "markup5ever" 2445 + version = "0.12.1" 2446 + source = "registry+https://github.com/rust-lang/crates.io-index" 2447 + checksum = "16ce3abbeba692c8b8441d036ef91aea6df8da2c6b6e21c7e14d3c18e526be45" 2448 + dependencies = [ 2449 + "log", 2450 + "phf", 2451 + "phf_codegen", 2452 + "string_cache", 2453 + "string_cache_codegen", 2454 + "tendril", 2455 + ] 2456 + 2457 + [[package]] 2458 + name = "markup5ever_rcdom" 2459 + version = "0.3.0" 2460 + source = "registry+https://github.com/rust-lang/crates.io-index" 2461 + checksum = "edaa21ab3701bfee5099ade5f7e1f84553fd19228cf332f13cd6e964bf59be18" 2462 + dependencies = [ 2463 + "html5ever", 2464 + "markup5ever", 2465 + "tendril", 2466 + "xml5ever", 2467 + ] 2468 + 2469 + [[package]] 2470 + name = "match-lookup" 2471 + version = "0.1.1" 2472 + source = "registry+https://github.com/rust-lang/crates.io-index" 2473 + checksum = "1265724d8cb29dbbc2b0f06fffb8bf1a8c0cf73a78eede9ba73a4a66c52a981e" 2474 + dependencies = [ 2475 + "proc-macro2", 2476 + "quote", 2477 + "syn 1.0.109", 2478 + ] 2479 + 2480 + [[package]] 2481 + name = "matchers" 2482 + version = "0.2.0" 2483 + source = "registry+https://github.com/rust-lang/crates.io-index" 2484 + checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9" 2485 + dependencies = [ 2486 + "regex-automata", 2487 + ] 2488 + 2489 + [[package]] 2490 + name = "matchit" 2491 + version = "0.8.4" 2492 + source = "registry+https://github.com/rust-lang/crates.io-index" 2493 + checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" 2494 + 2495 + [[package]] 2496 + name = "md-5" 2497 + version = "0.10.6" 2498 + source = "registry+https://github.com/rust-lang/crates.io-index" 2499 + checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" 2500 + dependencies = [ 2501 + "cfg-if", 2502 + "digest", 2503 + ] 2504 + 2505 + [[package]] 2506 + name = "memchr" 2507 + version = "2.7.6" 2508 + source = "registry+https://github.com/rust-lang/crates.io-index" 2509 + checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" 2510 + 2511 + [[package]] 2512 + name = "miette" 2513 + version = "7.6.0" 2514 + source = "registry+https://github.com/rust-lang/crates.io-index" 2515 + checksum = "5f98efec8807c63c752b5bd61f862c165c115b0a35685bdcfd9238c7aeb592b7" 2516 + dependencies = [ 2517 + "cfg-if", 2518 + "miette-derive", 2519 + "unicode-width", 2520 + ] 2521 + 2522 + [[package]] 2523 + name = "miette-derive" 2524 + version = "7.6.0" 2525 + source = "registry+https://github.com/rust-lang/crates.io-index" 2526 + checksum = "db5b29714e950dbb20d5e6f74f9dcec4edbcc1067bb7f8ed198c097b8c1a818b" 2527 + dependencies = [ 2528 + "proc-macro2", 2529 + "quote", 2530 + "syn 2.0.111", 2531 + ] 2532 + 2533 + [[package]] 2534 + name = "mime" 2535 + version = "0.3.17" 2536 + source = "registry+https://github.com/rust-lang/crates.io-index" 2537 + checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" 2538 + 2539 + [[package]] 2540 + name = "mime_guess" 2541 + version = "2.0.5" 2542 + source = "registry+https://github.com/rust-lang/crates.io-index" 2543 + checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" 2544 + dependencies = [ 2545 + "mime", 2546 + "unicase", 2547 + ] 2548 + 2549 + [[package]] 2550 + name = "mini-moka" 2551 + version = "0.10.3" 2552 + source = "registry+https://github.com/rust-lang/crates.io-index" 2553 + checksum = "c325dfab65f261f386debee8b0969da215b3fa0037e74c8a1234db7ba986d803" 2554 + dependencies = [ 2555 + "crossbeam-channel", 2556 + "crossbeam-utils", 2557 + "dashmap 5.5.3", 2558 + "skeptic", 2559 + "smallvec", 2560 + "tagptr", 2561 + "triomphe", 2562 + ] 2563 + 2564 + [[package]] 2565 + name = "minimal-lexical" 2566 + version = "0.2.1" 2567 + source = "registry+https://github.com/rust-lang/crates.io-index" 2568 + checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" 2569 + 2570 + [[package]] 2571 + name = "miniz_oxide" 2572 + version = "0.8.9" 2573 + source = "registry+https://github.com/rust-lang/crates.io-index" 2574 + checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" 2575 + dependencies = [ 2576 + "adler2", 2577 + "simd-adler32", 2578 + ] 2579 + 2580 + [[package]] 2581 + name = "mio" 2582 + version = "1.1.0" 2583 + source = "registry+https://github.com/rust-lang/crates.io-index" 2584 + checksum = "69d83b0086dc8ecf3ce9ae2874b2d1290252e2a30720bea58a5c6639b0092873" 2585 + dependencies = [ 2586 + "libc", 2587 + "wasi", 2588 + "windows-sys 0.61.2", 2589 + ] 2590 + 2591 + [[package]] 2592 + name = "multibase" 2593 + version = "0.9.2" 2594 + source = "registry+https://github.com/rust-lang/crates.io-index" 2595 + checksum = "8694bb4835f452b0e3bb06dbebb1d6fc5385b6ca1caf2e55fd165c042390ec77" 2596 + dependencies = [ 2597 + "base-x", 2598 + "base256emoji", 2599 + "data-encoding", 2600 + "data-encoding-macro", 2601 + ] 2602 + 2603 + [[package]] 2604 + name = "multihash" 2605 + version = "0.19.3" 2606 + source = "registry+https://github.com/rust-lang/crates.io-index" 2607 + checksum = "6b430e7953c29dd6a09afc29ff0bb69c6e306329ee6794700aee27b76a1aea8d" 2608 + dependencies = [ 2609 + "core2", 2610 + "serde", 2611 + "unsigned-varint 0.8.0", 2612 + ] 2613 + 2614 + [[package]] 2615 + name = "multipart" 2616 + version = "0.18.0" 2617 + source = "registry+https://github.com/rust-lang/crates.io-index" 2618 + checksum = "00dec633863867f29cb39df64a397cdf4a6354708ddd7759f70c7fb51c5f9182" 2619 + dependencies = [ 2620 + "buf_redux", 2621 + "httparse", 2622 + "log", 2623 + "mime", 2624 + "mime_guess", 2625 + "quick-error", 2626 + "rand 0.8.5", 2627 + "safemem", 2628 + "tempfile", 2629 + "twoway", 2630 + ] 2631 + 2632 + [[package]] 2633 + name = "n0-future" 2634 + version = "0.1.3" 2635 + source = "registry+https://github.com/rust-lang/crates.io-index" 2636 + checksum = "7bb0e5d99e681ab3c938842b96fcb41bf8a7bb4bfdb11ccbd653a7e83e06c794" 2637 + dependencies = [ 2638 + "cfg_aliases", 2639 + "derive_more", 2640 + "futures-buffered", 2641 + "futures-lite", 2642 + "futures-util", 2643 + "js-sys", 2644 + "pin-project", 2645 + "send_wrapper", 2646 + "tokio", 2647 + "tokio-util", 2648 + "wasm-bindgen", 2649 + "wasm-bindgen-futures", 2650 + "web-time", 2651 + ] 2652 + 2653 + [[package]] 2654 + name = "native-tls" 2655 + version = "0.2.14" 2656 + source = "registry+https://github.com/rust-lang/crates.io-index" 2657 + checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e" 2658 + dependencies = [ 2659 + "libc", 2660 + "log", 2661 + "openssl", 2662 + "openssl-probe", 2663 + "openssl-sys", 2664 + "schannel", 2665 + "security-framework", 2666 + "security-framework-sys", 2667 + "tempfile", 2668 + ] 2669 + 2670 + [[package]] 2671 + name = "ndk-context" 2672 + version = "0.1.1" 2673 + source = "registry+https://github.com/rust-lang/crates.io-index" 2674 + checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" 2675 + 2676 + [[package]] 2677 + name = "new_debug_unreachable" 2678 + version = "1.0.6" 2679 + source = "registry+https://github.com/rust-lang/crates.io-index" 2680 + checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" 2681 + 2682 + [[package]] 2683 + name = "nom" 2684 + version = "7.1.3" 2685 + source = "registry+https://github.com/rust-lang/crates.io-index" 2686 + checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" 2687 + dependencies = [ 2688 + "memchr", 2689 + "minimal-lexical", 2690 + ] 2691 + 2692 + [[package]] 2693 + name = "nu-ansi-term" 2694 + version = "0.50.3" 2695 + source = "registry+https://github.com/rust-lang/crates.io-index" 2696 + checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" 2697 + dependencies = [ 2698 + "windows-sys 0.61.2", 2699 + ] 2700 + 2701 + [[package]] 2702 + name = "num-bigint" 2703 + version = "0.4.6" 2704 + source = "registry+https://github.com/rust-lang/crates.io-index" 2705 + checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" 2706 + dependencies = [ 2707 + "num-integer", 2708 + "num-traits", 2709 + ] 2710 + 2711 + [[package]] 2712 + name = "num-bigint-dig" 2713 + version = "0.8.6" 2714 + source = "registry+https://github.com/rust-lang/crates.io-index" 2715 + checksum = "e661dda6640fad38e827a6d4a310ff4763082116fe217f279885c97f511bb0b7" 2716 + dependencies = [ 2717 + "lazy_static", 2718 + "libm", 2719 + "num-integer", 2720 + "num-iter", 2721 + "num-traits", 2722 + "rand 0.8.5", 2723 + "smallvec", 2724 + "zeroize", 2725 + ] 2726 + 2727 + [[package]] 2728 + name = "num-conv" 2729 + version = "0.1.0" 2730 + source = "registry+https://github.com/rust-lang/crates.io-index" 2731 + checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" 2732 + 2733 + [[package]] 2734 + name = "num-integer" 2735 + version = "0.1.46" 2736 + source = "registry+https://github.com/rust-lang/crates.io-index" 2737 + checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" 2738 + dependencies = [ 2739 + "num-traits", 2740 + ] 2741 + 2742 + [[package]] 2743 + name = "num-iter" 2744 + version = "0.1.45" 2745 + source = "registry+https://github.com/rust-lang/crates.io-index" 2746 + checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" 2747 + dependencies = [ 2748 + "autocfg", 2749 + "num-integer", 2750 + "num-traits", 2751 + ] 2752 + 2753 + [[package]] 2754 + name = "num-traits" 2755 + version = "0.2.19" 2756 + source = "registry+https://github.com/rust-lang/crates.io-index" 2757 + checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" 2758 + dependencies = [ 2759 + "autocfg", 2760 + "libm", 2761 + ] 2762 + 2763 + [[package]] 2764 + name = "num_cpus" 2765 + version = "1.17.0" 2766 + source = "registry+https://github.com/rust-lang/crates.io-index" 2767 + checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b" 2768 + dependencies = [ 2769 + "hermit-abi", 2770 + "libc", 2771 + ] 2772 + 2773 + [[package]] 2774 + name = "num_threads" 2775 + version = "0.1.7" 2776 + source = "registry+https://github.com/rust-lang/crates.io-index" 2777 + checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" 2778 + dependencies = [ 2779 + "libc", 2780 + ] 2781 + 2782 + [[package]] 2783 + name = "objc2" 2784 + version = "0.6.3" 2785 + source = "registry+https://github.com/rust-lang/crates.io-index" 2786 + checksum = "b7c2599ce0ec54857b29ce62166b0ed9b4f6f1a70ccc9a71165b6154caca8c05" 2787 + dependencies = [ 2788 + "objc2-encode", 2789 + ] 2790 + 2791 + [[package]] 2792 + name = "objc2-encode" 2793 + version = "4.1.0" 2794 + source = "registry+https://github.com/rust-lang/crates.io-index" 2795 + checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" 2796 + 2797 + [[package]] 2798 + name = "objc2-foundation" 2799 + version = "0.3.2" 2800 + source = "registry+https://github.com/rust-lang/crates.io-index" 2801 + checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" 2802 + dependencies = [ 2803 + "bitflags", 2804 + "objc2", 2805 + ] 2806 + 2807 + [[package]] 2808 + name = "once_cell" 2809 + version = "1.21.3" 2810 + source = "registry+https://github.com/rust-lang/crates.io-index" 2811 + checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" 2812 + 2813 + [[package]] 2814 + name = "openssl" 2815 + version = "0.10.75" 2816 + source = "registry+https://github.com/rust-lang/crates.io-index" 2817 + checksum = "08838db121398ad17ab8531ce9de97b244589089e290a384c900cb9ff7434328" 2818 + dependencies = [ 2819 + "bitflags", 2820 + "cfg-if", 2821 + "foreign-types", 2822 + "libc", 2823 + "once_cell", 2824 + "openssl-macros", 2825 + "openssl-sys", 2826 + ] 2827 + 2828 + [[package]] 2829 + name = "openssl-macros" 2830 + version = "0.1.1" 2831 + source = "registry+https://github.com/rust-lang/crates.io-index" 2832 + checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" 2833 + dependencies = [ 2834 + "proc-macro2", 2835 + "quote", 2836 + "syn 2.0.111", 2837 + ] 2838 + 2839 + [[package]] 2840 + name = "openssl-probe" 2841 + version = "0.1.6" 2842 + source = "registry+https://github.com/rust-lang/crates.io-index" 2843 + checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" 2844 + 2845 + [[package]] 2846 + name = "openssl-sys" 2847 + version = "0.9.111" 2848 + source = "registry+https://github.com/rust-lang/crates.io-index" 2849 + checksum = "82cab2d520aa75e3c58898289429321eb788c3106963d0dc886ec7a5f4adc321" 2850 + dependencies = [ 2851 + "cc", 2852 + "libc", 2853 + "pkg-config", 2854 + "vcpkg", 2855 + ] 2856 + 2857 + [[package]] 2858 + name = "ouroboros" 2859 + version = "0.18.5" 2860 + source = "registry+https://github.com/rust-lang/crates.io-index" 2861 + checksum = "1e0f050db9c44b97a94723127e6be766ac5c340c48f2c4bb3ffa11713744be59" 2862 + dependencies = [ 2863 + "aliasable", 2864 + "ouroboros_macro", 2865 + "static_assertions", 2866 + ] 2867 + 2868 + [[package]] 2869 + name = "ouroboros_macro" 2870 + version = "0.18.5" 2871 + source = "registry+https://github.com/rust-lang/crates.io-index" 2872 + checksum = "3c7028bdd3d43083f6d8d4d5187680d0d3560d54df4cc9d752005268b41e64d0" 2873 + dependencies = [ 2874 + "heck 0.4.1", 2875 + "proc-macro2", 2876 + "proc-macro2-diagnostics", 2877 + "quote", 2878 + "syn 2.0.111", 2879 + ] 2880 + 2881 + [[package]] 2882 + name = "p256" 2883 + version = "0.13.2" 2884 + source = "registry+https://github.com/rust-lang/crates.io-index" 2885 + checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" 2886 + dependencies = [ 2887 + "ecdsa", 2888 + "elliptic-curve", 2889 + "primeorder", 2890 + "sha2", 2891 + ] 2892 + 2893 + [[package]] 2894 + name = "p384" 2895 + version = "0.13.1" 2896 + source = "registry+https://github.com/rust-lang/crates.io-index" 2897 + checksum = "fe42f1670a52a47d448f14b6a5c61dd78fce51856e68edaa38f7ae3a46b8d6b6" 2898 + dependencies = [ 2899 + "ecdsa", 2900 + "elliptic-curve", 2901 + "primeorder", 2902 + "sha2", 2903 + ] 2904 + 2905 + [[package]] 2906 + name = "parking" 2907 + version = "2.2.1" 2908 + source = "registry+https://github.com/rust-lang/crates.io-index" 2909 + checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" 2910 + 2911 + [[package]] 2912 + name = "parking_lot" 2913 + version = "0.12.5" 2914 + source = "registry+https://github.com/rust-lang/crates.io-index" 2915 + checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" 2916 + dependencies = [ 2917 + "lock_api", 2918 + "parking_lot_core", 2919 + ] 2920 + 2921 + [[package]] 2922 + name = "parking_lot_core" 2923 + version = "0.9.12" 2924 + source = "registry+https://github.com/rust-lang/crates.io-index" 2925 + checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" 2926 + dependencies = [ 2927 + "cfg-if", 2928 + "libc", 2929 + "redox_syscall", 2930 + "smallvec", 2931 + "windows-link 0.2.1", 2932 + ] 2933 + 2934 + [[package]] 2935 + name = "pem" 2936 + version = "3.0.6" 2937 + source = "registry+https://github.com/rust-lang/crates.io-index" 2938 + checksum = "1d30c53c26bc5b31a98cd02d20f25a7c8567146caf63ed593a9d87b2775291be" 2939 + dependencies = [ 2940 + "base64 0.22.1", 2941 + "serde_core", 2942 + ] 2943 + 2944 + [[package]] 2945 + name = "pem-rfc7468" 2946 + version = "0.7.0" 2947 + source = "registry+https://github.com/rust-lang/crates.io-index" 2948 + checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" 2949 + dependencies = [ 2950 + "base64ct", 2951 + ] 2952 + 2953 + [[package]] 2954 + name = "percent-encoding" 2955 + version = "2.3.2" 2956 + source = "registry+https://github.com/rust-lang/crates.io-index" 2957 + checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" 2958 + 2959 + [[package]] 2960 + name = "phf" 2961 + version = "0.11.3" 2962 + source = "registry+https://github.com/rust-lang/crates.io-index" 2963 + checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" 2964 + dependencies = [ 2965 + "phf_shared", 2966 + ] 2967 + 2968 + [[package]] 2969 + name = "phf_codegen" 2970 + version = "0.11.3" 2971 + source = "registry+https://github.com/rust-lang/crates.io-index" 2972 + checksum = "aef8048c789fa5e851558d709946d6d79a8ff88c0440c587967f8e94bfb1216a" 2973 + dependencies = [ 2974 + "phf_generator", 2975 + "phf_shared", 2976 + ] 2977 + 2978 + [[package]] 2979 + name = "phf_generator" 2980 + version = "0.11.3" 2981 + source = "registry+https://github.com/rust-lang/crates.io-index" 2982 + checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" 2983 + dependencies = [ 2984 + "phf_shared", 2985 + "rand 0.8.5", 2986 + ] 2987 + 2988 + [[package]] 2989 + name = "phf_shared" 2990 + version = "0.11.3" 2991 + source = "registry+https://github.com/rust-lang/crates.io-index" 2992 + checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" 2993 + dependencies = [ 2994 + "siphasher", 2995 + ] 2996 + 2997 + [[package]] 2998 + name = "pin-project" 2999 + version = "1.1.10" 3000 + source = "registry+https://github.com/rust-lang/crates.io-index" 3001 + checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a" 3002 + dependencies = [ 3003 + "pin-project-internal", 3004 + ] 3005 + 3006 + [[package]] 3007 + name = "pin-project-internal" 3008 + version = "1.1.10" 3009 + source = "registry+https://github.com/rust-lang/crates.io-index" 3010 + checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" 3011 + dependencies = [ 3012 + "proc-macro2", 3013 + "quote", 3014 + "syn 2.0.111", 3015 + ] 3016 + 3017 + [[package]] 3018 + name = "pin-project-lite" 3019 + version = "0.2.16" 3020 + source = "registry+https://github.com/rust-lang/crates.io-index" 3021 + checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" 3022 + 3023 + [[package]] 3024 + name = "pin-utils" 3025 + version = "0.1.0" 3026 + source = "registry+https://github.com/rust-lang/crates.io-index" 3027 + checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 3028 + 3029 + [[package]] 3030 + name = "pkcs1" 3031 + version = "0.7.5" 3032 + source = "registry+https://github.com/rust-lang/crates.io-index" 3033 + checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" 3034 + dependencies = [ 3035 + "der", 3036 + "pkcs8", 3037 + "spki", 3038 + ] 3039 + 3040 + [[package]] 3041 + name = "pkcs8" 3042 + version = "0.10.2" 3043 + source = "registry+https://github.com/rust-lang/crates.io-index" 3044 + checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" 3045 + dependencies = [ 3046 + "der", 3047 + "spki", 3048 + ] 3049 + 3050 + [[package]] 3051 + name = "pkg-config" 3052 + version = "0.3.32" 3053 + source = "registry+https://github.com/rust-lang/crates.io-index" 3054 + checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" 3055 + 3056 + [[package]] 3057 + name = "potential_utf" 3058 + version = "0.1.4" 3059 + source = "registry+https://github.com/rust-lang/crates.io-index" 3060 + checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" 3061 + dependencies = [ 3062 + "zerovec", 3063 + ] 3064 + 3065 + [[package]] 3066 + name = "powerfmt" 3067 + version = "0.2.0" 3068 + source = "registry+https://github.com/rust-lang/crates.io-index" 3069 + checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" 3070 + 3071 + [[package]] 3072 + name = "ppv-lite86" 3073 + version = "0.2.21" 3074 + source = "registry+https://github.com/rust-lang/crates.io-index" 3075 + checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" 3076 + dependencies = [ 3077 + "zerocopy", 3078 + ] 3079 + 3080 + [[package]] 3081 + name = "precomputed-hash" 3082 + version = "0.1.1" 3083 + source = "registry+https://github.com/rust-lang/crates.io-index" 3084 + checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" 3085 + 3086 + [[package]] 3087 + name = "prettyplease" 3088 + version = "0.2.37" 3089 + source = "registry+https://github.com/rust-lang/crates.io-index" 3090 + checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" 3091 + dependencies = [ 3092 + "proc-macro2", 3093 + "syn 2.0.111", 3094 + ] 3095 + 3096 + [[package]] 3097 + name = "primeorder" 3098 + version = "0.13.6" 3099 + source = "registry+https://github.com/rust-lang/crates.io-index" 3100 + checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" 3101 + dependencies = [ 3102 + "elliptic-curve", 3103 + ] 3104 + 3105 + [[package]] 3106 + name = "proc-macro-error" 3107 + version = "1.0.4" 3108 + source = "registry+https://github.com/rust-lang/crates.io-index" 3109 + checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" 3110 + dependencies = [ 3111 + "proc-macro-error-attr", 3112 + "proc-macro2", 3113 + "quote", 3114 + "syn 1.0.109", 3115 + "version_check", 3116 + ] 3117 + 3118 + [[package]] 3119 + name = "proc-macro-error-attr" 3120 + version = "1.0.4" 3121 + source = "registry+https://github.com/rust-lang/crates.io-index" 3122 + checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" 3123 + dependencies = [ 3124 + "proc-macro2", 3125 + "quote", 3126 + "version_check", 3127 + ] 3128 + 3129 + [[package]] 3130 + name = "proc-macro2" 3131 + version = "1.0.103" 3132 + source = "registry+https://github.com/rust-lang/crates.io-index" 3133 + checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" 3134 + dependencies = [ 3135 + "unicode-ident", 3136 + ] 3137 + 3138 + [[package]] 3139 + name = "proc-macro2-diagnostics" 3140 + version = "0.10.1" 3141 + source = "registry+https://github.com/rust-lang/crates.io-index" 3142 + checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" 3143 + dependencies = [ 3144 + "proc-macro2", 3145 + "quote", 3146 + "syn 2.0.111", 3147 + "version_check", 3148 + "yansi", 3149 + ] 3150 + 3151 + [[package]] 3152 + name = "pulldown-cmark" 3153 + version = "0.9.6" 3154 + source = "registry+https://github.com/rust-lang/crates.io-index" 3155 + checksum = "57206b407293d2bcd3af849ce869d52068623f19e1b5ff8e8778e3309439682b" 3156 + dependencies = [ 3157 + "bitflags", 3158 + "memchr", 3159 + "unicase", 3160 + ] 3161 + 3162 + [[package]] 3163 + name = "quick-error" 3164 + version = "1.2.3" 3165 + source = "registry+https://github.com/rust-lang/crates.io-index" 3166 + checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" 3167 + 3168 + [[package]] 3169 + name = "quinn" 3170 + version = "0.11.9" 3171 + source = "registry+https://github.com/rust-lang/crates.io-index" 3172 + checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" 3173 + dependencies = [ 3174 + "bytes", 3175 + "cfg_aliases", 3176 + "pin-project-lite", 3177 + "quinn-proto", 3178 + "quinn-udp", 3179 + "rustc-hash", 3180 + "rustls", 3181 + "socket2 0.6.1", 3182 + "thiserror 2.0.17", 3183 + "tokio", 3184 + "tracing", 3185 + "web-time", 3186 + ] 3187 + 3188 + [[package]] 3189 + name = "quinn-proto" 3190 + version = "0.11.13" 3191 + source = "registry+https://github.com/rust-lang/crates.io-index" 3192 + checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31" 3193 + dependencies = [ 3194 + "bytes", 3195 + "getrandom 0.3.4", 3196 + "lru-slab", 3197 + "rand 0.9.2", 3198 + "ring", 3199 + "rustc-hash", 3200 + "rustls", 3201 + "rustls-pki-types", 3202 + "slab", 3203 + "thiserror 2.0.17", 3204 + "tinyvec", 3205 + "tracing", 3206 + "web-time", 3207 + ] 3208 + 3209 + [[package]] 3210 + name = "quinn-udp" 3211 + version = "0.5.14" 3212 + source = "registry+https://github.com/rust-lang/crates.io-index" 3213 + checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" 3214 + dependencies = [ 3215 + "cfg_aliases", 3216 + "libc", 3217 + "once_cell", 3218 + "socket2 0.6.1", 3219 + "tracing", 3220 + "windows-sys 0.60.2", 3221 + ] 3222 + 3223 + [[package]] 3224 + name = "quote" 3225 + version = "1.0.42" 3226 + source = "registry+https://github.com/rust-lang/crates.io-index" 3227 + checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" 3228 + dependencies = [ 3229 + "proc-macro2", 3230 + ] 3231 + 3232 + [[package]] 3233 + name = "r-efi" 3234 + version = "5.3.0" 3235 + source = "registry+https://github.com/rust-lang/crates.io-index" 3236 + checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" 3237 + 3238 + [[package]] 3239 + name = "rand" 3240 + version = "0.8.5" 3241 + source = "registry+https://github.com/rust-lang/crates.io-index" 3242 + checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" 3243 + dependencies = [ 3244 + "libc", 3245 + "rand_chacha 0.3.1", 3246 + "rand_core 0.6.4", 3247 + ] 3248 + 3249 + [[package]] 3250 + name = "rand" 3251 + version = "0.9.2" 3252 + source = "registry+https://github.com/rust-lang/crates.io-index" 3253 + checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" 3254 + dependencies = [ 3255 + "rand_chacha 0.9.0", 3256 + "rand_core 0.9.3", 3257 + ] 3258 + 3259 + [[package]] 3260 + name = "rand_chacha" 3261 + version = "0.3.1" 3262 + source = "registry+https://github.com/rust-lang/crates.io-index" 3263 + checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" 3264 + dependencies = [ 3265 + "ppv-lite86", 3266 + "rand_core 0.6.4", 3267 + ] 3268 + 3269 + [[package]] 3270 + name = "rand_chacha" 3271 + version = "0.9.0" 3272 + source = "registry+https://github.com/rust-lang/crates.io-index" 3273 + checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" 3274 + dependencies = [ 3275 + "ppv-lite86", 3276 + "rand_core 0.9.3", 3277 + ] 3278 + 3279 + [[package]] 3280 + name = "rand_core" 3281 + version = "0.6.4" 3282 + source = "registry+https://github.com/rust-lang/crates.io-index" 3283 + checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" 3284 + dependencies = [ 3285 + "getrandom 0.2.16", 3286 + ] 3287 + 3288 + [[package]] 3289 + name = "rand_core" 3290 + version = "0.9.3" 3291 + source = "registry+https://github.com/rust-lang/crates.io-index" 3292 + checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" 3293 + dependencies = [ 3294 + "getrandom 0.3.4", 3295 + ] 3296 + 3297 + [[package]] 3298 + name = "range-traits" 3299 + version = "0.3.2" 3300 + source = "registry+https://github.com/rust-lang/crates.io-index" 3301 + checksum = "d20581732dd76fa913c7dff1a2412b714afe3573e94d41c34719de73337cc8ab" 3302 + 3303 + [[package]] 3304 + name = "redox_syscall" 3305 + version = "0.5.18" 3306 + source = "registry+https://github.com/rust-lang/crates.io-index" 3307 + checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" 3308 + dependencies = [ 3309 + "bitflags", 3310 + ] 3311 + 3312 + [[package]] 3313 + name = "ref-cast" 3314 + version = "1.0.25" 3315 + source = "registry+https://github.com/rust-lang/crates.io-index" 3316 + checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d" 3317 + dependencies = [ 3318 + "ref-cast-impl", 3319 + ] 3320 + 3321 + [[package]] 3322 + name = "ref-cast-impl" 3323 + version = "1.0.25" 3324 + source = "registry+https://github.com/rust-lang/crates.io-index" 3325 + checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" 3326 + dependencies = [ 3327 + "proc-macro2", 3328 + "quote", 3329 + "syn 2.0.111", 3330 + ] 3331 + 3332 + [[package]] 3333 + name = "regex" 3334 + version = "1.12.2" 3335 + source = "registry+https://github.com/rust-lang/crates.io-index" 3336 + checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" 3337 + dependencies = [ 3338 + "aho-corasick", 3339 + "memchr", 3340 + "regex-automata", 3341 + "regex-syntax", 3342 + ] 3343 + 3344 + [[package]] 3345 + name = "regex-automata" 3346 + version = "0.4.13" 3347 + source = "registry+https://github.com/rust-lang/crates.io-index" 3348 + checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" 3349 + dependencies = [ 3350 + "aho-corasick", 3351 + "memchr", 3352 + "regex-syntax", 3353 + ] 3354 + 3355 + [[package]] 3356 + name = "regex-lite" 3357 + version = "0.1.8" 3358 + source = "registry+https://github.com/rust-lang/crates.io-index" 3359 + checksum = "8d942b98df5e658f56f20d592c7f868833fe38115e65c33003d8cd224b0155da" 3360 + 3361 + [[package]] 3362 + name = "regex-syntax" 3363 + version = "0.8.8" 3364 + source = "registry+https://github.com/rust-lang/crates.io-index" 3365 + checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" 3366 + 3367 + [[package]] 3368 + name = "reqwest" 3369 + version = "0.12.24" 3370 + source = "registry+https://github.com/rust-lang/crates.io-index" 3371 + checksum = "9d0946410b9f7b082a427e4ef5c8ff541a88b357bc6c637c40db3a68ac70a36f" 3372 + dependencies = [ 3373 + "async-compression", 3374 + "base64 0.22.1", 3375 + "bytes", 3376 + "encoding_rs", 3377 + "futures-core", 3378 + "futures-util", 3379 + "h2", 3380 + "http", 3381 + "http-body", 3382 + "http-body-util", 3383 + "hyper", 3384 + "hyper-rustls", 3385 + "hyper-tls", 3386 + "hyper-util", 3387 + "js-sys", 3388 + "log", 3389 + "mime", 3390 + "native-tls", 3391 + "percent-encoding", 3392 + "pin-project-lite", 3393 + "quinn", 3394 + "rustls", 3395 + "rustls-pki-types", 3396 + "serde", 3397 + "serde_json", 3398 + "serde_urlencoded", 3399 + "sync_wrapper", 3400 + "tokio", 3401 + "tokio-native-tls", 3402 + "tokio-rustls", 3403 + "tokio-util", 3404 + "tower", 3405 + "tower-http", 3406 + "tower-service", 3407 + "url", 3408 + "wasm-bindgen", 3409 + "wasm-bindgen-futures", 3410 + "wasm-streams", 3411 + "web-sys", 3412 + "webpki-roots 1.0.4", 3413 + ] 3414 + 3415 + [[package]] 3416 + name = "resolv-conf" 3417 + version = "0.7.6" 3418 + source = "registry+https://github.com/rust-lang/crates.io-index" 3419 + checksum = "1e061d1b48cb8d38042de4ae0a7a6401009d6143dc80d2e2d6f31f0bdd6470c7" 3420 + 3421 + [[package]] 3422 + name = "rfc6979" 3423 + version = "0.4.0" 3424 + source = "registry+https://github.com/rust-lang/crates.io-index" 3425 + checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" 3426 + dependencies = [ 3427 + "hmac", 3428 + "subtle", 3429 + ] 3430 + 3431 + [[package]] 3432 + name = "ring" 3433 + version = "0.17.14" 3434 + source = "registry+https://github.com/rust-lang/crates.io-index" 3435 + checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" 3436 + dependencies = [ 3437 + "cc", 3438 + "cfg-if", 3439 + "getrandom 0.2.16", 3440 + "libc", 3441 + "untrusted", 3442 + "windows-sys 0.52.0", 3443 + ] 3444 + 3445 + [[package]] 3446 + name = "rouille" 3447 + version = "3.6.2" 3448 + source = "registry+https://github.com/rust-lang/crates.io-index" 3449 + checksum = "3716fbf57fc1084d7a706adf4e445298d123e4a44294c4e8213caf1b85fcc921" 3450 + dependencies = [ 3451 + "base64 0.13.1", 3452 + "brotli", 3453 + "chrono", 3454 + "deflate", 3455 + "filetime", 3456 + "multipart", 3457 + "percent-encoding", 3458 + "rand 0.8.5", 3459 + "serde", 3460 + "serde_derive", 3461 + "serde_json", 3462 + "sha1_smol", 3463 + "threadpool", 3464 + "time", 3465 + "tiny_http", 3466 + "url", 3467 + ] 3468 + 3469 + [[package]] 3470 + name = "rsa" 3471 + version = "0.9.9" 3472 + source = "registry+https://github.com/rust-lang/crates.io-index" 3473 + checksum = "40a0376c50d0358279d9d643e4bf7b7be212f1f4ff1da9070a7b54d22ef75c88" 3474 + dependencies = [ 3475 + "const-oid", 3476 + "digest", 3477 + "num-bigint-dig", 3478 + "num-integer", 3479 + "num-traits", 3480 + "pkcs1", 3481 + "pkcs8", 3482 + "rand_core 0.6.4", 3483 + "signature", 3484 + "spki", 3485 + "subtle", 3486 + "zeroize", 3487 + ] 3488 + 3489 + [[package]] 3490 + name = "rustc-hash" 3491 + version = "2.1.1" 3492 + source = "registry+https://github.com/rust-lang/crates.io-index" 3493 + checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" 3494 + 3495 + [[package]] 3496 + name = "rustc_version" 3497 + version = "0.4.1" 3498 + source = "registry+https://github.com/rust-lang/crates.io-index" 3499 + checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" 3500 + dependencies = [ 3501 + "semver", 3502 + ] 3503 + 3504 + [[package]] 3505 + name = "rustix" 3506 + version = "1.1.2" 3507 + source = "registry+https://github.com/rust-lang/crates.io-index" 3508 + checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" 3509 + dependencies = [ 3510 + "bitflags", 3511 + "errno", 3512 + "libc", 3513 + "linux-raw-sys", 3514 + "windows-sys 0.61.2", 3515 + ] 3516 + 3517 + [[package]] 3518 + name = "rustls" 3519 + version = "0.23.35" 3520 + source = "registry+https://github.com/rust-lang/crates.io-index" 3521 + checksum = "533f54bc6a7d4f647e46ad909549eda97bf5afc1585190ef692b4286b198bd8f" 3522 + dependencies = [ 3523 + "once_cell", 3524 + "ring", 3525 + "rustls-pki-types", 3526 + "rustls-webpki", 3527 + "subtle", 3528 + "zeroize", 3529 + ] 3530 + 3531 + [[package]] 3532 + name = "rustls-pki-types" 3533 + version = "1.13.1" 3534 + source = "registry+https://github.com/rust-lang/crates.io-index" 3535 + checksum = "708c0f9d5f54ba0272468c1d306a52c495b31fa155e91bc25371e6df7996908c" 3536 + dependencies = [ 3537 + "web-time", 3538 + "zeroize", 3539 + ] 3540 + 3541 + [[package]] 3542 + name = "rustls-webpki" 3543 + version = "0.103.8" 3544 + source = "registry+https://github.com/rust-lang/crates.io-index" 3545 + checksum = "2ffdfa2f5286e2247234e03f680868ac2815974dc39e00ea15adc445d0aafe52" 3546 + dependencies = [ 3547 + "ring", 3548 + "rustls-pki-types", 3549 + "untrusted", 3550 + ] 3551 + 3552 + [[package]] 3553 + name = "rustversion" 3554 + version = "1.0.22" 3555 + source = "registry+https://github.com/rust-lang/crates.io-index" 3556 + checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" 3557 + 3558 + [[package]] 3559 + name = "ryu" 3560 + version = "1.0.20" 3561 + source = "registry+https://github.com/rust-lang/crates.io-index" 3562 + checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" 3563 + 3564 + [[package]] 3565 + name = "safemem" 3566 + version = "0.3.3" 3567 + source = "registry+https://github.com/rust-lang/crates.io-index" 3568 + checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072" 3569 + 3570 + [[package]] 3571 + name = "same-file" 3572 + version = "1.0.6" 3573 + source = "registry+https://github.com/rust-lang/crates.io-index" 3574 + checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" 3575 + dependencies = [ 3576 + "winapi-util", 3577 + ] 3578 + 3579 + [[package]] 3580 + name = "schannel" 3581 + version = "0.1.28" 3582 + source = "registry+https://github.com/rust-lang/crates.io-index" 3583 + checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1" 3584 + dependencies = [ 3585 + "windows-sys 0.61.2", 3586 + ] 3587 + 3588 + [[package]] 3589 + name = "schemars" 3590 + version = "0.9.0" 3591 + source = "registry+https://github.com/rust-lang/crates.io-index" 3592 + checksum = "4cd191f9397d57d581cddd31014772520aa448f65ef991055d7f61582c65165f" 3593 + dependencies = [ 3594 + "dyn-clone", 3595 + "ref-cast", 3596 + "serde", 3597 + "serde_json", 3598 + ] 3599 + 3600 + [[package]] 3601 + name = "schemars" 3602 + version = "1.1.0" 3603 + source = "registry+https://github.com/rust-lang/crates.io-index" 3604 + checksum = "9558e172d4e8533736ba97870c4b2cd63f84b382a3d6eb063da41b91cce17289" 3605 + dependencies = [ 3606 + "dyn-clone", 3607 + "ref-cast", 3608 + "serde", 3609 + "serde_json", 3610 + ] 3611 + 3612 + [[package]] 3613 + name = "scoped-tls" 3614 + version = "1.0.1" 3615 + source = "registry+https://github.com/rust-lang/crates.io-index" 3616 + checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" 3617 + 3618 + [[package]] 3619 + name = "scopeguard" 3620 + version = "1.2.0" 3621 + source = "registry+https://github.com/rust-lang/crates.io-index" 3622 + checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" 3623 + 3624 + [[package]] 3625 + name = "sec1" 3626 + version = "0.7.3" 3627 + source = "registry+https://github.com/rust-lang/crates.io-index" 3628 + checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" 3629 + dependencies = [ 3630 + "base16ct", 3631 + "der", 3632 + "generic-array", 3633 + "pkcs8", 3634 + "subtle", 3635 + "zeroize", 3636 + ] 3637 + 3638 + [[package]] 3639 + name = "security-framework" 3640 + version = "2.11.1" 3641 + source = "registry+https://github.com/rust-lang/crates.io-index" 3642 + checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" 3643 + dependencies = [ 3644 + "bitflags", 3645 + "core-foundation 0.9.4", 3646 + "core-foundation-sys", 3647 + "libc", 3648 + "security-framework-sys", 3649 + ] 3650 + 3651 + [[package]] 3652 + name = "security-framework-sys" 3653 + version = "2.15.0" 3654 + source = "registry+https://github.com/rust-lang/crates.io-index" 3655 + checksum = "cc1f0cbffaac4852523ce30d8bd3c5cdc873501d96ff467ca09b6767bb8cd5c0" 3656 + dependencies = [ 3657 + "core-foundation-sys", 3658 + "libc", 3659 + ] 3660 + 3661 + [[package]] 3662 + name = "semver" 3663 + version = "1.0.27" 3664 + source = "registry+https://github.com/rust-lang/crates.io-index" 3665 + checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" 3666 + dependencies = [ 3667 + "serde", 3668 + "serde_core", 3669 + ] 3670 + 3671 + [[package]] 3672 + name = "send_wrapper" 3673 + version = "0.6.0" 3674 + source = "registry+https://github.com/rust-lang/crates.io-index" 3675 + checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" 3676 + 3677 + [[package]] 3678 + name = "serde" 3679 + version = "1.0.228" 3680 + source = "registry+https://github.com/rust-lang/crates.io-index" 3681 + checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" 3682 + dependencies = [ 3683 + "serde_core", 3684 + "serde_derive", 3685 + ] 3686 + 3687 + [[package]] 3688 + name = "serde_bytes" 3689 + version = "0.11.19" 3690 + source = "registry+https://github.com/rust-lang/crates.io-index" 3691 + checksum = "a5d440709e79d88e51ac01c4b72fc6cb7314017bb7da9eeff678aa94c10e3ea8" 3692 + dependencies = [ 3693 + "serde", 3694 + "serde_core", 3695 + ] 3696 + 3697 + [[package]] 3698 + name = "serde_core" 3699 + version = "1.0.228" 3700 + source = "registry+https://github.com/rust-lang/crates.io-index" 3701 + checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" 3702 + dependencies = [ 3703 + "serde_derive", 3704 + ] 3705 + 3706 + [[package]] 3707 + name = "serde_derive" 3708 + version = "1.0.228" 3709 + source = "registry+https://github.com/rust-lang/crates.io-index" 3710 + checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" 3711 + dependencies = [ 3712 + "proc-macro2", 3713 + "quote", 3714 + "syn 2.0.111", 3715 + ] 3716 + 3717 + [[package]] 3718 + name = "serde_html_form" 3719 + version = "0.2.8" 3720 + source = "registry+https://github.com/rust-lang/crates.io-index" 3721 + checksum = "b2f2d7ff8a2140333718bb329f5c40fc5f0865b84c426183ce14c97d2ab8154f" 3722 + dependencies = [ 3723 + "form_urlencoded", 3724 + "indexmap 2.12.1", 3725 + "itoa", 3726 + "ryu", 3727 + "serde_core", 3728 + ] 3729 + 3730 + [[package]] 3731 + name = "serde_ipld_dagcbor" 3732 + version = "0.6.4" 3733 + source = "registry+https://github.com/rust-lang/crates.io-index" 3734 + checksum = "46182f4f08349a02b45c998ba3215d3f9de826246ba02bb9dddfe9a2a2100778" 3735 + dependencies = [ 3736 + "cbor4ii", 3737 + "ipld-core", 3738 + "scopeguard", 3739 + "serde", 3740 + ] 3741 + 3742 + [[package]] 3743 + name = "serde_json" 3744 + version = "1.0.145" 3745 + source = "registry+https://github.com/rust-lang/crates.io-index" 3746 + checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" 3747 + dependencies = [ 3748 + "itoa", 3749 + "memchr", 3750 + "ryu", 3751 + "serde", 3752 + "serde_core", 3753 + ] 3754 + 3755 + [[package]] 3756 + name = "serde_path_to_error" 3757 + version = "0.1.20" 3758 + source = "registry+https://github.com/rust-lang/crates.io-index" 3759 + checksum = "10a9ff822e371bb5403e391ecd83e182e0e77ba7f6fe0160b795797109d1b457" 3760 + dependencies = [ 3761 + "itoa", 3762 + "serde", 3763 + "serde_core", 3764 + ] 3765 + 3766 + [[package]] 3767 + name = "serde_repr" 3768 + version = "0.1.20" 3769 + source = "registry+https://github.com/rust-lang/crates.io-index" 3770 + checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" 3771 + dependencies = [ 3772 + "proc-macro2", 3773 + "quote", 3774 + "syn 2.0.111", 3775 + ] 3776 + 3777 + [[package]] 3778 + name = "serde_urlencoded" 3779 + version = "0.7.1" 3780 + source = "registry+https://github.com/rust-lang/crates.io-index" 3781 + checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" 3782 + dependencies = [ 3783 + "form_urlencoded", 3784 + "itoa", 3785 + "ryu", 3786 + "serde", 3787 + ] 3788 + 3789 + [[package]] 3790 + name = "serde_with" 3791 + version = "3.16.1" 3792 + source = "registry+https://github.com/rust-lang/crates.io-index" 3793 + checksum = "4fa237f2807440d238e0364a218270b98f767a00d3dada77b1c53ae88940e2e7" 3794 + dependencies = [ 3795 + "base64 0.22.1", 3796 + "chrono", 3797 + "hex", 3798 + "indexmap 1.9.3", 3799 + "indexmap 2.12.1", 3800 + "schemars 0.9.0", 3801 + "schemars 1.1.0", 3802 + "serde_core", 3803 + "serde_json", 3804 + "serde_with_macros", 3805 + "time", 3806 + ] 3807 + 3808 + [[package]] 3809 + name = "serde_with_macros" 3810 + version = "3.16.1" 3811 + source = "registry+https://github.com/rust-lang/crates.io-index" 3812 + checksum = "52a8e3ca0ca629121f70ab50f95249e5a6f925cc0f6ffe8256c45b728875706c" 3813 + dependencies = [ 3814 + "darling", 3815 + "proc-macro2", 3816 + "quote", 3817 + "syn 2.0.111", 3818 + ] 3819 + 3820 + [[package]] 3821 + name = "sha1" 3822 + version = "0.10.6" 3823 + source = "registry+https://github.com/rust-lang/crates.io-index" 3824 + checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" 3825 + dependencies = [ 3826 + "cfg-if", 3827 + "cpufeatures", 3828 + "digest", 3829 + ] 3830 + 3831 + [[package]] 3832 + name = "sha1_smol" 3833 + version = "1.0.1" 3834 + source = "registry+https://github.com/rust-lang/crates.io-index" 3835 + checksum = "bbfa15b3dddfee50a0fff136974b3e1bde555604ba463834a7eb7deb6417705d" 3836 + 3837 + [[package]] 3838 + name = "sha2" 3839 + version = "0.10.9" 3840 + source = "registry+https://github.com/rust-lang/crates.io-index" 3841 + checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" 3842 + dependencies = [ 3843 + "cfg-if", 3844 + "cpufeatures", 3845 + "digest", 3846 + ] 3847 + 3848 + [[package]] 3849 + name = "sharded-slab" 3850 + version = "0.1.7" 3851 + source = "registry+https://github.com/rust-lang/crates.io-index" 3852 + checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" 3853 + dependencies = [ 3854 + "lazy_static", 3855 + ] 3856 + 3857 + [[package]] 3858 + name = "shlex" 3859 + version = "1.3.0" 3860 + source = "registry+https://github.com/rust-lang/crates.io-index" 3861 + checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" 3862 + 3863 + [[package]] 3864 + name = "signature" 3865 + version = "2.2.0" 3866 + source = "registry+https://github.com/rust-lang/crates.io-index" 3867 + checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" 3868 + dependencies = [ 3869 + "digest", 3870 + "rand_core 0.6.4", 3871 + ] 3872 + 3873 + [[package]] 3874 + name = "simd-adler32" 3875 + version = "0.3.7" 3876 + source = "registry+https://github.com/rust-lang/crates.io-index" 3877 + checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" 3878 + 3879 + [[package]] 3880 + name = "simple_asn1" 3881 + version = "0.6.3" 3882 + source = "registry+https://github.com/rust-lang/crates.io-index" 3883 + checksum = "297f631f50729c8c99b84667867963997ec0b50f32b2a7dbcab828ef0541e8bb" 3884 + dependencies = [ 3885 + "num-bigint", 3886 + "num-traits", 3887 + "thiserror 2.0.17", 3888 + "time", 3889 + ] 3890 + 3891 + [[package]] 3892 + name = "siphasher" 3893 + version = "1.0.1" 3894 + source = "registry+https://github.com/rust-lang/crates.io-index" 3895 + checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" 3896 + 3897 + [[package]] 3898 + name = "skeptic" 3899 + version = "0.13.7" 3900 + source = "registry+https://github.com/rust-lang/crates.io-index" 3901 + checksum = "16d23b015676c90a0f01c197bfdc786c20342c73a0afdda9025adb0bc42940a8" 3902 + dependencies = [ 3903 + "bytecount", 3904 + "cargo_metadata", 3905 + "error-chain", 3906 + "glob", 3907 + "pulldown-cmark", 3908 + "tempfile", 3909 + "walkdir", 3910 + ] 3911 + 3912 + [[package]] 3913 + name = "slab" 3914 + version = "0.4.11" 3915 + source = "registry+https://github.com/rust-lang/crates.io-index" 3916 + checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" 3917 + 3918 + [[package]] 3919 + name = "smallvec" 3920 + version = "1.15.1" 3921 + source = "registry+https://github.com/rust-lang/crates.io-index" 3922 + checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" 3923 + dependencies = [ 3924 + "serde", 3925 + ] 3926 + 3927 + [[package]] 3928 + name = "smol_str" 3929 + version = "0.3.4" 3930 + source = "registry+https://github.com/rust-lang/crates.io-index" 3931 + checksum = "3498b0a27f93ef1402f20eefacfaa1691272ac4eca1cdc8c596cb0a245d6cbf5" 3932 + dependencies = [ 3933 + "borsh", 3934 + "serde_core", 3935 + ] 3936 + 3937 + [[package]] 3938 + name = "socket2" 3939 + version = "0.5.10" 3940 + source = "registry+https://github.com/rust-lang/crates.io-index" 3941 + checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" 3942 + dependencies = [ 3943 + "libc", 3944 + "windows-sys 0.52.0", 3945 + ] 3946 + 3947 + [[package]] 3948 + name = "socket2" 3949 + version = "0.6.1" 3950 + source = "registry+https://github.com/rust-lang/crates.io-index" 3951 + checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881" 3952 + dependencies = [ 3953 + "libc", 3954 + "windows-sys 0.60.2", 3955 + ] 3956 + 3957 + [[package]] 3958 + name = "spin" 3959 + version = "0.9.8" 3960 + source = "registry+https://github.com/rust-lang/crates.io-index" 3961 + checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" 3962 + dependencies = [ 3963 + "lock_api", 3964 + ] 3965 + 3966 + [[package]] 3967 + name = "spin" 3968 + version = "0.10.0" 3969 + source = "registry+https://github.com/rust-lang/crates.io-index" 3970 + checksum = "d5fe4ccb98d9c292d56fec89a5e07da7fc4cf0dc11e156b41793132775d3e591" 3971 + 3972 + [[package]] 3973 + name = "spki" 3974 + version = "0.7.3" 3975 + source = "registry+https://github.com/rust-lang/crates.io-index" 3976 + checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" 3977 + dependencies = [ 3978 + "base64ct", 3979 + "der", 3980 + ] 3981 + 3982 + [[package]] 3983 + name = "sqlx" 3984 + version = "0.8.6" 3985 + source = "registry+https://github.com/rust-lang/crates.io-index" 3986 + checksum = "1fefb893899429669dcdd979aff487bd78f4064e5e7907e4269081e0ef7d97dc" 3987 + dependencies = [ 3988 + "sqlx-core", 3989 + "sqlx-macros", 3990 + "sqlx-mysql", 3991 + "sqlx-postgres", 3992 + "sqlx-sqlite", 3993 + ] 3994 + 3995 + [[package]] 3996 + name = "sqlx-core" 3997 + version = "0.8.6" 3998 + source = "registry+https://github.com/rust-lang/crates.io-index" 3999 + checksum = "ee6798b1838b6a0f69c007c133b8df5866302197e404e8b6ee8ed3e3a5e68dc6" 4000 + dependencies = [ 4001 + "base64 0.22.1", 4002 + "bytes", 4003 + "chrono", 4004 + "crc", 4005 + "crossbeam-queue", 4006 + "either", 4007 + "event-listener", 4008 + "futures-core", 4009 + "futures-intrusive", 4010 + "futures-io", 4011 + "futures-util", 4012 + "hashbrown 0.15.5", 4013 + "hashlink", 4014 + "indexmap 2.12.1", 4015 + "log", 4016 + "memchr", 4017 + "once_cell", 4018 + "percent-encoding", 4019 + "rustls", 4020 + "serde", 4021 + "serde_json", 4022 + "sha2", 4023 + "smallvec", 4024 + "thiserror 2.0.17", 4025 + "tokio", 4026 + "tokio-stream", 4027 + "tracing", 4028 + "url", 4029 + "uuid", 4030 + "webpki-roots 0.26.11", 4031 + ] 4032 + 4033 + [[package]] 4034 + name = "sqlx-macros" 4035 + version = "0.8.6" 4036 + source = "registry+https://github.com/rust-lang/crates.io-index" 4037 + checksum = "a2d452988ccaacfbf5e0bdbc348fb91d7c8af5bee192173ac3636b5fb6e6715d" 4038 + dependencies = [ 4039 + "proc-macro2", 4040 + "quote", 4041 + "sqlx-core", 4042 + "sqlx-macros-core", 4043 + "syn 2.0.111", 4044 + ] 4045 + 4046 + [[package]] 4047 + name = "sqlx-macros-core" 4048 + version = "0.8.6" 4049 + source = "registry+https://github.com/rust-lang/crates.io-index" 4050 + checksum = "19a9c1841124ac5a61741f96e1d9e2ec77424bf323962dd894bdb93f37d5219b" 4051 + dependencies = [ 4052 + "dotenvy", 4053 + "either", 4054 + "heck 0.5.0", 4055 + "hex", 4056 + "once_cell", 4057 + "proc-macro2", 4058 + "quote", 4059 + "serde", 4060 + "serde_json", 4061 + "sha2", 4062 + "sqlx-core", 4063 + "sqlx-mysql", 4064 + "sqlx-postgres", 4065 + "sqlx-sqlite", 4066 + "syn 2.0.111", 4067 + "tokio", 4068 + "url", 4069 + ] 4070 + 4071 + [[package]] 4072 + name = "sqlx-mysql" 4073 + version = "0.8.6" 4074 + source = "registry+https://github.com/rust-lang/crates.io-index" 4075 + checksum = "aa003f0038df784eb8fecbbac13affe3da23b45194bd57dba231c8f48199c526" 4076 + dependencies = [ 4077 + "atoi", 4078 + "base64 0.22.1", 4079 + "bitflags", 4080 + "byteorder", 4081 + "bytes", 4082 + "chrono", 4083 + "crc", 4084 + "digest", 4085 + "dotenvy", 4086 + "either", 4087 + "futures-channel", 4088 + "futures-core", 4089 + "futures-io", 4090 + "futures-util", 4091 + "generic-array", 4092 + "hex", 4093 + "hkdf", 4094 + "hmac", 4095 + "itoa", 4096 + "log", 4097 + "md-5", 4098 + "memchr", 4099 + "once_cell", 4100 + "percent-encoding", 4101 + "rand 0.8.5", 4102 + "rsa", 4103 + "serde", 4104 + "sha1", 4105 + "sha2", 4106 + "smallvec", 4107 + "sqlx-core", 4108 + "stringprep", 4109 + "thiserror 2.0.17", 4110 + "tracing", 4111 + "uuid", 4112 + "whoami", 4113 + ] 4114 + 4115 + [[package]] 4116 + name = "sqlx-postgres" 4117 + version = "0.8.6" 4118 + source = "registry+https://github.com/rust-lang/crates.io-index" 4119 + checksum = "db58fcd5a53cf07c184b154801ff91347e4c30d17a3562a635ff028ad5deda46" 4120 + dependencies = [ 4121 + "atoi", 4122 + "base64 0.22.1", 4123 + "bitflags", 4124 + "byteorder", 4125 + "chrono", 4126 + "crc", 4127 + "dotenvy", 4128 + "etcetera", 4129 + "futures-channel", 4130 + "futures-core", 4131 + "futures-util", 4132 + "hex", 4133 + "hkdf", 4134 + "hmac", 4135 + "home", 4136 + "itoa", 4137 + "log", 4138 + "md-5", 4139 + "memchr", 4140 + "once_cell", 4141 + "rand 0.8.5", 4142 + "serde", 4143 + "serde_json", 4144 + "sha2", 4145 + "smallvec", 4146 + "sqlx-core", 4147 + "stringprep", 4148 + "thiserror 2.0.17", 4149 + "tracing", 4150 + "uuid", 4151 + "whoami", 4152 + ] 4153 + 4154 + [[package]] 4155 + name = "sqlx-sqlite" 4156 + version = "0.8.6" 4157 + source = "registry+https://github.com/rust-lang/crates.io-index" 4158 + checksum = "c2d12fe70b2c1b4401038055f90f151b78208de1f9f89a7dbfd41587a10c3eea" 4159 + dependencies = [ 4160 + "atoi", 4161 + "chrono", 4162 + "flume", 4163 + "futures-channel", 4164 + "futures-core", 4165 + "futures-executor", 4166 + "futures-intrusive", 4167 + "futures-util", 4168 + "libsqlite3-sys", 4169 + "log", 4170 + "percent-encoding", 4171 + "serde", 4172 + "serde_urlencoded", 4173 + "sqlx-core", 4174 + "thiserror 2.0.17", 4175 + "tracing", 4176 + "url", 4177 + "uuid", 4178 + ] 4179 + 4180 + [[package]] 4181 + name = "stable_deref_trait" 4182 + version = "1.2.1" 4183 + source = "registry+https://github.com/rust-lang/crates.io-index" 4184 + checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" 4185 + 4186 + [[package]] 4187 + name = "static-regular-grammar" 4188 + version = "2.0.2" 4189 + source = "registry+https://github.com/rust-lang/crates.io-index" 4190 + checksum = "4f4a6c40247579acfbb138c3cd7de3dab113ab4ac6227f1b7de7d626ee667957" 4191 + dependencies = [ 4192 + "abnf", 4193 + "btree-range-map", 4194 + "ciborium", 4195 + "hex_fmt", 4196 + "indoc", 4197 + "proc-macro-error", 4198 + "proc-macro2", 4199 + "quote", 4200 + "serde", 4201 + "sha2", 4202 + "syn 2.0.111", 4203 + "thiserror 1.0.69", 4204 + ] 4205 + 4206 + [[package]] 4207 + name = "static_assertions" 4208 + version = "1.1.0" 4209 + source = "registry+https://github.com/rust-lang/crates.io-index" 4210 + checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" 4211 + 4212 + [[package]] 4213 + name = "string_cache" 4214 + version = "0.8.9" 4215 + source = "registry+https://github.com/rust-lang/crates.io-index" 4216 + checksum = "bf776ba3fa74f83bf4b63c3dcbbf82173db2632ed8452cb2d891d33f459de70f" 4217 + dependencies = [ 4218 + "new_debug_unreachable", 4219 + "parking_lot", 4220 + "phf_shared", 4221 + "precomputed-hash", 4222 + "serde", 4223 + ] 4224 + 4225 + [[package]] 4226 + name = "string_cache_codegen" 4227 + version = "0.5.4" 4228 + source = "registry+https://github.com/rust-lang/crates.io-index" 4229 + checksum = "c711928715f1fe0fe509c53b43e993a9a557babc2d0a3567d0a3006f1ac931a0" 4230 + dependencies = [ 4231 + "phf_generator", 4232 + "phf_shared", 4233 + "proc-macro2", 4234 + "quote", 4235 + ] 4236 + 4237 + [[package]] 4238 + name = "stringprep" 4239 + version = "0.1.5" 4240 + source = "registry+https://github.com/rust-lang/crates.io-index" 4241 + checksum = "7b4df3d392d81bd458a8a621b8bffbd2302a12ffe288a9d931670948749463b1" 4242 + dependencies = [ 4243 + "unicode-bidi", 4244 + "unicode-normalization", 4245 + "unicode-properties", 4246 + ] 4247 + 4248 + [[package]] 4249 + name = "strsim" 4250 + version = "0.11.1" 4251 + source = "registry+https://github.com/rust-lang/crates.io-index" 4252 + checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" 4253 + 4254 + [[package]] 4255 + name = "subtle" 4256 + version = "2.6.1" 4257 + source = "registry+https://github.com/rust-lang/crates.io-index" 4258 + checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" 4259 + 4260 + [[package]] 4261 + name = "syn" 4262 + version = "1.0.109" 4263 + source = "registry+https://github.com/rust-lang/crates.io-index" 4264 + checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" 4265 + dependencies = [ 4266 + "proc-macro2", 4267 + "quote", 4268 + "unicode-ident", 4269 + ] 4270 + 4271 + [[package]] 4272 + name = "syn" 4273 + version = "2.0.111" 4274 + source = "registry+https://github.com/rust-lang/crates.io-index" 4275 + checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87" 4276 + dependencies = [ 4277 + "proc-macro2", 4278 + "quote", 4279 + "unicode-ident", 4280 + ] 4281 + 4282 + [[package]] 4283 + name = "sync_wrapper" 4284 + version = "1.0.2" 4285 + source = "registry+https://github.com/rust-lang/crates.io-index" 4286 + checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" 4287 + dependencies = [ 4288 + "futures-core", 4289 + ] 4290 + 4291 + [[package]] 4292 + name = "synstructure" 4293 + version = "0.13.2" 4294 + source = "registry+https://github.com/rust-lang/crates.io-index" 4295 + checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" 4296 + dependencies = [ 4297 + "proc-macro2", 4298 + "quote", 4299 + "syn 2.0.111", 4300 + ] 4301 + 4302 + [[package]] 4303 + name = "system-configuration" 4304 + version = "0.6.1" 4305 + source = "registry+https://github.com/rust-lang/crates.io-index" 4306 + checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" 4307 + dependencies = [ 4308 + "bitflags", 4309 + "core-foundation 0.9.4", 4310 + "system-configuration-sys", 4311 + ] 4312 + 4313 + [[package]] 4314 + name = "system-configuration-sys" 4315 + version = "0.6.0" 4316 + source = "registry+https://github.com/rust-lang/crates.io-index" 4317 + checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" 4318 + dependencies = [ 4319 + "core-foundation-sys", 4320 + "libc", 4321 + ] 4322 + 4323 + [[package]] 4324 + name = "tagptr" 4325 + version = "0.2.0" 4326 + source = "registry+https://github.com/rust-lang/crates.io-index" 4327 + checksum = "7b2093cf4c8eb1e67749a6762251bc9cd836b6fc171623bd0a9d324d37af2417" 4328 + 4329 + [[package]] 4330 + name = "tempfile" 4331 + version = "3.23.0" 4332 + source = "registry+https://github.com/rust-lang/crates.io-index" 4333 + checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16" 4334 + dependencies = [ 4335 + "fastrand", 4336 + "getrandom 0.3.4", 4337 + "once_cell", 4338 + "rustix", 4339 + "windows-sys 0.61.2", 4340 + ] 4341 + 4342 + [[package]] 4343 + name = "tendril" 4344 + version = "0.4.3" 4345 + source = "registry+https://github.com/rust-lang/crates.io-index" 4346 + checksum = "d24a120c5fc464a3458240ee02c299ebcb9d67b5249c8848b09d639dca8d7bb0" 4347 + dependencies = [ 4348 + "futf", 4349 + "mac", 4350 + "utf-8", 4351 + ] 4352 + 4353 + [[package]] 4354 + name = "thiserror" 4355 + version = "1.0.69" 4356 + source = "registry+https://github.com/rust-lang/crates.io-index" 4357 + checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" 4358 + dependencies = [ 4359 + "thiserror-impl 1.0.69", 4360 + ] 4361 + 4362 + [[package]] 4363 + name = "thiserror" 4364 + version = "2.0.17" 4365 + source = "registry+https://github.com/rust-lang/crates.io-index" 4366 + checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" 4367 + dependencies = [ 4368 + "thiserror-impl 2.0.17", 4369 + ] 4370 + 4371 + [[package]] 4372 + name = "thiserror-impl" 4373 + version = "1.0.69" 4374 + source = "registry+https://github.com/rust-lang/crates.io-index" 4375 + checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" 4376 + dependencies = [ 4377 + "proc-macro2", 4378 + "quote", 4379 + "syn 2.0.111", 4380 + ] 4381 + 4382 + [[package]] 4383 + name = "thiserror-impl" 4384 + version = "2.0.17" 4385 + source = "registry+https://github.com/rust-lang/crates.io-index" 4386 + checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" 4387 + dependencies = [ 4388 + "proc-macro2", 4389 + "quote", 4390 + "syn 2.0.111", 4391 + ] 4392 + 4393 + [[package]] 4394 + name = "thread_local" 4395 + version = "1.1.9" 4396 + source = "registry+https://github.com/rust-lang/crates.io-index" 4397 + checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" 4398 + dependencies = [ 4399 + "cfg-if", 4400 + ] 4401 + 4402 + [[package]] 4403 + name = "threadpool" 4404 + version = "1.8.1" 4405 + source = "registry+https://github.com/rust-lang/crates.io-index" 4406 + checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa" 4407 + dependencies = [ 4408 + "num_cpus", 4409 + ] 4410 + 4411 + [[package]] 4412 + name = "time" 4413 + version = "0.3.44" 4414 + source = "registry+https://github.com/rust-lang/crates.io-index" 4415 + checksum = "91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d" 4416 + dependencies = [ 4417 + "deranged", 4418 + "itoa", 4419 + "libc", 4420 + "num-conv", 4421 + "num_threads", 4422 + "powerfmt", 4423 + "serde", 4424 + "time-core", 4425 + "time-macros", 4426 + ] 4427 + 4428 + [[package]] 4429 + name = "time-core" 4430 + version = "0.1.6" 4431 + source = "registry+https://github.com/rust-lang/crates.io-index" 4432 + checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b" 4433 + 4434 + [[package]] 4435 + name = "time-macros" 4436 + version = "0.2.24" 4437 + source = "registry+https://github.com/rust-lang/crates.io-index" 4438 + checksum = "30cfb0125f12d9c277f35663a0a33f8c30190f4e4574868a330595412d34ebf3" 4439 + dependencies = [ 4440 + "num-conv", 4441 + "time-core", 4442 + ] 4443 + 4444 + [[package]] 4445 + name = "tiny_http" 4446 + version = "0.12.0" 4447 + source = "registry+https://github.com/rust-lang/crates.io-index" 4448 + checksum = "389915df6413a2e74fb181895f933386023c71110878cd0825588928e64cdc82" 4449 + dependencies = [ 4450 + "ascii", 4451 + "chunked_transfer", 4452 + "httpdate", 4453 + "log", 4454 + ] 4455 + 4456 + [[package]] 4457 + name = "tinystr" 4458 + version = "0.8.2" 4459 + source = "registry+https://github.com/rust-lang/crates.io-index" 4460 + checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" 4461 + dependencies = [ 4462 + "displaydoc", 4463 + "zerovec", 4464 + ] 4465 + 4466 + [[package]] 4467 + name = "tinyvec" 4468 + version = "1.10.0" 4469 + source = "registry+https://github.com/rust-lang/crates.io-index" 4470 + checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" 4471 + dependencies = [ 4472 + "tinyvec_macros", 4473 + ] 4474 + 4475 + [[package]] 4476 + name = "tinyvec_macros" 4477 + version = "0.1.1" 4478 + source = "registry+https://github.com/rust-lang/crates.io-index" 4479 + checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" 4480 + 4481 + [[package]] 4482 + name = "tokio" 4483 + version = "1.48.0" 4484 + source = "registry+https://github.com/rust-lang/crates.io-index" 4485 + checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408" 4486 + dependencies = [ 4487 + "bytes", 4488 + "libc", 4489 + "mio", 4490 + "pin-project-lite", 4491 + "socket2 0.6.1", 4492 + "tokio-macros", 4493 + "windows-sys 0.61.2", 4494 + ] 4495 + 4496 + [[package]] 4497 + name = "tokio-macros" 4498 + version = "2.6.0" 4499 + source = "registry+https://github.com/rust-lang/crates.io-index" 4500 + checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" 4501 + dependencies = [ 4502 + "proc-macro2", 4503 + "quote", 4504 + "syn 2.0.111", 4505 + ] 4506 + 4507 + [[package]] 4508 + name = "tokio-native-tls" 4509 + version = "0.3.1" 4510 + source = "registry+https://github.com/rust-lang/crates.io-index" 4511 + checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" 4512 + dependencies = [ 4513 + "native-tls", 4514 + "tokio", 4515 + ] 4516 + 4517 + [[package]] 4518 + name = "tokio-rustls" 4519 + version = "0.26.4" 4520 + source = "registry+https://github.com/rust-lang/crates.io-index" 4521 + checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" 4522 + dependencies = [ 4523 + "rustls", 4524 + "tokio", 4525 + ] 4526 + 4527 + [[package]] 4528 + name = "tokio-stream" 4529 + version = "0.1.17" 4530 + source = "registry+https://github.com/rust-lang/crates.io-index" 4531 + checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047" 4532 + dependencies = [ 4533 + "futures-core", 4534 + "pin-project-lite", 4535 + "tokio", 4536 + ] 4537 + 4538 + [[package]] 4539 + name = "tokio-util" 4540 + version = "0.7.17" 4541 + source = "registry+https://github.com/rust-lang/crates.io-index" 4542 + checksum = "2efa149fe76073d6e8fd97ef4f4eca7b67f599660115591483572e406e165594" 4543 + dependencies = [ 4544 + "bytes", 4545 + "futures-core", 4546 + "futures-sink", 4547 + "futures-util", 4548 + "pin-project-lite", 4549 + "tokio", 4550 + ] 4551 + 4552 + [[package]] 4553 + name = "tower" 4554 + version = "0.5.2" 4555 + source = "registry+https://github.com/rust-lang/crates.io-index" 4556 + checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" 4557 + dependencies = [ 4558 + "futures-core", 4559 + "futures-util", 4560 + "pin-project-lite", 4561 + "sync_wrapper", 4562 + "tokio", 4563 + "tower-layer", 4564 + "tower-service", 4565 + "tracing", 4566 + ] 4567 + 4568 + [[package]] 4569 + name = "tower-http" 4570 + version = "0.6.7" 4571 + source = "registry+https://github.com/rust-lang/crates.io-index" 4572 + checksum = "9cf146f99d442e8e68e585f5d798ccd3cad9a7835b917e09728880a862706456" 4573 + dependencies = [ 4574 + "bitflags", 4575 + "bytes", 4576 + "futures-util", 4577 + "http", 4578 + "http-body", 4579 + "iri-string", 4580 + "pin-project-lite", 4581 + "tower", 4582 + "tower-layer", 4583 + "tower-service", 4584 + "tracing", 4585 + ] 4586 + 4587 + [[package]] 4588 + name = "tower-layer" 4589 + version = "0.3.3" 4590 + source = "registry+https://github.com/rust-lang/crates.io-index" 4591 + checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" 4592 + 4593 + [[package]] 4594 + name = "tower-service" 4595 + version = "0.3.3" 4596 + source = "registry+https://github.com/rust-lang/crates.io-index" 4597 + checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" 4598 + 4599 + [[package]] 4600 + name = "tracing" 4601 + version = "0.1.43" 4602 + source = "registry+https://github.com/rust-lang/crates.io-index" 4603 + checksum = "2d15d90a0b5c19378952d479dc858407149d7bb45a14de0142f6c534b16fc647" 4604 + dependencies = [ 4605 + "log", 4606 + "pin-project-lite", 4607 + "tracing-attributes", 4608 + "tracing-core", 4609 + ] 4610 + 4611 + [[package]] 4612 + name = "tracing-attributes" 4613 + version = "0.1.31" 4614 + source = "registry+https://github.com/rust-lang/crates.io-index" 4615 + checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" 4616 + dependencies = [ 4617 + "proc-macro2", 4618 + "quote", 4619 + "syn 2.0.111", 4620 + ] 4621 + 4622 + [[package]] 4623 + name = "tracing-core" 4624 + version = "0.1.35" 4625 + source = "registry+https://github.com/rust-lang/crates.io-index" 4626 + checksum = "7a04e24fab5c89c6a36eb8558c9656f30d81de51dfa4d3b45f26b21d61fa0a6c" 4627 + dependencies = [ 4628 + "once_cell", 4629 + "valuable", 4630 + ] 4631 + 4632 + [[package]] 4633 + name = "tracing-log" 4634 + version = "0.2.0" 4635 + source = "registry+https://github.com/rust-lang/crates.io-index" 4636 + checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" 4637 + dependencies = [ 4638 + "log", 4639 + "once_cell", 4640 + "tracing-core", 4641 + ] 4642 + 4643 + [[package]] 4644 + name = "tracing-subscriber" 4645 + version = "0.3.22" 4646 + source = "registry+https://github.com/rust-lang/crates.io-index" 4647 + checksum = "2f30143827ddab0d256fd843b7a66d164e9f271cfa0dde49142c5ca0ca291f1e" 4648 + dependencies = [ 4649 + "matchers", 4650 + "nu-ansi-term", 4651 + "once_cell", 4652 + "regex-automata", 4653 + "sharded-slab", 4654 + "smallvec", 4655 + "thread_local", 4656 + "tracing", 4657 + "tracing-core", 4658 + "tracing-log", 4659 + ] 4660 + 4661 + [[package]] 4662 + name = "trait-variant" 4663 + version = "0.1.2" 4664 + source = "registry+https://github.com/rust-lang/crates.io-index" 4665 + checksum = "70977707304198400eb4835a78f6a9f928bf41bba420deb8fdb175cd965d77a7" 4666 + dependencies = [ 4667 + "proc-macro2", 4668 + "quote", 4669 + "syn 2.0.111", 4670 + ] 4671 + 4672 + [[package]] 4673 + name = "triomphe" 4674 + version = "0.1.15" 4675 + source = "registry+https://github.com/rust-lang/crates.io-index" 4676 + checksum = "dd69c5aa8f924c7519d6372789a74eac5b94fb0f8fcf0d4a97eb0bfc3e785f39" 4677 + 4678 + [[package]] 4679 + name = "try-lock" 4680 + version = "0.2.5" 4681 + source = "registry+https://github.com/rust-lang/crates.io-index" 4682 + checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" 4683 + 4684 + [[package]] 4685 + name = "twoway" 4686 + version = "0.1.8" 4687 + source = "registry+https://github.com/rust-lang/crates.io-index" 4688 + checksum = "59b11b2b5241ba34be09c3cc85a36e56e48f9888862e19cedf23336d35316ed1" 4689 + dependencies = [ 4690 + "memchr", 4691 + ] 4692 + 4693 + [[package]] 4694 + name = "typenum" 4695 + version = "1.19.0" 4696 + source = "registry+https://github.com/rust-lang/crates.io-index" 4697 + checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" 4698 + 4699 + [[package]] 4700 + name = "unicase" 4701 + version = "2.8.1" 4702 + source = "registry+https://github.com/rust-lang/crates.io-index" 4703 + checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" 4704 + 4705 + [[package]] 4706 + name = "unicode-bidi" 4707 + version = "0.3.18" 4708 + source = "registry+https://github.com/rust-lang/crates.io-index" 4709 + checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" 4710 + 4711 + [[package]] 4712 + name = "unicode-ident" 4713 + version = "1.0.22" 4714 + source = "registry+https://github.com/rust-lang/crates.io-index" 4715 + checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" 4716 + 4717 + [[package]] 4718 + name = "unicode-normalization" 4719 + version = "0.1.25" 4720 + source = "registry+https://github.com/rust-lang/crates.io-index" 4721 + checksum = "5fd4f6878c9cb28d874b009da9e8d183b5abc80117c40bbd187a1fde336be6e8" 4722 + dependencies = [ 4723 + "tinyvec", 4724 + ] 4725 + 4726 + [[package]] 4727 + name = "unicode-properties" 4728 + version = "0.1.4" 4729 + source = "registry+https://github.com/rust-lang/crates.io-index" 4730 + checksum = "7df058c713841ad818f1dc5d3fd88063241cc61f49f5fbea4b951e8cf5a8d71d" 4731 + 4732 + [[package]] 4733 + name = "unicode-segmentation" 4734 + version = "1.12.0" 4735 + source = "registry+https://github.com/rust-lang/crates.io-index" 4736 + checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" 4737 + 4738 + [[package]] 4739 + name = "unicode-width" 4740 + version = "0.1.14" 4741 + source = "registry+https://github.com/rust-lang/crates.io-index" 4742 + checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" 4743 + 4744 + [[package]] 4745 + name = "unicode-xid" 4746 + version = "0.2.6" 4747 + source = "registry+https://github.com/rust-lang/crates.io-index" 4748 + checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" 4749 + 4750 + [[package]] 4751 + name = "unsigned-varint" 4752 + version = "0.7.2" 4753 + source = "registry+https://github.com/rust-lang/crates.io-index" 4754 + checksum = "6889a77d49f1f013504cec6bf97a2c730394adedaeb1deb5ea08949a50541105" 4755 + 4756 + [[package]] 4757 + name = "unsigned-varint" 4758 + version = "0.8.0" 4759 + source = "registry+https://github.com/rust-lang/crates.io-index" 4760 + checksum = "eb066959b24b5196ae73cb057f45598450d2c5f71460e98c49b738086eff9c06" 4761 + 4762 + [[package]] 4763 + name = "untrusted" 4764 + version = "0.9.0" 4765 + source = "registry+https://github.com/rust-lang/crates.io-index" 4766 + checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" 4767 + 4768 + [[package]] 4769 + name = "url" 4770 + version = "2.5.7" 4771 + source = "registry+https://github.com/rust-lang/crates.io-index" 4772 + checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b" 4773 + dependencies = [ 4774 + "form_urlencoded", 4775 + "idna", 4776 + "percent-encoding", 4777 + "serde", 4778 + ] 4779 + 4780 + [[package]] 4781 + name = "urlencoding" 4782 + version = "2.1.3" 4783 + source = "registry+https://github.com/rust-lang/crates.io-index" 4784 + checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" 4785 + 4786 + [[package]] 4787 + name = "utf-8" 4788 + version = "0.7.6" 4789 + source = "registry+https://github.com/rust-lang/crates.io-index" 4790 + checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" 4791 + 4792 + [[package]] 4793 + name = "utf8_iter" 4794 + version = "1.0.4" 4795 + source = "registry+https://github.com/rust-lang/crates.io-index" 4796 + checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" 4797 + 4798 + [[package]] 4799 + name = "uuid" 4800 + version = "1.19.0" 4801 + source = "registry+https://github.com/rust-lang/crates.io-index" 4802 + checksum = "e2e054861b4bd027cd373e18e8d8d8e6548085000e41290d95ce0c373a654b4a" 4803 + dependencies = [ 4804 + "getrandom 0.3.4", 4805 + "js-sys", 4806 + "rand 0.9.2", 4807 + "wasm-bindgen", 4808 + ] 4809 + 4810 + [[package]] 4811 + name = "valuable" 4812 + version = "0.1.1" 4813 + source = "registry+https://github.com/rust-lang/crates.io-index" 4814 + checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" 4815 + 4816 + [[package]] 4817 + name = "vcpkg" 4818 + version = "0.2.15" 4819 + source = "registry+https://github.com/rust-lang/crates.io-index" 4820 + checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" 4821 + 4822 + [[package]] 4823 + name = "version_check" 4824 + version = "0.9.5" 4825 + source = "registry+https://github.com/rust-lang/crates.io-index" 4826 + checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" 4827 + 4828 + [[package]] 4829 + name = "walkdir" 4830 + version = "2.5.0" 4831 + source = "registry+https://github.com/rust-lang/crates.io-index" 4832 + checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" 4833 + dependencies = [ 4834 + "same-file", 4835 + "winapi-util", 4836 + ] 4837 + 4838 + [[package]] 4839 + name = "want" 4840 + version = "0.3.1" 4841 + source = "registry+https://github.com/rust-lang/crates.io-index" 4842 + checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" 4843 + dependencies = [ 4844 + "try-lock", 4845 + ] 4846 + 4847 + [[package]] 4848 + name = "wasi" 4849 + version = "0.11.1+wasi-snapshot-preview1" 4850 + source = "registry+https://github.com/rust-lang/crates.io-index" 4851 + checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" 4852 + 4853 + [[package]] 4854 + name = "wasip2" 4855 + version = "1.0.1+wasi-0.2.4" 4856 + source = "registry+https://github.com/rust-lang/crates.io-index" 4857 + checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" 4858 + dependencies = [ 4859 + "wit-bindgen", 4860 + ] 4861 + 4862 + [[package]] 4863 + name = "wasite" 4864 + version = "0.1.0" 4865 + source = "registry+https://github.com/rust-lang/crates.io-index" 4866 + checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" 4867 + 4868 + [[package]] 4869 + name = "wasm-bindgen" 4870 + version = "0.2.106" 4871 + source = "registry+https://github.com/rust-lang/crates.io-index" 4872 + checksum = "0d759f433fa64a2d763d1340820e46e111a7a5ab75f993d1852d70b03dbb80fd" 4873 + dependencies = [ 4874 + "cfg-if", 4875 + "once_cell", 4876 + "rustversion", 4877 + "wasm-bindgen-macro", 4878 + "wasm-bindgen-shared", 4879 + ] 4880 + 4881 + [[package]] 4882 + name = "wasm-bindgen-futures" 4883 + version = "0.4.56" 4884 + source = "registry+https://github.com/rust-lang/crates.io-index" 4885 + checksum = "836d9622d604feee9e5de25ac10e3ea5f2d65b41eac0d9ce72eb5deae707ce7c" 4886 + dependencies = [ 4887 + "cfg-if", 4888 + "js-sys", 4889 + "once_cell", 4890 + "wasm-bindgen", 4891 + "web-sys", 4892 + ] 4893 + 4894 + [[package]] 4895 + name = "wasm-bindgen-macro" 4896 + version = "0.2.106" 4897 + source = "registry+https://github.com/rust-lang/crates.io-index" 4898 + checksum = "48cb0d2638f8baedbc542ed444afc0644a29166f1595371af4fecf8ce1e7eeb3" 4899 + dependencies = [ 4900 + "quote", 4901 + "wasm-bindgen-macro-support", 4902 + ] 4903 + 4904 + [[package]] 4905 + name = "wasm-bindgen-macro-support" 4906 + version = "0.2.106" 4907 + source = "registry+https://github.com/rust-lang/crates.io-index" 4908 + checksum = "cefb59d5cd5f92d9dcf80e4683949f15ca4b511f4ac0a6e14d4e1ac60c6ecd40" 4909 + dependencies = [ 4910 + "bumpalo", 4911 + "proc-macro2", 4912 + "quote", 4913 + "syn 2.0.111", 4914 + "wasm-bindgen-shared", 4915 + ] 4916 + 4917 + [[package]] 4918 + name = "wasm-bindgen-shared" 4919 + version = "0.2.106" 4920 + source = "registry+https://github.com/rust-lang/crates.io-index" 4921 + checksum = "cbc538057e648b67f72a982e708d485b2efa771e1ac05fec311f9f63e5800db4" 4922 + dependencies = [ 4923 + "unicode-ident", 4924 + ] 4925 + 4926 + [[package]] 4927 + name = "wasm-streams" 4928 + version = "0.4.2" 4929 + source = "registry+https://github.com/rust-lang/crates.io-index" 4930 + checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65" 4931 + dependencies = [ 4932 + "futures-util", 4933 + "js-sys", 4934 + "wasm-bindgen", 4935 + "wasm-bindgen-futures", 4936 + "web-sys", 4937 + ] 4938 + 4939 + [[package]] 4940 + name = "web-sys" 4941 + version = "0.3.83" 4942 + source = "registry+https://github.com/rust-lang/crates.io-index" 4943 + checksum = "9b32828d774c412041098d182a8b38b16ea816958e07cf40eec2bc080ae137ac" 4944 + dependencies = [ 4945 + "js-sys", 4946 + "wasm-bindgen", 4947 + ] 4948 + 4949 + [[package]] 4950 + name = "web-time" 4951 + version = "1.1.0" 4952 + source = "registry+https://github.com/rust-lang/crates.io-index" 4953 + checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" 4954 + dependencies = [ 4955 + "js-sys", 4956 + "wasm-bindgen", 4957 + ] 4958 + 4959 + [[package]] 4960 + name = "webbrowser" 4961 + version = "1.0.6" 4962 + source = "registry+https://github.com/rust-lang/crates.io-index" 4963 + checksum = "00f1243ef785213e3a32fa0396093424a3a6ea566f9948497e5a2309261a4c97" 4964 + dependencies = [ 4965 + "core-foundation 0.10.1", 4966 + "jni", 4967 + "log", 4968 + "ndk-context", 4969 + "objc2", 4970 + "objc2-foundation", 4971 + "url", 4972 + "web-sys", 4973 + ] 4974 + 4975 + [[package]] 4976 + name = "webpage" 4977 + version = "2.0.1" 4978 + source = "registry+https://github.com/rust-lang/crates.io-index" 4979 + checksum = "70862efc041d46e6bbaa82bb9c34ae0596d090e86cbd14bd9e93b36ee6802eac" 4980 + dependencies = [ 4981 + "html5ever", 4982 + "markup5ever_rcdom", 4983 + "serde_json", 4984 + "url", 4985 + ] 4986 + 4987 + [[package]] 4988 + name = "webpki-roots" 4989 + version = "0.26.11" 4990 + source = "registry+https://github.com/rust-lang/crates.io-index" 4991 + checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" 4992 + dependencies = [ 4993 + "webpki-roots 1.0.4", 4994 + ] 4995 + 4996 + [[package]] 4997 + name = "webpki-roots" 4998 + version = "1.0.4" 4999 + source = "registry+https://github.com/rust-lang/crates.io-index" 5000 + checksum = "b2878ef029c47c6e8cf779119f20fcf52bde7ad42a731b2a304bc221df17571e" 5001 + dependencies = [ 5002 + "rustls-pki-types", 5003 + ] 5004 + 5005 + [[package]] 5006 + name = "whoami" 5007 + version = "1.6.1" 5008 + source = "registry+https://github.com/rust-lang/crates.io-index" 5009 + checksum = "5d4a4db5077702ca3015d3d02d74974948aba2ad9e12ab7df718ee64ccd7e97d" 5010 + dependencies = [ 5011 + "libredox", 5012 + "wasite", 5013 + ] 5014 + 5015 + [[package]] 5016 + name = "widestring" 5017 + version = "1.2.1" 5018 + source = "registry+https://github.com/rust-lang/crates.io-index" 5019 + checksum = "72069c3113ab32ab29e5584db3c6ec55d416895e60715417b5b883a357c3e471" 5020 + 5021 + [[package]] 5022 + name = "winapi-util" 5023 + version = "0.1.11" 5024 + source = "registry+https://github.com/rust-lang/crates.io-index" 5025 + checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" 5026 + dependencies = [ 5027 + "windows-sys 0.61.2", 5028 + ] 5029 + 5030 + [[package]] 5031 + name = "windows" 5032 + version = "0.61.3" 5033 + source = "registry+https://github.com/rust-lang/crates.io-index" 5034 + checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893" 5035 + dependencies = [ 5036 + "windows-collections", 5037 + "windows-core 0.61.2", 5038 + "windows-future", 5039 + "windows-link 0.1.3", 5040 + "windows-numerics", 5041 + ] 5042 + 5043 + [[package]] 5044 + name = "windows-collections" 5045 + version = "0.2.0" 5046 + source = "registry+https://github.com/rust-lang/crates.io-index" 5047 + checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" 5048 + dependencies = [ 5049 + "windows-core 0.61.2", 5050 + ] 5051 + 5052 + [[package]] 5053 + name = "windows-core" 5054 + version = "0.61.2" 5055 + source = "registry+https://github.com/rust-lang/crates.io-index" 5056 + checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" 5057 + dependencies = [ 5058 + "windows-implement", 5059 + "windows-interface", 5060 + "windows-link 0.1.3", 5061 + "windows-result 0.3.4", 5062 + "windows-strings 0.4.2", 5063 + ] 5064 + 5065 + [[package]] 5066 + name = "windows-core" 5067 + version = "0.62.2" 5068 + source = "registry+https://github.com/rust-lang/crates.io-index" 5069 + checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" 5070 + dependencies = [ 5071 + "windows-implement", 5072 + "windows-interface", 5073 + "windows-link 0.2.1", 5074 + "windows-result 0.4.1", 5075 + "windows-strings 0.5.1", 5076 + ] 5077 + 5078 + [[package]] 5079 + name = "windows-future" 5080 + version = "0.2.1" 5081 + source = "registry+https://github.com/rust-lang/crates.io-index" 5082 + checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" 5083 + dependencies = [ 5084 + "windows-core 0.61.2", 5085 + "windows-link 0.1.3", 5086 + "windows-threading", 5087 + ] 5088 + 5089 + [[package]] 5090 + name = "windows-implement" 5091 + version = "0.60.2" 5092 + source = "registry+https://github.com/rust-lang/crates.io-index" 5093 + checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" 5094 + dependencies = [ 5095 + "proc-macro2", 5096 + "quote", 5097 + "syn 2.0.111", 5098 + ] 5099 + 5100 + [[package]] 5101 + name = "windows-interface" 5102 + version = "0.59.3" 5103 + source = "registry+https://github.com/rust-lang/crates.io-index" 5104 + checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" 5105 + dependencies = [ 5106 + "proc-macro2", 5107 + "quote", 5108 + "syn 2.0.111", 5109 + ] 5110 + 5111 + [[package]] 5112 + name = "windows-link" 5113 + version = "0.1.3" 5114 + source = "registry+https://github.com/rust-lang/crates.io-index" 5115 + checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" 5116 + 5117 + [[package]] 5118 + name = "windows-link" 5119 + version = "0.2.1" 5120 + source = "registry+https://github.com/rust-lang/crates.io-index" 5121 + checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" 5122 + 5123 + [[package]] 5124 + name = "windows-numerics" 5125 + version = "0.2.0" 5126 + source = "registry+https://github.com/rust-lang/crates.io-index" 5127 + checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" 5128 + dependencies = [ 5129 + "windows-core 0.61.2", 5130 + "windows-link 0.1.3", 5131 + ] 5132 + 5133 + [[package]] 5134 + name = "windows-registry" 5135 + version = "0.6.1" 5136 + source = "registry+https://github.com/rust-lang/crates.io-index" 5137 + checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720" 5138 + dependencies = [ 5139 + "windows-link 0.2.1", 5140 + "windows-result 0.4.1", 5141 + "windows-strings 0.5.1", 5142 + ] 5143 + 5144 + [[package]] 5145 + name = "windows-result" 5146 + version = "0.3.4" 5147 + source = "registry+https://github.com/rust-lang/crates.io-index" 5148 + checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" 5149 + dependencies = [ 5150 + "windows-link 0.1.3", 5151 + ] 5152 + 5153 + [[package]] 5154 + name = "windows-result" 5155 + version = "0.4.1" 5156 + source = "registry+https://github.com/rust-lang/crates.io-index" 5157 + checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" 5158 + dependencies = [ 5159 + "windows-link 0.2.1", 5160 + ] 5161 + 5162 + [[package]] 5163 + name = "windows-strings" 5164 + version = "0.4.2" 5165 + source = "registry+https://github.com/rust-lang/crates.io-index" 5166 + checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" 5167 + dependencies = [ 5168 + "windows-link 0.1.3", 5169 + ] 5170 + 5171 + [[package]] 5172 + name = "windows-strings" 5173 + version = "0.5.1" 5174 + source = "registry+https://github.com/rust-lang/crates.io-index" 5175 + checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" 5176 + dependencies = [ 5177 + "windows-link 0.2.1", 5178 + ] 5179 + 5180 + [[package]] 5181 + name = "windows-sys" 5182 + version = "0.45.0" 5183 + source = "registry+https://github.com/rust-lang/crates.io-index" 5184 + checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" 5185 + dependencies = [ 5186 + "windows-targets 0.42.2", 5187 + ] 5188 + 5189 + [[package]] 5190 + name = "windows-sys" 5191 + version = "0.48.0" 5192 + source = "registry+https://github.com/rust-lang/crates.io-index" 5193 + checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" 5194 + dependencies = [ 5195 + "windows-targets 0.48.5", 5196 + ] 5197 + 5198 + [[package]] 5199 + name = "windows-sys" 5200 + version = "0.52.0" 5201 + source = "registry+https://github.com/rust-lang/crates.io-index" 5202 + checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" 5203 + dependencies = [ 5204 + "windows-targets 0.52.6", 5205 + ] 5206 + 5207 + [[package]] 5208 + name = "windows-sys" 5209 + version = "0.60.2" 5210 + source = "registry+https://github.com/rust-lang/crates.io-index" 5211 + checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" 5212 + dependencies = [ 5213 + "windows-targets 0.53.5", 5214 + ] 5215 + 5216 + [[package]] 5217 + name = "windows-sys" 5218 + version = "0.61.2" 5219 + source = "registry+https://github.com/rust-lang/crates.io-index" 5220 + checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" 5221 + dependencies = [ 5222 + "windows-link 0.2.1", 5223 + ] 5224 + 5225 + [[package]] 5226 + name = "windows-targets" 5227 + version = "0.42.2" 5228 + source = "registry+https://github.com/rust-lang/crates.io-index" 5229 + checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" 5230 + dependencies = [ 5231 + "windows_aarch64_gnullvm 0.42.2", 5232 + "windows_aarch64_msvc 0.42.2", 5233 + "windows_i686_gnu 0.42.2", 5234 + "windows_i686_msvc 0.42.2", 5235 + "windows_x86_64_gnu 0.42.2", 5236 + "windows_x86_64_gnullvm 0.42.2", 5237 + "windows_x86_64_msvc 0.42.2", 5238 + ] 5239 + 5240 + [[package]] 5241 + name = "windows-targets" 5242 + version = "0.48.5" 5243 + source = "registry+https://github.com/rust-lang/crates.io-index" 5244 + checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" 5245 + dependencies = [ 5246 + "windows_aarch64_gnullvm 0.48.5", 5247 + "windows_aarch64_msvc 0.48.5", 5248 + "windows_i686_gnu 0.48.5", 5249 + "windows_i686_msvc 0.48.5", 5250 + "windows_x86_64_gnu 0.48.5", 5251 + "windows_x86_64_gnullvm 0.48.5", 5252 + "windows_x86_64_msvc 0.48.5", 5253 + ] 5254 + 5255 + [[package]] 5256 + name = "windows-targets" 5257 + version = "0.52.6" 5258 + source = "registry+https://github.com/rust-lang/crates.io-index" 5259 + checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" 5260 + dependencies = [ 5261 + "windows_aarch64_gnullvm 0.52.6", 5262 + "windows_aarch64_msvc 0.52.6", 5263 + "windows_i686_gnu 0.52.6", 5264 + "windows_i686_gnullvm 0.52.6", 5265 + "windows_i686_msvc 0.52.6", 5266 + "windows_x86_64_gnu 0.52.6", 5267 + "windows_x86_64_gnullvm 0.52.6", 5268 + "windows_x86_64_msvc 0.52.6", 5269 + ] 5270 + 5271 + [[package]] 5272 + name = "windows-targets" 5273 + version = "0.53.5" 5274 + source = "registry+https://github.com/rust-lang/crates.io-index" 5275 + checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" 5276 + dependencies = [ 5277 + "windows-link 0.2.1", 5278 + "windows_aarch64_gnullvm 0.53.1", 5279 + "windows_aarch64_msvc 0.53.1", 5280 + "windows_i686_gnu 0.53.1", 5281 + "windows_i686_gnullvm 0.53.1", 5282 + "windows_i686_msvc 0.53.1", 5283 + "windows_x86_64_gnu 0.53.1", 5284 + "windows_x86_64_gnullvm 0.53.1", 5285 + "windows_x86_64_msvc 0.53.1", 5286 + ] 5287 + 5288 + [[package]] 5289 + name = "windows-threading" 5290 + version = "0.1.0" 5291 + source = "registry+https://github.com/rust-lang/crates.io-index" 5292 + checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6" 5293 + dependencies = [ 5294 + "windows-link 0.1.3", 5295 + ] 5296 + 5297 + [[package]] 5298 + name = "windows_aarch64_gnullvm" 5299 + version = "0.42.2" 5300 + source = "registry+https://github.com/rust-lang/crates.io-index" 5301 + checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" 5302 + 5303 + [[package]] 5304 + name = "windows_aarch64_gnullvm" 5305 + version = "0.48.5" 5306 + source = "registry+https://github.com/rust-lang/crates.io-index" 5307 + checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" 5308 + 5309 + [[package]] 5310 + name = "windows_aarch64_gnullvm" 5311 + version = "0.52.6" 5312 + source = "registry+https://github.com/rust-lang/crates.io-index" 5313 + checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 5314 + 5315 + [[package]] 5316 + name = "windows_aarch64_gnullvm" 5317 + version = "0.53.1" 5318 + source = "registry+https://github.com/rust-lang/crates.io-index" 5319 + checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" 5320 + 5321 + [[package]] 5322 + name = "windows_aarch64_msvc" 5323 + version = "0.42.2" 5324 + source = "registry+https://github.com/rust-lang/crates.io-index" 5325 + checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" 5326 + 5327 + [[package]] 5328 + name = "windows_aarch64_msvc" 5329 + version = "0.48.5" 5330 + source = "registry+https://github.com/rust-lang/crates.io-index" 5331 + checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" 5332 + 5333 + [[package]] 5334 + name = "windows_aarch64_msvc" 5335 + version = "0.52.6" 5336 + source = "registry+https://github.com/rust-lang/crates.io-index" 5337 + checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 5338 + 5339 + [[package]] 5340 + name = "windows_aarch64_msvc" 5341 + version = "0.53.1" 5342 + source = "registry+https://github.com/rust-lang/crates.io-index" 5343 + checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" 5344 + 5345 + [[package]] 5346 + name = "windows_i686_gnu" 5347 + version = "0.42.2" 5348 + source = "registry+https://github.com/rust-lang/crates.io-index" 5349 + checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" 5350 + 5351 + [[package]] 5352 + name = "windows_i686_gnu" 5353 + version = "0.48.5" 5354 + source = "registry+https://github.com/rust-lang/crates.io-index" 5355 + checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" 5356 + 5357 + [[package]] 5358 + name = "windows_i686_gnu" 5359 + version = "0.52.6" 5360 + source = "registry+https://github.com/rust-lang/crates.io-index" 5361 + checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 5362 + 5363 + [[package]] 5364 + name = "windows_i686_gnu" 5365 + version = "0.53.1" 5366 + source = "registry+https://github.com/rust-lang/crates.io-index" 5367 + checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" 5368 + 5369 + [[package]] 5370 + name = "windows_i686_gnullvm" 5371 + version = "0.52.6" 5372 + source = "registry+https://github.com/rust-lang/crates.io-index" 5373 + checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 5374 + 5375 + [[package]] 5376 + name = "windows_i686_gnullvm" 5377 + version = "0.53.1" 5378 + source = "registry+https://github.com/rust-lang/crates.io-index" 5379 + checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" 5380 + 5381 + [[package]] 5382 + name = "windows_i686_msvc" 5383 + version = "0.42.2" 5384 + source = "registry+https://github.com/rust-lang/crates.io-index" 5385 + checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" 5386 + 5387 + [[package]] 5388 + name = "windows_i686_msvc" 5389 + version = "0.48.5" 5390 + source = "registry+https://github.com/rust-lang/crates.io-index" 5391 + checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" 5392 + 5393 + [[package]] 5394 + name = "windows_i686_msvc" 5395 + version = "0.52.6" 5396 + source = "registry+https://github.com/rust-lang/crates.io-index" 5397 + checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 5398 + 5399 + [[package]] 5400 + name = "windows_i686_msvc" 5401 + version = "0.53.1" 5402 + source = "registry+https://github.com/rust-lang/crates.io-index" 5403 + checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" 5404 + 5405 + [[package]] 5406 + name = "windows_x86_64_gnu" 5407 + version = "0.42.2" 5408 + source = "registry+https://github.com/rust-lang/crates.io-index" 5409 + checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" 5410 + 5411 + [[package]] 5412 + name = "windows_x86_64_gnu" 5413 + version = "0.48.5" 5414 + source = "registry+https://github.com/rust-lang/crates.io-index" 5415 + checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" 5416 + 5417 + [[package]] 5418 + name = "windows_x86_64_gnu" 5419 + version = "0.52.6" 5420 + source = "registry+https://github.com/rust-lang/crates.io-index" 5421 + checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 5422 + 5423 + [[package]] 5424 + name = "windows_x86_64_gnu" 5425 + version = "0.53.1" 5426 + source = "registry+https://github.com/rust-lang/crates.io-index" 5427 + checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" 5428 + 5429 + [[package]] 5430 + name = "windows_x86_64_gnullvm" 5431 + version = "0.42.2" 5432 + source = "registry+https://github.com/rust-lang/crates.io-index" 5433 + checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" 5434 + 5435 + [[package]] 5436 + name = "windows_x86_64_gnullvm" 5437 + version = "0.48.5" 5438 + source = "registry+https://github.com/rust-lang/crates.io-index" 5439 + checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" 5440 + 5441 + [[package]] 5442 + name = "windows_x86_64_gnullvm" 5443 + version = "0.52.6" 5444 + source = "registry+https://github.com/rust-lang/crates.io-index" 5445 + checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 5446 + 5447 + [[package]] 5448 + name = "windows_x86_64_gnullvm" 5449 + version = "0.53.1" 5450 + source = "registry+https://github.com/rust-lang/crates.io-index" 5451 + checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" 5452 + 5453 + [[package]] 5454 + name = "windows_x86_64_msvc" 5455 + version = "0.42.2" 5456 + source = "registry+https://github.com/rust-lang/crates.io-index" 5457 + checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" 5458 + 5459 + [[package]] 5460 + name = "windows_x86_64_msvc" 5461 + version = "0.48.5" 5462 + source = "registry+https://github.com/rust-lang/crates.io-index" 5463 + checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" 5464 + 5465 + [[package]] 5466 + name = "windows_x86_64_msvc" 5467 + version = "0.52.6" 5468 + source = "registry+https://github.com/rust-lang/crates.io-index" 5469 + checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 5470 + 5471 + [[package]] 5472 + name = "windows_x86_64_msvc" 5473 + version = "0.53.1" 5474 + source = "registry+https://github.com/rust-lang/crates.io-index" 5475 + checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" 5476 + 5477 + [[package]] 5478 + name = "winreg" 5479 + version = "0.50.0" 5480 + source = "registry+https://github.com/rust-lang/crates.io-index" 5481 + checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" 5482 + dependencies = [ 5483 + "cfg-if", 5484 + "windows-sys 0.48.0", 5485 + ] 5486 + 5487 + [[package]] 5488 + name = "wit-bindgen" 5489 + version = "0.46.0" 5490 + source = "registry+https://github.com/rust-lang/crates.io-index" 5491 + checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" 5492 + 5493 + [[package]] 5494 + name = "writeable" 5495 + version = "0.6.2" 5496 + source = "registry+https://github.com/rust-lang/crates.io-index" 5497 + checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" 5498 + 5499 + [[package]] 5500 + name = "xml5ever" 5501 + version = "0.18.1" 5502 + source = "registry+https://github.com/rust-lang/crates.io-index" 5503 + checksum = "9bbb26405d8e919bc1547a5aa9abc95cbfa438f04844f5fdd9dc7596b748bf69" 5504 + dependencies = [ 5505 + "log", 5506 + "mac", 5507 + "markup5ever", 5508 + ] 5509 + 5510 + [[package]] 5511 + name = "yansi" 5512 + version = "1.0.1" 5513 + source = "registry+https://github.com/rust-lang/crates.io-index" 5514 + checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" 5515 + 5516 + [[package]] 5517 + name = "yoke" 5518 + version = "0.8.1" 5519 + source = "registry+https://github.com/rust-lang/crates.io-index" 5520 + checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" 5521 + dependencies = [ 5522 + "stable_deref_trait", 5523 + "yoke-derive", 5524 + "zerofrom", 5525 + ] 5526 + 5527 + [[package]] 5528 + name = "yoke-derive" 5529 + version = "0.8.1" 5530 + source = "registry+https://github.com/rust-lang/crates.io-index" 5531 + checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" 5532 + dependencies = [ 5533 + "proc-macro2", 5534 + "quote", 5535 + "syn 2.0.111", 5536 + "synstructure", 5537 + ] 5538 + 5539 + [[package]] 5540 + name = "zerocopy" 5541 + version = "0.8.31" 5542 + source = "registry+https://github.com/rust-lang/crates.io-index" 5543 + checksum = "fd74ec98b9250adb3ca554bdde269adf631549f51d8a8f8f0a10b50f1cb298c3" 5544 + dependencies = [ 5545 + "zerocopy-derive", 5546 + ] 5547 + 5548 + [[package]] 5549 + name = "zerocopy-derive" 5550 + version = "0.8.31" 5551 + source = "registry+https://github.com/rust-lang/crates.io-index" 5552 + checksum = "d8a8d209fdf45cf5138cbb5a506f6b52522a25afccc534d1475dad8e31105c6a" 5553 + dependencies = [ 5554 + "proc-macro2", 5555 + "quote", 5556 + "syn 2.0.111", 5557 + ] 5558 + 5559 + [[package]] 5560 + name = "zerofrom" 5561 + version = "0.1.6" 5562 + source = "registry+https://github.com/rust-lang/crates.io-index" 5563 + checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" 5564 + dependencies = [ 5565 + "zerofrom-derive", 5566 + ] 5567 + 5568 + [[package]] 5569 + name = "zerofrom-derive" 5570 + version = "0.1.6" 5571 + source = "registry+https://github.com/rust-lang/crates.io-index" 5572 + checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" 5573 + dependencies = [ 5574 + "proc-macro2", 5575 + "quote", 5576 + "syn 2.0.111", 5577 + "synstructure", 5578 + ] 5579 + 5580 + [[package]] 5581 + name = "zeroize" 5582 + version = "1.8.2" 5583 + source = "registry+https://github.com/rust-lang/crates.io-index" 5584 + checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" 5585 + dependencies = [ 5586 + "serde", 5587 + ] 5588 + 5589 + [[package]] 5590 + name = "zerotrie" 5591 + version = "0.2.3" 5592 + source = "registry+https://github.com/rust-lang/crates.io-index" 5593 + checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" 5594 + dependencies = [ 5595 + "displaydoc", 5596 + "yoke", 5597 + "zerofrom", 5598 + ] 5599 + 5600 + [[package]] 5601 + name = "zerovec" 5602 + version = "0.11.5" 5603 + source = "registry+https://github.com/rust-lang/crates.io-index" 5604 + checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" 5605 + dependencies = [ 5606 + "yoke", 5607 + "zerofrom", 5608 + "zerovec-derive", 5609 + ] 5610 + 5611 + [[package]] 5612 + name = "zerovec-derive" 5613 + version = "0.11.2" 5614 + source = "registry+https://github.com/rust-lang/crates.io-index" 5615 + checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" 5616 + dependencies = [ 5617 + "proc-macro2", 5618 + "quote", 5619 + "syn 2.0.111", 5620 + ]
+28
Cargo.toml
···
··· 1 + [package] 2 + name = "bspds" 3 + version = "0.1.0" 4 + edition = "2024" 5 + 6 + [dependencies] 7 + anyhow = "1.0.100" 8 + axum = "0.8.7" 9 + bcrypt = "0.17.1" 10 + bytes = "1.11.0" 11 + chrono = { version = "0.4.42", features = ["serde"] } 12 + cid = "0.11.1" 13 + dotenvy = "0.15.7" 14 + jacquard = "0.9.3" 15 + jacquard-axum = "0.9.2" 16 + jacquard-repo = "0.9.2" 17 + jsonwebtoken = { version = "10.2.0", features = ["rust_crypto"] } 18 + multihash = "0.19.3" 19 + reqwest = { version = "0.12.24", features = ["json"] } 20 + serde = { version = "1.0.228", features = ["derive"] } 21 + serde_ipld_dagcbor = "0.6.4" 22 + serde_json = "1.0.145" 23 + sha2 = "0.10.9" 24 + sqlx = { version = "0.8.6", features = ["runtime-tokio-rustls", "postgres", "uuid", "chrono", "json"] } 25 + tokio = { version = "1.48.0", features = ["macros", "rt-multi-thread", "time"] } 26 + tracing = "0.1.43" 27 + tracing-subscriber = "0.3.22" 28 + uuid = { version = "1.19.0", features = ["v4", "fast-rng"] }
+108
TODO.md
···
··· 1 + # Implementation TODOs 2 + 3 + Lewis' special big boy todofile 4 + 5 + ## 1. Server Infrastructure & Health 6 + - [x] Health Check 7 + - [x] Implement `GET /health` endpoint (returns "OK"). 8 + - [x] Server Description 9 + - [x] Implement `com.atproto.server.describeServer` (returns available user domains). 10 + 11 + ## 2. Authentication & Account Management (`com.atproto.server`) 12 + - [x] Account Creation 13 + - [x] Implement `com.atproto.server.createAccount`. 14 + - [x] Validate handle format (reject invalid characters). 15 + - [x] Create DID for new user. 16 + - [x] Initialize user repository. 17 + - [x] Return access JWT and DID. 18 + - [x] MST stuff I think... 19 + 20 + - [x] Session Management 21 + - [x] Implement `com.atproto.server.createSession` (Login). 22 + - [x] Validate identifier (handle/email) and password. 23 + - [x] Return access JWT, refresh JWT, and DID. 24 + - [x] Implement `com.atproto.server.getSession`. 25 + - [x] Verify JWT validity. 26 + - [x] Implement `com.atproto.server.refreshSession`. 27 + - [x] Implement `com.atproto.server.deleteSession` (Logout). 28 + - [x] Invalidate current session/token. 29 + 30 + ## 3. Repository Operations (`com.atproto.repo`) 31 + - [ ] Record CRUD 32 + - [ ] Implement `com.atproto.repo.createRecord`. 33 + - [ ] Generate `rkey` if not provided. 34 + - [ ] Validate schema against Lexicon. 35 + - [ ] Handle `swapCommit` for optimistic locking. 36 + - [ ] Implement `com.atproto.repo.putRecord`. 37 + - [ ] Handle create vs update logic. 38 + - [ ] Validate `repo` matches authenticated user. 39 + - [ ] Validate record schema (e.g., missing required fields). 40 + - [ ] Implement `com.atproto.repo.getRecord`. 41 + - [ ] Handle missing params (400 Bad Request). 42 + - [ ] Handle non-existent record (404 Not Found). 43 + - [ ] Implement `com.atproto.repo.deleteRecord`. 44 + - [ ] Implement `com.atproto.repo.listRecords`. 45 + - [ ] Support pagination (`limit`, `cursor`). 46 + - [ ] Blob Management 47 + - [ ] Implement `com.atproto.repo.uploadBlob`. 48 + - [ ] Enforce authentication. 49 + - [ ] Validate MIME types (reject unsupported). 50 + - [ ] Return blob reference (`$link`). 51 + - [ ] Repo Meta 52 + - [ ] Implement `com.atproto.repo.describeRepo`. 53 + 54 + ## 4. Actor & Profile (`app.bsky.actor`) 55 + - [ ] Profile Management 56 + - [ ] Implement `app.bsky.actor.getProfile`. 57 + - [ ] Resolve handle to DID. 58 + - [ ] Return profile record data. 59 + - [ ] Discovery 60 + - [ ] Implement `app.bsky.actor.searchActors`. 61 + 62 + ## 5. Feed & Timeline (`app.bsky.feed`) 63 + - [ ] Feed Retrieval 64 + - [ ] Implement `app.bsky.feed.getTimeline`. 65 + - [ ] Implement `app.bsky.feed.getAuthorFeed`. 66 + - [ ] Filter by actor. 67 + - [ ] Respect mutes (if viewer is authenticated). 68 + - [ ] Implement `app.bsky.feed.getPostThread`. 69 + - [ ] Construct thread tree (parents, replies). 70 + - [ ] Handle deleted posts (return `notFoundPost` view). 71 + - [ ] Record Types 72 + - [ ] Support `app.bsky.feed.post` record type. 73 + - [ ] Support `app.bsky.feed.like` record type. 74 + - [ ] Support `app.bsky.embed.images` in posts. 75 + 76 + ## 6. Social Graph (`app.bsky.graph`) 77 + - [ ] Relationships 78 + - [ ] Implement `app.bsky.graph.getFollows`. 79 + - [ ] Implement `app.bsky.graph.getFollowers`. 80 + - [ ] Implement `app.bsky.graph.getMutes`. 81 + - [ ] Implement `app.bsky.graph.getBlocks`. 82 + - [ ] Record Types 83 + - [ ] Support `app.bsky.graph.follow` record type. 84 + - [ ] Support `app.bsky.graph.mute` record type. 85 + 86 + ## 7. Notifications (`app.bsky.notification`) 87 + - [ ] Notification Management 88 + - [ ] Implement `app.bsky.notification.listNotifications`. 89 + - [ ] Aggregate notifications (likes, follows, replies). 90 + - [ ] Implement `app.bsky.notification.getUnreadCount`. 91 + - [ ] Track read state. 92 + - [ ] Reset count on list/read. 93 + 94 + ## 8. Identity (`com.atproto.identity`) 95 + - [ ] Resolution 96 + - [ ] Implement `com.atproto.identity.resolveHandle`. 97 + 98 + ## 9. Sync & Federation (`com.atproto.sync`) 99 + - [ ] Data Export 100 + - [ ] Implement `com.atproto.sync.getRepo` (Export CAR file). 101 + - [ ] Implement `com.atproto.sync.getBlocks`. 102 + 103 + ## 10. General Requirements 104 + - [ ] Validation 105 + - [ ] Ensure all endpoints validate input parameters. 106 + - [ ] Ensure proper error codes (400, 401, 404, 409). 107 + - [ ] Concurrency 108 + - [ ] Ensure thread safety for repo updates.
+50
docker-compose.yaml
···
··· 1 + services: 2 + app: 3 + build: 4 + context: . 5 + dockerfile: Dockerfile 6 + image: bspds 7 + ports: 8 + - "3000:3000" 9 + environment: 10 + SERVER_HOST: 0.0.0.0 11 + SERVER_PORT: 3000 12 + DATABASE_URL: postgres://postgres:postgres@db:5432/pds 13 + OBJECT_STORAGE_ENDPOINT: http://objsto:9000 14 + OBJECT_STORAGE_REGION: us-east-1 15 + OBJECT_STORAGE_BUCKET: pds-blobs 16 + OBJECT_STORAGE_ACCESS_KEY: minioadmin 17 + OBJECT_STORAGE_SECRET_KEY: minioadmin 18 + OBJECT_STORAGE_FORCE_PATH_STYLE: "true" 19 + JWT_SECRET: your-super-secret-jwt-key-please-change-me 20 + PDS_HOSTNAME: localhost:3000 21 + depends_on: 22 + - db 23 + - objsto 24 + 25 + db: 26 + image: postgres:latest 27 + environment: 28 + POSTGRES_USER: postgres 29 + POSTGRES_PASSWORD: postgres 30 + POSTGRES_DB: pds 31 + ports: 32 + - "5432:5432" 33 + volumes: 34 + - postgres_data:/var/lib/postgresql 35 + 36 + objsto: 37 + image: minio/minio 38 + ports: 39 + - "9000:9000" 40 + - "9001:9001" 41 + environment: 42 + MINIO_ROOT_USER: minioadmin 43 + MINIO_ROOT_PASSWORD: minioadmin 44 + volumes: 45 + - minio_data:/data 46 + command: server /data --console-address ":9001" 47 + 48 + volumes: 49 + postgres_data: 50 + minio_data:
+80
migrations/202512211400_initial_tables.sql
···
··· 1 + -- A very basic schema to get started. 2 + -- TODO: PRODUCTIONIZE BABY 3 + 4 + CREATE TABLE IF NOT EXISTS users ( 5 + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), 6 + handle TEXT NOT NULL UNIQUE, 7 + email TEXT NOT NULL UNIQUE, 8 + did TEXT NOT NULL UNIQUE, 9 + password_hash TEXT NOT NULL, 10 + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), 11 + updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() 12 + ); 13 + 14 + CREATE TABLE IF NOT EXISTS invite_codes ( 15 + code TEXT PRIMARY KEY, 16 + available_uses INT NOT NULL DEFAULT 1, 17 + created_by_user UUID NOT NULL REFERENCES users(id), 18 + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() 19 + ); 20 + 21 + CREATE TABLE IF NOT EXISTS invite_code_uses ( 22 + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), 23 + code TEXT NOT NULL REFERENCES invite_codes(code), 24 + used_by_user UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, 25 + used_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), 26 + UNIQUE(code, used_by_user) 27 + ); 28 + 29 + -- OIII THIS TABLE CONTAINS PLAINTEXT PRIVATE KEYS, TODO: encrypt at rest! 30 + CREATE TABLE IF NOT EXISTS user_keys ( 31 + user_id UUID PRIMARY KEY REFERENCES users(id) ON DELETE CASCADE, 32 + -- Storing as raw bytes 33 + -- secp256k1 is 32 bytes 34 + key_bytes BYTEA NOT NULL, 35 + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() 36 + ); 37 + 38 + CREATE TABLE IF NOT EXISTS repos ( 39 + user_id UUID PRIMARY KEY REFERENCES users(id) ON DELETE CASCADE, 40 + repo_root_cid TEXT NOT NULL, 41 + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), 42 + updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() 43 + ); 44 + 45 + CREATE TABLE IF NOT EXISTS blocks ( 46 + cid BYTEA PRIMARY KEY, 47 + data BYTEA NOT NULL, 48 + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() 49 + ); 50 + 51 + -- A denormalized table to quickly query for records 52 + -- TODO: Do I actually need this? 53 + CREATE TABLE IF NOT EXISTS records ( 54 + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), 55 + repo_id UUID NOT NULL REFERENCES repos(user_id) ON DELETE CASCADE, 56 + collection TEXT NOT NULL, 57 + rkey TEXT NOT NULL, 58 + record_cid TEXT NOT NULL, 59 + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), 60 + UNIQUE(repo_id, collection, rkey) 61 + ); 62 + 63 + CREATE TABLE IF NOT EXISTS blobs ( 64 + cid TEXT PRIMARY KEY, 65 + mime_type TEXT NOT NULL, 66 + size_bytes BIGINT NOT NULL, 67 + created_by_user UUID NOT NULL REFERENCES users(id), 68 + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), 69 + 70 + -- The key/path in the S3 bucket 71 + storage_key TEXT NOT NULL 72 + ); 73 + 74 + CREATE TABLE IF NOT EXISTS sessions ( 75 + access_jwt TEXT PRIMARY KEY, 76 + refresh_jwt TEXT NOT NULL UNIQUE, 77 + did TEXT NOT NULL REFERENCES users(did) ON DELETE CASCADE, 78 + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() 79 + ); 80 +
+2
src/api/mod.rs
···
··· 1 + pub mod server; 2 + pub mod repo;
+219
src/api/repo.rs
···
··· 1 + use axum::{ 2 + extract::State, 3 + Json, 4 + response::{IntoResponse, Response}, 5 + http::StatusCode, 6 + }; 7 + use serde::{Deserialize, Serialize}; 8 + use serde_json::json; 9 + use crate::state::AppState; 10 + use chrono::Utc; 11 + use sqlx::Row; 12 + use cid::Cid; 13 + use std::str::FromStr; 14 + use jacquard_repo::{mst::Mst, commit::Commit, storage::BlockStore}; 15 + use jacquard::types::{string::{Nsid, Tid}, did::Did, integer::LimitedU32}; 16 + use tracing::error; 17 + use std::sync::Arc; 18 + 19 + #[derive(Deserialize)] 20 + #[allow(dead_code)] 21 + pub struct CreateRecordInput { 22 + pub repo: String, 23 + pub collection: String, 24 + pub rkey: Option<String>, 25 + pub validate: Option<bool>, 26 + pub record: serde_json::Value, 27 + #[serde(rename = "swapCommit")] 28 + pub swap_commit: Option<String>, 29 + } 30 + 31 + #[derive(Serialize)] 32 + #[serde(rename_all = "camelCase")] 33 + pub struct CreateRecordOutput { 34 + pub uri: String, 35 + pub cid: String, 36 + } 37 + 38 + pub async fn create_record( 39 + State(state): State<AppState>, 40 + headers: axum::http::HeaderMap, 41 + Json(input): Json<CreateRecordInput>, 42 + ) -> Response { 43 + let auth_header = headers.get("Authorization"); 44 + if auth_header.is_none() { 45 + return (StatusCode::UNAUTHORIZED, Json(json!({"error": "AuthenticationRequired"}))).into_response(); 46 + } 47 + let token = auth_header.unwrap().to_str().unwrap_or("").replace("Bearer ", ""); 48 + 49 + if let Err(_) = crate::auth::verify_token(&token) { 50 + return (StatusCode::UNAUTHORIZED, Json(json!({"error": "AuthenticationFailed", "message": "Invalid token"}))).into_response(); 51 + } 52 + 53 + let session = sqlx::query("SELECT did FROM sessions WHERE access_jwt = $1") 54 + .bind(&token) 55 + .fetch_optional(&state.db) 56 + .await 57 + .unwrap_or(None); 58 + 59 + let did = match session { 60 + Some(row) => row.get::<String, _>("did"), 61 + None => return (StatusCode::UNAUTHORIZED, Json(json!({"error": "AuthenticationFailed"}))).into_response(), 62 + }; 63 + 64 + if input.repo != did { 65 + return (StatusCode::FORBIDDEN, Json(json!({"error": "InvalidRepo", "message": "Repo does not match authenticated user"}))).into_response(); 66 + } 67 + 68 + let user_query = sqlx::query("SELECT id FROM users WHERE did = $1") 69 + .bind(&did) 70 + .fetch_optional(&state.db) 71 + .await; 72 + 73 + let user_id: uuid::Uuid = match user_query { 74 + Ok(Some(row)) => row.get("id"), 75 + _ => return (StatusCode::INTERNAL_SERVER_ERROR, Json(json!({"error": "InternalError", "message": "User not found"}))).into_response(), 76 + }; 77 + 78 + let repo_root_query = sqlx::query("SELECT repo_root_cid FROM repos WHERE user_id = $1") 79 + .bind(user_id) 80 + .fetch_optional(&state.db) 81 + .await; 82 + 83 + let current_root_cid = match repo_root_query { 84 + Ok(Some(row)) => { 85 + let cid_str: String = row.get("repo_root_cid"); 86 + Cid::from_str(&cid_str).ok() 87 + }, 88 + _ => None, 89 + }; 90 + 91 + if current_root_cid.is_none() { 92 + error!("Repo root not found for user {}", did); 93 + return (StatusCode::INTERNAL_SERVER_ERROR, Json(json!({"error": "InternalError", "message": "Repo root not found"}))).into_response(); 94 + } 95 + let current_root_cid = current_root_cid.unwrap(); 96 + 97 + let commit_bytes = match state.block_store.get(&current_root_cid).await { 98 + Ok(Some(b)) => b, 99 + Ok(None) => { 100 + error!("Commit block not found: {}", current_root_cid); 101 + return (StatusCode::INTERNAL_SERVER_ERROR, Json(json!({"error": "InternalError"}))).into_response(); 102 + }, 103 + Err(e) => { 104 + error!("Failed to load commit block: {:?}", e); 105 + return (StatusCode::INTERNAL_SERVER_ERROR, Json(json!({"error": "InternalError"}))).into_response(); 106 + } 107 + }; 108 + 109 + let commit = match Commit::from_cbor(&commit_bytes) { 110 + Ok(c) => c, 111 + Err(e) => { 112 + error!("Failed to parse commit: {:?}", e); 113 + return (StatusCode::INTERNAL_SERVER_ERROR, Json(json!({"error": "InternalError"}))).into_response(); 114 + } 115 + }; 116 + 117 + let mst_root = commit.data; 118 + let store = Arc::new(state.block_store.clone()); 119 + let mst = Mst::load(store.clone(), mst_root, None); 120 + 121 + let collection_nsid = match input.collection.parse::<Nsid>() { 122 + Ok(n) => n, 123 + Err(_) => return (StatusCode::BAD_REQUEST, Json(json!({"error": "InvalidCollection"}))).into_response(), 124 + }; 125 + 126 + let rkey = input.rkey.unwrap_or_else(|| { 127 + Utc::now().format("%Y%m%d%H%M%S%f").to_string() 128 + }); 129 + 130 + let mut record_bytes = Vec::new(); 131 + if let Err(e) = serde_ipld_dagcbor::to_writer(&mut record_bytes, &input.record) { 132 + error!("Error serializing record: {:?}", e); 133 + return (StatusCode::BAD_REQUEST, Json(json!({"error": "InvalidRecord", "message": "Failed to serialize record"}))).into_response(); 134 + } 135 + 136 + let record_cid = match state.block_store.put(&record_bytes).await { 137 + Ok(c) => c, 138 + Err(e) => { 139 + error!("Failed to save record block: {:?}", e); 140 + return (StatusCode::INTERNAL_SERVER_ERROR, Json(json!({"error": "InternalError"}))).into_response(); 141 + } 142 + }; 143 + 144 + let key = format!("{}/{}", collection_nsid, rkey); 145 + if let Err(e) = mst.update(&key, record_cid).await { 146 + error!("Failed to update MST: {:?}", e); 147 + return (StatusCode::INTERNAL_SERVER_ERROR, Json(json!({"error": "InternalError"}))).into_response(); 148 + } 149 + 150 + let new_mst_root = match mst.root().await { 151 + Ok(c) => c, 152 + Err(e) => { 153 + error!("Failed to get new MST root: {:?}", e); 154 + return (StatusCode::INTERNAL_SERVER_ERROR, Json(json!({"error": "InternalError"}))).into_response(); 155 + } 156 + }; 157 + 158 + let did_obj = match Did::new(&did) { 159 + Ok(d) => d, 160 + Err(_) => return (StatusCode::INTERNAL_SERVER_ERROR, Json(json!({"error": "InternalError", "message": "Invalid DID"}))).into_response(), 161 + }; 162 + 163 + let rev = Tid::now(LimitedU32::MIN); 164 + 165 + let new_commit = Commit::new_unsigned( 166 + did_obj, 167 + new_mst_root, 168 + rev, 169 + Some(current_root_cid) 170 + ); 171 + 172 + let new_commit_bytes = match new_commit.to_cbor() { 173 + Ok(b) => b, 174 + Err(e) => { 175 + error!("Failed to serialize new commit: {:?}", e); 176 + return (StatusCode::INTERNAL_SERVER_ERROR, Json(json!({"error": "InternalError"}))).into_response(); 177 + } 178 + }; 179 + 180 + let new_root_cid = match state.block_store.put(&new_commit_bytes).await { 181 + Ok(c) => c, 182 + Err(e) => { 183 + error!("Failed to save new commit: {:?}", e); 184 + return (StatusCode::INTERNAL_SERVER_ERROR, Json(json!({"error": "InternalError"}))).into_response(); 185 + } 186 + }; 187 + 188 + let update_repo = sqlx::query("UPDATE repos SET repo_root_cid = $1 WHERE user_id = $2") 189 + .bind(new_root_cid.to_string()) 190 + .bind(user_id) 191 + .execute(&state.db) 192 + .await; 193 + 194 + if let Err(e) = update_repo { 195 + error!("Failed to update repo root in DB: {:?}", e); 196 + return (StatusCode::INTERNAL_SERVER_ERROR, Json(json!({"error": "InternalError"}))).into_response(); 197 + } 198 + 199 + let record_insert = sqlx::query( 200 + "INSERT INTO records (repo_id, collection, rkey, record_cid) VALUES ($1, $2, $3, $4) 201 + ON CONFLICT (repo_id, collection, rkey) DO UPDATE SET record_cid = $4, created_at = NOW()" 202 + ) 203 + .bind(user_id) 204 + .bind(&input.collection) 205 + .bind(&rkey) 206 + .bind(record_cid.to_string()) 207 + .execute(&state.db) 208 + .await; 209 + 210 + if let Err(e) = record_insert { 211 + error!("Error inserting record index: {:?}", e); 212 + } 213 + 214 + let output = CreateRecordOutput { 215 + uri: format!("at://{}/{}/{}", input.repo, input.collection, rkey), 216 + cid: record_cid.to_string(), 217 + }; 218 + (StatusCode::OK, Json(output)).into_response() 219 + }
+483
src/api/server.rs
···
··· 1 + use axum::{ 2 + extract::State, 3 + Json, 4 + response::{IntoResponse, Response}, 5 + http::StatusCode, 6 + }; 7 + use serde::{Deserialize, Serialize}; 8 + use serde_json::json; 9 + use crate::state::AppState; 10 + use sqlx::Row; 11 + use bcrypt::{hash, verify, DEFAULT_COST}; 12 + use tracing::{info, error, warn}; 13 + use jacquard_repo::{mst::Mst, commit::Commit, storage::BlockStore}; 14 + use jacquard::types::{string::Tid, did::Did, integer::LimitedU32}; 15 + use std::sync::Arc; 16 + 17 + #[derive(Deserialize)] 18 + pub struct CreateAccountInput { 19 + pub handle: String, 20 + pub email: String, 21 + pub password: String, 22 + #[serde(rename = "inviteCode")] 23 + pub invite_code: Option<String>, 24 + } 25 + 26 + #[derive(Serialize)] 27 + #[serde(rename_all = "camelCase")] 28 + pub struct CreateAccountOutput { 29 + pub access_jwt: String, 30 + pub refresh_jwt: String, 31 + pub handle: String, 32 + pub did: String, 33 + } 34 + 35 + pub async fn create_account( 36 + State(state): State<AppState>, 37 + Json(input): Json<CreateAccountInput>, 38 + ) -> Response { 39 + info!("create_account hit: {}", input.handle); 40 + if input.handle.contains('!') || input.handle.contains('@') { 41 + return (StatusCode::BAD_REQUEST, Json(json!({"error": "InvalidHandle", "message": "Handle contains invalid characters"}))).into_response(); 42 + } 43 + 44 + let mut tx = match state.db.begin().await { 45 + Ok(tx) => tx, 46 + Err(e) => { 47 + error!("Error starting transaction: {:?}", e); 48 + return (StatusCode::INTERNAL_SERVER_ERROR, Json(json!({"error": "InternalError"}))).into_response(); 49 + } 50 + }; 51 + 52 + let exists_query = sqlx::query("SELECT 1 FROM users WHERE handle = $1") 53 + .bind(&input.handle) 54 + .fetch_optional(&mut *tx) 55 + .await; 56 + 57 + match exists_query { 58 + Ok(Some(_)) => return (StatusCode::BAD_REQUEST, Json(json!({"error": "HandleTaken", "message": "Handle already taken"}))).into_response(), 59 + Err(e) => { 60 + error!("Error checking handle: {:?}", e); 61 + return (StatusCode::INTERNAL_SERVER_ERROR, Json(json!({"error": "InternalError"}))).into_response(); 62 + } 63 + Ok(None) => {} 64 + } 65 + 66 + if let Some(code) = &input.invite_code { 67 + let invite_query = sqlx::query("SELECT available_uses FROM invite_codes WHERE code = $1 FOR UPDATE") 68 + .bind(code) 69 + .fetch_optional(&mut *tx) 70 + .await; 71 + 72 + match invite_query { 73 + Ok(Some(row)) => { 74 + let uses: i32 = row.get("available_uses"); 75 + if uses <= 0 { 76 + return (StatusCode::BAD_REQUEST, Json(json!({"error": "InvalidInviteCode", "message": "Invite code exhausted"}))).into_response(); 77 + } 78 + 79 + let update_invite = sqlx::query("UPDATE invite_codes SET available_uses = available_uses - 1 WHERE code = $1") 80 + .bind(code) 81 + .execute(&mut *tx) 82 + .await; 83 + 84 + if let Err(e) = update_invite { 85 + error!("Error updating invite code: {:?}", e); 86 + return (StatusCode::INTERNAL_SERVER_ERROR, Json(json!({"error": "InternalError"}))).into_response(); 87 + } 88 + }, 89 + Ok(None) => return (StatusCode::BAD_REQUEST, Json(json!({"error": "InvalidInviteCode", "message": "Invite code not found"}))).into_response(), 90 + Err(e) => { 91 + error!("Error checking invite code: {:?}", e); 92 + return (StatusCode::INTERNAL_SERVER_ERROR, Json(json!({"error": "InternalError"}))).into_response(); 93 + } 94 + } 95 + } 96 + 97 + let did = format!("did:plc:{}", uuid::Uuid::new_v4()); 98 + 99 + let password_hash = match hash(&input.password, DEFAULT_COST) { 100 + Ok(h) => h, 101 + Err(e) => { 102 + error!("Error hashing password: {:?}", e); 103 + return (StatusCode::INTERNAL_SERVER_ERROR, Json(json!({"error": "InternalError"}))).into_response(); 104 + } 105 + }; 106 + 107 + let user_insert = sqlx::query("INSERT INTO users (handle, email, did, password_hash) VALUES ($1, $2, $3, $4) RETURNING id") 108 + .bind(&input.handle) 109 + .bind(&input.email) 110 + .bind(&did) 111 + .bind(&password_hash) 112 + .fetch_one(&mut *tx) 113 + .await; 114 + 115 + let user_id: uuid::Uuid = match user_insert { 116 + Ok(row) => row.get("id"), 117 + Err(e) => { 118 + error!("Error inserting user: {:?}", e); 119 + return (StatusCode::INTERNAL_SERVER_ERROR, Json(json!({"error": "InternalError"}))).into_response(); 120 + } 121 + }; 122 + 123 + let store = Arc::new(state.block_store.clone()); 124 + let mst = Mst::new(store.clone()); 125 + let mst_root = match mst.root().await { 126 + Ok(c) => c, 127 + Err(e) => { 128 + error!("Error creating MST root: {:?}", e); 129 + return (StatusCode::INTERNAL_SERVER_ERROR, Json(json!({"error": "InternalError"}))).into_response(); 130 + } 131 + }; 132 + 133 + let did_obj = match Did::new(&did) { 134 + Ok(d) => d, 135 + Err(_) => return (StatusCode::INTERNAL_SERVER_ERROR, Json(json!({"error": "InternalError", "message": "Invalid DID"}))).into_response(), 136 + }; 137 + 138 + let rev = Tid::now(LimitedU32::MIN); 139 + 140 + let commit = Commit::new_unsigned( 141 + did_obj, 142 + mst_root, 143 + rev, 144 + None 145 + ); 146 + 147 + let commit_bytes = match commit.to_cbor() { 148 + Ok(b) => b, 149 + Err(e) => { 150 + error!("Error serializing genesis commit: {:?}", e); 151 + return (StatusCode::INTERNAL_SERVER_ERROR, Json(json!({"error": "InternalError"}))).into_response(); 152 + } 153 + }; 154 + 155 + let commit_cid = match state.block_store.put(&commit_bytes).await { 156 + Ok(c) => c, 157 + Err(e) => { 158 + error!("Error saving genesis commit: {:?}", e); 159 + return (StatusCode::INTERNAL_SERVER_ERROR, Json(json!({"error": "InternalError"}))).into_response(); 160 + } 161 + }; 162 + 163 + let repo_insert = sqlx::query("INSERT INTO repos (user_id, repo_root_cid) VALUES ($1, $2)") 164 + .bind(user_id) 165 + .bind(commit_cid.to_string()) 166 + .execute(&mut *tx) 167 + .await; 168 + 169 + if let Err(e) = repo_insert { 170 + error!("Error initializing repo: {:?}", e); 171 + return (StatusCode::INTERNAL_SERVER_ERROR, Json(json!({"error": "InternalError"}))).into_response(); 172 + } 173 + 174 + if let Some(code) = &input.invite_code { 175 + let use_insert = sqlx::query("INSERT INTO invite_code_uses (code, used_by_user) VALUES ($1, $2)") 176 + .bind(code) 177 + .bind(user_id) 178 + .execute(&mut *tx) 179 + .await; 180 + 181 + if let Err(e) = use_insert { 182 + error!("Error recording invite usage: {:?}", e); 183 + return (StatusCode::INTERNAL_SERVER_ERROR, Json(json!({"error": "InternalError"}))).into_response(); 184 + } 185 + } 186 + 187 + let access_jwt = crate::auth::create_access_token(&did).map_err(|e| { 188 + error!("Error creating access token: {:?}", e); 189 + (StatusCode::INTERNAL_SERVER_ERROR, Json(json!({"error": "InternalError"}))).into_response() 190 + }); 191 + let access_jwt = match access_jwt { 192 + Ok(t) => t, 193 + Err(r) => return r, 194 + }; 195 + 196 + let refresh_jwt = crate::auth::create_refresh_token(&did).map_err(|e| { 197 + error!("Error creating refresh token: {:?}", e); 198 + (StatusCode::INTERNAL_SERVER_ERROR, Json(json!({"error": "InternalError"}))).into_response() 199 + }); 200 + let refresh_jwt = match refresh_jwt { 201 + Ok(t) => t, 202 + Err(r) => return r, 203 + }; 204 + 205 + let session_insert = sqlx::query("INSERT INTO sessions (access_jwt, refresh_jwt, did) VALUES ($1, $2, $3)") 206 + .bind(&access_jwt) 207 + .bind(&refresh_jwt) 208 + .bind(&did) 209 + .execute(&mut *tx) 210 + .await; 211 + 212 + if let Err(e) = session_insert { 213 + error!("Error inserting session: {:?}", e); 214 + return (StatusCode::INTERNAL_SERVER_ERROR, Json(json!({"error": "InternalError"}))).into_response(); 215 + } 216 + 217 + if let Err(e) = tx.commit().await { 218 + error!("Error committing transaction: {:?}", e); 219 + return (StatusCode::INTERNAL_SERVER_ERROR, Json(json!({"error": "InternalError"}))).into_response(); 220 + } 221 + 222 + (StatusCode::OK, Json(CreateAccountOutput { 223 + access_jwt, 224 + refresh_jwt, 225 + handle: input.handle, 226 + did, 227 + })).into_response() 228 + } 229 + 230 + #[derive(Deserialize)] 231 + pub struct CreateSessionInput { 232 + pub identifier: String, 233 + pub password: String, 234 + } 235 + 236 + #[derive(Serialize)] 237 + #[serde(rename_all = "camelCase")] 238 + pub struct CreateSessionOutput { 239 + pub access_jwt: String, 240 + pub refresh_jwt: String, 241 + pub handle: String, 242 + pub did: String, 243 + } 244 + 245 + pub async fn create_session( 246 + State(state): State<AppState>, 247 + Json(input): Json<CreateSessionInput>, 248 + ) -> Response { 249 + info!("create_session: identifier='{}'", input.identifier); 250 + 251 + let user_row = sqlx::query("SELECT did, handle, password_hash FROM users WHERE handle = $1 OR email = $1") 252 + .bind(&input.identifier) 253 + .fetch_optional(&state.db) 254 + .await; 255 + 256 + match user_row { 257 + Ok(Some(row)) => { 258 + let stored_hash: String = row.get("password_hash"); 259 + 260 + if verify(&input.password, &stored_hash).unwrap_or(false) { 261 + let did: String = row.get("did"); 262 + let handle: String = row.get("handle"); 263 + 264 + let access_jwt = match crate::auth::create_access_token(&did) { 265 + Ok(t) => t, 266 + Err(e) => { 267 + error!("Failed to create access token: {:?}", e); 268 + return (StatusCode::INTERNAL_SERVER_ERROR, Json(json!({"error": "InternalError"}))).into_response(); 269 + } 270 + }; 271 + 272 + let refresh_jwt = match crate::auth::create_refresh_token(&did) { 273 + Ok(t) => t, 274 + Err(e) => { 275 + error!("Failed to create refresh token: {:?}", e); 276 + return (StatusCode::INTERNAL_SERVER_ERROR, Json(json!({"error": "InternalError"}))).into_response(); 277 + } 278 + }; 279 + 280 + let session_insert = sqlx::query("INSERT INTO sessions (access_jwt, refresh_jwt, did) VALUES ($1, $2, $3)") 281 + .bind(&access_jwt) 282 + .bind(&refresh_jwt) 283 + .bind(&did) 284 + .execute(&state.db) 285 + .await; 286 + 287 + match session_insert { 288 + Ok(_) => { 289 + return (StatusCode::OK, Json(CreateSessionOutput { 290 + access_jwt, 291 + refresh_jwt, 292 + handle, 293 + did, 294 + })).into_response(); 295 + }, 296 + Err(e) => { 297 + error!("Failed to insert session: {:?}", e); 298 + return (StatusCode::INTERNAL_SERVER_ERROR, Json(json!({"error": "InternalError"}))).into_response(); 299 + } 300 + } 301 + } else { 302 + warn!("Password verification failed for identifier: {}", input.identifier); 303 + } 304 + }, 305 + Ok(None) => { 306 + warn!("User not found for identifier: {}", input.identifier); 307 + }, 308 + Err(e) => { 309 + error!("Database error fetching user: {:?}", e); 310 + return (StatusCode::INTERNAL_SERVER_ERROR, Json(json!({"error": "InternalError"}))).into_response(); 311 + } 312 + } 313 + 314 + (StatusCode::UNAUTHORIZED, Json(json!({"error": "AuthenticationFailed", "message": "Invalid identifier or password"}))).into_response() 315 + } 316 + 317 + pub async fn get_session( 318 + State(state): State<AppState>, 319 + headers: axum::http::HeaderMap, 320 + ) -> Response { 321 + let auth_header = headers.get("Authorization"); 322 + if auth_header.is_none() { 323 + return (StatusCode::UNAUTHORIZED, Json(json!({"error": "AuthenticationRequired"}))).into_response(); 324 + } 325 + 326 + let token = auth_header.unwrap().to_str().unwrap_or("").replace("Bearer ", ""); 327 + 328 + if let Err(_) = crate::auth::verify_token(&token) { 329 + return (StatusCode::UNAUTHORIZED, Json(json!({"error": "AuthenticationFailed", "message": "Invalid token"}))).into_response(); 330 + } 331 + 332 + let result = sqlx::query( 333 + r#" 334 + SELECT u.handle, u.did, u.email 335 + FROM sessions s 336 + JOIN users u ON s.did = u.did 337 + WHERE s.access_jwt = $1 338 + "# 339 + ) 340 + .bind(token) 341 + .fetch_optional(&state.db) 342 + .await; 343 + 344 + match result { 345 + Ok(Some(row)) => { 346 + let handle: String = row.get("handle"); 347 + let did: String = row.get("did"); 348 + let email: String = row.get("email"); 349 + 350 + return (StatusCode::OK, Json(json!({ 351 + "handle": handle, 352 + "did": did, 353 + "email": email, 354 + "didDoc": {} 355 + }))).into_response(); 356 + }, 357 + Ok(None) => { 358 + return (StatusCode::UNAUTHORIZED, Json(json!({"error": "AuthenticationFailed"}))).into_response(); 359 + }, 360 + Err(e) => { 361 + error!("Database error in get_session: {:?}", e); 362 + return (StatusCode::INTERNAL_SERVER_ERROR, Json(json!({"error": "InternalError"}))).into_response(); 363 + } 364 + } 365 + } 366 + 367 + pub async fn delete_session( 368 + State(state): State<AppState>, 369 + headers: axum::http::HeaderMap, 370 + ) -> Response { 371 + let auth_header = headers.get("Authorization"); 372 + if auth_header.is_none() { 373 + return (StatusCode::UNAUTHORIZED, Json(json!({"error": "AuthenticationRequired"}))).into_response(); 374 + } 375 + 376 + let token = auth_header.unwrap().to_str().unwrap_or("").replace("Bearer ", ""); 377 + 378 + let result = sqlx::query("DELETE FROM sessions WHERE access_jwt = $1") 379 + .bind(token) 380 + .execute(&state.db) 381 + .await; 382 + 383 + match result { 384 + Ok(res) => { 385 + if res.rows_affected() > 0 { 386 + return (StatusCode::OK, Json(json!({}))).into_response(); 387 + } 388 + }, 389 + Err(e) => { 390 + error!("Database error in delete_session: {:?}", e); 391 + } 392 + } 393 + 394 + (StatusCode::UNAUTHORIZED, Json(json!({"error": "AuthenticationFailed"}))).into_response() 395 + } 396 + 397 + pub async fn refresh_session( 398 + State(state): State<AppState>, 399 + headers: axum::http::HeaderMap, 400 + ) -> Response { 401 + let auth_header = headers.get("Authorization"); 402 + if auth_header.is_none() { 403 + return (StatusCode::UNAUTHORIZED, Json(json!({"error": "AuthenticationRequired"}))).into_response(); 404 + } 405 + 406 + let refresh_token = auth_header.unwrap().to_str().unwrap_or("").replace("Bearer ", ""); 407 + 408 + if let Err(_) = crate::auth::verify_token(&refresh_token) { 409 + return (StatusCode::UNAUTHORIZED, Json(json!({"error": "AuthenticationFailed", "message": "Invalid refresh token"}))).into_response(); 410 + } 411 + 412 + let session = sqlx::query("SELECT did FROM sessions WHERE refresh_jwt = $1") 413 + .bind(&refresh_token) 414 + .fetch_optional(&state.db) 415 + .await; 416 + 417 + match session { 418 + Ok(Some(session_row)) => { 419 + let did: String = session_row.get("did"); 420 + let new_access_jwt = match crate::auth::create_access_token(&did) { 421 + Ok(t) => t, 422 + Err(e) => { 423 + error!("Failed to create access token: {:?}", e); 424 + return (StatusCode::INTERNAL_SERVER_ERROR, Json(json!({"error": "InternalError"}))).into_response(); 425 + } 426 + }; 427 + let new_refresh_jwt = match crate::auth::create_refresh_token(&did) { 428 + Ok(t) => t, 429 + Err(e) => { 430 + error!("Failed to create refresh token: {:?}", e); 431 + return (StatusCode::INTERNAL_SERVER_ERROR, Json(json!({"error": "InternalError"}))).into_response(); 432 + } 433 + }; 434 + 435 + let update = sqlx::query("UPDATE sessions SET access_jwt = $1, refresh_jwt = $2 WHERE refresh_jwt = $3") 436 + .bind(&new_access_jwt) 437 + .bind(&new_refresh_jwt) 438 + .bind(&refresh_token) 439 + .execute(&state.db) 440 + .await; 441 + 442 + match update { 443 + Ok(_) => { 444 + let user = sqlx::query("SELECT handle FROM users WHERE did = $1") 445 + .bind(&did) 446 + .fetch_optional(&state.db) 447 + .await; 448 + 449 + match user { 450 + Ok(Some(u)) => { 451 + let handle: String = u.get("handle"); 452 + return (StatusCode::OK, Json(json!({ 453 + "accessJwt": new_access_jwt, 454 + "refreshJwt": new_refresh_jwt, 455 + "handle": handle, 456 + "did": did 457 + }))).into_response(); 458 + }, 459 + Ok(None) => { 460 + error!("User not found for existing session: {}", did); 461 + return (StatusCode::INTERNAL_SERVER_ERROR, Json(json!({"error": "InternalError"}))).into_response(); 462 + }, 463 + Err(e) => { 464 + error!("Database error fetching user: {:?}", e); 465 + return (StatusCode::INTERNAL_SERVER_ERROR, Json(json!({"error": "InternalError"}))).into_response(); 466 + } 467 + } 468 + }, 469 + Err(e) => { 470 + error!("Database error updating session: {:?}", e); 471 + return (StatusCode::INTERNAL_SERVER_ERROR, Json(json!({"error": "InternalError"}))).into_response(); 472 + } 473 + } 474 + }, 475 + Ok(None) => { 476 + return (StatusCode::UNAUTHORIZED, Json(json!({"error": "AuthenticationFailed", "message": "Invalid refresh token"}))).into_response(); 477 + }, 478 + Err(e) => { 479 + error!("Database error fetching session: {:?}", e); 480 + return (StatusCode::INTERNAL_SERVER_ERROR, Json(json!({"error": "InternalError"}))).into_response(); 481 + } 482 + } 483 + }
+59
src/auth.rs
···
··· 1 + use jsonwebtoken::{encode, decode, Header, Validation, EncodingKey, DecodingKey, TokenData}; 2 + use serde::{Deserialize, Serialize}; 3 + use chrono::{Utc, Duration}; 4 + use std::env; 5 + 6 + #[derive(Debug, Serialize, Deserialize)] 7 + pub struct Claims { 8 + // DID type shit 9 + pub sub: String, 10 + pub exp: usize, 11 + pub iat: usize, 12 + pub scope: String, 13 + pub jti: String, 14 + } 15 + 16 + pub fn create_access_token(did: &str) -> Result<String, jsonwebtoken::errors::Error> { 17 + let secret = env::var("JWT_SECRET").unwrap_or_else(|_| "secret".to_string()); 18 + let expiration = Utc::now() 19 + .checked_add_signed(Duration::minutes(15)) 20 + .expect("valid timestamp") 21 + .timestamp(); 22 + 23 + let claims = Claims { 24 + sub: did.to_owned(), 25 + exp: expiration as usize, 26 + iat: Utc::now().timestamp() as usize, 27 + scope: "access".to_string(), 28 + jti: uuid::Uuid::new_v4().to_string(), 29 + }; 30 + 31 + encode(&Header::default(), &claims, &EncodingKey::from_secret(secret.as_ref())) 32 + } 33 + 34 + pub fn create_refresh_token(did: &str) -> Result<String, jsonwebtoken::errors::Error> { 35 + let secret = env::var("JWT_SECRET").unwrap_or_else(|_| "secret".to_string()); 36 + let expiration = Utc::now() 37 + .checked_add_signed(Duration::days(7)) 38 + .expect("valid timestamp") 39 + .timestamp(); 40 + 41 + let claims = Claims { 42 + sub: did.to_owned(), 43 + exp: expiration as usize, 44 + iat: Utc::now().timestamp() as usize, 45 + scope: "refresh".to_string(), 46 + jti: uuid::Uuid::new_v4().to_string(), 47 + }; 48 + 49 + encode(&Header::default(), &claims, &EncodingKey::from_secret(secret.as_ref())) 50 + } 51 + 52 + pub fn verify_token(token: &str) -> Result<TokenData<Claims>, jsonwebtoken::errors::Error> { 53 + let secret = env::var("JWT_SECRET").unwrap_or_else(|_| "secret".to_string()); 54 + decode::<Claims>( 55 + token, 56 + &DecodingKey::from_secret(secret.as_ref()), 57 + &Validation::default(), 58 + ) 59 + }
+73
src/main.rs
···
··· 1 + mod api; 2 + mod state; 3 + mod auth; 4 + mod repo; 5 + 6 + use axum::{ 7 + extract::State, 8 + routing::{get, post}, 9 + Router, 10 + Json, 11 + response::IntoResponse, 12 + http::StatusCode, 13 + }; 14 + use serde_json::json; 15 + use std::net::SocketAddr; 16 + use state::AppState; 17 + use tracing::{info, error}; 18 + 19 + #[tokio::main] 20 + async fn main() { 21 + dotenvy::dotenv().ok(); 22 + tracing_subscriber::fmt::init(); 23 + 24 + let database_url = std::env::var("DATABASE_URL").expect("DATABASE_URL must be set"); 25 + 26 + let pool = sqlx::postgres::PgPoolOptions::new() 27 + .max_connections(5) 28 + .connect(&database_url) 29 + .await 30 + .expect("Failed to connect to Postgres"); 31 + 32 + sqlx::migrate!("./migrations") 33 + .run(&pool) 34 + .await 35 + .expect("Failed to run migrations"); 36 + 37 + let state = AppState::new(pool); 38 + 39 + let app = Router::new() 40 + .route("/health", get(health)) 41 + .route("/xrpc/com.atproto.server.describeServer", get(describe_server)) 42 + .route("/xrpc/com.atproto.server.createAccount", post(api::server::create_account)) 43 + .route("/xrpc/com.atproto.server.createSession", post(api::server::create_session)) 44 + .route("/xrpc/com.atproto.server.getSession", get(api::server::get_session)) 45 + .route("/xrpc/com.atproto.server.deleteSession", post(api::server::delete_session)) 46 + .route("/xrpc/com.atproto.server.refreshSession", post(api::server::refresh_session)) 47 + .route("/xrpc/com.atproto.repo.createRecord", post(api::repo::create_record)) 48 + .with_state(state); 49 + 50 + let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); 51 + info!("listening on {}", addr); 52 + let listener = tokio::net::TcpListener::bind(addr).await.unwrap(); 53 + axum::serve(listener, app).await.unwrap(); 54 + } 55 + 56 + async fn health(State(state): State<AppState>) -> impl IntoResponse { 57 + match sqlx::query("SELECT 1").execute(&state.db).await { 58 + Ok(_) => (StatusCode::OK, "OK"), 59 + Err(e) => { 60 + error!("Health check failed: {:?}", e); 61 + (StatusCode::SERVICE_UNAVAILABLE, "Service Unavailable") 62 + } 63 + } 64 + } 65 + 66 + async fn describe_server() -> impl IntoResponse { 67 + let domains_str = std::env::var("AVAILABLE_USER_DOMAINS").unwrap_or_else(|_| "example.com".to_string()); 68 + let domains: Vec<&str> = domains_str.split(',').map(|s| s.trim()).collect(); 69 + 70 + Json(json!({ 71 + "availableUserDomains": domains 72 + })) 73 + }
+94
src/repo/mod.rs
···
··· 1 + use jacquard_repo::storage::BlockStore; 2 + use jacquard_repo::error::RepoError; 3 + use jacquard_repo::repo::CommitData; 4 + use cid::Cid; 5 + use sqlx::{PgPool, Row}; 6 + use bytes::Bytes; 7 + use sha2::{Sha256, Digest}; 8 + use multihash::Multihash; 9 + 10 + #[derive(Clone)] 11 + pub struct PostgresBlockStore { 12 + pool: PgPool, 13 + } 14 + 15 + impl PostgresBlockStore { 16 + pub fn new(pool: PgPool) -> Self { 17 + Self { pool } 18 + } 19 + } 20 + 21 + impl BlockStore for PostgresBlockStore { 22 + async fn get(&self, cid: &Cid) -> Result<Option<Bytes>, RepoError> { 23 + let cid_bytes = cid.to_bytes(); 24 + let row = sqlx::query("SELECT data FROM blocks WHERE cid = $1") 25 + .bind(cid_bytes) 26 + .fetch_optional(&self.pool) 27 + .await 28 + .map_err(|e| RepoError::storage(e))?; 29 + 30 + match row { 31 + Some(row) => { 32 + let data: Vec<u8> = row.get("data"); 33 + Ok(Some(Bytes::from(data))) 34 + }, 35 + None => Ok(None), 36 + } 37 + } 38 + 39 + async fn put(&self, data: &[u8]) -> Result<Cid, RepoError> { 40 + let mut hasher = Sha256::new(); 41 + hasher.update(data); 42 + let hash = hasher.finalize(); 43 + let multihash = Multihash::wrap(0x12, &hash).unwrap(); 44 + let cid = Cid::new_v1(0x71, multihash); 45 + let cid_bytes = cid.to_bytes(); 46 + 47 + sqlx::query("INSERT INTO blocks (cid, data) VALUES ($1, $2) ON CONFLICT (cid) DO NOTHING") 48 + .bind(cid_bytes) 49 + .bind(data) 50 + .execute(&self.pool) 51 + .await 52 + .map_err(|e| RepoError::storage(e))?; 53 + 54 + Ok(cid) 55 + } 56 + 57 + async fn has(&self, cid: &Cid) -> Result<bool, RepoError> { 58 + let cid_bytes = cid.to_bytes(); 59 + let row = sqlx::query("SELECT 1 FROM blocks WHERE cid = $1") 60 + .bind(cid_bytes) 61 + .fetch_optional(&self.pool) 62 + .await 63 + .map_err(|e| RepoError::storage(e))?; 64 + 65 + Ok(row.is_some()) 66 + } 67 + 68 + async fn put_many(&self, blocks: impl IntoIterator<Item = (Cid, Bytes)> + Send) -> Result<(), RepoError> { 69 + let blocks: Vec<_> = blocks.into_iter().collect(); 70 + for (cid, data) in blocks { 71 + let cid_bytes = cid.to_bytes(); 72 + sqlx::query("INSERT INTO blocks (cid, data) VALUES ($1, $2) ON CONFLICT (cid) DO NOTHING") 73 + .bind(cid_bytes) 74 + .bind(data.as_ref()) 75 + .execute(&self.pool) 76 + .await 77 + .map_err(|e| RepoError::storage(e))?; 78 + } 79 + Ok(()) 80 + } 81 + 82 + async fn get_many(&self, cids: &[Cid]) -> Result<Vec<Option<Bytes>>, RepoError> { 83 + let mut results = Vec::new(); 84 + for cid in cids { 85 + results.push(self.get(cid).await?); 86 + } 87 + Ok(results) 88 + } 89 + 90 + async fn apply_commit(&self, commit: CommitData) -> Result<(), RepoError> { 91 + self.put_many(commit.blocks).await?; 92 + Ok(()) 93 + } 94 + }
+15
src/state.rs
···
··· 1 + use sqlx::PgPool; 2 + use crate::repo::PostgresBlockStore; 3 + 4 + #[derive(Clone)] 5 + pub struct AppState { 6 + pub db: PgPool, 7 + pub block_store: PostgresBlockStore, 8 + } 9 + 10 + impl AppState { 11 + pub fn new(db: PgPool) -> Self { 12 + let block_store = PostgresBlockStore::new(db.clone()); 13 + Self { db, block_store } 14 + } 15 + }
+36
tests/actor.rs
···
··· 1 + mod common; 2 + use common::*; 3 + use reqwest::StatusCode; 4 + 5 + #[tokio::test] 6 + async fn test_get_profile() { 7 + let client = client(); 8 + let params = [ 9 + ("actor", AUTH_DID), 10 + ]; 11 + let res = client.get(format!("{}/xrpc/app.bsky.actor.getProfile", BASE_URL)) 12 + .query(&params) 13 + .bearer_auth(AUTH_TOKEN) 14 + .send() 15 + .await 16 + .expect("Failed to send request"); 17 + 18 + assert_eq!(res.status(), StatusCode::OK); 19 + } 20 + 21 + #[tokio::test] 22 + async fn test_search_actors() { 23 + let client = client(); 24 + let params = [ 25 + ("q", "test"), 26 + ("limit", "10"), 27 + ]; 28 + let res = client.get(format!("{}/xrpc/app.bsky.actor.searchActors", BASE_URL)) 29 + .query(&params) 30 + .bearer_auth(AUTH_TOKEN) 31 + .send() 32 + .await 33 + .expect("Failed to send request"); 34 + 35 + assert_eq!(res.status(), StatusCode::OK); 36 + }
+101
tests/common/mod.rs
···
··· 1 + use reqwest::{header, Client, StatusCode}; 2 + use serde_json::{json, Value}; 3 + use chrono::Utc; 4 + #[allow(unused_imports)] 5 + use std::collections::HashMap; 6 + #[allow(unused_imports)] 7 + use std::time::Duration; 8 + 9 + pub const BASE_URL: &str = "http://127.0.0.1:3000"; 10 + #[allow(dead_code)] 11 + pub const AUTH_TOKEN: &str = "test-token"; 12 + #[allow(dead_code)] 13 + pub const BAD_AUTH_TOKEN: &str = "bad-token"; 14 + #[allow(dead_code)] 15 + pub const AUTH_DID: &str = "did:plc:fake"; 16 + #[allow(dead_code)] 17 + pub const TARGET_DID: &str = "did:plc:target"; 18 + 19 + pub fn client() -> Client { 20 + Client::new() 21 + } 22 + 23 + #[allow(dead_code)] 24 + pub async fn upload_test_blob(client: &Client, data: &'static str, mime: &'static str) -> Value { 25 + let res = client.post(format!("{}/xrpc/com.atproto.repo.uploadBlob", BASE_URL)) 26 + .header(header::CONTENT_TYPE, mime) 27 + .bearer_auth(AUTH_TOKEN) 28 + .body(data) 29 + .send() 30 + .await 31 + .expect("Failed to send uploadBlob request"); 32 + 33 + assert_eq!(res.status(), StatusCode::OK, "Failed to upload blob"); 34 + let body: Value = res.json().await.expect("Blob upload response was not JSON"); 35 + body["blob"].clone() 36 + } 37 + 38 + 39 + #[allow(dead_code)] 40 + pub async fn create_test_post( 41 + client: &Client, 42 + text: &str, 43 + reply_to: Option<Value> 44 + ) -> (String, String, String) { 45 + let collection = "app.bsky.feed.post"; 46 + let mut record = json!({ 47 + "$type": collection, 48 + "text": text, 49 + "createdAt": Utc::now().to_rfc3339() 50 + }); 51 + 52 + if let Some(reply_obj) = reply_to { 53 + record["reply"] = reply_obj; 54 + } 55 + 56 + let payload = json!({ 57 + "repo": AUTH_DID, 58 + "collection": collection, 59 + "record": record 60 + }); 61 + 62 + let res = client.post(format!("{}/xrpc/com.atproto.repo.createRecord", BASE_URL)) 63 + .bearer_auth(AUTH_TOKEN) 64 + .json(&payload) 65 + .send() 66 + .await 67 + .expect("Failed to send createRecord"); 68 + 69 + assert_eq!(res.status(), StatusCode::OK, "Failed to create post record"); 70 + let body: Value = res.json().await.expect("createRecord response was not JSON"); 71 + 72 + let uri = body["uri"].as_str().expect("Response had no URI").to_string(); 73 + let cid = body["cid"].as_str().expect("Response had no CID").to_string(); 74 + let rkey = uri.split('/').last().expect("URI was malformed").to_string(); 75 + 76 + (uri, cid, rkey) 77 + } 78 + 79 + pub async fn create_account_and_login(client: &Client) -> (String, String) { 80 + let handle = format!("user_{}", uuid::Uuid::new_v4()); 81 + let payload = json!({ 82 + "handle": handle, 83 + "email": format!("{}@example.com", handle), 84 + "password": "password" 85 + }); 86 + 87 + let res = client.post(format!("{}/xrpc/com.atproto.server.createAccount", BASE_URL)) 88 + .json(&payload) 89 + .send() 90 + .await 91 + .expect("Failed to create account"); 92 + 93 + if res.status() != StatusCode::OK { 94 + panic!("Failed to create account: {:?}", res.text().await); 95 + } 96 + 97 + let body: Value = res.json().await.expect("Invalid JSON"); 98 + let access_jwt = body["accessJwt"].as_str().expect("No accessJwt").to_string(); 99 + let did = body["did"].as_str().expect("No did").to_string(); 100 + (access_jwt, did) 101 + }
+53
tests/feed.rs
···
··· 1 + mod common; 2 + use common::*; 3 + use reqwest::StatusCode; 4 + 5 + use std::collections::HashMap; 6 + 7 + #[tokio::test] 8 + async fn test_get_timeline() { 9 + let client = client(); 10 + let params = [("limit", "30")]; 11 + let res = client.get(format!("{}/xrpc/app.bsky.feed.getTimeline", BASE_URL)) 12 + .query(&params) 13 + .bearer_auth(AUTH_TOKEN) 14 + .send() 15 + .await 16 + .expect("Failed to send request"); 17 + 18 + assert_eq!(res.status(), StatusCode::OK); 19 + } 20 + 21 + #[tokio::test] 22 + async fn test_get_author_feed() { 23 + let client = client(); 24 + let params = [ 25 + ("actor", AUTH_DID), 26 + ("limit", "30") 27 + ]; 28 + let res = client.get(format!("{}/xrpc/app.bsky.feed.getAuthorFeed", BASE_URL)) 29 + .query(&params) 30 + .bearer_auth(AUTH_TOKEN) 31 + .send() 32 + .await 33 + .expect("Failed to send request"); 34 + 35 + assert_eq!(res.status(), StatusCode::OK); 36 + } 37 + 38 + #[tokio::test] 39 + async fn test_get_post_thread() { 40 + let client = client(); 41 + let mut params = HashMap::new(); 42 + params.insert("uri", "at://did:plc:other/app.bsky.feed.post/3k12345"); 43 + params.insert("depth", "5"); 44 + 45 + let res = client.get(format!("{}/xrpc/app.bsky.feed.getPostThread", BASE_URL)) 46 + .query(&params) 47 + .bearer_auth(AUTH_TOKEN) 48 + .send() 49 + .await 50 + .expect("Failed to send request"); 51 + 52 + assert_eq!(res.status(), StatusCode::OK); 53 + }
+68
tests/graph.rs
···
··· 1 + mod common; 2 + use common::*; 3 + use reqwest::StatusCode; 4 + 5 + #[tokio::test] 6 + async fn test_get_follows() { 7 + let client = client(); 8 + let params = [ 9 + ("actor", AUTH_DID), 10 + ]; 11 + let res = client.get(format!("{}/xrpc/app.bsky.graph.getFollows", BASE_URL)) 12 + .query(&params) 13 + .bearer_auth(AUTH_TOKEN) 14 + .send() 15 + .await 16 + .expect("Failed to send request"); 17 + 18 + assert_eq!(res.status(), StatusCode::OK); 19 + } 20 + 21 + #[tokio::test] 22 + async fn test_get_followers() { 23 + let client = client(); 24 + let params = [ 25 + ("actor", AUTH_DID), 26 + ]; 27 + let res = client.get(format!("{}/xrpc/app.bsky.graph.getFollowers", BASE_URL)) 28 + .query(&params) 29 + .bearer_auth(AUTH_TOKEN) 30 + .send() 31 + .await 32 + .expect("Failed to send request"); 33 + 34 + assert_eq!(res.status(), StatusCode::OK); 35 + } 36 + 37 + #[tokio::test] 38 + async fn test_get_mutes() { 39 + let client = client(); 40 + let params = [ 41 + ("limit", "25"), 42 + ]; 43 + let res = client.get(format!("{}/xrpc/app.bsky.graph.getMutes", BASE_URL)) 44 + .query(&params) 45 + .bearer_auth(AUTH_TOKEN) 46 + .send() 47 + .await 48 + .expect("Failed to send request"); 49 + 50 + assert_eq!(res.status(), StatusCode::OK); 51 + } 52 + 53 + #[tokio::test] 54 + // User blocks, ie. not repo blocks ya know 55 + async fn test_get_user_blocks() { 56 + let client = client(); 57 + let params = [ 58 + ("limit", "25"), 59 + ]; 60 + let res = client.get(format!("{}/xrpc/app.bsky.graph.getBlocks", BASE_URL)) 61 + .query(&params) 62 + .bearer_auth(AUTH_TOKEN) 63 + .send() 64 + .await 65 + .expect("Failed to send request"); 66 + 67 + assert_eq!(res.status(), StatusCode::OK); 68 + }
+18
tests/identity.rs
···
··· 1 + mod common; 2 + use common::*; 3 + use reqwest::StatusCode; 4 + 5 + #[tokio::test] 6 + async fn test_resolve_handle() { 7 + let client = client(); 8 + let params = [ 9 + ("handle", "bsky.app"), 10 + ]; 11 + let res = client.get(format!("{}/xrpc/com.atproto.identity.resolveHandle", BASE_URL)) 12 + .query(&params) 13 + .send() 14 + .await 15 + .expect("Failed to send request"); 16 + 17 + assert_eq!(res.status(), StatusCode::OK); 18 + }
+936
tests/lifecycle.rs
···
··· 1 + mod common; 2 + use common::*; 3 + 4 + use reqwest::StatusCode; 5 + use serde_json::{json, Value}; 6 + use chrono::Utc; 7 + use std::time::Duration; 8 + 9 + use reqwest::Client; 10 + #[allow(unused_imports)] 11 + use std::collections::HashMap; 12 + 13 + #[tokio::test] 14 + async fn test_post_crud_lifecycle() { 15 + let client = client(); 16 + let collection = "app.bsky.feed.post"; 17 + 18 + let rkey = format!("e2e_lifecycle_{}", Utc::now().timestamp_millis()); 19 + let now = Utc::now().to_rfc3339(); 20 + 21 + let original_text = "Hello from the lifecycle test!"; 22 + let create_payload = json!({ 23 + "repo": AUTH_DID, 24 + "collection": collection, 25 + "rkey": rkey, 26 + "record": { 27 + "$type": collection, 28 + "text": original_text, 29 + "createdAt": now 30 + } 31 + }); 32 + 33 + let create_res = client.post(format!("{}/xrpc/com.atproto.repo.putRecord", BASE_URL)) 34 + .bearer_auth(AUTH_TOKEN) 35 + .json(&create_payload) 36 + .send() 37 + .await 38 + .expect("Failed to send create request"); 39 + 40 + assert_eq!(create_res.status(), StatusCode::OK, "Failed to create record"); 41 + let create_body: Value = create_res.json().await.expect("create response was not JSON"); 42 + let uri = create_body["uri"].as_str().unwrap(); 43 + 44 + 45 + let params = [ 46 + ("repo", AUTH_DID), 47 + ("collection", collection), 48 + ("rkey", &rkey), 49 + ]; 50 + let get_res = client.get(format!("{}/xrpc/com.atproto.repo.getRecord", BASE_URL)) 51 + .query(&params) 52 + .send() 53 + .await 54 + .expect("Failed to send get request"); 55 + 56 + assert_eq!(get_res.status(), StatusCode::OK, "Failed to get record after create"); 57 + let get_body: Value = get_res.json().await.expect("get response was not JSON"); 58 + assert_eq!(get_body["uri"], uri); 59 + assert_eq!(get_body["value"]["text"], original_text); 60 + 61 + 62 + let updated_text = "This post has been updated."; 63 + let update_payload = json!({ 64 + "repo": AUTH_DID, 65 + "collection": collection, 66 + "rkey": rkey, 67 + "record": { 68 + "$type": collection, 69 + "text": updated_text, 70 + "createdAt": now 71 + } 72 + }); 73 + 74 + let update_res = client.post(format!("{}/xrpc/com.atproto.repo.putRecord", BASE_URL)) 75 + .bearer_auth(AUTH_TOKEN) 76 + .json(&update_payload) 77 + .send() 78 + .await 79 + .expect("Failed to send update request"); 80 + 81 + assert_eq!(update_res.status(), StatusCode::OK, "Failed to update record"); 82 + 83 + 84 + let get_updated_res = client.get(format!("{}/xrpc/com.atproto.repo.getRecord", BASE_URL)) 85 + .query(&params) 86 + .send() 87 + .await 88 + .expect("Failed to send get-after-update request"); 89 + 90 + assert_eq!(get_updated_res.status(), StatusCode::OK, "Failed to get record after update"); 91 + let get_updated_body: Value = get_updated_res.json().await.expect("get-updated response was not JSON"); 92 + assert_eq!(get_updated_body["value"]["text"], updated_text, "Text was not updated"); 93 + 94 + 95 + let delete_payload = json!({ 96 + "repo": AUTH_DID, 97 + "collection": collection, 98 + "rkey": rkey 99 + }); 100 + 101 + let delete_res = client.post(format!("{}/xrpc/com.atproto.repo.deleteRecord", BASE_URL)) 102 + .bearer_auth(AUTH_TOKEN) 103 + .json(&delete_payload) 104 + .send() 105 + .await 106 + .expect("Failed to send delete request"); 107 + 108 + assert_eq!(delete_res.status(), StatusCode::OK, "Failed to delete record"); 109 + 110 + 111 + let get_deleted_res = client.get(format!("{}/xrpc/com.atproto.repo.getRecord", BASE_URL)) 112 + .query(&params) 113 + .send() 114 + .await 115 + .expect("Failed to send get-after-delete request"); 116 + 117 + assert_eq!(get_deleted_res.status(), StatusCode::NOT_FOUND, "Record was found, but it should be deleted"); 118 + } 119 + 120 + #[tokio::test] 121 + async fn test_post_with_image_lifecycle() { 122 + let client = client(); 123 + 124 + let now_str = Utc::now().to_rfc3339(); 125 + let fake_image_data = format!("This is a fake PNG for test at {}", now_str); 126 + 127 + let image_blob = upload_test_blob( 128 + &client, 129 + Box::leak(fake_image_data.into_boxed_str()), 130 + "image/png" 131 + ).await; 132 + 133 + let blob_ref = image_blob["ref"].clone(); 134 + assert!(blob_ref.is_object(), "Blob ref is not an object"); 135 + 136 + 137 + let collection = "app.bsky.feed.post"; 138 + let rkey = format!("e2e_image_post_{}", Utc::now().timestamp_millis()); 139 + 140 + let create_payload = json!({ 141 + "repo": AUTH_DID, 142 + "collection": collection, 143 + "rkey": rkey, 144 + "record": { 145 + "$type": collection, 146 + "text": "Check out this image!", 147 + "createdAt": Utc::now().to_rfc3339(), 148 + "embed": { 149 + "$type": "app.bsky.embed.images", 150 + "images": [ 151 + { 152 + "image": image_blob, 153 + "alt": "A test image" 154 + } 155 + ] 156 + } 157 + } 158 + }); 159 + 160 + let create_res = client.post(format!("{}/xrpc/com.atproto.repo.putRecord", BASE_URL)) 161 + .bearer_auth(AUTH_TOKEN) 162 + .json(&create_payload) 163 + .send() 164 + .await 165 + .expect("Failed to create image post"); 166 + 167 + assert_eq!(create_res.status(), StatusCode::OK, "Failed to create post with image"); 168 + 169 + 170 + let params = [ 171 + ("repo", AUTH_DID), 172 + ("collection", collection), 173 + ("rkey", &rkey), 174 + ]; 175 + let get_res = client.get(format!("{}/xrpc/com.atproto.repo.getRecord", BASE_URL)) 176 + .query(&params) 177 + .send() 178 + .await 179 + .expect("Failed to get image post"); 180 + 181 + assert_eq!(get_res.status(), StatusCode::OK, "Failed to get image post"); 182 + let get_body: Value = get_res.json().await.expect("get image post was not JSON"); 183 + 184 + let embed_image = &get_body["value"]["embed"]["images"][0]["image"]; 185 + assert!(embed_image.is_object(), "Embedded image is missing"); 186 + assert_eq!(embed_image["ref"], blob_ref, "Embedded blob ref does not match uploaded ref"); 187 + } 188 + 189 + #[tokio::test] 190 + async fn test_graph_lifecycle_follow_unfollow() { 191 + let client = client(); 192 + let collection = "app.bsky.graph.follow"; 193 + 194 + let create_payload = json!({ 195 + "repo": AUTH_DID, 196 + "collection": collection, 197 + // "rkey" is omitted, server will generate it right? 198 + "record": { 199 + "$type": collection, 200 + "subject": TARGET_DID, 201 + "createdAt": Utc::now().to_rfc3339() 202 + } 203 + }); 204 + 205 + let create_res = client.post(format!("{}/xrpc/com.atproto.repo.createRecord", BASE_URL)) 206 + .bearer_auth(AUTH_TOKEN) 207 + .json(&create_payload) 208 + .send() 209 + .await 210 + .expect("Failed to send follow createRecord"); 211 + 212 + assert_eq!(create_res.status(), StatusCode::OK, "Failed to create follow record"); 213 + let create_body: Value = create_res.json().await.expect("create follow response was not JSON"); 214 + let follow_uri = create_body["uri"].as_str().expect("Response had no URI"); 215 + 216 + let rkey = follow_uri.split('/').last().expect("URI was malformed"); 217 + 218 + 219 + let params_get_follows = [ 220 + ("actor", AUTH_DID), 221 + ]; 222 + let get_follows_res = client.get(format!("{}/xrpc/app.bsky.graph.getFollows", BASE_URL)) 223 + .query(&params_get_follows) 224 + .bearer_auth(AUTH_TOKEN) 225 + .send() 226 + .await 227 + .expect("Failed to send getFollows"); 228 + 229 + assert_eq!(get_follows_res.status(), StatusCode::OK, "getFollows did not return 200"); 230 + let get_follows_body: Value = get_follows_res.json().await.expect("getFollows response was not JSON"); 231 + 232 + let follows_list = get_follows_body["follows"].as_array().expect("follows key was not an array"); 233 + let is_following = follows_list.iter().any(|actor| { 234 + actor["did"].as_str() == Some(TARGET_DID) 235 + }); 236 + 237 + assert!(is_following, "getFollows list did not contain the target DID"); 238 + 239 + 240 + let delete_payload = json!({ 241 + "repo": AUTH_DID, 242 + "collection": collection, 243 + "rkey": rkey 244 + }); 245 + 246 + let delete_res = client.post(format!("{}/xrpc/com.atproto.repo.deleteRecord", BASE_URL)) 247 + .bearer_auth(AUTH_TOKEN) 248 + .json(&delete_payload) 249 + .send() 250 + .await 251 + .expect("Failed to send unfollow deleteRecord"); 252 + 253 + assert_eq!(delete_res.status(), StatusCode::OK, "Failed to delete follow record"); 254 + 255 + 256 + let get_unfollowed_res = client.get(format!("{}/xrpc/app.bsky.graph.getFollows", BASE_URL)) 257 + .query(&params_get_follows) 258 + .bearer_auth(AUTH_TOKEN) 259 + .send() 260 + .await 261 + .expect("Failed to send getFollows after delete"); 262 + 263 + assert_eq!(get_unfollowed_res.status(), StatusCode::OK, "getFollows (after delete) did not return 200"); 264 + let get_unfollowed_body: Value = get_unfollowed_res.json().await.expect("getFollows (after delete) was not JSON"); 265 + 266 + let follows_list_after = get_unfollowed_body["follows"].as_array().expect("follows key was not an array"); 267 + let is_still_following = follows_list_after.iter().any(|actor| { 268 + actor["did"].as_str() == Some(TARGET_DID) 269 + }); 270 + 271 + assert!(!is_still_following, "getFollows list *still* contains the target DID after unfollow"); 272 + } 273 + 274 + #[tokio::test] 275 + async fn test_list_records_pagination() { 276 + let client = client(); 277 + let collection = "app.bsky.feed.post"; 278 + let mut created_rkeys = Vec::new(); 279 + 280 + for i in 0..3 { 281 + let rkey = format!("e2e_pagination_{}", Utc::now().timestamp_millis()); 282 + let payload = json!({ 283 + "repo": AUTH_DID, 284 + "collection": collection, 285 + "rkey": rkey, 286 + "record": { 287 + "$type": collection, 288 + "text": format!("Pagination test post #{}", i), 289 + "createdAt": Utc::now().to_rfc3339() 290 + } 291 + }); 292 + 293 + let res = client.post(format!("{}/xrpc/com.atproto.repo.putRecord", BASE_URL)) 294 + .bearer_auth(AUTH_TOKEN) 295 + .json(&payload) 296 + .send() 297 + .await 298 + .expect("Failed to create pagination post"); 299 + 300 + assert_eq!(res.status(), StatusCode::OK, "Failed to create post for pagination test"); 301 + created_rkeys.push(rkey); 302 + tokio::time::sleep(Duration::from_millis(10)).await; 303 + } 304 + 305 + let params_page1 = [ 306 + ("repo", AUTH_DID), 307 + ("collection", collection), 308 + ("limit", "2"), 309 + ]; 310 + 311 + let page1_res = client.get(format!("{}/xrpc/com.atproto.repo.listRecords", BASE_URL)) 312 + .query(&params_page1) 313 + .send() 314 + .await 315 + .expect("Failed to send listRecords (page 1)"); 316 + 317 + assert_eq!(page1_res.status(), StatusCode::OK, "listRecords (page 1) failed"); 318 + let page1_body: Value = page1_res.json().await.expect("listRecords (page 1) was not JSON"); 319 + 320 + let page1_records = page1_body["records"].as_array().expect("records was not an array"); 321 + assert_eq!(page1_records.len(), 2, "Page 1 did not return 2 records"); 322 + 323 + let cursor = page1_body["cursor"].as_str().expect("Page 1 did not have a cursor"); 324 + 325 + 326 + let params_page2 = [ 327 + ("repo", AUTH_DID), 328 + ("collection", collection), 329 + ("limit", "2"), 330 + ("cursor", cursor), 331 + ]; 332 + 333 + let page2_res = client.get(format!("{}/xrpc/com.atproto.repo.listRecords", BASE_URL)) 334 + .query(&params_page2) 335 + .send() 336 + .await 337 + .expect("Failed to send listRecords (page 2)"); 338 + 339 + assert_eq!(page2_res.status(), StatusCode::OK, "listRecords (page 2) failed"); 340 + let page2_body: Value = page2_res.json().await.expect("listRecords (page 2) was not JSON"); 341 + 342 + let page2_records = page2_body["records"].as_array().expect("records was not an array"); 343 + assert_eq!(page2_records.len(), 1, "Page 2 did not return 1 record"); 344 + 345 + assert!(page2_body["cursor"].is_null() || page2_body["cursor"].as_str().is_none(), "Page 2 should not have a cursor"); 346 + 347 + 348 + for rkey in created_rkeys { 349 + let delete_payload = json!({ 350 + "repo": AUTH_DID, 351 + "collection": collection, 352 + "rkey": rkey 353 + }); 354 + client.post(format!("{}/xrpc/com.atproto.repo.deleteRecord", BASE_URL)) 355 + .bearer_auth(AUTH_TOKEN) 356 + .json(&delete_payload) 357 + .send() 358 + .await 359 + .expect("Failed to cleanup pagination post"); 360 + } 361 + } 362 + 363 + #[tokio::test] 364 + async fn test_reply_thread_lifecycle() { 365 + let client = client(); 366 + 367 + let (root_uri, root_cid, root_rkey) = create_test_post( 368 + &client, 369 + "This is the root of the thread", 370 + None 371 + ).await; 372 + 373 + 374 + let reply_ref = json!({ 375 + "root": { "uri": root_uri.clone(), "cid": root_cid.clone() }, 376 + "parent": { "uri": root_uri.clone(), "cid": root_cid.clone() } 377 + }); 378 + 379 + let (reply_uri, _reply_cid, reply_rkey) = create_test_post( 380 + &client, 381 + "This is a reply!", 382 + Some(reply_ref) 383 + ).await; 384 + 385 + 386 + let params = [ 387 + ("uri", &root_uri), 388 + ]; 389 + let res = client.get(format!("{}/xrpc/app.bsky.feed.getPostThread", BASE_URL)) 390 + .query(&params) 391 + .bearer_auth(AUTH_TOKEN) 392 + .send() 393 + .await 394 + .expect("Failed to send getPostThread"); 395 + 396 + assert_eq!(res.status(), StatusCode::OK, "getPostThread did not return 200"); 397 + let body: Value = res.json().await.expect("getPostThread response was not JSON"); 398 + 399 + assert_eq!(body["thread"]["$type"], "app.bsky.feed.defs#threadViewPost"); 400 + assert_eq!(body["thread"]["post"]["uri"], root_uri); 401 + 402 + let replies = body["thread"]["replies"].as_array().expect("replies was not an array"); 403 + assert!(!replies.is_empty(), "Replies array is empty, but should contain the reply"); 404 + 405 + let found_reply = replies.iter().find(|r| { 406 + r["post"]["uri"] == reply_uri 407 + }); 408 + 409 + assert!(found_reply.is_some(), "Our specific reply was not found in the thread's replies"); 410 + 411 + 412 + let collection = "app.bsky.feed.post"; 413 + client.post(format!("{}/xrpc/com.atproto.repo.deleteRecord", BASE_URL)) 414 + .bearer_auth(AUTH_TOKEN) 415 + .json(&json!({ "repo": AUTH_DID, "collection": collection, "rkey": reply_rkey })) 416 + .send().await.expect("Failed to delete reply"); 417 + 418 + client.post(format!("{}/xrpc/com.atproto.repo.deleteRecord", BASE_URL)) 419 + .bearer_auth(AUTH_TOKEN) 420 + .json(&json!({ "repo": AUTH_DID, "collection": collection, "rkey": root_rkey })) 421 + .send().await.expect("Failed to delete root post"); 422 + } 423 + 424 + #[tokio::test] 425 + async fn test_account_journey_lifecycle() { 426 + let client = client(); 427 + 428 + let ts = Utc::now().timestamp_millis(); 429 + let handle = format!("e2e-user-{}.test", ts); 430 + let email = format!("e2e-user-{}@test.com", ts); 431 + let password = "e2e-password-123"; 432 + 433 + let create_account_payload = json!({ 434 + "handle": handle, 435 + "email": email, 436 + "password": password 437 + }); 438 + 439 + let create_res = client.post(format!("{}/xrpc/com.atproto.server.createAccount", BASE_URL)) 440 + .json(&create_account_payload) 441 + .send() 442 + .await 443 + .expect("Failed to send createAccount"); 444 + 445 + assert_eq!(create_res.status(), StatusCode::OK, "Failed to create account"); 446 + let create_body: Value = create_res.json().await.expect("createAccount response was not JSON"); 447 + 448 + let new_did = create_body["did"].as_str().expect("Response had no DID").to_string(); 449 + let _new_jwt = create_body["accessJwt"].as_str().expect("Response had no accessJwt").to_string(); 450 + assert_eq!(create_body["handle"], handle); 451 + 452 + 453 + let session_payload = json!({ 454 + "identifier": handle, 455 + "password": password 456 + }); 457 + 458 + let session_res = client.post(format!("{}/xrpc/com.atproto.server.createSession", BASE_URL)) 459 + .json(&session_payload) 460 + .send() 461 + .await 462 + .expect("Failed to send createSession"); 463 + 464 + assert_eq!(session_res.status(), StatusCode::OK, "Failed to create session"); 465 + let session_body: Value = session_res.json().await.expect("createSession response was not JSON"); 466 + 467 + let session_jwt = session_body["accessJwt"].as_str().expect("Session response had no accessJwt").to_string(); 468 + assert_eq!(session_body["did"], new_did); 469 + 470 + 471 + let profile_payload = json!({ 472 + "repo": new_did, 473 + "collection": "app.bsky.actor.profile", 474 + "rkey": "self", // The rkey for a profile is always "self" 475 + "record": { 476 + "$type": "app.bsky.actor.profile", 477 + "displayName": "E2E Test User", 478 + "description": "A user created by the e2e test suite." 479 + } 480 + }); 481 + 482 + let profile_res = client.post(format!("{}/xrpc/com.atproto.repo.putRecord", BASE_URL)) 483 + .bearer_auth(&session_jwt) 484 + .json(&profile_payload) 485 + .send() 486 + .await 487 + .expect("Failed to send putRecord for profile"); 488 + 489 + assert_eq!(profile_res.status(), StatusCode::OK, "Failed to create profile"); 490 + 491 + 492 + let params_get_profile = [ 493 + ("actor", &handle), 494 + ]; 495 + let get_profile_res = client.get(format!("{}/xrpc/app.bsky.actor.getProfile", BASE_URL)) 496 + .query(&params_get_profile) 497 + .send() 498 + .await 499 + .expect("Failed to send getProfile"); 500 + 501 + assert_eq!(get_profile_res.status(), StatusCode::OK, "getProfile did not return 200"); 502 + let profile_body: Value = get_profile_res.json().await.expect("getProfile response was not JSON"); 503 + 504 + assert_eq!(profile_body["did"], new_did); 505 + assert_eq!(profile_body["handle"], handle); 506 + assert_eq!(profile_body["displayName"], "E2E Test User"); 507 + 508 + 509 + let logout_res = client.post(format!("{}/xrpc/com.atproto.server.deleteSession", BASE_URL)) 510 + .bearer_auth(&session_jwt) 511 + .send() 512 + .await 513 + .expect("Failed to send deleteSession"); 514 + 515 + assert_eq!(logout_res.status(), StatusCode::OK, "Failed to delete session"); 516 + 517 + 518 + let get_session_res = client.get(format!("{}/xrpc/com.atproto.server.getSession", BASE_URL)) 519 + .bearer_auth(&session_jwt) 520 + .send() 521 + .await 522 + .expect("Failed to send getSession"); 523 + 524 + assert_eq!(get_session_res.status(), StatusCode::UNAUTHORIZED, "Session was still valid after logout"); 525 + } 526 + 527 + async fn setup_new_user(handle_prefix: &str) -> (String, String) { 528 + let client = client(); 529 + let ts = Utc::now().timestamp_millis(); 530 + let handle = format!("{}-{}.test", handle_prefix, ts); 531 + let email = format!("{}-{}@test.com", handle_prefix, ts); 532 + let password = "e2e-password-123"; 533 + 534 + let create_account_payload = json!({ 535 + "handle": handle, 536 + "email": email, 537 + "password": password 538 + }); 539 + let create_res = client.post(format!("{}/xrpc/com.atproto.server.createAccount", BASE_URL)) 540 + .json(&create_account_payload) 541 + .send() 542 + .await 543 + .expect("setup_new_user: Failed to send createAccount"); 544 + assert_eq!(create_res.status(), StatusCode::OK, "setup_new_user: Failed to create account"); 545 + let create_body: Value = create_res.json().await.expect("setup_new_user: createAccount response was not JSON"); 546 + 547 + let new_did = create_body["did"].as_str().expect("setup_new_user: Response had no DID").to_string(); 548 + let new_jwt = create_body["accessJwt"].as_str().expect("setup_new_user: Response had no accessJwt").to_string(); 549 + 550 + let profile_payload = json!({ 551 + "repo": new_did.clone(), 552 + "collection": "app.bsky.actor.profile", 553 + "rkey": "self", 554 + "record": { 555 + "$type": "app.bsky.actor.profile", 556 + "displayName": format!("E2E User {}", handle), 557 + "description": "A user created by the e2e test suite." 558 + } 559 + }); 560 + let profile_res = client.post(format!("{}/xrpc/com.atproto.repo.putRecord", BASE_URL)) 561 + .bearer_auth(&new_jwt) 562 + .json(&profile_payload) 563 + .send() 564 + .await 565 + .expect("setup_new_user: Failed to send putRecord for profile"); 566 + assert_eq!(profile_res.status(), StatusCode::OK, "setup_new_user: Failed to create profile"); 567 + 568 + (new_did, new_jwt) 569 + } 570 + 571 + async fn create_record_as( 572 + client: &Client, 573 + jwt: &str, 574 + did: &str, 575 + collection: &str, 576 + record: Value, 577 + ) -> (String, String) { 578 + let payload = json!({ 579 + "repo": did, 580 + "collection": collection, 581 + "record": record 582 + }); 583 + 584 + let res = client.post(format!("{}/xrpc/com.atproto.repo.createRecord", BASE_URL)) 585 + .bearer_auth(jwt) 586 + .json(&payload) 587 + .send() 588 + .await 589 + .expect("create_record_as: Failed to send createRecord"); 590 + 591 + assert_eq!(res.status(), StatusCode::OK, "create_record_as: Failed to create record"); 592 + let body: Value = res.json().await.expect("create_record_as: response was not JSON"); 593 + 594 + let uri = body["uri"].as_str().expect("create_record_as: Response had no URI").to_string(); 595 + let cid = body["cid"].as_str().expect("create_record_as: Response had no CID").to_string(); 596 + (uri, cid) 597 + } 598 + 599 + async fn delete_record_as( 600 + client: &Client, 601 + jwt: &str, 602 + did: &str, 603 + collection: &str, 604 + rkey: &str, 605 + ) { 606 + let payload = json!({ 607 + "repo": did, 608 + "collection": collection, 609 + "rkey": rkey 610 + }); 611 + 612 + let res = client.post(format!("{}/xrpc/com.atproto.repo.deleteRecord", BASE_URL)) 613 + .bearer_auth(jwt) 614 + .json(&payload) 615 + .send() 616 + .await 617 + .expect("delete_record_as: Failed to send deleteRecord"); 618 + 619 + assert_eq!(res.status(), StatusCode::OK, "delete_record_as: Failed to delete record"); 620 + } 621 + 622 + 623 + #[tokio::test] 624 + async fn test_notification_lifecycle() { 625 + let client = client(); 626 + 627 + let (user_a_did, user_a_jwt) = setup_new_user("user-a-notif").await; 628 + let (user_b_did, user_b_jwt) = setup_new_user("user-b-notif").await; 629 + 630 + let (post_uri, post_cid) = create_record_as( 631 + &client, 632 + &user_a_jwt, 633 + &user_a_did, 634 + "app.bsky.feed.post", 635 + json!({ 636 + "$type": "app.bsky.feed.post", 637 + "text": "A post to be notified about", 638 + "createdAt": Utc::now().to_rfc3339() 639 + }), 640 + ).await; 641 + let post_ref = json!({ "uri": post_uri, "cid": post_cid }); 642 + 643 + let count_res_1 = client.get(format!("{}/xrpc/app.bsky.notification.getUnreadCount", BASE_URL)) 644 + .bearer_auth(&user_a_jwt) 645 + .send().await.expect("getUnreadCount 1 failed"); 646 + let count_body_1: Value = count_res_1.json().await.expect("count 1 not json"); 647 + assert_eq!(count_body_1["count"], 0, "Initial unread count was not 0"); 648 + 649 + create_record_as( 650 + &client, &user_b_jwt, &user_b_did, 651 + "app.bsky.graph.follow", 652 + json!({ 653 + "$type": "app.bsky.graph.follow", 654 + "subject": user_a_did, 655 + "createdAt": Utc::now().to_rfc3339() 656 + }), 657 + ).await; 658 + create_record_as( 659 + &client, &user_b_jwt, &user_b_did, 660 + "app.bsky.feed.like", 661 + json!({ 662 + "$type": "app.bsky.feed.like", 663 + "subject": post_ref, 664 + "createdAt": Utc::now().to_rfc3339() 665 + }), 666 + ).await; 667 + create_record_as( 668 + &client, &user_b_jwt, &user_b_did, 669 + "app.bsky.feed.post", 670 + json!({ 671 + "$type": "app.bsky.feed.post", 672 + "text": "This is a reply!", 673 + "reply": { "root": post_ref.clone(), "parent": post_ref.clone() }, 674 + "createdAt": Utc::now().to_rfc3339() 675 + }), 676 + ).await; 677 + 678 + tokio::time::sleep(Duration::from_millis(500)).await; 679 + 680 + let count_res_2 = client.get(format!("{}/xrpc/app.bsky.notification.getUnreadCount", BASE_URL)) 681 + .bearer_auth(&user_a_jwt) 682 + .send().await.expect("getUnreadCount 2 failed"); 683 + let count_body_2: Value = count_res_2.json().await.expect("count 2 not json"); 684 + assert_eq!(count_body_2["count"], 3, "Unread count was not 3 after actions"); 685 + 686 + let list_res = client.get(format!("{}/xrpc/app.bsky.notification.listNotifications", BASE_URL)) 687 + .bearer_auth(&user_a_jwt) 688 + .send().await.expect("listNotifications failed"); 689 + let list_body: Value = list_res.json().await.expect("list not json"); 690 + 691 + let notifs = list_body["notifications"].as_array().expect("notifications not array"); 692 + assert_eq!(notifs.len(), 3, "Notification list did not have 3 items"); 693 + 694 + let has_follow = notifs.iter().any(|n| n["reason"] == "follow" && n["author"]["did"] == user_b_did); 695 + let has_like = notifs.iter().any(|n| n["reason"] == "like" && n["author"]["did"] == user_b_did); 696 + let has_reply = notifs.iter().any(|n| n["reason"] == "reply" && n["author"]["did"] == user_b_did); 697 + 698 + assert!(has_follow, "Notification list missing 'follow'"); 699 + assert!(has_like, "Notification list missing 'like'"); 700 + assert!(has_reply, "Notification list missing 'reply'"); 701 + 702 + let count_res_3 = client.get(format!("{}/xrpc/app.bsky.notification.getUnreadCount", BASE_URL)) 703 + .bearer_auth(&user_a_jwt) 704 + .send().await.expect("getUnreadCount 3 failed"); 705 + let count_body_3: Value = count_res_3.json().await.expect("count 3 not json"); 706 + assert_eq!(count_body_3["count"], 0, "Unread count was not 0 after list"); 707 + } 708 + 709 + 710 + #[tokio::test] 711 + async fn test_mute_lifecycle_filters_feed() { 712 + let client = client(); 713 + 714 + let (user_a_did, user_a_jwt) = setup_new_user("user-a-mute").await; 715 + let (user_b_did, user_b_jwt) = setup_new_user("user-b-mute").await; 716 + 717 + let (post_uri, _) = create_record_as( 718 + &client, 719 + &user_b_jwt, 720 + &user_b_did, 721 + "app.bsky.feed.post", 722 + json!({ 723 + "$type": "app.bsky.feed.post", 724 + "text": "A post from User B", 725 + "createdAt": Utc::now().to_rfc3339() 726 + }), 727 + ).await; 728 + 729 + let feed_params_1 = [("actor", &user_b_did)]; 730 + let feed_res_1 = client.get(format!("{}/xrpc/app.bsky.feed.getAuthorFeed", BASE_URL)) 731 + .query(&feed_params_1) 732 + .bearer_auth(&user_a_jwt) 733 + .send().await.expect("getAuthorFeed 1 failed"); 734 + let feed_body_1: Value = feed_res_1.json().await.expect("feed 1 not json"); 735 + 736 + let feed_1 = feed_body_1["feed"].as_array().expect("feed 1 not array"); 737 + let found_post_1 = feed_1.iter().any(|p| p["post"]["uri"] == post_uri); 738 + assert!(found_post_1, "User B's post was not in their feed before mute"); 739 + 740 + let (mute_uri, _) = create_record_as( 741 + &client, &user_a_jwt, &user_a_did, 742 + "app.bsky.graph.mute", 743 + json!({ 744 + "$type": "app.bsky.graph.mute", 745 + "subject": user_b_did, 746 + "createdAt": Utc::now().to_rfc3339() 747 + }), 748 + ).await; 749 + let mute_rkey = mute_uri.split('/').last().unwrap(); 750 + 751 + let feed_params_2 = [("actor", &user_b_did)]; 752 + let feed_res_2 = client.get(format!("{}/xrpc/app.bsky.feed.getAuthorFeed", BASE_URL)) 753 + .query(&feed_params_2) 754 + .bearer_auth(&user_a_jwt) 755 + .send().await.expect("getAuthorFeed 2 failed"); 756 + let feed_body_2: Value = feed_res_2.json().await.expect("feed 2 not json"); 757 + 758 + let feed_2 = feed_body_2["feed"].as_array().expect("feed 2 not array"); 759 + assert!(feed_2.is_empty(), "User B's feed was not empty after mute"); 760 + 761 + delete_record_as( 762 + &client, &user_a_jwt, &user_a_did, 763 + "app.bsky.graph.mute", 764 + mute_rkey, 765 + ).await; 766 + 767 + let feed_params_3 = [("actor", &user_b_did)]; 768 + let feed_res_3 = client.get(format!("{}/xrpc/app.bsky.feed.getAuthorFeed", BASE_URL)) 769 + .query(&feed_params_3) 770 + .bearer_auth(&user_a_jwt) 771 + .send().await.expect("getAuthorFeed 3 failed"); 772 + let feed_body_3: Value = feed_res_3.json().await.expect("feed 3 not json"); 773 + 774 + let feed_3 = feed_body_3["feed"].as_array().expect("feed 3 not array"); 775 + let found_post_3 = feed_3.iter().any(|p| p["post"]["uri"] == post_uri); 776 + assert!(found_post_3, "User B's post did not reappear after unmute"); 777 + } 778 + 779 + 780 + #[tokio::test] 781 + async fn test_record_update_conflict_lifecycle() { 782 + let client = client(); 783 + 784 + let (user_did, user_jwt) = setup_new_user("user-conflict").await; 785 + 786 + let get_res = client.get(format!("{}/xrpc/com.atproto.repo.getRecord", BASE_URL)) 787 + .query(&[ 788 + ("repo", &user_did), 789 + ("collection", &"app.bsky.actor.profile".to_string()), 790 + ("rkey", &"self".to_string()), 791 + ]) 792 + .send().await.expect("getRecord failed"); 793 + let get_body: Value = get_res.json().await.expect("getRecord not json"); 794 + let cid_v1 = get_body["cid"].as_str().expect("Profile v1 had no CID").to_string(); 795 + 796 + let update_payload_v2 = json!({ 797 + "repo": user_did, 798 + "collection": "app.bsky.actor.profile", 799 + "rkey": "self", 800 + "record": { 801 + "$type": "app.bsky.actor.profile", 802 + "displayName": "Updated Name (v2)" 803 + }, 804 + "swapCommit": cid_v1 // <-- Correctly point to v1 805 + }); 806 + let update_res_v2 = client.post(format!("{}/xrpc/com.atproto.repo.putRecord", BASE_URL)) 807 + .bearer_auth(&user_jwt) 808 + .json(&update_payload_v2) 809 + .send().await.expect("putRecord v2 failed"); 810 + assert_eq!(update_res_v2.status(), StatusCode::OK, "v2 update failed"); 811 + let update_body_v2: Value = update_res_v2.json().await.expect("v2 body not json"); 812 + let cid_v2 = update_body_v2["cid"].as_str().expect("v2 response had no CID").to_string(); 813 + 814 + let update_payload_v3_stale = json!({ 815 + "repo": user_did, 816 + "collection": "app.bsky.actor.profile", 817 + "rkey": "self", 818 + "record": { 819 + "$type": "app.bsky.actor.profile", 820 + "displayName": "Stale Update (v3)" 821 + }, 822 + "swapCommit": cid_v1 823 + }); 824 + let update_res_v3_stale = client.post(format!("{}/xrpc/com.atproto.repo.putRecord", BASE_URL)) 825 + .bearer_auth(&user_jwt) 826 + .json(&update_payload_v3_stale) 827 + .send().await.expect("putRecord v3 (stale) failed"); 828 + 829 + assert_eq!( 830 + update_res_v3_stale.status(), 831 + StatusCode::CONFLICT, 832 + "Stale update did not cause a 409 Conflict" 833 + ); 834 + 835 + let update_payload_v3_good = json!({ 836 + "repo": user_did, 837 + "collection": "app.bsky.actor.profile", 838 + "rkey": "self", 839 + "record": { 840 + "$type": "app.bsky.actor.profile", 841 + "displayName": "Good Update (v3)" 842 + }, 843 + "swapCommit": cid_v2 // <-- Correct 844 + }); 845 + let update_res_v3_good = client.post(format!("{}/xrpc/com.atproto.repo.putRecord", BASE_URL)) 846 + .bearer_auth(&user_jwt) 847 + .json(&update_payload_v3_good) 848 + .send().await.expect("putRecord v3 (good) failed"); 849 + 850 + assert_eq!(update_res_v3_good.status(), StatusCode::OK, "v3 (good) update failed"); 851 + } 852 + 853 + 854 + #[tokio::test] 855 + async fn test_complex_thread_deletion_lifecycle() { 856 + let client = client(); 857 + 858 + let (user_a_did, user_a_jwt) = setup_new_user("user-a-thread").await; 859 + let (user_b_did, user_b_jwt) = setup_new_user("user-b-thread").await; 860 + let (user_c_did, user_c_jwt) = setup_new_user("user-c-thread").await; 861 + 862 + let (p1_uri, p1_cid) = create_record_as( 863 + &client, &user_a_jwt, &user_a_did, 864 + "app.bsky.feed.post", 865 + json!({ 866 + "$type": "app.bsky.feed.post", 867 + "text": "P1 (Root)", 868 + "createdAt": Utc::now().to_rfc3339() 869 + }), 870 + ).await; 871 + let p1_ref = json!({ "uri": p1_uri.clone(), "cid": p1_cid.clone() }); 872 + 873 + let (p2_uri, p2_cid) = create_record_as( 874 + &client, &user_b_jwt, &user_b_did, 875 + "app.bsky.feed.post", 876 + json!({ 877 + "$type": "app.bsky.feed.post", 878 + "text": "P2 (Reply)", 879 + "reply": { "root": p1_ref.clone(), "parent": p1_ref.clone() }, 880 + "createdAt": Utc::now().to_rfc3339() 881 + }), 882 + ).await; 883 + let p2_ref = json!({ "uri": p2_uri.clone(), "cid": p2_cid.clone() }); 884 + let p2_rkey = p2_uri.split('/').last().unwrap().to_string(); 885 + 886 + let (p3_uri, _) = create_record_as( 887 + &client, &user_c_jwt, &user_c_did, 888 + "app.bsky.feed.post", 889 + json!({ 890 + "$type": "app.bsky.feed.post", 891 + "text": "P3 (Grandchild)", 892 + "reply": { "root": p1_ref.clone(), "parent": p2_ref.clone() }, 893 + "createdAt": Utc::now().to_rfc3339() 894 + }), 895 + ).await; 896 + 897 + let thread_res_1 = client.get(format!("{}/xrpc/app.bsky.feed.getPostThread", BASE_URL)) 898 + .query(&[("uri", &p1_uri)]) 899 + .bearer_auth(&user_a_jwt) 900 + .send().await.expect("getThread 1 failed"); 901 + let thread_body_1: Value = thread_res_1.json().await.expect("thread 1 not json"); 902 + 903 + let p1_replies = thread_body_1["thread"]["replies"].as_array().unwrap(); 904 + assert_eq!(p1_replies.len(), 1, "P1 should have 1 reply"); 905 + assert_eq!(p1_replies[0]["post"]["uri"], p2_uri, "P1's reply is not P2"); 906 + 907 + let p2_replies = p1_replies[0]["replies"].as_array().unwrap(); 908 + assert_eq!(p2_replies.len(), 1, "P2 should have 1 reply"); 909 + assert_eq!(p2_replies[0]["post"]["uri"], p3_uri, "P2's reply is not P3"); 910 + 911 + delete_record_as( 912 + &client, &user_b_jwt, &user_b_did, 913 + "app.bsky.feed.post", 914 + &p2_rkey, 915 + ).await; 916 + 917 + let thread_res_2 = client.get(format!("{}/xrpc/app.bsky.feed.getPostThread", BASE_URL)) 918 + .query(&[("uri", &p1_uri)]) 919 + .bearer_auth(&user_a_jwt) 920 + .send().await.expect("getThread 2 failed"); 921 + let thread_body_2: Value = thread_res_2.json().await.expect("thread 2 not json"); 922 + 923 + let p1_replies_2 = thread_body_2["thread"]["replies"].as_array().unwrap(); 924 + assert_eq!(p1_replies_2.len(), 1, "P1 should still have 1 reply (the deleted one)"); 925 + 926 + let deleted_post = &p1_replies_2[0]; 927 + assert_eq!( 928 + deleted_post["$type"], "app.bsky.feed.defs#notFoundPost", 929 + "P2 did not appear as a notFoundPost" 930 + ); 931 + assert_eq!(deleted_post["uri"], p2_uri, "notFoundPost URI does not match P2"); 932 + 933 + let p3_reply = deleted_post["replies"].as_array().unwrap(); 934 + assert_eq!(p3_reply.len(), 1, "notFoundPost should still have P3 as a reply"); 935 + assert_eq!(p3_reply[0]["post"]["uri"], p3_uri, "The reply to the deleted post is not P3"); 936 + }
+31
tests/notification.rs
···
··· 1 + mod common; 2 + use common::*; 3 + use reqwest::StatusCode; 4 + 5 + #[tokio::test] 6 + async fn test_list_notifications() { 7 + let client = client(); 8 + let params = [ 9 + ("limit", "30"), 10 + ]; 11 + let res = client.get(format!("{}/xrpc/app.bsky.notification.listNotifications", BASE_URL)) 12 + .query(&params) 13 + .bearer_auth(AUTH_TOKEN) 14 + .send() 15 + .await 16 + .expect("Failed to send request"); 17 + 18 + assert_eq!(res.status(), StatusCode::OK); 19 + } 20 + 21 + #[tokio::test] 22 + async fn test_get_unread_count() { 23 + let client = client(); 24 + let res = client.get(format!("{}/xrpc/app.bsky.notification.getUnreadCount", BASE_URL)) 25 + .bearer_auth(AUTH_TOKEN) 26 + .send() 27 + .await 28 + .expect("Failed to send request"); 29 + 30 + assert_eq!(res.status(), StatusCode::OK); 31 + }
+354
tests/repo.rs
···
··· 1 + mod common; 2 + use common::*; 3 + 4 + use reqwest::{header, StatusCode}; 5 + use serde_json::{json, Value}; 6 + use chrono::Utc; 7 + 8 + #[tokio::test] 9 + #[ignore] 10 + async fn test_get_record() { 11 + let client = client(); 12 + let params = [ 13 + ("repo", "did:plc:12345"), 14 + ("collection", "app.bsky.actor.profile"), 15 + ("rkey", "self"), 16 + ]; 17 + 18 + let res = client.get(format!("{}/xrpc/com.atproto.repo.getRecord", BASE_URL)) 19 + .query(&params) 20 + .send() 21 + .await 22 + .expect("Failed to send request"); 23 + 24 + assert_eq!(res.status(), StatusCode::OK); 25 + let body: Value = res.json().await.expect("Response was not valid JSON"); 26 + assert_eq!(body["value"]["$type"], "app.bsky.actor.profile"); 27 + } 28 + 29 + #[tokio::test] 30 + #[ignore] 31 + async fn test_get_record_not_found() { 32 + let client = client(); 33 + let params = [ 34 + ("repo", "did:plc:12345"), 35 + ("collection", "app.bsky.feed.post"), 36 + ("rkey", "nonexistent"), 37 + ]; 38 + 39 + let res = client.get(format!("{}/xrpc/com.atproto.repo.getRecord", BASE_URL)) 40 + .query(&params) 41 + .send() 42 + .await 43 + .expect("Failed to send request"); 44 + 45 + assert_eq!(res.status(), StatusCode::NOT_FOUND); 46 + let body: Value = res.json().await.expect("Response was not valid JSON"); 47 + assert_eq!(body["error"], "NotFound"); 48 + } 49 + 50 + #[tokio::test] 51 + #[ignore] 52 + async fn test_upload_blob_no_auth() { 53 + let client = client(); 54 + let res = client.post(format!("{}/xrpc/com.atproto.repo.uploadBlob", BASE_URL)) 55 + .header(header::CONTENT_TYPE, "text/plain") 56 + .body("no auth") 57 + .send() 58 + .await 59 + .expect("Failed to send request"); 60 + 61 + assert_eq!(res.status(), StatusCode::UNAUTHORIZED); 62 + let body: Value = res.json().await.expect("Response was not valid JSON"); 63 + assert_eq!(body["error"], "AuthenticationFailed"); 64 + } 65 + 66 + #[tokio::test] 67 + #[ignore] 68 + async fn test_upload_blob_success() { 69 + let client = client(); 70 + let (token, _) = create_account_and_login(&client).await; 71 + let res = client.post(format!("{}/xrpc/com.atproto.repo.uploadBlob", BASE_URL)) 72 + .header(header::CONTENT_TYPE, "text/plain") 73 + .bearer_auth(token) 74 + .body("This is our blob data") 75 + .send() 76 + .await 77 + .expect("Failed to send request"); 78 + 79 + assert_eq!(res.status(), StatusCode::OK); 80 + let body: Value = res.json().await.expect("Response was not valid JSON"); 81 + assert!(body["blob"]["ref"]["$link"].as_str().is_some()); 82 + } 83 + 84 + #[tokio::test] 85 + #[ignore] 86 + async fn test_put_record_no_auth() { 87 + let client = client(); 88 + let payload = json!({ 89 + "repo": "did:plc:123", 90 + "collection": "app.bsky.feed.post", 91 + "rkey": "fake", 92 + "record": {} 93 + }); 94 + 95 + let res = client.post(format!("{}/xrpc/com.atproto.repo.putRecord", BASE_URL)) 96 + .json(&payload) 97 + .send() 98 + .await 99 + .expect("Failed to send request"); 100 + 101 + assert_eq!(res.status(), StatusCode::UNAUTHORIZED); 102 + let body: Value = res.json().await.expect("Response was not valid JSON"); 103 + assert_eq!(body["error"], "AuthenticationFailed"); 104 + } 105 + 106 + #[tokio::test] 107 + #[ignore] 108 + async fn test_put_record_success() { 109 + let client = client(); 110 + let (token, did) = create_account_and_login(&client).await; 111 + let now = Utc::now().to_rfc3339(); 112 + let payload = json!({ 113 + "repo": did, 114 + "collection": "app.bsky.feed.post", 115 + "rkey": "e2e_test_post", 116 + "record": { 117 + "$type": "app.bsky.feed.post", 118 + "text": "Hello from the e2e test script!", 119 + "createdAt": now 120 + } 121 + }); 122 + 123 + let res = client.post(format!("{}/xrpc/com.atproto.repo.putRecord", BASE_URL)) 124 + .bearer_auth(token) 125 + .json(&payload) 126 + .send() 127 + .await 128 + .expect("Failed to send request"); 129 + 130 + assert_eq!(res.status(), StatusCode::OK); 131 + let body: Value = res.json().await.expect("Response was not valid JSON"); 132 + assert!(body.get("uri").is_some()); 133 + assert!(body.get("cid").is_some()); 134 + } 135 + 136 + #[tokio::test] 137 + #[ignore] 138 + async fn test_get_record_missing_params() { 139 + let client = client(); 140 + // Missing `collection` and `rkey` 141 + let params = [ 142 + ("repo", "did:plc:12345"), 143 + ]; 144 + 145 + let res = client.get(format!("{}/xrpc/com.atproto.repo.getRecord", BASE_URL)) 146 + .query(&params) 147 + .send() 148 + .await 149 + .expect("Failed to send request"); 150 + 151 + // This will fail (get 404) until the handler validates query params 152 + assert_eq!(res.status(), StatusCode::BAD_REQUEST, "Expected 400 for missing params"); 153 + } 154 + 155 + #[tokio::test] 156 + #[ignore] 157 + async fn test_upload_blob_bad_token() { 158 + let client = client(); 159 + let res = client.post(format!("{}/xrpc/com.atproto.repo.uploadBlob", BASE_URL)) 160 + .header(header::CONTENT_TYPE, "text/plain") 161 + .bearer_auth(BAD_AUTH_TOKEN) 162 + .body("This is our blob data") 163 + .send() 164 + .await 165 + .expect("Failed to send request"); 166 + 167 + // This *should* pass if the auth stub is working correctly 168 + assert_eq!(res.status(), StatusCode::UNAUTHORIZED); 169 + let body: Value = res.json().await.expect("Response was not valid JSON"); 170 + assert_eq!(body["error"], "AuthenticationFailed"); 171 + } 172 + 173 + #[tokio::test] 174 + #[ignore] 175 + async fn test_put_record_mismatched_repo() { 176 + let client = client(); 177 + let (token, _) = create_account_and_login(&client).await; 178 + let now = Utc::now().to_rfc3339(); 179 + let payload = json!({ 180 + "repo": "did:plc:OTHER-USER", // This does NOT match AUTH_DID 181 + "collection": "app.bsky.feed.post", 182 + "rkey": "e2e_test_post", 183 + "record": { 184 + "$type": "app.bsky.feed.post", 185 + "text": "Hello from the e2e test script!", 186 + "createdAt": now 187 + } 188 + }); 189 + 190 + let res = client.post(format!("{}/xrpc/com.atproto.repo.putRecord", BASE_URL)) 191 + .bearer_auth(token) 192 + .json(&payload) 193 + .send() 194 + .await 195 + .expect("Failed to send request"); 196 + 197 + // This will fail (get 200) until handler validates repo matches auth 198 + assert_eq!(res.status(), StatusCode::FORBIDDEN, "Expected 403 for mismatched repo and auth"); 199 + } 200 + 201 + #[tokio::test] 202 + #[ignore] 203 + async fn test_put_record_invalid_schema() { 204 + let client = client(); 205 + let (token, did) = create_account_and_login(&client).await; 206 + let now = Utc::now().to_rfc3339(); 207 + let payload = json!({ 208 + "repo": did, 209 + "collection": "app.bsky.feed.post", 210 + "rkey": "e2e_test_invalid", 211 + "record": { 212 + "$type": "app.bsky.feed.post", 213 + // "text" field is missing, this is invalid 214 + "createdAt": now 215 + } 216 + }); 217 + 218 + let res = client.post(format!("{}/xrpc/com.atproto.repo.putRecord", BASE_URL)) 219 + .bearer_auth(token) 220 + .json(&payload) 221 + .send() 222 + .await 223 + .expect("Failed to send request"); 224 + 225 + // This will fail (get 200) until handler validates record schema 226 + assert_eq!(res.status(), StatusCode::BAD_REQUEST, "Expected 400 for invalid record schema"); 227 + } 228 + 229 + #[tokio::test] 230 + #[ignore] 231 + async fn test_upload_blob_unsupported_mime_type() { 232 + let client = client(); 233 + let (token, _) = create_account_and_login(&client).await; 234 + let res = client.post(format!("{}/xrpc/com.atproto.repo.uploadBlob", BASE_URL)) 235 + .header(header::CONTENT_TYPE, "application/xml") 236 + .bearer_auth(token) 237 + .body("<xml>not an image</xml>") 238 + .send() 239 + .await 240 + .expect("Failed to send request"); 241 + 242 + // This will fail (get 200) until handler validates mime type 243 + assert_eq!(res.status(), StatusCode::BAD_REQUEST, "Expected 400 for unsupported mime type"); 244 + } 245 + 246 + #[tokio::test] 247 + async fn test_list_records() { 248 + let client = client(); 249 + let (_, did) = create_account_and_login(&client).await; 250 + let params = [ 251 + ("repo", did.as_str()), 252 + ("collection", "app.bsky.feed.post"), 253 + ("limit", "10"), 254 + ]; 255 + let res = client.get(format!("{}/xrpc/com.atproto.repo.listRecords", BASE_URL)) 256 + .query(&params) 257 + .send() 258 + .await 259 + .expect("Failed to send request"); 260 + 261 + assert_eq!(res.status(), StatusCode::OK); 262 + } 263 + 264 + #[tokio::test] 265 + async fn test_delete_record() { 266 + let client = client(); 267 + let (token, did) = create_account_and_login(&client).await; 268 + let payload = json!({ 269 + "repo": did, 270 + "collection": "app.bsky.feed.post", 271 + "rkey": "some_post_to_delete" 272 + }); 273 + let res = client.post(format!("{}/xrpc/com.atproto.repo.deleteRecord", BASE_URL)) 274 + .bearer_auth(token) 275 + .json(&payload) 276 + .send() 277 + .await 278 + .expect("Failed to send request"); 279 + 280 + assert_eq!(res.status(), StatusCode::OK); 281 + } 282 + 283 + #[tokio::test] 284 + async fn test_describe_repo() { 285 + let client = client(); 286 + let (_, did) = create_account_and_login(&client).await; 287 + let params = [ 288 + ("repo", did.as_str()), 289 + ]; 290 + let res = client.get(format!("{}/xrpc/com.atproto.repo.describeRepo", BASE_URL)) 291 + .query(&params) 292 + .send() 293 + .await 294 + .expect("Failed to send request"); 295 + 296 + assert_eq!(res.status(), StatusCode::OK); 297 + } 298 + 299 + #[tokio::test] 300 + async fn test_create_record_success_with_generated_rkey() { 301 + let client = client(); 302 + let (token, did) = create_account_and_login(&client).await; 303 + let payload = json!({ 304 + "repo": did, 305 + "collection": "app.bsky.feed.post", 306 + "record": { 307 + "$type": "app.bsky.feed.post", 308 + "text": "Hello, world!", 309 + "createdAt": "2025-12-02T12:00:00Z" 310 + } 311 + }); 312 + 313 + let res = client.post(format!("{}/xrpc/com.atproto.repo.createRecord", BASE_URL)) 314 + .json(&payload) 315 + .bearer_auth(token) // Assuming auth is required 316 + .send() 317 + .await 318 + .expect("Failed to send request"); 319 + 320 + assert_eq!(res.status(), StatusCode::OK); 321 + let body: Value = res.json().await.expect("Response was not valid JSON"); 322 + let uri = body["uri"].as_str().unwrap(); 323 + assert!(uri.starts_with(&format!("at://{}/app.bsky.feed.post/", did))); 324 + // assert_eq!(body["cid"], "bafyreihy"); // CID is now real 325 + } 326 + 327 + #[tokio::test] 328 + async fn test_create_record_success_with_provided_rkey() { 329 + let client = client(); 330 + let (token, did) = create_account_and_login(&client).await; 331 + let rkey = "custom-rkey"; 332 + let payload = json!({ 333 + "repo": did, 334 + "collection": "app.bsky.feed.post", 335 + "rkey": rkey, 336 + "record": { 337 + "$type": "app.bsky.feed.post", 338 + "text": "Hello, world!", 339 + "createdAt": "2025-12-02T12:00:00Z" 340 + } 341 + }); 342 + 343 + let res = client.post(format!("{}/xrpc/com.atproto.repo.createRecord", BASE_URL)) 344 + .json(&payload) 345 + .bearer_auth(token) // Assuming auth is required 346 + .send() 347 + .await 348 + .expect("Failed to send request"); 349 + 350 + assert_eq!(res.status(), StatusCode::OK); 351 + let body: Value = res.json().await.expect("Response was not valid JSON"); 352 + assert_eq!(body["uri"], format!("at://{}/app.bsky.feed.post/{}", did, rkey)); 353 + // assert_eq!(body["cid"], "bafyreihy"); // CID is now real 354 + }
+164
tests/server.rs
···
··· 1 + mod common; 2 + use common::*; 3 + 4 + use reqwest::StatusCode; 5 + use serde_json::{json, Value}; 6 + 7 + #[tokio::test] 8 + async fn test_health() { 9 + let client = client(); 10 + let res = client.get(format!("{}/health", BASE_URL)) 11 + .send() 12 + .await 13 + .expect("Failed to send request"); 14 + 15 + assert_eq!(res.status(), StatusCode::OK); 16 + assert_eq!(res.text().await.unwrap(), "OK"); 17 + } 18 + 19 + #[tokio::test] 20 + async fn test_describe_server() { 21 + let client = client(); 22 + let res = client.get(format!("{}/xrpc/com.atproto.server.describeServer", BASE_URL)) 23 + .send() 24 + .await 25 + .expect("Failed to send request"); 26 + 27 + assert_eq!(res.status(), StatusCode::OK); 28 + let body: Value = res.json().await.expect("Response was not valid JSON"); 29 + assert!(body.get("availableUserDomains").is_some()); 30 + } 31 + 32 + #[tokio::test] 33 + async fn test_create_session() { 34 + let client = client(); 35 + 36 + let handle = format!("user_{}", uuid::Uuid::new_v4()); 37 + let payload = json!({ 38 + "handle": handle, 39 + "email": format!("{}@example.com", handle), 40 + "password": "password" 41 + }); 42 + let _ = client.post(format!("{}/xrpc/com.atproto.server.createAccount", BASE_URL)) 43 + .json(&payload) 44 + .send() 45 + .await; 46 + 47 + let payload = json!({ 48 + "identifier": handle, 49 + "password": "password" 50 + }); 51 + 52 + let res = client.post(format!("{}/xrpc/com.atproto.server.createSession", BASE_URL)) 53 + .json(&payload) 54 + .send() 55 + .await 56 + .expect("Failed to send request"); 57 + 58 + assert_eq!(res.status(), StatusCode::OK); 59 + let body: Value = res.json().await.expect("Response was not valid JSON"); 60 + assert!(body.get("accessJwt").is_some()); 61 + } 62 + 63 + #[tokio::test] 64 + async fn test_create_session_missing_identifier() { 65 + let client = client(); 66 + let payload = json!({ 67 + "password": "password" 68 + }); 69 + 70 + let res = client.post(format!("{}/xrpc/com.atproto.server.createSession", BASE_URL)) 71 + .json(&payload) 72 + .send() 73 + .await 74 + .expect("Failed to send request"); 75 + 76 + assert!(res.status() == StatusCode::BAD_REQUEST || res.status() == StatusCode::UNPROCESSABLE_ENTITY, 77 + "Expected 400 or 422 for missing identifier, got {}", res.status()); 78 + } 79 + 80 + #[tokio::test] 81 + async fn test_create_account_invalid_handle() { 82 + let client = client(); 83 + let payload = json!({ 84 + "handle": "invalid!handle.com", 85 + "email": "test@example.com", 86 + "password": "password" 87 + }); 88 + 89 + let res = client.post(format!("{}/xrpc/com.atproto.server.createAccount", BASE_URL)) 90 + .json(&payload) 91 + .send() 92 + .await 93 + .expect("Failed to send request"); 94 + 95 + assert_eq!(res.status(), StatusCode::BAD_REQUEST, "Expected 400 for invalid handle chars"); 96 + } 97 + 98 + #[tokio::test] 99 + async fn test_get_session() { 100 + let client = client(); 101 + let res = client.get(format!("{}/xrpc/com.atproto.server.getSession", BASE_URL)) 102 + .bearer_auth(AUTH_TOKEN) 103 + .send() 104 + .await 105 + .expect("Failed to send request"); 106 + 107 + assert_eq!(res.status(), StatusCode::UNAUTHORIZED); 108 + } 109 + 110 + #[tokio::test] 111 + async fn test_refresh_session() { 112 + let client = client(); 113 + 114 + let handle = format!("refresh_user_{}", uuid::Uuid::new_v4()); 115 + let payload = json!({ 116 + "handle": handle, 117 + "email": format!("{}@example.com", handle), 118 + "password": "password" 119 + }); 120 + let _ = client.post(format!("{}/xrpc/com.atproto.server.createAccount", BASE_URL)) 121 + .json(&payload) 122 + .send() 123 + .await; 124 + 125 + let login_payload = json!({ 126 + "identifier": handle, 127 + "password": "password" 128 + }); 129 + let res = client.post(format!("{}/xrpc/com.atproto.server.createSession", BASE_URL)) 130 + .json(&login_payload) 131 + .send() 132 + .await 133 + .expect("Failed to login"); 134 + 135 + assert_eq!(res.status(), StatusCode::OK); 136 + let body: Value = res.json().await.expect("Invalid JSON"); 137 + let refresh_jwt = body["refreshJwt"].as_str().expect("No refreshJwt").to_string(); 138 + let access_jwt = body["accessJwt"].as_str().expect("No accessJwt").to_string(); 139 + 140 + let res = client.post(format!("{}/xrpc/com.atproto.server.refreshSession", BASE_URL)) 141 + .bearer_auth(&refresh_jwt) 142 + .send() 143 + .await 144 + .expect("Failed to refresh"); 145 + 146 + assert_eq!(res.status(), StatusCode::OK); 147 + let body: Value = res.json().await.expect("Invalid JSON"); 148 + assert!(body["accessJwt"].as_str().is_some()); 149 + assert!(body["refreshJwt"].as_str().is_some()); 150 + assert_ne!(body["accessJwt"].as_str().unwrap(), access_jwt); 151 + assert_ne!(body["refreshJwt"].as_str().unwrap(), refresh_jwt); 152 + } 153 + 154 + #[tokio::test] 155 + async fn test_delete_session() { 156 + let client = client(); 157 + let res = client.post(format!("{}/xrpc/com.atproto.server.deleteSession", BASE_URL)) 158 + .bearer_auth(AUTH_TOKEN) 159 + .send() 160 + .await 161 + .expect("Failed to send request"); 162 + 163 + assert_eq!(res.status(), StatusCode::UNAUTHORIZED); 164 + }
+34
tests/sync.rs
···
··· 1 + mod common; 2 + use common::*; 3 + use reqwest::StatusCode; 4 + 5 + #[tokio::test] 6 + async fn test_get_repo() { 7 + let client = client(); 8 + let params = [ 9 + ("did", AUTH_DID), 10 + ]; 11 + let res = client.get(format!("{}/xrpc/com.atproto.sync.getRepo", BASE_URL)) 12 + .query(&params) 13 + .send() 14 + .await 15 + .expect("Failed to send request"); 16 + 17 + assert_eq!(res.status(), StatusCode::OK); 18 + } 19 + 20 + #[tokio::test] 21 + async fn test_get_blocks() { 22 + let client = client(); 23 + let params = [ 24 + ("did", AUTH_DID), 25 + // "cids" would be a list of CIDs 26 + ]; 27 + let res = client.get(format!("{}/xrpc/com.atproto.sync.getBlocks", BASE_URL)) 28 + .query(&params) 29 + .send() 30 + .await 31 + .expect("Failed to send request"); 32 + 33 + assert_eq!(res.status(), StatusCode::OK); 34 + }