diff --git a/Cargo.lock b/Cargo.lock index fd525f0..61dbc98 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -312,6 +312,17 @@ dependencies = [ "serde_json", ] +[[package]] +name = "async-lock" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" +dependencies = [ + "event-listener", + "event-listener-strategy", + "pin-project-lite", +] + [[package]] name = "async-stream" version = "0.3.6" @@ -553,6 +564,12 @@ dependencies = [ "thiserror 1.0.69", ] +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + [[package]] name = "base64" version = "0.21.7" @@ -606,6 +623,15 @@ dependencies = [ "serde", ] +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + [[package]] name = "bit-set" version = "0.8.0" @@ -697,7 +723,7 @@ dependencies = [ "serde_json", "serde_repr", "serde_urlencoded", - "thiserror 2.0.10", + "thiserror 2.0.11", "tokio", "tokio-util", "tower-service", @@ -842,10 +868,37 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3e368af43e418a04d52505cf3dbc23dda4e3407ae2fa99fd0e4f308ce546acc" [[package]] -name = "cc" -version = "1.2.7" +name = "cacache" +version = "13.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a012a0df96dd6d06ba9a1b29d6402d1a5d77c6befd2566afdc26e10603dc93d7" +checksum = "5c5063741c7b2e260bbede781cf4679632dd90e2718e99f7715e46824b65670b" +dependencies = [ + "digest", + "either", + "futures", + "hex 0.4.3", + "libc", + "memmap2", + "miette", + "reflink-copy", + "serde", + "serde_derive", + "serde_json", + "sha1", + "sha2", + "ssri", + "tempfile", + "thiserror 1.0.69", + "tokio", + "tokio-stream", + "walkdir", +] + +[[package]] +name = "cc" +version = "1.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8293772165d9345bdaaa39b45b2109591e63fe5e6fbc23c6ff930a048aa310b" dependencies = [ "jobserver", "libc", @@ -1268,6 +1321,18 @@ dependencies = [ "regex", ] +[[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" @@ -1335,6 +1400,33 @@ dependencies = [ "syn 2.0.96", ] +[[package]] +name = "curve25519-dalek" +version = "4.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" +dependencies = [ + "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.96", +] + [[package]] name = "darling" version = "0.20.10" @@ -1558,6 +1650,12 @@ dependencies = [ "serde_json", ] +[[package]] +name = "dotenv" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f" + [[package]] name = "dotenvy" version = "0.15.7" @@ -1600,6 +1698,50 @@ dependencies = [ "duct", ] +[[package]] +name = "dyn-clone" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125" + +[[package]] +name = "ecdsa" +version = "0.16.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +dependencies = [ + "der", + "digest", + "elliptic-curve", + "rfc6979", + "signature", + "spki", +] + +[[package]] +name = "ed25519" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" +dependencies = [ + "pkcs8", + "signature", +] + +[[package]] +name = "ed25519-dalek" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871" +dependencies = [ + "curve25519-dalek", + "ed25519", + "serde", + "sha2", + "subtle", + "zeroize", +] + [[package]] name = "ego-tree" version = "0.9.0" @@ -1621,6 +1763,27 @@ dependencies = [ "serde", ] +[[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 = "email-encoding" version = "0.3.1" @@ -1712,6 +1875,16 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "event-listener-strategy" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c3e4e0dd3673c1139bf041f3008816d9cf2946bbfac2945c09e523b8d7b05b2" +dependencies = [ + "event-listener", + "pin-project-lite", +] + [[package]] name = "eyre" version = "0.6.12" @@ -1748,6 +1921,22 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" +[[package]] +name = "ff" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" +dependencies = [ + "rand_core", + "subtle", +] + +[[package]] +name = "fiat-crypto" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" + [[package]] name = "figment" version = "0.10.19" @@ -2000,6 +2189,7 @@ checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", + "zeroize", ] [[package]] @@ -2082,6 +2272,17 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core", + "subtle", +] + [[package]] name = "h2" version = "0.4.7" @@ -2376,12 +2577,68 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "http-cache" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33b65cd1687caf2c7fff496741a2f264c26f54e6d6cec03dac8f276fa4e5430e" +dependencies = [ + "async-trait", + "bincode", + "cacache", + "http 1.2.0", + "http-cache-semantics", + "httpdate", + "moka", + "serde", + "url", +] + +[[package]] +name = "http-cache-reqwest" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "735586904a5ce0c13877c57cb4eb8195eb7c11ec1ffd64d4db053fb8559ca62e" +dependencies = [ + "anyhow", + "async-trait", + "http 1.2.0", + "http-cache", + "http-cache-semantics", + "reqwest", + "reqwest-middleware", + "serde", + "url", +] + +[[package]] +name = "http-cache-semantics" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92baf25cf0b8c9246baecf3a444546360a97b569168fdf92563ee6a47829920c" +dependencies = [ + "http 1.2.0", + "http-serde", + "serde", + "time", +] + [[package]] name = "http-range-header" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9171a2ea8a68358193d15dd5d70c1c10a2afc3e7e4c5bc92bc9f025cebd7359c" +[[package]] +name = "http-serde" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f056c8559e3757392c8d091e796416e4649d8e49e88b8d76df6c002f05027fd" +dependencies = [ + "http 1.2.0", + "serde", +] + [[package]] name = "httparse" version = "1.9.5" @@ -2938,7 +3195,7 @@ dependencies = [ "reqwest", "serde", "serde_json", - "thiserror 2.0.10", + "thiserror 2.0.11", "tokio", "tower-http", "tower-layer", @@ -3298,6 +3555,8 @@ dependencies = [ [[package]] name = "loco-gen" version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef868bd2df99c949018850b36fb700bba01b10001715f94390bcdb81f412f874" dependencies = [ "chrono", "clap", @@ -3316,6 +3575,8 @@ dependencies = [ [[package]] name = "loco-rs" version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2250c89f0f996c3493ec3d2588a2d63e2861a48df7b9585cb28fbf6faf15a1a0" dependencies = [ "argon2", "async-trait", @@ -3482,6 +3743,38 @@ version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +[[package]] +name = "memmap2" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327" +dependencies = [ + "libc", +] + +[[package]] +name = "miette" +version = "5.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59bb584eaeeab6bd0226ccf3509a69d7936d148cf3d036ad350abe35e8c6856e" +dependencies = [ + "miette-derive", + "once_cell", + "thiserror 1.0.69", + "unicode-width", +] + +[[package]] +name = "miette-derive" +version = "5.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49e7bc1560b95a3c4a25d03de42fe76ca718ab92d1a22a55b9b4cf67b3ae635c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", +] + [[package]] name = "mime" version = "0.3.17" @@ -3545,9 +3838,12 @@ version = "0.12.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a9321642ca94a4282428e6ea4af8cc2ca4eac48ac7a6a4ea8f33f76d0ce70926" dependencies = [ + "async-lock", "crossbeam-channel", "crossbeam-epoch", "crossbeam-utils", + "event-listener", + "futures-util", "loom", "parking_lot 0.12.3", "portable-atomic", @@ -3714,6 +4010,26 @@ dependencies = [ "libc", ] +[[package]] +name = "oauth2" +version = "5.0.0-rc.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23d385da3c602d29036d2f70beed71c36604df7570be17fed4c5b839616785bf" +dependencies = [ + "base64 0.22.1", + "chrono", + "getrandom", + "http 1.2.0", + "rand", + "reqwest", + "serde", + "serde_json", + "serde_path_to_error", + "sha2", + "thiserror 1.0.69", + "url", +] + [[package]] name = "object" version = "0.32.2" @@ -3784,6 +4100,37 @@ dependencies = [ "uuid", ] +[[package]] +name = "openidconnect" +version = "4.0.0-rc.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a93a50789d0b649986bfb104cdef97736ca072d579ec88496d5c6f9abed0ea85" +dependencies = [ + "base64 0.21.7", + "chrono", + "dyn-clone", + "ed25519-dalek", + "hmac", + "http 1.2.0", + "itertools 0.10.5", + "log", + "oauth2", + "p256", + "p384", + "rand", + "rsa", + "serde", + "serde-value", + "serde_json", + "serde_path_to_error", + "serde_plain", + "serde_with", + "sha2", + "subtle", + "thiserror 1.0.69", + "url", +] + [[package]] name = "openssl" version = "0.10.68" @@ -3864,9 +4211,9 @@ dependencies = [ [[package]] name = "ouroboros" -version = "0.18.4" +version = "0.18.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "944fa20996a25aded6b4795c6d63f10014a7a83f8be9828a11860b08c5fc4a67" +checksum = "1e0f050db9c44b97a94723127e6be766ac5c340c48f2c4bb3ffa11713744be59" dependencies = [ "aliasable", "ouroboros_macro", @@ -3875,12 +4222,11 @@ dependencies = [ [[package]] name = "ouroboros_macro" -version = "0.18.4" +version = "0.18.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39b0deead1528fd0e5947a8546a9642a9777c25f6e1e26f34c97b204bbb465bd" +checksum = "3c7028bdd3d43083f6d8d4d5187680d0d3560d54df4cc9d752005268b41e64d0" dependencies = [ "heck 0.4.1", - "itertools 0.12.1", "proc-macro2", "proc-macro2-diagnostics", "quote", @@ -3905,6 +4251,30 @@ version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" +[[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.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70786f51bcc69f6a4c0360e063a4cac5419ef7c5cd5b3c99ad70f3be5ba79209" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2", +] + [[package]] name = "parcel_selectors" version = "0.28.1" @@ -4101,7 +4471,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b7cafe60d6cf8e62e1b9b2ea516a089c008945bb5a275416789e7db0bc199dc" dependencies = [ "memchr", - "thiserror 2.0.10", + "thiserror 2.0.11", "ucd-trie", ] @@ -4306,6 +4676,15 @@ dependencies = [ "yansi", ] +[[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.2.0" @@ -4339,9 +4718,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.92" +version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" +checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" dependencies = [ "unicode-ident", ] @@ -4402,7 +4781,7 @@ dependencies = [ "serde_repr", "serde_with", "tap", - "thiserror 2.0.10", + "thiserror 2.0.11", "tracing", "typed-builder", "url", @@ -4447,7 +4826,7 @@ dependencies = [ "rustc-hash", "rustls", "socket2", - "thiserror 2.0.10", + "thiserror 2.0.11", "tokio", "tracing", ] @@ -4466,7 +4845,7 @@ dependencies = [ "rustls", "rustls-pki-types", "slab", - "thiserror 2.0.10", + "thiserror 2.0.11", "tinyvec", "tracing", "web-time", @@ -4495,7 +4874,7 @@ dependencies = [ "cfg_rust_features", "nom", "percent-encoding", - "thiserror 2.0.10", + "thiserror 2.0.11", "url", ] @@ -4585,11 +4964,15 @@ dependencies = [ "bytes", "chrono", "color-eyre", + "dotenv", "fancy-regex", "fastrand", "figment", "futures", "html-escape", + "http-cache", + "http-cache-reqwest", + "http-cache-semantics", "insta", "itertools 0.13.0", "jwt-authorizer", @@ -4600,8 +4983,10 @@ dependencies = [ "loco-rs", "log", "maplit", + "moka", "once_cell", "opendal 0.51.0", + "openidconnect", "qbit-rs", "quirks_path", "regex", @@ -4622,7 +5007,7 @@ dependencies = [ "tera", "testcontainers", "testcontainers-modules", - "thiserror 2.0.10", + "thiserror 2.0.11", "tokio", "tower 0.5.2", "tower-http", @@ -4691,6 +5076,17 @@ dependencies = [ "thiserror 1.0.69", ] +[[package]] +name = "reflink-copy" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a7aea22fc8204e0f291719120cbcdae4f25f0807d7b00f5b6b27d95a8f1a2ad" +dependencies = [ + "cfg-if", + "rustix", + "windows 0.59.0", +] + [[package]] name = "regex" version = "1.11.1" @@ -4880,6 +5276,16 @@ dependencies = [ "rand", ] +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac", + "subtle", +] + [[package]] name = "ring" version = "0.17.8" @@ -5229,9 +5635,9 @@ dependencies = [ [[package]] name = "sea-orm" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dbcf83248860dc632c46c7e81a221e041b50d0006191756cb001d9e8afc60a9" +checksum = "1a93194430b419da0801f404baf3b986399d6a2a4f43bc79bc96dea83f92ca43" dependencies = [ "async-stream", "async-trait", @@ -5257,9 +5663,9 @@ dependencies = [ [[package]] name = "sea-orm-cli" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a8dbef29c7e534a8e9afb49abfa946c7a9df3d85f610dfed9140215ff8bff17" +checksum = "0e6e0e741bfdf434e6f6aadab156ba4d439e78c9449048698d98fa377871224a" dependencies = [ "chrono", "clap", @@ -5274,11 +5680,12 @@ dependencies = [ [[package]] name = "sea-orm-macros" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49ce6f08134f3681b1ca92185b96fac898f26d9b4f5538d13f7032ef243d14b2" +checksum = "d19e8f22fb474a8a622eb516c46885a080535d8d559386188f525977eaad32b3" dependencies = [ "heck 0.4.1", + "proc-macro-crate", "proc-macro2", "quote", "sea-bae", @@ -5288,9 +5695,9 @@ dependencies = [ [[package]] name = "sea-orm-migration" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e53f46fe9874161ba57b15ff45d2589d754d7cbbbaab9470cb79cbada149ca7" +checksum = "c0bb76ba314552ce15e3a24778cf9c116fc1225fa406e48b0a36e5a3cdbc1e21" dependencies = [ "async-trait", "clap", @@ -5381,18 +5788,33 @@ checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" [[package]] name = "seaography" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bca7168531927846a9da73b20e65aa36cc258b377035286e70ebb34874097b1" +checksum = "654abfe38ded4b3dc80a4025894a3b9ce687b543d6f2b4c487c40835ac53effa" dependencies = [ "async-graphql", "fnv", "heck 0.4.1", "itertools 0.12.1", + "lazy_static", "sea-orm", "thiserror 1.0.69", ] +[[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 = "security-framework" version = "2.11.1" @@ -5517,6 +5939,15 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_plain" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce1fc6db65a611022b23a0dec6975d63fb80a302cb3388835ff02c097258d50" +dependencies = [ + "serde", +] + [[package]] name = "serde_regex" version = "1.1.0" @@ -5645,6 +6076,17 @@ dependencies = [ "stable_deref_trait", ] +[[package]] +name = "sha-1" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5058ada175748e33390e40e872bd0fe59a19f265d0158daa551c5a88a76009c" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "sha1" version = "0.10.6" @@ -5898,7 +6340,7 @@ dependencies = [ "serde_json", "sha2", "smallvec", - "thiserror 2.0.10", + "thiserror 2.0.11", "time", "tokio", "tokio-stream", @@ -5987,7 +6429,7 @@ dependencies = [ "smallvec", "sqlx-core", "stringprep", - "thiserror 2.0.10", + "thiserror 2.0.11", "time", "tracing", "uuid", @@ -6030,7 +6472,7 @@ dependencies = [ "smallvec", "sqlx-core", "stringprep", - "thiserror 2.0.10", + "thiserror 2.0.11", "time", "tracing", "uuid", @@ -6063,6 +6505,23 @@ dependencies = [ "uuid", ] +[[package]] +name = "ssri" +version = "9.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da7a2b3c2bc9693bcb40870c4e9b5bf0d79f9cb46273321bf855ec513e919082" +dependencies = [ + "base64 0.21.7", + "digest", + "hex 0.4.3", + "miette", + "serde", + "sha-1", + "sha2", + "thiserror 1.0.69", + "xxhash-rust", +] + [[package]] name = "stable_deref_trait" version = "1.2.0" @@ -6345,7 +6804,7 @@ dependencies = [ "serde_json", "serde_with", "signal-hook", - "thiserror 2.0.10", + "thiserror 2.0.11", "tokio", "tokio-stream", "tokio-tar", @@ -6374,11 +6833,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.10" +version = "2.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3ac7f54ca534db81081ef1c1e7f6ea8a3ef428d2fc069097c079443d24124d3" +checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc" dependencies = [ - "thiserror-impl 2.0.10", + "thiserror-impl 2.0.11", ] [[package]] @@ -6394,9 +6853,9 @@ dependencies = [ [[package]] name = "thiserror-impl" -version = "2.0.10" +version = "2.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e9465d30713b56a37ede7185763c3492a91be2f5fa68d958c44e41ab9248beb" +checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2" dependencies = [ "proc-macro2", "quote", @@ -6809,7 +7268,7 @@ dependencies = [ "log", "rand", "sha1", - "thiserror 2.0.10", + "thiserror 2.0.11", "utf-8", ] @@ -7300,6 +7759,16 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f919aee0a93304be7f62e8e5027811bbba96bcb1de84d6618be56e43f8a32a1" +dependencies = [ + "windows-core 0.59.0", + "windows-targets 0.53.0", +] + [[package]] name = "windows-core" version = "0.52.0" @@ -7315,13 +7784,26 @@ version = "0.58.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99" dependencies = [ - "windows-implement", - "windows-interface", - "windows-result", - "windows-strings", + "windows-implement 0.58.0", + "windows-interface 0.58.0", + "windows-result 0.2.0", + "windows-strings 0.1.0", "windows-targets 0.52.6", ] +[[package]] +name = "windows-core" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "810ce18ed2112484b0d4e15d022e5f598113e220c53e373fb31e67e21670c1ce" +dependencies = [ + "windows-implement 0.59.0", + "windows-interface 0.59.0", + "windows-result 0.3.0", + "windows-strings 0.3.0", + "windows-targets 0.53.0", +] + [[package]] name = "windows-implement" version = "0.58.0" @@ -7333,6 +7815,17 @@ dependencies = [ "syn 2.0.96", ] +[[package]] +name = "windows-implement" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83577b051e2f49a058c308f17f273b570a6a758386fc291b5f6a934dd84e48c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", +] + [[package]] name = "windows-interface" version = "0.58.0" @@ -7344,14 +7837,25 @@ dependencies = [ "syn 2.0.96", ] +[[package]] +name = "windows-interface" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb26fd936d991781ea39e87c3a27285081e3c0da5ca0fcbc02d368cc6f52ff01" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", +] + [[package]] name = "windows-registry" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0" dependencies = [ - "windows-result", - "windows-strings", + "windows-result 0.2.0", + "windows-strings 0.1.0", "windows-targets 0.52.6", ] @@ -7364,16 +7868,34 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-result" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d08106ce80268c4067c0571ca55a9b4e9516518eaa1a1fe9b37ca403ae1d1a34" +dependencies = [ + "windows-targets 0.53.0", +] + [[package]] name = "windows-strings" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" dependencies = [ - "windows-result", + "windows-result 0.2.0", "windows-targets 0.52.6", ] +[[package]] +name = "windows-strings" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b888f919960b42ea4e11c2f408fadb55f78a9f236d5eef084103c8ce52893491" +dependencies = [ + "windows-targets 0.53.0", +] + [[package]] name = "windows-sys" version = "0.48.0" @@ -7425,13 +7947,29 @@ dependencies = [ "windows_aarch64_gnullvm 0.52.6", "windows_aarch64_msvc 0.52.6", "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm", + "windows_i686_gnullvm 0.52.6", "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.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1e4c7e8ceaaf9cb7d7507c974735728ab453b67ef8f18febdd7c11fe59dca8b" +dependencies = [ + "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" @@ -7444,6 +7982,12 @@ 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" @@ -7456,6 +8000,12 @@ 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" @@ -7468,12 +8018,24 @@ 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" @@ -7486,6 +8048,12 @@ 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" @@ -7498,6 +8066,12 @@ 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" @@ -7510,6 +8084,12 @@ 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" @@ -7523,10 +8103,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] -name = "winnow" -version = "0.6.22" +name = "windows_x86_64_msvc" +version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39281189af81c07ec09db316b302a3e67bf9bd7cbf6c820b50e35fee9c2fa980" +checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" + +[[package]] +name = "winnow" +version = "0.6.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8d71a593cc5c42ad7876e2c1fda56f314f3754c084128833e64f1345ff8a03a" dependencies = [ "memchr", ] @@ -7573,6 +8159,12 @@ dependencies = [ "rustix", ] +[[package]] +name = "xxhash-rust" +version = "0.8.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdd20c5420375476fbd4394763288da7eb0cc0b8c11deed431a91562af7335d3" + [[package]] name = "yansi" version = "1.0.1" diff --git a/Cargo.toml b/Cargo.toml index 0034f94..7e2b16a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,7 @@ resolver = "2" testcontainers = { git = "https://github.com/testcontainers/testcontainers-rs.git", rev = "af21727" } # loco-rs = { git = "https://github.com/lonelyhentxi/loco.git", rev = "beb890e" } # loco-rs = { git = "https://github.com/loco-rs/loco.git" } -loco-rs = { path = "./patches/loco" } +# loco-rs = { path = "./patches/loco" } async-graphql = { git = "https://github.com/aumetra/async-graphql.git", rev = "690ece7" } async-graphql-axum = { git = "https://github.com/aumetra/async-graphql.git", rev = "690ece7" } jwt-authorizer = { git = "https://github.com/blablacio/jwt-authorizer.git", rev = "e956774" } diff --git a/README.md b/README.md index 387ea6d..489e0c6 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,8 @@ -# KONOBUNGU +

+ +
+ Konobangu +
status-badge
+

-Kono Bangumi? +

Kono bangumi?

\ No newline at end of file diff --git a/apps/app/.env.development b/apps/app/.env.development index 9ced364..d0abda2 100644 --- a/apps/app/.env.development +++ b/apps/app/.env.development @@ -7,8 +7,8 @@ BASIC_PASSWORD="konobangu" OIDC_PROVIDER_ENDPOINT="https://some-oidc-auth.com/oidc/.well-known/openid-configuration" OIDC_CLIENT_ID="" OIDC_CLIENT_SECRET="" -OIDC_API_ISSUER="https://some-oidc-auth.com/oidc" -OIDC_API_AUDIENCE="https://konobangu.com/api" +OIDC_ISSUER="https://some-oidc-auth.com/oidc" +OIDC_AUDIENCE="https://konobangu.com/api" OIDC_ICON_URL="" OIDC_EXTRA_SCOPE_REGEX="" OIDC_EXTRA_CLAIM_KEY="" diff --git a/apps/app/.env.example b/apps/app/.env.example index 3144d50..059e679 100644 --- a/apps/app/.env.example +++ b/apps/app/.env.example @@ -5,8 +5,8 @@ NEXT_PUBLIC_OIDC_PROVIDER_ENDPOINT="https://some-oidc-auth.com/oidc/.well-known/ NEXT_PUBLIC_OIDC_CLIENT_ID="" NEXT_PUBLIC_OIDC_CLIENT_SECRET="" NEXT_PUBLIC_OIDC_ICON_URL="" -OIDC_API_ISSUER="https://some-oidc-auth.com/oidc" -OIDC_API_AUDIENCE="https://konobangu.com/api" +OIDC_ISSUER="https://some-oidc-auth.com/oidc" +OIDC_AUDIENCE="https://konobangu.com/api" OIDC_EXTRA_SCOPES="" # 如 "read:konobangu,write:konobangu" OIDC_EXTRA_CLAIM_KEY="" OIDC_EXTRA_CLAIM_VALUE="" diff --git a/apps/proxy/.whistle/rules/files/0.konobangu b/apps/proxy/.whistle/rules/files/0.konobangu new file mode 100644 index 0000000..dc04a88 --- /dev/null +++ b/apps/proxy/.whistle/rules/files/0.konobangu @@ -0,0 +1,12 @@ +```x-forwarded.json +{ + "X-Forwarded-Host": "konobangu.com", + "X-Forwarded-Proto": "https" +} +``` + +^https://konobangu.com/api/playground*** reqHeaders://{x-forwarded.json} http://127.0.0.1:5002/api/playground$1 +^wss://konobangu.com/api/playground*** reqHeaders://{x-forwarded.json} ws://127.0.0.1:5002/api/playground$1 +^https://konobangu.com/api*** reqHeaders://{x-forwarded.json} http://127.0.0.1:5001/api$1 excludeFilter://^^https://konobangu.com/api/playground*** +^https://konobangu.com*** reqHeaders://{x-forwarded.json} http://127.0.0.1:5000$1 excludeFilter://^https://konobangu.com/api*** + diff --git a/apps/proxy/.whistle/rules/files/0.webui b/apps/proxy/.whistle/rules/files/0.webui deleted file mode 100644 index 18f51f8..0000000 --- a/apps/proxy/.whistle/rules/files/0.webui +++ /dev/null @@ -1,2 +0,0 @@ -^https://konobangu.com/*** http://127.0.0.1:5000/$1 excludeFilter://^https://konobangu.com/api/*** -^wss://konobangu.com/*** ws://127.0.0.1:5000/$1 ^excludeFilter://^wss://konobangu.com/api/*** \ No newline at end of file diff --git a/apps/proxy/.whistle/rules/files/2.recorder b/apps/proxy/.whistle/rules/files/2.recorder deleted file mode 100644 index f9998e7..0000000 --- a/apps/proxy/.whistle/rules/files/2.recorder +++ /dev/null @@ -1 +0,0 @@ -^https://konobangu.com/api/*** http://127.0.0.1:5001/api/$1 \ No newline at end of file diff --git a/apps/proxy/.whistle/rules/properties b/apps/proxy/.whistle/rules/properties index 8528bff..6cf4660 100644 --- a/apps/proxy/.whistle/rules/properties +++ b/apps/proxy/.whistle/rules/properties @@ -1 +1 @@ -{"filesOrder":["webui","recorder"],"selectedList":["webui","recorder"],"disabledDefalutRules":true} +{"filesOrder":["konobangu"],"selectedList":["konobangu"],"disabledDefalutRules":true} diff --git a/apps/proxy/.whistle/values/properties b/apps/proxy/.whistle/values/properties index e69de29..960e778 100644 --- a/apps/proxy/.whistle/values/properties +++ b/apps/proxy/.whistle/values/properties @@ -0,0 +1 @@ +{"filesOrder":[]} diff --git a/apps/proxy/package.json b/apps/proxy/package.json index 18bc1c5..495c299 100644 --- a/apps/proxy/package.json +++ b/apps/proxy/package.json @@ -3,7 +3,7 @@ "version": "0.1.0", "private": true, "scripts": { - "start": "cross-env WHISTLE_MODE=\"prod|capture|keepXFF\" whistle run -p 8899 -t 30000 -D . --no-global-plugins", + "start": "cross-env WHISTLE_MODE=\"prod|capture|keepXFF|x-forwarded-host|x-forwarded-proto\" whistle run -p 8899 -t 30000 -D .", "dev": "pnpm run start" }, "keywords": [], @@ -12,4 +12,4 @@ "whistle": "^2.9.61", "cross-env": "^7.0.3" } -} \ No newline at end of file +} diff --git a/apps/recorder/.gitignore b/apps/recorder/.gitignore index 3a6a884..cacf1e1 100644 --- a/apps/recorder/.gitignore +++ b/apps/recorder/.gitignore @@ -25,8 +25,3 @@ Cargo.lock # Dist node_modules dist/ - -# IDE -.vscode/* -!.vscode/extensions.json -.idea diff --git a/apps/recorder/Cargo.toml b/apps/recorder/Cargo.toml index 3075aa1..e556635 100644 --- a/apps/recorder/Cargo.toml +++ b/apps/recorder/Cargo.toml @@ -84,10 +84,7 @@ testcontainers = { version = "0.23.1", features = [ "reusable-containers", ], optional = true } testcontainers-modules = { version = "0.11.4", optional = true } - color-eyre = "0.6" - - log = "0.4.22" anyhow = "1.0.95" bollard = { version = "0.18", optional = true } @@ -102,6 +99,19 @@ axum-extra = "0.10.0" tower-http = "0.6.2" serde_yaml = "0.9.34" tera = "1.20.0" +openidconnect = "4.0.0-rc.1" +http-cache-reqwest = { version = "0.15", features = [ + "manager-cacache", + "manager-moka", +] } +moka = "0.12.10" +http-cache = { version = "0.20.0", features = [ + "cacache-tokio", + "manager-cacache", + "manager-moka", +], default-features = false } +http-cache-semantics = "2.1.0" +dotenv = "0.15.0" [dev-dependencies] serial_test = "3" diff --git a/apps/recorder/config/development.yaml b/apps/recorder/config/development.yaml index 2932a9b..ef30ae4 100644 --- a/apps/recorder/config/development.yaml +++ b/apps/recorder/config/development.yaml @@ -18,8 +18,9 @@ logger: server: # Port on which the server will listen. the server binding is 0.0.0.0:{PORT} port: 5001 + binding: "0.0.0.0" # The UI hostname or IP address that mailers will point to. - host: http://webui.konobangu.com + host: '{{ get_env(name="HOST", default="localhost") }}' # Out of the box middleware configuration. to disable middleware you can changed the `enable` field to `false` of comment the middleware block middlewares: # Enable Etag cache header middleware @@ -68,7 +69,7 @@ workers: # - BackgroundQueue - Workers operate asynchronously in the background, processing queued. # - ForegroundBlocking - Workers operate in the foreground and block until tasks are completed. # - BackgroundAsync - Workers operate asynchronously in the background, processing tasks with async capabilities. - mode: BackgroundQueue + mode: BackgroundAsync # Mailer Configuration. mailer: @@ -89,7 +90,7 @@ mailer: # Database Configuration database: # Database connection URI - uri: '{{ get_env(name="DATABASE_URL", default="postgres://konobangu:konobangu@127.0.0.1:5432/konobangu") }}' + uri: '{{ get_env(name="DATABASE_URL", default="postgres://konobangu:konobangu@localhost:5432/konobangu") }}' # When enabled, the sql query will be logged. enable_logging: true # Set the timeout duration when acquiring a connection. @@ -110,13 +111,13 @@ database: # Redis Configuration redis: # Redis connection URI - uri: '{{ get_env(name="REDIS_URL", default="redis://127.0.0.1:6379") }}' + uri: '{{ get_env(name="REDIS_URL", default="redis://localhost:6379") }}' # Dangerously flush all data in Redis on startup. dangerous operation, make sure that you using this flag only on dev environments or test mode dangerously_flush: false settings: dal: - data_dir: ./data + data_dir: '{{ get_env(name="DAL_DATA_DIR", default="./data") }}' mikan: base_url: "https://mikanani.me/" @@ -128,11 +129,17 @@ settings: leaky_bucket_refill_interval: 500 auth: - auth_type: "oidc" # or "basic" - basic_user: "konobangu" - basic_password: "konobangu" - oidc_api_issuer: "https://some-oidc-auth.com/oidc" - oidc_api_audience: "https://konobangu.com/api" - oidc_extra_scopes: "read:konobangu,write:konobangu" - oidc_extra_claim_key: "" - oidc_extra_claim_value: "" + auth_type: '{{ get_env(name="AUTH_TYPE", default = "basic") }}' + basic_user: '{{ get_env(name="BASIC_USER", default = "konobangu") }}' + basic_password: '{{ get_env(name="BASIC_PASSWORD", default = "konobangu") }}' + oidc_issuer: '{{ get_env(name="OIDC_ISSUER", default = "") }}' + oidc_audience: '{{ get_env(name="OIDC_AUDIENCE", default = "") }}' + oidc_client_id: '{{ get_env(name="OIDC_CLIENT_ID", default = "") }}' + oidc_client_secret: '{{ get_env(name="OIDC_CLIENT_SECRET", default = "") }}' + oidc_extra_scopes: '{{ get_env(name="OIDC_EXTRA_SCOPES", default = "") }}' + oidc_extra_claim_key: '{{ get_env(name="OIDC_EXTRA_CLAIM_KEY", default = "") }}' + oidc_extra_claim_value: '{{ get_env(name="OIDC_EXTRA_CLAIM_VALUE", default = "") }}' + + graphql: + depth_limit: null + complexity_limit: null diff --git a/apps/recorder/package.json b/apps/recorder/package.json index 8c53afb..57f6716 100644 --- a/apps/recorder/package.json +++ b/apps/recorder/package.json @@ -1,7 +1,28 @@ { "name": "recorder", "version": "1.0.0", + "scripts": { + "dev": "rsbuild dev", + "build": "rsbuild build", + "preview": "rsbuild preview" + }, "dependencies": { - "altair-static": "^8.1.3" + "@graphiql/react": "^0.28.2", + "@graphiql/toolkit": "^0.11.1", + "@tanstack/react-router": "^1.95.5", + "@tanstack/router-devtools": "^1.95.5", + "graphql-ws": "^5.16.2", + "oidc-client-ts": "^3.1.0", + "react": "^19.0.0", + "react-dom": "^19.0.0", + "react-oidc-context": "^3.2.0" + }, + "devDependencies": { + "@rsbuild/core": "^1.1.8", + "@rsbuild/plugin-react": "^1.0.7", + "@tanstack/router-plugin": "^1.95.5", + "@types/react": "^19.0.0", + "@types/react-dom": "^19.0.0", + "typescript": "^5.7.2" } } diff --git a/apps/recorder/assets/.gitkeep b/apps/recorder/public/.gitkeep similarity index 100% rename from apps/recorder/assets/.gitkeep rename to apps/recorder/public/.gitkeep diff --git a/apps/recorder/assets/static/404.html b/apps/recorder/public/assets/404.html similarity index 100% rename from apps/recorder/assets/static/404.html rename to apps/recorder/public/assets/404.html diff --git a/apps/recorder/public/assets/favicon.ico b/apps/recorder/public/assets/favicon.ico new file mode 100644 index 0000000..a314325 Binary files /dev/null and b/apps/recorder/public/assets/favicon.ico differ diff --git a/apps/recorder/rsbuild.config.ts b/apps/recorder/rsbuild.config.ts new file mode 100644 index 0000000..2aacdf0 --- /dev/null +++ b/apps/recorder/rsbuild.config.ts @@ -0,0 +1,72 @@ +import { defineConfig } from '@rsbuild/core'; +import { pluginReact } from '@rsbuild/plugin-react'; +import { TanStackRouterRspack } from '@tanstack/router-plugin/rspack'; + +export default defineConfig({ + plugins: [pluginReact()], + html: { + favicon: './public/assets/favicon.ico', + tags: [ + { + tag: 'script', + attrs: { src: 'https://cdn.tailwindcss.com' }, + }, + ], + }, + tools: { + rspack: { + plugins: [TanStackRouterRspack()], + }, + }, + source: { + define: { + 'process.env.AUTH_TYPE': JSON.stringify(process.env.AUTH_TYPE), + 'process.env.OIDC_CLIENT_ID': JSON.stringify(process.env.OIDC_CLIENT_ID), + 'process.env.OIDC_CLIENT_SECRET': JSON.stringify( + process.env.OIDC_CLIENT_SECRET + ), + 'process.env.OIDC_ISSUER': JSON.stringify(process.env.OIDC_ISSUER), + 'process.env.OIDC_AUDIENCE': JSON.stringify(process.env.OIDC_AUDIENCE), + 'process.env.OIDC_EXTRA_SCOPES': JSON.stringify( + process.env.OIDC_EXTRA_SCOPES + ), + }, + }, + dev: { + client: { + path: '/api/playground/rsbuild-hmr', + }, + setupMiddlewares: [ + (middlewares) => { + middlewares.unshift((req, res, next) => { + if (process.env.AUTH_TYPE === 'basic') { + res.setHeader('WWW-Authenticate', 'Basic realm="konobangu"'); + + const authorization = + (req.headers.authorization || '').split(' ')[1] || ''; + const [user, password] = Buffer.from(authorization, 'base64') + .toString() + .split(':'); + + if ( + user !== process.env.BASIC_USER || + password !== process.env.BASIC_PASSWORD + ) { + res.statusCode = 401; + res.write('Unauthorized'); + res.end(); + return; + } + } + next(); + }); + return middlewares; + }, + ], + }, + server: { + base: '/api/playground/', + host: '0.0.0.0', + port: 5002, + }, +}); diff --git a/apps/recorder/src/app/ext.rs b/apps/recorder/src/app/ext.rs index 0057abd..31b2c5b 100644 --- a/apps/recorder/src/app/ext.rs +++ b/apps/recorder/src/app/ext.rs @@ -1,4 +1,4 @@ -use loco_rs::app::AppContext; +use loco_rs::{app::AppContext, environment::Environment}; use crate::{ auth::service::AppAuthService, dal::AppDalClient, extract::mikan::AppMikanClient, @@ -21,6 +21,14 @@ pub trait AppContextExt { fn get_graphql_service(&self) -> &AppGraphQLService { AppGraphQLService::app_instance() } + + fn get_node_env(&self) -> Environment { + let node_env = std::env::var("NODE_ENV"); + match node_env.as_deref() { + Ok("production") => Environment::Production, + _ => Environment::Development, + } + } } impl AppContextExt for AppContext {} diff --git a/apps/recorder/src/app/index.tsx b/apps/recorder/src/app/index.tsx new file mode 100644 index 0000000..97e7434 --- /dev/null +++ b/apps/recorder/src/app/index.tsx @@ -0,0 +1,53 @@ +import { RouterProvider, createRouter } from '@tanstack/react-router'; +import type { UserManager } from 'oidc-client-ts'; +import { useMemo } from 'react'; +import { AuthProvider, useAuth } from 'react-oidc-context'; +import { buildUserManager } from '../auth/config'; +import { routeTree } from '../routeTree.gen'; + +// Set up a Router instance +const router = createRouter({ + routeTree, + basepath: '/api/playground', + defaultPreload: 'intent', + context: { + isAuthenticated: process.env.AUTH_TYPE === 'basic', + auth: undefined!, + userManager: undefined!, + }, +}); + +// Register things for typesafety +declare module '@tanstack/react-router' { + interface Register { + router: typeof router; + } +} + +const AppWithBasicAuth = () => { + return ; +}; + +const AppWithOidcAuthInner = ({ + userManager, +}: { userManager: UserManager }) => { + const auth = useAuth(); + return ( + + ); +}; + +const AppWithOidcAuth = () => { + const userManager = useMemo(() => buildUserManager(), []); + return ( + + + + ); +}; + +export const App = + process.env.AUTH_TYPE === 'oidc' ? AppWithOidcAuth : AppWithBasicAuth; diff --git a/apps/recorder/src/app/mod.rs b/apps/recorder/src/app/mod.rs index 52bcb91..90bd6ae 100644 --- a/apps/recorder/src/app/mod.rs +++ b/apps/recorder/src/app/mod.rs @@ -3,6 +3,7 @@ pub mod ext; use std::{ fs, path::{self, Path, PathBuf}, + sync::Arc, }; use async_trait::async_trait; @@ -88,28 +89,49 @@ impl Hooks for App { .flatten() .collect_vec(); + for working_root in working_roots_to_search.iter() { + let working_root = PathBuf::from(working_root); + for env_file in [ + working_root.join(format!(".env.{env}.local")), + working_root.join(format!(".env.{env}")), + working_root.join(".env.local"), + working_root.join(".env"), + ] { + tracing::info!(env_file =? env_file); + if env_file.exists() && env_file.is_file() { + dotenv::from_path(&env_file).map_err(loco_rs::Error::wrap)?; + tracing::info!("loaded env from {} success.", env_file.to_string_lossy()); + } + } + } + for working_root in working_roots_to_search.iter() { let working_root = PathBuf::from(working_root); let config_dir = working_root.as_path().join("config"); + for config_file in [ config_dir.join(format!("{env}.local.yaml")), config_dir.join(format!("{env}.yaml")), ] { if config_file.exists() && config_file.is_file() { - tracing::info!(config_file =? config_file, "loading environment from"); - let content = fs::read_to_string(config_file.clone())?; + let rendered = tera::Tera::one_off( &content, - &tera::Context::from_serialize(serde_json::json!({}))?, + &tera::Context::from_value(serde_json::json!({}))?, false, )?; App::set_working_root(working_root); - return serde_yaml::from_str(&rendered).map_err(|err| { - loco_rs::Error::YAMLFile(err, config_file.to_string_lossy().to_string()) - }); + let config_file = &config_file.to_string_lossy(); + + let res = serde_yaml::from_str(&rendered) + .map_err(|err| loco_rs::Error::YAMLFile(err, config_file.to_string()))?; + + tracing::info!("loading config from {} success", config_file); + + return Ok(res); } } } @@ -118,8 +140,7 @@ impl Hooks for App { "no configuration file found in search paths: {}", working_roots_to_search .iter() - .map(|p| path::absolute(PathBuf::from(p))) - .flatten() + .flat_map(|p| path::absolute(PathBuf::from(p))) .map(|p| p.to_string_lossy().to_string()) .join(",") ))) @@ -137,15 +158,28 @@ impl Hooks for App { } fn routes(ctx: &AppContext) -> AppRoutes { + let ctx = Arc::new(ctx.clone()); AppRoutes::with_default_routes() .prefix("/api") - .add_route(controllers::auth::routes()) .add_route(controllers::graphql::routes(ctx.clone())) } fn middlewares(ctx: &AppContext) -> Vec> { + use loco_rs::controller::middleware::static_assets::{FolderConfig, StaticAssets}; + let mut middlewares = middleware::default_middleware_stack(ctx); - middlewares.extend(controllers::graphql::asset_middlewares()); + middlewares.push(Box::new(StaticAssets { + enable: true, + must_exist: true, + folder: FolderConfig { + uri: String::from("/api/static"), + path: App::get_working_root().join("public").into(), + }, + fallback: App::get_working_root() + .join("public/assets/404.html") + .into(), + precompressed: false, + })); middlewares } diff --git a/apps/recorder/src/auth/basic.rs b/apps/recorder/src/auth/basic.rs index e1eccf3..8beffab 100644 --- a/apps/recorder/src/auth/basic.rs +++ b/apps/recorder/src/auth/basic.rs @@ -22,12 +22,12 @@ impl AuthBasic { .headers .get(AUTHORIZATION) .and_then(|s| s.to_str().ok()) - .ok_or_else(|| AuthError::BasicInvalidCredentials)?; + .ok_or(AuthError::BasicInvalidCredentials)?; let split = authorization.split_once(' '); match split { - Some((name, contents)) if name == "Basic" => { + Some(("Basic", contents)) => { let decoded = base64::engine::general_purpose::STANDARD .decode(contents) .map_err(|_| AuthError::BasicInvalidCredentials)?; @@ -80,4 +80,8 @@ impl AuthService for BasicAuthService { fn www_authenticate_header_value(&self) -> Option { Some(HeaderValue::from_static(r#"Basic realm="konobangu""#)) } + + fn auth_type(&self) -> AuthType { + AuthType::Basic + } } diff --git a/apps/recorder/src/auth/config.rs b/apps/recorder/src/auth/config.rs index 9382aa6..42a36b8 100644 --- a/apps/recorder/src/auth/config.rs +++ b/apps/recorder/src/auth/config.rs @@ -1,5 +1,6 @@ use jwt_authorizer::OneOrArray; use serde::{Deserialize, Serialize}; +use serde_with::{serde_as, NoneAsEmptyString}; #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct BasicAuthConfig { @@ -9,17 +10,24 @@ pub struct BasicAuthConfig { pub password: String, } +#[serde_as] #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct OidcAuthConfig { - #[serde(rename = "oidc_api_issuer")] + #[serde(rename = "oidc_issuer")] pub issuer: String, - #[serde(rename = "oidc_api_audience")] + #[serde(rename = "oidc_audience")] pub audience: String, + #[serde(rename = "oidc_client_id")] + pub client_id: String, + #[serde(rename = "oidc_client_secret")] + pub client_secret: String, #[serde(rename = "oidc_extra_scopes")] pub extra_scopes: Option>, + #[serde_as(as = "NoneAsEmptyString")] #[serde(rename = "oidc_extra_claim_key")] pub extra_claim_key: Option, #[serde(rename = "oidc_extra_claim_value")] + #[serde_as(as = "NoneAsEmptyString")] pub extra_claim_value: Option, } diff --git a/apps/recorder/src/auth/config.ts b/apps/recorder/src/auth/config.ts new file mode 100644 index 0000000..6821fe3 --- /dev/null +++ b/apps/recorder/src/auth/config.ts @@ -0,0 +1,31 @@ +import { type OidcClientSettings, UserManager } from 'oidc-client-ts'; + +export const PostLoginRedirectUriKey = 'post_login_redirect_uri'; + +export function buildOidcConfig(): OidcClientSettings { + const origin = window.location.origin; + + const resource = process.env.OIDC_AUDIENCE!; + + return { + authority: process.env.OIDC_ISSUER!, + client_id: process.env.OIDC_CLIENT_ID!, + client_secret: process.env.OIDC_CLIENT_SECRET!, + redirect_uri: `${origin}/api/playground/oidc/callback`, + disablePKCE: false, + scope: `openid profile email ${process.env.OIDC_EXTRA_SCOPES}`, + response_type: 'code', + resource, + post_logout_redirect_uri: `${origin}/api/playground`, + extraQueryParams: { + resource, + }, + extraTokenParams: { + resource, + }, + }; +} + +export function buildUserManager(): UserManager { + return new UserManager(buildOidcConfig()); +} diff --git a/apps/recorder/src/auth/errors.rs b/apps/recorder/src/auth/errors.rs index 0fa2798..6b04856 100644 --- a/apps/recorder/src/auth/errors.rs +++ b/apps/recorder/src/auth/errors.rs @@ -3,15 +3,56 @@ use axum::{ response::{IntoResponse, Response}, Json, }; +use openidconnect::{ + core::CoreErrorResponseType, ConfigurationError, RequestTokenError, SignatureVerificationError, + SigningError, StandardErrorResponse, +}; use serde::{Deserialize, Serialize}; use thiserror::Error; +use crate::{fetch::HttpClientError, models::auth::AuthType}; + #[derive(Debug, Error)] pub enum AuthError { + #[error("Not support auth method")] + NotSupportAuthMethod { + supported: Vec, + current: AuthType, + }, #[error("Invalid credentials")] BasicInvalidCredentials, #[error(transparent)] OidcInitError(#[from] jwt_authorizer::error::InitError), + #[error("Invalid oidc provider meta client error: {0}")] + OidcProviderHttpClientError(HttpClientError), + #[error(transparent)] + OidcProviderMetaError(#[from] openidconnect::DiscoveryError), + #[error("Invalid oidc provider URL: {0}")] + OidcProviderUrlError(url::ParseError), + #[error("Invalid oidc redirect URI: {0}")] + OidcRequestRedirectUriError(url::ParseError), + #[error("Oidc request session not found or expired")] + OidcCallbackRecordNotFoundOrExpiredError, + #[error("Invalid oidc request callback nonce")] + OidcInvalidNonceError, + #[error("Invalid oidc request callback state")] + OidcInvalidStateError, + #[error("Invalid oidc request callback code")] + OidcInvalidCodeError, + #[error(transparent)] + OidcCallbackTokenConfigrationError(#[from] ConfigurationError), + #[error(transparent)] + OidcRequestTokenError( + #[from] RequestTokenError>, + ), + #[error("Invalid oidc id token")] + OidcInvalidIdTokenError, + #[error("Invalid oidc access token")] + OidcInvalidAccessTokenError, + #[error(transparent)] + OidcSignatureVerificationError(#[from] SignatureVerificationError), + #[error(transparent)] + OidcSigningError(#[from] SigningError), #[error(transparent)] OidcJwtAuthError(#[from] jwt_authorizer::AuthError), #[error("Extra scopes {expected} do not match found scopes {found}")] diff --git a/apps/recorder/src/auth/guard.ts b/apps/recorder/src/auth/guard.ts new file mode 100644 index 0000000..a258519 --- /dev/null +++ b/apps/recorder/src/auth/guard.ts @@ -0,0 +1,21 @@ +import type { ParsedLocation } from '@tanstack/react-router'; +import type { RouterContext } from '../controllers/__root'; +import { PostLoginRedirectUriKey } from './config'; + +export const beforeLoadGuard = async ({ + context, + location, + // biome-ignore lint/complexity/noBannedTypes: +}: { context: RouterContext; location: ParsedLocation<{}> }) => { + if (!context.isAuthenticated) { + // TODO: FIXME + const user = await context.userManager.getUser(); + if (!user) { + try { + sessionStorage.setItem(PostLoginRedirectUriKey, location.href); + // biome-ignore lint/suspicious/noEmptyBlockStatements: + } catch {} + throw await context.auth.signinRedirect(); + } + } +}; diff --git a/apps/recorder/src/auth/middleware.rs b/apps/recorder/src/auth/middleware.rs index 8a5e886..071072a 100644 --- a/apps/recorder/src/auth/middleware.rs +++ b/apps/recorder/src/auth/middleware.rs @@ -1,3 +1,5 @@ +use std::sync::Arc; + use axum::{ extract::{Request, State}, http::header, @@ -9,34 +11,7 @@ use loco_rs::prelude::AppContext; use crate::{app::AppContextExt, auth::AuthService}; pub async fn api_auth_middleware( - State(ctx): State, - request: Request, - next: Next, -) -> Response { - let auth_service = ctx.get_auth_service(); - - let (mut parts, body) = request.into_parts(); - - let mut response = match auth_service.extract_user_info(&mut parts).await { - Ok(auth_user_info) => { - let mut request = Request::from_parts(parts, body); - request.extensions_mut().insert(auth_user_info); - next.run(request).await - } - Err(auth_error) => auth_error.into_response(), - }; - - if let Some(header_value) = auth_service.www_authenticate_header_value() { - response - .headers_mut() - .insert(header::WWW_AUTHENTICATE, header_value); - }; - - response -} - -pub async fn webui_auth_middleware( - State(ctx): State, + State(ctx): State>, request: Request, next: Next, ) -> Response { diff --git a/apps/recorder/src/auth/mod.rs b/apps/recorder/src/auth/mod.rs index ae46bde..2e295d7 100644 --- a/apps/recorder/src/auth/mod.rs +++ b/apps/recorder/src/auth/mod.rs @@ -7,5 +7,5 @@ pub mod service; pub use config::{AppAuthConfig, BasicAuthConfig, OidcAuthConfig}; pub use errors::AuthError; -pub use middleware::{api_auth_middleware, webui_auth_middleware}; +pub use middleware::api_auth_middleware; pub use service::{AppAuthService, AuthService, AuthUserInfo}; diff --git a/apps/recorder/src/auth/oidc.rs b/apps/recorder/src/auth/oidc.rs index 2dff393..5c4cda0 100644 --- a/apps/recorder/src/auth/oidc.rs +++ b/apps/recorder/src/auth/oidc.rs @@ -1,18 +1,28 @@ -use std::collections::{HashMap, HashSet}; +use std::{ + collections::{HashMap, HashSet}, + sync::Arc, +}; use async_trait::async_trait; use axum::http::{request::Parts, HeaderValue}; use itertools::Itertools; use jwt_authorizer::{authorizer::Authorizer, NumericDate, OneOrArray}; +use moka::future::Cache; +use openidconnect::{ + core::{CoreAuthenticationFlow, CoreClient, CoreProviderMetadata}, + AccessTokenHash, AuthorizationCode, ClientId, ClientSecret, CsrfToken, IssuerUrl, Nonce, + OAuth2TokenResponse, PkceCodeChallenge, PkceCodeVerifier, RedirectUrl, TokenResponse, +}; use serde::{Deserialize, Serialize}; use serde_json::Value; +use url::Url; use super::{ config::OidcAuthConfig, errors::AuthError, service::{AuthService, AuthUserInfo}, }; -use crate::models::auth::AuthType; +use crate::{fetch::HttpClient, models::auth::AuthType}; #[derive(Deserialize, Serialize, Clone, Debug)] pub struct OidcAuthClaims { @@ -76,23 +86,185 @@ impl OidcAuthClaims { } } +#[derive(Debug, Clone, Serialize)] +pub struct OidcAuthRequest { + pub auth_uri: Url, + #[serde(skip)] + pub redirect_uri: RedirectUrl, + #[serde(skip)] + pub csrf_token: CsrfToken, + #[serde(skip)] + pub nonce: Nonce, + #[serde(skip)] + pub pkce_verifier: Arc, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct OidcAuthCallbackQuery { + pub state: Option, + pub code: Option, + pub redirect_uri: Option, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct OidcAuthCallbackPayload { + pub access_token: String, +} + pub struct OidcAuthService { pub config: OidcAuthConfig, - pub authorizer: Authorizer, + pub api_authorizer: Authorizer, + pub oidc_provider_client: HttpClient, + pub oidc_request_cache: Cache, +} + +impl OidcAuthService { + pub async fn build_authorization_request( + &self, + redirect_uri: &str, + ) -> Result { + let provider_metadata = CoreProviderMetadata::discover_async( + IssuerUrl::new(self.config.issuer.clone()).map_err(AuthError::OidcProviderUrlError)?, + &self.oidc_provider_client, + ) + .await?; + + let redirect_uri = RedirectUrl::new(redirect_uri.to_string()) + .map_err(AuthError::OidcRequestRedirectUriError)?; + + let oidc_client = CoreClient::from_provider_metadata( + provider_metadata, + ClientId::new(self.config.client_id.clone()), + Some(ClientSecret::new(self.config.client_secret.clone())), + ) + .set_redirect_uri(redirect_uri.clone()); + + let (pkce_chanllenge, pkce_verifier) = PkceCodeChallenge::new_random_sha256(); + + let mut authorization_request = oidc_client + .authorize_url( + CoreAuthenticationFlow::AuthorizationCode, + CsrfToken::new_random, + Nonce::new_random, + ) + .set_pkce_challenge(pkce_chanllenge); + + { + if let Some(scopes) = self.config.extra_scopes.as_ref() { + authorization_request = authorization_request.add_scopes( + scopes + .iter() + .map(|s| openidconnect::Scope::new(s.to_string())), + ) + } + } + + let (auth_uri, csrf_token, nonce) = authorization_request.url(); + + Ok(OidcAuthRequest { + auth_uri, + csrf_token, + nonce, + pkce_verifier: Arc::new(pkce_verifier), + redirect_uri, + }) + } + + pub async fn store_authorization_request( + &self, + request: OidcAuthRequest, + ) -> Result<(), AuthError> { + self.oidc_request_cache + .insert(request.csrf_token.secret().to_string(), request) + .await; + Ok(()) + } + + pub async fn load_authorization_request( + &self, + state: &str, + ) -> Result { + let result = self + .oidc_request_cache + .get(state) + .await + .ok_or(AuthError::OidcCallbackRecordNotFoundOrExpiredError)?; + + self.oidc_request_cache.invalidate(state).await; + + Ok(result) + } + + pub async fn extract_authorization_request_callback( + &self, + query: OidcAuthCallbackQuery, + ) -> Result { + let csrf_token = query.state.ok_or(AuthError::OidcInvalidStateError)?; + + let code = query.code.ok_or(AuthError::OidcInvalidCodeError)?; + + let request_cache = self.load_authorization_request(&csrf_token).await?; + + let provider_metadata = CoreProviderMetadata::discover_async( + IssuerUrl::new(self.config.issuer.clone()).map_err(AuthError::OidcProviderUrlError)?, + &self.oidc_provider_client, + ) + .await?; + + let oidc_client = CoreClient::from_provider_metadata( + provider_metadata, + ClientId::new(self.config.client_id.clone()), + Some(ClientSecret::new(self.config.client_secret.clone())), + ) + .set_redirect_uri(request_cache.redirect_uri); + + let pkce_verifier = PkceCodeVerifier::new(request_cache.pkce_verifier.secret().to_string()); + + let token_response = oidc_client + .exchange_code(AuthorizationCode::new(code))? + .set_pkce_verifier(pkce_verifier) + .request_async(&HttpClient::default()) + .await?; + + let id_token = token_response + .id_token() + .ok_or(AuthError::OidcInvalidIdTokenError)?; + + let id_token_verifier = &oidc_client.id_token_verifier(); + + let claims = id_token + .claims(id_token_verifier, &request_cache.nonce) + .map_err(|_| AuthError::OidcInvalidNonceError)?; + + let access_token = token_response.access_token(); + + let actual_access_token_hash = AccessTokenHash::from_token( + access_token, + id_token.signing_alg()?, + id_token.signing_key(id_token_verifier)?, + )?; + + if let Some(expected_access_token_hash) = claims.access_token_hash() { + if actual_access_token_hash != *expected_access_token_hash { + return Err(AuthError::OidcInvalidAccessTokenError); + } + } + + Ok(OidcAuthCallbackPayload { + access_token: access_token.secret().to_string(), + }) + } } #[async_trait] impl AuthService for OidcAuthService { async fn extract_user_info(&self, request: &mut Parts) -> Result { let config = &self.config; - let token = - self.authorizer - .extract_token(&request.headers) - .ok_or(AuthError::OidcJwtAuthError( - jwt_authorizer::AuthError::MissingToken(), - ))?; + let token = self.api_authorizer.extract_token(&request.headers).ok_or( + AuthError::OidcJwtAuthError(jwt_authorizer::AuthError::MissingToken()), + )?; - let token_data = self.authorizer.check_auth(&token).await?; + let token_data = self.api_authorizer.check_auth(&token).await?; let claims = token_data.claims; if claims.sub.as_deref().is_none_or(|s| s.trim().is_empty()) { return Err(AuthError::OidcSubMissingError); @@ -139,4 +311,8 @@ impl AuthService for OidcAuthService { fn www_authenticate_header_value(&self) -> Option { Some(HeaderValue::from_static(r#"Bearer realm="konobangu""#)) } + + fn auth_type(&self) -> AuthType { + AuthType::Oidc + } } diff --git a/apps/recorder/src/auth/service.rs b/apps/recorder/src/auth/service.rs index 66ca719..a4d7433 100644 --- a/apps/recorder/src/auth/service.rs +++ b/apps/recorder/src/auth/service.rs @@ -1,3 +1,5 @@ +use std::time::Duration; + use async_trait::async_trait; use axum::{ extract::FromRequestParts, @@ -6,6 +8,7 @@ use axum::{ }; use jwt_authorizer::{JwtAuthorizer, Validation}; use loco_rs::app::{AppContext, Initializer}; +use moka::future::Cache; use once_cell::sync::OnceCell; use reqwest::header::HeaderValue; @@ -15,7 +18,15 @@ use super::{ oidc::{OidcAuthClaims, OidcAuthService}, AppAuthConfig, }; -use crate::{app::AppContextExt as _, config::AppConfigExt, models::auth::AuthType}; +use crate::{ + app::AppContextExt as _, + config::AppConfigExt, + fetch::{ + client::{HttpClientCacheBackendConfig, HttpClientCachePresetConfig}, + HttpClient, HttpClientConfig, + }, + models::auth::AuthType, +}; #[derive(Clone, Debug)] pub struct AuthUserInfo { @@ -43,6 +54,7 @@ impl FromRequestParts for AuthUserInfo { pub trait AuthService { async fn extract_user_info(&self, request: &mut Parts) -> Result; fn www_authenticate_header_value(&self) -> Option; + fn auth_type(&self) -> AuthType; } pub enum AppAuthService { @@ -74,7 +86,18 @@ impl AppAuthService { AppAuthService::Oidc(OidcAuthService { config, - authorizer: jwt_auth, + api_authorizer: jwt_auth, + oidc_provider_client: HttpClient::from_config(HttpClientConfig { + exponential_backoff_max_retries: Some(3), + cache_backend: Some(HttpClientCacheBackendConfig::Moka { cache_size: 1 }), + cache_preset: Some(HttpClientCachePresetConfig::RFC7234), + ..Default::default() + }) + .map_err(AuthError::OidcProviderHttpClientError)?, + oidc_request_cache: Cache::builder() + .time_to_live(Duration::from_mins(5)) + .name("oidc_request_cache") + .build(), }) } }; @@ -97,6 +120,13 @@ impl AuthService for AppAuthService { AppAuthService::Oidc(service) => service.www_authenticate_header_value(), } } + + fn auth_type(&self) -> AuthType { + match self { + AppAuthService::Basic(service) => service.auth_type(), + AppAuthService::Oidc(service) => service.auth_type(), + } + } } pub struct AppAuthServiceInitializer; diff --git a/apps/recorder/src/config/settings_mixin.yaml b/apps/recorder/src/config/settings_mixin.yaml index c33b94e..32ac438 100644 --- a/apps/recorder/src/config/settings_mixin.yaml +++ b/apps/recorder/src/config/settings_mixin.yaml @@ -1,16 +1,15 @@ -dal: - data_dir: ./data +# dal: +# data_dir: ./data -mikan: - http_client: - exponential_backoff_max_retries: 3 - leaky_bucket_max_tokens: 2 - leaky_bucket_initial_tokens: 0 - leaky_bucket_refill_tokens: 1 - leaky_bucket_refill_interval: 500 - base_url: "https://mikanani.me/" +# mikan: +# http_client: +# exponential_backoff_max_retries: 3 +# leaky_bucket_max_tokens: 2 +# leaky_bucket_initial_tokens: 0 +# leaky_bucket_refill_tokens: 1 +# leaky_bucket_refill_interval: 500 +# base_url: "https://mikanani.me/" -graphql: - playground_static: "./node_modules/altair-static/build/dist" - depth_limit: null - complexity_limit: null +# graphql: +# depth_limit: null +# complexity_limit: null diff --git a/apps/recorder/src/controllers/__root.tsx b/apps/recorder/src/controllers/__root.tsx new file mode 100644 index 0000000..0b7091f --- /dev/null +++ b/apps/recorder/src/controllers/__root.tsx @@ -0,0 +1,52 @@ +import { + Link, + Outlet, + createRootRouteWithContext, +} from '@tanstack/react-router'; +import { TanStackRouterDevtools } from '@tanstack/router-devtools'; +import type { UserManager } from 'oidc-client-ts'; +import type { AuthContextProps } from 'react-oidc-context'; + +export type RouterContext = + | { + isAuthenticated: false; + auth: AuthContextProps; + userManager: UserManager; + } + | { + isAuthenticated: true; + auth?: AuthContextProps; + userManager?: UserManager; + }; + +export const Route = createRootRouteWithContext()({ + component: RootComponent, +}); + +function RootComponent() { + return ( + <> +
+ + Home + {' '} + + GraphQL + +
+
+ + + + ); +} diff --git a/apps/recorder/src/controllers/auth/mod.rs b/apps/recorder/src/controllers/auth/mod.rs deleted file mode 100644 index 80ff4c0..0000000 --- a/apps/recorder/src/controllers/auth/mod.rs +++ /dev/null @@ -1,10 +0,0 @@ -use axum::response::IntoResponse; -use loco_rs::prelude::*; - -async fn current() -> impl IntoResponse { - "" -} - -pub fn routes() -> Routes { - Routes::new().prefix("/auth").add("/current", get(current)) -} diff --git a/apps/recorder/src/controllers/graphql/index.tsx b/apps/recorder/src/controllers/graphql/index.tsx new file mode 100644 index 0000000..c648d56 --- /dev/null +++ b/apps/recorder/src/controllers/graphql/index.tsx @@ -0,0 +1,37 @@ +import { GraphiQLProvider, QueryEditor } from '@graphiql/react'; +import { createGraphiQLFetcher } from '@graphiql/toolkit'; +import { createFileRoute } from '@tanstack/react-router'; +import { useMemo } from 'react'; +import { useAuth } from 'react-oidc-context'; +import { beforeLoadGuard } from '../../auth/guard'; +import '@graphiql/react/dist/style.css'; + +export const Route = createFileRoute('/graphql/')({ + component: RouteComponent, + beforeLoad: beforeLoadGuard, +}); + +function RouteComponent() { + const auth = useAuth(); + + const fetcher = useMemo( + () => + createGraphiQLFetcher({ + url: '/api/graphql', + headers: auth?.user?.access_token + ? { + Authorization: `Bearer ${auth.user.access_token}`, + } + : undefined, + }), + [auth] + ); + + return ( + +
+ +
+
+ ); +} diff --git a/apps/recorder/src/controllers/graphql/mod.rs b/apps/recorder/src/controllers/graphql/mod.rs index ab05e5b..e4da16f 100644 --- a/apps/recorder/src/controllers/graphql/mod.rs +++ b/apps/recorder/src/controllers/graphql/mod.rs @@ -1,43 +1,14 @@ -pub mod playground; - -use std::collections::HashMap; +use std::sync::Arc; use async_graphql_axum::{GraphQLRequest, GraphQLResponse}; -use axum::{ - extract::State, - http::HeaderMap, - middleware::from_fn_with_state, - response::Html, - routing::{get, post}, - Extension, -}; -use loco_rs::{app::AppContext, controller::middleware::MiddlewareLayer, prelude::Routes}; -use playground::{altair_graphql_playground_asset_middleware, AltairGraphQLPlayground}; -use reqwest::header; +use axum::{extract::State, middleware::from_fn_with_state, routing::post, Extension}; +use loco_rs::{app::AppContext, prelude::Routes}; use crate::{ app::AppContextExt, - auth::{api_auth_middleware, webui_auth_middleware, AuthUserInfo}, + auth::{api_auth_middleware, AuthUserInfo}, }; -async fn graphql_playground(header_map: HeaderMap) -> loco_rs::Result> { - let mut playground_config = AltairGraphQLPlayground::new("/api/graphql"); - - if let Some(authorization) = header_map.get(header::AUTHORIZATION) { - if let Ok(authorization) = authorization.to_str() { - playground_config.initial_headers = { - let mut m = HashMap::new(); - m.insert(header::AUTHORIZATION.to_string(), authorization.to_string()); - Some(m) - } - } - } - - let html = Html(playground_config.render("/api/graphql/playground/static/")?); - - Ok(html) -} - async fn graphql_handler( State(ctx): State, Extension(auth_user_info): Extension, @@ -51,21 +22,9 @@ async fn graphql_handler( graphql_service.schema.execute(req).await.into() } -pub fn routes(state: AppContext) -> Routes { - Routes::new() - .prefix("/graphql") - .add( - "/playground", - get(graphql_playground).layer(from_fn_with_state(state.clone(), webui_auth_middleware)), - ) - .add( - "/", - post(graphql_handler).layer(from_fn_with_state(state, api_auth_middleware)), - ) -} - -pub fn asset_middlewares() -> Vec> { - vec![Box::new(altair_graphql_playground_asset_middleware( - "/api/graphql/playground/static/", - ))] +pub fn routes(ctx: Arc) -> Routes { + Routes::new().prefix("/graphql").add( + "/", + post(graphql_handler).layer(from_fn_with_state(ctx, api_auth_middleware)), + ) } diff --git a/apps/recorder/src/controllers/graphql/playground.rs b/apps/recorder/src/controllers/graphql/playground.rs deleted file mode 100644 index 0b539f2..0000000 --- a/apps/recorder/src/controllers/graphql/playground.rs +++ /dev/null @@ -1,74 +0,0 @@ -use std::collections::HashMap; - -use lazy_static::lazy_static; -use loco_rs::controller::middleware::static_assets::{FolderConfig, StaticAssets}; -use regex::Regex; -use serde::Serialize; -use serde_json::Value; - -use crate::app::App; - -const ALTAIR_GRAPHQL_HTML: &'static str = - include_str!("../../../node_modules/altair-static/build/dist/index.html"); - -lazy_static! { - static ref ALTAIR_GRAPHQL_BASE_REGEX: Regex = Regex::new(r"").unwrap(); -} - -#[derive(Debug, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct AltairGraphQLPlayground<'a> { - #[serde(rename = "endpointURL")] - pub endpoint_url: &'a str, - /** - * URL to set as the subscription endpoint. This can be relative or - * absolute. - */ - pub subscriptions_endpoint: Option<&'a str>, - pub initial_headers: Option>, - pub initial_settings: Option>, - #[serde(flatten)] - pub other: Option>, -} - -impl<'a> AltairGraphQLPlayground<'a> { - /// Create a config for GraphQL playground. - pub fn new(endpoint_url: &'a str) -> Self { - Self { - endpoint_url, - subscriptions_endpoint: Default::default(), - initial_headers: Default::default(), - initial_settings: Default::default(), - other: Default::default(), - } - } - - pub fn render(&self, base_url: &str) -> loco_rs::Result { - let option = serde_json::to_string(self)?; - let render_str = ALTAIR_GRAPHQL_BASE_REGEX - .replace(ALTAIR_GRAPHQL_HTML, format!(r#""#)) - .replace( - "", - &format!("", option), - ); - - Ok(render_str) - } -} - -pub fn altair_graphql_playground_asset_middleware(base_url: &str) -> StaticAssets { - StaticAssets { - enable: true, - must_exist: true, - folder: FolderConfig { - uri: String::from(base_url), - path: App::get_working_root() - .join("node_modules/altair-static/build/dist") - .into(), - }, - fallback: App::get_working_root() - .join("assets/static/404.html") - .into(), - precompressed: false, - } -} diff --git a/apps/recorder/src/controllers/graphql/style.css b/apps/recorder/src/controllers/graphql/style.css new file mode 100644 index 0000000..e69de29 diff --git a/apps/recorder/src/controllers/index.tsx b/apps/recorder/src/controllers/index.tsx new file mode 100644 index 0000000..bba6f1b --- /dev/null +++ b/apps/recorder/src/controllers/index.tsx @@ -0,0 +1,9 @@ +import { createFileRoute } from '@tanstack/react-router'; + +export const Route = createFileRoute('/')({ + component: RouteComponent, +}); + +function RouteComponent() { + return
Hello to playground!
; +} diff --git a/apps/recorder/src/controllers/mod.rs b/apps/recorder/src/controllers/mod.rs index e292702..2f46754 100644 --- a/apps/recorder/src/controllers/mod.rs +++ b/apps/recorder/src/controllers/mod.rs @@ -1,2 +1,2 @@ -pub mod auth; pub mod graphql; +pub mod oidc; diff --git a/apps/recorder/src/controllers/oidc/callback.tsx b/apps/recorder/src/controllers/oidc/callback.tsx new file mode 100644 index 0000000..db92113 --- /dev/null +++ b/apps/recorder/src/controllers/oidc/callback.tsx @@ -0,0 +1,42 @@ +import { createFileRoute, redirect } from '@tanstack/react-router'; +import { useEffect } from 'react'; +import { useAuth } from 'react-oidc-context'; +import { PostLoginRedirectUriKey } from '../../auth/config'; + +export const Route = createFileRoute('/oidc/callback')({ + component: RouteComponent, + beforeLoad: ({ context }) => { + if (!context.auth) { + throw redirect({ + to: '/', + }); + } + }, +}); + +function RouteComponent() { + const auth = useAuth(); + + useEffect(() => { + if (!auth?.isLoading && auth?.isAuthenticated) { + try { + const redirectUri = sessionStorage.getItem(PostLoginRedirectUriKey); + if (redirectUri) { + history.replaceState(null, '', redirectUri); + } + // biome-ignore lint/suspicious/noEmptyBlockStatements: + } catch {} + } + }, [auth]); + + if (auth?.isLoading) { + return
Loading...
; + } + + return ( +
+ OpenID Connect Auth Callback Result:{' '} + {auth.error ? auth.error?.message : 'unknown'} +
+ ); +} diff --git a/apps/recorder/src/controllers/oidc/mod.rs b/apps/recorder/src/controllers/oidc/mod.rs new file mode 100644 index 0000000..4914a90 --- /dev/null +++ b/apps/recorder/src/controllers/oidc/mod.rs @@ -0,0 +1,70 @@ +use std::sync::Arc; + +use axum::{extract::Query, http::request::Parts}; +use loco_rs::prelude::*; + +use crate::{ + app::AppContextExt, + auth::{ + oidc::{OidcAuthCallbackPayload, OidcAuthCallbackQuery, OidcAuthRequest}, + AppAuthService, AuthError, AuthService, + }, + extract::http::ForwardedRelatedInfo, + models::auth::AuthType, +}; + +async fn oidc_callback( + State(ctx): State>, + Query(query): Query, +) -> Result, AuthError> { + let auth_service = ctx.get_auth_service(); + if let AppAuthService::Oidc(oidc_auth_service) = auth_service { + let response = oidc_auth_service + .extract_authorization_request_callback(query) + .await?; + Ok(Json(response)) + } else { + Err(AuthError::NotSupportAuthMethod { + supported: vec![auth_service.auth_type()], + current: AuthType::Oidc, + }) + } +} + +async fn oidc_auth( + State(ctx): State>, + parts: Parts, +) -> Result, AuthError> { + let auth_service = ctx.get_auth_service(); + if let AppAuthService::Oidc(oidc_auth_service) = auth_service { + let mut redirect_uri = ForwardedRelatedInfo::from_request_parts(&parts) + .resolved_origin() + .ok_or_else(|| AuthError::OidcRequestRedirectUriError(url::ParseError::EmptyHost))?; + + redirect_uri.set_path("/api/oidc/callback"); + + let auth_request = oidc_auth_service + .build_authorization_request(redirect_uri.as_str()) + .await?; + + { + oidc_auth_service + .store_authorization_request(auth_request.clone()) + .await?; + } + + Ok(Json(auth_request)) + } else { + Err(AuthError::NotSupportAuthMethod { + supported: vec![auth_service.auth_type()], + current: AuthType::Oidc, + }) + } +} + +pub fn routes(state: Arc) -> Routes { + Routes::new() + .prefix("/oidc") + .add("/auth", get(oidc_auth).with_state(state.clone())) + .add("/callback", get(oidc_callback).with_state(state)) +} diff --git a/apps/recorder/src/env.d.ts b/apps/recorder/src/env.d.ts new file mode 100644 index 0000000..b0ac762 --- /dev/null +++ b/apps/recorder/src/env.d.ts @@ -0,0 +1 @@ +/// diff --git a/apps/recorder/src/extract/errors.rs b/apps/recorder/src/extract/errors.rs index ed03791..1e73257 100644 --- a/apps/recorder/src/extract/errors.rs +++ b/apps/recorder/src/extract/errors.rs @@ -1,7 +1,7 @@ use thiserror::Error; #[derive(Error, Debug)] -pub enum ParseError { +pub enum ExtractError { #[error("Parse bangumi season error: {0}")] BangumiSeasonError(#[from] std::num::ParseIntError), #[error("Parse file url error: {0}")] diff --git a/apps/recorder/src/extract/http.rs b/apps/recorder/src/extract/http.rs new file mode 100644 index 0000000..4470ba9 --- /dev/null +++ b/apps/recorder/src/extract/http.rs @@ -0,0 +1,174 @@ +use axum::http::{header, request::Parts, HeaderName, HeaderValue, Uri}; +use itertools::Itertools; +use url::Url; + +/// Fields from a "Forwarded" header per [RFC7239 sec 4](https://www.rfc-editor.org/rfc/rfc7239#section-4) +#[derive(Debug, Clone)] +pub struct ForwardedHeader { + pub for_field: Vec, + pub by: Option, + pub host: Option, + pub proto: Option, +} + +impl ForwardedHeader { + /// Return the 'for' headers as a list of [std::net::IpAddr]'s. + pub fn for_as_ipaddr(self) -> Vec { + self.for_field + .iter() + .filter_map(|ip| { + if ip.contains(']') { + // this is an IPv6 address, get what's between the [] + ip.split(']') + .next()? + .split('[') + .next_back()? + .parse::() + .ok() + } else { + ip.parse::().ok() + } + }) + .collect::>() + } +} + +/// This parses the Forwarded header, and returns a list of the IPs in the +/// "for=" fields. Per [RFC7239 sec 4](https://www.rfc-editor.org/rfc/rfc7239#section-4) +impl TryFrom for ForwardedHeader { + type Error = String; + fn try_from(forwarded: HeaderValue) -> Result { + ForwardedHeader::try_from(&forwarded) + } +} + +/// This parses the Forwarded header, and returns a list of the IPs in the +/// "for=" fields. Per [RFC7239 sec 4](https://www.rfc-editor.org/rfc/rfc7239#section-4) +impl TryFrom<&HeaderValue> for ForwardedHeader { + type Error = String; + fn try_from(forwarded: &HeaderValue) -> Result { + let mut for_field: Vec = Vec::new(); + let mut by: Option = None; + let mut host: Option = None; + let mut proto: Option = None; + // first get the k=v pairs + forwarded + .to_str() + .map_err(|err| err.to_string())? + .split(';') + .for_each(|s| { + let s = s.trim().to_lowercase(); + // The for value can look like this: + // for=192.0.2.43, for=198.51.100.17 + // so we need to handle this case + if s.starts_with("for=") || s.starts_with("for =") { + // we have a valid thing to grab + let chunks: Vec = s + .split(',') + .filter_map(|chunk| { + chunk.trim().split('=').next_back().map(|c| c.to_string()) + }) + .collect::>(); + for_field.extend(chunks); + } else if s.starts_with("by=") { + by = s.split('=').next_back().map(|c| c.to_string()); + } else if s.starts_with("host=") { + host = s.split('=').next_back().map(|c| c.to_string()); + } else if s.starts_with("proto=") { + proto = s.split('=').next_back().map(|c| c.to_string()); + } else { + // probably need to work out what to do here + } + }); + + Ok(ForwardedHeader { + for_field, + by, + host, + proto, + }) + } +} + +#[derive(Clone, Debug)] +pub struct ForwardedRelatedInfo { + pub forwarded: Option, + pub x_forwarded_proto: Option, + pub x_forwarded_host: Option, + pub x_forwarded_for: Option>, + pub host: Option, + pub uri: Uri, + pub origin: Option, +} + +impl ForwardedRelatedInfo { + pub fn from_request_parts(request_parts: &Parts) -> ForwardedRelatedInfo { + let headers = &request_parts.headers; + let forwarded = headers + .get(header::FORWARDED) + .and_then(|s| ForwardedHeader::try_from(s.clone()).ok()); + + let x_forwarded_proto = headers + .get(HeaderName::from_static("x-forwarded-proto")) + .and_then(|s| s.to_str().map(String::from).ok()); + + let x_forwarded_host = headers + .get(HeaderName::from_static("x-forwarded-host")) + .and_then(|s| s.to_str().map(String::from).ok()); + + let x_forwarded_for = headers + .get(HeaderName::from_static("x-forwarded-for")) + .and_then(|s| s.to_str().ok()) + .and_then(|s| { + let l = s.split(",").map(|s| s.trim().to_string()).collect_vec(); + if l.is_empty() { + None + } else { + Some(l) + } + }); + + let host = headers + .get(header::HOST) + .and_then(|s| s.to_str().map(String::from).ok()); + + let origin = headers + .get(header::ORIGIN) + .and_then(|s| s.to_str().map(String::from).ok()); + + ForwardedRelatedInfo { + host, + x_forwarded_for, + x_forwarded_host, + x_forwarded_proto, + forwarded, + uri: request_parts.uri.clone(), + origin, + } + } + + pub fn resolved_protocol(&self) -> Option<&str> { + self.forwarded + .as_ref() + .and_then(|s| s.proto.as_deref()) + .or(self.x_forwarded_proto.as_deref()) + .or(self.uri.scheme_str()) + } + + pub fn resolved_host(&self) -> Option<&str> { + self.forwarded + .as_ref() + .and_then(|s| s.host.as_deref()) + .or(self.x_forwarded_host.as_deref()) + .or(self.uri.host()) + } + + pub fn resolved_origin(&self) -> Option { + if let (Some(protocol), Some(host)) = (self.resolved_protocol(), self.resolved_host()) { + let origin = format!("{}://{}", protocol, host); + Url::parse(&origin).ok() + } else { + None + } + } +} diff --git a/apps/recorder/src/extract/mikan/rss_parser.rs b/apps/recorder/src/extract/mikan/rss_parser.rs index a047d8e..79f81b2 100644 --- a/apps/recorder/src/extract/mikan/rss_parser.rs +++ b/apps/recorder/src/extract/mikan/rss_parser.rs @@ -8,7 +8,7 @@ use url::Url; use crate::{ extract::{ - errors::ParseError, + errors::ExtractError, mikan::{ web_parser::{parse_mikan_episode_id_from_homepage, MikanEpisodeHomepage}, AppMikanClient, @@ -101,7 +101,7 @@ impl MikanRssChannel { } impl TryFrom for MikanRssItem { - type Error = ParseError; + type Error = ExtractError; fn try_from(item: rss::Item) -> Result { let mime_type = item @@ -113,7 +113,7 @@ impl TryFrom for MikanRssItem { let homepage = item .link - .ok_or_else(|| ParseError::MikanRssItemFormatError { + .ok_or_else(|| ExtractError::MikanRssItemFormatError { reason: String::from("must to have link for homepage"), })?; @@ -124,7 +124,7 @@ impl TryFrom for MikanRssItem { let MikanEpisodeHomepage { mikan_episode_id, .. } = parse_mikan_episode_id_from_homepage(&homepage).ok_or_else(|| { - ParseError::MikanRssItemFormatError { + ExtractError::MikanRssItemFormatError { reason: String::from("homepage link format invalid"), } })?; @@ -142,7 +142,7 @@ impl TryFrom for MikanRssItem { mikan_episode_id, }) } else { - Err(ParseError::MimeError { + Err(ExtractError::MimeError { expected: String::from(BITTORRENT_MIME_TYPE), found: mime_type, desc: String::from("MikanRssItem"), @@ -291,7 +291,7 @@ pub async fn parse_mikan_rss_channel_from_rss_link( }, )); } else { - return Err(ParseError::MikanRssFormatError { + return Err(ExtractError::MikanRssFormatError { url: url.as_str().into(), } .into()); diff --git a/apps/recorder/src/extract/mod.rs b/apps/recorder/src/extract/mod.rs index 410f0e4..54fbd9f 100644 --- a/apps/recorder/src/extract/mod.rs +++ b/apps/recorder/src/extract/mod.rs @@ -1,6 +1,7 @@ pub mod defs; pub mod errors; pub mod html; +pub mod http; pub mod mikan; pub mod rawname; pub mod torrent; diff --git a/apps/recorder/src/fetch/client.rs b/apps/recorder/src/fetch/client.rs index 9b66718..a2a7227 100644 --- a/apps/recorder/src/fetch/client.rs +++ b/apps/recorder/src/fetch/client.rs @@ -1,6 +1,10 @@ -use std::{ops::Deref, time::Duration}; +use std::{ops::Deref, sync::Arc, time::Duration}; -use axum::http::Extensions; +use async_trait::async_trait; +use axum::http::{self, Extensions}; +use http_cache_reqwest::{ + CACacheManager, Cache, CacheManager, CacheMode, HttpCache, HttpCacheOptions, MokaManager, +}; use leaky_bucket::RateLimiter; use once_cell::sync::OnceCell; use reqwest::{ClientBuilder, Request, Response}; @@ -11,9 +15,29 @@ use reqwest_retry::{policies::ExponentialBackoff, RetryTransientMiddleware}; use reqwest_tracing::TracingMiddleware; use serde::{Deserialize, Serialize}; use serde_with::serde_as; -use async_trait::async_trait; +use thiserror::Error; use super::get_random_mobile_ua; +use crate::app::App; + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "snake_case", tag = "type")] +pub enum HttpClientCacheBackendConfig { + Moka { cache_size: u64 }, + CACache { cache_path: String }, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub enum HttpClientCachePresetConfig { + #[serde(rename = "rfc7234")] + RFC7234, +} + +impl Default for HttpClientCachePresetConfig { + fn default() -> Self { + Self::RFC7234 + } +} #[serde_as] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)] @@ -25,6 +49,51 @@ pub struct HttpClientConfig { #[serde_as(as = "Option")] pub leaky_bucket_refill_interval: Option, pub user_agent: Option, + pub cache_backend: Option, + pub cache_preset: Option, +} + +struct CacheBackend(Box); + +impl CacheBackend { + fn new(backend: T) -> Self { + Self(Box::new(backend)) + } +} + +#[async_trait::async_trait] +impl CacheManager for CacheBackend { + async fn get( + &self, + cache_key: &str, + ) -> http_cache::Result> + { + self.0.get(cache_key).await + } + + /// Attempts to cache a response and related policy. + async fn put( + &self, + cache_key: String, + res: http_cache::HttpResponse, + policy: http_cache_semantics::CachePolicy, + ) -> http_cache::Result { + self.0.put(cache_key, res, policy).await + } + /// Attempts to remove a record from cache. + async fn delete(&self, cache_key: &str) -> http_cache::Result<()> { + self.0.delete(cache_key).await + } +} + +#[derive(Debug, Error)] +pub enum HttpClientError { + #[error(transparent)] + ReqwestError(#[from] reqwest::Error), + #[error(transparent)] + ReqwestMiddlewareError(#[from] reqwest_middleware::Error), + #[error(transparent)] + HttpError(#[from] http::Error), } pub struct HttpClient { @@ -64,7 +133,7 @@ impl reqwest_middleware::Middleware for RateLimiterMiddleware { } impl HttpClient { - pub fn from_config(config: HttpClientConfig) -> reqwest::Result { + pub fn from_config(config: HttpClientConfig) -> Result { let reqwest_client_builder = ClientBuilder::new().user_agent( config .user_agent @@ -72,6 +141,10 @@ impl HttpClient { .unwrap_or_else(|| get_random_mobile_ua()), ); + #[cfg(not(target_arch = "wasm32"))] + let reqwest_client_builder = + reqwest_client_builder.redirect(reqwest::redirect::Policy::none()); + let reqwest_client = reqwest_client_builder.build()?; let mut reqwest_with_middleware_builder = @@ -112,6 +185,38 @@ impl HttpClient { reqwest_with_middleware_builder.with(RateLimiterMiddleware { rate_limiter }); } + if let (None, None) = (config.cache_backend.as_ref(), config.cache_preset.as_ref()) { + } else { + let cache_preset = config.cache_preset.as_ref().cloned().unwrap_or_default(); + let cache_backend = config + .cache_backend + .as_ref() + .map(|b| match b { + HttpClientCacheBackendConfig::CACache { cache_path } => { + let path = std::path::PathBuf::from( + App::get_working_root().join(cache_path).as_str(), + ); + CacheBackend::new(CACacheManager { path }) + } + HttpClientCacheBackendConfig::Moka { cache_size } => { + CacheBackend::new(MokaManager { + cache: Arc::new(moka::future::Cache::new(u64::max(*cache_size, 1))), + }) + } + }) + .unwrap_or_else(|| CacheBackend::new(MokaManager::default())); + + let http_cache = match cache_preset { + HttpClientCachePresetConfig::RFC7234 => HttpCache { + mode: CacheMode::Default, + manager: cache_backend, + options: HttpCacheOptions::default(), + }, + }; + reqwest_with_middleware_builder = + reqwest_with_middleware_builder.with(Cache(http_cache)); + } + let reqwest_with_middleware = reqwest_with_middleware_builder.build(); Ok(Self { diff --git a/apps/recorder/src/fetch/mod.rs b/apps/recorder/src/fetch/mod.rs index 1da43a5..a8b569c 100644 --- a/apps/recorder/src/fetch/mod.rs +++ b/apps/recorder/src/fetch/mod.rs @@ -3,10 +3,11 @@ pub mod client; pub mod core; pub mod html; pub mod image; +pub mod oidc; pub use core::get_random_mobile_ua; pub use bytes::fetch_bytes; -pub use client::{HttpClient, HttpClientConfig}; +pub use client::{HttpClient, HttpClientConfig, HttpClientError}; pub use html::fetch_html; pub use image::fetch_image; diff --git a/apps/recorder/src/fetch/oidc.rs b/apps/recorder/src/fetch/oidc.rs new file mode 100644 index 0000000..5159b41 --- /dev/null +++ b/apps/recorder/src/fetch/oidc.rs @@ -0,0 +1,36 @@ +use std::{future::Future, pin::Pin}; + +use axum::http; + +use super::{client::HttpClientError, HttpClient}; + +impl<'c> openidconnect::AsyncHttpClient<'c> for HttpClient { + type Error = HttpClientError; + + #[cfg(target_arch = "wasm32")] + type Future = Pin> + 'c>>; + #[cfg(not(target_arch = "wasm32"))] + type Future = + Pin> + Send + 'c>>; + + fn call(&'c self, request: openidconnect::HttpRequest) -> Self::Future { + Box::pin(async move { + let response = self.execute(request.try_into()?).await?; + + let mut builder = http::Response::builder().status(response.status()); + + #[cfg(not(target_arch = "wasm32"))] + { + builder = builder.version(response.version()); + } + + for (name, value) in response.headers().iter() { + builder = builder.header(name, value); + } + + builder + .body(response.bytes().await?.to_vec()) + .map_err(HttpClientError::HttpError) + }) + } +} diff --git a/apps/recorder/src/index.tsx b/apps/recorder/src/index.tsx new file mode 100644 index 0000000..6005622 --- /dev/null +++ b/apps/recorder/src/index.tsx @@ -0,0 +1,13 @@ +import React from 'react'; +import ReactDOM from 'react-dom/client'; +import { App } from './app'; + +const rootEl = document.getElementById('root'); +if (rootEl) { + const root = ReactDOM.createRoot(rootEl); + root.render( + + + + ); +} diff --git a/apps/recorder/src/routeTree.gen.ts b/apps/recorder/src/routeTree.gen.ts new file mode 100644 index 0000000..0cf6ee0 --- /dev/null +++ b/apps/recorder/src/routeTree.gen.ts @@ -0,0 +1,134 @@ +/* eslint-disable */ + +// @ts-nocheck + +// noinspection JSUnusedGlobalSymbols + +// This file was automatically generated by TanStack Router. +// You should NOT make any changes in this file as it will be overwritten. +// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified. + +// Import Routes + +import { Route as rootRoute } from './controllers/__root' +import { Route as IndexImport } from './controllers/index' +import { Route as GraphqlIndexImport } from './controllers/graphql/index' +import { Route as OidcCallbackImport } from './controllers/oidc/callback' + +// Create/Update Routes + +const IndexRoute = IndexImport.update({ + id: '/', + path: '/', + getParentRoute: () => rootRoute, +} as any) + +const GraphqlIndexRoute = GraphqlIndexImport.update({ + id: '/graphql/', + path: '/graphql/', + getParentRoute: () => rootRoute, +} as any) + +const OidcCallbackRoute = OidcCallbackImport.update({ + id: '/oidc/callback', + path: '/oidc/callback', + getParentRoute: () => rootRoute, +} as any) + +// Populate the FileRoutesByPath interface + +declare module '@tanstack/react-router' { + interface FileRoutesByPath { + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexImport + parentRoute: typeof rootRoute + } + '/oidc/callback': { + id: '/oidc/callback' + path: '/oidc/callback' + fullPath: '/oidc/callback' + preLoaderRoute: typeof OidcCallbackImport + parentRoute: typeof rootRoute + } + '/graphql/': { + id: '/graphql/' + path: '/graphql' + fullPath: '/graphql' + preLoaderRoute: typeof GraphqlIndexImport + parentRoute: typeof rootRoute + } + } +} + +// Create and export the route tree + +export interface FileRoutesByFullPath { + '/': typeof IndexRoute + '/oidc/callback': typeof OidcCallbackRoute + '/graphql': typeof GraphqlIndexRoute +} + +export interface FileRoutesByTo { + '/': typeof IndexRoute + '/oidc/callback': typeof OidcCallbackRoute + '/graphql': typeof GraphqlIndexRoute +} + +export interface FileRoutesById { + __root__: typeof rootRoute + '/': typeof IndexRoute + '/oidc/callback': typeof OidcCallbackRoute + '/graphql/': typeof GraphqlIndexRoute +} + +export interface FileRouteTypes { + fileRoutesByFullPath: FileRoutesByFullPath + fullPaths: '/' | '/oidc/callback' | '/graphql' + fileRoutesByTo: FileRoutesByTo + to: '/' | '/oidc/callback' | '/graphql' + id: '__root__' | '/' | '/oidc/callback' | '/graphql/' + fileRoutesById: FileRoutesById +} + +export interface RootRouteChildren { + IndexRoute: typeof IndexRoute + OidcCallbackRoute: typeof OidcCallbackRoute + GraphqlIndexRoute: typeof GraphqlIndexRoute +} + +const rootRouteChildren: RootRouteChildren = { + IndexRoute: IndexRoute, + OidcCallbackRoute: OidcCallbackRoute, + GraphqlIndexRoute: GraphqlIndexRoute, +} + +export const routeTree = rootRoute + ._addFileChildren(rootRouteChildren) + ._addFileTypes() + +/* ROUTE_MANIFEST_START +{ + "routes": { + "__root__": { + "filePath": "__root.tsx", + "children": [ + "/", + "/oidc/callback", + "/graphql/" + ] + }, + "/": { + "filePath": "index.tsx" + }, + "/oidc/callback": { + "filePath": "oidc/callback.tsx" + }, + "/graphql/": { + "filePath": "graphql/index.tsx" + } + } +} +ROUTE_MANIFEST_END */ diff --git a/apps/recorder/src/sync/core.rs b/apps/recorder/src/sync/core.rs index 2d18c1c..afe9b6e 100644 --- a/apps/recorder/src/sync/core.rs +++ b/apps/recorder/src/sync/core.rs @@ -64,7 +64,7 @@ impl TorrentSource { } else if let Some(basename) = url .clone() .path_segments() - .and_then(|segments| segments.last()) + .and_then(|mut segments| segments.next_back()) { if let (Some(match_hash), true) = ( TORRENT_HASH_RE.find(basename), diff --git a/apps/recorder/tests/models/subscribers.rs b/apps/recorder/tests/models/subscribers.rs index 455672d..adc8171 100644 --- a/apps/recorder/tests/models/subscribers.rs +++ b/apps/recorder/tests/models/subscribers.rs @@ -21,7 +21,8 @@ async fn can_find_by_pid() { // testing::seed::(&boot.app_context.db).await.unwrap(); // // let existing_subscriber = - // Model::find_by_pid(&boot.app_context, "11111111-1111-1111-1111-111111111111").await; + // Model::find_by_pid(&boot.app_context, + // "11111111-1111-1111-1111-111111111111").await; // // assert_debug_snapshot!(existing_subscriber); } diff --git a/apps/recorder/tsconfig.json b/apps/recorder/tsconfig.json new file mode 100644 index 0000000..88a04b9 --- /dev/null +++ b/apps/recorder/tsconfig.json @@ -0,0 +1,20 @@ +{ + "extends": "@konobangu/typescript-config/base.json", + "compilerOptions": { + "lib": ["DOM", "ES2022", "DOM.AsyncIterable", "DOM.Iterable"], + "jsx": "react-jsx", + "target": "ES2020", + "noEmit": true, + "skipLibCheck": true, + "useDefineForClassFields": true, + "module": "ESNext", + "isolatedModules": true, + "resolveJsonModule": true, + "moduleResolution": "Bundler", + "allowImportingTsExtensions": true, + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true + }, + "include": ["src"] +} diff --git a/apps/recorder/tsr.config.json b/apps/recorder/tsr.config.json new file mode 100644 index 0000000..977f114 --- /dev/null +++ b/apps/recorder/tsr.config.json @@ -0,0 +1,4 @@ +{ + "routesDirectory": "./src/controllers", + "generatedRouteTree": "./src/routeTree.gen.ts" +} diff --git a/biome.json b/biome.json index 79fcb7e..9f6488a 100644 --- a/biome.json +++ b/biome.json @@ -6,6 +6,9 @@ }, "linter": { "rules": { + "style": { + "noNonNullAssertion": "off" + }, "suspicious": { "noExplicitAny": "off" }, diff --git a/justfile b/justfile index 4a4a3ce..f9a8b34 100644 --- a/justfile +++ b/justfile @@ -15,8 +15,11 @@ dev-proxy: dev-recorder: cargo watch -w apps/recorder -x 'recorder start' +dev-playground: + pnpm run --filter=recorder dev + down-recorder: cargo run -p recorder --bin recorder_cli -- db down 999 --environment development play-recorder: - cargo recorder-playground \ No newline at end of file + cargo recorder-playground diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ecfbc8c..d7498bc 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -203,9 +203,52 @@ importers: apps/recorder: dependencies: - altair-static: - specifier: ^8.1.3 - version: 8.1.3 + '@graphiql/react': + specifier: ^0.28.2 + version: 0.28.2(@codemirror/language@6.0.0)(@types/node@22.10.5)(@types/react-dom@19.0.2(@types/react@19.0.1))(@types/react@19.0.1)(graphql-ws@5.16.2(graphql@16.10.0))(graphql@16.10.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@graphiql/toolkit': + specifier: ^0.11.1 + version: 0.11.1(@types/node@22.10.5)(graphql-ws@5.16.2(graphql@16.10.0))(graphql@16.10.0) + '@tanstack/react-router': + specifier: ^1.95.5 + version: 1.95.5(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@tanstack/router-devtools': + specifier: ^1.95.5 + version: 1.95.5(@tanstack/react-router@1.95.5(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(csstype@3.1.3)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + graphql-ws: + specifier: ^5.16.2 + version: 5.16.2(graphql@16.10.0) + oidc-client-ts: + specifier: ^3.1.0 + version: 3.1.0 + react: + specifier: ^19.0.0 + version: 19.0.0 + react-dom: + specifier: ^19.0.0 + version: 19.0.0(react@19.0.0) + react-oidc-context: + specifier: ^3.2.0 + version: 3.2.0(oidc-client-ts@3.1.0)(react@19.0.0) + devDependencies: + '@rsbuild/core': + specifier: ^1.1.8 + version: 1.1.13 + '@rsbuild/plugin-react': + specifier: ^1.0.7 + version: 1.1.0(@rsbuild/core@1.1.13) + '@tanstack/router-plugin': + specifier: ^1.95.5 + version: 1.95.5(@rsbuild/core@1.1.13)(@tanstack/react-router@1.95.5(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(vite@5.4.11(@types/node@22.10.5)(sass@1.77.4)(terser@5.37.0))(webpack@5.97.1) + '@types/react': + specifier: ^19.0.0 + version: 19.0.1 + '@types/react-dom': + specifier: ^19.0.0 + version: 19.0.2(@types/react@19.0.1) + typescript: + specifier: ^5.7.2 + version: 5.7.3 apps/storybook: dependencies: @@ -329,7 +372,7 @@ importers: version: 1.3.2(react@19.0.0) '@sentry/nextjs': specifier: ^8.43.0 - version: 8.47.0(@opentelemetry/core@1.30.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.56.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.0(@opentelemetry/api@1.9.0))(encoding@0.1.13)(next@15.1.3(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sass@1.77.4))(react@19.0.0)(webpack@5.97.1(esbuild@0.21.5)) + version: 8.47.0(@opentelemetry/core@1.30.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.56.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.0(@opentelemetry/api@1.9.0))(encoding@0.1.13)(next@15.1.3(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sass@1.77.4))(react@19.0.0)(webpack@5.97.1(esbuild@0.23.1)) date-fns: specifier: ^4.1.0 version: 4.1.0 @@ -344,7 +387,7 @@ importers: version: 0.468.0(react@19.0.0) mdx-bundler: specifier: ^10.0.3 - version: 10.0.3(acorn@8.14.0)(esbuild@0.21.5) + version: 10.0.3(acorn@8.14.0)(esbuild@0.23.1) react: specifier: ^19.0.0 version: 19.0.0 @@ -1898,6 +1941,15 @@ packages: peerDependencies: '@clerc/core': '*' + '@codemirror/language@6.0.0': + resolution: {integrity: sha512-rtjk5ifyMzOna1c7PBu7J1VCt0PvA5wy3o8eMVnxMKb7z8KA7JFecvD04dSn14vj/bBaAbqRsGed5OjtofEnLA==} + + '@codemirror/state@6.5.1': + resolution: {integrity: sha512-3rA9lcwciEB47ZevqvD8qgbzhM9qMb8vCcQCNmDfVRPQG4JT9mSb0Jg8H7YjKGGQcFnLN323fj9jdnG59Kx6bg==} + + '@codemirror/view@6.36.2': + resolution: {integrity: sha512-DZ6ONbs8qdJK0fdN7AB82CgI6tYXf4HWk1wSVa0+9bhVznCuuvhQtX8bFBoy3dv8rZSQqUd8GvhVAcielcidrA==} + '@colors/colors@1.5.0': resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==} engines: {node: '>=0.1.90'} @@ -1978,6 +2030,12 @@ packages: '@emnapi/runtime@1.3.1': resolution: {integrity: sha512-kEBmG8KyqtxJZv+ygbEim+KCGtIq1fC22Ms3S4ziXmYKm8uyoLX0MHONVKwp+9opg390VaKRNt4a7A9NwmpNhw==} + '@emotion/is-prop-valid@0.8.8': + resolution: {integrity: sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==} + + '@emotion/memoize@0.7.4': + resolution: {integrity: sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==} + '@endemolshinegroup/cosmiconfig-typescript-loader@3.0.2': resolution: {integrity: sha512-QRVtqJuS1mcT56oHpVegkKBlgtWjXw/gHNWO3eL9oyB5Sc7HBoc2OLG/nYpVfT/Jejvo3NUrD0Udk7XgoyDKkA==} engines: {node: '>=10.0.0'} @@ -2434,6 +2492,29 @@ packages: '@formatjs/intl-localematcher@0.5.9': resolution: {integrity: sha512-8zkGu/sv5euxbjfZ/xmklqLyDGQSxsLqg8XOq88JW3cmJtzhCP8EtSJXlaKZnVO4beEaoiT9wj4eIoCQ9smwxA==} + '@graphiql/react@0.28.2': + resolution: {integrity: sha512-6PE2Ff9dXpyQMHy3oKzCjT738kY2+wdQ4Xce8+1K+G2yMGZ716Fo0i4vW3S6ErHVI4S/C76gFTQlv/pzxU5ylg==} + peerDependencies: + graphql: ^15.5.0 || ^16.0.0 || ^17.0.0 + react: ^16.8.0 || ^17 || ^18 + react-dom: ^16.8.0 || ^17 || ^18 + + '@graphiql/toolkit@0.11.1': + resolution: {integrity: sha512-G02te70/oYYna5UhbH6TXwNxeQyWa+ChlPonUrKwC5Ot9ItraGJ9yUU4sS+YRaA+EvkzNoHG79XcW2k1QaAMiw==} + peerDependencies: + graphql: ^15.5.0 || ^16.0.0 || ^17.0.0 + graphql-ws: '>= 4.5.0' + peerDependenciesMeta: + graphql-ws: + optional: true + + '@headlessui/react@1.7.19': + resolution: {integrity: sha512-Ll+8q3OlMJfJbAKM/+/Y2q6PPYbryqNTXDbryx7SXLIDamkF6iQFbriYHga0dY44PvDhvvBWCx1Xj4U5+G4hOw==} + engines: {node: '>=10'} + peerDependencies: + react: ^16 || ^17 || ^18 + react-dom: ^16 || ^17 || ^18 + '@hexagon/base64@1.1.28': resolution: {integrity: sha512-lhqDEAvWixy3bZ+UOYbPwUbBkwBq5C1LAJ/xPC8Oi+lL54oyakv/npbA0aU2hgCsx/1NUd4IBvV03+aUBWxerw==} @@ -2578,6 +2659,15 @@ packages: '@levischuck/tiny-cbor@0.2.2': resolution: {integrity: sha512-f5CnPw997Y2GQ8FAvtuVVC19FX8mwNNC+1XJcIi16n/LTJifKO6QBgGLgN3YEmqtGMk17SKSuoWES3imJVxAVw==} + '@lezer/common@1.2.3': + resolution: {integrity: sha512-w7ojc8ejBqr2REPsWxJjrMFsA/ysDCFICn8zEOR9mrqzOu2amhITYuLD8ag6XZf0CFXDrhKqw7+tW8cX66NaDA==} + + '@lezer/highlight@1.2.1': + resolution: {integrity: sha512-Z5duk4RN/3zuVO7Jq0pGLJ3qynpxUVsh7IbUbGj88+uV2ApSAn6kWg2au3iJb+0Zi7kKtqffIESgNcRXWZWmSA==} + + '@lezer/lr@1.4.2': + resolution: {integrity: sha512-pu0K1jCIdnQ12aWNaAVU5bzi7Bd1w54J3ECgANPmYLtQKP0HBj2cE/5coBD66MT10xbtIuUr7tg0Shbsvk0mDA==} + '@liveblocks/client@2.15.1': resolution: {integrity: sha512-R4zpISMYxQzMQxCbGWldVCDmvtz8I/32ihyqG/SYccK1UfJxrlFaHyy0Y8DvxYoKLnlmfddqJ6phkuSWodEEMg==} @@ -2598,6 +2688,9 @@ packages: peerDependencies: next: ^12.1.4 || ^13 || ^14 || ^15 + '@marijn/find-cluster-break@1.0.2': + resolution: {integrity: sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==} + '@mdx-js/esbuild@3.1.0': resolution: {integrity: sha512-Jk42xUb1SEJxh6n2GBAtJjQISFIZccjz8XVEsHVhrlvZJAJziIxR9KyaFF6nTeTB/jCAFQGDgO7+oMRH/ApRsg==} peerDependencies: @@ -2624,6 +2717,28 @@ packages: '@module-federation/webpack-bundler-runtime@0.5.1': resolution: {integrity: sha512-mMhRFH0k2VjwHt3Jol9JkUsmI/4XlrAoBG3E0o7HoyoPYv1UFOWyqAflfANcUPgbYpvqmyLzDcO+3IT36LXnrA==} + '@motionone/animation@10.18.0': + resolution: {integrity: sha512-9z2p5GFGCm0gBsZbi8rVMOAJCtw1WqBTIPw3ozk06gDvZInBPIsQcHgYogEJ4yuHJ+akuW8g1SEIOpTOvYs8hw==} + + '@motionone/dom@10.12.0': + resolution: {integrity: sha512-UdPTtLMAktHiqV0atOczNYyDd/d8Cf5fFsd1tua03PqTwwCe/6lwhLSQ8a7TbnQ5SN0gm44N1slBfj+ORIhrqw==} + + '@motionone/easing@10.18.0': + resolution: {integrity: sha512-VcjByo7XpdLS4o9T8t99JtgxkdMcNWD3yHU/n6CLEz3bkmKDRZyYQ/wmSf6daum8ZXqfUAgFeCZSpJZIMxaCzg==} + + '@motionone/generators@10.18.0': + resolution: {integrity: sha512-+qfkC2DtkDj4tHPu+AFKVfR/C30O1vYdvsGYaR13W/1cczPrrcjdvYCj0VLFuRMN+lP1xvpNZHCRNM4fBzn1jg==} + + '@motionone/types@10.17.1': + resolution: {integrity: sha512-KaC4kgiODDz8hswCrS0btrVrzyU2CSQKO7Ps90ibBVSQmjkrt2teqta6/sOG59v7+dPnKMAg13jyqtMKV2yJ7A==} + + '@motionone/utils@10.18.0': + resolution: {integrity: sha512-3XVF7sgyTSI2KWvTf6uLlBJ5iAgRgmvp3bpuOiQJvInd4nZ19ET8lX5unn30SlmRH7hXbBbH+Gxd0m0klJ3Xtw==} + + '@n1ru4l/push-pull-async-iterable-iterator@3.2.0': + resolution: {integrity: sha512-3fkKj25kEjsfObL6IlKPAlHYPq/oYwUkkQ03zsTTiDjD7vg/RxjdiLeCydqtxHZP0JgsXL3D/X5oAkMGzuUp/Q==} + engines: {node: '>=12'} + '@next/bundle-analyzer@15.1.3': resolution: {integrity: sha512-dh5i2KBONWVhQzJnL10sv9+ImsKgGtOHHeA1dWp/H3MXphWBt1uGjXCwPCcitwimvNncHBmxaOyTm2FwfOLRSA==} @@ -4022,6 +4137,16 @@ packages: cpu: [x64] os: [win32] + '@rsbuild/core@1.1.13': + resolution: {integrity: sha512-XBL2hrin8731W6iTGGL+x3cv07n4vm2D7u6XHRwtQkRfySzAqGx7ThlQLdNX/dJwfsoQrYQuWl/qzaljjXtGtg==} + engines: {node: '>=16.7.0'} + hasBin: true + + '@rsbuild/plugin-react@1.1.0': + resolution: {integrity: sha512-uqdRoV2V91G1XIA14dAmxqYTlTDVf0ktpE7TgwG29oQ2j+DerF1kh29WPHK9HvGE34JTfaBrsme2Zmb6bGD0cw==} + peerDependencies: + '@rsbuild/core': 1.x + '@rspack/binding-darwin-arm64@1.1.8': resolution: {integrity: sha512-I7avr471ghQ3LAqKm2fuXuJPLgQ9gffn5Q4nHi8rsukuZUtiLDPfYzK1QuupEp2JXRWM1gG5lIbSUOht3cD6Ug==} cpu: [arm64] @@ -4083,6 +4208,14 @@ packages: resolution: {integrity: sha512-VynGOEsVw2s8TAlLf/uESfrgfrq2+rcXB1muPJYBWbsm1Oa6r5qVQhjA5ggM6z/coYPrsVMgovl3Ff7Q7OCp1w==} engines: {node: '>=16.0.0'} + '@rspack/plugin-react-refresh@1.0.1': + resolution: {integrity: sha512-KSBc3bsr3mrAPViv7w9MpE9KEWm6q87EyRXyHlRfJ9PpQ56NbX9KZ7AXo7jPeECb0q5sfpM2PSEf+syBiMgLSw==} + peerDependencies: + react-refresh: '>=0.10.0 <1.0.0' + peerDependenciesMeta: + react-refresh: + optional: true + '@scena/dragscroll@1.4.0': resolution: {integrity: sha512-3O8daaZD9VXA9CP3dra6xcgt/qrm0mg0xJCwiX6druCteQ9FFsXffkF8PrqxY4Z4VJ58fFKEa0RlKqbsi/XnRA==} @@ -4478,6 +4611,71 @@ packages: peerDependencies: tailwindcss: '>=3.0.0 || insiders || >=4.0.0-alpha.20' + '@tanstack/history@1.95.2': + resolution: {integrity: sha512-FgUauZLg+nRybH5dKyAYokmXs064rHc0qpMizKxTegSTMHj/z2B6T7pjEG2Zd6dBegeYNVL97ROg7Srr9cuBug==} + engines: {node: '>=12'} + + '@tanstack/react-router@1.95.5': + resolution: {integrity: sha512-tNS3an0y5noCqUZf1PYDgd/o7W+YnAyDwRx/ZkD/cQ019M2Jc5comTzo1GwpRz/ozT1aKbe9OSsCi5brEYL0zA==} + engines: {node: '>=12'} + peerDependencies: + react: '>=18' + react-dom: '>=18' + + '@tanstack/react-store@0.7.0': + resolution: {integrity: sha512-S/Rq17HaGOk+tQHV/yrePMnG1xbsKZIl/VsNWnNXt4XW+tTY8dTlvpJH2ZQ3GRALsusG5K6Q3unAGJ2pd9W/Ng==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + '@tanstack/react-virtual@3.11.2': + resolution: {integrity: sha512-OuFzMXPF4+xZgx8UzJha0AieuMihhhaWG0tCqpp6tDzlFwOmNBPYMuLOtMJ1Tr4pXLHmgjcWhG6RlknY2oNTdQ==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + '@tanstack/router-devtools@1.95.5': + resolution: {integrity: sha512-i6wzj3o8Qn1ZODgAYnOTHnVL9h0x1cYD3pzIXLunkhjhyUwECUhBow83gXsTxO/T7D1YFsuQj59TXaAoI+OcyQ==} + engines: {node: '>=12'} + peerDependencies: + '@tanstack/react-router': ^1.95.5 + react: '>=18' + react-dom: '>=18' + + '@tanstack/router-generator@1.95.5': + resolution: {integrity: sha512-Ot/IcIadFBZ7p3CiOVmUUOAT9PP4mIb+36QNtMK6/flstIcLy5+uPdSSjWyM7yeMjLQj8+B22m+djdtZ/XojnA==} + engines: {node: '>=12'} + peerDependencies: + '@tanstack/react-router': ^1.95.5 + peerDependenciesMeta: + '@tanstack/react-router': + optional: true + + '@tanstack/router-plugin@1.95.5': + resolution: {integrity: sha512-leVSXNJi3Vkqd9mA082bWCzz8AxpzCgI+poTBaIgVGwq3BoiU1j4XH/P/J/C1bfaFlg2hEcERmaeugDAaeTuAA==} + engines: {node: '>=12'} + peerDependencies: + '@rsbuild/core': '>=1.0.2' + vite: '>=5.0.0 || >=6.0.0' + webpack: '>=5.92.0' + peerDependenciesMeta: + '@rsbuild/core': + optional: true + vite: + optional: true + webpack: + optional: true + + '@tanstack/store@0.7.0': + resolution: {integrity: sha512-CNIhdoUsmD2NolYuaIs8VfWM467RK6oIBAW4nPEKZhg1smZ+/CwtCdpURgp7nxSqOaV9oKkzdWD80+bC66F/Jg==} + + '@tanstack/virtual-core@3.11.2': + resolution: {integrity: sha512-vTtpNt7mKCiZ1pwU9hfKPhpdVO2sVzFQsxoVBGtOSHxlrRRzYr8iQ2TlwbAcRYCcEiZ9ECAM8kBzH0v2+VzfKw==} + + '@tanstack/virtual-file-routes@1.87.6': + resolution: {integrity: sha512-PTpeM8SHL7AJM0pJOacFvHribbUODS51qe9NsMqku4mogh6BWObY1EeVmeGnp9o3VngAEsf+rJMs2zqIVz3WFA==} + engines: {node: '>=12'} + '@testing-library/dom@10.4.0': resolution: {integrity: sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==} engines: {node: '>=18'} @@ -4615,6 +4813,12 @@ packages: '@types/better-sqlite3@7.6.12': resolution: {integrity: sha512-fnQmj8lELIj7BSrZQAdBMHEHX8OZLYIHXqAKT1O7tDfLxaINzf00PMjw22r3N/xXh0w/sGHlO6SVaCQ2mj78lg==} + '@types/codemirror@0.0.90': + resolution: {integrity: sha512-8Z9+tSg27NPRGubbUPUCrt5DDG/OWzLph5BvcDykwR5D7RyZh5mhHG0uS1ePKV1YFCA+/cwc4Ey2AJAEFfV3IA==} + + '@types/codemirror@5.60.15': + resolution: {integrity: sha512-dTOvwEQ+ouKJ/rE9LT1Ue2hmP6H1mZv5+CCnNWu2qtiOe2LQa9lCprEY20HxiDmV/Bxh+dXjywmy5aKvoGjULA==} + '@types/connect@3.4.36': resolution: {integrity: sha512-P63Zd/JUGq+PdrM1lv0Wv5SBYeA2+CORvbrXbngriYY0jzLUWfQMQQxOhjONEz/wlHOAxOdY7CY65rgQdTjq2w==} @@ -4752,6 +4956,9 @@ packages: '@types/tedious@4.0.14': resolution: {integrity: sha512-KHPsfX/FoVbUGbyYvk1q9MMQHLPeRZhRJZdO45Q4YjvFkv4hMNghCWTvy7rdKessBsmtz4euWCWAB6/tVpI1Iw==} + '@types/tern@0.23.9': + resolution: {integrity: sha512-ypzHFE/wBzh+BlH6rrBgS5I/Z7RD21pGhZ2rltb/+ZrVM1awdZwjx7hE5XfuYgHWk9uvV5HLZN3SloevCAp3Bw==} + '@types/text-table@0.2.5': resolution: {integrity: sha512-hcZhlNvMkQG/k1vcZ6yHOl6WAYftQ2MLfTHcYRZ2xYZFD8tGVnE3qFV0lj1smQeDSR7/yY0PyuUalauf33bJeA==} @@ -5013,10 +5220,6 @@ packages: engines: {node: '>=4'} hasBin: true - altair-static@8.1.3: - resolution: {integrity: sha512-8OUH92VV5XiumtB5gsMDPbinl8nRO7GAKPJCK4Jsmrq8zDn0jgAN52SDl/FdvREjP924naP04DraehabEH2Z6w==} - engines: {node: '>= 6.9.1'} - ansi-colors@4.1.3: resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} engines: {node: '>=6'} @@ -5183,6 +5386,9 @@ packages: b4a@1.6.7: resolution: {integrity: sha512-OnAYlL5b7LEkALw87fUVafQw5rVR9RjwGd4KUwNQ6DrrNmaVaUCgLipfVlzrPQ4tWOR9P0IXGNOx50jYCCdSJg==} + babel-dead-code-elimination@1.0.8: + resolution: {integrity: sha512-og6HQERk0Cmm+nTT4Od2wbPtgABXFMPaHACjbKLulZIFMkYyXZLkUGuAxdgpMJBrxyt/XFpSz++lNzjbcMnPkQ==} + babel-loader@9.2.1: resolution: {integrity: sha512-fqe8naHt46e0yIdkjUZYqddSXfej3AHajX+CSO5X7oy0EmPc6o5Xh+RClNoHjnieWz9AW4kZxW9yyFMhVB1QLA==} engines: {node: '>= 14.15.0'} @@ -5512,6 +5718,10 @@ packages: resolution: {integrity: sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==} engines: {node: '>=0.8'} + clsx@1.2.1: + resolution: {integrity: sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==} + engines: {node: '>=6'} + clsx@2.1.1: resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} engines: {node: '>=6'} @@ -5522,6 +5732,16 @@ packages: react: ^18 || ^19 || ^19.0.0-rc react-dom: ^18 || ^19 || ^19.0.0-rc + codemirror-graphql@2.2.0: + resolution: {integrity: sha512-egIiewf5zEH5LLSkJpJDpYxO1OkNruD0gTWiBrS1JmXk7yjt5WPw7jSmDRkWJx8JheHONltaJPNPWdTUT5LRIQ==} + peerDependencies: + '@codemirror/language': 6.0.0 + codemirror: ^5.65.3 + graphql: ^15.5.0 || ^16.0.0 || ^17.0.0 + + codemirror@5.65.18: + resolution: {integrity: sha512-Gaz4gHnkbHMGgahNt3CA5HBk5lLQBqmD/pBgeB4kQU6OedZmqMBjlRF0LSrp2tJ4wlLNPm2FfaUd1pDy0mdlpA==} + collapse-white-space@2.1.0: resolution: {integrity: sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw==} @@ -5644,6 +5864,9 @@ packages: resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} engines: {node: '>= 0.6'} + copy-to-clipboard@3.3.3: + resolution: {integrity: sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==} + core-js-compat@3.39.0: resolution: {integrity: sha512-VgEUx3VwlExr5no0tXlBt+silBvhTryPwCXRI2Id1PN8WTKu7MreethvddqOubrYxkFdv/RnYrqlv1sFNAUelw==} @@ -5653,6 +5876,9 @@ packages: core-js-pure@3.40.0: resolution: {integrity: sha512-AtDzVIgRrmRKQai62yuSIN5vNiQjcJakJb4fbhVw3ehxx7Lohphvw9SGNWKhLFqSxC4ilD0g/L1huAYFQU3Q6A==} + core-js@3.39.0: + resolution: {integrity: sha512-raM0ew0/jJUqkJ0E6e8UDtl+y/7ktFivgWvqw8dNSQeNWoSDLvQ1H/RN3aPXB9tBd4/FhyR4RDPGhsNIMsAn7g==} + core-util-is@1.0.3: resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} @@ -5834,6 +6060,9 @@ packages: date-fns@4.1.0: resolution: {integrity: sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==} + debounce-promise@3.1.2: + resolution: {integrity: sha512-rZHcgBkbYavBeD9ej6sP56XfG53d51CD4dnaw989YX/nZ/ZJfgRx/9ePKmTNiUiyQvh4mtrMoS3OAWW+yoYtpg==} + debounce@1.2.1: resolution: {integrity: sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==} @@ -6509,6 +6738,15 @@ packages: fraction.js@4.3.7: resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==} + framer-motion@6.5.1: + resolution: {integrity: sha512-o1BGqqposwi7cgDrtg0dNONhkmPsUFDaLcKXigzuTFC5x58mE8iyTazxSudFzmT6MEyJKfjjU8ItoMe3W+3fiw==} + peerDependencies: + react: '>=16.8 || ^17.0.0 || ^18.0.0' + react-dom: '>=16.8 || ^17.0.0 || ^18.0.0' + + framesync@6.0.1: + resolution: {integrity: sha512-fUY88kXvGiIItgNC7wcTOl0SNRCVXMKSWW2Yzfmn7EKNc+MpCzcz9DhdHcdjbrtN3c6R4H5dTY2jiCpPdysEjA==} + framework-utils@1.1.0: resolution: {integrity: sha512-KAfqli5PwpFJ8o3psRNs8svpMGyCSAe8nmGcjQ0zZBWN2H6dZDnq+ABp3N3hdUmFeMrLtjOCTXD4yplUJIWceg==} @@ -6629,6 +6867,10 @@ packages: resolution: {integrity: sha512-E1b1lFFLvLgak2whF2xDBcOy6NLVGZBqqjJjsIhvopKfWWEi64pLVTWWehV8KlLerZkfNTA95sTe2OdJKm1OzQ==} engines: {node: '>= 14'} + get-value@3.0.1: + resolution: {integrity: sha512-mKZj9JLQrwMBtj5wxi6MH8Z5eSKaERpAwjg43dPtlGI1ZVEgH/qC7T8/6R2OBSUA+zzHBZgICsVJaEIV2tKTDA==} + engines: {node: '>=6.0'} + github-from-package@0.0.0: resolution: {integrity: sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==} @@ -6684,6 +6926,11 @@ packages: resolution: {integrity: sha512-7dUi7RvCoT/xast/o/dLN53oqND4yk0nsHkhRgn9w65C4PofCLOoJ39iSOg+qVDdWQPIEj+eszMHQ+aLVwwQSg==} engines: {node: '>=8'} + goober@2.1.16: + resolution: {integrity: sha512-erjk19y1U33+XAMe1VTvIONHYoSqE4iS7BYUZfHaqeohLmnC0FdxEh7rQU+6MZ4OajItzjZFSRtVANrQwNq6/g==} + peerDependencies: + csstype: ^3.0.10 + gopd@1.2.0: resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} engines: {node: '>= 0.4'} @@ -6698,6 +6945,22 @@ packages: resolution: {integrity: sha512-rEDCuqUQ4tbD78TpzsMtt5OIf0cBCSDWSJtUDaF6JsAh+k0v9r++NzxNEG87oDZx9ZwGhD8DaezR2L/yrw0Jdw==} engines: {node: '>=10'} + graphql-language-service@5.3.0: + resolution: {integrity: sha512-gCQIIy7lM9CB1KPLEb+DNZLczA9zuTLEOJE2hEQZTFYInogdmMDRa6RAkvM4LL0LcgcS+3uPs6KtHlcjCqRbUg==} + hasBin: true + peerDependencies: + graphql: ^15.5.0 || ^16.0.0 || ^17.0.0-alpha.2 + + graphql-ws@5.16.2: + resolution: {integrity: sha512-E1uccsZxt/96jH/OwmLPuXMACILs76pKF2i3W861LpKBCYtGIyPQGtWLuBLkND4ox1KHns70e83PS4te50nvPQ==} + engines: {node: '>=10'} + peerDependencies: + graphql: '>=0.11 <=16' + + graphql@16.10.0: + resolution: {integrity: sha512-AjqGKbDGUFRKIRCP9tCKiIGHyriz2oHEbPIbEtcSLSs4YjReZOIPQQWek4+6hjw62H9QShXHyaGivGiYVLeYFQ==} + engines: {node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0} + gray-matter@4.0.3: resolution: {integrity: sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q==} engines: {node: '>=6.0'} @@ -6775,6 +7038,9 @@ packages: header-case@1.0.1: resolution: {integrity: sha512-i0q9mkOeSuhXw6bGgiQCCBgY/jlZuV/7dZXyZ9c6LcBrqwvT8eT719E9uxE5LiZftdl+z81Ugbg/VvXV4OJOeQ==} + hey-listen@1.0.8: + resolution: {integrity: sha512-COpmrF2NOg4TBWUJ5UVyaCU2A88wEMkUPK4hNqyCkqHbxT92BbvfjoSozkAIIm6XhicGlJHhFdullInrdhwU8Q==} + hmac-drbg@1.0.1: resolution: {integrity: sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==} @@ -7095,6 +7361,10 @@ packages: resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} engines: {node: '>=12'} + is-plain-object@2.0.4: + resolution: {integrity: sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==} + engines: {node: '>=0.10.0'} + is-plain-object@5.0.0: resolution: {integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==} engines: {node: '>=0.10.0'} @@ -7105,6 +7375,10 @@ packages: is-potential-custom-element-name@1.0.1: resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} + is-primitive@3.0.1: + resolution: {integrity: sha512-GljRxhWvlCNRfZyORiH77FwdFwGcMO620o37EOYC0ORWdq+WYNVqW0w2Juzew4M+L81l6/QS3t5gkkihyRqv9w==} + engines: {node: '>=0.10.0'} + is-reference@1.2.1: resolution: {integrity: sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==} @@ -7179,6 +7453,10 @@ packages: isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + isobject@3.0.1: + resolution: {integrity: sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==} + engines: {node: '>=0.10.0'} + iterm2-version@4.2.0: resolution: {integrity: sha512-IoiNVk4SMPu6uTcK+1nA5QaHNok2BMDLjSl5UomrOixe5g4GkylhPwuiGdw00ysSCrXAKNMfFTu+u/Lk5f6OLQ==} engines: {node: '>=8'} @@ -7287,6 +7565,10 @@ packages: jsonfile@6.1.0: resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} + jwt-decode@4.0.0: + resolution: {integrity: sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA==} + engines: {node: '>=18'} + keycode@2.2.1: resolution: {integrity: sha512-Rdgz9Hl9Iv4QKi8b0OlCRQEzp4AgVxyCtz5S/+VIHezDmrDhkp2N2TqBWOLz0/gbeREXOOiI9/4b8BY9uw2vFg==} @@ -7314,6 +7596,9 @@ packages: lines-and-columns@1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + linkify-it@5.0.0: + resolution: {integrity: sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==} + lite-emit@2.3.0: resolution: {integrity: sha512-QMPrnwPho7lfkzZUN3a0RJ/oiwpt464eXf6aVh1HGOYh+s7Utu78q3FcFbW59c8TNWWQaz9flKN1cEb8dmxD+g==} @@ -7447,6 +7732,10 @@ packages: resolution: {integrity: sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q==} engines: {node: '>=16'} + markdown-it@14.1.0: + resolution: {integrity: sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==} + hasBin: true + markdown-table@3.0.4: resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==} @@ -7524,6 +7813,9 @@ packages: mdn-data@2.0.30: resolution: {integrity: sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==} + mdurl@2.0.0: + resolution: {integrity: sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==} + mdx-bundler@10.0.3: resolution: {integrity: sha512-vRtVZ5t+nUP0QtoRVgjDFO10YDjRgKe/19ie0IR8FqE8SugNn5RP4sCWBPzKoEwoGbqfQOrgHy+PHCVyfaCDQQ==} engines: {node: '>=18', npm: '>=6'} @@ -7551,6 +7843,15 @@ packages: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} + meros@1.3.0: + resolution: {integrity: sha512-2BNGOimxEz5hmjUG2FwoxCt5HN7BXdaWyFqEwxPTrJzVdABtrL4TiHTcsWSFAxPQ/tOnEaQEJh3qWq71QRMY+w==} + engines: {node: '>=13'} + peerDependencies: + '@types/node': '>=13' + peerDependenciesMeta: + '@types/node': + optional: true + methods@1.1.2: resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==} engines: {node: '>= 0.6'} @@ -7935,6 +8236,9 @@ packages: nth-check@2.1.1: resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} + nullthrows@1.1.1: + resolution: {integrity: sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw==} + nwsapi@2.2.16: resolution: {integrity: sha512-F1I/bimDpj3ncaNDhfyMWuFqmQDBwDB0Fogc2qpL3BWvkQteFD/8BzWuIRl83rq0DXfm8SGt/HFhLXZyljTXcQ==} @@ -7968,6 +8272,10 @@ packages: obuf@1.1.2: resolution: {integrity: sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==} + oidc-client-ts@3.1.0: + resolution: {integrity: sha512-IDopEXjiwjkmJLYZo6BTlvwOtnlSniWZkKZoXforC/oLZHC9wkIxd25Kwtmo5yKFMMVcsp3JY6bhcNJqdYk8+g==} + engines: {node: '>=18'} + on-finished@2.4.1: resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} engines: {node: '>= 0.8'} @@ -8314,6 +8622,9 @@ packages: resolution: {integrity: sha512-OBatVyC/N7SCW/FaDHrSd+vn0o5cS855TOmYi4OkdWUMSJCET/xip//ch8xGUvtr3i44X9LVyWwQlRMTN3pwSA==} engines: {node: '>=10'} + popmotion@11.0.3: + resolution: {integrity: sha512-Y55FLdj3UxkR7Vl3s7Qr4e9m0onSnP8W7d/xQLsoJM40vs6UKHFdygs6SWryasTZYqugMjm3BepCF4CWXDiHgA==} + possible-typed-array-names@1.0.0: resolution: {integrity: sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==} engines: {node: '>= 0.4'} @@ -8510,6 +8821,10 @@ packages: pump@3.0.2: resolution: {integrity: sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==} + punycode.js@2.3.1: + resolution: {integrity: sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==} + engines: {node: '>=6'} + punycode@1.4.1: resolution: {integrity: sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==} @@ -8562,6 +8877,11 @@ packages: resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} hasBin: true + react-compiler-runtime@19.0.0-beta-37ed2a7-20241206: + resolution: {integrity: sha512-9e6rCpVylr9EnVocgYAjft7+2v01BDpajeHKRoO+oc9pKcAMTpstHtHvE/TSVbyf4FvzCGjfKcfHM9XGTXI6Tw==} + peerDependencies: + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + react-confetti@6.2.2: resolution: {integrity: sha512-K+kTyOPgX+ZujMZ+Rmb7pZdHBvg+DzinG/w4Eh52WOB8/pfO38efnnrtEZNJmjTvLxc16RBYO+tPM68Fg8viBA==} engines: {node: '>=16'} @@ -8625,6 +8945,13 @@ packages: react-moveable@0.56.0: resolution: {integrity: sha512-FmJNmIOsOA36mdxbrc/huiE4wuXSRlmon/o+/OrfNhSiYYYL0AV5oObtPluEhb2Yr/7EfYWBHTxF5aWAvjg1SA==} + react-oidc-context@3.2.0: + resolution: {integrity: sha512-ZLaCRLWV84Cn9pFdsatmblqxLMv0np69GWVXq9RWGqAjppdOGXNIbIxWMByIio0oSCVUwdeqwYRnJme0tjqd8A==} + engines: {node: '>=18'} + peerDependencies: + oidc-client-ts: ^3.1.0 + react: '>=16.8.0' + react-promise-suspense@0.3.4: resolution: {integrity: sha512-I42jl7L3Ze6kZaq+7zXWSunBa3b1on5yfvUW6Eo/3fFOj6dZ5Bqmcd264nJbTK/gn1HjjILAjSwnZbV4RpSaNQ==} @@ -8632,6 +8959,10 @@ packages: resolution: {integrity: sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==} engines: {node: '>=0.10.0'} + react-refresh@0.16.0: + resolution: {integrity: sha512-FPvF2XxTSikpJxcr+bHut2H4gJ17+18Uy20D5/F+SKzFap62R3cM5wH6b8WN3LyGSYeQilLEcJcR1fjBSI2S1A==} + engines: {node: '>=0.10.0'} + react-remove-scroll-bar@2.3.8: resolution: {integrity: sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==} engines: {node: '>=10'} @@ -9105,6 +9436,10 @@ packages: resolution: {integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==} engines: {node: '>= 0.4'} + set-value@4.1.0: + resolution: {integrity: sha512-zTEg4HL0RwVrqcWs3ztF+x1vkxfm0lP+MQQFPiMJTKVceBwEV0A569Ou8l9IYQG8jOZdMVI1hGsc0tmeD2o/Lw==} + engines: {node: '>=11.0'} + setimmediate@1.0.5: resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==} @@ -9415,12 +9750,18 @@ packages: peerDependencies: webpack: ^5.0.0 + style-mod@4.1.2: + resolution: {integrity: sha512-wnD1HyVqpJUI2+eKZ+eo1UwghftP6yuFheBqqe+bWCotBjC2K1YnteJILRMs3SM4V/0dLEW1SC27MWP5y+mwmw==} + style-to-object@0.4.4: resolution: {integrity: sha512-HYNoHZa2GorYNyqiCaBgsxvcJIn7OHq6inEga+E6Ke3m5JkoqpQbnFssk4jwe+K7AhGa2fcha4wSOf1Kn01dMg==} style-to-object@1.0.8: resolution: {integrity: sha512-xT47I/Eo0rwJmaXC4oilDGDWLohVhR6o/xAQcPQN8q6QBuZVL8qMYL85kLmST5cPjAorwvqIA4qXTRQoYHaL6g==} + style-value-types@5.0.0: + resolution: {integrity: sha512-08yq36Ikn4kx4YU6RD7jWEv27v4V+PUsOGa4n/as8Et3CuODMJQ00ENeAVXAeydX4Z2j1XHZF1K2sX4mGl18fA==} + styled-jsx@5.1.6: resolution: {integrity: sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==} engines: {node: '>= 12.0.0'} @@ -9573,6 +9914,9 @@ packages: tiny-invariant@1.3.3: resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} + tiny-warning@1.0.3: + resolution: {integrity: sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==} + tinybench@2.9.0: resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} @@ -9619,6 +9963,9 @@ packages: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} + toggle-selection@1.0.6: + resolution: {integrity: sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==} + toidentifier@1.0.1: resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} engines: {node: '>=0.6'} @@ -9811,6 +10158,9 @@ packages: resolution: {integrity: sha512-VAH4IvQ7BDFYglMd7BPRDfLgxZZX4O4TFcRDA6EN5X7erNJJq+McIEp8np9aVtxrCJ6qx4GTYVfOWNjcqwZgRw==} engines: {node: '>=8'} + uc.micro@2.1.0: + resolution: {integrity: sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==} + uglify-js@3.19.3: resolution: {integrity: sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==} engines: {node: '>=0.8.0'} @@ -10069,6 +10419,12 @@ packages: vm-browserify@1.1.2: resolution: {integrity: sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==} + vscode-languageserver-types@3.17.5: + resolution: {integrity: sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==} + + w3c-keyname@2.2.8: + resolution: {integrity: sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==} + w3c-xmlserializer@5.0.0: resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==} engines: {node: '>=18'} @@ -11383,6 +11739,25 @@ snapshots: dependencies: '@clerc/core': 0.44.0 + '@codemirror/language@6.0.0': + dependencies: + '@codemirror/state': 6.5.1 + '@codemirror/view': 6.36.2 + '@lezer/common': 1.2.3 + '@lezer/highlight': 1.2.1 + '@lezer/lr': 1.4.2 + style-mod: 4.1.2 + + '@codemirror/state@6.5.1': + dependencies: + '@marijn/find-cluster-break': 1.0.2 + + '@codemirror/view@6.36.2': + dependencies: + '@codemirror/state': 6.5.1 + style-mod: 4.1.2 + w3c-keyname: 2.2.8 + '@colors/colors@1.5.0': optional: true @@ -11509,6 +11884,14 @@ snapshots: tslib: 2.8.1 optional: true + '@emotion/is-prop-valid@0.8.8': + dependencies: + '@emotion/memoize': 0.7.4 + optional: true + + '@emotion/memoize@0.7.4': + optional: true + '@endemolshinegroup/cosmiconfig-typescript-loader@3.0.2(cosmiconfig@7.0.0)(typescript@5.7.3)': dependencies: cosmiconfig: 7.0.0 @@ -11529,6 +11912,16 @@ snapshots: transitivePeerDependencies: - supports-color + '@esbuild-plugins/node-resolve@0.2.2(esbuild@0.23.1)': + dependencies: + '@types/resolve': 1.20.6 + debug: 4.4.0 + esbuild: 0.23.1 + escape-string-regexp: 4.0.0 + resolve: 1.22.10 + transitivePeerDependencies: + - supports-color + '@esbuild/aix-ppc64@0.19.11': optional: true @@ -11764,6 +12157,52 @@ snapshots: dependencies: tslib: 2.8.1 + '@graphiql/react@0.28.2(@codemirror/language@6.0.0)(@types/node@22.10.5)(@types/react-dom@19.0.2(@types/react@19.0.1))(@types/react@19.0.1)(graphql-ws@5.16.2(graphql@16.10.0))(graphql@16.10.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + dependencies: + '@graphiql/toolkit': 0.11.1(@types/node@22.10.5)(graphql-ws@5.16.2(graphql@16.10.0))(graphql@16.10.0) + '@headlessui/react': 1.7.19(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-dialog': 1.1.4(@types/react-dom@19.0.2(@types/react@19.0.1))(@types/react@19.0.1)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-dropdown-menu': 2.1.4(@types/react-dom@19.0.2(@types/react@19.0.1))(@types/react@19.0.1)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-tooltip': 1.1.6(@types/react-dom@19.0.2(@types/react@19.0.1))(@types/react@19.0.1)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-visually-hidden': 1.1.1(@types/react-dom@19.0.2(@types/react@19.0.1))(@types/react@19.0.1)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@types/codemirror': 5.60.15 + clsx: 1.2.1 + codemirror: 5.65.18 + codemirror-graphql: 2.2.0(@codemirror/language@6.0.0)(codemirror@5.65.18)(graphql@16.10.0) + copy-to-clipboard: 3.3.3 + framer-motion: 6.5.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + get-value: 3.0.1 + graphql: 16.10.0 + graphql-language-service: 5.3.0(graphql@16.10.0) + markdown-it: 14.1.0 + react: 19.0.0 + react-compiler-runtime: 19.0.0-beta-37ed2a7-20241206(react@19.0.0) + react-dom: 19.0.0(react@19.0.0) + set-value: 4.1.0 + transitivePeerDependencies: + - '@codemirror/language' + - '@types/node' + - '@types/react' + - '@types/react-dom' + - graphql-ws + + '@graphiql/toolkit@0.11.1(@types/node@22.10.5)(graphql-ws@5.16.2(graphql@16.10.0))(graphql@16.10.0)': + dependencies: + '@n1ru4l/push-pull-async-iterable-iterator': 3.2.0 + graphql: 16.10.0 + meros: 1.3.0(@types/node@22.10.5) + optionalDependencies: + graphql-ws: 5.16.2(graphql@16.10.0) + transitivePeerDependencies: + - '@types/node' + + '@headlessui/react@1.7.19(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + dependencies: + '@tanstack/react-virtual': 3.11.2(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + client-only: 0.0.1 + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + '@hexagon/base64@1.1.28': {} '@hookform/resolvers@3.9.1(react-hook-form@7.54.2(react@19.0.0))': @@ -11883,6 +12322,16 @@ snapshots: '@levischuck/tiny-cbor@0.2.2': {} + '@lezer/common@1.2.3': {} + + '@lezer/highlight@1.2.1': + dependencies: + '@lezer/common': 1.2.3 + + '@lezer/lr@1.4.2': + dependencies: + '@lezer/common': 1.2.3 + '@liveblocks/client@2.15.1': dependencies: '@liveblocks/core': 2.15.1 @@ -11909,6 +12358,8 @@ snapshots: next: 15.1.3(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sass@1.77.4) whatwg-fetch: 3.6.20 + '@marijn/find-cluster-break@1.0.2': {} + '@mdx-js/esbuild@3.1.0(acorn@8.14.0)(esbuild@0.21.5)': dependencies: '@mdx-js/mdx': 3.1.0(acorn@8.14.0) @@ -11921,6 +12372,18 @@ snapshots: - acorn - supports-color + '@mdx-js/esbuild@3.1.0(acorn@8.14.0)(esbuild@0.23.1)': + dependencies: + '@mdx-js/mdx': 3.1.0(acorn@8.14.0) + '@types/unist': 3.0.3 + esbuild: 0.23.1 + source-map: 0.7.4 + vfile: 6.0.3 + vfile-message: 4.0.2 + transitivePeerDependencies: + - acorn + - supports-color + '@mdx-js/mdx@3.1.0(acorn@8.14.0)': dependencies: '@types/estree': 1.0.6 @@ -11961,21 +12424,54 @@ snapshots: dependencies: '@module-federation/runtime': 0.5.1 '@module-federation/webpack-bundler-runtime': 0.5.1 - optional: true '@module-federation/runtime@0.5.1': dependencies: '@module-federation/sdk': 0.5.1 - optional: true - '@module-federation/sdk@0.5.1': - optional: true + '@module-federation/sdk@0.5.1': {} '@module-federation/webpack-bundler-runtime@0.5.1': dependencies: '@module-federation/runtime': 0.5.1 '@module-federation/sdk': 0.5.1 - optional: true + + '@motionone/animation@10.18.0': + dependencies: + '@motionone/easing': 10.18.0 + '@motionone/types': 10.17.1 + '@motionone/utils': 10.18.0 + tslib: 2.8.1 + + '@motionone/dom@10.12.0': + dependencies: + '@motionone/animation': 10.18.0 + '@motionone/generators': 10.18.0 + '@motionone/types': 10.17.1 + '@motionone/utils': 10.18.0 + hey-listen: 1.0.8 + tslib: 2.8.1 + + '@motionone/easing@10.18.0': + dependencies: + '@motionone/utils': 10.18.0 + tslib: 2.8.1 + + '@motionone/generators@10.18.0': + dependencies: + '@motionone/types': 10.17.1 + '@motionone/utils': 10.18.0 + tslib: 2.8.1 + + '@motionone/types@10.17.1': {} + + '@motionone/utils@10.18.0': + dependencies: + '@motionone/types': 10.17.1 + hey-listen: 1.0.8 + tslib: 2.8.1 + + '@n1ru4l/push-pull-async-iterable-iterator@3.2.0': {} '@next/bundle-analyzer@15.1.3(bufferutil@4.0.9)': dependencies: @@ -13407,6 +13903,19 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.29.1': optional: true + '@rsbuild/core@1.1.13': + dependencies: + '@rspack/core': 1.1.8(@swc/helpers@0.5.15) + '@rspack/lite-tapable': 1.0.1 + '@swc/helpers': 0.5.15 + core-js: 3.39.0 + + '@rsbuild/plugin-react@1.1.0(@rsbuild/core@1.1.13)': + dependencies: + '@rsbuild/core': 1.1.13 + '@rspack/plugin-react-refresh': 1.0.1(react-refresh@0.16.0) + react-refresh: 0.16.0 + '@rspack/binding-darwin-arm64@1.1.8': optional: true @@ -13445,7 +13954,6 @@ snapshots: '@rspack/binding-win32-arm64-msvc': 1.1.8 '@rspack/binding-win32-ia32-msvc': 1.1.8 '@rspack/binding-win32-x64-msvc': 1.1.8 - optional: true '@rspack/core@1.1.8(@swc/helpers@0.5.15)': dependencies: @@ -13455,10 +13963,15 @@ snapshots: caniuse-lite: 1.0.30001690 optionalDependencies: '@swc/helpers': 0.5.15 - optional: true - '@rspack/lite-tapable@1.0.1': - optional: true + '@rspack/lite-tapable@1.0.1': {} + + '@rspack/plugin-react-refresh@1.0.1(react-refresh@0.16.0)': + dependencies: + error-stack-parser: 2.1.4 + html-entities: 2.5.2 + optionalDependencies: + react-refresh: 0.16.0 '@scena/dragscroll@1.4.0': dependencies: @@ -13562,7 +14075,7 @@ snapshots: '@sentry/core@8.47.0': {} - '@sentry/nextjs@8.47.0(@opentelemetry/core@1.30.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.56.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.0(@opentelemetry/api@1.9.0))(encoding@0.1.13)(next@15.1.3(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sass@1.77.4))(react@19.0.0)(webpack@5.97.1(esbuild@0.21.5))': + '@sentry/nextjs@8.47.0(@opentelemetry/core@1.30.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.56.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.0(@opentelemetry/api@1.9.0))(encoding@0.1.13)(next@15.1.3(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sass@1.77.4))(react@19.0.0)(webpack@5.97.1(esbuild@0.23.1))': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/semantic-conventions': 1.28.0 @@ -13573,7 +14086,7 @@ snapshots: '@sentry/opentelemetry': 8.47.0(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.30.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.56.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.28.0) '@sentry/react': 8.47.0(react@19.0.0) '@sentry/vercel-edge': 8.47.0 - '@sentry/webpack-plugin': 2.22.7(encoding@0.1.13)(webpack@5.97.1(esbuild@0.21.5)) + '@sentry/webpack-plugin': 2.22.7(encoding@0.1.13)(webpack@5.97.1(esbuild@0.23.1)) chalk: 3.0.0 next: 15.1.3(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sass@1.77.4) resolve: 1.22.8 @@ -13675,12 +14188,12 @@ snapshots: '@opentelemetry/api': 1.9.0 '@sentry/core': 8.47.0 - '@sentry/webpack-plugin@2.22.7(encoding@0.1.13)(webpack@5.97.1(esbuild@0.21.5))': + '@sentry/webpack-plugin@2.22.7(encoding@0.1.13)(webpack@5.97.1(esbuild@0.23.1))': dependencies: '@sentry/bundler-plugin-core': 2.22.7(encoding@0.1.13) unplugin: 1.0.1 uuid: 9.0.1 - webpack: 5.97.1(esbuild@0.21.5) + webpack: 5.97.1(esbuild@0.23.1) transitivePeerDependencies: - encoding - supports-color @@ -14155,6 +14668,84 @@ snapshots: postcss-selector-parser: 6.0.10 tailwindcss: 3.4.17(ts-node@10.9.2(@types/node@22.10.5)(typescript@5.7.3)) + '@tanstack/history@1.95.2': {} + + '@tanstack/react-router@1.95.5(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + dependencies: + '@tanstack/history': 1.95.2 + '@tanstack/react-store': 0.7.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + jsesc: 3.1.0 + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + tiny-invariant: 1.3.3 + tiny-warning: 1.0.3 + + '@tanstack/react-store@0.7.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + dependencies: + '@tanstack/store': 0.7.0 + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + use-sync-external-store: 1.4.0(react@19.0.0) + + '@tanstack/react-virtual@3.11.2(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + dependencies: + '@tanstack/virtual-core': 3.11.2 + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + + '@tanstack/router-devtools@1.95.5(@tanstack/react-router@1.95.5(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(csstype@3.1.3)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + dependencies: + '@tanstack/react-router': 1.95.5(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + clsx: 2.1.1 + goober: 2.1.16(csstype@3.1.3) + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + transitivePeerDependencies: + - csstype + + '@tanstack/router-generator@1.95.5(@tanstack/react-router@1.95.5(react-dom@19.0.0(react@19.0.0))(react@19.0.0))': + dependencies: + '@tanstack/virtual-file-routes': 1.87.6 + prettier: 3.4.2 + tsx: 4.19.2 + zod: 3.24.1 + optionalDependencies: + '@tanstack/react-router': 1.95.5(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + + '@tanstack/router-plugin@1.95.5(@rsbuild/core@1.1.13)(@tanstack/react-router@1.95.5(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(vite@5.4.11(@types/node@22.10.5)(sass@1.77.4)(terser@5.37.0))(webpack@5.97.1)': + dependencies: + '@babel/core': 7.26.0 + '@babel/generator': 7.26.3 + '@babel/parser': 7.26.3 + '@babel/plugin-syntax-jsx': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-syntax-typescript': 7.25.9(@babel/core@7.26.0) + '@babel/template': 7.25.9 + '@babel/traverse': 7.26.4 + '@babel/types': 7.26.3 + '@tanstack/router-generator': 1.95.5(@tanstack/react-router@1.95.5(react-dom@19.0.0(react@19.0.0))(react@19.0.0)) + '@tanstack/virtual-file-routes': 1.87.6 + '@types/babel__core': 7.20.5 + '@types/babel__generator': 7.6.8 + '@types/babel__template': 7.4.4 + '@types/babel__traverse': 7.20.6 + babel-dead-code-elimination: 1.0.8 + chokidar: 3.6.0 + unplugin: 1.16.0 + zod: 3.24.1 + optionalDependencies: + '@rsbuild/core': 1.1.13 + vite: 5.4.11(@types/node@22.10.5)(sass@1.77.4)(terser@5.37.0) + webpack: 5.97.1 + transitivePeerDependencies: + - '@tanstack/react-router' + - supports-color + + '@tanstack/store@0.7.0': {} + + '@tanstack/virtual-core@3.11.2': {} + + '@tanstack/virtual-file-routes@1.87.6': {} + '@testing-library/dom@10.4.0': dependencies: '@babel/code-frame': 7.26.2 @@ -14333,6 +14924,14 @@ snapshots: dependencies: '@types/node': 22.10.1 + '@types/codemirror@0.0.90': + dependencies: + '@types/tern': 0.23.9 + + '@types/codemirror@5.60.15': + dependencies: + '@types/tern': 0.23.9 + '@types/connect@3.4.36': dependencies: '@types/node': 22.10.5 @@ -14483,6 +15082,10 @@ snapshots: dependencies: '@types/node': 22.10.5 + '@types/tern@0.23.9': + dependencies: + '@types/estree': 1.0.6 + '@types/text-table@0.2.5': {} '@types/through@0.0.33': @@ -14820,8 +15423,6 @@ snapshots: transitivePeerDependencies: - encoding - altair-static@8.1.3: {} - ansi-colors@4.1.3: {} ansi-escapes@4.3.2: @@ -14979,6 +15580,15 @@ snapshots: b4a@1.6.7: {} + babel-dead-code-elimination@1.0.8: + dependencies: + '@babel/core': 7.26.0 + '@babel/parser': 7.26.3 + '@babel/traverse': 7.26.4 + '@babel/types': 7.26.3 + transitivePeerDependencies: + - supports-color + babel-loader@9.2.1(@babel/core@7.26.0)(webpack@5.97.1(esbuild@0.23.1)): dependencies: '@babel/core': 7.26.0 @@ -15375,6 +15985,8 @@ snapshots: clone@2.1.2: {} + clsx@1.2.1: {} + clsx@2.1.1: {} cmdk@1.0.4(@types/react-dom@19.0.2(@types/react@19.0.1))(@types/react@19.0.1)(react-dom@19.0.0(react@19.0.0))(react@19.0.0): @@ -15389,6 +16001,16 @@ snapshots: - '@types/react' - '@types/react-dom' + codemirror-graphql@2.2.0(@codemirror/language@6.0.0)(codemirror@5.65.18)(graphql@16.10.0): + dependencies: + '@codemirror/language': 6.0.0 + '@types/codemirror': 0.0.90 + codemirror: 5.65.18 + graphql: 16.10.0 + graphql-language-service: 5.3.0(graphql@16.10.0) + + codemirror@5.65.18: {} + collapse-white-space@2.1.0: {} color-convert@1.9.3: @@ -15488,6 +16110,10 @@ snapshots: cookie@0.7.2: {} + copy-to-clipboard@3.3.3: + dependencies: + toggle-selection: 1.0.6 + core-js-compat@3.39.0: dependencies: browserslist: 4.24.3 @@ -15496,6 +16122,8 @@ snapshots: core-js-pure@3.40.0: {} + core-js@3.39.0: {} + core-util-is@1.0.3: {} cors@2.8.5: @@ -15723,6 +16351,8 @@ snapshots: date-fns@4.1.0: {} + debounce-promise@3.1.2: {} + debounce@1.2.1: {} debounce@2.0.0: {} @@ -16560,6 +17190,23 @@ snapshots: fraction.js@4.3.7: {} + framer-motion@6.5.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0): + dependencies: + '@motionone/dom': 10.12.0 + framesync: 6.0.1 + hey-listen: 1.0.8 + popmotion: 11.0.3 + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + style-value-types: 5.0.0 + tslib: 2.8.1 + optionalDependencies: + '@emotion/is-prop-valid': 0.8.8 + + framesync@6.0.1: + dependencies: + tslib: 2.8.1 + framework-utils@1.1.0: {} fresh@0.5.2: {} @@ -16691,6 +17338,10 @@ snapshots: transitivePeerDependencies: - supports-color + get-value@3.0.1: + dependencies: + isobject: 3.0.1 + github-from-package@0.0.0: {} github-slugger@2.0.0: {} @@ -16772,6 +17423,10 @@ snapshots: merge2: 1.4.1 slash: 3.0.0 + goober@2.1.16(csstype@3.1.3): + dependencies: + csstype: 3.1.3 + gopd@1.2.0: {} graceful-fs@4.2.11: {} @@ -16783,6 +17438,19 @@ snapshots: chalk: 4.1.2 tinygradient: 1.1.5 + graphql-language-service@5.3.0(graphql@16.10.0): + dependencies: + debounce-promise: 3.1.2 + graphql: 16.10.0 + nullthrows: 1.1.1 + vscode-languageserver-types: 3.17.5 + + graphql-ws@5.16.2(graphql@16.10.0): + dependencies: + graphql: 16.10.0 + + graphql@16.10.0: {} + gray-matter@4.0.3: dependencies: js-yaml: 3.14.1 @@ -16911,6 +17579,8 @@ snapshots: no-case: 2.3.2 upper-case: 1.1.3 + hey-listen@1.0.8: {} + hmac-drbg@1.0.1: dependencies: hash.js: 1.1.7 @@ -17255,12 +17925,18 @@ snapshots: is-plain-obj@4.1.0: {} + is-plain-object@2.0.4: + dependencies: + isobject: 3.0.1 + is-plain-object@5.0.0: {} is-platform@1.0.0: {} is-potential-custom-element-name@1.0.1: {} + is-primitive@3.0.1: {} + is-reference@1.2.1: dependencies: '@types/estree': 1.0.6 @@ -17328,6 +18004,8 @@ snapshots: isexe@2.0.0: {} + isobject@3.0.1: {} + iterm2-version@4.2.0: dependencies: app-path: 3.3.0 @@ -17353,7 +18031,7 @@ snapshots: jest-worker@27.5.1: dependencies: - '@types/node': 22.10.1 + '@types/node': 22.10.5 merge-stream: 2.0.0 supports-color: 8.1.1 @@ -17446,6 +18124,8 @@ snapshots: optionalDependencies: graceful-fs: 4.2.11 + jwt-decode@4.0.0: {} + keycode@2.2.1: {} keycon@1.4.0: @@ -17469,6 +18149,10 @@ snapshots: lines-and-columns@1.2.4: {} + linkify-it@5.0.0: + dependencies: + uc.micro: 2.1.0 + lite-emit@2.3.0: {} load-json-file@4.0.0: @@ -17587,6 +18271,15 @@ snapshots: markdown-extensions@2.0.0: {} + markdown-it@14.1.0: + dependencies: + argparse: 2.0.1 + entities: 4.5.0 + linkify-it: 5.0.0 + mdurl: 2.0.0 + punycode.js: 2.3.1 + uc.micro: 2.1.0 + markdown-table@3.0.4: {} marked@7.0.4: {} @@ -17782,6 +18475,8 @@ snapshots: mdn-data@2.0.30: {} + mdurl@2.0.0: {} + mdx-bundler@10.0.3(acorn@8.14.0)(esbuild@0.21.5): dependencies: '@babel/runtime': 7.26.0 @@ -17798,6 +18493,22 @@ snapshots: - acorn - supports-color + mdx-bundler@10.0.3(acorn@8.14.0)(esbuild@0.23.1): + dependencies: + '@babel/runtime': 7.26.0 + '@esbuild-plugins/node-resolve': 0.2.2(esbuild@0.23.1) + '@fal-works/esbuild-plugin-global-externals': 2.1.2 + '@mdx-js/esbuild': 3.1.0(acorn@8.14.0)(esbuild@0.23.1) + esbuild: 0.23.1 + gray-matter: 4.0.3 + remark-frontmatter: 5.0.0 + remark-mdx-frontmatter: 4.0.0 + uuid: 9.0.1 + vfile: 6.0.3 + transitivePeerDependencies: + - acorn + - supports-color + media-typer@0.3.0: {} memfs@3.5.3: @@ -17814,6 +18525,10 @@ snapshots: merge2@1.4.1: {} + meros@1.3.0(@types/node@22.10.5): + optionalDependencies: + '@types/node': 22.10.5 + methods@1.1.2: {} micromark-core-commonmark@2.0.2: @@ -18385,6 +19100,8 @@ snapshots: dependencies: boolbase: 1.0.0 + nullthrows@1.1.1: {} + nwsapi@2.2.16: {} object-assign@4.1.1: {} @@ -18413,6 +19130,10 @@ snapshots: obuf@1.1.2: {} + oidc-client-ts@3.1.0: + dependencies: + jwt-decode: 4.0.0 + on-finished@2.4.1: dependencies: ee-first: 1.1.1 @@ -18769,6 +19490,13 @@ snapshots: dependencies: '@babel/runtime': 7.26.0 + popmotion@11.0.3: + dependencies: + framesync: 6.0.1 + hey-listen: 1.0.8 + style-value-types: 5.0.0 + tslib: 2.8.1 + possible-typed-array-names@1.0.0: {} postcss-import@15.1.0(postcss@8.4.49): @@ -18905,8 +19633,7 @@ snapshots: prettier@3.3.3: {} - prettier@3.4.2: - optional: true + prettier@3.4.2: {} pretty-error@4.0.0: dependencies: @@ -18975,6 +19702,8 @@ snapshots: end-of-stream: 1.4.4 once: 1.4.0 + punycode.js@2.3.1: {} + punycode@1.4.1: {} punycode@2.3.1: {} @@ -19026,6 +19755,10 @@ snapshots: minimist: 1.2.8 strip-json-comments: 2.0.1 + react-compiler-runtime@19.0.0-beta-37ed2a7-20241206(react@19.0.0): + dependencies: + react: 19.0.0 + react-confetti@6.2.2(react@19.0.0): dependencies: react: 19.0.0 @@ -19143,12 +19876,19 @@ snapshots: react-css-styled: 1.1.9 react-selecto: 1.26.3 + react-oidc-context@3.2.0(oidc-client-ts@3.1.0)(react@19.0.0): + dependencies: + oidc-client-ts: 3.1.0 + react: 19.0.0 + react-promise-suspense@0.3.4: dependencies: fast-deep-equal: 2.0.1 react-refresh@0.14.2: {} + react-refresh@0.16.0: {} + react-remove-scroll-bar@2.3.8(@types/react@19.0.1)(react@19.0.0): dependencies: react: 19.0.0 @@ -19780,6 +20520,11 @@ snapshots: es-errors: 1.3.0 es-object-atoms: 1.0.0 + set-value@4.1.0: + dependencies: + is-plain-object: 2.0.4 + is-primitive: 3.0.1 + setimmediate@1.0.5: {} setprototypeof@1.2.0: {} @@ -20199,6 +20944,8 @@ snapshots: dependencies: webpack: 5.97.1(esbuild@0.23.1) + style-mod@4.1.2: {} + style-to-object@0.4.4: dependencies: inline-style-parser: 0.1.1 @@ -20207,6 +20954,11 @@ snapshots: dependencies: inline-style-parser: 0.2.4 + style-value-types@5.0.0: + dependencies: + hey-listen: 1.0.8 + tslib: 2.8.1 + styled-jsx@5.1.6(@babel/core@7.24.5)(react@19.0.0): dependencies: client-only: 0.0.1 @@ -20403,17 +21155,6 @@ snapshots: ansi-escapes: 4.3.2 supports-hyperlinks: 2.3.0 - terser-webpack-plugin@5.3.11(esbuild@0.21.5)(webpack@5.97.1(esbuild@0.21.5)): - dependencies: - '@jridgewell/trace-mapping': 0.3.25 - jest-worker: 27.5.1 - schema-utils: 4.3.0 - serialize-javascript: 6.0.2 - terser: 5.37.0 - webpack: 5.97.1(esbuild@0.21.5) - optionalDependencies: - esbuild: 0.21.5 - terser-webpack-plugin@5.3.11(esbuild@0.23.1)(webpack@5.97.1(esbuild@0.23.1)): dependencies: '@jridgewell/trace-mapping': 0.3.25 @@ -20467,6 +21208,8 @@ snapshots: tiny-invariant@1.3.3: {} + tiny-warning@1.0.3: {} + tinybench@2.9.0: {} tinycolor2@1.6.0: {} @@ -20508,6 +21251,8 @@ snapshots: dependencies: is-number: 7.0.0 + toggle-selection@1.0.6: {} + toidentifier@1.0.1: {} toml@3.0.0: {} @@ -20706,6 +21451,8 @@ snapshots: typical@4.0.0: {} + uc.micro@2.1.0: {} + uglify-js@3.19.3: optional: true @@ -21054,6 +21801,10 @@ snapshots: vm-browserify@1.1.2: {} + vscode-languageserver-types@3.17.5: {} + + w3c-keyname@2.2.8: {} + w3c-xmlserializer@5.0.0: dependencies: xml-name-validator: 5.0.0 @@ -21142,36 +21893,6 @@ snapshots: - esbuild - uglify-js - webpack@5.97.1(esbuild@0.21.5): - dependencies: - '@types/eslint-scope': 3.7.7 - '@types/estree': 1.0.6 - '@webassemblyjs/ast': 1.14.1 - '@webassemblyjs/wasm-edit': 1.14.1 - '@webassemblyjs/wasm-parser': 1.14.1 - acorn: 8.14.0 - browserslist: 4.24.3 - chrome-trace-event: 1.0.4 - enhanced-resolve: 5.18.0 - es-module-lexer: 1.6.0 - eslint-scope: 5.1.1 - events: 3.3.0 - glob-to-regexp: 0.4.1 - graceful-fs: 4.2.11 - json-parse-even-better-errors: 2.3.1 - loader-runner: 4.3.0 - mime-types: 2.1.35 - neo-async: 2.6.2 - schema-utils: 3.3.0 - tapable: 2.2.1 - terser-webpack-plugin: 5.3.11(esbuild@0.21.5)(webpack@5.97.1(esbuild@0.21.5)) - watchpack: 2.4.2 - webpack-sources: 3.2.3 - transitivePeerDependencies: - - '@swc/core' - - esbuild - - uglify-js - webpack@5.97.1(esbuild@0.23.1): dependencies: '@types/eslint-scope': 3.7.7 diff --git a/public/assets/android-chrome-192x192.png b/public/assets/android-chrome-192x192.png new file mode 100644 index 0000000..879c3ac Binary files /dev/null and b/public/assets/android-chrome-192x192.png differ diff --git a/public/assets/android-chrome-512x512.png b/public/assets/android-chrome-512x512.png new file mode 100644 index 0000000..9958b07 Binary files /dev/null and b/public/assets/android-chrome-512x512.png differ diff --git a/public/assets/apple-touch-icon.png b/public/assets/apple-touch-icon.png new file mode 100644 index 0000000..4ceead8 Binary files /dev/null and b/public/assets/apple-touch-icon.png differ diff --git a/public/assets/favicon-16x16.png b/public/assets/favicon-16x16.png new file mode 100644 index 0000000..81d9c4a Binary files /dev/null and b/public/assets/favicon-16x16.png differ diff --git a/public/assets/favicon-32x32.png b/public/assets/favicon-32x32.png new file mode 100644 index 0000000..b87374c Binary files /dev/null and b/public/assets/favicon-32x32.png differ diff --git a/public/assets/favicon.ico b/public/assets/favicon.ico new file mode 100644 index 0000000..a314325 Binary files /dev/null and b/public/assets/favicon.ico differ diff --git a/public/assets/favicon.png b/public/assets/favicon.png new file mode 100644 index 0000000..ba915d1 Binary files /dev/null and b/public/assets/favicon.png differ diff --git a/public/assets/favicon.webp b/public/assets/favicon.webp new file mode 100644 index 0000000..35c8996 Binary files /dev/null and b/public/assets/favicon.webp differ diff --git a/public/assets/icon.png b/public/assets/icon.png new file mode 100644 index 0000000..8038b94 Binary files /dev/null and b/public/assets/icon.png differ diff --git a/public/assets/icon.webp b/public/assets/icon.webp new file mode 100644 index 0000000..4962e9f Binary files /dev/null and b/public/assets/icon.webp differ diff --git a/public/assets/site.webmanifest b/public/assets/site.webmanifest new file mode 100644 index 0000000..8bb8939 --- /dev/null +++ b/public/assets/site.webmanifest @@ -0,0 +1 @@ +{"background_color":"#ffffff","display":"standalone","icons":[{"sizes":"192x192","src":"/android-chrome-192x192.png","type":"image/png"},{"sizes":"512x512","src":"/android-chrome-512x512.png","type":"image/png"}],"name":"","short_name":"","theme_color":"#ffffff"} \ No newline at end of file diff --git a/rustfmt.toml b/rustfmt.toml index d509e3a..fe826bc 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -5,3 +5,4 @@ imports_granularity = "Crate" use_small_heuristics = "Default" group_imports = "StdExternalCrate" format_strings = true +tab_spaces = 4 diff --git a/turbo.json b/turbo.json index 3682cc8..ac0ec9f 100644 --- a/turbo.json +++ b/turbo.json @@ -1,8 +1,6 @@ { "$schema": "https://turbo.build/schema.json", - "globalDependencies": [ - "**/.env.*local" - ], + "globalDependencies": ["**/.env.*local"], "ui": "tui", "tasks": { "build": { @@ -14,7 +12,6 @@ "BETTERSTACK_URL", "DATABASE_URL", "FLAGS_SECRET", - "STRIPE_SECRET_KEY", "SENTRY_AUTH_TOKEN", "SENTRY_ORG", "SENTRY_PROJECT",