diff --git a/Cargo.lock b/Cargo.lock index 99bf3b8..1e78051 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 4 [[package]] name = "aho-corasick" -version = "1.1.3" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +checksum = "c982642fa9e8606056828ee9a8505737230110bb1099153c79efe865c59d12ba" dependencies = [ "memchr", ] @@ -19,9 +19,9 @@ checksum = "e9d4ee0d472d1cd2e28c97dfa124b3d8d992e10eb0a035f33f5d12e3a177ba3b" [[package]] name = "android_system_properties" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +checksum = "ae221649c9976a6f6c56ae1facf410f3ddb33cc661c4b7b61020a912d4237fbc" dependencies = [ "libc", ] @@ -58,22 +58,22 @@ dependencies = [ [[package]] name = "anstyle-query" -version = "1.1.4" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2" +checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" dependencies = [ - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] name = "anstyle-wincon" -version = "3.0.10" +version = "3.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a" +checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" dependencies = [ "anstyle", "once_cell_polyfill", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -82,6 +82,15 @@ version = "1.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "330a5ed07fa54e4702c9d6c4174f74427fc0ef6e214bbd677ae50a5099946470" +[[package]] +name = "arc-swap" +version = "1.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c049c0be4daef0b145cb3555416b3b8ef5b7888a38aea1a3a155801fe7b0810b" +dependencies = [ + "rustversion", +] + [[package]] name = "arraydeque" version = "0.5.1" @@ -103,6 +112,17 @@ dependencies = [ "wait-timeout", ] +[[package]] +name = "async-trait" +version = "0.1.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82f6aeea286b8eb4dd3431a1be1b59d290ace00f5bfd8e2a159bc2a05e2c1667" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.3", +] + [[package]] name = "atomic" version = "0.6.1" @@ -131,9 +151,9 @@ dependencies = [ [[package]] name = "autocfg" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" [[package]] name = "backon" @@ -146,6 +166,12 @@ dependencies = [ "tokio", ] +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + [[package]] name = "base64" version = "0.22.1" @@ -154,15 +180,21 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "base64" -version = "0.23.0" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac07cdecf99051d9a5238b80f35af32cdeba5b336e55d957b318b50137e18da5" + +[[package]] +name = "base64ct" +version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b25655df2c3cdd83c5e5b293b88acd880332b2ddadd7c30ac43144fdc0033da9" +checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" [[package]] name = "bitflags" -version = "2.9.3" +version = "2.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34efbcccd345379ca2868b2b2c9d3782e9cc58ba87bc7d79d5b53d9c9ae6f25d" +checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da" [[package]] name = "block-buffer" @@ -175,26 +207,26 @@ dependencies = [ [[package]] name = "bstr" -version = "1.12.0" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "234113d19d0d7d613b40e86fb654acf958910802bcceab913a4f9e7cda03b1a4" +checksum = "6bb31b46c14244e20ee9984b11bf5c992b91fb6939fea616e3512c8baecdbe5f" dependencies = [ "memchr", "regex-automata", - "serde", + "serde_core", ] [[package]] name = "bumpalo" -version = "3.19.0" +version = "3.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" [[package]] name = "bytemuck" -version = "1.23.2" +version = "1.25.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3995eaeebcdf32f91f980d360f78732ddc061097ab4e39991ae7a6ace9194677" +checksum = "95832e849adfb21180ccb6826a99da14e5d266ae5c2e668e1602cf234f153797" [[package]] name = "bytes" @@ -210,9 +242,12 @@ checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04" [[package]] name = "camino" -version = "1.2.1" +version = "1.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "276a59bf2b2c967788139340c9f0c5b12d7fd6630315c15c217e559de85d2609" +checksum = "bb1307f12aa967b5a58416e87b3653360e0fd614a016b6e970db08fecbb1b80d" +dependencies = [ + "serde_core", +] [[package]] name = "cap-primitives" @@ -246,25 +281,44 @@ dependencies = [ ] [[package]] -name = "cc" -version = "1.2.34" +name = "cargo-platform" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42bc4aea80032b7bf409b0bc7ccad88853858911b7713a8062fdc0623867bedc" +checksum = "87a0c0e6148f11f01f32650a2ea02d532b2ad4e81d8bd41e6e565b5adc5e6082" dependencies = [ - "shlex", + "serde", + "serde_core", ] [[package]] -name = "cfg-if" -version = "1.0.3" +name = "cargo_metadata" +version = "0.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" +checksum = "ef987d17b0a113becdd19d3d0022d04d7ef41f9efe4f3fb63ac44ba61df3ade9" +dependencies = [ + "camino", + "cargo-platform", + "semver", + "serde", + "serde_json", + "thiserror 2.0.20", +] [[package]] -name = "cfg_aliases" -version = "0.2.1" +name = "cc" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d262e149917187838d5b42777c8253bcb64500067342904e7d429499a6f277e" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "chrono" @@ -277,14 +331,14 @@ dependencies = [ "num-traits", "serde", "wasm-bindgen", - "windows-link 0.2.0", + "windows-link", ] [[package]] name = "clap" -version = "4.6.5" +version = "4.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "301b56658598e48f3648647ac6fc887be7e7108eddfa4e9b63fcf3ec58c0cadf" +checksum = "473c7e07f409a8d772161724aa8db6a765a2532a70f9667eeb7b49d3d02fbdca" dependencies = [ "clap_builder", "clap_derive", @@ -299,14 +353,14 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.119", ] [[package]] name = "clap_builder" -version = "4.6.5" +version = "4.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94a65403d1a1bd28f7dc68eb8506e8874808ee5eecb59298de588e2e1407a078" +checksum = "7b48fea5a88e9ae728a2dcbedbfc0e730f7d60da42e1cb049a83c9fb8b789889" dependencies = [ "anstream", "anstyle", @@ -334,9 +388,9 @@ checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" [[package]] name = "colorchoice" -version = "1.0.4" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" +checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" [[package]] name = "colored" @@ -357,14 +411,20 @@ checksum = "4fe5f465a4f6fee88fad41b85d990f84c835335e85b5d9e6e63e0d06d28cba7c" dependencies = [ "encode_unicode", "libc", - "windows-sys 0.61.1", + "windows-sys 0.61.2", ] +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + [[package]] name = "convert_case" -version = "0.7.1" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb402b8d4c85569410425650ce3eddc7d698ed96d39a73f941b08fb63082f1e7" +checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9" dependencies = [ "unicode-segmentation", ] @@ -380,9 +440,9 @@ dependencies = [ [[package]] name = "core-foundation" -version = "0.9.4" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" dependencies = [ "core-foundation-sys", "libc", @@ -405,9 +465,9 @@ dependencies = [ [[package]] name = "crokey" -version = "1.3.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51360853ebbeb3df20c76c82aecf43d387a62860f1a59ba65ab51f00eea85aad" +checksum = "074bc31bff1084f74e5a145ad5f6ac74469fa312159faa5144f0b1c4506b8d80" dependencies = [ "crokey-proc_macros", "crossterm", @@ -418,15 +478,15 @@ dependencies = [ [[package]] name = "crokey-proc_macros" -version = "1.3.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bf1a727caeb5ee5e0a0826a97f205a9cf84ee964b0b48239fef5214a00ae439" +checksum = "b88c4ff2d5717616c3bb4a31d06b0b241ed811c7c0c41d077d77631bee7caad0" dependencies = [ "crossterm", "proc-macro2", "quote", "strict", - "syn 2.0.106", + "syn 2.0.119", ] [[package]] @@ -444,18 +504,18 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.15" +version = "0.5.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2" +checksum = "d85363c37faeca707aef026efa9f3b34d077bce547e48f770770625c6013679e" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-deque" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +checksum = "5181e0de7b61eb03a81e347d6dd8797bae9da5146707b51077e2d71a54ec0ceb" dependencies = [ "crossbeam-epoch", "crossbeam-utils", @@ -463,27 +523,27 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.9.18" +version = "0.9.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +checksum = "2d6914041f254d6e9176c01941b21115dcfb7089e55135a35411081bd106ef3f" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-queue" -version = "0.3.12" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115" +checksum = "803d13fb3b09d88be9f4dbc29062c66b19bf7170867ceb746d2a8689bf6c7a26" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-utils" -version = "0.8.21" +version = "0.8.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" +checksum = "61803da095bee82a81bb1a452ecc25d3b2f1416d1897eb86430c6159ef717c17" [[package]] name = "crossterm" @@ -512,6 +572,18 @@ dependencies = [ "winapi", ] +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "generic-array", + "rand_core", + "subtle", + "zeroize", +] + [[package]] name = "crypto-common" version = "0.1.6" @@ -523,33 +595,69 @@ dependencies = [ ] [[package]] -name = "deranged" -version = "0.4.0" +name = "curve25519-dalek" +version = "4.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" dependencies = [ - "powerfmt", + "cfg-if", + "cpufeatures", + "curve25519-dalek-derive", + "digest", + "fiat-crypto", + "rustc_version", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", ] +[[package]] +name = "der" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" +dependencies = [ + "const-oid", + "pem-rfc7468", + "zeroize", +] + +[[package]] +name = "deranged" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" + [[package]] name = "derive_more" -version = "2.0.1" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678" +checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134" dependencies = [ "derive_more-impl", ] [[package]] name = "derive_more-impl" -version = "2.0.1" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3" +checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" dependencies = [ "convert_case", "proc-macro2", "quote", - "syn 2.0.106", + "rustc_version", + "syn 2.0.119", ] [[package]] @@ -574,7 +682,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", + "const-oid", "crypto-common", + "subtle", ] [[package]] @@ -616,25 +726,25 @@ dependencies = [ "libc", "option-ext", "redox_users 0.5.2", - "windows-sys 0.61.1", + "windows-sys 0.61.2", ] [[package]] name = "displaydoc" -version = "0.2.5" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +checksum = "c6232dd377dcc64799954cbd3a9bb882e9cdc1308ccd87b1c098f1fb2eaf82a8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 3.0.3", ] [[package]] name = "document-features" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95249b50c6c185bee49034bcb378a49dc2b5dff0be90ff6616d31d64febab05d" +checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61" dependencies = [ "litrs", ] @@ -652,20 +762,70 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" [[package]] -name = "encode_unicode" -version = "1.0.0" +name = "ecdsa" +version = "0.16.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +dependencies = [ + "der", + "digest", + "elliptic-curve", + "rfc6979", + "signature", + "spki", +] [[package]] -name = "encoding_rs" -version = "0.8.35" +name = "ed25519" +version = "2.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" dependencies = [ - "cfg-if", + "pkcs8", + "signature", +] + +[[package]] +name = "ed25519-dalek" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70e796c081cee67dc755e1a36a0a172b897fab85fc3f6bc48307991f64e4eca9" +dependencies = [ + "curve25519-dalek", + "ed25519", + "serde", + "sha2", + "subtle", + "zeroize", +] + +[[package]] +name = "elliptic-curve" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct", + "crypto-bigint", + "digest", + "ff", + "generic-array", + "group", + "hkdf", + "pem-rfc7468", + "pkcs8", + "rand_core", + "sec1", + "subtle", + "zeroize", ] +[[package]] +name = "encode_unicode" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" + [[package]] name = "equivalent" version = "1.0.2" @@ -674,19 +834,35 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "errno" -version = "0.3.13" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] name = "fastrand" -version = "2.3.0" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da7c62ceae207dd37ea5b845da6a0696c799f85e97da1ab5b7910be3c1c80223" + +[[package]] +name = "ff" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" +dependencies = [ + "rand_core", + "subtle", +] + +[[package]] +name = "fiat-crypto" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" [[package]] name = "figment" @@ -715,6 +891,12 @@ dependencies = [ "serde", ] +[[package]] +name = "find-msvc-tools" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b73573e6edcd2af0cdf47bd6cb58f0b3839491263c314eaad1ccf24430e1de" + [[package]] name = "float-cmp" version = "0.10.0" @@ -736,7 +918,7 @@ dependencies = [ "intl_pluralrules", "rustc-hash", "self_cell", - "smallvec 1.15.1", + "smallvec 1.15.2", "unic-langid", ] @@ -756,7 +938,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "54f0d287c53ffd184d04d8677f590f4ac5379785529e5e08b1c8083acdd5c198" dependencies = [ "memchr", - "thiserror 2.0.19", + "thiserror 2.0.20", ] [[package]] @@ -803,9 +985,12 @@ dependencies = [ [[package]] name = "fragile" -version = "2.0.1" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28dd6caf6059519a65843af8fe2a3ae298b14b80179855aeb4adc2c1934ee619" +checksum = "8878864ba14bb86e818a412bfd6f18f9eabd4ec0f008a28e8f7eb61db532fcf9" +dependencies = [ + "futures-core", +] [[package]] name = "fs-set-times" @@ -830,9 +1015,9 @@ dependencies = [ [[package]] name = "futures" -version = "0.3.33" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a88cf1f829d945f548cf8fec32c61b1f202b6d93b45848602fc02af4b12ad218" +checksum = "9a31d2a3fbaaeb2af2368bbdd904aa8e812d3c04a1ee10d3171f52d556e5d0a3" dependencies = [ "futures-channel", "futures-core", @@ -845,9 +1030,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.33" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "262590f4fe6afeb0bc83be1daa64e52657fe185690a958af7f3ad0e92085c5ae" +checksum = "b1f9e3d69d39e4862ffed03ed071a76f9a13ba1d9109d355b0f0aa6b15e393c4" dependencies = [ "futures-core", "futures-sink", @@ -855,15 +1040,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.33" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cd50c473c80f6d7c3670a752354b8e569b1a7cbfdc0419ec88e5edad85e0dc7" +checksum = "92d699e522242e69e3003b94ecc1f960f3a5e015aa7c5d7486e65ad01dd94f5e" [[package]] name = "futures-executor" -version = "0.3.33" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6754879cc9f2c66f88c6e5c35344bb0bdb0708b0352b1201815667c7eabc7458" +checksum = "031b47cf1a3c6cc8bc2fc76cd437f521619387907d469316e7c0bc278f1f5432" dependencies = [ "futures-core", "futures-task", @@ -872,44 +1057,44 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.33" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4577ecaa3c4f96589d473f679a71b596316f6641bc350038b962a5daf0085d7a" +checksum = "53c0fa8157de1303bfffdaa1cc2a673bfffb60102f76b0ef4441659124373fed" [[package]] name = "futures-macro" -version = "0.3.33" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d6d3cde68c518367be28956066ddfef33813991b77a55005a69dae04bf3b10b" +checksum = "9fb9654ba8355388abeb8dcb4fc62f511300867002afc858860463bdd9fe0c44" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 3.0.3", ] [[package]] name = "futures-sink" -version = "0.3.33" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e34418ac499d6305c2fb5ad0ed2f6ac998c5f8ca209b4510f7f94242c647e307" +checksum = "1944426bf7d03f1d14f708785e4b33efd750b36d48a157b836b3efc15ede8e1d" [[package]] name = "futures-task" -version = "0.3.33" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b231ed28831efb4a61a08580c4bc233ec56bc009f4cd8f52da2c3cb97df0c109" +checksum = "cd417de3d1d015fc3bfd2b1ea46dfc7bab72ef86f1cc7cc9c78e728b34a6d1fd" [[package]] name = "futures-timer" -version = "3.0.3" +version = "3.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24" +checksum = "af43fadb8a98512d547e37b4e92e0ced13e205c061b87b4623eff01d918d6968" [[package]] name = "futures-util" -version = "0.3.33" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a77a90a256fce34da66415271e30f94ee91c57b04b8a2c042d9cf3220179deaa" +checksum = "0d50a92467f8ba5dd6e3ee5d4bd04d73ab2e4e1c44474a0674821dfce14b79bc" dependencies = [ "futures-channel", "futures-core", @@ -924,19 +1109,20 @@ dependencies = [ [[package]] name = "generic-array" -version = "0.14.7" +version = "0.14.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +checksum = "4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2" dependencies = [ "typenum", "version_check", + "zeroize", ] [[package]] name = "getrandom" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" dependencies = [ "cfg-if", "js-sys", @@ -947,23 +1133,20 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.3.3" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" +checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" dependencies = [ "cfg-if", - "js-sys", "libc", "r-efi", - "wasi 0.14.3+wasi-0.2.4", - "wasm-bindgen", ] [[package]] name = "glob" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" +checksum = "e4eba85ea1d0a966a983acd07deee566e67395d2d96b6fb39e62b5a833f1eb0b" [[package]] name = "gloo-timers" @@ -978,22 +1161,14 @@ dependencies = [ ] [[package]] -name = "h2" -version = "0.4.15" +name = "group" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cb093c84e8bd9b188d4c4a8cb6579fc016968d14c99882163cd3ff402a4f155" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" dependencies = [ - "atomic-waker", - "bytes 1.12.1", - "fnv", - "futures-core", - "futures-sink", - "http 1.3.1", - "indexmap", - "slab", - "tokio", - "tokio-util 0.7.16", - "tracing", + "ff", + "rand_core", + "subtle", ] [[package]] @@ -1005,12 +1180,6 @@ dependencies = [ "foldhash 0.1.5", ] -[[package]] -name = "hashbrown" -version = "0.16.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" - [[package]] name = "hashbrown" version = "0.17.1" @@ -1050,6 +1219,24 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" +[[package]] +name = "hkdf" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" +dependencies = [ + "hmac", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + [[package]] name = "html5ever" version = "0.35.0" @@ -1074,12 +1261,11 @@ dependencies = [ [[package]] name = "http" -version = "1.3.1" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" +checksum = "918d3568bebf352712bc2ef3d46a8bcf1a75b373be6539de198e9105cbbf9ce0" dependencies = [ "bytes 1.12.1", - "fnv", "itoa", ] @@ -1096,12 +1282,12 @@ dependencies = [ [[package]] name = "http-body" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +checksum = "ca2a8f2913ee65f60facd6a5905613afaa448497a0230cc41ce022d93290bc2c" dependencies = [ "bytes 1.12.1", - "http 1.3.1", + "http 1.5.0", ] [[package]] @@ -1112,11 +1298,21 @@ checksum = "e9f41fd6a08e4d4ec69df65976da761afd5ad5e58a9d4acb46bd1c953a9e3ff2" dependencies = [ "bytes 1.12.1", "futures-core", - "http 1.3.1", - "http-body 1.0.1", + "http 1.5.0", + "http-body 1.1.0", "pin-project-lite", ] +[[package]] +name = "http-serde" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f056c8559e3757392c8d091e796416e4649d8e49e88b8d76df6c002f05027fd" +dependencies = [ + "http 1.5.0", + "serde", +] + [[package]] name = "httparse" version = "1.10.1" @@ -1162,29 +1358,29 @@ dependencies = [ "bytes 1.12.1", "futures-channel", "futures-core", - "h2", - "http 1.3.1", - "http-body 1.0.1", + "http 1.5.0", + "http-body 1.1.0", "httparse", "httpdate", "itoa", "pin-project-lite", - "smallvec 1.15.1", + "smallvec 1.15.2", "tokio", "want", ] [[package]] name = "hyper-rustls" -version = "0.27.7" +version = "0.27.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" +checksum = "33ca68d021ef39cf6463ab54c1d0f5daf03377b70561305bb89a8f83aab66e0f" dependencies = [ - "http 1.3.1", + "http 1.5.0", "hyper 1.11.0", "hyper-util", + "log", "rustls", - "rustls-pki-types", + "rustls-native-certs", "tokio", "tokio-rustls", "tower-service", @@ -1192,18 +1388,15 @@ dependencies = [ ] [[package]] -name = "hyper-tls" -version = "0.6.0" +name = "hyper-timeout" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" +checksum = "2b90d566bffbce6a75bd8b09a05aa8c2cb1fabb6cb348f8840c9e4c90a0d83b0" dependencies = [ - "bytes 1.12.1", - "http-body-util", "hyper 1.11.0", "hyper-util", - "native-tls", + "pin-project-lite", "tokio", - "tokio-native-tls", "tower-service", ] @@ -1213,30 +1406,25 @@ version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" dependencies = [ - "base64 0.22.1", "bytes 1.12.1", "futures-channel", "futures-util", - "http 1.3.1", - "http-body 1.0.1", + "http 1.5.0", + "http-body 1.1.0", "hyper 1.11.0", - "ipnet", "libc", - "percent-encoding", "pin-project-lite", - "socket2 0.6.0", - "system-configuration", + "socket2 0.6.5", "tokio", "tower-service", "tracing", - "windows-registry", ] [[package]] name = "iana-time-zone" -version = "0.1.63" +version = "0.1.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8" +checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -1258,12 +1446,13 @@ dependencies = [ [[package]] name = "icu_collections" -version = "2.0.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" +checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c" dependencies = [ "displaydoc", "potential_utf", + "utf8_iter", "yoke", "zerofrom", "zerovec", @@ -1271,9 +1460,9 @@ dependencies = [ [[package]] name = "icu_locale_core" -version = "2.0.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" +checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29" dependencies = [ "displaydoc", "litemap", @@ -1284,57 +1473,52 @@ dependencies = [ [[package]] name = "icu_normalizer" -version = "2.0.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" +checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4" dependencies = [ - "displaydoc", "icu_collections", "icu_normalizer_data", "icu_properties", "icu_provider", - "smallvec 1.15.1", + "smallvec 1.15.2", "zerovec", ] [[package]] name = "icu_normalizer_data" -version = "2.0.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" +checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38" [[package]] name = "icu_properties" -version = "2.0.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" +checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de" dependencies = [ - "displaydoc", "icu_collections", "icu_locale_core", "icu_properties_data", "icu_provider", - "potential_utf", "zerotrie", "zerovec", ] [[package]] name = "icu_properties_data" -version = "2.0.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" +checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14" [[package]] name = "icu_provider" -version = "2.0.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" +checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421" dependencies = [ "displaydoc", "icu_locale_core", - "stable_deref_trait", - "tinystr", "writeable", "yoke", "zerofrom", @@ -1349,15 +1533,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" dependencies = [ "idna_adapter", - "smallvec 1.15.1", + "smallvec 1.15.2", "utf8_iter", ] [[package]] name = "idna_adapter" -version = "1.2.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +checksum = "cb68373c0d6620ef8105e855e7745e18b0d00d3bdb07fb532e434244cdb9a714" dependencies = [ "icu_normalizer", "icu_properties", @@ -1365,12 +1549,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.12.1" +version = "2.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" dependencies = [ "equivalent", - "hashbrown 0.16.1", + "hashbrown 0.17.1", ] [[package]] @@ -1431,40 +1615,41 @@ checksum = "06432fb54d3be7964ecd3649233cddf80db2832f47fec34c01f65b3d9d774983" [[package]] name = "ipnet" -version = "2.11.0" +version = "2.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" +checksum = "6a756c3fac73139e83f14c2d742155dd2b78d3ee56597b419a0579b7bdd6dd78" [[package]] name = "is-terminal" -version = "0.4.16" +version = "0.4.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9" +checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46" dependencies = [ "hermit-abi 0.5.2", "libc", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] name = "is_terminal_polyfill" -version = "1.70.1" +version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" [[package]] name = "itoa" -version = "1.0.15" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" [[package]] name = "js-sys" -version = "0.3.77" +version = "0.3.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +checksum = "0e0c1080212aad755ea003d18543e8768dd432c48819efd73a7bf1e39b7a5a3a" dependencies = [ - "once_cell", + "cfg-if", + "futures-util", "wasm-bindgen", ] @@ -1480,26 +1665,50 @@ dependencies = [ ] [[package]] -name = "lazy-regex" -version = "3.4.1" +name = "jsonwebtoken" +version = "10.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60c7310b93682b36b98fa7ea4de998d3463ccbebd94d935d6b48ba5b6ffa7126" +checksum = "eba32bfb4ffdeaca3e34431072faf01745c9b26d25504aa7a6cf5684334fc4fc" dependencies = [ - "lazy-regex-proc_macros", + "base64 0.22.1", + "ed25519-dalek", + "getrandom 0.2.17", + "hmac", + "js-sys", + "p256", + "p384", + "pem", + "rand", + "rsa", + "serde", + "serde_json", + "sha2", + "signature", + "simple_asn1", + "zeroize", +] + +[[package]] +name = "lazy-regex" +version = "3.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4994ba703f78b083e2f7946dac9251abd83fd43a0365f030e99b69be5b4b9ef9" +dependencies = [ + "lazy-regex-proc_macros", "once_cell", "regex", ] [[package]] name = "lazy-regex-proc_macros" -version = "3.4.1" +version = "3.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ba01db5ef81e17eb10a5e0f2109d1b3a3e29bac3070fdbd7d156bf7dbd206a1" +checksum = "fd97232314824e6dbef1918a871bb93f51070455e3715bf26e19a6d01aa977a0" dependencies = [ "proc-macro2", "quote", "regex", - "syn 2.0.106", + "syn 2.0.119", ] [[package]] @@ -1507,6 +1716,9 @@ name = "lazy_static" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +dependencies = [ + "spin", +] [[package]] name = "libc" @@ -1514,13 +1726,18 @@ version = "0.2.189" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2" +[[package]] +name = "libm" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" + [[package]] name = "libredox" -version = "0.1.9" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "391290121bad3d37fbddad76d8f5d1c1c314cfc646d143d7e07a3086ddff0ce3" +checksum = "2026a5056764a10b2bf5d56488cba40da507f5493a6a429340e2004d9ed085fa" dependencies = [ - "bitflags", "libc", ] @@ -1532,37 +1749,30 @@ checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" [[package]] name = "litemap" -version = "0.8.0" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" +checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" [[package]] name = "litrs" -version = "0.4.2" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5e54036fe321fd421e10d732f155734c4e4afd610dd556d9a82833ab3ee0bed" +checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092" [[package]] name = "lock_api" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" dependencies = [ - "autocfg", "scopeguard", ] [[package]] name = "log" -version = "0.4.28" +version = "0.4.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" - -[[package]] -name = "lru-slab" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" +checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad" [[package]] name = "mac" @@ -1601,7 +1811,7 @@ checksum = "ac84fd3f360fcc43dc5f5d186f02a94192761a080e8bc58621ad4d12296a58cf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.119", ] [[package]] @@ -1621,15 +1831,9 @@ checksum = "4facc753ae494aeb6e3c22f839b158aebd4f9270f55cd3c79906c45476c47ab4" [[package]] name = "memchr" -version = "2.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" - -[[package]] -name = "mime" -version = "0.3.17" +version = "2.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" +checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" [[package]] name = "minimad" @@ -1642,14 +1846,14 @@ dependencies = [ [[package]] name = "mio" -version = "1.0.4" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" +checksum = "30d65c71f1ce40ab09135ce117d742b9f8a19ff91a41a8b57ed50bc2de59c427" dependencies = [ "libc", "log", "wasi 0.11.1+wasi-snapshot-preview1", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -1675,14 +1879,14 @@ dependencies = [ "cfg-if", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.119", ] [[package]] name = "native-tls" -version = "0.2.14" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e" +checksum = "465500e14ea162429d264d44189adc38b199b62b1c21eea9f69e4b73cb03bbf2" dependencies = [ "libc", "log", @@ -1715,18 +1919,63 @@ checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" [[package]] name = "nu-ansi-term" -version = "0.50.1" +version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4a28e057d01f97e61255210fcff094d74ed0466038633e95017f5beb68e4399" +checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.61.2", +] + +[[package]] +name = "num-bigint" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c89e69e7e0f03bea5ef08013795c25018e101932225a656383bd384495ecc367" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-bigint-dig" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e661dda6640fad38e827a6d4a310ff4763082116fe217f279885c97f511bb0b7" +dependencies = [ + "lazy_static", + "libm", + "num-integer", + "num-iter", + "num-traits", + "rand", + "smallvec 1.15.2", + "zeroize", ] [[package]] name = "num-conv" -version = "0.1.0" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" +checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441" + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c92800bd69a1eac91786bcfe9da64a897eb72911b8dc3095decbd07429e8048b" +dependencies = [ + "num-integer", + "num-traits", +] [[package]] name = "num-traits" @@ -1735,6 +1984,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", + "libm", ] [[package]] @@ -1746,29 +1996,68 @@ dependencies = [ "libc", ] +[[package]] +name = "octocrab" +version = "0.54.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "432fad6f447c52acda76a7a0660f022b21f4c42f373bbdc29f04332ba19a89bf" +dependencies = [ + "arc-swap", + "async-trait", + "base64 0.22.1", + "bytes 1.12.1", + "cargo_metadata", + "cfg-if", + "chrono", + "futures", + "getrandom 0.2.17", + "http 1.5.0", + "http-body 1.1.0", + "http-body-util", + "http-serde", + "hyper 1.11.0", + "hyper-rustls", + "hyper-timeout", + "hyper-util", + "jsonwebtoken", + "percent-encoding", + "pin-project", + "rustls", + "secrecy", + "serde", + "serde_json", + "serde_path_to_error", + "serde_urlencoded", + "snafu", + "tokio", + "tower 0.5.3", + "tower-http", + "url", + "web-time", +] + [[package]] name = "once_cell" -version = "1.21.3" +version = "1.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" [[package]] name = "once_cell_polyfill" -version = "1.70.1" +version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" [[package]] name = "openssl" -version = "0.10.73" +version = "0.10.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8505734d46c8ab1e19a1dce3aef597ad87dcb4c37e7188231769bd6bd51cebf8" +checksum = "77823a27f0babb03091cb9ed9ef80af3b39dbc82f97e8fa530374b7dafd87a45" dependencies = [ "bitflags", "cfg-if", "foreign-types", "libc", - "once_cell", "openssl-macros", "openssl-sys", ] @@ -1781,20 +2070,20 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.119", ] [[package]] name = "openssl-probe" -version = "0.1.6" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" +checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" [[package]] name = "openssl-sys" -version = "0.9.109" +version = "0.9.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90096e2e47630d78b7d1c20952dc621f957103f8bc2c8359ec81290d75238571" +checksum = "b47e7e6bb2c38cd930d25a23b40fa52e068c10e85f3e03a7f5ba5aaca5713695" dependencies = [ "cc", "libc", @@ -1830,8 +2119,8 @@ dependencies = [ "serde", "serde-saphyr", "serde_json", - "thiserror 2.0.19", - "toml 0.9.8", + "thiserror 2.0.20", + "toml 0.9.12+spec-1.1.0", "tracing", "uncased", "unic-langid", @@ -1847,14 +2136,38 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.119", +] + +[[package]] +name = "p256" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2", +] + +[[package]] +name = "p384" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe42f1670a52a47d448f14b6a5c61dd78fce51856e68edaa38f7ae3a46b8d6b6" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2", ] [[package]] name = "parking_lot" -version = "0.12.4" +version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" dependencies = [ "lock_api", "parking_lot_core", @@ -1862,15 +2175,15 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.11" +version = "0.9.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" dependencies = [ "cfg-if", "libc", "redox_syscall", - "smallvec 1.15.1", - "windows-targets 0.52.6", + "smallvec 1.15.2", + "windows-link", ] [[package]] @@ -1893,7 +2206,26 @@ dependencies = [ "proc-macro2", "proc-macro2-diagnostics", "quote", - "syn 2.0.106", + "syn 2.0.119", +] + +[[package]] +name = "pem" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d30c53c26bc5b31a98cd02d20f25a7c8567146caf63ed593a9d87b2775291be" +dependencies = [ + "base64 0.22.1", + "serde_core", +] + +[[package]] +name = "pem-rfc7468" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" +dependencies = [ + "base64ct", ] [[package]] @@ -1904,20 +2236,19 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "pest" -version = "2.8.1" +version = "2.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1db05f56d34358a8b1066f67cbb203ee3e7ed2ba674a6263a1d5ec6db2204323" +checksum = "7df728be843c7070fab6ab7c328c4e9e9d78e23bf749c0669c86ee7ebfa050a2" dependencies = [ "memchr", - "thiserror 2.0.19", "ucd-trie", ] [[package]] name = "pest_derive" -version = "2.8.1" +version = "2.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb056d9e8ea77922845ec74a1c4e8fb17e7c218cc4fc11a15c5d25e189aa40bc" +checksum = "9e2dd6fc3b26b3462ee188aac870f5a41d398f1cd5e2408d16531bd71c9591fd" dependencies = [ "pest", "pest_generator", @@ -1925,25 +2256,24 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.8.1" +version = "2.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87e404e638f781eb3202dc82db6760c8ae8a1eeef7fb3fa8264b2ef280504966" +checksum = "6a7a9205cfb6f596a9e8b689c0a15f9ceb7a1aafae7aaf788150ac65b29975b6" dependencies = [ "pest", "pest_meta", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.119", ] [[package]] name = "pest_meta" -version = "2.8.1" +version = "2.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edd1101f170f5903fde0914f899bb503d9ff5271d7ba76bbb70bea63690cc0d5" +checksum = "85abd351c0de1e8384fc791a0737111a350394937e92b956b743dac12429f57c" dependencies = [ "pest", - "sha2", ] [[package]] @@ -1972,7 +2302,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" dependencies = [ "phf_shared", - "rand 0.8.5", + "rand", ] [[package]] @@ -1984,23 +2314,64 @@ dependencies = [ "siphasher", ] +[[package]] +name = "pin-project" +version = "1.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2466b2336ed02bcdca6b294417127b90ec92038d1d5c4fbeac971a922e0e0924" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c96395f0a926bc13b1c17622aaddda1ecb55d49c8f1bf9777e4d877800a43f8b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + [[package]] name = "pin-project-lite" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "pkcs1" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" +dependencies = [ + "der", + "pkcs8", + "spki", +] + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] [[package]] name = "pkg-config" -version = "0.3.32" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" +checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" [[package]] name = "potential_utf" -version = "0.1.3" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84df19adbe5b5a0782edcab45899906947ab039ccf4573713735ee7de1e6b08a" +checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564" dependencies = [ "zerovec", ] @@ -2042,27 +2413,36 @@ dependencies = [ [[package]] name = "predicates-core" -version = "1.0.9" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "727e462b119fe9c93fd0eb1429a5f7647394014cf3c04ab2c0350eeb09095ffa" +checksum = "cad38746f3166b4031b1a0d39ad9f954dd291e7854fcc0eed52ee41a0b50d144" [[package]] name = "predicates-tree" -version = "1.0.12" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72dd2d6d381dfb73a193c7fca536518d7caee39fc8503f74e7dc0be0531b425c" +checksum = "d0de1b847b39c8131db0467e9df1ff60e6d0562ab8e9a16e568ad0fdb372e2f2" dependencies = [ "predicates-core", "termtree", ] +[[package]] +name = "primeorder" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" +dependencies = [ + "elliptic-curve", +] + [[package]] name = "proc-macro-crate" -version = "3.3.0" +version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35" +checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" dependencies = [ - "toml_edit", + "toml_edit 0.25.13+spec-1.1.0", ] [[package]] @@ -2088,66 +2468,11 @@ checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.119", "version_check", "yansi", ] -[[package]] -name = "quinn" -version = "0.11.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" -dependencies = [ - "bytes 1.12.1", - "cfg_aliases", - "pin-project-lite", - "quinn-proto", - "quinn-udp", - "rustc-hash", - "rustls", - "socket2 0.6.0", - "thiserror 2.0.19", - "tokio", - "tracing", - "web-time", -] - -[[package]] -name = "quinn-proto" -version = "0.11.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31" -dependencies = [ - "bytes 1.12.1", - "getrandom 0.3.3", - "lru-slab", - "rand 0.9.2", - "ring", - "rustc-hash", - "rustls", - "rustls-pki-types", - "slab", - "thiserror 2.0.19", - "tinyvec", - "tracing", - "web-time", -] - -[[package]] -name = "quinn-udp" -version = "0.5.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" -dependencies = [ - "cfg_aliases", - "libc", - "once_cell", - "socket2 0.6.0", - "tracing", - "windows-sys 0.60.2", -] - [[package]] name = "quote" version = "1.0.47" @@ -2159,37 +2484,29 @@ dependencies = [ [[package]] name = "r-efi" -version = "5.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" - -[[package]] -name = "rand" -version = "0.8.5" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "rand_core 0.6.4", -] +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" [[package]] name = "rand" -version = "0.9.2" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +checksum = "22f6172bdec972074665ed81ed53b71da00bfc44b65a753cfde883ec4c702a1a" dependencies = [ + "libc", "rand_chacha", - "rand_core 0.9.3", + "rand_core", ] [[package]] name = "rand_chacha" -version = "0.9.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core 0.9.3", + "rand_core", ] [[package]] @@ -2197,21 +2514,15 @@ name = "rand_core" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" - -[[package]] -name = "rand_core" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" dependencies = [ - "getrandom 0.3.3", + "getrandom 0.2.17", ] [[package]] name = "redox_syscall" -version = "0.5.17" +version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ "bitflags", ] @@ -2222,7 +2533,7 @@ version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" dependencies = [ - "getrandom 0.2.16", + "getrandom 0.2.17", "libredox", "thiserror 1.0.69", ] @@ -2233,9 +2544,9 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" dependencies = [ - "getrandom 0.2.16", + "getrandom 0.2.17", "libredox", - "thiserror 2.0.19", + "thiserror 2.0.20", ] [[package]] @@ -2274,47 +2585,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba39f3699c378cd8970968dcbff9c43159ea4cfbd88d43c00b22f2ef10a435d2" [[package]] -name = "reqwest" -version = "0.12.28" +name = "rfc6979" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" dependencies = [ - "base64 0.22.1", - "bytes 1.12.1", - "encoding_rs", - "futures-core", - "h2", - "http 1.3.1", - "http-body 1.0.1", - "http-body-util", - "hyper 1.11.0", - "hyper-rustls", - "hyper-tls", - "hyper-util", - "js-sys", - "log", - "mime", - "native-tls", - "percent-encoding", - "pin-project-lite", - "quinn", - "rustls", - "rustls-pki-types", - "serde", - "serde_json", - "serde_urlencoded", - "sync_wrapper", - "tokio", - "tokio-native-tls", - "tokio-rustls", - "tower 0.5.2", - "tower-http", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "webpki-roots", + "hmac", + "subtle", ] [[package]] @@ -2325,12 +2602,32 @@ checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" dependencies = [ "cc", "cfg-if", - "getrandom 0.2.16", + "getrandom 0.2.17", "libc", "untrusted", "windows-sys 0.52.0", ] +[[package]] +name = "rsa" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8573f03f5883dcaebdfcf4725caa1ecb9c15b2ef50c43a07b816e06799bb12d" +dependencies = [ + "const-oid", + "digest", + "num-bigint-dig", + "num-integer", + "num-traits", + "pkcs1", + "pkcs8", + "rand_core", + "signature", + "spki", + "subtle", + "zeroize", +] + [[package]] name = "rstest" version = "0.26.1" @@ -2356,15 +2653,15 @@ dependencies = [ "regex", "relative-path", "rustc_version", - "syn 2.0.106", + "syn 2.0.119", "unicode-ident", ] [[package]] name = "rustc-hash" -version = "2.1.1" +version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" +checksum = "6b1e7f9a428571be2dc5bc0505c13fb6bf936822b894ec87abf8a08a4e51742d" [[package]] name = "rustc_version" @@ -2385,7 +2682,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.61.1", + "windows-sys 0.61.2", ] [[package]] @@ -2400,10 +2697,11 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.31" +version = "0.23.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0ebcbd2f03de0fc1122ad9bb24b127a5a6cd51d72604a3f3c50ac459762b6cc" +checksum = "0283386ce02abc0151e1761d08802dfe86c173b0b494af5cbc086574e453da06" dependencies = [ + "log", "once_cell", "ring", "rustls-pki-types", @@ -2412,21 +2710,32 @@ dependencies = [ "zeroize", ] +[[package]] +name = "rustls-native-certs" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dab5152771c58876a2146916e53e35057e1a4dfa2b9df0f0305b07f611fdea4d" +dependencies = [ + "openssl-probe", + "rustls-pki-types", + "schannel", + "security-framework", +] + [[package]] name = "rustls-pki-types" -version = "1.12.0" +version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79" +checksum = "2f4925028c7eb5d1fcdaf196971378ed9d2c1c4efc7dc5d011256f76c99c0a96" dependencies = [ - "web-time", "zeroize", ] [[package]] name = "rustls-webpki" -version = "0.103.4" +version = "0.103.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a17884ae0c1b773f1ccd2bd4a8c72f16da897310a98b0e84bf349ad5ead92fc" +checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e" dependencies = [ "ring", "rustls-pki-types", @@ -2435,15 +2744,15 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" +checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f" [[package]] name = "ryu" -version = "1.0.20" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" +checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" [[package]] name = "saphyr-parser" @@ -2457,24 +2766,47 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.27" +version = "0.1.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" +checksum = "91c1b7e4904c873ef0710c1f407dde2e6287de2bebc1bbbf7d430bb7cbffd939" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct", + "der", + "generic-array", + "pkcs8", + "subtle", + "zeroize", ] [[package]] -name = "scopeguard" -version = "1.2.0" +name = "secrecy" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +checksum = "e891af845473308773346dc847b2c23ee78fe442e0472ac50e22a18a93d3ae5a" +dependencies = [ + "zeroize", +] [[package]] name = "security-framework" -version = "2.11.1" +version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" +checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" dependencies = [ "bitflags", "core-foundation", @@ -2485,9 +2817,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.14.0" +version = "2.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" +checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" dependencies = [ "core-foundation-sys", "libc", @@ -2495,15 +2827,19 @@ dependencies = [ [[package]] name = "self_cell" -version = "1.2.2" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b12e76d157a900eb52e81bc6e9f3069344290341720e9178cde2407113ac8d89" +checksum = "2ab42ca02749e120097e328d91d415325bdf43b1c72c4c8badf37375fe40a813" [[package]] name = "semver" -version = "1.0.26" +version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" +dependencies = [ + "serde", + "serde_core", +] [[package]] name = "serde" @@ -2586,9 +2922,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "1.0.3" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e24345aa0fe688594e73770a5f6d1b216508b4f93484c0026d521acd30134392" +checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26" dependencies = [ "serde_core", ] @@ -2627,7 +2963,7 @@ checksum = "94e153fc76e1c6a068703d6d29c508a0b15c061c4b7e43da59cc097bc342673c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.119", ] [[package]] @@ -2652,9 +2988,9 @@ dependencies = [ [[package]] name = "shlex" -version = "1.3.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" [[package]] name = "signal-hook" @@ -2668,9 +3004,9 @@ dependencies = [ [[package]] name = "signal-hook-mio" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34db1a06d485c9142248b7a054f034b349b212551f3dfd19c94d45a754a217cd" +checksum = "b75a19a7a740b25bc7944bdee6172368f988763b744e3d4dfe753f6b4ece40cc" dependencies = [ "libc", "mio", @@ -2679,19 +3015,42 @@ dependencies = [ [[package]] name = "signal-hook-registry" -version = "1.4.6" +version = "1.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b" +checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" dependencies = [ + "errno", "libc", ] +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest", + "rand_core", +] + [[package]] name = "similar" version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa" +[[package]] +name = "simple_asn1" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d585997b0ac10be3c5ee635f1bab02d512760d14b7c468801ac8a01d9ae5f1d" +dependencies = [ + "num-bigint", + "num-traits", + "thiserror 2.0.20", + "time 0.3.55", +] + [[package]] name = "simple_logger" version = "1.16.0" @@ -2701,27 +3060,27 @@ dependencies = [ "atty", "colored", "log", - "time 0.3.41", + "time 0.3.55", "winapi", ] [[package]] name = "siphasher" -version = "1.0.1" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" +checksum = "8ee5873ec9cce0195efcb7a4e9507a04cd49aec9c83d0389df45b1ef7ba2e649" [[package]] name = "slab" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" [[package]] name = "smallvec" -version = "1.15.1" +version = "1.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" +checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90" [[package]] name = "smallvec" @@ -2729,6 +3088,27 @@ version = "2.0.0-alpha.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef784004ca8777809dcdad6ac37629f0a97caee4c685fcea805278d81dd8b857" +[[package]] +name = "snafu" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e84b3f4eacbf3a1ce05eac6763b4d629d60cbc94d632e4092c54ade71f1e1a2" +dependencies = [ + "snafu-derive", +] + +[[package]] +name = "snafu-derive" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1c97747dbf44bb1ca44a561ece23508e99cb592e862f22222dcf42f51d1e451" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.119", +] + [[package]] name = "socket2" version = "0.5.10" @@ -2741,19 +3121,35 @@ dependencies = [ [[package]] name = "socket2" -version = "0.6.0" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807" +checksum = "c3d1e2c7f27f8d4cb10542a02c49005dbd6e93095799d6f3be745fae9f8fedd4" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.61.2", +] + +[[package]] +name = "spin" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3763264f6b73151db08c50ff20d7d8a0b8796e021cdea7ceedad07b80155fa0e" + +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", ] [[package]] name = "stable_deref_trait" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" [[package]] name = "strict" @@ -2800,9 +3196,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.106" +version = "2.0.119" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" +checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297" dependencies = [ "proc-macro2", "quote", @@ -2825,9 +3221,6 @@ name = "sync_wrapper" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" -dependencies = [ - "futures-core", -] [[package]] name = "synstructure" @@ -2837,28 +3230,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", -] - -[[package]] -name = "system-configuration" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b" -dependencies = [ - "bitflags", - "core-foundation", - "system-configuration-sys", -] - -[[package]] -name = "system-configuration-sys" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" -dependencies = [ - "core-foundation-sys", - "libc", + "syn 2.0.119", ] [[package]] @@ -2868,10 +3240,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ "fastrand", - "getrandom 0.3.3", + "getrandom 0.4.3", "once_cell", "rustix", - "windows-sys 0.61.1", + "windows-sys 0.61.2", ] [[package]] @@ -2897,7 +3269,7 @@ dependencies = [ "lazy-regex", "minimad", "serde", - "thiserror 2.0.19", + "thiserror 2.0.20", "unicode-width", ] @@ -2941,11 +3313,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.19" +version = "2.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09a43598840e33d5b0331f38c5e30d13bb11c11210a4b58f0d9b18a5a5eefcd9" +checksum = "ec86235f5fcc2a73650310756d2ac5b138a5780bbbdfae3eeccec992c435ba4f" dependencies = [ - "thiserror-impl 2.0.19", + "thiserror-impl 2.0.20", ] [[package]] @@ -2956,14 +3328,14 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.119", ] [[package]] name = "thiserror-impl" -version = "2.0.19" +version = "2.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43cbfe0cf76104d42a574802844187e84a305e531ed54455f11fbde0f10541cd" +checksum = "bc04cd3e1236dd4a98afca4569f2deb3f120e5422a4023be2cb683f8486292af" dependencies = [ "proc-macro2", "quote", @@ -2972,9 +3344,9 @@ dependencies = [ [[package]] name = "thread_local" -version = "1.1.9" +version = "1.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" +checksum = "1ad99c4c6d32803332c548b1af0540b357b3f5fc0be8f6c6bfe8b2e6ae784070" dependencies = [ "cfg-if", ] @@ -2992,32 +3364,31 @@ dependencies = [ [[package]] name = "time" -version = "0.3.41" +version = "0.3.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" +checksum = "cdb87b95ec50ddfa440816d227a17b2ccbdda963a316a727fda0fc4334f7d134" dependencies = [ "deranged", - "itoa", "libc", "num-conv", "num_threads", "powerfmt", - "serde", + "serde_core", "time-core", "time-macros", ] [[package]] name = "time-core" -version = "0.1.4" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" +checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109" [[package]] name = "time-macros" -version = "0.2.22" +version = "0.2.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" +checksum = "7e689342a48d2ea927c87ea50cabf8594854bf940e9310208848d680d668ed85" dependencies = [ "num-conv", "time-core", @@ -3025,34 +3396,20 @@ dependencies = [ [[package]] name = "tinystr" -version = "0.8.1" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" +checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d" dependencies = [ "displaydoc", + "serde_core", "zerovec", ] -[[package]] -name = "tinyvec" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - [[package]] name = "tokio" -version = "1.50.0" +version = "1.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27ad5e34374e03cfffefc301becb44e9dc3c17584f414349ebe29ed26661822d" +checksum = "202caea871b69668250d242070849eb495be178ed697a3e98aebce5bc81a0bed" dependencies = [ "bytes 1.12.1", "libc", @@ -3060,20 +3417,20 @@ dependencies = [ "parking_lot", "pin-project-lite", "signal-hook-registry", - "socket2 0.6.0", + "socket2 0.6.5", "tokio-macros", - "windows-sys 0.61.1", + "windows-sys 0.61.2", ] [[package]] name = "tokio-macros" -version = "2.6.1" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c55a2eff8b69ce66c84f85e1da1c233edc36ceb85a2058d11b0d6a3c7e7569c" +checksum = "78773a2a397f451582ce068015985c33193cf6dea8b74d2a639fe457b2f07b0e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 3.0.3", ] [[package]] @@ -3088,9 +3445,9 @@ dependencies = [ [[package]] name = "tokio-rustls" -version = "0.26.2" +version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b" +checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" dependencies = [ "rustls", "tokio", @@ -3112,9 +3469,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.16" +version = "0.7.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14307c986784f72ef81c89db7d9e28d6ac26d16213b109ea501696195e6e3ce5" +checksum = "494815d09bf52b5548659851081238f0ca39ff638363907596da739561c62c52" dependencies = [ "bytes 1.12.1", "futures-core", @@ -3132,22 +3489,22 @@ dependencies = [ "serde", "serde_spanned 0.6.9", "toml_datetime 0.6.11", - "toml_edit", + "toml_edit 0.22.27", ] [[package]] name = "toml" -version = "0.9.8" +version = "0.9.12+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0dc8b1fb61449e27716ec0e1bdf0f6b8f3e8f6b05391e8497b8b6d7804ea6d8" +checksum = "cf92845e79fc2e2def6a5d828f0801e29a2f8acc037becc5ab08595c7d5e9863" dependencies = [ "indexmap", "serde_core", - "serde_spanned 1.0.3", - "toml_datetime 0.7.3", + "serde_spanned 1.1.1", + "toml_datetime 0.7.5+spec-1.1.0", "toml_parser", "toml_writer", - "winnow", + "winnow 0.7.15", ] [[package]] @@ -3161,9 +3518,18 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.7.3" +version = "0.7.5+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_datetime" +version = "1.1.1+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2cdb639ebbc97961c51720f858597f7f24c4fc295327923af55b74c3c724533" +checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7" dependencies = [ "serde_core", ] @@ -3179,16 +3545,28 @@ dependencies = [ "serde_spanned 0.6.9", "toml_datetime 0.6.11", "toml_write", - "winnow", + "winnow 0.7.15", +] + +[[package]] +name = "toml_edit" +version = "0.25.13+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6975367e4d2ef766d86af01ffad14b622fecc8d4357a998fbc4deb6e9bacaf9b" +dependencies = [ + "indexmap", + "toml_datetime 1.1.1+spec-1.1.0", + "toml_parser", + "winnow 1.0.4", ] [[package]] name = "toml_parser" -version = "1.0.4" +version = "1.1.3+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0cbe268d35bdb4bb5a56a2de88d0ad0eb70af5384a99d648cd4b3d04039800e" +checksum = "1d38ac1cf9b95face32296c0a3ede1fdc270627c9d9c02a7274dd6d960dc4d56" dependencies = [ - "winnow", + "winnow 1.0.4", ] [[package]] @@ -3199,9 +3577,9 @@ checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" [[package]] name = "toml_writer" -version = "1.0.4" +version = "1.1.2+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df8b2b54733674ad286d16267dcfc7a71ed5c776e4ac7aa3c3e2561f7c637bf2" +checksum = "7d56353a2a665ad0f41a421187180aab746c8c325620617ad883a99a1cbe66d2" [[package]] name = "tower" @@ -3216,17 +3594,19 @@ dependencies = [ [[package]] name = "tower" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" +checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" dependencies = [ "futures-core", "futures-util", "pin-project-lite", "sync_wrapper", "tokio", + "tokio-util 0.7.19", "tower-layer", "tower-service", + "tracing", ] [[package]] @@ -3237,14 +3617,12 @@ checksum = "4cfcf7e2740e6fc6d4d688b4ef00650406bb94adf4731e43c096c3a19fe40840" dependencies = [ "bitflags", "bytes 1.12.1", - "futures-util", - "http 1.3.1", - "http-body 1.0.1", + "http 1.5.0", + "http-body 1.1.0", "pin-project-lite", - "tower 0.5.2", "tower-layer", "tower-service", - "url", + "tracing", ] [[package]] @@ -3279,7 +3657,7 @@ checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.119", ] [[package]] @@ -3314,7 +3692,7 @@ dependencies = [ "once_cell", "regex-automata", "sharded-slab", - "smallvec 1.15.1", + "smallvec 1.15.2", "thread_local", "tracing", "tracing-core", @@ -3338,9 +3716,9 @@ dependencies = [ [[package]] name = "typenum" -version = "1.18.0" +version = "1.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" +checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" [[package]] name = "ucd-trie" @@ -3396,21 +3774,21 @@ checksum = "a1249a628de3ad34b821ecb1001355bca3940bcb2f88558f1a8bd82e977f75b5" dependencies = [ "proc-macro-hack", "quote", - "syn 2.0.106", + "syn 2.0.119", "unic-langid-impl", ] [[package]] name = "unicode-ident" -version = "1.0.18" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" [[package]] name = "unicode-segmentation" -version = "1.12.0" +version = "1.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" +checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8" [[package]] name = "unicode-width" @@ -3434,6 +3812,7 @@ dependencies = [ "idna", "percent-encoding", "serde", + "serde_derive", ] [[package]] @@ -3479,24 +3858,26 @@ dependencies = [ "anyhow", "assert_cmd", "backon", - "base64 0.23.0", + "base64 0.23.1", "bytes 1.12.1", "chrono", "clap", "diffy", "futures", "html5ever", + "http 1.5.0", "http-body-util", "hyper 1.11.0", + "hyper-rustls", "hyper-util", "insta", "libc", "markup5ever_rcdom", "mockall", + "octocrab", "ortho_config", "predicates", "regex", - "reqwest", "rstest", "serde", "serde_json", @@ -3505,7 +3886,7 @@ dependencies = [ "tempfile", "termimad", "third-wheel", - "thiserror 2.0.19", + "thiserror 2.0.20", "tokio", "tracing", "tracing-subscriber", @@ -3542,59 +3923,24 @@ version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" -[[package]] -name = "wasi" -version = "0.14.3+wasi-0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a51ae83037bdd272a9e28ce236db8c07016dd0d50c27038b3f407533c030c95" -dependencies = [ - "wit-bindgen", -] - [[package]] name = "wasm-bindgen" -version = "0.2.100" +version = "0.2.127" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +checksum = "1b70935747edd64d89de3efa29d73789b806c15798f8e7dca4d8ac356b50ce70" dependencies = [ "cfg-if", "once_cell", "rustversion", "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" -dependencies = [ - "bumpalo", - "log", - "proc-macro2", - "quote", - "syn 2.0.106", "wasm-bindgen-shared", ] -[[package]] -name = "wasm-bindgen-futures" -version = "0.4.50" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" -dependencies = [ - "cfg-if", - "js-sys", - "once_cell", - "wasm-bindgen", - "web-sys", -] - [[package]] name = "wasm-bindgen-macro" -version = "0.2.100" +version = "0.2.127" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +checksum = "77775f8f3f7217702089053b94958f8f54061a3f663417df76e19cbdcca29bc1" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -3602,36 +3948,26 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.100" +version = "0.2.127" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +checksum = "e11d33f857dc2fb11b8bc75aee111aa9cbeb12cd9f25efd3d4c2a3dd4e235284" dependencies = [ + "bumpalo", "proc-macro2", "quote", - "syn 2.0.106", - "wasm-bindgen-backend", + "syn 2.0.119", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.100" +version = "0.2.127" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +checksum = "7ef64dbcc55df09c7e5a46182d181c2cfa3e925f3da937ea764728b4bbb9dcbf" dependencies = [ "unicode-ident", ] -[[package]] -name = "web-sys" -version = "0.3.77" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - [[package]] name = "web-time" version = "1.1.0" @@ -3639,6 +3975,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" dependencies = [ "js-sys", + "serde", "wasm-bindgen", ] @@ -3656,9 +3993,9 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "1.0.2" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e8983c3ab33d6fb807cfcdad2491c4ea8cbc8ed839181c7dfd9c67c83e261b2" +checksum = "7dcd9d09a39985f5344844e66b0c530a33843579125f23e21e9f0f220850f22a" dependencies = [ "rustls-pki-types", ] @@ -3687,78 +4024,61 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-core" -version = "0.61.2" +version = "0.62.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" dependencies = [ "windows-implement", "windows-interface", - "windows-link 0.1.3", + "windows-link", "windows-result", "windows-strings", ] [[package]] name = "windows-implement" -version = "0.60.0" +version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.119", ] [[package]] name = "windows-interface" -version = "0.59.1" +version = "0.59.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.119", ] [[package]] name = "windows-link" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" - -[[package]] -name = "windows-link" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45e46c0661abb7180e7b9c281db115305d49ca1709ab8242adf09666d2173c65" - -[[package]] -name = "windows-registry" -version = "0.5.3" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b8a9ed28765efc97bbc954883f4e6796c33a06546ebafacbabee9696967499e" -dependencies = [ - "windows-link 0.1.3", - "windows-result", - "windows-strings", -] +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" [[package]] name = "windows-result" -version = "0.3.4" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" dependencies = [ - "windows-link 0.1.3", + "windows-link", ] [[package]] name = "windows-strings" -version = "0.4.2" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" dependencies = [ - "windows-link 0.1.3", + "windows-link", ] [[package]] @@ -3790,20 +4110,11 @@ dependencies = [ [[package]] name = "windows-sys" -version = "0.60.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" -dependencies = [ - "windows-targets 0.53.3", -] - -[[package]] -name = "windows-sys" -version = "0.61.1" +version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f109e41dd4a3c848907eb83d5a42ea98b3769495597450cf6d153507b166f0f" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" dependencies = [ - "windows-link 0.2.0", + "windows-link", ] [[package]] @@ -3830,30 +4141,13 @@ dependencies = [ "windows_aarch64_gnullvm 0.52.6", "windows_aarch64_msvc 0.52.6", "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm 0.52.6", + "windows_i686_gnullvm", "windows_i686_msvc 0.52.6", "windows_x86_64_gnu 0.52.6", "windows_x86_64_gnullvm 0.52.6", "windows_x86_64_msvc 0.52.6", ] -[[package]] -name = "windows-targets" -version = "0.53.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91" -dependencies = [ - "windows-link 0.1.3", - "windows_aarch64_gnullvm 0.53.0", - "windows_aarch64_msvc 0.53.0", - "windows_i686_gnu 0.53.0", - "windows_i686_gnullvm 0.53.0", - "windows_i686_msvc 0.53.0", - "windows_x86_64_gnu 0.53.0", - "windows_x86_64_gnullvm 0.53.0", - "windows_x86_64_msvc 0.53.0", -] - [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" @@ -3866,12 +4160,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" - [[package]] name = "windows_aarch64_msvc" version = "0.48.5" @@ -3884,12 +4172,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" -[[package]] -name = "windows_aarch64_msvc" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" - [[package]] name = "windows_i686_gnu" version = "0.48.5" @@ -3902,24 +4184,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" -[[package]] -name = "windows_i686_gnu" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" - [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" -[[package]] -name = "windows_i686_gnullvm" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" - [[package]] name = "windows_i686_msvc" version = "0.48.5" @@ -3932,12 +4202,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" -[[package]] -name = "windows_i686_msvc" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" - [[package]] name = "windows_x86_64_gnu" version = "0.48.5" @@ -3950,12 +4214,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" -[[package]] -name = "windows_x86_64_gnu" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" - [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" @@ -3968,12 +4226,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" - [[package]] name = "windows_x86_64_msvc" version = "0.48.5" @@ -3987,16 +4239,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] -name = "windows_x86_64_msvc" -version = "0.53.0" +name = "winnow" +version = "0.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" +checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945" +dependencies = [ + "memchr", +] [[package]] name = "winnow" -version = "0.7.13" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" +checksum = "23b97319f7b8343df12cc98938e5c3eb436064524c8d2b4e30a1d3a36eecdf81" dependencies = [ "memchr", ] @@ -4011,17 +4266,11 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "wit-bindgen" -version = "0.45.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "052283831dbae3d879dc7f51f3d92703a316ca49f91540417d38591826127814" - [[package]] name = "writeable" -version = "0.6.1" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" +checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" [[package]] name = "xdg" @@ -4047,11 +4296,10 @@ checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" [[package]] name = "yoke" -version = "0.8.0" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" +checksum = "709fe23a0424b6a435d82152b1bd3fdfb0833487d5fa90d05d42762a9891fef5" dependencies = [ - "serde", "stable_deref_trait", "yoke-derive", "zerofrom", @@ -4059,68 +4307,82 @@ dependencies = [ [[package]] name = "yoke-derive" -version = "0.8.0" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" +checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.119", "synstructure", ] [[package]] name = "zerocopy" -version = "0.8.26" +version = "0.8.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f" +checksum = "556764e583adb45a9f8d413c2a147fa7e8d821e48e12b14fd560b607998b75eb" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.26" +version = "0.8.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" +checksum = "f2ab42fc20575779bd240faa45f94a74256f755c0fa9e89f0ede20d91d0cdfc1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.119", ] [[package]] name = "zerofrom" -version = "0.1.6" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +checksum = "0ec05a11813ea801ff6d75110ad09cd0824ddba17dfe17128ea0d5f68e6c5272" dependencies = [ "zerofrom-derive", ] [[package]] name = "zerofrom-derive" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.119", "synstructure", ] [[package]] name = "zeroize" -version = "1.8.1" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +checksum = "3c50655cbb0fe3fc43170059e702f1ce5e19b84cec58dc87b037a09935c2f328" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] [[package]] name = "zerotrie" -version = "0.2.2" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" +checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf" dependencies = [ "displaydoc", "yoke", @@ -4129,10 +4391,11 @@ dependencies = [ [[package]] name = "zerovec" -version = "0.11.4" +version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7aa2bd55086f1ab526693ecbe444205da57e25f4489879da80635a46d90e73b" +checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239" dependencies = [ + "serde", "yoke", "zerofrom", "zerovec-derive", @@ -4140,13 +4403,13 @@ dependencies = [ [[package]] name = "zerovec-derive" -version = "0.11.1" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" +checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.119", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 5fa4bab..a0efa23 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,11 +10,10 @@ repository = "https://github.com/leynos/vk" [dependencies] clap = { version = "4.5.47", features = ["derive"] } -reqwest = { version = "0.12.23", features = ["json", "rustls-tls"] } serde = { version = "1.0.196", features = ["derive"] } serde_json = "1.0.113" serde_path_to_error = "0.1.17" -tokio = { version = "1.36.0", features = ["rt-multi-thread", "macros"] } +tokio = { version = "1.36.0", features = ["rt-multi-thread", "macros", "time"] } termimad = "0.35.1" thiserror = "2.0.16" url = "2.5.0" @@ -24,7 +23,41 @@ chrono = { version = "0.4.42", features = ["serde", "clock"] } anyhow = "1.0" backon = "1.5.2" html5ever = "0.35.0" +http = "1" +# GraphQL transport stack (replaces reqwest). `hyper-util`'s legacy pooled +# client over a `hyper-rustls` connector reproduces the connection pooling and +# rustls TLS that reqwest supplied. The `server` feature on `hyper-util` and +# the plain `hyper`/`http-body-util` crates are also exercised by the +# integration tests, so a single runtime entry covers both uses. +hyper = { version = "1", features = ["client", "http1"] } +hyper-util = { version = "0.1", features = [ + "client-legacy", + "http1", + "tokio", + "server", +] } +# webpki-roots + ring matches reqwest's `rustls-tls` feature, which expands to +# `rustls-tls-webpki-roots` (Mozilla's bundled roots) with the ring provider; +# see docs/adr-001-github-api-client-modernisation.md. Defaults are disabled to +# keep aws-lc-rs out of the graph so ring remains the sole crypto provider. +hyper-rustls = { version = "0.27", default-features = false, features = [ + "ring", + "webpki-roots", + "http1", + "tls12", +] } +http-body-util = "0.1" markup5ever_rcdom = "0.35.0" +# Patch updates within octocrab 0.54 are accepted; widening to 0.55 requires +# review. The `retry` feature is deliberately excluded so the REST reply path +# stays retry-free (see docs/adr-001-github-api-client-modernisation.md). +octocrab = { version = "~0.54", default-features = false, features = [ + "default-client", + "jwt-rust-crypto", + "rustls", + "rustls-ring", + "timeout", +] } ortho_config = "0.8.0" base64 = "0.23.0" tracing = "0.1" @@ -47,9 +80,6 @@ tokio = { version = "1.36.0", features = ["full"] } serde_json = "1.0" predicates = "3.1" mockall = "0.15.0" -hyper = "1.6.0" -hyper-util = { version = "0.1.17", features = ["server", "http1", "tokio"] } -http-body-util = "0.1" bytes = "1" futures = "0.3" insta = { version = "1.43", features = ["redactions"] } diff --git a/docs/adr-001-github-api-client-modernisation.md b/docs/adr-001-github-api-client-modernisation.md new file mode 100644 index 0000000..2287d2e --- /dev/null +++ b/docs/adr-001-github-api-client-modernisation.md @@ -0,0 +1,181 @@ +# Architectural decision record (ADR) 001: GitHub API client modernization + +## Status + +Accepted (2026-07-09). The project owner accepted the three-part programme +after reviewing the ExecPlan draft: octocrab for the REST resolve path, a +direct hyper transport inside the bespoke GraphQL client (removing reqwest), and +`graphql_client` codegen for compile-time-checked GraphQL queries. + +## Date + +2026-07-09. + +## Context and problem statement + +`vk` is a command-line tool that shows unresolved GitHub pull request review +comments. It talks to GitHub through two bespoke, hand-rolled clients built +directly on the `reqwest` crate: + +- A GraphQL client (`src/api/client/`) used by every subcommand. It carries a + substantial observability investment: transcript recording of each request, + redacted error snippets in failure context, `backon` jittered-exponential + retry with transient-error classification (HTTP 5xx, HTTP 429, and + HTML-looking bodies), `serde_path_to_error` deserialization diagnostics, and + an environment-variable endpoint override (`GITHUB_GRAPHQL_URL`). +- A feature-gated REST client (`src/resolve/rest.rs`, compiled only under the + `unstable-rest-resolve` feature) that posts review-comment replies with no + retry and its own environment-variable base-URL override (`GITHUB_API_URL`). + +The original decision to hand-roll these clients was never recorded, so the +constraints that justified it are no longer legible to maintainers. The current +arrangement carries four problems: + +- Two duplicated header-building and client-construction code paths that drift + independently. +- No compile-time checking of GraphQL query strings: queries are raw `&str` + constants and operation names are recovered by string-sniffing the query text. +- A dependency on `reqwest` for work that a single hyper stack could serve, + when the rest of the intended stack (hyper, rustls) is already present. +- Bespoke REST plumbing (authentication, base-URI handling, header + construction) that duplicates what a maintained library already provides. + +The question this record settles is how to modernize GitHub API access without +regressing the observable behaviour that the test suite pins: command output, +error-message fragments, retry semantics, transcript format, authentication +precedence, and the environment-variable endpoint overrides. + +## Decision drivers + +- Preserve the observability machinery. Transcript recording, error snippets, + and retry classification all depend on access to the raw HTTP response + (status and body), including partial-success GraphQL payloads that carry both + `data` and `errors`. +- Preserve the test infrastructure. The environment-variable endpoint overrides + redirect the binary to loopback servers, and roughly twenty tests assert + exact error text; neither may change. +- Converge on a single HTTP stack rather than maintaining two. +- Gain compile-time validation of GraphQL queries and their variables. +- Minimize bespoke plumbing by delegating maintained concerns to a library + where doing so does not compromise observability. +- Keep TLS rustls-only; introduce no native-tls or OpenSSL dependency. +- Remain compatible with the minimum supported Rust version (MSRV) of 1.89. + +## Options considered + +### Option 1: keep both bespoke clients (status quo) + +Retain the two `reqwest`-based clients unchanged. This preserves all behaviour +at zero migration cost but resolves none of the problems: there is no +compile-time query checking, the duplicated plumbing persists, and two HTTP +paths remain. + +### Option 2: route all traffic through octocrab + +Adopt [octocrab](https://docs.rs/octocrab) as the single transport for both +REST and GraphQL. Rejected: octocrab's `graphql()` helper hides the raw +response, discarding the partial-success `data` and the response body that the +transcript, error-snippet, and retry-classification machinery depend on. Its +default builder accepts no custom middleware layers through which that +machinery could be reattached, GraphQL cursor pagination remains hand-rolled +regardless, and the escape-hatch `_post` method would become the primary +interface. The compile-time-checking gain is marginal because octocrab does not +type GraphQL queries itself. + +### Option 3: graphql_client codegen with reqwest retained + +Adopt `graphql_client` codegen for typed queries while keeping `reqwest` as the +transport. This gains typed queries but keeps two HTTP stacks once octocrab +arrives for the REST path, and retains the bespoke REST plumbing that a +maintained library would otherwise absorb. + +### Option 4 (adopted): three-part split + +Separate the concerns by transport: + +- octocrab (tilde-pinned `~0.54`) serves the REST resolve path only, replacing + the bespoke `reqwest` REST client and inheriting maintained authentication + and base-URI plumbing. +- A direct hyper transport (`hyper-util` legacy client plus `hyper-rustls`, + promoted from dev-dependencies to runtime dependencies) replaces `reqwest` + inside the bespoke GraphQL client, which retains its observability machinery. + `reqwest` then leaves the dependency graph. +- `graphql_client` 0.16 codegen validates every query against GitHub's vendored + public schema + ([`schema.docs.graphql`](https://docs.github.com/public/fpt/schema.docs.graphql)) + at compile time. Generated types stay private behind conversions to the + existing exported domain structs. + +| Dimension | Option 1 | Option 2 | Option 3 | Option 4 | +| ----------------------------- | -------- | -------- | -------- | -------- | +| Compile-time query checking | No | Marginal | Yes | Yes | +| HTTP stacks after change | Two | One | Two | One | +| Observability preserved | Yes | No | Yes | Yes | +| Bespoke REST plumbing removed | No | Yes | No | Yes | +| Maintained REST library | No | Yes | Yes | Yes | + +_Table 1: Comparison of the four GitHub API client options against the decision +drivers._ + +## Decision outcome + +Adopt option 4, the three-part split. octocrab's value concentrates in its +typed REST surface and maintained plumbing, whereas the bespoke GraphQL +client's value is its observability, which octocrab's `graphql()` helper would +discard. octocrab's own GraphQL example delegates query typing to +`graphql_client`, confirming that the intended division of labour matches the +tools' strengths. + +The programme is delivered as three pull requests, each behaviour-preserving +and gated by the existing test suite: + +1. Adopt octocrab for the REST resolve path. +2. Replace `reqwest` inside the GraphQL client with a hyper transport and + remove `reqwest` from the dependency graph. +3. Adopt `graphql_client` codegen so malformed queries fail the build rather + than a runtime request. + +The REST-first ordering proves octocrab in-tree with the smallest blast radius; +the transport change then removes `reqwest` before the largest change; the +codegen change is type-level only and benefits from a settled transport beneath +it. + +## Migration plan + +The migration is tracked as a living document in the ExecPlan +[`docs/execplans/adopt-octocrab.md`](execplans/adopt-octocrab.md). Its numbered +phases correspond to the three pull requests above, and it records the +constraints, tolerances, risks, decision log, and per-phase acceptance criteria +in detail. This record does not duplicate that content; consult the ExecPlan +for the authoritative migration sequence and progress. + +## Known risks and limitations + +- octocrab has shipped breaking changes in patch releases (upstream issue 899). + The dependency is therefore tilde-pinned (`~0.54`) rather than caret-ranged, + with the reason recorded both here and in a `Cargo.toml` comment; widen only + after review. +- The hyper transport must reproduce the pooling, TLS root-store, and + total-request timeout semantics that `reqwest` provided for free. A subtle + difference under failure could change behaviour; the retry and timeout unit + tests act as a characterization harness because the transport change alters + nothing else. +- GitHub's vendored public schema is roughly 1.5 MB, and each + `#[derive(GraphQLQuery)]` re-parses it at compile time. The build-time impact + is bounded by the tolerance recorded in the ExecPlan; operations are grouped + per document to amortize the parse. +- GitHub's custom scalars (`DateTime`, `URI`, `HTML`, and related types) + require Rust type aliases in scope of each derive. A shared `scalars` module + supplies them; a missing alias surfaces as an easily misread compile error. + +## Design for reuse + +The refitted GraphQL client is shaped for cheap future extraction into a shared +crate. The sibling project frankie (a code-review terminal user interface, +currently REST-only on octocrab) will need the same GraphQL review-thread +surface, and the executor (transport, retry, transcript, typed operations, and +cursor pagination) is the reusable part while query documents stay per-project. +To keep extraction cheap, `vk`-specific coupling (`VkError` and +`vk::environment`) is confined to the edges of the transport and typed modules +rather than woven through them. Extraction itself is out of scope for this +decision. diff --git a/docs/contents.md b/docs/contents.md index d4e4a29..09f9078 100644 --- a/docs/contents.md +++ b/docs/contents.md @@ -27,6 +27,13 @@ - [Documentation style guide](documentation-style-guide.md): Use this for spelling, formatting, and document-structure rules. +## Architecture decision records + +- [ADR 001: GitHub API client](adr-001-github-api-client-modernisation.md): + Records the accepted decision to serve REST through octocrab, move the + bespoke GraphQL client onto hyper transport, and add `graphql_client` code + generation for typed queries. + ## Migration guides - [Ortho Config v0.6.0 migration guide](ortho-config-v0-6-0-migration-guide.md): @@ -39,5 +46,9 @@ - [Execution plans](execplans/): Use this directory for living implementation plans that need to survive context handoffs. + - [Adopt octocrab](execplans/adopt-octocrab.md): + Records the living plan to modernize GitHub API access: octocrab for REST, + hyper transport for the bespoke GraphQL client, and `graphql_client` + code generation for typed queries. - [Adopt Ortho Config v0.8.0](execplans/adopt-ortho-config-v0-8-0.md): Use this for the recorded plan behind the v0.8.0 configuration adoption work. diff --git a/docs/developers-guide.md b/docs/developers-guide.md index d110081..4e9d089 100644 --- a/docs/developers-guide.md +++ b/docs/developers-guide.md @@ -83,6 +83,23 @@ Before extracting a helper, port, or abstraction, check whether one already exists and document the new ownership boundary in the relevant design or developer document. +### REST resolve client + +REST review-comment replies use octocrab through its raw `_post` route. The +`http` and `octocrab` entries in `Cargo.toml` are direct dependencies: `http` +supplies the header types, while octocrab owns authentication, base-URI +handling, and request execution. The octocrab `retry` feature remains excluded +so the reply path stays retry-free. + +The connection timeout maps to octocrab's connect timeout. The request timeout +configures octocrab's read and write timeouts and also wraps the complete +`_post` operation, preserving a total deadline across connection, write, and +response-read work. + +Status interpretation remains in `vk`: the resolve client warns and continues +for HTTP 404, accepts other successful statuses, and maps every other non-2xx +status to `VkError::RequestContext` with the route and status. + ## Documentation maintenance Update documentation in the same branch as the behaviour it describes: diff --git a/docs/execplans/adopt-octocrab.md b/docs/execplans/adopt-octocrab.md new file mode 100644 index 0000000..7793642 --- /dev/null +++ b/docs/execplans/adopt-octocrab.md @@ -0,0 +1,708 @@ +# Modernize GitHub API access: octocrab REST, hyper transport, typed GraphQL + +This ExecPlan (execution plan) is a living document. The sections `Constraints`, +`Tolerances`, `Risks`, `Progress`, `Surprises & Discoveries`, `Decision Log`, +and `Outcomes & Retrospective` must be kept up to date as work proceeds. + +Status: IN PROGRESS + +## Purpose / big picture + +`vk` is a command-line tool that shows unresolved GitHub pull request review +comments in the terminal. Today it talks to GitHub through two hand-rolled HTTP +clients built directly on the `reqwest` crate: a GraphQL client +(`src/api/client/`) used by every subcommand, and a REST client +(`src/resolve/rest.rs`, compiled only under the `unstable-rest-resolve` +feature) used to post a reply before resolving a review thread. + +An earlier draft of this plan proposed routing everything through +[octocrab](https://docs.rs/octocrab). Review concluded octocrab is a weak fit +for the GraphQL side (its `graphql()` helper hides the raw response that `vk`'s +transcript, error-snippet, and retry machinery depend on) but a genuine win for +the REST side (typed pull-request APIs, maintained auth and base-URI plumbing). +The revised programme is therefore three separable changes, delivered as three +pull requests: + +1. PR 1 — adopt octocrab for the REST resolve path, replacing the bespoke + `reqwest`-based `RestClient`. +2. PR 2 — replace `reqwest` inside the bespoke GraphQL client with a direct + hyper transport, then remove `reqwest` from the dependency graph. The binary + converges on one HTTP stack (hyper/rustls), shared with octocrab. +3. PR 3 — adopt `graphql_client` codegen so every GraphQL query and its + variables are checked against GitHub's published schema at compile time, + removing the raw query-string constants and hand-maintained response + envelopes. + +`vk`'s externally observable behaviour is preserved throughout: command output, +error messages, retry semantics, transcript recording, authentication +precedence, and the environment-variable endpoint overrides that the entire +test suite depends on. + +Success is observable per PR: after PR 1, `tests/resolve.rs` passes with +octocrab serving the reply path; after PR 2, `cargo tree -i reqwest` reports +the crate absent while the full suite passes; after PR 3, malforming any query +in the vendored `.graphql` documents fails the build rather than a runtime +request, and the suite still passes. All gates (`make check-fmt`, `make lint`, +`make test`, `make markdownlint`, `make nixie`) pass at every commit. + +## Constraints + +Hard invariants that must hold throughout implementation. Violation requires +escalation, not workarounds. + +- The public CLI behaviour must not change: identical stdout/stderr for the + same inputs, including error-message fragments asserted by tests (for example + `"status 500"`, `"body snippet:"`, and serde error paths such as + `"repository.pullRequest.reviewThreads"`). +- The environment-variable overrides `GITHUB_GRAPHQL_URL` (full GraphQL + endpoint URL) and `GITHUB_API_URL` (REST base URL) must keep working, because + every network-facing test redirects the binary to a loopback server through + them. +- Token precedence must remain: `--github-token` flag, then + `VK_GITHUB_TOKEN`, then `GITHUB_TOKEN`, then the config-file value + (`src/auth.rs`). +- GraphQL requests must keep sending `User-Agent: vk`, + `Accept: application/vnd.github+json`, and `Authorization: Bearer ` + (only when a token is present; anonymous access must keep working and keep + printing the "GitHub token not set, using anonymous API access" warning). +- The transcript feature (`VK_TRANSCRIPT` / constructor parameter) must keep + writing the same JSON-lines format consumed by + `tests/e2e/common.rs::load_transcript` and the `tests/fixtures/pr42.json` + fixture: one object per line with keys `operation`, `status`, `request`, and + `response` (response body truncated to 500 characters). +- Retry behaviour on the GraphQL path must be preserved: jittered + exponential backoff via `backon` with the transient-error classification in + `src/api/retry.rs` (retry on request errors, empty responses, HTTP 5xx, HTTP + 429, and HTML-looking bodies). The REST reply path performs no retries today + and must not gain any. +- TLS must remain rustls-based; do not introduce a native-tls or OpenSSL + dependency. +- Dependency versions must use caret requirements unless a tilde pin is + documented with a reason (see Decision Log for the octocrab pin). +- The error type `VkError` and its variants remain the error surface of + `src/api/`; octocrab's, hyper's, and `graphql_client`'s error types must be + mapped at module boundaries and never leak into public signatures. +- The exported domain types (`ReviewThread`, `ReviewComment`, + `CommentConnection`, `PageInfo`, `PullRequestReview`, `Issue`, `User`) remain + the types consumers and the printer use; generated GraphQL types stay private + behind a conversion layer. +- All commit gates pass on every commit: `make check-fmt`, `make lint`, + `make test`, and for documentation changes `make markdownlint` and + `make nixie`. +- No single source file may exceed 400 lines; module-level `//!` comments + and en-GB-oxendict spelling are required in all new code. The vendored + GraphQL schema is third-party generated data, not source, and is exempt. + +## Tolerances (exception triggers) + +- Scope: if any single PR requires editing more than 20 source files or a + net change beyond roughly 1,200 lines (excluding the vendored schema and + `.graphql` documents), stop and escalate. +- Interface: if a public API of the `vk` library crate (anything re-exported + from `src/lib.rs`) must change signature or be removed, stop and escalate — + with one pre-approved exception: PR 3 may add typed operation-execution + methods to `GraphQLClient` and deprecate (not remove) the string-based + `run_query`/`fetch_page`/`paginate_all` surface if call-site migration leaves + them unused. +- Dependencies: this plan pre-approves `octocrab` and `http` (PR 1 — + `http` is already in the tree; `reqwest::header` re-exports it, and octocrab's + `add_header` and PR 2's transport interface both need it directly), + promotion of `hyper`, `hyper-util`, `hyper-rustls`, and `http-body-util` from + dev-dependencies to runtime dependencies (PR 2), and `graphql_client` (PR 3). + Any further new direct dependency requires escalation. +- Behaviour: if preserving an asserted behaviour (error text, transcript + format, header set, retry counts) proves impossible, stop and present + options; do not weaken tests to fit. +- Iterations: if a gate still fails after three fix attempts on the same + failure, stop and escalate. +- Build time: if PR 3's schema-parsing derives push a clean `cargo build` + more than 50% above the pre-PR baseline (record the baseline first), stop and + escalate with options (group operations per derive, prune the schema). + +## Risks + +- Risk: octocrab's typed REST models may not deserialize the minimal inline + JSON bodies used by `tests/resolve.rs`, and its error mapping may not + reproduce the 404-non-fatal / other-non-2xx-fatal semantics exactly. + Severity: medium. Likelihood: medium. Mitigation: PR 1 starts with a spike; + test stub bodies may be enriched to realistic fixtures (that is + strengthening, not weakening); fall back to octocrab's raw `_post` route + (which inherits auth and base-URI middleware but leaves response handling to + `vk`) if the typed handler cannot match semantics. +- Risk: octocrab has shipped breaking changes in patch releases before + (upstream issue 899). Severity: medium. Likelihood: medium. Mitigation: pin + with a tilde requirement (`~0.54`) and record the reason in a `Cargo.toml` + comment and the architectural decision record (ADR). +- Risk: a direct hyper transport must reassemble what `reqwest` provided + for free: connection pooling, TLS configuration, total-request timeout, and + body collection. A subtle difference (for example timeout scope or TLS root + store) could change behaviour under failure. Severity: medium. Likelihood: + medium. Mitigation: the retry and timeout unit tests in + `src/api/client/tests.rs` count attempts against scripted local servers and + act as a characterization harness; PR 2 changes nothing but the transport + internals, so any drift surfaces there. Root-store choice (webpki versus + native) is recorded in the Decision Log during implementation. +- Risk: `graphql_client`'s generated response types may produce + `serde_path_to_error` paths that differ from the hand-written structs, + breaking the e2e assertion on `"repository.pullRequest.reviewThreads"`. + Severity: low. Likelihood: low (generated fields carry the same camelCase + serde renames). Mitigation: the e2e test is in the harness; if paths differ, + adjust the conversion boundary, not the test. +- Risk: GitHub's vendored schema is ~73,000 lines (~1.5 MB) and each + `#[derive(GraphQLQuery)]` re-parses it at compile time. Severity: low. + Likelihood: medium. Mitigation: benchmark before and after (see Build time + tolerance); group operations into shared documents where sensible; generated + code is small because only selected fields are generated. +- Risk: GitHub's custom scalars (`DateTime`, `URI`, `HTML`, `GitObjectID`) + need Rust type aliases in scope of each derive; a missed alias is a compile + error easily misread. Severity: low. Likelihood: high (it will arise). + Mitigation: a single shared `scalars` module + (`type DateTime = chrono::DateTime`, `type URI = String`, + `type HTML = String`) imported by every operation module. +- Risk: `paginate_all` currently injects the cursor into an untyped JSON + variables map; typed `Variables` structs break that mechanism. Severity: + medium. Likelihood: certain (by design). Mitigation: PR 3 introduces a small + `CursorVariables` trait (set the cursor on a typed variables struct) + implemented per paginated operation; written test-first. +- Risk: binary size and compile time grow while both reqwest and the hyper + stack are present (during PR 1, and PR 2 before removal). Severity: low. + Likelihood: certain but transient. Mitigation: PR 2 ends with + `cargo tree -i reqwest` failing to find the package. + +## Progress + +- [x] (2026-07-09 12:20Z) Explored codebase (client layer, consumers, test + infrastructure, docs) and researched octocrab 0.54; findings folded into this + plan. +- [x] (2026-07-09 12:40Z) ExecPlan drafted and linked from + `docs/contents.md`. +- [x] (2026-07-09 14:10Z) Scope revised after review: octocrab confined to + REST; GraphQL keeps the bespoke client on a hyper transport with + `graphql_client` codegen. `graphql_client` 0.16 researched and confirmed + transport-agnostic. +- [x] (2026-07-09 15:30Z) Stage A: ADR + `docs/adr-001-github-api-client-modernisation.md` authored and linked from + `docs/vk-design.md` and `docs/contents.md`; octocrab `~0.54` added to + `Cargo.toml` (default features off; `default-client`, `jwt-rust-crypto`, + `rustls`, `rustls-ring`, `timeout`; `retry` excluded deliberately). +- [x] (2026-07-09 17:20Z) PR 1: octocrab REST resolve path complete. + `src/resolve/rest.rs` reworked onto octocrab via the raw `_post` route with + `RestClient::new` and `post_reply` signatures and semantics preserved, + `github_client` deleted, `x-github-api-version` and `Accept` headers restored + via builder `add_header` with a direct `http` dependency; `tests/resolve.rs` + green unchanged; design doc updated; all gates green; CodeRabbit review + completed with zero findings; draft pull request opened as leynos/vk#194. +- [x] (2026-07-28) Review follow-up restored the total REST reply deadline, + strengthened raw POST coverage for the route, body, authentication, required + headers, and status handling, and reconciled the documentation findings. +- [x] (2026-08-03) Documentation review follow-up corrected the dependency + record, documentation index, Oxford spelling, and REST ownership guidance. +- [x] (2026-07-09 18:30Z) PR 2 complete: `src/api/client/transport.rs` + added with `GraphQLClient` delegating to it; reqwest removed from the graph + entirely (`cargo tree -i reqwest` finds nothing in normal, dev, and + all-features graphs); transcript-replay test `e2e_pr_42` passes; design doc + and e2e testing guide corrected; all gates green; CodeRabbit review completed + with zero findings; draft pull request opened as leynos/vk#195 (stacked on PR + 1). +- [ ] PR 3: vendored schema, `.graphql` documents, generated types behind a + conversion layer, typed pagination; raw query constants deleted; full suite + green. +- [ ] Documentation pass per PR (`docs/vk-design.md` and the e2e guide + correction in whichever PR touches it first); retrospective completed. + +## Surprises & discoveries + +- Observation: despite the branch name, no octocrab groundwork exists; this + is a from-scratch adoption. Evidence: `grep octocrab Cargo.toml src/ -r` + returns nothing. Impact: the plan treats the migration as new work, not a + continuation. +- Observation: `docs/vk-end-to-end-testing-guide.md` describes `third-wheel` + as a man-in-the-middle proxy, but the implementation only uses + `third_wheel::hyper` re-exports for a plain loopback stub server driven by + env-var endpoint overrides. Evidence: `tests/utils/mod.rs:186-192` sets + `GITHUB_GRAPHQL_URL` and `GITHUB_API_URL`; no proxy or certificate trust is + configured anywhere. Impact: only the env-var overrides need preserving; the + guide should be corrected when first touched. +- Observation: the transcript writes the raw, unredacted request payload; + redaction (`redact_sensitive`) applies only to error-context snippets. + Evidence: `src/api/client/transcript.rs` versus + `src/api/client/mod.rs:126-134`. Impact: parity is the goal; do not silently + change redaction behaviour during the migration. Flagged for a possible + follow-up outside this plan. +- Observation: octocrab 0.54 with `default-features = false` fails to + compile unless one of its JWT crypto features is enabled, even when app (JWT) + auth is unused. Evidence: `compile_error!` at octocrab's `src/lib.rs:304` + during the first gate run. Impact: the dependency carries `jwt-rust-crypto` + (pure-Rust, matching the ring/rustls posture) alongside `default-client`, + `rustls`, `rustls-ring`, and `timeout`; this is the final feature set + anticipated by the Interfaces section. +- Observation: octocrab's own GraphQL example delegates query typing to + `graphql_client`, confirming the two tools' division of labour matches this + plan's (octocrab does not provide typed GraphQL itself). Evidence: + `examples/graphql_issues.rs` in the octocrab repository. Impact: supports + confining octocrab to REST. + +## Decision log + +- Decision: adopt the three-part programme — octocrab for REST only, a + direct hyper transport for the bespoke GraphQL client, and `graphql_client` + codegen for typed queries — instead of routing all traffic through octocrab. + Rationale: the bespoke GraphQL client is heavily leveraged for + instrumentability (transcript recording, body-snippet error context, + transient-retry classification on raw bodies), all of which octocrab's + `graphql()` helper hides; octocrab's value concentrates in its typed REST + surface and maintained plumbing. This keeps the observability investment, + converges on one HTTP stack, and adds the compile-time query checking the + bespoke client always lacked. Date/Author: 2026-07-09, direction given by the + user in review of the first draft. +- Decision: deliver as three pull requests in the order REST (PR 1), + transport (PR 2), codegen (PR 3). Rationale: PR 1 is the smallest and proves + octocrab in-tree with minimal blast radius; PR 2 removes reqwest early so the + dependency win lands before the largest change; PR 3 is type-level only and + benefits from a settled transport underneath it. PRs 2 and 3 are + order-independent if circumstances change. Date/Author: 2026-07-09, planning + session. +- Decision: keep the `GraphQLClient` facade and `VkError` taxonomy + throughout; all three PRs change internals or add typed surface only. + Rationale: consumers (`src/commands.rs`, `src/review_threads.rs`, + `src/reviews.rs`, `src/issues.rs`, `src/branch_pr/`, `src/resolve/`) and + roughly twenty network tests couple to the facade, the error text, and the + env-var overrides. Date/Author: 2026-07-09, planning session. +- Decision: keep `backon` retry as the single retry authority on the + GraphQL path; build octocrab (REST) without its retry feature so the reply + path stays retry-free as today. Rationale: preserves tested attempt counts; + octocrab's retry layer cannot see GraphQL rate-limit errors (HTTP 200 with an + `errors` payload) anyway. Date/Author: 2026-07-09, planning session. +- Decision: pin octocrab with a tilde requirement (`~0.54`). Rationale: + octocrab has a documented history of breaking changes in patch releases + (upstream issue 899). AGENTS.md permits tilde pins where the reason is + documented; this is that documentation, and the ADR repeats it. Date/Author: + 2026-07-09, planning session. +- Decision: in PR 3, keep the exported domain structs and convert from + generated `ResponseData` types at a private boundary (`From` + implementations), rather than exporting generated types. Rationale: the + domain structs are public API (re-exported from `src/lib.rs`) and are + consumed by the printer and summary modules; the generated types are an + implementation detail of the wire format. Date/Author: 2026-07-09, planning + session. +- Decision: design the refitted GraphQL client (PRs 2 and 3) for cheap + future extraction into a shared crate. Rationale: the sibling project frankie + (a code-review terminal user interface (TUI), currently REST-only octocrab) + will need the GraphQL-only review-thread surface (`isResolved`, + `resolveReviewThread`) that motivated this client, and the executor + (transport, retry, transcript, typed operations, cursor pagination) is the + reusable part while query documents stay per-project. Concretely: keep + `VkError` and `vk::environment` coupling at the edges of the transport and + typed modules rather than woven through them. Extraction itself is out of + scope for this plan. Date/Author: 2026-07-09, follow-up review discussion. +- Decision: PR 1 uses octocrab's raw `_post` route for the reply, not the + typed `pulls(...).comment(id).reply(...)` route. Rationale: the typed route + funnels non-2xx responses through octocrab's `Error::GitHub`, whose display + carries neither the request path nor the HTTP status code that + `tests/resolve.rs` asserts in stderr; `_post` returns the raw + `http::Response`, keeping the 404-non-fatal / other-non-2xx-fatal mapping and + error text in `vk`'s hands. Timeout mapping: octocrab's read and write + timeouts remain configured, `connect_timeout` maps directly, and `post_reply` + additionally enforces reqwest's original total-request deadline. Date/Author: + 2026-07-09, PR 1 implementation. +- Decision: in PR 1, keep octocrab's own `User-Agent: octocrab` on the + REST path (no test asserts the REST user agent, and octocrab hard-codes its + value first in the header list), but restore the + `x-github-api-version: 2022-11-28` pin and the + `Accept: application/vnd.github+json` header via the builder's `add_header`, + using a direct `http` dependency. Rationale: the API-version pin is + documented behaviour in `docs/vk-design.md` and protects against GitHub + changing its default API version; dropping it silently would contradict the + design document. Date/Author: 2026-07-09, PR 1 implementation. +- Decision: record the programme in a new ADR, + `docs/adr-001-github-api-client-modernisation.md`. Rationale: no ADRs exist; + the bespoke-client choice was never recorded. AGENTS.md requires substantive + decisions to be captured as ADRs following + `docs/documentation-style-guide.md`. Date/Author: 2026-07-09, planning + session (path renamed from `adr-001-adopt-octocrab.md` when the scope + changed). + +## Outcomes & retrospective + +To be completed as milestones land and at the end of the work. + +## Context and orientation + +The repository is a single Rust crate (`vk`, edition 2024, minimum supported +Rust version (MSRV) 1.89) with sources under `src/` and integration tests under +`tests/`. The `Makefile` is the canonical command runner. Read `AGENTS.md` +before contributing. + +The API layer, all under `src/api/`: + +- `src/api/mod.rs` re-exports `GraphQLClient`, `Endpoint`, `Query`, `Token` + (from `client/`), `paginate` (from `pagination.rs`), and `RetryConfig` (from + `retry.rs`). +- `src/api/client/mod.rs` defines `GraphQLClient` (fields: a + `reqwest::Client`, headers, endpoint, optional transcript, and retry config) + with constructors `new` (endpoint from the `GITHUB_GRAPHQL_URL` env var, + default `https://api.github.com/graphql`), `with_endpoint`, and + `with_endpoint_retry`; methods `run_query` (POST, backon retry loop), + `fetch_page` (cursor merge), and private `execute_single_request` / + `process_graphql_response` (status handling, transcript logging, GraphQL + error surfacing, `serde_path_to_error` deserialization). +- `src/api/client/helpers.rs` builds headers (`User-Agent: vk`, `Accept`, + optional `Authorization`) and provides snippet/redaction helpers. +- `src/api/client/types.rs` defines the `Query`, `Token`, and `Endpoint` + newtypes and the `GraphQLResponse` envelope. +- `src/api/client/transcript.rs` appends one JSON line per request to the + optional transcript file. +- `src/api/client/pagination.rs` implements `paginate_all` (cursor loop, + 1,000-page cap); `src/api/pagination.rs` holds the generic `paginate` helper + and `PageInfo` handling. +- `src/api/retry.rs` defines `RetryConfig` (5 attempts, 200 ms base delay, + 30 s request timeout, jitter) and `should_retry` transient classification. + +GraphQL queries are raw string constants in `src/graphql_queries.rs` +(`THREADS_QUERY`, `COMMENT_QUERY`, `ISSUE_QUERY`, `PR_FOR_BRANCH_QUERY`) and in +`src/resolve/graphql.rs` (`RESOLVE_THREAD_MUTATION`, `REVIEW_COMMENTS_PAGE`). +Responses deserialize into per-module serde structs that mirror the GraphQL +wire shape; the exported ones (`ReviewThread`, `ReviewComment`, +`CommentConnection`, `PageInfo`, `PullRequestReview`, `Issue`, `User`) are +public API and survive all three PRs. + +The REST path: `src/resolve/rest.rs` (only compiled with +`--features unstable-rest-resolve`) builds a second `reqwest::Client` +(`github_client`) and a `RestClient` whose base URL comes from the +`GITHUB_API_URL` env var (default `https://api.github.com`). Its one operation, +`post_reply`, POSTs to +`repos/{owner}/{name}/pulls/{pull_number}/comments/{comment_id}/replies`, +treating 404 as non-fatal (warn and continue) and other non-2xx as fatal, with +no retry. + +Authentication: `src/auth.rs::resolve_github_token` implements the precedence +chain; no `gh` CLI integration exists. The token is a plain string handed to +the client constructors. + +Tests that constrain this work: + +- `src/api/client/tests.rs` spins up loopback hyper servers and constructs + clients via `with_endpoint`/`with_endpoint_retry` using endpoints without a + path; it counts retry attempts and asserts error-text fragments. +- `src/test_utils/test_http.rs` and `src/branch_pr/tests.rs` follow the + same pattern. +- `tests/utils/mod.rs::vk_cmd` runs the real binary with + `GITHUB_GRAPHQL_URL=http://{addr}/graphql`, `GITHUB_API_URL=http://{addr}`, + and `GITHUB_TOKEN=dummy`. +- `tests/auth.rs` asserts the captured `Authorization` header and the + anonymous-access warning. +- `tests/resolve.rs` asserts the exact REST path and status-code semantics. +- `tests/cli.rs` holds two insta snapshots of rendered output (client-swap + insensitive, data-shape sensitive). +- `tests/e2e/common.rs::load_transcript` and `tests/fixtures/pr42.json` + encode the transcript JSON-lines format. + +Key library facts (verified 2026-07-09): + +- octocrab 0.54.0 (MSRV 1.85) builds on hyper 1.x and tower, rustls with + the ring provider by default. `OctocrabBuilder` provides `personal_token`, + `base_uri` (applies to all routes), `add_header`, and timeouts. + `pulls(owner, repo)` exposes typed review-comment operations including + replying to a comment; the raw `_post(route, body)` method returns an + unprocessed `http::Response` as a fallback. Resolving a review thread has no + REST endpoint; the `resolveReviewThread` GraphQL mutation is the only way. +- `graphql_client` 0.16.0 (January 2026, maintained, MSRV 1.66): + `#[derive(GraphQLQuery)]` with `schema_path`/`query_path` generates + `Variables` and `ResponseData` types per operation. + `Operation::build_query(variables)` returns a `QueryBody` that serializes to + the standard `{"query", "variables", "operationName"}` envelope, and + responses are plain serde — both compose with any transport, so the bespoke + client's envelope handling, transcript, and `serde_path_to_error` diagnostics + continue to work. The reqwest features are optional and stay off. Custom + scalars used by GitHub (`DateTime`, `URI`, `HTML`, and friends) need type + aliases in scope of the derive. +- GitHub's public GraphQL schema is published at + `https://docs.github.com/public/fpt/schema.docs.graphql` (~73,000 lines, ~1.5 + MB SDL); vendoring it whole is the established practice (octocrab and + graphql_client both do so in their examples). + +## Plan of work + +Stage A (lands with PR 1): author +`docs/adr-001-github-api-client-modernisation.md` following the ADR template in +`docs/documentation-style-guide.md`. Status: Accepted. Context: two bespoke +reqwest clients, an undocumented prior decision, and no compile-time query +checking. Options Considered: keep bespoke; route everything through octocrab; +the adopted three-part split; `graphql_client` plus reqwest without octocrab. +Decision Outcome: the three-part programme, with the octocrab tilde pin and its +rationale. Migration Plan: reference this ExecPlan. Reference the ADR from +`docs/vk-design.md` and list it in `docs/contents.md`. + +### PR 1 — octocrab for the REST resolve path + +Add the dependency (final feature list recorded here after the spike; the +`retry` feature is deliberately excluded so no retry layer exists, matching +today's retry-free reply path): + +```toml +# Tilde pin: octocrab has shipped breaking changes in patch releases +# (upstream issue 899); widen only after review. +octocrab = { version = "~0.54", default-features = false, features = [ + "default-client", "jwt-rust-crypto", "rustls", "rustls-ring", "timeout", +] } +``` + +Rework `src/resolve/rest.rs` behind its existing `pub(crate)` surface: +`RestClient::new(token, api, timeout, connect_timeout)` builds an +`octocrab::Octocrab` via `OctocrabBuilder` (`personal_token` when the token is +non-empty, `base_uri` from the existing parameter → `GITHUB_API_URL` → default +resolution, and connect/read/write timeouts from the existing arguments). +`post_reply` additionally enforces the original total-request deadline and uses +octocrab's raw `_post` route with the hand-built +`/repos/{owner}/{name}/pulls/{pull_number}/comments/{comment_id}/replies` +endpoint. It preserves the semantics `tests/resolve.rs` asserts: the exact +request path, a 404 mapped to a warning and success, and every other non-2xx +response mapped to a fatal `VkError` containing the route and status. Delete +`github_client` and the hand-rolled header constants. Acceptance: +`cargo test --features unstable-rest-resolve` and `make lint` (all-features) +pass; `tests/resolve.rs` unchanged in what it asserts. + +### PR 2 — hyper transport; remove reqwest + +Add a private transport module `src/api/client/transport.rs` owning a pooled +hyper client (`hyper_util::client::legacy::Client` with a `hyper_rustls` HTTPS +connector) and exposing one method mirroring `execute_single_request`'s +contract: take the JSON payload and headers, POST to the configured endpoint +URL (kept whole — no base/route splitting is needed because the bespoke client +posts to one absolute URL), enforce the total-request timeout via +`tokio::time::timeout` (mirroring reqwest's `.timeout()` scope: connect plus +body), collect the body with `http-body-util`, and return +`HttpResponse { status, body }` or a `VkError::RequestContext` with today's +context text. Promote `hyper`, `hyper-util`, `hyper-rustls`, and +`http-body-util` to runtime dependencies; record the chosen TLS root store +(webpki roots, matching the current `rustls-tls` posture) in the Decision Log. +Switch `execute_single_request` to delegate to the transport; the transcript +call, status check, snippets, retry loop, and headers logic are untouched. +Remove `reqwest` from `Cargo.toml` and replace any residual `reqwest::header` +imports with the `http` crate equivalents (they are the same types +re-exported). Acceptance: `make test` passes without any test edits; +`cargo tree -i reqwest` reports the package is not found. + +### PR 3 — typed GraphQL via graphql_client codegen + +Record the build-time baseline first (`cargo build` from clean, wall clock). +Vendor the schema at `graphql/schema.docs.graphql` with a short +`graphql/README.md` noting the source URL and refresh procedure. Move each +operation into a `.graphql` document under `graphql/` (thread listing, comment +paging, issue lookup, PR-for-branch, review-comments paging, the +`resolveReviewThread` mutation, and reviews listing), grouping related +operations per file to amortize the per-derive schema parse. Create a shared +scalars module (`type DateTime = chrono::DateTime`, +`type URI = String`, `type HTML = String`, extended as compile errors direct). +Derive `GraphQLQuery` per operation with +`response_derives = "Debug, Clone, PartialEq"`. + +Client surface: add a typed method to `GraphQLClient` (see Interfaces) that +takes an operation's `Variables`, builds the envelope with `build_query`, and +reuses the existing POST, transcript, retry, and `serde_path_to_error` +machinery; the codegen'd operation name replaces the string-sniffing +`operation_name` helper on this path. For pagination, introduce a +`CursorVariables` trait (set/replace the cursor on a typed variables struct) +and a typed `paginate_operation` counterpart to `paginate_all`, written +test-first against the existing scripted stub servers. + +Conversion boundary: implement `From` (or dedicated mapper +functions where `From` is awkward) producing the existing exported domain +structs, so `src/commands.rs`, the printer, and the summary modules do not +change. Migrate call sites module by module (review_threads, reviews, issues, +branch_pr, resolve/graphql), deleting each raw query constant as its operation +moves; `src/graphql_queries.rs` is deleted at the end. Red-green applies to the +new units (the `CursorVariables` trait, each conversion): write the rstest +cases first against fixture JSON and observe the expected failure before +implementing. Acceptance: full suite green; introducing a deliberate typo into +any `.graphql` document fails `cargo build` (demonstrate once, then revert); +build-time delta within tolerance. + +Documentation lands with each PR: `docs/vk-design.md` networking and resolve +sections (PRs 1–2), the third-wheel correction in +`docs/vk-end-to-end-testing-guide.md` (first PR that touches test docs), the +GraphQL section rewrite and schema-refresh procedure (PR 3), and +`docs/contents.md` whenever a document is added. + +## Concrete steps + +All commands run from the repository root +(`/home/leynos/Projects/vk.worktrees/adopt-octocrab`). Long outputs go through +`tee` to a log file for review, for example: + +```shell +make test 2>&1 | tee "/tmp/test-vk-adopt-octocrab.out" +``` + +Per-milestone sequence (repeat for each commit within each PR): + +1. Make the edits described in Plan of work. +2. `make check-fmt` (apply `make fmt` first if needed). +3. `make lint` — expect zero warnings; clippy runs with `-D warnings` and + `--all-features`, so the REST feature code is always linted. +4. `make test` — expect all tests to pass. The suite currently passes on a + clean checkout; any new failure is caused by the change under test. +5. For documentation edits: `make markdownlint` and `make nixie`. +6. Commit with an imperative-mood message; update the `Progress` section of + this plan in the same commit. + +Useful focused commands: + +```shell +cargo test --features unstable-rest-resolve --test resolve 2>&1 | tee /tmp/test-vk-adopt-octocrab.out +cargo test api::client 2>&1 | tee /tmp/test-vk-adopt-octocrab.out +cargo test --test e2e -- --ignored e2e_pr_42 2>&1 | tee /tmp/test-vk-adopt-octocrab.out +cargo tree -i reqwest # PR 2 exit: expect "package … not found" +cargo tree -d # check duplicate transitive versions +curl -L https://docs.github.com/public/fpt/schema.docs.graphql -o graphql/schema.docs.graphql +``` + +Expected shape of a passing test run (counts will drift as tests are added): + +```text +test result: ok. 0 failed; finished in … +``` + +## Validation and acceptance + +The programme is behaviour-preserving, so the existing suite is the primary +acceptance harness: every commit ends with `make check-fmt`, `make lint`, and +`make test` green with no test weakened or deleted. Enriching REST stub bodies +toward realistic fixtures (PR 1) and adding new tests is permitted; loosening +assertions is not. + +Red-green-refactor evidence is required for the genuinely new units: + +- PR 2: if any helper with observable logic is added to the transport + (beyond direct delegation), specify it with a failing test first; otherwise + the characterization harness (retry counts, error text, transcript replay) + stands in, and that substitution is recorded here. +- PR 3: the `CursorVariables` trait, `paginate_operation`, and each + `ResponseData` → domain conversion get rstest cases against fixture JSON + written before the implementation; run the focused test, observe the expected + failure, implement, observe the pass, then run the wider gates. + +End-to-end observations per PR: + +- PR 1: `cargo test --features unstable-rest-resolve --test resolve` + passes; a manual run against a loopback stub shows the reply POST hitting + `repos/{owner}/{name}/pulls/{n}/comments/{id}/replies` exactly as on `main`. +- PR 2: `cargo tree -i reqwest` fails to find the package; the ignored + transcript-replay test (`cargo test --test e2e -- --ignored e2e_pr_42`) + passes, proving transcript format parity; with no server listening, running + `cargo run -- pr ` with `GITHUB_TOKEN=dummy` and + `GITHUB_GRAPHQL_URL=http://127.0.0.1:1/graphql` fails with a + connection-refused `RequestContext` error naming the operation, exactly as on + `main`. +- PR 3: a deliberate field typo in a `.graphql` document turns into a + compile error (demonstrated once and reverted); the insta snapshots in + `tests/cli.rs` are byte-identical. + +Quality criteria: all gates above; documentation gates (`make markdownlint`, +`make nixie`) for the ADR, design-doc, and `graphql/README.md` changes; +build-time delta within the stated tolerance for PR 3. + +## Idempotence and recovery + +Every step is an ordinary source edit gated by the test suite; re-running any +command is safe. Each PR (and each commit within it) is independent, so a +failed attempt is abandoned with `git restore` / `git reset --hard HEAD` +without affecting completed work. The schema download is re-runnable and +version-controlled once vendored. Nothing in this plan touches user data, CI +configuration, or anything outside the repository; the only files written +outside it are `tee` logs under `/tmp`. + +## Artifacts and notes + +Record here, as milestones complete: the final octocrab feature set, the +`cargo tree -d` duplicate report, the clean-build baseline and post-PR 3 delta, +a sample transcript line proving format parity, and the closing test counts. + +## Interfaces and dependencies + +Dependencies to add: + +```toml +# PR 1. Tilde pin: octocrab has shipped breaking changes in patch +# releases (upstream issue 899); widen only after review. +# jwt-rust-crypto is mandatory under default-features = false. +octocrab = { version = "~0.54", default-features = false, features = [ + "default-client", "jwt-rust-crypto", "rustls", "rustls-ring", "timeout", +] } + +# PR 2 (promoted from dev-dependencies; align versions with the lockfile) +hyper = "1" +hyper-util = { version = "0.1", features = ["client", "client-legacy", "http1", "tokio"] } +hyper-rustls = { version = "0.27", features = ["webpki-roots", "http1", "ring"] } +http-body-util = "0.1" + +# PR 3 +graphql_client = "0.16" +``` + +Dependency to remove (PR 2): `reqwest`. + +In `src/api/client/transport.rs` (PR 2, new, private to `client`): + +```rust +/// Owns the pooled hyper client used for GraphQL requests. +pub(super) struct Transport { /* hyper_util legacy client + HTTPS connector */ } + +impl Transport { + pub(super) fn new() -> Result; + + /// POST `payload` to `endpoint` with `headers`, honouring `timeout` + /// across the whole request, returning status and body. + pub(super) async fn post_json( + &self, + endpoint: &Endpoint, + headers: &http::HeaderMap, + payload: &serde_json::Value, + timeout: std::time::Duration, + ) -> Result; +} +``` + +In `src/api/client/mod.rs` (PR 3, added; string-based methods retained until +unused, then deprecated per the Interface tolerance): + +```rust +/// Execute a codegen'd GraphQL operation using this client. +pub async fn run_operation( + &self, + variables: Q::Variables, +) -> Result; +``` + +In `src/api/pagination.rs` or a sibling module (PR 3): + +```rust +/// Implemented by generated Variables types for paginated operations. +pub(crate) trait CursorVariables { + fn set_cursor(&mut self, cursor: Option); +} +``` + +Unchanged public surface (re-exported from `src/lib.rs` and `src/api/`): +`GraphQLClient` and its constructors, `Endpoint`, `Token`, `Query`, +`RetryConfig`, `paginate`, `PageInfo`, `CommentConnection`, `ReviewThread`, +`ReviewComment`, `PullRequestReview`, `Issue`, `User`, and `VkError`. + +In `src/resolve/rest.rs` (PR 1), `RestClient` keeps its `pub(crate)` +construction and the `post_reply` free function; only the inner client type +changes from `reqwest::Client` to `octocrab::Octocrab`. + +## Revision note + +2026-07-09: initial draft proposed octocrab as the transport for both GraphQL +and REST. + +2026-07-09 (second revision): scope changed on user direction after review of +the first draft. octocrab is now confined to the REST resolve path; the bespoke +GraphQL client is retained for its observability machinery and moves from +reqwest to a direct hyper transport; `graphql_client` codegen adds compile-time +query checking. Delivery is three pull requests. The planned ADR was renamed +from `adr-001-adopt-octocrab.md` to +`adr-001-github-api-client-modernisation.md`. Constraints, tolerances, risks, +decisions, and interfaces were rewritten to match. PR 1 is complete; PRs 2 and +3 remain as authorized future work. diff --git a/docs/vk-design.md b/docs/vk-design.md index 311b97f..27cd63c 100644 --- a/docs/vk-design.md +++ b/docs/vk-design.md @@ -49,20 +49,23 @@ even when multiple comments reference the same code. - **Resolve threads**: `vk resolve ` resolves the thread via the `resolveReviewThread` GraphQL mutation. When compiled with the `unstable-rest-resolve` feature and a reply message is supplied (`-m`), it - posts a reply via the REST API before resolving. If the REST reply fails the - command aborts without calling `resolveReviewThread`, does not retry, and - does not apply backoff; missing comments return a warning and continue. The - resolver pages through the pull request's `reviewComments` connection using - typed `serde` structures (see `src/resolve/graphql.rs`), matching the - requested `databaseId` and extracting the owning thread identifier. - Pagination detects repeated or non-advancing cursors and aborts with an error - rather than looping indefinitely. This subcommand requires `GITHUB_TOKEN` - with sufficient scopes (resolving threads and posting replies require - `repo`); if absent, it aborts rather than performing anonymous calls. - Resolution steps emit debug spans via `tracing` to aid diagnostics; the - binary initialises `tracing_subscriber::fmt()` with an environment filter, so - running with `RUST_LOG=vk=debug` (or a more specific filter) surfaces the - spans on stderr. + posts a reply via the REST API before resolving. The REST reply is served + through [octocrab](https://docs.rs/octocrab) (see + [ADR 001](adr-001-github-api-client-modernisation.md)), pinning + `x-github-api-version: 2022-11-28`; status handling stays in `vk` via + octocrab's raw request route. If the REST reply fails the command aborts + without calling `resolveReviewThread`, does not retry, and does not apply + backoff; missing comments return a warning and continue. The resolver pages + through the pull request's `reviewComments` connection using typed `serde` + structures (see `src/resolve/graphql.rs`), matching the requested + `databaseId` and extracting the owning thread identifier. Pagination detects + repeated or non-advancing cursors and aborts with an error rather than + looping indefinitely. This subcommand requires `GITHUB_TOKEN` with sufficient + scopes (resolving threads and posting replies require `repo`); if absent, it + aborts rather than performing anonymous calls. Resolution steps emit debug + spans via `tracing` to aid diagnostics; the binary initializes + `tracing_subscriber::fmt()` with an environment filter, so running with + `RUST_LOG=vk=debug` (or a more specific filter) surfaces the spans on stderr. - **Configurable timeouts**: `--http-timeout` and `--connect-timeout` override the default 10 s request and 5 s connection limits for REST replies. @@ -79,6 +82,11 @@ even when multiple comments reference the same code. ## Architecture +The modernization of `vk`'s GitHub API access — octocrab for the REST resolve +path, hyper transport for the bespoke GraphQL client, and `graphql_client` +codegen for typed queries — is governed by +[ADR 001](adr-001-github-api-client-modernisation.md). + The code centres on these printing helpers: @@ -146,6 +154,14 @@ merges an optional cursor into a variables map and rejects non-object input upfront. The `paginate_all` helper loops until `PageInfo` indicates completion, discarding any items fetched before an error occurs. +Requests are sent by a private hyper-based transport +([src/api/client/transport.rs](../src/api/client/transport.rs)) that uses +rustls with Mozilla's webpki roots; this replaces the former `reqwest` client. +The total-request timeout spans both sending the request and collecting the +response body. Unlike the previous client, this transport supports neither +system proxies (`HTTP(S)_PROXY`) nor HTTP redirects, neither of which the +GraphQL path uses. These transport choices are governed by ADR 001. + ## Utility Splitting the printing logic into reusable `write_*` functions enables testing diff --git a/docs/vk-end-to-end-testing-guide.md b/docs/vk-end-to-end-testing-guide.md index 8ea7245..22d2a15 100644 --- a/docs/vk-end-to-end-testing-guide.md +++ b/docs/vk-end-to-end-testing-guide.md @@ -76,6 +76,18 @@ integration (CI) pipeline. ### Architectural Overview of the Chosen Testing Stack +> **Correction — how the harness actually works.** This guide was originally +> written around a Man-in-the-Middle (MITM) proxy design, and much of the +> discussion below still describes that aspirational approach. The implemented +> harness does not proxy or intercept traffic and performs no TLS +> interception. Instead, the helpers in `tests/utils/mod.rs` (`start_mitm`, +> `start_mitm_capture`, and `vk_cmd`) start plain hyper loopback stub servers +> and point the `vk` binary straight at them through the `GITHUB_GRAPHQL_URL` +> and `GITHUB_API_URL` environment variables. The `third-wheel` crate is used +> only for the hyper types it re-exports in some unit-test helpers; it is not +> run as a proxy. Treat MITM-proxy passages that follow as background context +> rather than a description of the current implementation. + To achieve a hermetic and comprehensive E2E testing environment for `vk`, this guide employs a carefully selected "testing triad" of Rust crates. Each component serves a distinct and complementary purpose, working in concert to @@ -88,16 +100,17 @@ provide a holistic solution. for success or failure and inspecting the contents of the standard error (`stderr`) stream for user-facing error messages.[^8] -- `third-wheel`: This crate provides the critical network isolation layer. It - is a Man-in-the-Middle (MITM) proxy, written in Rust, that can be embedded - directly within the test harness.[^11] Its role is to intercept all outgoing - HTTP requests that - - `vk` attempts to make to the GitHub GraphQL API. Instead of allowing these - requests to reach the internet, `third-wheel` captures them and returns - controlled, predefined responses from local fixture files. This makes the - tests completely independent of the network and ensures that the API's - behaviour is deterministic for every test run. +- `third-wheel`: In the implemented harness this crate is not run as a proxy. + It is a Man-in-the-Middle (MITM) proxy, written in Rust,[^11] but the tests + use only the hyper types it re-exports in some unit-test helpers. Network + isolation is instead achieved by running plain hyper loopback stub servers + (see `tests/utils/mod.rs`) and pointing `vk` at them via the + `GITHUB_GRAPHQL_URL` and `GITHUB_API_URL` environment variables. Rather than + intercepting traffic, these stub servers receive the binary's requests + directly and return controlled, predefined responses. This makes the tests + completely independent of the network and ensures that the API's behaviour is + deterministic for every test run. The MITM-proxy descriptions that follow are + retained as background on the original design. - `insta`: This crate is the output verifier, specialized for snapshot testing. Given that `vk` produces complex, styled terminal output via `termimad`, @@ -111,8 +124,8 @@ provide a holistic solution. approach is vastly superior to manual string assertions for validating rich UIs.[^14] -Together, these three tools form a powerful and cohesive system. `assert_cmd` -drives the application, `third-wheel` controls its external environment, and +Together, these tools form a powerful and cohesive system. `assert_cmd` drives +the application, loopback stub servers control its external environment, and `insta` verifies its final output. This architecture enables the creation of a test suite that is robust, maintainable, and provides the highest degree of confidence in the correctness of the `vk` CLI. @@ -259,34 +272,51 @@ this test provides high confidence that: With this foundation in place, the next step is to introduce the complexity of API mocking. -## Deterministic API Behaviour via Mocking with third-wheel +## Deterministic API behaviour via loopback stub servers + +> **Note:** The implemented harness uses loopback stub servers, not a MITM +> proxy. This section preserves the original MITM-proxy design as background; +> the stub-server approach that the tests actually use is described at the end +> of the section and in `tests/utils/mod.rs`. To create a truly hermetic test suite for a network-dependent application like `vk`, it is imperative to isolate it from the actual network. Making live calls to the GitHub API during tests is untenable; it would make the tests slow, flaky (dependent on network conditions and API availability), and would require valid authentication tokens, posing a security risk in CI environments. The -solution is to mock the API. +solution is to serve mock responses locally. -### The Role of a Man-in-the-Middle (MITM) Proxy in Testing +### Background: the role of a Man-in-the-Middle (MITM) proxy in testing + +The following describes the aspirational MITM-proxy design and is retained as +background rather than a description of the implemented harness. While one could attempt to mock the `GraphQLClient` struct within `vk`'s source code, this approach has significant drawbacks for E2E testing. It would require modifying the application code specifically for testing (e.g., with `#[cfg(test)]` attributes), which moves away from true black-box testing. -A superior approach is to use a Man-in-the-Middle (MITM) proxy. This technique -involves placing a server *between* the application under test and the real API +One design uses a Man-in-the-Middle (MITM) proxy. This technique involves +placing a server *between* the application under test and the real API endpoint. This proxy transparently intercepts all outgoing network traffic. For testing, this allows us to capture requests intended for `api.github.com` and return a controlled, deterministic response without the request ever leaving the local machine. This method requires zero changes to the `vk` source code, preserving the integrity of the black-box testing model. -The `third-wheel` crate is ideal for this purpose because it is a lightweight -MITM proxy written in Rust.[^11] This allows it to be embedded and controlled -programmatically from within the test code itself, eliminating the complexity -of managing a separate, external proxy process.[^11] +The `third-wheel` crate was originally chosen for this purpose because it is a +lightweight MITM proxy written in Rust.[^11] + +### What the implemented harness does instead + +Rather than proxying traffic, the tests point `vk` directly at a local stub +server. The helpers in `tests/utils/mod.rs` (`start_mitm` and +`start_mitm_capture`, whose names are historical) bind a plain hyper server to +a loopback port and dispatch each request to a shared response handler. The +`vk_cmd` helper then sets the `GITHUB_GRAPHQL_URL` and `GITHUB_API_URL` +environment variables so the binary sends its requests to that stub server. No +proxying or TLS interception occurs, and `third-wheel` is present only for the +hyper types it re-exports in some unit-test helpers. ### Step-by-Step: Embedding the third-wheel Mock Server diff --git a/src/api/client/helpers.rs b/src/api/client/helpers.rs index 8b58779..9ff5f87 100644 --- a/src/api/client/helpers.rs +++ b/src/api/client/helpers.rs @@ -1,6 +1,6 @@ //! Helper utilities for GraphQL request handling. -use reqwest::header::{ACCEPT, AUTHORIZATION, HeaderMap, HeaderValue, USER_AGENT}; +use http::header::{ACCEPT, AUTHORIZATION, HeaderMap, HeaderValue, USER_AGENT}; use serde_json::Value; use tracing::warn; @@ -150,7 +150,7 @@ mod tests { GraphQLError, Token, build_headers, handle_graphql_errors, operation_name, payload_snippet, snippet, }; - use reqwest::header::{ACCEPT, AUTHORIZATION, USER_AGENT}; + use http::header::{ACCEPT, AUTHORIZATION, USER_AGENT}; use rstest::rstest; use serde_json::json; diff --git a/src/api/client/mod.rs b/src/api/client/mod.rs index 1024000..f7bce41 100644 --- a/src/api/client/mod.rs +++ b/src/api/client/mod.rs @@ -4,10 +4,13 @@ mod helpers; mod http; mod pagination; mod transcript; +mod transport; mod types; use backon::Retryable; -use reqwest::header::HeaderMap; +// `::http` disambiguates the extern crate from this module's own `http` +// submodule, which would otherwise shadow it. +use ::http::header::HeaderMap; use serde::de::DeserializeOwned; use serde_json::{Value, json}; use std::borrow::Cow; @@ -20,9 +23,10 @@ use vk::environment; use self::helpers::{ BODY_SNIPPET_LEN, VALUE_SNIPPET_LEN, build_headers, handle_graphql_errors, operation_name, - payload_snippet, snippet, + snippet, }; use self::http::HttpResponse; +use self::transport::Transport; use self::types::GraphQLResponse; use super::retry::{RetryConfig, build_retry_builder, should_retry}; @@ -36,7 +40,7 @@ mod tests; /// The client handles authentication headers and optional request /// transcription for debugging. pub struct GraphQLClient { - client: reqwest::Client, + transport: Transport, headers: HeaderMap, endpoint: Endpoint, transcript: Option>>, @@ -104,7 +108,7 @@ impl GraphQLClient { .map_err(|e| VkError::Io(Box::new(e)))?; let headers = build_headers(&token)?; Ok(Self { - client: reqwest::Client::new(), + transport: Transport::new()?, headers, endpoint, transcript, @@ -123,50 +127,29 @@ impl GraphQLClient { payload: &serde_json::Value, operation: &str, ) -> Result { - let snip = payload_snippet(payload); - let make_ctx = |status: Option| { - let base = format!("operation {operation}; {snip}"); - match status { - Some(s) => format!("{base}; status {s}"), - None => base, - } - .boxed() - }; - - let response = self - .client - .post(self.endpoint.as_str()) - .headers(self.headers.clone()) - .json(payload) - .timeout(self.retry.request_timeout) - .send() - .await - .map_err(|e| VkError::RequestContext { - context: make_ctx(None), - source: e.into(), - })?; - let status = response.status(); - let status_u16 = status.as_u16(); - let status_err = response.error_for_status_ref().err(); - let body = response.text().await.map_err(|e| VkError::RequestContext { - context: make_ctx(Some(status_u16)), - source: e.into(), - })?; - let resp = HttpResponse { - status: status_u16, - body, - }; + let resp = self + .transport + .post_json( + &self.endpoint, + &self.headers, + payload, + self.retry.request_timeout, + ) + .await?; self.log_transcript(payload, operation, &resp); - if !(200..300).contains(&status_u16) { - let source: Box = match status_err { - Some(e) => Box::new(e), - None => Box::new(std::io::Error::other(format!( - "unexpected status {status_u16} without reqwest error" - ))), - }; + if !(200..300).contains(&resp.status) { + // The transport surfaces every completed HTTP response, so a + // non-2xx status is classified here. reqwest gave this a + // status-specific source error via `error_for_status_ref`; an + // `io::Error` carrying the same status reproduces the displayed + // information without the reqwest dependency. + let source: Box = Box::new(std::io::Error::other( + format!("HTTP status {}", resp.status), + )); return Err(VkError::RequestContext { context: format!( - "HTTP status {status_u16} | body snippet: {}", + "HTTP status {} | body snippet: {}", + resp.status, snippet(&resp.body, BODY_SNIPPET_LEN) ) .boxed(), diff --git a/src/api/client/transport.rs b/src/api/client/transport.rs new file mode 100644 index 0000000..bf7a16f --- /dev/null +++ b/src/api/client/transport.rs @@ -0,0 +1,176 @@ +//! Direct hyper transport for GraphQL requests. +//! +//! Replaces the former reqwest client with a pooled `hyper_util` legacy client +//! layered over a `hyper_rustls` HTTPS connector. This reassembles what reqwest +//! provided for free — connection pooling, rustls TLS, a total-request timeout, +//! and body collection — while leaving the surrounding transcript, retry, and +//! error-context machinery in [`super::GraphQLClient`] untouched. +//! +//! Deliberate behavioural differences from the reqwest client it replaces: +//! +//! - System proxies are not honoured. reqwest read `HTTP(S)_PROXY` from the +//! environment by default; a bare hyper client does not, and this transport +//! intentionally does not restore that. GraphQL traffic to GitHub (or a +//! loopback test server) does not traverse a proxy in any supported +//! deployment, so this is an accepted, untested blind spot. +//! - Redirects are not followed. reqwest followed up to ten redirects by +//! default; the GraphQL POST target never issues one in practice, so a 3xx +//! would surface as a non-2xx error exactly like any other unexpected status. + +use std::time::Duration; + +use http::header::CONTENT_TYPE; +use http::{HeaderMap, HeaderValue, Method, Request, Uri}; +use http_body_util::{BodyExt, Full}; +use hyper::body::Bytes; +use hyper_rustls::HttpsConnector; +use hyper_util::client::legacy::Client; +use hyper_util::client::legacy::connect::HttpConnector; +use hyper_util::rt::TokioExecutor; +use serde_json::Value; + +use super::helpers::{operation_name, payload_snippet, snippet}; +use super::http::HttpResponse; +use super::types::Endpoint; +use crate::VkError; +use crate::boxed::BoxedStr; + +/// Owns the pooled hyper client used for GraphQL requests. +pub(super) struct Transport { + client: Client, Full>, +} + +impl Transport { + /// Build a transport with a pooled client over an HTTPS/HTTP connector. + /// + /// The connector serves both `https://` (production) and `http://` (the + /// loopback stub servers used by the test suite) URLs. Its root store is + /// Mozilla's webpki roots with the ring crypto provider, matching reqwest's + /// former `rustls-tls` feature. + /// + /// # Errors + /// + /// Returns a [`VkError`] to preserve a fallible construction contract for a + /// future extraction into a shared crate, where TLS provider selection may + /// become fallible; the current webpki/ring setup cannot fail. + #[expect( + clippy::unnecessary_wraps, + reason = "fallible signature is part of the documented transport contract; \ + webpki-roots setup happens to be infallible today" + )] + pub(super) fn new() -> Result { + let connector = hyper_rustls::HttpsConnectorBuilder::new() + .with_webpki_roots() + .https_or_http() + .enable_http1() + .build(); + let client = Client::builder(TokioExecutor::new()).build(connector); + Ok(Self { client }) + } + + /// POST `payload` to `endpoint` with `headers`, honouring `timeout` across + /// the whole request, returning the response status and body. + /// + /// The `timeout` bounds the entire exchange (connection, send, and body + /// collection), mirroring reqwest's `.timeout()` scope. A timeout maps to a + /// [`VkError::RequestContext`], which the retry classifier treats as + /// transient, exactly as a reqwest timeout was treated. Transport and + /// body-collection failures reproduce the same context strings the reqwest + /// client built. + /// + /// # Errors + /// + /// Returns a [`VkError::RequestContext`] if the endpoint URL is invalid, the + /// request cannot be built or sent, the request times out, or the response + /// body cannot be collected. + pub(super) async fn post_json( + &self, + endpoint: &Endpoint, + headers: &HeaderMap, + payload: &Value, + timeout: Duration, + ) -> Result { + // Reconstruct the same request-context strings the reqwest client + // produced: the operation name (or a query snippet fallback) and a + // redacted payload snippet. Derived from the payload so this transport + // stays self-contained behind its fixed signature. + let snip = payload_snippet(payload); + let query = payload.get("query").and_then(Value::as_str).unwrap_or(""); + let operation = operation_name(query).map_or_else(|| snippet(query, 64), str::to_string); + let make_ctx = |status: Option| { + let base = format!("operation {operation}; {snip}"); + // Disambiguate from `BodyExt::boxed`, which is also in scope. + BoxedStr::boxed(match status { + Some(s) => format!("{base}; status {s}"), + None => base, + }) + }; + + let request = build_request(endpoint, headers, payload).map_err(|source| { + VkError::RequestContext { + context: make_ctx(None), + source, + } + })?; + + let exchange = async { + let response = + self.client + .request(request) + .await + .map_err(|e| VkError::RequestContext { + context: make_ctx(None), + source: Box::new(e), + })?; + let status = response.status().as_u16(); + let collected = + response + .into_body() + .collect() + .await + .map_err(|e| VkError::RequestContext { + context: make_ctx(Some(status)), + source: Box::new(e), + })?; + // Lossily decode, matching reqwest's UTF-8 text handling; decoding + // never fails, so only a collection error can surface above. + let body = String::from_utf8_lossy(&collected.to_bytes()).into_owned(); + Ok::(HttpResponse { status, body }) + }; + + match tokio::time::timeout(timeout, exchange).await { + Ok(result) => result, + Err(_elapsed) => Err(VkError::RequestContext { + context: make_ctx(None), + source: Box::new(std::io::Error::new( + std::io::ErrorKind::TimedOut, + format!("request timed out after {timeout:?}"), + )), + }), + } + } +} + +/// Boxed error alias for the low-level request-construction failures. +type BoxError = Box; + +/// Build the POST request, serialising `payload` as a JSON body and applying +/// `headers` plus a `Content-Type: application/json` header. +/// +/// Failures (an unparsable endpoint URL or payload serialisation) are returned +/// as boxed errors for the caller to wrap with the appropriate context. +fn build_request( + endpoint: &Endpoint, + headers: &HeaderMap, + payload: &Value, +) -> Result>, BoxError> { + let uri: Uri = endpoint.as_str().parse()?; + let body = Bytes::from(serde_json::to_vec(payload)?); + let mut request = Request::new(Full::new(body)); + *request.method_mut() = Method::POST; + *request.uri_mut() = uri; + let map = request.headers_mut(); + *map = headers.clone(); + map.insert(CONTENT_TYPE, HeaderValue::from_static("application/json")); + Ok(request) +} diff --git a/src/api/retry.rs b/src/api/retry.rs index 5d2bdab..bbdea83 100644 --- a/src/api/retry.rs +++ b/src/api/retry.rs @@ -58,8 +58,9 @@ pub fn build_retry_builder(config: RetryConfig) -> ExponentialBuilder { } /// Determines whether a `VkError` is transient and should be retried. -/// Returns `true` for network errors (`VkError::RequestContext`, -/// `VkError::Request`), empty responses (`VkError::EmptyResponse`), and for +/// Returns `true` for network errors (`VkError::RequestContext`, which the +/// hyper transport uses for connection failures, timeouts, and body-read +/// errors), empty responses (`VkError::EmptyResponse`), and for /// `VkError::BadResponseSerde` errors only when /// `is_transient_serde_error(status, snippet)` returns true (indicating /// server-side or rate-limit failures). All other `VkError` variants return @@ -78,9 +79,7 @@ pub fn build_retry_builder(config: RetryConfig) -> ExponentialBuilder { /// ``` pub fn should_retry(err: &VkError) -> bool { match err { - VkError::RequestContext { .. } | VkError::Request(_) | VkError::EmptyResponse { .. } => { - true - } + VkError::RequestContext { .. } | VkError::EmptyResponse { .. } => true, VkError::BadResponseSerde { status, snippet, .. } => is_transient_serde_error(*status, snippet), diff --git a/src/main.rs b/src/main.rs index fc4f42b..6757047 100644 --- a/src/main.rs +++ b/src/main.rs @@ -100,8 +100,6 @@ type SharedConfigError = Arc; pub enum VkError { #[error("unable to determine repository")] RepoNotFound, - #[error("request failed: {0}")] - Request(#[from] Box), #[error("request failed when running {context}: {source}")] RequestContext { context: Box, @@ -160,7 +158,6 @@ macro_rules! boxed_error_from { }; } -boxed_error_from!(reqwest::Error, Request); boxed_error_from!(std::io::Error, Io); impl From for VkError { diff --git a/src/resolve/rest.rs b/src/resolve/rest.rs index 71e545a..2ff3994 100644 --- a/src/resolve/rest.rs +++ b/src/resolve/rest.rs @@ -1,9 +1,15 @@ //! REST helpers for replying to review comments. +//! +//! The reply path is served by [`octocrab`], whose builder supplies the +//! authentication and base-URI plumbing previously hand-rolled on `reqwest`. +//! Only the raw `_post` route is used, so status-code handling stays under +//! `vk`'s control: a 404 is non-fatal (warn and continue), and any other +//! non-2xx status is fatal. use super::CommentRef; use crate::{VkError, boxed::BoxedStr}; -use reqwest::header::{ACCEPT, AUTHORIZATION, HeaderMap, HeaderName, HeaderValue, USER_AGENT}; -use reqwest::{StatusCode, Url}; +use http::header::{ACCEPT, HeaderName}; +use octocrab::Octocrab; use serde_json::json; #[cfg(test)] use std::sync::atomic::{AtomicUsize, Ordering}; @@ -11,71 +17,33 @@ use std::time::Duration; use tracing::warn; use vk::environment; -/// Build an authenticated client with GitHub headers. -/// -/// Returns [`VkError::RequestContext`] when the client cannot be built. -/// -/// # Examples -/// -/// ```ignore -/// # use crate::resolve::rest::github_client; -/// use std::time::Duration; -/// let client = github_client("token", Duration::from_secs(10), Duration::from_secs(5))?; -/// # Ok::<(), VkError>(()) -/// ``` -pub(crate) fn github_client( - token: &str, - timeout: Duration, - connect_timeout: Duration, -) -> Result { - let mut headers = HeaderMap::new(); - let parse_value = |value: &str, context: &str| -> Result { - HeaderValue::from_str(value).map_err(|e| VkError::RequestContext { - context: context.boxed(), - source: e.into(), - }) - }; - headers.insert(USER_AGENT, parse_value("vk", "build user agent header")?); - let auth_header = format!("Bearer {token}"); - headers.insert( - AUTHORIZATION, - parse_value(&auth_header, "build authorization header")?, - ); - headers.insert( - ACCEPT, - parse_value("application/vnd.github+json", "build accept header")?, - ); - let api_version_header = - HeaderName::from_bytes(b"x-github-api-version").map_err(|e| VkError::RequestContext { - context: "build x-github-api-version header name".boxed(), - source: e.into(), - })?; - headers.insert( - api_version_header, - parse_value("2022-11-28", "build x-github-api-version header value")?, - ); - reqwest::Client::builder() - .default_headers(headers) - .timeout(timeout) - .connect_timeout(connect_timeout) - .build() - .map_err(|e| VkError::RequestContext { - context: "build client".into(), - source: Box::new(e), - }) -} - /// GitHub REST client configuration. +/// +/// Wraps an [`Octocrab`] instance built from the resolved base URI and the +/// caller's authentication token. pub(crate) struct RestClient { - api: Url, - client: reqwest::Client, + client: Octocrab, + timeout: Duration, #[cfg(test)] request_count: AtomicUsize, } impl RestClient { /// Create a REST client targeting the provided `api` base URL. - /// Falls back to `GITHUB_API_URL` or the public GitHub endpoint when `api` is `None`. + /// + /// The base URI is resolved in order: the explicit `api` parameter, then the + /// `GITHUB_API_URL` environment variable, then the public GitHub endpoint. + /// Plain `http://` loopback addresses are accepted so tests can redirect the + /// client at a local stub server. + /// + /// `personal_token` is only set when `token` is non-empty, preserving + /// anonymous access. The `connect_timeout` maps directly onto octocrab's + /// connect timeout. The read and write timeouts are configured on octocrab, + /// and the original total-request deadline is retained for the complete + /// reply operation. + /// + /// Returns [`VkError::RequestContext`] when the base URI cannot be parsed or + /// the client cannot be built. pub(crate) fn new( token: &str, api: Option<&str>, @@ -89,19 +57,37 @@ impl RestClient { while base.ends_with('/') { base.pop(); } - let mut api = Url::parse(&base).map_err(|e| VkError::RequestContext { - context: format!("parse API base URL from {base}").boxed(), - source: Box::new(e), - })?; - let normalised_path = match api.path().trim_end_matches('/') { - "" => "/".to_owned(), - path => format!("{path}/"), - }; - api.set_path(&normalised_path); - let client = github_client(token, timeout, connect_timeout)?; + let mut builder = + Octocrab::builder() + .base_uri(base.as_str()) + .map_err(|e| VkError::RequestContext { + context: format!("parse API base URL from {base}").boxed(), + source: Box::new(e), + })?; + if !token.is_empty() { + builder = builder.personal_token(token.to_owned()); + } + // Restore the behaviourally meaningful headers the reqwest client + // sent: the pinned REST API version and the GitHub JSON media type. + // octocrab's default `User-Agent: octocrab` is left alone (decision + // recorded in the ExecPlan). + let client = builder + .add_header( + HeaderName::from_static("x-github-api-version"), + "2022-11-28".to_owned(), + ) + .add_header(ACCEPT, "application/vnd.github+json".to_owned()) + .set_connect_timeout(Some(connect_timeout)) + .set_read_timeout(Some(timeout)) + .set_write_timeout(Some(timeout)) + .build() + .map_err(|e| VkError::RequestContext { + context: "build client".boxed(), + source: Box::new(e), + })?; Ok(Self { - api, client, + timeout, #[cfg(test)] request_count: AtomicUsize::new(0), }) @@ -109,6 +95,11 @@ impl RestClient { } /// Post a reply to a review comment using the REST API. +/// +/// A 404 response is treated as non-fatal: the original comment is assumed to +/// have gone away, so the caller continues to resolve the thread. Any other +/// non-2xx status is mapped to [`VkError::RequestContext`], whose message names +/// the reply route and the failing status. pub(crate) async fn post_reply( rest: &RestClient, reference: CommentRef<'_>, @@ -116,49 +107,48 @@ pub(crate) async fn post_reply( ) -> Result<(), VkError> { let body = body.trim(); if body.is_empty() { - // Avoid GitHub 422s by skipping empty replies + // Avoid GitHub 422s by skipping empty replies. return Ok(()); } - let path = format!( - "repos/{}/{}/pulls/{}/comments/{}/replies", + let route = format!( + "/repos/{}/{}/pulls/{}/comments/{}/replies", reference.repo.owner, reference.repo.name, reference.pull_number, reference.comment_id ); - let url = rest.api.join(&path).map_err(|e| VkError::RequestContext { - context: format!( - "build reply URL for comment {} in repo {}/{}", - reference.comment_id, reference.repo.owner, reference.repo.name - ) - .boxed(), - source: Box::new(e), - })?; #[cfg(test)] rest.request_count.fetch_add(1, Ordering::SeqCst); - let res = rest - .client - .post(url) - .json(&json!({ "body": body })) - .send() - .await - .map_err(|e| VkError::RequestContext { - context: "post reply".into(), - source: Box::new(e), - })?; - if res.status() == StatusCode::NOT_FOUND { + let response = tokio::time::timeout( + rest.timeout, + rest.client + ._post(route.as_str(), Some(&json!({ "body": body }))), + ) + .await + .map_err(|e| VkError::RequestContext { + context: format!("post reply to {route}").boxed(), + source: Box::new(e), + })? + .map_err(|e| VkError::RequestContext { + context: "post reply".boxed(), + source: Box::new(e), + })?; + let status = response.status(); + if status.as_u16() == 404 { warn!( - "reply target not found (url={}): {}/{} comment {} in PR #{}", - res.url(), - reference.repo.owner, - reference.repo.name, - reference.comment_id, - reference.pull_number + "reply target not found (route={route}): {}/{} comment {} in PR #{}", + reference.repo.owner, reference.repo.name, reference.comment_id, reference.pull_number ); // Treat missing original comment as non-fatal: continue to resolve. return Ok(()); } - res.error_for_status() - .map(|_| ()) - .map_err(|e| VkError::Request(Box::new(e))) + if status.is_success() { + return Ok(()); + } + Err(VkError::RequestContext { + context: format!("post reply to {route}").boxed(), + source: Box::new(std::io::Error::other(format!( + "unexpected HTTP status {status}" + ))), + }) } #[cfg(test)] diff --git a/tests/resolve.rs b/tests/resolve.rs index d4ac73f..f24c3b1 100644 --- a/tests/resolve.rs +++ b/tests/resolve.rs @@ -4,7 +4,10 @@ use assert_cmd::prelude::*; use http_body_util::Full; -use hyper::{Response, StatusCode}; +use hyper::{ + Response, StatusCode, + header::{ACCEPT, AUTHORIZATION, CONTENT_TYPE}, +}; use predicates::prelude::*; use serde_json::Value; use std::borrow::ToOwned; @@ -134,13 +137,34 @@ async fn resolve_flows(#[case] pages: Vec, #[case] expected_posts: usize) async fn run_reply_flow( rest_status: StatusCode, ) -> (Vec, Vec, Vec, std::process::ExitStatus) { - let (addr, handler, shutdown) = start_mitm().await.expect("start server"); + let (addr, handler, shutdown) = start_mitm_capture().await.expect("start server"); let calls = Arc::new(Mutex::new(Vec::::new())); let clone = Arc::clone(&calls); *handler.lock().expect("lock handler") = Box::new(move |req| { let mut vec = clone.lock().expect("lock"); let gql_calls = vec.iter().filter(|c| c.ends_with("/graphql")).count(); vec.push(format!("{} {}", req.method(), req.uri().path())); + if req.uri().path().ends_with("/replies") { + assert_eq!(req.body().as_ref(), br#"{"body":"done"}"#); + assert_eq!( + req.headers().get(AUTHORIZATION).expect("authorization"), + "Bearer dummy" + ); + assert_eq!( + req.headers().get(ACCEPT).expect("accept"), + "application/vnd.github+json" + ); + assert_eq!( + req.headers() + .get("x-github-api-version") + .expect("GitHub API version"), + "2022-11-28" + ); + assert_eq!( + req.headers().get(CONTENT_TYPE).expect("content type"), + "application/json" + ); + } let status = if req.uri().path().ends_with("/replies") { rest_status } else { @@ -157,7 +181,7 @@ async fn run_reply_flow( }; Response::builder() .status(status) - .header("Content-Type", "application/json") + .header(CONTENT_TYPE, "application/json") .body(Full::from(body)) .expect("response") }); @@ -234,6 +258,53 @@ async fn resolve_flows_reply( assert_eq!(calls.as_slice(), expected); } +#[tokio::test] +async fn resolve_reply_honours_total_http_timeout() { + let (addr, handler, shutdown) = start_mitm().await.expect("start server"); + *handler.lock().expect("lock handler") = Box::new(move |req| { + if req.uri().path().ends_with("/replies") { + std::thread::sleep(std::time::Duration::from_secs(2)); + } + let body = if req.uri().path() == "/graphql" { + r#"{"data":{"repository":{"pullRequest":{"reviewComments":{"pageInfo":{"endCursor":null,"hasNextPage":false},"nodes":[{"databaseId":1,"pullRequestReviewThread":{"id":"t"}}]}}}}}"# + } else { + "{}" + }; + Response::builder() + .status(StatusCode::OK) + .header(CONTENT_TYPE, "application/json") + .body(Full::from(body)) + .expect("response") + }); + let output = tokio::task::spawn_blocking(move || { + vk_cmd(addr) + .args([ + "--http-timeout", + "1", + "resolve", + "https://github.com/o/r/pull/83#discussion_r1", + "-m", + "done", + ]) + .output() + .expect("run command") + }) + .await + .expect("spawn blocking"); + shutdown.shutdown().await; + let stderr = String::from_utf8_lossy(&output.stderr); + assert!( + !output.status.success(), + "expected timeout; stderr: {stderr}" + ); + assert!( + predicate::str::contains("post reply to /repos/o/r/pulls/83/comments/1/replies") + .and(predicate::str::contains("deadline has elapsed")) + .eval(&stderr), + "stderr: {stderr}" + ); +} + #[cfg(feature = "unstable-rest-resolve")] #[tokio::test] async fn resolve_skips_empty_reply() {