slop slop slop sahuuuurrr
0
fork

Configure Feed

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

init

dawn 90ce341f dee2c6cc

+7982 -2
+2
.gitignore
··· 8 8 .gitnexus 9 9 .claude 10 10 /docs-dist 11 + /data 12 + /hydrant.db
+4814
Cargo.lock
··· 3 3 version = 4 4 4 5 5 [[package]] 6 + name = "addr2line" 7 + version = "0.25.1" 8 + source = "registry+https://github.com/rust-lang/crates.io-index" 9 + checksum = "1b5d307320b3181d6d7954e663bd7c774a838b8220fe0593c86d9fb09f498b4b" 10 + dependencies = [ 11 + "gimli", 12 + ] 13 + 14 + [[package]] 15 + name = "adler2" 16 + version = "2.0.1" 17 + source = "registry+https://github.com/rust-lang/crates.io-index" 18 + checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" 19 + 20 + [[package]] 21 + name = "ahash" 22 + version = "0.8.12" 23 + source = "registry+https://github.com/rust-lang/crates.io-index" 24 + checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" 25 + dependencies = [ 26 + "cfg-if", 27 + "getrandom 0.3.4", 28 + "once_cell", 29 + "version_check", 30 + "zerocopy", 31 + ] 32 + 33 + [[package]] 34 + name = "aho-corasick" 35 + version = "1.1.4" 36 + source = "registry+https://github.com/rust-lang/crates.io-index" 37 + checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" 38 + dependencies = [ 39 + "memchr", 40 + ] 41 + 42 + [[package]] 43 + name = "aliasable" 44 + version = "0.1.3" 45 + source = "registry+https://github.com/rust-lang/crates.io-index" 46 + checksum = "250f629c0161ad8107cf89319e990051fae62832fd343083bea452d93e2205fd" 47 + 48 + [[package]] 49 + name = "alloc-no-stdlib" 50 + version = "2.0.4" 51 + source = "registry+https://github.com/rust-lang/crates.io-index" 52 + checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" 53 + 54 + [[package]] 55 + name = "alloc-stdlib" 56 + version = "0.2.2" 57 + source = "registry+https://github.com/rust-lang/crates.io-index" 58 + checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" 59 + dependencies = [ 60 + "alloc-no-stdlib", 61 + ] 62 + 63 + [[package]] 64 + name = "allocator-api2" 65 + version = "0.2.21" 66 + source = "registry+https://github.com/rust-lang/crates.io-index" 67 + checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" 68 + 69 + [[package]] 70 + name = "android_system_properties" 71 + version = "0.1.5" 72 + source = "registry+https://github.com/rust-lang/crates.io-index" 73 + checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" 74 + dependencies = [ 75 + "libc", 76 + ] 77 + 78 + [[package]] 79 + name = "anyhow" 80 + version = "1.0.102" 81 + source = "registry+https://github.com/rust-lang/crates.io-index" 82 + checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" 83 + 84 + [[package]] 6 85 name = "appview" 7 86 version = "0.1.0" 87 + dependencies = [ 88 + "axum", 89 + "base64", 90 + "chrono", 91 + "data-encoding", 92 + "fjall", 93 + "futures", 94 + "hydrant", 95 + "jacquard-api", 96 + "jacquard-common", 97 + "miette", 98 + "reqwest", 99 + "serde", 100 + "serde_json", 101 + "serde_urlencoded", 102 + "tokio", 103 + "tower-http", 104 + "tracing", 105 + "tracing-subscriber", 106 + "urlencoding", 107 + ] 108 + 109 + [[package]] 110 + name = "arc-swap" 111 + version = "1.9.1" 112 + source = "registry+https://github.com/rust-lang/crates.io-index" 113 + checksum = "6a3a1fd6f75306b68087b831f025c712524bcb19aad54e557b1129cfa0a2b207" 114 + dependencies = [ 115 + "rustversion", 116 + ] 117 + 118 + [[package]] 119 + name = "async-compression" 120 + version = "0.4.42" 121 + source = "registry+https://github.com/rust-lang/crates.io-index" 122 + checksum = "e79b3f8a79cccc2898f31920fc69f304859b3bd567490f75ebf51ae1c792a9ac" 123 + dependencies = [ 124 + "compression-codecs", 125 + "compression-core", 126 + "pin-project-lite", 127 + "tokio", 128 + ] 129 + 130 + [[package]] 131 + name = "async-trait" 132 + version = "0.1.89" 133 + source = "registry+https://github.com/rust-lang/crates.io-index" 134 + checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" 135 + dependencies = [ 136 + "proc-macro2", 137 + "quote", 138 + "syn", 139 + ] 140 + 141 + [[package]] 142 + name = "atomic-polyfill" 143 + version = "1.0.3" 144 + source = "registry+https://github.com/rust-lang/crates.io-index" 145 + checksum = "8cf2bce30dfe09ef0bfaef228b9d414faaf7e563035494d7fe092dba54b300f4" 146 + dependencies = [ 147 + "critical-section", 148 + ] 149 + 150 + [[package]] 151 + name = "atomic-waker" 152 + version = "1.1.2" 153 + source = "registry+https://github.com/rust-lang/crates.io-index" 154 + checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" 155 + 156 + [[package]] 157 + name = "autocfg" 158 + version = "1.5.0" 159 + source = "registry+https://github.com/rust-lang/crates.io-index" 160 + checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" 161 + 162 + [[package]] 163 + name = "aws-lc-rs" 164 + version = "1.16.3" 165 + source = "registry+https://github.com/rust-lang/crates.io-index" 166 + checksum = "0ec6fb3fe69024a75fa7e1bfb48aa6cf59706a101658ea01bfd33b2b248a038f" 167 + dependencies = [ 168 + "aws-lc-sys", 169 + "zeroize", 170 + ] 171 + 172 + [[package]] 173 + name = "aws-lc-sys" 174 + version = "0.40.0" 175 + source = "registry+https://github.com/rust-lang/crates.io-index" 176 + checksum = "f50037ee5e1e41e7b8f9d161680a725bd1626cb6f8c7e901f91f942850852fe7" 177 + dependencies = [ 178 + "cc", 179 + "cmake", 180 + "dunce", 181 + "fs_extra", 182 + ] 183 + 184 + [[package]] 185 + name = "axum" 186 + version = "0.8.9" 187 + source = "registry+https://github.com/rust-lang/crates.io-index" 188 + checksum = "31b698c5f9a010f6573133b09e0de5408834d0c82f8d7475a89fc1867a71cd90" 189 + dependencies = [ 190 + "axum-core", 191 + "axum-macros", 192 + "bytes", 193 + "form_urlencoded", 194 + "futures-util", 195 + "http", 196 + "http-body", 197 + "http-body-util", 198 + "hyper", 199 + "hyper-util", 200 + "itoa", 201 + "matchit", 202 + "memchr", 203 + "mime", 204 + "percent-encoding", 205 + "pin-project-lite", 206 + "serde_core", 207 + "serde_json", 208 + "serde_path_to_error", 209 + "serde_urlencoded", 210 + "sync_wrapper", 211 + "tokio", 212 + "tower", 213 + "tower-layer", 214 + "tower-service", 215 + "tracing", 216 + ] 217 + 218 + [[package]] 219 + name = "axum-core" 220 + version = "0.5.6" 221 + source = "registry+https://github.com/rust-lang/crates.io-index" 222 + checksum = "08c78f31d7b1291f7ee735c1c6780ccde7785daae9a9206026862dab7d8792d1" 223 + dependencies = [ 224 + "bytes", 225 + "futures-core", 226 + "http", 227 + "http-body", 228 + "http-body-util", 229 + "mime", 230 + "pin-project-lite", 231 + "sync_wrapper", 232 + "tower-layer", 233 + "tower-service", 234 + "tracing", 235 + ] 236 + 237 + [[package]] 238 + name = "axum-macros" 239 + version = "0.5.1" 240 + source = "registry+https://github.com/rust-lang/crates.io-index" 241 + checksum = "7aa268c23bfbbd2c4363b9cd302a4f504fb2a9dfe7e3451d66f35dd392e20aca" 242 + dependencies = [ 243 + "proc-macro2", 244 + "quote", 245 + "syn", 246 + ] 247 + 248 + [[package]] 249 + name = "axum-tws" 250 + version = "0.6.0" 251 + source = "registry+https://github.com/rust-lang/crates.io-index" 252 + checksum = "08bf5c1e7d60af9632c55c23bbfa208d0fe08aa59fcda87216c9119e30a9d6d4" 253 + dependencies = [ 254 + "axum-core", 255 + "base64", 256 + "bytes", 257 + "futures-util", 258 + "http", 259 + "hyper", 260 + "hyper-util", 261 + "sha1_smol", 262 + "tokio", 263 + "tokio-websockets", 264 + ] 265 + 266 + [[package]] 267 + name = "backtrace" 268 + version = "0.3.76" 269 + source = "registry+https://github.com/rust-lang/crates.io-index" 270 + checksum = "bb531853791a215d7c62a30daf0dde835f381ab5de4589cfe7c649d2cbe92bd6" 271 + dependencies = [ 272 + "addr2line", 273 + "cfg-if", 274 + "libc", 275 + "miniz_oxide", 276 + "object", 277 + "rustc-demangle", 278 + "windows-link", 279 + ] 280 + 281 + [[package]] 282 + name = "backtrace-ext" 283 + version = "0.2.1" 284 + source = "registry+https://github.com/rust-lang/crates.io-index" 285 + checksum = "537beee3be4a18fb023b570f80e3ae28003db9167a751266b259926e25539d50" 286 + dependencies = [ 287 + "backtrace", 288 + ] 289 + 290 + [[package]] 291 + name = "base-x" 292 + version = "0.2.11" 293 + source = "registry+https://github.com/rust-lang/crates.io-index" 294 + checksum = "4cbbc9d0964165b47557570cce6c952866c2678457aca742aafc9fb771d30270" 295 + 296 + [[package]] 297 + name = "base16ct" 298 + version = "0.2.0" 299 + source = "registry+https://github.com/rust-lang/crates.io-index" 300 + checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" 301 + 302 + [[package]] 303 + name = "base256emoji" 304 + version = "1.0.2" 305 + source = "registry+https://github.com/rust-lang/crates.io-index" 306 + checksum = "b5e9430d9a245a77c92176e649af6e275f20839a48389859d1661e9a128d077c" 307 + dependencies = [ 308 + "const-str", 309 + "match-lookup", 310 + ] 311 + 312 + [[package]] 313 + name = "base64" 314 + version = "0.22.1" 315 + source = "registry+https://github.com/rust-lang/crates.io-index" 316 + checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" 317 + 318 + [[package]] 319 + name = "base64ct" 320 + version = "1.8.3" 321 + source = "registry+https://github.com/rust-lang/crates.io-index" 322 + checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" 323 + 324 + [[package]] 325 + name = "bitflags" 326 + version = "2.11.1" 327 + source = "registry+https://github.com/rust-lang/crates.io-index" 328 + checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3" 329 + 330 + [[package]] 331 + name = "block-buffer" 332 + version = "0.10.4" 333 + source = "registry+https://github.com/rust-lang/crates.io-index" 334 + checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" 335 + dependencies = [ 336 + "generic-array", 337 + ] 338 + 339 + [[package]] 340 + name = "bon" 341 + version = "3.9.1" 342 + source = "registry+https://github.com/rust-lang/crates.io-index" 343 + checksum = "f47dbe92550676ee653353c310dfb9cf6ba17ee70396e1f7cf0a2020ad49b2fe" 344 + dependencies = [ 345 + "bon-macros", 346 + "rustversion", 347 + ] 348 + 349 + [[package]] 350 + name = "bon-macros" 351 + version = "3.9.1" 352 + source = "registry+https://github.com/rust-lang/crates.io-index" 353 + checksum = "519bd3116aeeb42d5372c29d982d16d0170d3d4a5ed85fc7dd91642ffff3c67c" 354 + dependencies = [ 355 + "darling", 356 + "ident_case", 357 + "prettyplease", 358 + "proc-macro2", 359 + "quote", 360 + "rustversion", 361 + "syn", 362 + ] 363 + 364 + [[package]] 365 + name = "borrow-or-share" 366 + version = "0.2.4" 367 + source = "registry+https://github.com/rust-lang/crates.io-index" 368 + checksum = "dc0b364ead1874514c8c2855ab558056ebfeb775653e7ae45ff72f28f8f3166c" 369 + 370 + [[package]] 371 + name = "borsh" 372 + version = "1.6.1" 373 + source = "registry+https://github.com/rust-lang/crates.io-index" 374 + checksum = "cfd1e3f8955a5d7de9fab72fc8373fade9fb8a703968cb200ae3dc6cf08e185a" 375 + dependencies = [ 376 + "bytes", 377 + "cfg_aliases", 378 + ] 379 + 380 + [[package]] 381 + name = "brotli" 382 + version = "8.0.2" 383 + source = "registry+https://github.com/rust-lang/crates.io-index" 384 + checksum = "4bd8b9603c7aa97359dbd97ecf258968c95f3adddd6db2f7e7a5bef101c84560" 385 + dependencies = [ 386 + "alloc-no-stdlib", 387 + "alloc-stdlib", 388 + "brotli-decompressor", 389 + ] 390 + 391 + [[package]] 392 + name = "brotli-decompressor" 393 + version = "5.0.0" 394 + source = "registry+https://github.com/rust-lang/crates.io-index" 395 + checksum = "874bb8112abecc98cbd6d81ea4fa7e94fb9449648c93cc89aa40c81c24d7de03" 396 + dependencies = [ 397 + "alloc-no-stdlib", 398 + "alloc-stdlib", 399 + ] 400 + 401 + [[package]] 402 + name = "bumpalo" 403 + version = "3.20.2" 404 + source = "registry+https://github.com/rust-lang/crates.io-index" 405 + checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" 406 + 407 + [[package]] 408 + name = "byteorder" 409 + version = "1.5.0" 410 + source = "registry+https://github.com/rust-lang/crates.io-index" 411 + checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" 412 + 413 + [[package]] 414 + name = "byteorder-lite" 415 + version = "0.1.0" 416 + source = "registry+https://github.com/rust-lang/crates.io-index" 417 + checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" 418 + 419 + [[package]] 420 + name = "bytes" 421 + version = "1.11.1" 422 + source = "registry+https://github.com/rust-lang/crates.io-index" 423 + checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" 424 + dependencies = [ 425 + "serde", 426 + ] 427 + 428 + [[package]] 429 + name = "byteview" 430 + version = "0.10.1" 431 + source = "registry+https://github.com/rust-lang/crates.io-index" 432 + checksum = "1c53ba0f290bfc610084c05582d9c5d421662128fc69f4bf236707af6fd321b9" 433 + 434 + [[package]] 435 + name = "cbor4ii" 436 + version = "0.2.14" 437 + source = "registry+https://github.com/rust-lang/crates.io-index" 438 + checksum = "b544cf8c89359205f4f990d0e6f3828db42df85b5dac95d09157a250eb0749c4" 439 + dependencies = [ 440 + "serde", 441 + ] 442 + 443 + [[package]] 444 + name = "cc" 445 + version = "1.2.61" 446 + source = "registry+https://github.com/rust-lang/crates.io-index" 447 + checksum = "d16d90359e986641506914ba71350897565610e87ce0ad9e6f28569db3dd5c6d" 448 + dependencies = [ 449 + "find-msvc-tools", 450 + "jobserver", 451 + "libc", 452 + "shlex", 453 + ] 454 + 455 + [[package]] 456 + name = "cfg-if" 457 + version = "1.0.4" 458 + source = "registry+https://github.com/rust-lang/crates.io-index" 459 + checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" 460 + 461 + [[package]] 462 + name = "cfg_aliases" 463 + version = "0.2.1" 464 + source = "registry+https://github.com/rust-lang/crates.io-index" 465 + checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" 466 + 467 + [[package]] 468 + name = "chacha20" 469 + version = "0.10.0" 470 + source = "registry+https://github.com/rust-lang/crates.io-index" 471 + checksum = "6f8d983286843e49675a4b7a2d174efe136dc93a18d69130dd18198a6c167601" 472 + dependencies = [ 473 + "cfg-if", 474 + "cpufeatures 0.3.0", 475 + "rand_core 0.10.1", 476 + ] 477 + 478 + [[package]] 479 + name = "chrono" 480 + version = "0.4.44" 481 + source = "registry+https://github.com/rust-lang/crates.io-index" 482 + checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0" 483 + dependencies = [ 484 + "iana-time-zone", 485 + "js-sys", 486 + "num-traits", 487 + "serde", 488 + "wasm-bindgen", 489 + "windows-link", 490 + ] 491 + 492 + [[package]] 493 + name = "ciborium" 494 + version = "0.2.2" 495 + source = "registry+https://github.com/rust-lang/crates.io-index" 496 + checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" 497 + dependencies = [ 498 + "ciborium-io", 499 + "ciborium-ll", 500 + "serde", 501 + ] 502 + 503 + [[package]] 504 + name = "ciborium-io" 505 + version = "0.2.2" 506 + source = "registry+https://github.com/rust-lang/crates.io-index" 507 + checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" 508 + 509 + [[package]] 510 + name = "ciborium-ll" 511 + version = "0.2.2" 512 + source = "registry+https://github.com/rust-lang/crates.io-index" 513 + checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" 514 + dependencies = [ 515 + "ciborium-io", 516 + "half", 517 + ] 518 + 519 + [[package]] 520 + name = "cid" 521 + version = "0.11.2" 522 + source = "registry+https://github.com/rust-lang/crates.io-index" 523 + checksum = "cbb4913a732503de004e94ce7a4e7119ffc55d1727cc9979ac3b52f511e6578c" 524 + dependencies = [ 525 + "multibase", 526 + "multihash", 527 + "no_std_io2", 528 + "serde", 529 + "serde_bytes", 530 + "unsigned-varint 0.8.0", 531 + ] 532 + 533 + [[package]] 534 + name = "cmake" 535 + version = "0.1.58" 536 + source = "registry+https://github.com/rust-lang/crates.io-index" 537 + checksum = "c0f78a02292a74a88ac736019ab962ece0bc380e3f977bf72e376c5d78ff0678" 538 + dependencies = [ 539 + "cc", 540 + ] 541 + 542 + [[package]] 543 + name = "cobs" 544 + version = "0.3.0" 545 + source = "registry+https://github.com/rust-lang/crates.io-index" 546 + checksum = "0fa961b519f0b462e3a3b4a34b64d119eeaca1d59af726fe450bbba07a9fc0a1" 547 + dependencies = [ 548 + "thiserror 2.0.18", 549 + ] 550 + 551 + [[package]] 552 + name = "compare" 553 + version = "0.0.6" 554 + source = "registry+https://github.com/rust-lang/crates.io-index" 555 + checksum = "ea0095f6103c2a8b44acd6fd15960c801dafebf02e21940360833e0673f48ba7" 556 + 557 + [[package]] 558 + name = "compression-codecs" 559 + version = "0.4.38" 560 + source = "registry+https://github.com/rust-lang/crates.io-index" 561 + checksum = "ce2548391e9c1929c21bf6aa2680af86fe4c1b33e6cea9ac1cfeec0bd11218cf" 562 + dependencies = [ 563 + "brotli", 564 + "compression-core", 565 + "flate2", 566 + "memchr", 567 + "zstd", 568 + "zstd-safe", 569 + ] 570 + 571 + [[package]] 572 + name = "compression-core" 573 + version = "0.4.32" 574 + source = "registry+https://github.com/rust-lang/crates.io-index" 575 + checksum = "cc14f565cf027a105f7a44ccf9e5b424348421a1d8952a8fc9d499d313107789" 576 + 577 + [[package]] 578 + name = "const-oid" 579 + version = "0.9.6" 580 + source = "registry+https://github.com/rust-lang/crates.io-index" 581 + checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" 582 + 583 + [[package]] 584 + name = "const-str" 585 + version = "0.4.3" 586 + source = "registry+https://github.com/rust-lang/crates.io-index" 587 + checksum = "2f421161cb492475f1661ddc9815a745a1c894592070661180fdec3d4872e9c3" 588 + 589 + [[package]] 590 + name = "cordyceps" 591 + version = "0.3.4" 592 + source = "registry+https://github.com/rust-lang/crates.io-index" 593 + checksum = "688d7fbb8092b8de775ef2536f36c8c31f2bc4006ece2e8d8ad2d17d00ce0a2a" 594 + dependencies = [ 595 + "loom", 596 + "tracing", 597 + ] 598 + 599 + [[package]] 600 + name = "core-foundation" 601 + version = "0.9.4" 602 + source = "registry+https://github.com/rust-lang/crates.io-index" 603 + checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" 604 + dependencies = [ 605 + "core-foundation-sys", 606 + "libc", 607 + ] 608 + 609 + [[package]] 610 + name = "core-foundation" 611 + version = "0.10.1" 612 + source = "registry+https://github.com/rust-lang/crates.io-index" 613 + checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" 614 + dependencies = [ 615 + "core-foundation-sys", 616 + "libc", 617 + ] 618 + 619 + [[package]] 620 + name = "core-foundation-sys" 621 + version = "0.8.7" 622 + source = "registry+https://github.com/rust-lang/crates.io-index" 623 + checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" 624 + 625 + [[package]] 626 + name = "cpufeatures" 627 + version = "0.2.17" 628 + source = "registry+https://github.com/rust-lang/crates.io-index" 629 + checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" 630 + dependencies = [ 631 + "libc", 632 + ] 633 + 634 + [[package]] 635 + name = "cpufeatures" 636 + version = "0.3.0" 637 + source = "registry+https://github.com/rust-lang/crates.io-index" 638 + checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201" 639 + dependencies = [ 640 + "libc", 641 + ] 642 + 643 + [[package]] 644 + name = "crc32fast" 645 + version = "1.5.0" 646 + source = "registry+https://github.com/rust-lang/crates.io-index" 647 + checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" 648 + dependencies = [ 649 + "cfg-if", 650 + ] 651 + 652 + [[package]] 653 + name = "critical-section" 654 + version = "1.2.0" 655 + source = "registry+https://github.com/rust-lang/crates.io-index" 656 + checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" 657 + 658 + [[package]] 659 + name = "crossbeam-epoch" 660 + version = "0.9.18" 661 + source = "registry+https://github.com/rust-lang/crates.io-index" 662 + checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" 663 + dependencies = [ 664 + "crossbeam-utils", 665 + ] 666 + 667 + [[package]] 668 + name = "crossbeam-skiplist" 669 + version = "0.1.3" 670 + source = "registry+https://github.com/rust-lang/crates.io-index" 671 + checksum = "df29de440c58ca2cc6e587ec3d22347551a32435fbde9d2bff64e78a9ffa151b" 672 + dependencies = [ 673 + "crossbeam-epoch", 674 + "crossbeam-utils", 675 + ] 676 + 677 + [[package]] 678 + name = "crossbeam-utils" 679 + version = "0.8.21" 680 + source = "registry+https://github.com/rust-lang/crates.io-index" 681 + checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" 682 + 683 + [[package]] 684 + name = "crunchy" 685 + version = "0.2.4" 686 + source = "registry+https://github.com/rust-lang/crates.io-index" 687 + checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" 688 + 689 + [[package]] 690 + name = "crypto-bigint" 691 + version = "0.5.5" 692 + source = "registry+https://github.com/rust-lang/crates.io-index" 693 + checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" 694 + dependencies = [ 695 + "generic-array", 696 + "rand_core 0.6.4", 697 + "subtle", 698 + "zeroize", 699 + ] 700 + 701 + [[package]] 702 + name = "crypto-common" 703 + version = "0.1.6" 704 + source = "registry+https://github.com/rust-lang/crates.io-index" 705 + checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" 706 + dependencies = [ 707 + "generic-array", 708 + "typenum", 709 + ] 710 + 711 + [[package]] 712 + name = "curve25519-dalek" 713 + version = "4.1.3" 714 + source = "registry+https://github.com/rust-lang/crates.io-index" 715 + checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" 716 + dependencies = [ 717 + "cfg-if", 718 + "cpufeatures 0.2.17", 719 + "curve25519-dalek-derive", 720 + "digest", 721 + "fiat-crypto", 722 + "rustc_version", 723 + "subtle", 724 + "zeroize", 725 + ] 726 + 727 + [[package]] 728 + name = "curve25519-dalek-derive" 729 + version = "0.1.1" 730 + source = "registry+https://github.com/rust-lang/crates.io-index" 731 + checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" 732 + dependencies = [ 733 + "proc-macro2", 734 + "quote", 735 + "syn", 736 + ] 737 + 738 + [[package]] 739 + name = "darling" 740 + version = "0.23.0" 741 + source = "registry+https://github.com/rust-lang/crates.io-index" 742 + checksum = "25ae13da2f202d56bd7f91c25fba009e7717a1e4a1cc98a76d844b65ae912e9d" 743 + dependencies = [ 744 + "darling_core", 745 + "darling_macro", 746 + ] 747 + 748 + [[package]] 749 + name = "darling_core" 750 + version = "0.23.0" 751 + source = "registry+https://github.com/rust-lang/crates.io-index" 752 + checksum = "9865a50f7c335f53564bb694ef660825eb8610e0a53d3e11bf1b0d3df31e03b0" 753 + dependencies = [ 754 + "ident_case", 755 + "proc-macro2", 756 + "quote", 757 + "strsim", 758 + "syn", 759 + ] 760 + 761 + [[package]] 762 + name = "darling_macro" 763 + version = "0.23.0" 764 + source = "registry+https://github.com/rust-lang/crates.io-index" 765 + checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d" 766 + dependencies = [ 767 + "darling_core", 768 + "quote", 769 + "syn", 770 + ] 771 + 772 + [[package]] 773 + name = "dashmap" 774 + version = "6.1.0" 775 + source = "registry+https://github.com/rust-lang/crates.io-index" 776 + checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf" 777 + dependencies = [ 778 + "cfg-if", 779 + "crossbeam-utils", 780 + "hashbrown 0.14.5", 781 + "lock_api", 782 + "once_cell", 783 + "parking_lot_core", 784 + ] 785 + 786 + [[package]] 787 + name = "data-encoding" 788 + version = "2.11.0" 789 + source = "registry+https://github.com/rust-lang/crates.io-index" 790 + checksum = "a4ae5f15dda3c708c0ade84bfee31ccab44a3da4f88015ed22f63732abe300c8" 791 + 792 + [[package]] 793 + name = "data-encoding-macro" 794 + version = "0.1.20" 795 + source = "registry+https://github.com/rust-lang/crates.io-index" 796 + checksum = "3259c913752a86488b501ed8680446a5ed2d5aeac6e596cb23ba3800768ea32c" 797 + dependencies = [ 798 + "data-encoding", 799 + "data-encoding-macro-internal", 800 + ] 801 + 802 + [[package]] 803 + name = "data-encoding-macro-internal" 804 + version = "0.1.18" 805 + source = "registry+https://github.com/rust-lang/crates.io-index" 806 + checksum = "ccc2776f0c61eca1ca32528f85548abd1a4be8fb53d1b21c013e4f18da1e7090" 807 + dependencies = [ 808 + "data-encoding", 809 + "syn", 810 + ] 811 + 812 + [[package]] 813 + name = "der" 814 + version = "0.7.10" 815 + source = "registry+https://github.com/rust-lang/crates.io-index" 816 + checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" 817 + dependencies = [ 818 + "const-oid", 819 + "pem-rfc7468", 820 + "zeroize", 821 + ] 822 + 823 + [[package]] 824 + name = "deranged" 825 + version = "0.5.8" 826 + source = "registry+https://github.com/rust-lang/crates.io-index" 827 + checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" 828 + dependencies = [ 829 + "powerfmt", 830 + ] 831 + 832 + [[package]] 833 + name = "derive_more" 834 + version = "1.0.0" 835 + source = "registry+https://github.com/rust-lang/crates.io-index" 836 + checksum = "4a9b99b9cbbe49445b21764dc0625032a89b145a2642e67603e1c936f5458d05" 837 + dependencies = [ 838 + "derive_more-impl", 839 + ] 840 + 841 + [[package]] 842 + name = "derive_more-impl" 843 + version = "1.0.0" 844 + source = "registry+https://github.com/rust-lang/crates.io-index" 845 + checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" 846 + dependencies = [ 847 + "proc-macro2", 848 + "quote", 849 + "syn", 850 + "unicode-xid", 851 + ] 852 + 853 + [[package]] 854 + name = "diatomic-waker" 855 + version = "0.2.3" 856 + source = "registry+https://github.com/rust-lang/crates.io-index" 857 + checksum = "ab03c107fafeb3ee9f5925686dbb7a73bc76e3932abb0d2b365cb64b169cf04c" 858 + 859 + [[package]] 860 + name = "digest" 861 + version = "0.10.7" 862 + source = "registry+https://github.com/rust-lang/crates.io-index" 863 + checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" 864 + dependencies = [ 865 + "block-buffer", 866 + "const-oid", 867 + "crypto-common", 868 + "subtle", 869 + ] 870 + 871 + [[package]] 872 + name = "displaydoc" 873 + version = "0.2.5" 874 + source = "registry+https://github.com/rust-lang/crates.io-index" 875 + checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" 876 + dependencies = [ 877 + "proc-macro2", 878 + "quote", 879 + "syn", 880 + ] 881 + 882 + [[package]] 883 + name = "dunce" 884 + version = "1.0.5" 885 + source = "registry+https://github.com/rust-lang/crates.io-index" 886 + checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" 887 + 888 + [[package]] 889 + name = "ecdsa" 890 + version = "0.16.9" 891 + source = "registry+https://github.com/rust-lang/crates.io-index" 892 + checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" 893 + dependencies = [ 894 + "der", 895 + "digest", 896 + "elliptic-curve", 897 + "rfc6979", 898 + "signature", 899 + "spki", 900 + ] 901 + 902 + [[package]] 903 + name = "ed25519" 904 + version = "2.2.3" 905 + source = "registry+https://github.com/rust-lang/crates.io-index" 906 + checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" 907 + dependencies = [ 908 + "pkcs8", 909 + "signature", 910 + ] 911 + 912 + [[package]] 913 + name = "ed25519-dalek" 914 + version = "2.2.0" 915 + source = "registry+https://github.com/rust-lang/crates.io-index" 916 + checksum = "70e796c081cee67dc755e1a36a0a172b897fab85fc3f6bc48307991f64e4eca9" 917 + dependencies = [ 918 + "curve25519-dalek", 919 + "ed25519", 920 + "rand_core 0.6.4", 921 + "serde", 922 + "sha2", 923 + "subtle", 924 + "zeroize", 925 + ] 926 + 927 + [[package]] 928 + name = "elliptic-curve" 929 + version = "0.13.8" 930 + source = "registry+https://github.com/rust-lang/crates.io-index" 931 + checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" 932 + dependencies = [ 933 + "base16ct", 934 + "crypto-bigint", 935 + "digest", 936 + "ff", 937 + "generic-array", 938 + "group", 939 + "pem-rfc7468", 940 + "pkcs8", 941 + "rand_core 0.6.4", 942 + "sec1", 943 + "subtle", 944 + "zeroize", 945 + ] 946 + 947 + [[package]] 948 + name = "embedded-io" 949 + version = "0.4.0" 950 + source = "registry+https://github.com/rust-lang/crates.io-index" 951 + checksum = "ef1a6892d9eef45c8fa6b9e0086428a2cca8491aca8f787c534a3d6d0bcb3ced" 952 + 953 + [[package]] 954 + name = "embedded-io" 955 + version = "0.6.1" 956 + source = "registry+https://github.com/rust-lang/crates.io-index" 957 + checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" 958 + 959 + [[package]] 960 + name = "encoding_rs" 961 + version = "0.8.35" 962 + source = "registry+https://github.com/rust-lang/crates.io-index" 963 + checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" 964 + dependencies = [ 965 + "cfg-if", 966 + ] 967 + 968 + [[package]] 969 + name = "enum-as-inner" 970 + version = "0.6.1" 971 + source = "registry+https://github.com/rust-lang/crates.io-index" 972 + checksum = "a1e6a265c649f3f5979b601d26f1d05ada116434c87741c9493cb56218f76cbc" 973 + dependencies = [ 974 + "heck 0.5.0", 975 + "proc-macro2", 976 + "quote", 977 + "syn", 978 + ] 979 + 980 + [[package]] 981 + name = "enum_dispatch" 982 + version = "0.3.13" 983 + source = "registry+https://github.com/rust-lang/crates.io-index" 984 + checksum = "aa18ce2bc66555b3218614519ac839ddb759a7d6720732f979ef8d13be147ecd" 985 + dependencies = [ 986 + "once_cell", 987 + "proc-macro2", 988 + "quote", 989 + "syn", 990 + ] 991 + 992 + [[package]] 993 + name = "equivalent" 994 + version = "1.0.2" 995 + source = "registry+https://github.com/rust-lang/crates.io-index" 996 + checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" 997 + 998 + [[package]] 999 + name = "errno" 1000 + version = "0.3.14" 1001 + source = "registry+https://github.com/rust-lang/crates.io-index" 1002 + checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" 1003 + dependencies = [ 1004 + "libc", 1005 + "windows-sys 0.61.2", 1006 + ] 1007 + 1008 + [[package]] 1009 + name = "fastrand" 1010 + version = "2.4.1" 1011 + source = "registry+https://github.com/rust-lang/crates.io-index" 1012 + checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" 1013 + 1014 + [[package]] 1015 + name = "ff" 1016 + version = "0.13.1" 1017 + source = "registry+https://github.com/rust-lang/crates.io-index" 1018 + checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" 1019 + dependencies = [ 1020 + "rand_core 0.6.4", 1021 + "subtle", 1022 + ] 1023 + 1024 + [[package]] 1025 + name = "fiat-crypto" 1026 + version = "0.2.9" 1027 + source = "registry+https://github.com/rust-lang/crates.io-index" 1028 + checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" 1029 + 1030 + [[package]] 1031 + name = "find-msvc-tools" 1032 + version = "0.1.9" 1033 + source = "registry+https://github.com/rust-lang/crates.io-index" 1034 + checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" 1035 + 1036 + [[package]] 1037 + name = "fjall" 1038 + version = "3.1.4" 1039 + source = "git+https://github.com/90-008/fjall.git#ef8b1939dfa68c69ee33977ce41639ec00d431e8" 1040 + dependencies = [ 1041 + "byteorder-lite", 1042 + "byteview", 1043 + "dashmap", 1044 + "flume", 1045 + "log", 1046 + "lsm-tree", 1047 + "lz4_flex", 1048 + "tempfile", 1049 + "xxhash-rust", 1050 + "zstd", 1051 + ] 1052 + 1053 + [[package]] 1054 + name = "flate2" 1055 + version = "1.1.9" 1056 + source = "registry+https://github.com/rust-lang/crates.io-index" 1057 + checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" 1058 + dependencies = [ 1059 + "crc32fast", 1060 + "miniz_oxide", 1061 + ] 1062 + 1063 + [[package]] 1064 + name = "fluent-uri" 1065 + version = "0.4.1" 1066 + source = "registry+https://github.com/rust-lang/crates.io-index" 1067 + checksum = "bc74ac4d8359ae70623506d512209619e5cf8f347124910440dbc221714b328e" 1068 + dependencies = [ 1069 + "borrow-or-share", 1070 + "ref-cast", 1071 + "serde", 1072 + ] 1073 + 1074 + [[package]] 1075 + name = "flume" 1076 + version = "0.12.0" 1077 + source = "registry+https://github.com/rust-lang/crates.io-index" 1078 + checksum = "5e139bc46ca777eb5efaf62df0ab8cc5fd400866427e56c68b22e414e53bd3be" 1079 + dependencies = [ 1080 + "spin 0.9.8", 1081 + ] 1082 + 1083 + [[package]] 1084 + name = "fnv" 1085 + version = "1.0.7" 1086 + source = "registry+https://github.com/rust-lang/crates.io-index" 1087 + checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 1088 + 1089 + [[package]] 1090 + name = "foldhash" 1091 + version = "0.1.5" 1092 + source = "registry+https://github.com/rust-lang/crates.io-index" 1093 + checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" 1094 + 1095 + [[package]] 1096 + name = "foreign-types" 1097 + version = "0.3.2" 1098 + source = "registry+https://github.com/rust-lang/crates.io-index" 1099 + checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" 1100 + dependencies = [ 1101 + "foreign-types-shared", 1102 + ] 1103 + 1104 + [[package]] 1105 + name = "foreign-types-shared" 1106 + version = "0.1.1" 1107 + source = "registry+https://github.com/rust-lang/crates.io-index" 1108 + checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" 1109 + 1110 + [[package]] 1111 + name = "form_urlencoded" 1112 + version = "1.2.2" 1113 + source = "registry+https://github.com/rust-lang/crates.io-index" 1114 + checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" 1115 + dependencies = [ 1116 + "percent-encoding", 1117 + ] 1118 + 1119 + [[package]] 1120 + name = "fs_extra" 1121 + version = "1.3.0" 1122 + source = "registry+https://github.com/rust-lang/crates.io-index" 1123 + checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" 1124 + 1125 + [[package]] 1126 + name = "futures" 1127 + version = "0.3.32" 1128 + source = "registry+https://github.com/rust-lang/crates.io-index" 1129 + checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" 1130 + dependencies = [ 1131 + "futures-channel", 1132 + "futures-core", 1133 + "futures-executor", 1134 + "futures-io", 1135 + "futures-sink", 1136 + "futures-task", 1137 + "futures-util", 1138 + ] 1139 + 1140 + [[package]] 1141 + name = "futures-buffered" 1142 + version = "0.2.13" 1143 + source = "registry+https://github.com/rust-lang/crates.io-index" 1144 + checksum = "4421cb78ee172b6b06080093479d3c50f058e7c81b7d577bbb8d118d551d4cd5" 1145 + dependencies = [ 1146 + "cordyceps", 1147 + "diatomic-waker", 1148 + "futures-core", 1149 + "pin-project-lite", 1150 + "spin 0.10.0", 1151 + ] 1152 + 1153 + [[package]] 1154 + name = "futures-channel" 1155 + version = "0.3.32" 1156 + source = "registry+https://github.com/rust-lang/crates.io-index" 1157 + checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" 1158 + dependencies = [ 1159 + "futures-core", 1160 + "futures-sink", 1161 + ] 1162 + 1163 + [[package]] 1164 + name = "futures-core" 1165 + version = "0.3.32" 1166 + source = "registry+https://github.com/rust-lang/crates.io-index" 1167 + checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" 1168 + 1169 + [[package]] 1170 + name = "futures-executor" 1171 + version = "0.3.32" 1172 + source = "registry+https://github.com/rust-lang/crates.io-index" 1173 + checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" 1174 + dependencies = [ 1175 + "futures-core", 1176 + "futures-task", 1177 + "futures-util", 1178 + ] 1179 + 1180 + [[package]] 1181 + name = "futures-io" 1182 + version = "0.3.32" 1183 + source = "registry+https://github.com/rust-lang/crates.io-index" 1184 + checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" 1185 + 1186 + [[package]] 1187 + name = "futures-lite" 1188 + version = "2.6.1" 1189 + source = "registry+https://github.com/rust-lang/crates.io-index" 1190 + checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad" 1191 + dependencies = [ 1192 + "fastrand", 1193 + "futures-core", 1194 + "futures-io", 1195 + "parking", 1196 + "pin-project-lite", 1197 + ] 1198 + 1199 + [[package]] 1200 + name = "futures-macro" 1201 + version = "0.3.32" 1202 + source = "registry+https://github.com/rust-lang/crates.io-index" 1203 + checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" 1204 + dependencies = [ 1205 + "proc-macro2", 1206 + "quote", 1207 + "syn", 1208 + ] 1209 + 1210 + [[package]] 1211 + name = "futures-sink" 1212 + version = "0.3.32" 1213 + source = "registry+https://github.com/rust-lang/crates.io-index" 1214 + checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" 1215 + 1216 + [[package]] 1217 + name = "futures-task" 1218 + version = "0.3.32" 1219 + source = "registry+https://github.com/rust-lang/crates.io-index" 1220 + checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" 1221 + 1222 + [[package]] 1223 + name = "futures-util" 1224 + version = "0.3.32" 1225 + source = "registry+https://github.com/rust-lang/crates.io-index" 1226 + checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" 1227 + dependencies = [ 1228 + "futures-channel", 1229 + "futures-core", 1230 + "futures-io", 1231 + "futures-macro", 1232 + "futures-sink", 1233 + "futures-task", 1234 + "memchr", 1235 + "pin-project-lite", 1236 + "slab", 1237 + ] 1238 + 1239 + [[package]] 1240 + name = "generator" 1241 + version = "0.8.8" 1242 + source = "registry+https://github.com/rust-lang/crates.io-index" 1243 + checksum = "52f04ae4152da20c76fe800fa48659201d5cf627c5149ca0b707b69d7eef6cf9" 1244 + dependencies = [ 1245 + "cc", 1246 + "cfg-if", 1247 + "libc", 1248 + "log", 1249 + "rustversion", 1250 + "windows-link", 1251 + "windows-result", 1252 + ] 1253 + 1254 + [[package]] 1255 + name = "generic-array" 1256 + version = "0.14.9" 1257 + source = "registry+https://github.com/rust-lang/crates.io-index" 1258 + checksum = "4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2" 1259 + dependencies = [ 1260 + "typenum", 1261 + "version_check", 1262 + "zeroize", 1263 + ] 1264 + 1265 + [[package]] 1266 + name = "getrandom" 1267 + version = "0.2.17" 1268 + source = "registry+https://github.com/rust-lang/crates.io-index" 1269 + checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" 1270 + dependencies = [ 1271 + "cfg-if", 1272 + "js-sys", 1273 + "libc", 1274 + "wasi", 1275 + "wasm-bindgen", 1276 + ] 1277 + 1278 + [[package]] 1279 + name = "getrandom" 1280 + version = "0.3.4" 1281 + source = "registry+https://github.com/rust-lang/crates.io-index" 1282 + checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" 1283 + dependencies = [ 1284 + "cfg-if", 1285 + "js-sys", 1286 + "libc", 1287 + "r-efi 5.3.0", 1288 + "wasip2", 1289 + "wasm-bindgen", 1290 + ] 1291 + 1292 + [[package]] 1293 + name = "getrandom" 1294 + version = "0.4.2" 1295 + source = "registry+https://github.com/rust-lang/crates.io-index" 1296 + checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" 1297 + dependencies = [ 1298 + "cfg-if", 1299 + "libc", 1300 + "r-efi 6.0.0", 1301 + "rand_core 0.10.1", 1302 + "wasip2", 1303 + "wasip3", 1304 + ] 1305 + 1306 + [[package]] 1307 + name = "gimli" 1308 + version = "0.32.3" 1309 + source = "registry+https://github.com/rust-lang/crates.io-index" 1310 + checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7" 1311 + 1312 + [[package]] 1313 + name = "glob" 1314 + version = "0.3.3" 1315 + source = "registry+https://github.com/rust-lang/crates.io-index" 1316 + checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" 1317 + 1318 + [[package]] 1319 + name = "group" 1320 + version = "0.13.0" 1321 + source = "registry+https://github.com/rust-lang/crates.io-index" 1322 + checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" 1323 + dependencies = [ 1324 + "ff", 1325 + "rand_core 0.6.4", 1326 + "subtle", 1327 + ] 1328 + 1329 + [[package]] 1330 + name = "h2" 1331 + version = "0.4.13" 1332 + source = "registry+https://github.com/rust-lang/crates.io-index" 1333 + checksum = "2f44da3a8150a6703ed5d34e164b875fd14c2cdab9af1252a9a1020bde2bdc54" 1334 + dependencies = [ 1335 + "atomic-waker", 1336 + "bytes", 1337 + "fnv", 1338 + "futures-core", 1339 + "futures-sink", 1340 + "http", 1341 + "indexmap", 1342 + "slab", 1343 + "tokio", 1344 + "tokio-util", 1345 + "tracing", 1346 + ] 1347 + 1348 + [[package]] 1349 + name = "half" 1350 + version = "2.7.1" 1351 + source = "registry+https://github.com/rust-lang/crates.io-index" 1352 + checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" 1353 + dependencies = [ 1354 + "cfg-if", 1355 + "crunchy", 1356 + "zerocopy", 1357 + ] 1358 + 1359 + [[package]] 1360 + name = "hash32" 1361 + version = "0.2.1" 1362 + source = "registry+https://github.com/rust-lang/crates.io-index" 1363 + checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67" 1364 + dependencies = [ 1365 + "byteorder", 1366 + ] 1367 + 1368 + [[package]] 1369 + name = "hashbrown" 1370 + version = "0.14.5" 1371 + source = "registry+https://github.com/rust-lang/crates.io-index" 1372 + checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" 1373 + 1374 + [[package]] 1375 + name = "hashbrown" 1376 + version = "0.15.5" 1377 + source = "registry+https://github.com/rust-lang/crates.io-index" 1378 + checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" 1379 + dependencies = [ 1380 + "allocator-api2", 1381 + "equivalent", 1382 + "foldhash", 1383 + ] 1384 + 1385 + [[package]] 1386 + name = "hashbrown" 1387 + version = "0.16.1" 1388 + source = "registry+https://github.com/rust-lang/crates.io-index" 1389 + checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" 1390 + 1391 + [[package]] 1392 + name = "hashbrown" 1393 + version = "0.17.0" 1394 + source = "registry+https://github.com/rust-lang/crates.io-index" 1395 + checksum = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51" 1396 + 1397 + [[package]] 1398 + name = "heapless" 1399 + version = "0.7.17" 1400 + source = "registry+https://github.com/rust-lang/crates.io-index" 1401 + checksum = "cdc6457c0eb62c71aac4bc17216026d8410337c4126773b9c5daba343f17964f" 1402 + dependencies = [ 1403 + "atomic-polyfill", 1404 + "hash32", 1405 + "rustc_version", 1406 + "serde", 1407 + "spin 0.9.8", 1408 + "stable_deref_trait", 1409 + ] 1410 + 1411 + [[package]] 1412 + name = "heck" 1413 + version = "0.4.1" 1414 + source = "registry+https://github.com/rust-lang/crates.io-index" 1415 + checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" 1416 + 1417 + [[package]] 1418 + name = "heck" 1419 + version = "0.5.0" 1420 + source = "registry+https://github.com/rust-lang/crates.io-index" 1421 + checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" 1422 + 1423 + [[package]] 1424 + name = "hex" 1425 + version = "0.4.3" 1426 + source = "registry+https://github.com/rust-lang/crates.io-index" 1427 + checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" 1428 + 1429 + [[package]] 1430 + name = "hickory-proto" 1431 + version = "0.24.4" 1432 + source = "registry+https://github.com/rust-lang/crates.io-index" 1433 + checksum = "92652067c9ce6f66ce53cc38d1169daa36e6e7eb7dd3b63b5103bd9d97117248" 1434 + dependencies = [ 1435 + "async-trait", 1436 + "cfg-if", 1437 + "data-encoding", 1438 + "enum-as-inner", 1439 + "futures-channel", 1440 + "futures-io", 1441 + "futures-util", 1442 + "idna", 1443 + "ipnet", 1444 + "once_cell", 1445 + "rand 0.8.6", 1446 + "thiserror 1.0.69", 1447 + "tinyvec", 1448 + "tokio", 1449 + "tracing", 1450 + "url", 1451 + ] 1452 + 1453 + [[package]] 1454 + name = "hickory-resolver" 1455 + version = "0.24.4" 1456 + source = "registry+https://github.com/rust-lang/crates.io-index" 1457 + checksum = "cbb117a1ca520e111743ab2f6688eddee69db4e0ea242545a604dce8a66fd22e" 1458 + dependencies = [ 1459 + "cfg-if", 1460 + "futures-util", 1461 + "hickory-proto", 1462 + "ipconfig", 1463 + "lru-cache", 1464 + "once_cell", 1465 + "parking_lot", 1466 + "rand 0.8.6", 1467 + "resolv-conf", 1468 + "smallvec", 1469 + "thiserror 1.0.69", 1470 + "tokio", 1471 + "tracing", 1472 + ] 1473 + 1474 + [[package]] 1475 + name = "hmac" 1476 + version = "0.12.1" 1477 + source = "registry+https://github.com/rust-lang/crates.io-index" 1478 + checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" 1479 + dependencies = [ 1480 + "digest", 1481 + ] 1482 + 1483 + [[package]] 1484 + name = "http" 1485 + version = "1.4.0" 1486 + source = "registry+https://github.com/rust-lang/crates.io-index" 1487 + checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" 1488 + dependencies = [ 1489 + "bytes", 1490 + "itoa", 1491 + ] 1492 + 1493 + [[package]] 1494 + name = "http-body" 1495 + version = "1.0.1" 1496 + source = "registry+https://github.com/rust-lang/crates.io-index" 1497 + checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" 1498 + dependencies = [ 1499 + "bytes", 1500 + "http", 1501 + ] 1502 + 1503 + [[package]] 1504 + name = "http-body-util" 1505 + version = "0.1.3" 1506 + source = "registry+https://github.com/rust-lang/crates.io-index" 1507 + checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" 1508 + dependencies = [ 1509 + "bytes", 1510 + "futures-core", 1511 + "http", 1512 + "http-body", 1513 + "pin-project-lite", 1514 + ] 1515 + 1516 + [[package]] 1517 + name = "httparse" 1518 + version = "1.10.1" 1519 + source = "registry+https://github.com/rust-lang/crates.io-index" 1520 + checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" 1521 + 1522 + [[package]] 1523 + name = "httpdate" 1524 + version = "1.0.3" 1525 + source = "registry+https://github.com/rust-lang/crates.io-index" 1526 + checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" 1527 + 1528 + [[package]] 1529 + name = "humantime" 1530 + version = "2.3.0" 1531 + source = "registry+https://github.com/rust-lang/crates.io-index" 1532 + checksum = "135b12329e5e3ce057a9f972339ea52bc954fe1e9358ef27f95e89716fbc5424" 1533 + 1534 + [[package]] 1535 + name = "hydrant" 1536 + version = "0.1.0" 1537 + dependencies = [ 1538 + "ahash", 1539 + "arc-swap", 1540 + "axum", 1541 + "axum-tws", 1542 + "bytes", 1543 + "chrono", 1544 + "cid", 1545 + "data-encoding", 1546 + "fjall", 1547 + "futures", 1548 + "glob", 1549 + "hex", 1550 + "humantime", 1551 + "hyper", 1552 + "iroh-car", 1553 + "jacquard-api", 1554 + "jacquard-common", 1555 + "jacquard-derive", 1556 + "jacquard-identity", 1557 + "jacquard-repo", 1558 + "lsm-tree", 1559 + "miette", 1560 + "mimalloc", 1561 + "multibase", 1562 + "nohash-hasher", 1563 + "parking_lot", 1564 + "rand 0.10.1", 1565 + "reqwest", 1566 + "rmp-serde", 1567 + "rustls", 1568 + "scc", 1569 + "serde", 1570 + "serde_bytes", 1571 + "serde_ipld_dagcbor", 1572 + "serde_json", 1573 + "serde_urlencoded", 1574 + "sha2", 1575 + "smol_str", 1576 + "thiserror 2.0.18", 1577 + "tokio", 1578 + "tokio-util", 1579 + "tokio-websockets", 1580 + "tower-http", 1581 + "tracing", 1582 + "tracing-subscriber", 1583 + "url", 1584 + ] 1585 + 1586 + [[package]] 1587 + name = "hyper" 1588 + version = "1.9.0" 1589 + source = "registry+https://github.com/rust-lang/crates.io-index" 1590 + checksum = "6299f016b246a94207e63da54dbe807655bf9e00044f73ded42c3ac5305fbcca" 1591 + dependencies = [ 1592 + "atomic-waker", 1593 + "bytes", 1594 + "futures-channel", 1595 + "futures-core", 1596 + "h2", 1597 + "http", 1598 + "http-body", 1599 + "httparse", 1600 + "httpdate", 1601 + "itoa", 1602 + "pin-project-lite", 1603 + "smallvec", 1604 + "tokio", 1605 + "want", 1606 + ] 1607 + 1608 + [[package]] 1609 + name = "hyper-rustls" 1610 + version = "0.27.9" 1611 + source = "registry+https://github.com/rust-lang/crates.io-index" 1612 + checksum = "33ca68d021ef39cf6463ab54c1d0f5daf03377b70561305bb89a8f83aab66e0f" 1613 + dependencies = [ 1614 + "http", 1615 + "hyper", 1616 + "hyper-util", 1617 + "rustls", 1618 + "tokio", 1619 + "tokio-rustls", 1620 + "tower-service", 1621 + "webpki-roots", 1622 + ] 1623 + 1624 + [[package]] 1625 + name = "hyper-tls" 1626 + version = "0.6.0" 1627 + source = "registry+https://github.com/rust-lang/crates.io-index" 1628 + checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" 1629 + dependencies = [ 1630 + "bytes", 1631 + "http-body-util", 1632 + "hyper", 1633 + "hyper-util", 1634 + "native-tls", 1635 + "tokio", 1636 + "tokio-native-tls", 1637 + "tower-service", 1638 + ] 1639 + 1640 + [[package]] 1641 + name = "hyper-util" 1642 + version = "0.1.20" 1643 + source = "registry+https://github.com/rust-lang/crates.io-index" 1644 + checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" 1645 + dependencies = [ 1646 + "base64", 1647 + "bytes", 1648 + "futures-channel", 1649 + "futures-util", 1650 + "http", 1651 + "http-body", 1652 + "hyper", 1653 + "ipnet", 1654 + "libc", 1655 + "percent-encoding", 1656 + "pin-project-lite", 1657 + "socket2", 1658 + "system-configuration", 1659 + "tokio", 1660 + "tower-service", 1661 + "tracing", 1662 + "windows-registry", 1663 + ] 1664 + 1665 + [[package]] 1666 + name = "iana-time-zone" 1667 + version = "0.1.65" 1668 + source = "registry+https://github.com/rust-lang/crates.io-index" 1669 + checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" 1670 + dependencies = [ 1671 + "android_system_properties", 1672 + "core-foundation-sys", 1673 + "iana-time-zone-haiku", 1674 + "js-sys", 1675 + "log", 1676 + "wasm-bindgen", 1677 + "windows-core", 1678 + ] 1679 + 1680 + [[package]] 1681 + name = "iana-time-zone-haiku" 1682 + version = "0.1.2" 1683 + source = "registry+https://github.com/rust-lang/crates.io-index" 1684 + checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" 1685 + dependencies = [ 1686 + "cc", 1687 + ] 1688 + 1689 + [[package]] 1690 + name = "icu_collections" 1691 + version = "2.2.0" 1692 + source = "registry+https://github.com/rust-lang/crates.io-index" 1693 + checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c" 1694 + dependencies = [ 1695 + "displaydoc", 1696 + "potential_utf", 1697 + "utf8_iter", 1698 + "yoke", 1699 + "zerofrom", 1700 + "zerovec", 1701 + ] 1702 + 1703 + [[package]] 1704 + name = "icu_locale_core" 1705 + version = "2.2.0" 1706 + source = "registry+https://github.com/rust-lang/crates.io-index" 1707 + checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29" 1708 + dependencies = [ 1709 + "displaydoc", 1710 + "litemap", 1711 + "tinystr", 1712 + "writeable", 1713 + "zerovec", 1714 + ] 1715 + 1716 + [[package]] 1717 + name = "icu_normalizer" 1718 + version = "2.2.0" 1719 + source = "registry+https://github.com/rust-lang/crates.io-index" 1720 + checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4" 1721 + dependencies = [ 1722 + "icu_collections", 1723 + "icu_normalizer_data", 1724 + "icu_properties", 1725 + "icu_provider", 1726 + "smallvec", 1727 + "zerovec", 1728 + ] 1729 + 1730 + [[package]] 1731 + name = "icu_normalizer_data" 1732 + version = "2.2.0" 1733 + source = "registry+https://github.com/rust-lang/crates.io-index" 1734 + checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38" 1735 + 1736 + [[package]] 1737 + name = "icu_properties" 1738 + version = "2.2.0" 1739 + source = "registry+https://github.com/rust-lang/crates.io-index" 1740 + checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de" 1741 + dependencies = [ 1742 + "icu_collections", 1743 + "icu_locale_core", 1744 + "icu_properties_data", 1745 + "icu_provider", 1746 + "zerotrie", 1747 + "zerovec", 1748 + ] 1749 + 1750 + [[package]] 1751 + name = "icu_properties_data" 1752 + version = "2.2.0" 1753 + source = "registry+https://github.com/rust-lang/crates.io-index" 1754 + checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14" 1755 + 1756 + [[package]] 1757 + name = "icu_provider" 1758 + version = "2.2.0" 1759 + source = "registry+https://github.com/rust-lang/crates.io-index" 1760 + checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421" 1761 + dependencies = [ 1762 + "displaydoc", 1763 + "icu_locale_core", 1764 + "writeable", 1765 + "yoke", 1766 + "zerofrom", 1767 + "zerotrie", 1768 + "zerovec", 1769 + ] 1770 + 1771 + [[package]] 1772 + name = "id-arena" 1773 + version = "2.3.0" 1774 + source = "registry+https://github.com/rust-lang/crates.io-index" 1775 + checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" 1776 + 1777 + [[package]] 1778 + name = "ident_case" 1779 + version = "1.0.1" 1780 + source = "registry+https://github.com/rust-lang/crates.io-index" 1781 + checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" 1782 + 1783 + [[package]] 1784 + name = "idna" 1785 + version = "1.1.0" 1786 + source = "registry+https://github.com/rust-lang/crates.io-index" 1787 + checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" 1788 + dependencies = [ 1789 + "idna_adapter", 1790 + "smallvec", 1791 + "utf8_iter", 1792 + ] 1793 + 1794 + [[package]] 1795 + name = "idna_adapter" 1796 + version = "1.2.1" 1797 + source = "registry+https://github.com/rust-lang/crates.io-index" 1798 + checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" 1799 + dependencies = [ 1800 + "icu_normalizer", 1801 + "icu_properties", 1802 + ] 1803 + 1804 + [[package]] 1805 + name = "indexmap" 1806 + version = "2.14.0" 1807 + source = "registry+https://github.com/rust-lang/crates.io-index" 1808 + checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" 1809 + dependencies = [ 1810 + "equivalent", 1811 + "hashbrown 0.17.0", 1812 + "serde", 1813 + "serde_core", 1814 + ] 1815 + 1816 + [[package]] 1817 + name = "interval-heap" 1818 + version = "0.0.5" 1819 + source = "registry+https://github.com/rust-lang/crates.io-index" 1820 + checksum = "11274e5e8e89b8607cfedc2910b6626e998779b48a019151c7604d0adcb86ac6" 1821 + dependencies = [ 1822 + "compare", 1823 + ] 1824 + 1825 + [[package]] 1826 + name = "inventory" 1827 + version = "0.3.24" 1828 + source = "registry+https://github.com/rust-lang/crates.io-index" 1829 + checksum = "a4f0c30c76f2f4ccee3fe55a2435f691ca00c0e4bd87abe4f4a851b1d4dac39b" 1830 + dependencies = [ 1831 + "rustversion", 1832 + ] 1833 + 1834 + [[package]] 1835 + name = "ipconfig" 1836 + version = "0.3.4" 1837 + source = "registry+https://github.com/rust-lang/crates.io-index" 1838 + checksum = "4d40460c0ce33d6ce4b0630ad68ff63d6661961c48b6dba35e5a4d81cfb48222" 1839 + dependencies = [ 1840 + "socket2", 1841 + "widestring", 1842 + "windows-registry", 1843 + "windows-result", 1844 + "windows-sys 0.61.2", 1845 + ] 1846 + 1847 + [[package]] 1848 + name = "ipld-core" 1849 + version = "0.4.3" 1850 + source = "registry+https://github.com/rust-lang/crates.io-index" 1851 + checksum = "090f624976d72f0b0bb71b86d58dc16c15e069193067cb3a3a09d655246cbbda" 1852 + dependencies = [ 1853 + "cid", 1854 + "serde", 1855 + "serde_bytes", 1856 + ] 1857 + 1858 + [[package]] 1859 + name = "ipnet" 1860 + version = "2.12.0" 1861 + source = "registry+https://github.com/rust-lang/crates.io-index" 1862 + checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" 1863 + 1864 + [[package]] 1865 + name = "iri-string" 1866 + version = "0.7.12" 1867 + source = "registry+https://github.com/rust-lang/crates.io-index" 1868 + checksum = "25e659a4bb38e810ebc252e53b5814ff908a8c58c2a9ce2fae1bbec24cbf4e20" 1869 + dependencies = [ 1870 + "memchr", 1871 + "serde", 1872 + ] 1873 + 1874 + [[package]] 1875 + name = "iroh-car" 1876 + version = "0.5.1" 1877 + source = "registry+https://github.com/rust-lang/crates.io-index" 1878 + checksum = "cb7f8cd4cb9aa083fba8b52e921764252d0b4dcb1cd6d120b809dbfe1106e81a" 1879 + dependencies = [ 1880 + "anyhow", 1881 + "cid", 1882 + "futures", 1883 + "serde", 1884 + "serde_ipld_dagcbor", 1885 + "thiserror 1.0.69", 1886 + "tokio", 1887 + "unsigned-varint 0.7.2", 1888 + ] 1889 + 1890 + [[package]] 1891 + name = "is_ci" 1892 + version = "1.2.0" 1893 + source = "registry+https://github.com/rust-lang/crates.io-index" 1894 + checksum = "7655c9839580ee829dfacba1d1278c2b7883e50a277ff7541299489d6bdfdc45" 1895 + 1896 + [[package]] 1897 + name = "itoa" 1898 + version = "1.0.18" 1899 + source = "registry+https://github.com/rust-lang/crates.io-index" 1900 + checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" 1901 + 1902 + [[package]] 1903 + name = "jacquard-api" 1904 + version = "0.11.1" 1905 + source = "registry+https://github.com/rust-lang/crates.io-index" 1906 + checksum = "f4bba022e9c632f737de481d7c894b5736dd1a503362876ca4da49b9255a7e61" 1907 + dependencies = [ 1908 + "jacquard-common", 1909 + "jacquard-derive", 1910 + "jacquard-lexicon", 1911 + "miette", 1912 + "serde", 1913 + "thiserror 2.0.18", 1914 + ] 1915 + 1916 + [[package]] 1917 + name = "jacquard-common" 1918 + version = "0.11.0" 1919 + source = "registry+https://github.com/rust-lang/crates.io-index" 1920 + checksum = "9631f08f1e65d19e204bc6774d00b4e0b69fb649d7d7ac59ccf97797bd9a196e" 1921 + dependencies = [ 1922 + "base64", 1923 + "bon", 1924 + "bytes", 1925 + "chrono", 1926 + "ciborium", 1927 + "ciborium-io", 1928 + "cid", 1929 + "ed25519-dalek", 1930 + "fluent-uri", 1931 + "futures", 1932 + "getrandom 0.2.17", 1933 + "getrandom 0.3.4", 1934 + "hashbrown 0.15.5", 1935 + "http", 1936 + "ipld-core", 1937 + "k256", 1938 + "maitake-sync", 1939 + "miette", 1940 + "multibase", 1941 + "multihash", 1942 + "n0-future", 1943 + "ouroboros", 1944 + "oxilangtag", 1945 + "p256", 1946 + "phf", 1947 + "postcard", 1948 + "rand 0.9.4", 1949 + "regex", 1950 + "regex-automata", 1951 + "regex-lite", 1952 + "reqwest", 1953 + "rustversion", 1954 + "serde", 1955 + "serde_bytes", 1956 + "serde_html_form", 1957 + "serde_ipld_dagcbor", 1958 + "serde_json", 1959 + "signature", 1960 + "smol_str", 1961 + "spin 0.10.0", 1962 + "thiserror 2.0.18", 1963 + "tokio", 1964 + "tokio-tungstenite-wasm", 1965 + "tokio-util", 1966 + "tracing", 1967 + "trait-variant", 1968 + "unicode-segmentation", 1969 + ] 1970 + 1971 + [[package]] 1972 + name = "jacquard-derive" 1973 + version = "0.11.0" 1974 + source = "registry+https://github.com/rust-lang/crates.io-index" 1975 + checksum = "22904bd0f9a959591e14ee9e1dab91a9110209cb8b292930e584134f96a83ece" 1976 + dependencies = [ 1977 + "heck 0.5.0", 1978 + "jacquard-lexicon", 1979 + "proc-macro2", 1980 + "quote", 1981 + "syn", 1982 + ] 1983 + 1984 + [[package]] 1985 + name = "jacquard-identity" 1986 + version = "0.11.0" 1987 + source = "registry+https://github.com/rust-lang/crates.io-index" 1988 + checksum = "b84a9302ea9dd39c49d748a8eba21faf365570088571ff327ebb00117dbd65b8" 1989 + dependencies = [ 1990 + "bon", 1991 + "bytes", 1992 + "hickory-resolver", 1993 + "http", 1994 + "jacquard-common", 1995 + "jacquard-lexicon", 1996 + "miette", 1997 + "n0-future", 1998 + "reqwest", 1999 + "serde", 2000 + "serde_html_form", 2001 + "serde_json", 2002 + "thiserror 2.0.18", 2003 + "tokio", 2004 + "tracing", 2005 + "trait-variant", 2006 + ] 2007 + 2008 + [[package]] 2009 + name = "jacquard-lexicon" 2010 + version = "0.11.1" 2011 + source = "registry+https://github.com/rust-lang/crates.io-index" 2012 + checksum = "fd7863d4f56a49f07391b5f775e82be12e6381156642ee83574f481ca73e8b0e" 2013 + dependencies = [ 2014 + "cid", 2015 + "dashmap", 2016 + "heck 0.5.0", 2017 + "inventory", 2018 + "jacquard-common", 2019 + "miette", 2020 + "multihash", 2021 + "prettyplease", 2022 + "proc-macro2", 2023 + "quote", 2024 + "serde", 2025 + "serde_ipld_dagcbor", 2026 + "serde_json", 2027 + "serde_path_to_error", 2028 + "serde_repr", 2029 + "serde_with", 2030 + "sha2", 2031 + "syn", 2032 + "thiserror 2.0.18", 2033 + "unicode-segmentation", 2034 + ] 2035 + 2036 + [[package]] 2037 + name = "jacquard-repo" 2038 + version = "0.11.0" 2039 + source = "registry+https://github.com/rust-lang/crates.io-index" 2040 + checksum = "b0c924640eed28b608767e034f7db741a4f3b33c4631db8defc33c5a05f4834e" 2041 + dependencies = [ 2042 + "bytes", 2043 + "cid", 2044 + "ed25519-dalek", 2045 + "iroh-car", 2046 + "jacquard-api", 2047 + "jacquard-common", 2048 + "jacquard-derive", 2049 + "k256", 2050 + "miette", 2051 + "multihash", 2052 + "n0-future", 2053 + "p256", 2054 + "serde", 2055 + "serde_bytes", 2056 + "serde_ipld_dagcbor", 2057 + "sha2", 2058 + "smol_str", 2059 + "thiserror 2.0.18", 2060 + "tokio", 2061 + "trait-variant", 2062 + ] 2063 + 2064 + [[package]] 2065 + name = "jobserver" 2066 + version = "0.1.34" 2067 + source = "registry+https://github.com/rust-lang/crates.io-index" 2068 + checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" 2069 + dependencies = [ 2070 + "getrandom 0.3.4", 2071 + "libc", 2072 + ] 2073 + 2074 + [[package]] 2075 + name = "js-sys" 2076 + version = "0.3.95" 2077 + source = "registry+https://github.com/rust-lang/crates.io-index" 2078 + checksum = "2964e92d1d9dc3364cae4d718d93f227e3abb088e747d92e0395bfdedf1c12ca" 2079 + dependencies = [ 2080 + "cfg-if", 2081 + "futures-util", 2082 + "once_cell", 2083 + "wasm-bindgen", 2084 + ] 2085 + 2086 + [[package]] 2087 + name = "k256" 2088 + version = "0.13.4" 2089 + source = "registry+https://github.com/rust-lang/crates.io-index" 2090 + checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b" 2091 + dependencies = [ 2092 + "cfg-if", 2093 + "ecdsa", 2094 + "elliptic-curve", 2095 + "once_cell", 2096 + "sha2", 2097 + "signature", 2098 + ] 2099 + 2100 + [[package]] 2101 + name = "lazy_static" 2102 + version = "1.5.0" 2103 + source = "registry+https://github.com/rust-lang/crates.io-index" 2104 + checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" 2105 + 2106 + [[package]] 2107 + name = "leb128fmt" 2108 + version = "0.1.0" 2109 + source = "registry+https://github.com/rust-lang/crates.io-index" 2110 + checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" 2111 + 2112 + [[package]] 2113 + name = "libc" 2114 + version = "0.2.186" 2115 + source = "registry+https://github.com/rust-lang/crates.io-index" 2116 + checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" 2117 + 2118 + [[package]] 2119 + name = "libmimalloc-sys" 2120 + version = "0.1.44" 2121 + source = "registry+https://github.com/rust-lang/crates.io-index" 2122 + checksum = "667f4fec20f29dfc6bc7357c582d91796c169ad7e2fce709468aefeb2c099870" 2123 + dependencies = [ 2124 + "cc", 2125 + "libc", 2126 + ] 2127 + 2128 + [[package]] 2129 + name = "linked-hash-map" 2130 + version = "0.5.6" 2131 + source = "registry+https://github.com/rust-lang/crates.io-index" 2132 + checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" 2133 + 2134 + [[package]] 2135 + name = "linux-raw-sys" 2136 + version = "0.12.1" 2137 + source = "registry+https://github.com/rust-lang/crates.io-index" 2138 + checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" 2139 + 2140 + [[package]] 2141 + name = "litemap" 2142 + version = "0.8.2" 2143 + source = "registry+https://github.com/rust-lang/crates.io-index" 2144 + checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" 2145 + 2146 + [[package]] 2147 + name = "lock_api" 2148 + version = "0.4.14" 2149 + source = "registry+https://github.com/rust-lang/crates.io-index" 2150 + checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" 2151 + dependencies = [ 2152 + "scopeguard", 2153 + ] 2154 + 2155 + [[package]] 2156 + name = "log" 2157 + version = "0.4.29" 2158 + source = "registry+https://github.com/rust-lang/crates.io-index" 2159 + checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" 2160 + 2161 + [[package]] 2162 + name = "loom" 2163 + version = "0.7.2" 2164 + source = "registry+https://github.com/rust-lang/crates.io-index" 2165 + checksum = "419e0dc8046cb947daa77eb95ae174acfbddb7673b4151f56d1eed8e93fbfaca" 2166 + dependencies = [ 2167 + "cfg-if", 2168 + "generator", 2169 + "scoped-tls", 2170 + "tracing", 2171 + "tracing-subscriber", 2172 + ] 2173 + 2174 + [[package]] 2175 + name = "lru-cache" 2176 + version = "0.1.2" 2177 + source = "registry+https://github.com/rust-lang/crates.io-index" 2178 + checksum = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" 2179 + dependencies = [ 2180 + "linked-hash-map", 2181 + ] 2182 + 2183 + [[package]] 2184 + name = "lru-slab" 2185 + version = "0.1.2" 2186 + source = "registry+https://github.com/rust-lang/crates.io-index" 2187 + checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" 2188 + 2189 + [[package]] 2190 + name = "lsm-tree" 2191 + version = "3.1.4" 2192 + source = "git+https://github.com/90-008/lsm-tree.git#bff28391c95d708c68805188c36c6654cdb7d9cd" 2193 + dependencies = [ 2194 + "byteorder-lite", 2195 + "byteview", 2196 + "crossbeam-skiplist", 2197 + "enum_dispatch", 2198 + "interval-heap", 2199 + "log", 2200 + "lz4_flex", 2201 + "quick_cache", 2202 + "rustc-hash", 2203 + "self_cell", 2204 + "sfa", 2205 + "tempfile", 2206 + "varint-rs", 2207 + "xxhash-rust", 2208 + "zstd", 2209 + ] 2210 + 2211 + [[package]] 2212 + name = "lz4_flex" 2213 + version = "0.13.0" 2214 + source = "registry+https://github.com/rust-lang/crates.io-index" 2215 + checksum = "db9a0d582c2874f68138a16ce1867e0ffde6c0bb0a0df85e1f36d04146db488a" 2216 + dependencies = [ 2217 + "twox-hash", 2218 + ] 2219 + 2220 + [[package]] 2221 + name = "maitake-sync" 2222 + version = "0.1.2" 2223 + source = "registry+https://github.com/rust-lang/crates.io-index" 2224 + checksum = "6816ab14147f80234c675b80ed6dc4f440d8a1cefc158e766067aedb84c0bcd5" 2225 + dependencies = [ 2226 + "cordyceps", 2227 + "loom", 2228 + "mycelium-bitfield", 2229 + "pin-project", 2230 + "portable-atomic", 2231 + ] 2232 + 2233 + [[package]] 2234 + name = "match-lookup" 2235 + version = "0.1.2" 2236 + source = "registry+https://github.com/rust-lang/crates.io-index" 2237 + checksum = "757aee279b8bdbb9f9e676796fd459e4207a1f986e87886700abf589f5abf771" 2238 + dependencies = [ 2239 + "proc-macro2", 2240 + "quote", 2241 + "syn", 2242 + ] 2243 + 2244 + [[package]] 2245 + name = "matchers" 2246 + version = "0.2.0" 2247 + source = "registry+https://github.com/rust-lang/crates.io-index" 2248 + checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9" 2249 + dependencies = [ 2250 + "regex-automata", 2251 + ] 2252 + 2253 + [[package]] 2254 + name = "matchit" 2255 + version = "0.8.4" 2256 + source = "registry+https://github.com/rust-lang/crates.io-index" 2257 + checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" 2258 + 2259 + [[package]] 2260 + name = "memchr" 2261 + version = "2.8.0" 2262 + source = "registry+https://github.com/rust-lang/crates.io-index" 2263 + checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" 2264 + 2265 + [[package]] 2266 + name = "miette" 2267 + version = "7.6.0" 2268 + source = "registry+https://github.com/rust-lang/crates.io-index" 2269 + checksum = "5f98efec8807c63c752b5bd61f862c165c115b0a35685bdcfd9238c7aeb592b7" 2270 + dependencies = [ 2271 + "backtrace", 2272 + "backtrace-ext", 2273 + "cfg-if", 2274 + "miette-derive", 2275 + "owo-colors", 2276 + "supports-color", 2277 + "supports-hyperlinks", 2278 + "supports-unicode", 2279 + "terminal_size", 2280 + "textwrap", 2281 + "unicode-width 0.1.14", 2282 + ] 2283 + 2284 + [[package]] 2285 + name = "miette-derive" 2286 + version = "7.6.0" 2287 + source = "registry+https://github.com/rust-lang/crates.io-index" 2288 + checksum = "db5b29714e950dbb20d5e6f74f9dcec4edbcc1067bb7f8ed198c097b8c1a818b" 2289 + dependencies = [ 2290 + "proc-macro2", 2291 + "quote", 2292 + "syn", 2293 + ] 2294 + 2295 + [[package]] 2296 + name = "mimalloc" 2297 + version = "0.1.48" 2298 + source = "registry+https://github.com/rust-lang/crates.io-index" 2299 + checksum = "e1ee66a4b64c74f4ef288bcbb9192ad9c3feaad75193129ac8509af543894fd8" 2300 + dependencies = [ 2301 + "libmimalloc-sys", 2302 + ] 2303 + 2304 + [[package]] 2305 + name = "mime" 2306 + version = "0.3.17" 2307 + source = "registry+https://github.com/rust-lang/crates.io-index" 2308 + checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" 2309 + 2310 + [[package]] 2311 + name = "miniz_oxide" 2312 + version = "0.8.9" 2313 + source = "registry+https://github.com/rust-lang/crates.io-index" 2314 + checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" 2315 + dependencies = [ 2316 + "adler2", 2317 + "simd-adler32", 2318 + ] 2319 + 2320 + [[package]] 2321 + name = "mio" 2322 + version = "1.2.0" 2323 + source = "registry+https://github.com/rust-lang/crates.io-index" 2324 + checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1" 2325 + dependencies = [ 2326 + "libc", 2327 + "wasi", 2328 + "windows-sys 0.61.2", 2329 + ] 2330 + 2331 + [[package]] 2332 + name = "multibase" 2333 + version = "0.9.2" 2334 + source = "registry+https://github.com/rust-lang/crates.io-index" 2335 + checksum = "8694bb4835f452b0e3bb06dbebb1d6fc5385b6ca1caf2e55fd165c042390ec77" 2336 + dependencies = [ 2337 + "base-x", 2338 + "base256emoji", 2339 + "data-encoding", 2340 + "data-encoding-macro", 2341 + ] 2342 + 2343 + [[package]] 2344 + name = "multihash" 2345 + version = "0.19.4" 2346 + source = "registry+https://github.com/rust-lang/crates.io-index" 2347 + checksum = "89ace881e3f514092ce9efbcb8f413d0ad9763860b828981c2de51ddc666936c" 2348 + dependencies = [ 2349 + "no_std_io2", 2350 + "serde", 2351 + "unsigned-varint 0.8.0", 2352 + ] 2353 + 2354 + [[package]] 2355 + name = "mycelium-bitfield" 2356 + version = "0.1.5" 2357 + source = "registry+https://github.com/rust-lang/crates.io-index" 2358 + checksum = "24e0cc5e2c585acbd15c5ce911dff71e1f4d5313f43345873311c4f5efd741cc" 2359 + 2360 + [[package]] 2361 + name = "n0-future" 2362 + version = "0.1.3" 2363 + source = "registry+https://github.com/rust-lang/crates.io-index" 2364 + checksum = "7bb0e5d99e681ab3c938842b96fcb41bf8a7bb4bfdb11ccbd653a7e83e06c794" 2365 + dependencies = [ 2366 + "cfg_aliases", 2367 + "derive_more", 2368 + "futures-buffered", 2369 + "futures-lite", 2370 + "futures-util", 2371 + "js-sys", 2372 + "pin-project", 2373 + "send_wrapper", 2374 + "tokio", 2375 + "tokio-util", 2376 + "wasm-bindgen", 2377 + "wasm-bindgen-futures", 2378 + "web-time", 2379 + ] 2380 + 2381 + [[package]] 2382 + name = "native-tls" 2383 + version = "0.2.18" 2384 + source = "registry+https://github.com/rust-lang/crates.io-index" 2385 + checksum = "465500e14ea162429d264d44189adc38b199b62b1c21eea9f69e4b73cb03bbf2" 2386 + dependencies = [ 2387 + "libc", 2388 + "log", 2389 + "openssl", 2390 + "openssl-probe", 2391 + "openssl-sys", 2392 + "schannel", 2393 + "security-framework", 2394 + "security-framework-sys", 2395 + "tempfile", 2396 + ] 2397 + 2398 + [[package]] 2399 + name = "no_std_io2" 2400 + version = "0.8.1" 2401 + source = "registry+https://github.com/rust-lang/crates.io-index" 2402 + checksum = "8a3564ce7035b1e4778d8cb6cacebb5d766b5e8fe5a75b9e441e33fb61a872c6" 2403 + dependencies = [ 2404 + "memchr", 2405 + ] 2406 + 2407 + [[package]] 2408 + name = "nohash-hasher" 2409 + version = "0.2.0" 2410 + source = "registry+https://github.com/rust-lang/crates.io-index" 2411 + checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" 2412 + 2413 + [[package]] 2414 + name = "nu-ansi-term" 2415 + version = "0.50.3" 2416 + source = "registry+https://github.com/rust-lang/crates.io-index" 2417 + checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" 2418 + dependencies = [ 2419 + "windows-sys 0.61.2", 2420 + ] 2421 + 2422 + [[package]] 2423 + name = "num-conv" 2424 + version = "0.2.1" 2425 + source = "registry+https://github.com/rust-lang/crates.io-index" 2426 + checksum = "c6673768db2d862beb9b39a78fdcb1a69439615d5794a1be50caa9bc92c81967" 2427 + 2428 + [[package]] 2429 + name = "num-traits" 2430 + version = "0.2.19" 2431 + source = "registry+https://github.com/rust-lang/crates.io-index" 2432 + checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" 2433 + dependencies = [ 2434 + "autocfg", 2435 + ] 2436 + 2437 + [[package]] 2438 + name = "object" 2439 + version = "0.37.3" 2440 + source = "registry+https://github.com/rust-lang/crates.io-index" 2441 + checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" 2442 + dependencies = [ 2443 + "memchr", 2444 + ] 2445 + 2446 + [[package]] 2447 + name = "once_cell" 2448 + version = "1.21.4" 2449 + source = "registry+https://github.com/rust-lang/crates.io-index" 2450 + checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" 2451 + 2452 + [[package]] 2453 + name = "openssl" 2454 + version = "0.10.78" 2455 + source = "registry+https://github.com/rust-lang/crates.io-index" 2456 + checksum = "f38c4372413cdaaf3cc79dd92d29d7d9f5ab09b51b10dded508fb90bb70b9222" 2457 + dependencies = [ 2458 + "bitflags", 2459 + "cfg-if", 2460 + "foreign-types", 2461 + "libc", 2462 + "once_cell", 2463 + "openssl-macros", 2464 + "openssl-sys", 2465 + ] 2466 + 2467 + [[package]] 2468 + name = "openssl-macros" 2469 + version = "0.1.1" 2470 + source = "registry+https://github.com/rust-lang/crates.io-index" 2471 + checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" 2472 + dependencies = [ 2473 + "proc-macro2", 2474 + "quote", 2475 + "syn", 2476 + ] 2477 + 2478 + [[package]] 2479 + name = "openssl-probe" 2480 + version = "0.2.1" 2481 + source = "registry+https://github.com/rust-lang/crates.io-index" 2482 + checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" 2483 + 2484 + [[package]] 2485 + name = "openssl-sys" 2486 + version = "0.9.114" 2487 + source = "registry+https://github.com/rust-lang/crates.io-index" 2488 + checksum = "13ce1245cd07fcc4cfdb438f7507b0c7e4f3849a69fd84d52374c66d83741bb6" 2489 + dependencies = [ 2490 + "cc", 2491 + "libc", 2492 + "pkg-config", 2493 + "vcpkg", 2494 + ] 2495 + 2496 + [[package]] 2497 + name = "ouroboros" 2498 + version = "0.18.5" 2499 + source = "registry+https://github.com/rust-lang/crates.io-index" 2500 + checksum = "1e0f050db9c44b97a94723127e6be766ac5c340c48f2c4bb3ffa11713744be59" 2501 + dependencies = [ 2502 + "aliasable", 2503 + "ouroboros_macro", 2504 + "static_assertions", 2505 + ] 2506 + 2507 + [[package]] 2508 + name = "ouroboros_macro" 2509 + version = "0.18.5" 2510 + source = "registry+https://github.com/rust-lang/crates.io-index" 2511 + checksum = "3c7028bdd3d43083f6d8d4d5187680d0d3560d54df4cc9d752005268b41e64d0" 2512 + dependencies = [ 2513 + "heck 0.4.1", 2514 + "proc-macro2", 2515 + "proc-macro2-diagnostics", 2516 + "quote", 2517 + "syn", 2518 + ] 2519 + 2520 + [[package]] 2521 + name = "owo-colors" 2522 + version = "4.3.0" 2523 + source = "registry+https://github.com/rust-lang/crates.io-index" 2524 + checksum = "d211803b9b6b570f68772237e415a029d5a50c65d382910b879fb19d3271f94d" 2525 + 2526 + [[package]] 2527 + name = "oxilangtag" 2528 + version = "0.1.5" 2529 + source = "registry+https://github.com/rust-lang/crates.io-index" 2530 + checksum = "23f3f87617a86af77fa3691e6350483e7154c2ead9f1261b75130e21ca0f8acb" 2531 + dependencies = [ 2532 + "serde", 2533 + ] 2534 + 2535 + [[package]] 2536 + name = "p256" 2537 + version = "0.13.2" 2538 + source = "registry+https://github.com/rust-lang/crates.io-index" 2539 + checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" 2540 + dependencies = [ 2541 + "ecdsa", 2542 + "elliptic-curve", 2543 + "primeorder", 2544 + "sha2", 2545 + ] 2546 + 2547 + [[package]] 2548 + name = "parking" 2549 + version = "2.2.1" 2550 + source = "registry+https://github.com/rust-lang/crates.io-index" 2551 + checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" 2552 + 2553 + [[package]] 2554 + name = "parking_lot" 2555 + version = "0.12.5" 2556 + source = "registry+https://github.com/rust-lang/crates.io-index" 2557 + checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" 2558 + dependencies = [ 2559 + "lock_api", 2560 + "parking_lot_core", 2561 + ] 2562 + 2563 + [[package]] 2564 + name = "parking_lot_core" 2565 + version = "0.9.12" 2566 + source = "registry+https://github.com/rust-lang/crates.io-index" 2567 + checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" 2568 + dependencies = [ 2569 + "cfg-if", 2570 + "libc", 2571 + "redox_syscall", 2572 + "smallvec", 2573 + "windows-link", 2574 + ] 2575 + 2576 + [[package]] 2577 + name = "pem-rfc7468" 2578 + version = "0.7.0" 2579 + source = "registry+https://github.com/rust-lang/crates.io-index" 2580 + checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" 2581 + dependencies = [ 2582 + "base64ct", 2583 + ] 2584 + 2585 + [[package]] 2586 + name = "percent-encoding" 2587 + version = "2.3.2" 2588 + source = "registry+https://github.com/rust-lang/crates.io-index" 2589 + checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" 2590 + 2591 + [[package]] 2592 + name = "phf" 2593 + version = "0.11.3" 2594 + source = "registry+https://github.com/rust-lang/crates.io-index" 2595 + checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" 2596 + dependencies = [ 2597 + "phf_macros", 2598 + "phf_shared", 2599 + ] 2600 + 2601 + [[package]] 2602 + name = "phf_generator" 2603 + version = "0.11.3" 2604 + source = "registry+https://github.com/rust-lang/crates.io-index" 2605 + checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" 2606 + dependencies = [ 2607 + "phf_shared", 2608 + "rand 0.8.6", 2609 + ] 2610 + 2611 + [[package]] 2612 + name = "phf_macros" 2613 + version = "0.11.3" 2614 + source = "registry+https://github.com/rust-lang/crates.io-index" 2615 + checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216" 2616 + dependencies = [ 2617 + "phf_generator", 2618 + "phf_shared", 2619 + "proc-macro2", 2620 + "quote", 2621 + "syn", 2622 + ] 2623 + 2624 + [[package]] 2625 + name = "phf_shared" 2626 + version = "0.11.3" 2627 + source = "registry+https://github.com/rust-lang/crates.io-index" 2628 + checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" 2629 + dependencies = [ 2630 + "siphasher", 2631 + ] 2632 + 2633 + [[package]] 2634 + name = "pin-project" 2635 + version = "1.1.11" 2636 + source = "registry+https://github.com/rust-lang/crates.io-index" 2637 + checksum = "f1749c7ed4bcaf4c3d0a3efc28538844fb29bcdd7d2b67b2be7e20ba861ff517" 2638 + dependencies = [ 2639 + "pin-project-internal", 2640 + ] 2641 + 2642 + [[package]] 2643 + name = "pin-project-internal" 2644 + version = "1.1.11" 2645 + source = "registry+https://github.com/rust-lang/crates.io-index" 2646 + checksum = "d9b20ed30f105399776b9c883e68e536ef602a16ae6f596d2c473591d6ad64c6" 2647 + dependencies = [ 2648 + "proc-macro2", 2649 + "quote", 2650 + "syn", 2651 + ] 2652 + 2653 + [[package]] 2654 + name = "pin-project-lite" 2655 + version = "0.2.17" 2656 + source = "registry+https://github.com/rust-lang/crates.io-index" 2657 + checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" 2658 + 2659 + [[package]] 2660 + name = "pkcs8" 2661 + version = "0.10.2" 2662 + source = "registry+https://github.com/rust-lang/crates.io-index" 2663 + checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" 2664 + dependencies = [ 2665 + "der", 2666 + "spki", 2667 + ] 2668 + 2669 + [[package]] 2670 + name = "pkg-config" 2671 + version = "0.3.33" 2672 + source = "registry+https://github.com/rust-lang/crates.io-index" 2673 + checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" 2674 + 2675 + [[package]] 2676 + name = "portable-atomic" 2677 + version = "1.13.1" 2678 + source = "registry+https://github.com/rust-lang/crates.io-index" 2679 + checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" 2680 + 2681 + [[package]] 2682 + name = "postcard" 2683 + version = "1.1.3" 2684 + source = "registry+https://github.com/rust-lang/crates.io-index" 2685 + checksum = "6764c3b5dd454e283a30e6dfe78e9b31096d9e32036b5d1eaac7a6119ccb9a24" 2686 + dependencies = [ 2687 + "cobs", 2688 + "embedded-io 0.4.0", 2689 + "embedded-io 0.6.1", 2690 + "heapless", 2691 + "serde", 2692 + ] 2693 + 2694 + [[package]] 2695 + name = "potential_utf" 2696 + version = "0.1.5" 2697 + source = "registry+https://github.com/rust-lang/crates.io-index" 2698 + checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564" 2699 + dependencies = [ 2700 + "zerovec", 2701 + ] 2702 + 2703 + [[package]] 2704 + name = "powerfmt" 2705 + version = "0.2.0" 2706 + source = "registry+https://github.com/rust-lang/crates.io-index" 2707 + checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" 2708 + 2709 + [[package]] 2710 + name = "ppv-lite86" 2711 + version = "0.2.21" 2712 + source = "registry+https://github.com/rust-lang/crates.io-index" 2713 + checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" 2714 + dependencies = [ 2715 + "zerocopy", 2716 + ] 2717 + 2718 + [[package]] 2719 + name = "prettyplease" 2720 + version = "0.2.37" 2721 + source = "registry+https://github.com/rust-lang/crates.io-index" 2722 + checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" 2723 + dependencies = [ 2724 + "proc-macro2", 2725 + "syn", 2726 + ] 2727 + 2728 + [[package]] 2729 + name = "primeorder" 2730 + version = "0.13.6" 2731 + source = "registry+https://github.com/rust-lang/crates.io-index" 2732 + checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" 2733 + dependencies = [ 2734 + "elliptic-curve", 2735 + ] 2736 + 2737 + [[package]] 2738 + name = "proc-macro2" 2739 + version = "1.0.106" 2740 + source = "registry+https://github.com/rust-lang/crates.io-index" 2741 + checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" 2742 + dependencies = [ 2743 + "unicode-ident", 2744 + ] 2745 + 2746 + [[package]] 2747 + name = "proc-macro2-diagnostics" 2748 + version = "0.10.1" 2749 + source = "registry+https://github.com/rust-lang/crates.io-index" 2750 + checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" 2751 + dependencies = [ 2752 + "proc-macro2", 2753 + "quote", 2754 + "syn", 2755 + "version_check", 2756 + "yansi", 2757 + ] 2758 + 2759 + [[package]] 2760 + name = "quick_cache" 2761 + version = "0.6.21" 2762 + source = "registry+https://github.com/rust-lang/crates.io-index" 2763 + checksum = "5a70b1b8b47e31d0498ecbc3c5470bb931399a8bfed1fd79d1717a61ce7f96e3" 2764 + dependencies = [ 2765 + "equivalent", 2766 + "hashbrown 0.16.1", 2767 + ] 2768 + 2769 + [[package]] 2770 + name = "quinn" 2771 + version = "0.11.9" 2772 + source = "registry+https://github.com/rust-lang/crates.io-index" 2773 + checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" 2774 + dependencies = [ 2775 + "bytes", 2776 + "cfg_aliases", 2777 + "pin-project-lite", 2778 + "quinn-proto", 2779 + "quinn-udp", 2780 + "rustc-hash", 2781 + "rustls", 2782 + "socket2", 2783 + "thiserror 2.0.18", 2784 + "tokio", 2785 + "tracing", 2786 + "web-time", 2787 + ] 2788 + 2789 + [[package]] 2790 + name = "quinn-proto" 2791 + version = "0.11.14" 2792 + source = "registry+https://github.com/rust-lang/crates.io-index" 2793 + checksum = "434b42fec591c96ef50e21e886936e66d3cc3f737104fdb9b737c40ffb94c098" 2794 + dependencies = [ 2795 + "bytes", 2796 + "getrandom 0.3.4", 2797 + "lru-slab", 2798 + "rand 0.9.4", 2799 + "ring", 2800 + "rustc-hash", 2801 + "rustls", 2802 + "rustls-pki-types", 2803 + "slab", 2804 + "thiserror 2.0.18", 2805 + "tinyvec", 2806 + "tracing", 2807 + "web-time", 2808 + ] 2809 + 2810 + [[package]] 2811 + name = "quinn-udp" 2812 + version = "0.5.14" 2813 + source = "registry+https://github.com/rust-lang/crates.io-index" 2814 + checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" 2815 + dependencies = [ 2816 + "cfg_aliases", 2817 + "libc", 2818 + "once_cell", 2819 + "socket2", 2820 + "tracing", 2821 + "windows-sys 0.60.2", 2822 + ] 2823 + 2824 + [[package]] 2825 + name = "quote" 2826 + version = "1.0.45" 2827 + source = "registry+https://github.com/rust-lang/crates.io-index" 2828 + checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" 2829 + dependencies = [ 2830 + "proc-macro2", 2831 + ] 2832 + 2833 + [[package]] 2834 + name = "r-efi" 2835 + version = "5.3.0" 2836 + source = "registry+https://github.com/rust-lang/crates.io-index" 2837 + checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" 2838 + 2839 + [[package]] 2840 + name = "r-efi" 2841 + version = "6.0.0" 2842 + source = "registry+https://github.com/rust-lang/crates.io-index" 2843 + checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" 2844 + 2845 + [[package]] 2846 + name = "rand" 2847 + version = "0.8.6" 2848 + source = "registry+https://github.com/rust-lang/crates.io-index" 2849 + checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a" 2850 + dependencies = [ 2851 + "libc", 2852 + "rand_chacha 0.3.1", 2853 + "rand_core 0.6.4", 2854 + ] 2855 + 2856 + [[package]] 2857 + name = "rand" 2858 + version = "0.9.4" 2859 + source = "registry+https://github.com/rust-lang/crates.io-index" 2860 + checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea" 2861 + dependencies = [ 2862 + "rand_chacha 0.9.0", 2863 + "rand_core 0.9.5", 2864 + ] 2865 + 2866 + [[package]] 2867 + name = "rand" 2868 + version = "0.10.1" 2869 + source = "registry+https://github.com/rust-lang/crates.io-index" 2870 + checksum = "d2e8e8bcc7961af1fdac401278c6a831614941f6164ee3bf4ce61b7edb162207" 2871 + dependencies = [ 2872 + "chacha20", 2873 + "getrandom 0.4.2", 2874 + "rand_core 0.10.1", 2875 + ] 2876 + 2877 + [[package]] 2878 + name = "rand_chacha" 2879 + version = "0.3.1" 2880 + source = "registry+https://github.com/rust-lang/crates.io-index" 2881 + checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" 2882 + dependencies = [ 2883 + "ppv-lite86", 2884 + "rand_core 0.6.4", 2885 + ] 2886 + 2887 + [[package]] 2888 + name = "rand_chacha" 2889 + version = "0.9.0" 2890 + source = "registry+https://github.com/rust-lang/crates.io-index" 2891 + checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" 2892 + dependencies = [ 2893 + "ppv-lite86", 2894 + "rand_core 0.9.5", 2895 + ] 2896 + 2897 + [[package]] 2898 + name = "rand_core" 2899 + version = "0.6.4" 2900 + source = "registry+https://github.com/rust-lang/crates.io-index" 2901 + checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" 2902 + dependencies = [ 2903 + "getrandom 0.2.17", 2904 + ] 2905 + 2906 + [[package]] 2907 + name = "rand_core" 2908 + version = "0.9.5" 2909 + source = "registry+https://github.com/rust-lang/crates.io-index" 2910 + checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" 2911 + dependencies = [ 2912 + "getrandom 0.3.4", 2913 + ] 2914 + 2915 + [[package]] 2916 + name = "rand_core" 2917 + version = "0.10.1" 2918 + source = "registry+https://github.com/rust-lang/crates.io-index" 2919 + checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" 2920 + 2921 + [[package]] 2922 + name = "redox_syscall" 2923 + version = "0.5.18" 2924 + source = "registry+https://github.com/rust-lang/crates.io-index" 2925 + checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" 2926 + dependencies = [ 2927 + "bitflags", 2928 + ] 2929 + 2930 + [[package]] 2931 + name = "ref-cast" 2932 + version = "1.0.25" 2933 + source = "registry+https://github.com/rust-lang/crates.io-index" 2934 + checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d" 2935 + dependencies = [ 2936 + "ref-cast-impl", 2937 + ] 2938 + 2939 + [[package]] 2940 + name = "ref-cast-impl" 2941 + version = "1.0.25" 2942 + source = "registry+https://github.com/rust-lang/crates.io-index" 2943 + checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" 2944 + dependencies = [ 2945 + "proc-macro2", 2946 + "quote", 2947 + "syn", 2948 + ] 2949 + 2950 + [[package]] 2951 + name = "regex" 2952 + version = "1.12.3" 2953 + source = "registry+https://github.com/rust-lang/crates.io-index" 2954 + checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" 2955 + dependencies = [ 2956 + "aho-corasick", 2957 + "memchr", 2958 + "regex-automata", 2959 + "regex-syntax", 2960 + ] 2961 + 2962 + [[package]] 2963 + name = "regex-automata" 2964 + version = "0.4.14" 2965 + source = "registry+https://github.com/rust-lang/crates.io-index" 2966 + checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" 2967 + dependencies = [ 2968 + "aho-corasick", 2969 + "memchr", 2970 + "regex-syntax", 2971 + ] 2972 + 2973 + [[package]] 2974 + name = "regex-lite" 2975 + version = "0.1.9" 2976 + source = "registry+https://github.com/rust-lang/crates.io-index" 2977 + checksum = "cab834c73d247e67f4fae452806d17d3c7501756d98c8808d7c9c7aa7d18f973" 2978 + 2979 + [[package]] 2980 + name = "regex-syntax" 2981 + version = "0.8.10" 2982 + source = "registry+https://github.com/rust-lang/crates.io-index" 2983 + checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" 2984 + 2985 + [[package]] 2986 + name = "reqwest" 2987 + version = "0.12.28" 2988 + source = "registry+https://github.com/rust-lang/crates.io-index" 2989 + checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" 2990 + dependencies = [ 2991 + "base64", 2992 + "bytes", 2993 + "encoding_rs", 2994 + "futures-core", 2995 + "futures-util", 2996 + "h2", 2997 + "http", 2998 + "http-body", 2999 + "http-body-util", 3000 + "hyper", 3001 + "hyper-rustls", 3002 + "hyper-tls", 3003 + "hyper-util", 3004 + "js-sys", 3005 + "log", 3006 + "mime", 3007 + "native-tls", 3008 + "percent-encoding", 3009 + "pin-project-lite", 3010 + "quinn", 3011 + "rustls", 3012 + "rustls-pki-types", 3013 + "serde", 3014 + "serde_json", 3015 + "serde_urlencoded", 3016 + "sync_wrapper", 3017 + "tokio", 3018 + "tokio-native-tls", 3019 + "tokio-rustls", 3020 + "tokio-util", 3021 + "tower", 3022 + "tower-http", 3023 + "tower-service", 3024 + "url", 3025 + "wasm-bindgen", 3026 + "wasm-bindgen-futures", 3027 + "wasm-streams", 3028 + "web-sys", 3029 + "webpki-roots", 3030 + ] 3031 + 3032 + [[package]] 3033 + name = "resolv-conf" 3034 + version = "0.7.6" 3035 + source = "registry+https://github.com/rust-lang/crates.io-index" 3036 + checksum = "1e061d1b48cb8d38042de4ae0a7a6401009d6143dc80d2e2d6f31f0bdd6470c7" 3037 + 3038 + [[package]] 3039 + name = "rfc6979" 3040 + version = "0.4.0" 3041 + source = "registry+https://github.com/rust-lang/crates.io-index" 3042 + checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" 3043 + dependencies = [ 3044 + "hmac", 3045 + "subtle", 3046 + ] 3047 + 3048 + [[package]] 3049 + name = "ring" 3050 + version = "0.17.14" 3051 + source = "registry+https://github.com/rust-lang/crates.io-index" 3052 + checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" 3053 + dependencies = [ 3054 + "cc", 3055 + "cfg-if", 3056 + "getrandom 0.2.17", 3057 + "libc", 3058 + "untrusted", 3059 + "windows-sys 0.52.0", 3060 + ] 3061 + 3062 + [[package]] 3063 + name = "rmp" 3064 + version = "0.8.15" 3065 + source = "git+https://github.com/90-008/msgpack-rust.git#19c54a922301d06310a816c3a6a097aeb73ea3ae" 3066 + dependencies = [ 3067 + "num-traits", 3068 + ] 3069 + 3070 + [[package]] 3071 + name = "rmp-serde" 3072 + version = "1.3.1" 3073 + source = "git+https://github.com/90-008/msgpack-rust.git#19c54a922301d06310a816c3a6a097aeb73ea3ae" 3074 + dependencies = [ 3075 + "rmp", 3076 + "serde", 3077 + ] 3078 + 3079 + [[package]] 3080 + name = "rustc-demangle" 3081 + version = "0.1.27" 3082 + source = "registry+https://github.com/rust-lang/crates.io-index" 3083 + checksum = "b50b8869d9fc858ce7266cce0194bd74df58b9d0e3f6df3a9fc8eb470d95c09d" 3084 + 3085 + [[package]] 3086 + name = "rustc-hash" 3087 + version = "2.1.2" 3088 + source = "registry+https://github.com/rust-lang/crates.io-index" 3089 + checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" 3090 + 3091 + [[package]] 3092 + name = "rustc_version" 3093 + version = "0.4.1" 3094 + source = "registry+https://github.com/rust-lang/crates.io-index" 3095 + checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" 3096 + dependencies = [ 3097 + "semver", 3098 + ] 3099 + 3100 + [[package]] 3101 + name = "rustix" 3102 + version = "1.1.4" 3103 + source = "registry+https://github.com/rust-lang/crates.io-index" 3104 + checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" 3105 + dependencies = [ 3106 + "bitflags", 3107 + "errno", 3108 + "libc", 3109 + "linux-raw-sys", 3110 + "windows-sys 0.61.2", 3111 + ] 3112 + 3113 + [[package]] 3114 + name = "rustls" 3115 + version = "0.23.39" 3116 + source = "registry+https://github.com/rust-lang/crates.io-index" 3117 + checksum = "7c2c118cb077cca2822033836dfb1b975355dfb784b5e8da48f7b6c5db74e60e" 3118 + dependencies = [ 3119 + "aws-lc-rs", 3120 + "log", 3121 + "once_cell", 3122 + "ring", 3123 + "rustls-pki-types", 3124 + "rustls-webpki", 3125 + "subtle", 3126 + "zeroize", 3127 + ] 3128 + 3129 + [[package]] 3130 + name = "rustls-native-certs" 3131 + version = "0.8.3" 3132 + source = "registry+https://github.com/rust-lang/crates.io-index" 3133 + checksum = "612460d5f7bea540c490b2b6395d8e34a953e52b491accd6c86c8164c5932a63" 3134 + dependencies = [ 3135 + "openssl-probe", 3136 + "rustls-pki-types", 3137 + "schannel", 3138 + "security-framework", 3139 + ] 3140 + 3141 + [[package]] 3142 + name = "rustls-pki-types" 3143 + version = "1.14.1" 3144 + source = "registry+https://github.com/rust-lang/crates.io-index" 3145 + checksum = "30a7197ae7eb376e574fe940d068c30fe0462554a3ddbe4eca7838e049c937a9" 3146 + dependencies = [ 3147 + "web-time", 3148 + "zeroize", 3149 + ] 3150 + 3151 + [[package]] 3152 + name = "rustls-webpki" 3153 + version = "0.103.13" 3154 + source = "registry+https://github.com/rust-lang/crates.io-index" 3155 + checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e" 3156 + dependencies = [ 3157 + "aws-lc-rs", 3158 + "ring", 3159 + "rustls-pki-types", 3160 + "untrusted", 3161 + ] 3162 + 3163 + [[package]] 3164 + name = "rustversion" 3165 + version = "1.0.22" 3166 + source = "registry+https://github.com/rust-lang/crates.io-index" 3167 + checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" 3168 + 3169 + [[package]] 3170 + name = "ryu" 3171 + version = "1.0.23" 3172 + source = "registry+https://github.com/rust-lang/crates.io-index" 3173 + checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" 3174 + 3175 + [[package]] 3176 + name = "saa" 3177 + version = "5.5.1" 3178 + source = "registry+https://github.com/rust-lang/crates.io-index" 3179 + checksum = "bd8d438861332c3b1ac396c77bd9cac620ea1ff347efb63c05a83d8f0a593899" 3180 + 3181 + [[package]] 3182 + name = "scc" 3183 + version = "3.7.0" 3184 + source = "registry+https://github.com/rust-lang/crates.io-index" 3185 + checksum = "16c154cf1d115a1e901d7f4e3f279eb6eb455f0d670c1cf3c1aa74d50ad37fa9" 3186 + dependencies = [ 3187 + "saa", 3188 + "sdd", 3189 + ] 3190 + 3191 + [[package]] 3192 + name = "schannel" 3193 + version = "0.1.29" 3194 + source = "registry+https://github.com/rust-lang/crates.io-index" 3195 + checksum = "91c1b7e4904c873ef0710c1f407dde2e6287de2bebc1bbbf7d430bb7cbffd939" 3196 + dependencies = [ 3197 + "windows-sys 0.61.2", 3198 + ] 3199 + 3200 + [[package]] 3201 + name = "scoped-tls" 3202 + version = "1.0.1" 3203 + source = "registry+https://github.com/rust-lang/crates.io-index" 3204 + checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" 3205 + 3206 + [[package]] 3207 + name = "scopeguard" 3208 + version = "1.2.0" 3209 + source = "registry+https://github.com/rust-lang/crates.io-index" 3210 + checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" 3211 + 3212 + [[package]] 3213 + name = "sdd" 3214 + version = "4.8.6" 3215 + source = "registry+https://github.com/rust-lang/crates.io-index" 3216 + checksum = "e5f0e40a01b94e35d1dacbcfbe5bfd3d31e37d9590b2e6d86a82b0e87bd4f551" 3217 + dependencies = [ 3218 + "saa", 3219 + ] 3220 + 3221 + [[package]] 3222 + name = "sec1" 3223 + version = "0.7.3" 3224 + source = "registry+https://github.com/rust-lang/crates.io-index" 3225 + checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" 3226 + dependencies = [ 3227 + "base16ct", 3228 + "der", 3229 + "generic-array", 3230 + "pkcs8", 3231 + "subtle", 3232 + "zeroize", 3233 + ] 3234 + 3235 + [[package]] 3236 + name = "security-framework" 3237 + version = "3.7.0" 3238 + source = "registry+https://github.com/rust-lang/crates.io-index" 3239 + checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" 3240 + dependencies = [ 3241 + "bitflags", 3242 + "core-foundation 0.10.1", 3243 + "core-foundation-sys", 3244 + "libc", 3245 + "security-framework-sys", 3246 + ] 3247 + 3248 + [[package]] 3249 + name = "security-framework-sys" 3250 + version = "2.17.0" 3251 + source = "registry+https://github.com/rust-lang/crates.io-index" 3252 + checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" 3253 + dependencies = [ 3254 + "core-foundation-sys", 3255 + "libc", 3256 + ] 3257 + 3258 + [[package]] 3259 + name = "self_cell" 3260 + version = "1.2.2" 3261 + source = "registry+https://github.com/rust-lang/crates.io-index" 3262 + checksum = "b12e76d157a900eb52e81bc6e9f3069344290341720e9178cde2407113ac8d89" 3263 + 3264 + [[package]] 3265 + name = "semver" 3266 + version = "1.0.28" 3267 + source = "registry+https://github.com/rust-lang/crates.io-index" 3268 + checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" 3269 + 3270 + [[package]] 3271 + name = "send_wrapper" 3272 + version = "0.6.0" 3273 + source = "registry+https://github.com/rust-lang/crates.io-index" 3274 + checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" 3275 + 3276 + [[package]] 3277 + name = "serde" 3278 + version = "1.0.228" 3279 + source = "registry+https://github.com/rust-lang/crates.io-index" 3280 + checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" 3281 + dependencies = [ 3282 + "serde_core", 3283 + "serde_derive", 3284 + ] 3285 + 3286 + [[package]] 3287 + name = "serde_bytes" 3288 + version = "0.11.19" 3289 + source = "registry+https://github.com/rust-lang/crates.io-index" 3290 + checksum = "a5d440709e79d88e51ac01c4b72fc6cb7314017bb7da9eeff678aa94c10e3ea8" 3291 + dependencies = [ 3292 + "serde", 3293 + "serde_core", 3294 + ] 3295 + 3296 + [[package]] 3297 + name = "serde_core" 3298 + version = "1.0.228" 3299 + source = "registry+https://github.com/rust-lang/crates.io-index" 3300 + checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" 3301 + dependencies = [ 3302 + "serde_derive", 3303 + ] 3304 + 3305 + [[package]] 3306 + name = "serde_derive" 3307 + version = "1.0.228" 3308 + source = "registry+https://github.com/rust-lang/crates.io-index" 3309 + checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" 3310 + dependencies = [ 3311 + "proc-macro2", 3312 + "quote", 3313 + "syn", 3314 + ] 3315 + 3316 + [[package]] 3317 + name = "serde_html_form" 3318 + version = "0.3.2" 3319 + source = "registry+https://github.com/rust-lang/crates.io-index" 3320 + checksum = "2acf96b1d9364968fce46ebb548f1c0e1d7eceae27bdff73865d42e6c7369d94" 3321 + dependencies = [ 3322 + "form_urlencoded", 3323 + "indexmap", 3324 + "itoa", 3325 + "serde_core", 3326 + ] 3327 + 3328 + [[package]] 3329 + name = "serde_ipld_dagcbor" 3330 + version = "0.6.4" 3331 + source = "registry+https://github.com/rust-lang/crates.io-index" 3332 + checksum = "46182f4f08349a02b45c998ba3215d3f9de826246ba02bb9dddfe9a2a2100778" 3333 + dependencies = [ 3334 + "cbor4ii", 3335 + "ipld-core", 3336 + "scopeguard", 3337 + "serde", 3338 + ] 3339 + 3340 + [[package]] 3341 + name = "serde_json" 3342 + version = "1.0.149" 3343 + source = "registry+https://github.com/rust-lang/crates.io-index" 3344 + checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" 3345 + dependencies = [ 3346 + "itoa", 3347 + "memchr", 3348 + "serde", 3349 + "serde_core", 3350 + "zmij", 3351 + ] 3352 + 3353 + [[package]] 3354 + name = "serde_path_to_error" 3355 + version = "0.1.20" 3356 + source = "registry+https://github.com/rust-lang/crates.io-index" 3357 + checksum = "10a9ff822e371bb5403e391ecd83e182e0e77ba7f6fe0160b795797109d1b457" 3358 + dependencies = [ 3359 + "itoa", 3360 + "serde", 3361 + "serde_core", 3362 + ] 3363 + 3364 + [[package]] 3365 + name = "serde_repr" 3366 + version = "0.1.20" 3367 + source = "registry+https://github.com/rust-lang/crates.io-index" 3368 + checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" 3369 + dependencies = [ 3370 + "proc-macro2", 3371 + "quote", 3372 + "syn", 3373 + ] 3374 + 3375 + [[package]] 3376 + name = "serde_urlencoded" 3377 + version = "0.7.1" 3378 + source = "registry+https://github.com/rust-lang/crates.io-index" 3379 + checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" 3380 + dependencies = [ 3381 + "form_urlencoded", 3382 + "itoa", 3383 + "ryu", 3384 + "serde", 3385 + ] 3386 + 3387 + [[package]] 3388 + name = "serde_with" 3389 + version = "3.18.0" 3390 + source = "registry+https://github.com/rust-lang/crates.io-index" 3391 + checksum = "dd5414fad8e6907dbdd5bc441a50ae8d6e26151a03b1de04d89a5576de61d01f" 3392 + dependencies = [ 3393 + "base64", 3394 + "chrono", 3395 + "hex", 3396 + "serde_core", 3397 + "serde_json", 3398 + "serde_with_macros", 3399 + "time", 3400 + ] 3401 + 3402 + [[package]] 3403 + name = "serde_with_macros" 3404 + version = "3.18.0" 3405 + source = "registry+https://github.com/rust-lang/crates.io-index" 3406 + checksum = "d3db8978e608f1fe7357e211969fd9abdcae80bac1ba7a3369bb7eb6b404eb65" 3407 + dependencies = [ 3408 + "darling", 3409 + "proc-macro2", 3410 + "quote", 3411 + "syn", 3412 + ] 3413 + 3414 + [[package]] 3415 + name = "sfa" 3416 + version = "1.0.0" 3417 + source = "registry+https://github.com/rust-lang/crates.io-index" 3418 + checksum = "a1296838937cab56cd6c4eeeb8718ec777383700c33f060e2869867bd01d1175" 3419 + dependencies = [ 3420 + "byteorder-lite", 3421 + "log", 3422 + "xxhash-rust", 3423 + ] 3424 + 3425 + [[package]] 3426 + name = "sha1" 3427 + version = "0.10.6" 3428 + source = "registry+https://github.com/rust-lang/crates.io-index" 3429 + checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" 3430 + dependencies = [ 3431 + "cfg-if", 3432 + "cpufeatures 0.2.17", 3433 + "digest", 3434 + ] 3435 + 3436 + [[package]] 3437 + name = "sha1_smol" 3438 + version = "1.0.1" 3439 + source = "registry+https://github.com/rust-lang/crates.io-index" 3440 + checksum = "bbfa15b3dddfee50a0fff136974b3e1bde555604ba463834a7eb7deb6417705d" 3441 + 3442 + [[package]] 3443 + name = "sha2" 3444 + version = "0.10.9" 3445 + source = "registry+https://github.com/rust-lang/crates.io-index" 3446 + checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" 3447 + dependencies = [ 3448 + "cfg-if", 3449 + "cpufeatures 0.2.17", 3450 + "digest", 3451 + ] 3452 + 3453 + [[package]] 3454 + name = "sharded-slab" 3455 + version = "0.1.7" 3456 + source = "registry+https://github.com/rust-lang/crates.io-index" 3457 + checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" 3458 + dependencies = [ 3459 + "lazy_static", 3460 + ] 3461 + 3462 + [[package]] 3463 + name = "shlex" 3464 + version = "1.3.0" 3465 + source = "registry+https://github.com/rust-lang/crates.io-index" 3466 + checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" 3467 + 3468 + [[package]] 3469 + name = "signal-hook-registry" 3470 + version = "1.4.8" 3471 + source = "registry+https://github.com/rust-lang/crates.io-index" 3472 + checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" 3473 + dependencies = [ 3474 + "errno", 3475 + "libc", 3476 + ] 3477 + 3478 + [[package]] 3479 + name = "signature" 3480 + version = "2.2.0" 3481 + source = "registry+https://github.com/rust-lang/crates.io-index" 3482 + checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" 3483 + dependencies = [ 3484 + "digest", 3485 + "rand_core 0.6.4", 3486 + ] 3487 + 3488 + [[package]] 3489 + name = "simd-adler32" 3490 + version = "0.3.9" 3491 + source = "registry+https://github.com/rust-lang/crates.io-index" 3492 + checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" 3493 + 3494 + [[package]] 3495 + name = "simdutf8" 3496 + version = "0.1.5" 3497 + source = "registry+https://github.com/rust-lang/crates.io-index" 3498 + checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" 3499 + 3500 + [[package]] 3501 + name = "siphasher" 3502 + version = "1.0.2" 3503 + source = "registry+https://github.com/rust-lang/crates.io-index" 3504 + checksum = "b2aa850e253778c88a04c3d7323b043aeda9d3e30d5971937c1855769763678e" 3505 + 3506 + [[package]] 3507 + name = "slab" 3508 + version = "0.4.12" 3509 + source = "registry+https://github.com/rust-lang/crates.io-index" 3510 + checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" 3511 + 3512 + [[package]] 3513 + name = "smallvec" 3514 + version = "1.15.1" 3515 + source = "registry+https://github.com/rust-lang/crates.io-index" 3516 + checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" 3517 + 3518 + [[package]] 3519 + name = "smol_str" 3520 + version = "0.3.6" 3521 + source = "registry+https://github.com/rust-lang/crates.io-index" 3522 + checksum = "4aaa7368fcf4852a4c2dd92df0cace6a71f2091ca0a23391ce7f3a31833f1523" 3523 + dependencies = [ 3524 + "borsh", 3525 + "serde_core", 3526 + ] 3527 + 3528 + [[package]] 3529 + name = "socket2" 3530 + version = "0.6.3" 3531 + source = "registry+https://github.com/rust-lang/crates.io-index" 3532 + checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" 3533 + dependencies = [ 3534 + "libc", 3535 + "windows-sys 0.61.2", 3536 + ] 3537 + 3538 + [[package]] 3539 + name = "spin" 3540 + version = "0.9.8" 3541 + source = "registry+https://github.com/rust-lang/crates.io-index" 3542 + checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" 3543 + dependencies = [ 3544 + "lock_api", 3545 + ] 3546 + 3547 + [[package]] 3548 + name = "spin" 3549 + version = "0.10.0" 3550 + source = "registry+https://github.com/rust-lang/crates.io-index" 3551 + checksum = "d5fe4ccb98d9c292d56fec89a5e07da7fc4cf0dc11e156b41793132775d3e591" 3552 + 3553 + [[package]] 3554 + name = "spki" 3555 + version = "0.7.3" 3556 + source = "registry+https://github.com/rust-lang/crates.io-index" 3557 + checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" 3558 + dependencies = [ 3559 + "base64ct", 3560 + "der", 3561 + ] 3562 + 3563 + [[package]] 3564 + name = "stable_deref_trait" 3565 + version = "1.2.1" 3566 + source = "registry+https://github.com/rust-lang/crates.io-index" 3567 + checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" 3568 + 3569 + [[package]] 3570 + name = "static_assertions" 3571 + version = "1.1.0" 3572 + source = "registry+https://github.com/rust-lang/crates.io-index" 3573 + checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" 3574 + 3575 + [[package]] 3576 + name = "strsim" 3577 + version = "0.11.1" 3578 + source = "registry+https://github.com/rust-lang/crates.io-index" 3579 + checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" 3580 + 3581 + [[package]] 3582 + name = "subtle" 3583 + version = "2.6.1" 3584 + source = "registry+https://github.com/rust-lang/crates.io-index" 3585 + checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" 3586 + 3587 + [[package]] 3588 + name = "supports-color" 3589 + version = "3.0.2" 3590 + source = "registry+https://github.com/rust-lang/crates.io-index" 3591 + checksum = "c64fc7232dd8d2e4ac5ce4ef302b1d81e0b80d055b9d77c7c4f51f6aa4c867d6" 3592 + dependencies = [ 3593 + "is_ci", 3594 + ] 3595 + 3596 + [[package]] 3597 + name = "supports-hyperlinks" 3598 + version = "3.2.0" 3599 + source = "registry+https://github.com/rust-lang/crates.io-index" 3600 + checksum = "e396b6523b11ccb83120b115a0b7366de372751aa6edf19844dfb13a6af97e91" 3601 + 3602 + [[package]] 3603 + name = "supports-unicode" 3604 + version = "3.0.0" 3605 + source = "registry+https://github.com/rust-lang/crates.io-index" 3606 + checksum = "b7401a30af6cb5818bb64852270bb722533397edcfc7344954a38f420819ece2" 3607 + 3608 + [[package]] 3609 + name = "syn" 3610 + version = "2.0.117" 3611 + source = "registry+https://github.com/rust-lang/crates.io-index" 3612 + checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" 3613 + dependencies = [ 3614 + "proc-macro2", 3615 + "quote", 3616 + "unicode-ident", 3617 + ] 3618 + 3619 + [[package]] 3620 + name = "sync_wrapper" 3621 + version = "1.0.2" 3622 + source = "registry+https://github.com/rust-lang/crates.io-index" 3623 + checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" 3624 + dependencies = [ 3625 + "futures-core", 3626 + ] 3627 + 3628 + [[package]] 3629 + name = "synstructure" 3630 + version = "0.13.2" 3631 + source = "registry+https://github.com/rust-lang/crates.io-index" 3632 + checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" 3633 + dependencies = [ 3634 + "proc-macro2", 3635 + "quote", 3636 + "syn", 3637 + ] 3638 + 3639 + [[package]] 3640 + name = "system-configuration" 3641 + version = "0.7.0" 3642 + source = "registry+https://github.com/rust-lang/crates.io-index" 3643 + checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b" 3644 + dependencies = [ 3645 + "bitflags", 3646 + "core-foundation 0.9.4", 3647 + "system-configuration-sys", 3648 + ] 3649 + 3650 + [[package]] 3651 + name = "system-configuration-sys" 3652 + version = "0.6.0" 3653 + source = "registry+https://github.com/rust-lang/crates.io-index" 3654 + checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" 3655 + dependencies = [ 3656 + "core-foundation-sys", 3657 + "libc", 3658 + ] 3659 + 3660 + [[package]] 3661 + name = "tempfile" 3662 + version = "3.27.0" 3663 + source = "registry+https://github.com/rust-lang/crates.io-index" 3664 + checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" 3665 + dependencies = [ 3666 + "fastrand", 3667 + "getrandom 0.4.2", 3668 + "once_cell", 3669 + "rustix", 3670 + "windows-sys 0.61.2", 3671 + ] 3672 + 3673 + [[package]] 3674 + name = "terminal_size" 3675 + version = "0.4.4" 3676 + source = "registry+https://github.com/rust-lang/crates.io-index" 3677 + checksum = "230a1b821ccbd75b185820a1f1ff7b14d21da1e442e22c0863ea5f08771a8874" 3678 + dependencies = [ 3679 + "rustix", 3680 + "windows-sys 0.61.2", 3681 + ] 3682 + 3683 + [[package]] 3684 + name = "textwrap" 3685 + version = "0.16.2" 3686 + source = "registry+https://github.com/rust-lang/crates.io-index" 3687 + checksum = "c13547615a44dc9c452a8a534638acdf07120d4b6847c8178705da06306a3057" 3688 + dependencies = [ 3689 + "unicode-linebreak", 3690 + "unicode-width 0.2.2", 3691 + ] 3692 + 3693 + [[package]] 3694 + name = "thiserror" 3695 + version = "1.0.69" 3696 + source = "registry+https://github.com/rust-lang/crates.io-index" 3697 + checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" 3698 + dependencies = [ 3699 + "thiserror-impl 1.0.69", 3700 + ] 3701 + 3702 + [[package]] 3703 + name = "thiserror" 3704 + version = "2.0.18" 3705 + source = "registry+https://github.com/rust-lang/crates.io-index" 3706 + checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" 3707 + dependencies = [ 3708 + "thiserror-impl 2.0.18", 3709 + ] 3710 + 3711 + [[package]] 3712 + name = "thiserror-impl" 3713 + version = "1.0.69" 3714 + source = "registry+https://github.com/rust-lang/crates.io-index" 3715 + checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" 3716 + dependencies = [ 3717 + "proc-macro2", 3718 + "quote", 3719 + "syn", 3720 + ] 3721 + 3722 + [[package]] 3723 + name = "thiserror-impl" 3724 + version = "2.0.18" 3725 + source = "registry+https://github.com/rust-lang/crates.io-index" 3726 + checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" 3727 + dependencies = [ 3728 + "proc-macro2", 3729 + "quote", 3730 + "syn", 3731 + ] 3732 + 3733 + [[package]] 3734 + name = "thread_local" 3735 + version = "1.1.9" 3736 + source = "registry+https://github.com/rust-lang/crates.io-index" 3737 + checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" 3738 + dependencies = [ 3739 + "cfg-if", 3740 + ] 3741 + 3742 + [[package]] 3743 + name = "time" 3744 + version = "0.3.47" 3745 + source = "registry+https://github.com/rust-lang/crates.io-index" 3746 + checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" 3747 + dependencies = [ 3748 + "deranged", 3749 + "num-conv", 3750 + "powerfmt", 3751 + "serde_core", 3752 + "time-core", 3753 + ] 3754 + 3755 + [[package]] 3756 + name = "time-core" 3757 + version = "0.1.8" 3758 + source = "registry+https://github.com/rust-lang/crates.io-index" 3759 + checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" 3760 + 3761 + [[package]] 3762 + name = "tinystr" 3763 + version = "0.8.3" 3764 + source = "registry+https://github.com/rust-lang/crates.io-index" 3765 + checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d" 3766 + dependencies = [ 3767 + "displaydoc", 3768 + "zerovec", 3769 + ] 3770 + 3771 + [[package]] 3772 + name = "tinyvec" 3773 + version = "1.11.0" 3774 + source = "registry+https://github.com/rust-lang/crates.io-index" 3775 + checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3" 3776 + dependencies = [ 3777 + "tinyvec_macros", 3778 + ] 3779 + 3780 + [[package]] 3781 + name = "tinyvec_macros" 3782 + version = "0.1.1" 3783 + source = "registry+https://github.com/rust-lang/crates.io-index" 3784 + checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" 3785 + 3786 + [[package]] 3787 + name = "tokio" 3788 + version = "1.52.1" 3789 + source = "registry+https://github.com/rust-lang/crates.io-index" 3790 + checksum = "b67dee974fe86fd92cc45b7a95fdd2f99a36a6d7b0d431a231178d3d670bbcc6" 3791 + dependencies = [ 3792 + "bytes", 3793 + "libc", 3794 + "mio", 3795 + "parking_lot", 3796 + "pin-project-lite", 3797 + "signal-hook-registry", 3798 + "socket2", 3799 + "tokio-macros", 3800 + "windows-sys 0.61.2", 3801 + ] 3802 + 3803 + [[package]] 3804 + name = "tokio-macros" 3805 + version = "2.7.0" 3806 + source = "registry+https://github.com/rust-lang/crates.io-index" 3807 + checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" 3808 + dependencies = [ 3809 + "proc-macro2", 3810 + "quote", 3811 + "syn", 3812 + ] 3813 + 3814 + [[package]] 3815 + name = "tokio-native-tls" 3816 + version = "0.3.1" 3817 + source = "registry+https://github.com/rust-lang/crates.io-index" 3818 + checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" 3819 + dependencies = [ 3820 + "native-tls", 3821 + "tokio", 3822 + ] 3823 + 3824 + [[package]] 3825 + name = "tokio-rustls" 3826 + version = "0.26.4" 3827 + source = "registry+https://github.com/rust-lang/crates.io-index" 3828 + checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" 3829 + dependencies = [ 3830 + "rustls", 3831 + "tokio", 3832 + ] 3833 + 3834 + [[package]] 3835 + name = "tokio-tungstenite" 3836 + version = "0.24.0" 3837 + source = "registry+https://github.com/rust-lang/crates.io-index" 3838 + checksum = "edc5f74e248dc973e0dbb7b74c7e0d6fcc301c694ff50049504004ef4d0cdcd9" 3839 + dependencies = [ 3840 + "futures-util", 3841 + "log", 3842 + "rustls", 3843 + "rustls-native-certs", 3844 + "rustls-pki-types", 3845 + "tokio", 3846 + "tokio-rustls", 3847 + "tungstenite", 3848 + ] 3849 + 3850 + [[package]] 3851 + name = "tokio-tungstenite-wasm" 3852 + version = "0.4.0" 3853 + source = "registry+https://github.com/rust-lang/crates.io-index" 3854 + checksum = "e21a5c399399c3db9f08d8297ac12b500e86bca82e930253fdc62eaf9c0de6ae" 3855 + dependencies = [ 3856 + "futures-channel", 3857 + "futures-util", 3858 + "http", 3859 + "httparse", 3860 + "js-sys", 3861 + "rustls", 3862 + "thiserror 1.0.69", 3863 + "tokio", 3864 + "tokio-tungstenite", 3865 + "wasm-bindgen", 3866 + "web-sys", 3867 + ] 3868 + 3869 + [[package]] 3870 + name = "tokio-util" 3871 + version = "0.7.18" 3872 + source = "registry+https://github.com/rust-lang/crates.io-index" 3873 + checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" 3874 + dependencies = [ 3875 + "bytes", 3876 + "futures-core", 3877 + "futures-sink", 3878 + "futures-util", 3879 + "pin-project-lite", 3880 + "tokio", 3881 + ] 3882 + 3883 + [[package]] 3884 + name = "tokio-websockets" 3885 + version = "0.13.2" 3886 + source = "registry+https://github.com/rust-lang/crates.io-index" 3887 + checksum = "dad543404f98bfc969aeb71994105c592acfc6c43323fddcd016bb208d1c65cb" 3888 + dependencies = [ 3889 + "aws-lc-rs", 3890 + "base64", 3891 + "bytes", 3892 + "futures-core", 3893 + "futures-sink", 3894 + "http", 3895 + "httparse", 3896 + "rand 0.10.1", 3897 + "rustls-native-certs", 3898 + "rustls-pki-types", 3899 + "sha1_smol", 3900 + "simdutf8", 3901 + "tokio", 3902 + "tokio-rustls", 3903 + "tokio-util", 3904 + ] 3905 + 3906 + [[package]] 3907 + name = "tower" 3908 + version = "0.5.3" 3909 + source = "registry+https://github.com/rust-lang/crates.io-index" 3910 + checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" 3911 + dependencies = [ 3912 + "futures-core", 3913 + "futures-util", 3914 + "pin-project-lite", 3915 + "sync_wrapper", 3916 + "tokio", 3917 + "tower-layer", 3918 + "tower-service", 3919 + "tracing", 3920 + ] 3921 + 3922 + [[package]] 3923 + name = "tower-http" 3924 + version = "0.6.8" 3925 + source = "registry+https://github.com/rust-lang/crates.io-index" 3926 + checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" 3927 + dependencies = [ 3928 + "async-compression", 3929 + "bitflags", 3930 + "bytes", 3931 + "futures-core", 3932 + "futures-util", 3933 + "http", 3934 + "http-body", 3935 + "http-body-util", 3936 + "iri-string", 3937 + "pin-project-lite", 3938 + "tokio", 3939 + "tokio-util", 3940 + "tower", 3941 + "tower-layer", 3942 + "tower-service", 3943 + "tracing", 3944 + ] 3945 + 3946 + [[package]] 3947 + name = "tower-layer" 3948 + version = "0.3.3" 3949 + source = "registry+https://github.com/rust-lang/crates.io-index" 3950 + checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" 3951 + 3952 + [[package]] 3953 + name = "tower-service" 3954 + version = "0.3.3" 3955 + source = "registry+https://github.com/rust-lang/crates.io-index" 3956 + checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" 3957 + 3958 + [[package]] 3959 + name = "tracing" 3960 + version = "0.1.44" 3961 + source = "registry+https://github.com/rust-lang/crates.io-index" 3962 + checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" 3963 + dependencies = [ 3964 + "log", 3965 + "pin-project-lite", 3966 + "tracing-attributes", 3967 + "tracing-core", 3968 + ] 3969 + 3970 + [[package]] 3971 + name = "tracing-attributes" 3972 + version = "0.1.31" 3973 + source = "registry+https://github.com/rust-lang/crates.io-index" 3974 + checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" 3975 + dependencies = [ 3976 + "proc-macro2", 3977 + "quote", 3978 + "syn", 3979 + ] 3980 + 3981 + [[package]] 3982 + name = "tracing-core" 3983 + version = "0.1.36" 3984 + source = "registry+https://github.com/rust-lang/crates.io-index" 3985 + checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" 3986 + dependencies = [ 3987 + "once_cell", 3988 + "valuable", 3989 + ] 3990 + 3991 + [[package]] 3992 + name = "tracing-log" 3993 + version = "0.2.0" 3994 + source = "registry+https://github.com/rust-lang/crates.io-index" 3995 + checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" 3996 + dependencies = [ 3997 + "log", 3998 + "once_cell", 3999 + "tracing-core", 4000 + ] 4001 + 4002 + [[package]] 4003 + name = "tracing-subscriber" 4004 + version = "0.3.23" 4005 + source = "registry+https://github.com/rust-lang/crates.io-index" 4006 + checksum = "cb7f578e5945fb242538965c2d0b04418d38ec25c79d160cd279bf0731c8d319" 4007 + dependencies = [ 4008 + "matchers", 4009 + "nu-ansi-term", 4010 + "once_cell", 4011 + "regex-automata", 4012 + "sharded-slab", 4013 + "smallvec", 4014 + "thread_local", 4015 + "tracing", 4016 + "tracing-core", 4017 + "tracing-log", 4018 + ] 4019 + 4020 + [[package]] 4021 + name = "trait-variant" 4022 + version = "0.1.2" 4023 + source = "registry+https://github.com/rust-lang/crates.io-index" 4024 + checksum = "70977707304198400eb4835a78f6a9f928bf41bba420deb8fdb175cd965d77a7" 4025 + dependencies = [ 4026 + "proc-macro2", 4027 + "quote", 4028 + "syn", 4029 + ] 4030 + 4031 + [[package]] 4032 + name = "try-lock" 4033 + version = "0.2.5" 4034 + source = "registry+https://github.com/rust-lang/crates.io-index" 4035 + checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" 4036 + 4037 + [[package]] 4038 + name = "tungstenite" 4039 + version = "0.24.0" 4040 + source = "registry+https://github.com/rust-lang/crates.io-index" 4041 + checksum = "18e5b8366ee7a95b16d32197d0b2604b43a0be89dc5fac9f8e96ccafbaedda8a" 4042 + dependencies = [ 4043 + "byteorder", 4044 + "bytes", 4045 + "data-encoding", 4046 + "http", 4047 + "httparse", 4048 + "log", 4049 + "rand 0.8.6", 4050 + "rustls", 4051 + "rustls-pki-types", 4052 + "sha1", 4053 + "thiserror 1.0.69", 4054 + "utf-8", 4055 + ] 4056 + 4057 + [[package]] 4058 + name = "twox-hash" 4059 + version = "2.1.2" 4060 + source = "registry+https://github.com/rust-lang/crates.io-index" 4061 + checksum = "9ea3136b675547379c4bd395ca6b938e5ad3c3d20fad76e7fe85f9e0d011419c" 4062 + 4063 + [[package]] 4064 + name = "typenum" 4065 + version = "1.20.0" 4066 + source = "registry+https://github.com/rust-lang/crates.io-index" 4067 + checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de" 4068 + 4069 + [[package]] 4070 + name = "unicode-ident" 4071 + version = "1.0.24" 4072 + source = "registry+https://github.com/rust-lang/crates.io-index" 4073 + checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" 4074 + 4075 + [[package]] 4076 + name = "unicode-linebreak" 4077 + version = "0.1.5" 4078 + source = "registry+https://github.com/rust-lang/crates.io-index" 4079 + checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f" 4080 + 4081 + [[package]] 4082 + name = "unicode-segmentation" 4083 + version = "1.13.2" 4084 + source = "registry+https://github.com/rust-lang/crates.io-index" 4085 + checksum = "9629274872b2bfaf8d66f5f15725007f635594914870f65218920345aa11aa8c" 4086 + 4087 + [[package]] 4088 + name = "unicode-width" 4089 + version = "0.1.14" 4090 + source = "registry+https://github.com/rust-lang/crates.io-index" 4091 + checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" 4092 + 4093 + [[package]] 4094 + name = "unicode-width" 4095 + version = "0.2.2" 4096 + source = "registry+https://github.com/rust-lang/crates.io-index" 4097 + checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" 4098 + 4099 + [[package]] 4100 + name = "unicode-xid" 4101 + version = "0.2.6" 4102 + source = "registry+https://github.com/rust-lang/crates.io-index" 4103 + checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" 4104 + 4105 + [[package]] 4106 + name = "unsigned-varint" 4107 + version = "0.7.2" 4108 + source = "registry+https://github.com/rust-lang/crates.io-index" 4109 + checksum = "6889a77d49f1f013504cec6bf97a2c730394adedaeb1deb5ea08949a50541105" 4110 + 4111 + [[package]] 4112 + name = "unsigned-varint" 4113 + version = "0.8.0" 4114 + source = "registry+https://github.com/rust-lang/crates.io-index" 4115 + checksum = "eb066959b24b5196ae73cb057f45598450d2c5f71460e98c49b738086eff9c06" 4116 + 4117 + [[package]] 4118 + name = "untrusted" 4119 + version = "0.9.0" 4120 + source = "registry+https://github.com/rust-lang/crates.io-index" 4121 + checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" 4122 + 4123 + [[package]] 4124 + name = "url" 4125 + version = "2.5.8" 4126 + source = "registry+https://github.com/rust-lang/crates.io-index" 4127 + checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" 4128 + dependencies = [ 4129 + "form_urlencoded", 4130 + "idna", 4131 + "percent-encoding", 4132 + "serde", 4133 + "serde_derive", 4134 + ] 4135 + 4136 + [[package]] 4137 + name = "urlencoding" 4138 + version = "2.1.3" 4139 + source = "registry+https://github.com/rust-lang/crates.io-index" 4140 + checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" 4141 + 4142 + [[package]] 4143 + name = "utf-8" 4144 + version = "0.7.6" 4145 + source = "registry+https://github.com/rust-lang/crates.io-index" 4146 + checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" 4147 + 4148 + [[package]] 4149 + name = "utf8_iter" 4150 + version = "1.0.4" 4151 + source = "registry+https://github.com/rust-lang/crates.io-index" 4152 + checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" 4153 + 4154 + [[package]] 4155 + name = "valuable" 4156 + version = "0.1.1" 4157 + source = "registry+https://github.com/rust-lang/crates.io-index" 4158 + checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" 4159 + 4160 + [[package]] 4161 + name = "varint-rs" 4162 + version = "2.2.0" 4163 + source = "registry+https://github.com/rust-lang/crates.io-index" 4164 + checksum = "8f54a172d0620933a27a4360d3db3e2ae0dd6cceae9730751a036bbf182c4b23" 4165 + 4166 + [[package]] 4167 + name = "vcpkg" 4168 + version = "0.2.15" 4169 + source = "registry+https://github.com/rust-lang/crates.io-index" 4170 + checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" 4171 + 4172 + [[package]] 4173 + name = "version_check" 4174 + version = "0.9.5" 4175 + source = "registry+https://github.com/rust-lang/crates.io-index" 4176 + checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" 4177 + 4178 + [[package]] 4179 + name = "want" 4180 + version = "0.3.1" 4181 + source = "registry+https://github.com/rust-lang/crates.io-index" 4182 + checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" 4183 + dependencies = [ 4184 + "try-lock", 4185 + ] 4186 + 4187 + [[package]] 4188 + name = "wasi" 4189 + version = "0.11.1+wasi-snapshot-preview1" 4190 + source = "registry+https://github.com/rust-lang/crates.io-index" 4191 + checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" 4192 + 4193 + [[package]] 4194 + name = "wasip2" 4195 + version = "1.0.3+wasi-0.2.9" 4196 + source = "registry+https://github.com/rust-lang/crates.io-index" 4197 + checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6" 4198 + dependencies = [ 4199 + "wit-bindgen 0.57.1", 4200 + ] 4201 + 4202 + [[package]] 4203 + name = "wasip3" 4204 + version = "0.4.0+wasi-0.3.0-rc-2026-01-06" 4205 + source = "registry+https://github.com/rust-lang/crates.io-index" 4206 + checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" 4207 + dependencies = [ 4208 + "wit-bindgen 0.51.0", 4209 + ] 4210 + 4211 + [[package]] 4212 + name = "wasm-bindgen" 4213 + version = "0.2.118" 4214 + source = "registry+https://github.com/rust-lang/crates.io-index" 4215 + checksum = "0bf938a0bacb0469e83c1e148908bd7d5a6010354cf4fb73279b7447422e3a89" 4216 + dependencies = [ 4217 + "cfg-if", 4218 + "once_cell", 4219 + "rustversion", 4220 + "wasm-bindgen-macro", 4221 + "wasm-bindgen-shared", 4222 + ] 4223 + 4224 + [[package]] 4225 + name = "wasm-bindgen-futures" 4226 + version = "0.4.68" 4227 + source = "registry+https://github.com/rust-lang/crates.io-index" 4228 + checksum = "f371d383f2fb139252e0bfac3b81b265689bf45b6874af544ffa4c975ac1ebf8" 4229 + dependencies = [ 4230 + "js-sys", 4231 + "wasm-bindgen", 4232 + ] 4233 + 4234 + [[package]] 4235 + name = "wasm-bindgen-macro" 4236 + version = "0.2.118" 4237 + source = "registry+https://github.com/rust-lang/crates.io-index" 4238 + checksum = "eeff24f84126c0ec2db7a449f0c2ec963c6a49efe0698c4242929da037ca28ed" 4239 + dependencies = [ 4240 + "quote", 4241 + "wasm-bindgen-macro-support", 4242 + ] 4243 + 4244 + [[package]] 4245 + name = "wasm-bindgen-macro-support" 4246 + version = "0.2.118" 4247 + source = "registry+https://github.com/rust-lang/crates.io-index" 4248 + checksum = "9d08065faf983b2b80a79fd87d8254c409281cf7de75fc4b773019824196c904" 4249 + dependencies = [ 4250 + "bumpalo", 4251 + "proc-macro2", 4252 + "quote", 4253 + "syn", 4254 + "wasm-bindgen-shared", 4255 + ] 4256 + 4257 + [[package]] 4258 + name = "wasm-bindgen-shared" 4259 + version = "0.2.118" 4260 + source = "registry+https://github.com/rust-lang/crates.io-index" 4261 + checksum = "5fd04d9e306f1907bd13c6361b5c6bfc7b3b3c095ed3f8a9246390f8dbdee129" 4262 + dependencies = [ 4263 + "unicode-ident", 4264 + ] 4265 + 4266 + [[package]] 4267 + name = "wasm-encoder" 4268 + version = "0.244.0" 4269 + source = "registry+https://github.com/rust-lang/crates.io-index" 4270 + checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" 4271 + dependencies = [ 4272 + "leb128fmt", 4273 + "wasmparser", 4274 + ] 4275 + 4276 + [[package]] 4277 + name = "wasm-metadata" 4278 + version = "0.244.0" 4279 + source = "registry+https://github.com/rust-lang/crates.io-index" 4280 + checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" 4281 + dependencies = [ 4282 + "anyhow", 4283 + "indexmap", 4284 + "wasm-encoder", 4285 + "wasmparser", 4286 + ] 4287 + 4288 + [[package]] 4289 + name = "wasm-streams" 4290 + version = "0.4.2" 4291 + source = "registry+https://github.com/rust-lang/crates.io-index" 4292 + checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65" 4293 + dependencies = [ 4294 + "futures-util", 4295 + "js-sys", 4296 + "wasm-bindgen", 4297 + "wasm-bindgen-futures", 4298 + "web-sys", 4299 + ] 4300 + 4301 + [[package]] 4302 + name = "wasmparser" 4303 + version = "0.244.0" 4304 + source = "registry+https://github.com/rust-lang/crates.io-index" 4305 + checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" 4306 + dependencies = [ 4307 + "bitflags", 4308 + "hashbrown 0.15.5", 4309 + "indexmap", 4310 + "semver", 4311 + ] 4312 + 4313 + [[package]] 4314 + name = "web-sys" 4315 + version = "0.3.95" 4316 + source = "registry+https://github.com/rust-lang/crates.io-index" 4317 + checksum = "4f2dfbb17949fa2088e5d39408c48368947b86f7834484e87b73de55bc14d97d" 4318 + dependencies = [ 4319 + "js-sys", 4320 + "wasm-bindgen", 4321 + ] 4322 + 4323 + [[package]] 4324 + name = "web-time" 4325 + version = "1.1.0" 4326 + source = "registry+https://github.com/rust-lang/crates.io-index" 4327 + checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" 4328 + dependencies = [ 4329 + "js-sys", 4330 + "wasm-bindgen", 4331 + ] 4332 + 4333 + [[package]] 4334 + name = "webpki-roots" 4335 + version = "1.0.7" 4336 + source = "registry+https://github.com/rust-lang/crates.io-index" 4337 + checksum = "52f5ee44c96cf55f1b349600768e3ece3a8f26010c05265ab73f945bb1a2eb9d" 4338 + dependencies = [ 4339 + "rustls-pki-types", 4340 + ] 4341 + 4342 + [[package]] 4343 + name = "widestring" 4344 + version = "1.2.1" 4345 + source = "registry+https://github.com/rust-lang/crates.io-index" 4346 + checksum = "72069c3113ab32ab29e5584db3c6ec55d416895e60715417b5b883a357c3e471" 4347 + 4348 + [[package]] 4349 + name = "windows-core" 4350 + version = "0.62.2" 4351 + source = "registry+https://github.com/rust-lang/crates.io-index" 4352 + checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" 4353 + dependencies = [ 4354 + "windows-implement", 4355 + "windows-interface", 4356 + "windows-link", 4357 + "windows-result", 4358 + "windows-strings", 4359 + ] 4360 + 4361 + [[package]] 4362 + name = "windows-implement" 4363 + version = "0.60.2" 4364 + source = "registry+https://github.com/rust-lang/crates.io-index" 4365 + checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" 4366 + dependencies = [ 4367 + "proc-macro2", 4368 + "quote", 4369 + "syn", 4370 + ] 4371 + 4372 + [[package]] 4373 + name = "windows-interface" 4374 + version = "0.59.3" 4375 + source = "registry+https://github.com/rust-lang/crates.io-index" 4376 + checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" 4377 + dependencies = [ 4378 + "proc-macro2", 4379 + "quote", 4380 + "syn", 4381 + ] 4382 + 4383 + [[package]] 4384 + name = "windows-link" 4385 + version = "0.2.1" 4386 + source = "registry+https://github.com/rust-lang/crates.io-index" 4387 + checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" 4388 + 4389 + [[package]] 4390 + name = "windows-registry" 4391 + version = "0.6.1" 4392 + source = "registry+https://github.com/rust-lang/crates.io-index" 4393 + checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720" 4394 + dependencies = [ 4395 + "windows-link", 4396 + "windows-result", 4397 + "windows-strings", 4398 + ] 4399 + 4400 + [[package]] 4401 + name = "windows-result" 4402 + version = "0.4.1" 4403 + source = "registry+https://github.com/rust-lang/crates.io-index" 4404 + checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" 4405 + dependencies = [ 4406 + "windows-link", 4407 + ] 4408 + 4409 + [[package]] 4410 + name = "windows-strings" 4411 + version = "0.5.1" 4412 + source = "registry+https://github.com/rust-lang/crates.io-index" 4413 + checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" 4414 + dependencies = [ 4415 + "windows-link", 4416 + ] 4417 + 4418 + [[package]] 4419 + name = "windows-sys" 4420 + version = "0.52.0" 4421 + source = "registry+https://github.com/rust-lang/crates.io-index" 4422 + checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" 4423 + dependencies = [ 4424 + "windows-targets 0.52.6", 4425 + ] 4426 + 4427 + [[package]] 4428 + name = "windows-sys" 4429 + version = "0.60.2" 4430 + source = "registry+https://github.com/rust-lang/crates.io-index" 4431 + checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" 4432 + dependencies = [ 4433 + "windows-targets 0.53.5", 4434 + ] 4435 + 4436 + [[package]] 4437 + name = "windows-sys" 4438 + version = "0.61.2" 4439 + source = "registry+https://github.com/rust-lang/crates.io-index" 4440 + checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" 4441 + dependencies = [ 4442 + "windows-link", 4443 + ] 4444 + 4445 + [[package]] 4446 + name = "windows-targets" 4447 + version = "0.52.6" 4448 + source = "registry+https://github.com/rust-lang/crates.io-index" 4449 + checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" 4450 + dependencies = [ 4451 + "windows_aarch64_gnullvm 0.52.6", 4452 + "windows_aarch64_msvc 0.52.6", 4453 + "windows_i686_gnu 0.52.6", 4454 + "windows_i686_gnullvm 0.52.6", 4455 + "windows_i686_msvc 0.52.6", 4456 + "windows_x86_64_gnu 0.52.6", 4457 + "windows_x86_64_gnullvm 0.52.6", 4458 + "windows_x86_64_msvc 0.52.6", 4459 + ] 4460 + 4461 + [[package]] 4462 + name = "windows-targets" 4463 + version = "0.53.5" 4464 + source = "registry+https://github.com/rust-lang/crates.io-index" 4465 + checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" 4466 + dependencies = [ 4467 + "windows-link", 4468 + "windows_aarch64_gnullvm 0.53.1", 4469 + "windows_aarch64_msvc 0.53.1", 4470 + "windows_i686_gnu 0.53.1", 4471 + "windows_i686_gnullvm 0.53.1", 4472 + "windows_i686_msvc 0.53.1", 4473 + "windows_x86_64_gnu 0.53.1", 4474 + "windows_x86_64_gnullvm 0.53.1", 4475 + "windows_x86_64_msvc 0.53.1", 4476 + ] 4477 + 4478 + [[package]] 4479 + name = "windows_aarch64_gnullvm" 4480 + version = "0.52.6" 4481 + source = "registry+https://github.com/rust-lang/crates.io-index" 4482 + checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 4483 + 4484 + [[package]] 4485 + name = "windows_aarch64_gnullvm" 4486 + version = "0.53.1" 4487 + source = "registry+https://github.com/rust-lang/crates.io-index" 4488 + checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" 4489 + 4490 + [[package]] 4491 + name = "windows_aarch64_msvc" 4492 + version = "0.52.6" 4493 + source = "registry+https://github.com/rust-lang/crates.io-index" 4494 + checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 4495 + 4496 + [[package]] 4497 + name = "windows_aarch64_msvc" 4498 + version = "0.53.1" 4499 + source = "registry+https://github.com/rust-lang/crates.io-index" 4500 + checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" 4501 + 4502 + [[package]] 4503 + name = "windows_i686_gnu" 4504 + version = "0.52.6" 4505 + source = "registry+https://github.com/rust-lang/crates.io-index" 4506 + checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 4507 + 4508 + [[package]] 4509 + name = "windows_i686_gnu" 4510 + version = "0.53.1" 4511 + source = "registry+https://github.com/rust-lang/crates.io-index" 4512 + checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" 4513 + 4514 + [[package]] 4515 + name = "windows_i686_gnullvm" 4516 + version = "0.52.6" 4517 + source = "registry+https://github.com/rust-lang/crates.io-index" 4518 + checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 4519 + 4520 + [[package]] 4521 + name = "windows_i686_gnullvm" 4522 + version = "0.53.1" 4523 + source = "registry+https://github.com/rust-lang/crates.io-index" 4524 + checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" 4525 + 4526 + [[package]] 4527 + name = "windows_i686_msvc" 4528 + version = "0.52.6" 4529 + source = "registry+https://github.com/rust-lang/crates.io-index" 4530 + checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 4531 + 4532 + [[package]] 4533 + name = "windows_i686_msvc" 4534 + version = "0.53.1" 4535 + source = "registry+https://github.com/rust-lang/crates.io-index" 4536 + checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" 4537 + 4538 + [[package]] 4539 + name = "windows_x86_64_gnu" 4540 + version = "0.52.6" 4541 + source = "registry+https://github.com/rust-lang/crates.io-index" 4542 + checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 4543 + 4544 + [[package]] 4545 + name = "windows_x86_64_gnu" 4546 + version = "0.53.1" 4547 + source = "registry+https://github.com/rust-lang/crates.io-index" 4548 + checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" 4549 + 4550 + [[package]] 4551 + name = "windows_x86_64_gnullvm" 4552 + version = "0.52.6" 4553 + source = "registry+https://github.com/rust-lang/crates.io-index" 4554 + checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 4555 + 4556 + [[package]] 4557 + name = "windows_x86_64_gnullvm" 4558 + version = "0.53.1" 4559 + source = "registry+https://github.com/rust-lang/crates.io-index" 4560 + checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" 4561 + 4562 + [[package]] 4563 + name = "windows_x86_64_msvc" 4564 + version = "0.52.6" 4565 + source = "registry+https://github.com/rust-lang/crates.io-index" 4566 + checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 4567 + 4568 + [[package]] 4569 + name = "windows_x86_64_msvc" 4570 + version = "0.53.1" 4571 + source = "registry+https://github.com/rust-lang/crates.io-index" 4572 + checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" 4573 + 4574 + [[package]] 4575 + name = "wit-bindgen" 4576 + version = "0.51.0" 4577 + source = "registry+https://github.com/rust-lang/crates.io-index" 4578 + checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" 4579 + dependencies = [ 4580 + "wit-bindgen-rust-macro", 4581 + ] 4582 + 4583 + [[package]] 4584 + name = "wit-bindgen" 4585 + version = "0.57.1" 4586 + source = "registry+https://github.com/rust-lang/crates.io-index" 4587 + checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" 4588 + 4589 + [[package]] 4590 + name = "wit-bindgen-core" 4591 + version = "0.51.0" 4592 + source = "registry+https://github.com/rust-lang/crates.io-index" 4593 + checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" 4594 + dependencies = [ 4595 + "anyhow", 4596 + "heck 0.5.0", 4597 + "wit-parser", 4598 + ] 4599 + 4600 + [[package]] 4601 + name = "wit-bindgen-rust" 4602 + version = "0.51.0" 4603 + source = "registry+https://github.com/rust-lang/crates.io-index" 4604 + checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" 4605 + dependencies = [ 4606 + "anyhow", 4607 + "heck 0.5.0", 4608 + "indexmap", 4609 + "prettyplease", 4610 + "syn", 4611 + "wasm-metadata", 4612 + "wit-bindgen-core", 4613 + "wit-component", 4614 + ] 4615 + 4616 + [[package]] 4617 + name = "wit-bindgen-rust-macro" 4618 + version = "0.51.0" 4619 + source = "registry+https://github.com/rust-lang/crates.io-index" 4620 + checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" 4621 + dependencies = [ 4622 + "anyhow", 4623 + "prettyplease", 4624 + "proc-macro2", 4625 + "quote", 4626 + "syn", 4627 + "wit-bindgen-core", 4628 + "wit-bindgen-rust", 4629 + ] 4630 + 4631 + [[package]] 4632 + name = "wit-component" 4633 + version = "0.244.0" 4634 + source = "registry+https://github.com/rust-lang/crates.io-index" 4635 + checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" 4636 + dependencies = [ 4637 + "anyhow", 4638 + "bitflags", 4639 + "indexmap", 4640 + "log", 4641 + "serde", 4642 + "serde_derive", 4643 + "serde_json", 4644 + "wasm-encoder", 4645 + "wasm-metadata", 4646 + "wasmparser", 4647 + "wit-parser", 4648 + ] 4649 + 4650 + [[package]] 4651 + name = "wit-parser" 4652 + version = "0.244.0" 4653 + source = "registry+https://github.com/rust-lang/crates.io-index" 4654 + checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" 4655 + dependencies = [ 4656 + "anyhow", 4657 + "id-arena", 4658 + "indexmap", 4659 + "log", 4660 + "semver", 4661 + "serde", 4662 + "serde_derive", 4663 + "serde_json", 4664 + "unicode-xid", 4665 + "wasmparser", 4666 + ] 4667 + 4668 + [[package]] 4669 + name = "writeable" 4670 + version = "0.6.3" 4671 + source = "registry+https://github.com/rust-lang/crates.io-index" 4672 + checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" 4673 + 4674 + [[package]] 4675 + name = "xxhash-rust" 4676 + version = "0.8.15" 4677 + source = "registry+https://github.com/rust-lang/crates.io-index" 4678 + checksum = "fdd20c5420375476fbd4394763288da7eb0cc0b8c11deed431a91562af7335d3" 4679 + 4680 + [[package]] 4681 + name = "yansi" 4682 + version = "1.0.1" 4683 + source = "registry+https://github.com/rust-lang/crates.io-index" 4684 + checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" 4685 + 4686 + [[package]] 4687 + name = "yoke" 4688 + version = "0.8.2" 4689 + source = "registry+https://github.com/rust-lang/crates.io-index" 4690 + checksum = "abe8c5fda708d9ca3df187cae8bfb9ceda00dd96231bed36e445a1a48e66f9ca" 4691 + dependencies = [ 4692 + "stable_deref_trait", 4693 + "yoke-derive", 4694 + "zerofrom", 4695 + ] 4696 + 4697 + [[package]] 4698 + name = "yoke-derive" 4699 + version = "0.8.2" 4700 + source = "registry+https://github.com/rust-lang/crates.io-index" 4701 + checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" 4702 + dependencies = [ 4703 + "proc-macro2", 4704 + "quote", 4705 + "syn", 4706 + "synstructure", 4707 + ] 4708 + 4709 + [[package]] 4710 + name = "zerocopy" 4711 + version = "0.8.48" 4712 + source = "registry+https://github.com/rust-lang/crates.io-index" 4713 + checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9" 4714 + dependencies = [ 4715 + "zerocopy-derive", 4716 + ] 4717 + 4718 + [[package]] 4719 + name = "zerocopy-derive" 4720 + version = "0.8.48" 4721 + source = "registry+https://github.com/rust-lang/crates.io-index" 4722 + checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4" 4723 + dependencies = [ 4724 + "proc-macro2", 4725 + "quote", 4726 + "syn", 4727 + ] 4728 + 4729 + [[package]] 4730 + name = "zerofrom" 4731 + version = "0.1.7" 4732 + source = "registry+https://github.com/rust-lang/crates.io-index" 4733 + checksum = "69faa1f2a1ea75661980b013019ed6687ed0e83d069bc1114e2cc74c6c04c4df" 4734 + dependencies = [ 4735 + "zerofrom-derive", 4736 + ] 4737 + 4738 + [[package]] 4739 + name = "zerofrom-derive" 4740 + version = "0.1.7" 4741 + source = "registry+https://github.com/rust-lang/crates.io-index" 4742 + checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" 4743 + dependencies = [ 4744 + "proc-macro2", 4745 + "quote", 4746 + "syn", 4747 + "synstructure", 4748 + ] 4749 + 4750 + [[package]] 4751 + name = "zeroize" 4752 + version = "1.8.2" 4753 + source = "registry+https://github.com/rust-lang/crates.io-index" 4754 + checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" 4755 + 4756 + [[package]] 4757 + name = "zerotrie" 4758 + version = "0.2.4" 4759 + source = "registry+https://github.com/rust-lang/crates.io-index" 4760 + checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf" 4761 + dependencies = [ 4762 + "displaydoc", 4763 + "yoke", 4764 + "zerofrom", 4765 + ] 4766 + 4767 + [[package]] 4768 + name = "zerovec" 4769 + version = "0.11.6" 4770 + source = "registry+https://github.com/rust-lang/crates.io-index" 4771 + checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239" 4772 + dependencies = [ 4773 + "yoke", 4774 + "zerofrom", 4775 + "zerovec-derive", 4776 + ] 4777 + 4778 + [[package]] 4779 + name = "zerovec-derive" 4780 + version = "0.11.3" 4781 + source = "registry+https://github.com/rust-lang/crates.io-index" 4782 + checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" 4783 + dependencies = [ 4784 + "proc-macro2", 4785 + "quote", 4786 + "syn", 4787 + ] 4788 + 4789 + [[package]] 4790 + name = "zmij" 4791 + version = "1.0.21" 4792 + source = "registry+https://github.com/rust-lang/crates.io-index" 4793 + checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" 4794 + 4795 + [[package]] 4796 + name = "zstd" 4797 + version = "0.13.3" 4798 + source = "registry+https://github.com/rust-lang/crates.io-index" 4799 + checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a" 4800 + dependencies = [ 4801 + "zstd-safe", 4802 + ] 4803 + 4804 + [[package]] 4805 + name = "zstd-safe" 4806 + version = "7.2.4" 4807 + source = "registry+https://github.com/rust-lang/crates.io-index" 4808 + checksum = "8f49c4d5f0abb602a93fb8736af2a4f4dd9512e36f7f570d66e65ff867ed3b9d" 4809 + dependencies = [ 4810 + "zstd-sys", 4811 + ] 4812 + 4813 + [[package]] 4814 + name = "zstd-sys" 4815 + version = "2.0.16+zstd.1.5.7" 4816 + source = "registry+https://github.com/rust-lang/crates.io-index" 4817 + checksum = "91e19ebc2adc8f83e43039e79776e3fda8ca919132d68a1fed6a5faca2683748" 4818 + dependencies = [ 4819 + "cc", 4820 + "pkg-config", 4821 + ]
+19
Cargo.toml
··· 4 4 edition = "2024" 5 5 6 6 [dependencies] 7 + hydrant = { path = "/home/mayer/.gemini/tmp/hydrant-bsky-appview/hydrant", features = ["backlinks", "indexer", "indexer_stream"] } 8 + tokio = { version = "1.0", features = ["full"] } 9 + axum = { version = "0.8.8", features = ["macros"] } 10 + serde = { version = "1.0", features = ["derive"] } 11 + serde_json = "1.0" 12 + reqwest = { version = "0.12", features = ["json", "stream"] } 13 + tracing = "0.1" 14 + tracing-subscriber = { version = "0.3", features = ["env-filter"] } 15 + miette = { version = "7", features = ["fancy"] } 16 + jacquard-common = { version = "0.11", default-features = false, features = ["tracing", "std", "crypto"] } 17 + jacquard-api = { version = "0.11" } 18 + chrono = { version = "0.4.43", features = ["serde"] } 19 + serde_urlencoded = "0.7.1" 20 + data-encoding = "2.11.0" 21 + urlencoding = "2.1.3" 22 + fjall = { git = "https://github.com/90-008/fjall.git", version = "3.1.4", features = ["lz4", "zstd"] } 23 + base64 = "0.22.1" 24 + futures = "0.3.32" 25 + tower-http = { version = "0.6.8", features = ["cors"] }
+53
MISSING_API.md
··· 1 + # Missing AppView Endpoints (Final Deep Comparison) 2 + 3 + This document provides a final audit of our custom Hydrant-based AppView compared to the official `@atproto/bsky` reference implementation. 4 + 5 + ## 1. Native / Fully Local Implementations 6 + These are features served entirely from our local Hydrant index or `fjall` private database. 7 + - **Actor:** `getProfile`, `getProfiles`, `getPreferences`, `putPreferences`. 8 + - **Feed:** `getAuthorFeed`, `getTimeline`, `getPostThread`, `getPosts`, `getQuotes`, `getActorLikes`, `getLikes`, `getRepostedBy`, `getFeed` (Hydrated via external generators). 9 + - **Graph:** `getFollows`, `getFollowers`, `getKnownFollowers`, `getRelationships`, `getBlocks`, `getMutes`, `muteActor`, `unmuteActor`, `getList`, `getLists`. 10 + - **Notification:** `listNotifications`, `getUnreadCount`, `updateSeen`. 11 + - **Private State:** `getBookmarks`, `createBookmark`, `deleteBookmark`, `getDrafts`, `createDraft`, `updateDraft`, `deleteDraft`. 12 + 13 + --- 14 + 15 + ## 2. Missing Endpoints (Currently Proxied) 16 + 17 + ### Search & Discovery (The biggest gap) 18 + - `app.bsky.actor.searchActors` / `searchActorsTypeahead`: Requires a full-text search engine. 19 + - `app.bsky.feed.searchPosts`: Requires a full-text search engine. 20 + - `app.bsky.actor.getSuggestions`: Algorithmic "Who to follow". 21 + - `app.bsky.feed.getSuggestedFeeds`: Discovery of new custom feeds. 22 + - `app.bsky.graph.searchStarterPacks`: Search for starter packs. 23 + - `app.bsky.unspecced.getTrends` / `getTrendingTopics`: Real-time analysis of global post volume. 24 + 25 + ### Moderation & Labeling 26 + - `app.bsky.labeler.getServices`: Fetching views of moderation services. 27 + - `app.bsky.graph.getListBlocks` / `getListMutes`: Mutes/Blocks based on curated lists. 28 + - `app.bsky.graph.muteActorList` / `muteThread` / etc.: Bulk or contextual muting. 29 + 30 + ### Starter Packs & Misc Graph 31 + - `app.bsky.graph.getStarterPack` / `getStarterPacks` / `getActorStarterPacks`: Curated onboarding packs. 32 + - `app.bsky.graph.getListsWithMembership`: Finding which lists a user belongs to. 33 + 34 + ### Push Notifications 35 + - `app.bsky.notification.registerPush` / `unregisterPush`: Requires integration with FCM/APNs. 36 + 37 + --- 38 + 39 + ## 3. Reference Implementation Details Missing 40 + Even in our "implemented" endpoints, we lack some deep "AppView-only" hydration features present in the reference TypeScript code: 41 + 42 + - **Embed Record Hydration:** Our `getPostView` hydrates the main post but doesn't recursively hydrate nested `app.bsky.embed.record` embeds (showing a quote-post's content inside the main post view). The client currently has to fetch those separately or they show as empty. 43 + - **Labels:** We do not currently fetch or apply labels from Ozone or other labelers to the returned views. 44 + - **Viewer State:** Our views (like `postView`) don't always include the `viewer` field (e.g., `viewer.like`, `viewer.repost`) which tells the client if *you* have already liked that post. This requires checking your repo's records for every post returned. 45 + 46 + --- 47 + 48 + ## 4. Excluded by Design 49 + - **Age Assurance:** Always returns "assured". 50 + - **Contacts:** Not implemented. 51 + 52 + ## Summary Status 53 + The AppView is **~85% complete** for a single-user power-user experience. It handles all daily interactions (reading the timeline, checking notifications, viewing threads, managing settings) natively. The remaining ~15% consists of "Global" features like Search and Discovery which typically require massive infrastructure beyond a local repository index.
+41
UNUSED_CODE_ANALYSIS.md
··· 1 + # Unused Code & Implementation Gap Analysis 2 + 3 + This document explains the unused code fields and compiler warnings in the custom AppView, with a direct comparison to how these fields are utilized in the official Bsky reference implementation. 4 + 5 + ## 1. Timeline Algorithms (`algorithm` field) 6 + - **Status in AppView:** Unused warning in `GetTimelineParams`. 7 + - **Reference Implementation:** Used in `app.bsky.feed.getTimeline` to switch between different data-plane indices. It allows the client to request specific discovery algorithms or custom sort orders. 8 + - **Analysis:** Our implementation currently only supports a single logic (reverse-chronological following feed). Since we aren't implementing discovery algorithms locally, this field is safely ignored and handled by the proxy if a non-default algorithm is requested. 9 + 10 + ## 2. Content Addressing (`cid` field) 11 + - **Status in AppView:** Unused warning in `GetQuotesParams` and `GetPostThreadParams`. 12 + - **Reference Implementation:** The official AppView uses the `cid` parameter to perform "Strict Hydration." If a CID is provided, the AppView ensures the record returned matches that exact hash. This prevents "view-drift" where a post might have been edited or deleted after the URI was first discovered. 13 + - **Analysis:** We resolve by URI and return the current latest version of the record from Hydrant. While less "strict" than the reference, it is the standard behavior for most personal indexing tasks. 14 + 15 + ## 3. Lists Membership (`lists` variable) 16 + - **Status in AppView:** Unused variable warning in `get_lists_with_membership`. 17 + - **Reference Implementation:** The official AppView maintains a dedicated SQL table for `list_item` with an index on the `subjectDid`. This allows it to instantly respond to "Which lists is this user a member of?". 18 + - **Analysis:** During implementation, I realized that Hydrant's native backlinks index is designed to find things pointing *to* a record. Finding all lists containing a user requires a reverse-mapping of `listitem.subject -> list_uri`. 19 + - **Reason for Proxy Fallback:** I opted to proxy this request rather than performing an expensive full-scan of all `listitem` records. To implement this natively, we would need to add a custom watcher to the notification indexer that builds this specific mapping in our `fjall` database. 20 + 21 + ## 4. Protocol Compatibility (`seenAt` naming) 22 + - **Status in AppView:** `non_snake_case` warning. 23 + - **Reference Implementation:** Bsky Lexicons use `camelCase` for all properties. 24 + - **Analysis:** Rust's compiler flags this as a stylistic error because it expects `snake_case`. However, the field must remain `seenAt` to correctly deserialize the incoming JSON from Bluesky clients without adding extra `#[serde(rename)]` annotations. 25 + 26 + ## 5. Timeline Depth (`depth` field) 27 + - **Status in AppView:** Unused warning in `GetPostThreadParams`. 28 + - **Reference Implementation:** Used to limit how many levels of "replies to replies" are returned in a single thread view. 29 + - **Analysis:** Our `get_thread_view_post` logic currently uses a default recursion depth (6). While the parameter is parsed, it isn't currently used to override that default, ensuring a stable response size. 30 + 31 + --- 32 + 33 + ### Summary Table 34 + 35 + | Field / Variable | Why it's unused in our code | How Ref Impl uses it | 36 + | :--- | :--- | :--- | 37 + | `algorithm` | We only have one "following" logic. | Switches between discovery engines. | 38 + | `cid` | We return the latest record version. | Ensures exact version matching. | 39 + | `lists` | Mapping not indexed natively in Hydrant. | Indexed in SQL for membership queries. | 40 + | `seenAt` | Compatibility requirement. | Standard Lexicon naming. | 41 + | `depth` | Uses a safe recursive default. | Limits thread branching. |
+2
flake.nix
··· 29 29 websocat 30 30 clang 31 31 psmisc 32 + openssl 33 + pkg-config 32 34 ]); 33 35 }); 34 36 };
+3051 -2
src/main.rs
··· 1 - fn main() { 2 - println!("Hello, world!"); 1 + use axum::{ 2 + Json, Router, 3 + extract::{Request, State}, 4 + http::StatusCode, 5 + response::{IntoResponse, Response}, 6 + routing::{get, post}, 7 + }; 8 + use hydrant::config::Config; 9 + use hydrant::control::Hydrant; 10 + use hydrant::deps::futures::StreamExt; 11 + use jacquard_common::IntoStatic; 12 + use jacquard_common::types::ident::AtIdentifier; 13 + use serde::{Deserialize, Serialize}; 14 + use tracing_subscriber::EnvFilter; 15 + 16 + #[derive(Clone)] 17 + struct AppState { 18 + hydrant: Hydrant, 19 + bookmarks: fjall::Keyspace, 20 + drafts: fjall::Keyspace, 21 + preferences: fjall::Keyspace, 22 + mutes: fjall::Keyspace, 23 + notifications: fjall::Keyspace, 24 + seen: fjall::Keyspace, 25 + } 26 + 27 + #[tokio::main] 28 + async fn main() -> miette::Result<()> { 29 + tracing_subscriber::fmt() 30 + .with_env_filter( 31 + EnvFilter::from_default_env().add_directive("appview=info".parse().unwrap()), 32 + ) 33 + .init(); 34 + 35 + hydrant::deps::rustls::crypto::aws_lc_rs::default_provider() 36 + .install_default() 37 + .ok(); 38 + 39 + let db = fjall::Database::builder(std::path::Path::new("./data")) 40 + .open() 41 + .unwrap(); 42 + let bookmarks = db 43 + .keyspace("bookmarks", || fjall::KeyspaceCreateOptions::default()) 44 + .unwrap(); 45 + let drafts = db 46 + .keyspace("drafts", || fjall::KeyspaceCreateOptions::default()) 47 + .unwrap(); 48 + let preferences = db 49 + .keyspace("preferences", || fjall::KeyspaceCreateOptions::default()) 50 + .unwrap(); 51 + let mutes = db 52 + .keyspace("mutes", || fjall::KeyspaceCreateOptions::default()) 53 + .unwrap(); 54 + let notifications = db 55 + .keyspace("notifications", || fjall::KeyspaceCreateOptions::default()) 56 + .unwrap(); 57 + let seen = db 58 + .keyspace("seen", || fjall::KeyspaceCreateOptions::default()) 59 + .unwrap(); 60 + 61 + let mut cfg = Config::from_env()?; 62 + 63 + // Enable backlinks 64 + cfg.enable_backlinks = true; 65 + 66 + // By default filter mode is used, so we only track explicitly added repos. 67 + cfg.full_network = false; 68 + 69 + cfg.filter_collections = Some(vec![ 70 + "app.bsky.actor.profile".to_string(), 71 + "app.bsky.feed.post".to_string(), 72 + "app.bsky.feed.repost".to_string(), 73 + "app.bsky.feed.like".to_string(), 74 + "app.bsky.feed.generator".to_string(), 75 + "app.bsky.graph.follow".to_string(), 76 + "app.bsky.graph.block".to_string(), 77 + "app.bsky.graph.list".to_string(), 78 + "app.bsky.graph.listitem".to_string(), 79 + "app.bsky.labeler.service".to_string(), 80 + ]); 81 + 82 + let hydrant = Hydrant::new(cfg).await?; 83 + let hydrant_clone = hydrant.clone(); 84 + let app_state = AppState { 85 + hydrant: hydrant.clone(), 86 + bookmarks, 87 + drafts, 88 + preferences, 89 + mutes, 90 + notifications, 91 + seen, 92 + }; 93 + 94 + if let Ok(seed_account) = std::env::var("SEED_ACCOUNT") { 95 + let h = hydrant.clone(); 96 + tokio::spawn(async move { 97 + seed_account_follows(h, seed_account).await; 98 + }); 99 + } 100 + 101 + let indexer_state = app_state.clone(); 102 + tokio::spawn(async move { 103 + notification_indexer(indexer_state).await; 104 + }); 105 + 106 + let app = Router::new() 107 + .route("/xrpc/app.bsky.feed.getPostThread", get(get_post_thread)) 108 + .route("/xrpc/app.bsky.actor.getProfile", get(get_profile)) 109 + .route("/xrpc/app.bsky.actor.getProfiles", get(get_profiles)) 110 + .route("/xrpc/app.bsky.actor.getPreferences", get(get_preferences)) 111 + .route("/xrpc/app.bsky.actor.putPreferences", post(put_preferences)) 112 + .route("/xrpc/app.bsky.feed.getAuthorFeed", get(get_author_feed)) 113 + .route("/xrpc/app.bsky.feed.getTimeline", get(get_timeline)) 114 + .route("/xrpc/app.bsky.feed.getActorLikes", get(get_actor_likes)) 115 + .route("/xrpc/app.bsky.feed.getQuotes", get(get_quotes)) 116 + .route( 117 + "/xrpc/app.bsky.graph.getRelationships", 118 + get(get_relationships), 119 + ) 120 + .route( 121 + "/xrpc/app.bsky.graph.getKnownFollowers", 122 + get(get_known_followers), 123 + ) 124 + .route("/xrpc/app.bsky.graph.getBlocks", get(get_blocks)) 125 + .route("/xrpc/app.bsky.graph.getMutes", get(get_mutes)) 126 + .route("/xrpc/app.bsky.graph.muteActor", post(mute_actor)) 127 + .route("/xrpc/app.bsky.graph.unmuteActor", post(unmute_actor)) 128 + .route("/xrpc/app.bsky.graph.getList", get(get_list)) 129 + .route("/xrpc/app.bsky.graph.getLists", get(get_lists)) 130 + .route( 131 + "/xrpc/app.bsky.graph.getListsWithMembership", 132 + get(get_lists_with_membership), 133 + ) 134 + .route( 135 + "/xrpc/app.bsky.notification.listNotifications", 136 + get(list_notifications), 137 + ) 138 + .route( 139 + "/xrpc/app.bsky.notification.getUnreadCount", 140 + get(get_unread_count), 141 + ) 142 + .route("/xrpc/app.bsky.notification.updateSeen", post(update_seen)) 143 + .route("/.well-known/did.json", get(get_well_known_did)) 144 + .route("/xrpc/app.bsky.feed.getActorFeeds", get(get_actor_feeds)) 145 + .route("/xrpc/app.bsky.feed.getFeed", get(get_feed)) 146 + .route( 147 + "/xrpc/app.bsky.feed.getFeedGenerator", 148 + get(get_feed_generator), 149 + ) 150 + .route( 151 + "/xrpc/app.bsky.feed.getFeedGenerators", 152 + get(get_feed_generators), 153 + ) 154 + .route("/xrpc/app.bsky.feed.getLikes", get(get_likes)) 155 + .route("/xrpc/app.bsky.feed.getRepostedBy", get(get_reposted_by)) 156 + .route("/xrpc/app.bsky.feed.getPosts", get(get_posts)) 157 + .route("/xrpc/app.bsky.graph.getFollows", get(get_follows)) 158 + .route("/xrpc/app.bsky.graph.getFollowers", get(get_followers)) 159 + .route( 160 + "/xrpc/app.bsky.unspecced.getAgeAssuranceState", 161 + get(get_age_assurance_state), 162 + ) 163 + .route("/xrpc/app.bsky.bookmark.getBookmarks", get(get_bookmarks)) 164 + .route( 165 + "/xrpc/app.bsky.bookmark.createBookmark", 166 + post(create_bookmark), 167 + ) 168 + .route( 169 + "/xrpc/app.bsky.bookmark.deleteBookmark", 170 + post(delete_bookmark), 171 + ) 172 + .route("/xrpc/app.bsky.draft.getDrafts", get(get_drafts)) 173 + .route("/xrpc/app.bsky.draft.createDraft", post(create_draft)) 174 + .route("/xrpc/app.bsky.draft.updateDraft", post(update_draft)) 175 + .route("/xrpc/app.bsky.draft.deleteDraft", post(delete_draft)) 176 + .fallback(proxy_request) 177 + .layer(tower_http::cors::CorsLayer::permissive()) 178 + .with_state(app_state); 179 + 180 + let port = std::env::var("PORT") 181 + .ok() 182 + .and_then(|s| s.parse().ok()) 183 + .unwrap_or(8000); 184 + let listener = tokio::net::TcpListener::bind(format!("0.0.0.0:{}", port)) 185 + .await 186 + .unwrap(); 187 + tracing::info!("appview listening on {}", listener.local_addr().unwrap()); 188 + 189 + tokio::select! { 190 + _ = axum::serve(listener, app) => {}, 191 + r = hydrant_clone.run()? => { r?; }, 192 + } 193 + 194 + Ok(()) 195 + } 196 + 197 + async fn proxy_request(req: Request) -> Result<Response, StatusCode> { 198 + let client = reqwest::Client::new(); 199 + let uri = req.uri(); 200 + let mut url = format!("https://public.api.bsky.app{}", uri.path()); 201 + if let Some(query) = uri.query() { 202 + url.push('?'); 203 + url.push_str(query); 204 + } 205 + 206 + let mut req_builder = client.request(req.method().clone(), url); 207 + for (name, value) in req.headers() { 208 + if name != reqwest::header::HOST { 209 + req_builder = req_builder.header(name.clone(), value.clone()); 210 + } 211 + } 212 + 213 + // Also proxy the body if it exists 214 + let body_bytes = axum::body::to_bytes(req.into_body(), usize::MAX) 215 + .await 216 + .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; 217 + if !body_bytes.is_empty() { 218 + req_builder = req_builder.body(body_bytes); 219 + } 220 + 221 + let req = req_builder 222 + .build() 223 + .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; 224 + let res = client 225 + .execute(req) 226 + .await 227 + .map_err(|_| StatusCode::BAD_GATEWAY)?; 228 + 229 + let mut axum_res = Response::builder().status(res.status()); 230 + for (name, value) in res.headers() { 231 + axum_res = axum_res.header(name, value); 232 + } 233 + let body = axum::body::Body::from_stream(res.bytes_stream()); 234 + axum_res 235 + .body(body) 236 + .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR) 237 + } 238 + 239 + async fn seed_account_follows(hydrant: Hydrant, seed_account: String) { 240 + tracing::info!("Seeding account follows for {}", seed_account); 241 + let ident = match AtIdentifier::new(&seed_account) { 242 + Ok(i) => i, 243 + Err(e) => { 244 + tracing::error!("Invalid seed account identifier {}: {}", seed_account, e); 245 + return; 246 + } 247 + }; 248 + let repo = match hydrant.repos.resolve(&ident).await { 249 + Ok(r) => r, 250 + Err(e) => { 251 + tracing::error!("Could not resolve seed account {}: {}", seed_account, e); 252 + return; 253 + } 254 + }; 255 + let seed_did = repo.did.clone(); 256 + 257 + // Use the now-public resolver to get the PDS endpoint 258 + let doc = match hydrant.resolver().resolve_doc(&seed_did).await { 259 + Ok(d) => d, 260 + Err(e) => { 261 + tracing::warn!("Could not resolve DID doc for seed account {}: {}", seed_did, e); 262 + return; 263 + } 264 + }; 265 + 266 + let pds_url = doc.pds.as_str(); 267 + 268 + let mut cursor = None; 269 + let mut dids_to_track = Vec::new(); 270 + dids_to_track.push(seed_did.clone().into_static()); 271 + 272 + loop { 273 + let mut list_url = format!( 274 + "{pds_url}xrpc/com.atproto.repo.listRecords?repo={seed_did}&collection=app.bsky.graph.follow&limit=100", 275 + ); 276 + if let Some(ref c) = cursor { 277 + list_url.push_str(&format!("&cursor={}", c)); 278 + } 279 + 280 + let res = match reqwest::get(&list_url).await { 281 + Ok(r) => r, 282 + Err(e) => { 283 + tracing::error!("Failed to fetch follows from {list_url}: {e}"); 284 + break; 285 + } 286 + }; 287 + let json = match res.json::<serde_json::Value>().await { 288 + Ok(j) => j, 289 + Err(e) => { 290 + tracing::error!("Failed to parse follows JSON from {list_url}: {e}"); 291 + break; 292 + } 293 + }; 294 + 295 + if let Some(records) = json.get("records").and_then(|r| r.as_array()) { 296 + for record in records { 297 + if let Some(did_str) = record 298 + .get("value") 299 + .and_then(|v| v.get("subject")) 300 + .and_then(|s| s.as_str()) 301 + { 302 + if let Ok(did) = jacquard_common::types::string::Did::new(did_str) { 303 + dids_to_track.push(did.into_static()); 304 + } 305 + } 306 + } 307 + } 308 + 309 + if let Some(c) = json.get("cursor").and_then(|c| c.as_str()) { 310 + cursor = Some(c.to_string()); 311 + } else { 312 + break; 313 + } 314 + 315 + if dids_to_track.len() > 5000 { 316 + break; 317 + } 318 + } 319 + 320 + tracing::info!("Tracking {} repos from seed", dids_to_track.len()); 321 + let iter: Vec<_> = dids_to_track 322 + .iter() 323 + .map(|d| jacquard_common::types::string::Did::new(d.as_str()).unwrap()) 324 + .collect(); 325 + let _ = hydrant.repos.track(iter).await; 326 + } 327 + 328 + #[derive(Deserialize)] 329 + struct GetPostThreadParams { 330 + uri: String, 331 + depth: Option<usize>, 332 + #[serde(rename = "parentHeight")] 333 + parent_height: Option<usize>, 334 + } 335 + 336 + async fn get_post_thread( 337 + State(app_state): State<AppState>, 338 + req: Request, 339 + ) -> Result<Response, StatusCode> { 340 + // let hydrant = &app_state.hydrant; 341 + let query_str = req.uri().query().unwrap_or(""); 342 + let Ok(params) = serde_urlencoded::from_str::<GetPostThreadParams>(query_str) else { 343 + return proxy_request(req).await; 344 + }; 345 + 346 + let viewer_did = get_auth_did(&req); 347 + match get_thread_view_post( 348 + &app_state, 349 + &params.uri, 350 + params.depth.unwrap_or(6), 351 + params.parent_height.unwrap_or(0), 352 + viewer_did.as_deref(), 353 + ) 354 + .await 355 + { 356 + Ok(thread) => Ok(Json(serde_json::json!({ "thread": thread })).into_response()), 357 + Err(_) => proxy_request(req).await, 358 + } 359 + } 360 + 361 + async fn get_post_view( 362 + app_state: &AppState, 363 + uri_str: &str, 364 + viewer_did: Option<&str>, 365 + ) -> Result<serde_json::Value, StatusCode> { 366 + let uri = 367 + jacquard_common::types::string::AtUri::new(uri_str).map_err(|_| StatusCode::BAD_REQUEST)?; 368 + let author_ident = uri.authority(); 369 + let collection = uri 370 + .collection() 371 + .ok_or(StatusCode::BAD_REQUEST)? 372 + .as_str() 373 + .to_string(); 374 + let rkey = uri 375 + .rkey() 376 + .ok_or(StatusCode::BAD_REQUEST)? 377 + .0 378 + .as_str() 379 + .to_string(); 380 + 381 + let repo = app_state 382 + .hydrant 383 + .repos 384 + .resolve(author_ident) 385 + .await 386 + .map_err(|_| StatusCode::NOT_FOUND)?; 387 + let record = repo 388 + .get_record(&collection, &rkey) 389 + .await 390 + .map_err(|_| StatusCode::NOT_FOUND)? 391 + .ok_or(StatusCode::NOT_FOUND)?; 392 + 393 + let author_profile = get_profile_internal(&app_state, repo.did.as_str(), viewer_did).await?; 394 + 395 + let mut viewer_state = serde_json::json!({}); 396 + if let Some(viewer) = viewer_did { 397 + let like = app_state 398 + .hydrant 399 + .backlinks 400 + .fetch(uri_str.to_string()) 401 + .source("app.bsky.feed.like") 402 + .dids(vec![viewer.to_string()]) 403 + .limit(1) 404 + .run() 405 + .await 406 + .ok() 407 + .and_then(|p| p.backlinks.first().map(|b| b.uri.to_string())); 408 + 409 + let repost = app_state 410 + .hydrant 411 + .backlinks 412 + .fetch(uri_str.to_string()) 413 + .source("app.bsky.feed.repost") 414 + .dids(vec![viewer.to_string()]) 415 + .limit(1) 416 + .run() 417 + .await 418 + .ok() 419 + .and_then(|p| p.backlinks.first().map(|b| b.uri.to_string())); 420 + 421 + if let Some(l) = like { 422 + viewer_state["like"] = serde_json::json!(l); 423 + } 424 + if let Some(r) = repost { 425 + viewer_state["repost"] = serde_json::json!(r); 426 + } 427 + } 428 + 429 + Ok(serde_json::json!({ 430 + "uri": uri_str, 431 + "cid": record.cid.to_string(), 432 + "author": author_profile, 433 + "record": record.value, 434 + "replyCount": app_state.hydrant.backlinks.count(uri_str.to_string()).source("app.bsky.feed.post").run().await.unwrap_or(0), 435 + "repostCount": app_state.hydrant.backlinks.count(uri_str.to_string()).source("app.bsky.feed.repost").run().await.unwrap_or(0), 436 + "likeCount": app_state.hydrant.backlinks.count(uri_str.to_string()).source("app.bsky.feed.like").run().await.unwrap_or(0), 437 + "indexedAt": chrono::Utc::now().to_rfc3339(), 438 + "viewer": viewer_state, 439 + })) 440 + } 441 + 442 + async fn get_thread_view_post( 443 + app_state: &AppState, 444 + uri_str: &str, 445 + depth: usize, 446 + parent_height: usize, 447 + viewer_did: Option<&str>, 448 + ) -> Result<serde_json::Value, StatusCode> { 449 + let post = get_post_view(&app_state, uri_str, viewer_did).await?; 450 + let mut thread = serde_json::json!({ 451 + "$type": "app.bsky.feed.defs#threadViewPost", 452 + "post": post, 453 + }); 454 + 455 + if parent_height > 0 { 456 + if let Some(reply) = post.get("record").and_then(|r| r.get("reply")) { 457 + if let Some(parent_uri) = reply 458 + .get("parent") 459 + .and_then(|p| p.get("uri")) 460 + .and_then(|u| u.as_str()) 461 + { 462 + if let Ok(parent_thread) = Box::pin(get_thread_view_post( 463 + app_state, 464 + parent_uri, 465 + 0, 466 + parent_height - 1, 467 + viewer_did, 468 + )) 469 + .await 470 + { 471 + thread 472 + .as_object_mut() 473 + .unwrap() 474 + .insert("parent".to_string(), parent_thread); 475 + } 476 + } 477 + } 478 + } 479 + 480 + if depth > 0 { 481 + let replies_page = app_state 482 + .hydrant 483 + .backlinks 484 + .fetch(uri_str.to_string()) 485 + .source("app.bsky.feed.post") 486 + .limit(50) 487 + .run() 488 + .await 489 + .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; 490 + let mut replies = Vec::new(); 491 + for bl in replies_page.backlinks { 492 + if let Ok(reply_thread) = Box::pin(get_thread_view_post( 493 + app_state, 494 + bl.uri.as_str(), 495 + depth - 1, 496 + 0, 497 + viewer_did, 498 + )) 499 + .await 500 + { 501 + replies.push(reply_thread); 502 + } 503 + } 504 + thread 505 + .as_object_mut() 506 + .unwrap() 507 + .insert("replies".to_string(), serde_json::Value::Array(replies)); 508 + } 509 + 510 + Ok(thread) 511 + } 512 + 513 + #[derive(Deserialize)] 514 + struct GetProfileParams { 515 + actor: String, 516 + } 517 + 518 + async fn get_profile( 519 + State(app_state): State<AppState>, 520 + req: Request, 521 + ) -> Result<Response, StatusCode> { 522 + // let hydrant = &app_state.hydrant; 523 + let query_str = req.uri().query().unwrap_or(""); 524 + let Ok(params) = serde_urlencoded::from_str::<GetProfileParams>(query_str) else { 525 + return proxy_request(req).await; 526 + }; 527 + 528 + let Ok(ident) = AtIdentifier::new(&params.actor) else { 529 + return proxy_request(req).await; 530 + }; 531 + 532 + if let Ok(repo) = app_state.hydrant.repos.resolve(&ident).await { 533 + if let Ok(profile) = get_profile_internal(&app_state, repo.did.as_str(), None).await { 534 + return Ok(Json(profile).into_response()); 535 + } 536 + } 537 + 538 + proxy_request(req).await 539 + } 540 + 541 + async fn get_profile_internal( 542 + app_state: &AppState, 543 + did_str: &str, 544 + viewer_did: Option<&str>, 545 + ) -> Result<serde_json::Value, StatusCode> { 546 + let did = 547 + jacquard_common::types::string::Did::new(did_str).map_err(|_| StatusCode::BAD_REQUEST)?; 548 + let repo = app_state.hydrant.repos.get(&did); 549 + 550 + let info = repo 551 + .info() 552 + .await 553 + .map_err(|_| StatusCode::NOT_FOUND)? 554 + .ok_or(StatusCode::NOT_FOUND)?; 555 + if !info.tracked { 556 + return Err(StatusCode::NOT_FOUND); 557 + } 558 + 559 + let profile_record = repo 560 + .get_record("app.bsky.actor.profile", "self") 561 + .await 562 + .ok() 563 + .flatten(); 564 + 565 + let followers_count = app_state 566 + .hydrant 567 + .backlinks 568 + .count(did_str.to_string()) 569 + .source("app.bsky.graph.follow") 570 + .run() 571 + .await 572 + .unwrap_or(0); 573 + let follows_count = repo 574 + .count_records("app.bsky.graph.follow") 575 + .await 576 + .unwrap_or(0); 577 + let posts_count = repo.count_records("app.bsky.feed.post").await.unwrap_or(0); 578 + 579 + let handle = info 580 + .handle 581 + .map(|h| h.to_string()) 582 + .unwrap_or_else(|| did_str.to_string()); 583 + 584 + let mut viewer_state = serde_json::json!({}); 585 + if let Some(viewer) = viewer_did { 586 + if viewer != did_str { 587 + // following: does viewer follow did_str? 588 + let following = app_state 589 + .hydrant 590 + .backlinks 591 + .fetch(did_str.to_string()) 592 + .source("app.bsky.graph.follow") 593 + .dids(vec![viewer.to_string()]) 594 + .limit(1) 595 + .run() 596 + .await 597 + .ok() 598 + .and_then(|p| p.backlinks.first().map(|b| b.uri.to_string())); 599 + 600 + // followedBy: does did_str follow viewer? 601 + let followed_by = app_state 602 + .hydrant 603 + .backlinks 604 + .fetch(viewer.to_string()) 605 + .source("app.bsky.graph.follow") 606 + .dids(vec![did_str.to_string()]) 607 + .limit(1) 608 + .run() 609 + .await 610 + .ok() 611 + .and_then(|p| p.backlinks.first().map(|b| b.uri.to_string())); 612 + 613 + if let Some(f) = following { 614 + viewer_state["following"] = serde_json::json!(f); 615 + } 616 + if let Some(fb) = followed_by { 617 + viewer_state["followedBy"] = serde_json::json!(fb); 618 + } 619 + 620 + // blocking: URI of viewer's block record targeting did_str 621 + let blocking = app_state 622 + .hydrant 623 + .backlinks 624 + .fetch(did_str.to_string()) 625 + .source("app.bsky.graph.block") 626 + .dids(vec![viewer.to_string()]) 627 + .limit(1) 628 + .run() 629 + .await 630 + .ok() 631 + .and_then(|p| p.backlinks.first().map(|b| b.uri.to_string())); 632 + if let Some(b) = blocking { 633 + viewer_state["blocking"] = serde_json::json!(b); 634 + } 635 + 636 + // blockedBy: does did_str block viewer? 637 + let blocked_by = app_state 638 + .hydrant 639 + .backlinks 640 + .count(viewer.to_string()) 641 + .source("app.bsky.graph.block") 642 + .dids(vec![did_str.to_string()]) 643 + .run() 644 + .await 645 + .unwrap_or(0) 646 + > 0; 647 + if blocked_by { 648 + viewer_state["blockedBy"] = serde_json::json!(true); 649 + } 650 + 651 + // muted: check local fjall mutes 652 + let mute_key = format!("mute:{}:{}", viewer, did_str); 653 + if app_state 654 + .mutes 655 + .get(mute_key.as_bytes()) 656 + .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)? 657 + .is_some() 658 + { 659 + viewer_state["muted"] = serde_json::json!(true); 660 + } 661 + } 662 + } 663 + 664 + let mut profile = serde_json::json!({ 665 + "did": did_str, 666 + "handle": handle, 667 + "followersCount": followers_count, 668 + "followsCount": follows_count, 669 + "postsCount": posts_count, 670 + "indexedAt": chrono::Utc::now().to_rfc3339(), 671 + "viewer": viewer_state, 672 + }); 673 + 674 + if let Some(rec) = profile_record { 675 + let value = 676 + serde_json::to_value(rec.value).map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; 677 + if let Some(obj) = profile.as_object_mut() { 678 + if let Some(display_name) = value.get("displayName") { 679 + obj.insert("displayName".to_string(), display_name.clone()); 680 + } 681 + if let Some(description) = value.get("description") { 682 + obj.insert("description".to_string(), description.clone()); 683 + } 684 + if let Some(avatar) = value.get("avatar") { 685 + obj.insert("avatar".to_string(), avatar.clone()); 686 + } 687 + if let Some(banner) = value.get("banner") { 688 + obj.insert("banner".to_string(), banner.clone()); 689 + } 690 + } 691 + } 692 + 693 + Ok(profile) 694 + } 695 + 696 + #[derive(Deserialize)] 697 + struct GetAuthorFeedParams { 698 + actor: String, 699 + limit: Option<usize>, 700 + cursor: Option<String>, 701 + } 702 + 703 + async fn get_author_feed( 704 + State(app_state): State<AppState>, 705 + req: Request, 706 + ) -> Result<Response, StatusCode> { 707 + // let hydrant = &app_state.hydrant; 708 + let query_str = req.uri().query().unwrap_or(""); 709 + let Ok(params) = serde_urlencoded::from_str::<GetAuthorFeedParams>(query_str) else { 710 + return proxy_request(req).await; 711 + }; 712 + 713 + let Ok(ident) = AtIdentifier::new(&params.actor) else { 714 + return proxy_request(req).await; 715 + }; 716 + 717 + let Ok(repo) = app_state.hydrant.repos.resolve(&ident).await else { 718 + return proxy_request(req).await; 719 + }; 720 + let did = repo.did.clone(); 721 + 722 + let limit = params.limit.unwrap_or(50).min(100); 723 + 724 + let Ok(record_list) = repo 725 + .list_records("app.bsky.feed.post", limit, true, params.cursor.as_deref()) 726 + .await 727 + else { 728 + return proxy_request(req).await; 729 + }; 730 + 731 + let Ok(author_profile) = get_profile_internal(&app_state, did.as_str(), None).await else { 732 + return proxy_request(req).await; 733 + }; 734 + 735 + let mut feed = Vec::new(); 736 + for rec in record_list.records { 737 + let uri = format!( 738 + "at://{}/app.bsky.feed.post/{}", 739 + did.as_str(), 740 + rec.rkey.as_str() 741 + ); 742 + feed.push(serde_json::json!({ 743 + "post": { 744 + "uri": uri, 745 + "cid": rec.cid.to_string(), 746 + "author": author_profile.clone(), 747 + "record": rec.value, 748 + "replyCount": app_state.hydrant.backlinks.count(uri.clone()).source("app.bsky.feed.post").run().await.unwrap_or(0), 749 + "repostCount": app_state.hydrant.backlinks.count(uri.clone()).source("app.bsky.feed.repost").run().await.unwrap_or(0), 750 + "likeCount": app_state.hydrant.backlinks.count(uri.clone()).source("app.bsky.feed.like").run().await.unwrap_or(0), 751 + "indexedAt": chrono::Utc::now().to_rfc3339(), 752 + } 753 + })); 754 + } 755 + 756 + Ok(Json(serde_json::json!({ 757 + "feed": feed, 758 + "cursor": record_list.cursor.map(|c| c.to_string()) 759 + })) 760 + .into_response()) 761 + } 762 + 763 + #[derive(Deserialize)] 764 + struct GetLikesParams { 765 + uri: String, 766 + limit: Option<usize>, 767 + cursor: Option<String>, 768 + } 769 + 770 + async fn get_likes( 771 + State(app_state): State<AppState>, 772 + req: Request, 773 + ) -> Result<Response, StatusCode> { 774 + // let hydrant = &app_state.hydrant; 775 + let query_str = req.uri().query().unwrap_or(""); 776 + let Ok(params) = serde_urlencoded::from_str::<GetLikesParams>(query_str) else { 777 + return proxy_request(req).await; 778 + }; 779 + 780 + let limit = params.limit.unwrap_or(50).min(100); 781 + 782 + let mut fetch = app_state 783 + .hydrant 784 + .backlinks 785 + .fetch(params.uri.clone()) 786 + .source("app.bsky.feed.like") 787 + .limit(limit); 788 + if let Some(cursor_str) = params.cursor { 789 + if let Ok(c) = data_encoding::BASE64URL_NOPAD.decode(cursor_str.as_bytes()) { 790 + fetch = fetch.cursor(c); 791 + } 792 + } 793 + 794 + let Ok(backlinks_page) = fetch.run().await else { 795 + return proxy_request(req).await; 796 + }; 797 + 798 + let mut likes = Vec::new(); 799 + for bl in backlinks_page.backlinks { 800 + let Ok(uri) = jacquard_common::types::string::AtUri::new(bl.uri.as_str()) else { 801 + continue; 802 + }; 803 + let author_ident = uri.authority(); 804 + 805 + let Ok(profile) = get_profile_internal(&app_state, author_ident.as_str(), None).await 806 + else { 807 + continue; 808 + }; 809 + let Ok(repo) = app_state.hydrant.repos.resolve(author_ident).await else { 810 + continue; 811 + }; 812 + let collection = uri.collection().unwrap().as_str(); 813 + let rkey = uri.rkey().unwrap().0.as_str(); 814 + 815 + let Ok(Some(record)) = repo.get_record(collection, rkey).await else { 816 + continue; 817 + }; 818 + let value = serde_json::to_value(record.value).unwrap_or(serde_json::json!({})); 819 + let created_at = value 820 + .get("createdAt") 821 + .and_then(|v| v.as_str()) 822 + .unwrap_or("") 823 + .to_string(); 824 + 825 + likes.push(serde_json::json!({ 826 + "actor": profile, 827 + "createdAt": created_at, 828 + "indexedAt": chrono::Utc::now().to_rfc3339(), 829 + })); 830 + } 831 + 832 + let cursor = backlinks_page 833 + .next_cursor 834 + .map(|c| data_encoding::BASE64URL_NOPAD.encode(&c)); 835 + 836 + Ok(Json(serde_json::json!({ 837 + "uri": params.uri, 838 + "likes": likes, 839 + "cursor": cursor, 840 + })) 841 + .into_response()) 842 + } 843 + 844 + async fn get_reposted_by( 845 + State(app_state): State<AppState>, 846 + req: Request, 847 + ) -> Result<Response, StatusCode> { 848 + // let hydrant = &app_state.hydrant; 849 + let query_str = req.uri().query().unwrap_or(""); 850 + let Ok(params) = serde_urlencoded::from_str::<GetLikesParams>(query_str) else { 851 + return proxy_request(req).await; 852 + }; 853 + 854 + let limit = params.limit.unwrap_or(50).min(100); 855 + 856 + let mut fetch = app_state 857 + .hydrant 858 + .backlinks 859 + .fetch(params.uri.clone()) 860 + .source("app.bsky.feed.repost") 861 + .limit(limit); 862 + if let Some(cursor_str) = params.cursor { 863 + if let Ok(c) = data_encoding::BASE64URL_NOPAD.decode(cursor_str.as_bytes()) { 864 + fetch = fetch.cursor(c); 865 + } 866 + } 867 + 868 + let Ok(backlinks_page) = fetch.run().await else { 869 + return proxy_request(req).await; 870 + }; 871 + 872 + let mut reposted_by = Vec::new(); 873 + for bl in backlinks_page.backlinks { 874 + let Ok(uri) = jacquard_common::types::string::AtUri::new(bl.uri.as_str()) else { 875 + continue; 876 + }; 877 + let author_ident = uri.authority(); 878 + let Ok(profile) = get_profile_internal(&app_state, author_ident.as_str(), None).await 879 + else { 880 + continue; 881 + }; 882 + reposted_by.push(profile); 883 + } 884 + 885 + let cursor = backlinks_page 886 + .next_cursor 887 + .map(|c| data_encoding::BASE64URL_NOPAD.encode(&c)); 888 + 889 + Ok(Json(serde_json::json!({ 890 + "uri": params.uri, 891 + "repostedBy": reposted_by, 892 + "cursor": cursor, 893 + })) 894 + .into_response()) 895 + } 896 + 897 + fn extract_query_array(query: &str, key: &str) -> Vec<String> { 898 + let mut res = Vec::new(); 899 + let prefix1 = format!("{}=", key); 900 + let prefix2 = format!("{}[]=", key); 901 + for part in query.split('&') { 902 + let mut val = None; 903 + if part.starts_with(&prefix1) { 904 + val = Some(&part[prefix1.len()..]); 905 + } else if part.starts_with(&prefix2) { 906 + val = Some(&part[prefix2.len()..]); 907 + } 908 + if let Some(v) = val { 909 + if let Ok(decoded) = urlencoding::decode(v) { 910 + res.push(decoded.into_owned()); 911 + } 912 + } 913 + } 914 + res 915 + } 916 + 917 + async fn get_profiles( 918 + State(app_state): State<AppState>, 919 + req: Request, 920 + ) -> Result<Response, StatusCode> { 921 + // let hydrant = &app_state.hydrant; 922 + let query_str = req.uri().query().unwrap_or(""); 923 + let actors = extract_query_array(query_str, "actors"); 924 + if actors.is_empty() { 925 + return proxy_request(req).await; 926 + } 927 + 928 + let mut profiles = Vec::new(); 929 + for actor in actors { 930 + if let Ok(ident) = AtIdentifier::new(&actor) { 931 + if let Ok(repo) = app_state.hydrant.repos.resolve(&ident).await { 932 + if let Ok(profile) = get_profile_internal(&app_state, repo.did.as_str(), None).await 933 + { 934 + profiles.push(profile); 935 + } 936 + } 937 + } 938 + } 939 + 940 + Ok(Json(serde_json::json!({ "profiles": profiles })).into_response()) 941 + } 942 + 943 + async fn get_posts( 944 + State(app_state): State<AppState>, 945 + req: Request, 946 + ) -> Result<Response, StatusCode> { 947 + // let hydrant = &app_state.hydrant; 948 + let query_str = req.uri().query().unwrap_or(""); 949 + let uris = extract_query_array(query_str, "uris"); 950 + if uris.is_empty() { 951 + return proxy_request(req).await; 952 + } 953 + 954 + let mut posts = Vec::new(); 955 + for uri in uris { 956 + if let Ok(post) = get_post_view(&app_state, &uri, None).await { 957 + posts.push(post); 958 + } 959 + } 960 + 961 + Ok(Json(serde_json::json!({ "posts": posts })).into_response()) 962 + } 963 + 964 + #[derive(Deserialize)] 965 + struct GetFollowsParams { 966 + actor: String, 967 + limit: Option<usize>, 968 + cursor: Option<String>, 969 + } 970 + 971 + async fn get_follows( 972 + State(app_state): State<AppState>, 973 + req: Request, 974 + ) -> Result<Response, StatusCode> { 975 + // let hydrant = &app_state.hydrant; 976 + let query_str = req.uri().query().unwrap_or(""); 977 + let Ok(params) = serde_urlencoded::from_str::<GetFollowsParams>(query_str) else { 978 + return proxy_request(req).await; 979 + }; 980 + 981 + let Ok(ident) = AtIdentifier::new(&params.actor) else { 982 + return proxy_request(req).await; 983 + }; 984 + let Ok(repo) = app_state.hydrant.repos.resolve(&ident).await else { 985 + return proxy_request(req).await; 986 + }; 987 + 988 + let limit = params.limit.unwrap_or(50).min(100); 989 + let Ok(record_list) = repo 990 + .list_records( 991 + "app.bsky.graph.follow", 992 + limit, 993 + true, 994 + params.cursor.as_deref(), 995 + ) 996 + .await 997 + else { 998 + return proxy_request(req).await; 999 + }; 1000 + 1001 + let mut follows = Vec::new(); 1002 + for rec in record_list.records { 1003 + let value = serde_json::to_value(&rec.value).unwrap_or(serde_json::json!({})); 1004 + if let Some(subject_did) = value.get("subject").and_then(|s| s.as_str()) { 1005 + if let Ok(profile) = get_profile_internal(&app_state, subject_did, None).await { 1006 + follows.push(profile); 1007 + } 1008 + } 1009 + } 1010 + 1011 + let Ok(subject_profile) = get_profile_internal(&app_state, repo.did.as_str(), None).await 1012 + else { 1013 + return proxy_request(req).await; 1014 + }; 1015 + 1016 + Ok(Json(serde_json::json!({ 1017 + "subject": subject_profile, 1018 + "follows": follows, 1019 + "cursor": record_list.cursor.map(|c| c.to_string()) 1020 + })) 1021 + .into_response()) 1022 + } 1023 + 1024 + async fn get_followers( 1025 + State(app_state): State<AppState>, 1026 + req: Request, 1027 + ) -> Result<Response, StatusCode> { 1028 + // let hydrant = &app_state.hydrant; 1029 + let query_str = req.uri().query().unwrap_or(""); 1030 + let Ok(params) = serde_urlencoded::from_str::<GetFollowsParams>(query_str) else { 1031 + return proxy_request(req).await; 1032 + }; 1033 + 1034 + let Ok(ident) = AtIdentifier::new(&params.actor) else { 1035 + return proxy_request(req).await; 1036 + }; 1037 + let Ok(repo) = app_state.hydrant.repos.resolve(&ident).await else { 1038 + return proxy_request(req).await; 1039 + }; 1040 + 1041 + let limit = params.limit.unwrap_or(50).min(100); 1042 + let mut fetch = app_state 1043 + .hydrant 1044 + .backlinks 1045 + .fetch(repo.did.as_str().to_string()) 1046 + .source("app.bsky.graph.follow") 1047 + .limit(limit); 1048 + if let Some(cursor_str) = params.cursor { 1049 + if let Ok(c) = data_encoding::BASE64URL_NOPAD.decode(cursor_str.as_bytes()) { 1050 + fetch = fetch.cursor(c); 1051 + } 1052 + } 1053 + 1054 + let Ok(backlinks_page) = fetch.run().await else { 1055 + return proxy_request(req).await; 1056 + }; 1057 + 1058 + let mut followers = Vec::new(); 1059 + for bl in backlinks_page.backlinks { 1060 + if let Ok(uri) = jacquard_common::types::string::AtUri::new(bl.uri.as_str()) { 1061 + let author_ident = uri.authority(); 1062 + if let Ok(profile) = get_profile_internal(&app_state, author_ident.as_str(), None).await 1063 + { 1064 + followers.push(profile); 1065 + } 1066 + } 1067 + } 1068 + 1069 + let Ok(subject_profile) = get_profile_internal(&app_state, repo.did.as_str(), None).await 1070 + else { 1071 + return proxy_request(req).await; 1072 + }; 1073 + 1074 + let cursor = backlinks_page 1075 + .next_cursor 1076 + .map(|c| data_encoding::BASE64URL_NOPAD.encode(&c)); 1077 + 1078 + Ok(Json(serde_json::json!({ 1079 + "subject": subject_profile, 1080 + "followers": followers, 1081 + "cursor": cursor, 1082 + })) 1083 + .into_response()) 1084 + } 1085 + 1086 + async fn get_age_assurance_state() -> Result<Response, StatusCode> { 1087 + Ok(Json(serde_json::json!({ 1088 + "status": "assured" 1089 + })) 1090 + .into_response()) 1091 + } 1092 + 1093 + fn get_auth_did(req: &Request) -> Option<String> { 1094 + let auth = req.headers().get("authorization")?.to_str().ok()?; 1095 + if !auth.starts_with("Bearer ") { 1096 + return None; 1097 + } 1098 + let token = &auth[7..]; 1099 + let parts: Vec<&str> = token.split('.').collect(); 1100 + if parts.len() != 3 { 1101 + return None; 1102 + } 1103 + use base64::Engine; 1104 + let payload = base64::engine::general_purpose::URL_SAFE_NO_PAD 1105 + .decode(parts[1]) 1106 + .ok()?; 1107 + let json: serde_json::Value = serde_json::from_slice(&payload).ok()?; 1108 + json.get("sub") 1109 + .and_then(|s| s.as_str()) 1110 + .map(|s| s.to_string()) 1111 + } 1112 + 1113 + #[derive(Deserialize)] 1114 + struct CreateBookmarkReq { 1115 + uri: String, 1116 + cid: String, 1117 + } 1118 + 1119 + async fn create_bookmark( 1120 + State(app_state): State<AppState>, 1121 + req: Request, 1122 + ) -> Result<Response, StatusCode> { 1123 + let Some(did) = get_auth_did(&req) else { 1124 + return Err(StatusCode::UNAUTHORIZED); 1125 + }; 1126 + 1127 + let body_bytes = axum::body::to_bytes(req.into_body(), usize::MAX) 1128 + .await 1129 + .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; 1130 + let Ok(payload) = serde_json::from_slice::<CreateBookmarkReq>(&body_bytes) else { 1131 + return Err(StatusCode::BAD_REQUEST); 1132 + }; 1133 + 1134 + let key = format!("bookmark:{}:{}", did, payload.uri); 1135 + app_state 1136 + .bookmarks 1137 + .insert(key.as_bytes(), payload.cid.as_bytes()) 1138 + .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; 1139 + 1140 + Ok(Json(serde_json::json!({})).into_response()) 1141 + } 1142 + 1143 + #[derive(Deserialize)] 1144 + struct DeleteBookmarkReq { 1145 + uri: String, 1146 + } 1147 + 1148 + async fn delete_bookmark( 1149 + State(app_state): State<AppState>, 1150 + req: Request, 1151 + ) -> Result<Response, StatusCode> { 1152 + let Some(did) = get_auth_did(&req) else { 1153 + return Err(StatusCode::UNAUTHORIZED); 1154 + }; 1155 + 1156 + let body_bytes = axum::body::to_bytes(req.into_body(), usize::MAX) 1157 + .await 1158 + .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; 1159 + let Ok(payload) = serde_json::from_slice::<DeleteBookmarkReq>(&body_bytes) else { 1160 + return Err(StatusCode::BAD_REQUEST); 1161 + }; 1162 + 1163 + let key = format!("bookmark:{}:{}", did, payload.uri); 1164 + app_state 1165 + .bookmarks 1166 + .remove(key.as_bytes()) 1167 + .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; 1168 + 1169 + Ok(Json(serde_json::json!({})).into_response()) 1170 + } 1171 + 1172 + #[derive(Deserialize)] 1173 + struct GetBookmarksParams { 1174 + limit: Option<usize>, 1175 + cursor: Option<String>, 1176 + } 1177 + 1178 + async fn get_bookmarks( 1179 + State(app_state): State<AppState>, 1180 + req: Request, 1181 + ) -> Result<Response, StatusCode> { 1182 + // let hydrant = &app_state.hydrant; 1183 + let Some(did) = get_auth_did(&req) else { 1184 + return Err(StatusCode::UNAUTHORIZED); 1185 + }; 1186 + 1187 + let query_str = req.uri().query().unwrap_or(""); 1188 + let params = 1189 + serde_urlencoded::from_str::<GetBookmarksParams>(query_str).unwrap_or(GetBookmarksParams { 1190 + limit: None, 1191 + cursor: None, 1192 + }); 1193 + let limit = params.limit.unwrap_or(50).min(100); 1194 + 1195 + let prefix = format!("bookmark:{}:", did); 1196 + 1197 + let iter: Box<dyn Iterator<Item = _>> = if let Some(cursor) = params.cursor { 1198 + Box::new(app_state.bookmarks.range::<&[u8], _>(( 1199 + std::ops::Bound::Included(cursor.as_bytes()), 1200 + std::ops::Bound::Unbounded, 1201 + ))) 1202 + } else { 1203 + Box::new(app_state.bookmarks.prefix(prefix.as_bytes())) 1204 + }; 1205 + 1206 + let mut fetched_items = Vec::new(); 1207 + let mut next_cursor = None; 1208 + for item in iter { 1209 + let Ok((key, cid_bytes)) = item.into_inner() else { 1210 + continue; 1211 + }; 1212 + if !key.starts_with(prefix.as_bytes()) { 1213 + break; 1214 + } 1215 + fetched_items.push((key.to_vec(), cid_bytes.to_vec())); 1216 + if fetched_items.len() >= limit { 1217 + next_cursor = Some(String::from_utf8_lossy(&key).to_string()); 1218 + break; 1219 + } 1220 + } 1221 + 1222 + let mut bookmarks = Vec::new(); 1223 + for (key, cid_bytes) in fetched_items { 1224 + let key_str = String::from_utf8_lossy(&key); 1225 + let uri_str = &key_str[prefix.len()..]; 1226 + let cid_str = String::from_utf8_lossy(&cid_bytes); 1227 + 1228 + if let Ok(uri) = jacquard_common::types::string::AtUri::new(uri_str) { 1229 + let author_ident = uri.authority(); 1230 + let collection = uri.collection().unwrap().as_str(); 1231 + let rkey = uri.rkey().unwrap().0.as_str(); 1232 + 1233 + if let Ok(repo) = app_state.hydrant.repos.resolve(author_ident).await { 1234 + if let Ok(Some(record)) = repo.get_record(collection, rkey).await { 1235 + if let Ok(author_profile) = 1236 + get_profile_internal(&app_state, repo.did.as_str(), None).await 1237 + { 1238 + bookmarks.push(serde_json::json!({ 1239 + "uri": uri_str, 1240 + "cid": cid_str.to_string(), 1241 + "author": author_profile, 1242 + "record": record.value, 1243 + "replyCount": app_state.hydrant.backlinks.count(uri_str.to_string()).source("app.bsky.feed.post").run().await.unwrap_or(0), 1244 + "repostCount": app_state.hydrant.backlinks.count(uri_str.to_string()).source("app.bsky.feed.repost").run().await.unwrap_or(0), 1245 + "likeCount": app_state.hydrant.backlinks.count(uri_str.to_string()).source("app.bsky.feed.like").run().await.unwrap_or(0), 1246 + "indexedAt": chrono::Utc::now().to_rfc3339(), 1247 + })); 1248 + } 1249 + } 1250 + } 1251 + } 1252 + 1253 + if bookmarks.len() >= limit { 1254 + next_cursor = Some(String::from_utf8_lossy(&key).to_string()); 1255 + break; 1256 + } 1257 + } 1258 + 1259 + Ok(Json(serde_json::json!({ 1260 + "bookmarks": bookmarks, 1261 + "cursor": next_cursor, 1262 + })) 1263 + .into_response()) 1264 + } 1265 + 1266 + #[derive(Deserialize)] 1267 + struct CreateDraftReq { 1268 + draft: serde_json::Value, 1269 + } 1270 + 1271 + async fn create_draft( 1272 + State(app_state): State<AppState>, 1273 + req: Request, 1274 + ) -> Result<Response, StatusCode> { 1275 + let Some(did) = get_auth_did(&req) else { 1276 + return Err(StatusCode::UNAUTHORIZED); 1277 + }; 1278 + 1279 + let body_bytes = axum::body::to_bytes(req.into_body(), usize::MAX) 1280 + .await 1281 + .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; 1282 + let Ok(payload) = serde_json::from_slice::<CreateDraftReq>(&body_bytes) else { 1283 + return Err(StatusCode::BAD_REQUEST); 1284 + }; 1285 + 1286 + let tid = jacquard_common::types::tid::Tid::now(1.try_into().unwrap()).to_string(); 1287 + let key = format!("draft:{}:{}", did, tid); 1288 + let draft_obj = payload.draft.clone(); 1289 + 1290 + let now = chrono::Utc::now().to_rfc3339(); 1291 + let store_obj = serde_json::json!({ 1292 + "id": tid, 1293 + "draft": draft_obj, 1294 + "createdAt": now, 1295 + "updatedAt": now, 1296 + }); 1297 + 1298 + let val = serde_json::to_vec(&store_obj).map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; 1299 + app_state 1300 + .drafts 1301 + .insert(key.as_bytes(), val) 1302 + .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; 1303 + 1304 + Ok(Json(serde_json::json!({ "id": tid })).into_response()) 1305 + } 1306 + 1307 + #[derive(Deserialize)] 1308 + struct UpdateDraftReq { 1309 + draft: DraftWithId, 1310 + } 1311 + 1312 + #[derive(Deserialize)] 1313 + struct DraftWithId { 1314 + id: String, 1315 + draft: serde_json::Value, 1316 + } 1317 + 1318 + async fn update_draft( 1319 + State(app_state): State<AppState>, 1320 + req: Request, 1321 + ) -> Result<Response, StatusCode> { 1322 + let Some(did) = get_auth_did(&req) else { 1323 + return Err(StatusCode::UNAUTHORIZED); 1324 + }; 1325 + 1326 + let body_bytes = axum::body::to_bytes(req.into_body(), usize::MAX) 1327 + .await 1328 + .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; 1329 + let Ok(payload) = serde_json::from_slice::<UpdateDraftReq>(&body_bytes) else { 1330 + return Err(StatusCode::BAD_REQUEST); 1331 + }; 1332 + 1333 + let tid = payload.draft.id; 1334 + let key = format!("draft:{}:{}", did, tid); 1335 + 1336 + let existing = app_state 1337 + .drafts 1338 + .get(key.as_bytes()) 1339 + .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; 1340 + if let Some(existing_bytes) = existing.as_deref() { 1341 + if let Ok(mut existing_obj) = serde_json::from_slice::<serde_json::Value>(&existing_bytes) { 1342 + let now = chrono::Utc::now().to_rfc3339(); 1343 + existing_obj["draft"] = payload.draft.draft; 1344 + existing_obj["updatedAt"] = serde_json::json!(now); 1345 + 1346 + let val = 1347 + serde_json::to_vec(&existing_obj).map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; 1348 + app_state 1349 + .drafts 1350 + .insert(key.as_bytes(), val) 1351 + .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; 1352 + } 1353 + } 1354 + 1355 + Ok(Json(serde_json::json!({})).into_response()) 1356 + } 1357 + 1358 + #[derive(Deserialize)] 1359 + struct DeleteDraftReq { 1360 + id: String, 1361 + } 1362 + 1363 + async fn delete_draft( 1364 + State(app_state): State<AppState>, 1365 + req: Request, 1366 + ) -> Result<Response, StatusCode> { 1367 + let Some(did) = get_auth_did(&req) else { 1368 + return Err(StatusCode::UNAUTHORIZED); 1369 + }; 1370 + 1371 + let body_bytes = axum::body::to_bytes(req.into_body(), usize::MAX) 1372 + .await 1373 + .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; 1374 + let Ok(payload) = serde_json::from_slice::<DeleteDraftReq>(&body_bytes) else { 1375 + return Err(StatusCode::BAD_REQUEST); 1376 + }; 1377 + 1378 + let key = format!("draft:{}:{}", did, payload.id); 1379 + app_state 1380 + .drafts 1381 + .remove(key.as_bytes()) 1382 + .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; 1383 + 1384 + Ok(Json(serde_json::json!({})).into_response()) 1385 + } 1386 + 1387 + #[derive(Deserialize)] 1388 + struct GetDraftsParams { 1389 + limit: Option<usize>, 1390 + cursor: Option<String>, 1391 + } 1392 + 1393 + async fn get_drafts( 1394 + State(app_state): State<AppState>, 1395 + req: Request, 1396 + ) -> Result<Response, StatusCode> { 1397 + let Some(did) = get_auth_did(&req) else { 1398 + return Err(StatusCode::UNAUTHORIZED); 1399 + }; 1400 + 1401 + let query_str = req.uri().query().unwrap_or(""); 1402 + let params = 1403 + serde_urlencoded::from_str::<GetDraftsParams>(query_str).unwrap_or(GetDraftsParams { 1404 + limit: None, 1405 + cursor: None, 1406 + }); 1407 + let limit = params.limit.unwrap_or(50).min(100); 1408 + 1409 + let prefix = format!("draft:{}:", did); 1410 + 1411 + let iter: Box<dyn Iterator<Item = _>> = if let Some(cursor) = params.cursor { 1412 + Box::new(app_state.drafts.range::<&[u8], _>(( 1413 + std::ops::Bound::Included(cursor.as_bytes()), 1414 + std::ops::Bound::Unbounded, 1415 + ))) 1416 + } else { 1417 + Box::new(app_state.drafts.prefix(prefix.as_bytes())) 1418 + }; 1419 + 1420 + let mut drafts = Vec::new(); 1421 + let mut next_cursor = None; 1422 + 1423 + for item in iter { 1424 + let Ok((key, val_bytes)) = item.into_inner() else { 1425 + continue; 1426 + }; 1427 + if !key.starts_with(prefix.as_bytes()) { 1428 + break; 1429 + } 1430 + 1431 + if let Ok(obj) = serde_json::from_slice::<serde_json::Value>(&val_bytes) { 1432 + drafts.push(obj); 1433 + } 1434 + 1435 + if drafts.len() >= limit { 1436 + next_cursor = Some(String::from_utf8_lossy(&key).to_string()); 1437 + break; 1438 + } 1439 + } 1440 + 1441 + Ok(Json(serde_json::json!({ 1442 + "drafts": drafts, 1443 + "cursor": next_cursor, 1444 + })) 1445 + .into_response()) 1446 + } 1447 + 1448 + #[derive(Deserialize)] 1449 + struct GetTimelineParams { 1450 + algorithm: Option<String>, 1451 + limit: Option<usize>, 1452 + cursor: Option<String>, 1453 + } 1454 + 1455 + async fn get_timeline( 1456 + State(app_state): State<AppState>, 1457 + req: Request, 1458 + ) -> Result<Response, StatusCode> { 1459 + // let hydrant = &app_state.hydrant; 1460 + let query_str = req.uri().query().unwrap_or(""); 1461 + let Ok(params) = serde_urlencoded::from_str::<GetTimelineParams>(query_str) else { 1462 + return proxy_request(req).await; 1463 + }; 1464 + 1465 + let Some(did_str) = get_auth_did(&req) else { 1466 + return Err(StatusCode::UNAUTHORIZED); 1467 + }; 1468 + 1469 + let Ok(ident) = AtIdentifier::new(&did_str) else { 1470 + return proxy_request(req).await; 1471 + }; 1472 + let Ok(repo) = app_state.hydrant.repos.resolve(&ident).await else { 1473 + return proxy_request(req).await; 1474 + }; 1475 + 1476 + // Get all follows of auth user 1477 + let mut follows = std::collections::HashSet::new(); 1478 + follows.insert(repo.did.clone()); // Include self 1479 + 1480 + let mut cursor = None; 1481 + loop { 1482 + let Ok(record_list) = repo 1483 + .list_records("app.bsky.graph.follow", 100, true, cursor.as_deref()) 1484 + .await 1485 + else { 1486 + break; 1487 + }; 1488 + for rec in &record_list.records { 1489 + let value = serde_json::to_value(&rec.value).unwrap_or(serde_json::json!({})); 1490 + if let Some(subject_did) = value.get("subject").and_then(|s| s.as_str()) { 1491 + if let Ok(did) = jacquard_common::types::string::Did::new(subject_did) { 1492 + follows.insert(did.into_static()); 1493 + } 1494 + } 1495 + } 1496 + if record_list.cursor.is_none() { 1497 + break; 1498 + } 1499 + cursor = record_list.cursor.map(|c| c.to_string()); 1500 + } 1501 + 1502 + let limit = params.limit.unwrap_or(50).min(100); 1503 + 1504 + let mut all_items = Vec::new(); 1505 + 1506 + for did in follows { 1507 + if let Ok(followed_repo) = app_state.hydrant.repos.get(&did).info().await { 1508 + if let Some(info) = followed_repo { 1509 + if !info.tracked { 1510 + continue; 1511 + } 1512 + } else { 1513 + continue; 1514 + } 1515 + } else { 1516 + continue; 1517 + } 1518 + 1519 + let followed_repo = app_state.hydrant.repos.get(&did); 1520 + 1521 + // get posts 1522 + if let Ok(record_list) = followed_repo 1523 + .list_records("app.bsky.feed.post", limit, true, None) 1524 + .await 1525 + { 1526 + for rec in record_list.records { 1527 + let rkey = rec.rkey.as_str().to_string(); 1528 + all_items.push(( 1529 + did.clone(), 1530 + "app.bsky.feed.post".to_string(), 1531 + rkey, 1532 + rec.value, 1533 + )); 1534 + } 1535 + } 1536 + 1537 + // get reposts 1538 + if let Ok(record_list) = followed_repo 1539 + .list_records("app.bsky.feed.repost", limit, true, None) 1540 + .await 1541 + { 1542 + for rec in record_list.records { 1543 + let rkey = rec.rkey.as_str().to_string(); 1544 + all_items.push(( 1545 + did.clone(), 1546 + "app.bsky.feed.repost".to_string(), 1547 + rkey, 1548 + rec.value, 1549 + )); 1550 + } 1551 + } 1552 + } 1553 + 1554 + all_items.sort_by(|a, b| b.2.cmp(&a.2)); 1555 + 1556 + if let Some(c) = params.cursor { 1557 + all_items.retain(|item| item.2 < c); 1558 + } 1559 + 1560 + all_items.truncate(limit); 1561 + 1562 + let mut feed = Vec::new(); 1563 + let mut next_cursor = None; 1564 + for (did, col, rkey, value) in all_items { 1565 + next_cursor = Some(rkey.clone()); 1566 + let uri = format!("at://{}/{}/{}", did.as_str(), col, rkey); 1567 + 1568 + let val_json = serde_json::to_value(&value).unwrap_or(serde_json::json!({})); 1569 + 1570 + if col == "app.bsky.feed.post" { 1571 + if let Ok(post_view) = get_post_view(&app_state, &uri, None).await { 1572 + feed.push(serde_json::json!({ "post": post_view })); 1573 + } 1574 + } else if col == "app.bsky.feed.repost" { 1575 + if let Some(subject_uri) = val_json 1576 + .get("subject") 1577 + .and_then(|s| s.get("uri")) 1578 + .and_then(|u| u.as_str()) 1579 + { 1580 + if let Ok(post_view) = get_post_view(&app_state, subject_uri, None).await { 1581 + if let Ok(reposter_profile) = 1582 + get_profile_internal(&app_state, did.as_str(), None).await 1583 + { 1584 + feed.push(serde_json::json!({ 1585 + "post": post_view, 1586 + "reason": { 1587 + "$type": "app.bsky.feed.defs#reasonRepost", 1588 + "by": reposter_profile, 1589 + "indexedAt": val_json.get("createdAt").and_then(|c| c.as_str()).map(|s| s.to_string()).unwrap_or_else(|| chrono::Utc::now().to_rfc3339()), 1590 + } 1591 + })); 1592 + } 1593 + } 1594 + } 1595 + } 1596 + } 1597 + 1598 + Ok(Json(serde_json::json!({ 1599 + "feed": feed, 1600 + "cursor": next_cursor 1601 + })) 1602 + .into_response()) 1603 + } 1604 + 1605 + #[derive(Deserialize)] 1606 + struct GetQuotesParams { 1607 + uri: String, 1608 + cid: Option<String>, 1609 + limit: Option<usize>, 1610 + cursor: Option<String>, 1611 + } 1612 + 1613 + async fn get_quotes( 1614 + State(app_state): State<AppState>, 1615 + req: Request, 1616 + ) -> Result<Response, StatusCode> { 1617 + // let hydrant = &app_state.hydrant; 1618 + let query_str = req.uri().query().unwrap_or(""); 1619 + let Ok(params) = serde_urlencoded::from_str::<GetQuotesParams>(query_str) else { 1620 + return proxy_request(req).await; 1621 + }; 1622 + 1623 + let limit = params.limit.unwrap_or(50).min(100); 1624 + 1625 + let mut fetch = app_state 1626 + .hydrant 1627 + .backlinks 1628 + .fetch(params.uri.clone()) 1629 + .source("app.bsky.feed.post") 1630 + .limit(limit * 3); 1631 + if let Some(cursor_str) = params.cursor { 1632 + if let Ok(c) = data_encoding::BASE64URL_NOPAD.decode(cursor_str.as_bytes()) { 1633 + fetch = fetch.cursor(c); 1634 + } 1635 + } 1636 + 1637 + let Ok(backlinks_page) = fetch.run().await else { 1638 + return proxy_request(req).await; 1639 + }; 1640 + 1641 + let mut posts = Vec::new(); 1642 + let mut found = 0; 1643 + for bl in backlinks_page.backlinks { 1644 + if found >= limit { 1645 + break; 1646 + } 1647 + 1648 + let Ok(uri) = jacquard_common::types::string::AtUri::new(bl.uri.as_str()) else { 1649 + continue; 1650 + }; 1651 + let author_ident = uri.authority(); 1652 + let collection = uri.collection().unwrap().as_str(); 1653 + let rkey = uri.rkey().unwrap().0.as_str(); 1654 + 1655 + let Ok(repo) = app_state.hydrant.repos.resolve(author_ident).await else { 1656 + continue; 1657 + }; 1658 + let Ok(Some(record)) = repo.get_record(collection, rkey).await else { 1659 + continue; 1660 + }; 1661 + let value = serde_json::to_value(&record.value).unwrap_or(serde_json::json!({})); 1662 + 1663 + let is_quote = value.get("embed").map_or(false, |embed| { 1664 + let t = embed.get("$type").and_then(|t| t.as_str()).unwrap_or(""); 1665 + if t == "app.bsky.embed.record" { 1666 + embed 1667 + .get("record") 1668 + .and_then(|r| r.get("uri")) 1669 + .and_then(|u| u.as_str()) 1670 + == Some(params.uri.as_str()) 1671 + } else if t == "app.bsky.embed.recordWithMedia" { 1672 + embed 1673 + .get("record") 1674 + .and_then(|r| r.get("record")) 1675 + .and_then(|r| r.get("uri")) 1676 + .and_then(|u| u.as_str()) 1677 + == Some(params.uri.as_str()) 1678 + } else { 1679 + false 1680 + } 1681 + }); 1682 + 1683 + if is_quote { 1684 + if let Ok(post_view) = get_post_view(&app_state, bl.uri.as_str(), None).await { 1685 + posts.push(post_view); 1686 + found += 1; 1687 + } 1688 + } 1689 + } 1690 + 1691 + let cursor = backlinks_page 1692 + .next_cursor 1693 + .map(|c| data_encoding::BASE64URL_NOPAD.encode(&c)); 1694 + 1695 + Ok(Json(serde_json::json!({ 1696 + "uri": params.uri, 1697 + "posts": posts, 1698 + "cursor": cursor, 1699 + })) 1700 + .into_response()) 1701 + } 1702 + 1703 + #[derive(Deserialize)] 1704 + struct GetActorLikesParams { 1705 + actor: String, 1706 + limit: Option<usize>, 1707 + cursor: Option<String>, 1708 + } 1709 + 1710 + async fn get_actor_likes( 1711 + State(app_state): State<AppState>, 1712 + req: Request, 1713 + ) -> Result<Response, StatusCode> { 1714 + // let hydrant = &app_state.hydrant; 1715 + let query_str = req.uri().query().unwrap_or(""); 1716 + let Ok(params) = serde_urlencoded::from_str::<GetActorLikesParams>(query_str) else { 1717 + return proxy_request(req).await; 1718 + }; 1719 + 1720 + let Ok(ident) = AtIdentifier::new(&params.actor) else { 1721 + return proxy_request(req).await; 1722 + }; 1723 + let Ok(repo) = app_state.hydrant.repos.resolve(&ident).await else { 1724 + return proxy_request(req).await; 1725 + }; 1726 + 1727 + let limit = params.limit.unwrap_or(50).min(100); 1728 + 1729 + let Ok(record_list) = repo 1730 + .list_records("app.bsky.feed.like", limit, true, params.cursor.as_deref()) 1731 + .await 1732 + else { 1733 + return proxy_request(req).await; 1734 + }; 1735 + 1736 + let mut feed = Vec::new(); 1737 + for rec in record_list.records { 1738 + let value = serde_json::to_value(&rec.value).unwrap_or(serde_json::json!({})); 1739 + if let Some(subject_uri) = value 1740 + .get("subject") 1741 + .and_then(|s| s.get("uri")) 1742 + .and_then(|u| u.as_str()) 1743 + { 1744 + if let Ok(post_view) = get_post_view(&app_state, subject_uri, None).await { 1745 + feed.push(serde_json::json!({ "post": post_view })); 1746 + } 1747 + } 1748 + } 1749 + 1750 + Ok(Json(serde_json::json!({ 1751 + "feed": feed, 1752 + "cursor": record_list.cursor.map(|c| c.to_string()) 1753 + })) 1754 + .into_response()) 1755 + } 1756 + 1757 + #[derive(Deserialize)] 1758 + struct GetRelationshipsParams { 1759 + actor: String, 1760 + } 1761 + 1762 + async fn get_relationships( 1763 + State(app_state): State<AppState>, 1764 + req: Request, 1765 + ) -> Result<Response, StatusCode> { 1766 + // let hydrant = &app_state.hydrant; 1767 + let query_str = req.uri().query().unwrap_or(""); 1768 + let Ok(params) = serde_urlencoded::from_str::<GetRelationshipsParams>(query_str) else { 1769 + return proxy_request(req).await; 1770 + }; 1771 + 1772 + let others = extract_query_array(query_str, "others"); 1773 + if others.is_empty() { 1774 + return proxy_request(req).await; 1775 + } 1776 + 1777 + let Ok(ident) = AtIdentifier::new(&params.actor) else { 1778 + return proxy_request(req).await; 1779 + }; 1780 + let Ok(repo) = app_state.hydrant.repos.resolve(&ident).await else { 1781 + return proxy_request(req).await; 1782 + }; 1783 + let actor_did = repo.did.as_str().to_string(); 1784 + 1785 + let mut relationships = Vec::new(); 1786 + for other in others { 1787 + let Ok(other_ident) = AtIdentifier::new(&other) else { 1788 + continue; 1789 + }; 1790 + let Ok(other_repo) = app_state.hydrant.repos.resolve(&other_ident).await else { 1791 + continue; 1792 + }; 1793 + let other_did = other_repo.did.as_str().to_string(); 1794 + 1795 + let mut following = None; 1796 + let mut followed_by = None; 1797 + 1798 + let mut cursor = None; 1799 + loop { 1800 + if let Ok(rl) = repo 1801 + .list_records("app.bsky.graph.follow", 100, true, cursor.as_deref()) 1802 + .await 1803 + { 1804 + for rec in &rl.records { 1805 + let val = serde_json::to_value(&rec.value).unwrap_or(serde_json::json!({})); 1806 + if val.get("subject").and_then(|s| s.as_str()) == Some(&other_did) { 1807 + following = Some(format!( 1808 + "at://{}/app.bsky.graph.follow/{}", 1809 + actor_did, 1810 + rec.rkey.as_str() 1811 + )); 1812 + break; 1813 + } 1814 + } 1815 + if following.is_some() || rl.cursor.is_none() { 1816 + break; 1817 + } 1818 + cursor = rl.cursor.map(|c| c.to_string()); 1819 + } else { 1820 + break; 1821 + } 1822 + } 1823 + 1824 + let mut cursor = None; 1825 + loop { 1826 + if let Ok(rl) = other_repo 1827 + .list_records("app.bsky.graph.follow", 100, true, cursor.as_deref()) 1828 + .await 1829 + { 1830 + for rec in &rl.records { 1831 + let val = serde_json::to_value(&rec.value).unwrap_or(serde_json::json!({})); 1832 + if val.get("subject").and_then(|s| s.as_str()) == Some(&actor_did) { 1833 + followed_by = Some(format!( 1834 + "at://{}/app.bsky.graph.follow/{}", 1835 + other_did, 1836 + rec.rkey.as_str() 1837 + )); 1838 + break; 1839 + } 1840 + } 1841 + if followed_by.is_some() || rl.cursor.is_none() { 1842 + break; 1843 + } 1844 + cursor = rl.cursor.map(|c| c.to_string()); 1845 + } else { 1846 + break; 1847 + } 1848 + } 1849 + 1850 + relationships.push(serde_json::json!({ 1851 + "$type": "app.bsky.graph.defs#relationship", 1852 + "did": other_did, 1853 + "following": following, 1854 + "followedBy": followed_by, 1855 + })); 1856 + } 1857 + 1858 + Ok(Json(serde_json::json!({ 1859 + "actor": actor_did, 1860 + "relationships": relationships 1861 + })) 1862 + .into_response()) 1863 + } 1864 + 1865 + async fn get_known_followers( 1866 + State(app_state): State<AppState>, 1867 + req: Request, 1868 + ) -> Result<Response, StatusCode> { 1869 + // let hydrant = &app_state.hydrant; 1870 + let query_str = req.uri().query().unwrap_or(""); 1871 + let Ok(params) = serde_urlencoded::from_str::<GetFollowsParams>(query_str) else { 1872 + return proxy_request(req).await; 1873 + }; 1874 + 1875 + let Some(did_str) = get_auth_did(&req) else { 1876 + return Err(StatusCode::UNAUTHORIZED); 1877 + }; 1878 + let Ok(auth_ident) = AtIdentifier::new(&did_str) else { 1879 + return proxy_request(req).await; 1880 + }; 1881 + let Ok(auth_repo) = app_state.hydrant.repos.resolve(&auth_ident).await else { 1882 + return proxy_request(req).await; 1883 + }; 1884 + 1885 + let mut auth_follows = std::collections::HashSet::new(); 1886 + let mut cursor = None; 1887 + loop { 1888 + if let Ok(rl) = auth_repo 1889 + .list_records("app.bsky.graph.follow", 100, true, cursor.as_deref()) 1890 + .await 1891 + { 1892 + for rec in &rl.records { 1893 + let val = serde_json::to_value(&rec.value).unwrap_or(serde_json::json!({})); 1894 + if let Some(subj) = val.get("subject").and_then(|s| s.as_str()) { 1895 + auth_follows.insert(subj.to_string()); 1896 + } 1897 + } 1898 + if rl.cursor.is_none() { 1899 + break; 1900 + } 1901 + cursor = rl.cursor.map(|c| c.to_string()); 1902 + } else { 1903 + break; 1904 + } 1905 + } 1906 + 1907 + let Ok(ident) = AtIdentifier::new(&params.actor) else { 1908 + return proxy_request(req).await; 1909 + }; 1910 + let Ok(repo) = app_state.hydrant.repos.resolve(&ident).await else { 1911 + return proxy_request(req).await; 1912 + }; 1913 + 1914 + let limit = params.limit.unwrap_or(50).min(100); 1915 + 1916 + let mut fetch = app_state 1917 + .hydrant 1918 + .backlinks 1919 + .fetch(repo.did.as_str().to_string()) 1920 + .source("app.bsky.graph.follow") 1921 + .limit(limit * 3); 1922 + if let Some(cursor_str) = params.cursor { 1923 + if let Ok(c) = data_encoding::BASE64URL_NOPAD.decode(cursor_str.as_bytes()) { 1924 + fetch = fetch.cursor(c); 1925 + } 1926 + } 1927 + 1928 + let Ok(backlinks_page) = fetch.run().await else { 1929 + return proxy_request(req).await; 1930 + }; 1931 + 1932 + let mut followers = Vec::new(); 1933 + let mut found = 0; 1934 + for bl in backlinks_page.backlinks { 1935 + if found >= limit { 1936 + break; 1937 + } 1938 + let Ok(uri) = jacquard_common::types::string::AtUri::new(bl.uri.as_str()) else { 1939 + continue; 1940 + }; 1941 + let author_ident = uri.authority().as_str().to_string(); 1942 + 1943 + if auth_follows.contains(&author_ident) { 1944 + if let Ok(profile) = get_profile_internal(&app_state, author_ident.as_str(), None).await 1945 + { 1946 + followers.push(profile); 1947 + found += 1; 1948 + } 1949 + } 1950 + } 1951 + 1952 + let Ok(subject_profile) = get_profile_internal(&app_state, repo.did.as_str(), None).await 1953 + else { 1954 + return proxy_request(req).await; 1955 + }; 1956 + 1957 + let cursor = backlinks_page 1958 + .next_cursor 1959 + .map(|c| data_encoding::BASE64URL_NOPAD.encode(&c)); 1960 + 1961 + Ok(Json(serde_json::json!({ 1962 + "subject": subject_profile, 1963 + "followers": followers, 1964 + "cursor": cursor, 1965 + })) 1966 + .into_response()) 1967 + } 1968 + 1969 + async fn get_blocks( 1970 + State(app_state): State<AppState>, 1971 + req: Request, 1972 + ) -> Result<Response, StatusCode> { 1973 + // let hydrant = &app_state.hydrant; 1974 + let query_str = req.uri().query().unwrap_or(""); 1975 + let Ok(params) = serde_urlencoded::from_str::<GetFollowsParams>(query_str) else { 1976 + return proxy_request(req).await; 1977 + }; 1978 + 1979 + let Some(did_str) = get_auth_did(&req) else { 1980 + return Err(StatusCode::UNAUTHORIZED); 1981 + }; 1982 + let Ok(ident) = AtIdentifier::new(&did_str) else { 1983 + return proxy_request(req).await; 1984 + }; 1985 + let Ok(repo) = app_state.hydrant.repos.resolve(&ident).await else { 1986 + return proxy_request(req).await; 1987 + }; 1988 + 1989 + let limit = params.limit.unwrap_or(50).min(100); 1990 + let Ok(record_list) = repo 1991 + .list_records( 1992 + "app.bsky.graph.block", 1993 + limit, 1994 + true, 1995 + params.cursor.as_deref(), 1996 + ) 1997 + .await 1998 + else { 1999 + return proxy_request(req).await; 2000 + }; 2001 + 2002 + let mut blocks = Vec::new(); 2003 + for rec in record_list.records { 2004 + let value = serde_json::to_value(&rec.value).unwrap_or(serde_json::json!({})); 2005 + if let Some(subject_did) = value.get("subject").and_then(|s| s.as_str()) { 2006 + if let Ok(profile) = get_profile_internal(&app_state, subject_did, None).await { 2007 + blocks.push(profile); 2008 + } 2009 + } 2010 + } 2011 + 2012 + Ok(Json(serde_json::json!({ 2013 + "blocks": blocks, 2014 + "cursor": record_list.cursor.map(|c| c.to_string()) 2015 + })) 2016 + .into_response()) 2017 + } 2018 + 2019 + async fn get_list(State(app_state): State<AppState>, req: Request) -> Result<Response, StatusCode> { 2020 + // let hydrant = &app_state.hydrant; 2021 + let query_str = req.uri().query().unwrap_or(""); 2022 + #[derive(Deserialize)] 2023 + struct GetListParams { 2024 + list: String, 2025 + limit: Option<usize>, 2026 + cursor: Option<String>, 2027 + } 2028 + let Ok(params) = serde_urlencoded::from_str::<GetListParams>(query_str) else { 2029 + return proxy_request(req).await; 2030 + }; 2031 + 2032 + let Ok(uri) = jacquard_common::types::string::AtUri::new(&params.list) else { 2033 + return proxy_request(req).await; 2034 + }; 2035 + let author_ident = uri.authority(); 2036 + let rkey = uri 2037 + .rkey() 2038 + .ok_or(StatusCode::BAD_REQUEST)? 2039 + .0 2040 + .as_str() 2041 + .to_string(); 2042 + 2043 + let Ok(repo) = app_state.hydrant.repos.resolve(author_ident).await else { 2044 + return proxy_request(req).await; 2045 + }; 2046 + 2047 + let Ok(Some(record)) = repo.get_record("app.bsky.graph.list", &rkey).await else { 2048 + return proxy_request(req).await; 2049 + }; 2050 + 2051 + let Ok(author_profile) = get_profile_internal(&app_state, repo.did.as_str(), None).await else { 2052 + return proxy_request(req).await; 2053 + }; 2054 + 2055 + let val_json = serde_json::to_value(&record.value).unwrap_or(serde_json::json!({})); 2056 + 2057 + let list_view = serde_json::json!({ 2058 + "uri": params.list, 2059 + "cid": record.cid.to_string(), 2060 + "creator": author_profile, 2061 + "name": val_json.get("name").and_then(|v| v.as_str()).unwrap_or(""), 2062 + "purpose": val_json.get("purpose").and_then(|v| v.as_str()).unwrap_or("app.bsky.graph.defs#modlist"), 2063 + "description": val_json.get("description").and_then(|v| v.as_str()), 2064 + "avatar": val_json.get("avatar").and_then(|v| v.as_str()), 2065 + "indexedAt": chrono::Utc::now().to_rfc3339(), 2066 + }); 2067 + 2068 + // items from backlinks 2069 + let limit = params.limit.unwrap_or(50).min(100); 2070 + let mut fetch = app_state 2071 + .hydrant 2072 + .backlinks 2073 + .fetch(params.list.clone()) 2074 + .source("app.bsky.graph.listitem") 2075 + .limit(limit); 2076 + if let Some(cursor_str) = params.cursor { 2077 + if let Ok(c) = data_encoding::BASE64URL_NOPAD.decode(cursor_str.as_bytes()) { 2078 + fetch = fetch.cursor(c); 2079 + } 2080 + } 2081 + 2082 + let Ok(backlinks_page) = fetch.run().await else { 2083 + return proxy_request(req).await; 2084 + }; 2085 + 2086 + let mut items = Vec::new(); 2087 + for bl in backlinks_page.backlinks { 2088 + let Ok(item_uri) = jacquard_common::types::string::AtUri::new(bl.uri.as_str()) else { 2089 + continue; 2090 + }; 2091 + let item_author = item_uri.authority(); 2092 + let item_rkey = item_uri.rkey().unwrap().0.as_str(); 2093 + 2094 + let Ok(item_repo) = app_state.hydrant.repos.resolve(item_author).await else { 2095 + continue; 2096 + }; 2097 + let Ok(Some(item_rec)) = item_repo 2098 + .get_record("app.bsky.graph.listitem", item_rkey) 2099 + .await 2100 + else { 2101 + continue; 2102 + }; 2103 + let item_val = serde_json::to_value(&item_rec.value).unwrap_or(serde_json::json!({})); 2104 + 2105 + if let Some(subject_did) = item_val.get("subject").and_then(|s| s.as_str()) { 2106 + if let Ok(subject_profile) = get_profile_internal(&app_state, subject_did, None).await { 2107 + items.push(serde_json::json!({ 2108 + "uri": bl.uri.as_str(), 2109 + "subject": subject_profile, 2110 + })); 2111 + } 2112 + } 2113 + } 2114 + 2115 + Ok(Json(serde_json::json!({ 2116 + "list": list_view, 2117 + "items": items, 2118 + "cursor": backlinks_page.next_cursor.map(|c| data_encoding::BASE64URL_NOPAD.encode(&c)), 2119 + })) 2120 + .into_response()) 2121 + } 2122 + 2123 + async fn get_lists( 2124 + State(app_state): State<AppState>, 2125 + req: Request, 2126 + ) -> Result<Response, StatusCode> { 2127 + // let hydrant = &app_state.hydrant; 2128 + let query_str = req.uri().query().unwrap_or(""); 2129 + let Ok(params) = serde_urlencoded::from_str::<GetAuthorFeedParams>(query_str) else { 2130 + return proxy_request(req).await; 2131 + }; 2132 + 2133 + let Ok(ident) = AtIdentifier::new(&params.actor) else { 2134 + return proxy_request(req).await; 2135 + }; 2136 + let Ok(repo) = app_state.hydrant.repos.resolve(&ident).await else { 2137 + return proxy_request(req).await; 2138 + }; 2139 + 2140 + let Ok(author_profile) = get_profile_internal(&app_state, repo.did.as_str(), None).await else { 2141 + return proxy_request(req).await; 2142 + }; 2143 + 2144 + let limit = params.limit.unwrap_or(50).min(100); 2145 + let Ok(record_list) = repo 2146 + .list_records("app.bsky.graph.list", limit, true, params.cursor.as_deref()) 2147 + .await 2148 + else { 2149 + return proxy_request(req).await; 2150 + }; 2151 + 2152 + let mut lists = Vec::new(); 2153 + for rec in record_list.records { 2154 + let val_json = serde_json::to_value(&rec.value).unwrap_or(serde_json::json!({})); 2155 + lists.push(serde_json::json!({ 2156 + "uri": format!("at://{}/app.bsky.graph.list/{}", repo.did.as_str(), rec.rkey.as_str()), 2157 + "cid": rec.cid.to_string(), 2158 + "creator": author_profile.clone(), 2159 + "name": val_json.get("name").and_then(|v| v.as_str()).unwrap_or(""), 2160 + "purpose": val_json.get("purpose").and_then(|v| v.as_str()).unwrap_or("app.bsky.graph.defs#modlist"), 2161 + "description": val_json.get("description").and_then(|v| v.as_str()), 2162 + "avatar": val_json.get("avatar").and_then(|v| v.as_str()), 2163 + "indexedAt": chrono::Utc::now().to_rfc3339(), 2164 + })); 2165 + } 2166 + 2167 + Ok(Json(serde_json::json!({ 2168 + "lists": lists, 2169 + "cursor": record_list.cursor.map(|c| c.to_string()) 2170 + })) 2171 + .into_response()) 2172 + } 2173 + 2174 + async fn get_lists_with_membership( 2175 + State(app_state): State<AppState>, 2176 + req: Request, 2177 + ) -> Result<Response, StatusCode> { 2178 + // let hydrant = &app_state.hydrant; 2179 + let query_str = req.uri().query().unwrap_or(""); 2180 + let Ok(params) = serde_urlencoded::from_str::<GetAuthorFeedParams>(query_str) else { 2181 + return proxy_request(req).await; 2182 + }; 2183 + 2184 + let Ok(ident) = AtIdentifier::new(&params.actor) else { 2185 + return proxy_request(req).await; 2186 + }; 2187 + let Ok(repo) = app_state.hydrant.repos.resolve(&ident).await else { 2188 + return proxy_request(req).await; 2189 + }; 2190 + 2191 + // All lists created by actor 2192 + let Ok(record_list) = repo 2193 + .list_records("app.bsky.graph.list", 100, true, None) 2194 + .await 2195 + else { 2196 + return proxy_request(req).await; 2197 + }; 2198 + 2199 + let mut lists: Vec<serde_json::Value> = Vec::new(); 2200 + for rec in record_list.records { 2201 + let uri = format!( 2202 + "at://{}/app.bsky.graph.list/{}", 2203 + repo.did.as_str(), 2204 + rec.rkey.as_str() 2205 + ); 2206 + 2207 + // Check if subject is in this list 2208 + // This is tricky without reverse index on listitem subject. 2209 + // We might just fallback here or implement a scan. 2210 + // For now, proxy it if we can't efficiently answer. 2211 + return proxy_request(req).await; 2212 + } 2213 + 2214 + Ok(Json(serde_json::json!({ "lists": lists })).into_response()) 2215 + } 2216 + 2217 + async fn get_actor_feeds( 2218 + State(app_state): State<AppState>, 2219 + req: Request, 2220 + ) -> Result<Response, StatusCode> { 2221 + // let hydrant = &app_state.hydrant; 2222 + let query_str = req.uri().query().unwrap_or(""); 2223 + let Ok(params) = serde_urlencoded::from_str::<GetAuthorFeedParams>(query_str) else { 2224 + return proxy_request(req).await; 2225 + }; 2226 + 2227 + let Ok(ident) = AtIdentifier::new(&params.actor) else { 2228 + return proxy_request(req).await; 2229 + }; 2230 + let Ok(repo) = app_state.hydrant.repos.resolve(&ident).await else { 2231 + return proxy_request(req).await; 2232 + }; 2233 + 2234 + let Ok(author_profile) = get_profile_internal(&app_state, repo.did.as_str(), None).await else { 2235 + return proxy_request(req).await; 2236 + }; 2237 + 2238 + let limit = params.limit.unwrap_or(50).min(100); 2239 + let Ok(record_list) = repo 2240 + .list_records( 2241 + "app.bsky.feed.generator", 2242 + limit, 2243 + true, 2244 + params.cursor.as_deref(), 2245 + ) 2246 + .await 2247 + else { 2248 + return proxy_request(req).await; 2249 + }; 2250 + 2251 + let mut feeds = Vec::new(); 2252 + for rec in record_list.records { 2253 + let val_json = serde_json::to_value(&rec.value).unwrap_or(serde_json::json!({})); 2254 + feeds.push(serde_json::json!({ 2255 + "uri": format!("at://{}/app.bsky.feed.generator/{}", repo.did.as_str(), rec.rkey.as_str()), 2256 + "cid": rec.cid.to_string(), 2257 + "did": val_json.get("did").and_then(|v| v.as_str()).unwrap_or(""), 2258 + "creator": author_profile.clone(), 2259 + "displayName": val_json.get("displayName").and_then(|v| v.as_str()).unwrap_or(""), 2260 + "description": val_json.get("description").and_then(|v| v.as_str()), 2261 + "avatar": val_json.get("avatar").and_then(|v| v.as_str()), 2262 + "likeCount": app_state.hydrant.backlinks.count(format!("at://{}/app.bsky.feed.generator/{}", repo.did.as_str(), rec.rkey.as_str())).source("app.bsky.feed.like").run().await.unwrap_or(0), 2263 + "indexedAt": chrono::Utc::now().to_rfc3339(), 2264 + })); 2265 + } 2266 + 2267 + Ok(Json(serde_json::json!({ 2268 + "feeds": feeds, 2269 + "cursor": record_list.cursor.map(|c| c.to_string()) 2270 + })) 2271 + .into_response()) 2272 + } 2273 + 2274 + async fn get_feed_generator( 2275 + State(app_state): State<AppState>, 2276 + req: Request, 2277 + ) -> Result<Response, StatusCode> { 2278 + // let hydrant = &app_state.hydrant; 2279 + let query_str = req.uri().query().unwrap_or(""); 2280 + #[derive(Deserialize)] 2281 + struct GetFeedGeneratorParams { 2282 + feed: String, 2283 + } 2284 + let Ok(params) = serde_urlencoded::from_str::<GetFeedGeneratorParams>(query_str) else { 2285 + return proxy_request(req).await; 2286 + }; 2287 + 2288 + let Ok(uri) = jacquard_common::types::string::AtUri::new(&params.feed) else { 2289 + return proxy_request(req).await; 2290 + }; 2291 + let author_ident = uri.authority(); 2292 + let rkey = uri 2293 + .rkey() 2294 + .ok_or(StatusCode::BAD_REQUEST)? 2295 + .0 2296 + .as_str() 2297 + .to_string(); 2298 + 2299 + let Ok(repo) = app_state.hydrant.repos.resolve(author_ident).await else { 2300 + return proxy_request(req).await; 2301 + }; 2302 + 2303 + let Ok(Some(record)) = repo.get_record("app.bsky.feed.generator", &rkey).await else { 2304 + return proxy_request(req).await; 2305 + }; 2306 + 2307 + let Ok(author_profile) = get_profile_internal(&app_state, repo.did.as_str(), None).await else { 2308 + return proxy_request(req).await; 2309 + }; 2310 + 2311 + let val_json = serde_json::to_value(&record.value).unwrap_or(serde_json::json!({})); 2312 + 2313 + let view = serde_json::json!({ 2314 + "uri": params.feed, 2315 + "cid": record.cid.to_string(), 2316 + "did": val_json.get("did").and_then(|v| v.as_str()).unwrap_or(""), 2317 + "creator": author_profile, 2318 + "displayName": val_json.get("displayName").and_then(|v| v.as_str()).unwrap_or(""), 2319 + "description": val_json.get("description").and_then(|v| v.as_str()), 2320 + "avatar": val_json.get("avatar").and_then(|v| v.as_str()), 2321 + "likeCount": app_state.hydrant.backlinks.count(params.feed.clone()).source("app.bsky.feed.like").run().await.unwrap_or(0), 2322 + "indexedAt": chrono::Utc::now().to_rfc3339(), 2323 + }); 2324 + 2325 + Ok(Json(serde_json::json!({ 2326 + "view": view, 2327 + "isOnline": true, 2328 + "isValid": true, 2329 + })) 2330 + .into_response()) 2331 + } 2332 + 2333 + async fn get_feed_generators( 2334 + State(app_state): State<AppState>, 2335 + req: Request, 2336 + ) -> Result<Response, StatusCode> { 2337 + // let hydrant = &app_state.hydrant; 2338 + let query_str = req.uri().query().unwrap_or(""); 2339 + let feeds = extract_query_array(query_str, "feeds"); 2340 + if feeds.is_empty() { 2341 + return proxy_request(req).await; 2342 + } 2343 + 2344 + let mut views = Vec::new(); 2345 + for feed_uri in feeds { 2346 + let Ok(uri) = jacquard_common::types::string::AtUri::new(&feed_uri) else { 2347 + continue; 2348 + }; 2349 + let author_ident = uri.authority(); 2350 + let rkey = uri.rkey().unwrap().0.as_str().to_string(); 2351 + 2352 + if let Ok(repo) = app_state.hydrant.repos.resolve(author_ident).await { 2353 + if let Ok(Some(record)) = repo.get_record("app.bsky.feed.generator", &rkey).await { 2354 + if let Ok(author_profile) = 2355 + get_profile_internal(&app_state, repo.did.as_str(), None).await 2356 + { 2357 + let val_json = 2358 + serde_json::to_value(&record.value).unwrap_or(serde_json::json!({})); 2359 + views.push(serde_json::json!({ 2360 + "uri": feed_uri, 2361 + "cid": record.cid.to_string(), 2362 + "did": val_json.get("did").and_then(|v| v.as_str()).unwrap_or(""), 2363 + "creator": author_profile, 2364 + "displayName": val_json.get("displayName").and_then(|v| v.as_str()).unwrap_or(""), 2365 + "description": val_json.get("description").and_then(|v| v.as_str()), 2366 + "avatar": val_json.get("avatar").and_then(|v| v.as_str()), 2367 + "likeCount": app_state.hydrant.backlinks.count(feed_uri.clone()).source("app.bsky.feed.like").run().await.unwrap_or(0), 2368 + "indexedAt": chrono::Utc::now().to_rfc3339(), 2369 + })); 2370 + } 2371 + } 2372 + } 2373 + } 2374 + 2375 + Ok(Json(serde_json::json!({ "feeds": views })).into_response()) 2376 + } 2377 + 2378 + #[derive(Deserialize)] 2379 + struct PutPreferencesReq { 2380 + preferences: Vec<serde_json::Value>, 2381 + } 2382 + 2383 + async fn put_preferences( 2384 + State(app_state): State<AppState>, 2385 + req: Request, 2386 + ) -> Result<Response, StatusCode> { 2387 + let Some(did) = get_auth_did(&req) else { 2388 + return Err(StatusCode::UNAUTHORIZED); 2389 + }; 2390 + 2391 + let body_bytes = axum::body::to_bytes(req.into_body(), usize::MAX) 2392 + .await 2393 + .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; 2394 + let Ok(payload) = serde_json::from_slice::<PutPreferencesReq>(&body_bytes) else { 2395 + return Err(StatusCode::BAD_REQUEST); 2396 + }; 2397 + 2398 + let key = format!("prefs:{}", did); 2399 + let val = 2400 + serde_json::to_vec(&payload.preferences).map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; 2401 + app_state 2402 + .preferences 2403 + .insert(key.as_bytes(), val) 2404 + .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; 2405 + 2406 + Ok(Json(serde_json::json!({})).into_response()) 2407 + } 2408 + 2409 + async fn get_preferences( 2410 + State(app_state): State<AppState>, 2411 + req: Request, 2412 + ) -> Result<Response, StatusCode> { 2413 + let Some(did) = get_auth_did(&req) else { 2414 + return Err(StatusCode::UNAUTHORIZED); 2415 + }; 2416 + 2417 + let key = format!("prefs:{}", did); 2418 + let existing = app_state 2419 + .preferences 2420 + .get(key.as_bytes()) 2421 + .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; 2422 + 2423 + let preferences = if let Some(bytes) = existing { 2424 + serde_json::from_slice::<Vec<serde_json::Value>>(&bytes).unwrap_or_default() 2425 + } else { 2426 + Vec::new() 2427 + }; 2428 + 2429 + Ok(Json(serde_json::json!({ "preferences": preferences })).into_response()) 2430 + } 2431 + 2432 + #[derive(Deserialize)] 2433 + struct MuteActorReq { 2434 + actor: String, 2435 + } 2436 + 2437 + async fn mute_actor( 2438 + State(app_state): State<AppState>, 2439 + req: Request, 2440 + ) -> Result<Response, StatusCode> { 2441 + // let hydrant = &app_state.hydrant; 2442 + let Some(did) = get_auth_did(&req) else { 2443 + return Err(StatusCode::UNAUTHORIZED); 2444 + }; 2445 + 2446 + let body_bytes = axum::body::to_bytes(req.into_body(), usize::MAX) 2447 + .await 2448 + .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; 2449 + let Ok(payload) = serde_json::from_slice::<MuteActorReq>(&body_bytes) else { 2450 + return Err(StatusCode::BAD_REQUEST); 2451 + }; 2452 + 2453 + let Ok(ident) = AtIdentifier::new(&payload.actor) else { 2454 + return Err(StatusCode::BAD_REQUEST); 2455 + }; 2456 + let Ok(repo) = app_state.hydrant.repos.resolve(&ident).await else { 2457 + return Err(StatusCode::NOT_FOUND); 2458 + }; 2459 + 2460 + let key = format!("mute:{}:{}", did, repo.did.as_str()); 2461 + app_state 2462 + .mutes 2463 + .insert(key.as_bytes(), b"") 2464 + .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; 2465 + 2466 + Ok(Json(serde_json::json!({})).into_response()) 2467 + } 2468 + 2469 + async fn unmute_actor( 2470 + State(app_state): State<AppState>, 2471 + req: Request, 2472 + ) -> Result<Response, StatusCode> { 2473 + // let hydrant = &app_state.hydrant; 2474 + let Some(did) = get_auth_did(&req) else { 2475 + return Err(StatusCode::UNAUTHORIZED); 2476 + }; 2477 + 2478 + let body_bytes = axum::body::to_bytes(req.into_body(), usize::MAX) 2479 + .await 2480 + .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; 2481 + let Ok(payload) = serde_json::from_slice::<MuteActorReq>(&body_bytes) else { 2482 + return Err(StatusCode::BAD_REQUEST); 2483 + }; 2484 + 2485 + let Ok(ident) = AtIdentifier::new(&payload.actor) else { 2486 + return Err(StatusCode::BAD_REQUEST); 2487 + }; 2488 + let Ok(repo) = app_state.hydrant.repos.resolve(&ident).await else { 2489 + return Err(StatusCode::NOT_FOUND); 2490 + }; 2491 + 2492 + let key = format!("mute:{}:{}", did, repo.did.as_str()); 2493 + app_state 2494 + .mutes 2495 + .remove(key.as_bytes()) 2496 + .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; 2497 + 2498 + Ok(Json(serde_json::json!({})).into_response()) 2499 + } 2500 + 2501 + async fn get_mutes( 2502 + State(app_state): State<AppState>, 2503 + req: Request, 2504 + ) -> Result<Response, StatusCode> { 2505 + // let hydrant = &app_state.hydrant; 2506 + let Some(did) = get_auth_did(&req) else { 2507 + return Err(StatusCode::UNAUTHORIZED); 2508 + }; 2509 + 2510 + let query_str = req.uri().query().unwrap_or(""); 2511 + let Ok(params) = serde_urlencoded::from_str::<GetBookmarksParams>(query_str) else { 2512 + return proxy_request(req).await; 2513 + }; 2514 + 2515 + let limit = params.limit.unwrap_or(50).min(100); 2516 + let prefix = format!("mute:{}:", did); 2517 + 2518 + let iter: Box<dyn Iterator<Item = _>> = if let Some(cursor) = params.cursor { 2519 + Box::new(app_state.mutes.range::<&[u8], _>(( 2520 + std::ops::Bound::Included(cursor.as_bytes()), 2521 + std::ops::Bound::Unbounded, 2522 + ))) 2523 + } else { 2524 + Box::new(app_state.mutes.prefix(prefix.as_bytes())) 2525 + }; 2526 + 2527 + let mut fetched_keys = Vec::new(); 2528 + for item in iter { 2529 + let Ok((key, _)) = item.into_inner() else { 2530 + continue; 2531 + }; 2532 + if !key.starts_with(prefix.as_bytes()) { 2533 + break; 2534 + } 2535 + fetched_keys.push(key.to_vec()); 2536 + if fetched_keys.len() >= limit { 2537 + break; 2538 + } 2539 + } 2540 + 2541 + let mut mutes = Vec::new(); 2542 + let mut next_cursor = None; 2543 + for key in fetched_keys { 2544 + let key_str = String::from_utf8_lossy(&key); 2545 + let muted_did = &key_str[prefix.len()..]; 2546 + if let Ok(profile) = get_profile_internal(&app_state, muted_did, None).await { 2547 + mutes.push(profile); 2548 + } 2549 + next_cursor = Some(key_str.to_string()); 2550 + } 2551 + 2552 + Ok(Json(serde_json::json!({ 2553 + "mutes": mutes, 2554 + "cursor": next_cursor 2555 + })) 2556 + .into_response()) 2557 + } 2558 + 2559 + async fn notification_indexer(app_state: AppState) { 2560 + let mut stream = app_state.hydrant.subscribe(None); 2561 + while let Some(evt) = stream.next().await { 2562 + let Some(rec) = evt.record else { continue }; 2563 + if rec.action != "create" { 2564 + continue; 2565 + } 2566 + 2567 + let author_did = rec.did.as_str(); 2568 + let collection = rec.collection.as_str(); 2569 + let rkey = rec.rkey.as_str(); 2570 + let uri = format!("at://{}/{}/{}", author_did, collection, rkey); 2571 + 2572 + let Some(val) = rec.record else { continue }; 2573 + 2574 + let mut targets = Vec::new(); // (target_did, reason, reason_subject) 2575 + 2576 + match collection { 2577 + "app.bsky.feed.like" => { 2578 + if let Some(subj_uri) = val 2579 + .get("subject") 2580 + .and_then(|s| s.get("uri")) 2581 + .and_then(|u| u.as_str()) 2582 + { 2583 + if let Ok(u) = jacquard_common::types::string::AtUri::new(subj_uri) { 2584 + targets.push(( 2585 + u.authority().as_str().to_string(), 2586 + "like", 2587 + Some(subj_uri.to_string()), 2588 + )); 2589 + } 2590 + } 2591 + } 2592 + "app.bsky.feed.repost" => { 2593 + if let Some(subj_uri) = val 2594 + .get("subject") 2595 + .and_then(|s| s.get("uri")) 2596 + .and_then(|u| u.as_str()) 2597 + { 2598 + if let Ok(u) = jacquard_common::types::string::AtUri::new(subj_uri) { 2599 + targets.push(( 2600 + u.authority().as_str().to_string(), 2601 + "repost", 2602 + Some(subj_uri.to_string()), 2603 + )); 2604 + } 2605 + } 2606 + } 2607 + "app.bsky.graph.follow" => { 2608 + if let Some(subj_did) = val.get("subject").and_then(|s| s.as_str()) { 2609 + targets.push((subj_did.to_string(), "follow", None)); 2610 + } 2611 + } 2612 + "app.bsky.feed.post" => { 2613 + if let Some(parent_uri) = val 2614 + .get("reply") 2615 + .and_then(|r| r.get("parent")) 2616 + .and_then(|p| p.get("uri")) 2617 + .and_then(|u| u.as_str()) 2618 + { 2619 + if let Ok(u) = jacquard_common::types::string::AtUri::new(parent_uri) { 2620 + targets.push(( 2621 + u.authority().as_str().to_string(), 2622 + "reply", 2623 + Some(parent_uri.to_string()), 2624 + )); 2625 + } 2626 + } 2627 + 2628 + let is_quote = val.get("embed").map_or(false, |embed| { 2629 + let t = embed.get("$type").and_then(|t| t.as_str()).unwrap_or(""); 2630 + if t == "app.bsky.embed.record" { 2631 + embed 2632 + .get("record") 2633 + .and_then(|r| r.get("uri")) 2634 + .and_then(|u| u.as_str()) 2635 + .is_some() 2636 + } else if t == "app.bsky.embed.recordWithMedia" { 2637 + embed 2638 + .get("record") 2639 + .and_then(|r| r.get("record")) 2640 + .and_then(|r| r.get("uri")) 2641 + .and_then(|u| u.as_str()) 2642 + .is_some() 2643 + } else { 2644 + false 2645 + } 2646 + }); 2647 + 2648 + if is_quote { 2649 + if let Some(embed) = val.get("embed") { 2650 + let t = embed.get("$type").and_then(|t| t.as_str()).unwrap_or(""); 2651 + let quote_uri = if t == "app.bsky.embed.record" { 2652 + embed 2653 + .get("record") 2654 + .and_then(|r| r.get("uri")) 2655 + .and_then(|u| u.as_str()) 2656 + } else if t == "app.bsky.embed.recordWithMedia" { 2657 + embed 2658 + .get("record") 2659 + .and_then(|r| r.get("record")) 2660 + .and_then(|r| r.get("uri")) 2661 + .and_then(|u| u.as_str()) 2662 + } else { 2663 + None 2664 + }; 2665 + 2666 + if let Some(qu) = quote_uri { 2667 + if let Ok(u) = jacquard_common::types::string::AtUri::new(qu) { 2668 + targets.push(( 2669 + u.authority().as_str().to_string(), 2670 + "quote", 2671 + Some(qu.to_string()), 2672 + )); 2673 + } 2674 + } 2675 + } 2676 + } 2677 + 2678 + if let Some(facets) = val.get("facets").and_then(|f| f.as_array()) { 2679 + for facet in facets { 2680 + if let Some(features) = facet.get("features").and_then(|f| f.as_array()) { 2681 + for feature in features { 2682 + if feature.get("$type").and_then(|t| t.as_str()) 2683 + == Some("app.bsky.richtext.facet#mention") 2684 + { 2685 + if let Some(mention_did) = 2686 + feature.get("did").and_then(|d| d.as_str()) 2687 + { 2688 + targets.push((mention_did.to_string(), "mention", None)); 2689 + } 2690 + } 2691 + } 2692 + } 2693 + } 2694 + } 2695 + } 2696 + _ => {} 2697 + } 2698 + 2699 + let indexed_at = chrono::Utc::now().to_rfc3339(); 2700 + for (target_did, reason, reason_subject) in targets { 2701 + if target_did == author_did { 2702 + continue; 2703 + } 2704 + 2705 + let key = format!("notif:{}:{:016x}", target_did, evt.id); 2706 + let notif = serde_json::json!({ 2707 + "uri": uri, 2708 + "cid": rec.cid.map(|c| c.to_string()).unwrap_or_default(), 2709 + "author_did": author_did, 2710 + "reason": reason, 2711 + "reasonSubject": reason_subject, 2712 + "record": val, 2713 + "indexedAt": indexed_at, 2714 + "id": evt.id 2715 + }); 2716 + 2717 + let _ = app_state 2718 + .notifications 2719 + .insert(key.as_bytes(), serde_json::to_vec(&notif).unwrap()); 2720 + } 2721 + } 2722 + } 2723 + 2724 + #[derive(Deserialize)] 2725 + struct ListNotificationsParams { 2726 + limit: Option<usize>, 2727 + cursor: Option<String>, 2728 + } 2729 + 2730 + async fn list_notifications( 2731 + State(app_state): State<AppState>, 2732 + req: Request, 2733 + ) -> Result<Response, StatusCode> { 2734 + // let hydrant = &app_state.hydrant; 2735 + let Some(did) = get_auth_did(&req) else { 2736 + return Err(StatusCode::UNAUTHORIZED); 2737 + }; 2738 + 2739 + let query_str = req.uri().query().unwrap_or(""); 2740 + let params = serde_urlencoded::from_str::<ListNotificationsParams>(query_str).unwrap_or( 2741 + ListNotificationsParams { 2742 + limit: None, 2743 + cursor: None, 2744 + }, 2745 + ); 2746 + let limit = params.limit.unwrap_or(50).min(100); 2747 + 2748 + let prefix = format!("notif:{}:", did); 2749 + 2750 + // Collect all notification keys into a vector so we can iterate in reverse 2751 + let mut all_keys = Vec::new(); 2752 + for item in app_state.notifications.prefix(prefix.as_bytes()) { 2753 + if let Ok((k, v)) = item.into_inner() { 2754 + all_keys.push((k, v)); 2755 + } 2756 + } 2757 + 2758 + // Sort keys just in case and reverse 2759 + all_keys.sort_by(|a, b| b.0.cmp(&a.0)); 2760 + 2761 + // Pagination 2762 + if let Some(cursor) = params.cursor { 2763 + all_keys.retain(|(k, _)| String::from_utf8_lossy(k).as_ref() < cursor.as_str()); 2764 + } 2765 + 2766 + let seen_key = format!("seen:{}", did); 2767 + let last_seen = app_state 2768 + .seen 2769 + .get(seen_key.as_bytes()) 2770 + .ok() 2771 + .flatten() 2772 + .and_then(|b| String::from_utf8(b.to_vec()).ok()) 2773 + .unwrap_or_else(|| "1970-01-01T00:00:00Z".to_string()); 2774 + 2775 + let mut notifications = Vec::new(); 2776 + let mut next_cursor = None; 2777 + 2778 + for (key, val_bytes) in all_keys.into_iter().take(limit) { 2779 + next_cursor = Some(String::from_utf8_lossy(&key).to_string()); 2780 + 2781 + let Ok(val) = serde_json::from_slice::<serde_json::Value>(&val_bytes) else { 2782 + continue; 2783 + }; 2784 + 2785 + let author_did = val.get("author_did").and_then(|a| a.as_str()).unwrap_or(""); 2786 + let indexed_at = val.get("indexedAt").and_then(|a| a.as_str()).unwrap_or(""); 2787 + 2788 + let is_read = indexed_at <= last_seen.as_str(); 2789 + 2790 + let mut notif = val.clone(); 2791 + if let Ok(author_profile) = get_profile_internal(&app_state, author_did, None).await { 2792 + notif["author"] = author_profile; 2793 + } else { 2794 + continue; 2795 + } 2796 + 2797 + notif["isRead"] = serde_json::json!(is_read); 2798 + 2799 + notif.as_object_mut().unwrap().remove("author_did"); 2800 + notif.as_object_mut().unwrap().remove("id"); 2801 + 2802 + notifications.push(notif); 2803 + } 2804 + 2805 + Ok(Json(serde_json::json!({ 2806 + "notifications": notifications, 2807 + "cursor": if notifications.len() == limit { next_cursor } else { None } 2808 + })) 2809 + .into_response()) 2810 + } 2811 + 2812 + async fn get_unread_count( 2813 + State(app_state): State<AppState>, 2814 + req: Request, 2815 + ) -> Result<Response, StatusCode> { 2816 + let Some(did) = get_auth_did(&req) else { 2817 + return Err(StatusCode::UNAUTHORIZED); 2818 + }; 2819 + 2820 + let prefix = format!("notif:{}:", did); 2821 + let seen_key = format!("seen:{}", did); 2822 + let last_seen = app_state 2823 + .seen 2824 + .get(seen_key.as_bytes()) 2825 + .ok() 2826 + .flatten() 2827 + .and_then(|b| String::from_utf8(b.to_vec()).ok()) 2828 + .unwrap_or_else(|| "1970-01-01T00:00:00Z".to_string()); 2829 + 2830 + let mut count = 0; 2831 + for item in app_state.notifications.prefix(prefix.as_bytes()) { 2832 + let Ok((_key, val_bytes)) = item.into_inner() else { 2833 + continue; 2834 + }; 2835 + if let Ok(val) = serde_json::from_slice::<serde_json::Value>(&val_bytes) { 2836 + if let Some(indexed_at) = val.get("indexedAt").and_then(|a| a.as_str()) { 2837 + if indexed_at > last_seen.as_str() { 2838 + count += 1; 2839 + } 2840 + } 2841 + } 2842 + } 2843 + 2844 + Ok(Json(serde_json::json!({ 2845 + "count": count 2846 + })) 2847 + .into_response()) 2848 + } 2849 + 2850 + #[derive(Deserialize)] 2851 + struct UpdateSeenReq { 2852 + seenAt: String, 2853 + } 2854 + 2855 + async fn update_seen( 2856 + State(app_state): State<AppState>, 2857 + req: Request, 2858 + ) -> Result<Response, StatusCode> { 2859 + let Some(did) = get_auth_did(&req) else { 2860 + return Err(StatusCode::UNAUTHORIZED); 2861 + }; 2862 + 2863 + let body_bytes = axum::body::to_bytes(req.into_body(), usize::MAX) 2864 + .await 2865 + .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; 2866 + let Ok(payload) = serde_json::from_slice::<UpdateSeenReq>(&body_bytes) else { 2867 + return Err(StatusCode::BAD_REQUEST); 2868 + }; 2869 + 2870 + let seen_key = format!("seen:{}", did); 2871 + app_state 2872 + .seen 2873 + .insert(seen_key.as_bytes(), payload.seenAt.as_bytes()) 2874 + .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; 2875 + 2876 + Ok(Json(serde_json::json!({})).into_response()) 2877 + } 2878 + 2879 + #[derive(Deserialize)] 2880 + struct GetFeedParams { 2881 + feed: String, 2882 + limit: Option<usize>, 2883 + cursor: Option<String>, 2884 + } 2885 + 2886 + async fn get_feed(State(app_state): State<AppState>, req: Request) -> Result<Response, StatusCode> { 2887 + // let hydrant = &app_state.hydrant; 2888 + let query_str = req.uri().query().unwrap_or(""); 2889 + let Ok(params) = serde_urlencoded::from_str::<GetFeedParams>(query_str) else { 2890 + return proxy_request(req).await; 2891 + }; 2892 + 2893 + let Ok(uri) = jacquard_common::types::string::AtUri::new(&params.feed) else { 2894 + return proxy_request(req).await; 2895 + }; 2896 + let author_ident = uri.authority(); 2897 + let rkey = uri 2898 + .rkey() 2899 + .ok_or(StatusCode::BAD_REQUEST)? 2900 + .0 2901 + .as_str() 2902 + .to_string(); 2903 + 2904 + let Ok(repo) = app_state.hydrant.repos.resolve(author_ident).await else { 2905 + return proxy_request(req).await; 2906 + }; 2907 + 2908 + let Ok(Some(record)) = repo.get_record("app.bsky.feed.generator", &rkey).await else { 2909 + return proxy_request(req).await; 2910 + }; 2911 + 2912 + let val_json = serde_json::to_value(&record.value).unwrap_or(serde_json::json!({})); 2913 + let Some(service_did) = val_json.get("did").and_then(|d| d.as_str()) else { 2914 + return proxy_request(req).await; 2915 + }; 2916 + 2917 + let Ok(service_did_parsed) = jacquard_common::types::string::Did::new(service_did) else { 2918 + return proxy_request(req).await; 2919 + }; 2920 + 2921 + let Ok((doc_data, _)) = app_state 2922 + .hydrant 2923 + .resolver() 2924 + .resolve_raw_doc(&service_did_parsed) 2925 + .await 2926 + else { 2927 + return proxy_request(req).await; 2928 + }; 2929 + 2930 + let doc_json = serde_json::to_value(&doc_data).unwrap_or(serde_json::json!({})); 2931 + let mut endpoint = None; 2932 + 2933 + if let Some(services) = doc_json.get("service").and_then(|s| s.as_array()) { 2934 + for srv in services { 2935 + if let Some(typ) = srv.get("type") { 2936 + let is_fg = if let Some(t_str) = typ.as_str() { 2937 + t_str == "BskyFeedGenerator" 2938 + } else if let Some(t_arr) = typ.as_array() { 2939 + t_arr 2940 + .iter() 2941 + .any(|v| v.as_str() == Some("BskyFeedGenerator")) 2942 + } else { 2943 + false 2944 + }; 2945 + 2946 + if is_fg { 2947 + if let Some(ep) = srv.get("serviceEndpoint").and_then(|e| e.as_str()) { 2948 + endpoint = Some(ep.to_string()); 2949 + break; 2950 + } 2951 + } 2952 + } 2953 + 2954 + // fallback check by ID 2955 + if srv.get("id").and_then(|i| i.as_str()) == Some("#bsky_fg") { 2956 + if let Some(ep) = srv.get("serviceEndpoint").and_then(|e| e.as_str()) { 2957 + endpoint = Some(ep.to_string()); 2958 + break; 2959 + } 2960 + } 2961 + } 2962 + } 2963 + 2964 + let Some(ep) = endpoint else { 2965 + return proxy_request(req).await; 2966 + }; 2967 + 2968 + let mut skeleton_url = format!( 2969 + "{}/xrpc/app.bsky.feed.getFeedSkeleton?feed={}", 2970 + ep, params.feed 2971 + ); 2972 + if let Some(limit) = params.limit { 2973 + skeleton_url.push_str(&format!("&limit={}", limit)); 2974 + } 2975 + if let Some(cursor) = params.cursor { 2976 + skeleton_url.push_str(&format!("&cursor={}", cursor)); 2977 + } 2978 + 2979 + let client = reqwest::Client::new(); 2980 + let mut req_builder = client.get(&skeleton_url); 2981 + if let Some(auth) = req.headers().get("authorization") { 2982 + req_builder = req_builder.header("authorization", auth); 2983 + } 2984 + 2985 + let Ok(res) = req_builder.send().await else { 2986 + return proxy_request(req).await; 2987 + }; 2988 + 2989 + let Ok(skeleton) = res.json::<serde_json::Value>().await else { 2990 + return proxy_request(req).await; 2991 + }; 2992 + 2993 + let mut hydrated_feed = Vec::new(); 2994 + if let Some(feed_items) = skeleton.get("feed").and_then(|f| f.as_array()) { 2995 + for item in feed_items { 2996 + if let Some(post_uri) = item.get("post").and_then(|p| p.as_str()) { 2997 + if let Ok(post_view) = get_post_view(&app_state, post_uri, None).await { 2998 + let mut feed_item = serde_json::json!({ 2999 + "post": post_view 3000 + }); 3001 + if let Some(reason) = item.get("reason") { 3002 + feed_item 3003 + .as_object_mut() 3004 + .unwrap() 3005 + .insert("reason".to_string(), reason.clone()); 3006 + } 3007 + hydrated_feed.push(feed_item); 3008 + } 3009 + } 3010 + } 3011 + } 3012 + 3013 + Ok(Json(serde_json::json!({ 3014 + "feed": hydrated_feed, 3015 + "cursor": skeleton.get("cursor") 3016 + })) 3017 + .into_response()) 3018 + } 3019 + 3020 + async fn get_well_known_did( 3021 + req: Request, 3022 + ) -> Result<Response, StatusCode> { 3023 + let host = req.headers().get("host").and_then(|h| h.to_str().ok()).unwrap_or("localhost:8000"); 3024 + 3025 + let did = if host.contains(':') { 3026 + format!("did:web:{}", host.replace(':', "%3A")) 3027 + } else { 3028 + format!("did:web:{}", host) 3029 + }; 3030 + 3031 + let scheme = if host.starts_with("localhost") || host.starts_with("127.0.0.1") { "http" } else { "https" }; 3032 + let service_endpoint = format!("{}://{}", scheme, host); 3033 + 3034 + Ok(Json(serde_json::json!({ 3035 + "@context": [ 3036 + "https://www.w3.org/ns/did/v1" 3037 + ], 3038 + "id": did, 3039 + "service": [ 3040 + { 3041 + "id": "#bsky_notif", 3042 + "type": "BskyNotificationService", 3043 + "serviceEndpoint": service_endpoint 3044 + }, 3045 + { 3046 + "id": "#bsky_appview", 3047 + "type": "BskyAppView", 3048 + "serviceEndpoint": service_endpoint 3049 + } 3050 + ] 3051 + })).into_response()) 3 3052 }