diff --git a/.envrc b/.envrc index 3550a30f2..fc98216c9 100644 --- a/.envrc +++ b/.envrc @@ -1 +1,2 @@ -use flake +PROFILE=$(cat .flake-profile 2>/dev/null) +use flake .#${PROFILE} diff --git a/.github/origin_wfs/core.yml b/.github/origin_wfs/core.yml index 7cb8de18c..3d0e80930 100644 --- a/.github/origin_wfs/core.yml +++ b/.github/origin_wfs/core.yml @@ -154,14 +154,13 @@ jobs: name: easytier-web-dashboard path: easytier-web/frontend/dist/ - - name: Cargo cache + - uses: Swatinem/rust-cache@v2 if: ${{ ! endsWith(matrix.TARGET, 'freebsd') }} - uses: actions/cache@v4 with: - path: | - ~/.cargo - ./target - key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} + # The prefix cache key, this can be changed to start a new cache manually. + # default: "v0-rust" + prefix-key: "" + - name: Setup protoc uses: arduino/setup-protoc@v3 @@ -187,12 +186,12 @@ jobs: fi if [[ $OS =~ ^ubuntu.*$ && $TARGET =~ ^mips.*$ ]]; then - cargo +nightly build -r --target $TARGET -Z build-std=std,panic_abort --package=easytier --features=jemalloc + cargo +nightly-2025-09-01 build -r --target $TARGET -Z build-std=std,panic_abort --package=easytier --features=jemalloc else if [[ $OS =~ ^windows.*$ ]]; then SUFFIX=.exe CORE_FEATURES="--features=mimalloc" - elif [[ $TARGET =~ ^riscv64.*$ ]]; then + elif [[ $TARGET =~ ^riscv64.*$ || $TARGET =~ ^loongarch64.*$ ]]; then CORE_FEATURES="--features=mimalloc" else CORE_FEATURES="--features=jemalloc" diff --git a/.github/origin_wfs/docker.yml b/.github/origin_wfs/docker.yml index 74000d01c..ecc58c73b 100644 --- a/.github/origin_wfs/docker.yml +++ b/.github/origin_wfs/docker.yml @@ -11,7 +11,7 @@ on: image_tag: description: 'Tag for this image build' type: string - default: 'v2.4.2' + default: 'v2.4.5' required: true mark_latest: description: 'Mark this image as latest' diff --git a/.github/origin_wfs/gui.yml b/.github/origin_wfs/gui.yml index 146406c6e..c402ff9bc 100644 --- a/.github/origin_wfs/gui.yml +++ b/.github/origin_wfs/gui.yml @@ -115,7 +115,7 @@ jobs: sudo apt install aptitude sudo aptitude install -y libgstreamer1.0-0:arm64 gstreamer1.0-plugins-base:arm64 gstreamer1.0-plugins-good:arm64 \ libgstreamer-gl1.0-0:arm64 libgstreamer-plugins-base1.0-0:arm64 libgstreamer-plugins-good1.0-0:arm64 libwebkit2gtk-4.1-0:arm64 \ - libwebkit2gtk-4.1-dev:arm64 libssl-dev:arm64 gcc-aarch64-linux-gnu + libwebkit2gtk-4.1-dev:arm64 libssl-dev:arm64 gcc-aarch64-linux-gnu libsoup-3.0-dev:arm64 libjavascriptcoregtk-4.1-dev:arm64 echo "PKG_CONFIG_SYSROOT_DIR=/usr/aarch64-linux-gnu/" >> "$GITHUB_ENV" echo "PKG_CONFIG_PATH=/usr/lib/aarch64-linux-gnu/pkgconfig/" >> "$GITHUB_ENV" @@ -151,13 +151,11 @@ jobs: pnpm -r install pnpm -r build - - name: Cargo cache - uses: actions/cache@v4 + - uses: Swatinem/rust-cache@v2 with: - path: | - ~/.cargo - ./target - key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} + # The prefix cache key, this can be changed to start a new cache manually. + # default: "v0-rust" + prefix-key: "" - name: Install rust target run: bash ./.github/workflows/install_rust.sh diff --git a/.github/origin_wfs/install_rust.sh b/.github/origin_wfs/install_rust.sh index 0c7176cba..778a78b75 100644 --- a/.github/origin_wfs/install_rust.sh +++ b/.github/origin_wfs/install_rust.sh @@ -44,8 +44,8 @@ if [[ $OS =~ ^ubuntu.*$ && $TARGET =~ ^mips.*$ ]]; then ar x libgcc.a _ctzsi2.o _clz.o _bswapsi2.o ar rcs libctz.a _ctzsi2.o _clz.o _bswapsi2.o - rustup toolchain install nightly-x86_64-unknown-linux-gnu - rustup component add rust-src --toolchain nightly-x86_64-unknown-linux-gnu + rustup toolchain install nightly-2025-09-01-x86_64-unknown-linux-gnu + rustup component add rust-src --toolchain nightly-2025-09-01-x86_64-unknown-linux-gnu # https://github.com/rust-lang/rust/issues/128808 # remove it after Cargo or rustc fix this. diff --git a/.github/origin_wfs/mobile.yml b/.github/origin_wfs/mobile.yml index b122c411f..dec7d2559 100644 --- a/.github/origin_wfs/mobile.yml +++ b/.github/origin_wfs/mobile.yml @@ -98,13 +98,11 @@ jobs: pnpm -r install pnpm -r build - - name: Cargo cache - uses: actions/cache@v4 + - uses: Swatinem/rust-cache@v2 with: - path: | - ~/.cargo - ./target - key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} + # The prefix cache key, this can be changed to start a new cache manually. + # default: "v0-rust" + prefix-key: "" - name: Install rust target run: | diff --git a/.github/origin_wfs/release.yml b/.github/origin_wfs/release.yml index 35643179a..fa917eb83 100644 --- a/.github/origin_wfs/release.yml +++ b/.github/origin_wfs/release.yml @@ -21,7 +21,7 @@ on: version: description: 'Version for this release' type: string - default: 'v2.4.2' + default: 'v2.4.5' required: true make_latest: description: 'Mark this release as latest' diff --git a/.gitignore b/.gitignore index d3a7e4651..1559e6abc 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,7 @@ target-*/ .vscode .idea /.direnv/ +/.direnv/ # perf & flamegraph perf.data @@ -41,6 +42,6 @@ easytier-gui/src-tauri/*.dll /easytier-contrib/easytier-ohrs/dist/ .direnv - +.flake-profile et.db* /easytier/config.toml diff --git a/Cargo.lock b/Cargo.lock index b6e0eef87..6c9aabf3f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -129,6 +129,24 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" +[[package]] +name = "android_log-sys" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84521a3cf562bc62942e294181d9eef17eb38ceb8c68677bc49f144e4c3d4f8d" + +[[package]] +name = "android_logger" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c494134f746c14dc653a35a4ea5aca24ac368529da5370ecf41fe0341c35772f" +dependencies = [ + "android_log-sys", + "env_logger", + "log", + "once_cell", +] + [[package]] name = "android_system_properties" version = "0.1.5" @@ -527,8 +545,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "504e3947307ac8326a5437504c517c4b56716c9d98fac0028c2acc7ca47d70ae" dependencies = [ "async-trait", - "axum-core", - "axum-macros", + "axum-core 0.4.5", + "axum-macros 0.4.2", "bytes", "futures-util", "http", @@ -537,7 +555,42 @@ dependencies = [ "hyper", "hyper-util", "itoa", - "matchit", + "matchit 0.7.3", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "rustversion", + "serde", + "serde_json", + "serde_path_to_error", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tower 0.5.2", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "axum" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "021e862c184ae977658b36c4500f7feac3221ca5da43e3f25bd04ab6c79a29b5" +dependencies = [ + "axum-core 0.5.2", + "axum-macros 0.5.0", + "bytes", + "form_urlencoded", + "futures-util", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-util", + "itoa", + "matchit 0.8.4", "memchr", "mime", "percent-encoding", @@ -576,13 +629,33 @@ dependencies = [ "tracing", ] +[[package]] +name = "axum-core" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68464cd0412f486726fb3373129ef5d2993f90c34bc2bc1c1e9943b2f4fc7ca6" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "http-body-util", + "mime", + "pin-project-lite", + "rustversion", + "sync_wrapper", + "tower-layer", + "tower-service", + "tracing", +] + [[package]] name = "axum-embed" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "077959a7f8cf438676af90b483304528eb7e16eadadb7f44e9ada4f9dceb9e62" dependencies = [ - "axum-core", + "axum-core 0.4.5", "chrono", "http", "mime_guess", @@ -597,7 +670,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5260ed0ecc8ace8e7e61a7406672faba598c8a86b8f4742fcdde0ddc979a318f" dependencies = [ "async-trait", - "axum", + "axum 0.7.7", "form_urlencoded", "serde", "subtle", @@ -621,6 +694,17 @@ dependencies = [ "syn 2.0.87", ] +[[package]] +name = "axum-macros" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "604fde5e028fea851ce1d8570bbdc034bec850d157f7569d10f347d06808c05c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + [[package]] name = "axum-messages" version = "0.7.0" @@ -628,7 +712,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40e85c86a8bd84f54833bca296a0204bd865958ade62bacadeae92dda34cfb8a" dependencies = [ "async-trait", - "axum-core", + "axum-core 0.4.5", "http", "parking_lot", "serde", @@ -1304,6 +1388,45 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "console-api" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8030735ecb0d128428b64cd379809817e620a40e5001c54465b99ec5feec2857" +dependencies = [ + "futures-core", + "prost", + "prost-types", + "tonic", + "tracing-core", +] + +[[package]] +name = "console-subscriber" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6539aa9c6a4cd31f4b1c040f860a1eac9aa80e7df6b05d506a6e7179936d6a01" +dependencies = [ + "console-api", + "crossbeam-channel", + "crossbeam-utils", + "futures-task", + "hdrhistogram", + "humantime", + "hyper-util", + "prost", + "prost-types", + "serde", + "serde_json", + "thread_local", + "tokio", + "tokio-stream", + "tonic", + "tracing", + "tracing-core", + "tracing-subscriber", +] + [[package]] name = "const-oid" version = "0.9.6" @@ -1935,6 +2058,12 @@ version = "0.15.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" +[[package]] +name = "downcast" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1435fa1053d8b2fbbe9be7e97eca7f33d37b28409959813daefc1446a14247f1" + [[package]] name = "downcast-rs" version = "1.2.1" @@ -1979,7 +2108,7 @@ checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125" [[package]] name = "easytier" -version = "2.4.2" +version = "2.4.5" dependencies = [ "aes-gcm", "anyhow", @@ -2000,6 +2129,7 @@ dependencies = [ "cidr", "clap", "clap_complete", + "console-subscriber", "crossbeam", "dashmap", "dbus", @@ -2017,6 +2147,7 @@ dependencies = [ "hickory-proto", "hickory-resolver", "hickory-server", + "hmac", "http", "http_req", "humansize", @@ -2062,12 +2193,14 @@ dependencies = [ "serde_json", "serial_test", "service-manager", + "sha2", "smoltcp", "socket2", "stun_codec", "sys-locale", "tabled", "tachyonix", + "tempfile", "thiserror 1.0.63", "thunk-rs", "tikv-jemalloc-ctl", @@ -2084,7 +2217,6 @@ dependencies = [ "toml 0.8.19", "tonic-build", "tracing", - "tracing-appender", "tracing-subscriber", "tun-easytier", "url", @@ -2102,6 +2234,19 @@ dependencies = [ "zstd", ] +[[package]] +name = "easytier-android-jni" +version = "0.1.0" +dependencies = [ + "android_logger", + "easytier", + "jni", + "log", + "once_cell", + "serde", + "serde_json", +] + [[package]] name = "easytier-ffi" version = "0.1.0" @@ -2116,7 +2261,7 @@ dependencies = [ [[package]] name = "easytier-gui" -version = "2.4.2" +version = "2.4.5" dependencies = [ "anyhow", "chrono", @@ -2164,13 +2309,50 @@ dependencies = [ "prost-build", ] +[[package]] +name = "easytier-uptime" +version = "0.1.0" +dependencies = [ + "anyhow", + "async-trait", + "axum 0.8.4", + "chrono", + "clap", + "dashmap", + "easytier", + "futures", + "jsonwebtoken", + "mockall", + "once_cell", + "parking_lot", + "reqwest", + "sea-orm", + "sea-orm-migration", + "serde", + "serde_json", + "serde_yaml", + "sqlx", + "tempfile", + "thiserror 1.0.63", + "tokio", + "tokio-test", + "tokio-util", + "toml 0.8.19", + "tower 0.5.2", + "tower-http", + "tracing", + "tracing-subscriber", + "uuid", + "validator", +] + [[package]] name = "easytier-web" -version = "2.4.2" +version = "2.4.5" dependencies = [ "anyhow", "async-trait", - "axum", + "axum 0.7.7", "axum-embed", "axum-login", "axum-messages", @@ -2369,6 +2551,16 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7f84e12ccf0a7ddc17a6c41c93326024c42920d7ee630d04950e6926645c0fe" +[[package]] +name = "env_logger" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd405aab171cb85d6735e5c8d9db038c17d3ca007a4d2c25f337935c3d90580" +dependencies = [ + "log", + "regex", +] + [[package]] name = "equivalent" version = "1.0.1" @@ -2447,9 +2639,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.1.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] name = "fdeflate" @@ -2573,6 +2765,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "fragile" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dd6caf6059519a65843af8fe2a3ae298b14b80179855aeb4adc2c1934ee619" + [[package]] name = "funty" version = "2.0.0" @@ -3195,11 +3393,24 @@ dependencies = [ "hashbrown 0.14.5", ] +[[package]] +name = "hdrhistogram" +version = "7.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "765c9198f173dd59ce26ff9f95ef0aafd0a0fe01fb9d72841bc5066a4c06511d" +dependencies = [ + "base64 0.21.7", + "byteorder", + "flate2", + "nom", + "num-traits", +] + [[package]] name = "heapless" -version = "0.8.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" +checksum = "b1edcd5a338e64688fbdcb7531a846cfd3476a54784dcb918a0844682bc7ada5" dependencies = [ "hash32", "stable_deref_trait", @@ -3267,7 +3478,7 @@ dependencies = [ "futures-channel", "futures-io", "futures-util", - "idna", + "idna 1.0.3", "ipnet", "once_cell", "rand 0.9.1", @@ -3490,6 +3701,19 @@ dependencies = [ "tower-service", ] +[[package]] +name = "hyper-timeout" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b90d566bffbce6a75bd8b09a05aa8c2cb1fabb6cb348f8840c9e4c90a0d83b0" +dependencies = [ + "hyper", + "hyper-util", + "pin-project-lite", + "tokio", + "tower-service", +] + [[package]] name = "hyper-tls" version = "0.6.0" @@ -3682,6 +3906,16 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + [[package]] name = "idna" version = "1.0.3" @@ -4016,6 +4250,21 @@ dependencies = [ "serde_json", ] +[[package]] +name = "jsonwebtoken" +version = "9.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a87cc7a48537badeae96744432de36f4be2b4a34a05a5ef32e9dd8a1c169dde" +dependencies = [ + "base64 0.22.1", + "js-sys", + "pem", + "ring", + "serde", + "serde_json", + "simple_asn1", +] + [[package]] name = "kcp-sys" version = "0.1.0" @@ -4343,6 +4592,12 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" +[[package]] +name = "matchit" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" + [[package]] name = "matrixmultiply" version = "0.3.9" @@ -4458,6 +4713,33 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "mockall" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43766c2b5203b10de348ffe19f7e54564b64f3d6018ff7648d1e2d6d3a0f0a48" +dependencies = [ + "cfg-if", + "downcast", + "fragile", + "lazy_static", + "mockall_derive", + "predicates", + "predicates-tree", +] + +[[package]] +name = "mockall_derive" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af7cbce79ec385a1d4f54baa90a76401eb15d9cab93685f62e7e9f942aa00ae2" +dependencies = [ + "cfg-if", + "proc-macro2", + "quote", + "syn 2.0.87", +] + [[package]] name = "moka" version = "0.12.10" @@ -5637,6 +5919,26 @@ dependencies = [ "siphasher 0.3.11", ] +[[package]] +name = "pin-project" +version = "1.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + [[package]] name = "pin-project-lite" version = "0.2.14" @@ -5871,6 +6173,32 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" +[[package]] +name = "predicates" +version = "3.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5d19ee57562043d37e82899fade9a22ebab7be9cef5026b07fda9cdd4293573" +dependencies = [ + "anstyle", + "predicates-core", +] + +[[package]] +name = "predicates-core" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "727e462b119fe9c93fd0eb1429a5f7647394014cf3c04ab2c0350eeb09095ffa" + +[[package]] +name = "predicates-tree" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72dd2d6d381dfb73a193c7fca536518d7caee39fc8503f74e7dc0be0531b425c" +dependencies = [ + "predicates-core", + "termtree", +] + [[package]] name = "prefix-trie" version = "0.7.0" @@ -7335,6 +7663,19 @@ dependencies = [ "syn 2.0.87", ] +[[package]] +name = "serde_yaml" +version = "0.9.34+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" +dependencies = [ + "indexmap 2.7.1", + "itoa", + "ryu", + "serde", + "unsafe-libyaml", +] + [[package]] name = "serde_yml" version = "0.0.11" @@ -7514,6 +7855,18 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" +[[package]] +name = "simple_asn1" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "297f631f50729c8c99b84667867963997ec0b50f32b2a7dbcab828ef0541e8bb" +dependencies = [ + "num-bigint", + "num-traits", + "thiserror 2.0.11", + "time", +] + [[package]] name = "siphasher" version = "0.3.11" @@ -7547,8 +7900,7 @@ dependencies = [ [[package]] name = "smoltcp" version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dad095989c1533c1c266d9b1e8d70a1329dd3723c3edac6d03bbd67e7bf6f4bb" +source = "git+https://github.com/smoltcp-rs/smoltcp.git?rev=0a926767a68bc88d5512afefa7529c5ecdade4ea#0a926767a68bc88d5512afefa7529c5ecdade4ea" dependencies = [ "bitflags 1.3.2", "byteorder", @@ -8494,15 +8846,15 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.12.0" +version = "3.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64" +checksum = "84fa4d11fadde498443cca10fd3ac23c951f0dc59e080e9f4b93d4df4e4eea53" dependencies = [ - "cfg-if", "fastrand", + "getrandom 0.3.2", "once_cell", - "rustix 0.38.34", - "windows-sys 0.59.0", + "rustix 1.0.7", + "windows-sys 0.60.2", ] [[package]] @@ -8526,6 +8878,12 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "termtree" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f50febec83f5ee1df3015341d8bd429f2d1cc62bcba7ea2076759d315084683" + [[package]] name = "thiserror" version = "1.0.63" @@ -8702,6 +9060,7 @@ dependencies = [ "signal-hook-registry", "socket2", "tokio-macros", + "tracing", "windows-sys 0.52.0", ] @@ -8751,15 +9110,28 @@ dependencies = [ [[package]] name = "tokio-stream" -version = "0.1.15" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af" +checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047" dependencies = [ "futures-core", "pin-project-lite", "tokio", ] +[[package]] +name = "tokio-test" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2468baabc3311435b55dd935f702f42cd1b8abb7e754fb7dfb16bd36aa88f9f7" +dependencies = [ + "async-stream", + "bytes", + "futures-core", + "tokio", + "tokio-stream", +] + [[package]] name = "tokio-util" version = "0.7.13" @@ -8768,8 +9140,12 @@ checksum = "d7fcaa8d55a2bdd6b83ace262b016eca0d79ee02818c5c1bcdf0305114081078" dependencies = [ "bytes", "futures-core", + "futures-io", "futures-sink", + "futures-util", + "hashbrown 0.14.5", "pin-project-lite", + "slab", "tokio", ] @@ -8903,6 +9279,36 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcc842091f2def52017664b53082ecbbeb5c7731092bad69d2c63050401dfd64" +[[package]] +name = "tonic" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877c5b330756d856ffcc4553ab34a5684481ade925ecc54bcd1bf02b1d0d4d52" +dependencies = [ + "async-stream", + "async-trait", + "axum 0.7.7", + "base64 0.22.1", + "bytes", + "h2", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-timeout", + "hyper-util", + "percent-encoding", + "pin-project", + "prost", + "socket2", + "tokio", + "tokio-stream", + "tower 0.4.13", + "tower-layer", + "tower-service", + "tracing", +] + [[package]] name = "tonic-build" version = "0.12.1" @@ -8922,6 +9328,15 @@ version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" dependencies = [ + "futures-core", + "futures-util", + "indexmap 1.9.3", + "pin-project", + "pin-project-lite", + "rand 0.8.5", + "slab", + "tokio", + "tokio-util", "tower-layer", "tower-service", "tracing", @@ -8950,7 +9365,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fd0118512cf0b3768f7fcccf0bef1ae41d68f2b45edc1e77432b36c97c56c6d" dependencies = [ "async-trait", - "axum-core", + "axum-core 0.4.5", "cookie", "futures-util", "http", @@ -9016,7 +9431,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fb6abbfcaf6436ec5a772cd9f965401da12db793e404ae6134eac066fa5a04f3" dependencies = [ "async-trait", - "axum-core", + "axum-core 0.4.5", "base64 0.22.1", "futures", "http", @@ -9068,18 +9483,6 @@ dependencies = [ "tracing-core", ] -[[package]] -name = "tracing-appender" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3566e8ce28cc0a3fe42519fc80e6b4c943cc4c8cef275620eb8dac2d3d4e06cf" -dependencies = [ - "crossbeam-channel", - "thiserror 1.0.63", - "time", - "tracing-subscriber", -] - [[package]] name = "tracing-attributes" version = "0.1.28" @@ -9369,6 +9772,12 @@ dependencies = [ "subtle", ] +[[package]] +name = "unsafe-libyaml" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" + [[package]] name = "untrusted" version = "0.9.0" @@ -9382,7 +9791,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" dependencies = [ "form_urlencoded", - "idna", + "idna 1.0.3", "percent-encoding", "serde", ] @@ -9454,6 +9863,36 @@ dependencies = [ "syn 2.0.87", ] +[[package]] +name = "validator" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db79c75af171630a3148bd3e6d7c4f42b6a9a014c2945bc5ed0020cbb8d9478e" +dependencies = [ + "idna 0.5.0", + "once_cell", + "regex", + "serde", + "serde_derive", + "serde_json", + "url", + "validator_derive", +] + +[[package]] +name = "validator_derive" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df0bcf92720c40105ac4b2dda2a4ea3aa717d4d6a862cc217da653a4bd5c6b10" +dependencies = [ + "darling", + "once_cell", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.87", +] + [[package]] name = "valuable" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index 4ab877171..0768f4f72 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,8 @@ members = [ "easytier-rpc-build", "easytier-web", "easytier-contrib/easytier-ffi", + "easytier-contrib/easytier-uptime", + "easytier-contrib/easytier-android-jni", ] default-members = ["easytier", "easytier-web"] exclude = [ @@ -14,6 +16,7 @@ exclude = [ [profile.dev] panic = "unwind" +debug = 2 [profile.release] panic = "abort" diff --git a/EasyTier.code-workspace b/EasyTier.code-workspace index ebe44138b..ae221d305 100644 --- a/EasyTier.code-workspace +++ b/EasyTier.code-workspace @@ -27,6 +27,10 @@ "name": "openharmony", "path": "easytier-contrib/easytier-ohrs" }, + { + "name": "uptime", + "path": "easytier-contrib/easytier-uptime" + }, { "name": "vpnservice", "path": "tauri-plugin-vpnservice" @@ -46,5 +50,8 @@ "editor.formatOnSaveMode": "modifications", "editor.formatOnPaste": false, "editor.formatOnType": true, + "[nix]": { + "editor.formatOnSave": false, + }, } } \ No newline at end of file diff --git a/README.md b/README.md index 4bc515ccd..8a6a627e4 100644 --- a/README.md +++ b/README.md @@ -59,7 +59,7 @@ cargo install --git https://github.com/EasyTier/EasyTier.git easytier # See https://easytier.cn/en/guide/installation.html#installation-methods # 4. Linux Quick Install -wget -O- https://raw.githubusercontent.com/EasyTier/EasyTier/main/script/install.sh | sudo bash +wget -O- https://raw.githubusercontent.com/EasyTier/EasyTier/main/script/install.sh | sudo bash -s install # 5. MacOS via Homebrew brew tap brewforge/chinese @@ -105,9 +105,9 @@ After successful execution, you can check the network status using `easytier-cli ```text | ipv4 | hostname | cost | lat_ms | loss_rate | rx_bytes | tx_bytes | tunnel_proto | nat_type | id | version | | ------------ | -------------- | ----- | ------ | --------- | -------- | -------- | ------------ | -------- | ---------- | --------------- | -| 10.126.126.1 | abc-1 | Local | * | * | * | * | udp | FullCone | 439804259 | 2.4.2-70e69a38~ | -| 10.126.126.2 | abc-2 | p2p | 3.452 | 0 | 17.33 kB | 20.42 kB | udp | FullCone | 390879727 | 2.4.2-70e69a38~ | -| | PublicServer_a | p2p | 27.796 | 0.000 | 50.01 kB | 67.46 kB | tcp | Unknown | 3771642457 | 2.4.2-70e69a38~ | +| 10.126.126.1 | abc-1 | Local | * | * | * | * | udp | FullCone | 439804259 | 2.4.5-70e69a38~ | +| 10.126.126.2 | abc-2 | p2p | 3.452 | 0 | 17.33 kB | 20.42 kB | udp | FullCone | 390879727 | 2.4.5-70e69a38~ | +| | PublicServer_a | p2p | 27.796 | 0.000 | 50.01 kB | 67.46 kB | tcp | Unknown | 3771642457 | 2.4.5-70e69a38~ | ``` You can test connectivity between nodes: @@ -286,7 +286,10 @@ sudo easytier-core --network-name mysharednode --network-secret mysharednode ### Contact Us - 💬 **[Telegram Group](https://t.me/easytier)** -- 👥 **[QQ Group: 949700262](https://qm.qq.com/cgi-bin/qm/qr?k=kC8YJ6Jb8vWJIDbZrZJB8pB5YZgPJA5-)** +- 👥 **[QQ Group]** + - No.1 [949700262](https://qm.qq.com/q/wFoTUChqZW) + - No.2 [837676408](https://qm.qq.com/q/4V33DrfgHe) + - No.3 [957189589](https://qm.qq.com/q/YNyTQjwlai) ## License diff --git a/README_CN.md b/README_CN.md index fba6bd68b..9c9b85859 100644 --- a/README_CN.md +++ b/README_CN.md @@ -59,7 +59,7 @@ cargo install --git https://github.com/EasyTier/EasyTier.git easytier # 参见 https://easytier.cn/guide/installation.html#%E5%AE%89%E8%A3%85%E6%96%B9%E5%BC%8F # 4. Linux 快速安装 -wget -O- https://raw.githubusercontent.com/EasyTier/EasyTier/main/script/install.sh | sudo bash +wget -O- https://raw.githubusercontent.com/EasyTier/EasyTier/main/script/install.sh | sudo bash -s install # 5. MacOS 通过 Homebrew 安装 brew tap brewforge/chinese @@ -106,9 +106,9 @@ sudo easytier-core -d --network-name abc --network-secret abc -p tcp://public.ea ```text | ipv4 | hostname | cost | lat_ms | loss_rate | rx_bytes | tx_bytes | tunnel_proto | nat_type | id | version | | ------------ | -------------- | ----- | ------ | --------- | -------- | -------- | ------------ | -------- | ---------- | --------------- | -| 10.126.126.1 | abc-1 | Local | * | * | * | * | udp | FullCone | 439804259 | 2.4.2-70e69a38~ | -| 10.126.126.2 | abc-2 | p2p | 3.452 | 0 | 17.33 kB | 20.42 kB | udp | FullCone | 390879727 | 2.4.2-70e69a38~ | -| | PublicServer_a | p2p | 27.796 | 0.000 | 50.01 kB | 67.46 kB | tcp | Unknown | 3771642457 | 2.4.2-70e69a38~ | +| 10.126.126.1 | abc-1 | Local | * | * | * | * | udp | FullCone | 439804259 | 2.4.5-70e69a38~ | +| 10.126.126.2 | abc-2 | p2p | 3.452 | 0 | 17.33 kB | 20.42 kB | udp | FullCone | 390879727 | 2.4.5-70e69a38~ | +| | PublicServer_a | p2p | 27.796 | 0.000 | 50.01 kB | 67.46 kB | tcp | Unknown | 3771642457 | 2.4.5-70e69a38~ | ``` 您可以测试节点之间的连通性: @@ -287,7 +287,10 @@ sudo easytier-core --network-name mysharednode --network-secret mysharednode ### 联系我们 - 💬 **[Telegram 群组](https://t.me/easytier)** -- 👥 **[QQ 群:949700262](https://qm.qq.com/cgi-bin/qm/qr?k=kC8YJ6Jb8vWJIDbZrZJB8pB5YZgPJA5-)** +- 👥 **QQ 群** + - 一群 [949700262](https://qm.qq.com/q/wFoTUChqZW) + - 二群 [837676408](https://qm.qq.com/q/4V33DrfgHe) + - 三群 [957189589](https://qm.qq.com/q/YNyTQjwlai) ## 许可证 diff --git a/android.nix b/android.nix new file mode 100644 index 000000000..821bcb413 --- /dev/null +++ b/android.nix @@ -0,0 +1,116 @@ +# Android build environment +{ + pkgs, + nixpkgs, + system, +}: + +let + androidEnv = pkgs.callPackage "${nixpkgs}/pkgs/development/mobile/androidenv" { + inherit pkgs; + licenseAccepted = true; + }; + + includeAuto = pkgs.stdenv.hostPlatform.isx86_64 || pkgs.stdenv.hostPlatform.isDarwin; + ndkVersion = "26.1.10909125"; + ndkVersions = [ ndkVersion ]; + + sdkArgs = { + includeNDK = true; + includeSources = true; + includeSystemImages = false; + includeEmulator = false; + inherit ndkVersions; + useGoogleAPIs = true; + useGoogleTVAddOns = true; + buildToolsVersions = [ "34.0.0" ]; + numLatestPlatformVersions = 10; + includeExtras = [ + "extras;google;gcm" + ] + ++ pkgs.lib.optionals includeAuto [ + "extras;google;auto" + ]; + extraLicenses = [ + "android-sdk-preview-license" + "android-googletv-license" + "android-sdk-arm-dbt-license" + "google-gdk-license" + "intel-android-extra-license" + "intel-android-sysimage-license" + "mips-android-sysimage-license" + ]; + }; + + androidComposition = androidEnv.composeAndroidPackages sdkArgs; + androidSdk = androidComposition.androidsdk; + platformTools = androidComposition.platform-tools; + cmake = androidComposition.cmake; + ndkHostTag = + if pkgs.stdenv.isLinux then + "linux-x86_64" + else if pkgs.stdenv.isDarwin then + "darwin-x86_64" + else + ""; + ndkToolchain = "${androidSdk}/libexec/android-sdk/ndk/${ndkVersion}/toolchains/llvm/prebuilt/${ndkHostTag}"; +in +{ + inherit + androidSdk + platformTools + cmake + ndkToolchain + ndkVersion + ; + + # List of packages required for Android development + packages = [ + pkgs.jdk # openjdk 21 + androidSdk + platformTools + cmake + pkgs.glibc_multi.dev + ]; + + # Provide Rust extensions/targets for use by the upper-level flake + rust = { + extensions = [ "rust-std" ]; + targets = [ + "aarch64-linux-android" + "armv7-linux-androideabi" + "i686-linux-android" + "x86_64-linux-android" + ]; + }; + + # Android environment variables and shellHook + envVars = { + LANG = "C.UTF-8"; + LC_ALL = "C.UTF-8"; + JAVA_HOME = "${pkgs.jdk}/lib/openjdk"; + ANDROID_SDK_ROOT = "${androidSdk}/libexec/android-sdk"; + ANDROID_NDK_ROOT = "\${ANDROID_SDK_ROOT}/ndk-bundle"; + NDK_HOME = "${androidSdk}/libexec/android-sdk/ndk/${ndkVersion}"; + LIBCLANG_PATH = "${ndkToolchain}/lib"; + KCP_SYS_EXTRA_HEADER_PATH = "${ndkToolchain}/lib/clang/19/include:${pkgs.glibc_multi.dev}/include"; + ZSTD_SYS_STATIC = "1"; + BINDGEN_EXTRA_CLANG_ARGS = "--sysroot=${ndkToolchain}/sysroot -I${ndkToolchain}/lib/clang/17/include "; + + shellHook = '' + echo "Android environment activated" + export GRADLE_OPTS="-Dorg.gradle.project.android.aapt2FromMavenOverride=$(echo "$ANDROID_SDK_ROOT/build-tools/"*"/aapt2")" + cmake_root="$(echo "$ANDROID_SDK_ROOT/cmake/"*/)" + export PATH="$cmake_root/bin:$PATH" + + unset NIX_CFLAGS_COMPILE + unset NIX_CFLAGS_COMPILE_FOR_BUILD + + cat < easytier-gui/local.properties + sdk.dir=$ANDROID_SDK_ROOT + ndk.dir=$ANDROID_NDK_ROOT + cmake.dir=$cmake_root + EOF + ''; + }; +} diff --git a/easytier-contrib/easytier-android-jni/Cargo.toml b/easytier-contrib/easytier-android-jni/Cargo.toml new file mode 100644 index 000000000..86aa08379 --- /dev/null +++ b/easytier-contrib/easytier-android-jni/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "easytier-android-jni" +version = "0.1.0" +edition = "2021" + +[lib] +crate-type = ["cdylib"] + +[dependencies] +jni = "0.21" +once_cell = "1.18.0" +log = "0.4" +android_logger = "0.13" +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +easytier = { path = "../../easytier" } \ No newline at end of file diff --git a/easytier-contrib/easytier-android-jni/README.md b/easytier-contrib/easytier-android-jni/README.md new file mode 100644 index 000000000..1062f400e --- /dev/null +++ b/easytier-contrib/easytier-android-jni/README.md @@ -0,0 +1,267 @@ +# EasyTier Android JNI + +这是 EasyTier 的 Android JNI 绑定库,允许 Android 应用程序调用 EasyTier 的网络功能。 + +## 功能特性 + +- 🚀 完整的 EasyTier FFI 接口封装 +- 📱 原生 Android JNI 支持 +- 🔧 支持多种 Android 架构 (arm64-v8a, armeabi-v7a, x86, x86_64) +- 🛡️ 类型安全的 Java 接口 +- 📝 详细的错误处理和日志记录 + +## 支持的架构 + +- `arm64-v8a` (aarch64-linux-android) +- `armeabi-v7a` (armv7-linux-androideabi) +- `x86` (i686-linux-android) +- `x86_64` (x86_64-linux-android) + +## 构建要求 + +### 系统要求 + +- Rust 1.70+ +- Android NDK r21+ +- Linux/macOS 开发环境 + +### 环境设置 + +1. **安装 Rust** + ```bash + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh + source ~/.cargo/env + ``` + +2. **安装 Android NDK** + - 下载 Android NDK: https://developer.android.com/ndk/downloads + - 解压到合适的目录 + - 设置环境变量: + ```bash + export ANDROID_NDK_ROOT=/path/to/android-ndk + ``` + +3. **添加 Android 目标** + ```bash + rustup target add aarch64-linux-android + rustup target add armv7-linux-androideabi + rustup target add i686-linux-android + rustup target add x86_64-linux-android + ``` + +## 构建步骤 + +1. **克隆项目并进入目录** + ```bash + cd /path/to/EasyTier/easytier-contrib/easytier-android-jni + ``` + +2. **运行构建脚本** + ```bash + ./build.sh + ``` + +3. **构建完成后,库文件将生成在 `target/android/` 目录下** + ``` + target/android/ + ├── arm64-v8a/ + │ └── libeasytier_android_jni.so + ├── armeabi-v7a/ + │ └── libeasytier_android_jni.so + ├── x86/ + │ └── libeasytier_android_jni.so + └── x86_64/ + └── libeasytier_android_jni.so + ``` + +## Android 项目集成 + +### 1. 复制库文件 + +将生成的 `.so` 文件复制到您的 Android 项目中: + +``` +your-android-project/ +└── src/main/ + ├── jniLibs/ + │ ├── arm64-v8a/ + │ │ └── libeasytier_android_jni.so + │ ├── armeabi-v7a/ + │ │ └── libeasytier_android_jni.so + │ ├── x86/ + │ │ └── libeasytier_android_jni.so + │ └── x86_64/ + │ └── libeasytier_android_jni.so + └── java/ + └── com/easytier/jni/ + └── EasyTierJNI.java +``` + +### 2. 复制 Java 接口 + +将 `java/com/easytier/jni/EasyTierJNI.java` 复制到您的 Android 项目的相应包路径下。 + +### 3. 添加权限 + +在 `AndroidManifest.xml` 中添加必要的权限: + +```xml + + + +``` + +## 使用示例 + +### 基本使用 + +```java +import com.easytier.jni.EasyTierJNI; +import java.util.Map; + +public class EasyTierManager { + + // 初始化网络实例 + public void startNetwork() { + String config = """ + inst_name = "my_instance" + network = "my_network" + """; + + try { + // 解析配置 + int result = EasyTierJNI.parseConfig(config); + if (result != 0) { + String error = EasyTierJNI.getLastError(); + throw new RuntimeException("配置解析失败: " + error); + } + + // 启动网络实例 + result = EasyTierJNI.runNetworkInstance(config); + if (result != 0) { + String error = EasyTierJNI.getLastError(); + throw new RuntimeException("网络实例启动失败: " + error); + } + + System.out.println("EasyTier 网络实例启动成功"); + + } catch (RuntimeException e) { + System.err.println("启动失败: " + e.getMessage()); + } + } + + // 获取网络信息 + public void getNetworkInfo() { + try { + Map infos = EasyTierJNI.collectNetworkInfosAsMap(10); + for (Map.Entry entry : infos.entrySet()) { + System.out.println(entry.getKey() + ": " + entry.getValue()); + } + } catch (RuntimeException e) { + System.err.println("获取网络信息失败: " + e.getMessage()); + } + } + + // 停止所有实例 + public void stopNetwork() { + try { + int result = EasyTierJNI.stopAllInstances(); + if (result == 0) { + System.out.println("所有网络实例已停止"); + } + } catch (RuntimeException e) { + System.err.println("停止网络失败: " + e.getMessage()); + } + } +} +``` + +### VPN 服务集成 + +如果您要在 Android VPN 服务中使用: + +```java +public class EasyTierVpnService extends VpnService { + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + // 建立 VPN 连接 + ParcelFileDescriptor vpnInterface = establishVpnInterface(); + + if (vpnInterface != null) { + int fd = vpnInterface.getFd(); + + // 设置 TUN 文件描述符 + try { + EasyTierJNI.setTunFd("my_instance", fd); + } catch (RuntimeException e) { + Log.e("EasyTier", "设置 TUN FD 失败", e); + } + } + + return START_STICKY; + } + + private ParcelFileDescriptor establishVpnInterface() { + Builder builder = new Builder(); + builder.setMtu(1500); + builder.addAddress("10.0.0.2", 24); + builder.addRoute("0.0.0.0", 0); + builder.setSession("EasyTier VPN"); + + return builder.establish(); + } +} +``` + +## API 参考 + +### EasyTierJNI 类方法 + +| 方法 | 描述 | 参数 | 返回值 | +|------|------|------|--------| +| `parseConfig(String config)` | 解析 TOML 配置 | config: 配置字符串 | 0=成功, -1=失败 | +| `runNetworkInstance(String config)` | 启动网络实例 | config: 配置字符串 | 0=成功, -1=失败 | +| `setTunFd(String instanceName, int fd)` | 设置 TUN 文件描述符 | instanceName: 实例名, fd: 文件描述符 | 0=成功, -1=失败 | +| `retainNetworkInstance(String[] names)` | 保留指定实例 | names: 实例名数组 | 0=成功, -1=失败 | +| `collectNetworkInfos(int maxLength)` | 收集网络信息 | maxLength: 最大条目数 | 信息字符串数组 | +| `collectNetworkInfosAsMap(int maxLength)` | 收集网络信息为 Map | maxLength: 最大条目数 | Map | +| `getLastError()` | 获取最后错误 | 无 | 错误消息字符串 | +| `stopAllInstances()` | 停止所有实例 | 无 | 0=成功, -1=失败 | +| `retainSingleInstance(String name)` | 保留单个实例 | name: 实例名 | 0=成功, -1=失败 | + +## 故障排除 + +### 常见问题 + +1. **构建失败: "Android NDK not found"** + - 确保设置了 `ANDROID_NDK_ROOT` 环境变量 + - 检查 NDK 路径是否正确 + +2. **运行时错误: "java.lang.UnsatisfiedLinkError"** + - 确保 `.so` 文件放在正确的 `jniLibs` 目录下 + - 检查目标架构是否匹配 + +3. **配置解析失败** + - 检查 TOML 配置格式是否正确 + - 使用 `getLastError()` 获取详细错误信息 + +### 调试技巧 + +- 启用 Android 日志查看 JNI 层的日志输出 +- 使用 `adb logcat -s EasyTier-JNI` 查看相关日志 +- 检查 `getLastError()` 返回的错误信息 + +## 许可证 + +本项目遵循与 EasyTier 主项目相同的许可证。 + +## 贡献 + +欢迎提交 Issue 和 Pull Request 来改进这个项目。 + +## 相关链接 + +- [EasyTier 主项目](https://github.com/EasyTier/EasyTier) +- [Android NDK 文档](https://developer.android.com/ndk) +- [Rust JNI 文档](https://docs.rs/jni/) \ No newline at end of file diff --git a/easytier-contrib/easytier-android-jni/build.sh b/easytier-contrib/easytier-android-jni/build.sh new file mode 100755 index 000000000..4bb1ea0f2 --- /dev/null +++ b/easytier-contrib/easytier-android-jni/build.sh @@ -0,0 +1,128 @@ +#!/bin/bash + +# EasyTier Android JNI 构建脚本 +# 用于编译适用于 Android 平台的 JNI 库 +# 使用 cargo-ndk 工具简化 Android 编译过程 + +set -e + +# 颜色输出 +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +REPO_ROOT=$(git rev-parse --show-toplevel) + +echo -e "${GREEN}EasyTier Android JNI 构建脚本 (使用 cargo-ndk)${NC}" +echo "==============================================" + +# 检查 Rust 是否安装 +if ! command -v rustc &> /dev/null; then + echo -e "${RED}错误: 未找到 Rust 编译器,请先安装 Rust${NC}" + exit 1 +fi + +# 检查 cargo 是否安装 +if ! command -v cargo &> /dev/null; then + echo -e "${RED}错误: 未找到 Cargo,请先安装 Rust 工具链${NC}" + exit 1 +fi + +# 检查 cargo-ndk 是否安装 +if ! cargo ndk --version &> /dev/null; then + echo -e "${YELLOW}cargo-ndk 未安装,正在安装...${NC}" + cargo install cargo-ndk + if ! cargo ndk --version &> /dev/null; then + echo -e "${RED}错误: cargo-ndk 安装失败${NC}" + exit 1 + fi +fi + +echo -e "${GREEN}cargo-ndk 版本: $(cargo ndk --version)${NC}" + +# Android 目标架构映射 (cargo-ndk 使用的架构名称) +# ANDROID_TARGETS=("arm64-v8a" "armeabi-v7a" "x86" "x86_64") +ANDROID_TARGETS=("arm64-v8a") + +# Android 架构到 Rust target 的映射 +declare -A TARGET_MAP +TARGET_MAP["arm64-v8a"]="aarch64-linux-android" +TARGET_MAP["armeabi-v7a"]="armv7-linux-androideabi" +TARGET_MAP["x86"]="i686-linux-android" +TARGET_MAP["x86_64"]="x86_64-linux-android" + +# 检查并安装所需的 Rust target +echo -e "${YELLOW}检查并安装 Android 目标架构...${NC}" +for android_target in "${ANDROID_TARGETS[@]}"; do + rust_target="${TARGET_MAP[$android_target]}" + if ! rustup target list --installed | grep -q "$rust_target"; then + echo -e "${YELLOW}安装目标架构: $rust_target (for $android_target)${NC}" + rustup target add "$rust_target" + else + echo -e "${GREEN}目标架构已安装: $rust_target (for $android_target)${NC}" + fi +done + +# 创建输出目录 +OUTPUT_DIR="./target/android" +mkdir -p "$OUTPUT_DIR" + +# 构建函数 +build_for_target() { + local android_target=$1 + echo -e "${YELLOW}构建目标: $android_target${NC}" + + # 首先构建 easytier-ffi + echo -e "${YELLOW}构建 easytier-ffi for $android_target${NC}" + (cd $REPO_ROOT/easytier-contrib/easytier-ffi && cargo ndk -t $android_target build --release) + + # 构建 JNI 库 + cargo ndk -t $android_target build --release + + # 复制库文件到输出目录 + # cargo-ndk 使用 Rust target 名称作为目录名,而不是 Android 架构名称 + rust_target="${TARGET_MAP[$android_target]}" + mkdir -p "$OUTPUT_DIR/$android_target" + cp "$REPO_ROOT/target/$rust_target/release/libeasytier_android_jni.so" "$OUTPUT_DIR/$android_target/" + echo -e "${GREEN}库文件已复制到: $OUTPUT_DIR/$android_target/${NC}" +} + +# 检查 Android NDK (cargo-ndk 会自动处理 NDK 路径) +if [ -z "$ANDROID_NDK_ROOT" ] && [ -z "$ANDROID_NDK_HOME" ] && [ -z "$NDK_HOME" ]; then + echo -e "${YELLOW}警告: 未设置 Android NDK 环境变量${NC}" + echo "cargo-ndk 将尝试自动检测 NDK 路径" + echo "如果构建失败,请设置以下环境变量之一:" + echo " - ANDROID_NDK_ROOT" + echo " - ANDROID_NDK_HOME" + echo " - NDK_HOME" +else + if [ -n "$ANDROID_NDK_ROOT" ]; then + echo -e "${GREEN}使用 Android NDK: $ANDROID_NDK_ROOT${NC}" + elif [ -n "$ANDROID_NDK_HOME" ]; then + echo -e "${GREEN}使用 Android NDK: $ANDROID_NDK_HOME${NC}" + elif [ -n "$NDK_HOME" ]; then + echo -e "${GREEN}使用 Android NDK: $NDK_HOME${NC}" + fi +fi + +# 构建所有目标 +echo -e "${YELLOW}开始构建所有目标架构...${NC}" +for target in "${ANDROID_TARGETS[@]}"; do + build_for_target "$target" +done + +echo -e "${GREEN}构建完成!${NC}" +echo -e "${GREEN}所有库文件已生成到: $OUTPUT_DIR${NC}" +echo "" +echo "目录结构:" +ls -la "$OUTPUT_DIR"/*/ + +echo "" +echo -e "${YELLOW}使用说明:${NC}" +echo "1. 将生成的 .so 文件复制到您的 Android 项目的 src/main/jniLibs/ 目录下" +echo "2. 将 java/com/easytier/jni/EasyTierJNI.java 复制到您的 Android 项目中" +echo "3. 在您的 Android 代码中调用 EasyTierJNI 类的方法" +echo "" +echo -e "${GREEN}注意: 此脚本使用 cargo-ndk 工具,无需手动设置复杂的环境变量${NC}" +echo -e "${GREEN}cargo-ndk 会自动处理交叉编译所需的工具链配置${NC}" diff --git a/easytier-contrib/easytier-android-jni/example_config.toml b/easytier-contrib/easytier-android-jni/example_config.toml new file mode 100644 index 000000000..0f85246ee --- /dev/null +++ b/easytier-contrib/easytier-android-jni/example_config.toml @@ -0,0 +1,56 @@ +# EasyTier Android JNI 示例配置文件 +# 这是一个基本的配置示例,展示如何配置 EasyTier 网络实例 + +# 实例名称 (必需) +inst_name = "android_instance" + +# 网络名称 (必需) +network = "my_easytier_network" + +# 网络密钥 (可选,用于网络加密) +# network_secret = "your_secret_key_here" + +# 监听地址 (可选) +# listeners = ["tcp://0.0.0.0:11010", "udp://0.0.0.0:11010"] + +# 对等节点地址 (可选) +# peers = ["tcp://peer1.example.com:11010", "udp://peer2.example.com:11010"] + +# 虚拟 IP 地址 (可选) +# ipv4 = "10.144.144.1" + +# 主机名 (可选) +# hostname = "android-device" + +# 启用 IPv6 (可选) +# ipv6 = "fd00::1" + +# 代理网络 (可选) +# proxy_networks = ["192.168.1.0/24"] + +# 退出节点 (可选) +# exit_nodes = ["peer1"] + +# 启用加密 (可选) +# enable_encryption = true + +# 启用 IPv4 转发 (可选) +# enable_ipv4 = true + +# 启用 IPv6 转发 (可选) +# enable_ipv6 = false + +# MTU 设置 (可选) +# mtu = 1420 + +# 日志级别 (可选: error, warn, info, debug, trace) +# log_level = "info" + +# 禁用 P2P (可选) +# disable_p2p = false + +# 使用多路径 (可选) +# use_multi_path = true + +# 延迟优先 (可选) +# latency_first = false \ No newline at end of file diff --git a/easytier-contrib/easytier-android-jni/kotlin/com/easytier/jni/EasyTierJNI.kt b/easytier-contrib/easytier-android-jni/kotlin/com/easytier/jni/EasyTierJNI.kt new file mode 100644 index 000000000..8f1b0efe0 --- /dev/null +++ b/easytier-contrib/easytier-android-jni/kotlin/com/easytier/jni/EasyTierJNI.kt @@ -0,0 +1,78 @@ +package com.easytier.jni + +/** EasyTier JNI 接口类 提供 Android 应用调用 EasyTier 网络功能的接口 */ +object EasyTierJNI { + + init { + // 加载本地库 + System.loadLibrary("easytier_android_jni") + } + + /** + * 设置 TUN 文件描述符 + * @param instanceName 实例名称 + * @param fd TUN 文件描述符 + * @return 0 表示成功,-1 表示失败 + * @throws RuntimeException 当操作失败时抛出异常 + */ + @JvmStatic external fun setTunFd(instanceName: String, fd: Int): Int + + /** + * 解析配置字符串 + * @param config TOML 格式的配置字符串 + * @return 0 表示成功,-1 表示失败 + * @throws RuntimeException 当配置解析失败时抛出异常 + */ + @JvmStatic external fun parseConfig(config: String): Int + + /** + * 运行网络实例 + * @param config TOML 格式的配置字符串 + * @return 0 表示成功,-1 表示失败 + * @throws RuntimeException 当实例启动失败时抛出异常 + */ + @JvmStatic external fun runNetworkInstance(config: String): Int + + /** + * 保留指定的网络实例,停止其他实例 + * @param instanceNames 要保留的实例名称数组,传入 null 或空数组将停止所有实例 + * @return 0 表示成功,-1 表示失败 + * @throws RuntimeException 当操作失败时抛出异常 + */ + @JvmStatic external fun retainNetworkInstance(instanceNames: Array?): Int + + /** + * 收集网络信息 + * @param maxLength 最大返回条目数 + * @return 包含网络信息的字符串数组,每个元素格式为 "key=value" + * @throws RuntimeException 当操作失败时抛出异常 + */ + @JvmStatic external fun collectNetworkInfos(maxLength: Int): String? + + /** + * 获取最后的错误消息 + * @return 错误消息字符串,如果没有错误则返回 null + */ + @JvmStatic external fun getLastError(): String? + + /** + * 便利方法:停止所有网络实例 + * @return 0 表示成功,-1 表示失败 + * @throws RuntimeException 当操作失败时抛出异常 + */ + @JvmStatic + fun stopAllInstances(): Int { + return retainNetworkInstance(null) + } + + /** + * 便利方法:停止指定实例外的所有实例 + * @param instanceName 要保留的实例名称 + * @return 0 表示成功,-1 表示失败 + * @throws RuntimeException 当操作失败时抛出异常 + */ + @JvmStatic + fun retainSingleInstance(instanceName: String): Int { + return retainNetworkInstance(arrayOf(instanceName)) + } +} diff --git a/easytier-contrib/easytier-android-jni/kotlin/com/easytier/jni/EasyTierManager.kt b/easytier-contrib/easytier-android-jni/kotlin/com/easytier/jni/EasyTierManager.kt new file mode 100644 index 000000000..e8330ec67 --- /dev/null +++ b/easytier-contrib/easytier-android-jni/kotlin/com/easytier/jni/EasyTierManager.kt @@ -0,0 +1,252 @@ +package com.easytier.jni + +import android.app.Activity +import android.content.Intent +import android.os.Handler +import android.os.Looper +import android.util.Log +import com.squareup.moshi.Moshi +import com.squareup.wire.WireJsonAdapterFactory +import common.Ipv4Inet +import web.NetworkInstanceRunningInfoMap + +fun parseIpv4InetToString(inet: Ipv4Inet?): String? { + val addr = inet?.address?.addr ?: return null + val networkLength = inet.network_length + + // 将 int32 转换为 IPv4 字符串 + val ip = + String.format( + "%d.%d.%d.%d", + (addr shr 24) and 0xFF, + (addr shr 16) and 0xFF, + (addr shr 8) and 0xFF, + addr and 0xFF + ) + + return "$ip/$networkLength" +} + +/** EasyTier 管理类 负责管理 EasyTier 实例的生命周期、监控网络状态变化、控制 VpnService */ +class EasyTierManager( + private val activity: Activity, + private val instanceName: String, + private val networkConfig: String +) { + companion object { + private const val TAG = "EasyTierManager" + private const val MONITOR_INTERVAL = 3000L // 3秒监控间隔 + } + + private val handler = Handler(Looper.getMainLooper()) + private var isRunning = false + private var currentIpv4: String? = null + private var currentProxyCidrs: List = emptyList() + private var vpnServiceIntent: Intent? = null + + // JSON 解析器 + private val moshi = Moshi.Builder().add(WireJsonAdapterFactory()).build() + private val adapter = moshi.adapter(NetworkInstanceRunningInfoMap::class.java) + + // 监控任务 + private val monitorRunnable = + object : Runnable { + override fun run() { + if (isRunning) { + monitorNetworkStatus() + handler.postDelayed(this, MONITOR_INTERVAL) + } + } + } + + /** 启动 EasyTier 实例和监控 */ + fun start() { + if (isRunning) { + Log.w(TAG, "EasyTier 实例已经在运行中") + return + } + + try { + // 启动 EasyTier 实例 + val result = EasyTierJNI.runNetworkInstance(networkConfig) + if (result == 0) { + isRunning = true + Log.i(TAG, "EasyTier 实例启动成功: $instanceName") + + // 开始监控网络状态 + handler.post(monitorRunnable) + } else { + Log.e(TAG, "EasyTier 实例启动失败: $result") + val error = EasyTierJNI.getLastError() + Log.e(TAG, "错误信息: $error") + } + } catch (e: Exception) { + Log.e(TAG, "启动 EasyTier 实例时发生异常", e) + } + } + + /** 停止 EasyTier 实例和监控 */ + fun stop() { + if (!isRunning) { + Log.w(TAG, "EasyTier 实例未在运行") + return + } + + isRunning = false + + // 停止监控任务 + handler.removeCallbacks(monitorRunnable) + + try { + // 停止 VpnService + stopVpnService() + + // 停止 EasyTier 实例 + EasyTierJNI.stopAllInstances() + Log.i(TAG, "EasyTier 实例已停止: $instanceName") + + // 重置状态 + currentIpv4 = null + currentProxyCidrs = emptyList() + } catch (e: Exception) { + Log.e(TAG, "停止 EasyTier 实例时发生异常", e) + } + } + + /** 监控网络状态 */ + private fun monitorNetworkStatus() { + try { + val infosJson = EasyTierJNI.collectNetworkInfos(10) + if (infosJson.isNullOrEmpty()) { + Log.d(TAG, "未获取到网络信息") + return + } + + val networkInfoMap = parseNetworkInfo(infosJson) + val networkInfo = networkInfoMap?.map?.get(instanceName) + + if (networkInfo == null) { + Log.d(TAG, "未找到实例 $instanceName 的网络信息") + return + } + + Log.d(TAG, "网络信息: $networkInfo") + + // 检查实例是否正在运行 + if (!networkInfo.running) { + Log.w(TAG, "EasyTier 实例未运行: ${networkInfo.error_msg}") + return + } + + val newIpv4Inet = networkInfo.my_node_info?.virtual_ipv4 + + if (newIpv4Inet == null) { + Log.w(TAG, "EasyTier No Ipv4: $networkInfo") + return + } + + // 获取当前节点的 IPv4 地址 + val newIpv4 = parseIpv4InetToString(newIpv4Inet) + + // 获取所有节点的 proxy_cidrs + val newProxyCidrs = mutableListOf() + networkInfo.routes?.forEach { route -> + route.proxy_cidrs?.let { cidrs -> newProxyCidrs.addAll(cidrs) } + } + + // 检查是否有变化 + val ipv4Changed = newIpv4 != currentIpv4 + val proxyCidrsChanged = newProxyCidrs != currentProxyCidrs + + if (ipv4Changed || proxyCidrsChanged) { + Log.i(TAG, "网络状态发生变化:") + Log.i(TAG, " IPv4: $currentIpv4 -> $newIpv4") + Log.i(TAG, " Proxy CIDRs: $currentProxyCidrs -> $newProxyCidrs") + + // 更新状态 + currentIpv4 = newIpv4 + currentProxyCidrs = newProxyCidrs.toList() + + // 重启 VpnService + if (newIpv4 != null) { + restartVpnService(newIpv4, newProxyCidrs) + } + } else { + Log.d(TAG, "网络状态无变化 - IPv4: $currentIpv4, Proxy CIDRs: ${currentProxyCidrs.size} 个") + } + } catch (e: Exception) { + Log.e(TAG, "监控网络状态时发生异常", e) + } + } + + /** 解析网络信息 JSON */ + private fun parseNetworkInfo(jsonString: String): NetworkInstanceRunningInfoMap? { + return try { + adapter.fromJson(jsonString) + } catch (e: Exception) { + Log.e(TAG, "解析网络信息失败", e) + null + } + } + + /** 重启 VpnService */ + private fun restartVpnService(ipv4: String, proxyCidrs: List) { + try { + // 先停止现有的 VpnService + stopVpnService() + + // 启动新的 VpnService + startVpnService(ipv4, proxyCidrs) + } catch (e: Exception) { + Log.e(TAG, "重启 VpnService 时发生异常", e) + } + } + + /** 启动 VpnService */ + private fun startVpnService(ipv4: String, proxyCidrs: List) { + try { + val intent = Intent(activity, EasyTierVpnService::class.java) + intent.putExtra("ipv4_address", ipv4) + intent.putStringArrayListExtra("proxy_cidrs", ArrayList(proxyCidrs)) + intent.putExtra("instance_name", instanceName) + + activity.startService(intent) + vpnServiceIntent = intent + + Log.i(TAG, "VpnService 已启动 - IPv4: $ipv4, Proxy CIDRs: $proxyCidrs") + } catch (e: Exception) { + Log.e(TAG, "启动 VpnService 时发生异常", e) + } + } + + /** 停止 VpnService */ + private fun stopVpnService() { + try { + vpnServiceIntent?.let { intent -> + activity.stopService(intent) + Log.i(TAG, "VpnService 已停止") + } + vpnServiceIntent = null + } catch (e: Exception) { + Log.e(TAG, "停止 VpnService 时发生异常", e) + } + } + + /** 获取当前状态信息 */ + fun getStatus(): EasyTierStatus { + return EasyTierStatus( + isRunning = isRunning, + instanceName = instanceName, + currentIpv4 = currentIpv4, + currentProxyCidrs = currentProxyCidrs.toList() + ) + } + + /** 状态数据类 */ + data class EasyTierStatus( + val isRunning: Boolean, + val instanceName: String, + val currentIpv4: String?, + val currentProxyCidrs: List + ) +} diff --git a/easytier-contrib/easytier-android-jni/kotlin/com/easytier/jni/EasyTierVpnService.t.kt b/easytier-contrib/easytier-android-jni/kotlin/com/easytier/jni/EasyTierVpnService.t.kt new file mode 100644 index 000000000..596582c72 --- /dev/null +++ b/easytier-contrib/easytier-android-jni/kotlin/com/easytier/jni/EasyTierVpnService.t.kt @@ -0,0 +1,143 @@ +package com.easytier.jni + +import android.content.Intent +import android.net.VpnService +import android.os.ParcelFileDescriptor +import android.util.Log +import kotlin.concurrent.thread + +class EasyTierVpnService : VpnService() { + + private var vpnInterface: ParcelFileDescriptor? = null + private var isRunning = false + private var instanceName: String? = null + + companion object { + private const val TAG = "EasyTierVpnService" + } + + override fun onCreate() { + super.onCreate() + Log.d(TAG, "VPN Service created") + } + + override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { + // 获取传入的参数 + val ipv4Address = intent?.getStringExtra("ipv4_address") + val proxyCidrs = intent?.getStringArrayListExtra("proxy_cidrs") ?: arrayListOf() + instanceName = intent?.getStringExtra("instance_name") + + if (ipv4Address == null || instanceName == null) { + Log.e(TAG, "缺少必要参数: ipv4Address=$ipv4Address, instanceName=$instanceName") + stopSelf() + return START_NOT_STICKY + } + + Log.i( + TAG, + "启动 VPN Service - IPv4: $ipv4Address, Proxy CIDRs: $proxyCidrs, Instance: $instanceName" + ) + + thread { + try { + setupVpnInterface(ipv4Address, proxyCidrs) + } catch (t: Throwable) { + Log.e(TAG, "VPN 设置失败", t) + stopSelf() + } + } + + return START_STICKY + } + + private fun setupVpnInterface(ipv4Address: String, proxyCidrs: List) { + try { + // 解析 IPv4 地址和网络长度 + val (ip, networkLength) = parseIpv4Address(ipv4Address) + + // 1. 准备 VpnService.Builder + val builder = Builder() + builder.setSession("EasyTier VPN") + .addAddress(ip, networkLength) + .addDnsServer("223.5.5.5") + .addDnsServer("114.114.114.114") + .addDisallowedApplication("com.easytier.easytiervpn") + + // 2. 添加路由表 - 为每个 proxy CIDR 添加路由 + proxyCidrs.forEach { cidr -> + try { + val (routeIp, routeLength) = parseCidr(cidr) + builder.addRoute(routeIp, routeLength) + Log.d(TAG, "添加路由: $routeIp/$routeLength") + } catch (e: Exception) { + Log.w(TAG, "解析 CIDR 失败: $cidr", e) + } + } + + // 3. 构建虚拟网络接口 + vpnInterface = builder.establish() + + if (vpnInterface == null) { + Log.e(TAG, "创建 VPN 接口失败") + return + } + + Log.i(TAG, "VPN 接口创建成功") + + // 4. 将 TUN 文件描述符传递给 EasyTier + instanceName?.let { name -> + val fd = vpnInterface!!.fd + val result = EasyTierJNI.setTunFd(name, fd) + if (result == 0) { + Log.i(TAG, "TUN 文件描述符设置成功: $fd") + } else { + Log.e(TAG, "TUN 文件描述符设置失败: $result") + } + } + + isRunning = true + + // 5. 保持服务运行 + while (isRunning && vpnInterface != null) { + Thread.sleep(1000) + } + } catch (t: Throwable) { + Log.e(TAG, "VPN 接口设置过程中发生错误", t) + } finally { + cleanup() + } + } + + /** 解析 IPv4 地址,返回 IP 和网络长度 */ + private fun parseIpv4Address(ipv4Address: String): Pair { + return if (ipv4Address.contains("/")) { + val parts = ipv4Address.split("/") + Pair(parts[0], parts[1].toInt()) + } else { + // 默认使用 /24 网络 + Pair(ipv4Address, 24) + } + } + + /** 解析 CIDR,返回 IP 和网络长度 */ + private fun parseCidr(cidr: String): Pair { + val parts = cidr.split("/") + if (parts.size != 2) { + throw IllegalArgumentException("无效的 CIDR 格式: $cidr") + } + return Pair(parts[0], parts[1].toInt()) + } + + private fun cleanup() { + isRunning = false + vpnInterface?.close() + vpnInterface = null + Log.i(TAG, "VPN 接口已清理") + } + + override fun onDestroy() { + super.onDestroy() + Log.d(TAG, "VPN Service destroyed") + cleanup() + } +} diff --git a/easytier-contrib/easytier-android-jni/kotlin/com/easytier/jni/README.md b/easytier-contrib/easytier-android-jni/kotlin/com/easytier/jni/README.md new file mode 100644 index 000000000..d1ae26d9a --- /dev/null +++ b/easytier-contrib/easytier-android-jni/kotlin/com/easytier/jni/README.md @@ -0,0 +1,41 @@ +# 使用说明 + +1. 需要将 proto 文件放入 app/src/main/proto +2. android/gradle/libs.versions.toml 中加入依赖 + +``` +# Wire 核心运行时 +android-wire-runtime = { group = "com.squareup.wire", name = "wire-runtime", version = "5.3.11" } +moshi = { module = "com.squareup.moshi:moshi", version.ref = "moshi" } +android-wire-moshi-adapter = { group = "com.squareup.wire", name = "wire-moshi-adapter", version = "5.3.11" } +kotlinx-serialization-json = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-json", version = "1.9.0" } +``` + +3. build.gradle.kts 中加入 + +``` +plugins { + ... + alias(libs.plugins.wire) +} + +dependencies { + ... + implementation(libs.android.wire.runtime) + implementation(libs.android.wire.moshi.adapter) + implementation(libs.moshi) +} + +... + +wire { + kotlin { + rpcRole = "none" + } +} +``` + +4. 调用 easytier-contrib/easytier-android-jni/build.sh 生成 jni 和 ffi 的 so 文件。 +并将生成的 so 文件放到 android/app/src/main/jniLibs/arm64-v8a 目录下。 + +5. 使用 EasyTierManager 可以拉起 EasyTier 实例并启动 Android VpnService 组件。 \ No newline at end of file diff --git a/easytier-contrib/easytier-android-jni/src/lib.rs b/easytier-contrib/easytier-android-jni/src/lib.rs new file mode 100644 index 000000000..f6ada2981 --- /dev/null +++ b/easytier-contrib/easytier-android-jni/src/lib.rs @@ -0,0 +1,319 @@ +use easytier::proto::api::manage::{NetworkInstanceRunningInfo, NetworkInstanceRunningInfoMap}; +use jni::objects::{JClass, JObjectArray, JString}; +use jni::sys::{jint, jstring}; +use jni::JNIEnv; +use once_cell::sync::Lazy; +use std::ffi::{CStr, CString}; +use std::ptr; + +// 定义 KeyValuePair 结构体 +#[repr(C)] +#[derive(Clone, Copy)] +pub struct KeyValuePair { + pub key: *const std::ffi::c_char, + pub value: *const std::ffi::c_char, +} + +// 声明外部 C 函数 +extern "C" { + fn set_tun_fd(inst_name: *const std::ffi::c_char, fd: std::ffi::c_int) -> std::ffi::c_int; + fn get_error_msg(out: *mut *const std::ffi::c_char); + fn free_string(s: *const std::ffi::c_char); + fn parse_config(cfg_str: *const std::ffi::c_char) -> std::ffi::c_int; + fn run_network_instance(cfg_str: *const std::ffi::c_char) -> std::ffi::c_int; + fn retain_network_instance( + inst_names: *const *const std::ffi::c_char, + length: usize, + ) -> std::ffi::c_int; + fn collect_network_infos(infos: *mut KeyValuePair, max_length: usize) -> std::ffi::c_int; +} + +// 初始化 Android 日志 +static LOGGER_INIT: Lazy<()> = Lazy::new(|| { + android_logger::init_once( + android_logger::Config::default() + .with_max_level(log::LevelFilter::Debug) + .with_tag("EasyTier-JNI"), + ); +}); + +// 辅助函数:从 Java String 转换为 CString +fn jstring_to_cstring(env: &mut JNIEnv, jstr: &JString) -> Result { + let java_str = env + .get_string(jstr) + .map_err(|e| format!("Failed to get string: {:?}", e))?; + let rust_str = java_str.to_str().map_err(|_| "Invalid UTF-8".to_string())?; + CString::new(rust_str).map_err(|_| "String contains null byte".to_string()) +} + +// 辅助函数:获取错误消息 +fn get_last_error() -> Option { + unsafe { + let mut error_ptr: *const std::ffi::c_char = ptr::null(); + get_error_msg(&mut error_ptr); + if error_ptr.is_null() { + None + } else { + let error_cstr = CStr::from_ptr(error_ptr); + let error_str = error_cstr.to_string_lossy().into_owned(); + free_string(error_ptr); + Some(error_str) + } + } +} + +// 辅助函数:抛出 Java 异常 +fn throw_exception(env: &mut JNIEnv, message: &str) { + let _ = env.throw_new("java/lang/RuntimeException", message); +} + +/// 设置 TUN 文件描述符 +#[no_mangle] +pub extern "system" fn Java_com_easytier_jni_EasyTierJNI_setTunFd( + mut env: JNIEnv, + _class: JClass, + inst_name: JString, + fd: jint, +) -> jint { + Lazy::force(&LOGGER_INIT); + + let inst_name_cstr = match jstring_to_cstring(&mut env, &inst_name) { + Ok(cstr) => cstr, + Err(e) => { + throw_exception(&mut env, &format!("Invalid instance name: {}", e)); + return -1; + } + }; + + unsafe { + let result = set_tun_fd(inst_name_cstr.as_ptr(), fd); + if result != 0 { + if let Some(error) = get_last_error() { + throw_exception(&mut env, &error); + } + } + result + } +} + +/// 解析配置 +#[no_mangle] +pub extern "system" fn Java_com_easytier_jni_EasyTierJNI_parseConfig( + mut env: JNIEnv, + _class: JClass, + config: JString, +) -> jint { + Lazy::force(&LOGGER_INIT); + + let config_cstr = match jstring_to_cstring(&mut env, &config) { + Ok(cstr) => cstr, + Err(e) => { + throw_exception(&mut env, &format!("Invalid config string: {}", e)); + return -1; + } + }; + + unsafe { + let result = parse_config(config_cstr.as_ptr()); + if result != 0 { + if let Some(error) = get_last_error() { + throw_exception(&mut env, &error); + } + } + result + } +} + +/// 运行网络实例 +#[no_mangle] +pub extern "system" fn Java_com_easytier_jni_EasyTierJNI_runNetworkInstance( + mut env: JNIEnv, + _class: JClass, + config: JString, +) -> jint { + Lazy::force(&LOGGER_INIT); + + let config_cstr = match jstring_to_cstring(&mut env, &config) { + Ok(cstr) => cstr, + Err(e) => { + throw_exception(&mut env, &format!("Invalid config string: {}", e)); + return -1; + } + }; + + unsafe { + let result = run_network_instance(config_cstr.as_ptr()); + if result != 0 { + if let Some(error) = get_last_error() { + throw_exception(&mut env, &error); + } + } + result + } +} + +/// 保持网络实例 +#[no_mangle] +pub extern "system" fn Java_com_easytier_jni_EasyTierJNI_retainNetworkInstance( + mut env: JNIEnv, + _class: JClass, + instance_names: JObjectArray, +) -> jint { + Lazy::force(&LOGGER_INIT); + + // 处理 null 数组的情况 + if instance_names.is_null() { + unsafe { + let result = retain_network_instance(ptr::null(), 0); + if result != 0 { + if let Some(error) = get_last_error() { + throw_exception(&mut env, &error); + } + } + return result; + } + } + + // 获取数组长度 + let array_length = match env.get_array_length(&instance_names) { + Ok(len) => len as usize, + Err(e) => { + throw_exception(&mut env, &format!("Failed to get array length: {:?}", e)); + return -1; + } + }; + + // 如果数组为空,停止所有实例 + if array_length == 0 { + unsafe { + let result = retain_network_instance(ptr::null(), 0); + if result != 0 { + if let Some(error) = get_last_error() { + throw_exception(&mut env, &error); + } + } + return result; + } + } + + // 转换 Java 字符串数组为 C 字符串数组 + let mut c_strings = Vec::with_capacity(array_length); + let mut c_string_ptrs = Vec::with_capacity(array_length); + + for i in 0..array_length { + let java_string = match env.get_object_array_element(&instance_names, i as i32) { + Ok(obj) => obj, + Err(e) => { + throw_exception( + &mut env, + &format!("Failed to get array element {}: {:?}", i, e), + ); + return -1; + } + }; + + if java_string.is_null() { + continue; // 跳过 null 元素 + } + + let jstring = JString::from(java_string); + let c_string = match jstring_to_cstring(&mut env, &jstring) { + Ok(cstr) => cstr, + Err(e) => { + throw_exception( + &mut env, + &format!("Invalid instance name at index {}: {}", i, e), + ); + return -1; + } + }; + + c_string_ptrs.push(c_string.as_ptr()); + c_strings.push(c_string); // 保持 CString 的所有权 + } + + unsafe { + let result = retain_network_instance(c_string_ptrs.as_ptr(), c_string_ptrs.len()); + if result != 0 { + if let Some(error) = get_last_error() { + throw_exception(&mut env, &error); + } + } + result + } +} + +/// 收集网络信息 +#[no_mangle] +pub extern "system" fn Java_com_easytier_jni_EasyTierJNI_collectNetworkInfos( + mut env: JNIEnv, + _class: JClass, +) -> jstring { + Lazy::force(&LOGGER_INIT); + + const MAX_INFOS: usize = 100; + let mut infos = vec![ + KeyValuePair { + key: ptr::null(), + value: ptr::null(), + }; + MAX_INFOS + ]; + + unsafe { + let count = collect_network_infos(infos.as_mut_ptr(), MAX_INFOS); + if count < 0 { + if let Some(error) = get_last_error() { + throw_exception(&mut env, &error); + } + return ptr::null_mut(); + } + + let mut ret = NetworkInstanceRunningInfoMap::default(); + + // 使用 serde_json 构建 JSON + for info in infos.iter().take(count as usize) { + let key_ptr = info.key; + let val_ptr = info.value; + if key_ptr.is_null() || val_ptr.is_null() { + break; + } + + let key = CStr::from_ptr(key_ptr).to_string_lossy(); + let val = CStr::from_ptr(val_ptr).to_string_lossy(); + let value = match serde_json::from_str::(val.as_ref()) { + Ok(v) => v, + Err(_) => { + throw_exception(&mut env, "Failed to parse JSON"); + continue; + } + }; + ret.map.insert(key.to_string(), value); + } + + let json_str = serde_json::to_string(&ret).unwrap_or_else(|_| "{}".to_string()); + + match env.new_string(&json_str) { + Ok(jstr) => jstr.into_raw(), + Err(_) => { + throw_exception(&mut env, "Failed to create JSON string"); + ptr::null_mut() + } + } + } +} + +/// 获取最后的错误信息 +#[no_mangle] +pub extern "system" fn Java_com_easytier_jni_EasyTierJNI_getLastError( + env: JNIEnv, + _class: JClass, +) -> jstring { + match get_last_error() { + Some(error) => match env.new_string(&error) { + Ok(jstr) => jstr.into_raw(), + Err(_) => ptr::null_mut(), + }, + None => ptr::null_mut(), + } +} diff --git a/easytier-contrib/easytier-ffi/src/lib.rs b/easytier-contrib/easytier-ffi/src/lib.rs index 1c2e5abd3..e63b97f2d 100644 --- a/easytier-contrib/easytier-ffi/src/lib.rs +++ b/easytier-contrib/easytier-ffi/src/lib.rs @@ -202,7 +202,7 @@ pub unsafe extern "C" fn collect_network_infos( std::slice::from_raw_parts_mut(infos, max_length) }; - let collected_infos = match INSTANCE_MANAGER.collect_network_infos() { + let collected_infos = match INSTANCE_MANAGER.collect_network_infos_sync() { Ok(infos) => infos, Err(e) => { set_error_msg(&format!("failed to collect network infos: {}", e)); diff --git a/easytier-contrib/easytier-magisk/action.sh b/easytier-contrib/easytier-magisk/action.sh index 7ab70f395..599a1199a 100644 --- a/easytier-contrib/easytier-magisk/action.sh +++ b/easytier-contrib/easytier-magisk/action.sh @@ -1,14 +1,43 @@ #!/data/adb/magisk/busybox sh MODDIR=${0%/*} +MODULE_PROP="${MODDIR}/module.prop" -# 查找 easytier-core 进程的 PID -PID=$(pgrep easytier-core) +ET_STATUS="" +REDIR_STATUS="" +# 更新module.prop文件中的description +update_module_description() { + local status_message=$1 + sed -i "/^description=/c\description=[状态]${status_message}" ${MODULE_PROP} +} -# 检查是否找到了进程 -if [ -z "$PID" ]; then - echo "easytier-core 进程未找到" + +if [ -f "${MODDIR}/disable" ]; then + ET_STATUS="已关闭" +elif pgrep -f 'easytier-core' >/dev/null; then + if [ -f "${MODDIR}/config/command_args"]; then + ET_STATUS="主程序已开启(启动参数模式)" + else + ET_STATUS="主程序已开启(配置文件模式)" + fi +fi + +#ET_STATUS不存在说明开启模块未正常运行,不修改状态 +if [ -n "$ET_STATUS" ]; then + if [ -f "${MODDIR}/enable_IP_rule" ]; then + rm -f "${MODDIR}/enable_IP_rule" + ${MODDIR}/hotspot_iprule.sh del + REDIR_STATUS="转发已禁用" + echo "热点子网转发已禁用" + echo "[ET-NAT] IP rule disabled." >> "${MODDIR}/log.log" + else + touch "${MODDIR}/enable_IP_rule" + ${MODDIR}/hotspot_iprule.sh del + ${MODDIR}/hotspot_iprule.sh add_once + REDIR_STATUS="转发已激活" + echo "热点子网转发已激活,热点开启后将自动将热点加入转发网络(要求已配置本地网络cidr=参数)。转发规则将随着热点开关而自动开关。该状态将保持到转发被禁用为止。" + echo "[ET-NAT] IP rule enabled." >> "${MODDIR}/log.log" + fi + update_module_description "${ET_STATUS} | ${REDIR_STATUS}" else - # 结束进程 - kill $PID - echo "已结束 easytier-core 进程 (PID: $PID)" + echo "主程序未正常启动,请先检查配置文件" fi diff --git a/easytier-contrib/easytier-magisk/config/command_args_sample b/easytier-contrib/easytier-magisk/config/command_args_sample new file mode 100644 index 000000000..9b61aaee2 --- /dev/null +++ b/easytier-contrib/easytier-magisk/config/command_args_sample @@ -0,0 +1 @@ +--config-server udp://127.0.0.1:22020/admin --machine-id easytier-magisk \ No newline at end of file diff --git a/easytier-contrib/easytier-magisk/customize.sh b/easytier-contrib/easytier-magisk/customize.sh index 6c7393a7e..f437828a7 100644 --- a/easytier-contrib/easytier-magisk/customize.sh +++ b/easytier-contrib/easytier-magisk/customize.sh @@ -3,5 +3,7 @@ ui_print '当前架构为' + $ARCH ui_print '当前系统版本为' + $API ui_print '安装目录为: /data/adb/modules/easytier_magisk' ui_print '配置文件位置: /data/adb/modules/easytier_magisk/config/config.toml' -ui_print '修改后配置文件后在magisk app点击操作按钮即可生效' -ui_print '记得重启' \ No newline at end of file +ui_print '如果需要自定义启动参数,可将 /data/adb/modules/easytier_magisk/config/command_args_sample 重命名为 command_args,并修改其中内容,使用自定义启动参数时会忽略配置文件' +ui_print '修改配置文件后在magisk app禁用应用再启动即可生效' +ui_print '点击操作按钮可启动/关闭热点子网转发,配合easytier的子网代理功能实现手机热点访问easytier网络' +ui_print '记得重启' diff --git a/easytier-contrib/easytier-magisk/easytier_core.sh b/easytier-contrib/easytier-magisk/easytier_core.sh index 7ff8de2a1..d4d845b2c 100644 --- a/easytier-contrib/easytier-magisk/easytier_core.sh +++ b/easytier-contrib/easytier-magisk/easytier_core.sh @@ -5,6 +5,7 @@ CONFIG_FILE="${MODDIR}/config/config.toml" LOG_FILE="${MODDIR}/log.log" MODULE_PROP="${MODDIR}/module.prop" EASYTIER="${MODDIR}/easytier-core" +REDIR_STATUS="" # 更新module.prop文件中的description update_module_description() { @@ -12,6 +13,12 @@ update_module_description() { sed -i "/^description=/c\description=[状态]${status_message}" ${MODULE_PROP} } +if [ -f "${MODDIR}/enable_IP_rule" ]; then + REDIR_STATUS="转发已激活" +else + REDIR_STATUS="转发已禁用" +fi + if [ ! -e /dev/net/tun ]; then if [ ! -d /dev/net ]; then mkdir -p /dev/net @@ -22,7 +29,7 @@ fi while true; do if ls $MODDIR | grep -q "disable"; then - update_module_description "关闭中" + update_module_description "关闭中 | ${REDIR_STATUS}" if pgrep -f 'easytier-core' >/dev/null; then echo "开关控制$(date "+%Y-%m-%d %H:%M:%S") 进程已存在,正在关闭 ..." pkill easytier-core # 关闭进程 @@ -35,10 +42,20 @@ while true; do continue fi - TZ=Asia/Shanghai ${EASYTIER} -c ${CONFIG_FILE} > ${LOG_FILE} & - sleep 5s # 等待easytier-core启动完成 - update_module_description "已开启(不一定运行成功)" + # 如果 config 目录下存在 command_args 文件,则读取其中的内容作为启动参数 + if [ -f "${MODDIR}/config/command_args" ]; then + TZ=Asia/Shanghai ${EASYTIER} $(cat ${MODDIR}/config/command_args) --hostname "$(getprop ro.product.brand)-$(getprop ro.product.model)" > ${LOG_FILE} & + sleep 5s # 等待easytier-core启动完成 + update_module_description "主程序已开启(启动参数模式) | ${REDIR_STATUS}" + else + TZ=Asia/Shanghai ${EASYTIER} -c ${CONFIG_FILE} --hostname "$(getprop ro.product.brand)-$(getprop ro.product.model)" > ${LOG_FILE} & + sleep 5s # 等待easytier-core启动完成 + update_module_description "主程序已开启(配置文件模式) | ${REDIR_STATUS}" + fi ip rule add from all lookup main + if ! pgrep -f 'easytier-core' >/dev/null; then + update_module_descriptio "主程序启动失败,请检查配置文件" + fi else echo "开关控制$(date "+%Y-%m-%d %H:%M:%S") 进程已存在" fi diff --git a/easytier-contrib/easytier-magisk/hotspot_iprule.sh b/easytier-contrib/easytier-magisk/hotspot_iprule.sh new file mode 100644 index 000000000..568e3a48c --- /dev/null +++ b/easytier-contrib/easytier-magisk/hotspot_iprule.sh @@ -0,0 +1,104 @@ +#!/system/bin/sh +MODDIR=${0%/*} +CONFIG_FILE="${MODDIR}/config/config.toml" +LOG_FILE="${MODDIR}/log.log" +ACTION="$1" # 参数:add add_once del + + +# 获取接口/IP +get_et_iface() { + awk ' + BEGIN { IGNORECASE = 1 } + /^[[:space:]]*dev_name[[:space:]]*=/ { + val = $0 + sub(/^[^=]*=[[:space:]]*/, "", val) + gsub(/[" \t]/, "", val) + print val + exit + } + ' "$CONFIG_FILE" +} +get_tun_iface() { + ip link | awk -F': ' '/ tun[[:alnum:]]+/ {print $2; exit}' +} +get_hot_iface() { + ip link | awk -F': ' '/(^| )(swlan[[:alnum:]_]*|softap[[:alnum:]_]*|p2p-wlan[[:alnum:]_]*|ap[[:alnum:]_]*)\:/ {print $2; exit}' | cut -d'@' -f1 | head -n1 +} +get_usb_iface() { + ip link | awk -F': ' '/(^| )(usb[[:alnum:]_]*|rndis[[:alnum:]_]*|eth[[:alnum:]_]*)\:/ {print $2; exit}' | cut -d'@' -f1 | head -n1 +} +get_hot_cidr() { + ip -4 addr show dev "$1" | awk '/inet /{print $2; exit}' +} + + +set_nat_rules() { + ET_IFACE=$(get_et_iface) + [ -z "$ET_IFACE" ] && ET_IFACE="$(get_tun_iface)" + HOT_IFACE=$(get_hot_iface) + USB_IFACE=$(get_usb_iface) + HOT_CIDR=$(get_hot_cidr "$HOT_IFACE") + USB_CIDR=$(get_hot_cidr "$USB_IFACE") + + # 如果热点关闭就删除自定义链 + [ -n "$ET_IFACE" ] && { [ -n "$HOT_CIDR" ] || [ -n "$USB_CIDR" ]; } || return 1 + + # 创建自定义链(如不存在) + iptables -t nat -N ET_NAT 2>/dev/null + iptables -N ET_FWD 2>/dev/null + + # 确保主链首条跳转到自定义链 + iptables -t nat -C POSTROUTING -j ET_NAT 2>/dev/null || \ + iptables -t nat -I POSTROUTING 1 -j ET_NAT + iptables -C FORWARD -j ET_FWD 2>/dev/null || \ + iptables -I FORWARD 1 -j ET_FWD + + # 添加规则 + if [ -n "$HOT_CIDR" ]; then + iptables -t nat -A ET_NAT -s "$HOT_CIDR" -o "$ET_IFACE" -j MASQUERADE + iptables -A ET_FWD -i "$HOT_IFACE" -o "$ET_IFACE" \ + -m state --state NEW,ESTABLISHED,RELATED -j ACCEPT + iptables -A ET_FWD -i "$ET_IFACE" -o "$HOT_IFACE" \ + -m state --state ESTABLISHED,RELATED -j ACCEPT + echo "[ET-NAT] Rules applied: $HOT_IFACE $HOT_CIDR ↔ $ET_IFACE" >> "$LOG_FILE" + fi + if [ -n "$USB_CIDR" ]; then + iptables -t nat -A ET_NAT -s "$USB_CIDR" -o "$ET_IFACE" -j MASQUERADE + iptables -A ET_FWD -i "$USB_IFACE" -o "$ET_IFACE" \ + -m state --state NEW,ESTABLISHED,RELATED -j ACCEPT + iptables -A ET_FWD -i "$ET_IFACE" -o "$USB_IFACE" \ + -m state --state ESTABLISHED,RELATED -j ACCEPT + echo "[ET-NAT] Rules applied: $USB_IFACE $USB_CIDR ↔ $ET_IFACE" >> "$LOG_FILE" + fi +} + +flush_rules() { + iptables -t nat -F ET_NAT 2>/dev/null + iptables -F ET_FWD 2>/dev/null + echo "[ET-NAT] Custom chains flushed." >> "$LOG_FILE" +} + +case "$ACTION" in + add) + set_nat_rules + echo "[ET-NAT] Guard started." >> "$LOG_FILE" + ip monitor link addr | while read -r _; do + if [ -f "${MODDIR}/enable_IP_rule" ]; then + flush_rules + set_nat_rules + fi + done + ;; + add_once) + flush_rules + set_nat_rules + echo "[ET-NAT] One-time rules applied." >> "$LOG_FILE" + ;; + del) + flush_rules + ;; + *) + echo "Usage: $0 [add|del]" + exit 1 + ;; +esac diff --git a/easytier-contrib/easytier-magisk/module.prop b/easytier-contrib/easytier-magisk/module.prop index c0e822ab5..c2289524f 100644 --- a/easytier-contrib/easytier-magisk/module.prop +++ b/easytier-contrib/easytier-magisk/module.prop @@ -1,6 +1,6 @@ id=easytier_magisk name=EasyTier_Magisk -version=v2.4.2 +version=v2.4.5 versionCode=1 author=EasyTier description=easytier magisk module @EasyTier(https://github.com/EasyTier/EasyTier) diff --git a/easytier-contrib/easytier-magisk/service.sh b/easytier-contrib/easytier-magisk/service.sh index c44301705..489f39218 100644 --- a/easytier-contrib/easytier-magisk/service.sh +++ b/easytier-contrib/easytier-magisk/service.sh @@ -18,10 +18,7 @@ sed -i 's/$(description=)$[^"]*/\1[状态]关闭中/' "$MODDIR/module.prop" sleep 3s "${MODDIR}/easytier_core.sh" & +"${MODDIR}/hotspot_iprule.sh" add & -# 检查是否启用模块 -while [ ! -f ${MODDIR}/disable ]; do - sleep 2 -done - -pkill easytier-core +# easytier_core.sh 和 hotspot_iprule.sh 都有内部循环做守护, +# 所以这里不需要再做守护了 diff --git a/easytier-contrib/easytier-ohrs/.gitignore b/easytier-contrib/easytier-ohrs/.gitignore new file mode 100644 index 000000000..d578fcc56 --- /dev/null +++ b/easytier-contrib/easytier-ohrs/.gitignore @@ -0,0 +1,9 @@ +dist/ +target/ +.DS_Store +.idea/ +package/libs + +*.har + +Cargo.lock diff --git a/easytier-contrib/easytier-ohrs/Cargo.lock b/easytier-contrib/easytier-ohrs/Cargo.lock index 821de2203..1bb2fd06c 100644 --- a/easytier-contrib/easytier-ohrs/Cargo.lock +++ b/easytier-contrib/easytier-ohrs/Cargo.lock @@ -53,12 +53,6 @@ version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" -[[package]] -name = "android-tzdata" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" - [[package]] name = "android_system_properties" version = "0.1.5" @@ -70,9 +64,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.19" +version = "0.6.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "301af1932e46185686725e0fad2f8f2aa7da69dd70bf6ecc44d6b703844a3933" +checksum = "3ae563653d1938f79b1ab1b5e668c87c76a9930414574a6583a7b7e11a8e6192" dependencies = [ "anstyle", "anstyle-parse", @@ -100,35 +94,35 @@ dependencies = [ [[package]] name = "anstyle-query" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8bdeb6047d8983be085bab0ba1472e6dc604e7041dbf6fcd5e71523014fae9" +checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] name = "anstyle-wincon" -version = "3.0.9" +version = "3.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "403f75924867bb1033c59fbf0797484329750cfbe3c4325cd33127941fabc882" +checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a" dependencies = [ "anstyle", "once_cell_polyfill", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] name = "anyhow" -version = "1.0.98" +version = "1.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" +checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" [[package]] name = "arbitrary" -version = "1.4.1" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223" +checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1" dependencies = [ "derive_arbitrary", ] @@ -157,7 +151,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -189,18 +183,18 @@ checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] name = "async-trait" -version = "0.1.88" +version = "0.1.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -226,7 +220,7 @@ checksum = "ffdcb70bdbc4d478427380519163274ac86e52916e10f0a8889adf0f96d3fee7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -252,12 +246,9 @@ dependencies = [ [[package]] name = "base62" -version = "2.2.1" +version = "2.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10e52a7bcb1d6beebee21fb5053af9e3cbb7a7ed1a4909e534040e676437ab1f" -dependencies = [ - "rustversion", -] +checksum = "1adf9755786e27479693dedd3271691a92b5e242ab139cacb9fb8e7fb5381111" [[package]] name = "base64" @@ -277,7 +268,7 @@ version = "0.71.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f58bf3d7db68cfbac37cfc485a8d711e87e064c3d0fe0435b92f7a407f9d6b3" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.4", "cexpr", "clang-sys", "itertools 0.13.0", @@ -286,7 +277,7 @@ dependencies = [ "regex", "rustc-hash", "shlex", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -297,9 +288,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.9.1" +version = "2.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" +checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394" [[package]] name = "blake2" @@ -376,12 +367,6 @@ version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "175812e0be2bccb6abe50bb8d566126198344f707e304f45c648fd8f2cc0365e" -[[package]] -name = "bytemuck" -version = "1.23.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c76a5792e44e4abe34d3abf15636779261d45a7450612059293d1d2cfc63422" - [[package]] name = "byteorder" version = "1.5.0" @@ -396,21 +381,11 @@ checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" [[package]] name = "bzip2" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49ecfb22d906f800d4fe833b6282cf4dc1c298f5057ca0b5445e5c209735ca47" -dependencies = [ - "bzip2-sys", -] - -[[package]] -name = "bzip2-sys" -version = "0.1.13+1.0.8" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225bff33b2141874fe80d71e07d6eec4f85c5c216453dd96388240f96e1acc14" +checksum = "bea8dcd42434048e4f7a304411d9273a411f647446c1234a65ce0554923f4cff" dependencies = [ - "cc", - "pkg-config", + "libbz2-rs-sys", ] [[package]] @@ -435,10 +410,11 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.28" +version = "1.2.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ad45f4f74e4e20eaa392913b7b33a7091c87e59628f4dd27888205ad888843c" +checksum = "80f41ae168f955c12fb8960b057d70d0ca153fb83182b57d86380443527be7e9" dependencies = [ + "find-msvc-tools", "jobserver", "libc", "shlex", @@ -461,9 +437,9 @@ dependencies = [ [[package]] name = "cfg-if" -version = "1.0.1" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" +checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" [[package]] name = "cfg_aliases" @@ -497,17 +473,16 @@ dependencies = [ [[package]] name = "chrono" -version = "0.4.41" +version = "0.4.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d" +checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2" dependencies = [ - "android-tzdata", "iana-time-zone", "js-sys", "num-traits", "serde", "wasm-bindgen", - "windows-link", + "windows-link 0.2.0", ] [[package]] @@ -543,9 +518,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.40" +version = "4.5.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40b6887a1d8685cebccf115538db5c0efe625ccac9696ad45c409d96566e910f" +checksum = "e2134bb3ea021b78629caa971416385309e0131b351b25e01dc16fb54e1b5fae" dependencies = [ "clap_builder", "clap_derive", @@ -553,9 +528,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.40" +version = "4.5.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0c66c08ce9f0c698cbce5c0279d0bb6ac936d8674174fe48f736533b964f59e" +checksum = "c2ba64afa3c0a6df7fa517765e31314e983f51dda798ffba27b988194fb65dc9" dependencies = [ "anstream", "anstyle", @@ -566,16 +541,25 @@ dependencies = [ "unicode-width 0.2.1", ] +[[package]] +name = "clap_complete" +version = "4.5.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75bf0b32ad2e152de789bb635ea4d3078f6b838ad7974143e99b99f45a04af4a" +dependencies = [ + "clap", +] + [[package]] name = "clap_derive" -version = "4.5.40" +version = "4.5.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2c7947ae4cc3d851207c1adb5b5e260ff0cca11446b1d6d1423788e442257ce" +checksum = "bbfd7eae0b0f1a6e63d4b13c9c478de77c2eb546fba158ad50b4203dc24b9f9c" dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -617,9 +601,9 @@ checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" [[package]] name = "convert_case" -version = "0.7.1" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb402b8d4c85569410425650ce3eddc7d698ed96d39a73f941b08fb63082f1e7" +checksum = "baaaa0ecca5b51987b9423ccdc971514dd8b0bb7b4060b983d3664dad3f1f89f" dependencies = [ "unicode-segmentation", ] @@ -676,9 +660,9 @@ checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" [[package]] name = "crc32fast" -version = "1.4.2" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" dependencies = [ "cfg-if", ] @@ -758,14 +742,20 @@ dependencies = [ [[package]] name = "ctor" -version = "0.2.9" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a2785755761f3ddc1492979ce1e48d2c00d09311c39e4466429188f3dd6501" +checksum = "ec09e802f5081de6157da9a75701d6c713d8dc3ba52571fd4bd25f412644e8a6" dependencies = [ - "quote", - "syn 2.0.104", + "ctor-proc-macro", + "dtor", ] +[[package]] +name = "ctor-proc-macro" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2931af7e13dc045d8e9d26afccc6fa115d64e115c9c84b1166288b46f6782c2" + [[package]] name = "curve25519-dalek" version = "4.1.3" @@ -789,7 +779,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -813,7 +803,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -824,7 +814,7 @@ checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ "darling_core", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -849,13 +839,13 @@ checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476" [[package]] name = "dbus" -version = "0.9.7" +version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bb21987b9fb1613058ba3843121dd18b163b254d8a6e797e144cbac14d96d1b" +checksum = "190b6255e8ab55a7b568df5a883e9497edc3e4821c06396612048b430e5ad1e9" dependencies = [ "libc", "libdbus-sys", - "winapi", + "windows-sys 0.59.0", ] [[package]] @@ -893,7 +883,7 @@ dependencies = [ "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -902,27 +892,27 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "10d60334b3b2e7c9d91ef8150abfb6fa4c1c39ebbcf4a81c2e346aad939fee3e" dependencies = [ - "thiserror 2.0.12", + "thiserror 2.0.16", ] [[package]] name = "deranged" -version = "0.4.0" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" +checksum = "d630bccd429a5bb5a64b5e94f693bfc48c9f8566418fda4c494cc94f911f87cc" dependencies = [ "powerfmt", ] [[package]] name = "derive_arbitrary" -version = "1.4.1" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30542c1ad912e0e3d22a1935c290e12e8a29d704a420177a31faad4a601a0800" +checksum = "1e567bd82dcff979e4b03460c307b3cdc9e96fde3d73bed1496d2bc75d9dd62a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -943,7 +933,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -953,7 +943,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" dependencies = [ "derive_builder_core", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -1005,15 +995,31 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] +[[package]] +name = "dtor" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97cbdf2ad6846025e8e25df05171abfb30e3ababa12ee0a0e44b9bbe570633a8" +dependencies = [ + "dtor-proc-macro", +] + +[[package]] +name = "dtor-proc-macro" +version = "0.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7454e41ff9012c00d53cf7f475c5e3afa3b91b7c90568495495e8d9bf47a1055" + [[package]] name = "easytier" -version = "2.4.2" -source = "git+https://github.com/EasyTier/EasyTier.git#a4bb555fac1046d0099c44676fa9d0d8cca55c99" +version = "2.4.4" +source = "git+https://github.com/EasyTier/EasyTier.git#4445916ba72a8340259d65f0c55f50af325c51d2" dependencies = [ "anyhow", + "arc-swap", "async-recursion", "async-ringbuf", "async-stream", @@ -1021,7 +1027,7 @@ dependencies = [ "atomic-shim", "auto_impl", "base64 0.22.1", - "bitflags 2.9.1", + "bitflags 2.9.4", "boringtun-easytier", "bytecodec", "byteorder", @@ -1029,6 +1035,7 @@ dependencies = [ "chrono", "cidr", "clap", + "clap_complete", "crossbeam", "dashmap", "dbus", @@ -1039,18 +1046,18 @@ dependencies = [ "gethostname", "git-version", "globwalk", - "hashbrown 0.15.4", + "hashbrown 0.15.5", "hickory-client", "hickory-proto", "hickory-resolver", "hickory-server", + "hmac", "http", "http_req", "humansize", "humantime-serde", "kcp-sys", "machine-uid", - "mimalloc", "multimap", "netlink-packet-core", "netlink-packet-route", @@ -1082,8 +1089,9 @@ dependencies = [ "serde", "serde_json", "service-manager", + "sha2", "smoltcp", - "socket2", + "socket2 0.5.10", "stun_codec", "sys-locale", "tabled", @@ -1100,7 +1108,6 @@ dependencies = [ "toml", "tonic-build", "tracing", - "tracing-appender", "tracing-subscriber", "tun-easytier", "url", @@ -1250,7 +1257,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -1267,24 +1274,24 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "errno" -version = "0.3.13" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.60.2", + "windows-sys 0.61.0", ] [[package]] name = "fastbloom" -version = "0.9.0" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27cea6e7f512d43b098939ff4d5a5d6fe3db07971e1d05176fe26c642d33f5b8" +checksum = "18c1ddb9231d8554c2d6bdf4cfaabf0c59251658c68b6c95cd52dd0c513a912a" dependencies = [ "getrandom 0.3.3", - "rand 0.9.1", + "libm", + "rand 0.9.2", "siphasher", - "wide", ] [[package]] @@ -1299,6 +1306,12 @@ version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" +[[package]] +name = "find-msvc-tools" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ced73b1dacfc750a6db6c0a0c3a3853c8b41997e2e2c563dc90804ae6867959" + [[package]] name = "fixedbitset" version = "0.5.7" @@ -1345,9 +1358,9 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[package]] name = "form_urlencoded" -version = "1.2.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" dependencies = [ "percent-encoding", ] @@ -1408,7 +1421,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -1456,9 +1469,9 @@ dependencies = [ [[package]] name = "generator" -version = "0.8.5" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d18470a76cb7f8ff746cf1f7470914f900252ec36bbc40b569d74b1258446827" +checksum = "605183a538e3e2a9c1038635cc5c2d194e2ee8fd0d1b66b8349fad7dbacce5a2" dependencies = [ "cc", "cfg-if", @@ -1511,7 +1524,7 @@ dependencies = [ "js-sys", "libc", "r-efi", - "wasi 0.14.2+wasi-0.2.4", + "wasi 0.14.7+wasi-0.2.4", "wasm-bindgen", ] @@ -1538,14 +1551,14 @@ checksum = "53010ccb100b96a67bc32c0175f0ed1426b31b655d562898e57325f81c023ac0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] name = "glob" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" [[package]] name = "globset" @@ -1556,8 +1569,8 @@ dependencies = [ "aho-corasick", "bstr", "log", - "regex-automata 0.4.9", - "regex-syntax 0.8.5", + "regex-automata", + "regex-syntax", ] [[package]] @@ -1573,9 +1586,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17da50a276f1e01e0ba6c029e47b7100754904ee8a278f886546e98575380785" +checksum = "f3c0b69cfcb4e1b9f1bf2f53f95f766e4661169728ec61cd3fe5a0166f2d1386" dependencies = [ "atomic-waker", "bytes", @@ -1607,20 +1620,26 @@ checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" [[package]] name = "hashbrown" -version = "0.15.4" +version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ "allocator-api2", "equivalent", "foldhash", ] +[[package]] +name = "hashbrown" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" + [[package]] name = "heapless" -version = "0.8.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" +checksum = "b1edcd5a338e64688fbdcb7531a846cfd3476a54784dcb918a0844682bc7ada5" dependencies = [ "hash32", "stable_deref_trait", @@ -1657,8 +1676,8 @@ dependencies = [ "hickory-proto", "once_cell", "radix_trie", - "rand 0.9.1", - "thiserror 2.0.12", + "rand 0.9.2", + "thiserror 2.0.16", "tokio", "tracing", ] @@ -1679,10 +1698,10 @@ dependencies = [ "idna", "ipnet", "once_cell", - "rand 0.9.1", + "rand 0.9.2", "ring", "serde", - "thiserror 2.0.12", + "thiserror 2.0.16", "tinyvec", "tokio", "tracing", @@ -1702,11 +1721,11 @@ dependencies = [ "moka", "once_cell", "parking_lot", - "rand 0.9.1", + "rand 0.9.2", "resolv-conf", "serde", "smallvec", - "thiserror 2.0.12", + "thiserror 2.0.16", "tokio", "tracing", ] @@ -1728,7 +1747,7 @@ dependencies = [ "ipnet", "prefix-trie", "serde", - "thiserror 2.0.12", + "thiserror 2.0.16", "time", "tokio", "tokio-util", @@ -1820,9 +1839,9 @@ dependencies = [ [[package]] name = "humantime" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b112acc8b3adf4b107a8ec20977da0273a8c386765a3ec0229bd500a1443f9f" +checksum = "135b12329e5e3ce057a9f972339ea52bc954fe1e9358ef27f95e89716fbc5424" [[package]] name = "humantime-serde" @@ -1836,19 +1855,21 @@ dependencies = [ [[package]] name = "hyper" -version = "1.6.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80" +checksum = "eb3aa54a13a0dfe7fbe3a59e0c76093041720fdc77b110cc0fc260fafb4dc51e" dependencies = [ + "atomic-waker", "bytes", "futures-channel", - "futures-util", + "futures-core", "h2", "http", "http-body", "httparse", "itoa", "pin-project-lite", + "pin-utils", "smallvec", "tokio", "want", @@ -1888,9 +1909,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.14" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc2fdfdbff08affe55bb779f33b053aa1fe5dd5b54c257343c17edfa55711bdb" +checksum = "3c6995591a8f1380fcb4ba966a252a4b29188d51d2b89e3a252f5305be65aea8" dependencies = [ "base64 0.22.1", "bytes", @@ -1904,7 +1925,7 @@ dependencies = [ "libc", "percent-encoding", "pin-project-lite", - "socket2", + "socket2 0.6.0", "system-configuration", "tokio", "tower-service", @@ -1914,9 +1935,9 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.63" +version = "0.1.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8" +checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -1924,7 +1945,7 @@ dependencies = [ "js-sys", "log", "wasm-bindgen", - "windows-core 0.61.2", + "windows-core 0.62.0", ] [[package]] @@ -2030,9 +2051,9 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "idna" -version = "1.0.3" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" dependencies = [ "idna_adapter", "smallvec", @@ -2059,7 +2080,7 @@ dependencies = [ "globset", "log", "memchr", - "regex-automata 0.4.9", + "regex-automata", "same-file", "walkdir", "winapi-util", @@ -2067,12 +2088,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.10.0" +version = "2.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" +checksum = "4b0f83760fb341a774ed326568e19f5a863af4a952def8c39f9ab92fd95b88e5" dependencies = [ "equivalent", - "hashbrown 0.15.4", + "hashbrown 0.16.0", ] [[package]] @@ -2086,11 +2107,11 @@ dependencies = [ [[package]] name = "io-uring" -version = "0.7.8" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b86e202f00093dcba4275d4636b93ef9dd75d025ae560d2521b45ea28ab49013" +checksum = "046fa2d4d00aea763528b4950358d0ead425372445dc8ff86312b3c69ff7727b" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.4", "cfg-if", "libc", ] @@ -2123,7 +2144,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b58db92f96b720de98181bbbe63c831e87005ab460c1bf306eb2622b4707997f" dependencies = [ - "socket2", + "socket2 0.5.10", "widestring", "windows-sys 0.48.0", "winreg 0.50.0", @@ -2220,9 +2241,9 @@ checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" [[package]] name = "jobserver" -version = "0.1.33" +version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a" +checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" dependencies = [ "getrandom 0.3.3", "libc", @@ -2230,9 +2251,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.77" +version = "0.3.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +checksum = "852f13bec5eba4ba9afbeb93fd7c13fe56147f055939ae21c43a29a0ecb2702e" dependencies = [ "once_cell", "wasm-bindgen", @@ -2241,18 +2262,18 @@ dependencies = [ [[package]] name = "kcp-sys" version = "0.1.0" -source = "git+https://github.com/EasyTier/kcp-sys#0f0a0558391ba391c089806c23f369651f6c9eeb" +source = "git+https://github.com/EasyTier/kcp-sys?rev=0f0a0558391ba391c089806c23f369651f6c9eeb#0f0a0558391ba391c089806c23f369651f6c9eeb" dependencies = [ "anyhow", "auto_impl", "bindgen", - "bitflags 2.9.1", + "bitflags 2.9.4", "bytes", "cc", "dashmap", "parking_lot", "rand 0.8.5", - "thiserror 2.0.12", + "thiserror 2.0.16", "tokio", "tokio-util", "tracing", @@ -2266,17 +2287,23 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +[[package]] +name = "libbz2-rs-sys" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c4a545a15244c7d945065b5d392b2d2d7f21526fba56ce51467b06ed445e8f7" + [[package]] name = "libc" -version = "0.2.174" +version = "0.2.175" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" +checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" [[package]] name = "libdbus-sys" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06085512b750d640299b79be4bad3d2fa90a9c00b1fd9e1b46364f66f0485c72" +checksum = "5cbe856efeb50e4681f010e9aaa2bf0a644e10139e54cde10fc83a307c23bd9f" dependencies = [ "cc", "pkg-config", @@ -2284,19 +2311,19 @@ dependencies = [ [[package]] name = "libloading" -version = "0.8.8" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667" +checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" dependencies = [ "cfg-if", - "windows-targets 0.53.2", + "windows-link 0.2.0", ] [[package]] name = "liblzma" -version = "0.4.2" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0791ab7e08ccc8e0ce893f6906eb2703ed8739d8e89b57c0714e71bad09024c8" +checksum = "10bf66f4598dc77ff96677c8e763655494f00ff9c1cf79e2eb5bb07bc31f807d" dependencies = [ "liblzma-sys", ] @@ -2318,31 +2345,21 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" -[[package]] -name = "libmimalloc-sys" -version = "0.1.43" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf88cd67e9de251c1781dbe2f641a1a3ad66eaae831b8a2c38fbdc5ddae16d4d" -dependencies = [ - "cc", - "libc", -] - [[package]] name = "libredox" -version = "0.1.4" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1580801010e535496706ba011c15f8532df6b42297d2e471fec38ceadd8c0638" +checksum = "416f7e718bdb06000964960ffa43b4335ad4012ae8b99060261aa4a8088d5ccb" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.4", "libc", ] [[package]] name = "libz-rs-sys" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "172a788537a2221661b480fee8dc5f96c580eb34fa88764d3205dc356c7e4221" +checksum = "840db8cf39d9ec4dd794376f38acc40d0fc65eec2a8f484f7fd375b84602becd" dependencies = [ "zlib-rs", ] @@ -2355,9 +2372,9 @@ checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" [[package]] name = "linux-raw-sys" -version = "0.9.4" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" +checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" [[package]] name = "litemap" @@ -2377,9 +2394,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.27" +version = "0.4.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" +checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" [[package]] name = "loom" @@ -2401,7 +2418,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "419e0dc8046cb947daa77eb95ae174acfbddb7673b4151f56d1eed8e93fbfaca" dependencies = [ "cfg-if", - "generator 0.8.5", + "generator 0.8.7", "scoped-tls", "tracing", "tracing-subscriber", @@ -2431,11 +2448,11 @@ checksum = "0ca88d725a0a943b096803bd34e73a4437208b6077654cc4ecb2947a5f91618d" [[package]] name = "matchers" -version = "0.1.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9" dependencies = [ - "regex-automata 0.1.10", + "regex-automata", ] [[package]] @@ -2459,15 +2476,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "mimalloc" -version = "0.1.47" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1791cbe101e95af5764f06f20f6760521f7158f69dbf9d6baf941ee1bf6bc40" -dependencies = [ - "libmimalloc-sys", -] - [[package]] name = "mime" version = "0.3.17" @@ -2530,50 +2538,53 @@ dependencies = [ [[package]] name = "napi-build-ohos" -version = "1.0.4" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ad5bf214216afe5b572da0bcd5cab932d17cbcca3dbe82991db0d765a764c8a" +checksum = "959f833e4ea8bec8f92b23b705b5558d42d8e63672c77b9b281b7228c5df6e88" [[package]] name = "napi-derive-backend-ohos" -version = "1.0.4" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd974d6316c670078fa15276c6134e5b45142b393db350b24682ae613733cdac" +checksum = "b27250baa967a15214e57384dd6228c59afbccb15ab8f97207c9758917544bf5" dependencies = [ "convert_case", - "once_cell", "proc-macro2", "quote", "semver", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] name = "napi-derive-ohos" -version = "1.0.4" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3a8b89bbc39f81c472e76813dcd837f311aae7850a24a01d0bf5858221b1fd2" +checksum = "c844efa85d53b5adc3b326520f3a108c3a737b7534ee10d406f81884e7e71b3c" dependencies = [ "convert_case", + "ctor", "napi-derive-backend-ohos", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] name = "napi-ohos" -version = "1.0.4" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32036ede4ef064610304337831e9d49dac23e7edc4e9efd076c8259eab6d19a9" +checksum = "44cd7f1a2b5b17e763d8fcc33f3a9f426c0303a1fcb9b89d7c553308c3a1db97" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.4", "chrono", "ctor", "encoding_rs", "futures-core", "indexmap", + "napi-build-ohos", "napi-sys-ohos", + "nohash-hasher", + "rustc-hash", "serde", "serde_json", "tokio", @@ -2582,9 +2593,9 @@ dependencies = [ [[package]] name = "napi-sys-ohos" -version = "1.0.4" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e18642400316f886a6f153b2fbc48f5652d0e117803057005f89f0e48217d64" +checksum = "ff0b7e156bf62a778295ba4f130cde6c2fe07936ebf9448fab6ca0f7c7040682" dependencies = [ "libloading", ] @@ -2624,7 +2635,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "483325d4bfef65699214858f097d504eb812c38ce7077d165f301ec406c3066e" dependencies = [ "anyhow", - "bitflags 2.9.1", + "bitflags 2.9.4", "byteorder", "libc", "log", @@ -2657,13 +2668,13 @@ dependencies = [ [[package]] name = "network-interface" -version = "2.0.1" +version = "2.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3329f515506e4a2de3aa6e07027a6758e22e0f0e8eaf64fa47261cec2282602" +checksum = "07709a6d4eba90ab10ec170a0530b3aafc81cb8a2d380e4423ae41fc55fe5745" dependencies = [ "cc", "libc", - "thiserror 1.0.69", + "thiserror 2.0.16", "winapi", ] @@ -2694,7 +2705,7 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.4", "cfg-if", "cfg_aliases", "libc", @@ -2707,6 +2718,12 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43794a0ace135be66a25d3ae77d41b91615fb68ae937f904090203e81f755b65" +[[package]] +name = "nohash-hasher" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" + [[package]] name = "nom" version = "7.1.3" @@ -2719,21 +2736,20 @@ dependencies = [ [[package]] name = "normpath" -version = "1.3.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8911957c4b1549ac0dc74e30db9c8b0e66ddcd6d7acc33098f4c63a64a6d7ed" +checksum = "bf23ab2b905654b4cb177e30b629937b3868311d4e1cba859f899c041046e69b" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.0", ] [[package]] name = "nu-ansi-term" -version = "0.46.0" +version = "0.50.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +checksum = "d4a28e057d01f97e61255210fcff094d74ed0466038633e95017f5beb68e4399" dependencies = [ - "overload", - "winapi", + "windows-sys 0.52.0", ] [[package]] @@ -2771,9 +2787,9 @@ dependencies = [ [[package]] name = "ohos-hilog-binding" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f360d22e965a34286283d36e8864fdfb04f443697641e8f6cbd64e670c3a3d5" +checksum = "860d1e3c2c5e3217d819a16c815d2d4dcbc7610285d2612d08745a29c353a503" dependencies = [ "libc", "ohos-hilogs-sys", @@ -2813,7 +2829,7 @@ version = "0.10.73" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8505734d46c8ab1e19a1dce3aef597ad87dcb4c37e7188231769bd6bd51cebf8" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.4", "cfg-if", "foreign-types", "libc", @@ -2830,7 +2846,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -2851,12 +2867,6 @@ dependencies = [ "vcpkg", ] -[[package]] -name = "overload" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" - [[package]] name = "papergrid" version = "0.12.0" @@ -2919,9 +2929,9 @@ dependencies = [ [[package]] name = "percent-encoding" -version = "2.3.1" +version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "petgraph" @@ -2940,7 +2950,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "54acf3a685220b533e437e264e4d932cfbdc4cc7ec0cd232ed73c08d03b8a7ca" dependencies = [ "fixedbitset", - "hashbrown 0.15.4", + "hashbrown 0.15.5", "indexmap", "serde", ] @@ -2965,9 +2975,9 @@ checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" [[package]] name = "plist" -version = "1.7.2" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d77244ce2d584cd84f6a15f86195b8c9b2a0dfbfd817c09e0464244091a58ed" +checksum = "740ebea15c5d1428f910cd1a5f52cebf8d25006245ed8ade92702f4943d91e07" dependencies = [ "base64 0.22.1", "indexmap", @@ -3023,7 +3033,7 @@ dependencies = [ "proc-macro2", "quote", "regex", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -3097,9 +3107,9 @@ dependencies = [ [[package]] name = "potential_utf" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585" +checksum = "84df19adbe5b5a0782edcab45899906947ab039ccf4573713735ee7de1e6b08a" dependencies = [ "zerovec", ] @@ -3110,13 +3120,19 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" +[[package]] +name = "ppmd-rust" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c834641d8ad1b348c9ee86dec3b9840d805acd5f24daa5f90c788951a52ff59b" + [[package]] name = "ppv-lite86" version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" dependencies = [ - "zerocopy 0.8.26", + "zerocopy 0.8.27", ] [[package]] @@ -3131,12 +3147,12 @@ dependencies = [ [[package]] name = "prettyplease" -version = "0.2.35" +version = "0.2.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "061c1221631e079b26479d25bbf2275bfe5917ae8419cd7e34f13bfc2aa7539a" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ "proc-macro2", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -3182,14 +3198,14 @@ dependencies = [ "proc-macro-error-attr2", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] name = "proc-macro2" -version = "1.0.95" +version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" +checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" dependencies = [ "unicode-ident", ] @@ -3220,7 +3236,7 @@ dependencies = [ "prost", "prost-types", "regex", - "syn 2.0.104", + "syn 2.0.106", "tempfile", ] @@ -3234,7 +3250,7 @@ dependencies = [ "itertools 0.14.0", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -3267,7 +3283,7 @@ checksum = "f4fce6b22f15cc8d8d400a2b98ad29202b33bd56c7d9ddd815bc803a807ecb65" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -3281,18 +3297,18 @@ dependencies = [ [[package]] name = "quick-xml" -version = "0.37.5" +version = "0.38.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "331e97a1af0bf59823e6eadffe373d7b27f485be8748f71471c662c1f269b7fb" +checksum = "42a232e7487fc2ef313d96dde7948e7a3c05101870d8985e4fd8d26aedd27b89" dependencies = [ "memchr", ] [[package]] name = "quinn" -version = "0.11.8" +version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "626214629cda6781b6dc1d316ba307189c85ba657213ce642d9c77670f8202c8" +checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" dependencies = [ "bytes", "cfg_aliases", @@ -3301,8 +3317,8 @@ dependencies = [ "quinn-udp", "rustc-hash", "rustls", - "socket2", - "thiserror 2.0.12", + "socket2 0.6.0", + "thiserror 2.0.16", "tokio", "tracing", "web-time", @@ -3310,22 +3326,22 @@ dependencies = [ [[package]] name = "quinn-proto" -version = "0.11.12" +version = "0.11.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49df843a9161c85bb8aae55f101bc0bac8bcafd637a620d9122fd7e0b2f7422e" +checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31" dependencies = [ "bytes", "fastbloom", "getrandom 0.3.3", "lru-slab", - "rand 0.9.1", + "rand 0.9.2", "ring", "rustc-hash", "rustls", "rustls-pki-types", "rustls-platform-verifier", "slab", - "thiserror 2.0.12", + "thiserror 2.0.16", "tinyvec", "tracing", "web-time", @@ -3333,16 +3349,16 @@ dependencies = [ [[package]] name = "quinn-udp" -version = "0.5.13" +version = "0.5.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcebb1209ee276352ef14ff8732e24cc2b02bbac986cd74a4c81bcb2f9881970" +checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2", + "socket2 0.6.0", "tracing", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -3383,9 +3399,9 @@ dependencies = [ [[package]] name = "rand" -version = "0.9.1" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" dependencies = [ "rand_chacha 0.9.0", "rand_core 0.9.3", @@ -3443,11 +3459,11 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.13" +version = "0.5.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d04b7d0ee6b4a0207a0a7adb104d23ecb0b47d6beae7152d0fa34b692b29fd6" +checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.4", ] [[package]] @@ -3463,53 +3479,38 @@ dependencies = [ [[package]] name = "regex" -version = "1.11.1" +version = "1.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +checksum = "23d7fd106d8c02486a8d64e778353d1cffe08ce79ac2e82f540c86d0facf6912" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.9", - "regex-syntax 0.8.5", + "regex-automata", + "regex-syntax", ] [[package]] name = "regex-automata" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" -dependencies = [ - "regex-syntax 0.6.29", -] - -[[package]] -name = "regex-automata" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +checksum = "6b9458fa0bfeeac22b5ca447c63aaf45f28439a709ccd244698632f9aa6394d6" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.8.5", + "regex-syntax", ] [[package]] name = "regex-syntax" -version = "0.6.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" - -[[package]] -name = "regex-syntax" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" +checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001" [[package]] name = "reqwest" -version = "0.12.22" +version = "0.12.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbc931937e6ca3a06e3b6c0aa7841849b160a90351d6ab467a8b9b9959767531" +checksum = "d429f34c8092b2d42c7c93cec323bb4adeb7c67698f70839adec842ec10c7ceb" dependencies = [ "base64 0.22.1", "bytes", @@ -3549,9 +3550,9 @@ dependencies = [ [[package]] name = "resolv-conf" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95325155c684b1c89f7765e30bc1c42e4a6da51ca513615660cb8a62ef9a88e3" +checksum = "6b3789b30bd25ba102de4beabd95d21ac45b69b1be7d14522bab988c526d6799" [[package]] name = "ring" @@ -3606,7 +3607,7 @@ dependencies = [ "serde", "serde_json", "serde_yaml", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -3634,9 +3635,9 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.25" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "989e6739f80c4ad5b13e0fd7fe89531180375b18520cc8c82080e4dc4035b84f" +checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" [[package]] name = "rustc-hash" @@ -3659,7 +3660,7 @@ version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.4", "errno", "libc", "linux-raw-sys 0.4.15", @@ -3668,22 +3669,22 @@ dependencies = [ [[package]] name = "rustix" -version = "1.0.7" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" +checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.4", "errno", "libc", - "linux-raw-sys 0.9.4", - "windows-sys 0.59.0", + "linux-raw-sys 0.11.0", + "windows-sys 0.61.0", ] [[package]] name = "rustls" -version = "0.23.28" +version = "0.23.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7160e3e10bf4535308537f3c4e1641468cd0e485175d6163087c0393c7d46643" +checksum = "cd3c25631629d034ce7cd9940adc9d45762d46de2b0f57193c4443b92c6d4d40" dependencies = [ "once_cell", "ring", @@ -3702,7 +3703,7 @@ dependencies = [ "openssl-probe", "rustls-pki-types", "schannel", - "security-framework 3.2.0", + "security-framework 3.5.0", ] [[package]] @@ -3726,9 +3727,9 @@ dependencies = [ [[package]] name = "rustls-platform-verifier" -version = "0.5.3" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19787cda76408ec5404443dc8b31795c87cd8fec49762dc75fa727740d34acc1" +checksum = "be59af91596cac372a6942530653ad0c3a246cdd491aaa9dcaee47f88d67d5a0" dependencies = [ "core-foundation 0.10.1", "core-foundation-sys", @@ -3739,9 +3740,9 @@ dependencies = [ "rustls-native-certs", "rustls-platform-verifier-android", "rustls-webpki", - "security-framework 3.2.0", + "security-framework 3.5.0", "security-framework-sys", - "webpki-root-certs 0.26.11", + "webpki-root-certs", "windows-sys 0.59.0", ] @@ -3753,9 +3754,9 @@ checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" [[package]] name = "rustls-webpki" -version = "0.103.3" +version = "0.103.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4a72fe2bcf7a6ac6fd7d0b9e5cb68aeb7d4c0a0271730218b3e92d43b4eb435" +checksum = "8572f3c2cb9934231157b45499fc41e1f58c589fdfb81a844ba873265e80f8eb" dependencies = [ "ring", "rustls-pki-types", @@ -3764,9 +3765,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.21" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] name = "ryu" @@ -3774,15 +3775,6 @@ version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" -[[package]] -name = "safe_arch" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96b02de82ddbe1b636e6170c21be622223aea188ef2e139be0a5b219ec215323" -dependencies = [ - "bytemuck", -] - [[package]] name = "same-file" version = "1.0.6" @@ -3794,11 +3786,11 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.27" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" +checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.0", ] [[package]] @@ -3819,7 +3811,7 @@ version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.4", "core-foundation 0.9.4", "core-foundation-sys", "libc", @@ -3828,11 +3820,11 @@ dependencies = [ [[package]] name = "security-framework" -version = "3.2.0" +version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "271720403f46ca04f7ba6f55d438f8bd878d6b8ca0a1046e8228c4145bcbb316" +checksum = "cc198e42d9b7510827939c9a15f5062a0c913f3371d765977e586d2fe6c16f4a" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.4", "core-foundation 0.10.1", "core-foundation-sys", "libc", @@ -3841,9 +3833,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.14.0" +version = "2.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" +checksum = "cc1f0cbffaac4852523ce30d8bd3c5cdc873501d96ff467ca09b6767bb8cd5c0" dependencies = [ "core-foundation-sys", "libc", @@ -3851,40 +3843,51 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.26" +version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" [[package]] name = "serde" -version = "1.0.219" +version = "1.0.226" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dca6411025b24b60bfa7ec1fe1f8e710ac09782dca409ee8237ba74b51295fd" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.226" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +checksum = "ba2ba63999edb9dac981fb34b3e5c0d111a69b0924e253ed29d83f7c99e966a4" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.219" +version = "1.0.226" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +checksum = "8db53ae22f34573731bafa1db20f04027b2d25e02d8205921b569171699cdb33" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] name = "serde_json" -version = "1.0.140" +version = "1.0.145" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" +checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" dependencies = [ "itoa", "memchr", "ryu", "serde", + "serde_core", ] [[package]] @@ -3946,6 +3949,17 @@ dependencies = [ "digest", ] +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "sharded-slab" version = "0.1.7" @@ -3963,9 +3977,9 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "signal-hook-registry" -version = "1.4.5" +version = "1.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9203b8055f63a2a00e2f593bb0510367fe707d7ff1e5c872de2f537b339e5410" +checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b" dependencies = [ "libc", ] @@ -3984,9 +3998,9 @@ checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" [[package]] name = "slab" -version = "0.4.10" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04dc19736151f35336d325007ac991178d504a119863a2fcb3758cdb5e52c50d" +checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" [[package]] name = "smallvec" @@ -3997,8 +4011,7 @@ checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] name = "smoltcp" version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dad095989c1533c1c266d9b1e8d70a1329dd3723c3edac6d03bbd67e7bf6f4bb" +source = "git+https://github.com/smoltcp-rs/smoltcp.git?rev=0a926767a68bc88d5512afefa7529c5ecdade4ea#0a926767a68bc88d5512afefa7529c5ecdade4ea" dependencies = [ "bitflags 1.3.2", "byteorder", @@ -4018,6 +4031,16 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "socket2" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + [[package]] name = "stable_deref_trait" version = "1.2.0" @@ -4064,9 +4087,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.104" +version = "2.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" +checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" dependencies = [ "proc-macro2", "quote", @@ -4090,7 +4113,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -4108,7 +4131,7 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.4", "core-foundation 0.9.4", "system-configuration-sys", ] @@ -4167,25 +4190,25 @@ checksum = "7b2093cf4c8eb1e67749a6762251bc9cd836b6fc171623bd0a9d324d37af2417" [[package]] name = "tempfile" -version = "3.20.0" +version = "3.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" +checksum = "84fa4d11fadde498443cca10fd3ac23c951f0dc59e080e9f4b93d4df4e4eea53" dependencies = [ "fastrand", "getrandom 0.3.3", "once_cell", - "rustix 1.0.7", - "windows-sys 0.59.0", + "rustix 1.1.2", + "windows-sys 0.61.0", ] [[package]] name = "terminal_size" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45c6481c4829e4cc63825e62c49186a34538b7b2750b73b266581ffb612fb5ed" +checksum = "60b8cb979cb11c32ce1603f8137b22262a9d131aaa5c37b5678025f22b8becd0" dependencies = [ - "rustix 1.0.7", - "windows-sys 0.59.0", + "rustix 1.1.2", + "windows-sys 0.60.2", ] [[package]] @@ -4199,11 +4222,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.12" +version = "2.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +checksum = "3467d614147380f2e4e374161426ff399c91084acd2363eaf549172b3d5e60c0" dependencies = [ - "thiserror-impl 2.0.12", + "thiserror-impl 2.0.16", ] [[package]] @@ -4214,18 +4237,18 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] name = "thiserror-impl" -version = "2.0.12" +version = "2.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" +checksum = "6c5e1be1c48b9172ee610da68fd9cd2770e7a4056cb3fc98710ee6906f0c7960" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -4239,14 +4262,14 @@ dependencies = [ [[package]] name = "thunk-rs" -version = "0.3.4" -source = "git+https://github.com/easytier/thunk.git#403f0d26d3d5bcfdfd76c23e36e517f19fe891e0" +version = "0.3.5" +source = "git+https://github.com/easytier/thunk.git#cbbeec75a66b7b3cf0824ae890d9d06bcfb9d1f3" [[package]] name = "time" -version = "0.3.41" +version = "0.3.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" +checksum = "91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d" dependencies = [ "deranged", "itoa", @@ -4261,15 +4284,15 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.4" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" +checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b" [[package]] name = "time-macros" -version = "0.2.22" +version = "0.2.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" +checksum = "30cfb0125f12d9c277f35663a0a33f8c30190f4e4574868a330595412d34ebf3" dependencies = [ "num-conv", "time-core", @@ -4293,9 +4316,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09b3661f17e86524eccd4371ab0429194e0d7c008abb45f7a7495b1719463c71" +checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" dependencies = [ "tinyvec_macros", ] @@ -4308,9 +4331,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.46.1" +version = "1.47.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cc3a2344dafbe23a245241fe8b09735b521110d30fcefbbd5feb1797ca35d17" +checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038" dependencies = [ "backtrace", "bytes", @@ -4321,9 +4344,9 @@ dependencies = [ "pin-project-lite", "signal-hook-registry", "slab", - "socket2", + "socket2 0.6.0", "tokio-macros", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -4334,7 +4357,7 @@ checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -4349,9 +4372,9 @@ dependencies = [ [[package]] name = "tokio-rustls" -version = "0.26.2" +version = "0.26.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b" +checksum = "05f63835928ca123f1bef57abbcd23bb2ba0ac9ae1235f1e65bda0d06e7786bd" dependencies = [ "rustls", "tokio", @@ -4370,9 +4393,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.15" +version = "0.7.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66a539a9ad6d5d281510d5bd368c973d636c02dbf8a67300bfb6b950696ad7df" +checksum = "14307c986784f72ef81c89db7d9e28d6ac26d16213b109ea501696195e6e3ce5" dependencies = [ "bytes", "futures-core", @@ -4454,7 +4477,7 @@ dependencies = [ "prost-build", "prost-types", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -4478,7 +4501,7 @@ version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.4", "bytes", "futures-util", "http", @@ -4514,18 +4537,6 @@ dependencies = [ "tracing-core", ] -[[package]] -name = "tracing-appender" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3566e8ce28cc0a3fe42519fc80e6b4c943cc4c8cef275620eb8dac2d3d4e06cf" -dependencies = [ - "crossbeam-channel", - "thiserror 1.0.69", - "time", - "tracing-subscriber", -] - [[package]] name = "tracing-attributes" version = "0.1.30" @@ -4534,7 +4545,7 @@ checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -4560,14 +4571,14 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.19" +version = "0.3.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" +checksum = "2054a14f5307d601f88daf0553e1cbf472acc4f2c51afab632431cdcd72124d5" dependencies = [ "matchers", "nu-ansi-term", "once_cell", - "regex", + "regex-automata", "sharded-slab", "smallvec", "thread_local", @@ -4657,9 +4668,9 @@ checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" [[package]] name = "unicode-ident" -version = "1.0.18" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" +checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d" [[package]] name = "unicode-segmentation" @@ -4703,9 +4714,9 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.5.4" +version = "2.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" +checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b" dependencies = [ "form_urlencoded", "idna", @@ -4727,13 +4738,13 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.17.0" +version = "1.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cf4199d1e5d15ddd86a694e4d0dffa9c323ce759fea589f00fef9d81cc1931d" +checksum = "2f87b8aa10b915a06587d0dec516c282ff295b475d94abf425d62b57710070a2" dependencies = [ "getrandom 0.3.3", "js-sys", - "rand 0.9.1", + "rand 0.9.2", "serde", "uuid-macro-internal", "wasm-bindgen", @@ -4741,13 +4752,13 @@ dependencies = [ [[package]] name = "uuid-macro-internal" -version = "1.17.0" +version = "1.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b682e8c381995ea03130e381928e0e005b7c9eb483c6c8682f50e07b33c2b7" +checksum = "d9384a660318abfbd7f8932c34d67e4d1ec511095f95972ddc01e19d7ba8413f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -4807,44 +4818,54 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasi" -version = "0.14.2+wasi-0.2.4" +version = "0.14.7+wasi-0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +checksum = "883478de20367e224c0090af9cf5f9fa85bed63a95c1abf3afc5c083ebc06e8c" dependencies = [ - "wit-bindgen-rt", + "wasip2", +] + +[[package]] +name = "wasip2" +version = "1.0.1+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" +dependencies = [ + "wit-bindgen", ] [[package]] name = "wasm-bindgen" -version = "0.2.100" +version = "0.2.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +checksum = "ab10a69fbd0a177f5f649ad4d8d3305499c42bab9aef2f7ff592d0ec8f833819" dependencies = [ "cfg-if", "once_cell", "rustversion", "wasm-bindgen-macro", + "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.100" +version = "0.2.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" +checksum = "0bb702423545a6007bbc368fde243ba47ca275e549c8a28617f56f6ba53b1d1c" dependencies = [ "bumpalo", "log", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.50" +version = "0.4.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" +checksum = "a0b221ff421256839509adbb55998214a70d829d3a28c69b4a6672e9d2a42f67" dependencies = [ "cfg-if", "js-sys", @@ -4855,9 +4876,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.100" +version = "0.2.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +checksum = "fc65f4f411d91494355917b605e1480033152658d71f722a90647f56a70c88a0" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -4865,31 +4886,31 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.100" +version = "0.2.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +checksum = "ffc003a991398a8ee604a401e194b6b3a39677b3173d6e74495eb51b82e99a32" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.100" +version = "0.2.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +checksum = "293c37f4efa430ca14db3721dfbe48d8c33308096bd44d80ebaa775ab71ba1cf" dependencies = [ "unicode-ident", ] [[package]] name = "web-sys" -version = "0.3.77" +version = "0.3.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" +checksum = "fbe734895e869dc429d78c4b433f8d17d95f8d05317440b4fad5ab2d33e596dc" dependencies = [ "js-sys", "wasm-bindgen", @@ -4917,18 +4938,9 @@ dependencies = [ [[package]] name = "webpki-root-certs" -version = "0.26.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75c7f0ef91146ebfb530314f5f1d24528d7f0767efbfd31dce919275413e393e" -dependencies = [ - "webpki-root-certs 1.0.1", -] - -[[package]] -name = "webpki-root-certs" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86138b15b2b7d561bc4469e77027b8dd005a43dc502e9031d1f5afc8ce1f280e" +checksum = "4e4ffd8df1c57e87c325000a3d6ef93db75279dc3a231125aac571650f22b12a" dependencies = [ "rustls-pki-types", ] @@ -4939,14 +4951,14 @@ version = "0.26.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" dependencies = [ - "webpki-roots 1.0.1", + "webpki-roots 1.0.2", ] [[package]] name = "webpki-roots" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8782dd5a41a24eed3a4f40b606249b3e236ca61adf1f25ea4d45c73de122b502" +checksum = "7e8983c3ab33d6fb807cfcdad2491c4ea8cbc8ed839181c7dfd9c67c83e261b2" dependencies = [ "rustls-pki-types", ] @@ -4971,20 +4983,10 @@ checksum = "24d643ce3fd3e5b54854602a080f34fb10ab75e0b813ee32d00ca2b44fa74762" dependencies = [ "either", "env_home", - "rustix 1.0.7", + "rustix 1.1.2", "winsafe", ] -[[package]] -name = "wide" -version = "0.7.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ce5da8ecb62bcd8ec8b7ea19f69a51275e91299be594ea5cc6ef7819e16cd03" -dependencies = [ - "bytemuck", - "safe_arch", -] - [[package]] name = "widestring" version = "1.2.0" @@ -4993,9 +4995,9 @@ checksum = "dd7cf3379ca1aac9eea11fba24fd7e315d621f8dfe35c8d7d2be8b793726e07d" [[package]] name = "wildmatch" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68ce1ab1f8c62655ebe1350f589c61e505cf94d385bc6a12899442d9081e71fd" +checksum = "39b7d07a236abaef6607536ccfaf19b396dbe3f5110ddb73d39f4562902ed382" [[package]] name = "winapi" @@ -5015,11 +5017,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.9" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.0", ] [[package]] @@ -5056,7 +5058,7 @@ dependencies = [ "windows-collections", "windows-core 0.61.2", "windows-future", - "windows-link", + "windows-link 0.1.3", "windows-numerics", ] @@ -5086,9 +5088,22 @@ checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" dependencies = [ "windows-implement", "windows-interface", - "windows-link", - "windows-result", - "windows-strings", + "windows-link 0.1.3", + "windows-result 0.3.4", + "windows-strings 0.4.2", +] + +[[package]] +name = "windows-core" +version = "0.62.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57fe7168f7de578d2d8a05b07fd61870d2e73b4020e9f49aa00da8471723497c" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link 0.2.0", + "windows-result 0.4.0", + "windows-strings 0.5.0", ] [[package]] @@ -5098,7 +5113,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" dependencies = [ "windows-core 0.61.2", - "windows-link", + "windows-link 0.1.3", "windows-threading", ] @@ -5110,7 +5125,7 @@ checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -5121,7 +5136,7 @@ checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -5130,6 +5145,12 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" +[[package]] +name = "windows-link" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45e46c0661abb7180e7b9c281db115305d49ca1709ab8242adf09666d2173c65" + [[package]] name = "windows-numerics" version = "0.2.0" @@ -5137,7 +5158,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" dependencies = [ "windows-core 0.61.2", - "windows-link", + "windows-link 0.1.3", ] [[package]] @@ -5146,9 +5167,9 @@ version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b8a9ed28765efc97bbc954883f4e6796c33a06546ebafacbabee9696967499e" dependencies = [ - "windows-link", - "windows-result", - "windows-strings", + "windows-link 0.1.3", + "windows-result 0.3.4", + "windows-strings 0.4.2", ] [[package]] @@ -5157,7 +5178,16 @@ version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" dependencies = [ - "windows-link", + "windows-link 0.1.3", +] + +[[package]] +name = "windows-result" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7084dcc306f89883455a206237404d3eaf961e5bd7e0f312f7c91f57eb44167f" +dependencies = [ + "windows-link 0.2.0", ] [[package]] @@ -5166,7 +5196,7 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d24d6bcc7f734a4091ecf8d7a64c5f7d7066f45585c1861eba06449909609c8a" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.4", "widestring", "windows-sys 0.52.0", ] @@ -5177,7 +5207,16 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" dependencies = [ - "windows-link", + "windows-link 0.1.3", +] + +[[package]] +name = "windows-strings" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7218c655a553b0bed4426cf54b20d7ba363ef543b52d515b3e48d7fd55318dda" +dependencies = [ + "windows-link 0.2.0", ] [[package]] @@ -5222,7 +5261,16 @@ version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" dependencies = [ - "windows-targets 0.53.2", + "windows-targets 0.53.3", +] + +[[package]] +name = "windows-sys" +version = "0.61.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e201184e40b2ede64bc2ea34968b28e33622acdbbf37104f0e4a33f7abe657aa" +dependencies = [ + "windows-link 0.2.0", ] [[package]] @@ -5273,10 +5321,11 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.53.2" +version = "0.53.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c66f69fcc9ce11da9966ddb31a40968cad001c5bedeb5c2b82ede4253ab48aef" +checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91" dependencies = [ + "windows-link 0.1.3", "windows_aarch64_gnullvm 0.53.0", "windows_aarch64_msvc 0.53.0", "windows_i686_gnu 0.53.0", @@ -5293,7 +5342,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6" dependencies = [ - "windows-link", + "windows-link 0.1.3", ] [[package]] @@ -5478,9 +5527,9 @@ checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" [[package]] name = "winnow" -version = "0.7.11" +version = "0.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74c7b26e3480b707944fc872477815d29a8e429d2f93a1ce000f5fa84a15cbcd" +checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" dependencies = [ "memchr", ] @@ -5525,13 +5574,10 @@ dependencies = [ ] [[package]] -name = "wit-bindgen-rt" -version = "0.39.0" +name = "wit-bindgen" +version = "0.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" -dependencies = [ - "bitflags 2.9.1", -] +checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" [[package]] name = "writeable" @@ -5553,9 +5599,9 @@ dependencies = [ [[package]] name = "xml-rs" -version = "0.8.26" +version = "0.8.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a62ce76d9b56901b19a74f19431b0d8b3bc7ca4ad685a746dfd78ca8f4fc6bda" +checksum = "6fd8403733700263c6eb89f192880191f1b83e332f7a20371ddcf421c4a337c7" [[package]] name = "yasna" @@ -5586,7 +5632,7 @@ checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", "synstructure", ] @@ -5602,11 +5648,11 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.26" +version = "0.8.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f" +checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c" dependencies = [ - "zerocopy-derive 0.8.26", + "zerocopy-derive 0.8.27", ] [[package]] @@ -5617,18 +5663,18 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] name = "zerocopy-derive" -version = "0.8.26" +version = "0.8.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" +checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -5648,7 +5694,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", "synstructure", ] @@ -5669,7 +5715,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -5685,9 +5731,9 @@ dependencies = [ [[package]] name = "zerovec" -version = "0.11.2" +version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428" +checksum = "e7aa2bd55086f1ab526693ecbe444205da57e25f4489879da80635a46d90e73b" dependencies = [ "yoke", "zerofrom", @@ -5702,14 +5748,14 @@ checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] name = "zip" -version = "4.2.0" +version = "4.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95ab361742de920c5535880f89bbd611ee62002bf11341d16a5f057bb8ba6899" +checksum = "caa8cd6af31c3b31c6631b8f483848b91589021b28fffe50adada48d4f4d2ed1" dependencies = [ "aes", "arbitrary", @@ -5724,6 +5770,7 @@ dependencies = [ "liblzma", "memchr", "pbkdf2", + "ppmd-rust", "sha1", "time", "zeroize", @@ -5733,9 +5780,9 @@ dependencies = [ [[package]] name = "zlib-rs" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "626bd9fa9734751fc50d6060752170984d7053f5a39061f524cda68023d4db8a" +checksum = "2f06ae92f42f5e5c42443fd094f245eb656abf56dd7cce9b8b263236565e00f2" [[package]] name = "zopfli" @@ -5769,9 +5816,9 @@ dependencies = [ [[package]] name = "zstd-sys" -version = "2.0.15+zstd.1.5.7" +version = "2.0.16+zstd.1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb81183ddd97d0c74cedf1d50d85c8d08c1b8b68ee863bdee9e706eedba1a237" +checksum = "91e19ebc2adc8f83e43039e79776e3fda8ca919132d68a1fed6a5faca2683748" dependencies = [ "cc", "pkg-config", diff --git a/easytier-contrib/easytier-ohrs/Cargo.toml b/easytier-contrib/easytier-ohrs/Cargo.toml index 45476f56d..bef483144 100644 --- a/easytier-contrib/easytier-ohrs/Cargo.toml +++ b/easytier-contrib/easytier-ohrs/Cargo.toml @@ -9,8 +9,8 @@ crate-type=["cdylib"] [dependencies] ohos-hilog-binding = {version = "*", features = ["redirect"]} easytier = { git = "https://github.com/EasyTier/EasyTier.git" } -napi-derive-ohos = "1.0.4" -napi-ohos = { version = "1.0.4", default-features = false, features = [ +napi-derive-ohos = "1.1" +napi-ohos = { version = "1.1", default-features = false, features = [ "serde-json", "latin1", "chrono_date", @@ -33,7 +33,7 @@ tracing = "0.1.41" uuid = { version = "1.17.0", features = ["v4"] } [build-dependencies] -napi-build-ohos = "1.0.4" +napi-build-ohos = "1.1" [profile.dev] panic = "unwind" debug = true diff --git a/easytier-contrib/easytier-ohrs/build.rs b/easytier-contrib/easytier-ohrs/build.rs index 1320ceb28..a6e462399 100644 --- a/easytier-contrib/easytier-ohrs/build.rs +++ b/easytier-contrib/easytier-ohrs/build.rs @@ -1,3 +1,3 @@ -fn main () { +fn main() { napi_build_ohos::setup(); -} \ No newline at end of file +} diff --git a/easytier-contrib/easytier-ohrs/package/CHANGELOG.md b/easytier-contrib/easytier-ohrs/package/CHANGELOG.md new file mode 100755 index 000000000..c6458f695 --- /dev/null +++ b/easytier-contrib/easytier-ohrs/package/CHANGELOG.md @@ -0,0 +1,2 @@ +# 0.0.1 +- init package diff --git a/easytier-contrib/easytier-ohrs/package/LICENSE b/easytier-contrib/easytier-ohrs/package/LICENSE new file mode 100755 index 000000000..0a041280b --- /dev/null +++ b/easytier-contrib/easytier-ohrs/package/LICENSE @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/easytier-contrib/easytier-ohrs/package/README.md b/easytier-contrib/easytier-ohrs/package/README.md new file mode 100755 index 000000000..d9808e356 --- /dev/null +++ b/easytier-contrib/easytier-ohrs/package/README.md @@ -0,0 +1,21 @@ +# `easytier-ohrs` + +## Install + +use `ohpm` to install package. + +```shell +ohpm install easytier-ohrs +``` + +## API + +```ts +// todo +``` + +## Usage + +```ts +// todo +``` diff --git a/easytier-contrib/easytier-ohrs/package/index.ets b/easytier-contrib/easytier-ohrs/package/index.ets new file mode 100755 index 000000000..59227c041 --- /dev/null +++ b/easytier-contrib/easytier-ohrs/package/index.ets @@ -0,0 +1,4 @@ +import * as api from "libeasytier_ohrs.so"; + +export * from 'libeasytier_ohrs.so'; +export default api; diff --git a/easytier-contrib/easytier-ohrs/package/oh-package.json5 b/easytier-contrib/easytier-ohrs/package/oh-package.json5 new file mode 100755 index 000000000..fccd2cc9e --- /dev/null +++ b/easytier-contrib/easytier-ohrs/package/oh-package.json5 @@ -0,0 +1,10 @@ +{ + "license": "LGPL-3.0", + "author": "easytier", + "name": "easytier-ohrs", + "description": "", + "main": "index.ets", + "version": "0.0.1", + "types": "libs/index.d.ts", + "dependencies": {} +} diff --git a/easytier-contrib/easytier-ohrs/package/src/main/module.json5 b/easytier-contrib/easytier-ohrs/package/src/main/module.json5 new file mode 100755 index 000000000..35fa44507 --- /dev/null +++ b/easytier-contrib/easytier-ohrs/package/src/main/module.json5 @@ -0,0 +1,7 @@ +{ + "module": { + "name": "easytier-ohrs", + "type": "har", + "deviceTypes": ["default", "tablet", "2in1"] + }, +} diff --git a/easytier-contrib/easytier-ohrs/src/lib.rs b/easytier-contrib/easytier-ohrs/src/lib.rs index e1e7518b8..fdf25915d 100644 --- a/easytier-contrib/easytier-ohrs/src/lib.rs +++ b/easytier-contrib/easytier-ohrs/src/lib.rs @@ -18,23 +18,18 @@ pub struct KeyValuePair { } #[napi] -pub fn set_tun_fd( - inst_id: String, - fd: i32, -) -> bool { +pub fn set_tun_fd(inst_id: String, fd: i32) -> bool { match Uuid::try_parse(&inst_id) { - Ok(uuid) => { - match INSTANCE_MANAGER.set_tun_fd(&uuid, fd) { - Ok(_) => { - hilog_debug!("[Rust] set tun fd {} to {}.", fd, inst_id); - true - } - Err(e) => { - hilog_error!("[Rust] cant set tun fd {} to {}. {}", fd, inst_id, e); - false - } + Ok(uuid) => match INSTANCE_MANAGER.set_tun_fd(&uuid, fd) { + Ok(_) => { + hilog_debug!("[Rust] set tun fd {} to {}.", fd, inst_id); + true } - } + Err(e) => { + hilog_error!("[Rust] cant set tun fd {} to {}. {}", fd, inst_id, e); + false + } + }, Err(e) => { hilog_error!("[Rust] cant covert {} to uuid. {}", inst_id, e); false @@ -45,9 +40,7 @@ pub fn set_tun_fd( #[napi] pub fn parse_config(cfg_str: String) -> bool { match TomlConfigLoader::new_from_str(&cfg_str) { - Ok(_) => { - true - } + Ok(_) => true, Err(e) => { hilog_error!("[Rust] parse config failed {}", e); false @@ -64,8 +57,8 @@ pub fn run_network_instance(cfg_str: String) -> bool { return false; } }; - - if INSTANCE_MANAGER.list_network_instance_ids().len() > 0 { + + if INSTANCE_MANAGER.list_network_instance_ids().len() > 0 { hilog_error!("[Rust] there is a running instance!"); return false; } @@ -99,7 +92,7 @@ pub fn stop_network_instance(inst_names: Vec) { #[napi] pub fn collect_network_infos() -> Vec { let mut result = Vec::new(); - match INSTANCE_MANAGER.collect_network_infos() { + match INSTANCE_MANAGER.collect_network_infos_sync() { Ok(map) => { for (uuid, info) in map.iter() { // convert value to json string @@ -134,15 +127,10 @@ pub fn collect_running_network() -> Vec { #[napi] pub fn is_running_network(inst_id: String) -> bool { match Uuid::try_parse(&inst_id) { - Ok(uuid) => { - INSTANCE_MANAGER - .list_network_instance_ids() - .contains(&uuid) - } + Ok(uuid) => INSTANCE_MANAGER.list_network_instance_ids().contains(&uuid), Err(e) => { hilog_error!("[Rust] cant covert {} to uuid. {}", inst_id, e); false } } - } diff --git a/easytier-contrib/easytier-ohrs/src/native_log.rs b/easytier-contrib/easytier-ohrs/src/native_log.rs index 221dce1cc..abfbc611f 100644 --- a/easytier-contrib/easytier-ohrs/src/native_log.rs +++ b/easytier-contrib/easytier-ohrs/src/native_log.rs @@ -1,7 +1,9 @@ +use napi_derive_ohos::napi; +use ohos_hilog_binding::{ + LogOptions, hilog_debug, hilog_error, hilog_info, hilog_warn, set_global_options, +}; use std::collections::HashMap; use std::panic; -use napi_derive_ohos::napi; -use ohos_hilog_binding::{hilog_debug, hilog_error, hilog_info, hilog_warn, set_global_options, LogOptions}; use tracing::{Event, Subscriber}; use tracing_core::Level; use tracing_subscriber::layer::{Context, Layer}; @@ -20,12 +22,9 @@ pub fn init_panic_hook() { } #[napi] -pub fn hilog_global_options( - domain: u32, - tag: String, -) { +pub fn hilog_global_options(domain: u32, tag: String) { ohos_hilog_binding::forward_stdio_to_hilog(); - set_global_options(LogOptions{ + set_global_options(LogOptions { domain, tag: Box::leak(tag.clone().into_boxed_str()), }) @@ -34,11 +33,9 @@ pub fn hilog_global_options( #[napi] pub fn init_tracing_subscriber() { tracing_subscriber::registry() - .with( - CallbackLayer { - callback: Box::new(tracing_callback), - } - ) + .with(CallbackLayer { + callback: Box::new(tracing_callback), + }) .init(); } @@ -93,6 +90,7 @@ impl<'a> tracing::field::Visit for FieldCollector<'a> { } fn record_debug(&mut self, field: &tracing::field::Field, value: &dyn std::fmt::Debug) { - self.0.insert(field.name().to_string(), format!("{:?}", value)); + self.0 + .insert(field.name().to_string(), format!("{:?}", value)); } -} \ No newline at end of file +} diff --git a/easytier-contrib/easytier-uptime/.env.development b/easytier-contrib/easytier-uptime/.env.development new file mode 100644 index 000000000..a38dd975a --- /dev/null +++ b/easytier-contrib/easytier-uptime/.env.development @@ -0,0 +1,17 @@ +# Development Environment Configuration +SERVER_HOST=127.0.0.1 +SERVER_PORT=8080 +DATABASE_PATH=uptime.db +DATABASE_MAX_CONNECTIONS=5 +HEALTH_CHECK_INTERVAL=60 +HEALTH_CHECK_TIMEOUT=15 +HEALTH_CHECK_RETRIES=2 +RUST_LOG=debug +LOG_LEVEL=debug +CORS_ALLOWED_ORIGINS=http://localhost:3000,http://localhost:8080 +CORS_ALLOWED_METHODS=GET,POST,PUT,DELETE,OPTIONS +CORS_ALLOWED_HEADERS=content-type,authorization +NODE_ENV=development +API_BASE_URL=/api +ENABLE_COMPRESSION=true +ENABLE_CORS=true \ No newline at end of file diff --git a/easytier-contrib/easytier-uptime/.env.example b/easytier-contrib/easytier-uptime/.env.example new file mode 100644 index 000000000..88aebf446 --- /dev/null +++ b/easytier-contrib/easytier-uptime/.env.example @@ -0,0 +1,29 @@ +# Server Configuration +SERVER_HOST=127.0.0.1 +SERVER_PORT=8080 + +# Database Configuration +DATABASE_PATH=uptime.db +DATABASE_MAX_CONNECTIONS=10 + +# Health Check Configuration +HEALTH_CHECK_INTERVAL=30 +HEALTH_CHECK_TIMEOUT=10 +HEALTH_CHECK_RETRIES=3 + +# Logging Configuration +RUST_LOG=info +LOG_LEVEL=info + +# CORS Configuration +CORS_ALLOWED_ORIGINS=http://localhost:3000,http://localhost:8080 +CORS_ALLOWED_METHODS=GET,POST,PUT,DELETE,OPTIONS +CORS_ALLOWED_HEADERS=content-type,authorization + +# Production Configuration +NODE_ENV=development +API_BASE_URL=/api + +# Security Configuration +ENABLE_COMPRESSION=true +ENABLE_CORS=true \ No newline at end of file diff --git a/easytier-contrib/easytier-uptime/.env.production b/easytier-contrib/easytier-uptime/.env.production new file mode 100644 index 000000000..6909ec7c7 --- /dev/null +++ b/easytier-contrib/easytier-uptime/.env.production @@ -0,0 +1,21 @@ +# Production Environment Configuration +SERVER_HOST=0.0.0.0 +SERVER_PORT=8080 +DATABASE_PATH=/var/lib/easytier-uptime/uptime.db +DATABASE_MAX_CONNECTIONS=20 +HEALTH_CHECK_INTERVAL=30 +HEALTH_CHECK_TIMEOUT=10 +HEALTH_CHECK_RETRIES=3 +RUST_LOG=info +LOG_LEVEL=info +CORS_ALLOWED_ORIGINS=https://yourdomain.com +CORS_ALLOWED_METHODS=GET,POST,PUT,DELETE,OPTIONS +CORS_ALLOWED_HEADERS=content-type,authorization +NODE_ENV=production +API_BASE_URL=/api +ENABLE_COMPRESSION=true +ENABLE_CORS=true + +# Security +SECRET_KEY=your-secret-key-here +JWT_SECRET=your-jwt-secret-here \ No newline at end of file diff --git a/easytier-contrib/easytier-uptime/.gitignore b/easytier-contrib/easytier-uptime/.gitignore new file mode 100644 index 000000000..81e8b4db3 --- /dev/null +++ b/easytier-contrib/easytier-uptime/.gitignore @@ -0,0 +1,3 @@ +*.db +*.db-shm +*.db-wal diff --git a/easytier-contrib/easytier-uptime/Cargo.toml b/easytier-contrib/easytier-uptime/Cargo.toml new file mode 100644 index 000000000..559dc339c --- /dev/null +++ b/easytier-contrib/easytier-uptime/Cargo.toml @@ -0,0 +1,63 @@ +[package] +name = "easytier-uptime" +version = "0.1.0" +edition = "2021" + +[dependencies] +tokio = { version = "1.0", features = ["full"] } +tracing = "0.1" +tracing-subscriber = { version = "0.3", features = ["env-filter"] } +anyhow = "1.0" +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +chrono = { version = "0.4", features = ["serde"] } +uuid = { version = "1.0", features = ["v4", "serde"] } + +# Axum web framework +axum = { version = "0.8.4", features = ["macros"] } +tower-http = { version = "0.6", features = ["cors", "compression-full"] } +tower = "0.5" + +# SeaORM dependencies +sea-orm = { version = "1.1", features = [ + "sqlx-sqlite", + "runtime-tokio-rustls", + "macros", + "with-chrono", + "with-uuid", + "with-json" +] } +sea-orm-migration = { version = "1.1" } +sqlx = { version = "0.8", features = ["sqlite", "runtime-tokio-rustls", "chrono", "uuid"] } + +# Validation +validator = { version = "0.18", features = ["derive"] } +thiserror = "1.0" +jsonwebtoken = "9.0" + +# Configuration and serialization +serde_yaml = "0.9" +toml = "0.8" + +# Network and async +async-trait = "0.1" +futures = "0.3" +tokio-util = { version = "0.7", features = ["full"] } + +# Filesystem operations +tempfile = "3.8" + +# Additional utilities +dashmap = "6.1.0" +clap = { version = "4.0", features = ["derive"] } +parking_lot = "0.12" +once_cell = "1.19" + +# EasyTier core +easytier = { path = "../../easytier" } + +# Testing +[dev-dependencies] +mockall = "0.12" +tokio-test = "0.4" +reqwest = "0.12" diff --git a/easytier-contrib/easytier-uptime/README.md b/easytier-contrib/easytier-uptime/README.md new file mode 100644 index 000000000..59ebaa69d --- /dev/null +++ b/easytier-contrib/easytier-uptime/README.md @@ -0,0 +1,272 @@ +# EasyTier Uptime Monitor + +一个用于监控 EasyTier 实例健康状态和运行时间的系统。 + +## 功能特性 + +- 🏥 **健康监控**: 实时监控 EasyTier 节点的健康状态 +- 📊 **数据统计**: 提供详细的运行时间和响应时间统计 +- 🔧 **实例管理**: 管理多个 EasyTier 实例 +- 🌐 **Web界面**: 直观的 Web 管理界面 +- 🚨 **告警系统**: 支持健康状态异常告警 +- 📈 **图表展示**: 可视化展示监控数据 + +## 系统架构 + +``` +┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ +│ Frontend │ │ Backend │ │ Database │ +│ (Vue.js) │◄──►│ (Rust/Axum) │◄──►│ (SQLite) │ +│ │ │ │ │ │ +│ ┌─────────────┐ │ │ ┌─────────────┐ │ │ ┌─────────────┐ │ +│ │ Dashboard │ │ │ │ API Routes │ │ │ │ Nodes │ │ +│ │ Health View │ │ │ │ Health │ │ │ │ Health │ │ +│ │ Node Mgmt │ │ │ │ Instances │ │ │ │ Instances │ │ +│ │ Charts │ │ │ │ Scheduler │ │ │ │ Stats │ │ +│ └─────────────┘ │ │ └─────────────┘ │ │ └─────────────┘ │ +└─────────────────┘ └─────────────────┘ └─────────────────┘ +``` + +## 快速开始 + +### 环境要求 + +- **Rust**: 1.70+ +- **Node.js**: 16+ +- **npm**: 8+ + +### 开发环境 + +1. **克隆项目** + ```bash + git clone + cd easytier-uptime + ``` + +2. **启动开发环境** + ```bash + ./start-dev.sh + ``` + +3. **访问应用** + - 前端界面: http://localhost:3000 + - 后端API: http://localhost:8080 + - 健康检查: http://localhost:8080/health + +### 生产环境 + +1. **启动生产环境** + ```bash + ./start-prod.sh + ``` + +2. **停止生产环境** + ```bash + ./stop-prod.sh + ``` + +## 配置说明 + +### 环境变量 + +#### 后端配置 (.env) + +| 变量名 | 默认值 | 说明 | +|--------|--------|------| +| `SERVER_HOST` | `127.0.0.1` | 服务器监听地址 | +| `SERVER_PORT` | `8080` | 服务器端口 | +| `DATABASE_PATH` | `uptime.db` | 数据库文件路径 | +| `DATABASE_MAX_CONNECTIONS` | `10` | 数据库最大连接数 | +| `HEALTH_CHECK_INTERVAL` | `30` | 健康检查间隔(秒) | +| `HEALTH_CHECK_TIMEOUT` | `10` | 健康检查超时(秒) | +| `HEALTH_CHECK_RETRIES` | `3` | 健康检查重试次数 | +| `RUST_LOG` | `info` | 日志级别 | +| `CORS_ALLOWED_ORIGINS` | `http://localhost:3000` | 允许的跨域来源 | +| `ENABLE_CORS` | `true` | 是否启用CORS | +| `ENABLE_COMPRESSION` | `true` | 是否启用压缩 | + +#### 前端配置 (frontend/.env) + +| 变量名 | 默认值 | 说明 | +|--------|--------|------| +| `VITE_APP_TITLE` | `EasyTier Uptime Monitor` | 应用标题 | +| `VITE_API_BASE_URL` | `/api` | API基础URL | +| `VITE_APP_ENV` | `development` | 应用环境 | +| `VITE_ENABLE_DEV_TOOLS` | `true` | 是否启用开发工具 | +| `VITE_API_TIMEOUT` | `10000` | API超时时间(毫秒) | + +## API 文档 + +### 健康检查 + +```http +GET /health +``` + +### 节点管理 + +```http +# 获取节点列表 +GET /api/nodes + +# 创建节点 +POST /api/nodes + +# 获取节点详情 +GET /api/nodes/{id} + +# 更新节点 +PUT /api/nodes/{id} + +# 删除节点 +DELETE /api/nodes/{id} +``` + +### 健康记录 + +```http +# 获取节点健康历史 +GET /api/nodes/{id}/health + +# 获取节点健康统计 +GET /api/nodes/{id}/health/stats +``` + +### 实例管理 + +```http +# 获取实例列表 +GET /api/instances + +# 创建实例 +POST /api/instances + +# 停止实例 +DELETE /api/instances/{id} +``` + +## 测试 + +### 运行集成测试 + +```bash +./test-integration.sh +``` + +### 运行单元测试 + +```bash +cargo test +``` + +### 测试覆盖率 + +```bash +cargo tarpaulin +``` + +## 部署 + +### Docker 部署 + +```bash +# 构建镜像 +docker build -t easytier-uptime . + +# 运行容器 +docker run -d -p 8080:8080 easytier-uptime +``` + +### 手动部署 + +1. **构建后端** + ```bash + cargo build --release + ``` + +2. **构建前端** + ```bash + cd frontend + npm install + npm run build + cd .. + ``` + +3. **配置环境** + ```bash + cp .env.production .env + # 编辑 .env 文件 + ``` + +4. **启动服务** + ```bash + ./start-prod.sh + ``` + +## 监控和日志 + +### 日志文件 + +- **后端日志**: `logs/backend.log` +- **前端日志**: `logs/frontend.log` +- **测试日志**: `test-results/` + +### 健康检查 + +系统提供以下健康检查端点: + +- `/health` - 基本健康检查 +- `/api/health/stats` - 健康统计信息 +- `/api/health/scheduler/status` - 调度器状态 + +## 故障排除 + +### 常见问题 + +1. **后端启动失败** + - 检查端口是否被占用 + - 确认数据库文件权限 + - 查看日志文件 `logs/backend.log` + +2. **前端连接失败** + - 检查后端服务是否运行 + - 确认API地址配置 + - 检查CORS配置 + +3. **健康检查失败** + - 确认目标节点可访问 + - 检查防火墙设置 + - 验证健康检查配置 + +### 性能优化 + +1. **数据库优化** + - 定期清理过期数据 + - 配置适当的连接池大小 + - 使用索引优化查询 + +2. **前端优化** + - 启用代码分割 + - 配置缓存策略 + - 优化图片和资源 + +3. **网络优化** + - 启用压缩 + - 配置CDN + - 优化API响应时间 + +## 贡献指南 + +1. Fork 项目 +2. 创建特性分支 +3. 提交更改 +4. 推送到分支 +5. 创建 Pull Request + +## 许可证 + +MIT License + +## 支持 + +如有问题或建议,请提交 Issue 或联系开发团队。 \ No newline at end of file diff --git a/easytier-contrib/easytier-uptime/frontend/.gitignore b/easytier-contrib/easytier-uptime/frontend/.gitignore new file mode 100644 index 000000000..a547bf36d --- /dev/null +++ b/easytier-contrib/easytier-uptime/frontend/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/easytier-contrib/easytier-uptime/frontend/README.md b/easytier-contrib/easytier-uptime/frontend/README.md new file mode 100644 index 000000000..1511959c2 --- /dev/null +++ b/easytier-contrib/easytier-uptime/frontend/README.md @@ -0,0 +1,5 @@ +# Vue 3 + Vite + +This template should help get you started developing with Vue 3 in Vite. The template uses Vue 3 ` + + diff --git a/easytier-contrib/easytier-uptime/frontend/package-lock.json b/easytier-contrib/easytier-uptime/frontend/package-lock.json new file mode 100644 index 000000000..92f9ad0c5 --- /dev/null +++ b/easytier-contrib/easytier-uptime/frontend/package-lock.json @@ -0,0 +1,2557 @@ +{ + "name": "easytier-uptime-frontend", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "easytier-uptime-frontend", + "version": "0.0.0", + "dependencies": { + "@element-plus/icons-vue": "^2.3.1", + "axios": "^1.7.9", + "dayjs": "^1.11.13", + "element-plus": "^2.8.8", + "vue": "^3.5.18", + "vue-router": "^4.4.5" + }, + "devDependencies": { + "@vitejs/plugin-vue": "^6.0.1", + "unplugin-auto-import": "^0.18.6", + "unplugin-vue-components": "^0.27.4", + "vite": "^7.1.2" + } + }, + "node_modules/@antfu/utils": { + "version": "0.7.10", + "resolved": "https://registry.npmjs.org/@antfu/utils/-/utils-0.7.10.tgz", + "integrity": "sha512-+562v9k4aI80m1+VuMHehNJWLOFjBnXn3tdOitzD0il5b7smkSBal4+a3oKiQTbrwMmN/TBUMDvbdoWDehgOww==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", + "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.0.tgz", + "integrity": "sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/types": { + "version": "7.28.2", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.2.tgz", + "integrity": "sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@ctrl/tinycolor": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-3.6.1.tgz", + "integrity": "sha512-SITSV6aIXsuVNV3f3O0f2n/cgyEDWoSqtZMYiAmcsYHydcKrOz3gUxB/iXd/Qf08+IZX4KpgNbvUdMBmWz+kcA==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/@element-plus/icons-vue": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@element-plus/icons-vue/-/icons-vue-2.3.2.tgz", + "integrity": "sha512-OzIuTaIfC8QXEPmJvB4Y4kw34rSXdCJzxcD1kFStBvr8bK6X1zQAYDo0CNMjojnfTqRQCJ0I7prlErcoRiET2A==", + "license": "MIT", + "peerDependencies": { + "vue": "^3.2.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.9.tgz", + "integrity": "sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.9.tgz", + "integrity": "sha512-5WNI1DaMtxQ7t7B6xa572XMXpHAaI/9Hnhk8lcxF4zVN4xstUgTlvuGDorBguKEnZO70qwEcLpfifMLoxiPqHQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.9.tgz", + "integrity": "sha512-IDrddSmpSv51ftWslJMvl3Q2ZT98fUSL2/rlUXuVqRXHCs5EUF1/f+jbjF5+NG9UffUDMCiTyh8iec7u8RlTLg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.9.tgz", + "integrity": "sha512-I853iMZ1hWZdNllhVZKm34f4wErd4lMyeV7BLzEExGEIZYsOzqDWDf+y082izYUE8gtJnYHdeDpN/6tUdwvfiw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.9.tgz", + "integrity": "sha512-XIpIDMAjOELi/9PB30vEbVMs3GV1v2zkkPnuyRRURbhqjyzIINwj+nbQATh4H9GxUgH1kFsEyQMxwiLFKUS6Rg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.9.tgz", + "integrity": "sha512-jhHfBzjYTA1IQu8VyrjCX4ApJDnH+ez+IYVEoJHeqJm9VhG9Dh2BYaJritkYK3vMaXrf7Ogr/0MQ8/MeIefsPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.9.tgz", + "integrity": "sha512-z93DmbnY6fX9+KdD4Ue/H6sYs+bhFQJNCPZsi4XWJoYblUqT06MQUdBCpcSfuiN72AbqeBFu5LVQTjfXDE2A6Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.9.tgz", + "integrity": "sha512-mrKX6H/vOyo5v71YfXWJxLVxgy1kyt1MQaD8wZJgJfG4gq4DpQGpgTB74e5yBeQdyMTbgxp0YtNj7NuHN0PoZg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.9.tgz", + "integrity": "sha512-HBU2Xv78SMgaydBmdor38lg8YDnFKSARg1Q6AT0/y2ezUAKiZvc211RDFHlEZRFNRVhcMamiToo7bDx3VEOYQw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.9.tgz", + "integrity": "sha512-BlB7bIcLT3G26urh5Dmse7fiLmLXnRlopw4s8DalgZ8ef79Jj4aUcYbk90g8iCa2467HX8SAIidbL7gsqXHdRw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.9.tgz", + "integrity": "sha512-e7S3MOJPZGp2QW6AK6+Ly81rC7oOSerQ+P8L0ta4FhVi+/j/v2yZzx5CqqDaWjtPFfYz21Vi1S0auHrap3Ma3A==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.9.tgz", + "integrity": "sha512-Sbe10Bnn0oUAB2AalYztvGcK+o6YFFA/9829PhOCUS9vkJElXGdphz0A3DbMdP8gmKkqPmPcMJmJOrI3VYB1JQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.9.tgz", + "integrity": "sha512-YcM5br0mVyZw2jcQeLIkhWtKPeVfAerES5PvOzaDxVtIyZ2NUBZKNLjC5z3/fUlDgT6w89VsxP2qzNipOaaDyA==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.9.tgz", + "integrity": "sha512-++0HQvasdo20JytyDpFvQtNrEsAgNG2CY1CLMwGXfFTKGBGQT3bOeLSYE2l1fYdvML5KUuwn9Z8L1EWe2tzs1w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.9.tgz", + "integrity": "sha512-uNIBa279Y3fkjV+2cUjx36xkx7eSjb8IvnL01eXUKXez/CBHNRw5ekCGMPM0BcmqBxBcdgUWuUXmVWwm4CH9kg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.9.tgz", + "integrity": "sha512-Mfiphvp3MjC/lctb+7D287Xw1DGzqJPb/J2aHHcHxflUo+8tmN/6d4k6I2yFR7BVo5/g7x2Monq4+Yew0EHRIA==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.9.tgz", + "integrity": "sha512-iSwByxzRe48YVkmpbgoxVzn76BXjlYFXC7NvLYq+b+kDjyyk30J0JY47DIn8z1MO3K0oSl9fZoRmZPQI4Hklzg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.9.tgz", + "integrity": "sha512-9jNJl6FqaUG+COdQMjSCGW4QiMHH88xWbvZ+kRVblZsWrkXlABuGdFJ1E9L7HK+T0Yqd4akKNa/lO0+jDxQD4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.9.tgz", + "integrity": "sha512-RLLdkflmqRG8KanPGOU7Rpg829ZHu8nFy5Pqdi9U01VYtG9Y0zOG6Vr2z4/S+/3zIyOxiK6cCeYNWOFR9QP87g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.9.tgz", + "integrity": "sha512-YaFBlPGeDasft5IIM+CQAhJAqS3St3nJzDEgsgFixcfZeyGPCd6eJBWzke5piZuZ7CtL656eOSYKk4Ls2C0FRQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.9.tgz", + "integrity": "sha512-1MkgTCuvMGWuqVtAvkpkXFmtL8XhWy+j4jaSO2wxfJtilVCi0ZE37b8uOdMItIHz4I6z1bWWtEX4CJwcKYLcuA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.9.tgz", + "integrity": "sha512-4Xd0xNiMVXKh6Fa7HEJQbrpP3m3DDn43jKxMjxLLRjWnRsfxjORYJlXPO4JNcXtOyfajXorRKY9NkOpTHptErg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.9.tgz", + "integrity": "sha512-WjH4s6hzo00nNezhp3wFIAfmGZ8U7KtrJNlFMRKxiI9mxEK1scOMAaa9i4crUtu+tBr+0IN6JCuAcSBJZfnphw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.9.tgz", + "integrity": "sha512-mGFrVJHmZiRqmP8xFOc6b84/7xa5y5YvR1x8djzXpJBSv/UsNK6aqec+6JDjConTgvvQefdGhFDAs2DLAds6gQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.9.tgz", + "integrity": "sha512-b33gLVU2k11nVx1OhX3C8QQP6UHQK4ZtN56oFWvVXvz2VkDoe6fbG8TOgHFxEvqeqohmRnIHe5A1+HADk4OQww==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.9.tgz", + "integrity": "sha512-PPOl1mi6lpLNQxnGoyAfschAodRFYXJ+9fs6WHXz7CSWKbOqiMZsubC+BQsVKuul+3vKLuwTHsS2c2y9EoKwxQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@floating-ui/core": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.3.tgz", + "integrity": "sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==", + "license": "MIT", + "dependencies": { + "@floating-ui/utils": "^0.2.10" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.3.tgz", + "integrity": "sha512-uZA413QEpNuhtb3/iIKoYMSK07keHPYeXF02Zhd6e213j+d1NamLix/mCLxBUDW/Gx52sPH2m+chlUsyaBs/Ag==", + "license": "MIT", + "dependencies": { + "@floating-ui/core": "^1.7.3", + "@floating-ui/utils": "^0.2.10" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.10.tgz", + "integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==", + "license": "MIT" + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "license": "MIT" + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@popperjs/core": { + "name": "@sxzz/popperjs-es", + "version": "2.11.7", + "resolved": "https://registry.npmjs.org/@sxzz/popperjs-es/-/popperjs-es-2.11.7.tgz", + "integrity": "sha512-Ccy0NlLkzr0Ex2FKvh2X+OyERHXJ88XJ1MXtsI9y9fGexlaXaVTPzBCRBwIxFkORuOb+uBqeu+RqnpgYTEZRUQ==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-beta.29", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.29.tgz", + "integrity": "sha512-NIJgOsMjbxAXvoGq/X0gD7VPMQ8j9g0BiDaNjVNVjvl+iKXxL3Jre0v31RmBYeLEmkbj2s02v8vFTbUXi5XS2Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/pluginutils": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.2.0.tgz", + "integrity": "sha512-qWJ2ZTbmumwiLFomfzTyt5Kng4hwPi9rwCYN4SHb6eaRU1KNO4ccxINHr/VhH4GgPlt1XfSTLX2LBTme8ne4Zw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.46.2.tgz", + "integrity": "sha512-Zj3Hl6sN34xJtMv7Anwb5Gu01yujyE/cLBDB2gnHTAHaWS1Z38L7kuSG+oAh0giZMqG060f/YBStXtMH6FvPMA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.46.2.tgz", + "integrity": "sha512-nTeCWY83kN64oQ5MGz3CgtPx8NSOhC5lWtsjTs+8JAJNLcP3QbLCtDDgUKQc/Ro/frpMq4SHUaHN6AMltcEoLQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.46.2.tgz", + "integrity": "sha512-HV7bW2Fb/F5KPdM/9bApunQh68YVDU8sO8BvcW9OngQVN3HHHkw99wFupuUJfGR9pYLLAjcAOA6iO+evsbBaPQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.46.2.tgz", + "integrity": "sha512-SSj8TlYV5nJixSsm/y3QXfhspSiLYP11zpfwp6G/YDXctf3Xkdnk4woJIF5VQe0of2OjzTt8EsxnJDCdHd2xMA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.46.2.tgz", + "integrity": "sha512-ZyrsG4TIT9xnOlLsSSi9w/X29tCbK1yegE49RYm3tu3wF1L/B6LVMqnEWyDB26d9Ecx9zrmXCiPmIabVuLmNSg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.46.2.tgz", + "integrity": "sha512-pCgHFoOECwVCJ5GFq8+gR8SBKnMO+xe5UEqbemxBpCKYQddRQMgomv1104RnLSg7nNvgKy05sLsY51+OVRyiVw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.46.2.tgz", + "integrity": "sha512-EtP8aquZ0xQg0ETFcxUbU71MZlHaw9MChwrQzatiE8U/bvi5uv/oChExXC4mWhjiqK7azGJBqU0tt5H123SzVA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.46.2.tgz", + "integrity": "sha512-qO7F7U3u1nfxYRPM8HqFtLd+raev2K137dsV08q/LRKRLEc7RsiDWihUnrINdsWQxPR9jqZ8DIIZ1zJJAm5PjQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.46.2.tgz", + "integrity": "sha512-3dRaqLfcOXYsfvw5xMrxAk9Lb1f395gkoBYzSFcc/scgRFptRXL9DOaDpMiehf9CO8ZDRJW2z45b6fpU5nwjng==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.46.2.tgz", + "integrity": "sha512-fhHFTutA7SM+IrR6lIfiHskxmpmPTJUXpWIsBXpeEwNgZzZZSg/q4i6FU4J8qOGyJ0TR+wXBwx/L7Ho9z0+uDg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loongarch64-gnu": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.46.2.tgz", + "integrity": "sha512-i7wfGFXu8x4+FRqPymzjD+Hyav8l95UIZ773j7J7zRYc3Xsxy2wIn4x+llpunexXe6laaO72iEjeeGyUFmjKeA==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.46.2.tgz", + "integrity": "sha512-B/l0dFcHVUnqcGZWKcWBSV2PF01YUt0Rvlurci5P+neqY/yMKchGU8ullZvIv5e8Y1C6wOn+U03mrDylP5q9Yw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.46.2.tgz", + "integrity": "sha512-32k4ENb5ygtkMwPMucAb8MtV8olkPT03oiTxJbgkJa7lJ7dZMr0GCFJlyvy+K8iq7F/iuOr41ZdUHaOiqyR3iQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.46.2.tgz", + "integrity": "sha512-t5B2loThlFEauloaQkZg9gxV05BYeITLvLkWOkRXogP4qHXLkWSbSHKM9S6H1schf/0YGP/qNKtiISlxvfmmZw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.46.2.tgz", + "integrity": "sha512-YKjekwTEKgbB7n17gmODSmJVUIvj8CX7q5442/CK80L8nqOUbMtf8b01QkG3jOqyr1rotrAnW6B/qiHwfcuWQA==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.46.2.tgz", + "integrity": "sha512-Jj5a9RUoe5ra+MEyERkDKLwTXVu6s3aACP51nkfnK9wJTraCC8IMe3snOfALkrjTYd2G1ViE1hICj0fZ7ALBPA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.46.2.tgz", + "integrity": "sha512-7kX69DIrBeD7yNp4A5b81izs8BqoZkCIaxQaOpumcJ1S/kmqNFjPhDu1LHeVXv0SexfHQv5cqHsxLOjETuqDuA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.46.2.tgz", + "integrity": "sha512-wiJWMIpeaak/jsbaq2HMh/rzZxHVW1rU6coyeNNpMwk5isiPjSTx0a4YLSlYDwBH/WBvLz+EtsNqQScZTLJy3g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.46.2.tgz", + "integrity": "sha512-gBgaUDESVzMgWZhcyjfs9QFK16D8K6QZpwAaVNJxYDLHWayOta4ZMjGm/vsAEy3hvlS2GosVFlBlP9/Wb85DqQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.46.2.tgz", + "integrity": "sha512-CvUo2ixeIQGtF6WvuB87XWqPQkoFAFqW+HUo/WzHwuHDvIwZCtjdWXoYCcr06iKGydiqTclC4jU/TNObC/xKZg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/lodash": { + "version": "4.17.20", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.20.tgz", + "integrity": "sha512-H3MHACvFUEiujabxhaI/ImO6gUrd8oOurg7LQtS7mbwIXA/cUqWrvBsaeJ23aZEPk1TAYkurjfMbSELfoCXlGA==", + "license": "MIT" + }, + "node_modules/@types/lodash-es": { + "version": "4.17.12", + "resolved": "https://registry.npmjs.org/@types/lodash-es/-/lodash-es-4.17.12.tgz", + "integrity": "sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==", + "license": "MIT", + "dependencies": { + "@types/lodash": "*" + } + }, + "node_modules/@types/web-bluetooth": { + "version": "0.0.16", + "resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.16.tgz", + "integrity": "sha512-oh8q2Zc32S6gd/j50GowEjKLoOVOwHP/bWVjKJInBwQqdOYMdPrf1oVlelTlyfFK3CKxL1uahMDAr+vy8T7yMQ==", + "license": "MIT" + }, + "node_modules/@vitejs/plugin-vue": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-6.0.1.tgz", + "integrity": "sha512-+MaE752hU0wfPFJEUAIxqw18+20euHHdxVtMvbFcOEpjEyfqXH/5DCoTHiVJ0J29EhTJdoTkjEv5YBKU9dnoTw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rolldown/pluginutils": "1.0.0-beta.29" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0", + "vue": "^3.2.25" + } + }, + "node_modules/@vue/compiler-core": { + "version": "3.5.18", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.18.tgz", + "integrity": "sha512-3slwjQrrV1TO8MoXgy3aynDQ7lslj5UqDxuHnrzHtpON5CBinhWjJETciPngpin/T3OuW3tXUf86tEurusnztw==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.0", + "@vue/shared": "3.5.18", + "entities": "^4.5.0", + "estree-walker": "^2.0.2", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-dom": { + "version": "3.5.18", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.18.tgz", + "integrity": "sha512-RMbU6NTU70++B1JyVJbNbeFkK+A+Q7y9XKE2EM4NLGm2WFR8x9MbAtWxPPLdm0wUkuZv9trpwfSlL6tjdIa1+A==", + "license": "MIT", + "dependencies": { + "@vue/compiler-core": "3.5.18", + "@vue/shared": "3.5.18" + } + }, + "node_modules/@vue/compiler-sfc": { + "version": "3.5.18", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.18.tgz", + "integrity": "sha512-5aBjvGqsWs+MoxswZPoTB9nSDb3dhd1x30xrrltKujlCxo48j8HGDNj3QPhF4VIS0VQDUrA1xUfp2hEa+FNyXA==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.0", + "@vue/compiler-core": "3.5.18", + "@vue/compiler-dom": "3.5.18", + "@vue/compiler-ssr": "3.5.18", + "@vue/shared": "3.5.18", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.17", + "postcss": "^8.5.6", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-ssr": { + "version": "3.5.18", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.18.tgz", + "integrity": "sha512-xM16Ak7rSWHkM3m22NlmcdIM+K4BMyFARAfV9hYFl+SFuRzrZ3uGMNW05kA5pmeMa0X9X963Kgou7ufdbpOP9g==", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.18", + "@vue/shared": "3.5.18" + } + }, + "node_modules/@vue/devtools-api": { + "version": "6.6.4", + "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.6.4.tgz", + "integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==", + "license": "MIT" + }, + "node_modules/@vue/reactivity": { + "version": "3.5.18", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.18.tgz", + "integrity": "sha512-x0vPO5Imw+3sChLM5Y+B6G1zPjwdOri9e8V21NnTnlEvkxatHEH5B5KEAJcjuzQ7BsjGrKtfzuQ5eQwXh8HXBg==", + "license": "MIT", + "dependencies": { + "@vue/shared": "3.5.18" + } + }, + "node_modules/@vue/runtime-core": { + "version": "3.5.18", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.18.tgz", + "integrity": "sha512-DUpHa1HpeOQEt6+3nheUfqVXRog2kivkXHUhoqJiKR33SO4x+a5uNOMkV487WPerQkL0vUuRvq/7JhRgLW3S+w==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.18", + "@vue/shared": "3.5.18" + } + }, + "node_modules/@vue/runtime-dom": { + "version": "3.5.18", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.18.tgz", + "integrity": "sha512-YwDj71iV05j4RnzZnZtGaXwPoUWeRsqinblgVJwR8XTXYZ9D5PbahHQgsbmzUvCWNF6x7siQ89HgnX5eWkr3mw==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.18", + "@vue/runtime-core": "3.5.18", + "@vue/shared": "3.5.18", + "csstype": "^3.1.3" + } + }, + "node_modules/@vue/server-renderer": { + "version": "3.5.18", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.18.tgz", + "integrity": "sha512-PvIHLUoWgSbDG7zLHqSqaCoZvHi6NNmfVFOqO+OnwvqMz/tqQr3FuGWS8ufluNddk7ZLBJYMrjcw1c6XzR12mA==", + "license": "MIT", + "dependencies": { + "@vue/compiler-ssr": "3.5.18", + "@vue/shared": "3.5.18" + }, + "peerDependencies": { + "vue": "3.5.18" + } + }, + "node_modules/@vue/shared": { + "version": "3.5.18", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.18.tgz", + "integrity": "sha512-cZy8Dq+uuIXbxCZpuLd2GJdeSO/lIzIspC2WtkqIpje5QyFbvLaI5wZtdUjLHjGZrlVX6GilejatWwVYYRc8tA==", + "license": "MIT" + }, + "node_modules/@vueuse/core": { + "version": "9.13.0", + "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-9.13.0.tgz", + "integrity": "sha512-pujnclbeHWxxPRqXWmdkKV5OX4Wk4YeK7wusHqRwU0Q7EFusHoqNA/aPhB6KCh9hEqJkLAJo7bb0Lh9b+OIVzw==", + "license": "MIT", + "dependencies": { + "@types/web-bluetooth": "^0.0.16", + "@vueuse/metadata": "9.13.0", + "@vueuse/shared": "9.13.0", + "vue-demi": "*" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@vueuse/core/node_modules/vue-demi": { + "version": "0.14.10", + "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.10.tgz", + "integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "vue-demi-fix": "bin/vue-demi-fix.js", + "vue-demi-switch": "bin/vue-demi-switch.js" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@vue/composition-api": "^1.0.0-rc.1", + "vue": "^3.0.0-0 || ^2.6.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + } + } + }, + "node_modules/@vueuse/metadata": { + "version": "9.13.0", + "resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-9.13.0.tgz", + "integrity": "sha512-gdU7TKNAUVlXXLbaF+ZCfte8BjRJQWPCa2J55+7/h+yDtzw3vOoGQDRXzI6pyKyo6bXFT5/QoPE4hAknExjRLQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@vueuse/shared": { + "version": "9.13.0", + "resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-9.13.0.tgz", + "integrity": "sha512-UrnhU+Cnufu4S6JLCPZnkWh0WwZGUp72ktOF2DFptMlOs3TOdVv8xJN53zhHGARmVOsz5KqOls09+J1NR6sBKw==", + "license": "MIT", + "dependencies": { + "vue-demi": "*" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@vueuse/shared/node_modules/vue-demi": { + "version": "0.14.10", + "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.10.tgz", + "integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "vue-demi-fix": "bin/vue-demi-fix.js", + "vue-demi-switch": "bin/vue-demi-switch.js" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@vue/composition-api": "^1.0.0-rc.1", + "vue": "^3.0.0-0 || ^2.6.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + } + } + }, + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/anymatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/async-validator": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/async-validator/-/async-validator-4.2.5.tgz", + "integrity": "sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg==", + "license": "MIT" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/axios": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.11.0.tgz", + "integrity": "sha512-1Lx3WLFQWm3ooKDYZD1eXmoGO9fxYQjrycfHFC8P0sCfQVXyROp0p9PFWBehewBOdCwHc+f/b8I0fMto5eSfwA==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.4", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/confbox": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz", + "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "license": "MIT" + }, + "node_modules/dayjs": { + "version": "1.11.13", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz", + "integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==", + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/element-plus": { + "version": "2.10.7", + "resolved": "https://registry.npmjs.org/element-plus/-/element-plus-2.10.7.tgz", + "integrity": "sha512-bL4yhepL8/0NEQA5+N2Q6ZVKLipIDkiQjK2mqtSmGh6CxJk1yaBMdG5HXfYkbk1htNcT3ULk9g23lzT323JGcA==", + "license": "MIT", + "dependencies": { + "@ctrl/tinycolor": "^3.4.1", + "@element-plus/icons-vue": "^2.3.1", + "@floating-ui/dom": "^1.0.1", + "@popperjs/core": "npm:@sxzz/popperjs-es@^2.11.7", + "@types/lodash": "^4.14.182", + "@types/lodash-es": "^4.17.6", + "@vueuse/core": "^9.1.0", + "async-validator": "^4.2.5", + "dayjs": "^1.11.13", + "escape-html": "^1.0.3", + "lodash": "^4.17.21", + "lodash-es": "^4.17.21", + "lodash-unified": "^1.0.2", + "memoize-one": "^6.0.0", + "normalize-wheel-es": "^1.2.0" + }, + "peerDependencies": { + "vue": "^3.2.0" + } + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/esbuild": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.9.tgz", + "integrity": "sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.9", + "@esbuild/android-arm": "0.25.9", + "@esbuild/android-arm64": "0.25.9", + "@esbuild/android-x64": "0.25.9", + "@esbuild/darwin-arm64": "0.25.9", + "@esbuild/darwin-x64": "0.25.9", + "@esbuild/freebsd-arm64": "0.25.9", + "@esbuild/freebsd-x64": "0.25.9", + "@esbuild/linux-arm": "0.25.9", + "@esbuild/linux-arm64": "0.25.9", + "@esbuild/linux-ia32": "0.25.9", + "@esbuild/linux-loong64": "0.25.9", + "@esbuild/linux-mips64el": "0.25.9", + "@esbuild/linux-ppc64": "0.25.9", + "@esbuild/linux-riscv64": "0.25.9", + "@esbuild/linux-s390x": "0.25.9", + "@esbuild/linux-x64": "0.25.9", + "@esbuild/netbsd-arm64": "0.25.9", + "@esbuild/netbsd-x64": "0.25.9", + "@esbuild/openbsd-arm64": "0.25.9", + "@esbuild/openbsd-x64": "0.25.9", + "@esbuild/openharmony-arm64": "0.25.9", + "@esbuild/sunos-x64": "0.25.9", + "@esbuild/win32-arm64": "0.25.9", + "@esbuild/win32-ia32": "0.25.9", + "@esbuild/win32-x64": "0.25.9" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "license": "MIT" + }, + "node_modules/exsolve": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.7.tgz", + "integrity": "sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fastq": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fdir": { + "version": "6.4.6", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz", + "integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/follow-redirects": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", + "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", + "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/js-tokens": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.1.tgz", + "integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/local-pkg": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.5.1.tgz", + "integrity": "sha512-9rrA30MRRP3gBD3HTGnC6cDFpaE1kVDWxWgqWJUN0RvDNAo+Nz/9GxB+nHOH0ifbVFy0hSA1V6vFDvnx54lTEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mlly": "^1.7.3", + "pkg-types": "^1.2.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "license": "MIT" + }, + "node_modules/lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", + "license": "MIT" + }, + "node_modules/lodash-unified": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/lodash-unified/-/lodash-unified-1.0.3.tgz", + "integrity": "sha512-WK9qSozxXOD7ZJQlpSqOT+om2ZfcT4yO+03FuzAHD0wF6S0l0090LRPDx3vhTTLZ8cFKpBn+IOcVXK6qOcIlfQ==", + "license": "MIT", + "peerDependencies": { + "@types/lodash-es": "*", + "lodash": "*", + "lodash-es": "*" + } + }, + "node_modules/magic-string": { + "version": "0.30.17", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", + "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/memoize-one": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz", + "integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==", + "license": "MIT" + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/micromatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mlly": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.7.4.tgz", + "integrity": "sha512-qmdSIPC4bDJXgZTCR7XosJiNKySV7O215tsPtDN9iEO/7q/76b/ijtgRu/+epFXSJhijtTCCGp3DWS549P3xKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.14.0", + "pathe": "^2.0.1", + "pkg-types": "^1.3.0", + "ufo": "^1.5.4" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-wheel-es": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/normalize-wheel-es/-/normalize-wheel-es-1.2.0.tgz", + "integrity": "sha512-Wj7+EJQ8mSuXr2iWfnujrimU35R2W4FAErEyTmJoJ7ucwTn2hOUSsRehMb5RSYkxXGTM7Y9QpvPmp++w5ftoJw==", + "license": "BSD-3-Clause" + }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pkg-types": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz", + "integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "confbox": "^0.1.8", + "mlly": "^1.7.4", + "pathe": "^2.0.1" + } + }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + }, + "node_modules/quansync": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/quansync/-/quansync-0.2.10.tgz", + "integrity": "sha512-t41VRkMYbkHyCYmOvx/6URnN80H7k4X0lLdBMGsz+maAwrJQYB1djpV6vHrQIBE0WBSGqhtEHrK9U3DWWH8v7A==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/antfu" + }, + { + "type": "individual", + "url": "https://github.com/sponsors/sxzz" + } + ], + "license": "MIT" + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/readdirp/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rollup": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.46.2.tgz", + "integrity": "sha512-WMmLFI+Boh6xbop+OAGo9cQ3OgX9MIg7xOQjn+pTCwOkk+FNDAeAemXkJ3HzDJrVXleLOFVa1ipuc1AmEx1Dwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.46.2", + "@rollup/rollup-android-arm64": "4.46.2", + "@rollup/rollup-darwin-arm64": "4.46.2", + "@rollup/rollup-darwin-x64": "4.46.2", + "@rollup/rollup-freebsd-arm64": "4.46.2", + "@rollup/rollup-freebsd-x64": "4.46.2", + "@rollup/rollup-linux-arm-gnueabihf": "4.46.2", + "@rollup/rollup-linux-arm-musleabihf": "4.46.2", + "@rollup/rollup-linux-arm64-gnu": "4.46.2", + "@rollup/rollup-linux-arm64-musl": "4.46.2", + "@rollup/rollup-linux-loongarch64-gnu": "4.46.2", + "@rollup/rollup-linux-ppc64-gnu": "4.46.2", + "@rollup/rollup-linux-riscv64-gnu": "4.46.2", + "@rollup/rollup-linux-riscv64-musl": "4.46.2", + "@rollup/rollup-linux-s390x-gnu": "4.46.2", + "@rollup/rollup-linux-x64-gnu": "4.46.2", + "@rollup/rollup-linux-x64-musl": "4.46.2", + "@rollup/rollup-win32-arm64-msvc": "4.46.2", + "@rollup/rollup-win32-ia32-msvc": "4.46.2", + "@rollup/rollup-win32-x64-msvc": "4.46.2", + "fsevents": "~2.3.2" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/scule": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/scule/-/scule-1.3.0.tgz", + "integrity": "sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g==", + "dev": true, + "license": "MIT" + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-literal": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-2.1.1.tgz", + "integrity": "sha512-631UJ6O00eNGfMiWG78ck80dfBab8X6IVFB51jZK5Icd7XAs60Z5y7QdSd/wGIklnWvRbUNloVzhOKKmutxQ6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "js-tokens": "^9.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.14", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz", + "integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.4.4", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/ufo": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.1.tgz", + "integrity": "sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==", + "dev": true, + "license": "MIT" + }, + "node_modules/unimport": { + "version": "3.14.6", + "resolved": "https://registry.npmjs.org/unimport/-/unimport-3.14.6.tgz", + "integrity": "sha512-CYvbDaTT04Rh8bmD8jz3WPmHYZRG/NnvYVzwD6V1YAlvvKROlAeNDUBhkBGzNav2RKaeuXvlWYaa1V4Lfi/O0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^5.1.4", + "acorn": "^8.14.0", + "escape-string-regexp": "^5.0.0", + "estree-walker": "^3.0.3", + "fast-glob": "^3.3.3", + "local-pkg": "^1.0.0", + "magic-string": "^0.30.17", + "mlly": "^1.7.4", + "pathe": "^2.0.1", + "picomatch": "^4.0.2", + "pkg-types": "^1.3.0", + "scule": "^1.3.0", + "strip-literal": "^2.1.1", + "unplugin": "^1.16.1" + } + }, + "node_modules/unimport/node_modules/confbox": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.2.2.tgz", + "integrity": "sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/unimport/node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/unimport/node_modules/local-pkg": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-1.1.1.tgz", + "integrity": "sha512-WunYko2W1NcdfAFpuLUoucsgULmgDBRkdxHxWQ7mK0cQqwPiy8E1enjuRBrhLtZkB5iScJ1XIPdhVEFK8aOLSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "mlly": "^1.7.4", + "pkg-types": "^2.0.1", + "quansync": "^0.2.8" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/unimport/node_modules/local-pkg/node_modules/pkg-types": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.2.0.tgz", + "integrity": "sha512-2SM/GZGAEkPp3KWORxQZns4M+WSeXbC2HEvmOIJe3Cmiv6ieAJvdVhDldtHqM5J1Y7MrR1XhkBT/rMlhh9FdqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "confbox": "^0.2.2", + "exsolve": "^1.0.7", + "pathe": "^2.0.3" + } + }, + "node_modules/unplugin": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/unplugin/-/unplugin-1.16.1.tgz", + "integrity": "sha512-4/u/j4FrCKdi17jaxuJA0jClGxB1AvU2hw/IuayPc4ay1XGaJs/rbb4v5WKwAjNifjmXK9PIFyuPiaK8azyR9w==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.14.0", + "webpack-virtual-modules": "^0.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/unplugin-auto-import": { + "version": "0.18.6", + "resolved": "https://registry.npmjs.org/unplugin-auto-import/-/unplugin-auto-import-0.18.6.tgz", + "integrity": "sha512-LMFzX5DtkTj/3wZuyG5bgKBoJ7WSgzqSGJ8ppDRdlvPh45mx6t6w3OcbExQi53n3xF5MYkNGPNR/HYOL95KL2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@antfu/utils": "^0.7.10", + "@rollup/pluginutils": "^5.1.3", + "fast-glob": "^3.3.2", + "local-pkg": "^0.5.1", + "magic-string": "^0.30.14", + "minimatch": "^9.0.5", + "unimport": "^3.13.4", + "unplugin": "^1.16.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@nuxt/kit": "^3.2.2", + "@vueuse/core": "*" + }, + "peerDependenciesMeta": { + "@nuxt/kit": { + "optional": true + }, + "@vueuse/core": { + "optional": true + } + } + }, + "node_modules/unplugin-vue-components": { + "version": "0.27.5", + "resolved": "https://registry.npmjs.org/unplugin-vue-components/-/unplugin-vue-components-0.27.5.tgz", + "integrity": "sha512-m9j4goBeNwXyNN8oZHHxvIIYiG8FQ9UfmKWeNllpDvhU7btKNNELGPt+o3mckQKuPwrE7e0PvCsx+IWuDSD9Vg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@antfu/utils": "^0.7.10", + "@rollup/pluginutils": "^5.1.3", + "chokidar": "^3.6.0", + "debug": "^4.3.7", + "fast-glob": "^3.3.2", + "local-pkg": "^0.5.1", + "magic-string": "^0.30.14", + "minimatch": "^9.0.5", + "mlly": "^1.7.3", + "unplugin": "^1.16.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@babel/parser": "^7.15.8", + "@nuxt/kit": "^3.2.2", + "vue": "2 || 3" + }, + "peerDependenciesMeta": { + "@babel/parser": { + "optional": true + }, + "@nuxt/kit": { + "optional": true + } + } + }, + "node_modules/vite": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.2.tgz", + "integrity": "sha512-J0SQBPlQiEXAF7tajiH+rUooJPo0l8KQgyg4/aMunNtrOa7bwuZJsJbDWzeljqQpgftxuq5yNJxQ91O9ts29UQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.25.0", + "fdir": "^6.4.6", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.14" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vue": { + "version": "3.5.18", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.18.tgz", + "integrity": "sha512-7W4Y4ZbMiQ3SEo+m9lnoNpV9xG7QVMLa+/0RFwwiAVkeYoyGXqWE85jabU4pllJNUzqfLShJ5YLptewhCWUgNA==", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.18", + "@vue/compiler-sfc": "3.5.18", + "@vue/runtime-dom": "3.5.18", + "@vue/server-renderer": "3.5.18", + "@vue/shared": "3.5.18" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/vue-router": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.5.1.tgz", + "integrity": "sha512-ogAF3P97NPm8fJsE4by9dwSYtDwXIY1nFY9T6DyQnGHd1E2Da94w9JIolpe42LJGIl0DwOHBi8TcRPlPGwbTtw==", + "license": "MIT", + "dependencies": { + "@vue/devtools-api": "^6.6.4" + }, + "funding": { + "url": "https://github.com/sponsors/posva" + }, + "peerDependencies": { + "vue": "^3.2.0" + } + }, + "node_modules/webpack-virtual-modules": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz", + "integrity": "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==", + "dev": true, + "license": "MIT" + } + } +} diff --git a/easytier-contrib/easytier-uptime/frontend/package.json b/easytier-contrib/easytier-uptime/frontend/package.json new file mode 100644 index 000000000..b94153f3c --- /dev/null +++ b/easytier-contrib/easytier-uptime/frontend/package.json @@ -0,0 +1,26 @@ +{ + "name": "easytier-uptime-frontend", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview" + }, + "dependencies": { + "@element-plus/icons-vue": "^2.3.1", + "axios": "^1.7.9", + "dayjs": "^1.11.13", + "easytier-uptime-frontend": "link:", + "element-plus": "^2.8.8", + "vue": "^3.5.18", + "vue-router": "^4.4.5" + }, + "devDependencies": { + "@vitejs/plugin-vue": "^6.0.1", + "unplugin-auto-import": "^0.18.6", + "unplugin-vue-components": "^0.27.4", + "vite": "^7.1.2" + } +} diff --git a/easytier-contrib/easytier-uptime/frontend/public/vite.svg b/easytier-contrib/easytier-uptime/frontend/public/vite.svg new file mode 100644 index 000000000..e7b8dfb1b --- /dev/null +++ b/easytier-contrib/easytier-uptime/frontend/public/vite.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/easytier-contrib/easytier-uptime/frontend/src/App.vue b/easytier-contrib/easytier-uptime/frontend/src/App.vue new file mode 100644 index 000000000..9978b1014 --- /dev/null +++ b/easytier-contrib/easytier-uptime/frontend/src/App.vue @@ -0,0 +1,326 @@ + + + + + diff --git a/easytier-contrib/easytier-uptime/frontend/src/api/index.js b/easytier-contrib/easytier-uptime/frontend/src/api/index.js new file mode 100644 index 000000000..f721dc2fa --- /dev/null +++ b/easytier-contrib/easytier-uptime/frontend/src/api/index.js @@ -0,0 +1,155 @@ +import axios from 'axios' + +// 创建axios实例 +const api = axios.create({ + baseURL: import.meta.env.VITE_API_BASE_URL || '', + timeout: 10000, + headers: { + 'Content-Type': 'application/json' + } +}) + +// 请求拦截器 +api.interceptors.request.use( + config => { + // 只在管理员相关的API请求中添加token + if (config.url && config.url.includes('/api/admin/')) { + const token = localStorage.getItem('admin_token') + if (token) { + config.headers.Authorization = `Bearer ${token}` + } + } + return config + }, + error => { + return Promise.reject(error) + } +) + +// 响应拦截器 +api.interceptors.response.use( + response => { + // 直接返回完整的response对象,让各个API方法自己处理数据格式 + return response + }, + error => { + console.error('API Error Details:', { + message: error.message, + status: error.response?.status, + statusText: error.response?.statusText, + data: error.response?.data, + config: { + url: error.config?.url, + method: error.config?.method, + headers: error.config?.headers + } + }) + return Promise.reject(error) + } +) + +// 节点相关API +export const nodeApi = { + // 获取节点列表 + async getNodes(params = {}) { + const response = await api.get('/api/nodes', { params }) + return response.data + }, + + // 创建节点 + async createNode(data) { + const response = await api.post('/api/nodes', data) + return response.data + }, + + // 获取单个节点 + async getNode(id) { + const response = await api.get(`/api/nodes/${id}`) + return response.data + }, + + // 更新节点 + async updateNode(id, data) { + const response = await api.put(`/api/nodes/${id}`, data) + return response.data + }, + + // 删除节点 + async deleteNode(id) { + const response = await api.delete(`/api/nodes/${id}`) + return response.data + }, + + // 获取节点健康记录 + async getNodeHealth(id, params = {}) { + const response = await api.get(`/api/nodes/${id}/health`, { params }) + return response.data + }, + + // 获取节点健康统计 + async getNodeHealthStats(id, params = {}) { + const response = await api.get(`/api/nodes/${id}/health/stats`, { params }) + return response.data + }, + + // 测试节点连接 + async testConnection(data) { + const response = await api.post('/api/test_connection', data) + return response.data + } +} + +// 健康检查API +export const healthApi = { + async check() { + const response = await api.get('/health') + return response.data + } +} + +// 管理员API +export const adminApi = { + // 管理员登录 + async login(password) { + const response = await api.post('/api/admin/login', { password }) + return response.data + }, + + // 验证token有效性 + async verifyToken() { + const response = await api.get('/api/admin/verify') + return response.data + }, + + // 获取所有节点(包括未审批的) + async getNodes(params = {}) { + const response = await api.get('/api/admin/nodes', { params }) + return response.data + }, + + // 审批节点 + async approveNode(id) { + const response = await api.put(`/api/admin/nodes/${id}/approve`) + return response.data + }, + + // 撤销审批节点 + async revokeApproval(id) { + const response = await api.put(`/api/admin/nodes/${id}/revoke`) + return response.data + }, + + // 删除节点 + async deleteNode(id) { + const response = await api.delete(`/api/admin/nodes/${id}`) + return response.data + }, + + // 更新节点 + async updateNode(id, data) { + const response = await api.put(`/api/admin/nodes/${id}`, data) + return response.data + } +} + +export default api \ No newline at end of file diff --git a/easytier-contrib/easytier-uptime/frontend/src/assets/vue.svg b/easytier-contrib/easytier-uptime/frontend/src/assets/vue.svg new file mode 100644 index 000000000..770e9d333 --- /dev/null +++ b/easytier-contrib/easytier-uptime/frontend/src/assets/vue.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/easytier-contrib/easytier-uptime/frontend/src/components/HealthTimeline.vue b/easytier-contrib/easytier-uptime/frontend/src/components/HealthTimeline.vue new file mode 100644 index 000000000..8bd516bcd --- /dev/null +++ b/easytier-contrib/easytier-uptime/frontend/src/components/HealthTimeline.vue @@ -0,0 +1,405 @@ + + + + + \ No newline at end of file diff --git a/easytier-contrib/easytier-uptime/frontend/src/components/NodeForm.vue b/easytier-contrib/easytier-uptime/frontend/src/components/NodeForm.vue new file mode 100644 index 000000000..a984b548c --- /dev/null +++ b/easytier-contrib/easytier-uptime/frontend/src/components/NodeForm.vue @@ -0,0 +1,507 @@ + + + + + \ No newline at end of file diff --git a/easytier-contrib/easytier-uptime/frontend/src/main.js b/easytier-contrib/easytier-uptime/frontend/src/main.js new file mode 100644 index 000000000..52363073f --- /dev/null +++ b/easytier-contrib/easytier-uptime/frontend/src/main.js @@ -0,0 +1,22 @@ +import { createApp } from 'vue' +import ElementPlus from 'element-plus' +import 'element-plus/dist/index.css' +import * as ElementPlusIconsVue from '@element-plus/icons-vue' +import router from './router' +import App from './App.vue' +import './style.css' + +const app = createApp(App) + +// 注册Element Plus +app.use(ElementPlus) + +// 注册所有图标 +for (const [key, component] of Object.entries(ElementPlusIconsVue)) { + app.component(key, component) +} + +// 注册路由 +app.use(router) + +app.mount('#app') diff --git a/easytier-contrib/easytier-uptime/frontend/src/router/index.js b/easytier-contrib/easytier-uptime/frontend/src/router/index.js new file mode 100644 index 000000000..d11515837 --- /dev/null +++ b/easytier-contrib/easytier-uptime/frontend/src/router/index.js @@ -0,0 +1,78 @@ +import { createRouter, createWebHistory } from 'vue-router' +import NodeDashboard from '../views/NodeDashboard.vue' +import SubmitNode from '../views/SubmitNode.vue' +import AdminLogin from '../views/AdminLogin.vue' +import AdminDashboard from '../views/AdminDashboard.vue' + +const routes = [ + { + path: '/', + name: 'Dashboard', + component: NodeDashboard, + meta: { + title: '节点状态监控' + } + }, + { + path: '/submit', + name: 'Submit', + component: SubmitNode, + meta: { + title: '提交共享节点' + } + }, + { + path: '/admin/login', + name: 'AdminLogin', + component: AdminLogin, + meta: { + title: '管理员登录' + } + }, + { + path: '/admin', + name: 'AdminDashboard', + component: AdminDashboard, + meta: { + title: '管理员面板', + requiresAuth: true + } + } +] + +const router = createRouter({ + history: createWebHistory(), + routes +}) + +// 路由守卫 +router.beforeEach(async (to, from, next) => { + // 设置页面标题 + if (to.meta.title) { + document.title = `${to.meta.title} - EasyTier Uptime` + } + + // 检查管理员权限 + if (to.meta.requiresAuth) { + const token = localStorage.getItem('admin_token') + if (!token) { + next('/admin/login') + return + } + + // 验证token有效性 + try { + const { adminApi } = await import('../api') + await adminApi.verifyToken() + } catch (error) { + console.error('Token verification failed:', error) + localStorage.removeItem('admin_token') + next('/admin/login') + return + } + } + + next() +}) + +export default router \ No newline at end of file diff --git a/easytier-contrib/easytier-uptime/frontend/src/style.css b/easytier-contrib/easytier-uptime/frontend/src/style.css new file mode 100644 index 000000000..8fb166839 --- /dev/null +++ b/easytier-contrib/easytier-uptime/frontend/src/style.css @@ -0,0 +1,243 @@ +/* 自定义样式 */ +:root { + --primary-color: #409EFF; + --success-color: #67C23A; + --warning-color: #E6A23C; + --danger-color: #F56C6C; + --info-color: #909399; + + --text-primary: #303133; + --text-regular: #606266; + --text-secondary: #909399; + --text-placeholder: #C0C4CC; + + --border-base: #DCDFE6; + --border-light: #E4E7ED; + --border-lighter: #EBEEF5; + --border-extra-light: #F2F6FC; + + --background-base: #F5F7FA; + --background-light: #FAFAFA; +} + +/* 滚动条样式 */ +::-webkit-scrollbar { + width: 6px; + height: 6px; +} + +::-webkit-scrollbar-track { + background: #f1f1f1; + border-radius: 3px; +} + +::-webkit-scrollbar-thumb { + background: #c1c1c1; + border-radius: 3px; +} + +::-webkit-scrollbar-thumb:hover { + background: #a8a8a8; +} + +/* 工具类 */ +.text-center { + text-align: center; +} + +.text-left { + text-align: left; +} + +.text-right { + text-align: right; +} + +.flex { + display: flex; +} + +.flex-center { + display: flex; + align-items: center; + justify-content: center; +} + +.flex-between { + display: flex; + align-items: center; + justify-content: space-between; +} + +.flex-column { + flex-direction: column; +} + +.flex-1 { + flex: 1; +} + +.mb-10 { + margin-bottom: 10px; +} + +.mb-20 { + margin-bottom: 20px; +} + +.mt-10 { + margin-top: 10px; +} + +.mt-20 { + margin-top: 20px; +} + +.p-10 { + padding: 10px; +} + +.p-20 { + padding: 20px; +} + +/* 动画效果 */ +.fade-in { + animation: fadeIn 0.3s ease-in; +} + +@keyframes fadeIn { + from { + opacity: 0; + transform: translateY(10px); + } + + to { + opacity: 1; + transform: translateY(0); + } +} + +.slide-up { + animation: slideUp 0.3s ease-out; +} + +@keyframes slideUp { + from { + opacity: 0; + transform: translateY(20px); + } + + to { + opacity: 1; + transform: translateY(0); + } +} + +/* 响应式断点 */ +@media (max-width: 768px) { + .mobile-hidden { + display: none !important; + } +} + +@media (min-width: 769px) { + .desktop-hidden { + display: none !important; + } +} + +/* 状态指示器 */ +.status-online { + color: var(--success-color); +} + +.status-offline { + color: var(--danger-color); +} + +.status-warning { + color: var(--warning-color); +} + +/* 卡片阴影效果 */ +.card-shadow { + box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1); + transition: box-shadow 0.3s; +} + +.card-shadow:hover { + box-shadow: 0 4px 20px 0 rgba(0, 0, 0, 0.15); +} + +/* 加载状态 */ +.loading-overlay { + position: relative; +} + +.loading-overlay::after { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(255, 255, 255, 0.8); + display: flex; + align-items: center; + justify-content: center; + z-index: 1000; +} + +/* 表格样式增强 */ +.el-table .el-table__row:hover { + cursor: pointer; +} + +/* 按钮组样式 */ +.button-group { + display: flex; + gap: 10px; + flex-wrap: wrap; +} + +.button-group .el-button { + margin: 0; +} + +/* 统计卡片样式 */ +.stat-card { + text-align: center; + padding: 10px; + background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%); + border-radius: 8px; + transition: transform 0.3s; +} + +.stat-card:hover { + transform: translateY(-2px); +} + +/* 标签样式 */ +.tag-group { + display: flex; + gap: 8px; + flex-wrap: wrap; +} + +/* 描述列表样式 */ +.description-list { + display: grid; + grid-template-columns: auto 1fr; + gap: 10px 20px; + align-items: center; +} + +.description-list .label { + font-weight: 600; + color: var(--text-regular); +} + +.description-list .value { + color: var(--text-primary); +} \ No newline at end of file diff --git a/easytier-contrib/easytier-uptime/frontend/src/views/AdminDashboard.vue b/easytier-contrib/easytier-uptime/frontend/src/views/AdminDashboard.vue new file mode 100644 index 000000000..372ed3f86 --- /dev/null +++ b/easytier-contrib/easytier-uptime/frontend/src/views/AdminDashboard.vue @@ -0,0 +1,579 @@ + + + + + \ No newline at end of file diff --git a/easytier-contrib/easytier-uptime/frontend/src/views/AdminLogin.vue b/easytier-contrib/easytier-uptime/frontend/src/views/AdminLogin.vue new file mode 100644 index 000000000..f03749006 --- /dev/null +++ b/easytier-contrib/easytier-uptime/frontend/src/views/AdminLogin.vue @@ -0,0 +1,251 @@ + + + + + \ No newline at end of file diff --git a/easytier-contrib/easytier-uptime/frontend/src/views/NodeDashboard.vue b/easytier-contrib/easytier-uptime/frontend/src/views/NodeDashboard.vue new file mode 100644 index 000000000..757cb94e0 --- /dev/null +++ b/easytier-contrib/easytier-uptime/frontend/src/views/NodeDashboard.vue @@ -0,0 +1,573 @@ + + + + + diff --git a/easytier-contrib/easytier-uptime/frontend/src/views/SubmitNode.vue b/easytier-contrib/easytier-uptime/frontend/src/views/SubmitNode.vue new file mode 100644 index 000000000..7b3cf3dbf --- /dev/null +++ b/easytier-contrib/easytier-uptime/frontend/src/views/SubmitNode.vue @@ -0,0 +1,351 @@ + + + + + \ No newline at end of file diff --git a/easytier-contrib/easytier-uptime/frontend/vite.config.js b/easytier-contrib/easytier-uptime/frontend/vite.config.js new file mode 100644 index 000000000..2b83c87dd --- /dev/null +++ b/easytier-contrib/easytier-uptime/frontend/vite.config.js @@ -0,0 +1,30 @@ +import { defineConfig } from 'vite' +import vue from '@vitejs/plugin-vue' +import AutoImport from 'unplugin-auto-import/vite' +import Components from 'unplugin-vue-components/vite' +import { ElementPlusResolver } from 'unplugin-vue-components/resolvers' + +// https://vite.dev/config/ +export default defineConfig({ + plugins: [ + vue(), + AutoImport({ + resolvers: [ElementPlusResolver()], + }), + Components({ + resolvers: [ElementPlusResolver()], + }), + ], + server: { + proxy: { + '/api': { + target: 'http://localhost:8080', + changeOrigin: true, + }, + '/health': { + target: 'http://localhost:8080', + changeOrigin: true, + } + } + } +}) diff --git a/easytier-contrib/easytier-uptime/src/api/error.rs b/easytier-contrib/easytier-uptime/src/api/error.rs new file mode 100644 index 000000000..3eacff11b --- /dev/null +++ b/easytier-contrib/easytier-uptime/src/api/error.rs @@ -0,0 +1,80 @@ +use axum::http::StatusCode; +use axum::response::{IntoResponse, Response}; +use serde_json::json; +use thiserror::Error; + +#[derive(Debug, Error)] +pub enum ApiError { + #[error("Database error: {0}")] + Database(#[from] sea_orm::DbErr), + + #[error("Validation error: {0}")] + Validation(String), + + #[error("Not found: {0}")] + NotFound(String), + + #[error("Bad request: {0}")] + BadRequest(String), + + #[error("Internal server error: {0}")] + Internal(String), + + #[error("Unauthorized: {0}")] + Unauthorized(String), + + #[error("Forbidden: {0}")] + Forbidden(String), +} + +impl IntoResponse for ApiError { + fn into_response(self) -> Response { + let (status, error_message) = match self { + ApiError::Database(err) => ( + StatusCode::INTERNAL_SERVER_ERROR, + format!("Database error: {}", err), + ), + ApiError::Validation(msg) => (StatusCode::BAD_REQUEST, msg), + ApiError::NotFound(msg) => (StatusCode::NOT_FOUND, msg), + ApiError::BadRequest(msg) => (StatusCode::BAD_REQUEST, msg), + ApiError::Internal(msg) => (StatusCode::INTERNAL_SERVER_ERROR, msg), + ApiError::Unauthorized(msg) => (StatusCode::UNAUTHORIZED, msg), + ApiError::Forbidden(msg) => (StatusCode::FORBIDDEN, msg), + }; + + let body = json!({ + "error": { + "code": status.as_u16(), + "message": error_message + } + }); + + (status, axum::Json(body)).into_response() + } +} + +pub type ApiResult = Result; + +impl From for ApiError { + fn from(err: validator::ValidationErrors) -> Self { + let errors: Vec = err + .field_errors() + .iter() + .map(|(field, errors)| { + let error_msgs: Vec = errors + .iter() + .map(|error| { + if let Some(msg) = &error.message { + msg.to_string() + } else { + format!("Validation failed for field: {}", field) + } + }) + .collect(); + error_msgs.join(", ") + }) + .collect(); + + ApiError::Validation(errors.join("; ")) + } +} diff --git a/easytier-contrib/easytier-uptime/src/api/handlers.rs b/easytier-contrib/easytier-uptime/src/api/handlers.rs new file mode 100644 index 000000000..6ad937fb4 --- /dev/null +++ b/easytier-contrib/easytier-uptime/src/api/handlers.rs @@ -0,0 +1,507 @@ +use std::ops::{Div, Mul}; + +use axum::extract::{Path, Query, State}; +use axum::Json; +use sea_orm::{ + ColumnTrait, Condition, EntityTrait, IntoActiveModel, ModelTrait, Order, PaginatorTrait, + QueryFilter, QueryOrder, QuerySelect, Set, TryIntoModel, +}; +use serde::Deserialize; +use validator::Validate; + +use crate::api::{ + error::{ApiError, ApiResult}, + models::*, +}; +use crate::db::entity::{self, health_records, shared_nodes}; +use crate::db::{operations::*, Db}; +use crate::health_checker_manager::HealthCheckerManager; +use std::sync::Arc; + +#[derive(Clone)] +pub struct AppState { + pub db: Db, + pub health_checker_manager: Arc, +} + +pub async fn health_check() -> Json> { + Json(ApiResponse::message("Service is healthy".to_string())) +} + +pub async fn get_nodes( + State(app_state): State, + Query(pagination): Query, + Query(filters): Query, +) -> ApiResult>>> { + let page = pagination.page.unwrap_or(1); + let per_page = pagination.per_page.unwrap_or(20); + + let offset = (page - 1) * per_page; + + let mut query = entity::shared_nodes::Entity::find(); + + // 普通用户只能看到已审核的节点 + query = query.filter(entity::shared_nodes::Column::IsApproved.eq(true)); + + if let Some(is_active) = filters.is_active { + query = query.filter(entity::shared_nodes::Column::IsActive.eq(is_active)); + } + + if let Some(protocol) = filters.protocol { + query = query.filter(entity::shared_nodes::Column::Protocol.eq(protocol)); + } + + if let Some(search) = filters.search { + query = query.filter( + sea_orm::Condition::any() + .add(entity::shared_nodes::Column::Name.contains(&search)) + .add(entity::shared_nodes::Column::Host.contains(&search)) + .add(entity::shared_nodes::Column::Description.contains(&search)), + ); + } + + let total = query.clone().count(app_state.db.orm_db()).await?; + let nodes = query + .order_by_asc(entity::shared_nodes::Column::Id) + .limit(Some(per_page as u64)) + .offset(Some(offset as u64)) + .all(app_state.db.orm_db()) + .await?; + + let mut node_responses: Vec = nodes.into_iter().map(NodeResponse::from).collect(); + let total_pages = total.div_ceil(per_page as u64); + + // 为每个节点添加健康状态信息 + for node_response in &mut node_responses { + if let Some(mut health_record) = app_state + .health_checker_manager + .get_node_memory_record(node_response.id) + { + node_response.current_health_status = + Some(health_record.get_current_health_status().to_string()); + node_response.last_check_time = Some(health_record.get_last_check_time()); + node_response.last_response_time = health_record.get_last_response_time(); + + // 获取24小时健康统计 + if let Some(stats) = app_state + .health_checker_manager + .get_node_health_stats(node_response.id, 24) + { + node_response.health_percentage_24h = Some(stats.health_percentage); + } + + let (total_ring, healthy_ring) = health_record.get_counter_ring(); + node_response.health_record_total_counter_ring = total_ring; + node_response.health_record_healthy_counter_ring = healthy_ring; + node_response.ring_granularity = health_record.get_ring_granularity(); + } + } + + // remove sensitive information + node_responses.iter_mut().for_each(|node| { + tracing::info!("node: {:?}", node); + node.network_name = None; + node.network_secret = None; + + // make cur connection and max conn round to percentage + if node.max_connections != 0 { + node.current_connections = node.current_connections.mul(100).div(node.max_connections); + node.max_connections = 100; + } else { + node.current_connections = 0; + node.max_connections = 0; + } + + node.wechat = None; + node.qq_number = None; + node.mail = None; + }); + + Ok(Json(ApiResponse::success(PaginatedResponse { + items: node_responses, + total, + page, + per_page, + total_pages: total_pages as u32, + }))) +} + +pub async fn create_node( + State(app_state): State, + Json(request): Json, +) -> ApiResult>> { + request.validate()?; + + let node = NodeOperations::create_node(&app_state.db, request).await?; + + Ok(Json(ApiResponse::success(NodeResponse::from(node)))) +} + +pub async fn test_connection( + State(app_state): State, + Json(request): Json, +) -> ApiResult>> { + let mut node = NodeOperations::create_node_model(request); + node.id = Set(0); + let node = node.try_into_model()?; + app_state + .health_checker_manager + .test_connection(&node, std::time::Duration::from_secs(5)) + .await + .map_err(|e| ApiError::Internal(e.to_string()))?; + + Ok(Json(ApiResponse::success(NodeResponse::from(node)))) +} + +pub async fn get_node( + State(app_state): State, + Path(id): Path, +) -> ApiResult>> { + let node = NodeOperations::get_node_by_id(&app_state.db, id) + .await? + .ok_or_else(|| ApiError::NotFound(format!("Node with id {} not found", id)))?; + + Ok(Json(ApiResponse::success(NodeResponse::from(node)))) +} + +pub async fn get_node_health( + State(app_state): State, + Path(node_id): Path, + Query(pagination): Query, + Query(filters): Query, +) -> ApiResult>>> { + let page = pagination.page.unwrap_or(1); + let per_page = pagination.per_page.unwrap_or(20); + let offset = (page - 1) * per_page; + + let mut query = entity::health_records::Entity::find() + .filter(entity::health_records::Column::NodeId.eq(node_id)); + + if let Some(status) = filters.status { + query = query.filter(entity::health_records::Column::Status.eq(status)); + } + + if let Some(since) = filters.since { + query = query.filter(entity::health_records::Column::CheckedAt.gte(since.naive_utc())); + } + + let total = query.clone().count(app_state.db.orm_db()).await?; + let records = query + .order_by_desc(entity::health_records::Column::CheckedAt) + .limit(Some(per_page as u64)) + .offset(Some(offset as u64)) + .all(app_state.db.orm_db()) + .await?; + + let record_responses: Vec = records + .into_iter() + .map(HealthRecordResponse::from) + .collect(); + let total_pages = total.div_ceil(per_page as u64); + + Ok(Json(ApiResponse::success(PaginatedResponse { + items: record_responses, + total, + page, + per_page, + total_pages: total_pages as u32, + }))) +} + +pub async fn get_node_health_stats( + State(app_state): State, + Path(node_id): Path, + Query(params): Query, +) -> ApiResult>> { + let hours = params.hours.unwrap_or(24); + let stats = HealthOperations::get_health_stats(&app_state.db, node_id, hours).await?; + + Ok(Json(ApiResponse::success(HealthStatsResponse::from(stats)))) +} + +#[derive(Debug, Deserialize)] +pub struct HealthStatsParams { + pub hours: Option, +} + +#[derive(Debug, Deserialize)] +pub struct InstanceFilterParams { + pub node_id: Option, + pub status: Option, +} + +// 管理员相关处理器 +use crate::config::AppConfig; +use axum::http::{HeaderMap, StatusCode}; +use chrono::{Duration, Utc}; +use jsonwebtoken::{decode, encode, DecodingKey, EncodingKey, Header, Validation}; +use serde::Serialize; + +#[derive(Debug, Serialize, Deserialize)] +struct AdminClaims { + sub: String, + exp: usize, + iat: usize, +} + +pub async fn get_node_connect_url( + State(app_state): State, + Path(id): Path, +) -> ApiResult { + let node = NodeOperations::get_node_by_id(&app_state.db, id) + .await? + .ok_or_else(|| ApiError::NotFound(format!("Node with id {} not found", id)))?; + let connect_url = format!("{}://{}:{}", node.protocol, node.host, node.port); + Ok(connect_url) +} + +pub async fn admin_login( + Json(request): Json, +) -> ApiResult>> { + request + .validate() + .map_err(|e| ApiError::Validation(e.to_string()))?; + + let config = AppConfig::default(); + + if request.password != config.security.admin_password { + return Err(ApiError::Unauthorized("Invalid password".to_string())); + } + + let now = Utc::now(); + let expires_at = now + Duration::hours(24); + + let claims = AdminClaims { + sub: "admin".to_string(), + exp: expires_at.timestamp() as usize, + iat: now.timestamp() as usize, + }; + + let token = encode( + &Header::default(), + &claims, + &EncodingKey::from_secret(config.security.jwt_secret.as_ref()), + ) + .map_err(|e| ApiError::Internal(format!("Token generation failed: {}", e)))?; + + Ok(Json(ApiResponse::success(AdminLoginResponse { + token, + expires_at, + }))) +} + +pub async fn admin_get_nodes( + State(app_state): State, + Query(pagination): Query, + Query(filters): Query, + headers: HeaderMap, +) -> ApiResult>>> { + verify_admin_token(&headers)?; + + let page = pagination.page.unwrap_or(1); + let per_page = pagination.per_page.unwrap_or(200); + let offset = (page - 1) * per_page; + + let mut query = entity::shared_nodes::Entity::find(); + + if let Some(is_active) = filters.is_active { + query = query.filter(entity::shared_nodes::Column::IsActive.eq(is_active)); + } + + if let Some(is_approved) = filters.is_approved { + query = query.filter(entity::shared_nodes::Column::IsApproved.eq(is_approved)); + } + + if let Some(protocol) = filters.protocol { + query = query.filter(entity::shared_nodes::Column::Protocol.eq(protocol)); + } + + if let Some(search) = filters.search { + query = query.filter( + sea_orm::Condition::any() + .add(entity::shared_nodes::Column::Name.contains(&search)) + .add(entity::shared_nodes::Column::Host.contains(&search)) + .add(entity::shared_nodes::Column::Description.contains(&search)), + ); + } + + let total = query.clone().count(app_state.db.orm_db()).await?; + + let nodes = query + .order_by(entity::shared_nodes::Column::CreatedAt, Order::Desc) + .offset(offset as u64) + .limit(per_page as u64) + .all(app_state.db.orm_db()) + .await?; + + let node_responses: Vec = nodes.into_iter().map(NodeResponse::from).collect(); + + let total_pages = (total as f64 / per_page as f64).ceil() as u32; + + Ok(Json(ApiResponse::success(PaginatedResponse { + items: node_responses, + total, + page, + per_page, + total_pages, + }))) +} + +pub async fn admin_approve_node( + State(app_state): State, + Path(id): Path, + headers: HeaderMap, +) -> ApiResult>> { + verify_admin_token(&headers)?; + + let node = entity::shared_nodes::Entity::find_by_id(id) + .one(app_state.db.orm_db()) + .await? + .ok_or_else(|| ApiError::NotFound("Node not found".to_string()))?; + + let mut active_model = node.into_active_model(); + active_model.is_approved = sea_orm::Set(true); + + let updated_node = entity::shared_nodes::Entity::update(active_model) + .exec(app_state.db.orm_db()) + .await?; + + Ok(Json(ApiResponse::success(NodeResponse::from(updated_node)))) +} + +pub async fn admin_update_node( + State(app_state): State, + Path(id): Path, + headers: HeaderMap, + Json(request): Json, +) -> ApiResult>> { + verify_admin_token(&headers)?; + request.validate()?; + + let mut node = NodeOperations::get_node_by_id(&app_state.db, id) + .await? + .ok_or_else(|| ApiError::NotFound(format!("Node with id {} not found", id)))?; + + let mut node = node.into_active_model(); + + if let Some(name) = request.name { + node.name = Set(name); + } + if let Some(host) = request.host { + node.host = Set(host); + } + if let Some(port) = request.port { + node.port = Set(port); + } + if let Some(protocol) = request.protocol { + node.protocol = Set(protocol); + } + if let Some(description) = request.description { + node.description = Set(description); + } + if let Some(max_connections) = request.max_connections { + node.max_connections = Set(max_connections); + } + if let Some(is_active) = request.is_active { + node.is_active = Set(is_active); + } + if let Some(allow_relay) = request.allow_relay { + node.allow_relay = Set(allow_relay); + } + if let Some(network_name) = request.network_name { + node.network_name = Set(network_name); + } + if let Some(network_secret) = request.network_secret { + node.network_secret = Set(network_secret); + } + if let Some(wechat) = request.wechat { + node.wechat = Set(wechat); + } + if let Some(mail) = request.mail { + node.mail = Set(mail); + } + if let Some(qq_number) = request.qq_number { + node.qq_number = Set(qq_number); + } + + node.updated_at = Set(chrono::Utc::now().fixed_offset()); + + tracing::info!("updated node: {:?}", node); + + let updated_node = entity::shared_nodes::Entity::update(node) + .exec(app_state.db.orm_db()) + .await?; + + Ok(Json(ApiResponse::success(NodeResponse::from(updated_node)))) +} + +pub async fn admin_revoke_approval( + State(app_state): State, + Path(id): Path, + headers: HeaderMap, +) -> ApiResult>> { + verify_admin_token(&headers)?; + + let node = entity::shared_nodes::Entity::find_by_id(id) + .one(app_state.db.orm_db()) + .await? + .ok_or_else(|| ApiError::NotFound("Node not found".to_string()))?; + + let mut active_model = node.into_active_model(); + active_model.is_approved = sea_orm::Set(false); + + let updated_node = entity::shared_nodes::Entity::update(active_model) + .exec(app_state.db.orm_db()) + .await?; + + Ok(Json(ApiResponse::success(NodeResponse::from(updated_node)))) +} + +pub async fn admin_delete_node( + State(app_state): State, + Path(id): Path, + headers: HeaderMap, +) -> ApiResult>> { + verify_admin_token(&headers)?; + + let node = entity::shared_nodes::Entity::find_by_id(id) + .one(app_state.db.orm_db()) + .await? + .ok_or_else(|| ApiError::NotFound("Node not found".to_string()))?; + + node.delete(app_state.db.orm_db()).await?; + + Ok(Json(ApiResponse::message( + "Node deleted successfully".to_string(), + ))) +} + +pub async fn admin_verify_token(headers: HeaderMap) -> ApiResult>> { + verify_admin_token(&headers)?; + Ok(Json(ApiResponse::message("Token is valid".to_string()))) +} + +fn verify_admin_token(headers: &HeaderMap) -> ApiResult<()> { + let config = AppConfig::default(); + + let auth_header = headers + .get("authorization") + .ok_or_else(|| ApiError::Unauthorized("Missing authorization header".to_string()))?; + + let auth_str = auth_header + .to_str() + .map_err(|_| ApiError::Unauthorized("Invalid authorization header".to_string()))?; + + let token = auth_str + .strip_prefix("Bearer ") + .ok_or_else(|| ApiError::Unauthorized("Invalid authorization format".to_string()))?; + + let _claims = decode::( + token, + &DecodingKey::from_secret(config.security.jwt_secret.as_ref()), + &Validation::default(), + ) + .map_err(|_| ApiError::Unauthorized("Invalid token".to_string()))?; + + Ok(()) +} diff --git a/easytier-contrib/easytier-uptime/src/api/mod.rs b/easytier-contrib/easytier-uptime/src/api/mod.rs new file mode 100644 index 000000000..e5b5acbe2 --- /dev/null +++ b/easytier-contrib/easytier-uptime/src/api/mod.rs @@ -0,0 +1,8 @@ +pub mod error; +pub mod handlers; +pub mod models; +pub mod routes; + +pub use error::{ApiError, ApiResult}; +pub use handlers::*; +pub use models::*; diff --git a/easytier-contrib/easytier-uptime/src/api/models.rs b/easytier-contrib/easytier-uptime/src/api/models.rs new file mode 100644 index 000000000..abf434870 --- /dev/null +++ b/easytier-contrib/easytier-uptime/src/api/models.rs @@ -0,0 +1,316 @@ +use crate::db::entity; +use chrono::{DateTime, Utc}; +use serde::{Deserialize, Serialize}; +use validator::Validate; + +#[derive(Debug, Serialize, Deserialize)] +pub struct ApiResponse { + pub success: bool, + pub data: Option, + pub error: Option, + pub message: Option, +} + +impl ApiResponse { + pub fn success(data: T) -> Self { + Self { + success: true, + data: Some(data), + error: None, + message: None, + } + } + + pub fn error(error: String) -> Self { + Self { + success: false, + data: None, + error: Some(error), + message: None, + } + } + + pub fn message(message: String) -> Self { + Self { + success: true, + data: None, + error: None, + message: Some(message), + } + } +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct PaginatedResponse { + pub items: Vec, + pub total: u64, + pub page: u32, + pub per_page: u32, + pub total_pages: u32, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct PaginationParams { + pub page: Option, + pub per_page: Option, +} + +impl Default for PaginationParams { + fn default() -> Self { + Self { + page: Some(1), + per_page: Some(20), + } + } +} + +#[derive(Debug, Serialize, Deserialize, Validate)] +#[validate(schema(function = "validate_contact_info", skip_on_field_errors = false))] +pub struct CreateNodeRequest { + #[validate(length(min = 1, max = 100))] + pub name: String, + + #[validate(length(min = 1, max = 255))] + pub host: String, + + #[validate(range(min = 1, max = 65535))] + pub port: i32, + + #[validate(length(min = 1, max = 20))] + pub protocol: String, + + #[validate(length(max = 500))] + pub description: Option, + + #[validate(range(min = 1, max = 10000))] + pub max_connections: i32, + + pub allow_relay: bool, + + #[validate(length(min = 1, max = 100))] + pub network_name: String, + + #[validate(length(max = 100))] + pub network_secret: Option, + + // 联系方式字段 + #[validate(length(max = 20))] + pub qq_number: Option, + + #[validate(length(max = 50))] + pub wechat: Option, + + #[validate(email)] + pub mail: Option, +} + +// 自定义验证函数:确保至少填写一种联系方式 +fn validate_contact_info(request: &CreateNodeRequest) -> Result<(), validator::ValidationError> { + let has_qq = request + .qq_number + .as_ref() + .is_some_and(|s| !s.trim().is_empty()); + let has_wechat = request + .wechat + .as_ref() + .is_some_and(|s| !s.trim().is_empty()); + let has_mail = request.mail.as_ref().is_some_and(|s| !s.trim().is_empty()); + + if !has_qq && !has_wechat && !has_mail { + return Err(validator::ValidationError::new("contact_required")); + } + + Ok(()) +} + +#[derive(Debug, Serialize, Deserialize, Validate)] +pub struct UpdateNodeRequest { + #[validate(length(min = 1, max = 100))] + pub name: Option, + + #[validate(length(min = 1, max = 255))] + pub host: Option, + + #[validate(range(min = 1, max = 65535))] + pub port: Option, + + #[validate(length(min = 1, max = 20))] + pub protocol: Option, + + #[validate(length(max = 500))] + pub description: Option, + + #[validate(range(min = 1, max = 10000))] + pub max_connections: Option, + + pub is_active: Option, + + pub allow_relay: Option, + + #[validate(length(min = 1, max = 100))] + pub network_name: Option, + + #[validate(length(max = 100))] + pub network_secret: Option, + + // 联系方式字段 + #[validate(length(max = 20))] + pub qq_number: Option, + + #[validate(length(max = 50))] + pub wechat: Option, + + #[validate(email)] + pub mail: Option, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct NodeResponse { + pub id: i32, + pub name: String, + pub host: String, + pub port: i32, + pub protocol: String, + pub version: Option, + pub description: Option, + pub max_connections: i32, + pub current_connections: i32, + pub is_active: bool, + pub is_approved: bool, + pub allow_relay: bool, + pub network_name: Option, + pub network_secret: Option, + pub created_at: chrono::DateTime, + pub updated_at: chrono::DateTime, + pub address: String, + pub usage_percentage: f64, + // 健康状态相关字段 + pub current_health_status: Option, + pub last_check_time: Option>, + pub last_response_time: Option, + pub health_percentage_24h: Option, + + pub health_record_total_counter_ring: Vec, + pub health_record_healthy_counter_ring: Vec, + pub ring_granularity: u32, + + // 联系方式字段 + pub qq_number: Option, + pub wechat: Option, + pub mail: Option, +} + +impl From for NodeResponse { + fn from(node: entity::shared_nodes::Model) -> Self { + Self { + id: node.id, + name: node.name.clone(), + host: node.host.clone(), + port: node.port, + protocol: node.protocol.clone(), + version: Some(node.version.clone()), + description: Some(node.description.clone()), + max_connections: node.max_connections, + current_connections: node.current_connections, + is_active: node.is_active, + is_approved: node.is_approved, + allow_relay: node.allow_relay, + network_name: Some(node.network_name.clone()), + network_secret: Some(node.network_secret.clone()), + created_at: node.created_at.into(), + updated_at: node.updated_at.into(), + address: format!("{}://{}:{}", node.protocol, node.host, node.port), + usage_percentage: node.current_connections as f64 / node.max_connections as f64 * 100.0, + // 健康状态字段初始化为 None,将在 handlers 中填充 + current_health_status: None, + last_check_time: None, + last_response_time: None, + health_percentage_24h: None, + + health_record_healthy_counter_ring: Vec::new(), + health_record_total_counter_ring: Vec::new(), + ring_granularity: 0, + + // 联系方式字段 + qq_number: if node.qq_number.is_empty() { + None + } else { + Some(node.qq_number) + }, + wechat: if node.wechat.is_empty() { + None + } else { + Some(node.wechat) + }, + mail: if node.mail.is_empty() { + None + } else { + Some(node.mail) + }, + } + } +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct HealthRecordResponse { + pub id: i32, + pub node_id: i32, + pub status: String, + pub response_time: Option, + pub error_message: Option, + pub checked_at: chrono::DateTime, +} + +impl From for HealthRecordResponse { + fn from(record: entity::health_records::Model) -> Self { + Self { + id: record.id, + node_id: record.node_id, + status: record.status.to_string(), + response_time: Some(record.response_time), + error_message: Some(record.error_message), + checked_at: record.checked_at.into(), + } + } +} + +pub type HealthStatsResponse = crate::db::HealthStats; + +#[derive(Debug, Serialize, Deserialize)] +pub struct NodeFilterParams { + pub is_active: Option, + pub protocol: Option, + pub search: Option, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct HealthFilterParams { + pub status: Option, + pub since: Option>, +} + +// 管理员相关模型 +#[derive(Debug, Serialize, Deserialize, Validate)] +pub struct AdminLoginRequest { + #[validate(length(min = 1))] + pub password: String, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct AdminLoginResponse { + pub token: String, + pub expires_at: DateTime, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct ApproveNodeRequest { + pub approved: bool, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct AdminNodeFilterParams { + pub is_active: Option, + pub is_approved: Option, + pub protocol: Option, + pub search: Option, +} diff --git a/easytier-contrib/easytier-uptime/src/api/routes.rs b/easytier-contrib/easytier-uptime/src/api/routes.rs new file mode 100644 index 000000000..e4e07d1da --- /dev/null +++ b/easytier-contrib/easytier-uptime/src/api/routes.rs @@ -0,0 +1,64 @@ +use axum::routing::{delete, get, post, put}; +use axum::Router; +use tower_http::compression::CompressionLayer; +use tower_http::cors::CorsLayer; + +use super::handlers::AppState; +use super::handlers::{ + admin_approve_node, admin_delete_node, admin_get_nodes, admin_login, admin_revoke_approval, + admin_update_node, admin_verify_token, create_node, get_node, get_node_health, + get_node_health_stats, get_nodes, health_check, +}; +use crate::api::{get_node_connect_url, test_connection}; +use crate::config::AppConfig; +use crate::db::Db; + +pub fn create_routes() -> Router { + let config = AppConfig::default(); + + let compression_layer = if config.security.enable_compression { + Some( + CompressionLayer::new() + .br(true) + .deflate(true) + .gzip(true) + .zstd(true), + ) + } else { + None + }; + + let cors_layer = if config.cors.enabled { + Some(CorsLayer::very_permissive()) + } else { + None + }; + + let mut router = Router::new() + .route("/node/{id}", get(get_node_connect_url)) + .route("/health", get(health_check)) + .route("/api/nodes", get(get_nodes).post(create_node)) + .route("/api/test_connection", post(test_connection)) + .route("/api/nodes/{id}/health", get(get_node_health)) + .route("/api/nodes/{id}/health/stats", get(get_node_health_stats)) + // 管理员路由 + .route("/api/admin/login", post(admin_login)) + .route("/api/admin/verify", get(admin_verify_token)) + .route("/api/admin/nodes", get(admin_get_nodes)) + .route("/api/admin/nodes/{id}/approve", put(admin_approve_node)) + .route("/api/admin/nodes/{id}/revoke", put(admin_revoke_approval)) + .route( + "/api/admin/nodes/{id}", + put(admin_update_node).delete(admin_delete_node), + ); + + if let Some(layer) = compression_layer { + router = router.layer(layer); + } + + if let Some(layer) = cors_layer { + router = router.layer(layer); + } + + router +} diff --git a/easytier-contrib/easytier-uptime/src/config.rs b/easytier-contrib/easytier-uptime/src/config.rs new file mode 100644 index 000000000..e3c981123 --- /dev/null +++ b/easytier-contrib/easytier-uptime/src/config.rs @@ -0,0 +1,198 @@ +use std::env; +use std::net::{IpAddr, SocketAddr}; +use std::path::PathBuf; + +#[derive(Debug, Clone)] +pub struct AppConfig { + pub server: ServerConfig, + pub database: DatabaseConfig, + pub health_check: HealthCheckConfig, + pub logging: LoggingConfig, + pub cors: CorsConfig, + pub security: SecurityConfig, +} + +#[derive(Debug, Clone)] +pub struct ServerConfig { + pub host: String, + pub port: u16, + pub addr: SocketAddr, +} + +#[derive(Debug, Clone)] +pub struct DatabaseConfig { + pub path: PathBuf, + pub max_connections: u32, +} + +#[derive(Debug, Clone)] +pub struct HealthCheckConfig { + pub interval_seconds: u64, + pub timeout_seconds: u64, + pub max_retries: u32, +} + +#[derive(Debug, Clone)] +pub struct LoggingConfig { + pub level: String, + pub rust_log: String, +} + +#[derive(Debug, Clone)] +pub struct CorsConfig { + pub allowed_origins: Vec, + pub allowed_methods: Vec, + pub allowed_headers: Vec, + pub enabled: bool, +} + +#[derive(Debug, Clone)] +pub struct SecurityConfig { + pub enable_compression: bool, + pub secret_key: String, + pub jwt_secret: String, + pub admin_password: String, +} + +impl Default for AppConfig { + fn default() -> Self { + Self::from_env().unwrap_or_else(|_| Self::default_config()) + } +} + +impl AppConfig { + pub fn from_env() -> Result { + let server_config = ServerConfig { + host: env::var("SERVER_HOST").unwrap_or_else(|_| "127.0.0.1".to_string()), + port: env::var("SERVER_PORT") + .map(|s| s.parse().unwrap_or(8080)) + .unwrap_or(8080), + addr: SocketAddr::from(( + env::var("SERVER_HOST") + .unwrap_or_else(|_| "127.0.0.1".to_string()) + .parse::() + .unwrap(), + env::var("SERVER_PORT") + .map(|s| s.parse().unwrap_or(8080)) + .unwrap_or(8080), + )), + }; + + let database_config = DatabaseConfig { + path: PathBuf::from( + env::var("DATABASE_PATH").unwrap_or_else(|_| "uptime.db".to_string()), + ), + max_connections: env::var("DATABASE_MAX_CONNECTIONS") + .map(|s| s.parse().unwrap_or(10)) + .unwrap_or(10), + }; + + let health_check_config = HealthCheckConfig { + interval_seconds: env::var("HEALTH_CHECK_INTERVAL") + .map(|s| s.parse().unwrap_or(30)) + .unwrap_or(30), + timeout_seconds: env::var("HEALTH_CHECK_TIMEOUT") + .map(|s| s.parse().unwrap_or(10)) + .unwrap_or(10), + max_retries: env::var("HEALTH_CHECK_RETRIES") + .map(|s| s.parse().unwrap_or(3)) + .unwrap_or(3), + }; + + let logging_config = LoggingConfig { + level: env::var("LOG_LEVEL").unwrap_or_else(|_| "info".to_string()), + rust_log: env::var("RUST_LOG").unwrap_or_else(|_| "info".to_string()), + }; + + let cors_config = CorsConfig { + allowed_origins: env::var("CORS_ALLOWED_ORIGINS") + .unwrap_or_else(|_| "http://localhost:3000,http://localhost:8080".to_string()) + .split(',') + .map(|s| s.trim().to_string()) + .collect(), + allowed_methods: env::var("CORS_ALLOWED_METHODS") + .unwrap_or_else(|_| "GET,POST,PUT,DELETE,OPTIONS".to_string()) + .split(',') + .map(|s| s.trim().to_string()) + .collect(), + allowed_headers: env::var("CORS_ALLOWED_HEADERS") + .unwrap_or_else(|_| "content-type,authorization".to_string()) + .split(',') + .map(|s| s.trim().to_string()) + .collect(), + enabled: env::var("ENABLE_CORS") + .map(|s| s.parse().unwrap_or(true)) + .unwrap_or(true), + }; + + let security_config = SecurityConfig { + enable_compression: env::var("ENABLE_COMPRESSION") + .map(|s| s.parse().unwrap_or(true)) + .unwrap_or(true), + secret_key: env::var("SECRET_KEY").unwrap_or_else(|_| "default-secret-key".to_string()), + jwt_secret: env::var("JWT_SECRET").unwrap_or_else(|_| "default-jwt-secret".to_string()), + admin_password: env::var("ADMIN_PASSWORD").unwrap_or_else(|_| "admin123".to_string()), + }; + + Ok(AppConfig { + server: server_config, + database: database_config, + health_check: health_check_config, + logging: logging_config, + cors: cors_config, + security: security_config, + }) + } + + pub fn default_config() -> Self { + Self { + server: ServerConfig { + host: "127.0.0.1".to_string(), + port: 8080, + addr: SocketAddr::from(([127, 0, 0, 1], 8080)), + }, + database: DatabaseConfig { + path: PathBuf::from("uptime.db"), + max_connections: 10, + }, + health_check: HealthCheckConfig { + interval_seconds: 30, + timeout_seconds: 10, + max_retries: 3, + }, + logging: LoggingConfig { + level: "info".to_string(), + rust_log: "info".to_string(), + }, + cors: CorsConfig { + allowed_origins: vec![ + "http://localhost:3000".to_string(), + "http://localhost:8080".to_string(), + ], + allowed_methods: vec![ + "GET".to_string(), + "POST".to_string(), + "PUT".to_string(), + "DELETE".to_string(), + "OPTIONS".to_string(), + ], + allowed_headers: vec!["content-type".to_string(), "authorization".to_string()], + enabled: true, + }, + security: SecurityConfig { + enable_compression: true, + secret_key: "default-secret-key".to_string(), + jwt_secret: "default-jwt-secret".to_string(), + admin_password: "admin123".to_string(), + }, + } + } + + pub fn is_development(&self) -> bool { + env::var("NODE_ENV").unwrap_or_else(|_| "development".to_string()) == "development" + } + + pub fn is_production(&self) -> bool { + env::var("NODE_ENV").unwrap_or_else(|_| "development".to_string()) == "production" + } +} diff --git a/easytier-contrib/easytier-uptime/src/db/cleanup.rs b/easytier-contrib/easytier-uptime/src/db/cleanup.rs new file mode 100644 index 000000000..0645b1fee --- /dev/null +++ b/easytier-contrib/easytier-uptime/src/db/cleanup.rs @@ -0,0 +1,360 @@ +use crate::db::entity::*; +use crate::db::Db; +use sea_orm::*; +use tokio::time::{sleep, Duration}; +use tracing::{error, info, warn}; + +/// 数据清理策略配置 +#[derive(Debug, Clone)] +pub struct CleanupConfig { + /// 健康记录保留天数 + pub health_record_retention_days: i64, + /// 每个节点保留的健康记录最大数量 + pub max_health_records_per_node: u64, + /// 清理任务运行间隔(秒) + pub cleanup_interval_seconds: u64, + /// 是否启用自动清理 + pub auto_cleanup_enabled: bool, +} + +impl Default for CleanupConfig { + fn default() -> Self { + Self { + health_record_retention_days: 30, + max_health_records_per_node: 70000, + cleanup_interval_seconds: 1200, // 20分钟 + auto_cleanup_enabled: true, + } + } +} + +/// 数据清理管理器 +pub struct CleanupManager { + db: Db, + config: CleanupConfig, + running: std::sync::Arc, +} + +impl CleanupManager { + /// 创建新的清理管理器 + pub fn new(db: Db, config: CleanupConfig) -> Self { + Self { + db, + config, + running: std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false)), + } + } + + /// 使用默认配置创建清理管理器 + pub fn with_default_config(db: Db) -> Self { + Self::new(db, CleanupConfig::default()) + } + + /// 启动自动清理任务 + pub async fn start_auto_cleanup(&self) -> anyhow::Result<()> { + if self.config.auto_cleanup_enabled { + let running = self.running.clone(); + let db = self.db.clone(); + let config = self.config.clone(); + + running.store(true, std::sync::atomic::Ordering::SeqCst); + + tokio::spawn(async move { + info!("Auto cleanup task started"); + + while running.load(std::sync::atomic::Ordering::SeqCst) { + if let Err(e) = Self::perform_cleanup(&db, &config).await { + error!("Auto cleanup failed: {}", e); + } + + sleep(Duration::from_secs(config.cleanup_interval_seconds)).await; + } + + info!("Auto cleanup task stopped"); + }); + } + + Ok(()) + } + + /// 停止自动清理任务 + pub fn stop_auto_cleanup(&self) { + self.running + .store(false, std::sync::atomic::Ordering::SeqCst); + } + + /// 执行一次完整的清理操作 + pub async fn perform_cleanup(db: &Db, config: &CleanupConfig) -> anyhow::Result { + let mut result = CleanupResult::default(); + + // 清理旧的健康记录 + let health_cleanup_result = + Self::cleanup_old_health_records(db, config.health_record_retention_days).await?; + result.old_health_records_cleaned = health_cleanup_result.records_removed; + + // 清理过量的健康记录 + let excess_cleanup_result = + Self::cleanup_excess_health_records(db, config.max_health_records_per_node).await?; + result.excess_health_records_cleaned = excess_cleanup_result.records_removed; + + // 数据库维护 + let maintenance_result = Self::perform_database_maintenance(db).await?; + result.vacuum_performed = maintenance_result.vacuum_performed; + result.analyze_performed = maintenance_result.analyze_performed; + + info!("Cleanup completed: {:?}", result); + + Ok(result) + } + + /// 清理旧的健康记录 + async fn cleanup_old_health_records( + db: &Db, + days: i64, + ) -> anyhow::Result { + let cutoff = chrono::Local::now().fixed_offset() - chrono::Duration::days(days); + + let result = health_records::Entity::delete_many() + .filter(health_records::Column::CheckedAt.lt(cutoff)) + .exec(db.orm_db()) + .await?; + + let records_removed = result.rows_affected; + + if records_removed > 0 { + info!( + "Cleaned {} old health records (older than {} days)", + records_removed, days + ); + } + + Ok(CleanupHealthRecordsResult { records_removed }) + } + + /// 清理过量的健康记录 + async fn cleanup_excess_health_records( + db: &Db, + max_records: u64, + ) -> anyhow::Result { + // 获取所有节点 + let nodes = shared_nodes::Entity::find().all(db.orm_db()).await?; + + let mut total_removed = 0; + + for node in nodes { + // 计算需要删除的记录数量 + let total_count = health_records::Entity::find() + .filter(health_records::Column::NodeId.eq(node.id)) + .count(db.orm_db()) + .await?; + + if total_count > max_records { + let to_remove = total_count - max_records; + + // 获取需要保留的最小ID + let keep_id = health_records::Entity::find() + .filter(health_records::Column::NodeId.eq(node.id)) + .order_by_desc(health_records::Column::CheckedAt) + .offset(max_records) + .limit(1) + .into_model::() + .one(db.orm_db()) + .await?; + + info!( + "Node {}: total count: {}, to remove: {}, last keep record: {:?}", + node.id, total_count, to_remove, keep_id + ); + + if let Some(keep_record) = keep_id { + // 删除比保留记录更早的记录 + let result = health_records::Entity::delete_many() + .filter(health_records::Column::NodeId.eq(node.id)) + .filter(health_records::Column::Id.lt(keep_record.id)) + .exec(db.orm_db()) + .await?; + + total_removed += result.rows_affected; + } + } + } + + if total_removed > 0 { + info!( + "Cleaned {} excess health records (max {} per node)", + total_removed, max_records + ); + } + + Ok(CleanupExcessRecordsResult { + records_removed: total_removed, + }) + } + + /// 执行数据库维护操作 + async fn perform_database_maintenance(db: &Db) -> anyhow::Result { + let mut vacuum_performed = false; + let mut analyze_performed = false; + + // 执行 ANALYZE + match db + .orm_db() + .execute(Statement::from_string( + DatabaseBackend::Sqlite, + "ANALYZE".to_string(), + )) + .await + { + Ok(_) => { + analyze_performed = true; + info!("Database ANALYZE completed"); + } + Err(e) => { + warn!("Database ANALYZE failed: {}", e); + } + } + + // 执行 VACUUM(仅在需要时) + if vacuum_performed || analyze_performed { + match db + .orm_db() + .execute(Statement::from_string( + DatabaseBackend::Sqlite, + "VACUUM".to_string(), + )) + .await + { + Ok(_) => { + vacuum_performed = true; + info!("Database VACUUM completed"); + } + Err(e) => { + warn!("Database VACUUM failed: {}", e); + } + } + } + + Ok(DatabaseMaintenanceResult { + vacuum_performed, + analyze_performed, + }) + } + + /// 获取数据库统计信息 + pub async fn get_database_stats(db: &Db) -> anyhow::Result { + let total_nodes = shared_nodes::Entity::find().count(db.orm_db()).await?; + + let total_health_records = health_records::Entity::find().count(db.orm_db()).await?; + + let active_nodes = shared_nodes::Entity::find() + .filter(shared_nodes::Column::IsActive.eq(true)) + .count(db.orm_db()) + .await?; + + Ok(DatabaseStats { + total_nodes, + active_nodes, + total_health_records, + }) + } + + /// 获取清理配置 + pub fn get_config(&self) -> &CleanupConfig { + &self.config + } + + /// 更新清理配置 + pub fn update_config(&mut self, config: CleanupConfig) { + self.config = config; + } +} + +/// 清理结果 +#[derive(Default, Debug, Clone, serde::Serialize)] +pub struct CleanupResult { + pub old_health_records_cleaned: u64, + pub old_instances_cleaned: u64, + pub excess_health_records_cleaned: u64, + pub vacuum_performed: bool, + pub analyze_performed: bool, +} + +/// 健康记录清理结果 +#[derive(Debug, Clone, serde::Serialize)] +pub struct CleanupHealthRecordsResult { + pub records_removed: u64, +} + +/// 停止实例清理结果 +#[derive(Debug, Clone, serde::Serialize)] +pub struct CleanupStoppedInstancesResult { + pub instances_removed: u64, +} + +/// 过量记录清理结果 +#[derive(Debug, Clone, serde::Serialize)] +pub struct CleanupExcessRecordsResult { + pub records_removed: u64, +} + +/// 数据库维护结果 +#[derive(Debug, Clone, serde::Serialize)] +pub struct DatabaseMaintenanceResult { + pub vacuum_performed: bool, + pub analyze_performed: bool, +} + +/// 数据库统计信息 +#[derive(Debug, Clone, serde::Serialize)] +pub struct DatabaseStats { + pub total_nodes: u64, + pub active_nodes: u64, + pub total_health_records: u64, +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::Db; + + #[tokio::test] + async fn test_cleanup_manager() { + let db = Db::memory_db().await; + let cleanup_manager = CleanupManager::with_default_config(db.clone()); + + // 测试获取配置 + let config = cleanup_manager.get_config(); + assert_eq!(config.health_record_retention_days, 30); + + // 测试清理操作 + let result = CleanupManager::perform_cleanup(&db, config).await.unwrap(); + println!("Cleanup result: {:?}", result); + + // 测试获取统计信息 + let stats = CleanupManager::get_database_stats(&db).await.unwrap(); + println!("Database stats: {:?}", stats); + } + + #[tokio::test] + async fn test_cleanup_config() { + let config = CleanupConfig { + health_record_retention_days: 7, + max_health_records_per_node: 500, + cleanup_interval_seconds: 1800, + auto_cleanup_enabled: false, + }; + + let db = Db::memory_db().await; + let mut cleanup_manager = CleanupManager::new(db, config.clone()); + + assert_eq!(cleanup_manager.get_config().health_record_retention_days, 7); + + // 测试更新配置 + let new_config = CleanupConfig::default(); + cleanup_manager.update_config(new_config); + assert_eq!( + cleanup_manager.get_config().health_record_retention_days, + 30 + ); + } +} diff --git a/easytier-contrib/easytier-uptime/src/db/entity/connection_instances.rs b/easytier-contrib/easytier-uptime/src/db/entity/connection_instances.rs new file mode 100644 index 000000000..1101910bd --- /dev/null +++ b/easytier-contrib/easytier-uptime/src/db/entity/connection_instances.rs @@ -0,0 +1,39 @@ +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.0 + +use sea_orm::entity::prelude::*; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)] +#[sea_orm(table_name = "connection_instances")] +pub struct Model { + #[sea_orm(primary_key)] + pub id: i32, + pub node_id: i32, + #[sea_orm(unique)] + pub instance_id: String, + pub status: String, + #[sea_orm(column_type = "Text")] + pub config: String, + pub started_at: DateTimeWithTimeZone, + pub stopped_at: DateTimeWithTimeZone, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm( + belongs_to = "super::shared_nodes::Entity", + from = "Column::NodeId", + to = "super::shared_nodes::Column::Id", + on_update = "Cascade", + on_delete = "Cascade" + )] + SharedNodes, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::SharedNodes.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/easytier-contrib/easytier-uptime/src/db/entity/health_records.rs b/easytier-contrib/easytier-uptime/src/db/entity/health_records.rs new file mode 100644 index 000000000..2dbbe2e34 --- /dev/null +++ b/easytier-contrib/easytier-uptime/src/db/entity/health_records.rs @@ -0,0 +1,37 @@ +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.0 + +use sea_orm::entity::prelude::*; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)] +#[sea_orm(table_name = "health_records")] +pub struct Model { + #[sea_orm(primary_key)] + pub id: i32, + pub node_id: i32, + pub status: String, + pub response_time: i32, + #[sea_orm(column_type = "Text")] + pub error_message: String, + pub checked_at: DateTimeWithTimeZone, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm( + belongs_to = "super::shared_nodes::Entity", + from = "Column::NodeId", + to = "super::shared_nodes::Column::Id", + on_update = "Cascade", + on_delete = "Cascade" + )] + SharedNodes, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::SharedNodes.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/easytier-contrib/easytier-uptime/src/db/entity/mod.rs b/easytier-contrib/easytier-uptime/src/db/entity/mod.rs new file mode 100644 index 000000000..8b427a886 --- /dev/null +++ b/easytier-contrib/easytier-uptime/src/db/entity/mod.rs @@ -0,0 +1,6 @@ +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.0 + +pub mod prelude; + +pub mod health_records; +pub mod shared_nodes; diff --git a/easytier-contrib/easytier-uptime/src/db/entity/prelude.rs b/easytier-contrib/easytier-uptime/src/db/entity/prelude.rs new file mode 100644 index 000000000..c1b7ed1bc --- /dev/null +++ b/easytier-contrib/easytier-uptime/src/db/entity/prelude.rs @@ -0,0 +1,4 @@ +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.0 + +pub use super::health_records::Entity as HealthRecords; +pub use super::shared_nodes::Entity as SharedNodes; diff --git a/easytier-contrib/easytier-uptime/src/db/entity/shared_nodes.rs b/easytier-contrib/easytier-uptime/src/db/entity/shared_nodes.rs new file mode 100644 index 000000000..e5baa116a --- /dev/null +++ b/easytier-contrib/easytier-uptime/src/db/entity/shared_nodes.rs @@ -0,0 +1,44 @@ +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.0 + +use sea_orm::entity::prelude::*; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)] +#[sea_orm(table_name = "shared_nodes")] +pub struct Model { + #[sea_orm(primary_key)] + pub id: i32, + pub name: String, + pub host: String, + pub port: i32, + pub protocol: String, + pub version: String, + pub allow_relay: bool, + pub network_name: String, + pub network_secret: String, + #[sea_orm(column_type = "Text")] + pub description: String, + pub max_connections: i32, + pub current_connections: i32, + pub is_active: bool, + pub is_approved: bool, + pub qq_number: String, + pub wechat: String, + pub mail: String, + pub created_at: DateTimeWithTimeZone, + pub updated_at: DateTimeWithTimeZone, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm(has_many = "super::health_records::Entity")] + HealthRecords, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::HealthRecords.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/easytier-contrib/easytier-uptime/src/db/mod.rs b/easytier-contrib/easytier-uptime/src/db/mod.rs new file mode 100644 index 000000000..e25a9407a --- /dev/null +++ b/easytier-contrib/easytier-uptime/src/db/mod.rs @@ -0,0 +1,351 @@ +pub mod cleanup; +pub mod entity; +pub mod operations; + +use std::fmt; + +use sea_orm::{ + prelude::*, sea_query::OnConflict, ColumnTrait as _, DatabaseConnection, DbErr, EntityTrait, + QueryFilter as _, Set, SqlxSqliteConnector, Statement, TransactionTrait as _, +}; +use sea_orm_migration::MigratorTrait as _; +use serde::{Deserialize, Serialize}; +use sqlx::{migrate::MigrateDatabase as _, Sqlite, SqlitePool}; + +use crate::migrator; + +#[derive(Debug, Clone)] +pub struct Db { + db_path: String, + db: SqlitePool, + orm_db: DatabaseConnection, +} + +impl Db { + pub async fn new(db_path: T) -> anyhow::Result { + let db = Self::prepare_db(db_path.to_string().as_str()).await?; + let orm_db = SqlxSqliteConnector::from_sqlx_sqlite_pool(db.clone()); + + // 运行数据库迁移 + migrator::Migrator::up(&orm_db, None).await?; + + // 优化 SQLite 性能 + Self::optimize_sqlite(&orm_db).await?; + + Ok(Self { + db_path: db_path.to_string(), + db, + orm_db, + }) + } + + pub async fn memory_db() -> Self { + Self::new(":memory:").await.unwrap() + } + + #[tracing::instrument(ret)] + async fn prepare_db(db_path: &str) -> anyhow::Result { + if !Sqlite::database_exists(db_path).await.unwrap_or(false) { + tracing::info!("Database not found, creating a new one"); + Sqlite::create_database(db_path).await?; + } + + let db = sqlx::pool::PoolOptions::new() + .max_lifetime(None) + .idle_timeout(None) + .connect(db_path) + .await?; + + Ok(db) + } + + async fn optimize_sqlite(db: &DatabaseConnection) -> Result<(), DbErr> { + // 优化 SQLite 性能 + let pragmas = vec![ + "PRAGMA journal_mode = WAL", // 使用 WAL 模式提高并发性能 + "PRAGMA synchronous = NORMAL", // 平衡性能和数据安全 + "PRAGMA cache_size = 10000", // 增加缓存大小 + "PRAGMA temp_store = memory", // 临时存储使用内存 + "PRAGMA mmap_size = 268435456", // 内存映射大小 256MB + "PRAGMA foreign_keys = ON", // 启用外键约束 + ]; + + for pragma in pragmas { + db.execute(sea_orm::Statement::from_string( + sea_orm::DatabaseBackend::Sqlite, + pragma.to_string(), + )) + .await?; + } + + Ok(()) + } + + pub fn inner(&self) -> SqlitePool { + self.db.clone() + } + + pub fn orm_db(&self) -> &DatabaseConnection { + &self.orm_db + } + + /// 清理旧的健康度记录(删除30天前的记录) + pub async fn cleanup_old_health_records(&self) -> Result { + use chrono::Duration; + use entity::health_records; + + let cutoff_date = chrono::Utc::now().naive_utc() - Duration::days(30); + + let result = health_records::Entity::delete_many() + .filter(health_records::Column::CheckedAt.lt(cutoff_date)) + .exec(self.orm_db()) + .await?; + + Ok(result.rows_affected) + } + + /// 获取数据库统计信息 + pub async fn get_database_stats(&self) -> anyhow::Result { + use entity::{health_records, shared_nodes}; + + let node_count = shared_nodes::Entity::find().count(self.orm_db()).await?; + + let health_record_count = health_records::Entity::find().count(self.orm_db()).await?; + + let active_nodes_count = shared_nodes::Entity::find() + .filter(shared_nodes::Column::IsActive.eq(true)) + .count(self.orm_db()) + .await?; + + Ok(DatabaseStats { + total_nodes: node_count, + active_nodes: active_nodes_count, + total_health_records: health_record_count, + }) + } +} + +#[derive(Debug, Clone, serde::Serialize)] +pub struct DatabaseStats { + pub total_nodes: u64, + pub active_nodes: u64, + pub total_health_records: u64, +} + +/// 健康状态枚举 +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub enum HealthStatus { + /// 健康状态 + Healthy, + /// 不健康状态 + Unhealthy, + /// 超时状态 + Timeout, + /// 连接错误 + ConnectionError, + /// 未知错误 + Unknown, +} + +impl fmt::Display for HealthStatus { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + HealthStatus::Healthy => write!(f, "healthy"), + HealthStatus::Unhealthy => write!(f, "unhealthy"), + HealthStatus::Timeout => write!(f, "timeout"), + HealthStatus::ConnectionError => write!(f, "connection_error"), + HealthStatus::Unknown => write!(f, "unknown"), + } + } +} + +impl From for HealthStatus { + fn from(s: String) -> Self { + match s.to_lowercase().as_str() { + "healthy" => HealthStatus::Healthy, + "unhealthy" => HealthStatus::Unhealthy, + "timeout" => HealthStatus::Timeout, + "connection_error" => HealthStatus::ConnectionError, + _ => HealthStatus::Unknown, + } + } +} + +impl From<&str> for HealthStatus { + fn from(s: &str) -> Self { + HealthStatus::from(s.to_string()) + } +} + +/// 健康统计信息 +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct HealthStats { + /// 总检查次数 + pub total_checks: u64, + /// 健康检查次数 + pub healthy_count: u64, + /// 不健康检查次数 + pub unhealthy_count: u64, + /// 健康百分比 + pub health_percentage: f64, + /// 平均响应时间(毫秒) + pub average_response_time: Option, + /// 正常运行时间百分比 + pub uptime_percentage: f64, + /// 最后检查时间 + pub last_check_time: Option>, + /// 最后健康状态 + pub last_status: Option, +} + +impl Default for HealthStats { + fn default() -> Self { + Self { + total_checks: 0, + healthy_count: 0, + unhealthy_count: 0, + health_percentage: 0.0, + average_response_time: None, + uptime_percentage: 0.0, + last_check_time: None, + last_status: None, + } + } +} + +impl HealthStats { + /// 从健康记录列表创建统计信息 + pub fn from_records(records: &[self::entity::health_records::Model]) -> Self { + if records.is_empty() { + return Self::default(); + } + + let total_checks = records.len() as u64; + let healthy_count = records.iter().filter(|r| r.is_healthy()).count() as u64; + let unhealthy_count = total_checks - healthy_count; + + let health_percentage = if total_checks > 0 { + (healthy_count as f64 / total_checks as f64) * 100.0 + } else { + 0.0 + }; + + // 计算平均响应时间(只计算健康状态的记录) + let healthy_records: Vec<_> = records + .iter() + .filter(|r| r.is_healthy() && r.response_time > 0) + .collect(); + + let average_response_time = if !healthy_records.is_empty() { + let total_time: i32 = healthy_records.iter().map(|r| r.response_time).sum(); + Some(total_time as f64 / healthy_records.len() as f64) + } else { + None + }; + + // 正常运行时间百分比(基于健康状态) + let uptime_percentage = health_percentage; + + // 获取最后的检查信息 + let last_record = records.first(); // records 应该按时间倒序排列 + let last_check_time = last_record.map(|r| r.checked_at.into()); + let last_status = last_record.map(|r| HealthStatus::from(r.status.clone())); + + Self { + total_checks, + healthy_count, + unhealthy_count, + health_percentage, + average_response_time, + uptime_percentage, + last_check_time, + last_status, + } + } +} + +/// Model 的扩展方法 +impl entity::health_records::Model { + /// 检查记录是否为健康状态 + pub fn is_healthy(&self) -> bool { + let status = HealthStatus::from(self.status.clone()); + matches!(status, HealthStatus::Healthy) + } + + /// 创建新的活动模型 + pub fn new_active_model( + node_id: i32, + status: HealthStatus, + response_time: Option, + error_message: Option, + ) -> entity::health_records::ActiveModel { + entity::health_records::ActiveModel { + node_id: Set(node_id), + status: Set(status.to_string()), + response_time: Set(response_time.unwrap_or(0)), + error_message: Set(error_message.unwrap_or_default()), + checked_at: Set(chrono::Utc::now().fixed_offset()), + ..Default::default() + } + } + + /// 获取健康状态 + pub fn get_status(&self) -> HealthStatus { + HealthStatus::from(self.status.clone()) + } +} + +/// Model 的扩展方法 +impl entity::shared_nodes::Model { + /// 创建新的活动模型 + #[allow(clippy::too_many_arguments)] + pub fn new_active_model( + name: String, + host: String, + port: i32, + protocol: String, + version: Option, + description: Option, + max_connections: i32, + allow_relay: bool, + network_name: String, + network_secret: Option, + ) -> entity::shared_nodes::ActiveModel { + let now = chrono::Utc::now().fixed_offset(); + entity::shared_nodes::ActiveModel { + name: Set(name), + host: Set(host), + port: Set(port), + protocol: Set(protocol), + version: Set(version.unwrap_or_default()), + description: Set(description.unwrap_or_default()), + max_connections: Set(max_connections), + current_connections: Set(0), + is_active: Set(true), + is_approved: Set(false), + allow_relay: Set(allow_relay), + network_name: Set(network_name), + network_secret: Set(network_secret.unwrap_or_default()), + created_at: Set(now), + updated_at: Set(now), + ..Default::default() + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use sea_orm::{ColumnTrait, EntityTrait, QueryFilter as _}; + + #[tokio::test] + async fn test_database_creation() { + let db = Db::memory_db().await; + let stats = db.get_database_stats().await.unwrap(); + + // 初始状态下应该没有记录 + assert_eq!(stats.total_nodes, 0); + assert_eq!(stats.active_nodes, 0); + assert_eq!(stats.total_health_records, 0); + } +} diff --git a/easytier-contrib/easytier-uptime/src/db/operations.rs b/easytier-contrib/easytier-uptime/src/db/operations.rs new file mode 100644 index 000000000..94af0f55c --- /dev/null +++ b/easytier-contrib/easytier-uptime/src/db/operations.rs @@ -0,0 +1,343 @@ +use crate::api::CreateNodeRequest; +use crate::db::entity::*; +use crate::db::Db; +use crate::db::HealthStats; +use crate::db::HealthStatus; +use sea_orm::*; + +/// 节点管理操作 +pub struct NodeOperations; + +impl NodeOperations { + pub fn create_node_model(req: CreateNodeRequest) -> shared_nodes::ActiveModel { + shared_nodes::ActiveModel { + id: NotSet, + name: Set(req.name), + host: Set(req.host), + port: Set(req.port), + protocol: Set(req.protocol), + version: Set("".to_string()), + description: Set(req.description.unwrap_or_default()), + max_connections: Set(req.max_connections), + current_connections: Set(0), + is_active: Set(false), + is_approved: Set(false), + allow_relay: Set(req.allow_relay), + network_name: Set(req.network_name), + network_secret: Set(req.network_secret.unwrap_or_default()), + qq_number: Set(req.qq_number.unwrap_or_default()), + wechat: Set(req.wechat.unwrap_or_default()), + mail: Set(req.mail.unwrap_or_default()), + created_at: Set(chrono::Utc::now().fixed_offset()), + updated_at: Set(chrono::Utc::now().fixed_offset()), + } + } + + /// 创建新节点 + pub async fn create_node( + db: &Db, + req: CreateNodeRequest, + ) -> Result { + let node = Self::create_node_model(req); + let insert_result = shared_nodes::Entity::insert(node).exec(db.orm_db()).await?; + + shared_nodes::Entity::find_by_id(insert_result.last_insert_id) + .one(db.orm_db()) + .await? + .ok_or(DbErr::RecordNotFound( + "Failed to retrieve created node".to_string(), + )) + } + + /// 获取所有节点 + pub async fn get_all_nodes(db: &Db) -> Result, DbErr> { + shared_nodes::Entity::find() + .order_by_asc(shared_nodes::Column::Id) + .all(db.orm_db()) + .await + } + + /// 根据ID获取节点 + pub async fn get_node_by_id(db: &Db, id: i32) -> Result, DbErr> { + shared_nodes::Entity::find_by_id(id).one(db.orm_db()).await + } + + /// 更新节点状态 + pub async fn update_node_status( + db: &Db, + id: i32, + is_active: bool, + current_connections: Option, + ) -> Result { + let mut node = shared_nodes::Entity::find_by_id(id) + .one(db.orm_db()) + .await? + .ok_or(DbErr::RecordNotFound("Node not found".to_string()))?; + + let mut node = node.into_active_model(); + + node.is_active = Set(is_active); + if let Some(connections) = current_connections { + node.current_connections = Set(connections); + } + node.updated_at = Set(chrono::Utc::now().fixed_offset()); + + let updated_node = shared_nodes::Entity::update(node).exec(db.orm_db()).await?; + + Ok(updated_node) + } + + /// 删除节点 + pub async fn delete_node(db: &Db, id: i32) -> Result { + let result = shared_nodes::Entity::delete_by_id(id) + .exec(db.orm_db()) + .await?; + Ok(result.rows_affected) + } + + /// 获取活跃节点 + pub async fn get_active_nodes(db: &Db) -> Result, DbErr> { + shared_nodes::Entity::find() + .filter(shared_nodes::Column::IsActive.eq(true)) + .order_by_asc(shared_nodes::Column::Id) + .all(db.orm_db()) + .await + } + + /// 检查节点是否存在(根据host、port、protocol) + pub async fn node_exists( + db: &Db, + host: &str, + port: i32, + protocol: &str, + ) -> Result { + let count = shared_nodes::Entity::find() + .filter(shared_nodes::Column::Host.eq(host)) + .filter(shared_nodes::Column::Port.eq(port)) + .filter(shared_nodes::Column::Protocol.eq(protocol)) + .count(db.orm_db()) + .await?; + + Ok(count > 0) + } + + pub async fn update_node_version( + db: &Db, + node_id: i32, + version: String, + ) -> Result { + let mut node = shared_nodes::Entity::find_by_id(node_id) + .one(db.orm_db()) + .await? + .ok_or(DbErr::RecordNotFound("Node not found".to_string()))?; + + let mut node = node.into_active_model(); + + node.version = Set(version); + node.updated_at = Set(chrono::Utc::now().fixed_offset()); + + let updated_node = shared_nodes::Entity::update(node).exec(db.orm_db()).await?; + + Ok(updated_node) + } +} + +/// 健康记录操作 +pub struct HealthOperations; + +impl HealthOperations { + /// 创建健康记录 + pub async fn create_health_record( + db: &Db, + node_id: i32, + status: HealthStatus, + response_time: Option, + error_message: Option, + ) -> Result { + let record = + health_records::Model::new_active_model(node_id, status, response_time, error_message); + + let insert_result = health_records::Entity::insert(record) + .exec(db.orm_db()) + .await?; + + health_records::Entity::find_by_id(insert_result.last_insert_id) + .one(db.orm_db()) + .await? + .ok_or(DbErr::RecordNotFound( + "Failed to retrieve created health record".to_string(), + )) + } + + /// 获取节点的健康记录 + pub async fn get_node_health_records( + db: &Db, + node_id: i32, + from_date: Option, + limit: Option, + ) -> Result, DbErr> { + let mut query = health_records::Entity::find() + .filter(health_records::Column::NodeId.eq(node_id)) + .order_by_desc(health_records::Column::CheckedAt); + + if let Some(from_date) = from_date { + query = query.filter(health_records::Column::CheckedAt.gte(from_date)); + } + + if let Some(limit) = limit { + query = query.limit(Some(limit)); + } + + query.all(db.orm_db()).await + } + + /// 获取节点最近的健康状态 + pub async fn get_latest_health_status( + db: &Db, + node_id: i32, + ) -> Result, DbErr> { + health_records::Entity::find() + .filter(health_records::Column::NodeId.eq(node_id)) + .order_by_desc(health_records::Column::CheckedAt) + .one(db.orm_db()) + .await + } + + /// 获取健康统计信息 + pub async fn get_health_stats(db: &Db, node_id: i32, hours: i64) -> Result { + let since = chrono::Utc::now().naive_utc() - chrono::Duration::hours(hours); + + let records = health_records::Entity::find() + .filter(health_records::Column::NodeId.eq(node_id)) + .filter(health_records::Column::CheckedAt.gte(since)) + .order_by_desc(health_records::Column::CheckedAt) + .all(db.orm_db()) + .await?; + + Ok(HealthStats::from_records(&records)) + } + + /// 清理旧的健康记录 + pub async fn cleanup_old_records(db: &Db, days: i64) -> Result { + let cutoff = chrono::Utc::now().naive_utc() - chrono::Duration::days(days); + + let result = health_records::Entity::delete_many() + .filter(health_records::Column::CheckedAt.lt(cutoff)) + .exec(db.orm_db()) + .await?; + + Ok(result.rows_affected) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::Db; + + #[tokio::test] + async fn test_node_operations() { + let db = Db::memory_db().await; + + let req = CreateNodeRequest { + name: "Test Node".to_string(), + host: "test.example.com".to_string(), + port: 11010, + protocol: "tcp".to_string(), + description: Some("Test node".to_string()), + max_connections: 100, + allow_relay: false, + network_name: "test-network".to_string(), + network_secret: Some("test-secret".to_string()), + qq_number: Some("123456789".to_string()), + wechat: Some("test_wechat".to_string()), + mail: Some("test@example.com".to_string()), + }; + + // 测试创建节点 + let node = NodeOperations::create_node(&db, req).await.unwrap(); + + assert_eq!(node.name, "Test Node"); + assert_eq!(node.host, "test.example.com"); + assert_eq!(node.port, 11010); + assert!(node.is_active); + + // 测试获取节点 + let found_node = NodeOperations::get_node_by_id(&db, node.id).await.unwrap(); + assert!(found_node.is_some()); + assert_eq!(found_node.unwrap().id, node.id); + + // 测试获取所有节点 + let all_nodes = NodeOperations::get_all_nodes(&db).await.unwrap(); + assert_eq!(all_nodes.len(), 1); + + // 测试节点存在性检查 + let exists = NodeOperations::node_exists(&db, "test.example.com", 11010, "tcp") + .await + .unwrap(); + assert!(exists); + + let not_exists = NodeOperations::node_exists(&db, "nonexistent.com", 8080, "tcp") + .await + .unwrap(); + assert!(!not_exists); + } + + #[tokio::test] + async fn test_health_operations() { + let db = Db::memory_db().await; + + let req = CreateNodeRequest { + name: "Test Node".to_string(), + host: "test.example.com".to_string(), + port: 11010, + protocol: "tcp".to_string(), + description: Some("Test node".to_string()), + max_connections: 100, + allow_relay: false, + network_name: "test-network".to_string(), + network_secret: Some("test-secret".to_string()), + qq_number: Some("123456789".to_string()), + wechat: Some("test_wechat".to_string()), + mail: Some("test@example.com".to_string()), + }; + + // 创建测试节点 + let node = NodeOperations::create_node(&db, req).await.unwrap(); + // 测试创建健康记录 + let record = HealthOperations::create_health_record( + &db, + node.id, + HealthStatus::Healthy, + Some(100), + None, + ) + .await + .unwrap(); + + assert_eq!(record.node_id, node.id); + assert!(record.is_healthy()); + assert_eq!(record.response_time, 100); + + // 测试获取健康记录 + let records = HealthOperations::get_node_health_records(&db, node.id, None, None) + .await + .unwrap(); + assert_eq!(records.len(), 1); + + // 测试获取最新状态 + let latest = HealthOperations::get_latest_health_status(&db, node.id) + .await + .unwrap(); + assert!(latest.is_some()); + assert_eq!(latest.unwrap().id, record.id); + + // 测试健康统计 + let stats = HealthOperations::get_health_stats(&db, node.id, 24) + .await + .unwrap(); + assert_eq!(stats.total_checks, 1); + assert_eq!(stats.healthy_count, 1); + assert_eq!(stats.health_percentage, 100.0); + } +} diff --git a/easytier-contrib/easytier-uptime/src/health_checker.rs b/easytier-contrib/easytier-uptime/src/health_checker.rs new file mode 100644 index 000000000..1d443446d --- /dev/null +++ b/easytier-contrib/easytier-uptime/src/health_checker.rs @@ -0,0 +1,660 @@ +use std::{ + ops::{DerefMut, Div}, + sync::Arc, + time::{Duration, Instant}, +}; + +use anyhow::Context as _; +use dashmap::DashMap; +use easytier::{ + common::{ + config::{ConfigLoader, NetworkIdentity, PeerConfig, TomlConfigLoader}, + scoped_task::ScopedTask, + }, + defer, + instance_manager::NetworkInstanceManager, + launcher::ConfigSource, +}; +use serde::{Deserialize, Serialize}; +use sqlx::any; +use tracing::{debug, error, info, instrument, warn}; + +use crate::db::{ + entity::shared_nodes, + operations::{HealthOperations, NodeOperations}, + Db, HealthStatus, +}; + +pub struct HealthCheckOneNode { + node_id: String, +} + +const HEALTH_CHECK_RING_GRANULARITY_SEC: usize = 60 * 15; // 15分钟 +const HEALTH_CHECK_RING_MAX_DURATION_SEC: usize = 60 * 60 * 24; // 最多一天 + +// const HEALTH_CHECK_RING_GRANULARITY_SEC: usize = 10; +// const HEALTH_CHECK_RING_MAX_DURATION_SEC: usize = 60; + +const HEALTH_CHECK_RING_SIZE: usize = + HEALTH_CHECK_RING_MAX_DURATION_SEC / HEALTH_CHECK_RING_GRANULARITY_SEC; + +#[derive(Debug, Default, Clone)] +struct RingItem { + counter: u64, + round: u64, +} + +impl RingItem { + fn try_update_round(&mut self, timestamp: u64) { + let cur_round = + timestamp.div((HEALTH_CHECK_RING_GRANULARITY_SEC * HEALTH_CHECK_RING_SIZE) as u64); + if self.round != cur_round { + self.round = cur_round; + self.counter = 0; + } + } + + fn inc(&mut self, timestamp: u64) { + self.try_update_round(timestamp); + self.counter += 1; + } + + fn get(&mut self, timestamp: u64) -> u64 { + self.try_update_round(timestamp); + self.counter + } +} + +#[derive(Debug, Clone)] +pub struct HealthyMemRecord { + node_id: i32, + current_health_status: HealthStatus, + last_error_info: Option, + last_check_time: chrono::DateTime, + last_response_time: Option, + + // the current time is corresponding to the index by modulo with UNIX-timestamp. + total_check_counter_ring: Vec, + healthy_counter_ring: Vec, +} + +impl HealthyMemRecord { + pub fn new(node_id: i32) -> Self { + Self { + node_id, + current_health_status: HealthStatus::Unknown, + last_error_info: None, + last_check_time: chrono::Utc::now(), + last_response_time: None, + total_check_counter_ring: vec![Default::default(); HEALTH_CHECK_RING_SIZE], + healthy_counter_ring: vec![Default::default(); HEALTH_CHECK_RING_SIZE], + } + } + + /// 从数据库记录初始化内存记录 + pub fn from_db_records( + node_id: i32, + records: &[crate::db::entity::health_records::Model], + ) -> Self { + let mut mem_record = Self::new(node_id); + + if let Some(latest) = records.first() { + mem_record.current_health_status = latest.get_status(); + mem_record.last_check_time = latest.checked_at.to_utc(); + mem_record.last_response_time = if latest.response_time == 0 { + None + } else { + Some(latest.response_time) + }; + mem_record.last_error_info = if latest.error_message.is_empty() { + None + } else { + Some(latest.error_message.clone()) + }; + } + + // 填充环形缓冲区 + mem_record.populate_ring_from_records(records); + mem_record + } + + /// 从历史记录填充环形缓冲区 + fn populate_ring_from_records(&mut self, records: &[crate::db::entity::health_records::Model]) { + let now = chrono::Utc::now().timestamp() as usize; + + for record in records { + let record_time = record.checked_at.to_utc().timestamp() as usize; + let time_diff = now.saturating_sub(record_time); + + // 只处理在环形缓冲区时间范围内的记录 + if time_diff < HEALTH_CHECK_RING_MAX_DURATION_SEC { + let ring_index = + (record_time / HEALTH_CHECK_RING_GRANULARITY_SEC) % HEALTH_CHECK_RING_SIZE; + self.total_check_counter_ring[ring_index].inc(record_time as u64); + + if record.get_status() == HealthStatus::Healthy { + self.healthy_counter_ring[ring_index].inc(record_time as u64); + } + } + } + } + + /// 更新健康状态并记录到环形缓冲区 + pub fn update_health_status( + &mut self, + status: HealthStatus, + response_time: Option, + error_message: Option, + ) { + self.current_health_status = status.clone(); + self.last_check_time = chrono::Utc::now(); + self.last_response_time = response_time; + self.last_error_info = error_message; + + // 更新环形缓冲区 + let now = chrono::Utc::now().timestamp() as usize; + let ring_index = (now / HEALTH_CHECK_RING_GRANULARITY_SEC) % HEALTH_CHECK_RING_SIZE; + + self.total_check_counter_ring[ring_index].inc(now as u64); + self.healthy_counter_ring[ring_index].try_update_round(now as u64); + if status == HealthStatus::Healthy { + self.healthy_counter_ring[ring_index].inc(now as u64); + } + } + + /// 获取健康统计信息 + pub fn get_health_stats(&self, hours: u64) -> crate::db::HealthStats { + let now = chrono::Utc::now().timestamp() as usize; + + let mut total_checks = 0; + let mut healthy_count = 0; + + for ring_index in 0..HEALTH_CHECK_RING_SIZE { + total_checks += self.total_check_counter_ring[ring_index].counter; + healthy_count += self.healthy_counter_ring[ring_index].counter; + } + + let health_percentage = if total_checks > 0 { + (healthy_count as f64 / total_checks as f64) * 100.0 + } else { + 0.0 + }; + + crate::db::HealthStats { + total_checks, + healthy_count, + unhealthy_count: total_checks - healthy_count, + health_percentage, + average_response_time: self.last_response_time.map(|rt| rt as f64), + uptime_percentage: health_percentage, + last_check_time: Some(self.last_check_time), + last_status: Some(self.current_health_status.clone()), + } + } + + /// 获取当前健康状态 + pub fn get_current_health_status(&self) -> &HealthStatus { + &self.current_health_status + } + + /// 获取最后检查时间 + pub fn get_last_check_time(&self) -> chrono::DateTime { + self.last_check_time + } + + /// 获取最后响应时间 + pub fn get_last_response_time(&self) -> Option { + self.last_response_time + } + + /// 获取最后错误信息 + pub fn get_last_error_info(&self) -> &Option { + &self.last_error_info + } + + pub fn get_counter_ring(&mut self) -> (Vec, Vec) { + let now = self.last_check_time.timestamp() as usize; + + let mut total_ring = vec![0; HEALTH_CHECK_RING_SIZE]; + let mut healthy_ring = vec![0; HEALTH_CHECK_RING_SIZE]; + + let mut total_checks = 0; + let mut healthy_count = 0; + + for i in 0..HEALTH_CHECK_RING_SIZE { + let ring_time = now - (i * HEALTH_CHECK_RING_GRANULARITY_SEC); + let ring_index = + ring_time.div_euclid(HEALTH_CHECK_RING_GRANULARITY_SEC) % HEALTH_CHECK_RING_SIZE; + total_ring[i] = self.total_check_counter_ring[ring_index].get(ring_time as u64); + healthy_ring[i] = self.healthy_counter_ring[ring_index].counter; + } + + (total_ring, healthy_ring) + } + + pub fn get_ring_granularity(&self) -> u32 { + HEALTH_CHECK_RING_GRANULARITY_SEC as u32 + } +} + +pub struct HealthChecker { + db: Db, + instance_mgr: Arc, + inst_id_map: DashMap, + node_tasks: DashMap>, + node_records: Arc>, + node_cfg: Arc>, +} + +impl HealthChecker { + pub fn new(db: Db) -> Self { + let instance_mgr = Arc::new(NetworkInstanceManager::new()); + Self { + db, + instance_mgr, + inst_id_map: DashMap::new(), + node_tasks: DashMap::new(), + node_records: Arc::new(DashMap::new()), + node_cfg: Arc::new(DashMap::new()), + } + } + + /// 启动时从数据库加载所有节点的健康记录到内存 + pub async fn load_health_records_from_db(&self) -> anyhow::Result<()> { + info!("Loading health records from database..."); + + // 获取所有活跃节点 + let nodes = NodeOperations::get_all_nodes(&self.db) + .await + .with_context(|| "Failed to get all nodes from database")?; + + let from_date = chrono::Utc::now().naive_utc() + - chrono::Duration::seconds(HEALTH_CHECK_RING_MAX_DURATION_SEC as i64); + + for node in nodes { + // 获取每个节点最近的健康记录(用于初始化环形缓冲区) + let records = + HealthOperations::get_node_health_records(&self.db, node.id, Some(from_date), None) + .await + .with_context(|| { + format!("Failed to get health records for node {}", node.id) + })?; + + // 创建内存记录 + let mem_record = HealthyMemRecord::from_db_records(node.id, &records); + self.node_records.insert(node.id, mem_record); + + debug!( + "Loaded {} health records for node {} ({})", + records.len(), + node.id, + node.name + ); + } + + info!( + "Loaded health records for {} nodes", + self.node_records.len() + ); + Ok(()) + } + + /// 获取节点的内存健康记录 + pub fn get_node_memory_record(&self, node_id: i32) -> Option { + self.node_records.get(&node_id).map(|entry| entry.clone()) + } + + /// 获取节点的健康统计信息(从内存) + pub fn get_node_health_stats( + &self, + node_id: i32, + hours: u64, + ) -> Option { + self.node_records + .get(&node_id) + .map(|record| record.get_health_stats(hours)) + } + + /// 获取所有节点的当前健康状态(从内存) + pub fn get_all_nodes_health_status(&self) -> Vec<(i32, HealthStatus, Option)> { + self.node_records + .iter() + .map(|entry| { + let record = entry.value(); + ( + record.node_id, + record.current_health_status.clone(), + record.last_error_info.clone(), + ) + }) + .collect() + } + + pub async fn try_update_node(&self, node_id: i32) -> anyhow::Result<()> { + let old_cfg = self + .node_cfg + .get(&node_id) + .ok_or_else(|| anyhow::anyhow!("old node cfg not found, node_id: {}", node_id))? + .clone(); + let new_cfg = self.get_node_cfg(node_id, Some(old_cfg.get_id())).await?; + + if new_cfg.dump() != old_cfg.dump() { + self.remove_node(node_id).await?; + self.add_node(node_id).await?; + info!("node {} cfg updated", node_id); + } + + Ok(()) + } + + async fn get_node_cfg_with_model( + &self, + node_info: &shared_nodes::Model, + inst_id: Option, + ) -> anyhow::Result { + let cfg = TomlConfigLoader::default(); + cfg.set_peers(vec![PeerConfig { + uri: format!( + "{}://{}:{}", + node_info.protocol, node_info.host, node_info.port + ) + .parse() + .with_context(|| "failed to parse peer uri")?, + }]); + + let inst_id = inst_id.unwrap_or(uuid::Uuid::new_v4()); + cfg.set_id(inst_id); + cfg.set_network_identity(NetworkIdentity::new( + node_info.network_name.clone(), + node_info.network_secret.clone(), + )); + + cfg.set_hostname(Some("HealthCheckNode".to_string())); + + let mut flags = cfg.get_flags(); + flags.no_tun = true; + flags.disable_p2p = true; + flags.disable_udp_hole_punching = true; + cfg.set_flags(flags); + + Ok(cfg) + } + + pub async fn test_connection( + &self, + node_info: &shared_nodes::Model, + max_time: Duration, + ) -> anyhow::Result<()> { + let cfg = self.get_node_cfg_with_model(node_info, None).await?; + defer!({ + let _ = self + .instance_mgr + .delete_network_instance(vec![cfg.get_id()]); + }); + self.instance_mgr + .run_network_instance(cfg.clone(), ConfigSource::FFI) + .with_context(|| "failed to run network instance")?; + + let now = Instant::now(); + let mut err = None; + while now.elapsed() < max_time { + match Self::test_node_healthy(cfg.get_id(), self.instance_mgr.clone()).await { + Ok(_) => { + return Ok(()); + } + Err(e) => { + warn!( + "test node healthy failed, node_info: {:?}, err: {}", + node_info, e + ); + err = Some(e); + } + } + tokio::time::sleep(Duration::from_millis(100)).await; + } + Err(anyhow::anyhow!("test node healthy failed, err: {:?}", err)) + } + + async fn get_node_cfg( + &self, + node_id: i32, + inst_id: Option, + ) -> anyhow::Result { + let node_info = NodeOperations::get_node_by_id(&self.db, node_id) + .await + .with_context(|| format!("failed to get node by id: {}", node_id))? + .ok_or_else(|| anyhow::anyhow!("node not found"))?; + self.get_node_cfg_with_model(&node_info, inst_id).await + } + + pub async fn add_node(&self, node_id: i32) -> anyhow::Result<()> { + let cfg = self.get_node_cfg(node_id, None).await?; + info!( + "Add node {} to health checker, cfg: {}", + node_id, + cfg.dump() + ); + + self.instance_mgr + .run_network_instance(cfg.clone(), ConfigSource::Web) + .with_context(|| "failed to run network instance")?; + self.inst_id_map.insert(node_id, cfg.get_id()); + + // 初始化内存记录(如果不存在) + if !self.node_records.contains_key(&node_id) { + // 从数据库加载历史记录 + let from_date = chrono::Utc::now().naive_utc() + - chrono::Duration::seconds(HEALTH_CHECK_RING_MAX_DURATION_SEC as i64); + if let Ok(records) = + HealthOperations::get_node_health_records(&self.db, node_id, Some(from_date), None) + .await + { + let mem_record = HealthyMemRecord::from_db_records(node_id, &records); + self.node_records.insert(node_id, mem_record); + info!( + "Initialized memory record for node {} with {} historical records", + node_id, + records.len() + ); + } else { + self.node_records + .insert(node_id, HealthyMemRecord::new(node_id)); + info!("Initialized new memory record for node {}", node_id); + } + } + + // 启动健康检查任务 + let task = ScopedTask::from(tokio::spawn(Self::node_health_check_task( + node_id, + cfg.get_id(), + Arc::clone(&self.instance_mgr), + self.db.clone(), + Arc::clone(&self.node_records), + ))); + self.node_tasks.insert(node_id, task); + self.node_cfg.insert(node_id, cfg.clone()); + + Ok(()) + } + + pub async fn remove_node(&self, node_id: i32) -> anyhow::Result<()> { + self.node_tasks.remove(&node_id); + if let Some(inst_id) = self.inst_id_map.remove(&node_id) { + let _ = self.instance_mgr.delete_network_instance(vec![inst_id.1]); + } + self.node_cfg.remove(&node_id); + // 保留内存记录,不删除,以便后续查询历史数据 + info!( + "Removed health check task for node {}, memory record retained", + node_id + ); + Ok(()) + } + + #[instrument(err, ret, skip(instance_mgr))] + async fn test_node_healthy( + inst_id: uuid::Uuid, + instance_mgr: Arc, + // return version, response time on healthy, conn_count + ) -> anyhow::Result<(String, u64, u32)> { + let Some(instance) = instance_mgr.get_network_info(&inst_id).await else { + anyhow::bail!("healthy check node is not started"); + }; + + let running = instance.running; + // health check node is not running, update db + if !running { + anyhow::bail!("healthy check node is not running"); + } + + if let Some(err) = instance.error_msg { + anyhow::bail!("healthy check node has error: {}", err); + } + + let p = instance.peer_route_pairs; + // dst node is not online + let Some(dst_node) = p.iter().find(|x| { + // we disable p2p, so we only check direct connected peer + x.route.as_ref().is_some_and(|route| { + !route.feature_flag.unwrap().is_public_server && route.hostname != "HealthCheckNode" + }) && x.peer.as_ref().is_some_and(|p| !p.conns.is_empty()) + }) else { + anyhow::bail!("dst node is not online"); + }; + + let Some(route_info) = &dst_node.route else { + anyhow::bail!("dst node route is not found"); + }; + + let Some(peer_info) = &dst_node.peer else { + anyhow::bail!("dst node peer is not found"); + }; + + let version = route_info + .version + .clone() + .split("-") + .next() + .unwrap_or("") + .to_string(); + + // 计算响应时间(这里可以根据实际需要实现) + let response_time = peer_info + .conns + .iter() + .filter_map(|x| x.stats) + .map(|x| x.latency_us) + .min() + .unwrap_or(0); + + let peer_id = peer_info.peer_id; + + let conn_count = if let Some(summary) = instance.foreign_network_summary { + summary + .info_map + .get(&peer_id) + .map(|x| x.network_count) + .unwrap_or(0) + } else { + 0 + }; + + Ok((version, response_time, conn_count)) + } + + async fn node_health_check_task( + node_id: i32, + inst_id: uuid::Uuid, + instance_mgr: Arc, + db: Db, + node_records: Arc>, + ) { + /// 记录健康状态到数据库和内存 + async fn record_health_status( + db: &Db, + node_records: &Arc>, + node_id: i32, + status: HealthStatus, + response_time: Option, + error_message: Option, + ) { + // 写入数据库 + if let Err(e) = HealthOperations::create_health_record( + db, + node_id, + status.clone(), + response_time, + error_message.clone(), + ) + .await + { + error!("Failed to create health record for node {}: {}", node_id, e); + } + + // 更新内存记录 + if let Some(mut record) = node_records.get_mut(&node_id) { + record.update_health_status(status, response_time, error_message); + } else { + let mut new_record = HealthyMemRecord::new(node_id); + new_record.update_health_status(status, response_time, error_message); + node_records.insert(node_id, new_record); + } + } + let mut tick = tokio::time::interval(Duration::from_secs(5)); + let mut counter: u64 = 0; + loop { + if counter != 0 { + tick.tick().await; + } + counter += 1; + + match Self::test_node_healthy(inst_id, instance_mgr.clone()).await { + Ok((version, response_time, conn_count)) => { + if let Err(e) = NodeOperations::update_node_status( + &db, + node_id, + true, + Some(conn_count as i32), + ) + .await + { + error!("Failed to update node status for node {}: {}", node_id, e); + } + + record_health_status( + &db, + &node_records, + node_id, + HealthStatus::Healthy, + Some(response_time as i32), + None, + ) + .await; + + // update node version + if let Err(e) = NodeOperations::update_node_version(&db, node_id, version).await + { + error!("Failed to update node version for node {}: {}", node_id, e); + } + } + Err(e) => { + if let Err(e) = + NodeOperations::update_node_status(&db, node_id, false, None).await + { + error!("Failed to update node status for node {}: {}", node_id, e); + } + + record_health_status( + &db, + &node_records, + node_id, + HealthStatus::Unhealthy, + None, + Some(format!("inst id: {}, err: {}", inst_id, e)), + ) + .await; + } + } + } + } +} diff --git a/easytier-contrib/easytier-uptime/src/health_checker_manager.rs b/easytier-contrib/easytier-uptime/src/health_checker_manager.rs new file mode 100644 index 000000000..fe26f0556 --- /dev/null +++ b/easytier-contrib/easytier-uptime/src/health_checker_manager.rs @@ -0,0 +1,160 @@ +use std::{collections::HashSet, sync::Arc, time::Duration}; + +use anyhow::Context as _; +use tokio::time::{interval, Interval}; +use tracing::{error, info}; + +use crate::{ + db::{entity::shared_nodes, operations::NodeOperations, Db}, + health_checker::HealthChecker, +}; + +/// HealthChecker的封装器,用于监控数据库中节点的添加和删除 +pub struct HealthCheckerManager { + health_checker: Arc, + db: Db, + current_nodes: Arc>>, + monitor_interval: Duration, +} + +impl HealthCheckerManager { + /// 创建新的HealthCheckerManager实例 + pub fn new(health_checker: Arc, db: Db) -> Self { + Self { + health_checker, + db, + current_nodes: Arc::new(tokio::sync::RwLock::new(HashSet::new())), + monitor_interval: Duration::from_secs(1), // 默认每1秒检查一次 + } + } + + /// 设置监控间隔 + pub fn with_monitor_interval(mut self, interval: Duration) -> Self { + self.monitor_interval = interval; + self + } + + /// 启动监控任务 + pub async fn start_monitoring(&self) -> anyhow::Result<()> { + // 启动定期检查任务 + let health_checker = Arc::clone(&self.health_checker); + let db = self.db.clone(); + let current_nodes = Arc::clone(&self.current_nodes); + let monitor_interval = self.monitor_interval; + + tokio::spawn(async move { + let mut ticker = interval(monitor_interval); + loop { + if let Err(e) = Self::check_node_changes(&health_checker, &db, ¤t_nodes).await + { + tracing::error!("Error checking node changes: {}", e); + } + ticker.tick().await; + } + }); + + Ok(()) + } + + /// 检查节点变化并更新监控 + async fn check_node_changes( + health_checker: &Arc, + db: &Db, + current_nodes: &Arc>>, + ) -> anyhow::Result<()> { + // 获取数据库中当前的所有节点 + let db_nodes = NodeOperations::get_all_nodes(db) + .await + .with_context(|| "Failed to get all nodes from database")?; + + let db_node_ids: HashSet = db_nodes.iter().map(|node| node.id).collect(); + + let mut current_nodes_guard = current_nodes.write().await; + + // 检查新增的节点 + for &node_id in &db_node_ids { + if !current_nodes_guard.contains(&node_id) { + // 新节点,添加到监控 + if let Err(e) = health_checker.add_node(node_id).await { + error!("Failed to add node {} to health checker: {}", node_id, e); + continue; + } + current_nodes_guard.insert(node_id); + info!("Added new node {} to health monitoring", node_id); + } else if let Err(e) = health_checker.try_update_node(node_id).await { + error!("Failed to add node {} to health checker: {}", node_id, e); + } + } + + // 检查删除的节点 + let nodes_to_remove: Vec = current_nodes_guard + .iter() + .filter(|&&node_id| !db_node_ids.contains(&node_id)) + .copied() + .collect(); + + for node_id in nodes_to_remove { + // 节点已删除,从监控中移除 + if let Err(e) = health_checker.remove_node(node_id).await { + error!( + "Failed to remove node {} from health checker: {}", + node_id, e + ); + continue; + } + current_nodes_guard.remove(&node_id); + info!("Removed node {} from health monitoring", node_id); + } + + Ok(()) + } + + /// 手动触发节点变化检查 + pub async fn refresh_nodes(&self) -> anyhow::Result<()> { + Self::check_node_changes(&self.health_checker, &self.db, &self.current_nodes).await + } + + /// 获取当前监控的节点数量 + pub async fn get_monitored_node_count(&self) -> usize { + self.current_nodes.read().await.len() + } + + /// 获取当前监控的节点ID列表 + pub async fn get_monitored_nodes(&self) -> Vec { + self.current_nodes.read().await.iter().copied().collect() + } + + /// 获取节点的内存健康记录 + pub fn get_node_memory_record( + &self, + node_id: i32, + ) -> Option { + self.health_checker.get_node_memory_record(node_id) + } + + /// 获取节点的健康统计信息 + pub fn get_node_health_stats( + &self, + node_id: i32, + hours: u64, + ) -> Option { + self.health_checker.get_node_health_stats(node_id, hours) + } + + /// 获取所有节点的当前健康状态 + pub fn get_all_nodes_health_status( + &self, + ) -> Vec<(i32, crate::db::HealthStatus, Option)> { + self.health_checker.get_all_nodes_health_status() + } + + pub async fn test_connection( + &self, + node_info: &shared_nodes::Model, + max_time: Duration, + ) -> anyhow::Result<()> { + self.health_checker + .test_connection(node_info, max_time) + .await + } +} diff --git a/easytier-contrib/easytier-uptime/src/main.rs b/easytier-contrib/easytier-uptime/src/main.rs new file mode 100644 index 000000000..910814c29 --- /dev/null +++ b/easytier-contrib/easytier-uptime/src/main.rs @@ -0,0 +1,153 @@ +#![allow(unused)] + +mod api; +mod config; +mod db; +mod health_checker; +mod health_checker_manager; +mod migrator; + +use api::routes::create_routes; +use clap::Parser; +use config::AppConfig; +use db::{operations::NodeOperations, Db}; +use health_checker::HealthChecker; +use health_checker_manager::HealthCheckerManager; +use std::env; +use std::net::SocketAddr; +use std::path::PathBuf; +use std::sync::Arc; +use std::time::Duration; +use tracing_subscriber::EnvFilter; + +use crate::db::cleanup::{CleanupConfig, CleanupManager}; + +#[derive(Parser, Debug)] +#[command(author, version, about, long_about = None)] +struct Args { + /// Admin password for management access + #[arg(long, env = "ADMIN_PASSWORD")] + admin_password: Option, +} + +#[tokio::main] +async fn main() -> anyhow::Result<()> { + // 加载配置 + let config = AppConfig::default(); + + // 初始化日志 + tracing_subscriber::fmt() + .with_max_level(match config.logging.level.as_str() { + "debug" => tracing::Level::DEBUG, + "info" => tracing::Level::INFO, + "warn" => tracing::Level::WARN, + "error" => tracing::Level::ERROR, + _ => tracing::Level::INFO, + }) + .with_target(false) + .with_thread_ids(true) + .with_env_filter(EnvFilter::new("easytier_uptime")) + .init(); + + // 解析命令行参数 + let args = Args::parse(); + + // 如果提供了管理员密码,设置环境变量 + if let Some(password) = args.admin_password { + env::set_var("ADMIN_PASSWORD", password); + } + + tracing::info!( + "Admin password configured: {}", + !config.security.admin_password.is_empty() + ); + + // 创建数据库连接 + let db = Db::new(&config.database.path.to_string_lossy()).await?; + + // 获取数据库统计信息 + let stats = db.get_database_stats().await?; + tracing::info!("Database initialized successfully!"); + tracing::info!("Database stats: {:?}", stats); + + // 创建配置目录 + let config_dir = PathBuf::from("./configs"); + tokio::fs::create_dir_all(&config_dir).await?; + + // 创建健康检查器和管理器 + let health_checker = Arc::new(HealthChecker::new(db.clone())); + let health_checker_manager = HealthCheckerManager::new(health_checker, db.clone()) + .with_monitor_interval(Duration::from_secs(1)); // 每30秒检查一次节点变化 + + let cleanup_manager = CleanupManager::new(db.clone(), CleanupConfig::default()); + cleanup_manager.start_auto_cleanup().await?; + + // 启动节点监控 + health_checker_manager.start_monitoring().await?; + tracing::info!("Health checker manager started successfully!"); + + let monitored_count = health_checker_manager.get_monitored_node_count().await; + tracing::info!("Currently monitoring {} nodes", monitored_count); + + // 创建应用状态 + let app_state = crate::api::handlers::AppState { + db: db.clone(), + health_checker_manager: Arc::new(health_checker_manager), + }; + + // 创建 API 路由 + let app = create_routes().with_state(app_state); + + // 配置服务器地址 + let addr = config.server.addr; + + tracing::info!("Starting server on http://{}", addr); + tracing::info!("Available endpoints:"); + tracing::info!(" GET /health - Health check"); + tracing::info!(" GET /api/nodes - Get nodes (paginated, approved only)"); + tracing::info!(" POST /api/nodes - Create node (pending approval)"); + tracing::info!(" GET /api/nodes/:id - Get node by ID"); + tracing::info!(" PUT /api/nodes/:id - Update node"); + tracing::info!(" DELETE /api/nodes/:id - Delete node"); + tracing::info!(" GET /api/nodes/:id/health - Get node health history"); + tracing::info!(" GET /api/nodes/:id/health/stats - Get node health stats"); + tracing::info!("Admin endpoints:"); + tracing::info!(" POST /api/admin/login - Admin login"); + tracing::info!(" GET /api/admin/nodes - Get all nodes (including pending)"); + tracing::info!(" PUT /api/admin/nodes/:id/approve - Approve/reject node"); + tracing::info!(" DELETE /api/admin/nodes/:id - Delete node (admin only)"); + + // 启动服务器 + let listener = tokio::net::TcpListener::bind(addr).await?; + + // 设置优雅关闭 + let shutdown_signal = Arc::new(tokio::sync::Notify::new()); + let server_shutdown_signal = shutdown_signal.clone(); + + // 启动服务器任务 + let server_handle = tokio::spawn(async move { + axum::serve(listener, app) + .with_graceful_shutdown(async move { + server_shutdown_signal.notified().await; + }) + .await + .unwrap(); + }); + + // 等待 Ctrl+C 信号 + tokio::select! { + _ = tokio::signal::ctrl_c() => { + tracing::info!("Received shutdown signal"); + } + _ = server_handle => { + tracing::info!("Server task completed"); + } + } + + // 优雅关闭 + tracing::info!("Shutting down gracefully..."); + shutdown_signal.notify_waiters(); + + tracing::info!("Shutdown complete"); + Ok(()) +} diff --git a/easytier-contrib/easytier-uptime/src/migrator/m20250101_000001_create_tables.rs b/easytier-contrib/easytier-uptime/src/migrator/m20250101_000001_create_tables.rs new file mode 100644 index 000000000..5c88876ee --- /dev/null +++ b/easytier-contrib/easytier-uptime/src/migrator/m20250101_000001_create_tables.rs @@ -0,0 +1,181 @@ +use sea_orm_migration::{prelude::*, schema::*}; + +pub struct Migration; + +impl MigrationName for Migration { + fn name(&self) -> &str { + "m20250101_000001_create_tables" + } +} + +#[derive(DeriveIden)] +pub enum SharedNodes { + Table, + Id, + Name, + Host, + Port, + Protocol, + Version, + AllowRelay, + NetworkName, + NetworkSecret, + Description, + MaxConnections, + CurrentConnections, + IsActive, + IsApproved, + QQNumber, + Wechat, + Mail, + CreatedAt, + UpdatedAt, +} + +#[derive(DeriveIden)] +pub enum HealthRecords { + Table, + Id, + NodeId, + Status, + ResponseTime, + ErrorMessage, + CheckedAt, +} + +#[async_trait::async_trait] +impl MigrationTrait for Migration { + async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { + // 创建共享节点表 + manager + .create_table( + Table::create() + .if_not_exists() + .table(SharedNodes::Table) + .col(pk_auto(SharedNodes::Id).not_null()) + .col(string(SharedNodes::Name).not_null()) + .col(string(SharedNodes::Host).not_null()) + .col(integer(SharedNodes::Port).not_null()) + .col(string(SharedNodes::Protocol).not_null().default("tcp")) + .col(string(SharedNodes::Version)) + .col(boolean(SharedNodes::AllowRelay).default(false)) + .col(string(SharedNodes::NetworkName)) + .col(string(SharedNodes::NetworkSecret)) + .col(text(SharedNodes::Description)) + .col(integer(SharedNodes::MaxConnections).default(100)) + .col(integer(SharedNodes::CurrentConnections).default(0)) + .col(boolean(SharedNodes::IsActive).default(true)) + .col(boolean(SharedNodes::IsApproved).default(false)) + .col(string(SharedNodes::QQNumber)) + .col(string(SharedNodes::Wechat)) + .col(string(SharedNodes::Mail)) + .col( + timestamp_with_time_zone(SharedNodes::CreatedAt) + .default(Expr::current_timestamp()), + ) + .col( + timestamp_with_time_zone(SharedNodes::UpdatedAt) + .default(Expr::current_timestamp()), + ) + .to_owned(), + ) + .await?; + + // 创建唯一约束 + manager + .create_index( + Index::create() + .name("idx_shared_nodes_host_port_protocol") + .table(SharedNodes::Table) + .col(SharedNodes::Host) + .col(SharedNodes::Port) + .col(SharedNodes::Protocol) + .unique() + .to_owned(), + ) + .await?; + + // 创建健康度记录表 + manager + .create_table( + Table::create() + .if_not_exists() + .table(HealthRecords::Table) + .col(pk_auto(HealthRecords::Id).not_null()) + .col(integer(HealthRecords::NodeId).not_null()) + .col(string(HealthRecords::Status).not_null()) + .col(integer(HealthRecords::ResponseTime)) + .col(text(HealthRecords::ErrorMessage).null()) + .col( + timestamp_with_time_zone(HealthRecords::CheckedAt) + .default(Expr::current_timestamp()), + ) + .foreign_key( + ForeignKey::create() + .name("fk_health_records_node_id_to_shared_nodes_id") + .from(HealthRecords::Table, HealthRecords::NodeId) + .to(SharedNodes::Table, SharedNodes::Id) + .on_delete(ForeignKeyAction::Cascade) + .on_update(ForeignKeyAction::Cascade), + ) + .to_owned(), + ) + .await?; + + // 创建健康度记录索引 + manager + .create_index( + Index::create() + .name("idx_health_records_node_id") + .table(HealthRecords::Table) + .col(HealthRecords::NodeId) + .to_owned(), + ) + .await?; + + manager + .create_index( + Index::create() + .name("idx_health_records_checked_at") + .table(HealthRecords::Table) + .col(HealthRecords::CheckedAt) + .to_owned(), + ) + .await?; + + manager + .create_index( + Index::create() + .name("idx_health_records_node_time") + .table(HealthRecords::Table) + .col(HealthRecords::NodeId) + .col(HealthRecords::CheckedAt) + .to_owned(), + ) + .await?; + + manager + .create_index( + Index::create() + .name("idx_health_records_status") + .table(HealthRecords::Table) + .col(HealthRecords::Status) + .to_owned(), + ) + .await?; + + Ok(()) + } + + async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .drop_table(Table::drop().table(HealthRecords::Table).to_owned()) + .await?; + + manager + .drop_table(Table::drop().table(SharedNodes::Table).to_owned()) + .await?; + + Ok(()) + } +} diff --git a/easytier-contrib/easytier-uptime/src/migrator/mod.rs b/easytier-contrib/easytier-uptime/src/migrator/mod.rs new file mode 100644 index 000000000..1d492e9e6 --- /dev/null +++ b/easytier-contrib/easytier-uptime/src/migrator/mod.rs @@ -0,0 +1,12 @@ +use sea_orm_migration::prelude::*; + +mod m20250101_000001_create_tables; + +pub struct Migrator; + +#[async_trait::async_trait] +impl MigratorTrait for Migrator { + fn migrations() -> Vec> { + vec![Box::new(m20250101_000001_create_tables::Migration)] + } +} diff --git a/easytier-contrib/easytier-uptime/start-dev.sh b/easytier-contrib/easytier-uptime/start-dev.sh new file mode 100755 index 000000000..b336b973b --- /dev/null +++ b/easytier-contrib/easytier-uptime/start-dev.sh @@ -0,0 +1,95 @@ +#!/bin/bash + +# EasyTier Uptime Monitor 开发环境启动脚本 + +set -e + +echo "🚀 Starting EasyTier Uptime Monitor Development Environment..." + +# 检查依赖 +echo "📦 Checking dependencies..." + +# 检查 Rust +if ! command -v cargo &> /dev/null; then + echo "❌ Rust is not installed. Please install Rust first." + exit 1 +fi + +# 检查 Node.js +if ! command -v node &> /dev/null; then + echo "❌ Node.js is not installed. Please install Node.js first." + exit 1 +fi + +# 检查 npm +if ! command -v npm &> /dev/null; then + echo "❌ npm is not installed. Please install npm first." + exit 1 +fi + +# 设置环境变量 +export RUST_LOG=debug +export NODE_ENV=development + +# 创建必要的目录 +echo "📁 Creating directories..." +mkdir -p logs +mkdir -p configs +mkdir -p frontend/dist + +# 复制环境配置文件 +if [ ! -f .env ]; then + echo "📝 Creating environment configuration..." + cp .env.development .env +fi + +# 安装前端依赖 +echo "📦 Installing frontend dependencies..." +cd frontend +if [ ! -d "node_modules" ]; then + npm install +fi +cd .. + +# 启动后端服务 +echo "🔧 Starting backend server..." +cargo run & +BACKEND_PID=$! + +# 等待后端服务启动 +echo "⏳ Waiting for backend server to start..." +sleep 5 + +# 启动前端开发服务器 +echo "🎨 Starting frontend development server..." +cd frontend +npm run dev & +FRONTEND_PID=$! +cd .. + +# 等待前端服务启动 +echo "⏳ Waiting for frontend server to start..." +sleep 3 + +echo "✅ Development environment started successfully!" +echo "🌐 Frontend: http://localhost:3000" +echo "🔧 Backend API: http://localhost:8080" +echo "📊 API Health Check: http://localhost:8080/health" +echo "" +echo "Press Ctrl+C to stop all services" + +# 清理函数 +cleanup() { + echo "" + echo "🛑 Stopping services..." + kill $BACKEND_PID 2>/dev/null || true + kill $FRONTEND_PID 2>/dev/null || true + echo "✅ All services stopped" + exit 0 +} + +# 设置信号处理 +trap cleanup SIGINT SIGTERM + +# 等待用户中断 +wait \ No newline at end of file diff --git a/easytier-contrib/easytier-uptime/start-prod.sh b/easytier-contrib/easytier-uptime/start-prod.sh new file mode 100755 index 000000000..67152fa5a --- /dev/null +++ b/easytier-contrib/easytier-uptime/start-prod.sh @@ -0,0 +1,98 @@ +#!/bin/bash + +# EasyTier Uptime Monitor 生产环境启动脚本 + +set -e + +echo "🚀 Starting EasyTier Uptime Monitor Production Environment..." + +# 检查依赖 +echo "📦 Checking dependencies..." + +# 检查 Rust +if ! command -v cargo &> /dev/null; then + echo "❌ Rust is not installed. Please install Rust first." + exit 1 +fi + +# 检查 Node.js +if ! command -v node &> /dev/null; then + echo "❌ Node.js is not installed. Please install Node.js first." + exit 1 +fi + +# 检查 npm +if ! command -v npm &> /dev/null; then + echo "❌ npm is not installed. Please install npm first." + exit 1 +fi + +# 设置环境变量 +export RUST_LOG=info +export NODE_ENV=production + +# 创建必要的目录 +echo "📁 Creating directories..." +mkdir -p logs +mkdir -p configs +mkdir -p /var/lib/easytier-uptime +mkdir -p frontend/dist + +# 复制环境配置文件 +if [ ! -f .env ]; then + echo "📝 Creating environment configuration..." + cp .env.production .env +fi + +# 构建后端 +echo "🔧 Building backend..." +cargo build --release + +# 构建前端 +echo "🎨 Building frontend..." +cd frontend +if [ ! -d "node_modules" ]; then + npm install +fi +npm run build +cd .. + +# 启动后端服务 +echo "🔧 Starting backend server..." +nohup ./target/release/easytier-uptime > logs/backend.log 2>&1 & +BACKEND_PID=$! + +# 等待后端服务启动 +echo "⏳ Waiting for backend server to start..." +sleep 5 + +# 设置静态文件服务 +echo "🌐 Setting up static file server..." +cd frontend/dist +python3 -m http.server 8081 > ../../logs/frontend.log 2>&1 & +FRONTEND_PID=$! +cd ../.. + +# 等待前端服务启动 +echo "⏳ Waiting for frontend server to start..." +sleep 3 + +echo "✅ Production environment started successfully!" +echo "🌐 Frontend: http://localhost:8081" +echo "🔧 Backend API: http://localhost:8080" +echo "📊 API Health Check: http://localhost:8080/health" +echo "" +echo "Backend PID: $BACKEND_PID" +echo "Frontend PID: $FRONTEND_PID" +echo "" +echo "To stop services:" +echo " kill $BACKEND_PID" +echo " kill $FRONTEND_PID" +echo "" +echo "Or use the stop script: ./stop-prod.sh" + +# 保存PID到文件 +echo $BACKEND_PID > logs/backend.pid +echo $FRONTEND_PID > logs/frontend.pid + +echo "✅ PIDs saved to logs/" \ No newline at end of file diff --git a/easytier-contrib/easytier-uptime/stop-prod.sh b/easytier-contrib/easytier-uptime/stop-prod.sh new file mode 100755 index 000000000..69adc99cb --- /dev/null +++ b/easytier-contrib/easytier-uptime/stop-prod.sh @@ -0,0 +1,46 @@ +#!/bin/bash + +# EasyTier Uptime Monitor 停止服务脚本 + +set -e + +echo "🛑 Stopping EasyTier Uptime Monitor services..." + +# 检查PID文件 +if [ -f "logs/backend.pid" ]; then + BACKEND_PID=$(cat logs/backend.pid) + echo "🔧 Stopping backend server (PID: $BACKEND_PID)..." + kill $BACKEND_PID 2>/dev/null || true + rm logs/backend.pid + echo "✅ Backend server stopped" +else + echo "⚠️ Backend PID file not found" +fi + +if [ -f "logs/frontend.pid" ]; then + FRONTEND_PID=$(cat logs/frontend.pid) + echo "🌐 Stopping frontend server (PID: $FRONTEND_PID)..." + kill $FRONTEND_PID 2>/dev/null || true + rm logs/frontend.pid + echo "✅ Frontend server stopped" +else + echo "⚠️ Frontend PID file not found" +fi + +# 强制杀死可能残留的进程 +echo "🔍 Checking for remaining processes..." +REMAINING_BACKEND=$(ps aux | grep 'easytier-uptime' | grep -v grep | awk '{print $2}' || true) +if [ ! -z "$REMAINING_BACKEND" ]; then + echo "🔧 Killing remaining backend processes..." + echo $REMAINING_BACKEND | xargs kill -9 2>/dev/null || true + echo "✅ Remaining backend processes killed" +fi + +REMAINING_FRONTEND=$(ps aux | grep 'python3 -m http.server' | grep -v grep | awk '{print $2}' || true) +if [ ! -z "$REMAINING_FRONTEND" ]; then + echo "🌐 Killing remaining frontend processes..." + echo $REMAINING_FRONTEND | xargs kill -9 2>/dev/null || true + echo "✅ Remaining frontend processes killed" +fi + +echo "✅ All services stopped successfully!" \ No newline at end of file diff --git a/easytier-contrib/easytier-uptime/test-integration.sh b/easytier-contrib/easytier-uptime/test-integration.sh new file mode 100755 index 000000000..6865994d1 --- /dev/null +++ b/easytier-contrib/easytier-uptime/test-integration.sh @@ -0,0 +1,144 @@ +#!/bin/bash + +# EasyTier Uptime Monitor 集成测试脚本 + +set -e + +echo "🧪 Running EasyTier Uptime Monitor Integration Tests..." + +# 检查依赖 +echo "📦 Checking dependencies..." + +# 检查 Rust +if ! command -v cargo &> /dev/null; then + echo "❌ Rust is not installed. Please install Rust first." + exit 1 +fi + +# 检查 curl +if ! command -v curl &> /dev/null; then + echo "❌ curl is not installed. Please install curl first." + exit 1 +fi + +# 设置环境变量 +export RUST_LOG=info +export NODE_ENV=test + +# 创建测试目录 +echo "📁 Creating test directories..." +mkdir -p test-results +mkdir -p test-logs + +# 复制测试环境配置 +if [ ! -f .env ]; then + echo "📝 Creating test environment configuration..." + cp .env.development .env +fi + +# 构建项目 +echo "🔧 Building project..." +cargo build + +# 启动后端服务进行测试 +echo "🚀 Starting backend server for testing..." +cargo run & +BACKEND_PID=$! + +# 等待后端服务启动 +echo "⏳ Waiting for backend server to start..." +sleep 5 + +# 检查服务是否运行 +echo "🔍 Checking if server is running..." +if curl -f http://localhost:8080/health > /dev/null 2>&1; then + echo "✅ Backend server is running" +else + echo "❌ Backend server failed to start" + kill $BACKEND_PID 2>/dev/null || true + exit 1 +fi + +# 运行API测试 +echo "🧪 Running API tests..." +if cargo test api_test --lib -- --nocapture > test-results/api-test.log 2>&1; then + echo "✅ API tests passed" +else + echo "❌ API tests failed" + echo "Check test-results/api-test.log for details" +fi + +# 运行健康检查测试 +echo "🏥 Running health check tests..." +curl -s http://localhost:8080/health | jq . > test-results/health-check.json +if [ $? -eq 0 ]; then + echo "✅ Health check test passed" +else + echo "❌ Health check test failed" +fi + +# 运行节点管理测试 +echo "🔧 Running node management tests..." +# 创建测试节点 +curl -s -X POST http://localhost:8080/api/nodes \ + -H "Content-Type: application/json" \ + -d '{ + "name": "Test Node", + "host": "127.0.0.1", + "port": 11010, + "protocol": "tcp", + "version": "1.0.0", + "description": "Test node for integration testing", + "max_connections": 100 + }' > test-results/create-node.json + +# 获取节点列表 +curl -s http://localhost:8080/api/nodes > test-results/get-nodes.json + +echo "✅ Node management tests completed" + +# 停止后端服务 +echo "🛑 Stopping backend server..." +kill $BACKEND_PID 2>/dev/null || true +sleep 2 + +# 强制杀死可能残留的进程 +pkill -f easytier-uptime 2>/dev/null || true + +echo "✅ Integration tests completed!" +echo "📊 Test results saved to test-results/" +echo "📋 Test logs saved to test-logs/" + +# 生成测试报告 +echo "📝 Generating test report..." +cat > test-results/test-report.md << EOF +# EasyTier Uptime Monitor Integration Test Report + +## Test Summary +- **Test Date**: $(date) +- **Test Environment**: Integration +- **Backend PID**: $BACKEND_PID + +## Test Results + +### API Tests +- Status: $(grep -q "test result: ok" test-results/api-test.log && echo "PASSED" || echo "FAILED") +- Log: [api-test.log](api-test.log) + +### Health Check +- Status: $(jq -r '.success' test-results/health-check.json 2>/dev/null || echo "FAILED") +- Response: $(cat test-results/health-check.json 2>/dev/null || echo "No response") + +### Node Management +- Status: COMPLETED +- Create Node: [create-node.json](create-node.json) +- Get Nodes: [get-nodes.json](get-nodes.json) + +## System Information +- **Rust Version**: $(rustc --version) +- **Cargo Version**: $(cargo --version) +- **System**: $(uname -a) + +EOF + +echo "✅ Test report generated: test-results/test-report.md" \ No newline at end of file diff --git a/easytier-contrib/easytier-uptime/test_health_stats.rs b/easytier-contrib/easytier-uptime/test_health_stats.rs new file mode 100644 index 000000000..863286b9a --- /dev/null +++ b/easytier-contrib/easytier-uptime/test_health_stats.rs @@ -0,0 +1,66 @@ +//! 测试 HealthyStats 功能的示例代码 + +use easytier_uptime::db::entity::health_records::{HealthStatus, HealthStats, Model}; +use sea_orm::prelude::*; + +fn main() { + // 创建一些模拟的健康记录 + let records = vec![ + Model { + id: 1, + node_id: 1, + status: HealthStatus::Healthy.to_string(), + response_time: 100, + error_message: String::new(), + checked_at: chrono::Utc::now().fixed_offset(), + }, + Model { + id: 2, + node_id: 1, + status: HealthStatus::Healthy.to_string(), + response_time: 150, + error_message: String::new(), + checked_at: chrono::Utc::now().fixed_offset(), + }, + Model { + id: 3, + node_id: 1, + status: HealthStatus::Unhealthy.to_string(), + response_time: 0, + error_message: "Connection failed".to_string(), + checked_at: chrono::Utc::now().fixed_offset(), + }, + ]; + + // 从记录创建统计信息 + let stats = HealthStats::from_records(&records); + + println!("健康统计信息:"); + println!("总检查次数: {}", stats.total_checks); + println!("健康检查次数: {}", stats.healthy_count); + println!("不健康检查次数: {}", stats.unhealthy_count); + println!("健康百分比: {:.2}%", stats.health_percentage); + println!("平均响应时间: {:?} ms", stats.average_response_time); + println!("正常运行时间百分比: {:.2}%", stats.uptime_percentage); + println!("最后检查时间: {:?}", stats.last_check_time); + println!("最后状态: {:?}", stats.last_status); + + // 测试健康状态转换 + println!("\n健康状态测试:"); + let status_healthy = HealthStatus::from("healthy"); + let status_unhealthy = HealthStatus::from("unhealthy"); + let status_timeout = HealthStatus::from("timeout"); + let status_unknown = HealthStatus::from("invalid_status"); + + println!("healthy -> {:?}", status_healthy); + println!("unhealthy -> {:?}", status_unhealthy); + println!("timeout -> {:?}", status_timeout); + println!("invalid_status -> {:?}", status_unknown); + + // 测试记录的健康状态检查 + println!("\n记录健康状态检查:"); + for record in &records { + println!("记录 {} 是否健康: {}", record.id, record.is_healthy()); + println!("记录 {} 状态: {:?}", record.id, record.get_status()); + } +} \ No newline at end of file diff --git a/easytier-gui/.gitignore b/easytier-gui/.gitignore index 3613fa41b..490f6b51a 100644 --- a/easytier-gui/.gitignore +++ b/easytier-gui/.gitignore @@ -25,3 +25,5 @@ dist-ssr *.sw? vite.config.ts.timestamp* + +local.properties \ No newline at end of file diff --git a/easytier-gui/package.json b/easytier-gui/package.json index f91d8fa2e..9df6e4479 100644 --- a/easytier-gui/package.json +++ b/easytier-gui/package.json @@ -1,7 +1,7 @@ { "name": "easytier-gui", "type": "module", - "version": "2.4.2", + "version": "2.4.5", "private": true, "packageManager": "pnpm@9.12.1+sha512.e5a7e52a4183a02d5931057f7a0dbff9d5e9ce3161e33fa68ae392125b79282a8a8a470a51dfc8a0ed86221442eb2fb57019b0990ed24fab519bf0e1bc5ccfc4", "scripts": { @@ -13,18 +13,17 @@ "lint:fix": "eslint . --ignore-pattern src-tauri --fix" }, "dependencies": { - "@primevue/themes": "4.3.3", + "@primeuix/themes": "^1.2.3", "@tauri-apps/plugin-autostart": "2.0.0", "@tauri-apps/plugin-clipboard-manager": "2.3.0", "@tauri-apps/plugin-os": "2.3.0", "@tauri-apps/plugin-process": "2.3.0", "@tauri-apps/plugin-shell": "2.3.0", "@vueuse/core": "^11.2.0", - "aura": "link:@primevue\\themes\\aura", "easytier-frontend-lib": "workspace:*", "ip-num": "1.5.1", "pinia": "^2.2.4", - "primevue": "4.3.3", + "primevue": "^4.3.9", "tauri-plugin-vpnservice-api": "workspace:*", "vue": "^3.5.12", "vue-router": "^4.4.5" @@ -32,7 +31,7 @@ "devDependencies": { "@antfu/eslint-config": "^3.7.3", "@intlify/unplugin-vue-i18n": "^5.2.0", - "@primevue/auto-import-resolver": "4.3.3", + "@primevue/auto-import-resolver": "4.3.9", "@tauri-apps/api": "2.7.0", "@tauri-apps/cli": "2.7.1", "@types/default-gateway": "^7.2.2", diff --git a/easytier-gui/pnpm-lock.yaml b/easytier-gui/pnpm-lock.yaml deleted file mode 100644 index 0e6cc6890..000000000 --- a/easytier-gui/pnpm-lock.yaml +++ /dev/null @@ -1,7220 +0,0 @@ -lockfileVersion: '9.0' - -settings: - autoInstallPeers: true - excludeLinksFromLockfile: false - -importers: - - .: - dependencies: - '@primevue/themes': - specifier: ^4.1.0 - version: 4.1.0 - '@tauri-apps/plugin-autostart': - specifier: 2.0.0-rc.1 - version: 2.0.0-rc.1 - '@tauri-apps/plugin-clipboard-manager': - specifier: 2.0.0-rc.1 - version: 2.0.0-rc.1 - '@tauri-apps/plugin-os': - specifier: 2.0.0-rc.1 - version: 2.0.0-rc.1 - '@tauri-apps/plugin-process': - specifier: 2.0.0-rc.1 - version: 2.0.0-rc.1 - '@tauri-apps/plugin-shell': - specifier: 2.0.0-rc.1 - version: 2.0.0-rc.1 - '@vueuse/core': - specifier: ^11.1.0 - version: 11.1.0(vue@3.4.38(typescript@5.6.3)) - aura: - specifier: link:@primevue\themes\aura - version: link:@primevue/themes/aura - ip-num: - specifier: 1.5.1 - version: 1.5.1 - pinia: - specifier: ^2.2.4 - version: 2.2.4(typescript@5.6.3)(vue@3.4.38(typescript@5.6.3)) - primeflex: - specifier: ^3.3.1 - version: 3.3.1 - primeicons: - specifier: ^7.0.0 - version: 7.0.0 - primevue: - specifier: ^4.1.0 - version: 4.1.0(vue@3.4.38(typescript@5.6.3)) - tauri-plugin-vpnservice-api: - specifier: link:..\tauri-plugin-vpnservice - version: link:../tauri-plugin-vpnservice - vue: - specifier: '=3.4.38' - version: 3.4.38(typescript@5.6.3) - vue-i18n: - specifier: ^10.0.4 - version: 10.0.4(vue@3.4.38(typescript@5.6.3)) - vue-router: - specifier: ^4.4.5 - version: 4.4.5(vue@3.4.38(typescript@5.6.3)) - devDependencies: - '@antfu/eslint-config': - specifier: ^3.7.3 - version: 3.7.3(@typescript-eslint/utils@8.8.1(eslint@9.12.0(jiti@1.21.6))(typescript@5.6.3))(@vue/compiler-sfc@3.5.11)(eslint-plugin-format@0.1.2(eslint@9.12.0(jiti@1.21.6)))(eslint@9.12.0(jiti@1.21.6))(typescript@5.6.3) - '@intlify/unplugin-vue-i18n': - specifier: ^5.2.0 - version: 5.2.0(@vue/compiler-dom@3.5.11)(eslint@9.12.0(jiti@1.21.6))(rollup@4.24.0)(typescript@5.6.3)(vue-i18n@10.0.4(vue@3.4.38(typescript@5.6.3)))(vue@3.4.38(typescript@5.6.3)) - '@primevue/auto-import-resolver': - specifier: ^4.1.0 - version: 4.1.0 - '@tauri-apps/api': - specifier: 2.0.0-rc.0 - version: 2.0.0-rc.0 - '@tauri-apps/cli': - specifier: 2.0.0-rc.3 - version: 2.0.0-rc.3 - '@types/node': - specifier: ^22.7.4 - version: 22.7.5 - '@types/uuid': - specifier: ^10.0.0 - version: 10.0.0 - '@vitejs/plugin-vue': - specifier: ^5.1.4 - version: 5.1.4(vite@5.4.8(@types/node@22.7.5))(vue@3.4.38(typescript@5.6.3)) - '@vue-macros/volar': - specifier: 0.30.3 - version: 0.30.3(rollup@4.24.0)(typescript@5.6.3)(vue-tsc@2.1.6(typescript@5.6.3))(vue@3.4.38(typescript@5.6.3)) - autoprefixer: - specifier: ^10.4.20 - version: 10.4.20(postcss@8.4.47) - eslint: - specifier: ^9.12.0 - version: 9.12.0(jiti@1.21.6) - eslint-plugin-format: - specifier: ^0.1.2 - version: 0.1.2(eslint@9.12.0(jiti@1.21.6)) - internal-ip: - specifier: ^8.0.0 - version: 8.0.0 - postcss: - specifier: ^8.4.47 - version: 8.4.47 - tailwindcss: - specifier: ^3.4.13 - version: 3.4.13 - typescript: - specifier: ^5.6.2 - version: 5.6.3 - unplugin-auto-import: - specifier: ^0.18.3 - version: 0.18.3(@vueuse/core@11.1.0(vue@3.4.38(typescript@5.6.3)))(rollup@4.24.0) - unplugin-vue-components: - specifier: ^0.27.4 - version: 0.27.4(@babel/parser@7.25.8)(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3)) - unplugin-vue-macros: - specifier: ^2.12.3 - version: 2.12.3(@vueuse/core@11.1.0(vue@3.4.38(typescript@5.6.3)))(esbuild@0.23.1)(rollup@4.24.0)(typescript@5.6.3)(vite@5.4.8(@types/node@22.7.5))(vue-tsc@2.1.6(typescript@5.6.3))(vue@3.4.38(typescript@5.6.3)) - unplugin-vue-markdown: - specifier: ^0.26.2 - version: 0.26.2(rollup@4.24.0)(vite@5.4.8(@types/node@22.7.5)) - unplugin-vue-router: - specifier: ^0.10.8 - version: 0.10.8(rollup@4.24.0)(vue-router@4.4.5(vue@3.4.38(typescript@5.6.3)))(vue@3.4.38(typescript@5.6.3)) - uuid: - specifier: ^10.0.0 - version: 10.0.0 - vite: - specifier: ^5.4.8 - version: 5.4.8(@types/node@22.7.5) - vite-plugin-vue-devtools: - specifier: ^7.4.6 - version: 7.4.6(rollup@4.24.0)(vite@5.4.8(@types/node@22.7.5))(vue@3.4.38(typescript@5.6.3)) - vite-plugin-vue-layouts: - specifier: ^0.11.0 - version: 0.11.0(vite@5.4.8(@types/node@22.7.5))(vue-router@4.4.5(vue@3.4.38(typescript@5.6.3)))(vue@3.4.38(typescript@5.6.3)) - vue-tsc: - specifier: ^2.1.6 - version: 2.1.6(typescript@5.6.3) - -packages: - - '@alloc/quick-lru@5.2.0': - resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} - engines: {node: '>=10'} - - '@ampproject/remapping@2.3.0': - resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} - engines: {node: '>=6.0.0'} - - '@antfu/eslint-config@3.7.3': - resolution: {integrity: sha512-vzhKtzQT+f/xBV8T5U8SFy3D7uAqL2CEcjsJVqtA7F8tdKvGuC/96uWeEKMHk5lRfijgj+xRvb+c4qQn60YlIA==} - hasBin: true - peerDependencies: - '@eslint-react/eslint-plugin': ^1.5.8 - '@prettier/plugin-xml': ^3.4.1 - '@unocss/eslint-plugin': '>=0.50.0' - astro-eslint-parser: ^1.0.2 - eslint: ^9.10.0 - eslint-plugin-astro: ^1.2.0 - eslint-plugin-format: '>=0.1.0' - eslint-plugin-react-hooks: ^4.6.0 - eslint-plugin-react-refresh: ^0.4.4 - eslint-plugin-solid: ^0.14.3 - eslint-plugin-svelte: '>=2.35.1' - prettier-plugin-astro: ^0.13.0 - prettier-plugin-slidev: ^1.0.5 - svelte-eslint-parser: '>=0.37.0' - peerDependenciesMeta: - '@eslint-react/eslint-plugin': - optional: true - '@prettier/plugin-xml': - optional: true - '@unocss/eslint-plugin': - optional: true - astro-eslint-parser: - optional: true - eslint-plugin-astro: - optional: true - eslint-plugin-format: - optional: true - eslint-plugin-react-hooks: - optional: true - eslint-plugin-react-refresh: - optional: true - eslint-plugin-solid: - optional: true - eslint-plugin-svelte: - optional: true - prettier-plugin-astro: - optional: true - prettier-plugin-slidev: - optional: true - svelte-eslint-parser: - optional: true - - '@antfu/install-pkg@0.4.1': - resolution: {integrity: sha512-T7yB5QNG29afhWVkVq7XeIMBa5U/vs9mX69YqayXypPRmYzUmzwnYltplHmPtZ4HPCn+sQKeXW8I47wCbuBOjw==} - - '@antfu/utils@0.7.10': - resolution: {integrity: sha512-+562v9k4aI80m1+VuMHehNJWLOFjBnXn3tdOitzD0il5b7smkSBal4+a3oKiQTbrwMmN/TBUMDvbdoWDehgOww==} - - '@babel/code-frame@7.25.7': - resolution: {integrity: sha512-0xZJFNE5XMpENsgfHYTw8FbX4kv53mFLn2i3XPoq69LyhYSCBJtitaHx9QnsVTrsogI4Z3+HtEfZ2/GFPOtf5g==} - engines: {node: '>=6.9.0'} - - '@babel/compat-data@7.25.8': - resolution: {integrity: sha512-ZsysZyXY4Tlx+Q53XdnOFmqwfB9QDTHYxaZYajWRoBLuLEAwI2UIbtxOjWh/cFaa9IKUlcB+DDuoskLuKu56JA==} - engines: {node: '>=6.9.0'} - - '@babel/core@7.25.8': - resolution: {integrity: sha512-Oixnb+DzmRT30qu9d3tJSQkxuygWm32DFykT4bRoORPa9hZ/L4KhVB/XiRm6KG+roIEM7DBQlmg27kw2HZkdZg==} - engines: {node: '>=6.9.0'} - - '@babel/generator@7.25.7': - resolution: {integrity: sha512-5Dqpl5fyV9pIAD62yK9P7fcA768uVPUyrQmqpqstHWgMma4feF1x/oFysBCVZLY5wJ2GkMUCdsNDnGZrPoR6rA==} - engines: {node: '>=6.9.0'} - - '@babel/helper-annotate-as-pure@7.25.7': - resolution: {integrity: sha512-4xwU8StnqnlIhhioZf1tqnVWeQ9pvH/ujS8hRfw/WOza+/a+1qv69BWNy+oY231maTCWgKWhfBU7kDpsds6zAA==} - engines: {node: '>=6.9.0'} - - '@babel/helper-compilation-targets@7.25.7': - resolution: {integrity: sha512-DniTEax0sv6isaw6qSQSfV4gVRNtw2rte8HHM45t9ZR0xILaufBRNkpMifCRiAPyvL4ACD6v0gfCwCmtOQaV4A==} - engines: {node: '>=6.9.0'} - - '@babel/helper-create-class-features-plugin@7.25.7': - resolution: {integrity: sha512-bD4WQhbkx80mAyj/WCm4ZHcF4rDxkoLFO6ph8/5/mQ3z4vAzltQXAmbc7GvVJx5H+lk5Mi5EmbTeox5nMGCsbw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - - '@babel/helper-member-expression-to-functions@7.25.7': - resolution: {integrity: sha512-O31Ssjd5K6lPbTX9AAYpSKrZmLeagt9uwschJd+Ixo6QiRyfpvgtVQp8qrDR9UNFjZ8+DO34ZkdrN+BnPXemeA==} - engines: {node: '>=6.9.0'} - - '@babel/helper-module-imports@7.25.7': - resolution: {integrity: sha512-o0xCgpNmRohmnoWKQ0Ij8IdddjyBFE4T2kagL/x6M3+4zUgc+4qTOUBoNe4XxDskt1HPKO007ZPiMgLDq2s7Kw==} - engines: {node: '>=6.9.0'} - - '@babel/helper-module-transforms@7.25.7': - resolution: {integrity: sha512-k/6f8dKG3yDz/qCwSM+RKovjMix563SLxQFo0UhRNo239SP6n9u5/eLtKD6EAjwta2JHJ49CsD8pms2HdNiMMQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - - '@babel/helper-optimise-call-expression@7.25.7': - resolution: {integrity: sha512-VAwcwuYhv/AT+Vfr28c9y6SHzTan1ryqrydSTFGjU0uDJHw3uZ+PduI8plCLkRsDnqK2DMEDmwrOQRsK/Ykjng==} - engines: {node: '>=6.9.0'} - - '@babel/helper-plugin-utils@7.25.7': - resolution: {integrity: sha512-eaPZai0PiqCi09pPs3pAFfl/zYgGaE6IdXtYvmf0qlcDTd3WCtO7JWCcRd64e0EQrcYgiHibEZnOGsSY4QSgaw==} - engines: {node: '>=6.9.0'} - - '@babel/helper-replace-supers@7.25.7': - resolution: {integrity: sha512-iy8JhqlUW9PtZkd4pHM96v6BdJ66Ba9yWSE4z0W4TvSZwLBPkyDsiIU3ENe4SmrzRBs76F7rQXTy1lYC49n6Lw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - - '@babel/helper-simple-access@7.25.7': - resolution: {integrity: sha512-FPGAkJmyoChQeM+ruBGIDyrT2tKfZJO8NcxdC+CWNJi7N8/rZpSxK7yvBJ5O/nF1gfu5KzN7VKG3YVSLFfRSxQ==} - engines: {node: '>=6.9.0'} - - '@babel/helper-skip-transparent-expression-wrappers@7.25.7': - resolution: {integrity: sha512-pPbNbchZBkPMD50K0p3JGcFMNLVUCuU/ABybm/PGNj4JiHrpmNyqqCphBk4i19xXtNV0JhldQJJtbSW5aUvbyA==} - engines: {node: '>=6.9.0'} - - '@babel/helper-string-parser@7.25.7': - resolution: {integrity: sha512-CbkjYdsJNHFk8uqpEkpCvRs3YRp9tY6FmFY7wLMSYuGYkrdUi7r2lc4/wqsvlHoMznX3WJ9IP8giGPq68T/Y6g==} - engines: {node: '>=6.9.0'} - - '@babel/helper-validator-identifier@7.25.7': - resolution: {integrity: sha512-AM6TzwYqGChO45oiuPqwL2t20/HdMC1rTPAesnBCgPCSF1x3oN9MVUwQV2iyz4xqWrctwK5RNC8LV22kaQCNYg==} - engines: {node: '>=6.9.0'} - - '@babel/helper-validator-option@7.25.7': - resolution: {integrity: sha512-ytbPLsm+GjArDYXJ8Ydr1c/KJuutjF2besPNbIZnZ6MKUxi/uTA22t2ymmA4WFjZFpjiAMO0xuuJPqK2nvDVfQ==} - engines: {node: '>=6.9.0'} - - '@babel/helpers@7.25.7': - resolution: {integrity: sha512-Sv6pASx7Esm38KQpF/U/OXLwPPrdGHNKoeblRxgZRLXnAtnkEe4ptJPDtAZM7fBLadbc1Q07kQpSiGQ0Jg6tRA==} - engines: {node: '>=6.9.0'} - - '@babel/highlight@7.25.7': - resolution: {integrity: sha512-iYyACpW3iW8Fw+ZybQK+drQre+ns/tKpXbNESfrhNnPLIklLbXr7MYJ6gPEd0iETGLOK+SxMjVvKb/ffmk+FEw==} - engines: {node: '>=6.9.0'} - - '@babel/parser@7.25.8': - resolution: {integrity: sha512-HcttkxzdPucv3nNFmfOOMfFf64KgdJVqm1KaCm25dPGMLElo9nsLvXeJECQg8UzPuBGLyTSA0ZzqCtDSzKTEoQ==} - engines: {node: '>=6.0.0'} - hasBin: true - - '@babel/plugin-proposal-decorators@7.25.7': - resolution: {integrity: sha512-q1mqqqH0e1lhmsEQHV5U8OmdueBC2y0RFr2oUzZoFRtN3MvPmt2fsFRcNQAoGLTSNdHBFUYGnlgcRFhkBbKjPw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-decorators@7.25.7': - resolution: {integrity: sha512-oXduHo642ZhstLVYTe2z2GSJIruU0c/W3/Ghr6A5yGMsVrvdnxO1z+3pbTcT7f3/Clnt+1z8D/w1r1f1SHaCHw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-import-attributes@7.25.7': - resolution: {integrity: sha512-AqVo+dguCgmpi/3mYBdu9lkngOBlQ2w2vnNpa6gfiCxQZLzV4ZbhsXitJ2Yblkoe1VQwtHSaNmIaGll/26YWRw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-import-meta@7.10.4': - resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-jsx@7.25.7': - resolution: {integrity: sha512-ruZOnKO+ajVL/MVx+PwNBPOkrnXTXoWMtte1MBpegfCArhqOe3Bj52avVj1huLLxNKYKXYaSxZ2F+woK1ekXfw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-typescript@7.25.7': - resolution: {integrity: sha512-rR+5FDjpCHqqZN2bzZm18bVYGaejGq5ZkpVCJLXor/+zlSrSoc4KWcHI0URVWjl/68Dyr1uwZUz/1njycEAv9g==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-typescript@7.25.7': - resolution: {integrity: sha512-VKlgy2vBzj8AmEzunocMun2fF06bsSWV+FvVXohtL6FGve/+L217qhHxRTVGHEDO/YR8IANcjzgJsd04J8ge5Q==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/template@7.25.7': - resolution: {integrity: sha512-wRwtAgI3bAS+JGU2upWNL9lSlDcRCqD05BZ1n3X2ONLH1WilFP6O1otQjeMK/1g0pvYcXC7b/qVUB1keofjtZA==} - engines: {node: '>=6.9.0'} - - '@babel/traverse@7.25.7': - resolution: {integrity: sha512-jatJPT1Zjqvh/1FyJs6qAHL+Dzb7sTb+xr7Q+gM1b+1oBsMsQQ4FkVKb6dFlJvLlVssqkRzV05Jzervt9yhnzg==} - engines: {node: '>=6.9.0'} - - '@babel/types@7.25.8': - resolution: {integrity: sha512-JWtuCu8VQsMladxVz/P4HzHUGCAwpuqacmowgXFs5XjxIgKuNjnLokQzuVjlTvIzODaDmpjT3oxcC48vyk9EWg==} - engines: {node: '>=6.9.0'} - - '@clack/core@0.3.4': - resolution: {integrity: sha512-H4hxZDXgHtWTwV3RAVenqcC4VbJZNegbBjlPvzOzCouXtS2y3sDvlO3IsbrPNWuLWPPlYVYPghQdSF64683Ldw==} - - '@clack/prompts@0.7.0': - resolution: {integrity: sha512-0MhX9/B4iL6Re04jPrttDm+BsP8y6mS7byuv0BvXgdXhbV5PdlsHt55dvNsuBCPZ7xq1oTAOOuotR9NFbQyMSA==} - bundledDependencies: - - is-unicode-supported - - '@dprint/formatter@0.3.0': - resolution: {integrity: sha512-N9fxCxbaBOrDkteSOzaCqwWjso5iAe+WJPsHC021JfHNj2ThInPNEF13ORDKta3llq5D1TlclODCvOvipH7bWQ==} - - '@dprint/markdown@0.17.8': - resolution: {integrity: sha512-ukHFOg+RpG284aPdIg7iPrCYmMs3Dqy43S1ejybnwlJoFiW02b+6Bbr5cfZKFRYNP3dKGM86BqHEnMzBOyLvvA==} - - '@dprint/toml@0.6.3': - resolution: {integrity: sha512-zQ42I53sb4WVHA+5yoY1t59Zk++Ot02AvUgtNKLzTT8mPyVqVChFcePa3on/xIoKEgH+RoepgPHzqfk9837YFw==} - - '@es-joy/jsdoccomment@0.48.0': - resolution: {integrity: sha512-G6QUWIcC+KvSwXNsJyDTHvqUdNoAVJPPgkc3+Uk4WBKqZvoXhlvazOgm9aL0HwihJLQf0l+tOE2UFzXBqCqgDw==} - engines: {node: '>=16'} - - '@esbuild/aix-ppc64@0.21.5': - resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==} - engines: {node: '>=12'} - cpu: [ppc64] - os: [aix] - - '@esbuild/aix-ppc64@0.23.1': - resolution: {integrity: sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [aix] - - '@esbuild/android-arm64@0.21.5': - resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==} - engines: {node: '>=12'} - cpu: [arm64] - os: [android] - - '@esbuild/android-arm64@0.23.1': - resolution: {integrity: sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==} - engines: {node: '>=18'} - cpu: [arm64] - os: [android] - - '@esbuild/android-arm@0.21.5': - resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==} - engines: {node: '>=12'} - cpu: [arm] - os: [android] - - '@esbuild/android-arm@0.23.1': - resolution: {integrity: sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==} - engines: {node: '>=18'} - cpu: [arm] - os: [android] - - '@esbuild/android-x64@0.21.5': - resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==} - engines: {node: '>=12'} - cpu: [x64] - os: [android] - - '@esbuild/android-x64@0.23.1': - resolution: {integrity: sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==} - engines: {node: '>=18'} - cpu: [x64] - os: [android] - - '@esbuild/darwin-arm64@0.21.5': - resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==} - engines: {node: '>=12'} - cpu: [arm64] - os: [darwin] - - '@esbuild/darwin-arm64@0.23.1': - resolution: {integrity: sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==} - engines: {node: '>=18'} - cpu: [arm64] - os: [darwin] - - '@esbuild/darwin-x64@0.21.5': - resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==} - engines: {node: '>=12'} - cpu: [x64] - os: [darwin] - - '@esbuild/darwin-x64@0.23.1': - resolution: {integrity: sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==} - engines: {node: '>=18'} - cpu: [x64] - os: [darwin] - - '@esbuild/freebsd-arm64@0.21.5': - resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==} - engines: {node: '>=12'} - cpu: [arm64] - os: [freebsd] - - '@esbuild/freebsd-arm64@0.23.1': - resolution: {integrity: sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==} - engines: {node: '>=18'} - cpu: [arm64] - os: [freebsd] - - '@esbuild/freebsd-x64@0.21.5': - resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==} - engines: {node: '>=12'} - cpu: [x64] - os: [freebsd] - - '@esbuild/freebsd-x64@0.23.1': - resolution: {integrity: sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==} - engines: {node: '>=18'} - cpu: [x64] - os: [freebsd] - - '@esbuild/linux-arm64@0.21.5': - resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==} - engines: {node: '>=12'} - cpu: [arm64] - os: [linux] - - '@esbuild/linux-arm64@0.23.1': - resolution: {integrity: sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==} - engines: {node: '>=18'} - cpu: [arm64] - os: [linux] - - '@esbuild/linux-arm@0.21.5': - resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==} - engines: {node: '>=12'} - cpu: [arm] - os: [linux] - - '@esbuild/linux-arm@0.23.1': - resolution: {integrity: sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==} - engines: {node: '>=18'} - cpu: [arm] - os: [linux] - - '@esbuild/linux-ia32@0.21.5': - resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==} - engines: {node: '>=12'} - cpu: [ia32] - os: [linux] - - '@esbuild/linux-ia32@0.23.1': - resolution: {integrity: sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==} - engines: {node: '>=18'} - cpu: [ia32] - os: [linux] - - '@esbuild/linux-loong64@0.21.5': - resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==} - engines: {node: '>=12'} - cpu: [loong64] - os: [linux] - - '@esbuild/linux-loong64@0.23.1': - resolution: {integrity: sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==} - engines: {node: '>=18'} - cpu: [loong64] - os: [linux] - - '@esbuild/linux-mips64el@0.21.5': - resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==} - engines: {node: '>=12'} - cpu: [mips64el] - os: [linux] - - '@esbuild/linux-mips64el@0.23.1': - resolution: {integrity: sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==} - engines: {node: '>=18'} - cpu: [mips64el] - os: [linux] - - '@esbuild/linux-ppc64@0.21.5': - resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==} - engines: {node: '>=12'} - cpu: [ppc64] - os: [linux] - - '@esbuild/linux-ppc64@0.23.1': - resolution: {integrity: sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [linux] - - '@esbuild/linux-riscv64@0.21.5': - resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==} - engines: {node: '>=12'} - cpu: [riscv64] - os: [linux] - - '@esbuild/linux-riscv64@0.23.1': - resolution: {integrity: sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==} - engines: {node: '>=18'} - cpu: [riscv64] - os: [linux] - - '@esbuild/linux-s390x@0.21.5': - resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==} - engines: {node: '>=12'} - cpu: [s390x] - os: [linux] - - '@esbuild/linux-s390x@0.23.1': - resolution: {integrity: sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==} - engines: {node: '>=18'} - cpu: [s390x] - os: [linux] - - '@esbuild/linux-x64@0.21.5': - resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==} - engines: {node: '>=12'} - cpu: [x64] - os: [linux] - - '@esbuild/linux-x64@0.23.1': - resolution: {integrity: sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==} - engines: {node: '>=18'} - cpu: [x64] - os: [linux] - - '@esbuild/netbsd-x64@0.21.5': - resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==} - engines: {node: '>=12'} - cpu: [x64] - os: [netbsd] - - '@esbuild/netbsd-x64@0.23.1': - resolution: {integrity: sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==} - engines: {node: '>=18'} - cpu: [x64] - os: [netbsd] - - '@esbuild/openbsd-arm64@0.23.1': - resolution: {integrity: sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==} - engines: {node: '>=18'} - cpu: [arm64] - os: [openbsd] - - '@esbuild/openbsd-x64@0.21.5': - resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==} - engines: {node: '>=12'} - cpu: [x64] - os: [openbsd] - - '@esbuild/openbsd-x64@0.23.1': - resolution: {integrity: sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==} - engines: {node: '>=18'} - cpu: [x64] - os: [openbsd] - - '@esbuild/sunos-x64@0.21.5': - resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==} - engines: {node: '>=12'} - cpu: [x64] - os: [sunos] - - '@esbuild/sunos-x64@0.23.1': - resolution: {integrity: sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==} - engines: {node: '>=18'} - cpu: [x64] - os: [sunos] - - '@esbuild/win32-arm64@0.21.5': - resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==} - engines: {node: '>=12'} - cpu: [arm64] - os: [win32] - - '@esbuild/win32-arm64@0.23.1': - resolution: {integrity: sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==} - engines: {node: '>=18'} - cpu: [arm64] - os: [win32] - - '@esbuild/win32-ia32@0.21.5': - resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==} - engines: {node: '>=12'} - cpu: [ia32] - os: [win32] - - '@esbuild/win32-ia32@0.23.1': - resolution: {integrity: sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==} - engines: {node: '>=18'} - cpu: [ia32] - os: [win32] - - '@esbuild/win32-x64@0.21.5': - resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==} - engines: {node: '>=12'} - cpu: [x64] - os: [win32] - - '@esbuild/win32-x64@0.23.1': - resolution: {integrity: sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==} - engines: {node: '>=18'} - cpu: [x64] - os: [win32] - - '@eslint-community/eslint-plugin-eslint-comments@4.4.0': - resolution: {integrity: sha512-yljsWl5Qv3IkIRmJ38h3NrHXFCm4EUl55M8doGTF6hvzvFF8kRpextgSrg2dwHev9lzBZyafCr9RelGIyQm6fw==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0 - - '@eslint-community/eslint-utils@4.4.0': - resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 - - '@eslint-community/regexpp@4.11.1': - resolution: {integrity: sha512-m4DVN9ZqskZoLU5GlWZadwDnYo3vAEydiUayB9widCl9ffWx2IvPnp6n3on5rJmziJSw9Bv+Z3ChDVdMwXCY8Q==} - engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} - - '@eslint/compat@1.2.0': - resolution: {integrity: sha512-CkPWddN7J9JPrQedEr2X7AjK9y1jaMJtxZ4A/+jTMFA2+n5BWhcKHW/EbJyARqg2zzQfgtWUtVmG3hrG6+nGpg==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - eslint: ^9.10.0 - peerDependenciesMeta: - eslint: - optional: true - - '@eslint/config-array@0.18.0': - resolution: {integrity: sha512-fTxvnS1sRMu3+JjXwJG0j/i4RT9u4qJ+lqS/yCGap4lH4zZGzQ7tu+xZqQmcMZq5OBZDL4QRxQzRjkWcGt8IVw==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@eslint/core@0.6.0': - resolution: {integrity: sha512-8I2Q8ykA4J0x0o7cg67FPVnehcqWTBehu/lmY+bolPFHGjh49YzGBMXTvpqVgEbBdvNCSxj6iFgiIyHzf03lzg==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@eslint/eslintrc@3.1.0': - resolution: {integrity: sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@eslint/js@9.12.0': - resolution: {integrity: sha512-eohesHH8WFRUprDNyEREgqP6beG6htMeUYeCpkEgBCieCMme5r9zFWjzAJp//9S+Kub4rqE+jXe9Cp1a7IYIIA==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@eslint/markdown@6.2.0': - resolution: {integrity: sha512-ZLWZ6RNy5flf1Nk2DBt0V77MQpQEo8snkjVT75P5J0SJkE/QNoqgy7+dBvNjlyZuj664pU43uDXWg3J8AfF0IQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@eslint/object-schema@2.1.4': - resolution: {integrity: sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@eslint/plugin-kit@0.2.0': - resolution: {integrity: sha512-vH9PiIMMwvhCx31Af3HiGzsVNULDbyVkHXwlemn/B0TFj/00ho3y55efXrUZTfQipxoHC5u4xq6zblww1zm1Ig==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@humanfs/core@0.19.0': - resolution: {integrity: sha512-2cbWIHbZVEweE853g8jymffCA+NCMiuqeECeBBLm8dg2oFdjuGJhgN4UAbI+6v0CKbbhvtXA4qV8YR5Ji86nmw==} - engines: {node: '>=18.18.0'} - - '@humanfs/node@0.16.5': - resolution: {integrity: sha512-KSPA4umqSG4LHYRodq31VDwKAvaTF4xmVlzM8Aeh4PlU1JQ3IG0wiA8C25d3RQ9nJyM3mBHyI53K06VVL/oFFg==} - engines: {node: '>=18.18.0'} - - '@humanwhocodes/module-importer@1.0.1': - resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} - engines: {node: '>=12.22'} - - '@humanwhocodes/retry@0.3.1': - resolution: {integrity: sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==} - engines: {node: '>=18.18'} - - '@intlify/bundle-utils@9.0.0-beta.0': - resolution: {integrity: sha512-xVaMrgbr60fYE1Jkq+k6grs2ZoXqh1EU71RVKkHkKh3KP7T6OYtG1Vbp1T09/jCUbv1GBd8Ir5WdZDyN+e8BpQ==} - engines: {node: '>= 18'} - peerDependencies: - petite-vue-i18n: '*' - vue-i18n: '*' - peerDependenciesMeta: - petite-vue-i18n: - optional: true - vue-i18n: - optional: true - - '@intlify/core-base@10.0.4': - resolution: {integrity: sha512-GG428DkrrWCMhxRMRQZjuS7zmSUzarYcaHJqG9VB8dXAxw4iQDoKVQ7ChJRB6ZtsCsX3Jse1PEUlHrJiyQrOTg==} - engines: {node: '>= 16'} - - '@intlify/message-compiler@10.0.0': - resolution: {integrity: sha512-OcaWc63NC/9p1cMdgoNKBj4d61BH8sUW1Hfs6YijTd9656ZR4rNqXAlRnBrfS5ABq0vjQjpa8VnyvH9hK49yBw==} - engines: {node: '>= 16'} - - '@intlify/message-compiler@10.0.4': - resolution: {integrity: sha512-AFbhEo10DP095/45EauinQJ5hJ3rJUmuuqltGguvc3WsvezZN+g8qNHLGWKu60FHQVizMrQY7VJ+zVlBXlQQkQ==} - engines: {node: '>= 16'} - - '@intlify/shared@10.0.0': - resolution: {integrity: sha512-6ngLfI7DOTew2dcF9WMJx+NnMWghMBhIiHbGg+wRvngpzD5KZJZiJVuzMsUQE1a5YebEmtpTEfUrDp/NqVGdiw==} - engines: {node: '>= 16'} - - '@intlify/shared@10.0.4': - resolution: {integrity: sha512-ukFn0I01HsSgr3VYhYcvkTCLS7rGa0gw4A4AMpcy/A9xx/zRJy7PS2BElMXLwUazVFMAr5zuiTk3MQeoeGXaJg==} - engines: {node: '>= 16'} - - '@intlify/unplugin-vue-i18n@5.2.0': - resolution: {integrity: sha512-pmRiPY2Nj9mmSrixT69aO45XxGUr5fDBy/IIw4ajLlDTJm5TSmQKA5YNdsH0uxVDCPWy5tlQrF18hkDwI7UJvg==} - engines: {node: '>= 18'} - peerDependencies: - petite-vue-i18n: '*' - vue: ^3.2.25 - vue-i18n: '*' - peerDependenciesMeta: - petite-vue-i18n: - optional: true - vue-i18n: - optional: true - - '@intlify/vue-i18n-extensions@7.0.0': - resolution: {integrity: sha512-MtvfJnb4aklpCU5Q/dkWkBT/vGsp3qERiPIwtTq5lX4PCLHtUprAJZp8wQj5ZcwDaFCU7+yVMjYbeXpIf927cA==} - engines: {node: '>= 18'} - peerDependencies: - '@intlify/shared': ^9.0.0 || ^10.0.0 - '@vue/compiler-dom': ^3.0.0 - vue: ^3.0.0 - vue-i18n: ^9.0.0 || ^10.0.0 - peerDependenciesMeta: - '@intlify/shared': - optional: true - '@vue/compiler-dom': - optional: true - vue: - optional: true - vue-i18n: - optional: true - - '@isaacs/cliui@8.0.2': - resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} - engines: {node: '>=12'} - - '@jridgewell/gen-mapping@0.3.5': - resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==} - engines: {node: '>=6.0.0'} - - '@jridgewell/resolve-uri@3.1.2': - resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} - engines: {node: '>=6.0.0'} - - '@jridgewell/set-array@1.2.1': - resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} - engines: {node: '>=6.0.0'} - - '@jridgewell/sourcemap-codec@1.5.0': - resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} - - '@jridgewell/trace-mapping@0.3.25': - resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} - - '@mdit-vue/plugin-component@2.1.3': - resolution: {integrity: sha512-9AG17beCgpEw/4ldo/M6Y/1Rh4E1bqMmr/rCkWKmCAxy9tJz3lzY7HQJanyHMJufwsb3WL5Lp7Om/aPcQTZ9SA==} - - '@mdit-vue/plugin-frontmatter@2.1.3': - resolution: {integrity: sha512-KxsSCUVBEmn6sJcchSTiI5v9bWaoRxe68RBYRDGcSEY1GTnfQ5gQPMIsM48P4q1luLEIWurVGGrRu7u93//LDQ==} - - '@mdit-vue/types@2.1.0': - resolution: {integrity: sha512-TMBB/BQWVvwtpBdWD75rkZx4ZphQ6MN0O4QB2Bc0oI5PC2uE57QerhNxdRZ7cvBHE2iY2C+BUNUziCfJbjIRRA==} - - '@nodelib/fs.scandir@2.1.5': - resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} - engines: {node: '>= 8'} - - '@nodelib/fs.stat@2.0.5': - resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} - engines: {node: '>= 8'} - - '@nodelib/fs.walk@1.2.8': - resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} - engines: {node: '>= 8'} - - '@pkgjs/parseargs@0.11.0': - resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} - engines: {node: '>=14'} - - '@pkgr/core@0.1.1': - resolution: {integrity: sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==} - engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} - - '@polka/url@1.0.0-next.28': - resolution: {integrity: sha512-8LduaNlMZGwdZ6qWrKlfa+2M4gahzFkprZiAt2TF8uS0qQgBizKXpXURqvTJ4WtmupWxaLqjRb2UCTe72mu+Aw==} - - '@primeuix/styled@0.2.0': - resolution: {integrity: sha512-3Q6bDrmwTW88tzJsFIFenC0VyXLj0+/wYw+TZnJ/4CCDfehR4WfTs4EZdpuFtYqvmbpJ6zWXAiwSCNdSYTZkyA==} - engines: {node: '>=12.11.0'} - - '@primeuix/utils@0.2.0': - resolution: {integrity: sha512-AaDIeRFlsbkVTk2s0mlEjnGSLi31X669NVwo+n+AVAnBdDiQznjipNTpHbOobVBtjOKZize74PChK6uoaSBRUw==} - engines: {node: '>=12.11.0'} - - '@primevue/auto-import-resolver@4.1.0': - resolution: {integrity: sha512-5s6rT86VxUDgiD39lb/iq2Fv3SrN5PyqP8sGY9NLfrrjqCp7HxvT4b76emYZa/XoEwA2PgF6V7znMBXwc3vHsw==} - engines: {node: '>=12.11.0'} - - '@primevue/core@4.1.0': - resolution: {integrity: sha512-YulYm+PtoYSyLv/pNl3oyjvN+C9Ba/RSpW3bZAVkMRz9SWSA6lj2QaN+/9uxhokWZPL63zCHsHzIBV5YMvZshQ==} - engines: {node: '>=12.11.0'} - peerDependencies: - vue: ^3.0.0 - - '@primevue/icons@4.1.0': - resolution: {integrity: sha512-npY8Jy3HX1+Qbv1jCRdAevOcOj355b0x1Wmepa7omhgQFIUVs2o18HGohYml4HJpmEAu6aKnUIhhodFMuglMeQ==} - engines: {node: '>=12.11.0'} - - '@primevue/metadata@4.1.0': - resolution: {integrity: sha512-mQRFiZjn3QFF6AFe5bOYMc9I9day+VzxNSKOzuGgJj/fb3mB8bTwmORvDtzhaZs9E6FtAjmL6FRdL9rPYwUOpw==} - engines: {node: '>=12.11.0'} - - '@primevue/themes@4.1.0': - resolution: {integrity: sha512-EfAvNAI78rq8uPA+XFmHlACUXj2YpSM+PFjnFrTwHjqdSkmrBDweysNEI0p+/rj7l3rZBZLedEvbHcjP+9iumA==} - engines: {node: '>=12.11.0'} - - '@rollup/pluginutils@5.1.2': - resolution: {integrity: sha512-/FIdS3PyZ39bjZlwqFnWqCOVnW7o963LtKMwQOD0NhQqw22gSr2YY1afu3FxRip4ZCZNsD5jq6Aaz6QV3D/Njw==} - engines: {node: '>=14.0.0'} - peerDependencies: - rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 - peerDependenciesMeta: - rollup: - optional: true - - '@rollup/rollup-android-arm-eabi@4.24.0': - resolution: {integrity: sha512-Q6HJd7Y6xdB48x8ZNVDOqsbh2uByBhgK8PiQgPhwkIw/HC/YX5Ghq2mQY5sRMZWHb3VsFkWooUVOZHKr7DmDIA==} - cpu: [arm] - os: [android] - - '@rollup/rollup-android-arm64@4.24.0': - resolution: {integrity: sha512-ijLnS1qFId8xhKjT81uBHuuJp2lU4x2yxa4ctFPtG+MqEE6+C5f/+X/bStmxapgmwLwiL3ih122xv8kVARNAZA==} - cpu: [arm64] - os: [android] - - '@rollup/rollup-darwin-arm64@4.24.0': - resolution: {integrity: sha512-bIv+X9xeSs1XCk6DVvkO+S/z8/2AMt/2lMqdQbMrmVpgFvXlmde9mLcbQpztXm1tajC3raFDqegsH18HQPMYtA==} - cpu: [arm64] - os: [darwin] - - '@rollup/rollup-darwin-x64@4.24.0': - resolution: {integrity: sha512-X6/nOwoFN7RT2svEQWUsW/5C/fYMBe4fnLK9DQk4SX4mgVBiTA9h64kjUYPvGQ0F/9xwJ5U5UfTbl6BEjaQdBQ==} - cpu: [x64] - os: [darwin] - - '@rollup/rollup-linux-arm-gnueabihf@4.24.0': - resolution: {integrity: sha512-0KXvIJQMOImLCVCz9uvvdPgfyWo93aHHp8ui3FrtOP57svqrF/roSSR5pjqL2hcMp0ljeGlU4q9o/rQaAQ3AYA==} - cpu: [arm] - os: [linux] - libc: [glibc] - - '@rollup/rollup-linux-arm-musleabihf@4.24.0': - resolution: {integrity: sha512-it2BW6kKFVh8xk/BnHfakEeoLPv8STIISekpoF+nBgWM4d55CZKc7T4Dx1pEbTnYm/xEKMgy1MNtYuoA8RFIWw==} - cpu: [arm] - os: [linux] - libc: [musl] - - '@rollup/rollup-linux-arm64-gnu@4.24.0': - resolution: {integrity: sha512-i0xTLXjqap2eRfulFVlSnM5dEbTVque/3Pi4g2y7cxrs7+a9De42z4XxKLYJ7+OhE3IgxvfQM7vQc43bwTgPwA==} - cpu: [arm64] - os: [linux] - libc: [glibc] - - '@rollup/rollup-linux-arm64-musl@4.24.0': - resolution: {integrity: sha512-9E6MKUJhDuDh604Qco5yP/3qn3y7SLXYuiC0Rpr89aMScS2UAmK1wHP2b7KAa1nSjWJc/f/Lc0Wl1L47qjiyQw==} - cpu: [arm64] - os: [linux] - libc: [musl] - - '@rollup/rollup-linux-powerpc64le-gnu@4.24.0': - resolution: {integrity: sha512-2XFFPJ2XMEiF5Zi2EBf4h73oR1V/lycirxZxHZNc93SqDN/IWhYYSYj8I9381ikUFXZrz2v7r2tOVk2NBwxrWw==} - cpu: [ppc64] - os: [linux] - libc: [glibc] - - '@rollup/rollup-linux-riscv64-gnu@4.24.0': - resolution: {integrity: sha512-M3Dg4hlwuntUCdzU7KjYqbbd+BLq3JMAOhCKdBE3TcMGMZbKkDdJ5ivNdehOssMCIokNHFOsv7DO4rlEOfyKpg==} - cpu: [riscv64] - os: [linux] - libc: [glibc] - - '@rollup/rollup-linux-s390x-gnu@4.24.0': - resolution: {integrity: sha512-mjBaoo4ocxJppTorZVKWFpy1bfFj9FeCMJqzlMQGjpNPY9JwQi7OuS1axzNIk0nMX6jSgy6ZURDZ2w0QW6D56g==} - cpu: [s390x] - os: [linux] - libc: [glibc] - - '@rollup/rollup-linux-x64-gnu@4.24.0': - resolution: {integrity: sha512-ZXFk7M72R0YYFN5q13niV0B7G8/5dcQ9JDp8keJSfr3GoZeXEoMHP/HlvqROA3OMbMdfr19IjCeNAnPUG93b6A==} - cpu: [x64] - os: [linux] - libc: [glibc] - - '@rollup/rollup-linux-x64-musl@4.24.0': - resolution: {integrity: sha512-w1i+L7kAXZNdYl+vFvzSZy8Y1arS7vMgIy8wusXJzRrPyof5LAb02KGr1PD2EkRcl73kHulIID0M501lN+vobQ==} - cpu: [x64] - os: [linux] - libc: [musl] - - '@rollup/rollup-win32-arm64-msvc@4.24.0': - resolution: {integrity: sha512-VXBrnPWgBpVDCVY6XF3LEW0pOU51KbaHhccHw6AS6vBWIC60eqsH19DAeeObl+g8nKAz04QFdl/Cefta0xQtUQ==} - cpu: [arm64] - os: [win32] - - '@rollup/rollup-win32-ia32-msvc@4.24.0': - resolution: {integrity: sha512-xrNcGDU0OxVcPTH/8n/ShH4UevZxKIO6HJFK0e15XItZP2UcaiLFd5kiX7hJnqCbSztUF8Qot+JWBC/QXRPYWQ==} - cpu: [ia32] - os: [win32] - - '@rollup/rollup-win32-x64-msvc@4.24.0': - resolution: {integrity: sha512-fbMkAF7fufku0N2dE5TBXcNlg0pt0cJue4xBRE2Qc5Vqikxr4VCgKj/ht6SMdFcOacVA9rqF70APJ8RN/4vMJw==} - cpu: [x64] - os: [win32] - - '@stylistic/eslint-plugin@2.9.0': - resolution: {integrity: sha512-OrDyFAYjBT61122MIY1a3SfEgy3YCMgt2vL4eoPmvTwDBwyQhAXurxNQznlRD/jESNfYWfID8Ej+31LljvF7Xg==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - eslint: '>=8.40.0' - - '@tauri-apps/api@2.0.0-rc.0': - resolution: {integrity: sha512-v454Qs3REHc3Za59U+/eSmBsdmF+3NE5+76+lFDaitVqN4ZglDHENDaMARYKGJVZuxiSkzyqG0SeG7lLQjVkPA==} - engines: {node: '>= 18.18', npm: '>= 6.6.0', yarn: '>= 1.19.1'} - - '@tauri-apps/api@2.0.2': - resolution: {integrity: sha512-3wSwmG+1kr6WrgAFKK5ijkNFPp8TT3FLj3YHUb5EwMO+3FxX4uWlfSWkeeBy+Kc1RsKzugtYLuuya+98Flj+3w==} - - '@tauri-apps/cli-darwin-arm64@2.0.0-rc.3': - resolution: {integrity: sha512-szYCSr/ChbCF+S6Wnr15TYpI2cZR07d+AQOiFGuScP0preM8Pbsk/sb0hfLwqzepjVFFNVWQba9sG7FEW2Y2XA==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [darwin] - - '@tauri-apps/cli-darwin-x64@2.0.0-rc.3': - resolution: {integrity: sha512-BJv6EJOY1DJbRzVtfg8CcBAlnS5OjhBAc5YKjh4BT7EyOcop8HStBSxhL6yjWrUP7eLR1iIsW/uSehVJwzYIdQ==} - engines: {node: '>= 10'} - cpu: [x64] - os: [darwin] - - '@tauri-apps/cli-linux-arm-gnueabihf@2.0.0-rc.3': - resolution: {integrity: sha512-fwx805/xL4sF/EdMYqcUHQHzMYwo+OVTBTz5x/JWK8D57rnmLHAP+ZhnfFsZQLRo2QRT2l1Ye3bDyU+QRA1JFA==} - engines: {node: '>= 10'} - cpu: [arm] - os: [linux] - - '@tauri-apps/cli-linux-arm64-gnu@2.0.0-rc.3': - resolution: {integrity: sha512-3KauzO1Ls4kuY0nr82S4X8XFxlQAMN+Mqp8LLqvQ+PPMp92XQAkPH7osQdoHIEoW5gsE69U2JaiQ5tHSqNM9og==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [linux] - libc: [glibc] - - '@tauri-apps/cli-linux-arm64-musl@2.0.0-rc.3': - resolution: {integrity: sha512-ngHS0foffm1xO5gqnDKGeYMKj8ceGmrFP5dDldoaaMQubw1SyFa0pRUjb7fZSYiO7F4SOSa8NYeMqlF9peZmnQ==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [linux] - libc: [musl] - - '@tauri-apps/cli-linux-x64-gnu@2.0.0-rc.3': - resolution: {integrity: sha512-0/am9pVvuUHGmz32M8ffz1fpLnc08j3nzcRe5wUdL2AxfT+wKMII+Dn99GtCVgcdDW4jSXDMRUwrBkGocGC2OA==} - engines: {node: '>= 10'} - cpu: [x64] - os: [linux] - libc: [glibc] - - '@tauri-apps/cli-linux-x64-musl@2.0.0-rc.3': - resolution: {integrity: sha512-r7mRi8q8TqTFVjb9kAsU7IgwUgno2s8Ip4xwq9psQhlRE3JGEZQmSEcy1jqTjfl6KFh6lJcDR7l+9/EMhL/D3Q==} - engines: {node: '>= 10'} - cpu: [x64] - os: [linux] - libc: [musl] - - '@tauri-apps/cli-win32-arm64-msvc@2.0.0-rc.3': - resolution: {integrity: sha512-2J6KjmDIQCw6HF1X6/yPcd+JLl7pxrH2zVMGmNllaoWhHeByvRobqFWnT7gcdHaA3dGTo432CwWvOgTgrINQpQ==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [win32] - - '@tauri-apps/cli-win32-ia32-msvc@2.0.0-rc.3': - resolution: {integrity: sha512-8q75CsHDSEDdgi6xPwim+BaQZFCswK2Dn/qL38V3Mh9kmVvC8oGJMPC66bC20dF+v3KWeFm2FNNGQqOSXCveHg==} - engines: {node: '>= 10'} - cpu: [ia32] - os: [win32] - - '@tauri-apps/cli-win32-x64-msvc@2.0.0-rc.3': - resolution: {integrity: sha512-qeBRJYalahxEXolekcpZJ/HBrIJacG2NWJBGhhi797mIwnbmlpbHMc8blIJtNNNwVUb2BjXuxKQVfojQ5YYrcg==} - engines: {node: '>= 10'} - cpu: [x64] - os: [win32] - - '@tauri-apps/cli@2.0.0-rc.3': - resolution: {integrity: sha512-iNF95pieBmverl1EmQyqh+fhcIClS544fN5Ex5lAbYLTiHZ/gm3lOfVBhF6NPaKd/sfLuy7K1tfDXlHztBfANw==} - engines: {node: '>= 10'} - hasBin: true - - '@tauri-apps/plugin-autostart@2.0.0-rc.1': - resolution: {integrity: sha512-5h801HJf6Z5CtUmJhwv+PBSn2nNAWqsuKmu0hABn/IpP2AElZev4XicMzrnYVevJeIhWgRA8HNpg3s8pbic1Rw==} - - '@tauri-apps/plugin-clipboard-manager@2.0.0-rc.1': - resolution: {integrity: sha512-hFgUABMmQuVGKwHb8PR9fuqfk0WRkedbWUt/ZV5sL4Q6kLrsp3JYJvtzVPeMYdeBvMqHl8WXNxAc/zwSld2h9w==} - - '@tauri-apps/plugin-os@2.0.0-rc.1': - resolution: {integrity: sha512-PV8zlSTmYfiN2xzILUmlDSEycS7UYbH2yXk/ZqF+qQU6/s+OVQvmSth4EhllFjcpvPbtqELvpzfjw+2qEouchA==} - - '@tauri-apps/plugin-process@2.0.0-rc.1': - resolution: {integrity: sha512-Bl22xdoiu+AqEP6rzjb7DUJwdLDnejuRFukpkdrqF1/VEWJK5PuE903l+8mIOsd17zZ1Ua8y8WaBWnOXx4QHmw==} - - '@tauri-apps/plugin-shell@2.0.0-rc.1': - resolution: {integrity: sha512-JtNROc0rqEwN/g93ig5pK4cl1vUo2yn+osCpY9de64cy/d9hRzof7AuYOgvt/Xcd5VPQmlgo2AGvUh5sQRSR1A==} - - '@types/debug@4.1.12': - resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==} - - '@types/estree@1.0.6': - resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==} - - '@types/json-schema@7.0.15': - resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} - - '@types/linkify-it@5.0.0': - resolution: {integrity: sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==} - - '@types/markdown-it@14.1.2': - resolution: {integrity: sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==} - - '@types/mdast@4.0.4': - resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==} - - '@types/mdurl@2.0.0': - resolution: {integrity: sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==} - - '@types/ms@0.7.34': - resolution: {integrity: sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==} - - '@types/node@22.7.5': - resolution: {integrity: sha512-jML7s2NAzMWc//QSJ1a3prpk78cOPchGvXJsC3C6R6PSMoooztvRVQEz89gmBTBY1SPMaqo5teB4uNHPdetShQ==} - - '@types/normalize-package-data@2.4.4': - resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==} - - '@types/unist@3.0.3': - resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} - - '@types/uuid@10.0.0': - resolution: {integrity: sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==} - - '@types/web-bluetooth@0.0.20': - resolution: {integrity: sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow==} - - '@typescript-eslint/eslint-plugin@8.8.1': - resolution: {integrity: sha512-xfvdgA8AP/vxHgtgU310+WBnLB4uJQ9XdyP17RebG26rLtDrQJV3ZYrcopX91GrHmMoH8bdSwMRh2a//TiJ1jQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - '@typescript-eslint/parser': ^8.0.0 || ^8.0.0-alpha.0 - eslint: ^8.57.0 || ^9.0.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - - '@typescript-eslint/parser@8.8.1': - resolution: {integrity: sha512-hQUVn2Lij2NAxVFEdvIGxT9gP1tq2yM83m+by3whWFsWC+1y8pxxxHUFE1UqDu2VsGi2i6RLcv4QvouM84U+ow==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - eslint: ^8.57.0 || ^9.0.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - - '@typescript-eslint/scope-manager@7.18.0': - resolution: {integrity: sha512-jjhdIE/FPF2B7Z1uzc6i3oWKbGcHb87Qw7AWj6jmEqNOfDFbJWtjt/XfwCpvNkpGWlcJaog5vTR+VV8+w9JflA==} - engines: {node: ^18.18.0 || >=20.0.0} - - '@typescript-eslint/scope-manager@8.8.1': - resolution: {integrity: sha512-X4JdU+66Mazev/J0gfXlcC/dV6JI37h+93W9BRYXrSn0hrE64IoWgVkO9MSJgEzoWkxONgaQpICWg8vAN74wlA==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@typescript-eslint/type-utils@8.8.1': - resolution: {integrity: sha512-qSVnpcbLP8CALORf0za+vjLYj1Wp8HSoiI8zYU5tHxRVj30702Z1Yw4cLwfNKhTPWp5+P+k1pjmD5Zd1nhxiZA==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - - '@typescript-eslint/types@7.18.0': - resolution: {integrity: sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==} - engines: {node: ^18.18.0 || >=20.0.0} - - '@typescript-eslint/types@8.8.1': - resolution: {integrity: sha512-WCcTP4SDXzMd23N27u66zTKMuEevH4uzU8C9jf0RO4E04yVHgQgW+r+TeVTNnO1KIfrL8ebgVVYYMMO3+jC55Q==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@typescript-eslint/typescript-estree@7.18.0': - resolution: {integrity: sha512-aP1v/BSPnnyhMHts8cf1qQ6Q1IFwwRvAQGRvBFkWlo3/lH29OXA3Pts+c10nxRxIBrDnoMqzhgdwVe5f2D6OzA==} - engines: {node: ^18.18.0 || >=20.0.0} - peerDependencies: - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - - '@typescript-eslint/typescript-estree@8.8.1': - resolution: {integrity: sha512-A5d1R9p+X+1js4JogdNilDuuq+EHZdsH9MjTVxXOdVFfTJXunKJR/v+fNNyO4TnoOn5HqobzfRlc70NC6HTcdg==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - - '@typescript-eslint/utils@8.8.1': - resolution: {integrity: sha512-/QkNJDbV0bdL7H7d0/y0qBbV2HTtf0TIyjSDTvvmQEzeVx8jEImEbLuOA4EsvE8gIgqMitns0ifb5uQhMj8d9w==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - eslint: ^8.57.0 || ^9.0.0 - - '@typescript-eslint/visitor-keys@7.18.0': - resolution: {integrity: sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg==} - engines: {node: ^18.18.0 || >=20.0.0} - - '@typescript-eslint/visitor-keys@8.8.1': - resolution: {integrity: sha512-0/TdC3aeRAsW7MDvYRwEc1Uwm0TIBfzjPFgg60UU2Haj5qsCs9cc3zNgY71edqE3LbWfF/WoZQd3lJoDXFQpag==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@vitejs/plugin-vue@5.1.4': - resolution: {integrity: sha512-N2XSI2n3sQqp5w7Y/AN/L2XDjBIRGqXko+eDp42sydYSBeJuSm5a1sLf8zakmo8u7tA8NmBgoDLA1HeOESjp9A==} - engines: {node: ^18.0.0 || >=20.0.0} - peerDependencies: - vite: ^5.0.0 - vue: ^3.2.25 - - '@vitest/eslint-plugin@1.1.7': - resolution: {integrity: sha512-pTWGW3y6lH2ukCuuffpan6kFxG6nIuoesbhMiQxskyQMRcCN5t9SXsKrNHvEw3p8wcCsgJoRqFZVkOTn6TjclA==} - peerDependencies: - '@typescript-eslint/utils': '>= 8.0' - eslint: '>= 8.57.0' - typescript: '>= 5.0.0' - vitest: '*' - peerDependenciesMeta: - typescript: - optional: true - vitest: - optional: true - - '@volar/language-core@2.4.6': - resolution: {integrity: sha512-FxUfxaB8sCqvY46YjyAAV6c3mMIq/NWQMVvJ+uS4yxr1KzOvyg61gAuOnNvgCvO4TZ7HcLExBEsWcDu4+K4E8A==} - - '@volar/source-map@2.4.6': - resolution: {integrity: sha512-Nsh7UW2ruK+uURIPzjJgF0YRGP5CX9nQHypA2OMqdM2FKy7rh+uv3XgPnWPw30JADbKvZ5HuBzG4gSbVDYVtiw==} - - '@volar/typescript@2.4.6': - resolution: {integrity: sha512-NMIrA7y5OOqddL9VtngPWYmdQU03htNKFtAYidbYfWA0TOhyGVd9tfcP4TsLWQ+RBWDZCbBqsr8xzU0ZOxYTCQ==} - - '@vue-macros/api@0.11.1': - resolution: {integrity: sha512-bdn0nKgZk6mqwvoyFYufDu489uZ9SG9DJLG9dyUnUqmRo0Dz4TuPp1WpTh+N65bElRE5EIZg+5QhhUkZR2Q7xw==} - engines: {node: '>=16.14.0'} - - '@vue-macros/better-define@1.9.1': - resolution: {integrity: sha512-9PMeOlgsrg/QaeVi8zx28+SCwwjns/Xmdf8B0xDeGhc5VY7MtjqMXycJ5y462sYFuiB7TgiVNp0xyrey3BiH1w==} - engines: {node: '>=16.14.0'} - - '@vue-macros/boolean-prop@0.5.1': - resolution: {integrity: sha512-exB41s6Ke3MiunCVujXzUTXo76w+POkwps6Lo+TTMw6dvksTc0SKUTu86l2bs1XhOA+04+vXQdQodhJJQdpJVg==} - engines: {node: '>=16.14.0'} - - '@vue-macros/chain-call@0.4.1': - resolution: {integrity: sha512-hrltcOfBOd2+aV5gHfQSFIFby5Rkm7HgEmP06bO8LnlYre0yOvJBFfCb6FMOSwBeT/iEyhwvxTzFS7SEa9ic7A==} - engines: {node: '>=16.14.0'} - - '@vue-macros/common@1.14.0': - resolution: {integrity: sha512-xwQhDoEXRNXobNQmdqOD20yUGdVLVLZe4zhDlT9q/E+z+mvT3wukaAoJG80XRnv/BcgOOCVpxqpkQZ3sNTgjWA==} - engines: {node: '>=16.14.0'} - peerDependencies: - vue: ^2.7.0 || ^3.2.25 - peerDependenciesMeta: - vue: - optional: true - - '@vue-macros/config@0.4.2': - resolution: {integrity: sha512-tCz1sZ2tY+z7gDXrZ9wSiRf5Txj9Ms7V20vHkuGcf0I8Ny256SpHwSkYHyIdj2Z1gCgST8bgXYjROBQJah98rA==} - engines: {node: '>=16.14.0'} - - '@vue-macros/define-emit@0.4.1': - resolution: {integrity: sha512-GWg49zQHYKUyVdV95fKk/I0VCT09KnGPl2Kg/RQb91aWVseLOvh6bDs27VAhjTLzbX5990gM5fQOdhEtpplMdw==} - engines: {node: '>=16.14.0'} - peerDependencies: - vue: ^2.7.0 || ^3.2.25 - - '@vue-macros/define-models@1.3.1': - resolution: {integrity: sha512-OPU78x02g5aPW7w80rUHbxlsQ80MaYa2COxuhjPyuOzuArJyKeCFBBHyNyBOvnxQO6UQMlRsnvlBvgpuNlFs3w==} - engines: {node: '>=16.14.0'} - peerDependencies: - '@vueuse/core': '>=9.0.0' - peerDependenciesMeta: - '@vueuse/core': - optional: true - - '@vue-macros/define-prop@0.5.1': - resolution: {integrity: sha512-ffTWCJw1JBRm8Br6LJJ3rpygs1A9ygQKlIwAh9JEEyvUIueB+gP/hQ9an4iZoSyuYJzCMArcSSaJoC3oFJG43w==} - engines: {node: '>=16.14.0'} - peerDependencies: - vue: ^2.7.0 || ^3.2.25 - - '@vue-macros/define-props-refs@1.3.1': - resolution: {integrity: sha512-7wDmjRyTqlUnDqczcVSdfRZ9MnP54w2iy7VgCQPg/qEH920Id5afhU3KWZ8kyO84VCaJF/rGhGy7tqdaFayIGg==} - engines: {node: '>=16.14.0'} - peerDependencies: - vue: ^2.7.0 || ^3.2.25 - - '@vue-macros/define-props@4.0.1': - resolution: {integrity: sha512-/HEyRIVssvY9iUl3amehHnC9ZCh/fwoanNOI2mkFlmId0+dkGklkELuD9CJKXeQ0vl0bc7dmKB9qtEDVBcz8WQ==} - engines: {node: '>=16.14.0'} - peerDependencies: - '@vue-macros/reactivity-transform': ^1.1.1 - vue: ^2.7.0 || ^3.2.25 - - '@vue-macros/define-render@1.6.1': - resolution: {integrity: sha512-y1ToCwIhzpDAve7VdxYSDMKYKS7/XzoqdkV/pTbQqGvahTLtdaTOl++9K24I/AUE23qvZmJN6jPTSNDOhZnLUg==} - engines: {node: '>=16.14.0'} - peerDependencies: - vue: ^2.7.0 || ^3.0.0 - - '@vue-macros/define-slots@1.2.1': - resolution: {integrity: sha512-xoDRxs2WguQbWUpYujfqPzUU4uTzXeY8bITFhAAXMq95W14EJNUZu05q18yrvSrO/Nu5C8qgq+NW4nZ3Mzg70g==} - engines: {node: '>=16.14.0'} - peerDependencies: - vue: ^2.7.0 || ^3.0.0 - - '@vue-macros/devtools@0.4.0': - resolution: {integrity: sha512-767WYNXNZqyarp92FkcSGxk5twi1S8QqmwG8UiplIExzSAG2tA2Hria/MQP4vth9/gh8hjekib6ipOjoCDZUpw==} - engines: {node: '>=16.14.0'} - peerDependencies: - vite: ^4.0.0 || ^5.0.0-0 - peerDependenciesMeta: - vite: - optional: true - - '@vue-macros/export-expose@0.3.1': - resolution: {integrity: sha512-K/9/kMhplJ+XlWm0UpCroV9mvxO2wsm7LBXvp7uhrp+bswCGMdwhg0nHpXmEuRQMIzZNKsClN+CKCho3s/HMJg==} - engines: {node: '>=16.14.0'} - peerDependencies: - vue: ^2.7.0 || ^3.2.25 - - '@vue-macros/export-props@0.6.1': - resolution: {integrity: sha512-/0tKCOHWP7jI0XReyO+oEsPc+/BjlPQrzSB2kQ4mvsWSgTF2S1vpJ2oc7QKEHWsf4UqolSuQAbnMo1fxWyF9+A==} - engines: {node: '>=16.14.0'} - peerDependencies: - vue: ^2.7.0 || ^3.2.25 - - '@vue-macros/export-render@0.3.1': - resolution: {integrity: sha512-8R/60xMfeIkhecu84ZIJLkJVtEkFHgTAMSmR8xQ22NhsDHZuQmvikHN1mZBqFu8JDg5Q0Qgf7vLK0W70abHNyw==} - engines: {node: '>=16.14.0'} - peerDependencies: - vue: ^2.7.0 || ^3.2.25 - - '@vue-macros/hoist-static@1.6.1': - resolution: {integrity: sha512-BFduGP6ezNb7R3RSIJkGvHcOa9hA0uv3g6tC8j4RoB0HHI8s69+dSXPy6bq22Tjg/b/wZJ9QAdsjWv7l5Oi8kg==} - engines: {node: '>=16.14.0'} - - '@vue-macros/jsx-directive@0.9.1': - resolution: {integrity: sha512-eyTahYLDspQwRxMXqR7GqZntFi436jC4l04tJWq8r4KrNVjXyS/5OZLTmEOuHFrAzH91imX0wpaiRAyPvLXLsw==} - engines: {node: '>=16.14.0'} - - '@vue-macros/named-template@0.5.1': - resolution: {integrity: sha512-JtCSq158I0BQmBFdaNI3pwfhElvZonU3pTfL4+g2d3KJCO8gI0JJyvr5bUIVxJJ1p29550skW49vTHq0GsNTQQ==} - engines: {node: '>=16.14.0'} - - '@vue-macros/reactivity-transform@1.1.1': - resolution: {integrity: sha512-hc6WVqH6x0y3XCqSPjtRa/0v+PgY9P0zXaBnmPCpJmw3sM3v6SS9lWEw/v/kjmFWVtHyTBKU8rXQZ5dqFZ8sTA==} - engines: {node: '>=16.14.0'} - peerDependencies: - vue: ^2.7.0 || ^3.2.25 - - '@vue-macros/script-lang@0.2.1': - resolution: {integrity: sha512-2QH+SBlaCentUUf//TAp7dNW5olnpKvrr07UasXnBGTT6Af1FeKcwBH4+X2QISiTdg/CXCVObl6B1uDQaLM/Cw==} - engines: {node: '>=16.14.0'} - peerDependencies: - vue: ^2.7.0 || ^3.0.0 - - '@vue-macros/setup-block@0.4.1': - resolution: {integrity: sha512-MZoX/jNpr5pJd7PicQuvRsmX9/5cqakTTTTeah0IoigqVB3NQ7qrvBqMCWKyalkKrC8Z3cqHT0lSw6ju2v5M0Q==} - engines: {node: '>=16.14.0'} - - '@vue-macros/setup-component@0.18.1': - resolution: {integrity: sha512-q5FKi2GqzuL6suzshZ/PxCTyJGfpmHwg6k3OhInhZeCDH7o9OBPHeLJx5RZGjk/XrfnBpKNtDNZGrxyqBaihVg==} - engines: {node: '>=16.14.0'} - - '@vue-macros/setup-sfc@0.18.1': - resolution: {integrity: sha512-4jYo7/o1ECOIourc66UTm3xRt00vMl7ego00XSX2LJbWIRepgkiJ4cVoLg4Daux3D3xRH4qVmNbUkMtyrv+kFQ==} - engines: {node: '>=16.14.0'} - - '@vue-macros/short-bind@1.1.1': - resolution: {integrity: sha512-eP0Y1Yt7IG6GJytfogkL0wLzH2KPqxOXt66+aILpz8J//7F84D7qMWAnG5KA1vLM/1m6rTRazhMB++KS45kdKg==} - engines: {node: '>=16.14.0'} - - '@vue-macros/short-emits@1.6.1': - resolution: {integrity: sha512-WcfUNoJd4eUqSkhnsJprSgm648cVCCuxaWc7SmZnbMqAUm+Csf6fKDJJeaNACVbS011tXtcg2aWKxCfLtxB/8Q==} - engines: {node: '>=16.14.0'} - - '@vue-macros/short-vmodel@1.5.1': - resolution: {integrity: sha512-DT/6ZWe90/qrFtgicKCRljdvcwM25Nk66lPF9UFydwOFHKa6yOwTdeexjU3W/uG8X4QLlQBTCsDKbjLbhK1i7g==} - engines: {node: '>=16.14.0'} - - '@vue-macros/volar@0.30.3': - resolution: {integrity: sha512-35ecwiHaKizySmEIsSHIdyBpf0AgP9wZkUJUIO4xq9lAlSXHkTMjELuwVCRFCCyjZS/UuB+rPqxguKiSaNKmGg==} - engines: {node: '>=16.14.0'} - peerDependencies: - vue-tsc: 2.1.6 - peerDependenciesMeta: - vue-tsc: - optional: true - - '@vue/babel-helper-vue-transform-on@1.2.5': - resolution: {integrity: sha512-lOz4t39ZdmU4DJAa2hwPYmKc8EsuGa2U0L9KaZaOJUt0UwQNjNA3AZTq6uEivhOKhhG1Wvy96SvYBoFmCg3uuw==} - - '@vue/babel-plugin-jsx@1.2.5': - resolution: {integrity: sha512-zTrNmOd4939H9KsRIGmmzn3q2zvv1mjxkYZHgqHZgDrXz5B1Q3WyGEjO2f+JrmKghvl1JIRcvo63LgM1kH5zFg==} - peerDependencies: - '@babel/core': ^7.0.0-0 - peerDependenciesMeta: - '@babel/core': - optional: true - - '@vue/babel-plugin-resolve-type@1.2.5': - resolution: {integrity: sha512-U/ibkQrf5sx0XXRnUZD1mo5F7PkpKyTbfXM3a3rC4YnUz6crHEz9Jg09jzzL6QYlXNto/9CePdOg/c87O4Nlfg==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@vue/compiler-core@3.4.38': - resolution: {integrity: sha512-8IQOTCWnLFqfHzOGm9+P8OPSEDukgg3Huc92qSG49if/xI2SAwLHQO2qaPQbjCWPBcQoO1WYfXfTACUrWV3c5A==} - - '@vue/compiler-core@3.5.11': - resolution: {integrity: sha512-PwAdxs7/9Hc3ieBO12tXzmTD+Ln4qhT/56S+8DvrrZ4kLDn4Z/AMUr8tXJD0axiJBS0RKIoNaR0yMuQB9v9Udg==} - - '@vue/compiler-dom@3.4.38': - resolution: {integrity: sha512-Osc/c7ABsHXTsETLgykcOwIxFktHfGSUDkb05V61rocEfsFDcjDLH/IHJSNJP+/Sv9KeN2Lx1V6McZzlSb9EhQ==} - - '@vue/compiler-dom@3.5.11': - resolution: {integrity: sha512-pyGf8zdbDDRkBrEzf8p7BQlMKNNF5Fk/Cf/fQ6PiUz9at4OaUfyXW0dGJTo2Vl1f5U9jSLCNf0EZJEogLXoeew==} - - '@vue/compiler-sfc@3.4.38': - resolution: {integrity: sha512-s5QfZ+9PzPh3T5H4hsQDJtI8x7zdJaew/dCGgqZ2630XdzaZ3AD8xGZfBqpT8oaD/p2eedd+pL8tD5vvt5ZYJQ==} - - '@vue/compiler-sfc@3.5.11': - resolution: {integrity: sha512-gsbBtT4N9ANXXepprle+X9YLg2htQk1sqH/qGJ/EApl+dgpUBdTv3yP7YlR535uHZY3n6XaR0/bKo0BgwwDniw==} - - '@vue/compiler-ssr@3.4.38': - resolution: {integrity: sha512-YXznKFQ8dxYpAz9zLuVvfcXhc31FSPFDcqr0kyujbOwNhlmaNvL2QfIy+RZeJgSn5Fk54CWoEUeW+NVBAogGaw==} - - '@vue/compiler-ssr@3.5.11': - resolution: {integrity: sha512-P4+GPjOuC2aFTk1Z4WANvEhyOykcvEd5bIj2KVNGKGfM745LaXGr++5njpdBTzVz5pZifdlR1kpYSJJpIlSePA==} - - '@vue/compiler-vue2@2.7.16': - resolution: {integrity: sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A==} - - '@vue/devtools-api@6.6.4': - resolution: {integrity: sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==} - - '@vue/devtools-core@7.4.6': - resolution: {integrity: sha512-7ATNPEbVqThOOAp2bg/YUIm9MqqgimbSk24D05hdXUp89JlXX12aTzdrWd9xZRwS78hDR+wCToHl1C/8sopBrg==} - peerDependencies: - vue: ^3.0.0 - - '@vue/devtools-kit@7.4.6': - resolution: {integrity: sha512-NbYBwPWgEic1AOd9bWExz9weBzFdjiIfov0yRn4DrRfR+EQJCI9dn4I0XS7IxYGdkmUJi8mFW42LLk18WsGqew==} - - '@vue/devtools-shared@7.4.6': - resolution: {integrity: sha512-rPeSBzElnHYMB05Cc056BQiJpgocQjY8XVulgni+O9a9Gr9tNXgPteSzFFD+fT/iWMxNuUgGKs9CuW5DZewfIg==} - - '@vue/language-core@2.1.6': - resolution: {integrity: sha512-MW569cSky9R/ooKMh6xa2g1D0AtRKbL56k83dzus/bx//RDJk24RHWkMzbAlXjMdDNyxAaagKPRquBIxkxlCkg==} - peerDependencies: - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - - '@vue/reactivity@3.4.38': - resolution: {integrity: sha512-4vl4wMMVniLsSYYeldAKzbk72+D3hUnkw9z8lDeJacTxAkXeDAP1uE9xr2+aKIN0ipOL8EG2GPouVTH6yF7Gnw==} - - '@vue/reactivity@3.5.11': - resolution: {integrity: sha512-Nqo5VZEn8MJWlCce8XoyVqHZbd5P2NH+yuAaFzuNSR96I+y1cnuUiq7xfSG+kyvLSiWmaHTKP1r3OZY4mMD50w==} - - '@vue/runtime-core@3.4.38': - resolution: {integrity: sha512-21z3wA99EABtuf+O3IhdxP0iHgkBs1vuoCAsCKLVJPEjpVqvblwBnTj42vzHRlWDCyxu9ptDm7sI2ZMcWrQqlA==} - - '@vue/runtime-core@3.5.11': - resolution: {integrity: sha512-7PsxFGqwfDhfhh0OcDWBG1DaIQIVOLgkwA5q6MtkPiDFjp5gohVnJEahSktwSFLq7R5PtxDKy6WKURVN1UDbzA==} - - '@vue/runtime-dom@3.4.38': - resolution: {integrity: sha512-afZzmUreU7vKwKsV17H1NDThEEmdYI+GCAK/KY1U957Ig2NATPVjCROv61R19fjZNzMmiU03n79OMnXyJVN0UA==} - - '@vue/runtime-dom@3.5.11': - resolution: {integrity: sha512-GNghjecT6IrGf0UhuYmpgaOlN7kxzQBhxWEn08c/SQDxv1yy4IXI1bn81JgEpQ4IXjRxWtPyI8x0/7TF5rPfYQ==} - - '@vue/server-renderer@3.4.38': - resolution: {integrity: sha512-NggOTr82FbPEkkUvBm4fTGcwUY8UuTsnWC/L2YZBmvaQ4C4Jl/Ao4HHTB+l7WnFCt5M/dN3l0XLuyjzswGYVCA==} - peerDependencies: - vue: 3.4.38 - - '@vue/server-renderer@3.5.11': - resolution: {integrity: sha512-cVOwYBxR7Wb1B1FoxYvtjJD8X/9E5nlH4VSkJy2uMA1MzYNdzAAB//l8nrmN9py/4aP+3NjWukf9PZ3TeWULaA==} - peerDependencies: - vue: 3.5.11 - - '@vue/shared@3.4.38': - resolution: {integrity: sha512-q0xCiLkuWWQLzVrecPb0RMsNWyxICOjPrcrwxTUEHb1fsnvni4dcuyG7RT/Ie7VPTvnjzIaWzRMUBsrqNj/hhw==} - - '@vue/shared@3.5.11': - resolution: {integrity: sha512-W8GgysJVnFo81FthhzurdRAWP/byq3q2qIw70e0JWblzVhjgOMiC2GyovXrZTFQJnFVryYaKGP3Tc9vYzYm6PQ==} - - '@vueuse/core@11.1.0': - resolution: {integrity: sha512-P6dk79QYA6sKQnghrUz/1tHi0n9mrb/iO1WTMk/ElLmTyNqgDeSZ3wcDf6fRBGzRJbeG1dxzEOvLENMjr+E3fg==} - - '@vueuse/metadata@11.1.0': - resolution: {integrity: sha512-l9Q502TBTaPYGanl1G+hPgd3QX5s4CGnpXriVBR5fEZ/goI6fvDaVmIl3Td8oKFurOxTmbXvBPSsgrd6eu6HYg==} - - '@vueuse/shared@11.1.0': - resolution: {integrity: sha512-YUtIpY122q7osj+zsNMFAfMTubGz0sn5QzE5gPzAIiCmtt2ha3uQUY1+JPyL4gRCTsLPX82Y9brNbo/aqlA91w==} - - acorn-jsx@5.3.2: - resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} - peerDependencies: - acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 - - acorn@8.12.1: - resolution: {integrity: sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==} - engines: {node: '>=0.4.0'} - hasBin: true - - ajv@6.12.6: - resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} - - ansi-regex@5.0.1: - resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} - engines: {node: '>=8'} - - ansi-regex@6.1.0: - resolution: {integrity: sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==} - engines: {node: '>=12'} - - ansi-styles@3.2.1: - resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} - engines: {node: '>=4'} - - ansi-styles@4.3.0: - resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} - engines: {node: '>=8'} - - ansi-styles@6.2.1: - resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} - engines: {node: '>=12'} - - any-promise@1.3.0: - resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} - - anymatch@3.1.3: - resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} - engines: {node: '>= 8'} - - are-docs-informative@0.0.2: - resolution: {integrity: sha512-ixiS0nLNNG5jNQzgZJNoUpBKdo9yTYZMGJ+QgT2jmjR7G7+QHRCc4v6LQ3NgE7EBJq+o0ams3waJwkrlBom8Ig==} - engines: {node: '>=14'} - - arg@5.0.2: - resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} - - argparse@1.0.10: - resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} - - argparse@2.0.1: - resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} - - array-union@2.1.0: - resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} - engines: {node: '>=8'} - - ast-kit@1.2.1: - resolution: {integrity: sha512-h31wotR7rkFLrlmGPn0kGqOZ/n5EQFvp7dBs400chpHDhHc8BK3gpvyHDluRujuGgeoTAv3dSIMz9BI3JxAWyQ==} - engines: {node: '>=16.14.0'} - - ast-walker-scope@0.6.2: - resolution: {integrity: sha512-1UWOyC50xI3QZkRuDj6PqDtpm1oHWtYs+NQGwqL/2R11eN3Q81PHAHPM0SWW3BNQm53UDwS//Jv8L4CCVLM1bQ==} - engines: {node: '>=16.14.0'} - - autoprefixer@10.4.20: - resolution: {integrity: sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==} - engines: {node: ^10 || ^12 || >=14} - hasBin: true - peerDependencies: - postcss: ^8.1.0 - - balanced-match@1.0.2: - resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} - - binary-extensions@2.3.0: - resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} - engines: {node: '>=8'} - - birpc@0.2.19: - resolution: {integrity: sha512-5WeXXAvTmitV1RqJFppT5QtUiz2p1mRSYU000Jkft5ZUCLJIk4uQriYNO50HknxKwM6jd8utNc66K1qGIwwWBQ==} - - boolbase@1.0.0: - resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} - - brace-expansion@1.1.11: - resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} - - brace-expansion@2.0.1: - resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} - - braces@3.0.3: - resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} - engines: {node: '>=8'} - - browserslist@4.24.0: - resolution: {integrity: sha512-Rmb62sR1Zpjql25eSanFGEhAxcFwfA1K0GuQcLoaJBAcENegrQut3hYdhXFF1obQfiDyqIW/cLM5HSJ/9k884A==} - engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} - hasBin: true - - builtin-modules@3.3.0: - resolution: {integrity: sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==} - engines: {node: '>=6'} - - bundle-name@4.1.0: - resolution: {integrity: sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==} - engines: {node: '>=18'} - - bundle-require@5.0.0: - resolution: {integrity: sha512-GuziW3fSSmopcx4KRymQEJVbZUfqlCqcq7dvs6TYwKRZiegK/2buMxQTPs6MGlNv50wms1699qYO54R8XfRX4w==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - peerDependencies: - esbuild: '>=0.18' - - callsites@3.1.0: - resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} - engines: {node: '>=6'} - - camelcase-css@2.0.1: - resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==} - engines: {node: '>= 6'} - - caniuse-lite@1.0.30001668: - resolution: {integrity: sha512-nWLrdxqCdblixUO+27JtGJJE/txpJlyUy5YN1u53wLZkP0emYCo5zgS6QYft7VUYR42LGgi/S5hdLZTrnyIddw==} - - ccount@2.0.1: - resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} - - chalk@2.4.2: - resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} - engines: {node: '>=4'} - - chalk@4.1.2: - resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} - engines: {node: '>=10'} - - character-entities@2.0.2: - resolution: {integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==} - - chokidar@3.6.0: - resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} - engines: {node: '>= 8.10.0'} - - ci-info@4.0.0: - resolution: {integrity: sha512-TdHqgGf9odd8SXNuxtUBVx8Nv+qZOejE6qyqiy5NtbYYQOeFa6zmHkxlPzmaLxWWHsU6nJmB7AETdVPi+2NBUg==} - engines: {node: '>=8'} - - cidr-regex@4.0.3: - resolution: {integrity: sha512-HOwDIy/rhKeMf6uOzxtv7FAbrz8zPjmVKfSpM+U7/bNBXC5rtOyr758jxcptiSx6ZZn5LOhPJT5WWxPAGDV8dw==} - engines: {node: '>=14'} - - cidr-tools@6.4.2: - resolution: {integrity: sha512-KZC8t2ipCqU2M+ISmTxRDGu9bku5MRU3V1cWyGEFJTZEzRhGvBJvVsbpZO5UAu12fExRFihtYGXAlgFFpmK9jw==} - engines: {node: '>=16'} - - clean-regexp@1.0.0: - resolution: {integrity: sha512-GfisEZEJvzKrmGWkvfhgzcz/BllN1USeqD2V6tg14OAOgaCD2Z/PUEuxnAZ/nPvmaHRG7a8y77p1T/IRQ4D1Hw==} - engines: {node: '>=4'} - - cliui@8.0.1: - resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} - engines: {node: '>=12'} - - clone-regexp@3.0.0: - resolution: {integrity: sha512-ujdnoq2Kxb8s3ItNBtnYeXdm07FcU0u8ARAT1lQ2YdMwQC+cdiXX8KoqMVuglztILivceTtp4ivqGSmEmhBUJw==} - engines: {node: '>=12'} - - color-convert@1.9.3: - resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} - - color-convert@2.0.1: - resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} - engines: {node: '>=7.0.0'} - - color-name@1.1.3: - resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} - - color-name@1.1.4: - resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} - - commander@4.1.1: - resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} - engines: {node: '>= 6'} - - comment-parser@1.4.1: - resolution: {integrity: sha512-buhp5kePrmda3vhc5B9t7pUQXAb2Tnd0qgpkIhPhkHXxJpiPJ11H0ZEU0oBpJ2QztSbzG/ZxMj/CHsYJqRHmyg==} - engines: {node: '>= 12.0.0'} - - computeds@0.0.1: - resolution: {integrity: sha512-7CEBgcMjVmitjYo5q8JTJVra6X5mQ20uTThdK+0kR7UEaDrAWEQcRiBtWJzga4eRpP6afNwwLsX2SET2JhVB1Q==} - - concat-map@0.0.1: - resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} - - confbox@0.1.8: - resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==} - - convert-hrtime@5.0.0: - resolution: {integrity: sha512-lOETlkIeYSJWcbbcvjRKGxVMXJR+8+OQb/mTPbA4ObPMytYIsUbuOE0Jzy60hjARYszq1id0j8KgVhC+WGZVTg==} - engines: {node: '>=12'} - - convert-source-map@2.0.0: - resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} - - copy-anything@3.0.5: - resolution: {integrity: sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w==} - engines: {node: '>=12.13'} - - core-js-compat@3.38.1: - resolution: {integrity: sha512-JRH6gfXxGmrzF3tZ57lFx97YARxCXPaMzPo6jELZhv88pBH5VXpQ+y0znKGlFnzuaihqhLbefxSJxWJMPtfDzw==} - - cross-spawn@7.0.3: - resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} - engines: {node: '>= 8'} - - cssesc@3.0.0: - resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} - engines: {node: '>=4'} - hasBin: true - - csstype@3.1.3: - resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} - - de-indent@1.0.2: - resolution: {integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==} - - debug@3.2.7: - resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true - - debug@4.3.7: - resolution: {integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==} - engines: {node: '>=6.0'} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true - - decode-named-character-reference@1.0.2: - resolution: {integrity: sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==} - - deep-is@0.1.4: - resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} - - default-browser-id@5.0.0: - resolution: {integrity: sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA==} - engines: {node: '>=18'} - - default-browser@5.2.1: - resolution: {integrity: sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg==} - engines: {node: '>=18'} - - default-gateway@7.2.2: - resolution: {integrity: sha512-AD7TrdNNPXRZIGw63dw+lnGmT4v7ggZC5NHNJgAYWm5njrwoze1q5JSAW9YuLy2tjnoLUG/r8FEB93MCh9QJPg==} - engines: {node: '>= 16'} - - define-lazy-prop@3.0.0: - resolution: {integrity: sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==} - engines: {node: '>=12'} - - defu@6.1.4: - resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==} - - dequal@2.0.3: - resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} - engines: {node: '>=6'} - - devlop@1.1.0: - resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} - - didyoumean@1.2.2: - resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} - - dir-glob@3.0.1: - resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} - engines: {node: '>=8'} - - dlv@1.1.3: - resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==} - - doctrine@3.0.0: - resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} - engines: {node: '>=6.0.0'} - - eastasianwidth@0.2.0: - resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} - - electron-to-chromium@1.5.36: - resolution: {integrity: sha512-HYTX8tKge/VNp6FGO+f/uVDmUkq+cEfcxYhKf15Akc4M5yxt5YmorwlAitKWjWhWQnKcDRBAQKXkhqqXMqcrjw==} - - emoji-regex@8.0.0: - resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} - - emoji-regex@9.2.2: - resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} - - enhanced-resolve@5.17.1: - resolution: {integrity: sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==} - engines: {node: '>=10.13.0'} - - entities@4.5.0: - resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} - engines: {node: '>=0.12'} - - error-ex@1.3.2: - resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} - - error-stack-parser-es@0.1.5: - resolution: {integrity: sha512-xHku1X40RO+fO8yJ8Wh2f2rZWVjqyhb1zgq1yZ8aZRQkv6OOKhKWRUaht3eSCUbAOBaKIgM+ykwFLE+QUxgGeg==} - - es-module-lexer@1.5.4: - resolution: {integrity: sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==} - - esbuild@0.21.5: - resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} - engines: {node: '>=12'} - hasBin: true - - esbuild@0.23.1: - resolution: {integrity: sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==} - engines: {node: '>=18'} - hasBin: true - - escalade@3.2.0: - resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} - engines: {node: '>=6'} - - escape-string-regexp@1.0.5: - resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} - engines: {node: '>=0.8.0'} - - escape-string-regexp@4.0.0: - resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} - engines: {node: '>=10'} - - escape-string-regexp@5.0.0: - resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} - engines: {node: '>=12'} - - escodegen@2.1.0: - resolution: {integrity: sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==} - engines: {node: '>=6.0'} - hasBin: true - - eslint-compat-utils@0.5.1: - resolution: {integrity: sha512-3z3vFexKIEnjHE3zCMRo6fn/e44U7T1khUjg+Hp0ZQMCigh28rALD0nPFBcGZuiLC5rLZa2ubQHDRln09JfU2Q==} - engines: {node: '>=12'} - peerDependencies: - eslint: '>=6.0.0' - - eslint-config-flat-gitignore@0.3.0: - resolution: {integrity: sha512-0Ndxo4qGhcewjTzw52TK06Mc00aDtHNTdeeW2JfONgDcLkRO/n/BteMRzNVpLQYxdCC/dFEilfM9fjjpGIJ9Og==} - peerDependencies: - eslint: ^9.5.0 - - eslint-flat-config-utils@0.4.0: - resolution: {integrity: sha512-kfd5kQZC+BMO0YwTol6zxjKX1zAsk8JfSAopbKjKqmENTJcew+yBejuvccAg37cvOrN0Mh+DVbeyznuNWEjt4A==} - - eslint-formatting-reporter@0.0.0: - resolution: {integrity: sha512-k9RdyTqxqN/wNYVaTk/ds5B5rA8lgoAmvceYN7bcZMBwU7TuXx5ntewJv81eF3pIL/CiJE+pJZm36llG8yhyyw==} - peerDependencies: - eslint: '>=8.40.0' - - eslint-import-resolver-node@0.3.9: - resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==} - - eslint-merge-processors@0.1.0: - resolution: {integrity: sha512-IvRXXtEajLeyssvW4wJcZ2etxkR9mUf4zpNwgI+m/Uac9RfXHskuJefkHUcawVzePnd6xp24enp5jfgdHzjRdQ==} - peerDependencies: - eslint: '*' - - eslint-parser-plain@0.1.0: - resolution: {integrity: sha512-oOeA6FWU0UJT/Rxc3XF5Cq0nbIZbylm7j8+plqq0CZoE6m4u32OXJrR+9iy4srGMmF6v6pmgvP1zPxSRIGh3sg==} - - eslint-plugin-antfu@2.7.0: - resolution: {integrity: sha512-gZM3jq3ouqaoHmUNszb1Zo2Ux7RckSvkGksjLWz9ipBYGSv1EwwBETN6AdiUXn+RpVHXTbEMPAPlXJazcA6+iA==} - peerDependencies: - eslint: '*' - - eslint-plugin-command@0.2.6: - resolution: {integrity: sha512-T0bHZ1oblW1xUHUVoBKZJR2osSNNGkfZuK4iqboNwuNS/M7tdp3pmURaJtTi/XDzitxaQ02lvOdFH0mUd5QLvQ==} - peerDependencies: - eslint: '*' - - eslint-plugin-es-x@7.8.0: - resolution: {integrity: sha512-7Ds8+wAAoV3T+LAKeu39Y5BzXCrGKrcISfgKEqTS4BDN8SFEDQd0S43jiQ8vIa3wUKD07qitZdfzlenSi8/0qQ==} - engines: {node: ^14.18.0 || >=16.0.0} - peerDependencies: - eslint: '>=8' - - eslint-plugin-format@0.1.2: - resolution: {integrity: sha512-ZrcO3aiumgJ6ENAv65IWkPjtW77ML/5mp0YrRK0jdvvaZJb+4kKWbaQTMr/XbJo6CtELRmCApAziEKh7L2NbdQ==} - peerDependencies: - eslint: ^8.40.0 || ^9.0.0 - - eslint-plugin-import-x@4.3.1: - resolution: {integrity: sha512-5TriWkXulDl486XnYYRgsL+VQoS/7mhN/2ci02iLCuL7gdhbiWxnsuL/NTcaKY9fpMgsMFjWZBtIGW7pb+RX0g==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - eslint: ^8.57.0 || ^9.0.0 - - eslint-plugin-jsdoc@50.3.1: - resolution: {integrity: sha512-SY9oUuTMr6aWoJggUS40LtMjsRzJPB5ZT7F432xZIHK3EfHF+8i48GbUBpwanrtlL9l1gILNTHK9o8gEhYLcKA==} - engines: {node: '>=18'} - peerDependencies: - eslint: ^7.0.0 || ^8.0.0 || ^9.0.0 - - eslint-plugin-jsonc@2.16.0: - resolution: {integrity: sha512-Af/ZL5mgfb8FFNleH6KlO4/VdmDuTqmM+SPnWcdoWywTetv7kq+vQe99UyQb9XO3b0OWLVuTH7H0d/PXYCMdSg==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: '>=6.0.0' - - eslint-plugin-n@17.11.1: - resolution: {integrity: sha512-93IUD82N6tIEgjztVI/l3ElHtC2wTa9boJHrD8iN+NyDxjxz/daZUZKfkedjBZNdg6EqDk4irybUsiPwDqXAEA==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - eslint: '>=8.23.0' - - eslint-plugin-no-only-tests@3.3.0: - resolution: {integrity: sha512-brcKcxGnISN2CcVhXJ/kEQlNa0MEfGRtwKtWA16SkqXHKitaKIMrfemJKLKX1YqDU5C/5JY3PvZXd5jEW04e0Q==} - engines: {node: '>=5.0.0'} - - eslint-plugin-perfectionist@3.8.0: - resolution: {integrity: sha512-BYJWbQVOjvIGK9V1xUfn790HuvkePjxti8epOi1H6sdzo0N4RehBmQ8coHPbgA/f12BUG1NIoDtQhI9mUm+o2A==} - engines: {node: ^18.0.0 || >=20.0.0} - peerDependencies: - astro-eslint-parser: ^1.0.2 - eslint: '>=8.0.0' - svelte: '>=3.0.0' - svelte-eslint-parser: ^0.41.1 - vue-eslint-parser: '>=9.0.0' - peerDependenciesMeta: - astro-eslint-parser: - optional: true - svelte: - optional: true - svelte-eslint-parser: - optional: true - vue-eslint-parser: - optional: true - - eslint-plugin-regexp@2.6.0: - resolution: {integrity: sha512-FCL851+kislsTEQEMioAlpDuK5+E5vs0hi1bF8cFlPlHcEjeRhuAzEsGikXRreE+0j4WhW2uO54MqTjXtYOi3A==} - engines: {node: ^18 || >=20} - peerDependencies: - eslint: '>=8.44.0' - - eslint-plugin-toml@0.11.1: - resolution: {integrity: sha512-Y1WuMSzfZpeMIrmlP1nUh3kT8p96mThIq4NnHrYUhg10IKQgGfBZjAWnrg9fBqguiX4iFps/x/3Hb5TxBisfdw==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: '>=6.0.0' - - eslint-plugin-unicorn@55.0.0: - resolution: {integrity: sha512-n3AKiVpY2/uDcGrS3+QsYDkjPfaOrNrsfQxU9nt5nitd9KuvVXrfAvgCO9DYPSfap+Gqjw9EOrXIsBp5tlHZjA==} - engines: {node: '>=18.18'} - peerDependencies: - eslint: '>=8.56.0' - - eslint-plugin-unused-imports@4.1.4: - resolution: {integrity: sha512-YptD6IzQjDardkl0POxnnRBhU1OEePMV0nd6siHaRBbd+lyh6NAhFEobiznKU7kTsSsDeSD62Pe7kAM1b7dAZQ==} - peerDependencies: - '@typescript-eslint/eslint-plugin': ^8.0.0-0 || ^7.0.0 || ^6.0.0 || ^5.0.0 - eslint: ^9.0.0 || ^8.0.0 - peerDependenciesMeta: - '@typescript-eslint/eslint-plugin': - optional: true - - eslint-plugin-vue@9.29.0: - resolution: {integrity: sha512-hamyjrBhNH6Li6R1h1VF9KHfshJlKgKEg3ARbGTn72CMNDSMhWbgC7NdkRDEh25AFW+4SDATzyNM+3gWuZii8g==} - engines: {node: ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: ^6.2.0 || ^7.0.0 || ^8.0.0 || ^9.0.0 - - eslint-plugin-yml@1.14.0: - resolution: {integrity: sha512-ESUpgYPOcAYQO9czugcX5OqRvn/ydDVwGCPXY4YjPqc09rHaUVUA6IE6HLQys4rXk/S+qx3EwTd1wHCwam/OWQ==} - engines: {node: ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: '>=6.0.0' - - eslint-processor-vue-blocks@0.1.2: - resolution: {integrity: sha512-PfpJ4uKHnqeL/fXUnzYkOax3aIenlwewXRX8jFinA1a2yCFnLgMuiH3xvCgvHHUlV2xJWQHbCTdiJWGwb3NqpQ==} - peerDependencies: - '@vue/compiler-sfc': ^3.3.0 - eslint: ^8.50.0 || ^9.0.0 - - eslint-scope@7.2.2: - resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - - eslint-scope@8.1.0: - resolution: {integrity: sha512-14dSvlhaVhKKsa9Fx1l8A17s7ah7Ef7wCakJ10LYk6+GYmP9yDti2oq2SEwcyndt6knfcZyhyxwY3i9yL78EQw==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - eslint-visitor-keys@3.4.3: - resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - - eslint-visitor-keys@4.1.0: - resolution: {integrity: sha512-Q7lok0mqMUSf5a/AdAZkA5a/gHcO6snwQClVNNvFKCAVlxXucdU8pKydU5ZVZjBx5xr37vGbFFWtLQYreLzrZg==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - eslint@9.12.0: - resolution: {integrity: sha512-UVIOlTEWxwIopRL1wgSQYdnVDcEvs2wyaO6DGo5mXqe3r16IoCNWkR29iHhyaP4cICWjbgbmFUGAhh0GJRuGZw==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - hasBin: true - peerDependencies: - jiti: '*' - peerDependenciesMeta: - jiti: - optional: true - - espree@10.2.0: - resolution: {integrity: sha512-upbkBJbckcCNBDBDXEbuhjbP68n+scUd3k/U2EkyM9nw+I/jPiL4cLF/Al06CF96wRltFda16sxDFrxsI1v0/g==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - espree@9.6.1: - resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - - esprima@4.0.1: - resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} - engines: {node: '>=4'} - hasBin: true - - esquery@1.6.0: - resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} - engines: {node: '>=0.10'} - - esrecurse@4.3.0: - resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} - engines: {node: '>=4.0'} - - estraverse@5.3.0: - resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} - engines: {node: '>=4.0'} - - estree-walker@2.0.2: - resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} - - estree-walker@3.0.3: - resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} - - esutils@2.0.3: - resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} - engines: {node: '>=0.10.0'} - - execa@7.2.0: - resolution: {integrity: sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA==} - engines: {node: ^14.18.0 || ^16.14.0 || >=18.0.0} - - execa@8.0.1: - resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==} - engines: {node: '>=16.17'} - - extend-shallow@2.0.1: - resolution: {integrity: sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==} - engines: {node: '>=0.10.0'} - - fast-deep-equal@3.1.3: - resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} - - fast-diff@1.3.0: - resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==} - - fast-glob@3.3.2: - resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} - engines: {node: '>=8.6.0'} - - fast-json-stable-stringify@2.1.0: - resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} - - fast-levenshtein@2.0.6: - resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} - - fastq@1.17.1: - resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} - - file-entry-cache@8.0.0: - resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} - engines: {node: '>=16.0.0'} - - fill-range@7.1.1: - resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} - engines: {node: '>=8'} - - find-up-simple@1.0.0: - resolution: {integrity: sha512-q7Us7kcjj2VMePAa02hDAF6d+MzsdsAWEwYyOpwUtlerRBkOEPBCRZrAV4XfcSN8fHAgaD0hP7miwoay6DCprw==} - engines: {node: '>=18'} - - find-up@4.1.0: - resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} - engines: {node: '>=8'} - - find-up@5.0.0: - resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} - engines: {node: '>=10'} - - flat-cache@4.0.1: - resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} - engines: {node: '>=16'} - - flatted@3.3.1: - resolution: {integrity: sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==} - - foreground-child@3.3.0: - resolution: {integrity: sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==} - engines: {node: '>=14'} - - fraction.js@4.3.7: - resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==} - - fs-extra@11.2.0: - resolution: {integrity: sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==} - engines: {node: '>=14.14'} - - fsevents@2.3.3: - resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} - engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} - os: [darwin] - - function-bind@1.1.2: - resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} - - function-timeout@0.1.1: - resolution: {integrity: sha512-0NVVC0TaP7dSTvn1yMiy6d6Q8gifzbvQafO46RtLG/kHJUBNd+pVRGOBoK44wNBvtSPUJRfdVvkFdD3p0xvyZg==} - engines: {node: '>=14.16'} - - gensync@1.0.0-beta.2: - resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} - engines: {node: '>=6.9.0'} - - get-caller-file@2.0.5: - resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} - engines: {node: 6.* || 8.* || >= 10.*} - - get-stream@6.0.1: - resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} - engines: {node: '>=10'} - - get-stream@8.0.1: - resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==} - engines: {node: '>=16'} - - get-tsconfig@4.8.1: - resolution: {integrity: sha512-k9PN+cFBmaLWtVz29SkUoqU5O0slLuHJXt/2P+tMVFT+phsSGXGkp9t3rQIqdz0e+06EHNGs3oM6ZX1s2zHxRg==} - - glob-parent@5.1.2: - resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} - engines: {node: '>= 6'} - - glob-parent@6.0.2: - resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} - engines: {node: '>=10.13.0'} - - glob@10.4.5: - resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} - hasBin: true - - globals@11.12.0: - resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} - engines: {node: '>=4'} - - globals@13.24.0: - resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==} - engines: {node: '>=8'} - - globals@14.0.0: - resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} - engines: {node: '>=18'} - - globals@15.11.0: - resolution: {integrity: sha512-yeyNSjdbyVaWurlwCpcA6XNBrHTMIeDdj0/hnvX/OLJ9ekOXYbLsLinH/MucQyGvNnXhidTdNhTtJaffL2sMfw==} - engines: {node: '>=18'} - - globby@11.1.0: - resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} - engines: {node: '>=10'} - - graceful-fs@4.2.11: - resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} - - graphemer@1.4.0: - resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} - - gray-matter@4.0.3: - resolution: {integrity: sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q==} - engines: {node: '>=6.0'} - - has-flag@3.0.0: - resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} - engines: {node: '>=4'} - - has-flag@4.0.0: - resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} - engines: {node: '>=8'} - - hasown@2.0.2: - resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} - engines: {node: '>= 0.4'} - - he@1.2.0: - resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} - hasBin: true - - hookable@5.5.3: - resolution: {integrity: sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==} - - hosted-git-info@2.8.9: - resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==} - - html-tags@3.3.1: - resolution: {integrity: sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==} - engines: {node: '>=8'} - - human-signals@4.3.1: - resolution: {integrity: sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==} - engines: {node: '>=14.18.0'} - - human-signals@5.0.0: - resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==} - engines: {node: '>=16.17.0'} - - ignore@5.3.2: - resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} - engines: {node: '>= 4'} - - import-fresh@3.3.0: - resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} - engines: {node: '>=6'} - - importx@0.4.4: - resolution: {integrity: sha512-Lo1pukzAREqrBnnHC+tj+lreMTAvyxtkKsMxLY8H15M/bvLl54p3YuoTI70Tz7Il0AsgSlD7Lrk/FaApRcBL7w==} - - imurmurhash@0.1.4: - resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} - engines: {node: '>=0.8.19'} - - indent-string@4.0.0: - resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} - engines: {node: '>=8'} - - internal-ip@8.0.0: - resolution: {integrity: sha512-e6c3zxr9COnnc29PIz9LffmALOt0XhIJdR7f83DyHcQksL3B40KGmU3Sr1lrHja3i7Zyqo+AbwKZ+nZiMvg/OA==} - engines: {node: '>=16'} - - ip-bigint@7.3.0: - resolution: {integrity: sha512-2qVAe0Q9+Y+5nGvmogwK9y4kefD5Ks5l/IG0Jo1lhU9gIF34jifhqrwXwzkIl+LC594Q6SyAlngs4p890xsXVw==} - engines: {node: '>=16'} - - ip-num@1.5.1: - resolution: {integrity: sha512-QziFxgxq3mjIf5CuwlzXFYscHxgLqdEdJKRo2UJ5GurL5zrSRMzT/O+nK0ABimoFH8MWF8YwIiwECYsHc1LpUQ==} - - ip-regex@5.0.0: - resolution: {integrity: sha512-fOCG6lhoKKakwv+C6KdsOnGvgXnmgfmp0myi3bcNwj3qfwPAxRKWEuFhvEFF7ceYIz6+1jRZ+yguLFAmUNPEfw==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - - is-arrayish@0.2.1: - resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} - - is-binary-path@2.1.0: - resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} - engines: {node: '>=8'} - - is-builtin-module@3.2.1: - resolution: {integrity: sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==} - engines: {node: '>=6'} - - is-core-module@2.15.1: - resolution: {integrity: sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==} - engines: {node: '>= 0.4'} - - is-docker@3.0.0: - resolution: {integrity: sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - hasBin: true - - is-extendable@0.1.1: - resolution: {integrity: sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==} - engines: {node: '>=0.10.0'} - - is-extglob@2.1.1: - resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} - engines: {node: '>=0.10.0'} - - is-fullwidth-code-point@3.0.0: - resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} - engines: {node: '>=8'} - - is-glob@4.0.3: - resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} - engines: {node: '>=0.10.0'} - - is-inside-container@1.0.0: - resolution: {integrity: sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==} - engines: {node: '>=14.16'} - hasBin: true - - is-ip@5.0.1: - resolution: {integrity: sha512-FCsGHdlrOnZQcp0+XT5a+pYowf33itBalCl+7ovNXC/7o5BhIpG14M3OrpPPdBSIQJCm+0M5+9mO7S9VVTTCFw==} - engines: {node: '>=14.16'} - - is-number@7.0.0: - resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} - engines: {node: '>=0.12.0'} - - is-regexp@3.1.0: - resolution: {integrity: sha512-rbku49cWloU5bSMI+zaRaXdQHXnthP6DZ/vLnfdSKyL4zUzuWnomtOEiZZOd+ioQ+avFo/qau3KPTc7Fjy1uPA==} - engines: {node: '>=12'} - - is-stream@3.0.0: - resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - - is-what@4.1.16: - resolution: {integrity: sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==} - engines: {node: '>=12.13'} - - is-wsl@3.1.0: - resolution: {integrity: sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==} - engines: {node: '>=16'} - - isexe@2.0.0: - resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} - - jackspeak@3.4.3: - resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} - - jiti@1.21.6: - resolution: {integrity: sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==} - hasBin: true - - jiti@2.0.0-beta.3: - resolution: {integrity: sha512-pmfRbVRs/7khFrSAYnSiJ8C0D5GvzkE4Ey2pAvUcJsw1ly/p+7ut27jbJrjY79BpAJQJ4gXYFtK6d1Aub+9baQ==} - hasBin: true - - js-tokens@4.0.0: - resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} - - js-tokens@9.0.0: - resolution: {integrity: sha512-WriZw1luRMlmV3LGJaR6QOJjWwgLUTf89OwT2lUOyjX2dJGBwgmIkbcz+7WFZjrZM635JOIR517++e/67CP9dQ==} - - js-yaml@3.14.1: - resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} - hasBin: true - - js-yaml@4.1.0: - resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} - hasBin: true - - jsdoc-type-pratt-parser@4.1.0: - resolution: {integrity: sha512-Hicd6JK5Njt2QB6XYFS7ok9e37O8AYk3jTcppG4YVQnYjOemymvTcmc7OWsmq/Qqj5TdRFO5/x/tIPmBeRtGHg==} - engines: {node: '>=12.0.0'} - - jsesc@0.5.0: - resolution: {integrity: sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==} - hasBin: true - - jsesc@3.0.2: - resolution: {integrity: sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==} - engines: {node: '>=6'} - hasBin: true - - json-buffer@3.0.1: - resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} - - json-parse-even-better-errors@2.3.1: - resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} - - json-schema-traverse@0.4.1: - resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} - - json-stable-stringify-without-jsonify@1.0.1: - resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} - - json5@2.2.3: - resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} - engines: {node: '>=6'} - hasBin: true - - jsonc-eslint-parser@2.4.0: - resolution: {integrity: sha512-WYDyuc/uFcGp6YtM2H0uKmUwieOuzeE/5YocFJLnLfclZ4inf3mRn8ZVy1s7Hxji7Jxm6Ss8gqpexD/GlKoGgg==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - - jsonfile@6.1.0: - resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} - - keyv@4.5.4: - resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} - - kind-of@6.0.3: - resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} - engines: {node: '>=0.10.0'} - - kolorist@1.8.0: - resolution: {integrity: sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==} - - levn@0.4.1: - resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} - engines: {node: '>= 0.8.0'} - - lilconfig@2.1.0: - resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==} - engines: {node: '>=10'} - - lilconfig@3.1.2: - resolution: {integrity: sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==} - engines: {node: '>=14'} - - 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==} - - load-tsconfig@0.2.5: - resolution: {integrity: sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - - local-pkg@0.5.0: - resolution: {integrity: sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg==} - engines: {node: '>=14'} - - locate-path@5.0.0: - resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} - engines: {node: '>=8'} - - locate-path@6.0.0: - resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} - engines: {node: '>=10'} - - lodash.merge@4.6.2: - resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} - - lodash@4.17.21: - resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} - - longest-streak@3.1.0: - resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==} - - lru-cache@10.4.3: - resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} - - lru-cache@5.1.1: - resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} - - magic-string-ast@0.6.2: - resolution: {integrity: sha512-oN3Bcd7ZVt+0VGEs7402qR/tjgjbM7kPlH/z7ufJnzTLVBzXJITRHOJiwMmmYMgZfdoWQsfQcY+iKlxiBppnMA==} - engines: {node: '>=16.14.0'} - - magic-string@0.30.12: - resolution: {integrity: sha512-Ea8I3sQMVXr8JhN4z+H/d8zwo+tYDgHE9+5G4Wnrwhs0gaK9fXTKx0Tw5Xwsd/bCPTTZNRAdpyzvoeORe9LYpw==} - - make-synchronized@0.2.9: - resolution: {integrity: sha512-4wczOs8SLuEdpEvp3vGo83wh8rjJ78UsIk7DIX5fxdfmfMJGog4bQzxfvOwq7Q3yCHLC4jp1urPHIxRS/A93gA==} - - markdown-it@14.1.0: - resolution: {integrity: sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==} - hasBin: true - - markdown-table@3.0.3: - resolution: {integrity: sha512-Z1NL3Tb1M9wH4XESsCDEksWoKTdlUafKc4pt0GRwjUyXaCFZ+dc3g2erqB6zm3szA2IUSi7VnPI+o/9jnxh9hw==} - - mdast-util-find-and-replace@3.0.1: - resolution: {integrity: sha512-SG21kZHGC3XRTSUhtofZkBzZTJNM5ecCi0SK2IMKmSXR8vO3peL+kb1O0z7Zl83jKtutG4k5Wv/W7V3/YHvzPA==} - - mdast-util-from-markdown@2.0.1: - resolution: {integrity: sha512-aJEUyzZ6TzlsX2s5B4Of7lN7EQtAxvtradMMglCQDyaTFgse6CmtmdJ15ElnVRlCg1vpNyVtbem0PWzlNieZsA==} - - mdast-util-gfm-autolink-literal@2.0.1: - resolution: {integrity: sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==} - - mdast-util-gfm-footnote@2.0.0: - resolution: {integrity: sha512-5jOT2boTSVkMnQ7LTrd6n/18kqwjmuYqo7JUPe+tRCY6O7dAuTFMtTPauYYrMPpox9hlN0uOx/FL8XvEfG9/mQ==} - - mdast-util-gfm-strikethrough@2.0.0: - resolution: {integrity: sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==} - - mdast-util-gfm-table@2.0.0: - resolution: {integrity: sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==} - - mdast-util-gfm-task-list-item@2.0.0: - resolution: {integrity: sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==} - - mdast-util-gfm@3.0.0: - resolution: {integrity: sha512-dgQEX5Amaq+DuUqf26jJqSK9qgixgd6rYDHAv4aTBuA92cTknZlKpPfa86Z/s8Dj8xsAQpFfBmPUHWJBWqS4Bw==} - - mdast-util-phrasing@4.1.0: - resolution: {integrity: sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==} - - mdast-util-to-markdown@2.1.0: - resolution: {integrity: sha512-SR2VnIEdVNCJbP6y7kVTJgPLifdr8WEU440fQec7qHoHOUz/oJ2jmNRqdDQ3rbiStOXb2mCDGTuwsK5OPUgYlQ==} - - mdast-util-to-string@4.0.0: - resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==} - - mdurl@2.0.0: - resolution: {integrity: sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==} - - merge-stream@2.0.0: - resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} - - merge2@1.4.1: - resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} - engines: {node: '>= 8'} - - micromark-core-commonmark@2.0.1: - resolution: {integrity: sha512-CUQyKr1e///ZODyD1U3xit6zXwy1a8q2a1S1HKtIlmgvurrEpaw/Y9y6KSIbF8P59cn/NjzHyO+Q2fAyYLQrAA==} - - micromark-extension-gfm-autolink-literal@2.1.0: - resolution: {integrity: sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==} - - micromark-extension-gfm-footnote@2.1.0: - resolution: {integrity: sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==} - - micromark-extension-gfm-strikethrough@2.1.0: - resolution: {integrity: sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==} - - micromark-extension-gfm-table@2.1.0: - resolution: {integrity: sha512-Ub2ncQv+fwD70/l4ou27b4YzfNaCJOvyX4HxXU15m7mpYY+rjuWzsLIPZHJL253Z643RpbcP1oeIJlQ/SKW67g==} - - micromark-extension-gfm-tagfilter@2.0.0: - resolution: {integrity: sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==} - - micromark-extension-gfm-task-list-item@2.1.0: - resolution: {integrity: sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==} - - micromark-extension-gfm@3.0.0: - resolution: {integrity: sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==} - - micromark-factory-destination@2.0.0: - resolution: {integrity: sha512-j9DGrQLm/Uhl2tCzcbLhy5kXsgkHUrjJHg4fFAeoMRwJmJerT9aw4FEhIbZStWN8A3qMwOp1uzHr4UL8AInxtA==} - - micromark-factory-label@2.0.0: - resolution: {integrity: sha512-RR3i96ohZGde//4WSe/dJsxOX6vxIg9TimLAS3i4EhBAFx8Sm5SmqVfR8E87DPSR31nEAjZfbt91OMZWcNgdZw==} - - micromark-factory-space@2.0.0: - resolution: {integrity: sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==} - - micromark-factory-title@2.0.0: - resolution: {integrity: sha512-jY8CSxmpWLOxS+t8W+FG3Xigc0RDQA9bKMY/EwILvsesiRniiVMejYTE4wumNc2f4UbAa4WsHqe3J1QS1sli+A==} - - micromark-factory-whitespace@2.0.0: - resolution: {integrity: sha512-28kbwaBjc5yAI1XadbdPYHX/eDnqaUFVikLwrO7FDnKG7lpgxnvk/XGRhX/PN0mOZ+dBSZ+LgunHS+6tYQAzhA==} - - micromark-util-character@2.1.0: - resolution: {integrity: sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==} - - micromark-util-chunked@2.0.0: - resolution: {integrity: sha512-anK8SWmNphkXdaKgz5hJvGa7l00qmcaUQoMYsBwDlSKFKjc6gjGXPDw3FNL3Nbwq5L8gE+RCbGqTw49FK5Qyvg==} - - micromark-util-classify-character@2.0.0: - resolution: {integrity: sha512-S0ze2R9GH+fu41FA7pbSqNWObo/kzwf8rN/+IGlW/4tC6oACOs8B++bh+i9bVyNnwCcuksbFwsBme5OCKXCwIw==} - - micromark-util-combine-extensions@2.0.0: - resolution: {integrity: sha512-vZZio48k7ON0fVS3CUgFatWHoKbbLTK/rT7pzpJ4Bjp5JjkZeasRfrS9wsBdDJK2cJLHMckXZdzPSSr1B8a4oQ==} - - micromark-util-decode-numeric-character-reference@2.0.1: - resolution: {integrity: sha512-bmkNc7z8Wn6kgjZmVHOX3SowGmVdhYS7yBpMnuMnPzDq/6xwVA604DuOXMZTO1lvq01g+Adfa0pE2UKGlxL1XQ==} - - micromark-util-decode-string@2.0.0: - resolution: {integrity: sha512-r4Sc6leeUTn3P6gk20aFMj2ntPwn6qpDZqWvYmAG6NgvFTIlj4WtrAudLi65qYoaGdXYViXYw2pkmn7QnIFasA==} - - micromark-util-encode@2.0.0: - resolution: {integrity: sha512-pS+ROfCXAGLWCOc8egcBvT0kf27GoWMqtdarNfDcjb6YLuV5cM3ioG45Ys2qOVqeqSbjaKg72vU+Wby3eddPsA==} - - micromark-util-html-tag-name@2.0.0: - resolution: {integrity: sha512-xNn4Pqkj2puRhKdKTm8t1YHC/BAjx6CEwRFXntTaRf/x16aqka6ouVoutm+QdkISTlT7e2zU7U4ZdlDLJd2Mcw==} - - micromark-util-normalize-identifier@2.0.0: - resolution: {integrity: sha512-2xhYT0sfo85FMrUPtHcPo2rrp1lwbDEEzpx7jiH2xXJLqBuy4H0GgXk5ToU8IEwoROtXuL8ND0ttVa4rNqYK3w==} - - micromark-util-resolve-all@2.0.0: - resolution: {integrity: sha512-6KU6qO7DZ7GJkaCgwBNtplXCvGkJToU86ybBAUdavvgsCiG8lSSvYxr9MhwmQ+udpzywHsl4RpGJsYWG1pDOcA==} - - micromark-util-sanitize-uri@2.0.0: - resolution: {integrity: sha512-WhYv5UEcZrbAtlsnPuChHUAsu/iBPOVaEVsntLBIdpibO0ddy8OzavZz3iL2xVvBZOpolujSliP65Kq0/7KIYw==} - - micromark-util-subtokenize@2.0.1: - resolution: {integrity: sha512-jZNtiFl/1aY73yS3UGQkutD0UbhTt68qnRpw2Pifmz5wV9h8gOVsN70v+Lq/f1rKaU/W8pxRe8y8Q9FX1AOe1Q==} - - micromark-util-symbol@2.0.0: - resolution: {integrity: sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==} - - micromark-util-types@2.0.0: - resolution: {integrity: sha512-oNh6S2WMHWRZrmutsRmDDfkzKtxF+bc2VxLC9dvtrDIRFln627VsFP6fLMgTryGDljgLPjkrzQSDcPrjPyDJ5w==} - - micromark@4.0.0: - resolution: {integrity: sha512-o/sd0nMof8kYff+TqcDx3VSrgBTcZpSvYcAHIfHhv5VAuNmisCxjhx6YmxS8PFEpb9z5WKWKPdzf0jM23ro3RQ==} - - micromatch@4.0.8: - resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} - engines: {node: '>=8.6'} - - mimic-fn@4.0.0: - resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} - engines: {node: '>=12'} - - min-indent@1.0.1: - resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} - engines: {node: '>=4'} - - minimatch@3.1.2: - resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} - - minimatch@9.0.5: - resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} - engines: {node: '>=16 || 14 >=14.17'} - - minipass@7.1.2: - resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} - engines: {node: '>=16 || 14 >=14.17'} - - mitt@3.0.1: - resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==} - - mlly@1.7.2: - resolution: {integrity: sha512-tN3dvVHYVz4DhSXinXIk7u9syPYaJvio118uomkovAtWBT+RdbP6Lfh/5Lvo519YMmwBafwlh20IPTXIStscpA==} - - mrmime@2.0.0: - resolution: {integrity: sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw==} - engines: {node: '>=10'} - - ms@2.1.3: - resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} - - muggle-string@0.4.1: - resolution: {integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==} - - mz@2.7.0: - resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} - - nanoid@3.3.7: - resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} - engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} - hasBin: true - - natural-compare-lite@1.4.0: - resolution: {integrity: sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==} - - natural-compare@1.4.0: - resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} - - node-releases@2.0.18: - resolution: {integrity: sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==} - - normalize-package-data@2.5.0: - resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==} - - normalize-path@3.0.0: - resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} - engines: {node: '>=0.10.0'} - - normalize-range@0.1.2: - resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==} - engines: {node: '>=0.10.0'} - - npm-run-path@5.3.0: - resolution: {integrity: sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - - nth-check@2.1.1: - resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} - - object-assign@4.1.1: - resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} - engines: {node: '>=0.10.0'} - - object-hash@3.0.0: - resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==} - engines: {node: '>= 6'} - - onetime@6.0.0: - resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} - engines: {node: '>=12'} - - open@10.1.0: - resolution: {integrity: sha512-mnkeQ1qP5Ue2wd+aivTD3NHd/lZ96Lu0jgf0pwktLPtx6cTZiH7tyeGRRHs0zX0rbrahXPnXlUnbeXyaBBuIaw==} - engines: {node: '>=18'} - - optionator@0.9.4: - resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} - engines: {node: '>= 0.8.0'} - - p-event@5.0.1: - resolution: {integrity: sha512-dd589iCQ7m1L0bmC5NLlVYfy3TbBEsMUfWx9PyAgPeIcFZ/E2yaTZ4Rz4MiBmmJShviiftHVXOqfnfzJ6kyMrQ==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - - p-limit@2.3.0: - resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} - engines: {node: '>=6'} - - p-limit@3.1.0: - resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} - engines: {node: '>=10'} - - p-locate@4.1.0: - resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} - engines: {node: '>=8'} - - p-locate@5.0.0: - resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} - engines: {node: '>=10'} - - p-timeout@5.1.0: - resolution: {integrity: sha512-auFDyzzzGZZZdHz3BtET9VEz0SE/uMEAx7uWfGPucfzEwwe/xH0iVeZibQmANYE/hp9T2+UUZT5m+BKyrDp3Ew==} - engines: {node: '>=12'} - - p-try@2.2.0: - resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} - engines: {node: '>=6'} - - package-json-from-dist@1.0.1: - resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} - - package-manager-detector@0.2.2: - resolution: {integrity: sha512-VgXbyrSNsml4eHWIvxxG/nTL4wgybMTXCV2Un/+yEc3aDKKU6nQBZjbeP3Pl3qm9Qg92X/1ng4ffvCeD/zwHgg==} - - parent-module@1.0.1: - resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} - engines: {node: '>=6'} - - parse-gitignore@2.0.0: - resolution: {integrity: sha512-RmVuCHWsfu0QPNW+mraxh/xjQVw/lhUCUru8Zni3Ctq3AoMhpDTq0OVdKS6iesd6Kqb7viCV3isAL43dciOSog==} - engines: {node: '>=14'} - - parse-imports@2.2.1: - resolution: {integrity: sha512-OL/zLggRp8mFhKL0rNORUTR4yBYujK/uU+xZL+/0Rgm2QE4nLO9v8PzEweSJEbMGKmDRjJE4R3IMJlL2di4JeQ==} - engines: {node: '>= 18'} - - parse-json@5.2.0: - resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} - engines: {node: '>=8'} - - path-browserify@1.0.1: - resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} - - path-exists@4.0.0: - resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} - engines: {node: '>=8'} - - path-key@3.1.1: - resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} - engines: {node: '>=8'} - - path-key@4.0.0: - resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} - engines: {node: '>=12'} - - path-parse@1.0.7: - resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} - - path-scurry@1.11.1: - resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} - engines: {node: '>=16 || 14 >=14.18'} - - path-type@4.0.0: - resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} - engines: {node: '>=8'} - - pathe@1.1.2: - resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} - - perfect-debounce@1.0.0: - resolution: {integrity: sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==} - - picocolors@1.1.0: - resolution: {integrity: sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==} - - picomatch@2.3.1: - resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} - engines: {node: '>=8.6'} - - picomatch@4.0.2: - resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==} - engines: {node: '>=12'} - - pify@2.3.0: - resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} - engines: {node: '>=0.10.0'} - - pinia@2.2.4: - resolution: {integrity: sha512-K7ZhpMY9iJ9ShTC0cR2+PnxdQRuwVIsXDO/WIEV/RnMC/vmSoKDTKW/exNQYPI+4ij10UjXqdNiEHwn47McANQ==} - peerDependencies: - '@vue/composition-api': ^1.4.0 - typescript: '>=4.4.4' - vue: ^2.6.14 || ^3.3.0 - peerDependenciesMeta: - '@vue/composition-api': - optional: true - typescript: - optional: true - - pirates@4.0.6: - resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==} - engines: {node: '>= 6'} - - pkg-types@1.2.1: - resolution: {integrity: sha512-sQoqa8alT3nHjGuTjuKgOnvjo4cljkufdtLMnO2LBP/wRwuDlo1tkaEdMxCRhyGRPacv/ztlZgDPm2b7FAmEvw==} - - pluralize@8.0.0: - resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==} - engines: {node: '>=4'} - - postcss-import@15.1.0: - resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==} - engines: {node: '>=14.0.0'} - peerDependencies: - postcss: ^8.0.0 - - postcss-js@4.0.1: - resolution: {integrity: sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==} - engines: {node: ^12 || ^14 || >= 16} - peerDependencies: - postcss: ^8.4.21 - - postcss-load-config@4.0.2: - resolution: {integrity: sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==} - engines: {node: '>= 14'} - peerDependencies: - postcss: '>=8.0.9' - ts-node: '>=9.0.0' - peerDependenciesMeta: - postcss: - optional: true - ts-node: - optional: true - - postcss-nested@6.2.0: - resolution: {integrity: sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==} - engines: {node: '>=12.0'} - peerDependencies: - postcss: ^8.2.14 - - postcss-selector-parser@6.1.2: - resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==} - engines: {node: '>=4'} - - postcss-value-parser@4.2.0: - resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} - - postcss@8.4.47: - resolution: {integrity: sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==} - engines: {node: ^10 || ^12 || >=14} - - prelude-ls@1.2.1: - resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} - engines: {node: '>= 0.8.0'} - - prettier-linter-helpers@1.0.0: - resolution: {integrity: sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==} - engines: {node: '>=6.0.0'} - - prettier@3.3.3: - resolution: {integrity: sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==} - engines: {node: '>=14'} - hasBin: true - - primeflex@3.3.1: - resolution: {integrity: sha512-zaOq3YvcOYytbAmKv3zYc+0VNS9Wg5d37dfxZnveKBFPr7vEIwfV5ydrpiouTft8MVW6qNjfkaQphHSnvgQbpQ==} - - primeicons@7.0.0: - resolution: {integrity: sha512-jK3Et9UzwzTsd6tzl2RmwrVY/b8raJ3QZLzoDACj+oTJ0oX7L9Hy+XnVwgo4QVKlKpnP/Ur13SXV/pVh4LzaDw==} - - primevue@4.1.0: - resolution: {integrity: sha512-iR/RysaTnZeIG3UVxdhazU7MA8nEODOpHk8WSINwYU0WMsA/ZghbchHOD5a/LYuLuZa3V03j7mX4LMKroeV+ag==} - engines: {node: '>=12.11.0'} - - punycode.js@2.3.1: - resolution: {integrity: sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==} - engines: {node: '>=6'} - - punycode@2.3.1: - resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} - engines: {node: '>=6'} - - queue-microtask@1.2.3: - resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} - - read-cache@1.0.0: - resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==} - - read-pkg-up@7.0.1: - resolution: {integrity: sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==} - engines: {node: '>=8'} - - read-pkg@5.2.0: - resolution: {integrity: sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==} - engines: {node: '>=8'} - - readdirp@3.6.0: - resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} - engines: {node: '>=8.10.0'} - - refa@0.12.1: - resolution: {integrity: sha512-J8rn6v4DBb2nnFqkqwy6/NnTYMcgLA+sLr0iIO41qpv0n+ngb7ksag2tMRl0inb1bbO/esUwzW1vbJi7K0sI0g==} - engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} - - regexp-ast-analysis@0.7.1: - resolution: {integrity: sha512-sZuz1dYW/ZsfG17WSAG7eS85r5a0dDsvg+7BiiYR5o6lKCAtUrEwdmRmaGF6rwVj3LcmAeYkOWKEPlbPzN3Y3A==} - engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} - - regexp-tree@0.1.27: - resolution: {integrity: sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==} - hasBin: true - - regjsparser@0.10.0: - resolution: {integrity: sha512-qx+xQGZVsy55CH0a1hiVwHmqjLryfh7wQyF5HO07XJ9f7dQMY/gPQHhlyDkIzJKC+x2fUCpCcUODUUUFrm7SHA==} - hasBin: true - - require-directory@2.1.1: - resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} - engines: {node: '>=0.10.0'} - - resolve-from@4.0.0: - resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} - engines: {node: '>=4'} - - resolve-pkg-maps@1.0.0: - resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} - - resolve.exports@2.0.2: - resolution: {integrity: sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==} - engines: {node: '>=10'} - - resolve@1.22.8: - resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} - hasBin: true - - reusify@1.0.4: - resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} - engines: {iojs: '>=1.0.0', node: '>=0.10.0'} - - rfdc@1.4.1: - resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} - - rollup@4.24.0: - resolution: {integrity: sha512-DOmrlGSXNk1DM0ljiQA+i+o0rSLhtii1je5wgk60j49d1jHT5YYttBv1iWOnYSTG+fZZESUOSNiAl89SIet+Cg==} - engines: {node: '>=18.0.0', npm: '>=8.0.0'} - hasBin: true - - run-applescript@7.0.0: - resolution: {integrity: sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A==} - engines: {node: '>=18'} - - run-parallel@1.2.0: - resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} - - scslre@0.3.0: - resolution: {integrity: sha512-3A6sD0WYP7+QrjbfNA2FN3FsOaGGFoekCVgTyypy53gPxhbkCIjtO6YWgdrfM+n/8sI8JeXZOIxsHjMTNxQ4nQ==} - engines: {node: ^14.0.0 || >=16.0.0} - - scule@1.3.0: - resolution: {integrity: sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g==} - - section-matter@1.0.0: - resolution: {integrity: sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==} - engines: {node: '>=4'} - - semver@5.7.2: - resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==} - hasBin: true - - semver@6.3.1: - resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} - hasBin: true - - semver@7.6.3: - resolution: {integrity: sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==} - engines: {node: '>=10'} - hasBin: true - - shebang-command@2.0.0: - resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} - engines: {node: '>=8'} - - shebang-regex@3.0.0: - resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} - engines: {node: '>=8'} - - signal-exit@3.0.7: - resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} - - signal-exit@4.1.0: - resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} - engines: {node: '>=14'} - - sirv@2.0.4: - resolution: {integrity: sha512-94Bdh3cC2PKrbgSOUqTiGPWVZeSiXfKOVZNJniWoqrWrRkB1CJzBU3NEbiTsPcYy1lDsANA/THzS+9WBiy5nfQ==} - engines: {node: '>= 10'} - - sisteransi@1.0.5: - resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} - - slash@3.0.0: - resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} - engines: {node: '>=8'} - - slashes@3.0.12: - resolution: {integrity: sha512-Q9VME8WyGkc7pJf6QEkj3wE+2CnvZMI+XJhwdTPR8Z/kWQRXi7boAWLDibRPyHRTUTPx5FaU7MsyrjI3yLB4HA==} - - source-map-js@1.2.1: - resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} - engines: {node: '>=0.10.0'} - - source-map@0.6.1: - resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} - engines: {node: '>=0.10.0'} - - spdx-correct@3.2.0: - resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==} - - spdx-exceptions@2.5.0: - resolution: {integrity: sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==} - - spdx-expression-parse@3.0.1: - resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==} - - spdx-expression-parse@4.0.0: - resolution: {integrity: sha512-Clya5JIij/7C6bRR22+tnGXbc4VKlibKSVj2iHvVeX5iMW7s1SIQlqu699JkODJJIhh/pUu8L0/VLh8xflD+LQ==} - - spdx-license-ids@3.0.20: - resolution: {integrity: sha512-jg25NiDV/1fLtSgEgyvVyDunvaNHbuwF9lfNV17gSmPFAlYzdfNBlLtLzXTevwkPj7DhGbmN9VnmJIgLnhvaBw==} - - speakingurl@14.0.1: - resolution: {integrity: sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==} - engines: {node: '>=0.10.0'} - - sprintf-js@1.0.3: - resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} - - stable-hash@0.0.4: - resolution: {integrity: sha512-LjdcbuBeLcdETCrPn9i8AYAZ1eCtu4ECAWtP7UleOiZ9LzVxRzzUZEoZ8zB24nhkQnDWyET0I+3sWokSDS3E7g==} - - string-natural-compare@3.0.1: - resolution: {integrity: sha512-n3sPwynL1nwKi3WJ6AIsClwBMa0zTi54fn2oLU6ndfTSIO05xaznjSf15PcBZU6FNWbmN5Q6cxT4V5hGvB4taw==} - - string-width@4.2.3: - resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} - engines: {node: '>=8'} - - string-width@5.1.2: - resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} - engines: {node: '>=12'} - - strip-ansi@6.0.1: - resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} - engines: {node: '>=8'} - - strip-ansi@7.1.0: - resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} - engines: {node: '>=12'} - - strip-bom-string@1.0.0: - resolution: {integrity: sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==} - engines: {node: '>=0.10.0'} - - strip-final-newline@3.0.0: - resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==} - engines: {node: '>=12'} - - strip-indent@3.0.0: - resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} - engines: {node: '>=8'} - - strip-json-comments@3.1.1: - resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} - engines: {node: '>=8'} - - strip-literal@2.1.0: - resolution: {integrity: sha512-Op+UycaUt/8FbN/Z2TWPBLge3jWrP3xj10f3fnYxf052bKuS3EKs1ZQcVGjnEMdsNVAM+plXRdmjrZ/KgG3Skw==} - - sucrase@3.35.0: - resolution: {integrity: sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==} - engines: {node: '>=16 || 14 >=14.17'} - hasBin: true - - super-regex@0.2.0: - resolution: {integrity: sha512-WZzIx3rC1CvbMDloLsVw0lkZVKJWbrkJ0k1ghKFmcnPrW1+jWbgTkTEWVtD9lMdmI4jZEz40+naBxl1dCUhXXw==} - engines: {node: '>=14.16'} - - superjson@2.2.1: - resolution: {integrity: sha512-8iGv75BYOa0xRJHK5vRLEjE2H/i4lulTjzpUXic3Eg8akftYjkmQDa8JARQ42rlczXyFR3IeRoeFCc7RxHsYZA==} - engines: {node: '>=16'} - - supports-color@5.5.0: - resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} - engines: {node: '>=4'} - - supports-color@7.2.0: - resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} - engines: {node: '>=8'} - - supports-preserve-symlinks-flag@1.0.0: - resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} - engines: {node: '>= 0.4'} - - svg-tags@1.0.0: - resolution: {integrity: sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==} - - synckit@0.6.2: - resolution: {integrity: sha512-Vhf+bUa//YSTYKseDiiEuQmhGCoIF3CVBhunm3r/DQnYiGT4JssmnKQc44BIyOZRK2pKjXXAgbhfmbeoC9CJpA==} - engines: {node: '>=12.20'} - - synckit@0.9.2: - resolution: {integrity: sha512-vrozgXDQwYO72vHjUb/HnFbQx1exDjoKzqx23aXEg2a9VIg2TSFZ8FmeZpTjUCFMYw7mpX4BE2SFu8wI7asYsw==} - engines: {node: ^14.18.0 || >=16.0.0} - - tailwindcss@3.4.13: - resolution: {integrity: sha512-KqjHOJKogOUt5Bs752ykCeiwvi0fKVkr5oqsFNt/8px/tA8scFPIlkygsf6jXrfCqGHz7VflA6+yytWuM+XhFw==} - engines: {node: '>=14.0.0'} - hasBin: true - - tapable@2.2.1: - resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==} - engines: {node: '>=6'} - - text-table@0.2.0: - resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} - - thenify-all@1.6.0: - resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} - engines: {node: '>=0.8'} - - thenify@3.3.1: - resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} - - time-span@5.1.0: - resolution: {integrity: sha512-75voc/9G4rDIJleOo4jPvN4/YC4GRZrY8yy1uU4lwrB3XEQbWve8zXoO5No4eFrGcTAMYyoY67p8jRQdtA1HbA==} - engines: {node: '>=12'} - - tinyexec@0.3.0: - resolution: {integrity: sha512-tVGE0mVJPGb0chKhqmsoosjsS+qUnJVGJpZgsHYQcGoPlG3B51R3PouqTgEGH2Dc9jjFyOqOpix6ZHNMXp1FZg==} - - to-fast-properties@2.0.0: - resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} - engines: {node: '>=4'} - - to-regex-range@5.0.1: - resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} - engines: {node: '>=8.0'} - - toml-eslint-parser@0.10.0: - resolution: {integrity: sha512-khrZo4buq4qVmsGzS5yQjKe/WsFvV8fGfOjDQN0q4iy9FjRfPWRgTFrU8u1R2iu/SfWLhY9WnCi4Jhdrcbtg+g==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - - totalist@3.0.1: - resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} - engines: {node: '>=6'} - - ts-api-utils@1.3.0: - resolution: {integrity: sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==} - engines: {node: '>=16'} - peerDependencies: - typescript: '>=4.2.0' - - ts-interface-checker@0.1.13: - resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} - - tslib@2.7.0: - resolution: {integrity: sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==} - - tsx@4.19.1: - resolution: {integrity: sha512-0flMz1lh74BR4wOvBjuh9olbnwqCPc35OOlfyzHba0Dc+QNUeWX/Gq2YTbnwcWPO3BMd8fkzRVrHcsR+a7z7rA==} - engines: {node: '>=18.0.0'} - hasBin: true - - type-check@0.4.0: - resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} - engines: {node: '>= 0.8.0'} - - type-fest@0.20.2: - resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} - engines: {node: '>=10'} - - type-fest@0.6.0: - resolution: {integrity: sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==} - engines: {node: '>=8'} - - type-fest@0.8.1: - resolution: {integrity: sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==} - engines: {node: '>=8'} - - typescript@5.6.3: - resolution: {integrity: sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==} - engines: {node: '>=14.17'} - hasBin: true - - uc.micro@2.1.0: - resolution: {integrity: sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==} - - ufo@1.5.4: - resolution: {integrity: sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ==} - - unconfig@0.5.5: - resolution: {integrity: sha512-VQZ5PT9HDX+qag0XdgQi8tJepPhXiR/yVOkn707gJDKo31lGjRilPREiQJ9Z6zd/Ugpv6ZvO5VxVIcatldYcNQ==} - - undici-types@6.19.8: - resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==} - - unimport@3.13.1: - resolution: {integrity: sha512-nNrVzcs93yrZQOW77qnyOVHtb68LegvhYFwxFMfuuWScmwQmyVCG/NBuN8tYsaGzgQUVYv34E/af+Cc9u4og4A==} - - unist-util-is@6.0.0: - resolution: {integrity: sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==} - - unist-util-stringify-position@4.0.0: - resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==} - - unist-util-visit-parents@6.0.1: - resolution: {integrity: sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==} - - unist-util-visit@5.0.0: - resolution: {integrity: sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==} - - universalify@2.0.1: - resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} - engines: {node: '>= 10.0.0'} - - unplugin-auto-import@0.18.3: - resolution: {integrity: sha512-q3FUtGQjYA2e+kb1WumyiQMjHM27MrTQ05QfVwtLRVhyYe+KF6TblBYaEX9L6Z0EibsqaXAiW+RFfkcQpfaXzg==} - engines: {node: '>=14'} - peerDependencies: - '@nuxt/kit': ^3.2.2 - '@vueuse/core': '*' - peerDependenciesMeta: - '@nuxt/kit': - optional: true - '@vueuse/core': - optional: true - - unplugin-combine@1.0.3: - resolution: {integrity: sha512-vCpXdYCTcGwRGv7iF/COh7dupqyIrRxwe5kTKF3ZiVnO4toyvU+tpoTj570Bf9SpJG4JspGnfjcZIU6SBIKryA==} - engines: {node: '>=16.14.0'} - peerDependencies: - '@rspack/core': '*' - esbuild: '>=0.13' - rolldown: '*' - rollup: ^3.2.0 || ^4.0.0 - vite: ^2.3.0 || ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0-0 - webpack: 4 || 5 - peerDependenciesMeta: - '@rspack/core': - optional: true - esbuild: - optional: true - rolldown: - optional: true - rollup: - optional: true - vite: - optional: true - webpack: - optional: true - - unplugin-vue-components@0.27.4: - resolution: {integrity: sha512-1XVl5iXG7P1UrOMnaj2ogYa5YTq8aoh5jwDPQhemwO/OrXW+lPQKDXd1hMz15qxQPxgb/XXlbgo3HQ2rLEbmXQ==} - engines: {node: '>=14'} - peerDependencies: - '@babel/parser': ^7.15.8 - '@nuxt/kit': ^3.2.2 - vue: 2 || 3 - peerDependenciesMeta: - '@babel/parser': - optional: true - '@nuxt/kit': - optional: true - - unplugin-vue-define-options@1.5.1: - resolution: {integrity: sha512-Ss+sHK0D98UPrzrJ1Bh7QmZtdymvGn7IVniB/Y1vsWQiKNOznjN8XLo228AfaHSFuIfx5x7wuNDqKTL5xBqhgw==} - engines: {node: '>=16.14.0'} - - unplugin-vue-macros@2.12.3: - resolution: {integrity: sha512-qzCMjgdN66amHYHb6IqXESeKOZfL058yoNyaOqhtLS98Xz1VU5C4q3Gsry0GHkkkVXfe2ckfvpP4u9CzWCUhGw==} - engines: {node: '>=16.14.0'} - peerDependencies: - vue: ^2.7.0 || ^3.2.25 - - unplugin-vue-markdown@0.26.2: - resolution: {integrity: sha512-FjmhLZ+RRx7PFmfBCTwNUZLAj0Y9z0y/j79rTgYuXH9u+K6tZBFB+GpFFBm+4yMQ0la3MNCl7KHbaSvfna2bEA==} - peerDependencies: - vite: ^2.0.0 || ^3.0.0-0 || ^4.0.0-0 || ^5.0.0-0 - - unplugin-vue-router@0.10.8: - resolution: {integrity: sha512-xi+eLweYAqolIoTRSmumbi6Yx0z5M0PLvl+NFNVWHJgmE2ByJG1SZbrn+TqyuDtIyln20KKgq8tqmL7aLoiFjw==} - peerDependencies: - vue-router: ^4.4.0 - peerDependenciesMeta: - vue-router: - optional: true - - unplugin@1.14.1: - resolution: {integrity: sha512-lBlHbfSFPToDYp9pjXlUEFVxYLaue9f9T1HC+4OHlmj+HnMDdz9oZY+erXfoCe/5V/7gKUSY2jpXPb9S7f0f/w==} - engines: {node: '>=14.0.0'} - peerDependencies: - webpack-sources: ^3 - peerDependenciesMeta: - webpack-sources: - optional: true - - update-browserslist-db@1.1.1: - resolution: {integrity: sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==} - hasBin: true - peerDependencies: - browserslist: '>= 4.21.0' - - uri-js@4.4.1: - resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} - - util-deprecate@1.0.2: - resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} - - uuid@10.0.0: - resolution: {integrity: sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==} - hasBin: true - - validate-npm-package-license@3.0.4: - resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} - - vite-hot-client@0.2.3: - resolution: {integrity: sha512-rOGAV7rUlUHX89fP2p2v0A2WWvV3QMX2UYq0fRqsWSvFvev4atHWqjwGoKaZT1VTKyLGk533ecu3eyd0o59CAg==} - peerDependencies: - vite: ^2.6.0 || ^3.0.0 || ^4.0.0 || ^5.0.0-0 - - vite-plugin-inspect@0.8.7: - resolution: {integrity: sha512-/XXou3MVc13A5O9/2Nd6xczjrUwt7ZyI9h8pTnUMkr5SshLcb0PJUOVq2V+XVkdeU4njsqAtmK87THZuO2coGA==} - engines: {node: '>=14'} - peerDependencies: - '@nuxt/kit': '*' - vite: ^3.1.0 || ^4.0.0 || ^5.0.0-0 - peerDependenciesMeta: - '@nuxt/kit': - optional: true - - vite-plugin-vue-devtools@7.4.6: - resolution: {integrity: sha512-lOKur3qovCB3BQStL0qfHEoIusqya1ngfxfWuqn9DTa6h9rlw6+S3PV4geOP5YBGYQ4NW1hRX70OD8I+sYr1dA==} - engines: {node: '>=v14.21.3'} - peerDependencies: - vite: ^3.1.0 || ^4.0.0-0 || ^5.0.0-0 - - vite-plugin-vue-inspector@5.2.0: - resolution: {integrity: sha512-wWxyb9XAtaIvV/Lr7cqB1HIzmHZFVUJsTNm3yAxkS87dgh/Ky4qr2wDEWNxF23fdhVa3jQ8MZREpr4XyiuaRqA==} - peerDependencies: - vite: ^3.0.0-0 || ^4.0.0-0 || ^5.0.0-0 - - vite-plugin-vue-layouts@0.11.0: - resolution: {integrity: sha512-uh6NW7lt+aOXujK4eHfiNbeo55K9OTuB7fnv+5RVc4OBn/cZull6ThXdYH03JzKanUfgt6QZ37NbbtJ0og59qw==} - peerDependencies: - vite: ^4.0.0 || ^5.0.0 - vue: ^3.2.4 - vue-router: ^4.0.11 - - vite@5.4.8: - resolution: {integrity: sha512-FqrItQ4DT1NC4zCUqMB4c4AZORMKIa0m8/URVCZ77OZ/QSNeJ54bU1vrFADbDsuwfIPcgknRkmqakQcgnL4GiQ==} - engines: {node: ^18.0.0 || >=20.0.0} - hasBin: true - peerDependencies: - '@types/node': ^18.0.0 || >=20.0.0 - less: '*' - lightningcss: ^1.21.0 - sass: '*' - sass-embedded: '*' - stylus: '*' - sugarss: '*' - terser: ^5.4.0 - peerDependenciesMeta: - '@types/node': - optional: true - less: - optional: true - lightningcss: - optional: true - sass: - optional: true - sass-embedded: - optional: true - stylus: - optional: true - sugarss: - optional: true - terser: - optional: true - - vscode-uri@3.0.8: - resolution: {integrity: sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw==} - - vue-demi@0.14.10: - resolution: {integrity: sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==} - engines: {node: '>=12'} - hasBin: true - peerDependencies: - '@vue/composition-api': ^1.0.0-rc.1 - vue: ^3.0.0-0 || ^2.6.0 - peerDependenciesMeta: - '@vue/composition-api': - optional: true - - vue-eslint-parser@9.4.3: - resolution: {integrity: sha512-2rYRLWlIpaiN8xbPiDyXZXRgLGOtWxERV7ND5fFAv5qo1D2N9Fu9MNajBNc6o13lZ+24DAWCkQCvj4klgmcITg==} - engines: {node: ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: '>=6.0.0' - - vue-i18n@10.0.4: - resolution: {integrity: sha512-1xkzVxqBLk2ZFOmeI+B5r1J7aD/WtNJ4j9k2mcFcQo5BnOmHBmD7z4/oZohh96AAaRZ4Q7mNQvxc9h+aT+Md3w==} - engines: {node: '>= 16'} - peerDependencies: - vue: ^3.0.0 - - vue-router@4.4.5: - resolution: {integrity: sha512-4fKZygS8cH1yCyuabAXGUAsyi1b2/o/OKgu/RUb+znIYOxPRxdkytJEx+0wGcpBE1pX6vUgh5jwWOKRGvuA/7Q==} - peerDependencies: - vue: ^3.2.0 - - vue-tsc@2.1.6: - resolution: {integrity: sha512-f98dyZp5FOukcYmbFpuSCJ4Z0vHSOSmxGttZJCsFeX0M4w/Rsq0s4uKXjcSRsZqsRgQa6z7SfuO+y0HVICE57Q==} - hasBin: true - peerDependencies: - typescript: '>=5.0.0' - - vue@3.4.38: - resolution: {integrity: sha512-f0ZgN+mZ5KFgVv9wz0f4OgVKukoXtS3nwET4c2vLBGQR50aI8G0cqbFtLlX9Yiyg3LFGBitruPHt2PxwTduJEw==} - peerDependencies: - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - - vue@3.5.11: - resolution: {integrity: sha512-/8Wurrd9J3lb72FTQS7gRMNQD4nztTtKPmuDuPuhqXmmpD6+skVjAeahNpVzsuky6Sy9gy7wn8UadqPtt9SQIg==} - peerDependencies: - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - - webpack-virtual-modules@0.6.2: - resolution: {integrity: sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==} - - which@2.0.2: - resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} - engines: {node: '>= 8'} - hasBin: true - - word-wrap@1.2.5: - resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} - engines: {node: '>=0.10.0'} - - wrap-ansi@7.0.0: - resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} - engines: {node: '>=10'} - - wrap-ansi@8.1.0: - resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} - engines: {node: '>=12'} - - xml-name-validator@4.0.0: - resolution: {integrity: sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==} - engines: {node: '>=12'} - - y18n@5.0.8: - resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} - engines: {node: '>=10'} - - yallist@3.1.1: - resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} - - yaml-eslint-parser@1.2.3: - resolution: {integrity: sha512-4wZWvE398hCP7O8n3nXKu/vdq1HcH01ixYlCREaJL5NUMwQ0g3MaGFUBNSlmBtKmhbtVG/Cm6lyYmSVTEVil8A==} - engines: {node: ^14.17.0 || >=16.0.0} - - yaml@2.5.1: - resolution: {integrity: sha512-bLQOjaX/ADgQ20isPJRvF0iRUHIxVhYvr53Of7wGcWlO2jvtUlH5m87DsmulFVxRpNLOnI4tB6p/oh8D7kpn9Q==} - engines: {node: '>= 14'} - hasBin: true - - yargs-parser@21.1.1: - resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} - engines: {node: '>=12'} - - yargs@17.7.2: - resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} - engines: {node: '>=12'} - - yocto-queue@0.1.0: - resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} - engines: {node: '>=10'} - - zwitch@2.0.4: - resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} - -snapshots: - - '@alloc/quick-lru@5.2.0': {} - - '@ampproject/remapping@2.3.0': - dependencies: - '@jridgewell/gen-mapping': 0.3.5 - '@jridgewell/trace-mapping': 0.3.25 - - '@antfu/eslint-config@3.7.3(@typescript-eslint/utils@8.8.1(eslint@9.12.0(jiti@1.21.6))(typescript@5.6.3))(@vue/compiler-sfc@3.5.11)(eslint-plugin-format@0.1.2(eslint@9.12.0(jiti@1.21.6)))(eslint@9.12.0(jiti@1.21.6))(typescript@5.6.3)': - dependencies: - '@antfu/install-pkg': 0.4.1 - '@clack/prompts': 0.7.0 - '@eslint-community/eslint-plugin-eslint-comments': 4.4.0(eslint@9.12.0(jiti@1.21.6)) - '@eslint/markdown': 6.2.0 - '@stylistic/eslint-plugin': 2.9.0(eslint@9.12.0(jiti@1.21.6))(typescript@5.6.3) - '@typescript-eslint/eslint-plugin': 8.8.1(@typescript-eslint/parser@8.8.1(eslint@9.12.0(jiti@1.21.6))(typescript@5.6.3))(eslint@9.12.0(jiti@1.21.6))(typescript@5.6.3) - '@typescript-eslint/parser': 8.8.1(eslint@9.12.0(jiti@1.21.6))(typescript@5.6.3) - '@vitest/eslint-plugin': 1.1.7(@typescript-eslint/utils@8.8.1(eslint@9.12.0(jiti@1.21.6))(typescript@5.6.3))(eslint@9.12.0(jiti@1.21.6))(typescript@5.6.3) - eslint: 9.12.0(jiti@1.21.6) - eslint-config-flat-gitignore: 0.3.0(eslint@9.12.0(jiti@1.21.6)) - eslint-flat-config-utils: 0.4.0 - eslint-merge-processors: 0.1.0(eslint@9.12.0(jiti@1.21.6)) - eslint-plugin-antfu: 2.7.0(eslint@9.12.0(jiti@1.21.6)) - eslint-plugin-command: 0.2.6(eslint@9.12.0(jiti@1.21.6)) - eslint-plugin-import-x: 4.3.1(eslint@9.12.0(jiti@1.21.6))(typescript@5.6.3) - eslint-plugin-jsdoc: 50.3.1(eslint@9.12.0(jiti@1.21.6)) - eslint-plugin-jsonc: 2.16.0(eslint@9.12.0(jiti@1.21.6)) - eslint-plugin-n: 17.11.1(eslint@9.12.0(jiti@1.21.6)) - eslint-plugin-no-only-tests: 3.3.0 - eslint-plugin-perfectionist: 3.8.0(eslint@9.12.0(jiti@1.21.6))(typescript@5.6.3)(vue-eslint-parser@9.4.3(eslint@9.12.0(jiti@1.21.6))) - eslint-plugin-regexp: 2.6.0(eslint@9.12.0(jiti@1.21.6)) - eslint-plugin-toml: 0.11.1(eslint@9.12.0(jiti@1.21.6)) - eslint-plugin-unicorn: 55.0.0(eslint@9.12.0(jiti@1.21.6)) - eslint-plugin-unused-imports: 4.1.4(@typescript-eslint/eslint-plugin@8.8.1(@typescript-eslint/parser@8.8.1(eslint@9.12.0(jiti@1.21.6))(typescript@5.6.3))(eslint@9.12.0(jiti@1.21.6))(typescript@5.6.3))(eslint@9.12.0(jiti@1.21.6)) - eslint-plugin-vue: 9.29.0(eslint@9.12.0(jiti@1.21.6)) - eslint-plugin-yml: 1.14.0(eslint@9.12.0(jiti@1.21.6)) - eslint-processor-vue-blocks: 0.1.2(@vue/compiler-sfc@3.5.11)(eslint@9.12.0(jiti@1.21.6)) - globals: 15.11.0 - jsonc-eslint-parser: 2.4.0 - local-pkg: 0.5.0 - parse-gitignore: 2.0.0 - picocolors: 1.1.0 - toml-eslint-parser: 0.10.0 - vue-eslint-parser: 9.4.3(eslint@9.12.0(jiti@1.21.6)) - yaml-eslint-parser: 1.2.3 - yargs: 17.7.2 - optionalDependencies: - eslint-plugin-format: 0.1.2(eslint@9.12.0(jiti@1.21.6)) - transitivePeerDependencies: - - '@typescript-eslint/utils' - - '@vue/compiler-sfc' - - supports-color - - svelte - - typescript - - vitest - - '@antfu/install-pkg@0.4.1': - dependencies: - package-manager-detector: 0.2.2 - tinyexec: 0.3.0 - - '@antfu/utils@0.7.10': {} - - '@babel/code-frame@7.25.7': - dependencies: - '@babel/highlight': 7.25.7 - picocolors: 1.1.0 - - '@babel/compat-data@7.25.8': {} - - '@babel/core@7.25.8': - dependencies: - '@ampproject/remapping': 2.3.0 - '@babel/code-frame': 7.25.7 - '@babel/generator': 7.25.7 - '@babel/helper-compilation-targets': 7.25.7 - '@babel/helper-module-transforms': 7.25.7(@babel/core@7.25.8) - '@babel/helpers': 7.25.7 - '@babel/parser': 7.25.8 - '@babel/template': 7.25.7 - '@babel/traverse': 7.25.7 - '@babel/types': 7.25.8 - convert-source-map: 2.0.0 - debug: 4.3.7 - gensync: 1.0.0-beta.2 - json5: 2.2.3 - semver: 6.3.1 - transitivePeerDependencies: - - supports-color - - '@babel/generator@7.25.7': - dependencies: - '@babel/types': 7.25.8 - '@jridgewell/gen-mapping': 0.3.5 - '@jridgewell/trace-mapping': 0.3.25 - jsesc: 3.0.2 - - '@babel/helper-annotate-as-pure@7.25.7': - dependencies: - '@babel/types': 7.25.8 - - '@babel/helper-compilation-targets@7.25.7': - dependencies: - '@babel/compat-data': 7.25.8 - '@babel/helper-validator-option': 7.25.7 - browserslist: 4.24.0 - lru-cache: 5.1.1 - semver: 6.3.1 - - '@babel/helper-create-class-features-plugin@7.25.7(@babel/core@7.25.8)': - dependencies: - '@babel/core': 7.25.8 - '@babel/helper-annotate-as-pure': 7.25.7 - '@babel/helper-member-expression-to-functions': 7.25.7 - '@babel/helper-optimise-call-expression': 7.25.7 - '@babel/helper-replace-supers': 7.25.7(@babel/core@7.25.8) - '@babel/helper-skip-transparent-expression-wrappers': 7.25.7 - '@babel/traverse': 7.25.7 - semver: 6.3.1 - transitivePeerDependencies: - - supports-color - - '@babel/helper-member-expression-to-functions@7.25.7': - dependencies: - '@babel/traverse': 7.25.7 - '@babel/types': 7.25.8 - transitivePeerDependencies: - - supports-color - - '@babel/helper-module-imports@7.25.7': - dependencies: - '@babel/traverse': 7.25.7 - '@babel/types': 7.25.8 - transitivePeerDependencies: - - supports-color - - '@babel/helper-module-transforms@7.25.7(@babel/core@7.25.8)': - dependencies: - '@babel/core': 7.25.8 - '@babel/helper-module-imports': 7.25.7 - '@babel/helper-simple-access': 7.25.7 - '@babel/helper-validator-identifier': 7.25.7 - '@babel/traverse': 7.25.7 - transitivePeerDependencies: - - supports-color - - '@babel/helper-optimise-call-expression@7.25.7': - dependencies: - '@babel/types': 7.25.8 - - '@babel/helper-plugin-utils@7.25.7': {} - - '@babel/helper-replace-supers@7.25.7(@babel/core@7.25.8)': - dependencies: - '@babel/core': 7.25.8 - '@babel/helper-member-expression-to-functions': 7.25.7 - '@babel/helper-optimise-call-expression': 7.25.7 - '@babel/traverse': 7.25.7 - transitivePeerDependencies: - - supports-color - - '@babel/helper-simple-access@7.25.7': - dependencies: - '@babel/traverse': 7.25.7 - '@babel/types': 7.25.8 - transitivePeerDependencies: - - supports-color - - '@babel/helper-skip-transparent-expression-wrappers@7.25.7': - dependencies: - '@babel/traverse': 7.25.7 - '@babel/types': 7.25.8 - transitivePeerDependencies: - - supports-color - - '@babel/helper-string-parser@7.25.7': {} - - '@babel/helper-validator-identifier@7.25.7': {} - - '@babel/helper-validator-option@7.25.7': {} - - '@babel/helpers@7.25.7': - dependencies: - '@babel/template': 7.25.7 - '@babel/types': 7.25.8 - - '@babel/highlight@7.25.7': - dependencies: - '@babel/helper-validator-identifier': 7.25.7 - chalk: 2.4.2 - js-tokens: 4.0.0 - picocolors: 1.1.0 - - '@babel/parser@7.25.8': - dependencies: - '@babel/types': 7.25.8 - - '@babel/plugin-proposal-decorators@7.25.7(@babel/core@7.25.8)': - dependencies: - '@babel/core': 7.25.8 - '@babel/helper-create-class-features-plugin': 7.25.7(@babel/core@7.25.8) - '@babel/helper-plugin-utils': 7.25.7 - '@babel/plugin-syntax-decorators': 7.25.7(@babel/core@7.25.8) - transitivePeerDependencies: - - supports-color - - '@babel/plugin-syntax-decorators@7.25.7(@babel/core@7.25.8)': - dependencies: - '@babel/core': 7.25.8 - '@babel/helper-plugin-utils': 7.25.7 - - '@babel/plugin-syntax-import-attributes@7.25.7(@babel/core@7.25.8)': - dependencies: - '@babel/core': 7.25.8 - '@babel/helper-plugin-utils': 7.25.7 - - '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.25.8)': - dependencies: - '@babel/core': 7.25.8 - '@babel/helper-plugin-utils': 7.25.7 - - '@babel/plugin-syntax-jsx@7.25.7(@babel/core@7.25.8)': - dependencies: - '@babel/core': 7.25.8 - '@babel/helper-plugin-utils': 7.25.7 - - '@babel/plugin-syntax-typescript@7.25.7(@babel/core@7.25.8)': - dependencies: - '@babel/core': 7.25.8 - '@babel/helper-plugin-utils': 7.25.7 - - '@babel/plugin-transform-typescript@7.25.7(@babel/core@7.25.8)': - dependencies: - '@babel/core': 7.25.8 - '@babel/helper-annotate-as-pure': 7.25.7 - '@babel/helper-create-class-features-plugin': 7.25.7(@babel/core@7.25.8) - '@babel/helper-plugin-utils': 7.25.7 - '@babel/helper-skip-transparent-expression-wrappers': 7.25.7 - '@babel/plugin-syntax-typescript': 7.25.7(@babel/core@7.25.8) - transitivePeerDependencies: - - supports-color - - '@babel/template@7.25.7': - dependencies: - '@babel/code-frame': 7.25.7 - '@babel/parser': 7.25.8 - '@babel/types': 7.25.8 - - '@babel/traverse@7.25.7': - dependencies: - '@babel/code-frame': 7.25.7 - '@babel/generator': 7.25.7 - '@babel/parser': 7.25.8 - '@babel/template': 7.25.7 - '@babel/types': 7.25.8 - debug: 4.3.7 - globals: 11.12.0 - transitivePeerDependencies: - - supports-color - - '@babel/types@7.25.8': - dependencies: - '@babel/helper-string-parser': 7.25.7 - '@babel/helper-validator-identifier': 7.25.7 - to-fast-properties: 2.0.0 - - '@clack/core@0.3.4': - dependencies: - picocolors: 1.1.0 - sisteransi: 1.0.5 - - '@clack/prompts@0.7.0': - dependencies: - '@clack/core': 0.3.4 - picocolors: 1.1.0 - sisteransi: 1.0.5 - - '@dprint/formatter@0.3.0': {} - - '@dprint/markdown@0.17.8': {} - - '@dprint/toml@0.6.3': {} - - '@es-joy/jsdoccomment@0.48.0': - dependencies: - comment-parser: 1.4.1 - esquery: 1.6.0 - jsdoc-type-pratt-parser: 4.1.0 - - '@esbuild/aix-ppc64@0.21.5': - optional: true - - '@esbuild/aix-ppc64@0.23.1': - optional: true - - '@esbuild/android-arm64@0.21.5': - optional: true - - '@esbuild/android-arm64@0.23.1': - optional: true - - '@esbuild/android-arm@0.21.5': - optional: true - - '@esbuild/android-arm@0.23.1': - optional: true - - '@esbuild/android-x64@0.21.5': - optional: true - - '@esbuild/android-x64@0.23.1': - optional: true - - '@esbuild/darwin-arm64@0.21.5': - optional: true - - '@esbuild/darwin-arm64@0.23.1': - optional: true - - '@esbuild/darwin-x64@0.21.5': - optional: true - - '@esbuild/darwin-x64@0.23.1': - optional: true - - '@esbuild/freebsd-arm64@0.21.5': - optional: true - - '@esbuild/freebsd-arm64@0.23.1': - optional: true - - '@esbuild/freebsd-x64@0.21.5': - optional: true - - '@esbuild/freebsd-x64@0.23.1': - optional: true - - '@esbuild/linux-arm64@0.21.5': - optional: true - - '@esbuild/linux-arm64@0.23.1': - optional: true - - '@esbuild/linux-arm@0.21.5': - optional: true - - '@esbuild/linux-arm@0.23.1': - optional: true - - '@esbuild/linux-ia32@0.21.5': - optional: true - - '@esbuild/linux-ia32@0.23.1': - optional: true - - '@esbuild/linux-loong64@0.21.5': - optional: true - - '@esbuild/linux-loong64@0.23.1': - optional: true - - '@esbuild/linux-mips64el@0.21.5': - optional: true - - '@esbuild/linux-mips64el@0.23.1': - optional: true - - '@esbuild/linux-ppc64@0.21.5': - optional: true - - '@esbuild/linux-ppc64@0.23.1': - optional: true - - '@esbuild/linux-riscv64@0.21.5': - optional: true - - '@esbuild/linux-riscv64@0.23.1': - optional: true - - '@esbuild/linux-s390x@0.21.5': - optional: true - - '@esbuild/linux-s390x@0.23.1': - optional: true - - '@esbuild/linux-x64@0.21.5': - optional: true - - '@esbuild/linux-x64@0.23.1': - optional: true - - '@esbuild/netbsd-x64@0.21.5': - optional: true - - '@esbuild/netbsd-x64@0.23.1': - optional: true - - '@esbuild/openbsd-arm64@0.23.1': - optional: true - - '@esbuild/openbsd-x64@0.21.5': - optional: true - - '@esbuild/openbsd-x64@0.23.1': - optional: true - - '@esbuild/sunos-x64@0.21.5': - optional: true - - '@esbuild/sunos-x64@0.23.1': - optional: true - - '@esbuild/win32-arm64@0.21.5': - optional: true - - '@esbuild/win32-arm64@0.23.1': - optional: true - - '@esbuild/win32-ia32@0.21.5': - optional: true - - '@esbuild/win32-ia32@0.23.1': - optional: true - - '@esbuild/win32-x64@0.21.5': - optional: true - - '@esbuild/win32-x64@0.23.1': - optional: true - - '@eslint-community/eslint-plugin-eslint-comments@4.4.0(eslint@9.12.0(jiti@1.21.6))': - dependencies: - escape-string-regexp: 4.0.0 - eslint: 9.12.0(jiti@1.21.6) - ignore: 5.3.2 - - '@eslint-community/eslint-utils@4.4.0(eslint@9.12.0(jiti@1.21.6))': - dependencies: - eslint: 9.12.0(jiti@1.21.6) - eslint-visitor-keys: 3.4.3 - - '@eslint-community/regexpp@4.11.1': {} - - '@eslint/compat@1.2.0(eslint@9.12.0(jiti@1.21.6))': - optionalDependencies: - eslint: 9.12.0(jiti@1.21.6) - - '@eslint/config-array@0.18.0': - dependencies: - '@eslint/object-schema': 2.1.4 - debug: 4.3.7 - minimatch: 3.1.2 - transitivePeerDependencies: - - supports-color - - '@eslint/core@0.6.0': {} - - '@eslint/eslintrc@3.1.0': - dependencies: - ajv: 6.12.6 - debug: 4.3.7 - espree: 10.2.0 - globals: 14.0.0 - ignore: 5.3.2 - import-fresh: 3.3.0 - js-yaml: 4.1.0 - minimatch: 3.1.2 - strip-json-comments: 3.1.1 - transitivePeerDependencies: - - supports-color - - '@eslint/js@9.12.0': {} - - '@eslint/markdown@6.2.0': - dependencies: - '@eslint/plugin-kit': 0.2.0 - mdast-util-from-markdown: 2.0.1 - mdast-util-gfm: 3.0.0 - micromark-extension-gfm: 3.0.0 - transitivePeerDependencies: - - supports-color - - '@eslint/object-schema@2.1.4': {} - - '@eslint/plugin-kit@0.2.0': - dependencies: - levn: 0.4.1 - - '@humanfs/core@0.19.0': {} - - '@humanfs/node@0.16.5': - dependencies: - '@humanfs/core': 0.19.0 - '@humanwhocodes/retry': 0.3.1 - - '@humanwhocodes/module-importer@1.0.1': {} - - '@humanwhocodes/retry@0.3.1': {} - - '@intlify/bundle-utils@9.0.0-beta.0(vue-i18n@10.0.4(vue@3.4.38(typescript@5.6.3)))': - dependencies: - '@intlify/message-compiler': 10.0.0 - '@intlify/shared': 10.0.0 - acorn: 8.12.1 - escodegen: 2.1.0 - estree-walker: 2.0.2 - jsonc-eslint-parser: 2.4.0 - mlly: 1.7.2 - source-map-js: 1.2.1 - yaml-eslint-parser: 1.2.3 - optionalDependencies: - vue-i18n: 10.0.4(vue@3.4.38(typescript@5.6.3)) - - '@intlify/core-base@10.0.4': - dependencies: - '@intlify/message-compiler': 10.0.4 - '@intlify/shared': 10.0.4 - - '@intlify/message-compiler@10.0.0': - dependencies: - '@intlify/shared': 10.0.0 - source-map-js: 1.2.1 - - '@intlify/message-compiler@10.0.4': - dependencies: - '@intlify/shared': 10.0.4 - source-map-js: 1.2.1 - - '@intlify/shared@10.0.0': {} - - '@intlify/shared@10.0.4': {} - - '@intlify/unplugin-vue-i18n@5.2.0(@vue/compiler-dom@3.5.11)(eslint@9.12.0(jiti@1.21.6))(rollup@4.24.0)(typescript@5.6.3)(vue-i18n@10.0.4(vue@3.4.38(typescript@5.6.3)))(vue@3.4.38(typescript@5.6.3))': - dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@9.12.0(jiti@1.21.6)) - '@intlify/bundle-utils': 9.0.0-beta.0(vue-i18n@10.0.4(vue@3.4.38(typescript@5.6.3))) - '@intlify/shared': 10.0.0 - '@intlify/vue-i18n-extensions': 7.0.0(@intlify/shared@10.0.0)(@vue/compiler-dom@3.5.11)(vue-i18n@10.0.4(vue@3.4.38(typescript@5.6.3)))(vue@3.4.38(typescript@5.6.3)) - '@rollup/pluginutils': 5.1.2(rollup@4.24.0) - '@typescript-eslint/scope-manager': 7.18.0 - '@typescript-eslint/typescript-estree': 7.18.0(typescript@5.6.3) - debug: 4.3.7 - fast-glob: 3.3.2 - js-yaml: 4.1.0 - json5: 2.2.3 - pathe: 1.1.2 - picocolors: 1.1.0 - source-map-js: 1.2.1 - unplugin: 1.14.1 - vue: 3.4.38(typescript@5.6.3) - optionalDependencies: - vue-i18n: 10.0.4(vue@3.4.38(typescript@5.6.3)) - transitivePeerDependencies: - - '@vue/compiler-dom' - - eslint - - rollup - - supports-color - - typescript - - webpack-sources - - '@intlify/vue-i18n-extensions@7.0.0(@intlify/shared@10.0.0)(@vue/compiler-dom@3.5.11)(vue-i18n@10.0.4(vue@3.4.38(typescript@5.6.3)))(vue@3.4.38(typescript@5.6.3))': - dependencies: - '@babel/parser': 7.25.8 - optionalDependencies: - '@intlify/shared': 10.0.0 - '@vue/compiler-dom': 3.5.11 - vue: 3.4.38(typescript@5.6.3) - vue-i18n: 10.0.4(vue@3.4.38(typescript@5.6.3)) - - '@isaacs/cliui@8.0.2': - dependencies: - string-width: 5.1.2 - string-width-cjs: string-width@4.2.3 - strip-ansi: 7.1.0 - strip-ansi-cjs: strip-ansi@6.0.1 - wrap-ansi: 8.1.0 - wrap-ansi-cjs: wrap-ansi@7.0.0 - - '@jridgewell/gen-mapping@0.3.5': - dependencies: - '@jridgewell/set-array': 1.2.1 - '@jridgewell/sourcemap-codec': 1.5.0 - '@jridgewell/trace-mapping': 0.3.25 - - '@jridgewell/resolve-uri@3.1.2': {} - - '@jridgewell/set-array@1.2.1': {} - - '@jridgewell/sourcemap-codec@1.5.0': {} - - '@jridgewell/trace-mapping@0.3.25': - dependencies: - '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.5.0 - - '@mdit-vue/plugin-component@2.1.3': - dependencies: - '@types/markdown-it': 14.1.2 - markdown-it: 14.1.0 - - '@mdit-vue/plugin-frontmatter@2.1.3': - dependencies: - '@mdit-vue/types': 2.1.0 - '@types/markdown-it': 14.1.2 - gray-matter: 4.0.3 - markdown-it: 14.1.0 - - '@mdit-vue/types@2.1.0': {} - - '@nodelib/fs.scandir@2.1.5': - dependencies: - '@nodelib/fs.stat': 2.0.5 - run-parallel: 1.2.0 - - '@nodelib/fs.stat@2.0.5': {} - - '@nodelib/fs.walk@1.2.8': - dependencies: - '@nodelib/fs.scandir': 2.1.5 - fastq: 1.17.1 - - '@pkgjs/parseargs@0.11.0': - optional: true - - '@pkgr/core@0.1.1': {} - - '@polka/url@1.0.0-next.28': {} - - '@primeuix/styled@0.2.0': - dependencies: - '@primeuix/utils': 0.2.0 - - '@primeuix/utils@0.2.0': {} - - '@primevue/auto-import-resolver@4.1.0': - dependencies: - '@primevue/metadata': 4.1.0 - - '@primevue/core@4.1.0(vue@3.4.38(typescript@5.6.3))': - dependencies: - '@primeuix/styled': 0.2.0 - '@primeuix/utils': 0.2.0 - vue: 3.4.38(typescript@5.6.3) - - '@primevue/icons@4.1.0(vue@3.4.38(typescript@5.6.3))': - dependencies: - '@primeuix/utils': 0.2.0 - '@primevue/core': 4.1.0(vue@3.4.38(typescript@5.6.3)) - transitivePeerDependencies: - - vue - - '@primevue/metadata@4.1.0': {} - - '@primevue/themes@4.1.0': - dependencies: - '@primeuix/styled': 0.2.0 - - '@rollup/pluginutils@5.1.2(rollup@4.24.0)': - dependencies: - '@types/estree': 1.0.6 - estree-walker: 2.0.2 - picomatch: 2.3.1 - optionalDependencies: - rollup: 4.24.0 - - '@rollup/rollup-android-arm-eabi@4.24.0': - optional: true - - '@rollup/rollup-android-arm64@4.24.0': - optional: true - - '@rollup/rollup-darwin-arm64@4.24.0': - optional: true - - '@rollup/rollup-darwin-x64@4.24.0': - optional: true - - '@rollup/rollup-linux-arm-gnueabihf@4.24.0': - optional: true - - '@rollup/rollup-linux-arm-musleabihf@4.24.0': - optional: true - - '@rollup/rollup-linux-arm64-gnu@4.24.0': - optional: true - - '@rollup/rollup-linux-arm64-musl@4.24.0': - optional: true - - '@rollup/rollup-linux-powerpc64le-gnu@4.24.0': - optional: true - - '@rollup/rollup-linux-riscv64-gnu@4.24.0': - optional: true - - '@rollup/rollup-linux-s390x-gnu@4.24.0': - optional: true - - '@rollup/rollup-linux-x64-gnu@4.24.0': - optional: true - - '@rollup/rollup-linux-x64-musl@4.24.0': - optional: true - - '@rollup/rollup-win32-arm64-msvc@4.24.0': - optional: true - - '@rollup/rollup-win32-ia32-msvc@4.24.0': - optional: true - - '@rollup/rollup-win32-x64-msvc@4.24.0': - optional: true - - '@stylistic/eslint-plugin@2.9.0(eslint@9.12.0(jiti@1.21.6))(typescript@5.6.3)': - dependencies: - '@typescript-eslint/utils': 8.8.1(eslint@9.12.0(jiti@1.21.6))(typescript@5.6.3) - eslint: 9.12.0(jiti@1.21.6) - eslint-visitor-keys: 4.1.0 - espree: 10.2.0 - estraverse: 5.3.0 - picomatch: 4.0.2 - transitivePeerDependencies: - - supports-color - - typescript - - '@tauri-apps/api@2.0.0-rc.0': {} - - '@tauri-apps/api@2.0.2': {} - - '@tauri-apps/cli-darwin-arm64@2.0.0-rc.3': - optional: true - - '@tauri-apps/cli-darwin-x64@2.0.0-rc.3': - optional: true - - '@tauri-apps/cli-linux-arm-gnueabihf@2.0.0-rc.3': - optional: true - - '@tauri-apps/cli-linux-arm64-gnu@2.0.0-rc.3': - optional: true - - '@tauri-apps/cli-linux-arm64-musl@2.0.0-rc.3': - optional: true - - '@tauri-apps/cli-linux-x64-gnu@2.0.0-rc.3': - optional: true - - '@tauri-apps/cli-linux-x64-musl@2.0.0-rc.3': - optional: true - - '@tauri-apps/cli-win32-arm64-msvc@2.0.0-rc.3': - optional: true - - '@tauri-apps/cli-win32-ia32-msvc@2.0.0-rc.3': - optional: true - - '@tauri-apps/cli-win32-x64-msvc@2.0.0-rc.3': - optional: true - - '@tauri-apps/cli@2.0.0-rc.3': - optionalDependencies: - '@tauri-apps/cli-darwin-arm64': 2.0.0-rc.3 - '@tauri-apps/cli-darwin-x64': 2.0.0-rc.3 - '@tauri-apps/cli-linux-arm-gnueabihf': 2.0.0-rc.3 - '@tauri-apps/cli-linux-arm64-gnu': 2.0.0-rc.3 - '@tauri-apps/cli-linux-arm64-musl': 2.0.0-rc.3 - '@tauri-apps/cli-linux-x64-gnu': 2.0.0-rc.3 - '@tauri-apps/cli-linux-x64-musl': 2.0.0-rc.3 - '@tauri-apps/cli-win32-arm64-msvc': 2.0.0-rc.3 - '@tauri-apps/cli-win32-ia32-msvc': 2.0.0-rc.3 - '@tauri-apps/cli-win32-x64-msvc': 2.0.0-rc.3 - - '@tauri-apps/plugin-autostart@2.0.0-rc.1': - dependencies: - '@tauri-apps/api': 2.0.2 - - '@tauri-apps/plugin-clipboard-manager@2.0.0-rc.1': - dependencies: - '@tauri-apps/api': 2.0.2 - - '@tauri-apps/plugin-os@2.0.0-rc.1': - dependencies: - '@tauri-apps/api': 2.0.2 - - '@tauri-apps/plugin-process@2.0.0-rc.1': - dependencies: - '@tauri-apps/api': 2.0.2 - - '@tauri-apps/plugin-shell@2.0.0-rc.1': - dependencies: - '@tauri-apps/api': 2.0.2 - - '@types/debug@4.1.12': - dependencies: - '@types/ms': 0.7.34 - - '@types/estree@1.0.6': {} - - '@types/json-schema@7.0.15': {} - - '@types/linkify-it@5.0.0': {} - - '@types/markdown-it@14.1.2': - dependencies: - '@types/linkify-it': 5.0.0 - '@types/mdurl': 2.0.0 - - '@types/mdast@4.0.4': - dependencies: - '@types/unist': 3.0.3 - - '@types/mdurl@2.0.0': {} - - '@types/ms@0.7.34': {} - - '@types/node@22.7.5': - dependencies: - undici-types: 6.19.8 - - '@types/normalize-package-data@2.4.4': {} - - '@types/unist@3.0.3': {} - - '@types/uuid@10.0.0': {} - - '@types/web-bluetooth@0.0.20': {} - - '@typescript-eslint/eslint-plugin@8.8.1(@typescript-eslint/parser@8.8.1(eslint@9.12.0(jiti@1.21.6))(typescript@5.6.3))(eslint@9.12.0(jiti@1.21.6))(typescript@5.6.3)': - dependencies: - '@eslint-community/regexpp': 4.11.1 - '@typescript-eslint/parser': 8.8.1(eslint@9.12.0(jiti@1.21.6))(typescript@5.6.3) - '@typescript-eslint/scope-manager': 8.8.1 - '@typescript-eslint/type-utils': 8.8.1(eslint@9.12.0(jiti@1.21.6))(typescript@5.6.3) - '@typescript-eslint/utils': 8.8.1(eslint@9.12.0(jiti@1.21.6))(typescript@5.6.3) - '@typescript-eslint/visitor-keys': 8.8.1 - eslint: 9.12.0(jiti@1.21.6) - graphemer: 1.4.0 - ignore: 5.3.2 - natural-compare: 1.4.0 - ts-api-utils: 1.3.0(typescript@5.6.3) - optionalDependencies: - typescript: 5.6.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/parser@8.8.1(eslint@9.12.0(jiti@1.21.6))(typescript@5.6.3)': - dependencies: - '@typescript-eslint/scope-manager': 8.8.1 - '@typescript-eslint/types': 8.8.1 - '@typescript-eslint/typescript-estree': 8.8.1(typescript@5.6.3) - '@typescript-eslint/visitor-keys': 8.8.1 - debug: 4.3.7 - eslint: 9.12.0(jiti@1.21.6) - optionalDependencies: - typescript: 5.6.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/scope-manager@7.18.0': - dependencies: - '@typescript-eslint/types': 7.18.0 - '@typescript-eslint/visitor-keys': 7.18.0 - - '@typescript-eslint/scope-manager@8.8.1': - dependencies: - '@typescript-eslint/types': 8.8.1 - '@typescript-eslint/visitor-keys': 8.8.1 - - '@typescript-eslint/type-utils@8.8.1(eslint@9.12.0(jiti@1.21.6))(typescript@5.6.3)': - dependencies: - '@typescript-eslint/typescript-estree': 8.8.1(typescript@5.6.3) - '@typescript-eslint/utils': 8.8.1(eslint@9.12.0(jiti@1.21.6))(typescript@5.6.3) - debug: 4.3.7 - ts-api-utils: 1.3.0(typescript@5.6.3) - optionalDependencies: - typescript: 5.6.3 - transitivePeerDependencies: - - eslint - - supports-color - - '@typescript-eslint/types@7.18.0': {} - - '@typescript-eslint/types@8.8.1': {} - - '@typescript-eslint/typescript-estree@7.18.0(typescript@5.6.3)': - dependencies: - '@typescript-eslint/types': 7.18.0 - '@typescript-eslint/visitor-keys': 7.18.0 - debug: 4.3.7 - globby: 11.1.0 - is-glob: 4.0.3 - minimatch: 9.0.5 - semver: 7.6.3 - ts-api-utils: 1.3.0(typescript@5.6.3) - optionalDependencies: - typescript: 5.6.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/typescript-estree@8.8.1(typescript@5.6.3)': - dependencies: - '@typescript-eslint/types': 8.8.1 - '@typescript-eslint/visitor-keys': 8.8.1 - debug: 4.3.7 - fast-glob: 3.3.2 - is-glob: 4.0.3 - minimatch: 9.0.5 - semver: 7.6.3 - ts-api-utils: 1.3.0(typescript@5.6.3) - optionalDependencies: - typescript: 5.6.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/utils@8.8.1(eslint@9.12.0(jiti@1.21.6))(typescript@5.6.3)': - dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@9.12.0(jiti@1.21.6)) - '@typescript-eslint/scope-manager': 8.8.1 - '@typescript-eslint/types': 8.8.1 - '@typescript-eslint/typescript-estree': 8.8.1(typescript@5.6.3) - eslint: 9.12.0(jiti@1.21.6) - transitivePeerDependencies: - - supports-color - - typescript - - '@typescript-eslint/visitor-keys@7.18.0': - dependencies: - '@typescript-eslint/types': 7.18.0 - eslint-visitor-keys: 3.4.3 - - '@typescript-eslint/visitor-keys@8.8.1': - dependencies: - '@typescript-eslint/types': 8.8.1 - eslint-visitor-keys: 3.4.3 - - '@vitejs/plugin-vue@5.1.4(vite@5.4.8(@types/node@22.7.5))(vue@3.4.38(typescript@5.6.3))': - dependencies: - vite: 5.4.8(@types/node@22.7.5) - vue: 3.4.38(typescript@5.6.3) - - '@vitest/eslint-plugin@1.1.7(@typescript-eslint/utils@8.8.1(eslint@9.12.0(jiti@1.21.6))(typescript@5.6.3))(eslint@9.12.0(jiti@1.21.6))(typescript@5.6.3)': - dependencies: - '@typescript-eslint/utils': 8.8.1(eslint@9.12.0(jiti@1.21.6))(typescript@5.6.3) - eslint: 9.12.0(jiti@1.21.6) - optionalDependencies: - typescript: 5.6.3 - - '@volar/language-core@2.4.6': - dependencies: - '@volar/source-map': 2.4.6 - - '@volar/source-map@2.4.6': {} - - '@volar/typescript@2.4.6': - dependencies: - '@volar/language-core': 2.4.6 - path-browserify: 1.0.1 - vscode-uri: 3.0.8 - - '@vue-macros/api@0.11.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))': - dependencies: - '@babel/types': 7.25.8 - '@vue-macros/common': 1.14.0(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3)) - resolve.exports: 2.0.2 - transitivePeerDependencies: - - rollup - - vue - - '@vue-macros/better-define@1.9.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))': - dependencies: - '@vue-macros/api': 0.11.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3)) - '@vue-macros/common': 1.14.0(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3)) - unplugin: 1.14.1 - transitivePeerDependencies: - - rollup - - vue - - webpack-sources - - '@vue-macros/boolean-prop@0.5.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))': - dependencies: - '@vue-macros/common': 1.14.0(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3)) - '@vue/compiler-core': 3.5.11 - transitivePeerDependencies: - - rollup - - vue - - '@vue-macros/chain-call@0.4.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))': - dependencies: - '@vue-macros/common': 1.14.0(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3)) - unplugin: 1.14.1 - transitivePeerDependencies: - - rollup - - vue - - webpack-sources - - '@vue-macros/common@1.14.0(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))': - dependencies: - '@babel/types': 7.25.8 - '@rollup/pluginutils': 5.1.2(rollup@4.24.0) - '@vue/compiler-sfc': 3.5.11 - ast-kit: 1.2.1 - local-pkg: 0.5.0 - magic-string-ast: 0.6.2 - optionalDependencies: - vue: 3.4.38(typescript@5.6.3) - transitivePeerDependencies: - - rollup - - '@vue-macros/common@1.14.0(rollup@4.24.0)(vue@3.5.11(typescript@5.6.3))': - dependencies: - '@babel/types': 7.25.8 - '@rollup/pluginutils': 5.1.2(rollup@4.24.0) - '@vue/compiler-sfc': 3.5.11 - ast-kit: 1.2.1 - local-pkg: 0.5.0 - magic-string-ast: 0.6.2 - optionalDependencies: - vue: 3.5.11(typescript@5.6.3) - transitivePeerDependencies: - - rollup - - '@vue-macros/config@0.4.2(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))': - dependencies: - '@vue-macros/common': 1.14.0(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3)) - make-synchronized: 0.2.9 - unconfig: 0.5.5 - transitivePeerDependencies: - - rollup - - supports-color - - vue - - '@vue-macros/define-emit@0.4.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))': - dependencies: - '@vue-macros/api': 0.11.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3)) - '@vue-macros/common': 1.14.0(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3)) - unplugin: 1.14.1 - vue: 3.4.38(typescript@5.6.3) - transitivePeerDependencies: - - rollup - - webpack-sources - - '@vue-macros/define-models@1.3.1(@vueuse/core@11.1.0(vue@3.4.38(typescript@5.6.3)))(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))': - dependencies: - '@vue-macros/common': 1.14.0(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3)) - ast-walker-scope: 0.6.2 - unplugin: 1.14.1 - optionalDependencies: - '@vueuse/core': 11.1.0(vue@3.4.38(typescript@5.6.3)) - transitivePeerDependencies: - - rollup - - vue - - webpack-sources - - '@vue-macros/define-prop@0.5.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))': - dependencies: - '@vue-macros/api': 0.11.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3)) - '@vue-macros/common': 1.14.0(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3)) - unplugin: 1.14.1 - vue: 3.4.38(typescript@5.6.3) - transitivePeerDependencies: - - rollup - - webpack-sources - - '@vue-macros/define-props-refs@1.3.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))': - dependencies: - '@vue-macros/common': 1.14.0(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3)) - unplugin: 1.14.1 - vue: 3.4.38(typescript@5.6.3) - transitivePeerDependencies: - - rollup - - webpack-sources - - '@vue-macros/define-props@4.0.1(@vue-macros/reactivity-transform@1.1.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3)))(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))': - dependencies: - '@vue-macros/common': 1.14.0(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3)) - '@vue-macros/reactivity-transform': 1.1.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3)) - unplugin: 1.14.1 - vue: 3.4.38(typescript@5.6.3) - transitivePeerDependencies: - - rollup - - webpack-sources - - '@vue-macros/define-render@1.6.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))': - dependencies: - '@vue-macros/common': 1.14.0(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3)) - unplugin: 1.14.1 - vue: 3.4.38(typescript@5.6.3) - transitivePeerDependencies: - - rollup - - webpack-sources - - '@vue-macros/define-slots@1.2.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))': - dependencies: - '@vue-macros/common': 1.14.0(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3)) - unplugin: 1.14.1 - vue: 3.4.38(typescript@5.6.3) - transitivePeerDependencies: - - rollup - - webpack-sources - - '@vue-macros/devtools@0.4.0(typescript@5.6.3)(vite@5.4.8(@types/node@22.7.5))': - dependencies: - sirv: 2.0.4 - vue: 3.5.11(typescript@5.6.3) - optionalDependencies: - vite: 5.4.8(@types/node@22.7.5) - transitivePeerDependencies: - - typescript - - '@vue-macros/export-expose@0.3.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))': - dependencies: - '@vue-macros/common': 1.14.0(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3)) - '@vue/compiler-sfc': 3.5.11 - unplugin: 1.14.1 - vue: 3.4.38(typescript@5.6.3) - transitivePeerDependencies: - - rollup - - webpack-sources - - '@vue-macros/export-props@0.6.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))': - dependencies: - '@vue-macros/common': 1.14.0(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3)) - unplugin: 1.14.1 - vue: 3.4.38(typescript@5.6.3) - transitivePeerDependencies: - - rollup - - webpack-sources - - '@vue-macros/export-render@0.3.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))': - dependencies: - '@vue-macros/common': 1.14.0(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3)) - unplugin: 1.14.1 - vue: 3.4.38(typescript@5.6.3) - transitivePeerDependencies: - - rollup - - webpack-sources - - '@vue-macros/hoist-static@1.6.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))': - dependencies: - '@vue-macros/common': 1.14.0(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3)) - unplugin: 1.14.1 - transitivePeerDependencies: - - rollup - - vue - - webpack-sources - - '@vue-macros/jsx-directive@0.9.1(rollup@4.24.0)(typescript@5.6.3)': - dependencies: - '@vue-macros/common': 1.14.0(rollup@4.24.0)(vue@3.5.11(typescript@5.6.3)) - unplugin: 1.14.1 - vue: 3.5.11(typescript@5.6.3) - transitivePeerDependencies: - - rollup - - typescript - - webpack-sources - - '@vue-macros/named-template@0.5.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))': - dependencies: - '@vue-macros/common': 1.14.0(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3)) - '@vue/compiler-dom': 3.5.11 - unplugin: 1.14.1 - transitivePeerDependencies: - - rollup - - vue - - webpack-sources - - '@vue-macros/reactivity-transform@1.1.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))': - dependencies: - '@babel/parser': 7.25.8 - '@vue-macros/common': 1.14.0(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3)) - '@vue/compiler-core': 3.5.11 - '@vue/shared': 3.5.11 - magic-string: 0.30.12 - unplugin: 1.14.1 - vue: 3.4.38(typescript@5.6.3) - transitivePeerDependencies: - - rollup - - webpack-sources - - '@vue-macros/script-lang@0.2.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))': - dependencies: - '@vue-macros/common': 1.14.0(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3)) - unplugin: 1.14.1 - vue: 3.4.38(typescript@5.6.3) - transitivePeerDependencies: - - rollup - - webpack-sources - - '@vue-macros/setup-block@0.4.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))': - dependencies: - '@vue-macros/common': 1.14.0(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3)) - '@vue/compiler-dom': 3.5.11 - unplugin: 1.14.1 - transitivePeerDependencies: - - rollup - - vue - - webpack-sources - - '@vue-macros/setup-component@0.18.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))': - dependencies: - '@vue-macros/common': 1.14.0(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3)) - unplugin: 1.14.1 - transitivePeerDependencies: - - rollup - - vue - - webpack-sources - - '@vue-macros/setup-sfc@0.18.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))': - dependencies: - '@vue-macros/common': 1.14.0(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3)) - unplugin: 1.14.1 - transitivePeerDependencies: - - rollup - - vue - - webpack-sources - - '@vue-macros/short-bind@1.1.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))': - dependencies: - '@vue-macros/common': 1.14.0(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3)) - '@vue/compiler-core': 3.5.11 - transitivePeerDependencies: - - rollup - - vue - - '@vue-macros/short-emits@1.6.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))': - dependencies: - '@vue-macros/common': 1.14.0(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3)) - unplugin: 1.14.1 - transitivePeerDependencies: - - rollup - - vue - - webpack-sources - - '@vue-macros/short-vmodel@1.5.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3))': - dependencies: - '@vue-macros/common': 1.14.0(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3)) - '@vue/compiler-core': 3.5.11 - transitivePeerDependencies: - - rollup - - vue - - '@vue-macros/volar@0.30.3(rollup@4.24.0)(typescript@5.6.3)(vue-tsc@2.1.6(typescript@5.6.3))(vue@3.4.38(typescript@5.6.3))': - dependencies: - '@vue-macros/boolean-prop': 0.5.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3)) - '@vue-macros/common': 1.14.0(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3)) - '@vue-macros/config': 0.4.2(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3)) - '@vue-macros/short-bind': 1.1.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3)) - '@vue-macros/short-vmodel': 1.5.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3)) - '@vue/language-core': 2.1.6(typescript@5.6.3) - muggle-string: 0.4.1 - optionalDependencies: - vue-tsc: 2.1.6(typescript@5.6.3) - transitivePeerDependencies: - - rollup - - supports-color - - typescript - - vue - - '@vue/babel-helper-vue-transform-on@1.2.5': {} - - '@vue/babel-plugin-jsx@1.2.5(@babel/core@7.25.8)': - dependencies: - '@babel/helper-module-imports': 7.25.7 - '@babel/helper-plugin-utils': 7.25.7 - '@babel/plugin-syntax-jsx': 7.25.7(@babel/core@7.25.8) - '@babel/template': 7.25.7 - '@babel/traverse': 7.25.7 - '@babel/types': 7.25.8 - '@vue/babel-helper-vue-transform-on': 1.2.5 - '@vue/babel-plugin-resolve-type': 1.2.5(@babel/core@7.25.8) - html-tags: 3.3.1 - svg-tags: 1.0.0 - optionalDependencies: - '@babel/core': 7.25.8 - transitivePeerDependencies: - - supports-color - - '@vue/babel-plugin-resolve-type@1.2.5(@babel/core@7.25.8)': - dependencies: - '@babel/code-frame': 7.25.7 - '@babel/core': 7.25.8 - '@babel/helper-module-imports': 7.25.7 - '@babel/helper-plugin-utils': 7.25.7 - '@babel/parser': 7.25.8 - '@vue/compiler-sfc': 3.5.11 - transitivePeerDependencies: - - supports-color - - '@vue/compiler-core@3.4.38': - dependencies: - '@babel/parser': 7.25.8 - '@vue/shared': 3.4.38 - entities: 4.5.0 - estree-walker: 2.0.2 - source-map-js: 1.2.1 - - '@vue/compiler-core@3.5.11': - dependencies: - '@babel/parser': 7.25.8 - '@vue/shared': 3.5.11 - entities: 4.5.0 - estree-walker: 2.0.2 - source-map-js: 1.2.1 - - '@vue/compiler-dom@3.4.38': - dependencies: - '@vue/compiler-core': 3.4.38 - '@vue/shared': 3.4.38 - - '@vue/compiler-dom@3.5.11': - dependencies: - '@vue/compiler-core': 3.5.11 - '@vue/shared': 3.5.11 - - '@vue/compiler-sfc@3.4.38': - dependencies: - '@babel/parser': 7.25.8 - '@vue/compiler-core': 3.4.38 - '@vue/compiler-dom': 3.4.38 - '@vue/compiler-ssr': 3.4.38 - '@vue/shared': 3.4.38 - estree-walker: 2.0.2 - magic-string: 0.30.12 - postcss: 8.4.47 - source-map-js: 1.2.1 - - '@vue/compiler-sfc@3.5.11': - dependencies: - '@babel/parser': 7.25.8 - '@vue/compiler-core': 3.5.11 - '@vue/compiler-dom': 3.5.11 - '@vue/compiler-ssr': 3.5.11 - '@vue/shared': 3.5.11 - estree-walker: 2.0.2 - magic-string: 0.30.12 - postcss: 8.4.47 - source-map-js: 1.2.1 - - '@vue/compiler-ssr@3.4.38': - dependencies: - '@vue/compiler-dom': 3.4.38 - '@vue/shared': 3.4.38 - - '@vue/compiler-ssr@3.5.11': - dependencies: - '@vue/compiler-dom': 3.5.11 - '@vue/shared': 3.5.11 - - '@vue/compiler-vue2@2.7.16': - dependencies: - de-indent: 1.0.2 - he: 1.2.0 - - '@vue/devtools-api@6.6.4': {} - - '@vue/devtools-core@7.4.6(vite@5.4.8(@types/node@22.7.5))(vue@3.4.38(typescript@5.6.3))': - dependencies: - '@vue/devtools-kit': 7.4.6 - '@vue/devtools-shared': 7.4.6 - mitt: 3.0.1 - nanoid: 3.3.7 - pathe: 1.1.2 - vite-hot-client: 0.2.3(vite@5.4.8(@types/node@22.7.5)) - vue: 3.4.38(typescript@5.6.3) - transitivePeerDependencies: - - vite - - '@vue/devtools-kit@7.4.6': - dependencies: - '@vue/devtools-shared': 7.4.6 - birpc: 0.2.19 - hookable: 5.5.3 - mitt: 3.0.1 - perfect-debounce: 1.0.0 - speakingurl: 14.0.1 - superjson: 2.2.1 - - '@vue/devtools-shared@7.4.6': - dependencies: - rfdc: 1.4.1 - - '@vue/language-core@2.1.6(typescript@5.6.3)': - dependencies: - '@volar/language-core': 2.4.6 - '@vue/compiler-dom': 3.5.11 - '@vue/compiler-vue2': 2.7.16 - '@vue/shared': 3.5.11 - computeds: 0.0.1 - minimatch: 9.0.5 - muggle-string: 0.4.1 - path-browserify: 1.0.1 - optionalDependencies: - typescript: 5.6.3 - - '@vue/reactivity@3.4.38': - dependencies: - '@vue/shared': 3.4.38 - - '@vue/reactivity@3.5.11': - dependencies: - '@vue/shared': 3.5.11 - - '@vue/runtime-core@3.4.38': - dependencies: - '@vue/reactivity': 3.4.38 - '@vue/shared': 3.4.38 - - '@vue/runtime-core@3.5.11': - dependencies: - '@vue/reactivity': 3.5.11 - '@vue/shared': 3.5.11 - - '@vue/runtime-dom@3.4.38': - dependencies: - '@vue/reactivity': 3.4.38 - '@vue/runtime-core': 3.4.38 - '@vue/shared': 3.4.38 - csstype: 3.1.3 - - '@vue/runtime-dom@3.5.11': - dependencies: - '@vue/reactivity': 3.5.11 - '@vue/runtime-core': 3.5.11 - '@vue/shared': 3.5.11 - csstype: 3.1.3 - - '@vue/server-renderer@3.4.38(vue@3.4.38(typescript@5.6.3))': - dependencies: - '@vue/compiler-ssr': 3.4.38 - '@vue/shared': 3.4.38 - vue: 3.4.38(typescript@5.6.3) - - '@vue/server-renderer@3.5.11(vue@3.5.11(typescript@5.6.3))': - dependencies: - '@vue/compiler-ssr': 3.5.11 - '@vue/shared': 3.5.11 - vue: 3.5.11(typescript@5.6.3) - - '@vue/shared@3.4.38': {} - - '@vue/shared@3.5.11': {} - - '@vueuse/core@11.1.0(vue@3.4.38(typescript@5.6.3))': - dependencies: - '@types/web-bluetooth': 0.0.20 - '@vueuse/metadata': 11.1.0 - '@vueuse/shared': 11.1.0(vue@3.4.38(typescript@5.6.3)) - vue-demi: 0.14.10(vue@3.4.38(typescript@5.6.3)) - transitivePeerDependencies: - - '@vue/composition-api' - - vue - - '@vueuse/metadata@11.1.0': {} - - '@vueuse/shared@11.1.0(vue@3.4.38(typescript@5.6.3))': - dependencies: - vue-demi: 0.14.10(vue@3.4.38(typescript@5.6.3)) - transitivePeerDependencies: - - '@vue/composition-api' - - vue - - acorn-jsx@5.3.2(acorn@8.12.1): - dependencies: - acorn: 8.12.1 - - acorn@8.12.1: {} - - ajv@6.12.6: - dependencies: - fast-deep-equal: 3.1.3 - fast-json-stable-stringify: 2.1.0 - json-schema-traverse: 0.4.1 - uri-js: 4.4.1 - - ansi-regex@5.0.1: {} - - ansi-regex@6.1.0: {} - - ansi-styles@3.2.1: - dependencies: - color-convert: 1.9.3 - - ansi-styles@4.3.0: - dependencies: - color-convert: 2.0.1 - - ansi-styles@6.2.1: {} - - any-promise@1.3.0: {} - - anymatch@3.1.3: - dependencies: - normalize-path: 3.0.0 - picomatch: 2.3.1 - - are-docs-informative@0.0.2: {} - - arg@5.0.2: {} - - argparse@1.0.10: - dependencies: - sprintf-js: 1.0.3 - - argparse@2.0.1: {} - - array-union@2.1.0: {} - - ast-kit@1.2.1: - dependencies: - '@babel/parser': 7.25.8 - pathe: 1.1.2 - - ast-walker-scope@0.6.2: - dependencies: - '@babel/parser': 7.25.8 - ast-kit: 1.2.1 - - autoprefixer@10.4.20(postcss@8.4.47): - dependencies: - browserslist: 4.24.0 - caniuse-lite: 1.0.30001668 - fraction.js: 4.3.7 - normalize-range: 0.1.2 - picocolors: 1.1.0 - postcss: 8.4.47 - postcss-value-parser: 4.2.0 - - balanced-match@1.0.2: {} - - binary-extensions@2.3.0: {} - - birpc@0.2.19: {} - - boolbase@1.0.0: {} - - brace-expansion@1.1.11: - dependencies: - balanced-match: 1.0.2 - concat-map: 0.0.1 - - brace-expansion@2.0.1: - dependencies: - balanced-match: 1.0.2 - - braces@3.0.3: - dependencies: - fill-range: 7.1.1 - - browserslist@4.24.0: - dependencies: - caniuse-lite: 1.0.30001668 - electron-to-chromium: 1.5.36 - node-releases: 2.0.18 - update-browserslist-db: 1.1.1(browserslist@4.24.0) - - builtin-modules@3.3.0: {} - - bundle-name@4.1.0: - dependencies: - run-applescript: 7.0.0 - - bundle-require@5.0.0(esbuild@0.23.1): - dependencies: - esbuild: 0.23.1 - load-tsconfig: 0.2.5 - - callsites@3.1.0: {} - - camelcase-css@2.0.1: {} - - caniuse-lite@1.0.30001668: {} - - ccount@2.0.1: {} - - chalk@2.4.2: - dependencies: - ansi-styles: 3.2.1 - escape-string-regexp: 1.0.5 - supports-color: 5.5.0 - - chalk@4.1.2: - dependencies: - ansi-styles: 4.3.0 - supports-color: 7.2.0 - - character-entities@2.0.2: {} - - chokidar@3.6.0: - dependencies: - anymatch: 3.1.3 - braces: 3.0.3 - glob-parent: 5.1.2 - is-binary-path: 2.1.0 - is-glob: 4.0.3 - normalize-path: 3.0.0 - readdirp: 3.6.0 - optionalDependencies: - fsevents: 2.3.3 - - ci-info@4.0.0: {} - - cidr-regex@4.0.3: - dependencies: - ip-regex: 5.0.0 - - cidr-tools@6.4.2: - dependencies: - cidr-regex: 4.0.3 - ip-bigint: 7.3.0 - ip-regex: 5.0.0 - string-natural-compare: 3.0.1 - - clean-regexp@1.0.0: - dependencies: - escape-string-regexp: 1.0.5 - - cliui@8.0.1: - dependencies: - string-width: 4.2.3 - strip-ansi: 6.0.1 - wrap-ansi: 7.0.0 - - clone-regexp@3.0.0: - dependencies: - is-regexp: 3.1.0 - - color-convert@1.9.3: - dependencies: - color-name: 1.1.3 - - color-convert@2.0.1: - dependencies: - color-name: 1.1.4 - - color-name@1.1.3: {} - - color-name@1.1.4: {} - - commander@4.1.1: {} - - comment-parser@1.4.1: {} - - computeds@0.0.1: {} - - concat-map@0.0.1: {} - - confbox@0.1.8: {} - - convert-hrtime@5.0.0: {} - - convert-source-map@2.0.0: {} - - copy-anything@3.0.5: - dependencies: - is-what: 4.1.16 - - core-js-compat@3.38.1: - dependencies: - browserslist: 4.24.0 - - cross-spawn@7.0.3: - dependencies: - path-key: 3.1.1 - shebang-command: 2.0.0 - which: 2.0.2 - - cssesc@3.0.0: {} - - csstype@3.1.3: {} - - de-indent@1.0.2: {} - - debug@3.2.7: - dependencies: - ms: 2.1.3 - - debug@4.3.7: - dependencies: - ms: 2.1.3 - - decode-named-character-reference@1.0.2: - dependencies: - character-entities: 2.0.2 - - deep-is@0.1.4: {} - - default-browser-id@5.0.0: {} - - default-browser@5.2.1: - dependencies: - bundle-name: 4.1.0 - default-browser-id: 5.0.0 - - default-gateway@7.2.2: - dependencies: - execa: 7.2.0 - - define-lazy-prop@3.0.0: {} - - defu@6.1.4: {} - - dequal@2.0.3: {} - - devlop@1.1.0: - dependencies: - dequal: 2.0.3 - - didyoumean@1.2.2: {} - - dir-glob@3.0.1: - dependencies: - path-type: 4.0.0 - - dlv@1.1.3: {} - - doctrine@3.0.0: - dependencies: - esutils: 2.0.3 - - eastasianwidth@0.2.0: {} - - electron-to-chromium@1.5.36: {} - - emoji-regex@8.0.0: {} - - emoji-regex@9.2.2: {} - - enhanced-resolve@5.17.1: - dependencies: - graceful-fs: 4.2.11 - tapable: 2.2.1 - - entities@4.5.0: {} - - error-ex@1.3.2: - dependencies: - is-arrayish: 0.2.1 - - error-stack-parser-es@0.1.5: {} - - es-module-lexer@1.5.4: {} - - esbuild@0.21.5: - optionalDependencies: - '@esbuild/aix-ppc64': 0.21.5 - '@esbuild/android-arm': 0.21.5 - '@esbuild/android-arm64': 0.21.5 - '@esbuild/android-x64': 0.21.5 - '@esbuild/darwin-arm64': 0.21.5 - '@esbuild/darwin-x64': 0.21.5 - '@esbuild/freebsd-arm64': 0.21.5 - '@esbuild/freebsd-x64': 0.21.5 - '@esbuild/linux-arm': 0.21.5 - '@esbuild/linux-arm64': 0.21.5 - '@esbuild/linux-ia32': 0.21.5 - '@esbuild/linux-loong64': 0.21.5 - '@esbuild/linux-mips64el': 0.21.5 - '@esbuild/linux-ppc64': 0.21.5 - '@esbuild/linux-riscv64': 0.21.5 - '@esbuild/linux-s390x': 0.21.5 - '@esbuild/linux-x64': 0.21.5 - '@esbuild/netbsd-x64': 0.21.5 - '@esbuild/openbsd-x64': 0.21.5 - '@esbuild/sunos-x64': 0.21.5 - '@esbuild/win32-arm64': 0.21.5 - '@esbuild/win32-ia32': 0.21.5 - '@esbuild/win32-x64': 0.21.5 - - esbuild@0.23.1: - optionalDependencies: - '@esbuild/aix-ppc64': 0.23.1 - '@esbuild/android-arm': 0.23.1 - '@esbuild/android-arm64': 0.23.1 - '@esbuild/android-x64': 0.23.1 - '@esbuild/darwin-arm64': 0.23.1 - '@esbuild/darwin-x64': 0.23.1 - '@esbuild/freebsd-arm64': 0.23.1 - '@esbuild/freebsd-x64': 0.23.1 - '@esbuild/linux-arm': 0.23.1 - '@esbuild/linux-arm64': 0.23.1 - '@esbuild/linux-ia32': 0.23.1 - '@esbuild/linux-loong64': 0.23.1 - '@esbuild/linux-mips64el': 0.23.1 - '@esbuild/linux-ppc64': 0.23.1 - '@esbuild/linux-riscv64': 0.23.1 - '@esbuild/linux-s390x': 0.23.1 - '@esbuild/linux-x64': 0.23.1 - '@esbuild/netbsd-x64': 0.23.1 - '@esbuild/openbsd-arm64': 0.23.1 - '@esbuild/openbsd-x64': 0.23.1 - '@esbuild/sunos-x64': 0.23.1 - '@esbuild/win32-arm64': 0.23.1 - '@esbuild/win32-ia32': 0.23.1 - '@esbuild/win32-x64': 0.23.1 - - escalade@3.2.0: {} - - escape-string-regexp@1.0.5: {} - - escape-string-regexp@4.0.0: {} - - escape-string-regexp@5.0.0: {} - - escodegen@2.1.0: - dependencies: - esprima: 4.0.1 - estraverse: 5.3.0 - esutils: 2.0.3 - optionalDependencies: - source-map: 0.6.1 - - eslint-compat-utils@0.5.1(eslint@9.12.0(jiti@1.21.6)): - dependencies: - eslint: 9.12.0(jiti@1.21.6) - semver: 7.6.3 - - eslint-config-flat-gitignore@0.3.0(eslint@9.12.0(jiti@1.21.6)): - dependencies: - '@eslint/compat': 1.2.0(eslint@9.12.0(jiti@1.21.6)) - eslint: 9.12.0(jiti@1.21.6) - find-up-simple: 1.0.0 - - eslint-flat-config-utils@0.4.0: - dependencies: - pathe: 1.1.2 - - eslint-formatting-reporter@0.0.0(eslint@9.12.0(jiti@1.21.6)): - dependencies: - eslint: 9.12.0(jiti@1.21.6) - prettier-linter-helpers: 1.0.0 - - eslint-import-resolver-node@0.3.9: - dependencies: - debug: 3.2.7 - is-core-module: 2.15.1 - resolve: 1.22.8 - transitivePeerDependencies: - - supports-color - - eslint-merge-processors@0.1.0(eslint@9.12.0(jiti@1.21.6)): - dependencies: - eslint: 9.12.0(jiti@1.21.6) - - eslint-parser-plain@0.1.0: {} - - eslint-plugin-antfu@2.7.0(eslint@9.12.0(jiti@1.21.6)): - dependencies: - '@antfu/utils': 0.7.10 - eslint: 9.12.0(jiti@1.21.6) - - eslint-plugin-command@0.2.6(eslint@9.12.0(jiti@1.21.6)): - dependencies: - '@es-joy/jsdoccomment': 0.48.0 - eslint: 9.12.0(jiti@1.21.6) - - eslint-plugin-es-x@7.8.0(eslint@9.12.0(jiti@1.21.6)): - dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@9.12.0(jiti@1.21.6)) - '@eslint-community/regexpp': 4.11.1 - eslint: 9.12.0(jiti@1.21.6) - eslint-compat-utils: 0.5.1(eslint@9.12.0(jiti@1.21.6)) - - eslint-plugin-format@0.1.2(eslint@9.12.0(jiti@1.21.6)): - dependencies: - '@dprint/formatter': 0.3.0 - '@dprint/markdown': 0.17.8 - '@dprint/toml': 0.6.3 - eslint: 9.12.0(jiti@1.21.6) - eslint-formatting-reporter: 0.0.0(eslint@9.12.0(jiti@1.21.6)) - eslint-parser-plain: 0.1.0 - prettier: 3.3.3 - synckit: 0.9.2 - - eslint-plugin-import-x@4.3.1(eslint@9.12.0(jiti@1.21.6))(typescript@5.6.3): - dependencies: - '@typescript-eslint/utils': 8.8.1(eslint@9.12.0(jiti@1.21.6))(typescript@5.6.3) - debug: 4.3.7 - doctrine: 3.0.0 - eslint: 9.12.0(jiti@1.21.6) - eslint-import-resolver-node: 0.3.9 - get-tsconfig: 4.8.1 - is-glob: 4.0.3 - minimatch: 9.0.5 - semver: 7.6.3 - stable-hash: 0.0.4 - tslib: 2.7.0 - transitivePeerDependencies: - - supports-color - - typescript - - eslint-plugin-jsdoc@50.3.1(eslint@9.12.0(jiti@1.21.6)): - dependencies: - '@es-joy/jsdoccomment': 0.48.0 - are-docs-informative: 0.0.2 - comment-parser: 1.4.1 - debug: 4.3.7 - escape-string-regexp: 4.0.0 - eslint: 9.12.0(jiti@1.21.6) - espree: 10.2.0 - esquery: 1.6.0 - parse-imports: 2.2.1 - semver: 7.6.3 - spdx-expression-parse: 4.0.0 - synckit: 0.9.2 - transitivePeerDependencies: - - supports-color - - eslint-plugin-jsonc@2.16.0(eslint@9.12.0(jiti@1.21.6)): - dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@9.12.0(jiti@1.21.6)) - eslint: 9.12.0(jiti@1.21.6) - eslint-compat-utils: 0.5.1(eslint@9.12.0(jiti@1.21.6)) - espree: 9.6.1 - graphemer: 1.4.0 - jsonc-eslint-parser: 2.4.0 - natural-compare: 1.4.0 - synckit: 0.6.2 - - eslint-plugin-n@17.11.1(eslint@9.12.0(jiti@1.21.6)): - dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@9.12.0(jiti@1.21.6)) - enhanced-resolve: 5.17.1 - eslint: 9.12.0(jiti@1.21.6) - eslint-plugin-es-x: 7.8.0(eslint@9.12.0(jiti@1.21.6)) - get-tsconfig: 4.8.1 - globals: 15.11.0 - ignore: 5.3.2 - minimatch: 9.0.5 - semver: 7.6.3 - - eslint-plugin-no-only-tests@3.3.0: {} - - eslint-plugin-perfectionist@3.8.0(eslint@9.12.0(jiti@1.21.6))(typescript@5.6.3)(vue-eslint-parser@9.4.3(eslint@9.12.0(jiti@1.21.6))): - dependencies: - '@typescript-eslint/types': 8.8.1 - '@typescript-eslint/utils': 8.8.1(eslint@9.12.0(jiti@1.21.6))(typescript@5.6.3) - eslint: 9.12.0(jiti@1.21.6) - minimatch: 9.0.5 - natural-compare-lite: 1.4.0 - optionalDependencies: - vue-eslint-parser: 9.4.3(eslint@9.12.0(jiti@1.21.6)) - transitivePeerDependencies: - - supports-color - - typescript - - eslint-plugin-regexp@2.6.0(eslint@9.12.0(jiti@1.21.6)): - dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@9.12.0(jiti@1.21.6)) - '@eslint-community/regexpp': 4.11.1 - comment-parser: 1.4.1 - eslint: 9.12.0(jiti@1.21.6) - jsdoc-type-pratt-parser: 4.1.0 - refa: 0.12.1 - regexp-ast-analysis: 0.7.1 - scslre: 0.3.0 - - eslint-plugin-toml@0.11.1(eslint@9.12.0(jiti@1.21.6)): - dependencies: - debug: 4.3.7 - eslint: 9.12.0(jiti@1.21.6) - eslint-compat-utils: 0.5.1(eslint@9.12.0(jiti@1.21.6)) - lodash: 4.17.21 - toml-eslint-parser: 0.10.0 - transitivePeerDependencies: - - supports-color - - eslint-plugin-unicorn@55.0.0(eslint@9.12.0(jiti@1.21.6)): - dependencies: - '@babel/helper-validator-identifier': 7.25.7 - '@eslint-community/eslint-utils': 4.4.0(eslint@9.12.0(jiti@1.21.6)) - ci-info: 4.0.0 - clean-regexp: 1.0.0 - core-js-compat: 3.38.1 - eslint: 9.12.0(jiti@1.21.6) - esquery: 1.6.0 - globals: 15.11.0 - indent-string: 4.0.0 - is-builtin-module: 3.2.1 - jsesc: 3.0.2 - pluralize: 8.0.0 - read-pkg-up: 7.0.1 - regexp-tree: 0.1.27 - regjsparser: 0.10.0 - semver: 7.6.3 - strip-indent: 3.0.0 - - eslint-plugin-unused-imports@4.1.4(@typescript-eslint/eslint-plugin@8.8.1(@typescript-eslint/parser@8.8.1(eslint@9.12.0(jiti@1.21.6))(typescript@5.6.3))(eslint@9.12.0(jiti@1.21.6))(typescript@5.6.3))(eslint@9.12.0(jiti@1.21.6)): - dependencies: - eslint: 9.12.0(jiti@1.21.6) - optionalDependencies: - '@typescript-eslint/eslint-plugin': 8.8.1(@typescript-eslint/parser@8.8.1(eslint@9.12.0(jiti@1.21.6))(typescript@5.6.3))(eslint@9.12.0(jiti@1.21.6))(typescript@5.6.3) - - eslint-plugin-vue@9.29.0(eslint@9.12.0(jiti@1.21.6)): - dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@9.12.0(jiti@1.21.6)) - eslint: 9.12.0(jiti@1.21.6) - globals: 13.24.0 - natural-compare: 1.4.0 - nth-check: 2.1.1 - postcss-selector-parser: 6.1.2 - semver: 7.6.3 - vue-eslint-parser: 9.4.3(eslint@9.12.0(jiti@1.21.6)) - xml-name-validator: 4.0.0 - transitivePeerDependencies: - - supports-color - - eslint-plugin-yml@1.14.0(eslint@9.12.0(jiti@1.21.6)): - dependencies: - debug: 4.3.7 - eslint: 9.12.0(jiti@1.21.6) - eslint-compat-utils: 0.5.1(eslint@9.12.0(jiti@1.21.6)) - lodash: 4.17.21 - natural-compare: 1.4.0 - yaml-eslint-parser: 1.2.3 - transitivePeerDependencies: - - supports-color - - eslint-processor-vue-blocks@0.1.2(@vue/compiler-sfc@3.5.11)(eslint@9.12.0(jiti@1.21.6)): - dependencies: - '@vue/compiler-sfc': 3.5.11 - eslint: 9.12.0(jiti@1.21.6) - - eslint-scope@7.2.2: - dependencies: - esrecurse: 4.3.0 - estraverse: 5.3.0 - - eslint-scope@8.1.0: - dependencies: - esrecurse: 4.3.0 - estraverse: 5.3.0 - - eslint-visitor-keys@3.4.3: {} - - eslint-visitor-keys@4.1.0: {} - - eslint@9.12.0(jiti@1.21.6): - dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@9.12.0(jiti@1.21.6)) - '@eslint-community/regexpp': 4.11.1 - '@eslint/config-array': 0.18.0 - '@eslint/core': 0.6.0 - '@eslint/eslintrc': 3.1.0 - '@eslint/js': 9.12.0 - '@eslint/plugin-kit': 0.2.0 - '@humanfs/node': 0.16.5 - '@humanwhocodes/module-importer': 1.0.1 - '@humanwhocodes/retry': 0.3.1 - '@types/estree': 1.0.6 - '@types/json-schema': 7.0.15 - ajv: 6.12.6 - chalk: 4.1.2 - cross-spawn: 7.0.3 - debug: 4.3.7 - escape-string-regexp: 4.0.0 - eslint-scope: 8.1.0 - eslint-visitor-keys: 4.1.0 - espree: 10.2.0 - esquery: 1.6.0 - esutils: 2.0.3 - fast-deep-equal: 3.1.3 - file-entry-cache: 8.0.0 - find-up: 5.0.0 - glob-parent: 6.0.2 - ignore: 5.3.2 - imurmurhash: 0.1.4 - is-glob: 4.0.3 - json-stable-stringify-without-jsonify: 1.0.1 - lodash.merge: 4.6.2 - minimatch: 3.1.2 - natural-compare: 1.4.0 - optionator: 0.9.4 - text-table: 0.2.0 - optionalDependencies: - jiti: 1.21.6 - transitivePeerDependencies: - - supports-color - - espree@10.2.0: - dependencies: - acorn: 8.12.1 - acorn-jsx: 5.3.2(acorn@8.12.1) - eslint-visitor-keys: 4.1.0 - - espree@9.6.1: - dependencies: - acorn: 8.12.1 - acorn-jsx: 5.3.2(acorn@8.12.1) - eslint-visitor-keys: 3.4.3 - - esprima@4.0.1: {} - - esquery@1.6.0: - dependencies: - estraverse: 5.3.0 - - esrecurse@4.3.0: - dependencies: - estraverse: 5.3.0 - - estraverse@5.3.0: {} - - estree-walker@2.0.2: {} - - estree-walker@3.0.3: - dependencies: - '@types/estree': 1.0.6 - - esutils@2.0.3: {} - - execa@7.2.0: - dependencies: - cross-spawn: 7.0.3 - get-stream: 6.0.1 - human-signals: 4.3.1 - is-stream: 3.0.0 - merge-stream: 2.0.0 - npm-run-path: 5.3.0 - onetime: 6.0.0 - signal-exit: 3.0.7 - strip-final-newline: 3.0.0 - - execa@8.0.1: - dependencies: - cross-spawn: 7.0.3 - get-stream: 8.0.1 - human-signals: 5.0.0 - is-stream: 3.0.0 - merge-stream: 2.0.0 - npm-run-path: 5.3.0 - onetime: 6.0.0 - signal-exit: 4.1.0 - strip-final-newline: 3.0.0 - - extend-shallow@2.0.1: - dependencies: - is-extendable: 0.1.1 - - fast-deep-equal@3.1.3: {} - - fast-diff@1.3.0: {} - - fast-glob@3.3.2: - dependencies: - '@nodelib/fs.stat': 2.0.5 - '@nodelib/fs.walk': 1.2.8 - glob-parent: 5.1.2 - merge2: 1.4.1 - micromatch: 4.0.8 - - fast-json-stable-stringify@2.1.0: {} - - fast-levenshtein@2.0.6: {} - - fastq@1.17.1: - dependencies: - reusify: 1.0.4 - - file-entry-cache@8.0.0: - dependencies: - flat-cache: 4.0.1 - - fill-range@7.1.1: - dependencies: - to-regex-range: 5.0.1 - - find-up-simple@1.0.0: {} - - find-up@4.1.0: - dependencies: - locate-path: 5.0.0 - path-exists: 4.0.0 - - find-up@5.0.0: - dependencies: - locate-path: 6.0.0 - path-exists: 4.0.0 - - flat-cache@4.0.1: - dependencies: - flatted: 3.3.1 - keyv: 4.5.4 - - flatted@3.3.1: {} - - foreground-child@3.3.0: - dependencies: - cross-spawn: 7.0.3 - signal-exit: 4.1.0 - - fraction.js@4.3.7: {} - - fs-extra@11.2.0: - dependencies: - graceful-fs: 4.2.11 - jsonfile: 6.1.0 - universalify: 2.0.1 - - fsevents@2.3.3: - optional: true - - function-bind@1.1.2: {} - - function-timeout@0.1.1: {} - - gensync@1.0.0-beta.2: {} - - get-caller-file@2.0.5: {} - - get-stream@6.0.1: {} - - get-stream@8.0.1: {} - - get-tsconfig@4.8.1: - dependencies: - resolve-pkg-maps: 1.0.0 - - glob-parent@5.1.2: - dependencies: - is-glob: 4.0.3 - - glob-parent@6.0.2: - dependencies: - is-glob: 4.0.3 - - glob@10.4.5: - dependencies: - foreground-child: 3.3.0 - jackspeak: 3.4.3 - minimatch: 9.0.5 - minipass: 7.1.2 - package-json-from-dist: 1.0.1 - path-scurry: 1.11.1 - - globals@11.12.0: {} - - globals@13.24.0: - dependencies: - type-fest: 0.20.2 - - globals@14.0.0: {} - - globals@15.11.0: {} - - globby@11.1.0: - dependencies: - array-union: 2.1.0 - dir-glob: 3.0.1 - fast-glob: 3.3.2 - ignore: 5.3.2 - merge2: 1.4.1 - slash: 3.0.0 - - graceful-fs@4.2.11: {} - - graphemer@1.4.0: {} - - gray-matter@4.0.3: - dependencies: - js-yaml: 3.14.1 - kind-of: 6.0.3 - section-matter: 1.0.0 - strip-bom-string: 1.0.0 - - has-flag@3.0.0: {} - - has-flag@4.0.0: {} - - hasown@2.0.2: - dependencies: - function-bind: 1.1.2 - - he@1.2.0: {} - - hookable@5.5.3: {} - - hosted-git-info@2.8.9: {} - - html-tags@3.3.1: {} - - human-signals@4.3.1: {} - - human-signals@5.0.0: {} - - ignore@5.3.2: {} - - import-fresh@3.3.0: - dependencies: - parent-module: 1.0.1 - resolve-from: 4.0.0 - - importx@0.4.4: - dependencies: - bundle-require: 5.0.0(esbuild@0.23.1) - debug: 4.3.7 - esbuild: 0.23.1 - jiti: 2.0.0-beta.3 - jiti-v1: jiti@1.21.6 - pathe: 1.1.2 - tsx: 4.19.1 - transitivePeerDependencies: - - supports-color - - imurmurhash@0.1.4: {} - - indent-string@4.0.0: {} - - internal-ip@8.0.0: - dependencies: - cidr-tools: 6.4.2 - default-gateway: 7.2.2 - is-ip: 5.0.1 - p-event: 5.0.1 - - ip-bigint@7.3.0: {} - - ip-num@1.5.1: {} - - ip-regex@5.0.0: {} - - is-arrayish@0.2.1: {} - - is-binary-path@2.1.0: - dependencies: - binary-extensions: 2.3.0 - - is-builtin-module@3.2.1: - dependencies: - builtin-modules: 3.3.0 - - is-core-module@2.15.1: - dependencies: - hasown: 2.0.2 - - is-docker@3.0.0: {} - - is-extendable@0.1.1: {} - - is-extglob@2.1.1: {} - - is-fullwidth-code-point@3.0.0: {} - - is-glob@4.0.3: - dependencies: - is-extglob: 2.1.1 - - is-inside-container@1.0.0: - dependencies: - is-docker: 3.0.0 - - is-ip@5.0.1: - dependencies: - ip-regex: 5.0.0 - super-regex: 0.2.0 - - is-number@7.0.0: {} - - is-regexp@3.1.0: {} - - is-stream@3.0.0: {} - - is-what@4.1.16: {} - - is-wsl@3.1.0: - dependencies: - is-inside-container: 1.0.0 - - isexe@2.0.0: {} - - jackspeak@3.4.3: - dependencies: - '@isaacs/cliui': 8.0.2 - optionalDependencies: - '@pkgjs/parseargs': 0.11.0 - - jiti@1.21.6: {} - - jiti@2.0.0-beta.3: {} - - js-tokens@4.0.0: {} - - js-tokens@9.0.0: {} - - js-yaml@3.14.1: - dependencies: - argparse: 1.0.10 - esprima: 4.0.1 - - js-yaml@4.1.0: - dependencies: - argparse: 2.0.1 - - jsdoc-type-pratt-parser@4.1.0: {} - - jsesc@0.5.0: {} - - jsesc@3.0.2: {} - - json-buffer@3.0.1: {} - - json-parse-even-better-errors@2.3.1: {} - - json-schema-traverse@0.4.1: {} - - json-stable-stringify-without-jsonify@1.0.1: {} - - json5@2.2.3: {} - - jsonc-eslint-parser@2.4.0: - dependencies: - acorn: 8.12.1 - eslint-visitor-keys: 3.4.3 - espree: 9.6.1 - semver: 7.6.3 - - jsonfile@6.1.0: - dependencies: - universalify: 2.0.1 - optionalDependencies: - graceful-fs: 4.2.11 - - keyv@4.5.4: - dependencies: - json-buffer: 3.0.1 - - kind-of@6.0.3: {} - - kolorist@1.8.0: {} - - levn@0.4.1: - dependencies: - prelude-ls: 1.2.1 - type-check: 0.4.0 - - lilconfig@2.1.0: {} - - lilconfig@3.1.2: {} - - lines-and-columns@1.2.4: {} - - linkify-it@5.0.0: - dependencies: - uc.micro: 2.1.0 - - load-tsconfig@0.2.5: {} - - local-pkg@0.5.0: - dependencies: - mlly: 1.7.2 - pkg-types: 1.2.1 - - locate-path@5.0.0: - dependencies: - p-locate: 4.1.0 - - locate-path@6.0.0: - dependencies: - p-locate: 5.0.0 - - lodash.merge@4.6.2: {} - - lodash@4.17.21: {} - - longest-streak@3.1.0: {} - - lru-cache@10.4.3: {} - - lru-cache@5.1.1: - dependencies: - yallist: 3.1.1 - - magic-string-ast@0.6.2: - dependencies: - magic-string: 0.30.12 - - magic-string@0.30.12: - dependencies: - '@jridgewell/sourcemap-codec': 1.5.0 - - make-synchronized@0.2.9: {} - - 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.3: {} - - mdast-util-find-and-replace@3.0.1: - dependencies: - '@types/mdast': 4.0.4 - escape-string-regexp: 5.0.0 - unist-util-is: 6.0.0 - unist-util-visit-parents: 6.0.1 - - mdast-util-from-markdown@2.0.1: - dependencies: - '@types/mdast': 4.0.4 - '@types/unist': 3.0.3 - decode-named-character-reference: 1.0.2 - devlop: 1.1.0 - mdast-util-to-string: 4.0.0 - micromark: 4.0.0 - micromark-util-decode-numeric-character-reference: 2.0.1 - micromark-util-decode-string: 2.0.0 - micromark-util-normalize-identifier: 2.0.0 - micromark-util-symbol: 2.0.0 - micromark-util-types: 2.0.0 - unist-util-stringify-position: 4.0.0 - transitivePeerDependencies: - - supports-color - - mdast-util-gfm-autolink-literal@2.0.1: - dependencies: - '@types/mdast': 4.0.4 - ccount: 2.0.1 - devlop: 1.1.0 - mdast-util-find-and-replace: 3.0.1 - micromark-util-character: 2.1.0 - - mdast-util-gfm-footnote@2.0.0: - dependencies: - '@types/mdast': 4.0.4 - devlop: 1.1.0 - mdast-util-from-markdown: 2.0.1 - mdast-util-to-markdown: 2.1.0 - micromark-util-normalize-identifier: 2.0.0 - transitivePeerDependencies: - - supports-color - - mdast-util-gfm-strikethrough@2.0.0: - dependencies: - '@types/mdast': 4.0.4 - mdast-util-from-markdown: 2.0.1 - mdast-util-to-markdown: 2.1.0 - transitivePeerDependencies: - - supports-color - - mdast-util-gfm-table@2.0.0: - dependencies: - '@types/mdast': 4.0.4 - devlop: 1.1.0 - markdown-table: 3.0.3 - mdast-util-from-markdown: 2.0.1 - mdast-util-to-markdown: 2.1.0 - transitivePeerDependencies: - - supports-color - - mdast-util-gfm-task-list-item@2.0.0: - dependencies: - '@types/mdast': 4.0.4 - devlop: 1.1.0 - mdast-util-from-markdown: 2.0.1 - mdast-util-to-markdown: 2.1.0 - transitivePeerDependencies: - - supports-color - - mdast-util-gfm@3.0.0: - dependencies: - mdast-util-from-markdown: 2.0.1 - mdast-util-gfm-autolink-literal: 2.0.1 - mdast-util-gfm-footnote: 2.0.0 - mdast-util-gfm-strikethrough: 2.0.0 - mdast-util-gfm-table: 2.0.0 - mdast-util-gfm-task-list-item: 2.0.0 - mdast-util-to-markdown: 2.1.0 - transitivePeerDependencies: - - supports-color - - mdast-util-phrasing@4.1.0: - dependencies: - '@types/mdast': 4.0.4 - unist-util-is: 6.0.0 - - mdast-util-to-markdown@2.1.0: - dependencies: - '@types/mdast': 4.0.4 - '@types/unist': 3.0.3 - longest-streak: 3.1.0 - mdast-util-phrasing: 4.1.0 - mdast-util-to-string: 4.0.0 - micromark-util-decode-string: 2.0.0 - unist-util-visit: 5.0.0 - zwitch: 2.0.4 - - mdast-util-to-string@4.0.0: - dependencies: - '@types/mdast': 4.0.4 - - mdurl@2.0.0: {} - - merge-stream@2.0.0: {} - - merge2@1.4.1: {} - - micromark-core-commonmark@2.0.1: - dependencies: - decode-named-character-reference: 1.0.2 - devlop: 1.1.0 - micromark-factory-destination: 2.0.0 - micromark-factory-label: 2.0.0 - micromark-factory-space: 2.0.0 - micromark-factory-title: 2.0.0 - micromark-factory-whitespace: 2.0.0 - micromark-util-character: 2.1.0 - micromark-util-chunked: 2.0.0 - micromark-util-classify-character: 2.0.0 - micromark-util-html-tag-name: 2.0.0 - micromark-util-normalize-identifier: 2.0.0 - micromark-util-resolve-all: 2.0.0 - micromark-util-subtokenize: 2.0.1 - micromark-util-symbol: 2.0.0 - micromark-util-types: 2.0.0 - - micromark-extension-gfm-autolink-literal@2.1.0: - dependencies: - micromark-util-character: 2.1.0 - micromark-util-sanitize-uri: 2.0.0 - micromark-util-symbol: 2.0.0 - micromark-util-types: 2.0.0 - - micromark-extension-gfm-footnote@2.1.0: - dependencies: - devlop: 1.1.0 - micromark-core-commonmark: 2.0.1 - micromark-factory-space: 2.0.0 - micromark-util-character: 2.1.0 - micromark-util-normalize-identifier: 2.0.0 - micromark-util-sanitize-uri: 2.0.0 - micromark-util-symbol: 2.0.0 - micromark-util-types: 2.0.0 - - micromark-extension-gfm-strikethrough@2.1.0: - dependencies: - devlop: 1.1.0 - micromark-util-chunked: 2.0.0 - micromark-util-classify-character: 2.0.0 - micromark-util-resolve-all: 2.0.0 - micromark-util-symbol: 2.0.0 - micromark-util-types: 2.0.0 - - micromark-extension-gfm-table@2.1.0: - dependencies: - devlop: 1.1.0 - micromark-factory-space: 2.0.0 - micromark-util-character: 2.1.0 - micromark-util-symbol: 2.0.0 - micromark-util-types: 2.0.0 - - micromark-extension-gfm-tagfilter@2.0.0: - dependencies: - micromark-util-types: 2.0.0 - - micromark-extension-gfm-task-list-item@2.1.0: - dependencies: - devlop: 1.1.0 - micromark-factory-space: 2.0.0 - micromark-util-character: 2.1.0 - micromark-util-symbol: 2.0.0 - micromark-util-types: 2.0.0 - - micromark-extension-gfm@3.0.0: - dependencies: - micromark-extension-gfm-autolink-literal: 2.1.0 - micromark-extension-gfm-footnote: 2.1.0 - micromark-extension-gfm-strikethrough: 2.1.0 - micromark-extension-gfm-table: 2.1.0 - micromark-extension-gfm-tagfilter: 2.0.0 - micromark-extension-gfm-task-list-item: 2.1.0 - micromark-util-combine-extensions: 2.0.0 - micromark-util-types: 2.0.0 - - micromark-factory-destination@2.0.0: - dependencies: - micromark-util-character: 2.1.0 - micromark-util-symbol: 2.0.0 - micromark-util-types: 2.0.0 - - micromark-factory-label@2.0.0: - dependencies: - devlop: 1.1.0 - micromark-util-character: 2.1.0 - micromark-util-symbol: 2.0.0 - micromark-util-types: 2.0.0 - - micromark-factory-space@2.0.0: - dependencies: - micromark-util-character: 2.1.0 - micromark-util-types: 2.0.0 - - micromark-factory-title@2.0.0: - dependencies: - micromark-factory-space: 2.0.0 - micromark-util-character: 2.1.0 - micromark-util-symbol: 2.0.0 - micromark-util-types: 2.0.0 - - micromark-factory-whitespace@2.0.0: - dependencies: - micromark-factory-space: 2.0.0 - micromark-util-character: 2.1.0 - micromark-util-symbol: 2.0.0 - micromark-util-types: 2.0.0 - - micromark-util-character@2.1.0: - dependencies: - micromark-util-symbol: 2.0.0 - micromark-util-types: 2.0.0 - - micromark-util-chunked@2.0.0: - dependencies: - micromark-util-symbol: 2.0.0 - - micromark-util-classify-character@2.0.0: - dependencies: - micromark-util-character: 2.1.0 - micromark-util-symbol: 2.0.0 - micromark-util-types: 2.0.0 - - micromark-util-combine-extensions@2.0.0: - dependencies: - micromark-util-chunked: 2.0.0 - micromark-util-types: 2.0.0 - - micromark-util-decode-numeric-character-reference@2.0.1: - dependencies: - micromark-util-symbol: 2.0.0 - - micromark-util-decode-string@2.0.0: - dependencies: - decode-named-character-reference: 1.0.2 - micromark-util-character: 2.1.0 - micromark-util-decode-numeric-character-reference: 2.0.1 - micromark-util-symbol: 2.0.0 - - micromark-util-encode@2.0.0: {} - - micromark-util-html-tag-name@2.0.0: {} - - micromark-util-normalize-identifier@2.0.0: - dependencies: - micromark-util-symbol: 2.0.0 - - micromark-util-resolve-all@2.0.0: - dependencies: - micromark-util-types: 2.0.0 - - micromark-util-sanitize-uri@2.0.0: - dependencies: - micromark-util-character: 2.1.0 - micromark-util-encode: 2.0.0 - micromark-util-symbol: 2.0.0 - - micromark-util-subtokenize@2.0.1: - dependencies: - devlop: 1.1.0 - micromark-util-chunked: 2.0.0 - micromark-util-symbol: 2.0.0 - micromark-util-types: 2.0.0 - - micromark-util-symbol@2.0.0: {} - - micromark-util-types@2.0.0: {} - - micromark@4.0.0: - dependencies: - '@types/debug': 4.1.12 - debug: 4.3.7 - decode-named-character-reference: 1.0.2 - devlop: 1.1.0 - micromark-core-commonmark: 2.0.1 - micromark-factory-space: 2.0.0 - micromark-util-character: 2.1.0 - micromark-util-chunked: 2.0.0 - micromark-util-combine-extensions: 2.0.0 - micromark-util-decode-numeric-character-reference: 2.0.1 - micromark-util-encode: 2.0.0 - micromark-util-normalize-identifier: 2.0.0 - micromark-util-resolve-all: 2.0.0 - micromark-util-sanitize-uri: 2.0.0 - micromark-util-subtokenize: 2.0.1 - micromark-util-symbol: 2.0.0 - micromark-util-types: 2.0.0 - transitivePeerDependencies: - - supports-color - - micromatch@4.0.8: - dependencies: - braces: 3.0.3 - picomatch: 2.3.1 - - mimic-fn@4.0.0: {} - - min-indent@1.0.1: {} - - minimatch@3.1.2: - dependencies: - brace-expansion: 1.1.11 - - minimatch@9.0.5: - dependencies: - brace-expansion: 2.0.1 - - minipass@7.1.2: {} - - mitt@3.0.1: {} - - mlly@1.7.2: - dependencies: - acorn: 8.12.1 - pathe: 1.1.2 - pkg-types: 1.2.1 - ufo: 1.5.4 - - mrmime@2.0.0: {} - - ms@2.1.3: {} - - muggle-string@0.4.1: {} - - mz@2.7.0: - dependencies: - any-promise: 1.3.0 - object-assign: 4.1.1 - thenify-all: 1.6.0 - - nanoid@3.3.7: {} - - natural-compare-lite@1.4.0: {} - - natural-compare@1.4.0: {} - - node-releases@2.0.18: {} - - normalize-package-data@2.5.0: - dependencies: - hosted-git-info: 2.8.9 - resolve: 1.22.8 - semver: 5.7.2 - validate-npm-package-license: 3.0.4 - - normalize-path@3.0.0: {} - - normalize-range@0.1.2: {} - - npm-run-path@5.3.0: - dependencies: - path-key: 4.0.0 - - nth-check@2.1.1: - dependencies: - boolbase: 1.0.0 - - object-assign@4.1.1: {} - - object-hash@3.0.0: {} - - onetime@6.0.0: - dependencies: - mimic-fn: 4.0.0 - - open@10.1.0: - dependencies: - default-browser: 5.2.1 - define-lazy-prop: 3.0.0 - is-inside-container: 1.0.0 - is-wsl: 3.1.0 - - optionator@0.9.4: - dependencies: - deep-is: 0.1.4 - fast-levenshtein: 2.0.6 - levn: 0.4.1 - prelude-ls: 1.2.1 - type-check: 0.4.0 - word-wrap: 1.2.5 - - p-event@5.0.1: - dependencies: - p-timeout: 5.1.0 - - p-limit@2.3.0: - dependencies: - p-try: 2.2.0 - - p-limit@3.1.0: - dependencies: - yocto-queue: 0.1.0 - - p-locate@4.1.0: - dependencies: - p-limit: 2.3.0 - - p-locate@5.0.0: - dependencies: - p-limit: 3.1.0 - - p-timeout@5.1.0: {} - - p-try@2.2.0: {} - - package-json-from-dist@1.0.1: {} - - package-manager-detector@0.2.2: {} - - parent-module@1.0.1: - dependencies: - callsites: 3.1.0 - - parse-gitignore@2.0.0: {} - - parse-imports@2.2.1: - dependencies: - es-module-lexer: 1.5.4 - slashes: 3.0.12 - - parse-json@5.2.0: - dependencies: - '@babel/code-frame': 7.25.7 - error-ex: 1.3.2 - json-parse-even-better-errors: 2.3.1 - lines-and-columns: 1.2.4 - - path-browserify@1.0.1: {} - - path-exists@4.0.0: {} - - path-key@3.1.1: {} - - path-key@4.0.0: {} - - path-parse@1.0.7: {} - - path-scurry@1.11.1: - dependencies: - lru-cache: 10.4.3 - minipass: 7.1.2 - - path-type@4.0.0: {} - - pathe@1.1.2: {} - - perfect-debounce@1.0.0: {} - - picocolors@1.1.0: {} - - picomatch@2.3.1: {} - - picomatch@4.0.2: {} - - pify@2.3.0: {} - - pinia@2.2.4(typescript@5.6.3)(vue@3.4.38(typescript@5.6.3)): - dependencies: - '@vue/devtools-api': 6.6.4 - vue: 3.4.38(typescript@5.6.3) - vue-demi: 0.14.10(vue@3.4.38(typescript@5.6.3)) - optionalDependencies: - typescript: 5.6.3 - - pirates@4.0.6: {} - - pkg-types@1.2.1: - dependencies: - confbox: 0.1.8 - mlly: 1.7.2 - pathe: 1.1.2 - - pluralize@8.0.0: {} - - postcss-import@15.1.0(postcss@8.4.47): - dependencies: - postcss: 8.4.47 - postcss-value-parser: 4.2.0 - read-cache: 1.0.0 - resolve: 1.22.8 - - postcss-js@4.0.1(postcss@8.4.47): - dependencies: - camelcase-css: 2.0.1 - postcss: 8.4.47 - - postcss-load-config@4.0.2(postcss@8.4.47): - dependencies: - lilconfig: 3.1.2 - yaml: 2.5.1 - optionalDependencies: - postcss: 8.4.47 - - postcss-nested@6.2.0(postcss@8.4.47): - dependencies: - postcss: 8.4.47 - postcss-selector-parser: 6.1.2 - - postcss-selector-parser@6.1.2: - dependencies: - cssesc: 3.0.0 - util-deprecate: 1.0.2 - - postcss-value-parser@4.2.0: {} - - postcss@8.4.47: - dependencies: - nanoid: 3.3.7 - picocolors: 1.1.0 - source-map-js: 1.2.1 - - prelude-ls@1.2.1: {} - - prettier-linter-helpers@1.0.0: - dependencies: - fast-diff: 1.3.0 - - prettier@3.3.3: {} - - primeflex@3.3.1: {} - - primeicons@7.0.0: {} - - primevue@4.1.0(vue@3.4.38(typescript@5.6.3)): - dependencies: - '@primeuix/styled': 0.2.0 - '@primeuix/utils': 0.2.0 - '@primevue/core': 4.1.0(vue@3.4.38(typescript@5.6.3)) - '@primevue/icons': 4.1.0(vue@3.4.38(typescript@5.6.3)) - transitivePeerDependencies: - - vue - - punycode.js@2.3.1: {} - - punycode@2.3.1: {} - - queue-microtask@1.2.3: {} - - read-cache@1.0.0: - dependencies: - pify: 2.3.0 - - read-pkg-up@7.0.1: - dependencies: - find-up: 4.1.0 - read-pkg: 5.2.0 - type-fest: 0.8.1 - - read-pkg@5.2.0: - dependencies: - '@types/normalize-package-data': 2.4.4 - normalize-package-data: 2.5.0 - parse-json: 5.2.0 - type-fest: 0.6.0 - - readdirp@3.6.0: - dependencies: - picomatch: 2.3.1 - - refa@0.12.1: - dependencies: - '@eslint-community/regexpp': 4.11.1 - - regexp-ast-analysis@0.7.1: - dependencies: - '@eslint-community/regexpp': 4.11.1 - refa: 0.12.1 - - regexp-tree@0.1.27: {} - - regjsparser@0.10.0: - dependencies: - jsesc: 0.5.0 - - require-directory@2.1.1: {} - - resolve-from@4.0.0: {} - - resolve-pkg-maps@1.0.0: {} - - resolve.exports@2.0.2: {} - - resolve@1.22.8: - dependencies: - is-core-module: 2.15.1 - path-parse: 1.0.7 - supports-preserve-symlinks-flag: 1.0.0 - - reusify@1.0.4: {} - - rfdc@1.4.1: {} - - rollup@4.24.0: - dependencies: - '@types/estree': 1.0.6 - optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.24.0 - '@rollup/rollup-android-arm64': 4.24.0 - '@rollup/rollup-darwin-arm64': 4.24.0 - '@rollup/rollup-darwin-x64': 4.24.0 - '@rollup/rollup-linux-arm-gnueabihf': 4.24.0 - '@rollup/rollup-linux-arm-musleabihf': 4.24.0 - '@rollup/rollup-linux-arm64-gnu': 4.24.0 - '@rollup/rollup-linux-arm64-musl': 4.24.0 - '@rollup/rollup-linux-powerpc64le-gnu': 4.24.0 - '@rollup/rollup-linux-riscv64-gnu': 4.24.0 - '@rollup/rollup-linux-s390x-gnu': 4.24.0 - '@rollup/rollup-linux-x64-gnu': 4.24.0 - '@rollup/rollup-linux-x64-musl': 4.24.0 - '@rollup/rollup-win32-arm64-msvc': 4.24.0 - '@rollup/rollup-win32-ia32-msvc': 4.24.0 - '@rollup/rollup-win32-x64-msvc': 4.24.0 - fsevents: 2.3.3 - - run-applescript@7.0.0: {} - - run-parallel@1.2.0: - dependencies: - queue-microtask: 1.2.3 - - scslre@0.3.0: - dependencies: - '@eslint-community/regexpp': 4.11.1 - refa: 0.12.1 - regexp-ast-analysis: 0.7.1 - - scule@1.3.0: {} - - section-matter@1.0.0: - dependencies: - extend-shallow: 2.0.1 - kind-of: 6.0.3 - - semver@5.7.2: {} - - semver@6.3.1: {} - - semver@7.6.3: {} - - shebang-command@2.0.0: - dependencies: - shebang-regex: 3.0.0 - - shebang-regex@3.0.0: {} - - signal-exit@3.0.7: {} - - signal-exit@4.1.0: {} - - sirv@2.0.4: - dependencies: - '@polka/url': 1.0.0-next.28 - mrmime: 2.0.0 - totalist: 3.0.1 - - sisteransi@1.0.5: {} - - slash@3.0.0: {} - - slashes@3.0.12: {} - - source-map-js@1.2.1: {} - - source-map@0.6.1: - optional: true - - spdx-correct@3.2.0: - dependencies: - spdx-expression-parse: 3.0.1 - spdx-license-ids: 3.0.20 - - spdx-exceptions@2.5.0: {} - - spdx-expression-parse@3.0.1: - dependencies: - spdx-exceptions: 2.5.0 - spdx-license-ids: 3.0.20 - - spdx-expression-parse@4.0.0: - dependencies: - spdx-exceptions: 2.5.0 - spdx-license-ids: 3.0.20 - - spdx-license-ids@3.0.20: {} - - speakingurl@14.0.1: {} - - sprintf-js@1.0.3: {} - - stable-hash@0.0.4: {} - - string-natural-compare@3.0.1: {} - - string-width@4.2.3: - dependencies: - emoji-regex: 8.0.0 - is-fullwidth-code-point: 3.0.0 - strip-ansi: 6.0.1 - - string-width@5.1.2: - dependencies: - eastasianwidth: 0.2.0 - emoji-regex: 9.2.2 - strip-ansi: 7.1.0 - - strip-ansi@6.0.1: - dependencies: - ansi-regex: 5.0.1 - - strip-ansi@7.1.0: - dependencies: - ansi-regex: 6.1.0 - - strip-bom-string@1.0.0: {} - - strip-final-newline@3.0.0: {} - - strip-indent@3.0.0: - dependencies: - min-indent: 1.0.1 - - strip-json-comments@3.1.1: {} - - strip-literal@2.1.0: - dependencies: - js-tokens: 9.0.0 - - sucrase@3.35.0: - dependencies: - '@jridgewell/gen-mapping': 0.3.5 - commander: 4.1.1 - glob: 10.4.5 - lines-and-columns: 1.2.4 - mz: 2.7.0 - pirates: 4.0.6 - ts-interface-checker: 0.1.13 - - super-regex@0.2.0: - dependencies: - clone-regexp: 3.0.0 - function-timeout: 0.1.1 - time-span: 5.1.0 - - superjson@2.2.1: - dependencies: - copy-anything: 3.0.5 - - supports-color@5.5.0: - dependencies: - has-flag: 3.0.0 - - supports-color@7.2.0: - dependencies: - has-flag: 4.0.0 - - supports-preserve-symlinks-flag@1.0.0: {} - - svg-tags@1.0.0: {} - - synckit@0.6.2: - dependencies: - tslib: 2.7.0 - - synckit@0.9.2: - dependencies: - '@pkgr/core': 0.1.1 - tslib: 2.7.0 - - tailwindcss@3.4.13: - dependencies: - '@alloc/quick-lru': 5.2.0 - arg: 5.0.2 - chokidar: 3.6.0 - didyoumean: 1.2.2 - dlv: 1.1.3 - fast-glob: 3.3.2 - glob-parent: 6.0.2 - is-glob: 4.0.3 - jiti: 1.21.6 - lilconfig: 2.1.0 - micromatch: 4.0.8 - normalize-path: 3.0.0 - object-hash: 3.0.0 - picocolors: 1.1.0 - postcss: 8.4.47 - postcss-import: 15.1.0(postcss@8.4.47) - postcss-js: 4.0.1(postcss@8.4.47) - postcss-load-config: 4.0.2(postcss@8.4.47) - postcss-nested: 6.2.0(postcss@8.4.47) - postcss-selector-parser: 6.1.2 - resolve: 1.22.8 - sucrase: 3.35.0 - transitivePeerDependencies: - - ts-node - - tapable@2.2.1: {} - - text-table@0.2.0: {} - - thenify-all@1.6.0: - dependencies: - thenify: 3.3.1 - - thenify@3.3.1: - dependencies: - any-promise: 1.3.0 - - time-span@5.1.0: - dependencies: - convert-hrtime: 5.0.0 - - tinyexec@0.3.0: {} - - to-fast-properties@2.0.0: {} - - to-regex-range@5.0.1: - dependencies: - is-number: 7.0.0 - - toml-eslint-parser@0.10.0: - dependencies: - eslint-visitor-keys: 3.4.3 - - totalist@3.0.1: {} - - ts-api-utils@1.3.0(typescript@5.6.3): - dependencies: - typescript: 5.6.3 - - ts-interface-checker@0.1.13: {} - - tslib@2.7.0: {} - - tsx@4.19.1: - dependencies: - esbuild: 0.23.1 - get-tsconfig: 4.8.1 - optionalDependencies: - fsevents: 2.3.3 - - type-check@0.4.0: - dependencies: - prelude-ls: 1.2.1 - - type-fest@0.20.2: {} - - type-fest@0.6.0: {} - - type-fest@0.8.1: {} - - typescript@5.6.3: {} - - uc.micro@2.1.0: {} - - ufo@1.5.4: {} - - unconfig@0.5.5: - dependencies: - '@antfu/utils': 0.7.10 - defu: 6.1.4 - importx: 0.4.4 - transitivePeerDependencies: - - supports-color - - undici-types@6.19.8: {} - - unimport@3.13.1(rollup@4.24.0): - dependencies: - '@rollup/pluginutils': 5.1.2(rollup@4.24.0) - acorn: 8.12.1 - escape-string-regexp: 5.0.0 - estree-walker: 3.0.3 - fast-glob: 3.3.2 - local-pkg: 0.5.0 - magic-string: 0.30.12 - mlly: 1.7.2 - pathe: 1.1.2 - pkg-types: 1.2.1 - scule: 1.3.0 - strip-literal: 2.1.0 - unplugin: 1.14.1 - transitivePeerDependencies: - - rollup - - webpack-sources - - unist-util-is@6.0.0: - dependencies: - '@types/unist': 3.0.3 - - unist-util-stringify-position@4.0.0: - dependencies: - '@types/unist': 3.0.3 - - unist-util-visit-parents@6.0.1: - dependencies: - '@types/unist': 3.0.3 - unist-util-is: 6.0.0 - - unist-util-visit@5.0.0: - dependencies: - '@types/unist': 3.0.3 - unist-util-is: 6.0.0 - unist-util-visit-parents: 6.0.1 - - universalify@2.0.1: {} - - unplugin-auto-import@0.18.3(@vueuse/core@11.1.0(vue@3.4.38(typescript@5.6.3)))(rollup@4.24.0): - dependencies: - '@antfu/utils': 0.7.10 - '@rollup/pluginutils': 5.1.2(rollup@4.24.0) - fast-glob: 3.3.2 - local-pkg: 0.5.0 - magic-string: 0.30.12 - minimatch: 9.0.5 - unimport: 3.13.1(rollup@4.24.0) - unplugin: 1.14.1 - optionalDependencies: - '@vueuse/core': 11.1.0(vue@3.4.38(typescript@5.6.3)) - transitivePeerDependencies: - - rollup - - webpack-sources - - unplugin-combine@1.0.3(esbuild@0.23.1)(rollup@4.24.0)(vite@5.4.8(@types/node@22.7.5)): - dependencies: - '@antfu/utils': 0.7.10 - unplugin: 1.14.1 - optionalDependencies: - esbuild: 0.23.1 - rollup: 4.24.0 - vite: 5.4.8(@types/node@22.7.5) - transitivePeerDependencies: - - webpack-sources - - unplugin-vue-components@0.27.4(@babel/parser@7.25.8)(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3)): - dependencies: - '@antfu/utils': 0.7.10 - '@rollup/pluginutils': 5.1.2(rollup@4.24.0) - chokidar: 3.6.0 - debug: 4.3.7 - fast-glob: 3.3.2 - local-pkg: 0.5.0 - magic-string: 0.30.12 - minimatch: 9.0.5 - mlly: 1.7.2 - unplugin: 1.14.1 - vue: 3.4.38(typescript@5.6.3) - optionalDependencies: - '@babel/parser': 7.25.8 - transitivePeerDependencies: - - rollup - - supports-color - - webpack-sources - - unplugin-vue-define-options@1.5.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3)): - dependencies: - '@vue-macros/common': 1.14.0(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3)) - ast-walker-scope: 0.6.2 - unplugin: 1.14.1 - transitivePeerDependencies: - - rollup - - vue - - webpack-sources - - unplugin-vue-macros@2.12.3(@vueuse/core@11.1.0(vue@3.4.38(typescript@5.6.3)))(esbuild@0.23.1)(rollup@4.24.0)(typescript@5.6.3)(vite@5.4.8(@types/node@22.7.5))(vue-tsc@2.1.6(typescript@5.6.3))(vue@3.4.38(typescript@5.6.3)): - dependencies: - '@vue-macros/better-define': 1.9.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3)) - '@vue-macros/boolean-prop': 0.5.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3)) - '@vue-macros/chain-call': 0.4.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3)) - '@vue-macros/common': 1.14.0(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3)) - '@vue-macros/config': 0.4.2(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3)) - '@vue-macros/define-emit': 0.4.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3)) - '@vue-macros/define-models': 1.3.1(@vueuse/core@11.1.0(vue@3.4.38(typescript@5.6.3)))(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3)) - '@vue-macros/define-prop': 0.5.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3)) - '@vue-macros/define-props': 4.0.1(@vue-macros/reactivity-transform@1.1.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3)))(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3)) - '@vue-macros/define-props-refs': 1.3.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3)) - '@vue-macros/define-render': 1.6.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3)) - '@vue-macros/define-slots': 1.2.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3)) - '@vue-macros/devtools': 0.4.0(typescript@5.6.3)(vite@5.4.8(@types/node@22.7.5)) - '@vue-macros/export-expose': 0.3.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3)) - '@vue-macros/export-props': 0.6.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3)) - '@vue-macros/export-render': 0.3.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3)) - '@vue-macros/hoist-static': 1.6.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3)) - '@vue-macros/jsx-directive': 0.9.1(rollup@4.24.0)(typescript@5.6.3) - '@vue-macros/named-template': 0.5.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3)) - '@vue-macros/reactivity-transform': 1.1.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3)) - '@vue-macros/script-lang': 0.2.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3)) - '@vue-macros/setup-block': 0.4.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3)) - '@vue-macros/setup-component': 0.18.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3)) - '@vue-macros/setup-sfc': 0.18.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3)) - '@vue-macros/short-bind': 1.1.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3)) - '@vue-macros/short-emits': 1.6.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3)) - '@vue-macros/short-vmodel': 1.5.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3)) - '@vue-macros/volar': 0.30.3(rollup@4.24.0)(typescript@5.6.3)(vue-tsc@2.1.6(typescript@5.6.3))(vue@3.4.38(typescript@5.6.3)) - unplugin: 1.14.1 - unplugin-combine: 1.0.3(esbuild@0.23.1)(rollup@4.24.0)(vite@5.4.8(@types/node@22.7.5)) - unplugin-vue-define-options: 1.5.1(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3)) - vue: 3.4.38(typescript@5.6.3) - transitivePeerDependencies: - - '@rspack/core' - - '@vueuse/core' - - esbuild - - rolldown - - rollup - - supports-color - - typescript - - vite - - vue-tsc - - webpack - - webpack-sources - - unplugin-vue-markdown@0.26.2(rollup@4.24.0)(vite@5.4.8(@types/node@22.7.5)): - dependencies: - '@mdit-vue/plugin-component': 2.1.3 - '@mdit-vue/plugin-frontmatter': 2.1.3 - '@mdit-vue/types': 2.1.0 - '@rollup/pluginutils': 5.1.2(rollup@4.24.0) - '@types/markdown-it': 14.1.2 - markdown-it: 14.1.0 - unplugin: 1.14.1 - vite: 5.4.8(@types/node@22.7.5) - transitivePeerDependencies: - - rollup - - webpack-sources - - unplugin-vue-router@0.10.8(rollup@4.24.0)(vue-router@4.4.5(vue@3.4.38(typescript@5.6.3)))(vue@3.4.38(typescript@5.6.3)): - dependencies: - '@babel/types': 7.25.8 - '@rollup/pluginutils': 5.1.2(rollup@4.24.0) - '@vue-macros/common': 1.14.0(rollup@4.24.0)(vue@3.4.38(typescript@5.6.3)) - ast-walker-scope: 0.6.2 - chokidar: 3.6.0 - fast-glob: 3.3.2 - json5: 2.2.3 - local-pkg: 0.5.0 - magic-string: 0.30.12 - mlly: 1.7.2 - pathe: 1.1.2 - scule: 1.3.0 - unplugin: 1.14.1 - yaml: 2.5.1 - optionalDependencies: - vue-router: 4.4.5(vue@3.4.38(typescript@5.6.3)) - transitivePeerDependencies: - - rollup - - vue - - webpack-sources - - unplugin@1.14.1: - dependencies: - acorn: 8.12.1 - webpack-virtual-modules: 0.6.2 - - update-browserslist-db@1.1.1(browserslist@4.24.0): - dependencies: - browserslist: 4.24.0 - escalade: 3.2.0 - picocolors: 1.1.0 - - uri-js@4.4.1: - dependencies: - punycode: 2.3.1 - - util-deprecate@1.0.2: {} - - uuid@10.0.0: {} - - validate-npm-package-license@3.0.4: - dependencies: - spdx-correct: 3.2.0 - spdx-expression-parse: 3.0.1 - - vite-hot-client@0.2.3(vite@5.4.8(@types/node@22.7.5)): - dependencies: - vite: 5.4.8(@types/node@22.7.5) - - vite-plugin-inspect@0.8.7(rollup@4.24.0)(vite@5.4.8(@types/node@22.7.5)): - dependencies: - '@antfu/utils': 0.7.10 - '@rollup/pluginutils': 5.1.2(rollup@4.24.0) - debug: 4.3.7 - error-stack-parser-es: 0.1.5 - fs-extra: 11.2.0 - open: 10.1.0 - perfect-debounce: 1.0.0 - picocolors: 1.1.0 - sirv: 2.0.4 - vite: 5.4.8(@types/node@22.7.5) - transitivePeerDependencies: - - rollup - - supports-color - - vite-plugin-vue-devtools@7.4.6(rollup@4.24.0)(vite@5.4.8(@types/node@22.7.5))(vue@3.4.38(typescript@5.6.3)): - dependencies: - '@vue/devtools-core': 7.4.6(vite@5.4.8(@types/node@22.7.5))(vue@3.4.38(typescript@5.6.3)) - '@vue/devtools-kit': 7.4.6 - '@vue/devtools-shared': 7.4.6 - execa: 8.0.1 - sirv: 2.0.4 - vite: 5.4.8(@types/node@22.7.5) - vite-plugin-inspect: 0.8.7(rollup@4.24.0)(vite@5.4.8(@types/node@22.7.5)) - vite-plugin-vue-inspector: 5.2.0(vite@5.4.8(@types/node@22.7.5)) - transitivePeerDependencies: - - '@nuxt/kit' - - rollup - - supports-color - - vue - - vite-plugin-vue-inspector@5.2.0(vite@5.4.8(@types/node@22.7.5)): - dependencies: - '@babel/core': 7.25.8 - '@babel/plugin-proposal-decorators': 7.25.7(@babel/core@7.25.8) - '@babel/plugin-syntax-import-attributes': 7.25.7(@babel/core@7.25.8) - '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.25.8) - '@babel/plugin-transform-typescript': 7.25.7(@babel/core@7.25.8) - '@vue/babel-plugin-jsx': 1.2.5(@babel/core@7.25.8) - '@vue/compiler-dom': 3.5.11 - kolorist: 1.8.0 - magic-string: 0.30.12 - vite: 5.4.8(@types/node@22.7.5) - transitivePeerDependencies: - - supports-color - - vite-plugin-vue-layouts@0.11.0(vite@5.4.8(@types/node@22.7.5))(vue-router@4.4.5(vue@3.4.38(typescript@5.6.3)))(vue@3.4.38(typescript@5.6.3)): - dependencies: - debug: 4.3.7 - fast-glob: 3.3.2 - vite: 5.4.8(@types/node@22.7.5) - vue: 3.4.38(typescript@5.6.3) - vue-router: 4.4.5(vue@3.4.38(typescript@5.6.3)) - transitivePeerDependencies: - - supports-color - - vite@5.4.8(@types/node@22.7.5): - dependencies: - esbuild: 0.21.5 - postcss: 8.4.47 - rollup: 4.24.0 - optionalDependencies: - '@types/node': 22.7.5 - fsevents: 2.3.3 - - vscode-uri@3.0.8: {} - - vue-demi@0.14.10(vue@3.4.38(typescript@5.6.3)): - dependencies: - vue: 3.4.38(typescript@5.6.3) - - vue-eslint-parser@9.4.3(eslint@9.12.0(jiti@1.21.6)): - dependencies: - debug: 4.3.7 - eslint: 9.12.0(jiti@1.21.6) - eslint-scope: 7.2.2 - eslint-visitor-keys: 3.4.3 - espree: 9.6.1 - esquery: 1.6.0 - lodash: 4.17.21 - semver: 7.6.3 - transitivePeerDependencies: - - supports-color - - vue-i18n@10.0.4(vue@3.4.38(typescript@5.6.3)): - dependencies: - '@intlify/core-base': 10.0.4 - '@intlify/shared': 10.0.4 - '@vue/devtools-api': 6.6.4 - vue: 3.4.38(typescript@5.6.3) - - vue-router@4.4.5(vue@3.4.38(typescript@5.6.3)): - dependencies: - '@vue/devtools-api': 6.6.4 - vue: 3.4.38(typescript@5.6.3) - - vue-tsc@2.1.6(typescript@5.6.3): - dependencies: - '@volar/typescript': 2.4.6 - '@vue/language-core': 2.1.6(typescript@5.6.3) - semver: 7.6.3 - typescript: 5.6.3 - - vue@3.4.38(typescript@5.6.3): - dependencies: - '@vue/compiler-dom': 3.4.38 - '@vue/compiler-sfc': 3.4.38 - '@vue/runtime-dom': 3.4.38 - '@vue/server-renderer': 3.4.38(vue@3.4.38(typescript@5.6.3)) - '@vue/shared': 3.4.38 - optionalDependencies: - typescript: 5.6.3 - - vue@3.5.11(typescript@5.6.3): - dependencies: - '@vue/compiler-dom': 3.5.11 - '@vue/compiler-sfc': 3.5.11 - '@vue/runtime-dom': 3.5.11 - '@vue/server-renderer': 3.5.11(vue@3.5.11(typescript@5.6.3)) - '@vue/shared': 3.5.11 - optionalDependencies: - typescript: 5.6.3 - - webpack-virtual-modules@0.6.2: {} - - which@2.0.2: - dependencies: - isexe: 2.0.0 - - word-wrap@1.2.5: {} - - wrap-ansi@7.0.0: - dependencies: - ansi-styles: 4.3.0 - string-width: 4.2.3 - strip-ansi: 6.0.1 - - wrap-ansi@8.1.0: - dependencies: - ansi-styles: 6.2.1 - string-width: 5.1.2 - strip-ansi: 7.1.0 - - xml-name-validator@4.0.0: {} - - y18n@5.0.8: {} - - yallist@3.1.1: {} - - yaml-eslint-parser@1.2.3: - dependencies: - eslint-visitor-keys: 3.4.3 - lodash: 4.17.21 - yaml: 2.5.1 - - yaml@2.5.1: {} - - yargs-parser@21.1.1: {} - - yargs@17.7.2: - dependencies: - cliui: 8.0.1 - escalade: 3.2.0 - get-caller-file: 2.0.5 - require-directory: 2.1.1 - string-width: 4.2.3 - y18n: 5.0.8 - yargs-parser: 21.1.1 - - yocto-queue@0.1.0: {} - - zwitch@2.0.4: {} diff --git a/easytier-gui/src-tauri/Cargo.toml b/easytier-gui/src-tauri/Cargo.toml index b4333ac9e..f98a52cf8 100644 --- a/easytier-gui/src-tauri/Cargo.toml +++ b/easytier-gui/src-tauri/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "easytier-gui" -version = "2.4.2" +version = "2.4.5" description = "EasyTier GUI" authors = ["you"] edition = "2021" diff --git a/easytier-gui/src-tauri/src/lib.rs b/easytier-gui/src-tauri/src/lib.rs index 1333fe813..1c7eb46a9 100644 --- a/easytier-gui/src-tauri/src/lib.rs +++ b/easytier-gui/src-tauri/src/lib.rs @@ -30,6 +30,23 @@ fn easytier_version() -> Result { Ok(easytier::VERSION.to_string()) } +#[tauri::command] +fn set_dock_visibility(app: tauri::AppHandle, visible: bool) -> Result<(), String> { + #[cfg(target_os = "macos")] + { + use tauri::ActivationPolicy; + app.set_activation_policy(if visible { + ActivationPolicy::Regular + } else { + ActivationPolicy::Accessory + }) + .map_err(|e| e.to_string())?; + } + #[cfg(not(target_os = "macos"))] + let _ = (app, visible); + Ok(()) +} + #[tauri::command] fn is_autostart() -> Result { let args: Vec = std::env::args().collect(); @@ -76,9 +93,10 @@ fn retain_network_instance(instance_ids: Vec) -> Result<(), String> { } #[tauri::command] -fn collect_network_infos() -> Result, String> { +async fn collect_network_infos() -> Result, String> { let infos = INSTANCE_MANAGER .collect_network_infos() + .await .map_err(|e| e.to_string())?; let mut ret = BTreeMap::new(); @@ -199,6 +217,8 @@ pub fn run() { dir: Some(log_dir.to_string_lossy().to_string()), level: None, file: None, + size_mb: None, + count: None, }) .build() .map_err(|e| e.to_string())?; @@ -243,7 +263,8 @@ pub fn run() { set_logging_level, set_tun_fd, is_autostart, - easytier_version + easytier_version, + set_dock_visibility ]) .on_window_event(|_win, event| match event { #[cfg(not(target_os = "android"))] diff --git a/easytier-gui/src-tauri/tauri.conf.json b/easytier-gui/src-tauri/tauri.conf.json index 907deded0..0eeb6a5bd 100644 --- a/easytier-gui/src-tauri/tauri.conf.json +++ b/easytier-gui/src-tauri/tauri.conf.json @@ -17,9 +17,13 @@ "createUpdaterArtifacts": false }, "productName": "easytier-gui", - "version": "2.4.2", + "version": "2.4.5", "identifier": "com.kkrainbow.easytier", - "plugins": {}, + "plugins": { + "shell": { + "open": "^.+" + } + }, "app": { "windows": [ { diff --git a/easytier-gui/src/auto-imports.d.ts b/easytier-gui/src/auto-imports.d.ts index 18e781e20..d96e2af50 100644 --- a/easytier-gui/src/auto-imports.d.ts +++ b/easytier-gui/src/auto-imports.d.ts @@ -9,7 +9,6 @@ declare global { const EffectScope: typeof import('vue')['EffectScope'] const MenuItemExit: typeof import('./composables/tray')['MenuItemExit'] const MenuItemShow: typeof import('./composables/tray')['MenuItemShow'] - const ReinitTray: typeof import('./composables/tray')['ReinitTray'] const acceptHMRUpdate: typeof import('pinia')['acceptHMRUpdate'] const collectNetworkInfos: typeof import('./composables/network')['collectNetworkInfos'] const computed: typeof import('vue')['computed'] @@ -18,10 +17,8 @@ declare global { const customRef: typeof import('vue')['customRef'] const defineAsyncComponent: typeof import('vue')['defineAsyncComponent'] const defineComponent: typeof import('vue')['defineComponent'] - const definePage: typeof import('unplugin-vue-router/runtime')['definePage'] const defineStore: typeof import('pinia')['defineStore'] const effectScope: typeof import('vue')['effectScope'] - const event2human: typeof import('./composables/utils')['event2human'] const generateMenuItem: typeof import('./composables/tray')['generateMenuItem'] const generateNetworkConfig: typeof import('./composables/network')['generateNetworkConfig'] const getActivePinia: typeof import('pinia')['getActivePinia'] @@ -30,7 +27,6 @@ declare global { const getEasytierVersion: typeof import('./composables/network')['getEasytierVersion'] const getOsHostname: typeof import('./composables/network')['getOsHostname'] const h: typeof import('vue')['h'] - const initMobileService: typeof import('./composables/mobile_vpn')['initMobileService'] const initMobileVpnService: typeof import('./composables/mobile_vpn')['initMobileVpnService'] const inject: typeof import('vue')['inject'] const isAutostart: typeof import('./composables/network')['isAutostart'] @@ -38,7 +34,6 @@ declare global { const isReactive: typeof import('vue')['isReactive'] const isReadonly: typeof import('vue')['isReadonly'] const isRef: typeof import('vue')['isRef'] - const loadRunningInstanceIdsFromLocalStorage: typeof import('./stores/network')['loadRunningInstanceIdsFromLocalStorage'] const mapActions: typeof import('pinia')['mapActions'] const mapGetters: typeof import('pinia')['mapGetters'] const mapState: typeof import('pinia')['mapState'] @@ -46,8 +41,6 @@ declare global { const mapWritableState: typeof import('pinia')['mapWritableState'] const markRaw: typeof import('vue')['markRaw'] const nextTick: typeof import('vue')['nextTick'] - const num2ipv4: typeof import('./composables/utils')['num2ipv4'] - const num2ipv6: typeof import('./composables/utils')['num2ipv6'] const onActivated: typeof import('vue')['onActivated'] const onBeforeMount: typeof import('vue')['onBeforeMount'] const onBeforeRouteLeave: typeof import('vue-router')['onBeforeRouteLeave'] @@ -74,7 +67,6 @@ declare global { const retainNetworkInstance: typeof import('./composables/network')['retainNetworkInstance'] const runNetworkInstance: typeof import('./composables/network')['runNetworkInstance'] const setActivePinia: typeof import('pinia')['setActivePinia'] - const setAutoLaunchStatus: typeof import('./composables/network')['setAutoLaunchStatus'] const setLoggingLevel: typeof import('./composables/network')['setLoggingLevel'] const setMapStoreSuffix: typeof import('pinia')['setMapStoreSuffix'] const setTrayMenu: typeof import('./composables/tray')['setTrayMenu'] @@ -85,7 +77,6 @@ declare global { const shallowReadonly: typeof import('vue')['shallowReadonly'] const shallowRef: typeof import('vue')['shallowRef'] const storeToRefs: typeof import('pinia')['storeToRefs'] - const timeAgoCn: typeof import('./composables/utils')['timeAgoCn'] const toRaw: typeof import('vue')['toRaw'] const toRef: typeof import('vue')['toRef'] const toRefs: typeof import('vue')['toRefs'] @@ -116,6 +107,7 @@ declare global { export type { Component, ComponentPublicInstance, ComputedRef, DirectiveBinding, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, MaybeRef, MaybeRefOrGetter, VNode, WritableComputedRef } from 'vue' import('vue') } + // for vue template auto import import { UnwrapRef } from 'vue' declare module 'vue' { @@ -216,4 +208,4 @@ declare module 'vue' { readonly watchPostEffect: UnwrapRef readonly watchSyncEffect: UnwrapRef } -} +} \ No newline at end of file diff --git a/easytier-gui/src/composables/mobile_vpn.ts b/easytier-gui/src/composables/mobile_vpn.ts index a231bde95..547d622fa 100644 --- a/easytier-gui/src/composables/mobile_vpn.ts +++ b/easytier-gui/src/composables/mobile_vpn.ts @@ -93,7 +93,7 @@ async function registerVpnServiceListener() { ) } -function getRoutesForVpn(routes: Route[]): string[] { +function getRoutesForVpn(routes: Route[], node_config: NetworkTypes.NetworkConfig): string[] { if (!routes) { return [] } @@ -108,6 +108,10 @@ function getRoutesForVpn(routes: Route[]): string[] { } } + node_config.routes.forEach(r => { + ret.push(r) + }) + // sort and dedup return Array.from(new Set(ret)).sort() } @@ -142,7 +146,7 @@ async function onNetworkInstanceChange() { network_length = 24 } - const routes = getRoutesForVpn(curNetworkInfo?.routes) + const routes = getRoutesForVpn(curNetworkInfo?.routes, networkStore.curNetwork) const ipChanged = virtual_ip !== curVpnStatus.ipv4Addr const routesChanged = JSON.stringify(routes) !== JSON.stringify(curVpnStatus.routes) diff --git a/easytier-gui/src/composables/tray.ts b/easytier-gui/src/composables/tray.ts index 67c731a83..acaa7d0df 100644 --- a/easytier-gui/src/composables/tray.ts +++ b/easytier-gui/src/composables/tray.ts @@ -53,9 +53,9 @@ export async function useTray(init: boolean = false) { export async function generateMenuItem() { return [ - await MenuItemExit('Exit'), - await PredefinedMenuItem.new({ item: 'Separator' }), await MenuItemShow('Show / Hide'), + await PredefinedMenuItem.new({ item: 'Separator' }), + await MenuItemExit('Exit'), ] } diff --git a/easytier-gui/src/main.ts b/easytier-gui/src/main.ts index bb38b2d8b..cbd8be045 100644 --- a/easytier-gui/src/main.ts +++ b/easytier-gui/src/main.ts @@ -1,4 +1,4 @@ -import Aura from '@primevue/themes/aura' +import Aura from '@primeuix/themes/aura'; import PrimeVue from 'primevue/config' import ToastService from 'primevue/toastservice' diff --git a/easytier-gui/src/modules/dock_visibility.ts b/easytier-gui/src/modules/dock_visibility.ts new file mode 100644 index 000000000..bcdf4cd6c --- /dev/null +++ b/easytier-gui/src/modules/dock_visibility.ts @@ -0,0 +1,18 @@ +import { invoke } from '@tauri-apps/api/core' + +export async function loadDockVisibilityAsync(visible: boolean): Promise { + try { + await invoke('set_dock_visibility', { visible }) + localStorage.setItem('dock_visibility', JSON.stringify(visible)) + return visible + } + catch (e) { + console.error('Failed to set dock visibility:', e) + return getDockVisibilityStatus() + } +} + +export function getDockVisibilityStatus(): boolean { + const stored = localStorage.getItem('dock_visibility') + return stored !== null ? JSON.parse(stored) : true +} diff --git a/easytier-gui/src/pages/index.vue b/easytier-gui/src/pages/index.vue index adfc58817..609c55a67 100644 --- a/easytier-gui/src/pages/index.vue +++ b/easytier-gui/src/pages/index.vue @@ -13,6 +13,7 @@ import { NetworkTypes, Config, Status, Utils, I18nUtils, ConfigEditDialog } from import { isAutostart, setLoggingLevel } from '~/composables/network' import { useTray } from '~/composables/tray' import { getAutoLaunchStatusAsync as getAutoLaunchStatus, loadAutoLaunchStatusAsync } from '~/modules/auto_launch' +import { getDockVisibilityStatus, loadDockVisibilityAsync } from '~/modules/dock_visibility' const { t, locale } = useI18n() const visible = ref(false) @@ -144,8 +145,8 @@ onMounted(async () => { window.setTimeout(async () => { await setTrayMenu([ - await MenuItemExit(t('tray.exit')), await MenuItemShow(t('tray.show')), + await MenuItemExit(t('tray.exit')), ]) }, 1000) }) @@ -165,8 +166,8 @@ const setting_menu_items = ref([ command: async () => { await I18nUtils.loadLanguageAsync((locale.value === 'en' ? 'cn' : 'en')) await setTrayMenu([ - await MenuItemExit(t('tray.exit')), await MenuItemShow(t('tray.show')), + await MenuItemExit(t('tray.exit')), ]) }, }, @@ -177,6 +178,14 @@ const setting_menu_items = ref([ await loadAutoLaunchStatusAsync(!getAutoLaunchStatus()) }, }, + { + label: () => getDockVisibilityStatus() ? t('hide_dock_icon') : t('show_dock_icon'), + icon: 'pi pi-eye-slash', + command: async () => { + await loadDockVisibilityAsync(!getDockVisibilityStatus()) + }, + visible: () => type() === 'macos', + }, { label: () => t('logging'), icon: 'pi pi-file', @@ -283,61 +292,63 @@ async function saveTomlConfig(tomlConfig: string) { -
- - - - + - - +
+ diff --git a/easytier-web/Cargo.toml b/easytier-web/Cargo.toml index da682e916..1b8bfd19d 100644 --- a/easytier-web/Cargo.toml +++ b/easytier-web/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "easytier-web" -version = "2.4.2" +version = "2.4.5" edition = "2021" description = "Config server for easytier. easytier-core gets config from this and web frontend use it as restful api server." diff --git a/easytier-web/frontend-lib/package.json b/easytier-web/frontend-lib/package.json index 8eabe3918..045592584 100644 --- a/easytier-web/frontend-lib/package.json +++ b/easytier-web/frontend-lib/package.json @@ -18,18 +18,19 @@ "preview": "vite preview" }, "dependencies": { - "@primevue/themes": "4.3.3", + "@primeuix/themes": "^1.2.3", "@vueuse/core": "^11.1.0", - "aura": "link:@primevue\\themes\\aura", "axios": "^1.7.7", + "chart.js": "^4.5.0", "floating-vue": "^5.2", "ip-num": "1.5.1", "primeicons": "^7.0.0", - "primevue": "4.3.3", + "primevue": "^4.3.9", "tailwindcss-primeui": "^0.3.4", "ts-md5": "^1.3.1", "uuid": "^11.0.2", "vue": "^3.5.12", + "vue-chartjs": "^5.3.2", "vue-i18n": "^10.0.4" }, "devDependencies": { diff --git a/easytier-web/frontend-lib/src/components/Config.vue b/easytier-web/frontend-lib/src/components/Config.vue index 71c6b687e..dd33177a2 100644 --- a/easytier-web/frontend-lib/src/components/Config.vue +++ b/easytier-web/frontend-lib/src/components/Config.vue @@ -165,11 +165,12 @@ const bool_flags: BoolFlag[] = [ { field: 'proxy_forward_by_system', help: 'proxy_forward_by_system_help' }, { field: 'disable_encryption', help: 'disable_encryption_help' }, { field: 'disable_udp_hole_punching', help: 'disable_udp_hole_punching_help' }, + { field: 'disable_sym_hole_punching', help: 'disable_sym_hole_punching_help' }, { field: 'enable_magic_dns', help: 'enable_magic_dns_help' }, { field: 'enable_private_mode', help: 'enable_private_mode_help' }, ] -const portForwardProtocolOptions = ref(["tcp","udp"]); +const portForwardProtocolOptions = ref(["tcp", "udp"]); @@ -177,7 +178,7 @@ const portForwardProtocolOptions = ref(["tcp","udp"]);
-
+
@@ -226,9 +227,8 @@ const portForwardProtocolOptions = ref(["tcp","udp"]); class="grow" multiple fluid :suggestions="peerSuggestions" @complete="searchPeerSuggestions" /> + v-model="curNetwork.public_server_url" :suggestions="publicServerSuggestions" class="grow" + dropdown :complete-on-focus="false" @complete="searchPresetPublicServers" />
@@ -307,23 +307,6 @@ const portForwardProtocolOptions = ref(["tcp","udp"]);
-
-
- - -
-
- -
-
- - -
-
-
@@ -435,56 +418,36 @@ const portForwardProtocolOptions = ref(["tcp","udp"]);
- +
- + : - +
- + : - +
-
-
diff --git a/easytier-web/frontend-lib/src/components/NetworkChart.vue b/easytier-web/frontend-lib/src/components/NetworkChart.vue new file mode 100644 index 000000000..90eb58a74 --- /dev/null +++ b/easytier-web/frontend-lib/src/components/NetworkChart.vue @@ -0,0 +1,279 @@ + + + \ No newline at end of file diff --git a/easytier-web/frontend-lib/src/components/Status.vue b/easytier-web/frontend-lib/src/components/Status.vue index 01c5eb6c2..37cec2f54 100644 --- a/easytier-web/frontend-lib/src/components/Status.vue +++ b/easytier-web/frontend-lib/src/components/Status.vue @@ -1,11 +1,12 @@ diff --git a/easytier-web/frontend/src/main.ts b/easytier-web/frontend/src/main.ts index 6c74d1cd6..134146771 100644 --- a/easytier-web/frontend/src/main.ts +++ b/easytier-web/frontend/src/main.ts @@ -4,7 +4,7 @@ import './style.css' import App from './App.vue' import EasytierFrontendLib from 'easytier-frontend-lib' import PrimeVue from 'primevue/config' -import Aura from '@primevue/themes/aura' +import Aura from '@primeuix/themes/aura'; import ConfirmationService from 'primevue/confirmationservice'; import { I18nUtils } from 'easytier-frontend-lib' diff --git a/easytier-web/src/client_manager/mod.rs b/easytier-web/src/client_manager/mod.rs index f9e08024a..8e4010dc9 100644 --- a/easytier-web/src/client_manager/mod.rs +++ b/easytier-web/src/client_manager/mod.rs @@ -247,9 +247,10 @@ impl ClientManager { #[cfg(test)] mod tests { - use std::time::Duration; + use std::{sync::Arc, time::Duration}; use easytier::{ + instance_manager::NetworkInstanceManager, tunnel::{ common::tests::wait_for_condition, udp::{UdpTunnelConnector, UdpTunnelListener}, @@ -273,7 +274,12 @@ mod tests { .unwrap(); let connector = UdpTunnelConnector::new("udp://127.0.0.1:54333".parse().unwrap()); - let _c = WebClient::new(connector, "test", "test"); + let _c = WebClient::new( + connector, + "test", + "test", + Arc::new(NetworkInstanceManager::new()), + ); wait_for_condition( || async { mgr.client_sessions.len() == 1 }, diff --git a/easytier-web/src/client_manager/session.rs b/easytier-web/src/client_manager/session.rs index 97a22598c..8287787cd 100644 --- a/easytier-web/src/client_manager/session.rs +++ b/easytier-web/src/client_manager/session.rs @@ -4,13 +4,13 @@ use anyhow::Context; use easytier::{ common::scoped_task::ScopedTask, proto::{ + api::manage::{ + NetworkConfig, RunNetworkInstanceRequest, WebClientService, + WebClientServiceClientFactory, + }, rpc_impl::bidirect::BidirectRpcManager, rpc_types::{self, controller::BaseController}, - web::{ - HeartbeatRequest, HeartbeatResponse, NetworkConfig, RunNetworkInstanceRequest, - WebClientService, WebClientServiceClientFactory, WebServerService, - WebServerServiceServer, - }, + web::{HeartbeatRequest, HeartbeatResponse, WebServerService, WebServerServiceServer}, }, tunnel::Tunnel, }; diff --git a/easytier-web/src/main.rs b/easytier-web/src/main.rs index b5949567e..35a2cd7c4 100644 --- a/easytier-web/src/main.rs +++ b/easytier-web/src/main.rs @@ -119,6 +119,8 @@ impl LoggingConfigLoader for &Cli { dir: self.file_log_dir.clone(), level: self.file_log_level.clone(), file: None, + size_mb: None, + count: None, } } } diff --git a/easytier-web/src/restful/network.rs b/easytier-web/src/restful/network.rs index 9a103cc53..90f5c5af8 100644 --- a/easytier-web/src/restful/network.rs +++ b/easytier-web/src/restful/network.rs @@ -8,7 +8,7 @@ use axum_login::AuthUser; use easytier::launcher::NetworkConfig; use easytier::proto::common::Void; use easytier::proto::rpc_types::controller::BaseController; -use easytier::proto::{self, web::*}; +use easytier::proto::{self, api::manage::*, web::*}; use crate::client_manager::session::{Location, Session}; use crate::client_manager::ClientManager; diff --git a/easytier/Cargo.toml b/easytier/Cargo.toml index 57b3298ac..768b40348 100644 --- a/easytier/Cargo.toml +++ b/easytier/Cargo.toml @@ -3,7 +3,7 @@ name = "easytier" description = "A full meshed p2p VPN, connecting all your devices in one network with one command." homepage = "https://github.com/EasyTier/EasyTier" repository = "https://github.com/EasyTier/EasyTier" -version = "2.4.2" +version = "2.4.5" edition = "2021" authors = ["kkrainbow"] keywords = ["vpn", "p2p", "network", "easytier"] @@ -40,7 +40,7 @@ tracing-subscriber = { version = "0.3", features = [ "local-time", "time", ] } -tracing-appender = "0.2.3" +console-subscriber = { version = "0.4.1", optional = true } thiserror = "1.0" auto_impl = "1.1.0" crossbeam = "0.8.4" @@ -167,7 +167,7 @@ mimalloc = { version = "*", optional = true } # mips atomic-shim = "0.2.0" -smoltcp = { version = "0.12.0", optional = true, default-features = false, features = [ +smoltcp = { git = "https://github.com/smoltcp-rs/smoltcp.git", rev = "0a926767a68bc88d5512afefa7529c5ecdade4ea", optional = true, default-features = false, features = [ "std", "medium-ip", "proto-ipv4", @@ -218,6 +218,8 @@ derive_builder = "0.20.2" humantime-serde = "1.1.1" multimap = "0.10.0" version-compare = "0.2.0" +hmac = "0.12.1" +sha2 = "0.10.8" [target.'cfg(any(target_os = "linux", target_os = "macos", target_os = "windows", target_os = "freebsd"))'.dependencies] machine-uid = "0.5.3" @@ -239,6 +241,7 @@ windows = { version = "0.52.0", features = [ "Win32_System_Com", "Win32_Networking", "Win32_System_Ole", + "Win32_System_Variant", "Win32_Networking_WinSock", "Win32_System_IO", ] } @@ -259,11 +262,17 @@ jemallocator = { package = "tikv-jemallocator", version = "0.6.0", optional = tr ] } jemalloc-ctl = { package = "tikv-jemalloc-ctl", version = "0.6.0", optional = true, features = [ ] } + +[target.'cfg(not(target_os = "macos"))'.dependencies] jemalloc-sys = { package = "tikv-jemalloc-sys", version = "0.6.0", features = [ "background_threads_runtime_support", "background_threads", ], optional = true } +[target.'cfg(target_os = "macos")'.dependencies] +jemalloc-sys = { package = "tikv-jemalloc-sys", version = "0.6.0", features = [ +], optional = true } + [build-dependencies] tonic-build = "0.12" globwalk = "0.8.1" @@ -291,6 +300,7 @@ serial_test = "3.0.0" rstest = "0.25.0" futures-util = "0.3.30" maplit = "1.0.2" +tempfile = "3.22.0" [target.'cfg(target_os = "linux")'.dev-dependencies] defguard_wireguard_rs = "0.4.2" @@ -325,3 +335,4 @@ smoltcp = ["dep:smoltcp", "dep:parking_lot"] socks5 = ["dep:smoltcp"] jemalloc = ["dep:jemallocator", "dep:jemalloc-sys"] jemalloc-prof = ["jemalloc", "dep:jemalloc-ctl", "jemalloc-ctl/stats", "jemalloc-sys/profiling", "jemalloc-sys/stats"] +tracing = ["tokio/tracing", "dep:console-subscriber"] diff --git a/easytier/build.rs b/easytier/build.rs index 682738709..adef2ef1b 100644 --- a/easytier/build.rs +++ b/easytier/build.rs @@ -144,7 +144,10 @@ fn main() -> Result<(), Box> { let proto_files = [ "src/proto/error.proto", "src/proto/tests.proto", - "src/proto/cli.proto", + "src/proto/api_instance.proto", + "src/proto/api_logger.proto", + "src/proto/api_config.proto", + "src/proto/api_manage.proto", "src/proto/web.proto", "src/proto/magic_dns.proto", "src/proto/acl.proto", @@ -160,8 +163,9 @@ fn main() -> Result<(), Box> { .type_attribute(".acl", "#[derive(serde::Serialize, serde::Deserialize)]") .type_attribute(".common", "#[derive(serde::Serialize, serde::Deserialize)]") .type_attribute(".error", "#[derive(serde::Serialize, serde::Deserialize)]") - .type_attribute(".cli", "#[derive(serde::Serialize, serde::Deserialize)]") + .type_attribute(".api", "#[derive(serde::Serialize, serde::Deserialize)]") .type_attribute(".web", "#[derive(serde::Serialize, serde::Deserialize)]") + .type_attribute(".config", "#[derive(serde::Serialize, serde::Deserialize)]") .type_attribute( "peer_rpc.GetIpListResponse", "#[derive(serde::Serialize, serde::Deserialize)]", @@ -169,8 +173,16 @@ fn main() -> Result<(), Box> { .type_attribute("peer_rpc.DirectConnectedPeerInfo", "#[derive(Hash)]") .type_attribute("peer_rpc.PeerInfoForGlobalMap", "#[derive(Hash)]") .type_attribute("peer_rpc.ForeignNetworkRouteInfoKey", "#[derive(Hash, Eq)]") + .type_attribute( + "peer_rpc.RouteForeignNetworkSummary.Info", + "#[derive(Hash, Eq, serde::Serialize, serde::Deserialize)]", + ) + .type_attribute( + "peer_rpc.RouteForeignNetworkSummary", + "#[derive(Hash, Eq, serde::Serialize, serde::Deserialize)]", + ) .type_attribute("common.RpcDescriptor", "#[derive(Hash, Eq)]") - .field_attribute(".web.NetworkConfig", "#[serde(default)]") + .field_attribute(".api.manage.NetworkConfig", "#[serde(default)]") .service_generator(Box::new(rpc_build::ServiceGenerator::new())) .btree_map(["."]) .skip_debug([".common.Ipv4Addr", ".common.Ipv6Addr", ".common.UUID"]); diff --git a/easytier/locales/app.yml b/easytier/locales/app.yml index 67a07437c..b132c3f89 100644 --- a/easytier/locales/app.yml +++ b/easytier/locales/app.yml @@ -151,6 +151,9 @@ core_clap: disable_udp_hole_punching: en: "disable udp hole punching" zh-CN: "禁用UDP打洞功能" + disable_sym_hole_punching: + en: "if true, disable udp nat hole punching for symmetric nat (NAT4), which is based on birthday attack and may be blocked by ISP." + zh-CN: "如果为true,则禁用基于生日攻击的对称NAT (NAT4) UDP 打洞功能,该打洞方式可能会被运营商封锁" relay_all_peer_rpc: en: "relay all peer rpc packets, even if the peer is not in the relay network whitelist. this can help peers not in relay network whitelist to establish p2p connection." zh-CN: "转发所有对等节点的RPC数据包,即使对等节点不在转发网络白名单中。这可以帮助白名单外网络中的对等节点建立P2P连接。" @@ -187,6 +190,9 @@ core_clap: accept_dns: en: "if true, enable magic dns. with magic dns, you can access other nodes with a domain name, e.g.: .et.net. magic dns will modify your system dns settings, enable it carefully." zh-CN: "如果为true,则启用魔法DNS。使用魔法DNS,您可以使用域名访问其他节点,例如:.et.net。魔法DNS将修改您的系统DNS设置,请谨慎启用。" + tld_dns_zone: + en: "specify the top-level domain zone for magic DNS. if not provided, defaults to the value from dns_server module (et.net.). only used when accept_dns is true." + zh-CN: "指定魔法DNS的顶级域名区域。如果未提供,默认使用dns_server模块中的值(et.net.)。仅在accept_dns为true时使用。" private_mode: en: "if true, nodes with different network names or passwords from this network are not allowed to perform handshake or relay through this node." zh-CN: "如果为true,则不允许使用了与本网络不相同的网络名称和密码的节点通过本节点进行握手或中转" @@ -205,6 +211,21 @@ core_clap: enable_relay_foreign_network_kcp: en: "if true, allow relay kcp packets from foreign network. default is false (not forward foreign network kcp packets)" zh-CN: "如果为true,则作为共享节点时也可以转发其他网络的 KCP 数据包。默认值为false(不转发)" + stun_servers: + en: "Override default STUN servers; If configured but empty, STUN servers are not used" + zh-CN: "覆盖内置的默认 STUN server 列表;如果设置了但是为空,则不使用 STUN servers;如果没设置,则使用默认 STUN server 列表" + stun_servers_v6: + en: "Override default STUN servers, IPv6; If configured but empty, IPv6 STUN servers are not used" + zh-CN: "覆盖内置的默认 IPv6 STUN server 列表;如果设置了但是为空,则不使用 IPv6 STUN servers;如果没设置,则使用默认 IPv6 STUN server 列表" + check_config: + en: Check config validity without starting the network + zh-CN: 检查配置文件的有效性并退出 + file_log_size_mb: + en: "per file log size in MB, default is 100MB" + zh-CN: "单个文件日志大小,单位 MB,默认值为 100MB" + file_log_count: + en: "max file log count, default is 10" + zh-CN: "最大文件日志数量,默认值为 10" core_app: panic_backtrace_save: diff --git a/easytier/src/arch/windows.rs b/easytier/src/arch/windows.rs index 1c7799cad..9d97ecfcf 100644 --- a/easytier/src/arch/windows.rs +++ b/easytier/src/arch/windows.rs @@ -1,4 +1,4 @@ -use std::{io, net::SocketAddr, os::windows::io::AsRawSocket}; +use std::{io, mem::ManuallyDrop, net::SocketAddr, os::windows::io::AsRawSocket}; use anyhow::Context; use network_interface::NetworkInterfaceConfig; @@ -18,6 +18,8 @@ use windows::{ System::Com::{ CoCreateInstance, CoInitializeEx, CoUninitialize, CLSCTX_ALL, COINIT_MULTITHREADED, }, + System::Ole::{SafeArrayCreateVector, SafeArrayPutElement}, + System::Variant::{VARENUM, VARIANT, VT_ARRAY, VT_BSTR, VT_VARIANT}, }, }; @@ -82,7 +84,7 @@ pub fn find_interface_index(iface_name: &str) -> io::Result { tracing::error!("Failed to find interface index for {}", iface_name); Err(io::Error::new( io::ErrorKind::NotFound, - format!("{}", iface_name), + iface_name.to_string(), )) } @@ -247,20 +249,20 @@ pub fn add_interface_to_firewall_allowlist(interface_name: &str) -> anyhow::Resu ); // Create rules for each protocol type - add_protocol_firewall_rules(&policy, interface_name, "TCP", 6)?; // TCP protocol number 6 + add_protocol_firewall_rules(&policy, interface_name, "TCP", Some(6))?; // TCP protocol number 6 tracing::debug!("Added TCP firewall rules for interface: {}", interface_name); - add_protocol_firewall_rules(&policy, interface_name, "UDP", 17)?; // UDP protocol number 17 + add_protocol_firewall_rules(&policy, interface_name, "UDP", Some(17))?; // UDP protocol number 17 tracing::debug!("Added UDP firewall rules for interface: {}", interface_name); - add_protocol_firewall_rules(&policy, interface_name, "ICMP", 1)?; // ICMP protocol number 1 + add_protocol_firewall_rules(&policy, interface_name, "ICMP", Some(1))?; // ICMP protocol number 1 tracing::debug!( "Added ICMP firewall rules for interface: {}", interface_name ); // Add fallback rules for all protocols - add_all_protocols_firewall_rules(&policy, interface_name)?; + add_protocol_firewall_rules(&policy, interface_name, "ALL", None)?; tracing::debug!( "Added fallback all-protocols rules for interface: {}", interface_name @@ -279,7 +281,7 @@ fn add_protocol_firewall_rules( policy: &INetFwPolicy2, interface_name: &str, protocol_name: &str, - protocol_number: i32, + protocol_number: Option, ) -> anyhow::Result<()> { // Create rules for both inbound and outbound traffic for (is_inbound, direction_name) in [(true, "Inbound"), (false, "Outbound")] { @@ -307,7 +309,9 @@ fn add_protocol_firewall_rules( unsafe { rule.SetName(&name_bstr)?; rule.SetDescription(&desc_bstr)?; - rule.SetProtocol(protocol_number)?; + if let Some(protocol_number) = protocol_number { + rule.SetProtocol(protocol_number)?; + } rule.SetAction(NET_FW_ACTION_ALLOW)?; if is_inbound { @@ -322,61 +326,35 @@ fn add_protocol_firewall_rules( )?; rule.SetGrouping(&BSTR::from("EasyTier"))?; - // Get rule collection and add new rule - let rules = policy.Rules()?; - rules.Remove(&name_bstr)?; // Remove existing rule with same name first - rules.Add(&rule)?; - } - } + // Set the interface for this rule to apply to the specific network interface + // According to Microsoft docs, interfaces should be represented by their friendly name + // We need to create a SAFEARRAY of VARIANT strings containing the interface name + let interface_bstr = BSTR::from(interface_name); - Ok(()) -} - -/// Add fallback rules for all protocols -fn add_all_protocols_firewall_rules( - policy: &INetFwPolicy2, - interface_name: &str, -) -> anyhow::Result<()> { - // Create rules for both inbound and outbound traffic - for (is_inbound, direction_name) in [(true, "Inbound"), (false, "Outbound")] { - // Create firewall rule instance - let rule: INetFwRule = unsafe { - CoCreateInstance( - &windows::Win32::NetworkManagement::WindowsFirewall::NetFwRule, - None, - CLSCTX_ALL, - ) - }?; - - let rule_name = format!( - "EasyTier {} - All Protocols ({})", - interface_name, direction_name - ); - let description = format!( - "Allow all protocol traffic on EasyTier interface {}", - interface_name - ); + // Create a SAFEARRAY containing one interface name + let interface_array = SafeArrayCreateVector(VT_VARIANT, 0, 1); + if interface_array.is_null() { + return Err(anyhow::anyhow!("Failed to create SAFEARRAY")); + } - let name_bstr = BSTR::from(&rule_name); - let desc_bstr = BSTR::from(&description); + let index = 0i32; + let mut variant_interface = VARIANT::default(); + (*variant_interface.Anonymous.Anonymous).vt = VT_BSTR; + (*variant_interface.Anonymous.Anonymous).Anonymous.bstrVal = + ManuallyDrop::new(interface_bstr); - unsafe { - rule.SetName(&name_bstr)?; - rule.SetDescription(&desc_bstr)?; - // Don't set protocol - allows all protocols by default - rule.SetAction(NET_FW_ACTION_ALLOW)?; + SafeArrayPutElement( + interface_array, + &index as *const _ as *const i32, + &variant_interface as *const _ as *const std::ffi::c_void, + )?; - if is_inbound { - rule.SetDirection(NET_FW_RULE_DIR_IN)?; - } else { - rule.SetDirection(NET_FW_RULE_DIR_OUT)?; - } + // Create the VARIANT that contains the SAFEARRAY + let mut interface_variant = VARIANT::default(); + (*interface_variant.Anonymous.Anonymous).vt = VARENUM(VT_ARRAY.0 | VT_VARIANT.0); + (*interface_variant.Anonymous.Anonymous).Anonymous.parray = interface_array; - rule.SetEnabled(windows::Win32::Foundation::VARIANT_TRUE)?; - rule.SetProfiles( - NET_FW_PROFILE2_PRIVATE.0 | NET_FW_PROFILE2_PUBLIC.0 | NET_FW_PROFILE2_DOMAIN.0, - )?; - rule.SetGrouping(&BSTR::from("EasyTier"))?; + rule.SetInterfaces(interface_variant)?; // Get rule collection and add new rule let rules = policy.Rules()?; @@ -402,8 +380,7 @@ pub fn remove_interface_firewall_rules(interface_name: &str) -> anyhow::Result<( let rules = unsafe { policy.Rules()? }; - // Remove protocol-specific rules - for protocol_name in ["TCP", "UDP", "ICMP"] { + for protocol_name in ["TCP", "UDP", "ICMP", "ALL"] { for direction in ["Inbound", "Outbound"] { let rule_name = format!( "EasyTier {} - {} Protocol ({})", @@ -416,18 +393,6 @@ pub fn remove_interface_firewall_rules(interface_name: &str) -> anyhow::Result<( } } - // Remove fallback protocol rules - for direction in ["Inbound", "Outbound"] { - let rule_name = format!( - "EasyTier {} - All Protocols ({})", - interface_name, direction - ); - let name_bstr = BSTR::from(&rule_name); - unsafe { - let _ = rules.Remove(&name_bstr); // Ignore errors, rule might not exist - } - } - Ok(()) } diff --git a/easytier/src/common/acl_processor.rs b/easytier/src/common/acl_processor.rs index 6f979e3c0..b8ffaabc7 100644 --- a/easytier/src/common/acl_processor.rs +++ b/easytier/src/common/acl_processor.rs @@ -1,5 +1,5 @@ use std::{ - collections::HashMap, + collections::{HashMap, HashSet}, net::{IpAddr, SocketAddr}, str::FromStr as _, sync::Arc, @@ -61,6 +61,8 @@ pub struct FastLookupRule { pub dst_ip_ranges: Vec, pub src_port_ranges: Vec<(u16, u16)>, pub dst_port_ranges: Vec<(u16, u16)>, + pub source_groups: HashSet, + pub destination_groups: HashSet, pub action: Action, pub enabled: bool, pub stateful: bool, @@ -78,6 +80,8 @@ pub struct AclCacheKey { pub dst_ip: IpAddr, pub src_port: u16, pub dst_port: u16, + pub src_groups: Arc>, + pub dst_groups: Arc>, } impl AclCacheKey { @@ -89,6 +93,8 @@ impl AclCacheKey { dst_ip: packet_info.dst_ip, src_port: packet_info.src_port.unwrap_or(0), dst_port: packet_info.dst_port.unwrap_or(0), + src_groups: packet_info.src_groups.clone(), + dst_groups: packet_info.dst_groups.clone(), } } } @@ -116,6 +122,8 @@ pub struct PacketInfo { pub dst_port: Option, pub protocol: Protocol, pub packet_size: usize, + pub src_groups: Arc>, + pub dst_groups: Arc>, } // ACL processing result @@ -684,6 +692,28 @@ impl AclProcessor { } } + // Source group check + if !rule.source_groups.is_empty() { + let matches = packet_info + .src_groups + .iter() + .any(|group| rule.source_groups.contains(group)); + if !matches { + return false; + } + } + + // Destination group check + if !rule.destination_groups.is_empty() { + let matches = packet_info + .dst_groups + .iter() + .any(|group| rule.destination_groups.contains(group)); + if !matches { + return false; + } + } + true } @@ -804,6 +834,8 @@ impl AclProcessor { dst_ip_ranges, src_port_ranges, dst_port_ranges, + source_groups: rule.source_groups.iter().cloned().collect(), + destination_groups: rule.destination_groups.iter().cloned().collect(), action: rule.action(), enabled: rule.enabled, stateful: rule.stateful, @@ -1071,6 +1103,8 @@ impl AclRuleBuilder { rate_limit: 0, burst_limit: 0, stateful: true, + source_groups: vec![], + destination_groups: vec![], }; inbound_chain.rules.push(tcp_rule); rule_priority -= 1; @@ -1093,6 +1127,8 @@ impl AclRuleBuilder { rate_limit: 0, burst_limit: 0, stateful: false, + source_groups: vec![], + destination_groups: vec![], }; inbound_chain.rules.push(udp_rule); } @@ -1108,6 +1144,10 @@ impl AclRuleBuilder { } else { acl.acl_v1 = Some(AclV1 { chains: vec![inbound_chain], + group: Some(GroupInfo { + declares: vec![], + members: vec![], + }), }); } @@ -1144,6 +1184,106 @@ mod tests { use std::hash::{Hash, Hasher}; use std::net::{IpAddr, Ipv4Addr}; + #[tokio::test] + async fn test_group_based_acl_rules() { + let mut acl_config = Acl::default(); + let mut acl_v1 = AclV1::default(); + let mut chain = Chain { + name: "group_test_chain".to_string(), + chain_type: ChainType::Inbound as i32, + enabled: true, + default_action: Action::Drop as i32, + ..Default::default() + }; + + // Rules + chain.rules.push(Rule { + name: "allow_admins_to_db".to_string(), + priority: 100, + enabled: true, + action: Action::Allow as i32, + protocol: Protocol::Any as i32, + source_groups: vec!["admin".to_string()], + destination_groups: vec!["db-server".to_string()], + ..Default::default() + }); + chain.rules.push(Rule { + name: "allow_devs_from_anywhere".to_string(), + priority: 90, + enabled: true, + action: Action::Allow as i32, + protocol: Protocol::Any as i32, + source_groups: vec!["dev".to_string()], + ..Default::default() + }); + chain.rules.push(Rule { + name: "deny_guests_to_db".to_string(), + priority: 80, + enabled: true, + action: Action::Drop as i32, + protocol: Protocol::Any as i32, + source_groups: vec!["guest".to_string()], + destination_groups: vec!["db-server".to_string()], + ..Default::default() + }); + chain.rules.push(Rule { + name: "allow_specific_ip".to_string(), + priority: 70, + enabled: true, + action: Action::Allow as i32, + protocol: Protocol::Any as i32, + source_ips: vec!["1.2.3.4/32".to_string()], + ..Default::default() + }); + + acl_v1.chains.push(chain); + acl_config.acl_v1 = Some(acl_v1); + + let processor = AclProcessor::new(acl_config); + + // Case 3.1: Source group match (devs from anywhere) + let mut packet_info = create_test_packet_info(); + packet_info.src_groups = Arc::new(vec!["dev".to_string()]); + let result = processor.process_packet(&packet_info, ChainType::Inbound); + assert_eq!(result.action, Action::Allow); + assert_eq!(result.matched_rule, Some(RuleId::Priority(90))); + + // Case 3.2: Source group no match + packet_info.src_groups = Arc::new(vec!["guest".to_string()]); + let result = processor.process_packet(&packet_info, ChainType::Inbound); + assert_eq!(result.action, Action::Drop); // Default drop + assert_eq!(result.matched_rule, Some(RuleId::Default)); + + // Case 3.3: Destination group match (deny guests to db) + packet_info.src_groups = Arc::new(vec!["guest".to_string()]); + packet_info.dst_groups = Arc::new(vec!["db-server".to_string()]); + let result = processor.process_packet(&packet_info, ChainType::Inbound); + assert_eq!(result.action, Action::Drop); + assert_eq!(result.matched_rule, Some(RuleId::Priority(80))); + + // Case 3.4: Source and Destination groups match + packet_info.src_groups = Arc::new(vec!["admin".to_string()]); + packet_info.dst_groups = Arc::new(vec!["db-server".to_string()]); + let result = processor.process_packet(&packet_info, ChainType::Inbound); + assert_eq!(result.action, Action::Allow); + assert_eq!(result.matched_rule, Some(RuleId::Priority(100))); + + // Case 3.5: Partial match (admin to web-server) + packet_info.src_groups = Arc::new(vec!["admin".to_string()]); + packet_info.dst_groups = Arc::new(vec!["web-server".to_string()]); + let result = processor.process_packet(&packet_info, ChainType::Inbound); + assert_eq!(result.action, Action::Drop); // Default drop + assert_eq!(result.matched_rule, Some(RuleId::Default)); + + // Case 3.6: Rule with no group definition + packet_info.src_ip = "1.2.3.4".parse().unwrap(); + packet_info.src_groups = Arc::new(vec!["admin".to_string()]); + packet_info.dst_groups = Arc::new(vec![]); + let result = processor.process_packet(&packet_info, ChainType::Inbound); + assert_eq!(result.action, Action::Allow); + assert_eq!(result.matched_rule, Some(RuleId::Priority(70))); + } + fn create_test_acl_config() -> Acl { let mut acl_config = Acl::default(); @@ -1182,6 +1322,8 @@ mod tests { dst_port: Some(80), protocol: Protocol::Tcp, packet_size: 1024, + src_groups: Arc::new(vec![]), + dst_groups: Arc::new(vec![]), } } @@ -1380,6 +1522,8 @@ mod tests { dst_port: Some(53), // DNS protocol: Protocol::Udp, // UDP packet_size: 512, + src_groups: Arc::new(vec![]), + dst_groups: Arc::new(vec![]), }; // Test TCP packet (should hit stateful+rate-limited rule) @@ -1390,6 +1534,8 @@ mod tests { dst_port: Some(80), // HTTP protocol: Protocol::Tcp, // TCP packet_size: 1024, + src_groups: Arc::new(vec![]), + dst_groups: Arc::new(vec![]), }; // Process UDP packets multiple times diff --git a/easytier/src/common/config.rs b/easytier/src/common/config.rs index 07f78945d..60ec6bc30 100644 --- a/easytier/src/common/config.rs +++ b/easytier/src/common/config.rs @@ -11,6 +11,8 @@ use cidr::IpCidr; use serde::{Deserialize, Serialize}; use crate::{ + common::stun::StunInfoCollector, + instance::dns_server::DEFAULT_ET_DNS_ZONE, proto::{ acl::Acl, common::{CompressionAlgoPb, PortForwardConfigPb, SocketType}, @@ -50,6 +52,8 @@ pub fn gen_default_flags() -> Flags { foreign_relay_bps_limit: u64::MAX, multi_thread_count: 2, encryption_algorithm: "aes-gcm".to_string(), + disable_sym_hole_punching: false, + tld_dns_zone: DEFAULT_ET_DNS_ZONE.to_string(), } } @@ -151,6 +155,7 @@ pub trait ConfigLoader: Send + Sync { mapped_cidr: Option, ) -> Result<(), anyhow::Error>; fn remove_proxy_cidr(&self, cidr: cidr::Ipv4Cidr); + fn clear_proxy_cidrs(&self); fn get_proxy_cidrs(&self) -> Vec; fn get_network_identity(&self) -> NetworkIdentity; @@ -167,12 +172,6 @@ pub trait ConfigLoader: Send + Sync { fn get_mapped_listeners(&self) -> Vec; fn set_mapped_listeners(&self, listeners: Option>); - fn get_rpc_portal(&self) -> Option; - fn set_rpc_portal(&self, addr: SocketAddr); - - fn get_rpc_portal_whitelist(&self) -> Option>; - fn set_rpc_portal_whitelist(&self, whitelist: Option>); - fn get_vpn_portal_config(&self) -> Option; fn set_vpn_portal_config(&self, config: VpnPortalConfig); @@ -200,6 +199,12 @@ pub trait ConfigLoader: Send + Sync { fn get_udp_whitelist(&self) -> Vec; fn set_udp_whitelist(&self, whitelist: Vec); + fn get_stun_servers(&self) -> Option>; + fn set_stun_servers(&self, servers: Option>); + + fn get_stun_servers_v6(&self) -> Option>; + fn set_stun_servers_v6(&self, servers: Option>); + fn dump(&self) -> String; } @@ -305,6 +310,8 @@ pub struct FileLoggerConfig { pub level: Option, pub file: Option, pub dir: Option, + pub size_mb: Option, + pub count: Option, } #[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Default)] @@ -371,7 +378,7 @@ impl From for PortForwardConfigPb { } } -#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)] +#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)] struct Config { netns: Option, hostname: Option, @@ -388,9 +395,6 @@ struct Config { peer: Option>, proxy_network: Option>, - rpc_portal: Option, - rpc_portal_whitelist: Option>, - vpn_portal_config: Option, routes: Option>, @@ -408,6 +412,8 @@ struct Config { tcp_whitelist: Option>, udp_whitelist: Option>, + stun_servers: Option>, + stun_servers_v6: Option>, } #[derive(Debug, Clone)] @@ -599,6 +605,11 @@ impl ConfigLoader for TomlConfigLoader { } } + fn clear_proxy_cidrs(&self) { + let mut locked_config = self.config.lock().unwrap(); + locked_config.proxy_network = None; + } + fn get_proxy_cidrs(&self) -> Vec { self.config .lock() @@ -675,22 +686,6 @@ impl ConfigLoader for TomlConfigLoader { self.config.lock().unwrap().mapped_listeners = listeners; } - fn get_rpc_portal(&self) -> Option { - self.config.lock().unwrap().rpc_portal - } - - fn set_rpc_portal(&self, addr: SocketAddr) { - self.config.lock().unwrap().rpc_portal = Some(addr); - } - - fn get_rpc_portal_whitelist(&self) -> Option> { - self.config.lock().unwrap().rpc_portal_whitelist.clone() - } - - fn set_rpc_portal_whitelist(&self, whitelist: Option>) { - self.config.lock().unwrap().rpc_portal_whitelist = whitelist; - } - fn get_vpn_portal_config(&self) -> Option { self.config.lock().unwrap().vpn_portal_config.clone() } @@ -787,6 +782,22 @@ impl ConfigLoader for TomlConfigLoader { self.config.lock().unwrap().udp_whitelist = Some(whitelist); } + fn get_stun_servers(&self) -> Option> { + self.config.lock().unwrap().stun_servers.clone() + } + + fn set_stun_servers(&self, servers: Option>) { + self.config.lock().unwrap().stun_servers = servers; + } + + fn get_stun_servers_v6(&self) -> Option> { + self.config.lock().unwrap().stun_servers_v6.clone() + } + + fn set_stun_servers_v6(&self, servers: Option>) { + self.config.lock().unwrap().stun_servers_v6 = servers; + } + fn dump(&self) -> String { let default_flags_json = serde_json::to_string(&gen_default_flags()).unwrap(); let default_flags_hashmap = @@ -809,6 +820,12 @@ impl ConfigLoader for TomlConfigLoader { let mut config = self.config.lock().unwrap().clone(); config.flags = Some(flag_map); + if config.stun_servers == Some(StunInfoCollector::get_default_servers()) { + config.stun_servers = None; + } + if config.stun_servers_v6 == Some(StunInfoCollector::get_default_servers_v6()) { + config.stun_servers_v6 = None; + } toml::to_string_pretty(&config).unwrap() } } @@ -817,6 +834,39 @@ impl ConfigLoader for TomlConfigLoader { pub mod tests { use super::*; + #[test] + fn test_stun_servers_config() { + let config = TomlConfigLoader::default(); + let stun_servers = config.get_stun_servers(); + assert!(stun_servers.is_none()); + + // Test setting custom stun servers + let custom_servers = vec!["txt:stun.easytier.cn".to_string()]; + config.set_stun_servers(Some(custom_servers.clone())); + + let retrieved_servers = config.get_stun_servers(); + assert_eq!(retrieved_servers.unwrap(), custom_servers); + } + + #[test] + fn test_stun_servers_toml_parsing() { + let config_str = r#" +instance_name = "test" +stun_servers = [ + "stun.l.google.com:19302", + "stun1.l.google.com:19302", + "txt:stun.easytier.cn" +]"#; + + let config = TomlConfigLoader::new_from_str(config_str).unwrap(); + let stun_servers = config.get_stun_servers().unwrap(); + + assert_eq!(stun_servers.len(), 3); + assert_eq!(stun_servers[0], "stun.l.google.com:19302"); + assert_eq!(stun_servers[1], "stun1.l.google.com:19302"); + assert_eq!(stun_servers[2], "txt:stun.easytier.cn"); + } + #[tokio::test] async fn full_example_test() { let config_str = r#" diff --git a/easytier/src/common/global_ctx.rs b/easytier/src/common/global_ctx.rs index 2c5e2cb25..670553b29 100644 --- a/easytier/src/common/global_ctx.rs +++ b/easytier/src/common/global_ctx.rs @@ -1,4 +1,5 @@ use std::collections::hash_map::DefaultHasher; +use std::net::IpAddr; use std::{ hash::Hasher, sync::{Arc, Mutex}, @@ -8,8 +9,11 @@ use crate::common::config::ProxyNetworkConfig; use crate::common::stats_manager::StatsManager; use crate::common::token_bucket::TokenBucketManager; use crate::peers::acl_filter::AclFilter; -use crate::proto::cli::PeerConnInfo; +use crate::proto::acl::GroupIdentity; +use crate::proto::api::config::InstanceConfigPatch; +use crate::proto::api::instance::PeerConnInfo; use crate::proto::common::{PeerFeatureFlag, PortForwardConfigPb}; +use crate::proto::peer_rpc::PeerGroupInfo; use crossbeam::atomic::AtomicCell; use super::{ @@ -41,13 +45,16 @@ pub enum GlobalCtxEvent { Connecting(url::Url), ConnectError(String, String, String), // (dst, ip version, error message) - VpnPortalClientConnected(String, String), // (portal, client ip) + VpnPortalStarted(String), // (portal) + VpnPortalClientConnected(String, String), // (portal, client ip) VpnPortalClientDisconnected(String, String), // (portal, client ip) DhcpIpv4Changed(Option, Option), // (old, new) DhcpIpv4Conflicted(Option), PortForwardAdded(PortForwardConfigPb), + + ConfigPatched(InstanceConfigPatch), } pub type EventBus = tokio::sync::broadcast::Sender; @@ -112,7 +119,21 @@ impl GlobalCtx { let (event_bus, _) = tokio::sync::broadcast::channel(8); - let stun_info_collection = Arc::new(StunInfoCollector::new_with_default_servers()); + let stun_info_collector = StunInfoCollector::new_with_default_servers(); + + if let Some(stun_servers) = config_fs.get_stun_servers() { + stun_info_collector.set_stun_servers(stun_servers); + } else { + stun_info_collector.set_stun_servers(StunInfoCollector::get_default_servers()); + } + + if let Some(stun_servers) = config_fs.get_stun_servers_v6() { + stun_info_collector.set_stun_servers_v6(stun_servers); + } else { + stun_info_collector.set_stun_servers_v6(StunInfoCollector::get_default_servers_v6()); + } + + let stun_info_collector = Arc::new(stun_info_collector); let enable_exit_node = config_fs.get_flags().enable_exit_node || cfg!(target_env = "ohos"); let proxy_forward_by_system = config_fs.get_flags().proxy_forward_by_system; @@ -138,12 +159,12 @@ impl GlobalCtx { ip_collector: Mutex::new(Some(Arc::new(IPCollector::new( net_ns, - stun_info_collection.clone(), + stun_info_collector.clone(), )))), hostname: Mutex::new(hostname), - stun_info_collection: Mutex::new(stun_info_collection), + stun_info_collection: Mutex::new(stun_info_collector), running_listeners: Mutex::new(Vec::new()), @@ -223,6 +244,13 @@ impl GlobalCtx { self.config.get_id() } + pub fn is_ip_in_same_network(&self, ip: &IpAddr) -> bool { + match ip { + IpAddr::V4(v4) => self.get_ipv4().map(|x| x.contains(v4)).unwrap_or(false), + IpAddr::V6(v6) => self.get_ipv6().map(|x| x.contains(v6)).unwrap_or(false), + } + } + pub fn get_network_identity(&self) -> NetworkIdentity { self.config.get_network_identity() } @@ -346,6 +374,7 @@ impl GlobalCtx { } pub fn set_quic_proxy_port(&self, port: Option) { + self.acl_filter.set_quic_udp_port(port.unwrap_or(0)); self.quic_proxy_port.store(port); } @@ -360,6 +389,37 @@ impl GlobalCtx { pub fn get_acl_filter(&self) -> &Arc { &self.acl_filter } + + pub fn get_acl_groups(&self, peer_id: PeerId) -> Vec { + use std::collections::HashSet; + self.config + .get_acl() + .and_then(|acl| acl.acl_v1) + .and_then(|acl_v1| acl_v1.group) + .map_or_else(Vec::new, |group| { + let memberships: HashSet<_> = group.members.iter().collect(); + group + .declares + .iter() + .filter(|g| memberships.contains(&g.group_name)) + .map(|g| { + PeerGroupInfo::generate_with_proof( + g.group_name.clone(), + g.group_secret.clone(), + peer_id, + ) + }) + .collect() + }) + } + + pub fn get_acl_group_declarations(&self) -> Vec { + self.config + .get_acl() + .and_then(|acl| acl.acl_v1) + .and_then(|acl_v1| acl_v1.group) + .map_or_else(Vec::new, |group| group.declares.to_vec()) + } } #[cfg(test)] diff --git a/easytier/src/common/ifcfg/darwin.rs b/easytier/src/common/ifcfg/darwin.rs index 3c751534a..43940056d 100644 --- a/easytier/src/common/ifcfg/darwin.rs +++ b/easytier/src/common/ifcfg/darwin.rs @@ -71,12 +71,7 @@ impl IfConfiguerTrait for MacIfConfiger { run_shell_cmd(format!("ifconfig {} inet delete", name).as_str()).await } else { run_shell_cmd( - format!( - "ifconfig {} inet {} delete", - name, - ip.unwrap().address().to_string() - ) - .as_str(), + format!("ifconfig {} inet {} delete", name, ip.unwrap().address()).as_str(), ) .await } diff --git a/easytier/src/common/ifcfg/win/luid.rs b/easytier/src/common/ifcfg/win/luid.rs index 29af75c9d..bae2316a6 100644 --- a/easytier/src/common/ifcfg/win/luid.rs +++ b/easytier/src/common/ifcfg/win/luid.rs @@ -701,8 +701,12 @@ impl InterfaceLuid { // SAFETY: Both NET_LUID_LH unions should be the same. We're just copying out // the u64 value and re-wrapping it, since wintun doesn't refer to the windows // crate's version of NET_LUID_LH. - self.try_set_mtu(AF_INET as ADDRESS_FAMILY, mtu)?; - self.try_set_mtu(AF_INET6 as ADDRESS_FAMILY, mtu)?; + if let Err(e) = self.try_set_mtu(AF_INET as ADDRESS_FAMILY, mtu) { + tracing::warn!("Failed to set IPv4 MTU: {:?}", e); + } + if let Err(e) = self.try_set_mtu(AF_INET6 as ADDRESS_FAMILY, mtu) { + tracing::warn!("Failed to set IPv6 MTU: {:?}", e); + } Ok(()) } diff --git a/easytier/src/common/ifcfg/win/types.rs b/easytier/src/common/ifcfg/win/types.rs index ee030c408..dae0be942 100644 --- a/easytier/src/common/ifcfg/win/types.rs +++ b/easytier/src/common/ifcfg/win/types.rs @@ -43,10 +43,9 @@ pub fn convert_ipv4addr_to_inaddr(ip: &Ipv4Addr) -> winapi::shared::inaddr::in_a pub fn convert_ipv6addr_to_inaddr(ip: &Ipv6Addr) -> winapi::shared::in6addr::in6_addr { let mut winaddr = winapi::shared::in6addr::in6_addr::default(); let octets = ip.octets(); - for i in 0..octets.len() { - unsafe { winaddr.u.Byte_mut()[i] = octets[i] }; + for (i, &octet) in octets.iter().enumerate() { + unsafe { winaddr.u.Byte_mut()[i] = octet }; } - winaddr } diff --git a/easytier/src/common/ifcfg/windows.rs b/easytier/src/common/ifcfg/windows.rs index 3d5d27d4c..078219e9d 100644 --- a/easytier/src/common/ifcfg/windows.rs +++ b/easytier/src/common/ifcfg/windows.rs @@ -35,7 +35,7 @@ fn format_win_error(error: u32) -> String { null_mut(), error, 0, - buffer.as_mut_ptr() as *mut u16, + buffer.as_mut_ptr(), size, null_mut(), ); @@ -43,9 +43,7 @@ fn format_win_error(error: u32) -> String { let str_end = buffer.iter().position(|&b| b == 0).unwrap_or(buffer.len()); format!( "{} (code: {})", - String::from_utf16_lossy(&buffer[..str_end]) - .trim() - .to_string(), + String::from_utf16_lossy(&buffer[..str_end]).trim(), error ) } diff --git a/easytier/src/common/mod.rs b/easytier/src/common/mod.rs index 7d2572c73..86632069c 100644 --- a/easytier/src/common/mod.rs +++ b/easytier/src/common/mod.rs @@ -26,6 +26,7 @@ pub mod stats_manager; pub mod stun; pub mod stun_codec_ext; pub mod token_bucket; +pub mod tracing_rolling_appender; pub fn get_logger_timer( format: F, diff --git a/easytier/src/common/network.rs b/easytier/src/common/network.rs index 73dd97c66..ac8061d9e 100644 --- a/easytier/src/common/network.rs +++ b/easytier/src/common/network.rs @@ -59,18 +59,52 @@ impl InterfaceFilter { } } +// Cache for networksetup command output +#[cfg(target_os = "macos")] +static NETWORKSETUP_CACHE: std::sync::OnceLock> = + std::sync::OnceLock::new(); + #[cfg(any(target_os = "macos", target_os = "freebsd"))] impl InterfaceFilter { #[cfg(target_os = "macos")] - async fn is_interface_physical(&self) -> bool { - let interface_name = &self.iface.name; - let output = tokio::process::Command::new("networksetup") - .args(&["-listallhardwareports"]) + async fn get_networksetup_output() -> String { + use anyhow::Context; + use std::time::{Duration, Instant}; + let cache = NETWORKSETUP_CACHE.get_or_init(|| Mutex::new((String::new(), Instant::now()))); + let mut cache_guard = cache.lock().await; + + // Check if cache is still valid (less than 1 minute old) + if cache_guard.1.elapsed() < Duration::from_secs(60) && !cache_guard.0.is_empty() { + return cache_guard.0.clone(); + } + + // Cache is expired or empty, fetch new data + let stdout = tokio::process::Command::new("networksetup") + .args(["-listallhardwareports"]) .output() .await - .expect("Failed to execute command"); + .with_context(|| "Failed to execute networksetup command") + .and_then(|output| { + std::str::from_utf8(&output.stdout) + .map(|s| s.to_string()) + .with_context(|| "Failed to convert networksetup output to string") + }) + .unwrap_or_else(|e| { + tracing::error!("Failed to execute networksetup command: {:?}", e); + String::new() + }); - let stdout = std::str::from_utf8(&output.stdout).expect("Invalid UTF-8"); + // Update cache + cache_guard.0 = stdout.clone(); + cache_guard.1 = Instant::now(); + + stdout + } + + #[cfg(target_os = "macos")] + async fn is_interface_physical(&self) -> bool { + let interface_name = &self.iface.name; + let stdout = Self::get_networksetup_output().await; let lines: Vec<&str> = stdout.lines().collect(); @@ -79,11 +113,7 @@ impl InterfaceFilter { if line.contains("Device:") && line.contains(interface_name) { let next_line = lines[i + 1]; - if next_line.contains("Virtual Interface") { - return false; - } else { - return true; - } + return !next_line.contains("Virtual Interface"); } } diff --git a/easytier/src/common/stats_manager.rs b/easytier/src/common/stats_manager.rs index 432901324..39cc6737a 100644 --- a/easytier/src/common/stats_manager.rs +++ b/easytier/src/common/stats_manager.rs @@ -66,6 +66,8 @@ pub enum MetricName { CompressionBytesTxBefore, /// Compression bytes after compression CompressionBytesTxAfter, + + TcpProxyConnect, } impl fmt::Display for MetricName { @@ -112,6 +114,8 @@ impl fmt::Display for MetricName { MetricName::CompressionBytesRxAfter => write!(f, "compression_bytes_rx_after"), MetricName::CompressionBytesTxBefore => write!(f, "compression_bytes_tx_before"), MetricName::CompressionBytesTxAfter => write!(f, "compression_bytes_tx_after"), + + MetricName::TcpProxyConnect => write!(f, "tcp_proxy_connect"), } } } @@ -139,6 +143,10 @@ pub enum LabelType { ErrorType(String), /// Status Status(String), + /// Dst Ip + DstIp(String), + /// Mapped Dst Ip + MappedDstIp(String), } impl fmt::Display for LabelType { @@ -154,6 +162,8 @@ impl fmt::Display for LabelType { LabelType::CompressionAlgo(algo) => write!(f, "compression_algo={}", algo), LabelType::ErrorType(err) => write!(f, "error_type={}", err), LabelType::Status(status) => write!(f, "status={}", status), + LabelType::DstIp(ip) => write!(f, "dst_ip={}", ip), + LabelType::MappedDstIp(ip) => write!(f, "mapped_dst_ip={}", ip), } } } @@ -171,6 +181,8 @@ impl LabelType { LabelType::CompressionAlgo(_) => "compression_algo", LabelType::ErrorType(_) => "error_type", LabelType::Status(_) => "status", + LabelType::DstIp(_) => "dst_ip", + LabelType::MappedDstIp(_) => "mapped_dst_ip", } } @@ -186,6 +198,8 @@ impl LabelType { LabelType::CompressionAlgo(algo) => algo.clone(), LabelType::ErrorType(err) => err.clone(), LabelType::Status(status) => status.clone(), + LabelType::DstIp(ip) => ip.clone(), + LabelType::MappedDstIp(ip) => ip.clone(), } } } @@ -501,7 +515,9 @@ impl StatsManager { loop { interval.tick().await; - let cutoff_time = Instant::now() - Duration::from_secs(180); // 3 minutes + let Some(cutoff_time) = Instant::now().checked_sub(Duration::from_secs(180)) else { + continue; + }; let Some(counters) = counters_clone.upgrade() else { break; @@ -648,7 +664,7 @@ impl Default for StatsManager { mod tests { use super::*; use crate::common::stats_manager::{LabelSet, LabelType, MetricName, StatsManager}; - use crate::proto::cli::{ + use crate::proto::api::instance::{ GetPrometheusStatsRequest, GetPrometheusStatsResponse, GetStatsRequest, GetStatsResponse, }; use std::collections::BTreeMap; @@ -802,16 +818,19 @@ mod tests { #[tokio::test] async fn test_stats_rpc_data_structures() { // Test GetStatsRequest - let request = GetStatsRequest {}; - assert_eq!(request, GetStatsRequest {}); + let request = GetStatsRequest { instance: None }; + assert_eq!(request, GetStatsRequest { instance: None }); // Test GetStatsResponse let response = GetStatsResponse { metrics: vec![] }; assert!(response.metrics.is_empty()); // Test GetPrometheusStatsRequest - let prometheus_request = GetPrometheusStatsRequest {}; - assert_eq!(prometheus_request, GetPrometheusStatsRequest {}); + let prometheus_request = GetPrometheusStatsRequest { instance: None }; + assert_eq!( + prometheus_request, + GetPrometheusStatsRequest { instance: None } + ); // Test GetPrometheusStatsResponse let prometheus_response = GetPrometheusStatsResponse { @@ -851,7 +870,7 @@ mod tests { } // This simulates what the RPC service would do - let _metric_snapshot = crate::proto::cli::MetricSnapshot { + let _metric_snapshot = crate::proto::api::instance::MetricSnapshot { name: metric.name.to_string(), value: metric.value, labels, diff --git a/easytier/src/common/stun.rs b/easytier/src/common/stun.rs index e9763b546..f586ed4f6 100644 --- a/easytier/src/common/stun.rs +++ b/easytier/src/common/stun.rs @@ -718,10 +718,10 @@ impl StunInfoCollectorTrait for StunInfoCollector { } impl StunInfoCollector { - pub fn new(stun_servers: Vec) -> Self { + pub fn new(stun_servers: Vec, stun_servers_v6: Vec) -> Self { Self { stun_servers: Arc::new(RwLock::new(stun_servers)), - stun_servers_v6: Arc::new(RwLock::new(Self::get_default_servers_v6())), + stun_servers_v6: Arc::new(RwLock::new(stun_servers_v6)), udp_nat_test_result: Arc::new(RwLock::new(None)), public_ipv6: Arc::new(AtomicCell::new(None)), nat_test_result_time: Arc::new(AtomicCell::new(Local::now())), @@ -732,12 +732,22 @@ impl StunInfoCollector { } pub fn new_with_default_servers() -> Self { - Self::new(Self::get_default_servers()) + Self::new(Self::get_default_servers(), Self::get_default_servers_v6()) + } + + pub fn set_stun_servers(&self, stun_servers: Vec) { + let mut g = self.stun_servers.write().unwrap(); + *g = stun_servers; + } + + pub fn set_stun_servers_v6(&self, stun_servers_v6: Vec) { + let mut g = self.stun_servers_v6.write().unwrap(); + *g = stun_servers_v6; } pub fn get_default_servers() -> Vec { - // NOTICE: we may need to choose stun stun server based on geo location - // stun server cross nation may return a external ip address with high latency and loss rate + // NOTICE: we may need to choose stun server based on geolocation + // stun server cross nation may return an external ip address with high latency and loss rate [ "txt:stun.easytier.cn", "stun.miwifi.com", diff --git a/easytier/src/common/tracing_rolling_appender/LICENSE b/easytier/src/common/tracing_rolling_appender/LICENSE new file mode 100644 index 000000000..3872c786d --- /dev/null +++ b/easytier/src/common/tracing_rolling_appender/LICENSE @@ -0,0 +1,242 @@ +This module is copied and modified from https://github.com/cavivie/tracing-rolling-file + +tracing-rolling-file is dual-licensed under The MIT License [1] and +Apache 2.0 License [2]. + +Copyright (c) 2021 Cavivie and contributors. + +This is same as the Rust Project's own license. + + +[1]: , which is reproduced below: + +~~~~ +The MIT License (MIT) + +Copyright (c) 2021, eFolder Inc dba Axcient. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +~~~~ + + +[2]: , which is reproduced below: + +~~~~ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +~~~~ \ No newline at end of file diff --git a/easytier/src/common/tracing_rolling_appender/base.rs b/easytier/src/common/tracing_rolling_appender/base.rs new file mode 100644 index 000000000..99af38dc0 --- /dev/null +++ b/easytier/src/common/tracing_rolling_appender/base.rs @@ -0,0 +1,517 @@ +use super::*; + +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub struct RollingConditionBase { + last_write_opt: Option>, + frequency_opt: Option, + max_size_opt: Option, +} + +impl RollingConditionBase { + /// Constructs a new struct that does not yet have any condition set. + pub fn new() -> RollingConditionBase { + RollingConditionBase { + last_write_opt: None, + frequency_opt: None, + max_size_opt: None, + } + } + + /// Sets a condition to rollover on the given frequency + pub fn frequency(mut self, x: RollingFrequency) -> RollingConditionBase { + self.frequency_opt = Some(x); + self + } + + /// Sets a condition to rollover when the date changes + pub fn daily(mut self) -> RollingConditionBase { + self.frequency_opt = Some(RollingFrequency::EveryDay); + self + } + + /// Sets a condition to rollover when the date or hour changes + pub fn hourly(mut self) -> RollingConditionBase { + self.frequency_opt = Some(RollingFrequency::EveryHour); + self + } + + /// Sets a condition to rollover when the date or minute changes + pub fn minutely(mut self) -> RollingConditionBase { + self.frequency_opt = Some(RollingFrequency::EveryMinute); + self + } + + /// Sets a condition to rollover when a certain size is reached + pub fn max_size(mut self, x: u64) -> RollingConditionBase { + self.max_size_opt = Some(x); + self + } +} + +impl Default for RollingConditionBase { + fn default() -> Self { + RollingConditionBase::new().frequency(RollingFrequency::EveryDay) + } +} + +impl RollingCondition for RollingConditionBase { + fn should_rollover(&mut self, now: &DateTime, current_filesize: u64) -> bool { + let mut rollover = false; + if let Some(frequency) = self.frequency_opt.as_ref() { + if let Some(last_write) = self.last_write_opt.as_ref() { + if frequency.equivalent_datetime(now) != frequency.equivalent_datetime(last_write) { + rollover = true; + } + } + } + if let Some(max_size) = self.max_size_opt.as_ref() { + if current_filesize >= *max_size { + rollover = true; + } + } + self.last_write_opt = Some(*now); + rollover + } +} + +pub struct RollingFileAppenderBaseBuilder { + condition: RollingConditionBase, + filename: String, + max_filecount: usize, + current_filesize: u64, + writer_opt: Option>, +} + +impl Default for RollingFileAppenderBaseBuilder { + fn default() -> Self { + RollingFileAppenderBaseBuilder { + condition: RollingConditionBase::default(), + filename: String::new(), + max_filecount: 10, + current_filesize: 0, + writer_opt: None, + } + } +} + +impl RollingFileAppenderBaseBuilder { + /// Sets the log filename. Uses absolute path if provided, otherwise + /// creates files in the current working directory. + pub fn filename(mut self, filename: String) -> Self { + self.filename = filename; + self + } + + /// Sets a condition for the maximum number of files to create before rolling + /// over and deleting the oldest one. + pub fn max_filecount(mut self, max_filecount: usize) -> Self { + self.max_filecount = max_filecount; + self + } + + /// Sets a condition to rollover on a daily basis + pub fn condition_daily(mut self) -> Self { + self.condition.frequency_opt = Some(RollingFrequency::EveryDay); + self + } + + /// Sets a condition to rollover when the date or hour changes + pub fn condition_hourly(mut self) -> Self { + self.condition.frequency_opt = Some(RollingFrequency::EveryHour); + self + } + + /// Sets a condition to rollover when the date or minute changes + pub fn condition_minutely(mut self) -> Self { + self.condition.frequency_opt = Some(RollingFrequency::EveryMinute); + self + } + + /// Sets a condition to rollover when a certain size is reached + pub fn condition_max_file_size(mut self, x: u64) -> Self { + self.condition.max_size_opt = Some(x); + self + } + + /// Builds a RollingFileAppenderBase instance from the current settings. + /// + /// Returns an error if the filename is empty. + pub fn build(self) -> Result { + if self.filename.is_empty() { + return Err("A filename is required to be set and can not be blank"); + } + Ok(RollingFileAppenderBase { + condition: self.condition, + filename: self.filename, + max_filecount: self.max_filecount, + current_filesize: self.current_filesize, + writer_opt: self.writer_opt, + }) + } +} + +impl RollingFileAppenderBase { + /// Creates a new rolling file appender builder instance with the default + /// settings without a filename set. + pub fn builder() -> RollingFileAppenderBaseBuilder { + RollingFileAppenderBaseBuilder::default() + } +} + +/// A rolling file appender with a rolling condition based on date/time or size. +pub type RollingFileAppenderBase = RollingFileAppender; + +// LCOV_EXCL_START +#[cfg(test)] +mod test { + use super::*; + + struct Context { + _tempdir: tempfile::TempDir, + rolling: RollingFileAppenderBase, + } + + impl Context { + fn verify_contains(&mut self, needle: &str, n: usize) { + self.rolling.flush().unwrap(); + let p = self.rolling.filename_for(n); + let haystack = fs::read_to_string(&p).unwrap(); + if !haystack.contains(needle) { + panic!("file {:?} did not contain expected contents {}", p, needle); + } + } + } + + fn build_context(condition: RollingConditionBase, max_files: usize) -> Context { + let tempdir = tempfile::tempdir().unwrap(); + let filename = tempdir.path().join("test.log"); + let rolling = RollingFileAppenderBase::new(filename, condition, max_files).unwrap(); + Context { + _tempdir: tempdir, + rolling, + } + } + + fn build_builder_context(mut builder: RollingFileAppenderBaseBuilder) -> Context { + if builder.filename.is_empty() { + builder = builder.filename(String::from("test.log")); + } + let tempdir = tempfile::tempdir().unwrap(); + let filename = tempdir.path().join(&builder.filename); + builder = builder.filename(String::from(filename.as_os_str().to_str().unwrap())); + Context { + _tempdir: tempdir, + rolling: builder.build().unwrap(), + } + } + + #[test] + fn frequency_every_day() { + let mut c = build_context(RollingConditionBase::new().daily(), 9); + c.rolling + .write_with_datetime( + b"Line 1\n", + &Local.with_ymd_and_hms(2021, 3, 30, 1, 2, 3).unwrap(), + ) + .unwrap(); + c.rolling + .write_with_datetime( + b"Line 2\n", + &Local.with_ymd_and_hms(2021, 3, 30, 1, 3, 0).unwrap(), + ) + .unwrap(); + c.rolling + .write_with_datetime( + b"Line 3\n", + &Local.with_ymd_and_hms(2021, 3, 31, 1, 4, 0).unwrap(), + ) + .unwrap(); + c.rolling + .write_with_datetime( + b"Line 4\n", + &Local.with_ymd_and_hms(2021, 5, 31, 1, 4, 0).unwrap(), + ) + .unwrap(); + c.rolling + .write_with_datetime( + b"Line 5\n", + &Local.with_ymd_and_hms(2022, 5, 31, 1, 4, 0).unwrap(), + ) + .unwrap(); + assert!(!AsRef::::as_ref(&c.rolling.filename_for(4)).exists()); + c.verify_contains("Line 1", 3); + c.verify_contains("Line 2", 3); + c.verify_contains("Line 3", 2); + c.verify_contains("Line 4", 1); + c.verify_contains("Line 5", 0); + } + + #[test] + fn frequency_every_day_limited_files() { + let mut c = build_context(RollingConditionBase::new().daily(), 2); + c.rolling + .write_with_datetime( + b"Line 1\n", + &Local.with_ymd_and_hms(2021, 3, 30, 1, 2, 3).unwrap(), + ) + .unwrap(); + c.rolling + .write_with_datetime( + b"Line 2\n", + &Local.with_ymd_and_hms(2021, 3, 30, 1, 3, 0).unwrap(), + ) + .unwrap(); + c.rolling + .write_with_datetime( + b"Line 3\n", + &Local.with_ymd_and_hms(2021, 3, 31, 1, 4, 0).unwrap(), + ) + .unwrap(); + c.rolling + .write_with_datetime( + b"Line 4\n", + &Local.with_ymd_and_hms(2021, 5, 31, 1, 4, 0).unwrap(), + ) + .unwrap(); + c.rolling + .write_with_datetime( + b"Line 5\n", + &Local.with_ymd_and_hms(2022, 5, 31, 1, 4, 0).unwrap(), + ) + .unwrap(); + assert!(!AsRef::::as_ref(&c.rolling.filename_for(4)).exists()); + assert!(!AsRef::::as_ref(&c.rolling.filename_for(3)).exists()); + c.verify_contains("Line 3", 2); + c.verify_contains("Line 4", 1); + c.verify_contains("Line 5", 0); + } + + #[test] + fn frequency_every_hour() { + let mut c = build_context(RollingConditionBase::new().hourly(), 9); + c.rolling + .write_with_datetime( + b"Line 1\n", + &Local.with_ymd_and_hms(2021, 3, 30, 1, 2, 3).unwrap(), + ) + .unwrap(); + c.rolling + .write_with_datetime( + b"Line 2\n", + &Local.with_ymd_and_hms(2021, 3, 30, 1, 3, 2).unwrap(), + ) + .unwrap(); + c.rolling + .write_with_datetime( + b"Line 3\n", + &Local.with_ymd_and_hms(2021, 3, 30, 2, 1, 0).unwrap(), + ) + .unwrap(); + c.rolling + .write_with_datetime( + b"Line 4\n", + &Local.with_ymd_and_hms(2021, 3, 31, 2, 1, 0).unwrap(), + ) + .unwrap(); + assert!(!AsRef::::as_ref(&c.rolling.filename_for(3)).exists()); + c.verify_contains("Line 1", 2); + c.verify_contains("Line 2", 2); + c.verify_contains("Line 3", 1); + c.verify_contains("Line 4", 0); + } + + #[test] + fn frequency_every_minute() { + let mut c = build_context( + RollingConditionBase::new().frequency(RollingFrequency::EveryMinute), + 9, + ); + c.rolling + .write_with_datetime( + b"Line 1\n", + &Local.with_ymd_and_hms(2021, 3, 30, 1, 2, 3).unwrap(), + ) + .unwrap(); + c.rolling + .write_with_datetime( + b"Line 2\n", + &Local.with_ymd_and_hms(2021, 3, 30, 1, 2, 3).unwrap(), + ) + .unwrap(); + c.rolling + .write_with_datetime( + b"Line 3\n", + &Local.with_ymd_and_hms(2021, 3, 30, 1, 2, 4).unwrap(), + ) + .unwrap(); + c.rolling + .write_with_datetime( + b"Line 4\n", + &Local.with_ymd_and_hms(2021, 3, 30, 1, 3, 0).unwrap(), + ) + .unwrap(); + c.rolling + .write_with_datetime( + b"Line 5\n", + &Local.with_ymd_and_hms(2021, 3, 30, 2, 3, 0).unwrap(), + ) + .unwrap(); + c.rolling + .write_with_datetime( + b"Line 6\n", + &Local.with_ymd_and_hms(2022, 3, 30, 2, 3, 0).unwrap(), + ) + .unwrap(); + assert!(!AsRef::::as_ref(&c.rolling.filename_for(4)).exists()); + c.verify_contains("Line 1", 3); + c.verify_contains("Line 2", 3); + c.verify_contains("Line 3", 3); + c.verify_contains("Line 4", 2); + c.verify_contains("Line 5", 1); + c.verify_contains("Line 6", 0); + } + + #[test] + fn max_size() { + let mut c = build_context(RollingConditionBase::new().max_size(10), 9); + c.rolling + .write_with_datetime( + b"12345", + &Local.with_ymd_and_hms(2021, 3, 30, 1, 2, 3).unwrap(), + ) + .unwrap(); + c.rolling + .write_with_datetime( + b"6789", + &Local.with_ymd_and_hms(2021, 3, 30, 1, 3, 3).unwrap(), + ) + .unwrap(); + c.rolling + .write_with_datetime(b"0", &Local.with_ymd_and_hms(2021, 3, 30, 2, 3, 3).unwrap()) + .unwrap(); + c.rolling + .write_with_datetime( + b"abcdefghijkl", + &Local.with_ymd_and_hms(2021, 3, 31, 2, 3, 3).unwrap(), + ) + .unwrap(); + c.rolling + .write_with_datetime( + b"ZZZ", + &Local.with_ymd_and_hms(2022, 3, 31, 1, 2, 3).unwrap(), + ) + .unwrap(); + assert!(!AsRef::::as_ref(&c.rolling.filename_for(3)).exists()); + c.verify_contains("1234567890", 2); + c.verify_contains("abcdefghijkl", 1); + c.verify_contains("ZZZ", 0); + } + + #[test] + fn max_size_existing() { + let mut c = build_context(RollingConditionBase::new().max_size(10), 9); + c.rolling + .write_with_datetime( + b"12345", + &Local.with_ymd_and_hms(2021, 3, 30, 1, 2, 3).unwrap(), + ) + .unwrap(); + // close the file and make sure that it can re-open it, and that it + // resets the file size properly. + c.rolling.writer_opt.take(); + c.rolling.current_filesize = 0; + c.rolling + .write_with_datetime( + b"6789", + &Local.with_ymd_and_hms(2021, 3, 30, 1, 3, 3).unwrap(), + ) + .unwrap(); + c.rolling + .write_with_datetime(b"0", &Local.with_ymd_and_hms(2021, 3, 30, 2, 3, 3).unwrap()) + .unwrap(); + c.rolling + .write_with_datetime( + b"abcdefghijkl", + &Local.with_ymd_and_hms(2021, 3, 31, 2, 3, 3).unwrap(), + ) + .unwrap(); + c.rolling + .write_with_datetime( + b"ZZZ", + &Local.with_ymd_and_hms(2022, 3, 31, 1, 2, 3).unwrap(), + ) + .unwrap(); + assert!(!AsRef::::as_ref(&c.rolling.filename_for(3)).exists()); + c.verify_contains("1234567890", 2); + c.verify_contains("abcdefghijkl", 1); + c.verify_contains("ZZZ", 0); + } + + #[test] + fn daily_and_max_size() { + let mut c = build_context(RollingConditionBase::new().daily().max_size(10), 9); + c.rolling + .write_with_datetime( + b"12345", + &Local.with_ymd_and_hms(2021, 3, 30, 1, 2, 3).unwrap(), + ) + .unwrap(); + c.rolling + .write_with_datetime( + b"6789", + &Local.with_ymd_and_hms(2021, 3, 30, 2, 3, 3).unwrap(), + ) + .unwrap(); + c.rolling + .write_with_datetime(b"0", &Local.with_ymd_and_hms(2021, 3, 31, 2, 3, 3).unwrap()) + .unwrap(); + c.rolling + .write_with_datetime( + b"abcdefghijkl", + &Local.with_ymd_and_hms(2021, 3, 31, 3, 3, 3).unwrap(), + ) + .unwrap(); + c.rolling + .write_with_datetime( + b"ZZZ", + &Local.with_ymd_and_hms(2021, 3, 31, 4, 4, 4).unwrap(), + ) + .unwrap(); + assert!(!AsRef::::as_ref(&c.rolling.filename_for(3)).exists()); + c.verify_contains("123456789", 2); + c.verify_contains("0abcdefghijkl", 1); + c.verify_contains("ZZZ", 0); + } + + #[test] + fn rolling_file_appender_builder() { + let builder = RollingFileAppender::builder(); + + let builder = builder.condition_daily().condition_max_file_size(10); + let mut c = build_builder_context(builder); + c.rolling + .write_with_datetime( + b"abcdefghijklmnop", + &Local.with_ymd_and_hms(2021, 3, 31, 4, 4, 4).unwrap(), + ) + .unwrap(); + c.rolling + .write_with_datetime( + b"12345678", + &Local.with_ymd_and_hms(2021, 3, 31, 5, 4, 4).unwrap(), + ) + .unwrap(); + assert!(AsRef::::as_ref(&c.rolling.filename_for(1)).exists()); + assert!(Path::new(&c.rolling.filename_for(0)).exists()); + c.verify_contains("abcdefghijklmnop", 1); + c.verify_contains("12345678", 0); + } + + #[test] + fn rolling_file_appender_builder_no_filename() { + let builder = RollingFileAppender::builder(); + let appender = builder.condition_daily().build(); + assert!(appender.is_err()); + } +} +// LCOV_EXCL_STOP diff --git a/easytier/src/common/tracing_rolling_appender/mod.rs b/easytier/src/common/tracing_rolling_appender/mod.rs new file mode 100644 index 000000000..bc47c3a56 --- /dev/null +++ b/easytier/src/common/tracing_rolling_appender/mod.rs @@ -0,0 +1,224 @@ +#![deny(warnings)] + +use chrono::prelude::*; +use std::{ + convert::TryFrom, + fs::{self, File, OpenOptions}, + io::{self, BufWriter, Write}, + path::Path, +}; + +/// Determines when a file should be "rolled over". +pub trait RollingCondition { + /// Determine and return whether or not the file should be rolled over. + fn should_rollover(&mut self, now: &DateTime, current_filesize: u64) -> bool; +} + +/// Determines how often a file should be rolled over +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum RollingFrequency { + EveryDay, + EveryHour, + EveryMinute, +} + +impl RollingFrequency { + /// Calculates a datetime that will be different if data should be in + /// different files. + pub fn equivalent_datetime(&self, dt: &DateTime) -> DateTime { + let (year, month, day) = (dt.year(), dt.month(), dt.day()); + let (hour, min, sec) = match self { + RollingFrequency::EveryDay => (0, 0, 0), + RollingFrequency::EveryHour => (dt.hour(), 0, 0), + RollingFrequency::EveryMinute => (dt.hour(), dt.minute(), 0), + }; + Local + .with_ymd_and_hms(year, month, day, hour, min, sec) + .unwrap() + } +} + +/// Writes data to a file, and "rolls over" to preserve older data in +/// a separate set of files. Old files have a Debian-style naming scheme +/// where we have base_filename, base_filename.1, ..., base_filename.N +/// where N is the maximum number of rollover files to keep. +#[derive(Debug)] +pub struct RollingFileAppender +where + RC: RollingCondition, +{ + condition: RC, + filename: String, + max_filecount: usize, + current_filesize: u64, + writer_opt: Option>, +} + +impl RollingFileAppender +where + RC: RollingCondition, +{ + /// Creates a new rolling file appender with the given condition. + /// The filename parent path must already exist. + pub fn new( + filename: impl AsRef, + condition: RC, + max_filecount: usize, + ) -> io::Result> { + let filename = filename.as_ref().to_str().unwrap().to_string(); + let mut appender = RollingFileAppender { + condition, + filename, + max_filecount, + current_filesize: 0, + writer_opt: None, + }; + // Fail if we can't open the file initially... + appender.open_writer_if_needed()?; + Ok(appender) + } + + /// Determines the final filename, where n==0 indicates the current file + fn filename_for(&self, n: usize) -> String { + let f = self.filename.clone(); + if n > 0 { + format!("{}.{}", f, n) + } else { + f + } + } + + /// Rotates old files to make room for a new one. + /// This may result in the deletion of the oldest file + fn rotate_files(&mut self) -> io::Result<()> { + // ignore any failure removing the oldest file (may not exist) + let _ = fs::remove_file(self.filename_for(self.max_filecount.max(1))); + let mut r = Ok(()); + for i in (0..self.max_filecount.max(1)).rev() { + let rotate_from = self.filename_for(i); + let rotate_to = self.filename_for(i + 1); + if let Err(e) = fs::rename(&rotate_from, &rotate_to).or_else(|e| match e.kind() { + io::ErrorKind::NotFound => Ok(()), + _ => Err(e), + }) { + // capture the error, but continue the loop, + // to maximize ability to rename everything + r = Err(e); + } + } + r + } + + /// Forces a rollover to happen immediately. + pub fn rollover(&mut self) -> io::Result<()> { + // Before closing, make sure all data is flushed successfully. + self.flush()?; + // We must close the current file before rotating files + self.writer_opt.take(); + self.current_filesize = 0; + self.rotate_files()?; + self.open_writer_if_needed() + } + + /// Opens a writer for the current file. + fn open_writer_if_needed(&mut self) -> io::Result<()> { + if self.writer_opt.is_none() { + let path = self.filename_for(0); + let path = Path::new(&path); + let mut open_options = OpenOptions::new(); + open_options.append(true).create(true); + let new_file = match open_options.open(path) { + Ok(new_file) => new_file, + Err(err) => { + let Some(parent) = path.parent() else { + return Err(err); + }; + fs::create_dir_all(parent)?; + open_options.open(path)? + } + }; + self.writer_opt = Some(BufWriter::new(new_file)); + self.current_filesize = path.metadata().map_or(0, |m| m.len()); + } + Ok(()) + } + + /// Writes data using the given datetime to calculate the rolling condition + pub fn write_with_datetime(&mut self, buf: &[u8], now: &DateTime) -> io::Result { + if self.condition.should_rollover(now, self.current_filesize) { + if let Err(e) = self.rollover() { + // If we can't rollover, just try to continue writing anyway + // (better than missing data). + // This will likely used to implement logging, so + // avoid using log::warn and log to stderr directly + eprintln!("WARNING: Failed to rotate logfile {}: {}", self.filename, e); + } + } + self.open_writer_if_needed()?; + if let Some(writer) = self.writer_opt.as_mut() { + let buf_len = buf.len(); + writer.write_all(buf).map(|_| { + self.current_filesize += u64::try_from(buf_len).unwrap_or(u64::MAX); + buf_len + }) + } else { + Err(io::Error::other("unexpected condition: writer is missing")) + } + } +} + +impl io::Write for RollingFileAppender +where + RC: RollingCondition, +{ + fn write(&mut self, buf: &[u8]) -> io::Result { + let now = Local::now(); + self.write_with_datetime(buf, &now) + } + + fn flush(&mut self) -> io::Result<()> { + if let Some(writer) = self.writer_opt.as_mut() { + writer.flush()?; + } + Ok(()) + } +} + +pub struct FileAppenderWrapper { + appender: std::sync::Arc>, +} + +impl tracing_subscriber::fmt::MakeWriter<'_> for FileAppenderWrapper { + type Writer = FileAppenderWriter; + + fn make_writer(&self) -> Self::Writer { + FileAppenderWriter { + appender: self.appender.clone(), + } + } +} + +impl FileAppenderWrapper { + pub fn new(appender: RollingFileAppenderBase) -> Self { + Self { + appender: std::sync::Arc::new(parking_lot::Mutex::new(appender)), + } + } +} + +pub struct FileAppenderWriter { + appender: std::sync::Arc>, +} + +impl std::io::Write for FileAppenderWriter { + fn write(&mut self, buf: &[u8]) -> std::io::Result { + self.appender.lock().write(buf) + } + + fn flush(&mut self) -> std::io::Result<()> { + self.appender.lock().flush() + } +} + +pub mod base; +pub use base::*; diff --git a/easytier/src/connector/direct.rs b/easytier/src/connector/direct.rs index 2a6ffb578..d91e4d57f 100644 --- a/easytier/src/connector/direct.rs +++ b/easytier/src/connector/direct.rs @@ -35,7 +35,7 @@ use crate::{ use_global_var, }; -use crate::proto::cli::PeerConnInfo; +use crate::proto::api::instance::PeerConnInfo; use anyhow::Context; use rand::Rng; use tokio::{net::UdpSocket, task::JoinSet, time::timeout}; diff --git a/easytier/src/connector/manual.rs b/easytier/src/connector/manual.rs index 81bb7b985..6d2696d74 100644 --- a/easytier/src/connector/manual.rs +++ b/easytier/src/connector/manual.rs @@ -18,12 +18,14 @@ use crate::{ common::{dns::socket_addrs, join_joinset_background, PeerId}, peers::peer_conn::PeerConnId, proto::{ - cli::{ - ConnectorManageAction, ListConnectorResponse, ManageConnectorResponse, PeerConnInfo, + api::instance::{ + Connector, ConnectorManageRpc, ConnectorStatus, ListConnectorRequest, + ListConnectorResponse, PeerConnInfo, }, rpc_types::{self, controller::BaseController}, }, tunnel::{IpVersion, TunnelConnector}, + utils::weak_upgrade, }; use crate::{ @@ -33,10 +35,6 @@ use crate::{ netns::NetNS, }, peers::peer_manager::PeerManager, - proto::cli::{ - Connector, ConnectorManageRpc, ConnectorStatus, ListConnectorRequest, - ManageConnectorRequest, - }, use_global_var, }; @@ -126,6 +124,14 @@ impl ManualConnectorManager { Ok(()) } + pub async fn clear_connectors(&self) { + self.list_connectors().await.iter().for_each(|x| { + if let Some(url) = &x.url { + self.data.removed_conn_urls.insert(url.to_string()); + } + }); + } + pub async fn list_connectors(&self) -> Vec { let conn_urls: BTreeSet = self .data @@ -421,7 +427,7 @@ impl ManualConnectorManager { } #[derive(Clone)] -pub struct ConnectorManagerRpcService(pub Arc); +pub struct ConnectorManagerRpcService(pub Weak); #[async_trait::async_trait] impl ConnectorManageRpc for ConnectorManagerRpcService { @@ -433,31 +439,10 @@ impl ConnectorManageRpc for ConnectorManagerRpcService { _request: ListConnectorRequest, ) -> Result { let mut ret = ListConnectorResponse::default(); - let connectors = self.0.list_connectors().await; + let connectors = weak_upgrade(&self.0)?.list_connectors().await; ret.connectors = connectors; Ok(ret) } - - async fn manage_connector( - &self, - _: BaseController, - req: ManageConnectorRequest, - ) -> Result { - let url: url::Url = req.url.ok_or(anyhow::anyhow!("url is empty"))?.into(); - if req.action == ConnectorManageAction::Remove as i32 { - self.0 - .remove_connector(url.clone()) - .await - .with_context(|| format!("remove connector failed: {:?}", url))?; - return Ok(ManageConnectorResponse::default()); - } else { - self.0 - .add_connector_by_url(url.as_str()) - .await - .with_context(|| format!("add connector failed: {:?}", url))?; - } - Ok(ManageConnectorResponse::default()) - } } #[cfg(test)] diff --git a/easytier/src/connector/udp_hole_punch/common.rs b/easytier/src/connector/udp_hole_punch/common.rs index 975a9fee3..c4696413f 100644 --- a/easytier/src/connector/udp_hole_punch/common.rs +++ b/easytier/src/connector/udp_hole_punch/common.rs @@ -111,7 +111,24 @@ impl UdpNatType { } } - pub(crate) fn get_punch_hole_method(&self, other: Self) -> UdpPunchClientMethod { + pub(crate) fn get_punch_hole_method( + &self, + other: Self, + global_ctx: ArcGlobalCtx, + ) -> UdpPunchClientMethod { + // Check if symmetric NAT hole punching is disabled + let disable_sym_hole_punching = global_ctx.get_flags().disable_sym_hole_punching; + + // If symmetric NAT hole punching is disabled, treat symmetric as cone + if disable_sym_hole_punching && self.is_sym() { + // Convert symmetric to cone type for hole punching logic + if other.is_sym() { + return UdpPunchClientMethod::None; + } else { + return UdpPunchClientMethod::ConeToCone; + } + } + if other.is_unknown() { if self.is_sym() { return UdpPunchClientMethod::SymToCone; @@ -163,8 +180,9 @@ impl UdpNatType { other: Self, my_peer_id: PeerId, dst_peer_id: PeerId, + global_ctx: ArcGlobalCtx, ) -> bool { - match self.get_punch_hole_method(other) { + match self.get_punch_hole_method(other, global_ctx) { UdpPunchClientMethod::None => false, UdpPunchClientMethod::ConeToCone | UdpPunchClientMethod::SymToCone => true, UdpPunchClientMethod::EasySymToEasySym => my_peer_id < dst_peer_id, diff --git a/easytier/src/connector/udp_hole_punch/mod.rs b/easytier/src/connector/udp_hole_punch/mod.rs index d9fc36cf5..6d3ca9f03 100644 --- a/easytier/src/connector/udp_hole_punch/mod.rs +++ b/easytier/src/connector/udp_hole_punch/mod.rs @@ -466,7 +466,9 @@ impl PeerTaskLauncher for UdpHolePunchPeerTaskLauncher { continue; } - if !my_nat_type.can_punch_hole_as_client(peer_nat_type, my_peer_id, peer_id) { + let global_ctx = data.peer_mgr.get_global_ctx(); + if !my_nat_type.can_punch_hole_as_client(peer_nat_type, my_peer_id, peer_id, global_ctx) + { continue; } @@ -493,7 +495,10 @@ impl PeerTaskLauncher for UdpHolePunchPeerTaskLauncher { item: Self::CollectPeerItem, ) -> JoinHandle> { let data = data.clone(); - let punch_method = item.my_nat_type.get_punch_hole_method(item.dst_nat_type); + let global_ctx = data.peer_mgr.get_global_ctx(); + let punch_method = item + .my_nat_type + .get_punch_hole_method(item.dst_nat_type, global_ctx); match punch_method { UdpPunchClientMethod::ConeToCone => tokio::spawn(data.cone_to_cone(item)), UdpPunchClientMethod::SymToCone => tokio::spawn(data.sym_to_cone(item)), diff --git a/easytier/src/easytier-cli.rs b/easytier/src/easytier-cli.rs index 273e2f350..3379f9693 100644 --- a/easytier/src/easytier-cli.rs +++ b/easytier/src/easytier-cli.rs @@ -25,29 +25,40 @@ use crate::{ constants::EASYTIER_VERSION, stun::{StunInfoCollector, StunInfoCollectorTrait}, }, + peers, proto::{ - cli::{ - list_peer_route_pair, AclManageRpc, AclManageRpcClientFactory, AddPortForwardRequest, - ConnectorManageRpc, ConnectorManageRpcClientFactory, DumpRouteRequest, - GetAclStatsRequest, GetPrometheusStatsRequest, GetStatsRequest, - GetVpnPortalInfoRequest, GetWhitelistRequest, ListConnectorRequest, - ListForeignNetworkRequest, ListGlobalForeignNetworkRequest, ListMappedListenerRequest, - ListPeerRequest, ListPeerResponse, ListPortForwardRequest, ListRouteRequest, - ListRouteResponse, ManageMappedListenerRequest, MappedListenerManageAction, - MappedListenerManageRpc, MappedListenerManageRpcClientFactory, NodeInfo, PeerManageRpc, - PeerManageRpcClientFactory, PortForwardManageRpc, PortForwardManageRpcClientFactory, - RemovePortForwardRequest, SetWhitelistRequest, ShowNodeInfoRequest, StatsRpc, - StatsRpcClientFactory, TcpProxyEntryState, TcpProxyEntryTransportType, TcpProxyRpc, - TcpProxyRpcClientFactory, VpnPortalRpc, VpnPortalRpcClientFactory, - ManageMappedListenerRequest, MappedListenerManageRpc, MappedListenerManageRpcClientFactory, ListMappedListenerRequest, MappedListenerManageAction + api::{ + config::{ + AclPatch, ConfigPatchAction, ConfigRpc, ConfigRpcClientFactory, + InstanceConfigPatch, PatchConfigRequest, PortForwardPatch, StringPatch, UrlPatch, + }, + instance::{ + instance_identifier::{InstanceSelector, Selector}, + list_peer_route_pair, AclManageRpc, AclManageRpcClientFactory, ConnectorManageRpc, + ConnectorManageRpcClientFactory, DumpRouteRequest, GetAclStatsRequest, + GetPrometheusStatsRequest, GetStatsRequest, GetVpnPortalInfoRequest, + GetWhitelistRequest, InstanceIdentifier, ListConnectorRequest, + ListForeignNetworkRequest, ListGlobalForeignNetworkRequest, + ListMappedListenerRequest, ListPeerRequest, ListPeerResponse, + ListPortForwardRequest, ListRouteRequest, ListRouteResponse, + MappedListenerManageRpc, MappedListenerManageRpcClientFactory, NodeInfo, + PeerManageRpc, PeerManageRpcClientFactory, PortForwardManageRpc, + PortForwardManageRpcClientFactory, ShowNodeInfoRequest, StatsRpc, + StatsRpcClientFactory, TcpProxyEntryState, TcpProxyEntryTransportType, TcpProxyRpc, + TcpProxyRpcClientFactory, VpnPortalRpc, VpnPortalRpcClientFactory, + }, + logger::{ + GetLoggerConfigRequest, LogLevel, LoggerRpc, LoggerRpcClientFactory, + SetLoggerConfigRequest, + }, }, - common::{NatType, SocketType}, + common::{NatType, PortForwardConfigPb, SocketType}, peer_rpc::{GetGlobalPeerMapRequest, PeerCenterRpc, PeerCenterRpcClientFactory}, rpc_impl::standalone::StandAloneClient, rpc_types::controller::BaseController, }, tunnel::tcp::TcpTunnelConnector, - utils::{cost_to_str, float_to_str, PeerRoutePair}, + utils::{cost_to_str, PeerRoutePair}, }; rust_i18n::i18n!("locales", fallback = "en"); @@ -55,8 +66,12 @@ rust_i18n::i18n!("locales", fallback = "en"); #[derive(Parser, Debug)] #[command(name = "easytier-cli", author, version = EASYTIER_VERSION, about, long_about = None)] struct Cli { - /// the instance name - #[arg(short = 'p', long, default_value = "127.0.0.1:15888")] + #[arg( + short = 'p', + long, + default_value = "127.0.0.1:15888", + help = "easytier-core rpc portal address" + )] rpc_portal: SocketAddr, #[arg(short, long, default_value = "false", help = "verbose output")] @@ -71,6 +86,9 @@ struct Cli { )] output_format: OutputFormat, + #[command(flatten)] + instance_select: InstanceSelectArgs, + #[command(subcommand)] sub_command: SubCommand, } @@ -105,6 +123,8 @@ enum SubCommand { Whitelist(WhitelistArgs), #[command(about = "show statistics information")] Stats(StatsArgs), + #[command(about = "manage logger configuration")] + Logger(LoggerArgs), #[command(about = t!("core_clap.generate_completions").to_string())] GenAutocomplete { shell: Shell }, } @@ -115,6 +135,28 @@ enum OutputFormat { Json, } +#[derive(Parser, Debug)] +struct InstanceSelectArgs { + #[arg(short = 'i', long = "instance-id", help = "the instance id")] + id: Option, + + #[arg(short = 'n', long = "instance-name", help = "the instance name")] + name: Option, +} + +impl From<&InstanceSelectArgs> for InstanceIdentifier { + fn from(args: &InstanceSelectArgs) -> Self { + InstanceIdentifier { + selector: match args.id { + Some(id) => Some(Selector::Id(id.into())), + None => Some(Selector::InstanceSelector(InstanceSelector { + name: args.name.clone(), + })), + }, + } + } +} + #[derive(Args, Debug)] struct PeerArgs { #[command(subcommand)] @@ -272,6 +314,23 @@ enum StatsSubCommand { Prometheus, } +#[derive(Args, Debug)] +struct LoggerArgs { + #[command(subcommand)] + sub_command: Option, +} + +#[derive(Subcommand, Debug)] +enum LoggerSubCommand { + /// Get current logger configuration + Get, + /// Set logger level + Set { + #[arg(help = "Log level (disabled, error, warning, info, debug, trace)")] + level: String, + }, +} + #[derive(Args, Debug)] struct ServiceArgs { #[arg(short, long, default_value = env!("CARGO_PKG_NAME"), help = "service name")] @@ -329,6 +388,7 @@ struct CommandHandler<'a> { client: tokio::sync::Mutex, verbose: bool, output_format: &'a OutputFormat, + instance_selector: InstanceIdentifier, } type RpcClient = StandAloneClient; @@ -443,16 +503,44 @@ impl CommandHandler<'_> { .with_context(|| "failed to get stats client")?) } + async fn get_logger_client( + &self, + ) -> Result>, Error> { + Ok(self + .client + .lock() + .await + .scoped_client::>("".to_string()) + .await + .with_context(|| "failed to get logger client")?) + } + + async fn get_config_client( + &self, + ) -> Result>, Error> { + Ok(self + .client + .lock() + .await + .scoped_client::>("".to_string()) + .await + .with_context(|| "failed to get config client")?) + } + async fn list_peers(&self) -> Result { let client = self.get_peer_manager_client().await?; - let request = ListPeerRequest::default(); + let request = ListPeerRequest { + instance: Some(self.instance_selector.clone()), + }; let response = client.list_peer(BaseController::default(), request).await?; Ok(response) } async fn list_routes(&self) -> Result { let client = self.get_peer_manager_client().await?; - let request = ListRouteRequest::default(); + let request = ListRouteRequest { + instance: Some(self.instance_selector.clone()), + }; let response = client .list_route(BaseController::default(), request) .await?; @@ -484,12 +572,19 @@ impl CommandHandler<'_> { ipv4: String, hostname: String, cost: String, + #[tabled(rename = "lat(ms)")] lat_ms: String, + #[tabled(rename = "loss")] loss_rate: String, + #[tabled(rename = "rx")] rx_bytes: String, + #[tabled(rename = "tx")] tx_bytes: String, + #[tabled(rename = "tunnel")] tunnel_proto: String, + #[tabled(rename = "NAT")] nat_type: String, + #[tabled(skip)] id: String, version: String, } @@ -497,6 +592,11 @@ impl CommandHandler<'_> { impl From for PeerTableItem { fn from(p: PeerRoutePair) -> Self { let route = p.route.clone().unwrap_or_default(); + let lat_ms = if route.cost == 1 { + p.get_latency_ms().unwrap_or(0.0) + } else { + route.path_latency_latency_first() as f64 + }; PeerTableItem { cidr: route.ipv4_addr.map(|ip| ip.to_string()).unwrap_or_default(), ipv4: route @@ -506,12 +606,8 @@ impl CommandHandler<'_> { .unwrap_or_default(), hostname: route.hostname.clone(), cost: cost_to_str(route.cost), - lat_ms: if route.cost == 1 { - float_to_str(p.get_latency_ms().unwrap_or(0.0), 3) - } else { - route.path_latency_latency_first().to_string() - }, - loss_rate: float_to_str(p.get_loss_rate().unwrap_or(0.0), 3), + lat_ms: format!("{:.2}", lat_ms), + loss_rate: format!("{:.1}%", p.get_loss_rate().unwrap_or(0.0) * 100.0), rx_bytes: format_size(p.get_rx_bytes().unwrap_or(0), humansize::DECIMAL), tx_bytes: format_size(p.get_tx_bytes().unwrap_or(0), humansize::DECIMAL), tunnel_proto: p @@ -564,7 +660,12 @@ impl CommandHandler<'_> { let client = self.get_peer_manager_client().await?; let node_info = client - .show_node_info(BaseController::default(), ShowNodeInfoRequest::default()) + .show_node_info( + BaseController::default(), + ShowNodeInfoRequest { + instance: Some(self.instance_selector.clone()), + }, + ) .await? .node_info .ok_or(anyhow::anyhow!("node info not found"))?; @@ -574,10 +675,34 @@ impl CommandHandler<'_> { items.push(p.into()); } - // Sort items by ipv4 (using IpAddr for proper numeric comparison) first, then by hostname + // Sort items: local IP first, then public servers, then other servers by IP items.sort_by(|a, b| { use std::net::{IpAddr, Ipv4Addr}; use std::str::FromStr; + + // Priority 1: Local IP (cost is "Local") + let a_is_local = a.cost == "Local"; + let b_is_local = b.cost == "Local"; + if a_is_local != b_is_local { + return if a_is_local { + std::cmp::Ordering::Less + } else { + std::cmp::Ordering::Greater + }; + } + + // Priority 2: Public servers + let a_is_public = a.hostname.starts_with(peers::PUBLIC_SERVER_HOSTNAME_PREFIX); + let b_is_public = b.hostname.starts_with(peers::PUBLIC_SERVER_HOSTNAME_PREFIX); + if a_is_public != b_is_public { + return if a_is_public { + std::cmp::Ordering::Less + } else { + std::cmp::Ordering::Greater + }; + } + + // Priority 3: Sort by IP address let a_ip = IpAddr::from_str(&a.ipv4).unwrap_or(IpAddr::V4(Ipv4Addr::UNSPECIFIED)); let b_ip = IpAddr::from_str(&b.ipv4).unwrap_or(IpAddr::V4(Ipv4Addr::UNSPECIFIED)); match a_ip.cmp(&b_ip) { @@ -593,7 +718,9 @@ impl CommandHandler<'_> { async fn handle_route_dump(&self) -> Result<(), Error> { let client = self.get_peer_manager_client().await?; - let request = DumpRouteRequest::default(); + let request = DumpRouteRequest { + instance: Some(self.instance_selector.clone()), + }; let response = client .dump_route(BaseController::default(), request) .await?; @@ -603,7 +730,9 @@ impl CommandHandler<'_> { async fn handle_foreign_network_list(&self) -> Result<(), Error> { let client = self.get_peer_manager_client().await?; - let request = ListForeignNetworkRequest::default(); + let request = ListForeignNetworkRequest { + instance: Some(self.instance_selector.clone()), + }; let response = client .list_foreign_network(BaseController::default(), request) .await?; @@ -646,7 +775,9 @@ impl CommandHandler<'_> { async fn handle_global_foreign_network_list(&self) -> Result<(), Error> { let client = self.get_peer_manager_client().await?; - let request = ListGlobalForeignNetworkRequest::default(); + let request = ListGlobalForeignNetworkRequest { + instance: Some(self.instance_selector.clone()), + }; let response = client .list_global_foreign_network(BaseController::default(), request) .await?; @@ -695,7 +826,12 @@ impl CommandHandler<'_> { let mut items: Vec = vec![]; let client = self.get_peer_manager_client().await?; let node_info = client - .show_node_info(BaseController::default(), ShowNodeInfoRequest::default()) + .show_node_info( + BaseController::default(), + ShowNodeInfoRequest { + instance: Some(self.instance_selector.clone()), + }, + ) .await? .node_info .ok_or(anyhow::anyhow!("node info not found"))?; @@ -817,7 +953,9 @@ impl CommandHandler<'_> { async fn handle_connector_list(&self) -> Result<(), Error> { let client = self.get_connector_manager_client().await?; - let request = ListConnectorRequest::default(); + let request = ListConnectorRequest { + instance: Some(self.instance_selector.clone()), + }; let response = client .list_connector(BaseController::default(), request) .await?; @@ -831,7 +969,9 @@ impl CommandHandler<'_> { async fn handle_acl_stats(&self) -> Result<(), Error> { let client = self.get_acl_manager_client().await?; - let request = GetAclStatsRequest::default(); + let request = GetAclStatsRequest { + instance: Some(self.instance_selector.clone()), + }; let response = client .get_acl_stats(BaseController::default(), request) .await?; @@ -851,7 +991,9 @@ impl CommandHandler<'_> { async fn handle_mapped_listener_list(&self) -> Result<(), Error> { let client = self.get_mapped_listener_manager_client().await?; - let request = ListMappedListenerRequest::default(); + let request = ListMappedListenerRequest { + instance: Some(self.instance_selector.clone()), + }; let response = client .list_mapped_listener(BaseController::default(), request) .await?; @@ -866,28 +1008,25 @@ impl CommandHandler<'_> { Ok(()) } - async fn handle_mapped_listener_add(&self, url: &str) -> Result<(), Error> { - let url = Self::mapped_listener_validate_url(url)?; - let client = self.get_mapped_listener_manager_client().await?; - let request = ManageMappedListenerRequest { - action: MappedListenerManageAction::MappedListenerAdd as i32, - url: Some(url.into()), - }; - let _response = client - .manage_mapped_listener(BaseController::default(), request) - .await?; - Ok(()) - } - - async fn handle_mapped_listener_remove(&self, url: &str) -> Result<(), Error> { + async fn handle_mapped_listener_modify( + &self, + url: &str, + action: ConfigPatchAction, + ) -> Result<(), Error> { let url = Self::mapped_listener_validate_url(url)?; - let client = self.get_mapped_listener_manager_client().await?; - let request = ManageMappedListenerRequest { - action: MappedListenerManageAction::MappedListenerRemove as i32, - url: Some(url.into()), + let client = self.get_config_client().await?; + let request = PatchConfigRequest { + instance: Some(self.instance_selector.clone()), + patch: Some(InstanceConfigPatch { + mapped_listeners: vec![UrlPatch { + action: action.into(), + url: Some(url.into()), + }], + ..Default::default() + }), }; let _response = client - .manage_mapped_listener(BaseController::default(), request) + .patch_config(BaseController::default(), request) .await?; Ok(()) } @@ -904,83 +1043,56 @@ impl CommandHandler<'_> { Ok(url) } - async fn handle_port_forward_add( + async fn handle_port_forward_modify( &self, + action: ConfigPatchAction, protocol: &str, bind_addr: &str, - dst_addr: &str, + dst_addr: Option<&str>, ) -> Result<(), Error> { let bind_addr: std::net::SocketAddr = bind_addr .parse() .with_context(|| format!("Invalid bind address: {}", bind_addr))?; - let dst_addr: std::net::SocketAddr = dst_addr - .parse() - .with_context(|| format!("Invalid destination address: {}", dst_addr))?; - if protocol != "tcp" && protocol != "udp" { - return Err(anyhow::anyhow!("Protocol must be 'tcp' or 'udp'")); - } + let socket_type = match protocol { + "tcp" => SocketType::Tcp, + "udp" => SocketType::Udp, + _ => return Err(anyhow::anyhow!("Protocol must be 'tcp' or 'udp'")), + }; - let client = self.get_port_forward_manager_client().await?; - let request = AddPortForwardRequest { - cfg: Some( - PortForwardConfig { - proto: protocol.to_string(), - bind_addr, - dst_addr, - } - .into(), - ), + let client = self.get_config_client().await?; + let request = PatchConfigRequest { + instance: Some(self.instance_selector.clone()), + patch: Some(InstanceConfigPatch { + port_forwards: vec![PortForwardPatch { + action: action.into(), + cfg: Some(PortForwardConfigPb { + bind_addr: Some(bind_addr.into()), + dst_addr: dst_addr.map(|s| s.parse::().unwrap().into()), + socket_type: socket_type.into(), + }), + }], + ..Default::default() + }), }; client - .add_port_forward(BaseController::default(), request) + .patch_config(BaseController::default(), request) .await?; println!( - "Port forward rule added: {} {} -> {}", - protocol, bind_addr, dst_addr + "Port forward rule {}: {} {}", + action.as_str_name().to_lowercase(), + protocol, + bind_addr ); Ok(()) } - async fn handle_port_forward_remove( - &self, - protocol: &str, - bind_addr: &str, - dst_addr: Option<&str>, - ) -> Result<(), Error> { - let bind_addr: std::net::SocketAddr = bind_addr - .parse() - .with_context(|| format!("Invalid bind address: {}", bind_addr))?; - - if protocol != "tcp" && protocol != "udp" { - return Err(anyhow::anyhow!("Protocol must be 'tcp' or 'udp'")); - } - - let client = self.get_port_forward_manager_client().await?; - let request = RemovePortForwardRequest { - cfg: Some( - PortForwardConfig { - proto: protocol.to_string(), - bind_addr, - dst_addr: dst_addr - .map(|s| s.parse::().unwrap()) - .unwrap_or("0.0.0.0:0".parse::().unwrap()), - } - .into(), - ), - }; - - client - .remove_port_forward(BaseController::default(), request) - .await?; - println!("Port forward rule removed: {} {}", protocol, bind_addr); - Ok(()) - } - async fn handle_port_forward_list(&self) -> Result<(), Error> { let client = self.get_port_forward_manager_client().await?; - let request = ListPortForwardRequest::default(); + let request = ListPortForwardRequest { + instance: Some(self.instance_selector.clone()), + }; let response = client .list_port_forward(BaseController::default(), request) .await?; @@ -1021,78 +1133,118 @@ impl CommandHandler<'_> { } async fn handle_whitelist_set_tcp(&self, ports: &str) -> Result<(), Error> { - let tcp_ports = Self::parse_port_list(ports)?; - let client = self.get_acl_manager_client().await?; - - // Get current UDP ports to preserve them - let current = client - .get_whitelist(BaseController::default(), GetWhitelistRequest::default()) - .await?; - let request = SetWhitelistRequest { - tcp_ports, - udp_ports: current.udp_ports, + let mut whitelist = Self::parse_port_list(ports)? + .into_iter() + .map(|p| StringPatch { + action: ConfigPatchAction::Add.into(), + value: p, + }) + .collect::>(); + whitelist.insert( + 0, + StringPatch { + action: ConfigPatchAction::Clear.into(), + value: "".to_string(), + }, + ); + let client = self.get_config_client().await?; + + let request = PatchConfigRequest { + instance: Some(self.instance_selector.clone()), + patch: Some(InstanceConfigPatch { + acl: Some(AclPatch { + tcp_whitelist: whitelist, + ..Default::default() + }), + ..Default::default() + }), }; client - .set_whitelist(BaseController::default(), request) + .patch_config(BaseController::default(), request) .await?; println!("TCP whitelist updated: {}", ports); Ok(()) } async fn handle_whitelist_set_udp(&self, ports: &str) -> Result<(), Error> { - let udp_ports = Self::parse_port_list(ports)?; - let client = self.get_acl_manager_client().await?; - - // Get current TCP ports to preserve them - let current = client - .get_whitelist(BaseController::default(), GetWhitelistRequest::default()) - .await?; - let request = SetWhitelistRequest { - tcp_ports: current.tcp_ports, - udp_ports, + let mut whitelist = Self::parse_port_list(ports)? + .into_iter() + .map(|p| StringPatch { + action: ConfigPatchAction::Add.into(), + value: p, + }) + .collect::>(); + whitelist.insert( + 0, + StringPatch { + action: ConfigPatchAction::Clear.into(), + value: "".to_string(), + }, + ); + let client = self.get_config_client().await?; + + let request = PatchConfigRequest { + instance: Some(self.instance_selector.clone()), + patch: Some(InstanceConfigPatch { + acl: Some(AclPatch { + udp_whitelist: whitelist, + ..Default::default() + }), + ..Default::default() + }), }; client - .set_whitelist(BaseController::default(), request) + .patch_config(BaseController::default(), request) .await?; println!("UDP whitelist updated: {}", ports); Ok(()) } async fn handle_whitelist_clear_tcp(&self) -> Result<(), Error> { - let client = self.get_acl_manager_client().await?; - - // Get current UDP ports to preserve them - let current = client - .get_whitelist(BaseController::default(), GetWhitelistRequest::default()) - .await?; - let request = SetWhitelistRequest { - tcp_ports: vec![], - udp_ports: current.udp_ports, + let client = self.get_config_client().await?; + + let request = PatchConfigRequest { + instance: Some(self.instance_selector.clone()), + patch: Some(InstanceConfigPatch { + acl: Some(AclPatch { + tcp_whitelist: vec![StringPatch { + action: ConfigPatchAction::Clear.into(), + value: "".to_string(), + }], + ..Default::default() + }), + ..Default::default() + }), }; client - .set_whitelist(BaseController::default(), request) + .patch_config(BaseController::default(), request) .await?; println!("TCP whitelist cleared"); Ok(()) } async fn handle_whitelist_clear_udp(&self) -> Result<(), Error> { - let client = self.get_acl_manager_client().await?; - - // Get current TCP ports to preserve them - let current = client - .get_whitelist(BaseController::default(), GetWhitelistRequest::default()) - .await?; - let request = SetWhitelistRequest { - tcp_ports: current.tcp_ports, - udp_ports: vec![], + let client = self.get_config_client().await?; + + let request = PatchConfigRequest { + instance: Some(self.instance_selector.clone()), + patch: Some(InstanceConfigPatch { + acl: Some(AclPatch { + udp_whitelist: vec![StringPatch { + action: ConfigPatchAction::Clear.into(), + value: "".to_string(), + }], + ..Default::default() + }), + ..Default::default() + }), }; client - .set_whitelist(BaseController::default(), request) + .patch_config(BaseController::default(), request) .await?; println!("UDP whitelist cleared"); Ok(()) @@ -1100,7 +1252,9 @@ impl CommandHandler<'_> { async fn handle_whitelist_show(&self) -> Result<(), Error> { let client = self.get_acl_manager_client().await?; - let request = GetWhitelistRequest::default(); + let request = GetWhitelistRequest { + instance: Some(self.instance_selector.clone()), + }; let response = client .get_whitelist(BaseController::default(), request) .await?; @@ -1131,6 +1285,66 @@ impl CommandHandler<'_> { Ok(()) } + async fn handle_logger_get(&self) -> Result<(), Error> { + let client = self.get_logger_client().await?; + let request = GetLoggerConfigRequest::default(); + let response = client + .get_logger_config(BaseController::default(), request) + .await?; + + match self.output_format { + OutputFormat::Table => { + let level_str = match response.level() { + LogLevel::Disabled => "disabled", + LogLevel::Error => "error", + LogLevel::Warning => "warning", + LogLevel::Info => "info", + LogLevel::Debug => "debug", + LogLevel::Trace => "trace", + }; + println!("Current Log Level: {}", level_str); + } + OutputFormat::Json => { + let json = serde_json::to_string_pretty(&response)?; + println!("{}", json); + } + } + + Ok(()) + } + + async fn handle_logger_set(&self, level: &str) -> Result<(), Error> { + let log_level = match level.to_lowercase().as_str() { + "disabled" => LogLevel::Disabled, + "error" => LogLevel::Error, + "warning" => LogLevel::Warning, + "info" => LogLevel::Info, + "debug" => LogLevel::Debug, + "trace" => LogLevel::Trace, + _ => return Err(anyhow::anyhow!("Invalid log level: {}. Valid levels are: disabled, error, warning, info, debug, trace", level)), + }; + + let client = self.get_logger_client().await?; + let request = SetLoggerConfigRequest { + level: log_level.into(), + }; + let response = client + .set_logger_config(BaseController::default(), request) + .await?; + + match self.output_format { + OutputFormat::Table => { + println!("Log level successfully set to: {}", level); + } + OutputFormat::Json => { + let json = serde_json::to_string_pretty(&response)?; + println!("{}", json); + } + } + + Ok(()) + } + fn parse_port_list(ports_str: &str) -> Result, Error> { let mut ports = Vec::new(); for port_spec in ports_str.split(',') { @@ -1299,6 +1513,7 @@ impl Service { ServiceManagerKind::Systemd => Some(self.make_systemd_unit(options).unwrap()), ServiceManagerKind::Rcd => Some(self.make_rcd_script(options).unwrap()), ServiceManagerKind::OpenRc => Some(self.make_open_rc_script(options).unwrap()), + ServiceManagerKind::Launchd => None, // 使用 service-manager-rs 的默认 plist 生成 _ => { #[cfg(target_os = "windows")] { @@ -1436,7 +1651,7 @@ where { match format { OutputFormat::Table => { - println!("{}", tabled::Table::new(items).with(Style::modern())); + println!("{}", tabled::Table::new(items).with(Style::markdown())); } OutputFormat::Json => { println!("{}", serde_json::to_string_pretty(items)?); @@ -1461,6 +1676,7 @@ async fn main() -> Result<(), Error> { client: tokio::sync::Mutex::new(client), verbose: cli.verbose, output_format: &cli.output_format, + instance_selector: (&cli.instance_select).into(), }; match cli.sub_command { @@ -1501,11 +1717,15 @@ async fn main() -> Result<(), Error> { SubCommand::MappedListener(mapped_listener_args) => { match mapped_listener_args.sub_command { Some(MappedListenerSubCommand::Add { url }) => { - handler.handle_mapped_listener_add(&url).await?; + handler + .handle_mapped_listener_modify(&url, ConfigPatchAction::Add) + .await?; println!("add mapped listener: {url}"); } Some(MappedListenerSubCommand::Remove { url }) => { - handler.handle_mapped_listener_remove(&url).await?; + handler + .handle_mapped_listener_modify(&url, ConfigPatchAction::Remove) + .await?; println!("remove mapped listener: {url}"); } Some(MappedListenerSubCommand::List) | None => { @@ -1556,7 +1776,12 @@ async fn main() -> Result<(), Error> { let node_info = handler .get_peer_manager_client() .await? - .show_node_info(BaseController::default(), ShowNodeInfoRequest::default()) + .show_node_info( + BaseController::default(), + ShowNodeInfoRequest { + instance: Some((&cli.instance_select).into()), + }, + ) .await? .node_info .ok_or(anyhow::anyhow!("node info not found"))?; @@ -1660,7 +1885,9 @@ async fn main() -> Result<(), Error> { let resp = vpn_portal_client .get_vpn_portal_info( BaseController::default(), - GetVpnPortalInfoRequest::default(), + GetVpnPortalInfoRequest { + instance: Some((&cli.instance_select).into()), + }, ) .await? .vpn_portal_info @@ -1679,7 +1906,12 @@ async fn main() -> Result<(), Error> { SubCommand::Node(sub_cmd) => { let client = handler.get_peer_manager_client().await?; let node_info = client - .show_node_info(BaseController::default(), ShowNodeInfoRequest::default()) + .show_node_info( + BaseController::default(), + ShowNodeInfoRequest { + instance: Some((&cli.instance_select).into()), + }, + ) .await? .node_info .ok_or(anyhow::anyhow!("node info not found"))?; @@ -1728,7 +1960,7 @@ async fn main() -> Result<(), Error> { builder.push_record(vec![format!("Listener {}", idx).as_str(), l]); } - println!("{}", builder.build().with(Style::modern())); + println!("{}", builder.build().with(Style::markdown())); } Some(NodeSubCommand::Config) => { println!("{}", node_info.config); @@ -1868,7 +2100,12 @@ async fn main() -> Result<(), Error> { dst_addr, }) => { handler - .handle_port_forward_add(protocol, bind_addr, dst_addr) + .handle_port_forward_modify( + ConfigPatchAction::Add, + protocol, + bind_addr, + Some(dst_addr), + ) .await?; } Some(PortForwardSubCommand::Remove { @@ -1877,7 +2114,12 @@ async fn main() -> Result<(), Error> { dst_addr, }) => { handler - .handle_port_forward_remove(protocol, bind_addr, dst_addr.as_deref()) + .handle_port_forward_modify( + ConfigPatchAction::Remove, + protocol, + bind_addr, + dst_addr.as_deref(), + ) .await?; } Some(PortForwardSubCommand::List) | None => { @@ -1904,7 +2146,9 @@ async fn main() -> Result<(), Error> { SubCommand::Stats(stats_args) => match &stats_args.sub_command { Some(StatsSubCommand::Show) | None => { let client = handler.get_stats_client().await?; - let request = GetStatsRequest {}; + let request = GetStatsRequest { + instance: Some((&cli.instance_select).into()), + }; let response = client.get_stats(BaseController::default(), request).await?; if cli.output_format == OutputFormat::Json { @@ -1956,7 +2200,9 @@ async fn main() -> Result<(), Error> { } Some(StatsSubCommand::Prometheus) => { let client = handler.get_stats_client().await?; - let request = GetPrometheusStatsRequest {}; + let request = GetPrometheusStatsRequest { + instance: Some((&cli.instance_select).into()), + }; let response = client .get_prometheus_stats(BaseController::default(), request) .await?; @@ -1964,6 +2210,14 @@ async fn main() -> Result<(), Error> { println!("{}", response.prometheus_text); } }, + SubCommand::Logger(logger_args) => match &logger_args.sub_command { + Some(LoggerSubCommand::Get) | None => { + handler.handle_logger_get().await?; + } + Some(LoggerSubCommand::Set { level }) => { + handler.handle_logger_set(level).await?; + } + }, SubCommand::GenAutocomplete { shell } => { let mut cmd = Cli::command(); easytier::print_completions(shell, &mut cmd, "easytier-cli"); @@ -2061,12 +2315,12 @@ mod win_service_manager { let service = self .service_manager .create_service(&service_info, ServiceAccess::ALL_ACCESS) - .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; + .map_err(io::Error::other)?; if let Some(s) = description { service .set_description(s.clone()) - .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; + .map_err(io::Error::other)?; } if let Some(work_dir) = ctx.working_directory { @@ -2080,33 +2334,27 @@ mod win_service_manager { let service = self .service_manager .open_service(ctx.label.to_qualified_name(), ServiceAccess::ALL_ACCESS) - .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; + .map_err(io::Error::other)?; - service - .delete() - .map_err(|e| io::Error::new(io::ErrorKind::Other, e)) + service.delete().map_err(io::Error::other) } fn start(&self, ctx: ServiceStartCtx) -> io::Result<()> { let service = self .service_manager .open_service(ctx.label.to_qualified_name(), ServiceAccess::ALL_ACCESS) - .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; + .map_err(io::Error::other)?; - service - .start(&[] as &[&OsStr]) - .map_err(|e| io::Error::new(io::ErrorKind::Other, e)) + service.start(&[] as &[&OsStr]).map_err(io::Error::other) } fn stop(&self, ctx: ServiceStopCtx) -> io::Result<()> { let service = self .service_manager .open_service(ctx.label.to_qualified_name(), ServiceAccess::ALL_ACCESS) - .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; + .map_err(io::Error::other)?; - _ = service - .stop() - .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; + _ = service.stop().map_err(io::Error::other)?; Ok(()) } @@ -2118,10 +2366,7 @@ mod win_service_manager { fn set_level(&mut self, level: ServiceLevel) -> io::Result<()> { match level { ServiceLevel::System => Ok(()), - _ => Err(io::Error::new( - io::ErrorKind::Other, - "Unsupported service level", - )), + _ => Err(io::Error::other("Unsupported service level")), } } @@ -2137,13 +2382,11 @@ mod win_service_manager { return Ok(ServiceStatus::NotInstalled); } } - return Err(io::Error::new(io::ErrorKind::Other, e)); + return Err(io::Error::other(e)); } }; - let status = service - .query_status() - .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; + let status = service.query_status().map_err(io::Error::other)?; match status.current_state { windows_service::service::ServiceState::Stopped => Ok(ServiceStatus::Stopped(None)), diff --git a/easytier/src/easytier-core.rs b/easytier/src/easytier-core.rs index 3b4f982c2..814892d3a 100644 --- a/easytier/src/easytier-core.rs +++ b/easytier/src/easytier-core.rs @@ -13,7 +13,7 @@ use std::{ use anyhow::Context; use cidr::IpCidr; use clap::{CommandFactory, Parser}; - +use clap_complete::Shell; use clap_complete::Shell; use easytier::{ common::{ @@ -31,10 +31,12 @@ use easytier::{ instance_manager::NetworkInstanceManager, launcher::{add_proxy_network_to_config, ConfigSource}, proto::common::{CompressionAlgoPb, NatType}, + rpc_service::ApiRpcServer, tunnel::{IpVersion, PROTO_PORT_OFFSET}, utils::{init_logger, setup_panic_handler}, web_client, }; +use tokio::io::AsyncReadExt; #[cfg(target_os = "windows")] windows_service::define_windows_service!(ffi_service_main, win_service_main); @@ -130,8 +132,14 @@ struct Cli { #[command(flatten)] logging_options: LoggingOptions, + #[command(flatten)] + rpc_portal_options: RpcPortalOptions, + #[clap(long, help = t!("core_clap.generate_completions").to_string())] gen_autocomplete: Option, + + #[clap(long, help = t!("core_clap.check_config").to_string())] + check_config: bool, } #[derive(Parser, Debug)] @@ -202,22 +210,6 @@ struct NetworkOptions { )] proxy_networks: Vec, - #[arg( - short, - long, - env = "ET_RPC_PORTAL", - help = t!("core_clap.rpc_portal").to_string(), - )] - rpc_portal: Option, - - #[arg( - long, - env = "ET_RPC_PORTAL_WHITELIST", - value_delimiter = ',', - help = t!("core_clap.rpc_portal_whitelist").to_string(), - )] - rpc_portal_whitelist: Option>, - #[arg( short, long, @@ -288,7 +280,6 @@ struct NetworkOptions { long, env = "ET_ENCRYPTION_ALGORITHM", help = t!("core_clap.encryption_algorithm").to_string(), - default_value = "aes-gcm", value_parser = get_avaliable_encrypt_methods() )] encryption_algorithm: Option, @@ -425,6 +416,15 @@ struct NetworkOptions { )] disable_udp_hole_punching: Option, + #[arg( + long, + env = "ET_DISABLE_SYM_HOLE_PUNCHING", + help = t!("core_clap.disable_sym_hole_punching").to_string(), + num_args = 0..=1, + default_missing_value = "true" + )] + disable_sym_hole_punching: Option, + #[arg( long, env = "ET_RELAY_ALL_PEER_RPC", @@ -508,6 +508,12 @@ struct NetworkOptions { )] accept_dns: Option, + #[arg( + long = "tld-dns-zone", + env = "ET_TLD_DNS_ZONE", + help = t!("core_clap.tld_dns_zone").to_string())] + tld_dns_zone: Option, + #[arg( long, env = "ET_PRIVATE_MODE", @@ -555,6 +561,24 @@ struct NetworkOptions { default_missing_value = "true" )] enable_relay_foreign_network_kcp: Option, + + #[arg( + long, + env = "ET_STUN_SERVERS", + value_delimiter = ',', + help = t!("core_clap.stun_servers").to_string(), + num_args = 0.. + )] + stun_servers: Option>, + + #[arg( + long, + env = "ET_STUN_SERVERS_V6", + value_delimiter = ',', + help = t!("core_clap.stun_servers_v6").to_string(), + num_args = 0.. + )] + stun_servers_v6: Option>, } #[derive(Parser, Debug)] @@ -579,6 +603,39 @@ struct LoggingOptions { help = t!("core_clap.file_log_dir").to_string() )] file_log_dir: Option, + + #[arg( + long, + env = "ET_FILE_LOG_SIZE", + help = t!("core_clap.file_log_size_mb").to_string() + )] + file_log_size: Option, + + #[arg( + long, + env = "ET_FILE_LOG_COUNT", + help = t!("core_clap.file_log_count").to_string() + )] + file_log_count: Option, +} + +#[derive(Parser, Debug)] +struct RpcPortalOptions { + #[arg( + short, + long, + env = "ET_RPC_PORTAL", + help = t!("core_clap.rpc_portal").to_string(), + )] + rpc_portal: Option, + + #[arg( + long, + env = "ET_RPC_PORTAL_WHITELIST", + value_delimiter = ',', + help = t!("core_clap.rpc_portal_whitelist").to_string(), + )] + rpc_portal_whitelist: Option>, } rust_i18n::i18n!("locales", fallback = "en"); @@ -628,14 +685,6 @@ impl Cli { Ok(listeners) } - - fn parse_rpc_portal(rpc_portal: String) -> anyhow::Result { - if let Ok(port) = rpc_portal.parse::() { - return Ok(format!("0.0.0.0:{}", port).parse().unwrap()); - } - - Ok(rpc_portal.parse()?) - } } impl NetworkOptions { @@ -743,24 +792,6 @@ impl NetworkOptions { add_proxy_network_to_config(n, cfg)?; } - let rpc_portal = if let Some(r) = &self.rpc_portal { - Cli::parse_rpc_portal(r.clone()) - .with_context(|| format!("failed to parse rpc portal: {}", r))? - } else if let Some(r) = cfg.get_rpc_portal() { - r - } else { - Cli::parse_rpc_portal("0".into())? - }; - cfg.set_rpc_portal(rpc_portal); - - if let Some(rpc_portal_whitelist) = &self.rpc_portal_whitelist { - let mut whitelist = cfg.get_rpc_portal_whitelist().unwrap_or_default(); - for cidr in rpc_portal_whitelist { - whitelist.push(*cidr); - } - cfg.set_rpc_portal_whitelist(Some(whitelist)); - } - if let Some(external_nodes) = self.external_node.as_ref() { let mut old_peers = cfg.get_peers(); old_peers.push(PeerConfig { @@ -910,6 +941,11 @@ impl NetworkOptions { f.enable_relay_foreign_network_kcp = self .enable_relay_foreign_network_kcp .unwrap_or(f.enable_relay_foreign_network_kcp); + f.disable_sym_hole_punching = self.disable_sym_hole_punching.unwrap_or(false); + // Configure tld_dns_zone: use provided value if set + if let Some(tld_dns_zone) = &self.tld_dns_zone { + f.tld_dns_zone = tld_dns_zone.clone(); + } cfg.set_flags(f); if !self.exit_nodes.is_empty() { @@ -924,6 +960,17 @@ impl NetworkOptions { old_udp_whitelist.extend(self.udp_whitelist.clone()); cfg.set_udp_whitelist(old_udp_whitelist); + if let Some(stun_servers) = &self.stun_servers { + let mut old_stun_servers = cfg.get_stun_servers().unwrap_or_default(); + old_stun_servers.extend(stun_servers.iter().cloned()); + cfg.set_stun_servers(Some(old_stun_servers)); + } + + if let Some(stun_servers_v6) = &self.stun_servers_v6 { + let mut old_stun_servers_v6 = cfg.get_stun_servers_v6().unwrap_or_default(); + old_stun_servers_v6.extend(stun_servers_v6.iter().cloned()); + cfg.set_stun_servers_v6(Some(old_stun_servers_v6)); + } Ok(()) } } @@ -940,6 +987,8 @@ impl LoggingConfigLoader for &LoggingOptions { level: self.file_log_level.clone(), dir: self.file_log_dir.clone(), file: None, + size_mb: self.file_log_size, + count: self.file_log_count, } } } @@ -1014,6 +1063,18 @@ fn win_service_event_loop( }); } +fn parse_cli() -> Cli { + let mut cli = Cli::parse(); + // for --stun-servers="", we want vec![], but clap will give vec![""], hack for that + if let Some(stun_servers) = &mut cli.network_options.stun_servers { + stun_servers.retain(|s| !s.trim().is_empty()); + } + if let Some(stun_servers_v6) = &mut cli.network_options.stun_servers_v6 { + stun_servers_v6.retain(|s| !s.trim().is_empty()); + } + cli +} + #[cfg(target_os = "windows")] fn win_service_main(arg: Vec) { use std::sync::Arc; @@ -1024,7 +1085,7 @@ fn win_service_main(arg: Vec) { _ = win_service_set_work_dir(&arg[0]); - let cli = Cli::parse(); + let cli = parse_cli(); let stop_notify_send = Arc::new(Notify::new()); let stop_notify_recv = Arc::clone(&stop_notify_send); @@ -1056,7 +1117,17 @@ fn win_service_main(arg: Vec) { } async fn run_main(cli: Cli) -> anyhow::Result<()> { - init_logger(&cli.logging_options, false)?; + init_logger(&cli.logging_options, true)?; + + let manager = Arc::new(NetworkInstanceManager::new()); + + let _rpc_server = ApiRpcServer::new( + cli.rpc_portal_options.rpc_portal, + cli.rpc_portal_options.rpc_portal_whitelist, + manager.clone(), + )? + .serve() + .await?; if cli.config_server.is_some() { set_default_machine_id(cli.machine_id); @@ -1106,18 +1177,25 @@ async fn run_main(cli: Cli) -> anyhow::Result<()> { create_connector_by_url(c_url.as_str(), &global_ctx, IpVersion::Both).await?, token.to_string(), hostname, + manager, ); tokio::signal::ctrl_c().await.unwrap(); return Ok(()); } - let manager = NetworkInstanceManager::new(); let mut crate_cli_network = cli.config_file.is_none() || cli.network_options.network_name.is_some(); if let Some(config_files) = cli.config_file { let config_file_count = config_files.len(); for config_file in config_files { - let mut cfg = TomlConfigLoader::new(&config_file) - .with_context(|| format!("failed to load config file: {:?}", config_file))?; + let mut cfg = if config_file == PathBuf::from("-") { + let mut stdin = String::new(); + _ = tokio::io::stdin().read_to_string(&mut stdin).await?; + TomlConfigLoader::new_from_str(stdin.as_str()) + .with_context(|| "failed to load config from stdin")? + } else { + TomlConfigLoader::new(&config_file) + .with_context(|| format!("failed to load config file: {:?}", config_file))? + }; if cli.network_options.can_merge(&cfg, config_file_count) { cli.network_options.merge_into(&mut cfg).with_context(|| { @@ -1151,7 +1229,7 @@ async fn run_main(cli: Cli) -> anyhow::Result<()> { tokio::select! { _ = manager.wait() => { - let infos = manager.collect_network_infos()?; + let infos = manager.collect_network_infos().await?; let errs = infos .into_values() .filter_map(|info| info.error_msg) @@ -1232,7 +1310,24 @@ async fn main() -> ExitCode { set_prof_active(true); let _monitor = std::thread::spawn(memory_monitor); - let cli = Cli::parse(); + let cli = parse_cli(); + + if let Some(shell) = cli.gen_autocomplete { + let mut cmd = Cli::command(); + easytier::print_completions(shell, &mut cmd, "easytier-core"); + return ExitCode::SUCCESS; + } + + // Verify configurations + if cli.check_config { + if let Err(e) = validate_config(&cli).await { + eprintln!("Config validation failed: {:?}", e); + return ExitCode::FAILURE; + } else { + return ExitCode::SUCCESS; + } + } + if let Some(shell) = cli.gen_autocomplete { let mut cmd = Cli::command(); easytier::print_completions(shell, &mut cmd, "easytier-core"); @@ -1252,3 +1347,25 @@ async fn main() -> ExitCode { ExitCode::from(ret_code) } + +async fn validate_config(cli: &Cli) -> anyhow::Result<()> { + // Check if config file is provided + let config_files = cli + .config_file + .as_ref() + .ok_or_else(|| anyhow::anyhow!("--config-file is required when using --check-config"))?; + + for config_file in config_files { + if config_file == &PathBuf::from("-") { + let mut stdin = String::new(); + _ = tokio::io::stdin().read_to_string(&mut stdin).await?; + TomlConfigLoader::new_from_str(stdin.as_str()) + .with_context(|| "config source: stdin")?; + } else { + TomlConfigLoader::new(config_file) + .with_context(|| format!("config source: {:?}", config_file))?; + }; + } + + Ok(()) +} diff --git a/easytier/src/easytier_core.rs b/easytier/src/easytier_core.rs index 81266f3d7..5dd88b3f9 100644 --- a/easytier/src/easytier_core.rs +++ b/easytier/src/easytier_core.rs @@ -1,38 +1,30 @@ #![allow(dead_code)] use rust_i18n::t; + use std::{ net::{IpAddr, SocketAddr}, path::PathBuf, process::ExitCode, sync::Arc, }; -use clap_complete::Shell; + use anyhow::Context; use cidr::IpCidr; use clap::{CommandFactory, Parser}; -use crate::print_completions; - -use crate::{ - common::{ - config::{ - get_avaliable_encrypt_methods, ConfigLoader, ConsoleLoggerConfig, FileLoggerConfig, - LoggingConfigLoader, NetworkIdentity, PeerConfig, PortForwardConfig, TomlConfigLoader, - VpnPortalConfig, - }, - constants::EASYTIER_VERSION, - global_ctx::GlobalCtx, - set_default_machine_id, - stun::MockStunInfoCollector, +use clap_complete::Shell; +use crate::{common::{ + config::{ + get_avaliable_encrypt_methods, ConfigLoader, ConsoleLoggerConfig, FileLoggerConfig, + LoggingConfigLoader, NetworkIdentity, PeerConfig, PortForwardConfig, TomlConfigLoader, + VpnPortalConfig, }, - connector::create_connector_by_url, - instance_manager::NetworkInstanceManager, - launcher::{add_proxy_network_to_config, ConfigSource}, - proto::common::{CompressionAlgoPb, NatType}, - tunnel::{IpVersion, PROTO_PORT_OFFSET}, - utils::{init_logger, setup_panic_handler}, - web_client, -}; + constants::EASYTIER_VERSION, + global_ctx::GlobalCtx, + set_default_machine_id, + stun::MockStunInfoCollector, +}, connector::create_connector_by_url, instance_manager::NetworkInstanceManager, launcher::{add_proxy_network_to_config, ConfigSource}, print_completions, proto::common::{CompressionAlgoPb, NatType}, rpc_service::ApiRpcServer, tunnel::{IpVersion, PROTO_PORT_OFFSET}, utils::{init_logger, setup_panic_handler}, web_client}; +use tokio::io::AsyncReadExt; #[cfg(target_os = "windows")] windows_service::define_windows_service!(ffi_service_main, win_service_main); @@ -46,6 +38,7 @@ static GLOBAL_MIMALLOC: MiMalloc = MiMalloc; #[cfg(feature = "jemalloc-prof")] use jemalloc_ctl::{epoch, stats, Access as _, AsName as _}; +use crate::common::constants::WIN_SERVICE_WORK_DIR_REG_KEY; #[cfg(feature = "jemalloc")] #[global_allocator] @@ -128,8 +121,14 @@ pub(crate) struct Cli { #[command(flatten)] logging_options: LoggingOptions, + #[command(flatten)] + rpc_portal_options: RpcPortalOptions, + #[clap(long, help = t!("core_clap.generate_completions").to_string())] gen_autocomplete: Option, + + #[clap(long, help = t!("core_clap.check_config").to_string())] + check_config: bool, } #[derive(Parser, Debug)] @@ -200,22 +199,6 @@ struct NetworkOptions { )] proxy_networks: Vec, - #[arg( - short, - long, - env = "ET_RPC_PORTAL", - help = t!("core_clap.rpc_portal").to_string(), - )] - rpc_portal: Option, - - #[arg( - long, - env = "ET_RPC_PORTAL_WHITELIST", - value_delimiter = ',', - help = t!("core_clap.rpc_portal_whitelist").to_string(), - )] - rpc_portal_whitelist: Option>, - #[arg( short, long, @@ -286,7 +269,6 @@ struct NetworkOptions { long, env = "ET_ENCRYPTION_ALGORITHM", help = t!("core_clap.encryption_algorithm").to_string(), - default_value = "aes-gcm", value_parser = get_avaliable_encrypt_methods() )] encryption_algorithm: Option, @@ -423,6 +405,15 @@ struct NetworkOptions { )] disable_udp_hole_punching: Option, + #[arg( + long, + env = "ET_DISABLE_SYM_HOLE_PUNCHING", + help = t!("core_clap.disable_sym_hole_punching").to_string(), + num_args = 0..=1, + default_missing_value = "true" + )] + disable_sym_hole_punching: Option, + #[arg( long, env = "ET_RELAY_ALL_PEER_RPC", @@ -506,6 +497,12 @@ struct NetworkOptions { )] accept_dns: Option, + #[arg( + long = "tld-dns-zone", + env = "ET_TLD_DNS_ZONE", + help = t!("core_clap.tld_dns_zone").to_string())] + tld_dns_zone: Option, + #[arg( long, env = "ET_PRIVATE_MODE", @@ -553,6 +550,24 @@ struct NetworkOptions { default_missing_value = "true" )] enable_relay_foreign_network_kcp: Option, + + #[arg( + long, + env = "ET_STUN_SERVERS", + value_delimiter = ',', + help = t!("core_clap.stun_servers").to_string(), + num_args = 0.. + )] + stun_servers: Option>, + + #[arg( + long, + env = "ET_STUN_SERVERS_V6", + value_delimiter = ',', + help = t!("core_clap.stun_servers_v6").to_string(), + num_args = 0.. + )] + stun_servers_v6: Option>, } #[derive(Parser, Debug)] @@ -577,6 +592,39 @@ struct LoggingOptions { help = t!("core_clap.file_log_dir").to_string() )] file_log_dir: Option, + + #[arg( + long, + env = "ET_FILE_LOG_SIZE", + help = t!("core_clap.file_log_size_mb").to_string() + )] + file_log_size: Option, + + #[arg( + long, + env = "ET_FILE_LOG_COUNT", + help = t!("core_clap.file_log_count").to_string() + )] + file_log_count: Option, +} + +#[derive(Parser, Debug)] +struct RpcPortalOptions { + #[arg( + short, + long, + env = "ET_RPC_PORTAL", + help = t!("core_clap.rpc_portal").to_string(), + )] + rpc_portal: Option, + + #[arg( + long, + env = "ET_RPC_PORTAL_WHITELIST", + value_delimiter = ',', + help = t!("core_clap.rpc_portal_whitelist").to_string(), + )] + rpc_portal_whitelist: Option>, } rust_i18n::i18n!("locales", fallback = "en"); @@ -626,14 +674,6 @@ impl Cli { Ok(listeners) } - - fn parse_rpc_portal(rpc_portal: String) -> anyhow::Result { - if let Ok(port) = rpc_portal.parse::() { - return Ok(format!("0.0.0.0:{}", port).parse().unwrap()); - } - - Ok(rpc_portal.parse()?) - } } impl NetworkOptions { @@ -741,24 +781,6 @@ impl NetworkOptions { add_proxy_network_to_config(n, cfg)?; } - let rpc_portal = if let Some(r) = &self.rpc_portal { - Cli::parse_rpc_portal(r.clone()) - .with_context(|| format!("failed to parse rpc portal: {}", r))? - } else if let Some(r) = cfg.get_rpc_portal() { - r - } else { - Cli::parse_rpc_portal("0".into())? - }; - cfg.set_rpc_portal(rpc_portal); - - if let Some(rpc_portal_whitelist) = &self.rpc_portal_whitelist { - let mut whitelist = cfg.get_rpc_portal_whitelist().unwrap_or_default(); - for cidr in rpc_portal_whitelist { - whitelist.push(*cidr); - } - cfg.set_rpc_portal_whitelist(Some(whitelist)); - } - if let Some(external_nodes) = self.external_node.as_ref() { let mut old_peers = cfg.get_peers(); old_peers.push(PeerConfig { @@ -822,8 +844,8 @@ impl NetworkOptions { port_forward.host_str().expect("local bind host is missing"), port_forward.port().expect("local bind port is missing") ) - .parse() - .unwrap_or_else(|_| panic!("failed to parse local bind addr {}", example_str)); + .parse() + .unwrap_or_else(|_| panic!("failed to parse local bind addr {}", example_str)); let dst_addr = port_forward .path_segments() @@ -891,7 +913,7 @@ impl NetworkOptions { compression ), } - .into(); + .into(); } f.bind_device = self.bind_device.unwrap_or(f.bind_device); f.enable_kcp_proxy = self.enable_kcp_proxy.unwrap_or(f.enable_kcp_proxy); @@ -908,6 +930,11 @@ impl NetworkOptions { f.enable_relay_foreign_network_kcp = self .enable_relay_foreign_network_kcp .unwrap_or(f.enable_relay_foreign_network_kcp); + f.disable_sym_hole_punching = self.disable_sym_hole_punching.unwrap_or(false); + // Configure tld_dns_zone: use provided value if set + if let Some(tld_dns_zone) = &self.tld_dns_zone { + f.tld_dns_zone = tld_dns_zone.clone(); + } cfg.set_flags(f); if !self.exit_nodes.is_empty() { @@ -922,6 +949,17 @@ impl NetworkOptions { old_udp_whitelist.extend(self.udp_whitelist.clone()); cfg.set_udp_whitelist(old_udp_whitelist); + if let Some(stun_servers) = &self.stun_servers { + let mut old_stun_servers = cfg.get_stun_servers().unwrap_or_default(); + old_stun_servers.extend(stun_servers.iter().cloned()); + cfg.set_stun_servers(Some(old_stun_servers)); + } + + if let Some(stun_servers_v6) = &self.stun_servers_v6 { + let mut old_stun_servers_v6 = cfg.get_stun_servers_v6().unwrap_or_default(); + old_stun_servers_v6.extend(stun_servers_v6.iter().cloned()); + cfg.set_stun_servers_v6(Some(old_stun_servers_v6)); + } Ok(()) } } @@ -938,13 +976,15 @@ impl LoggingConfigLoader for &LoggingOptions { level: self.file_log_level.clone(), dir: self.file_log_dir.clone(), file: None, + size_mb: self.file_log_size, + count: self.file_log_count, } } } #[cfg(target_os = "windows")] fn win_service_set_work_dir(service_name: &std::ffi::OsString) -> anyhow::Result<()> { - use crate::common::constants::WIN_SERVICE_WORK_DIR_REG_KEY; + // use crate::common::constants::WIN_SERVICE_WORK_DIR_REG_KEY; use winreg::enums::*; use winreg::RegKey; @@ -1012,6 +1052,18 @@ fn win_service_event_loop( }); } +fn parse_cli() -> Cli { + let mut cli = Cli::parse(); + // for --stun-servers="", we want vec![], but clap will give vec![""], hack for that + if let Some(stun_servers) = &mut cli.network_options.stun_servers { + stun_servers.retain(|s| !s.trim().is_empty()); + } + if let Some(stun_servers_v6) = &mut cli.network_options.stun_servers_v6 { + stun_servers_v6.retain(|s| !s.trim().is_empty()); + } + cli +} + #[cfg(target_os = "windows")] fn win_service_main(arg: Vec) { use std::sync::Arc; @@ -1022,7 +1074,7 @@ fn win_service_main(arg: Vec) { _ = win_service_set_work_dir(&arg[0]); - let cli = Cli::parse(); + let cli = parse_cli(); let stop_notify_send = Arc::new(Notify::new()); let stop_notify_recv = Arc::clone(&stop_notify_send); @@ -1054,7 +1106,17 @@ fn win_service_main(arg: Vec) { } pub(crate) async fn run_main(cli: Cli) -> anyhow::Result<()> { - init_logger(&cli.logging_options, false)?; + init_logger(&cli.logging_options, true)?; + + let manager = Arc::new(NetworkInstanceManager::new()); + + let _rpc_server = ApiRpcServer::new( + cli.rpc_portal_options.rpc_portal, + cli.rpc_portal_options.rpc_portal_whitelist, + manager.clone(), + )? + .serve() + .await?; if cli.config_server.is_some() { set_default_machine_id(cli.machine_id); @@ -1065,8 +1127,8 @@ pub(crate) async fn run_main(cli: Cli) -> anyhow::Result<()> { "udp://config-server.easytier.cn:22020/{}", config_server_url_s ) - .parse() - .unwrap(), + .parse() + .unwrap(), }; let mut c_url = config_server_url.clone(); @@ -1104,18 +1166,25 @@ pub(crate) async fn run_main(cli: Cli) -> anyhow::Result<()> { create_connector_by_url(c_url.as_str(), &global_ctx, IpVersion::Both).await?, token.to_string(), hostname, + manager, ); tokio::signal::ctrl_c().await.unwrap(); return Ok(()); } - let manager = NetworkInstanceManager::new(); let mut crate_cli_network = cli.config_file.is_none() || cli.network_options.network_name.is_some(); if let Some(config_files) = cli.config_file { let config_file_count = config_files.len(); for config_file in config_files { - let mut cfg = TomlConfigLoader::new(&config_file) - .with_context(|| format!("failed to load config file: {:?}", config_file))?; + let mut cfg = if config_file == PathBuf::from("-") { + let mut stdin = String::new(); + _ = tokio::io::stdin().read_to_string(&mut stdin).await?; + TomlConfigLoader::new_from_str(stdin.as_str()) + .with_context(|| "failed to load config from stdin")? + } else { + TomlConfigLoader::new(&config_file) + .with_context(|| format!("failed to load config file: {:?}", config_file))? + }; if cli.network_options.can_merge(&cfg, config_file_count) { cli.network_options.merge_into(&mut cfg).with_context(|| { @@ -1151,7 +1220,7 @@ pub(crate) async fn run_main(cli: Cli) -> anyhow::Result<()> { tokio::select! { _ = manager.wait() => { - let infos = manager.collect_network_infos()?; + let infos = manager.collect_network_infos().await?; let errs = infos .into_values() .filter_map(|info| info.error_msg) @@ -1212,7 +1281,7 @@ fn memory_monitor() { } #[tokio::main(flavor = "current_thread")] -pub(crate) async fn main() -> ExitCode { +async fn main() -> ExitCode { let locale = sys_locale::get_locale().unwrap_or_else(|| String::from("en-US")); rust_i18n::set_locale(&locale); setup_panic_handler(); @@ -1236,7 +1305,24 @@ pub(crate) async fn main() -> ExitCode { set_prof_active(true); let _monitor = std::thread::spawn(memory_monitor); - let cli = Cli::parse(); + let cli = parse_cli(); + + if let Some(shell) = cli.gen_autocomplete { + let mut cmd = Cli::command(); + print_completions(shell, &mut cmd, "easytier-core"); + return ExitCode::SUCCESS; + } + + // Verify configurations + if cli.check_config { + if let Err(e) = validate_config(&cli).await { + eprintln!("Config validation failed: {:?}", e); + return ExitCode::FAILURE; + } else { + return ExitCode::SUCCESS; + } + } + if let Some(shell) = cli.gen_autocomplete { let mut cmd = Cli::command(); print_completions(shell, &mut cmd, "easytier-core"); @@ -1256,3 +1342,25 @@ pub(crate) async fn main() -> ExitCode { ExitCode::from(ret_code) } + +async fn validate_config(cli: &Cli) -> anyhow::Result<()> { + // Check if config file is provided + let config_files = cli + .config_file + .as_ref() + .ok_or_else(|| anyhow::anyhow!("--config-file is required when using --check-config"))?; + + for config_file in config_files { + if config_file == &PathBuf::from("-") { + let mut stdin = String::new(); + _ = tokio::io::stdin().read_to_string(&mut stdin).await?; + TomlConfigLoader::new_from_str(stdin.as_str()) + .with_context(|| "config source: stdin")?; + } else { + TomlConfigLoader::new(config_file) + .with_context(|| format!("config source: {:?}", config_file))?; + }; + } + + Ok(()) +} diff --git a/easytier/src/gateway/icmp_proxy.rs b/easytier/src/gateway/icmp_proxy.rs index fc1f3dbc1..a9738eec8 100644 --- a/easytier/src/gateway/icmp_proxy.rs +++ b/easytier/src/gateway/icmp_proxy.rs @@ -274,16 +274,18 @@ impl IcmpProxy { } let peer_manager = self.peer_manager.clone(); + let is_latency_first = self.global_ctx.get_flags().latency_first; self.tasks.lock().await.spawn( async move { - while let Some(msg) = receiver.recv().await { - let hdr = msg.peer_manager_header().unwrap(); + while let Some(mut msg) = receiver.recv().await { + let hdr = msg.mut_peer_manager_header().unwrap(); + hdr.set_latency_first(is_latency_first); let to_peer_id = hdr.to_peer_id.into(); let Some(pm) = peer_manager.upgrade() else { tracing::warn!("peer manager is gone, icmp proxy send loop exit"); return; }; - let ret = pm.send_msg(msg, to_peer_id).await; + let ret = pm.send_msg_for_proxy(msg, to_peer_id).await; if ret.is_err() { tracing::error!("send icmp packet to peer failed: {:?}", ret); } diff --git a/easytier/src/gateway/kcp_proxy.rs b/easytier/src/gateway/kcp_proxy.rs index 2c3485636..adaa7f6e7 100644 --- a/easytier/src/gateway/kcp_proxy.rs +++ b/easytier/src/gateway/kcp_proxy.rs @@ -40,7 +40,7 @@ use crate::{ peers::{acl_filter::AclFilter, peer_manager::PeerManager, NicPacketFilter, PeerPacketFilter}, proto::{ acl::{Action, ChainType, Protocol}, - cli::{ + api::instance::{ ListTcpProxyEntryRequest, ListTcpProxyEntryResponse, TcpProxyEntry, TcpProxyEntryState, TcpProxyEntryTransportType, TcpProxyRpc, }, @@ -107,7 +107,7 @@ async fn handle_kcp_output( let mut packet = ZCPacket::new_with_payload(&packet.inner().freeze()); packet.fill_peer_manager_hdr(peer_mgr.my_peer_id(), dst_peer_id, packet_type); - if let Err(e) = peer_mgr.send_msg(packet, dst_peer_id).await { + if let Err(e) = peer_mgr.send_msg_for_proxy(packet, dst_peer_id).await { tracing::error!("failed to send kcp packet to peer: {:?}", e); } } @@ -266,6 +266,11 @@ impl> NicPacketFilt .check_dst_allow_kcp_input(&ip_packet.get_destination()) .await { + tracing::warn!( + "{:?} proxy src: dst {} not allow kcp input", + self.get_tcp_proxy().get_transport_type(), + ip_packet.get_destination() + ); return false; } } else { @@ -288,6 +293,12 @@ impl> NicPacketFilt if ip_packet.get_source() != my_ipv4.address() && !self.get_tcp_proxy().is_smoltcp_enabled() { + tracing::warn!( + "{:?} nat 2 nat packet, src: {} dst: {} not allow kcp input", + self.get_tcp_proxy().get_transport_type(), + ip_packet.get_source(), + ip_packet.get_destination() + ); return false; } }; @@ -440,12 +451,13 @@ impl KcpProxyDst { } } - #[tracing::instrument(ret)] + #[tracing::instrument(ret, skip(route))] async fn handle_one_in_stream( kcp_stream: KcpStream, global_ctx: ArcGlobalCtx, proxy_entries: Arc>, cidr_set: Arc, + route: Arc, ) -> Result<()> { let mut conn_data = kcp_stream.conn_data().clone(); let parsed_conn_data = KcpConnData::decode(&mut conn_data) @@ -481,6 +493,13 @@ impl KcpProxyDst { proxy_entries.remove(&conn_id); } + let src_ip = src_socket.ip(); + let dst_ip = dst_socket.ip(); + let (src_groups, dst_groups) = tokio::join!( + route.get_peer_groups_by_ip(&src_ip), + route.get_peer_groups_by_ip(&dst_ip) + ); + let send_to_self = Some(dst_socket.ip()) == global_ctx.get_ipv4().map(|ip| IpAddr::V4(ip.address())); @@ -491,12 +510,14 @@ impl KcpProxyDst { let acl_handler = ProxyAclHandler { acl_filter: global_ctx.get_acl_filter().clone(), packet_info: PacketInfo { - src_ip: src_socket.ip(), - dst_ip: dst_socket.ip(), + src_ip, + dst_ip, src_port: Some(src_socket.port()), dst_port: Some(dst_socket.port()), protocol: Protocol::Tcp, packet_size: conn_data.len(), + src_groups, + dst_groups, }, chain_type: if send_to_self { ChainType::Inbound @@ -530,6 +551,7 @@ impl KcpProxyDst { let global_ctx = self.peer_manager.get_global_ctx().clone(); let proxy_entries = self.proxy_entries.clone(); let cidr_set = self.cidr_set.clone(); + let route = Arc::new(self.peer_manager.get_route()); self.tasks.spawn(async move { while let Ok(conn) = kcp_endpoint.accept().await { let stream = KcpStream::new(&kcp_endpoint, conn) @@ -539,9 +561,16 @@ impl KcpProxyDst { let global_ctx = global_ctx.clone(); let proxy_entries = proxy_entries.clone(); let cidr_set = cidr_set.clone(); + let route = route.clone(); tokio::spawn(async move { - let _ = Self::handle_one_in_stream(stream, global_ctx, proxy_entries, cidr_set) - .await; + let _ = Self::handle_one_in_stream( + stream, + global_ctx, + proxy_entries, + cidr_set, + route, + ) + .await; }); } }); diff --git a/easytier/src/gateway/quic_proxy.rs b/easytier/src/gateway/quic_proxy.rs index d2aa63251..7ccca2a45 100644 --- a/easytier/src/gateway/quic_proxy.rs +++ b/easytier/src/gateway/quic_proxy.rs @@ -22,7 +22,7 @@ use crate::gateway::tcp_proxy::{NatDstConnector, NatDstTcpConnector, TcpProxy}; use crate::gateway::CidrSet; use crate::peers::peer_manager::PeerManager; use crate::proto::acl::{ChainType, Protocol}; -use crate::proto::cli::{ +use crate::proto::api::instance::{ ListTcpProxyEntryRequest, ListTcpProxyEntryResponse, TcpProxyEntry, TcpProxyEntryState, TcpProxyEntryTransportType, TcpProxyRpc, }; @@ -200,6 +200,11 @@ impl TcpProxyForKcpSrcTrait for TcpProxyForQUICSrc { let Some(peer_info) = peer_map.get_route_peer_info(dst_peer_id).await else { return false; }; + tracing::debug!( + "check dst {} allow quic input, peer info: {:?}", + dst_ip, + peer_info + ); let Some(quic_port) = peer_info.quic_port else { return false; }; @@ -247,10 +252,14 @@ pub struct QUICProxyDst { endpoint: Arc, proxy_entries: Arc>, tasks: Arc>>, + route: Arc, } impl QUICProxyDst { - pub fn new(global_ctx: ArcGlobalCtx) -> Result { + pub fn new( + global_ctx: ArcGlobalCtx, + route: Arc, + ) -> Result { let _g = global_ctx.net_ns.guard(); let (endpoint, _) = make_server_endpoint("0.0.0.0:0".parse().unwrap()) .map_err(|e| anyhow::anyhow!("failed to create QUIC endpoint: {}", e))?; @@ -261,6 +270,7 @@ impl QUICProxyDst { endpoint: Arc::new(endpoint), proxy_entries: Arc::new(DashMap::new()), tasks, + route, }) } @@ -270,6 +280,7 @@ impl QUICProxyDst { let ctx = self.global_ctx.clone(); let cidr_set = Arc::new(CidrSet::new(ctx.clone())); let proxy_entries = self.proxy_entries.clone(); + let route = self.route.clone(); let task = async move { loop { @@ -289,6 +300,7 @@ impl QUICProxyDst { ctx.clone(), cidr_set.clone(), proxy_entries.clone(), + route.clone(), )); } None => { @@ -312,6 +324,7 @@ impl QUICProxyDst { ctx: Arc, cidr_set: Arc, proxy_entries: Arc>, + route: Arc, ) { let remote_addr = conn.remote_address(); defer!( @@ -319,7 +332,14 @@ impl QUICProxyDst { ); let ret = timeout( std::time::Duration::from_secs(10), - Self::handle_connection(conn, ctx, cidr_set, remote_addr, proxy_entries.clone()), + Self::handle_connection( + conn, + ctx, + cidr_set, + remote_addr, + proxy_entries.clone(), + route, + ), ) .await; @@ -348,6 +368,7 @@ impl QUICProxyDst { cidr_set: Arc, proxy_entry_key: SocketAddr, proxy_entries: Arc>, + route: Arc, ) -> Result<(QUICStream, TcpStream, ProxyAclHandler)> { let conn = incoming.await.with_context(|| "accept failed")?; let addr = conn.remote_address(); @@ -379,6 +400,13 @@ impl QUICProxyDst { dst_socket.set_ip(real_ip); } + let src_ip = addr.ip(); + let dst_ip = *dst_socket.ip(); + let (src_groups, dst_groups) = tokio::join!( + route.get_peer_groups_by_ip(&src_ip), + route.get_peer_groups_by_ipv4(&dst_ip) + ); + let send_to_self = Some(*dst_socket.ip()) == ctx.get_ipv4().map(|ip| ip.address()); if send_to_self && ctx.no_tun() { dst_socket = format!("127.0.0.1:{}", dst_socket.port()).parse().unwrap(); @@ -398,12 +426,14 @@ impl QUICProxyDst { let acl_handler = ProxyAclHandler { acl_filter: ctx.get_acl_filter().clone(), packet_info: PacketInfo { - src_ip: addr.ip(), - dst_ip: (*dst_socket.ip()).into(), + src_ip, + dst_ip: dst_ip.into(), src_port: Some(addr.port()), dst_port: Some(dst_socket.port()), protocol: Protocol::Tcp, packet_size: len as usize, + src_groups, + dst_groups, }, chain_type: if send_to_self { ChainType::Inbound diff --git a/easytier/src/gateway/socks5.rs b/easytier/src/gateway/socks5.rs index 84b2cdb51..8112e28e9 100644 --- a/easytier/src/gateway/socks5.rs +++ b/easytier/src/gateway/socks5.rs @@ -1,6 +1,9 @@ use std::{ net::{IpAddr, Ipv4Addr, SocketAddr}, - sync::{Arc, Weak}, + sync::{ + atomic::{AtomicBool, Ordering}, + Arc, Weak, + }, time::{Duration, Instant}, }; @@ -38,7 +41,7 @@ use tokio::{ io::{AsyncRead, AsyncWrite}, net::{TcpListener, TcpSocket, UdpSocket}, select, - sync::{mpsc, Mutex}, + sync::{mpsc, Mutex, Notify}, task::JoinSet, time::timeout, }; @@ -302,8 +305,7 @@ impl Socks5ServerNet { tracing::error!("send to smoltcp stack failed: {:?}", e); } } - tracing::error!("smoltcp stack sink exited"); - panic!("smoltcp stack sink exited"); + tracing::warn!("smoltcp stack sink exited"); }); forward_tasks.spawn(async move { @@ -324,8 +326,7 @@ impl Socks5ServerNet { tracing::error!("send to peer failed in smoltcp sender: {:?}", e); } } - tracing::error!("smoltcp stack stream exited"); - panic!("smoltcp stack stream exited"); + tracing::warn!("smoltcp stack stream exited"); }); let interface_config = smoltcp::iface::Config::new(smoltcp::wire::HardwareAddress::Ip); @@ -418,12 +419,21 @@ pub struct Socks5Server { kcp_endpoint: Mutex>>, - cancel_tokens: DashMap, + socks5_enabled: Arc, + cancel_tokens: Arc>, + port_forward_list_change_notifier: Arc, } #[async_trait::async_trait] impl PeerPacketFilter for Socks5Server { async fn try_process_packet_from_peer(&self, packet: ZCPacket) -> Option { + if self.cancel_tokens.is_empty() + && self.entries.is_empty() + && !self.socks5_enabled.load(Ordering::Relaxed) + { + return Some(packet); + } + let hdr = packet.peer_manager_header().unwrap(); if hdr.packet_type != PacketType::Data as u8 { return Some(packet); @@ -438,7 +448,9 @@ impl PeerPacketFilter for Socks5Server { let entry_key = match ipv4.get_next_level_protocol() { IpNextHeaderProtocols::Tcp => { - let tcp_packet = TcpPacket::new(ipv4.payload()).unwrap(); + let Some(tcp_packet) = TcpPacket::new(ipv4.payload()) else { + return Some(packet); + }; Socks5Entry { dst: SocketAddr::new(ipv4.get_source().into(), tcp_packet.get_source()), src: SocketAddr::new( @@ -467,7 +479,9 @@ impl PeerPacketFilter for Socks5Server { return Some(packet); } - let udp_packet = UdpPacket::new(ipv4.payload()).unwrap(); + let Some(udp_packet) = UdpPacket::new(ipv4.payload()) else { + return Some(packet); + }; Socks5Entry { dst: SocketAddr::new(ipv4.get_source().into(), udp_packet.get_source()), src: SocketAddr::new( @@ -519,7 +533,9 @@ impl Socks5Server { kcp_endpoint: Mutex::new(None), - cancel_tokens: DashMap::new(), + socks5_enabled: Arc::new(AtomicBool::new(false)), + cancel_tokens: Arc::new(DashMap::new()), + port_forward_list_change_notifier: Arc::new(Notify::new()), }) } @@ -531,9 +547,18 @@ impl Socks5Server { let entries = self.entries.clone(); let tcp_forward_task = self.tcp_forward_task.clone(); let udp_client_map = self.udp_client_map.clone(); + let cancel_tokens = self.cancel_tokens.clone(); + let port_forward_list_change_notifier = self.port_forward_list_change_notifier.clone(); + let socks5_enabled = self.socks5_enabled.clone(); self.tasks.lock().unwrap().spawn(async move { let mut prev_ipv4 = None; loop { + if cancel_tokens.is_empty() && !socks5_enabled.load(Ordering::Relaxed) { + let _ = net.lock().await.take(); + port_forward_list_change_notifier.notified().await; + continue; + } + let mut event_recv = global_ctx.subscribe(); let cur_ipv4 = global_ctx.get_ipv4(); @@ -570,7 +595,6 @@ impl Socks5Server { kcp_endpoint: Option>, ) -> Result<(), Error> { *self.kcp_endpoint.lock().await = kcp_endpoint; - let mut need_start = false; if let Some(proxy_url) = self.global_ctx.config.get_socks5_portal() { let bind_addr = format!( "{}:{}", @@ -598,22 +622,18 @@ impl Socks5Server { } }); + self.socks5_enabled.store(true, Ordering::Relaxed); join_joinset_background(self.tasks.clone(), "socks5 server".to_string()); - - need_start = true; }; let cfgs = self.global_ctx.config.get_port_forwards(); self.reload_port_forwards(&cfgs).await?; - need_start = need_start || !cfgs.is_empty(); - if need_start { - self.peer_manager - .add_packet_process_pipeline(Box::new(self.clone())) - .await; + self.peer_manager + .add_packet_process_pipeline(Box::new(self.clone())) + .await; - self.run_net_update_task().await; - } + self.run_net_update_task().await; Ok(()) } @@ -635,6 +655,7 @@ impl Socks5Server { self.add_port_forward(cfg.clone()).await?; } } + self.port_forward_list_change_notifier.notify_one(); Ok(()) } diff --git a/easytier/src/gateway/tcp_proxy.rs b/easytier/src/gateway/tcp_proxy.rs index 12ed94fd2..c60996324 100644 --- a/easytier/src/gateway/tcp_proxy.rs +++ b/easytier/src/gateway/tcp_proxy.rs @@ -24,9 +24,10 @@ use crate::common::error::Result; use crate::common::global_ctx::{ArcGlobalCtx, GlobalCtx}; use crate::common::join_joinset_background; +use crate::common::stats_manager::{LabelSet, LabelType, MetricName}; use crate::peers::peer_manager::PeerManager; use crate::peers::{NicPacketFilter, PeerPacketFilter}; -use crate::proto::cli::{ +use crate::proto::api::instance::{ ListTcpProxyEntryRequest, ListTcpProxyEntryResponse, TcpProxyEntry, TcpProxyEntryState, TcpProxyEntryTransportType, TcpProxyRpc, }; @@ -70,27 +71,12 @@ impl NatDstConnector for NatDstTcpConnector { return Err(e.into()); } }; - if let Err(e) = socket.set_nodelay(true) { - tracing::warn!("set_nodelay failed, ignore it: {:?}", e); - } - - const TCP_KEEPALIVE_TIME: Duration = Duration::from_secs(5); - const TCP_KEEPALIVE_INTERVAL: Duration = Duration::from_secs(2); - const TCP_KEEPALIVE_RETRIES: u32 = 2; let stream = timeout(Duration::from_secs(10), socket.connect(nat_dst)) .await? .with_context(|| format!("connect to nat dst failed: {:?}", nat_dst))?; - let ka = TcpKeepalive::new() - .with_time(TCP_KEEPALIVE_TIME) - .with_interval(TCP_KEEPALIVE_INTERVAL); - - #[cfg(not(target_os = "windows"))] - let ka = ka.with_retries(TCP_KEEPALIVE_RETRIES); - - let sf = SockRef::from(&stream); - sf.set_tcp_keepalive(&ka)?; + prepare_kernel_tcp_socket(&stream)?; Ok(stream) } @@ -279,11 +265,33 @@ enum ProxyTcpListener { SmolTcpListener(SmolTcpListener), } +fn prepare_kernel_tcp_socket(stream: &TcpStream) -> Result<()> { + const TCP_KEEPALIVE_TIME: Duration = Duration::from_secs(5); + const TCP_KEEPALIVE_INTERVAL: Duration = Duration::from_secs(2); + const TCP_KEEPALIVE_RETRIES: u32 = 2; + + let ka = TcpKeepalive::new() + .with_time(TCP_KEEPALIVE_TIME) + .with_interval(TCP_KEEPALIVE_INTERVAL); + + #[cfg(not(target_os = "windows"))] + let ka = ka.with_retries(TCP_KEEPALIVE_RETRIES); + + let sf = SockRef::from(&stream); + sf.set_tcp_keepalive(&ka)?; + if let Err(e) = sf.set_nodelay(true) { + tracing::warn!("set_nodelay failed, ignore it: {:?}", e); + } + + Ok(()) +} + impl ProxyTcpListener { pub async fn accept(&mut self) -> Result<(ProxyTcpStream, SocketAddr)> { match self { Self::KernelTcpListener(listener) => { let (stream, addr) = listener.accept().await?; + prepare_kernel_tcp_socket(&stream)?; Ok((ProxyTcpStream::KernelTcpStream(stream), addr)) } #[cfg(feature = "smoltcp")] @@ -722,6 +730,21 @@ impl TcpProxy { nat_entry.real_dst }; + global_ctx + .stats_manager() + .get_counter( + MetricName::TcpProxyConnect, + LabelSet::new() + .with_label_type(LabelType::Protocol( + connector.transport_type().as_str_name().to_string(), + )) + .with_label_type(LabelType::DstIp(nat_dst.ip().to_string())) + .with_label_type(LabelType::MappedDstIp( + nat_entry.mapped_dst.ip().to_string(), + )), + ) + .inc(); + let _guard = global_ctx.net_ns.guard(); let Ok(dst_tcp_stream) = connector.connect(nat_entry.src, nat_dst).await else { tracing::error!("connect to dst failed: {:?}", nat_entry); diff --git a/easytier/src/gateway/tokio_smoltcp/socket.rs b/easytier/src/gateway/tokio_smoltcp/socket.rs index ffd574153..414093b93 100644 --- a/easytier/src/gateway/tokio_smoltcp/socket.rs +++ b/easytier/src/gateway/tokio_smoltcp/socket.rs @@ -118,10 +118,19 @@ impl TcpStream { ) -> io::Result { let handle = reactor.socket_allocator().new_tcp_socket(); - reactor - .get_socket::(*handle) - .connect(&mut reactor.context(), remote_endpoint, local_endpoint) - .map_err(map_err)?; + // see https://github.com/spacemeowx2/tokio-smoltcp/pull/12 + let connect_result = { + // Issue #11. We must lock the context before we call connect to + // avoid lock inversion deadlocks, but drop it before constructing + // the TcpStream to avoid a second mutable borror of the reactor. + let mut context = reactor.context(); + reactor.get_socket::(*handle).connect( + &mut context, + remote_endpoint, + local_endpoint, + ) + }; + connect_result.map_err(map_err)?; let local_addr = ep2sa(&local_endpoint); let peer_addr = ep2sa(&remote_endpoint); @@ -147,11 +156,14 @@ impl TcpStream { } let (peer_addr, local_addr) = { let socket = reactor.get_socket::(*listener.handle); - ( - // should be Some, because the state is Established - ep2sa(&socket.remote_endpoint().unwrap()), - ep2sa(&socket.local_endpoint().unwrap()), - ) + match (socket.remote_endpoint(), socket.local_endpoint()) { + (Some(remote_endpoint), Some(local_endpoint)) => ( + // should be Some, because the state is Established + ep2sa(&remote_endpoint), + ep2sa(&local_endpoint), + ), + _ => return Err(io::ErrorKind::NotConnected.into()), + } }; Ok(( diff --git a/easytier/src/gateway/udp_proxy.rs b/easytier/src/gateway/udp_proxy.rs index 44007491a..c71090508 100644 --- a/easytier/src/gateway/udp_proxy.rs +++ b/easytier/src/gateway/udp_proxy.rs @@ -436,11 +436,14 @@ impl UdpProxy { // forward packets to peer manager let mut receiver = self.receiver.lock().await.take().unwrap(); let peer_manager = self.peer_manager.clone(); + let is_latency_first = self.global_ctx.get_flags().latency_first; self.tasks.lock().await.spawn(async move { - while let Ok(msg) = receiver.recv().await { - let to_peer_id: PeerId = msg.peer_manager_header().unwrap().to_peer_id.get(); + while let Ok(mut msg) = receiver.recv().await { + let hdr = msg.mut_peer_manager_header().unwrap(); + hdr.set_latency_first(is_latency_first); + let to_peer_id = hdr.to_peer_id.into(); tracing::trace!(?msg, ?to_peer_id, "udp nat packet response send"); - let ret = peer_manager.send_msg(msg, to_peer_id).await; + let ret = peer_manager.send_msg_for_proxy(msg, to_peer_id).await; if ret.is_err() { tracing::error!("send icmp packet to peer failed: {:?}", ret); } diff --git a/easytier/src/helper.rs b/easytier/src/helper.rs index b1b5bf7c9..7b641f91f 100644 --- a/easytier/src/helper.rs +++ b/easytier/src/helper.rs @@ -1,19 +1,19 @@ -use crate::easytier_core::{run_main, Cli}; use crate::peers::peer_manager::PeerManager; use crate::peers::rpc_service::PeerManagerRpcService; -use crate::proto::cli::{list_peer_route_pair, NodeInfo, PeerManageRpc, ShowNodeInfoRequest}; use crate::proto::rpc_types::controller::BaseController; use crate::utils::{cost_to_str, float_to_str, PeerRoutePair}; use cidr::Ipv4Inet; -use clap::Parser; use humansize::format_size; use lazy_static::lazy_static; use std::alloc::{alloc_zeroed, Layout}; use std::ptr; use std::str::FromStr; use std::sync::Arc; +use clap::Parser; use tokio::sync::RwLock; use tokio_util::sync::CancellationToken; +use crate::easytier_core::{run_main, Cli}; +use crate::proto::api::instance::{list_peer_route_pair, NodeInfo, PeerManageRpc, ShowNodeInfoRequest}; lazy_static! { pub static ref g_peermanager: RwLock>> = RwLock::new(None); diff --git a/easytier/src/instance/dns_server/client_instance.rs b/easytier/src/instance/dns_server/client_instance.rs index b27d6a86f..f1d5f35b0 100644 --- a/easytier/src/instance/dns_server/client_instance.rs +++ b/easytier/src/instance/dns_server/client_instance.rs @@ -5,7 +5,7 @@ use tokio::task::JoinSet; use crate::{ peers::peer_manager::PeerManager, proto::{ - cli::Route, + api::instance::Route, common::Void, magic_dns::{ HandshakeRequest, MagicDnsServerRpc, MagicDnsServerRpcClientFactory, @@ -17,7 +17,7 @@ use crate::{ tunnel::tcp::TcpTunnelConnector, }; -use super::{DEFAULT_ET_DNS_ZONE, MAGIC_DNS_INSTANCE_ADDR}; +use super::MAGIC_DNS_INSTANCE_ADDR; pub struct MagicDnsClientInstance { rpc_client: StandAloneClient, @@ -68,9 +68,11 @@ impl MagicDnsClientInstance { ipv4_addr: ctx.get_ipv4().map(Into::into), ..Default::default() }); + // Use configured tld_dns_zone (always set by default) + let flags = ctx.config.get_flags(); let req = UpdateDnsRecordRequest { routes, - zone: DEFAULT_ET_DNS_ZONE.to_string(), + zone: flags.tld_dns_zone.clone(), }; tracing::debug!( "MagicDnsClientInstance::update_dns_task: update dns records: {:?}", diff --git a/easytier/src/instance/dns_server/config.rs b/easytier/src/instance/dns_server/config.rs index e249ecfe7..d00f8a372 100644 --- a/easytier/src/instance/dns_server/config.rs +++ b/easytier/src/instance/dns_server/config.rs @@ -68,7 +68,7 @@ pub struct Record { } impl Record { - fn name(&self) -> anyhow::Result { + pub fn name(&self) -> anyhow::Result { let name = rr::Name::from_str(self.name.as_str())?; Ok(name) } diff --git a/easytier/src/instance/dns_server/server_instance.rs b/easytier/src/instance/dns_server/server_instance.rs index c25d21b67..638adea66 100644 --- a/easytier/src/instance/dns_server/server_instance.rs +++ b/easytier/src/instance/dns_server/server_instance.rs @@ -1,4 +1,4 @@ -// single-instance server in one machine, every easytier instance that has ip address and tun device will try create a server instance. +// single-instance server in one machine, every easytier instance that has ip address and tun device will try to create a server instance. // magic dns client will connect to this server to update the dns records. // magic dns server will add the dns server ip address to the tun device, and forward the dns request to the dns server @@ -6,22 +6,12 @@ // magic dns client will establish a long live tcp connection to the magic dns server, and when the server stops or crashes, // all the clients will exit and let the easytier instance to launch a new server instance. -use std::{collections::BTreeMap, net::Ipv4Addr, str::FromStr, sync::Arc, time::Duration}; - -use anyhow::Context; -use cidr::Ipv4Inet; -use dashmap::DashMap; -use hickory_proto::rr::LowerName; -use multimap::MultiMap; -use pnet::packet::{ - icmp::{self, IcmpTypes, MutableIcmpPacket}, - ip::IpNextHeaderProtocols, - ipv4::{self, MutableIpv4Packet}, - tcp::{self, MutableTcpPacket}, - udp::{self, MutableUdpPacket}, - MutablePacket, +use super::{ + config::{GeneralConfigBuilder, RunConfigBuilder}, + server::Server, + system_config::{OSConfig, SystemConfig}, + MAGIC_DNS_INSTANCE_ADDR, }; - use crate::{ common::{ ifcfg::{IfConfiger, IfConfiguerTrait}, @@ -30,11 +20,10 @@ use crate::{ instance::dns_server::{ config::{Record, RecordBuilder, RecordType}, server::build_authority, - DEFAULT_ET_DNS_ZONE, }, peers::{peer_manager::PeerManager, NicPacketFilter}, proto::{ - cli::Route, + api::instance::Route, common::{TunnelInfo, Void}, magic_dns::{ dns_record::{self}, @@ -46,13 +35,27 @@ use crate::{ }, tunnel::{packet_def::ZCPacket, tcp::TcpTunnelListener}, }; - -use super::{ - config::{GeneralConfigBuilder, RunConfigBuilder}, - server::Server, - system_config::{OSConfig, SystemConfig}, - MAGIC_DNS_INSTANCE_ADDR, +use anyhow::Context; +use cidr::Ipv4Inet; +use dashmap::DashMap; +use hickory_proto::rr::LowerName; +use hickory_proto::serialize::binary::{BinDecodable, BinEncoder}; +use hickory_server::authority::{MessageRequest, MessageResponse}; +use hickory_server::server::{Request, RequestHandler, ResponseHandler, ResponseInfo}; +use multimap::MultiMap; +use pnet::packet::icmp::{IcmpTypes, MutableIcmpPacket}; +use pnet::packet::ipv4::Ipv4Packet; +use pnet::packet::udp::UdpPacket; +use pnet::packet::{ + icmp, + ip::IpNextHeaderProtocols, + ipv4::{self, MutableIpv4Packet}, + udp::{self, MutableUdpPacket}, + MutablePacket, Packet, }; +use std::net::{SocketAddr, SocketAddrV4}; +use std::sync::Mutex; +use std::{collections::BTreeMap, io, net::Ipv4Addr, str::FromStr, sync::Arc, time::Duration}; static NIC_PIPELINE_NAME: &str = "magic_dns_server"; @@ -111,6 +114,12 @@ impl MagicDnsServerInstanceData { .ttl(Duration::from_secs(1)) .build()?; + // check record name valid for dns + if let Err(e) = record.name() { + tracing::error!("Invalid subdomain label: {}", e); + continue; + } + records.push(record); } @@ -130,7 +139,7 @@ impl MagicDnsServerInstanceData { self.dns_server .upsert( LowerName::from_str(zone) - .with_context(|| "Invalid zone name, expect fomat like \"et.net.\"")?, + .with_context(|| "Invalid zone name, expect format like \"et.net.\"")?, Arc::new(authority), ) .await; @@ -173,6 +182,14 @@ impl MagicDnsServerRpc for MagicDnsServerInstanceData { Ok(Default::default()) } + async fn heartbeat( + &self, + _ctrl: Self::Controller, + _input: Void, + ) -> crate::proto::rpc_types::error::Result { + Ok(Default::default()) + } + async fn update_dns_record( &self, ctrl: Self::Controller, @@ -217,97 +234,203 @@ impl MagicDnsServerRpc for MagicDnsServerInstanceData { } Ok(GetDnsRecordResponse { records: ret }) } +} - async fn heartbeat( - &self, - _ctrl: Self::Controller, - _input: Void, - ) -> crate::proto::rpc_types::error::Result { - Ok(Default::default()) - } +// This should only be used for UDP response. +// For other protocols, the variable `max_size` in `send_response` should be u16::MAX. +#[derive(Clone)] +struct ResponseWrapper { + response: Arc>>, } +trait RecordIter<'a>: Iterator + Send + 'a {} +impl<'a, T> RecordIter<'a> for T where T: Iterator + Send + 'a {} + #[async_trait::async_trait] -impl NicPacketFilter for MagicDnsServerInstanceData { - async fn try_process_packet_from_nic(&self, zc_packet: &mut ZCPacket) -> bool { - let data = zc_packet.mut_payload(); - let mut ip_packet = MutableIpv4Packet::new(data).unwrap(); - if ip_packet.get_version() != 4 || ip_packet.get_destination() != self.fake_ip { - return false; - } +impl ResponseHandler for ResponseWrapper { + async fn send_response<'a>( + &mut self, + response: MessageResponse< + '_, + 'a, + impl RecordIter<'a>, + impl RecordIter<'a>, + impl RecordIter<'a>, + impl RecordIter<'a>, + >, + ) -> io::Result { + let mut buffer = self + .response + .lock() + .map_err(|_| io::Error::other("lock poisoned"))?; + + let mut encoder = BinEncoder::new(&mut buffer); + + // `max_size` should be u16::MAX for protocol other than UDP. + let max_size = if let Some(edns) = response.get_edns() { + edns.max_payload() + } else { + hickory_proto::udp::MAX_RECEIVE_BUFFER_SIZE as u16 + }; - match ip_packet.get_next_level_protocol() { - IpNextHeaderProtocols::Udp => { - let Some(dns_udp_addr) = self.dns_server.udp_local_addr() else { - return false; - }; - - let Some(mut udp_packet) = MutableUdpPacket::new(ip_packet.payload_mut()) else { - return false; - }; - if udp_packet.get_destination() == 53 { - // for dns request - udp_packet.set_destination(dns_udp_addr.port()); - } else if udp_packet.get_source() == dns_udp_addr.port() { - // for dns response - udp_packet.set_source(53); - } else { - return false; - } - udp_packet.set_checksum(udp::ipv4_checksum( - &udp_packet.to_immutable(), - &self.fake_ip, - &self.tun_ip, - )); - } + encoder.set_max_size(max_size); + response + .destructive_emit(&mut encoder) + .map_err(io::Error::other) + } +} - IpNextHeaderProtocols::Tcp => { - let Some(dns_tcp_addr) = self.dns_server.tcp_local_addr() else { - return false; - }; - - let Some(mut tcp_packet) = MutableTcpPacket::new(ip_packet.payload_mut()) else { - return false; - }; - if tcp_packet.get_destination() == 53 { - // for dns request - tcp_packet.set_destination(dns_tcp_addr.port()); - } else if tcp_packet.get_source() == dns_tcp_addr.port() { - // for dns response - tcp_packet.set_source(53); - } else { - return false; - } - tcp_packet.set_checksum(tcp::ipv4_checksum( - &tcp_packet.to_immutable(), - &self.fake_ip, - &self.tun_ip, - )); +impl MagicDnsServerInstanceData { + /// Replace content of incoming UDP DNS request and ICMP echo request packet with reply data, + /// and swap source and destination IP addresses to send it back. + async fn handle_ip_packet(&self, zc_packet: &mut ZCPacket) -> Option<()> { + let (ip_header_length, ip_protocol, src_ip, dst_ip) = { + let ip_packet = Ipv4Packet::new(zc_packet.payload())?; + + if ip_packet.get_version() != 4 { + return None; } + ( + ip_packet.get_header_length() as usize * 4, + ip_packet.get_next_level_protocol(), + ip_packet.get_source(), + ip_packet.get_destination(), + ) + }; + + if dst_ip != self.fake_ip { + return None; + } + + match ip_protocol { + IpNextHeaderProtocols::Udp => { + self.handle_udp_packet(zc_packet, ip_header_length, src_ip, dst_ip) + .await?; + } IpNextHeaderProtocols::Icmp => { - let Some(mut icmp_packet) = MutableIcmpPacket::new(ip_packet.payload_mut()) else { - return false; - }; - if icmp_packet.get_icmp_type() != IcmpTypes::EchoRequest { - return false; - } - icmp_packet.set_icmp_type(IcmpTypes::EchoReply); - icmp_packet.set_checksum(icmp::checksum(&icmp_packet.to_immutable())); + self.handle_icmp_packet(zc_packet, ip_header_length)?; } - _ => { - return false; + return None; } } - ip_packet.set_source(self.fake_ip); - ip_packet.set_destination(self.tun_ip); + let mut ip_packet = MutableIpv4Packet::new(zc_packet.mut_payload())?; + ip_packet.set_source(dst_ip); + ip_packet.set_destination(src_ip); ip_packet.set_checksum(ipv4::checksum(&ip_packet.to_immutable())); + zc_packet.mut_peer_manager_header().unwrap().to_peer_id = self.my_peer_id.into(); - true + Some(()) + } + + /// Extract the DNS request message and send it to the hickory-dns server instance. + /// Replace the content of the UDP packet with the response message. + async fn handle_udp_packet( + &self, + zc_packet: &mut ZCPacket, + ip_header_length: usize, + src_ip: Ipv4Addr, + dst_ip: Ipv4Addr, + ) -> Option<()> { + let (src_port, dst_port, request, request_length) = { + let udp_packet = UdpPacket::new(&zc_packet.payload()[ip_header_length..])?; + + let src_port = udp_packet.get_source(); + let dst_port = udp_packet.get_destination(); + + // Remove this to support any UDP port + if dst_port != 53 { + return None; + } + + let request_payload = udp_packet.payload(); + + ( + src_port, + dst_port, + Request::new( + MessageRequest::from_bytes(request_payload).ok()?, + SocketAddr::from(SocketAddrV4::new(src_ip, src_port)), + hickory_proto::xfer::Protocol::Udp, + ), + request_payload.len(), + ) + }; + + let response_payload = { + let response_payload_arc = Arc::new(Mutex::new(Vec::with_capacity(512))); + + self.dns_server + .read_catalog() + .await + .handle_request( + &request, + ResponseWrapper { + response: response_payload_arc.clone(), + }, + ) + .await; + + Arc::into_inner(response_payload_arc)?.into_inner().ok()? + }; + + let response_length = response_payload.len(); + let delta_length = response_length as isize - request_length as isize; + + let inner_length = (zc_packet.buf_len() as isize + delta_length) as usize; + if zc_packet.mut_inner().capacity() < inner_length { + let header_length = inner_length - response_length; + zc_packet.mut_inner().truncate(header_length); + } + zc_packet.mut_inner().resize(inner_length, 0); + + let mut ip_packet = MutableIpv4Packet::new(zc_packet.mut_payload())?; + + let ip_length = (ip_packet.get_total_length() as isize + delta_length) as u16; + ip_packet.set_total_length(ip_length); + + let mut udp_packet = MutableUdpPacket::new(ip_packet.payload_mut())?; + + let udp_length = (udp_packet.get_length() as isize + delta_length) as u16; + udp_packet.set_length(udp_length); + + udp_packet.set_source(dst_port); + udp_packet.set_destination(src_port); + + udp_packet.payload_mut().copy_from_slice(&response_payload); + + udp_packet.set_checksum(udp::ipv4_checksum( + &udp_packet.to_immutable(), + &dst_ip, + &src_ip, + )); + + Some(()) + } + + fn handle_icmp_packet(&self, zc_packet: &mut ZCPacket, ip_header_length: usize) -> Option<()> { + let mut icmp_packet = + MutableIcmpPacket::new(&mut zc_packet.mut_payload()[ip_header_length..])?; + + if icmp_packet.get_icmp_type() != IcmpTypes::EchoRequest { + return None; + } + + icmp_packet.set_icmp_type(IcmpTypes::EchoReply); + icmp_packet.set_checksum(icmp::checksum(&icmp_packet.to_immutable())); + + Some(()) + } +} + +#[async_trait::async_trait] +impl NicPacketFilter for MagicDnsServerInstanceData { + async fn try_process_packet_from_nic(&self, zc_packet: &mut ZCPacket) -> bool { + self.handle_ip_packet(zc_packet).await.is_some() } fn id(&self) -> String { @@ -376,23 +499,14 @@ impl MagicDnsServerInstance { tun_inet: Ipv4Inet, fake_ip: Ipv4Addr, ) -> Result { - let tcp_listener = TcpTunnelListener::new(MAGIC_DNS_INSTANCE_ADDR.parse().unwrap()); + let tcp_listener = TcpTunnelListener::new(MAGIC_DNS_INSTANCE_ADDR.parse()?); let mut rpc_server = StandAloneServer::new(tcp_listener); rpc_server.serve().await?; - let bind_addr = tun_inet.address(); - let dns_config = RunConfigBuilder::default() - .general( - GeneralConfigBuilder::default() - .listen_udp(format!("{}:0", bind_addr)) - .listen_tcp(format!("{}:0", bind_addr)) - .build() - .unwrap(), - ) + .general(GeneralConfigBuilder::default().build()?) .excluded_forward_nameservers(vec![fake_ip.into()]) - .build() - .unwrap(); + .build()?; let mut dns_server = Server::new(dns_config); dns_server.run().await?; @@ -426,9 +540,11 @@ impl MagicDnsServerInstance { peer_mgr .add_nic_packet_process_pipeline(Box::new(data.clone())) .await; - + // Use configured tld_dns_zone or fall back to DEFAULT_ET_DNS_ZONE if empty + let flags = peer_mgr.get_global_ctx().config.get_flags(); + let tld_dns_zone_clone = flags.tld_dns_zone.clone(); let data_clone = data.clone(); - tokio::task::spawn_blocking(move || data_clone.do_system_config(DEFAULT_ET_DNS_ZONE)) + tokio::task::spawn_blocking(move || data_clone.do_system_config(&tld_dns_zone_clone)) .await .context("Failed to configure system")??; diff --git a/easytier/src/instance/dns_server/system_config/windows.rs b/easytier/src/instance/dns_server/system_config/windows.rs index f626cae31..94f014d40 100644 --- a/easytier/src/instance/dns_server/system_config/windows.rs +++ b/easytier/src/instance/dns_server/system_config/windows.rs @@ -39,7 +39,7 @@ impl InterfaceControl { if matches!(e.kind(), io::ErrorKind::NotFound) { Ok(()) // 忽略不存在的值 } else { - Err(e.into()) + Err(e) } } } @@ -106,10 +106,7 @@ impl InterfaceControl { .output() .expect("failed to execute process"); if !output.status.success() { - return Err(io::Error::new( - io::ErrorKind::Other, - "Failed to flush DNS cache", - )); + return Err(io::Error::other("Failed to flush DNS cache")); } Ok(()) } @@ -122,10 +119,7 @@ impl InterfaceControl { .output() .expect("failed to execute process"); if !output.status.success() { - return Err(io::Error::new( - io::ErrorKind::Other, - "Failed to register DNS", - )); + return Err(io::Error::other("Failed to register DNS")); } Ok(()) } diff --git a/easytier/src/instance/dns_server/tests.rs b/easytier/src/instance/dns_server/tests.rs index fb046b299..f7e3a8f25 100644 --- a/easytier/src/instance/dns_server/tests.rs +++ b/easytier/src/instance/dns_server/tests.rs @@ -8,6 +8,7 @@ use hickory_client::client::{Client, ClientHandle as _}; use hickory_proto::rr; use hickory_proto::runtime::TokioRuntimeProvider; use hickory_proto::udp::UdpClientStream; +use tokio::sync::Notify; use tokio_util::sync::CancellationToken; use crate::common::global_ctx::tests::get_mock_global_ctx; @@ -15,25 +16,48 @@ use crate::connector::udp_hole_punch::tests::replace_stun_info_collector; use crate::instance::dns_server::runner::DnsRunner; use crate::instance::dns_server::server_instance::MagicDnsServerInstance; -use crate::instance::dns_server::DEFAULT_ET_DNS_ZONE; +use crate::instance::dns_server::{DEFAULT_ET_DNS_ZONE, MAGIC_DNS_FAKE_IP}; use crate::instance::virtual_nic::NicCtx; use crate::peers::peer_manager::{PeerManager, RouteAlgoType}; use crate::peers::create_packet_recv_chan; -use crate::proto::cli::Route; +use crate::proto::api::instance::Route; use crate::proto::common::NatType; pub async fn prepare_env(dns_name: &str, tun_ip: Ipv4Inet) -> (Arc, NicCtx) { + prepare_env_with_tld_dns_zone(dns_name, tun_ip, None).await +} + +pub async fn prepare_env_with_tld_dns_zone( + dns_name: &str, + tun_ip: Ipv4Inet, + tld_dns_zone: Option<&str>, +) -> (Arc, NicCtx) { let ctx = get_mock_global_ctx(); ctx.set_hostname(dns_name.to_owned()); ctx.set_ipv4(Some(tun_ip)); + + if tld_dns_zone.is_some() { + let mut flags = ctx.config.get_flags(); + flags.accept_dns = true; // Enable DNS + if let Some(zone) = tld_dns_zone { + flags.tld_dns_zone = zone.to_string(); + } + ctx.config.set_flags(flags); + } + let (s, r) = create_packet_recv_chan(); let peer_mgr = Arc::new(PeerManager::new(RouteAlgoType::Ospf, ctx, s)); peer_mgr.run().await.unwrap(); replace_stun_info_collector(peer_mgr.clone(), NatType::PortRestricted); let r = Arc::new(tokio::sync::Mutex::new(r)); - let mut virtual_nic = NicCtx::new(peer_mgr.get_global_ctx(), &peer_mgr, r); + let mut virtual_nic = NicCtx::new( + peer_mgr.get_global_ctx(), + &peer_mgr, + r, + Arc::new(Notify::new()), + ); virtual_nic.run(Some(tun_ip), None).await.unwrap(); (peer_mgr, virtual_nic) @@ -78,11 +102,23 @@ async fn test_magic_dns_server_instance() { .await .unwrap(); - let routes = vec![Route { - hostname: "test1".to_string(), - ipv4_addr: Some(Ipv4Inet::from_str("8.8.8.8/24").unwrap().into()), - ..Default::default() - }]; + let routes = vec![ + Route { + hostname: "test1".to_string(), + ipv4_addr: Some(Ipv4Inet::from_str("8.8.8.8/24").unwrap().into()), + ..Default::default() + }, + Route { + hostname: "中文".to_string(), + ipv4_addr: Some(Ipv4Inet::from_str("8.8.8.8/24").unwrap().into()), + ..Default::default() + }, + Route { + hostname: ".invalid".to_string(), + ipv4_addr: Some(Ipv4Inet::from_str("8.8.8.8/24").unwrap().into()), + ..Default::default() + }, + ]; dns_server_inst .data .update_dns_records(routes.iter(), DEFAULT_ET_DNS_ZONE) @@ -90,45 +126,58 @@ async fn test_magic_dns_server_instance() { .unwrap(); check_dns_record(&fake_ip, "test1.et.net", "8.8.8.8").await; + check_dns_record(&fake_ip, "中文.et.net", "8.8.8.8").await; } #[tokio::test] async fn test_magic_dns_runner() { - let tun_ip = Ipv4Inet::from_str("10.144.144.10/24").unwrap(); - let (peer_mgr, virtual_nic) = prepare_env("test1", tun_ip).await; - let tun_name = virtual_nic.ifname().await.unwrap(); - let fake_ip = Ipv4Addr::from_str("100.100.100.101").unwrap(); - let mut dns_runner = DnsRunner::new(peer_mgr, Some(tun_name), tun_ip, fake_ip); - - let cancel_token = CancellationToken::new(); - let cancel_token_clone = cancel_token.clone(); - let t = tokio::spawn(async move { - dns_runner.run(cancel_token_clone).await; - }); - tokio::time::sleep(Duration::from_secs(3)).await; - check_dns_record(&fake_ip, "test1.et.net", "10.144.144.10").await; - - // add a new dns runner - let tun_ip2 = Ipv4Inet::from_str("10.144.144.20/24").unwrap(); - let (peer_mgr, virtual_nic) = prepare_env("test2", tun_ip2).await; - let tun_name2 = virtual_nic.ifname().await.unwrap(); - let mut dns_runner2 = DnsRunner::new(peer_mgr, Some(tun_name2), tun_ip2, fake_ip); - let cancel_token2 = CancellationToken::new(); - let cancel_token2_clone = cancel_token2.clone(); - let t2 = tokio::spawn(async move { - dns_runner2.run(cancel_token2_clone).await; - }); - tokio::time::sleep(Duration::from_secs(3)).await; - check_dns_record(&fake_ip, "test1.et.net", "10.144.144.10").await; - check_dns_record(&fake_ip, "test2.et.net", "10.144.144.20").await; - - // stop runner 1, runner 2 will take over the dns server - cancel_token.cancel(); - t.await.unwrap(); - - tokio::time::sleep(Duration::from_secs(3)).await; - check_dns_record(&fake_ip, "test2.et.net", "10.144.144.20").await; - - cancel_token2.cancel(); - t2.await.unwrap(); + // Test first runner with default DNS settings + { + let tun_ip = Ipv4Inet::from_str("10.144.144.10/24").unwrap(); + let (peer_mgr, virtual_nic) = prepare_env("test1", tun_ip).await; + let tun_name = virtual_nic.ifname().await.unwrap(); + let fake_ip = Ipv4Addr::from_str(MAGIC_DNS_FAKE_IP).unwrap(); + let mut dns_runner = DnsRunner::new(peer_mgr, Some(tun_name), tun_ip, fake_ip); + + let cancel_token = CancellationToken::new(); + let cancel_token_clone = cancel_token.clone(); + let t = tokio::spawn(async move { + dns_runner.run(cancel_token_clone).await; + }); + tokio::time::sleep(Duration::from_secs(3)).await; + + // Test default settings: query should resolve test1.et.net to tunnel IP via default fake IP + check_dns_record(&fake_ip, "test1.et.net", "10.144.144.10").await; + + cancel_token.cancel(); + t.await.unwrap(); + + // Wait a bit for cleanup + tokio::time::sleep(Duration::from_secs(1)).await; + } + + // Test second runner with different TLD zone + { + let tun_ip = Ipv4Inet::from_str("10.144.144.20/24").unwrap(); + // NOTE: Using same fake IP to avoid system DNS configuration conflicts + let custom_tld_zone = "custom.local."; // Different TLD zone is safer + let (peer_mgr, virtual_nic) = + prepare_env_with_tld_dns_zone("test2", tun_ip, Some(custom_tld_zone)).await; + let tun_name = virtual_nic.ifname().await.unwrap(); + let fake_ip = Ipv4Addr::from_str(MAGIC_DNS_FAKE_IP).unwrap(); + let mut dns_runner = DnsRunner::new(peer_mgr, Some(tun_name), tun_ip, fake_ip); + + let cancel_token = CancellationToken::new(); + let cancel_token_clone = cancel_token.clone(); + let t = tokio::spawn(async move { + dns_runner.run(cancel_token_clone).await; + }); + tokio::time::sleep(Duration::from_secs(3)).await; + + // Test with same fake IP but different TLD zone + check_dns_record(&fake_ip, "test2.custom.local", "10.144.144.20").await; + + cancel_token.cancel(); + t.await.unwrap(); + } } diff --git a/easytier/src/instance/instance.rs b/easytier/src/instance/instance.rs index 45151039b..37247c0ff 100644 --- a/easytier/src/instance/instance.rs +++ b/easytier/src/instance/instance.rs @@ -3,10 +3,13 @@ use std::collections::HashSet; use std::net::{IpAddr, Ipv4Addr}; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Weak}; +use std::time::Duration; use anyhow::Context; use cidr::{IpCidr, Ipv4Inet}; +use futures::FutureExt; +use tokio::sync::{oneshot, Notify}; use tokio::{sync::Mutex, task::JoinSet}; use tokio_util::sync::CancellationToken; @@ -29,22 +32,24 @@ use crate::peers::peer_conn::PeerConnId; use crate::peers::peer_manager::{PeerManager, RouteAlgoType}; use crate::peers::rpc_service::PeerManagerRpcService; use crate::peers::{create_packet_recv_chan, recv_packet_from_chan, PacketRecvChanReceiver}; -use crate::proto::cli::VpnPortalRpc; -use crate::proto::cli::{ - AddPortForwardRequest, AddPortForwardResponse, GetPrometheusStatsRequest, - GetPrometheusStatsResponse, GetStatsRequest, GetStatsResponse, ListMappedListenerRequest, - ListMappedListenerResponse, ListPortForwardRequest, ListPortForwardResponse, - ManageMappedListenerRequest, ManageMappedListenerResponse, MappedListener, - MappedListenerManageAction, MappedListenerManageRpc, MetricSnapshot, PortForwardManageRpc, - RemovePortForwardRequest, RemovePortForwardResponse, StatsRpc, +use crate::proto::api::config::{ + ConfigPatchAction, ConfigRpc, GetConfigRequest, GetConfigResponse, PatchConfigRequest, + PatchConfigResponse, PortForwardPatch, }; -use crate::proto::cli::{GetVpnPortalInfoRequest, GetVpnPortalInfoResponse, VpnPortalInfo}; +use crate::proto::api::instance::{ + GetPrometheusStatsRequest, GetPrometheusStatsResponse, GetStatsRequest, GetStatsResponse, + GetVpnPortalInfoRequest, GetVpnPortalInfoResponse, ListMappedListenerRequest, + ListMappedListenerResponse, ListPortForwardRequest, ListPortForwardResponse, MappedListener, + MappedListenerManageRpc, MetricSnapshot, PortForwardManageRpc, StatsRpc, VpnPortalInfo, + VpnPortalRpc, +}; +use crate::proto::api::manage::NetworkConfig; use crate::proto::common::{PortForwardConfigPb, TunnelInfo}; -use crate::proto::peer_rpc::PeerCenterRpcServer; -use crate::proto::rpc_impl::standalone::{RpcServerHook, StandAloneServer}; +use crate::proto::rpc_impl::standalone::RpcServerHook; use crate::proto::rpc_types; use crate::proto::rpc_types::controller::BaseController; -use crate::tunnel::tcp::TcpTunnelListener; +use crate::rpc_service::InstanceRpcService; +use crate::utils::weak_upgrade; use crate::vpn_portal::{self, VpnPortal}; use super::dns_server::runner::DnsRunner; @@ -133,7 +138,7 @@ struct MagicDnsContainer { } // nic container will be cleared when dhcp ip changed -pub(crate) struct NicCtxContainer { +pub struct NicCtxContainer { nic_ctx: Option>, magic_dns: Option, } @@ -223,6 +228,281 @@ impl RpcServerHook for InstanceRpcServerHook { } } +#[derive(Clone)] +pub struct InstanceConfigPatcher { + global_ctx: Weak, + socks5_server: Weak, + peer_manager: Weak, + conn_manager: Weak, +} + +impl InstanceConfigPatcher { + pub async fn apply_patch( + &self, + patch: crate::proto::api::config::InstanceConfigPatch, + ) -> Result<(), anyhow::Error> { + let patch_for_event = patch.clone(); + + self.patch_port_forwards(patch.port_forwards).await?; + self.patch_acl(patch.acl).await?; + self.patch_proxy_networks(patch.proxy_networks).await?; + self.patch_routes(patch.routes).await?; + self.patch_exit_nodes(patch.exit_nodes).await?; + self.patch_mapped_listeners(patch.mapped_listeners).await?; + self.patch_connector(patch.connectors).await?; + + let global_ctx = weak_upgrade(&self.global_ctx)?; + if let Some(hostname) = patch.hostname { + global_ctx.set_hostname(hostname.clone()); + global_ctx.config.set_hostname(Some(hostname)); + } + if let Some(ipv4) = patch.ipv4 { + if !global_ctx.config.get_dhcp() { + global_ctx.set_ipv4(Some(ipv4.into())); + global_ctx.config.set_ipv4(Some(ipv4.into())); + } + } + if let Some(ipv6) = patch.ipv6 { + global_ctx.set_ipv6(Some(ipv6.into())); + global_ctx.config.set_ipv6(Some(ipv6.into())); + } + + global_ctx.issue_event(GlobalCtxEvent::ConfigPatched(patch_for_event)); + + Ok(()) + } + + fn trace_patchables( + patches: &Vec>, + ) { + for patch in patches { + match patch.action { + Some(ConfigPatchAction::Add) | Some(ConfigPatchAction::Remove) => { + if let Some(value) = &patch.value { + tracing::info!("{:?} {:?}", patch.action, value); + } else { + tracing::warn!( + "Ignored {:?} patch with no value for type '{}'. Please ensure the patch value is provided.", + patch.action, + std::any::type_name::() + ); + } + } + Some(ConfigPatchAction::Clear) => { + tracing::info!("Clear all for type '{}'", std::any::type_name::()); + } + None => { + tracing::warn!( + "Invalid patch action for type '{}'", + std::any::type_name::() + ); + } + } + } + } + + async fn patch_port_forwards( + &self, + port_forwards: Vec, + ) -> Result<(), anyhow::Error> { + if port_forwards.is_empty() { + return Ok(()); + } + let Some(socks5_server) = self.socks5_server.upgrade() else { + return Err(anyhow::anyhow!("socks5 server not available")); + }; + let global_ctx = weak_upgrade(&self.global_ctx)?; + + let mut current_forwards = global_ctx.config.get_port_forwards(); + let patches = port_forwards.into_iter().map(Into::into).collect(); + InstanceConfigPatcher::trace_patchables(&patches); + crate::proto::api::config::patch_vec(&mut current_forwards, patches); + + global_ctx + .config + .set_port_forwards(current_forwards.clone()); + socks5_server + .reload_port_forwards(¤t_forwards) + .await + .with_context(|| "Failed to reload port forwards")?; + + Ok(()) + } + + async fn patch_acl( + &self, + acl_patch: Option, + ) -> Result<(), anyhow::Error> { + let Some(acl_patch) = acl_patch else { + return Ok(()); + }; + let global_ctx = weak_upgrade(&self.global_ctx)?; + if let Some(acl) = acl_patch.acl { + global_ctx.config.set_acl(Some(acl)); + } + if !acl_patch.tcp_whitelist.is_empty() { + let mut current_whitelist = global_ctx.config.get_tcp_whitelist(); + let patches = acl_patch + .tcp_whitelist + .into_iter() + .map(Into::into) + .collect(); + InstanceConfigPatcher::trace_patchables(&patches); + crate::proto::api::config::patch_vec(&mut current_whitelist, patches); + global_ctx.config.set_tcp_whitelist(current_whitelist); + } + if !acl_patch.udp_whitelist.is_empty() { + let mut current_whitelist = global_ctx.config.get_udp_whitelist(); + let patches = acl_patch + .udp_whitelist + .into_iter() + .map(Into::into) + .collect(); + InstanceConfigPatcher::trace_patchables(&patches); + crate::proto::api::config::patch_vec(&mut current_whitelist, patches); + global_ctx.config.set_udp_whitelist(current_whitelist); + } + global_ctx + .get_acl_filter() + .reload_rules(AclRuleBuilder::build(&global_ctx)?.as_ref()); + Ok(()) + } + + async fn patch_proxy_networks( + &self, + proxy_networks: Vec, + ) -> Result<(), anyhow::Error> { + if proxy_networks.is_empty() { + return Ok(()); + } + let global_ctx = weak_upgrade(&self.global_ctx)?; + for proxy_network_patch in proxy_networks { + let Some(cidr) = proxy_network_patch.cidr.map(|c| c.into()) else { + tracing::warn!("Proxy network cidr is None, skipping."); + continue; + }; + let mapped_cidr: Option = + proxy_network_patch.mapped_cidr.map(|s| s.into()); + match ConfigPatchAction::try_from(proxy_network_patch.action) { + Ok(ConfigPatchAction::Add) => { + tracing::info!("Proxy network added: {}", cidr); + global_ctx.config.add_proxy_cidr(cidr, mapped_cidr)?; + } + Ok(ConfigPatchAction::Remove) => { + tracing::info!("Proxy network removed: {}", cidr); + global_ctx.config.remove_proxy_cidr(cidr); + } + Ok(ConfigPatchAction::Clear) => { + tracing::info!("Proxy networks cleared."); + global_ctx.config.clear_proxy_cidrs(); + } + Err(_) => { + tracing::warn!( + "Invalid proxy network action: {}", + proxy_network_patch.action + ); + } + } + } + Ok(()) + } + + async fn patch_routes( + &self, + routes: Vec, + ) -> Result<(), anyhow::Error> { + if routes.is_empty() { + return Ok(()); + } + let global_ctx = weak_upgrade(&self.global_ctx)?; + let mut current_routes = global_ctx.config.get_routes().unwrap_or_default(); + let patches = routes.into_iter().map(Into::into).collect(); + InstanceConfigPatcher::trace_patchables(&patches); + crate::proto::api::config::patch_vec(&mut current_routes, patches); + if current_routes.is_empty() { + global_ctx.config.set_routes(None); + } else { + global_ctx.config.set_routes(Some(current_routes)); + } + Ok(()) + } + + async fn patch_exit_nodes( + &self, + exit_nodes: Vec, + ) -> Result<(), anyhow::Error> { + if exit_nodes.is_empty() { + return Ok(()); + } + let global_ctx = weak_upgrade(&self.global_ctx)?; + let peer_manager = weak_upgrade(&self.peer_manager)?; + let mut current_exit_nodes = global_ctx.config.get_exit_nodes(); + let patches = exit_nodes.into_iter().map(Into::into).collect(); + InstanceConfigPatcher::trace_patchables(&patches); + crate::proto::api::config::patch_vec(&mut current_exit_nodes, patches); + global_ctx.config.set_exit_nodes(current_exit_nodes); + peer_manager.update_exit_nodes().await; + + Ok(()) + } + + async fn patch_mapped_listeners( + &self, + mapped_listeners: Vec, + ) -> Result<(), anyhow::Error> { + if mapped_listeners.is_empty() { + return Ok(()); + } + let global_ctx = weak_upgrade(&self.global_ctx)?; + let mut current_mapped_listeners = global_ctx.config.get_mapped_listeners(); + let patches = mapped_listeners.into_iter().map(Into::into).collect(); + InstanceConfigPatcher::trace_patchables(&patches); + crate::proto::api::config::patch_vec(&mut current_mapped_listeners, patches); + if current_mapped_listeners.is_empty() { + global_ctx.config.set_mapped_listeners(None); + } else { + global_ctx + .config + .set_mapped_listeners(Some(current_mapped_listeners)); + } + Ok(()) + } + + async fn patch_connector( + &self, + connectors: Vec, + ) -> Result<(), anyhow::Error> { + if connectors.is_empty() { + return Ok(()); + } + let conn_manager = weak_upgrade(&self.conn_manager)?; + for connector in connectors { + let Some(url) = connector.url.map(Into::::into) else { + tracing::warn!("Connector url is None, skipping."); + return Ok(()); + }; + match ConfigPatchAction::try_from(connector.action) { + Ok(ConfigPatchAction::Add) => { + tracing::info!("Connector added: {}", url); + conn_manager.add_connector_by_url(url.as_str()).await?; + } + Ok(ConfigPatchAction::Remove) => { + tracing::info!("Connector removed: {}", url); + conn_manager.remove_connector(url).await?; + } + Ok(ConfigPatchAction::Clear) => { + tracing::info!("Connectors cleared."); + conn_manager.clear_connectors().await; + } + Err(_) => { + tracing::warn!("Invalid connector action: {}", connector.action); + } + } + } + Ok(()) + } +} + pub struct Instance { inst_name: String, @@ -252,8 +532,6 @@ pub struct Instance { #[cfg(feature = "socks5")] socks5_server: Arc, - rpc_server: Option>, - global_ctx: ArcGlobalCtx, } @@ -304,12 +582,6 @@ impl Instance { #[cfg(feature = "socks5")] let socks5_server = Socks5Server::new(global_ctx.clone(), peer_manager.clone(), None); - let rpc_server = global_ctx.config.get_rpc_portal().map(|s| { - StandAloneServer::new(TcpTunnelListener::new( - format!("tcp://{}", s).parse().unwrap(), - )) - }); - Instance { inst_name: global_ctx.inst_name.clone(), id, @@ -337,8 +609,6 @@ impl Instance { #[cfg(feature = "socks5")] socks5_server, - rpc_server, - global_ctx, } } @@ -425,6 +695,7 @@ impl Instance { let default_ipv4_addr = Ipv4Inet::new(Ipv4Addr::new(10, 126, 126, 0), 24).unwrap(); let mut current_dhcp_ip: Option = None; let mut next_sleep_time = 0; + let nic_closed_notifier = Arc::new(Notify::new()); loop { tokio::time::sleep(std::time::Duration::from_secs(next_sleep_time)).await; @@ -433,6 +704,11 @@ impl Instance { return; }; + if nic_closed_notifier.notified().now_or_never().is_some() { + tracing::debug!("nic ctx is closed, try recreate it"); + current_dhcp_ip = None; + } + // do not allocate ip if no peer connected let routes = peer_manager_c.list_routes().await; if routes.is_empty() { @@ -494,6 +770,7 @@ impl Instance { global_ctx_c.clone(), &peer_manager_c, _peer_packet_receiver.clone(), + nic_closed_notifier.clone(), ); if let Err(e) = new_nic_ctx.run(Some(ip), global_ctx_c.get_ipv6()).await { tracing::error!( @@ -526,12 +803,82 @@ impl Instance { }); } + fn check_for_static_ip(&self, first_round_output: oneshot::Sender>) { + let ipv4_addr = self.global_ctx.get_ipv4(); + let ipv6_addr = self.global_ctx.get_ipv6(); + + // Only run if we have at least one IP address (IPv4 or IPv6) + if ipv4_addr.is_none() && ipv6_addr.is_none() { + let _ = first_round_output.send(Ok(())); + return; + } + + let nic_ctx = self.nic_ctx.clone(); + let peer_mgr = Arc::downgrade(&self.peer_manager); + let peer_packet_receiver = self.peer_packet_receiver.clone(); + + tokio::spawn(async move { + let mut output_tx = Some(first_round_output); + loop { + let Some(peer_manager) = peer_mgr.upgrade() else { + tracing::warn!("peer manager is dropped, stop static ip check."); + if let Some(output_tx) = output_tx.take() { + let _ = output_tx.send(Err(Error::Unknown)); + return; + } + return; + }; + + let close_notifier = Arc::new(Notify::new()); + let mut new_nic_ctx = NicCtx::new( + peer_manager.get_global_ctx(), + &peer_manager, + peer_packet_receiver.clone(), + close_notifier.clone(), + ); + + if let Err(e) = new_nic_ctx.run(ipv4_addr, ipv6_addr).await { + if let Some(output_tx) = output_tx.take() { + let _ = output_tx.send(Err(e)); + return; + } + tracing::error!("failed to create new nic ctx, err: {:?}", e); + tokio::time::sleep(Duration::from_secs(1)).await; + continue; + } + let ifname = new_nic_ctx.ifname().await; + + // Create Magic DNS runner only if we have IPv4 + let dns_runner = if let Some(ipv4) = ipv4_addr { + Self::create_magic_dns_runner(peer_manager, ifname, ipv4) + } else { + None + }; + Self::use_new_nic_ctx(nic_ctx.clone(), new_nic_ctx, dns_runner).await; + + if let Some(output_tx) = output_tx.take() { + let _ = output_tx.send(Ok(())); + } + + // NOTICE: make sure we do not hold the peer manager here, + while close_notifier.notified().now_or_never().is_none() { + tokio::time::sleep(Duration::from_secs(1)).await; + if peer_mgr.strong_count() == 0 { + tracing::warn!("peer manager is dropped, stop static ip check."); + return; + } + } + } + }); + } + async fn run_quic_dst(&mut self) -> Result<(), Error> { - if !self.global_ctx.get_flags().enable_quic_proxy { + if self.global_ctx.get_flags().disable_quic_input { return Ok(()); } - let quic_dst = QUICProxyDst::new(self.global_ctx.clone())?; + let route = Arc::new(self.peer_manager.get_route()); + let quic_dst = QUICProxyDst::new(self.global_ctx.clone(), route)?; quic_dst.start().await?; self.global_ctx .set_quic_proxy_port(Some(quic_dst.local_addr()?.port())); @@ -553,28 +900,9 @@ impl Instance { if !self.global_ctx.config.get_flags().no_tun { #[cfg(not(any(target_os = "android", target_env = "ohos")))] { - let ipv4_addr = self.global_ctx.get_ipv4(); - let ipv6_addr = self.global_ctx.get_ipv6(); - - // Only run if we have at least one IP address (IPv4 or IPv6) - if ipv4_addr.is_some() || ipv6_addr.is_some() { - let mut new_nic_ctx = NicCtx::new( - self.global_ctx.clone(), - &self.peer_manager, - self.peer_packet_receiver.clone(), - ); - - new_nic_ctx.run(ipv4_addr, ipv6_addr).await?; - let ifname = new_nic_ctx.ifname().await; - - // Create Magic DNS runner only if we have IPv4 - let dns_runner = if let Some(ipv4) = ipv4_addr { - Self::create_magic_dns_runner(self.peer_manager.clone(), ifname, ipv4) - } else { - None - }; - Self::use_new_nic_ctx(self.nic_ctx.clone(), new_nic_ctx, dns_runner).await; - } + let (output_tx, output_rx) = oneshot::channel(); + self.check_for_static_ip(output_tx); + output_rx.await.unwrap()?; } } @@ -644,8 +972,6 @@ impl Instance { ) .await?; - self.run_rpc_server().await?; - Ok(()) } @@ -744,7 +1070,7 @@ impl Instance { &self, ) -> impl MappedListenerManageRpc + Clone { #[derive(Clone)] - pub struct MappedListenerManagerRpcService(Arc); + pub struct MappedListenerManagerRpcService(Weak); #[async_trait::async_trait] impl MappedListenerManageRpc for MappedListenerManagerRpcService { @@ -756,7 +1082,7 @@ impl Instance { _request: ListMappedListenerRequest, ) -> Result { let mut ret = ListMappedListenerResponse::default(); - let urls = self.0.config.get_mapped_listeners(); + let urls = weak_upgrade(&self.0)?.config.get_mapped_listeners(); let mapped_listeners: Vec = urls .into_iter() .map(|u| MappedListener { @@ -766,28 +1092,9 @@ impl Instance { ret.mappedlisteners = mapped_listeners; Ok(ret) } - - async fn manage_mapped_listener( - &self, - _: BaseController, - req: ManageMappedListenerRequest, - ) -> Result { - let url: url::Url = req.url.ok_or(anyhow::anyhow!("url is empty"))?.into(); - - let urls = self.0.config.get_mapped_listeners(); - let mut set_urls: HashSet = urls.into_iter().collect(); - if req.action == MappedListenerManageAction::MappedListenerRemove as i32 { - set_urls.remove(&url); - } else if req.action == MappedListenerManageAction::MappedListenerAdd as i32 { - set_urls.insert(url); - } - let urls: Vec = set_urls.into_iter().collect(); - self.0.config.set_mapped_listeners(Some(urls)); - Ok(ManageMappedListenerResponse::default()) - } } - MappedListenerManagerRpcService(self.global_ctx.clone()) + MappedListenerManagerRpcService(Arc::downgrade(&self.global_ctx)) } fn get_port_forward_manager_rpc_service( @@ -795,7 +1102,7 @@ impl Instance { ) -> impl PortForwardManageRpc + Clone { #[derive(Clone)] pub struct PortForwardManagerRpcService { - global_ctx: ArcGlobalCtx, + global_ctx: Weak, socks5_server: Weak, } @@ -803,68 +1110,19 @@ impl Instance { impl PortForwardManageRpc for PortForwardManagerRpcService { type Controller = BaseController; - async fn add_port_forward( - &self, - _: BaseController, - request: AddPortForwardRequest, - ) -> Result { - let Some(socks5_server) = self.socks5_server.upgrade() else { - return Err(anyhow::anyhow!("socks5 server not available").into()); - }; - if let Some(cfg) = request.cfg { - tracing::info!("Port forward rule added: {:?}", cfg); - let mut current_forwards = self.global_ctx.config.get_port_forwards(); - current_forwards.push(cfg.into()); - self.global_ctx - .config - .set_port_forwards(current_forwards.clone()); - socks5_server - .reload_port_forwards(¤t_forwards) - .await - .with_context(|| "Failed to reload port forwards")?; - } - Ok(AddPortForwardResponse {}) - } - - async fn remove_port_forward( - &self, - _: BaseController, - request: RemovePortForwardRequest, - ) -> Result { - let Some(socks5_server) = self.socks5_server.upgrade() else { - return Err(anyhow::anyhow!("socks5 server not available").into()); - }; - let Some(cfg) = request.cfg else { - return Err(anyhow::anyhow!("port forward config is empty").into()); - }; - let cfg = cfg.into(); - let mut current_forwards = self.global_ctx.config.get_port_forwards(); - current_forwards.retain(|e| *e != cfg); - self.global_ctx - .config - .set_port_forwards(current_forwards.clone()); - socks5_server - .reload_port_forwards(¤t_forwards) - .await - .with_context(|| "Failed to reload port forwards")?; - - tracing::info!("Port forward rule removed: {:?}", cfg); - Ok(RemovePortForwardResponse {}) - } - async fn list_port_forward( &self, _: BaseController, _request: ListPortForwardRequest, ) -> Result { - let forwards = self.global_ctx.config.get_port_forwards(); + let forwards = weak_upgrade(&self.global_ctx)?.config.get_port_forwards(); let cfgs: Vec = forwards.into_iter().map(Into::into).collect(); Ok(ListPortForwardResponse { cfgs }) } } PortForwardManagerRpcService { - global_ctx: self.global_ctx.clone(), + global_ctx: Arc::downgrade(&self.global_ctx), socks5_server: Arc::downgrade(&self.socks5_server), } } @@ -872,7 +1130,7 @@ impl Instance { fn get_stats_rpc_service(&self) -> impl StatsRpc + Clone { #[derive(Clone)] pub struct StatsRpcService { - global_ctx: ArcGlobalCtx, + global_ctx: Weak, } #[async_trait::async_trait] @@ -884,8 +1142,9 @@ impl Instance { _: BaseController, _request: GetStatsRequest, ) -> Result { - let stats_manager = self.global_ctx.stats_manager(); - let snapshots = stats_manager.get_all_metrics(); + let snapshots = weak_upgrade(&self.global_ctx)? + .stats_manager() + .get_all_metrics(); let metrics = snapshots .into_iter() @@ -911,102 +1170,205 @@ impl Instance { _: BaseController, _request: GetPrometheusStatsRequest, ) -> Result { - let stats_manager = self.global_ctx.stats_manager(); - let prometheus_text = stats_manager.export_prometheus(); + let prometheus_text = weak_upgrade(&self.global_ctx)? + .stats_manager() + .export_prometheus(); Ok(GetPrometheusStatsResponse { prometheus_text }) } } StatsRpcService { - global_ctx: self.global_ctx.clone(), + global_ctx: Arc::downgrade(&self.global_ctx), } } - async fn run_rpc_server(&mut self) -> Result<(), Error> { - let Some(_) = self.global_ctx.config.get_rpc_portal() else { - tracing::info!("rpc server not enabled, because rpc_portal is not set."); - return Ok(()); - }; + pub fn get_config_patcher(&self) -> InstanceConfigPatcher { + InstanceConfigPatcher { + global_ctx: Arc::downgrade(&self.global_ctx), + socks5_server: Arc::downgrade(&self.socks5_server), + peer_manager: Arc::downgrade(&self.peer_manager), + conn_manager: Arc::downgrade(&self.conn_manager), + } + } - use crate::proto::cli::*; - - let peer_mgr = self.peer_manager.clone(); - let conn_manager = self.conn_manager.clone(); - let peer_center = self.peer_center.clone(); - let vpn_portal_rpc = self.get_vpn_portal_rpc_service(); - let mapped_listener_manager_rpc = self.get_mapped_listener_manager_rpc_service(); - let port_forward_manager_rpc = self.get_port_forward_manager_rpc_service(); - let stats_rpc_service = self.get_stats_rpc_service(); - - let s = self.rpc_server.as_mut().unwrap(); - let peer_mgr_rpc_service = PeerManagerRpcService::new(peer_mgr.clone()); - s.registry() - .register(PeerManageRpcServer::new(peer_mgr_rpc_service.clone()), ""); - s.registry() - .register(AclManageRpcServer::new(peer_mgr_rpc_service), ""); - s.registry().register( - ConnectorManageRpcServer::new(ConnectorManagerRpcService(conn_manager)), - "", - ); + fn get_config_service(&self) -> impl ConfigRpc + Clone { + #[derive(Clone)] + pub struct ConfigRpcService { + patcher: InstanceConfigPatcher, + global_ctx: Weak, + } - s.registry() - .register(PeerCenterRpcServer::new(peer_center.get_rpc_service()), ""); - s.registry() - .register(VpnPortalRpcServer::new(vpn_portal_rpc), ""); - s.registry().register( - MappedListenerManageRpcServer::new(mapped_listener_manager_rpc), - "", - ); - s.registry().register( - PortForwardManageRpcServer::new(port_forward_manager_rpc), - "", - ); - s.registry().register( - crate::proto::cli::StatsRpcServer::new(stats_rpc_service), - "", - ); + #[async_trait::async_trait] + impl ConfigRpc for ConfigRpcService { + type Controller = BaseController; - if let Some(ip_proxy) = self.ip_proxy.as_ref() { - s.registry().register( - TcpProxyRpcServer::new(TcpProxyRpcService::new(ip_proxy.tcp_proxy.clone())), - "tcp", - ); - } - if let Some(kcp_proxy) = self.kcp_proxy_src.as_ref() { - s.registry().register( - TcpProxyRpcServer::new(TcpProxyRpcService::new(kcp_proxy.get_tcp_proxy())), - "kcp_src", - ); + async fn patch_config( + &self, + _: Self::Controller, + request: PatchConfigRequest, + ) -> crate::proto::rpc_types::error::Result { + let Some(patch) = request.patch else { + return Ok(PatchConfigResponse::default()); + }; + + self.patcher.apply_patch(patch).await?; + Ok(PatchConfigResponse::default()) + } + + async fn get_config( + &self, + _: Self::Controller, + _request: GetConfigRequest, + ) -> crate::proto::rpc_types::error::Result { + let global_ctx = weak_upgrade(&self.global_ctx)?; + let config = NetworkConfig::new_from_config(&global_ctx.config)?; + Ok(GetConfigResponse { + config: Some(config), + }) + } } - if let Some(kcp_proxy) = self.kcp_proxy_dst.as_ref() { - s.registry().register( - TcpProxyRpcServer::new(KcpProxyDstRpcService::new(kcp_proxy)), - "kcp_dst", - ); + ConfigRpcService { + patcher: self.get_config_patcher(), + global_ctx: Arc::downgrade(&self.global_ctx), } + } - if let Some(quic_proxy) = self.quic_proxy_src.as_ref() { - s.registry().register( - TcpProxyRpcServer::new(TcpProxyRpcService::new(quic_proxy.get_tcp_proxy())), - "quic_src", - ); + pub fn get_api_rpc_service(&self) -> impl InstanceRpcService { + use crate::proto::api::instance::*; + + #[derive(Clone)] + struct ApiRpcServiceImpl { + peer_mgr_rpc_service: A, + connector_mgr_rpc_service: B, + mapped_listener_mgr_rpc_service: C, + vpn_portal_rpc_service: D, + tcp_proxy_rpc_services: dashmap::DashMap< + String, + Arc + Send + Sync>, + >, + acl_manage_rpc_service: E, + port_forward_manage_rpc_service: F, + stats_rpc_service: G, + config_rpc_service: H, } - if let Some(quic_proxy) = self.quic_proxy_dst.as_ref() { - s.registry().register( - TcpProxyRpcServer::new(QUICProxyDstRpcService::new(quic_proxy)), - "quic_dst", - ); + #[async_trait::async_trait] + impl< + A: PeerManageRpc + Send + Sync, + B: ConnectorManageRpc + Send + Sync, + C: MappedListenerManageRpc + Send + Sync, + D: VpnPortalRpc + Send + Sync, + E: AclManageRpc + Send + Sync, + F: PortForwardManageRpc + Send + Sync, + G: StatsRpc + Send + Sync, + H: ConfigRpc + Send + Sync, + > InstanceRpcService for ApiRpcServiceImpl + { + fn get_peer_manage_service(&self) -> &dyn PeerManageRpc { + &self.peer_mgr_rpc_service + } + + fn get_connector_manage_service( + &self, + ) -> &dyn ConnectorManageRpc { + &self.connector_mgr_rpc_service + } + + fn get_mapped_listener_manage_service( + &self, + ) -> &dyn MappedListenerManageRpc { + &self.mapped_listener_mgr_rpc_service + } + + fn get_vpn_portal_service(&self) -> &dyn VpnPortalRpc { + &self.vpn_portal_rpc_service + } + + fn get_proxy_service( + &self, + client_type: &str, + ) -> Option + Send + Sync>> + { + self.tcp_proxy_rpc_services + .get(client_type) + .map(|e| e.clone()) + } + + fn get_acl_manage_service(&self) -> &dyn AclManageRpc { + &self.acl_manage_rpc_service + } + + fn get_port_forward_manage_service( + &self, + ) -> &dyn PortForwardManageRpc { + &self.port_forward_manage_rpc_service + } + + fn get_stats_service(&self) -> &dyn StatsRpc { + &self.stats_rpc_service + } + + fn get_config_service(&self) -> &dyn ConfigRpc { + &self.config_rpc_service + } } - s.set_hook(Arc::new(InstanceRpcServerHook::new( - self.global_ctx.config.get_rpc_portal_whitelist(), - ))); + ApiRpcServiceImpl { + peer_mgr_rpc_service: PeerManagerRpcService::new(self.peer_manager.clone()), + connector_mgr_rpc_service: ConnectorManagerRpcService(Arc::downgrade( + &self.conn_manager, + )), + mapped_listener_mgr_rpc_service: self.get_mapped_listener_manager_rpc_service(), + vpn_portal_rpc_service: self.get_vpn_portal_rpc_service(), + tcp_proxy_rpc_services: { + let tcp_proxy_rpc_services: dashmap::DashMap< + String, + Arc + Send + Sync>, + > = dashmap::DashMap::new(); + + if let Some(ip_proxy) = self.ip_proxy.as_ref() { + tcp_proxy_rpc_services.insert( + "tcp".to_string(), + Arc::new(TcpProxyRpcService::new(ip_proxy.tcp_proxy.clone())), + ); + } + if let Some(kcp_proxy) = self.kcp_proxy_src.as_ref() { + tcp_proxy_rpc_services.insert( + "kcp_src".to_string(), + Arc::new(TcpProxyRpcService::new(kcp_proxy.get_tcp_proxy())), + ); + } + + if let Some(kcp_proxy) = self.kcp_proxy_dst.as_ref() { + tcp_proxy_rpc_services.insert( + "kcp_dst".to_string(), + Arc::new(KcpProxyDstRpcService::new(kcp_proxy)), + ); + } + + if let Some(quic_proxy) = self.quic_proxy_src.as_ref() { + tcp_proxy_rpc_services.insert( + "quic_src".to_string(), + Arc::new(TcpProxyRpcService::new(quic_proxy.get_tcp_proxy())), + ); + } - let _g = self.global_ctx.net_ns.guard(); - Ok(s.serve().await.with_context(|| "rpc server start failed")?) + if let Some(quic_proxy) = self.quic_proxy_dst.as_ref() { + tcp_proxy_rpc_services.insert( + "quic_dst".to_string(), + Arc::new(QUICProxyDstRpcService::new(quic_proxy)), + ); + } + + tcp_proxy_rpc_services + }, + acl_manage_rpc_service: PeerManagerRpcService::new(self.peer_manager.clone()), + port_forward_manage_rpc_service: self.get_port_forward_manager_rpc_service(), + stats_rpc_service: self.get_stats_rpc_service(), + config_rpc_service: self.get_config_service(), + } } pub fn get_global_ctx(&self) -> ArcGlobalCtx { @@ -1038,10 +1400,12 @@ impl Instance { if fd <= 0 { return Ok(()); } + let close_notifier = Arc::new(Notify::new()); let mut new_nic_ctx = NicCtx::new( global_ctx.clone(), &peer_manager, peer_packet_receiver.clone(), + close_notifier.clone(), ); new_nic_ctx .run_for_android(fd) @@ -1060,9 +1424,6 @@ impl Instance { pub async fn clear_resources(&mut self) { self.peer_manager.clear_resources().await; let _ = self.nic_ctx.lock().await.take(); - if let Some(rpc_server) = self.rpc_server.take() { - rpc_server.registry().unregister_all(); - }; } } @@ -1071,9 +1432,6 @@ impl Drop for Instance { let my_peer_id = self.peer_manager.my_peer_id(); let pm = Arc::downgrade(&self.peer_manager); let nic_ctx = self.nic_ctx.clone(); - if let Some(rpc_server) = self.rpc_server.take() { - rpc_server.registry().unregister_all(); - }; tokio::spawn(async move { nic_ctx.lock().await.take(); if let Some(pm) = pm.upgrade() { diff --git a/easytier/src/instance/virtual_nic.rs b/easytier/src/instance/virtual_nic.rs index 44b32eb2c..84bdfd05c 100644 --- a/easytier/src/instance/virtual_nic.rs +++ b/easytier/src/instance/virtual_nic.rs @@ -29,7 +29,7 @@ use pin_project_lite::pin_project; use pnet::packet::{ipv4::Ipv4Packet, ipv6::Ipv6Packet}; use tokio::{ io::{AsyncRead, AsyncWrite, ReadBuf}, - sync::Mutex, + sync::{Mutex, Notify}, task::JoinSet, }; use tokio_util::bytes::Bytes; @@ -127,10 +127,7 @@ impl PacketProtocol { match self { PacketProtocol::IPv4 => Ok(libc::PF_INET as u16), PacketProtocol::IPv6 => Ok(libc::PF_INET6 as u16), - PacketProtocol::Other(_) => Err(io::Error::new( - io::ErrorKind::Other, - "neither an IPv4 nor IPv6 packet", - )), + PacketProtocol::Other(_) => Err(io::Error::other("neither an IPv4 nor IPv6 packet")), } } @@ -399,7 +396,7 @@ impl VirtualNic { Err(e) => { println!("Failed to add Easytier to firewall allowlist, Subnet proxy and KCP proxy may not work properly. error: {}", e); println!("You can add firewall rules manually, or use --use-smoltcp to run with user-space TCP/IP stack."); - println!(""); + println!(); } } @@ -409,7 +406,7 @@ impl VirtualNic { } if !dev_name.is_empty() { - config.tun_name(format!("{}", dev_name)); + config.tun_name(&dev_name); } else { use rand::distributions::Distribution as _; let c = crate::arch::windows::interface_count()?; @@ -626,6 +623,8 @@ pub struct NicCtx { peer_mgr: Weak, peer_packet_receiver: Arc>, + close_notifier: Arc, + nic: Arc>, tasks: JoinSet<()>, } @@ -635,11 +634,15 @@ impl NicCtx { global_ctx: ArcGlobalCtx, peer_manager: &Arc, peer_packet_receiver: Arc>, + close_notifier: Arc, ) -> Self { NicCtx { global_ctx: global_ctx.clone(), peer_mgr: Arc::downgrade(peer_manager), peer_packet_receiver, + + close_notifier, + nic: Arc::new(Mutex::new(VirtualNic::new(global_ctx))), tasks: JoinSet::new(), } @@ -753,6 +756,7 @@ impl NicCtx { let Some(mgr) = self.peer_mgr.upgrade() else { return Err(anyhow::anyhow!("peer manager not available").into()); }; + let close_notifier = self.close_notifier.clone(); self.tasks.spawn(async move { while let Some(ret) = stream.next().await { if ret.is_err() { @@ -761,7 +765,8 @@ impl NicCtx { } Self::do_forward_nic_to_peers(ret.unwrap(), mgr.as_ref()).await; } - panic!("nic stream closed"); + close_notifier.notify_one(); + tracing::error!("nic closed when recving from it"); }); Ok(()) @@ -769,6 +774,7 @@ impl NicCtx { fn do_forward_peers_to_nic(&mut self, mut sink: Pin>) { let channel = self.peer_packet_receiver.clone(); + let close_notifier = self.close_notifier.clone(); self.tasks.spawn(async move { // unlock until coroutine finished let mut channel = channel.lock().await; @@ -782,7 +788,8 @@ impl NicCtx { tracing::error!(?ret, "do_forward_tunnel_to_nic sink error"); } } - panic!("peer packet receiver closed"); + close_notifier.notify_one(); + tracing::error!("nic closed when sending to it"); }); } @@ -894,7 +901,7 @@ impl NicCtx { // remove the 10.0.0.0/24 route (which is added by rust-tun by default) let _ = nic .ifcfg - .remove_ipv4_route(&nic.ifname(), "10.0.0.0".parse().unwrap(), 24) + .remove_ipv4_route(nic.ifname(), "10.0.0.0".parse().unwrap(), 24) .await; } diff --git a/easytier/src/instance_manager.rs b/easytier/src/instance_manager.rs index 1ec399bde..d2dc7a70b 100644 --- a/easytier/src/instance_manager.rs +++ b/easytier/src/instance_manager.rs @@ -9,7 +9,8 @@ use crate::{ scoped_task::ScopedTask, }, launcher::{ConfigSource, NetworkInstance, NetworkInstanceRunningInfo}, - proto, + proto::{self}, + rpc_service::InstanceRpcService, }; pub struct NetworkInstanceManager { @@ -122,18 +123,35 @@ impl NetworkInstanceManager { Ok(self.list_network_instance_ids()) } - pub fn collect_network_infos( + pub async fn collect_network_infos( &self, ) -> Result, anyhow::Error> { let mut ret = BTreeMap::new(); for instance in self.instance_map.iter() { - if let Some(info) = instance.get_running_info() { + if let Ok(info) = instance.get_running_info().await { ret.insert(*instance.key(), info); } } Ok(ret) } + pub fn collect_network_infos_sync( + &self, + ) -> Result, anyhow::Error> { + tokio::runtime::Runtime::new()?.block_on(self.collect_network_infos()) + } + + pub async fn get_network_info( + &self, + instance_id: &uuid::Uuid, + ) -> Option { + self.instance_map + .get(instance_id)? + .get_running_info() + .await + .ok() + } + pub fn list_network_instance_ids(&self) -> Vec { self.instance_map.iter().map(|item| *item.key()).collect() } @@ -144,6 +162,26 @@ impl NetworkInstanceManager { .map(|instance| instance.value().get_inst_name()) } + pub fn filter_network_instance( + &self, + filter: impl Fn(&uuid::Uuid, &NetworkInstance) -> bool, + ) -> Vec { + self.instance_map + .iter() + .filter(|item| filter(item.key(), item.value())) + .map(|item| *item.key()) + .collect() + } + + pub fn get_instance_service( + &self, + instance_id: &uuid::Uuid, + ) -> Option> { + self.instance_map + .get(instance_id) + .and_then(|instance| instance.value().get_api_service()) + } + pub fn set_tun_fd(&self, instance_id: &uuid::Uuid, fd: i32) -> Result<(), anyhow::Error> { let mut instance = self .instance_map @@ -264,6 +302,13 @@ fn handle_event( ); } + GlobalCtxEvent::VpnPortalStarted(portal) => { + print_event( + instance_id, + format!("vpn portal started. portal: {}", portal), + ); + } + GlobalCtxEvent::VpnPortalClientConnected(portal, client_addr) => { print_event( instance_id, @@ -306,6 +351,10 @@ fn handle_event( ), ); } + + GlobalCtxEvent::ConfigPatched(patch) => { + print_event(instance_id, format!("config patched. patch: {:?}", patch)); + } } } else { events = events.resubscribe(); @@ -323,7 +372,7 @@ fn print_event(instance_id: uuid::Uuid, msg: String) { ); } -fn peer_conn_info_to_string(p: proto::cli::PeerConnInfo) -> String { +fn peer_conn_info_to_string(p: proto::api::instance::PeerConnInfo) -> String { format!( "my_peer_id: {}, dst_peer_id: {}, tunnel_info: {:?}", p.my_peer_id, p.peer_id, p.tunnel diff --git a/easytier/src/launcher.rs b/easytier/src/launcher.rs index 897322d7b..2fd08526d 100644 --- a/easytier/src/launcher.rs +++ b/easytier/src/launcher.rs @@ -1,5 +1,7 @@ use crate::common::config::PortForwardConfig; -use crate::proto::web; +use crate::proto::api::{self, manage}; +use crate::proto::rpc_types::controller::BaseController; +use crate::rpc_service::InstanceRpcService; use crate::{ common::{ config::{ @@ -8,11 +10,9 @@ use crate::{ }, constants::EASYTIER_VERSION, global_ctx::{EventBusSubscriber, GlobalCtxEvent}, - stun::StunInfoCollectorTrait, }, instance::instance::Instance, - peers::rpc_service::PeerManagerRpcService, - proto::cli::{list_peer_route_pair, PeerInfo, Route}, + proto::api::instance::list_peer_route_pair, }; use anyhow::Context; use chrono::{DateTime, Local}; @@ -24,7 +24,9 @@ use std::{ use tokio::{sync::broadcast, task::JoinSet}; use crate::helper::{g_peermanager, set_running_state}; -pub type MyNodeInfo = crate::proto::web::MyNodeInfo; +pub type MyNodeInfo = crate::proto::api::manage::MyNodeInfo; + +type ArcMutApiService = Arc>>>; #[derive(serde::Serialize, Clone)] pub struct Event { @@ -34,11 +36,7 @@ pub struct Event { struct EasyTierData { events: RwLock>, - my_node_info: RwLock, - routes: RwLock>, - peers: RwLock>, tun_fd: Arc>>, - tun_dev_name: RwLock, event_subscriber: RwLock>, instance_stop_notifier: Arc, } @@ -49,11 +47,7 @@ impl Default for EasyTierData { Self { event_subscriber: RwLock::new(tx), events: RwLock::new(VecDeque::new()), - my_node_info: RwLock::new(MyNodeInfo::default()), - routes: RwLock::new(Vec::new()), - peers: RwLock::new(Vec::new()), tun_fd: Arc::new(RwLock::new(None)), - tun_dev_name: RwLock::new(String::new()), instance_stop_notifier: Arc::new(tokio::sync::Notify::new()), } } @@ -63,23 +57,21 @@ pub struct EasyTierLauncher { instance_alive: Arc, stop_flag: Arc, thread_handle: Option>, + api_service: ArcMutApiService, running_cfg: String, - fetch_node_info: bool, - error_msg: Arc>>, data: Arc, } impl EasyTierLauncher { - pub fn new(fetch_node_info: bool) -> Self { + pub fn new() -> Self { let instance_alive = Arc::new(AtomicBool::new(false)); Self { instance_alive, thread_handle: None, + api_service: Arc::new(RwLock::new(None)), error_msg: Arc::new(RwLock::new(None)), running_cfg: String::new(), - fetch_node_info, - stop_flag: Arc::new(AtomicBool::new(false)), data: Arc::new(EasyTierData::default()), } @@ -134,8 +126,8 @@ impl EasyTierLauncher { async fn easytier_routine( cfg: TomlConfigLoader, stop_signal: Arc, + api_service: ArcMutApiService, data: Arc, - fetch_node_info: bool, ) -> Result<(), anyhow::Error> { let mut instance = Instance::new(cfg); let mut tasks = JoinSet::new(); @@ -145,88 +137,32 @@ impl EasyTierLauncher { *guard = Some(instance.get_peer_manager()); } - { - // Subscribe to global context events - let data_c = data.clone(); - let global_ctx_c = instance.get_global_ctx(); - let peer_mgr_c = instance.get_peer_manager().clone(); - let vpn_portal = instance.get_vpn_portal_inst(); - tasks.spawn(async move { - loop { - // Update TUN Device Name - *data_c.tun_dev_name.write().unwrap() = - global_ctx_c.get_flags().dev_name.clone(); - - let node_info = MyNodeInfo { - virtual_ipv4: global_ctx_c.get_ipv4().map(|ip| ip.into()), - hostname: global_ctx_c.get_hostname(), - version: EASYTIER_VERSION.to_string(), - ips: Some(global_ctx_c.get_ip_collector().collect_ip_addrs().await), - stun_info: Some(global_ctx_c.get_stun_info_collector().get_stun_info()), - listeners: global_ctx_c - .get_running_listeners() - .into_iter() - .map(Into::into) - .collect(), - vpn_portal_cfg: Some( - vpn_portal - .lock() - .await - .dump_client_config(peer_mgr_c.clone()) - .await, - ), - }; - *data_c.my_node_info.write().unwrap() = node_info.clone(); - *data_c.routes.write().unwrap() = peer_mgr_c.list_routes().await; - *data_c.peers.write().unwrap() = - PeerManagerRpcService::list_peers(&peer_mgr_c).await; - tokio::time::sleep(std::time::Duration::from_secs(1)).await; - } - }); + api_service + .write() + .unwrap() + .replace(Arc::new(instance.get_api_rpc_service())); + drop(api_service); - // update my node info - if fetch_node_info { - let data_c = data.clone(); - let global_ctx_c = instance.get_global_ctx(); - let peer_mgr_c = instance.get_peer_manager().clone(); - let vpn_portal = instance.get_vpn_portal_inst(); - tasks.spawn(async move { - loop { - // Update TUN Device Name - *data_c.tun_dev_name.write().unwrap() = - global_ctx_c.get_flags().dev_name.clone(); - - let node_info = MyNodeInfo { - virtual_ipv4: global_ctx_c.get_ipv4().map(|ip| ip.into()), - hostname: global_ctx_c.get_hostname(), - version: EASYTIER_VERSION.to_string(), - ips: Some(global_ctx_c.get_ip_collector().collect_ip_addrs().await), - stun_info: Some(global_ctx_c.get_stun_info_collector().get_stun_info()), - listeners: global_ctx_c - .get_running_listeners() - .into_iter() - .map(Into::into) - .collect(), - vpn_portal_cfg: Some( - vpn_portal - .lock() - .await - .dump_client_config(peer_mgr_c.clone()) - .await, - ), - }; - *data_c.my_node_info.write().unwrap() = node_info.clone(); - *data_c.routes.write().unwrap() = peer_mgr_c.list_routes().await; - *data_c.peers.write().unwrap() = - PeerManagerRpcService::list_peers(&peer_mgr_c).await; - tokio::time::sleep(std::time::Duration::from_secs(1)).await; + // Subscribe to global context events + let global_ctx = instance.get_global_ctx(); + let data_c = data.clone(); + tasks.spawn(async move { + let mut receiver = global_ctx.subscribe(); + loop { + match receiver.recv().await { + Ok(event) => { + Self::handle_easytier_event(event.clone(), &data_c).await; } - }); + Err(broadcast::error::RecvError::Closed) => { + break; + } + Err(broadcast::error::RecvError::Lagged(_)) => { + // do nothing currently + receiver = receiver.resubscribe(); + } + } } - - #[cfg(target_os = "android")] - Self::run_routine_for_android(&instance, &data, &mut tasks).await; - } + }); #[cfg(any(target_os = "android", target_env = "ohos"))] Self::run_routine_for_android(&instance, &data, &mut tasks).await; @@ -249,21 +185,6 @@ impl EasyTierLauncher { Ok(()) } - fn select_proper_rpc_port(cfg: &TomlConfigLoader) { - let Some(mut f) = cfg.get_rpc_portal() else { - return; - }; - - if f.port() == 0 { - let Some(port) = crate::utils::find_free_tcp_port(15888..15900) else { - tracing::warn!("No free port found for RPC portal, skipping setting RPC portal"); - return; - }; - f.set_port(port); - cfg.set_rpc_portal(f); - } - } - pub fn start(&mut self, cfg_generator: F) where F: FnOnce() -> Result + Send + Sync, @@ -279,15 +200,13 @@ impl EasyTierLauncher { self.running_cfg = cfg.dump(); - Self::select_proper_rpc_port(&cfg); - let stop_flag = self.stop_flag.clone(); let instance_alive = self.instance_alive.clone(); instance_alive.store(true, std::sync::atomic::Ordering::Relaxed); let data = self.data.clone(); - let fetch_node_info = self.fetch_node_info; + let api_service = self.api_service.clone(); self.thread_handle = Some(std::thread::spawn(move || { let rt = if cfg.get_flags().multi_thread { @@ -317,8 +236,8 @@ impl EasyTierLauncher { let ret = rt.block_on(Self::easytier_routine( cfg, stop_notifier.clone(), + api_service, data, - fetch_node_info, )); if let Err(e) = ret { error_msg.write().unwrap().replace(format!("{:?}", e)); @@ -337,25 +256,25 @@ impl EasyTierLauncher { .load(std::sync::atomic::Ordering::Relaxed) } - pub fn get_dev_name(&self) -> String { - self.data.tun_dev_name.read().unwrap().clone() - } - pub fn get_events(&self) -> Vec { let events = self.data.events.read().unwrap(); events.iter().cloned().collect() } - pub fn get_node_info(&self) -> MyNodeInfo { - self.data.my_node_info.read().unwrap().clone() - } - - pub fn get_routes(&self) -> Vec { - self.data.routes.read().unwrap().clone() + pub fn get_api_service(&self) -> Option> { + match self.api_service.read() { + Ok(guard) => guard.clone(), + Err(e) => { + tracing::error!("Failed to acquire read lock for api_service: {:?}", e); + None + } + } } +} - pub fn get_peers(&self) -> Vec { - self.data.peers.read().unwrap().clone() +impl Default for EasyTierLauncher { + fn default() -> Self { + Self::new() } } @@ -371,7 +290,7 @@ impl Drop for EasyTierLauncher { } } -pub type NetworkInstanceRunningInfo = crate::proto::web::NetworkInstanceRunningInfo; +pub type NetworkInstanceRunningInfo = crate::proto::api::manage::NetworkInstanceRunningInfo; #[derive(Debug, Clone, PartialEq, Eq)] pub enum ConfigSource { @@ -398,12 +317,6 @@ impl NetworkInstance { } } - fn get_fetch_node_info(&self) -> bool { - match self.config_source { - ConfigSource::Cli | ConfigSource::File => false, - ConfigSource::Web | ConfigSource::GUI | ConfigSource::FFI => true, - } - } pub fn get_config_source(&self) -> ConfigSource { self.config_source.clone() @@ -413,18 +326,77 @@ impl NetworkInstance { self.launcher.is_some() && self.launcher.as_ref().unwrap().running() } - pub fn get_running_info(&self) -> Option { - self.launcher.as_ref()?; - - let launcher = self.launcher.as_ref().unwrap(); - - let peers = launcher.get_peers(); - let routes = launcher.get_routes(); + pub async fn get_running_info(&self) -> anyhow::Result { + let launcher = self.launcher.as_ref().ok_or_else(|| { + anyhow::anyhow!("instance is not running, please start the instance first") + })?; + let api_service = self.get_api_service().ok_or_else(|| { + anyhow::anyhow!("failed to get api service, instance may not be running") + })?; + let ctrl = BaseController::default(); + + let peers = api_service + .get_peer_manage_service() + .list_peer(ctrl.clone(), api::instance::ListPeerRequest::default()) + .await? + .peer_infos; + let my_info = api_service + .get_peer_manage_service() + .show_node_info(ctrl.clone(), api::instance::ShowNodeInfoRequest::default()) + .await? + .node_info + .ok_or_else(|| anyhow::anyhow!("failed to get my node info"))?; + let vpn_portal_cfg = api_service + .get_vpn_portal_service() + .get_vpn_portal_info( + ctrl.clone(), + api::instance::GetVpnPortalInfoRequest::default(), + ) + .await? + .vpn_portal_info + .map(|i| i.client_config); + let routes = api_service + .get_peer_manage_service() + .list_route(ctrl.clone(), api::instance::ListRouteRequest::default()) + .await? + .routes; let peer_route_pairs = list_peer_route_pair(peers.clone(), routes.clone()); - - Some(NetworkInstanceRunningInfo { - dev_name: launcher.get_dev_name(), - my_node_info: Some(launcher.get_node_info()), + let foreign_network_summary = api_service + .get_peer_manage_service() + .get_foreign_network_summary( + ctrl.clone(), + api::instance::GetForeignNetworkSummaryRequest::default(), + ) + .await? + .summary; + let dev_name = api_service + .get_config_service() + .get_config(ctrl.clone(), api::config::GetConfigRequest::default()) + .await? + .config + .ok_or_else(|| anyhow::anyhow!("failed to get config"))? + .dev_name + .unwrap_or_else(|| "".to_string()); + + Ok(NetworkInstanceRunningInfo { + dev_name, + my_node_info: Some(MyNodeInfo { + virtual_ipv4: my_info + .ipv4_addr + .parse::() + .ok() + .map(Into::into), + hostname: my_info.hostname, + version: EASYTIER_VERSION.to_string(), + ips: my_info.ip_list, + stun_info: my_info.stun_info, + listeners: my_info + .listeners + .into_iter() + .map(|s| s.parse::().unwrap().into()) + .collect(), + vpn_portal_cfg, + }), events: launcher .get_events() .iter() @@ -435,6 +407,7 @@ impl NetworkInstance { peer_route_pairs, running: launcher.running(), error_msg: launcher.error_msg(), + foreign_network_summary, }) } @@ -453,7 +426,7 @@ impl NetworkInstance { return Ok(self.subscribe_event().unwrap()); } - let launcher = EasyTierLauncher::new(self.get_fetch_node_info()); + let launcher = EasyTierLauncher::new(); self.launcher = Some(launcher); let ev = self.subscribe_event().unwrap(); @@ -484,6 +457,12 @@ impl NetworkInstance { None } } + + pub fn get_api_service(&self) -> Option> { + self.launcher + .as_ref() + .and_then(|launcher| launcher.get_api_service()) + } } pub fn add_proxy_network_to_config( @@ -516,8 +495,8 @@ pub fn add_proxy_network_to_config( Ok(()) } -pub type NetworkingMethod = crate::proto::web::NetworkingMethod; -pub type NetworkConfig = crate::proto::web::NetworkConfig; +pub type NetworkingMethod = crate::proto::api::manage::NetworkingMethod; +pub type NetworkConfig = crate::proto::api::manage::NetworkConfig; impl NetworkConfig { pub fn gen_config(&self) -> Result { @@ -598,26 +577,6 @@ impl NetworkConfig { add_proxy_network_to_config(n, &cfg)?; } - cfg.set_rpc_portal( - format!("0.0.0.0:{}", self.rpc_port.unwrap_or_default()) - .parse() - .with_context(|| format!("failed to parse rpc portal port: {:?}", self.rpc_port))?, - ); - - if self.rpc_portal_whitelists.is_empty() { - cfg.set_rpc_portal_whitelist(None); - } else { - cfg.set_rpc_portal_whitelist(Some( - self.rpc_portal_whitelists - .iter() - .map(|s| { - s.parse() - .with_context(|| format!("failed to parse rpc portal whitelist: {}", s)) - }) - .collect::, _>>()?, - )); - } - if !self.port_forwards.is_empty() { cfg.set_port_forwards( self.port_forwards @@ -641,7 +600,7 @@ impl NetworkConfig { .collect::>(), ); } - + if self.enable_vpn_portal.unwrap_or_default() { let cidr = format!( "{}/{}", @@ -795,6 +754,10 @@ impl NetworkConfig { flags.disable_udp_hole_punching = disable_udp_hole_punching; } + if let Some(disable_sym_hole_punching) = self.disable_sym_hole_punching { + flags.disable_sym_hole_punching = disable_sym_hole_punching; + } + if let Some(enable_magic_dns) = self.enable_magic_dns { flags.accept_dns = enable_magic_dns; } @@ -811,7 +774,7 @@ impl NetworkConfig { Ok(cfg) } - pub fn new_from_config(config: &TomlConfigLoader) -> Result { + pub fn new_from_config(config: impl ConfigLoader) -> Result { let default_config = TomlConfigLoader::default(); let mut result = Self { @@ -868,19 +831,11 @@ impl NetworkConfig { }) .collect(); - if let Some(rpc_portal) = config.get_rpc_portal() { - result.rpc_port = Some(rpc_portal.port() as i32); - } - - if let Some(whitelist) = config.get_rpc_portal_whitelist() { - result.rpc_portal_whitelists = whitelist.iter().map(|w| w.to_string()).collect(); - } - let port_forwards = config.get_port_forwards(); if !port_forwards.is_empty() { result.port_forwards = port_forwards .iter() - .map(|f| web::PortForwardConfig { + .map(|f| manage::PortForwardConfig { proto: f.proto.clone(), bind_ip: f.bind_addr.ip().to_string(), bind_port: f.bind_addr.port() as u32, @@ -969,7 +924,6 @@ mod tests { config.set_dhcp(false); config.set_inst_name("default".to_string()); config.set_listeners(vec![]); - config.set_rpc_portal(std::net::SocketAddr::from(([0, 0, 0, 0], 0))); config } @@ -1081,27 +1035,6 @@ mod tests { } } - if rng.gen_bool(0.8) { - let port = rng.gen_range(0..65535); - config.set_rpc_portal(std::net::SocketAddr::from(([0, 0, 0, 0], port))); - - if rng.gen_bool(0.6) { - let whitelist_count = rng.gen_range(1..3); - let mut whitelist = Vec::new(); - for _ in 0..whitelist_count { - let ip = Ipv4Addr::new( - rng.gen_range(1..254), - rng.gen_range(0..255), - rng.gen_range(0..255), - rng.gen_range(0..255), - ); - let cidr = format!("{}/32", ip); - whitelist.push(cidr.parse().unwrap()); - } - config.set_rpc_portal_whitelist(Some(whitelist)); - } - } - if rng.gen_bool(0.5) { let vpn_network = format!( "{}.{}.{}.0/{}", diff --git a/easytier/src/lib.rs b/easytier/src/lib.rs index 88bdab3fe..6455f6195 100644 --- a/easytier/src/lib.rs +++ b/easytier/src/lib.rs @@ -8,7 +8,7 @@ use clap_complete::Generator; mod arch; mod easytier_core; mod gateway; -mod instance; +pub mod instance; mod peer_center; mod vpn_portal; @@ -18,6 +18,7 @@ pub mod instance_manager; pub mod launcher; pub mod peers; pub mod proto; +pub mod rpc_service; pub mod tunnel; pub mod utils; pub mod web_client; diff --git a/easytier/src/peer_center/instance.rs b/easytier/src/peer_center/instance.rs index 430acad60..90f88d0e8 100644 --- a/easytier/src/peer_center/instance.rs +++ b/easytier/src/peer_center/instance.rs @@ -39,7 +39,7 @@ pub trait PeerCenterPeerManagerTrait: Send + Sync + 'static { fn my_peer_id(&self) -> PeerId; fn get_global_ctx(&self) -> Arc; fn get_rpc_mgr(&self) -> Weak; - async fn list_routes(&self) -> Vec; + async fn list_routes(&self) -> Vec; } struct PeerCenterBase { @@ -426,7 +426,7 @@ impl PeerCenterPeerManagerTrait for PeerManager { Arc::downgrade(&self.get_peer_rpc_mgr()) } - async fn list_routes(&self) -> Vec { + async fn list_routes(&self) -> Vec { self.list_routes().await } } @@ -478,7 +478,7 @@ impl PeerCenterPeerManagerTrait for PeerMapWithPeerRpcManager { Arc::downgrade(&self.rpc_mgr) } - async fn list_routes(&self) -> Vec { + async fn list_routes(&self) -> Vec { self.peer_map.list_route_infos().await } } diff --git a/easytier/src/peer_center/mod.rs b/easytier/src/peer_center/mod.rs index f1e4ec775..690f20741 100644 --- a/easytier/src/peer_center/mod.rs +++ b/easytier/src/peer_center/mod.rs @@ -7,7 +7,7 @@ use std::collections::BTreeMap; -use crate::proto::cli::PeerInfo; +use crate::proto::api::instance::PeerInfo; use crate::proto::peer_rpc::{DirectConnectedPeerInfo, PeerInfoForGlobalMap}; pub mod instance; diff --git a/easytier/src/peers/acl_filter.rs b/easytier/src/peers/acl_filter.rs index 4fdfd93d5..180865a7e 100644 --- a/easytier/src/peers/acl_filter.rs +++ b/easytier/src/peers/acl_filter.rs @@ -1,5 +1,5 @@ use std::net::{Ipv4Addr, Ipv6Addr}; -use std::sync::atomic::Ordering; +use std::sync::atomic::{AtomicU16, Ordering}; use std::{ net::IpAddr, sync::{atomic::AtomicBool, Arc}, @@ -25,6 +25,7 @@ pub struct AclFilter { // Use ArcSwap for lock-free atomic replacement during hot reload acl_processor: ArcSwap, acl_enabled: Arc, + quic_udp_port: AtomicU16, } impl Default for AclFilter { @@ -38,6 +39,7 @@ impl AclFilter { Self { acl_processor: ArcSwap::from(Arc::new(AclProcessor::new(Acl::default()))), acl_enabled: Arc::new(AtomicBool::new(false)), + quic_udp_port: AtomicU16::new(0), } } @@ -88,7 +90,11 @@ impl AclFilter { } /// Extract packet information for ACL processing - fn extract_packet_info(&self, packet: &ZCPacket) -> Option { + fn extract_packet_info( + &self, + packet: &ZCPacket, + route: &(dyn super::route_trait::Route + Send + Sync + 'static), + ) -> Option { let payload = packet.payload(); let src_ip; @@ -155,6 +161,15 @@ impl AclFilter { _ => Protocol::Unspecified, }; + let src_groups = packet + .get_src_peer_id() + .map(|peer_id| route.get_peer_groups(peer_id)) + .unwrap_or_else(|| Arc::new(Vec::new())); + let dst_groups = packet + .get_dst_peer_id() + .map(|peer_id| route.get_peer_groups(peer_id)) + .unwrap_or_else(|| Arc::new(Vec::new())); + Some(PacketInfo { src_ip, dst_ip, @@ -162,6 +177,8 @@ impl AclFilter { dst_port, protocol: acl_protocol, packet_size: payload.len(), + src_groups, + dst_groups, }) } @@ -181,6 +198,8 @@ impl AclFilter { dst_ip = %packet_info.dst_ip, src_port = packet_info.src_port, dst_port = packet_info.dst_port, + src_group = packet_info.src_groups.join(","), + dst_group = packet_info.dst_groups.join(","), protocol = ?packet_info.protocol, action = ?result.action, rule = result.matched_rule_str().as_deref().unwrap_or("unknown"), @@ -226,6 +245,40 @@ impl AclFilter { processor.increment_stat(AclStatKey::PacketsTotal); } + fn check_is_quic_packet( + &self, + packet_info: &PacketInfo, + my_ipv4: &Option, + my_ipv6: &Option, + ) -> bool { + if packet_info.protocol != Protocol::Udp { + return false; + } + + let quic_port = self.get_quic_udp_port(); + if quic_port == 0 { + return false; + } + + // quic input + if packet_info.dst_port == Some(quic_port) + && (packet_info.dst_ip == my_ipv4.unwrap_or(Ipv4Addr::UNSPECIFIED) + || packet_info.dst_ip == my_ipv6.unwrap_or(Ipv6Addr::UNSPECIFIED)) + { + return true; + } + + // quic output + if packet_info.src_port == Some(quic_port) + && (packet_info.src_ip == my_ipv4.unwrap_or(Ipv4Addr::UNSPECIFIED) + || packet_info.src_ip == my_ipv6.unwrap_or(Ipv6Addr::UNSPECIFIED)) + { + return true; + } + + false + } + /// Common ACL processing logic pub fn process_packet_with_acl( &self, @@ -233,6 +286,7 @@ impl AclFilter { is_in: bool, my_ipv4: Option, my_ipv6: Option, + route: &(dyn super::route_trait::Route + Send + Sync + 'static), ) -> bool { if !self.acl_enabled.load(Ordering::Relaxed) { return true; @@ -243,7 +297,7 @@ impl AclFilter { } // Extract packet information - let packet_info = match self.extract_packet_info(packet) { + let packet_info = match self.extract_packet_info(packet, route) { Some(info) => info, None => { tracing::warn!( @@ -256,6 +310,10 @@ impl AclFilter { } }; + if self.check_is_quic_packet(&packet_info, &my_ipv4, &my_ipv6) { + return true; + } + let chain_type = if is_in { if packet_info.dst_ip == my_ipv4.unwrap_or(Ipv4Addr::UNSPECIFIED) || packet_info.dst_ip == my_ipv6.unwrap_or(Ipv6Addr::UNSPECIFIED) @@ -292,4 +350,12 @@ impl AclFilter { } } } + + pub fn get_quic_udp_port(&self) -> u16 { + self.quic_udp_port.load(Ordering::Relaxed) + } + + pub fn set_quic_udp_port(&self, port: u16) { + self.quic_udp_port.store(port, Ordering::Relaxed); + } } diff --git a/easytier/src/peers/foreign_network_manager.rs b/easytier/src/peers/foreign_network_manager.rs index 5b4dc7476..150ec3ad1 100644 --- a/easytier/src/peers/foreign_network_manager.rs +++ b/easytier/src/peers/foreign_network_manager.rs @@ -2,7 +2,7 @@ foreign_network_manager is used to forward packets of other networks. currently only forward packets of peers that directly connected to this node. -in future, with the help wo peer center we can forward packets of peers that +in the future, with the help wo peer center we can forward packets of peers that connected to any node in the local network. */ use std::{ @@ -32,7 +32,7 @@ use crate::{ peer_center::instance::{PeerCenterInstance, PeerMapWithPeerRpcManager}, peers::route_trait::{Route, RouteInterface}, proto::{ - cli::{ForeignNetworkEntryPb, ListForeignNetworkResponse, PeerInfo}, + api::instance::{ForeignNetworkEntryPb, ListForeignNetworkResponse, PeerInfo}, common::LimiterConfig, peer_rpc::DirectConnectorRpcServer, }, @@ -49,7 +49,7 @@ use super::{ peer_rpc_service::DirectConnectorManagerRpcServer, recv_packet_from_chan, route_trait::NextHopPolicy, - PacketRecvChan, PacketRecvChanReceiver, + PacketRecvChan, PacketRecvChanReceiver, PUBLIC_SERVER_HOSTNAME_PREFIX, }; #[async_trait::async_trait] @@ -161,12 +161,18 @@ impl ForeignNetworkEntry { ) -> ArcGlobalCtx { let config = TomlConfigLoader::default(); config.set_network_identity(network.clone()); - config.set_hostname(Some(format!("PublicServer_{}", global_ctx.get_hostname()))); + config.set_hostname(Some(format!( + "{}{}", + PUBLIC_SERVER_HOSTNAME_PREFIX, + global_ctx.get_hostname() + ))); let mut flags = config.get_flags(); flags.disable_relay_kcp = !global_ctx.get_flags().enable_relay_foreign_network_kcp; config.set_flags(flags); + config.set_mapped_listeners(Some(global_ctx.config.get_mapped_listeners())); + let foreign_global_ctx = Arc::new(GlobalCtx::new(config)); foreign_global_ctx .replace_stun_info_collector(Box::new(global_ctx.get_stun_info_collector().clone())); diff --git a/easytier/src/peers/mod.rs b/easytier/src/peers/mod.rs index fcccfe0c1..d9893dcb5 100644 --- a/easytier/src/peers/mod.rs +++ b/easytier/src/peers/mod.rs @@ -64,3 +64,5 @@ pub async fn recv_packet_from_chan( .await .ok_or(anyhow::anyhow!("recv_packet_from_chan failed")) } + +pub const PUBLIC_SERVER_HOSTNAME_PREFIX: &str = "PublicServer_"; diff --git a/easytier/src/peers/peer.rs b/easytier/src/peers/peer.rs index bdcb712fe..09ccd8d9a 100644 --- a/easytier/src/peers/peer.rs +++ b/easytier/src/peers/peer.rs @@ -11,7 +11,7 @@ use super::{ peer_conn::{PeerConn, PeerConnId}, PacketRecvChan, }; -use crate::{common::scoped_task::ScopedTask, proto::cli::PeerConnInfo}; +use crate::{common::scoped_task::ScopedTask, proto::api::instance::PeerConnInfo}; use crate::{ common::{ error::Error, diff --git a/easytier/src/peers/peer_conn.rs b/easytier/src/peers/peer_conn.rs index 5a45e4634..fe5e70296 100644 --- a/easytier/src/peers/peer_conn.rs +++ b/easytier/src/peers/peer_conn.rs @@ -32,7 +32,7 @@ use crate::{ PeerId, }, proto::{ - cli::{PeerConnInfo, PeerConnStats}, + api::instance::{PeerConnInfo, PeerConnStats}, common::TunnelInfo, peer_rpc::HandshakeRequest, }, diff --git a/easytier/src/peers/peer_manager.rs b/easytier/src/peers/peer_manager.rs index 1a112d321..cf7b8606b 100644 --- a/easytier/src/peers/peer_manager.rs +++ b/easytier/src/peers/peer_manager.rs @@ -32,15 +32,17 @@ use crate::{ peer_conn::PeerConn, peer_rpc::PeerRpcManagerTransport, recv_packet_from_chan, - route_trait::{ForeignNetworkRouteInfoMap, NextHopPolicy, RouteInterface}, + route_trait::{ForeignNetworkRouteInfoMap, MockRoute, NextHopPolicy, RouteInterface}, PeerPacketFilter, }, proto::{ - cli::{ + api::instance::{ self, list_global_foreign_network_response::OneForeignNetwork, ListGlobalForeignNetworkResponse, }, - peer_rpc::{ForeignNetworkRouteInfoEntry, ForeignNetworkRouteInfoKey}, + peer_rpc::{ + ForeignNetworkRouteInfoEntry, ForeignNetworkRouteInfoKey, RouteForeignNetworkSummary, + }, }, tunnel::{ self, @@ -150,7 +152,7 @@ pub struct PeerManager { encryptor: Arc, data_compress_algo: CompressorAlgo, - exit_nodes: Vec, + exit_nodes: RwLock>, reserved_my_peer_id_map: DashMap, @@ -302,7 +304,7 @@ impl PeerManager { encryptor, data_compress_algo, - exit_nodes, + exit_nodes: RwLock::new(exit_nodes), reserved_my_peer_id_map: DashMap::new(), @@ -632,6 +634,7 @@ impl PeerManager { let acl_filter = self.global_ctx.get_acl_filter().clone(); let global_ctx = self.global_ctx.clone(); let stats_mgr = self.global_ctx.stats_manager().clone(); + let route = self.get_route(); let label_set = LabelSet::new().with_label_type(LabelType::NetworkName(global_ctx.get_network_name())); @@ -735,6 +738,7 @@ impl PeerManager { true, global_ctx.get_ipv4().map(|x| x.address()), global_ctx.get_ipv6().map(|x| x.address()), + &route, ) { continue; } @@ -912,11 +916,11 @@ impl PeerManager { pub fn get_route(&self) -> Box { match &self.route_algo_inst { RouteAlgoInst::Ospf(route) => Box::new(route.clone()), - RouteAlgoInst::None => panic!("no route"), + RouteAlgoInst::None => Box::new(MockRoute {}), } } - pub async fn list_routes(&self) -> Vec { + pub async fn list_routes(&self) -> Vec { self.get_route().list_routes().await } @@ -953,12 +957,18 @@ impl PeerManager { resp } + pub async fn get_foreign_network_summary(&self) -> RouteForeignNetworkSummary { + self.get_route().get_foreign_network_summary().await + } + async fn run_nic_packet_process_pipeline(&self, data: &mut ZCPacket) { - if !self - .global_ctx - .get_acl_filter() - .process_packet_with_acl(data, false, None, None) - { + if !self.global_ctx.get_acl_filter().process_packet_with_acl( + data, + false, + None, + None, + &self.get_route(), + ) { return; } @@ -985,8 +995,30 @@ impl PeerManager { } } - pub async fn send_msg(&self, msg: ZCPacket, dst_peer_id: PeerId) -> Result<(), Error> { - Self::send_msg_internal(&self.peers, &self.foreign_network_client, msg, dst_peer_id).await + pub async fn send_msg_for_proxy( + &self, + mut msg: ZCPacket, + dst_peer_id: PeerId, + ) -> Result<(), Error> { + self.self_tx_counters + .compress_tx_bytes_before + .add(msg.buf_len() as u64); + + Self::try_compress_and_encrypt(self.data_compress_algo, &self.encryptor, &mut msg).await?; + + self.self_tx_counters + .compress_tx_bytes_after + .add(msg.buf_len() as u64); + + let msg_len = msg.buf_len() as u64; + let result = + Self::send_msg_internal(&self.peers, &self.foreign_network_client, msg, dst_peer_id) + .await; + if result.is_ok() { + self.self_tx_counters.self_tx_bytes.add(msg_len); + self.self_tx_counters.self_tx_packets.inc(); + } + result } async fn send_msg_internal( @@ -1033,11 +1065,20 @@ impl PeerManager { || ipv4_addr.is_multicast() || *ipv4_addr == ipv4_inet.last_address() { - dst_peers.extend(self.peers.list_routes().await.iter().map(|x| *x.key())); + dst_peers.extend(self.peers.list_routes().await.iter().filter_map(|x| { + if *x.key() != self.my_peer_id { + Some(*x.key()) + } else { + None + } + })); } else if let Some(peer_id) = self.peers.get_peer_id_by_ipv4(ipv4_addr).await { dst_peers.push(peer_id); - } else { - for exit_node in &self.exit_nodes { + } else if !self + .global_ctx + .is_ip_in_same_network(&std::net::IpAddr::V4(*ipv4_addr)) + { + for exit_node in self.exit_nodes.read().await.iter() { let IpAddr::V4(exit_node) = exit_node else { continue; }; @@ -1050,8 +1091,12 @@ impl PeerManager { } #[cfg(target_env = "ohos")] { - if dst_peers.is_empty() { - tracing::info!("no peer id for ipv4: {}, set exit_node for ohos", ipv4_addr); + if dst_peers.is_empty() + && !self + .global_ctx + .is_ip_in_same_network(&std::net::IpAddr::V4(*ipv4_addr)) + { + tracing::trace!("no peer id for ipv4: {}, set exit_node for ohos", ipv4_addr); dst_peers.push(self.my_peer_id.clone()); is_exit_node = true; } @@ -1074,7 +1119,7 @@ impl PeerManager { dst_peers.push(peer_id); } else if !ipv6_addr.is_unicast_link_local() { // NOTE: never route link local address to exit node. - for exit_node in &self.exit_nodes { + for exit_node in self.exit_nodes.read().await.iter() { let IpAddr::V6(exit_node) = exit_node else { continue; }; @@ -1260,8 +1305,8 @@ impl PeerManager { self.foreign_network_client.clone() } - pub async fn get_my_info(&self) -> cli::NodeInfo { - cli::NodeInfo { + pub async fn get_my_info(&self) -> instance::NodeInfo { + instance::NodeInfo { peer_id: self.my_peer_id, ipv4_addr: self .global_ctx @@ -1364,6 +1409,11 @@ impl PeerManager { return false; }; + if next_hop_id == dst_peer_id { + // dst p2p, no need to relay + return true; + } + let Some(next_hop_info) = route.get_peer_info(next_hop_id).await else { return false; }; @@ -1379,6 +1429,11 @@ impl PeerManager { true } + + pub async fn update_exit_nodes(&self) { + let exit_nodes = self.global_ctx.config.get_exit_nodes(); + *self.exit_nodes.write().await = exit_nodes; + } } #[cfg(test)] diff --git a/easytier/src/peers/peer_map.rs b/easytier/src/peers/peer_map.rs index 4c900b2f8..7f49d46ca 100644 --- a/easytier/src/peers/peer_map.rs +++ b/easytier/src/peers/peer_map.rs @@ -14,7 +14,7 @@ use crate::{ PeerId, }, proto::{ - cli::{self, PeerConnInfo}, + api::instance::{self, PeerConnInfo}, peer_rpc::RoutePeerInfo, }, tunnel::{packet_def::ZCPacket, TunnelError}, @@ -336,7 +336,7 @@ impl PeerMap { route_map } - pub async fn list_route_infos(&self) -> Vec { + pub async fn list_route_infos(&self) -> Vec { if let Some(route) = self.routes.read().await.iter().next() { return route.list_routes().await; } diff --git a/easytier/src/peers/peer_ospf_route.rs b/easytier/src/peers/peer_ospf_route.rs index 0a9699130..155dfa660 100644 --- a/easytier/src/peers/peer_ospf_route.rs +++ b/easytier/src/peers/peer_ospf_route.rs @@ -1,5 +1,7 @@ use std::{ - collections::BTreeSet, + collections::{ + HashMap, {BTreeMap, BTreeSet}, + }, fmt::Debug, net::{Ipv4Addr, Ipv6Addr}, sync::{ @@ -33,11 +35,13 @@ use crate::{ }, peers::route_trait::{Route, RouteInterfaceBox}, proto::{ + acl::GroupIdentity, common::{Ipv4Inet, NatType, StunInfo}, peer_rpc::{ - route_foreign_network_infos, ForeignNetworkRouteInfoEntry, ForeignNetworkRouteInfoKey, - OspfRouteRpc, OspfRouteRpcClientFactory, OspfRouteRpcServer, PeerIdVersion, - RouteForeignNetworkInfos, RoutePeerInfo, RoutePeerInfos, SyncRouteInfoError, + route_foreign_network_infos, route_foreign_network_summary, + ForeignNetworkRouteInfoEntry, ForeignNetworkRouteInfoKey, OspfRouteRpc, + OspfRouteRpcClientFactory, OspfRouteRpcServer, PeerIdVersion, RouteForeignNetworkInfos, + RouteForeignNetworkSummary, RoutePeerInfo, RoutePeerInfos, SyncRouteInfoError, SyncRouteInfoRequest, SyncRouteInfoResponse, }, rpc_types::{ @@ -126,6 +130,7 @@ impl RoutePeerInfo { network_length: 24, quic_port: None, ipv6_addr: None, + groups: Vec::new(), } } @@ -167,6 +172,8 @@ impl RoutePeerInfo { quic_port: global_ctx.get_quic_proxy_port().map(|x| x as u32), ipv6_addr: global_ctx.get_ipv6().map(|x| x.into()), + + groups: global_ctx.get_acl_groups(my_peer_id), }; let need_update_periodically = if let Ok(Ok(d)) = @@ -186,7 +193,7 @@ impl RoutePeerInfo { } } -impl From for crate::proto::cli::Route { +impl From for crate::proto::api::instance::Route { fn from(val: RoutePeerInfo) -> Self { let network_length = if val.network_length == 0 { 24 @@ -194,7 +201,7 @@ impl From for crate::proto::cli::Route { val.network_length }; - crate::proto::cli::Route { + crate::proto::api::instance::Route { peer_id: val.peer_id, ipv4_addr: val.ipv4_addr.map(|ipv4_addr| Ipv4Inet { address: Some(ipv4_addr), @@ -295,6 +302,8 @@ struct SyncedRouteInfo { raw_peer_infos: DashMap, conn_map: DashMap, AtomicVersion)>, foreign_network: DashMap, + group_trust_map: DashMap>>, + group_trust_map_cache: DashMap>>, // cache for group trust map, should sync with group_trust_map version: AtomicVersion, } @@ -305,6 +314,7 @@ impl Debug for SyncedRouteInfo { .field("peer_infos", &self.peer_infos) .field("conn_map", &self.conn_map) .field("foreign_network", &self.foreign_network) + .field("group_trust_map", &self.group_trust_map) .field("version", &self.version.get()) .finish() } @@ -323,6 +333,8 @@ impl SyncedRouteInfo { self.raw_peer_infos.remove(&peer_id); self.conn_map.remove(&peer_id); self.foreign_network.retain(|k, _| k.peer_id != peer_id); + self.group_trust_map.remove(&peer_id); + self.group_trust_map_cache.remove(&peer_id); self.version.inc(); } @@ -513,8 +525,10 @@ impl SyncedRouteInfo { let new_version = new.version; let old_version = old.version; *old = new; + drop(old); if new_version != old_version { + self.update_my_group_trusts(my_peer_id); self.version.inc(); true } else { @@ -563,7 +577,7 @@ impl SyncedRouteInfo { let need_renew = is_newer || now .duration_since(entry.last_update.unwrap().try_into().unwrap()) - .unwrap() + .unwrap_or(Duration::from_secs(0)) > UPDATE_PEER_INFO_PERIOD; if need_renew { new_entry.version = std::cmp::max(new_entry.version + 1, now_version); @@ -585,7 +599,11 @@ impl SyncedRouteInfo { assert!(!item.value().foreign_peer_ids.is_empty()); self.foreign_network .entry(item.key().clone()) - .and_modify(|v| panic!("key should not exist, {:?}", v)) + .and_modify(|old_entry| { + if item.value().version > old_entry.version { + *old_entry = item.value().clone(); + } + }) .or_insert_with(|| { let mut v = item.value().clone(); v.version = now_version; @@ -612,6 +630,97 @@ impl SyncedRouteInfo { self.is_peer_bidirectly_connected(src_peer_id, dst_peer_id) || self.is_peer_bidirectly_connected(dst_peer_id, src_peer_id) } + + fn verify_and_update_group_trusts( + &self, + peer_infos: &[RoutePeerInfo], + local_group_declarations: &[GroupIdentity], + ) { + let local_group_declarations = local_group_declarations + .iter() + .map(|g| (g.group_name.as_str(), g.group_secret.as_str())) + .collect::>(); + + let verify_groups = |old_trusted_groups: Option<&HashMap>>, + info: &RoutePeerInfo| + -> HashMap> { + let mut trusted_groups_for_peer: HashMap> = HashMap::new(); + + for group_proof in &info.groups { + let name = &group_proof.group_name; + let proof_bytes = group_proof.group_proof.clone(); + + // If we already trusted this group and the proof hasn't changed, reuse it. + if old_trusted_groups + .and_then(|g| g.get(name)) + .map(|old| old == &proof_bytes) + .unwrap_or(false) + { + trusted_groups_for_peer.insert(name.clone(), proof_bytes); + continue; + } + + if let Some(&local_secret) = + local_group_declarations.get(group_proof.group_name.as_str()) + { + if group_proof.verify(local_secret, info.peer_id) { + trusted_groups_for_peer.insert(name.clone(), proof_bytes); + } else { + tracing::warn!( + peer_id = info.peer_id, + group = %group_proof.group_name, + "Group proof verification failed" + ); + } + } + } + + trusted_groups_for_peer + }; + + for info in peer_infos { + match self.group_trust_map.entry(info.peer_id) { + dashmap::mapref::entry::Entry::Occupied(mut entry) => { + let old_trusted_groups = entry.get().clone(); + let trusted_groups_for_peer = verify_groups(Some(&old_trusted_groups), info); + + if trusted_groups_for_peer.is_empty() { + entry.remove(); + self.group_trust_map_cache.remove(&info.peer_id); + } else { + self.group_trust_map_cache.insert( + info.peer_id, + Arc::new(trusted_groups_for_peer.keys().cloned().collect()), + ); + *entry.get_mut() = trusted_groups_for_peer; + } + } + dashmap::mapref::entry::Entry::Vacant(entry) => { + let trusted_groups_for_peer = verify_groups(None, info); + + if !trusted_groups_for_peer.is_empty() { + self.group_trust_map_cache.insert( + info.peer_id, + Arc::new(trusted_groups_for_peer.keys().cloned().collect()), + ); + entry.insert(trusted_groups_for_peer); + } + } + } + } + } + + fn update_my_group_trusts(&self, my_peer_id: PeerId) { + let mut my_group_map = HashMap::new(); + let mut my_group_names = Vec::new(); + for group in self.peer_infos.entry(my_peer_id).or_default().groups.iter() { + my_group_map.insert(group.group_name.clone(), group.group_proof.clone()); + my_group_names.push(group.group_name.clone()); + } + self.group_trust_map.insert(my_peer_id, my_group_map); + self.group_trust_map_cache + .insert(my_peer_id, Arc::new(my_group_names)); + } } type PeerGraph = Graph; @@ -625,15 +734,20 @@ struct NextHopInfo { } // dst_peer_id -> (next_hop_peer_id, cost, path_len) type NextHopMap = DashMap; +#[derive(Debug, Clone, Copy)] +struct PeerIdAndVersion { + peer_id: PeerId, + version: Version, +} // computed with SyncedRouteInfo. used to get next hop. #[derive(Debug)] struct RouteTable { peer_infos: DashMap, next_hop_map: NextHopMap, - ipv4_peer_id_map: DashMap, - ipv6_peer_id_map: DashMap, - cidr_peer_id_map: DashMap, + ipv4_peer_id_map: DashMap, + ipv6_peer_id_map: DashMap, + cidr_peer_id_map: DashMap, next_hop_map_version: AtomicVersion, } @@ -739,15 +853,15 @@ impl RouteTable { }); self.ipv4_peer_id_map.retain(|_, v| { // remove ipv4 map for peers we cannot reach. - self.next_hop_map.contains_key(v) + self.next_hop_map.contains_key(&v.peer_id) }); self.ipv6_peer_id_map.retain(|_, v| { // remove ipv6 map for peers we cannot reach. - self.next_hop_map.contains_key(v) + self.next_hop_map.contains_key(&v.peer_id) }); self.cidr_peer_id_map.retain(|_, v| { // remove cidr map for peers we cannot reach. - self.next_hop_map.contains_key(v) + self.next_hop_map.contains_key(&v.peer_id) }); } @@ -862,8 +976,19 @@ impl RouteTable { self.peer_infos.insert(*peer_id, info.clone()); - let is_new_peer_better = |old_peer_id: PeerId| -> bool { - let old_next_hop = self.get_next_hop(old_peer_id); + let peer_id_and_version = PeerIdAndVersion { + peer_id: *peer_id, + version, + }; + + let is_new_peer_better = |old_peer: &PeerIdAndVersion| -> bool { + if peer_id_and_version.version > old_peer.version { + return true; + } + if peer_id_and_version.peer_id == old_peer.peer_id { + return false; + } + let old_next_hop = self.get_next_hop(old_peer.peer_id); let new_next_hop = item.value(); old_next_hop.is_none() || new_next_hop.path_len < old_next_hop.unwrap().path_len }; @@ -872,34 +997,34 @@ impl RouteTable { self.ipv4_peer_id_map .entry(ipv4_addr.into()) .and_modify(|v| { - if *v != *peer_id && is_new_peer_better(*v) { - *v = *peer_id; + if is_new_peer_better(v) { + *v = peer_id_and_version; } }) - .or_insert(*peer_id); + .or_insert(peer_id_and_version); } if let Some(ipv6_addr) = info.ipv6_addr.and_then(|x| x.address) { self.ipv6_peer_id_map .entry(ipv6_addr.into()) .and_modify(|v| { - if *v != *peer_id && is_new_peer_better(*v) { - *v = *peer_id; + if is_new_peer_better(v) { + *v = peer_id_and_version; } }) - .or_insert(*peer_id); + .or_insert(peer_id_and_version); } for cidr in info.proxy_cidrs.iter() { self.cidr_peer_id_map .entry(cidr.parse().unwrap()) .and_modify(|v| { - if *v != *peer_id && is_new_peer_better(*v) { + if is_new_peer_better(v) { // if the next hop is not set or the new next hop is better, update it. - *v = *peer_id; + *v = peer_id_and_version; } }) - .or_insert(*peer_id); + .or_insert(peer_id_and_version); } } } @@ -909,7 +1034,7 @@ impl RouteTable { for item in self.cidr_peer_id_map.iter() { let (k, v) = item.pair(); if k.contains(&ipv4) { - return Some(*v); + return Some(v.peer_id); } } None @@ -1153,6 +1278,8 @@ impl PeerRouteServiceImpl { raw_peer_infos: DashMap::new(), conn_map: DashMap::new(), foreign_network: DashMap::new(), + group_trust_map: DashMap::new(), + group_trust_map_cache: DashMap::new(), version: AtomicVersion::new(), }, cached_local_conn_map: std::sync::Mutex::new(RouteConnBitmap::new()), @@ -1429,6 +1556,9 @@ impl PeerRouteServiceImpl { ) -> Option { let mut foreign_networks = RouteForeignNetworkInfos::default(); for item in self.synced_route_info.foreign_network.iter() { + if item.key().peer_id == session.dst_peer_id { + continue; + } if session .dst_saved_foreign_network_versions .get(item.key()) @@ -1678,6 +1808,14 @@ impl PeerRouteServiceImpl { fn get_peer_info_last_update(&self) -> std::time::Instant { self.peer_info_last_update.load() } + + fn get_peer_groups(&self, peer_id: PeerId) -> Arc> { + self.synced_route_info + .group_trust_map_cache + .get(&peer_id) + .map(|groups| groups.value().clone()) + .unwrap_or_default() + } } impl Drop for PeerRouteServiceImpl { @@ -2015,6 +2153,12 @@ impl RouteSessionManager { peer_infos, raw_peer_infos.as_ref().unwrap(), )?; + service_impl + .synced_route_info + .verify_and_update_group_trusts( + peer_infos, + &service_impl.global_ctx.get_acl_group_declarations(), + ); session.update_dst_saved_peer_info_version(peer_infos); need_update_route_table = true; } @@ -2231,7 +2375,7 @@ impl Route for PeerRoute { .map(|x| x.next_hop_peer_id) } - async fn list_routes(&self) -> Vec { + async fn list_routes(&self) -> Vec { let route_table = &self.service_impl.route_table; let route_table_with_cost = &self.service_impl.route_table_with_cost; let mut routes = Vec::new(); @@ -2243,7 +2387,7 @@ impl Route for PeerRoute { continue; }; let next_hop_peer_latency_first = route_table_with_cost.get_next_hop(*item.key()); - let mut route: crate::proto::cli::Route = item.value().clone().into(); + let mut route: crate::proto::api::instance::Route = item.value().clone().into(); route.next_hop_peer_id = next_hop_peer.next_hop_peer_id; route.cost = next_hop_peer.path_len as i32; route.path_latency = next_hop_peer.path_latency; @@ -2262,8 +2406,17 @@ impl Route for PeerRoute { async fn get_peer_id_by_ipv4(&self, ipv4_addr: &Ipv4Addr) -> Option { let route_table = &self.service_impl.route_table; - if let Some(peer_id) = route_table.ipv4_peer_id_map.get(ipv4_addr) { - return Some(*peer_id); + if let Some(p) = route_table.ipv4_peer_id_map.get(ipv4_addr) { + return Some(p.peer_id); + } + + // only get peer id for proxy when the dst ipv4 is not in same network with us + if self + .global_ctx + .is_ip_in_same_network(&std::net::IpAddr::V4(*ipv4_addr)) + { + tracing::trace!(?ipv4_addr, "ipv4 addr is in same network with us"); + return None; } if let Some(peer_id) = route_table.get_peer_id_for_proxy(ipv4_addr) { @@ -2276,8 +2429,8 @@ impl Route for PeerRoute { async fn get_peer_id_by_ipv6(&self, ipv6_addr: &Ipv6Addr) -> Option { let route_table = &self.service_impl.route_table; - if let Some(peer_id) = route_table.ipv6_peer_id_map.get(ipv6_addr) { - return Some(*peer_id); + if let Some(p) = route_table.ipv6_peer_id_map.get(ipv6_addr) { + return Some(p.peer_id); } // TODO: Add proxy support for IPv6 similar to IPv4 @@ -2320,6 +2473,16 @@ impl Route for PeerRoute { foreign_networks } + async fn get_foreign_network_summary(&self) -> RouteForeignNetworkSummary { + let mut info_map: BTreeMap = BTreeMap::new(); + for item in self.service_impl.synced_route_info.foreign_network.iter() { + let entry = info_map.entry(item.key().peer_id).or_default(); + entry.network_count += 1; + entry.peer_count += item.value().foreign_peer_ids.len() as u32; + } + RouteForeignNetworkSummary { info_map } + } + async fn list_peers_own_foreign_network( &self, network_identity: &NetworkIdentity, @@ -2353,6 +2516,10 @@ impl Route for PeerRoute { async fn get_peer_info_last_update_time(&self) -> Instant { self.service_impl.get_peer_info_last_update() } + + fn get_peer_groups(&self, peer_id: PeerId) -> Arc> { + self.service_impl.get_peer_groups(peer_id) + } } impl PeerPacketFilter for Arc {} @@ -2365,6 +2532,7 @@ mod tests { time::Duration, }; + use cidr::{Ipv4Cidr, Ipv4Inet, Ipv6Inet}; use dashmap::DashMap; use prost_reflect::{DynamicMessage, ReflectMessage}; @@ -2376,7 +2544,7 @@ mod tests { peer_manager::{PeerManager, RouteAlgoType}, peer_ospf_route::PeerRouteServiceImpl, route_trait::{NextHopPolicy, Route, RouteCostCalculatorInterface}, - tests::connect_peer_manager, + tests::{connect_peer_manager, create_mock_peer_manager}, }, proto::{ common::NatType, @@ -2836,4 +3004,58 @@ mod tests { assert_eq!(req, req2); } + + #[tokio::test] + async fn test_peer_id_map_override() { + let p_a = create_mock_peer_manager().await; + let p_b = create_mock_peer_manager().await; + let p_c = create_mock_peer_manager().await; + + connect_peer_manager(p_a.clone(), p_b.clone()).await; + connect_peer_manager(p_b.clone(), p_c.clone()).await; + + let ip: Ipv4Inet = "10.0.0.1/24".parse().unwrap(); + let ipv6: Ipv6Inet = "2001:db8::1/64".parse().unwrap(); + let proxy: Ipv4Cidr = "10.3.0.0/24".parse().unwrap(); + let check_route_peer_id = async |p: Arc| { + let p = p.clone(); + wait_for_condition( + || async { + p_a.get_route().get_peer_id_by_ipv4(&ip.address()).await == Some(p.my_peer_id()) + && p_a.get_route().get_peer_id_by_ipv6(&ipv6.address()).await + == Some(p.my_peer_id()) + && p_a + .get_route() + .get_peer_id_by_ipv4(&proxy.first_address()) + .await + == Some(p.my_peer_id()) + }, + Duration::from_secs(5), + ) + .await; + }; + + p_c.get_global_ctx().set_ipv4(Some(ip)); + p_c.get_global_ctx().set_ipv6(Some(ipv6)); + p_c.get_global_ctx() + .config + .add_proxy_cidr(proxy, None) + .unwrap(); + check_route_peer_id(p_c.clone()).await; + + p_b.get_global_ctx().set_ipv4(Some(ip)); + p_b.get_global_ctx().set_ipv6(Some(ipv6)); + p_b.get_global_ctx() + .config + .add_proxy_cidr(proxy, None) + .unwrap(); + check_route_peer_id(p_b.clone()).await; + + p_b.get_global_ctx() + .set_ipv4(Some("10.0.0.2/24".parse().unwrap())); + p_b.get_global_ctx() + .set_ipv6(Some("2001:db8::2/64".parse().unwrap())); + p_b.get_global_ctx().config.remove_proxy_cidr(proxy); + check_route_peer_id(p_c.clone()).await; + } } diff --git a/easytier/src/peers/route_trait.rs b/easytier/src/peers/route_trait.rs index 831c2ba0b..ef9b99c4d 100644 --- a/easytier/src/peers/route_trait.rs +++ b/easytier/src/peers/route_trait.rs @@ -9,7 +9,7 @@ use crate::{ common::{global_ctx::NetworkIdentity, PeerId}, proto::peer_rpc::{ ForeignNetworkRouteInfoEntry, ForeignNetworkRouteInfoKey, RouteForeignNetworkInfos, - RoutePeerInfo, + RouteForeignNetworkSummary, RoutePeerInfo, }, }; @@ -74,7 +74,7 @@ pub trait Route { self.get_next_hop(peer_id).await } - async fn list_routes(&self) -> Vec; + async fn list_routes(&self) -> Vec; async fn get_peer_id_by_ipv4(&self, _ipv4: &Ipv4Addr) -> Option { None @@ -102,6 +102,10 @@ pub trait Route { Default::default() } + async fn get_foreign_network_summary(&self) -> RouteForeignNetworkSummary { + Default::default() + } + // my peer id in foreign network is different from the one in local network // this function is used to get the peer id in local network async fn get_origin_my_peer_id( @@ -118,9 +122,58 @@ pub trait Route { async fn get_peer_info_last_update_time(&self) -> std::time::Instant; + fn get_peer_groups(&self, peer_id: PeerId) -> Arc>; + + async fn get_peer_groups_by_ip(&self, ip: &std::net::IpAddr) -> Arc> { + match self.get_peer_id_by_ip(ip).await { + Some(peer_id) => self.get_peer_groups(peer_id), + None => Arc::new(Vec::new()), + } + } + + async fn get_peer_groups_by_ipv4(&self, ipv4: &Ipv4Addr) -> Arc> { + match self.get_peer_id_by_ipv4(ipv4).await { + Some(peer_id) => self.get_peer_groups(peer_id), + None => Arc::new(Vec::new()), + } + } + async fn dump(&self) -> String { "this route implementation does not support dump".to_string() } } pub type ArcRoute = Arc>; + +pub struct MockRoute {} + +#[async_trait::async_trait] +impl Route for MockRoute { + async fn open(&self, _interface: RouteInterfaceBox) -> Result { + panic!("mock route") + } + + async fn close(&self) { + panic!("mock route") + } + + async fn get_next_hop(&self, _peer_id: PeerId) -> Option { + panic!("mock route") + } + + async fn list_routes(&self) -> Vec { + panic!("mock route") + } + + async fn get_peer_info(&self, _peer_id: PeerId) -> Option { + panic!("mock route") + } + + async fn get_peer_info_last_update_time(&self) -> std::time::Instant { + panic!("mock route") + } + + fn get_peer_groups(&self, _peer_id: PeerId) -> Arc> { + panic!("mock route") + } +} diff --git a/easytier/src/peers/rpc_service.rs b/easytier/src/peers/rpc_service.rs index c8fc21c6a..1a70a2148 100644 --- a/easytier/src/peers/rpc_service.rs +++ b/easytier/src/peers/rpc_service.rs @@ -1,30 +1,35 @@ -use std::sync::Arc; +use std::{ + ops::Deref, + sync::{Arc, Weak}, +}; use crate::{ - common::acl_processor::AclRuleBuilder, proto::{ - cli::{ + api::instance::{ AclManageRpc, DumpRouteRequest, DumpRouteResponse, GetAclStatsRequest, - GetAclStatsResponse, GetWhitelistRequest, GetWhitelistResponse, - ListForeignNetworkRequest, ListForeignNetworkResponse, ListGlobalForeignNetworkRequest, + GetAclStatsResponse, GetForeignNetworkSummaryRequest, GetForeignNetworkSummaryResponse, + GetWhitelistRequest, GetWhitelistResponse, ListForeignNetworkRequest, + ListForeignNetworkResponse, ListGlobalForeignNetworkRequest, ListGlobalForeignNetworkResponse, ListPeerRequest, ListPeerResponse, ListRouteRequest, - ListRouteResponse, PeerInfo, PeerManageRpc, SetWhitelistRequest, SetWhitelistResponse, - ShowNodeInfoRequest, ShowNodeInfoResponse, + ListRouteResponse, PeerInfo, PeerManageRpc, ShowNodeInfoRequest, ShowNodeInfoResponse, }, rpc_types::{self, controller::BaseController}, }, + utils::weak_upgrade, }; use super::peer_manager::PeerManager; #[derive(Clone)] pub struct PeerManagerRpcService { - peer_manager: Arc, + peer_manager: Weak, } impl PeerManagerRpcService { pub fn new(peer_manager: Arc) -> Self { - PeerManagerRpcService { peer_manager } + PeerManagerRpcService { + peer_manager: Arc::downgrade(&peer_manager), + } } pub async fn list_peers(peer_manager: &PeerManager) -> Vec { @@ -82,7 +87,8 @@ impl PeerManageRpc for PeerManagerRpcService { ) -> Result { let mut reply = ListPeerResponse::default(); - let peers = PeerManagerRpcService::list_peers(&self.peer_manager).await; + let peers = + PeerManagerRpcService::list_peers(weak_upgrade(&self.peer_manager)?.deref()).await; for peer in peers { reply.peer_infos.push(peer); } @@ -96,7 +102,7 @@ impl PeerManageRpc for PeerManagerRpcService { _request: ListRouteRequest, // Accept request of type HelloRequest ) -> Result { let reply = ListRouteResponse { - routes: self.peer_manager.list_routes().await, + routes: weak_upgrade(&self.peer_manager)?.list_routes().await, }; Ok(reply) } @@ -107,7 +113,7 @@ impl PeerManageRpc for PeerManagerRpcService { _request: DumpRouteRequest, // Accept request of type HelloRequest ) -> Result { let reply = DumpRouteResponse { - result: self.peer_manager.dump_route().await, + result: weak_upgrade(&self.peer_manager)?.dump_route().await, }; Ok(reply) } @@ -117,8 +123,7 @@ impl PeerManageRpc for PeerManagerRpcService { _: BaseController, _request: ListForeignNetworkRequest, // Accept request of type HelloRequest ) -> Result { - let reply = self - .peer_manager + let reply = weak_upgrade(&self.peer_manager)? .get_foreign_network_manager() .list_foreign_networks() .await; @@ -130,7 +135,23 @@ impl PeerManageRpc for PeerManagerRpcService { _: BaseController, _request: ListGlobalForeignNetworkRequest, ) -> Result { - Ok(self.peer_manager.list_global_foreign_network().await) + Ok(weak_upgrade(&self.peer_manager)? + .list_global_foreign_network() + .await) + } + + async fn get_foreign_network_summary( + &self, + _: BaseController, + _request: GetForeignNetworkSummaryRequest, + ) -> Result { + Ok(GetForeignNetworkSummaryResponse { + summary: Some( + weak_upgrade(&self.peer_manager)? + .get_foreign_network_summary() + .await, + ), + }) } async fn show_node_info( @@ -139,7 +160,7 @@ impl PeerManageRpc for PeerManagerRpcService { _request: ShowNodeInfoRequest, // Accept request of type HelloRequest ) -> Result { Ok(ShowNodeInfoResponse { - node_info: Some(self.peer_manager.get_my_info().await), + node_info: Some(weak_upgrade(&self.peer_manager)?.get_my_info().await), }) } } @@ -153,8 +174,7 @@ impl AclManageRpc for PeerManagerRpcService { _: BaseController, _request: GetAclStatsRequest, ) -> Result { - let acl_stats = self - .peer_manager + let acl_stats = weak_upgrade(&self.peer_manager)? .get_global_ctx() .get_acl_filter() .get_stats(); @@ -163,34 +183,12 @@ impl AclManageRpc for PeerManagerRpcService { }) } - async fn set_whitelist( - &self, - _: BaseController, - request: SetWhitelistRequest, - ) -> Result { - tracing::info!( - "Setting whitelist - TCP: {:?}, UDP: {:?}", - request.tcp_ports, - request.udp_ports - ); - - let global_ctx = self.peer_manager.get_global_ctx(); - - global_ctx.config.set_tcp_whitelist(request.tcp_ports); - global_ctx.config.set_udp_whitelist(request.udp_ports); - global_ctx - .get_acl_filter() - .reload_rules(AclRuleBuilder::build(&global_ctx)?.as_ref()); - - Ok(SetWhitelistResponse {}) - } - async fn get_whitelist( &self, _: BaseController, _request: GetWhitelistRequest, ) -> Result { - let global_ctx = self.peer_manager.get_global_ctx(); + let global_ctx = weak_upgrade(&self.peer_manager)?.get_global_ctx(); let tcp_ports = global_ctx.config.get_tcp_whitelist(); let udp_ports = global_ctx.config.get_udp_whitelist(); tracing::info!( diff --git a/easytier/src/proto/acl.proto b/easytier/src/proto/acl.proto index 393fc74f1..f8ea02e1b 100644 --- a/easytier/src/proto/acl.proto +++ b/easytier/src/proto/acl.proto @@ -67,6 +67,10 @@ message Rule { // Connection tracking bool stateful = 13; // Enable connection tracking + + // Group matching criteria + repeated string source_groups = 14; + repeated string destination_groups = 15; } // Rule chain with metadata and optimization hints @@ -84,7 +88,20 @@ message Chain { Action default_action = 6; } -message AclV1 { repeated Chain chains = 1; } +message GroupInfo { + repeated GroupIdentity declares = 1; + repeated string members = 2; +} + +message GroupIdentity { + string group_name = 1; + string group_secret = 2; +} + +message AclV1 { + repeated Chain chains = 1; + GroupInfo group = 2; +} enum ConnState { New = 0; diff --git a/easytier/src/proto/api.rs b/easytier/src/proto/api.rs new file mode 100644 index 000000000..c9cdb7689 --- /dev/null +++ b/easytier/src/proto/api.rs @@ -0,0 +1,262 @@ +pub mod config { + include!(concat!(env!("OUT_DIR"), "/api.config.rs")); + pub struct Patchable { + pub action: Option, + pub value: Option, + } + + impl From for Patchable { + fn from(patch: PortForwardPatch) -> Self { + Patchable { + action: ConfigPatchAction::try_from(patch.action).ok(), + value: patch.cfg.map(Into::into), + } + } + } + + impl From for Patchable { + fn from(value: RoutePatch) -> Self { + Patchable { + action: ConfigPatchAction::try_from(value.action).ok(), + value: value.cidr.map(Into::into), + } + } + } + + impl From for Patchable { + fn from(value: ExitNodePatch) -> Self { + Patchable { + action: ConfigPatchAction::try_from(value.action).ok(), + value: value.node.map(Into::into), + } + } + } + + impl From for Patchable { + fn from(value: StringPatch) -> Self { + Patchable { + action: ConfigPatchAction::try_from(value.action).ok(), + value: Some(value.value), + } + } + } + + impl From for Patchable { + fn from(value: UrlPatch) -> Self { + Patchable { + action: ConfigPatchAction::try_from(value.action).ok(), + value: value.url.map(Into::into), + } + } + } + + pub fn patch_vec(v: &mut Vec, patches: Vec>) + where + T: PartialEq, + { + for patch in patches { + match patch.action { + Some(ConfigPatchAction::Add) => { + if let Some(value) = patch.value { + v.push(value); + } + } + Some(ConfigPatchAction::Remove) => { + if let Some(value) = patch.value { + v.retain(|x| x != &value); + } + } + Some(ConfigPatchAction::Clear) => { + v.clear(); + } + None => {} + } + } + } +} + +pub mod instance { + include!(concat!(env!("OUT_DIR"), "/api.instance.rs")); + + impl PeerRoutePair { + pub fn get_latency_ms(&self) -> Option { + let mut ret = u64::MAX; + let p = self.peer.as_ref()?; + let default_conn_id = p.default_conn_id.map(|id| id.to_string()); + for conn in p.conns.iter() { + let Some(stats) = &conn.stats else { + continue; + }; + if default_conn_id == Some(conn.conn_id.to_string()) { + return Some(f64::from(stats.latency_us as u32) / 1000.0); + } + ret = ret.min(stats.latency_us); + } + + if ret == u64::MAX { + None + } else { + Some(f64::from(ret as u32) / 1000.0) + } + } + + pub fn get_rx_bytes(&self) -> Option { + let mut ret = 0; + let p = self.peer.as_ref()?; + for conn in p.conns.iter() { + let Some(stats) = &conn.stats else { + continue; + }; + ret += stats.rx_bytes; + } + + if ret == 0 { + None + } else { + Some(ret) + } + } + + pub fn get_tx_bytes(&self) -> Option { + let mut ret = 0; + let p = self.peer.as_ref()?; + for conn in p.conns.iter() { + let Some(stats) = &conn.stats else { + continue; + }; + ret += stats.tx_bytes; + } + + if ret == 0 { + None + } else { + Some(ret) + } + } + + pub fn get_loss_rate(&self) -> Option { + let mut ret = 0.0; + let p = self.peer.as_ref()?; + for conn in p.conns.iter() { + ret += conn.loss_rate; + } + + if ret == 0.0 { + None + } else { + Some(ret as f64) + } + } + + fn is_tunnel_ipv6(tunnel_info: &super::super::common::TunnelInfo) -> bool { + let Some(local_addr) = &tunnel_info.local_addr else { + return false; + }; + + let u: url::Url = local_addr.clone().into(); + u.host() + .map(|h| matches!(h, url::Host::Ipv6(_))) + .unwrap_or(false) + } + + fn get_tunnel_proto_str(tunnel_info: &super::super::common::TunnelInfo) -> String { + if Self::is_tunnel_ipv6(tunnel_info) { + format!("{}6", tunnel_info.tunnel_type) + } else { + tunnel_info.tunnel_type.clone() + } + } + + pub fn get_conn_protos(&self) -> Option> { + let mut ret = vec![]; + let p = self.peer.as_ref()?; + for conn in p.conns.iter() { + let Some(tunnel_info) = &conn.tunnel else { + continue; + }; + // insert if not exists + let tunnel_type = Self::get_tunnel_proto_str(tunnel_info); + if !ret.contains(&tunnel_type) { + ret.push(tunnel_type); + } + } + + if ret.is_empty() { + None + } else { + Some(ret) + } + } + + pub fn get_udp_nat_type(&self) -> String { + use crate::proto::common::NatType; + let mut ret = NatType::Unknown; + if let Some(r) = &self.route.clone().unwrap_or_default().stun_info { + ret = NatType::try_from(r.udp_nat_type).unwrap(); + } + format!("{:?}", ret) + } + } + + pub fn list_peer_route_pair(peers: Vec, routes: Vec) -> Vec { + let mut pairs: Vec = vec![]; + + for route in routes.iter() { + let peer = peers.iter().find(|peer| peer.peer_id == route.peer_id); + let pair = PeerRoutePair { + route: Some(route.clone()), + peer: peer.cloned(), + }; + + pairs.push(pair); + } + + pairs.sort_by(|a, b| { + let a_is_public_server = a + .route + .as_ref() + .and_then(|r| r.feature_flag.as_ref()) + .is_some_and(|f| f.is_public_server); + + let b_is_public_server = b + .route + .as_ref() + .and_then(|r| r.feature_flag.as_ref()) + .is_some_and(|f| f.is_public_server); + + if a_is_public_server != b_is_public_server { + return if a_is_public_server { + std::cmp::Ordering::Less + } else { + std::cmp::Ordering::Greater + }; + } + + let a_ip = a + .route + .as_ref() + .and_then(|r| r.ipv4_addr.as_ref()) + .and_then(|ipv4| ipv4.address.as_ref()) + .map_or(0, |addr| addr.addr); + + let b_ip = b + .route + .as_ref() + .and_then(|r| r.ipv4_addr.as_ref()) + .and_then(|ipv4| ipv4.address.as_ref()) + .map_or(0, |addr| addr.addr); + + a_ip.cmp(&b_ip) + }); + + pairs + } +} + +pub mod logger { + include!(concat!(env!("OUT_DIR"), "/api.logger.rs")); +} + +pub mod manage { + include!(concat!(env!("OUT_DIR"), "/api.manage.rs")); +} diff --git a/easytier/src/proto/api_config.proto b/easytier/src/proto/api_config.proto new file mode 100644 index 000000000..568f48554 --- /dev/null +++ b/easytier/src/proto/api_config.proto @@ -0,0 +1,84 @@ +syntax = "proto3"; + +import "common.proto"; +import "acl.proto"; +import "api_instance.proto"; +import "api_manage.proto"; + +package api.config; + +enum ConfigPatchAction { + ADD = 0; + REMOVE = 1; + CLEAR = 2; +} + +message InstanceConfigPatch { + optional string hostname = 1; + optional common.Ipv4Inet ipv4 = 2; + optional common.Ipv6Inet ipv6 = 3; + repeated PortForwardPatch port_forwards = 4; + optional AclPatch acl = 5; + repeated ProxyNetworkPatch proxy_networks = 6; + repeated RoutePatch routes = 7; + repeated ExitNodePatch exit_nodes = 8; + repeated UrlPatch mapped_listeners = 9; + repeated UrlPatch connectors = 10; +} + +message PortForwardPatch { + ConfigPatchAction action = 1; + common.PortForwardConfigPb cfg = 2; +} + +message StringPatch { + ConfigPatchAction action = 1; + string value = 2; +} + +message UrlPatch { + ConfigPatchAction action = 1; + common.Url url = 2; +} + +message AclPatch { + optional acl.Acl acl = 1; + repeated StringPatch tcp_whitelist = 2; + repeated StringPatch udp_whitelist = 3; +} + +message ProxyNetworkPatch { + ConfigPatchAction action = 1; + common.Ipv4Inet cidr = 2; + optional common.Ipv4Inet mapped_cidr = 3; +} + +message RoutePatch { + ConfigPatchAction action = 1; + common.Ipv4Inet cidr = 2; +} + +message ExitNodePatch { + ConfigPatchAction action = 1; + common.IpAddr node = 2; +} + +message PatchConfigRequest { + InstanceConfigPatch patch = 1; + api.instance.InstanceIdentifier instance = 2; +} + +message PatchConfigResponse {} + +message GetConfigRequest { + api.instance.InstanceIdentifier instance = 1; +} + +message GetConfigResponse { + api.manage.NetworkConfig config = 1; +} + +service ConfigRpc { + rpc PatchConfig(PatchConfigRequest) returns (PatchConfigResponse); + rpc GetConfig(GetConfigRequest) returns (GetConfigResponse); +} diff --git a/easytier/src/proto/cli.proto b/easytier/src/proto/api_instance.proto similarity index 65% rename from easytier/src/proto/cli.proto rename to easytier/src/proto/api_instance.proto index d1ecb3f89..2afc2c5d0 100644 --- a/easytier/src/proto/cli.proto +++ b/easytier/src/proto/api_instance.proto @@ -4,7 +4,16 @@ import "common.proto"; import "peer_rpc.proto"; import "acl.proto"; -package cli; +package api.instance; + +message InstanceIdentifier { + message InstanceSelector { optional string name = 1; } + + oneof selector { + common.UUID id = 1; + InstanceSelector instance_selector = 2; + } +} message Status { int32 code = 1; @@ -41,7 +50,7 @@ message PeerInfo { repeated common.UUID directly_connected_conns = 4; } -message ListPeerRequest {} +message ListPeerRequest { InstanceIdentifier instance = 1; } message ListPeerResponse { repeated PeerInfo peer_infos = 1; @@ -89,19 +98,19 @@ message NodeInfo { peer_rpc.GetIpListResponse ip_list = 11; } -message ShowNodeInfoRequest {} +message ShowNodeInfoRequest { InstanceIdentifier instance = 1; } message ShowNodeInfoResponse { NodeInfo node_info = 1; } -message ListRouteRequest {} +message ListRouteRequest { InstanceIdentifier instance = 1; } message ListRouteResponse { repeated Route routes = 1; } -message DumpRouteRequest {} +message DumpRouteRequest { InstanceIdentifier instance = 1; } message DumpRouteResponse { string result = 1; } -message ListForeignNetworkRequest {} +message ListForeignNetworkRequest { InstanceIdentifier instance = 1; } message ForeignNetworkEntryPb { repeated PeerInfo peers = 1; @@ -114,7 +123,7 @@ message ListForeignNetworkResponse { map foreign_networks = 1; } -message ListGlobalForeignNetworkRequest {} +message ListGlobalForeignNetworkRequest { InstanceIdentifier instance = 1; } message ListGlobalForeignNetworkResponse { // foreign network in the entire network @@ -130,6 +139,12 @@ message ListGlobalForeignNetworkResponse { map foreign_networks = 1; } +message GetForeignNetworkSummaryRequest { InstanceIdentifier instance = 1; } + +message GetForeignNetworkSummaryResponse { + peer_rpc.RouteForeignNetworkSummary summary = 1; +} + service PeerManageRpc { rpc ListPeer(ListPeerRequest) returns (ListPeerResponse); rpc ListRoute(ListRouteRequest) returns (ListRouteResponse); @@ -139,6 +154,8 @@ service PeerManageRpc { rpc ListGlobalForeignNetwork(ListGlobalForeignNetworkRequest) returns (ListGlobalForeignNetworkResponse); rpc ShowNodeInfo(ShowNodeInfoRequest) returns (ShowNodeInfoResponse); + rpc GetForeignNetworkSummary(GetForeignNetworkSummaryRequest) + returns (GetForeignNetworkSummaryResponse); } enum ConnectorStatus { @@ -152,50 +169,25 @@ message Connector { ConnectorStatus status = 2; } -message ListConnectorRequest {} +message ListConnectorRequest { InstanceIdentifier instance = 1; } message ListConnectorResponse { repeated Connector connectors = 1; } -enum ConnectorManageAction { - ADD = 0; - REMOVE = 1; -} - -message ManageConnectorRequest { - ConnectorManageAction action = 1; - common.Url url = 2; -} - -message ManageConnectorResponse {} - service ConnectorManageRpc { rpc ListConnector(ListConnectorRequest) returns (ListConnectorResponse); - rpc ManageConnector(ManageConnectorRequest) returns (ManageConnectorResponse); -} - -message MappedListener { - common.Url url = 1; } -message ListMappedListenerRequest {} +message MappedListener { common.Url url = 1; } -message ListMappedListenerResponse { repeated MappedListener mappedlisteners = 1; } - -enum MappedListenerManageAction { - MAPPED_LISTENER_ADD = 0; - MAPPED_LISTENER_REMOVE = 1; -} +message ListMappedListenerRequest { InstanceIdentifier instance = 1; } -message ManageMappedListenerRequest { - MappedListenerManageAction action = 1; - common.Url url = 2; +message ListMappedListenerResponse { + repeated MappedListener mappedlisteners = 1; } -message ManageMappedListenerResponse {} - service MappedListenerManageRpc { - rpc ListMappedListener(ListMappedListenerRequest) returns (ListMappedListenerResponse); - rpc ManageMappedListener(ManageMappedListenerRequest) returns (ManageMappedListenerResponse); + rpc ListMappedListener(ListMappedListenerRequest) + returns (ListMappedListenerResponse); } message VpnPortalInfo { @@ -204,7 +196,7 @@ message VpnPortalInfo { repeated string connected_clients = 3; } -message GetVpnPortalInfoRequest {} +message GetVpnPortalInfoRequest { InstanceIdentifier instance = 1; } message GetVpnPortalInfoResponse { VpnPortalInfo vpn_portal_info = 1; } service VpnPortalRpc { @@ -219,19 +211,19 @@ enum TcpProxyEntryTransportType { } enum TcpProxyEntryState { - Unknown = 0; - // receive syn packet but not start connecting to dst - SynReceived = 1; - // connecting to dst - ConnectingDst = 2; - // connected to dst - Connected = 3; - // connection closed - Closed = 4; - // closing src - ClosingSrc = 5; - // closing dst - ClosingDst = 6; + Unknown = 0; + // receive syn packet but not start connecting to dst + SynReceived = 1; + // connecting to dst + ConnectingDst = 2; + // connected to dst + Connected = 3; + // connection closed + Closed = 4; + // closing src + ClosingSrc = 5; + // closing dst + ClosingDst = 6; } message TcpProxyEntry { @@ -242,64 +234,38 @@ message TcpProxyEntry { TcpProxyEntryTransportType transport_type = 5; } -message ListTcpProxyEntryRequest {} +message ListTcpProxyEntryRequest { InstanceIdentifier instance = 1; } -message ListTcpProxyEntryResponse { - repeated TcpProxyEntry entries = 1; -} +message ListTcpProxyEntryResponse { repeated TcpProxyEntry entries = 1; } service TcpProxyRpc { rpc ListTcpProxyEntry(ListTcpProxyEntryRequest) returns (ListTcpProxyEntryResponse); } -message GetAclStatsRequest {} +message GetAclStatsRequest { InstanceIdentifier instance = 1; } -message GetAclStatsResponse { - acl.AclStats acl_stats = 1; -} +message GetAclStatsResponse { acl.AclStats acl_stats = 1; } service AclManageRpc { rpc GetAclStats(GetAclStatsRequest) returns (GetAclStatsResponse); - rpc SetWhitelist(SetWhitelistRequest) returns (SetWhitelistResponse); rpc GetWhitelist(GetWhitelistRequest) returns (GetWhitelistResponse); } -message SetWhitelistRequest { - repeated string tcp_ports = 1; - repeated string udp_ports = 2; -} - -message SetWhitelistResponse {} - -message GetWhitelistRequest {} +message GetWhitelistRequest { InstanceIdentifier instance = 1; } message GetWhitelistResponse { repeated string tcp_ports = 1; repeated string udp_ports = 2; } -message AddPortForwardRequest { - common.PortForwardConfigPb cfg = 1; -} - -message AddPortForwardResponse {} - -message RemovePortForwardRequest { - common.PortForwardConfigPb cfg = 1; -} - -message RemovePortForwardResponse {} - -message ListPortForwardRequest {} +message ListPortForwardRequest { InstanceIdentifier instance = 1; } message ListPortForwardResponse { repeated common.PortForwardConfigPb cfgs = 1; } service PortForwardManageRpc { - rpc AddPortForward(AddPortForwardRequest) returns (AddPortForwardResponse); - rpc RemovePortForward(RemovePortForwardRequest) returns (RemovePortForwardResponse); rpc ListPortForward(ListPortForwardRequest) returns (ListPortForwardResponse); } @@ -309,19 +275,16 @@ message MetricSnapshot { map labels = 3; } -message GetStatsRequest {} +message GetStatsRequest { InstanceIdentifier instance = 1; } -message GetStatsResponse { - repeated MetricSnapshot metrics = 1; -} +message GetStatsResponse { repeated MetricSnapshot metrics = 1; } -message GetPrometheusStatsRequest {} +message GetPrometheusStatsRequest { InstanceIdentifier instance = 1; } -message GetPrometheusStatsResponse { - string prometheus_text = 1; -} +message GetPrometheusStatsResponse { string prometheus_text = 1; } service StatsRpc { rpc GetStats(GetStatsRequest) returns (GetStatsResponse); - rpc GetPrometheusStats(GetPrometheusStatsRequest) returns (GetPrometheusStatsResponse); + rpc GetPrometheusStats(GetPrometheusStatsRequest) + returns (GetPrometheusStatsResponse); } diff --git a/easytier/src/proto/api_logger.proto b/easytier/src/proto/api_logger.proto new file mode 100644 index 000000000..c738ba255 --- /dev/null +++ b/easytier/src/proto/api_logger.proto @@ -0,0 +1,24 @@ +syntax = "proto3"; + +package api.logger; + +enum LogLevel { + DISABLED = 0; + ERROR = 1; + WARNING = 2; + INFO = 3; + DEBUG = 4; + TRACE = 5; +} + +message SetLoggerConfigRequest { LogLevel level = 1; } + +message SetLoggerConfigResponse {} + +message GetLoggerConfigRequest {} + +message GetLoggerConfigResponse { LogLevel level = 1; } +service LoggerRpc { + rpc SetLoggerConfig(SetLoggerConfigRequest) returns (SetLoggerConfigResponse); + rpc GetLoggerConfig(GetLoggerConfigRequest) returns (GetLoggerConfigResponse); +} diff --git a/easytier/src/proto/api_manage.proto b/easytier/src/proto/api_manage.proto new file mode 100644 index 000000000..73cf922bd --- /dev/null +++ b/easytier/src/proto/api_manage.proto @@ -0,0 +1,157 @@ +syntax = "proto3"; + +import "common.proto"; +import "peer_rpc.proto"; +import "api_instance.proto"; + +package api.manage; + +enum NetworkingMethod { + PublicServer = 0; + Manual = 1; + Standalone = 2; +} + +message NetworkConfig { + optional string instance_id = 1; + + optional bool dhcp = 2; + optional string virtual_ipv4 = 3; + optional int32 network_length = 4; + optional string hostname = 5; + optional string network_name = 6; + optional string network_secret = 7; + optional NetworkingMethod networking_method = 8; + + optional string public_server_url = 9; + repeated string peer_urls = 10; + + repeated string proxy_cidrs = 11; + + optional bool enable_vpn_portal = 12; + optional int32 vpn_portal_listen_port = 13; + optional string vpn_portal_client_network_addr = 14; + optional int32 vpn_portal_client_network_len = 15; + + optional bool advanced_settings = 16; + + repeated string listener_urls = 17; + // optional int32 rpc_port = 18; + optional bool latency_first = 19; + + optional string dev_name = 20; + + optional bool use_smoltcp = 21; + optional bool disable_ipv6 = 47; + optional bool enable_kcp_proxy = 22; + optional bool disable_kcp_input = 23; + optional bool disable_p2p = 24; + optional bool bind_device = 25; + optional bool no_tun = 26; + + optional bool enable_exit_node = 27; + optional bool relay_all_peer_rpc = 28; + optional bool multi_thread = 29; + optional bool enable_relay_network_whitelist = 30; + repeated string relay_network_whitelist = 31; + optional bool enable_manual_routes = 32; + repeated string routes = 33; + repeated string exit_nodes = 34; + optional bool proxy_forward_by_system = 35; + optional bool disable_encryption = 36; + optional bool enable_socks5 = 37; + optional int32 socks5_port = 38; + optional bool disable_udp_hole_punching = 39; + optional int32 mtu = 40; + repeated string mapped_listeners = 41; + + optional bool enable_magic_dns = 42; + optional bool enable_private_mode = 43; + + // repeated string rpc_portal_whitelists = 44; + + optional bool enable_quic_proxy = 45; + optional bool disable_quic_input = 46; + repeated PortForwardConfig port_forwards = 48; + + optional bool disable_sym_hole_punching = 49; +} + +message PortForwardConfig { + string bind_ip = 1; + uint32 bind_port = 2; + string dst_ip = 3; + uint32 dst_port = 4; + string proto = 5; +} + +message MyNodeInfo { + common.Ipv4Inet virtual_ipv4 = 1; + string hostname = 2; + string version = 3; + peer_rpc.GetIpListResponse ips = 4; + common.StunInfo stun_info = 5; + repeated common.Url listeners = 6; + optional string vpn_portal_cfg = 7; +} + +message NetworkInstanceRunningInfo { + string dev_name = 1; + MyNodeInfo my_node_info = 2; + repeated string events = 3; + repeated api.instance.Route routes = 4; + repeated api.instance.PeerInfo peers = 5; + repeated api.instance.PeerRoutePair peer_route_pairs = 6; + bool running = 7; + optional string error_msg = 8; + peer_rpc.RouteForeignNetworkSummary foreign_network_summary = 9; +} + +message NetworkInstanceRunningInfoMap { + map map = 1; +} + +message ValidateConfigRequest { NetworkConfig config = 1; } + +message ValidateConfigResponse { string toml_config = 1; } + +message RunNetworkInstanceRequest { + common.UUID inst_id = 1; + NetworkConfig config = 2; +} + +message RunNetworkInstanceResponse { common.UUID inst_id = 1; } + +message RetainNetworkInstanceRequest { repeated common.UUID inst_ids = 1; } + +message RetainNetworkInstanceResponse { + repeated common.UUID remain_inst_ids = 1; +} + +message CollectNetworkInfoRequest { repeated common.UUID inst_ids = 1; } + +message CollectNetworkInfoResponse { NetworkInstanceRunningInfoMap info = 1; } + +message ListNetworkInstanceRequest {} + +message ListNetworkInstanceResponse { repeated common.UUID inst_ids = 1; } + +message DeleteNetworkInstanceRequest { repeated common.UUID inst_ids = 1; } + +message DeleteNetworkInstanceResponse { + repeated common.UUID remain_inst_ids = 1; +} + +service WebClientService { + rpc ValidateConfig(ValidateConfigRequest) returns (ValidateConfigResponse) {} + rpc RunNetworkInstance(RunNetworkInstanceRequest) + returns (RunNetworkInstanceResponse) {} + rpc RetainNetworkInstance(RetainNetworkInstanceRequest) + returns (RetainNetworkInstanceResponse) {} + rpc CollectNetworkInfo(CollectNetworkInfoRequest) + returns (CollectNetworkInfoResponse) {} + rpc ListNetworkInstance(ListNetworkInstanceRequest) + returns (ListNetworkInstanceResponse) {} + rpc DeleteNetworkInstance(DeleteNetworkInstanceRequest) + returns (DeleteNetworkInstanceResponse) {} +} diff --git a/easytier/src/proto/cli.rs b/easytier/src/proto/cli.rs deleted file mode 100644 index 4522871c4..000000000 --- a/easytier/src/proto/cli.rs +++ /dev/null @@ -1,117 +0,0 @@ -include!(concat!(env!("OUT_DIR"), "/cli.rs")); - -impl PeerRoutePair { - pub fn get_latency_ms(&self) -> Option { - let mut ret = u64::MAX; - let p = self.peer.as_ref()?; - let default_conn_id = p.default_conn_id.map(|id| id.to_string()); - for conn in p.conns.iter() { - let Some(stats) = &conn.stats else { - continue; - }; - if default_conn_id == Some(conn.conn_id.to_string()) { - return Some(f64::from(stats.latency_us as u32) / 1000.0); - } - ret = ret.min(stats.latency_us); - } - - if ret == u64::MAX { - None - } else { - Some(f64::from(ret as u32) / 1000.0) - } - } - - pub fn get_rx_bytes(&self) -> Option { - let mut ret = 0; - let p = self.peer.as_ref()?; - for conn in p.conns.iter() { - let Some(stats) = &conn.stats else { - continue; - }; - ret += stats.rx_bytes; - } - - if ret == 0 { - None - } else { - Some(ret) - } - } - - pub fn get_tx_bytes(&self) -> Option { - let mut ret = 0; - let p = self.peer.as_ref()?; - for conn in p.conns.iter() { - let Some(stats) = &conn.stats else { - continue; - }; - ret += stats.tx_bytes; - } - - if ret == 0 { - None - } else { - Some(ret) - } - } - - pub fn get_loss_rate(&self) -> Option { - let mut ret = 0.0; - let p = self.peer.as_ref()?; - for conn in p.conns.iter() { - ret += conn.loss_rate; - } - - if ret == 0.0 { - None - } else { - Some(ret as f64) - } - } - - pub fn get_conn_protos(&self) -> Option> { - let mut ret = vec![]; - let p = self.peer.as_ref()?; - for conn in p.conns.iter() { - let Some(tunnel_info) = &conn.tunnel else { - continue; - }; - // insert if not exists - if !ret.contains(&tunnel_info.tunnel_type) { - ret.push(tunnel_info.tunnel_type.clone()); - } - } - - if ret.is_empty() { - None - } else { - Some(ret) - } - } - - pub fn get_udp_nat_type(&self) -> String { - use crate::proto::common::NatType; - let mut ret = NatType::Unknown; - if let Some(r) = &self.route.clone().unwrap_or_default().stun_info { - ret = NatType::try_from(r.udp_nat_type).unwrap(); - } - format!("{:?}", ret) - } -} - -pub fn list_peer_route_pair(peers: Vec, routes: Vec) -> Vec { - let mut pairs: Vec = vec![]; - - for route in routes.iter() { - let peer = peers.iter().find(|peer| peer.peer_id == route.peer_id); - let pair = PeerRoutePair { - route: Some(route.clone()), - peer: peer.cloned(), - }; - - pairs.push(pair); - } - - pairs -} diff --git a/easytier/src/proto/common.proto b/easytier/src/proto/common.proto index 8e01dc10f..5915b8943 100644 --- a/easytier/src/proto/common.proto +++ b/easytier/src/proto/common.proto @@ -49,9 +49,15 @@ message FlagsInConfig { // enable relay foreign network kcp packets bool enable_relay_foreign_network_kcp = 28; - - // encryption algorithm to use, empty string means default (aes-gcm) + + // encryption algorithm to use, empty string means default (aes-gcm) string encryption_algorithm = 29; + + // disable symmetric nat hole punching, treat symmetric as cone when enabled + bool disable_sym_hole_punching = 30; + + // tld dns zone for magic dns + string tld_dns_zone = 31; } message RpcDescriptor { @@ -141,6 +147,13 @@ message Ipv6Addr { uint32 part4 = 4; } +message IpAddr { + oneof ip { + Ipv4Addr ipv4 = 1; + Ipv6Addr ipv6 = 2; + }; +} + message Ipv4Inet { Ipv4Addr address = 1; uint32 network_length = 2; diff --git a/easytier/src/proto/common.rs b/easytier/src/proto/common.rs index 7a2abafba..5a153c11a 100644 --- a/easytier/src/proto/common.rs +++ b/easytier/src/proto/common.rs @@ -108,6 +108,43 @@ impl From for Ipv4Inet { } } +impl From for IpAddr { + fn from(value: std::net::IpAddr) -> Self { + match value { + std::net::IpAddr::V4(v4) => IpAddr { + ip: Some(ip_addr::Ip::Ipv4(Ipv4Addr::from(v4))), + }, + std::net::IpAddr::V6(v6) => IpAddr { + ip: Some(ip_addr::Ip::Ipv6(Ipv6Addr::from(v6))), + }, + } + } +} + +impl From for std::net::IpAddr { + fn from(value: IpAddr) -> Self { + match value.ip { + Some(ip_addr::Ip::Ipv4(v4)) => std::net::IpAddr::V4(v4.into()), + Some(ip_addr::Ip::Ipv6(v6)) => std::net::IpAddr::V6(v6.into()), + None => panic!("IpAddr is None"), + } + } +} + +impl Display for IpAddr { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", std::net::IpAddr::from(*self)) + } +} + +impl FromStr for IpAddr { + type Err = anyhow::Error; + + fn from_str(s: &str) -> Result { + Ok(IpAddr::from(std::net::IpAddr::from_str(s)?)) + } +} + impl From for cidr::Ipv4Inet { fn from(value: Ipv4Inet) -> Self { cidr::Ipv4Inet::new( @@ -118,6 +155,16 @@ impl From for cidr::Ipv4Inet { } } +impl From for cidr::Ipv4Cidr { + fn from(value: Ipv4Inet) -> Self { + cidr::Ipv4Cidr::new( + value.address.unwrap_or_default().into(), + value.network_length as u8, + ) + .unwrap() + } +} + impl fmt::Display for Ipv4Inet { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", cidr::Ipv4Inet::from(*self)) diff --git a/easytier/src/proto/magic_dns.proto b/easytier/src/proto/magic_dns.proto index 54790aa9a..788ccc6ea 100644 --- a/easytier/src/proto/magic_dns.proto +++ b/easytier/src/proto/magic_dns.proto @@ -2,7 +2,7 @@ syntax = "proto3"; import "google/protobuf/timestamp.proto"; import "common.proto"; -import "cli.proto"; +import "api_instance.proto"; package magic_dns; @@ -30,7 +30,7 @@ message DnsRecordList { message UpdateDnsRecordRequest { string zone = 1; - repeated cli.Route routes = 2; + repeated api.instance.Route routes = 2; } message GetDnsRecordResponse { diff --git a/easytier/src/proto/mod.rs b/easytier/src/proto/mod.rs index fd8b45524..ba3a16eea 100644 --- a/easytier/src/proto/mod.rs +++ b/easytier/src/proto/mod.rs @@ -2,7 +2,7 @@ pub mod rpc_impl; pub mod rpc_types; pub mod acl; -pub mod cli; +pub mod api; pub mod common; pub mod error; pub mod magic_dns; diff --git a/easytier/src/proto/peer_rpc.proto b/easytier/src/proto/peer_rpc.proto index 0bfb432dc..61644a4f7 100644 --- a/easytier/src/proto/peer_rpc.proto +++ b/easytier/src/proto/peer_rpc.proto @@ -25,6 +25,8 @@ message RoutePeerInfo { optional uint32 quic_port = 14; optional common.Ipv6Inet ipv6_addr = 15; + + repeated PeerGroupInfo groups = 16; } message PeerIdVersion { @@ -60,6 +62,21 @@ message RouteForeignNetworkInfos { repeated Info infos = 1; } +message RouteForeignNetworkSummary { + message Info { + uint32 peer_id = 1; + uint32 network_count = 2; + uint32 peer_count = 3; + } + + map info_map = 1; +} + +message PeerGroupInfo { + string group_name = 1; + bytes group_proof = 2; +} + message SyncRouteInfoRequest { uint32 my_peer_id = 1; uint64 my_session_id = 2; diff --git a/easytier/src/proto/peer_rpc.rs b/easytier/src/proto/peer_rpc.rs index 8b2b43241..6a6271501 100644 --- a/easytier/src/proto/peer_rpc.rs +++ b/easytier/src/proto/peer_rpc.rs @@ -1 +1,245 @@ +use hmac::{Hmac, Mac}; +use sha2::Sha256; + +use crate::common::PeerId; + include!(concat!(env!("OUT_DIR"), "/peer_rpc.rs")); + +impl PeerGroupInfo { + pub fn generate_with_proof(group_name: String, group_secret: String, peer_id: PeerId) -> Self { + let mut mac = Hmac::::new_from_slice(group_secret.as_bytes()) + .expect("HMAC can take key of any size"); + + let mut data_to_sign = group_name.as_bytes().to_vec(); + data_to_sign.push(0x00); // Add a delimiter byte + data_to_sign.extend_from_slice(&peer_id.to_be_bytes()); + + mac.update(&data_to_sign); + + let proof = mac.finalize().into_bytes().to_vec(); + + PeerGroupInfo { + group_name, + group_proof: proof, + } + } + + pub fn verify(&self, group_secret: &str, peer_id: PeerId) -> bool { + let mut verifier = Hmac::::new_from_slice(group_secret.as_bytes()) + .expect("HMAC can take key of any size"); + + let mut data_to_sign = self.group_name.as_bytes().to_vec(); + data_to_sign.push(0x00); // Add a delimiter byte + data_to_sign.extend_from_slice(&peer_id.to_be_bytes()); + + verifier.update(&data_to_sign); + + verifier.verify_slice(&self.group_proof).is_ok() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_peer_group_info_new() { + let group_name = "test_group".to_string(); + let group_secret = "secret123".to_string(); + let peer_id = 42u32; + + let peer_group_info = + PeerGroupInfo::generate_with_proof(group_name.clone(), group_secret, peer_id); + + assert_eq!(peer_group_info.group_name, group_name); + assert!(!peer_group_info.group_proof.is_empty()); + // HMAC-SHA256 produces a 32-byte output + assert_eq!(peer_group_info.group_proof.len(), 32); + } + + #[test] + fn test_peer_group_info_verify_valid() { + let group_name = "test_group".to_string(); + let group_secret = "secret123".to_string(); + let peer_id = 42u32; + + let peer_group_info = + PeerGroupInfo::generate_with_proof(group_name, group_secret.clone(), peer_id); + + // Verification should succeed using the same secret and peer_id + assert!(peer_group_info.verify(&group_secret, peer_id)); + } + + #[test] + fn test_peer_group_info_verify_invalid_secret() { + let group_name = "test_group".to_string(); + let group_secret = "secret123".to_string(); + let peer_id = 42u32; + + let peer_group_info = PeerGroupInfo::generate_with_proof(group_name, group_secret, peer_id); + + // Verification should fail with a wrong secret + assert!(!peer_group_info.verify("wrong_secret", peer_id)); + } + + #[test] + fn test_peer_group_info_verify_invalid_peer_id() { + let group_name = "test_group".to_string(); + let group_secret = "secret123".to_string(); + let peer_id = 42u32; + + let peer_group_info = + PeerGroupInfo::generate_with_proof(group_name, group_secret.clone(), peer_id); + + // Verification should fail with a wrong peer_id + assert!(!peer_group_info.verify(&group_secret, 999u32)); + } + + #[test] + fn test_peer_group_info_different_groups_different_proofs() { + let group_secret = "secret123".to_string(); + let peer_id = 42u32; + + let group1 = + PeerGroupInfo::generate_with_proof("group1".to_string(), group_secret.clone(), peer_id); + let group2 = + PeerGroupInfo::generate_with_proof("group2".to_string(), group_secret, peer_id); + + // Different group names should produce different proofs + assert_ne!(group1.group_proof, group2.group_proof); + } + + #[test] + fn test_peer_group_info_same_params_same_proof() { + let group_name = "test_group".to_string(); + let group_secret = "secret123".to_string(); + let peer_id = 42u32; + + let group1 = + PeerGroupInfo::generate_with_proof(group_name.clone(), group_secret.clone(), peer_id); + let group2 = PeerGroupInfo::generate_with_proof(group_name, group_secret, peer_id); + + // Same parameters should produce the same proof + assert_eq!(group1.group_proof, group2.group_proof); + } + + #[test] + fn test_peer_group_info_empty_group_name() { + let group_name = "".to_string(); + let group_secret = "secret123".to_string(); + let peer_id = 42u32; + + let peer_group_info = + PeerGroupInfo::generate_with_proof(group_name.clone(), group_secret.clone(), peer_id); + + assert_eq!(peer_group_info.group_name, group_name); + assert!(peer_group_info.verify(&group_secret, peer_id)); + } + + #[test] + fn test_peer_group_info_empty_secret() { + let group_name = "test_group".to_string(); + let group_secret = "".to_string(); + let peer_id = 42u32; + + let peer_group_info = + PeerGroupInfo::generate_with_proof(group_name, group_secret.clone(), peer_id); + + assert!(peer_group_info.verify(&group_secret, peer_id)); + } + + #[test] + fn test_peer_group_info_unicode_group_name() { + let group_name = "测试组🚀".to_string(); + let group_secret = "secret123".to_string(); + let peer_id = 42u32; + + let peer_group_info = + PeerGroupInfo::generate_with_proof(group_name.clone(), group_secret.clone(), peer_id); + + assert_eq!(peer_group_info.group_name, group_name); + assert!(peer_group_info.verify(&group_secret, peer_id)); + } + + #[test] + fn test_peer_group_info_unicode_secret() { + let group_name = "test_group".to_string(); + let group_secret = "密码123🔐".to_string(); + let peer_id = 42u32; + + let peer_group_info = + PeerGroupInfo::generate_with_proof(group_name, group_secret.clone(), peer_id); + + assert!(peer_group_info.verify(&group_secret, peer_id)); + } + + #[test] + fn test_peer_group_info_zero_peer_id() { + let group_name = "test_group".to_string(); + let group_secret = "secret123".to_string(); + let peer_id = 0u32; + + let peer_group_info = + PeerGroupInfo::generate_with_proof(group_name, group_secret.clone(), peer_id); + + assert!(peer_group_info.verify(&group_secret, peer_id)); + } + + #[test] + fn test_peer_group_info_max_peer_id() { + let group_name = "test_group".to_string(); + let group_secret = "secret123".to_string(); + let peer_id = u32::MAX; + + let peer_group_info = + PeerGroupInfo::generate_with_proof(group_name, group_secret.clone(), peer_id); + + assert!(peer_group_info.verify(&group_secret, peer_id)); + } + + #[test] + #[ignore] + fn perf_test_generate_with_proof() { + let group_name = "test_group".to_string(); + let group_secret = "secret123".to_string(); + let peer_id = 42u32; + let iterations = 100000; + + let start = std::time::Instant::now(); + for _ in 0..iterations { + let _ = PeerGroupInfo::generate_with_proof( + group_name.clone(), + group_secret.clone(), + peer_id, + ); + } + let duration = start.elapsed(); + + println!( + "generate_with_proof took {:?} for {} iterations", + duration, iterations + ); + println!("Avg time per iteration: {:?}", duration / iterations as u32); + } + + #[test] + #[ignore] + fn perf_test_verify() { + let group_name = "test_group".to_string(); + let group_secret = "secret123".to_string(); + let peer_id = 42u32; + let iterations = 100000; + + let peer_group_info = + PeerGroupInfo::generate_with_proof(group_name.clone(), group_secret.clone(), peer_id); + + let start = std::time::Instant::now(); + for _ in 0..iterations { + assert!(peer_group_info.verify(&group_secret, peer_id)); + } + let duration = start.elapsed(); + + println!("verify took {:?} for {} iterations", duration, iterations); + println!("Avg time per iteration: {:?}", duration / iterations as u32); + } +} diff --git a/easytier/src/proto/rpc_types/error.rs b/easytier/src/proto/rpc_types/error.rs index ba86d7505..351bb4616 100644 --- a/easytier/src/proto/rpc_types/error.rs +++ b/easytier/src/proto/rpc_types/error.rs @@ -6,7 +6,7 @@ use thiserror; #[derive(Debug, thiserror::Error)] pub enum Error { - #[error("rust tun error {0}")] + #[error("Rust error: {0}")] ExecutionError(#[from] anyhow::Error), #[error("Decode error: {0}")] diff --git a/easytier/src/proto/web.proto b/easytier/src/proto/web.proto index 68d12251d..68429bc9a 100644 --- a/easytier/src/proto/web.proto +++ b/easytier/src/proto/web.proto @@ -1,185 +1,23 @@ syntax = "proto3"; import "common.proto"; -import "peer_rpc.proto"; -import "cli.proto"; package web; -enum NetworkingMethod { - PublicServer = 0; - Manual = 1; - Standalone = 2; -} - -message NetworkConfig { - optional string instance_id = 1; - - optional bool dhcp = 2; - optional string virtual_ipv4 = 3; - optional int32 network_length = 4; - optional string hostname = 5; - optional string network_name = 6; - optional string network_secret = 7; - optional NetworkingMethod networking_method = 8; - - optional string public_server_url = 9; - repeated string peer_urls = 10; - - repeated string proxy_cidrs = 11; - - optional bool enable_vpn_portal = 12; - optional int32 vpn_portal_listen_port = 13; - optional string vpn_portal_client_network_addr = 14; - optional int32 vpn_portal_client_network_len = 15; - - optional bool advanced_settings = 16; - - repeated string listener_urls = 17; - optional int32 rpc_port = 18; - optional bool latency_first = 19; - - optional string dev_name = 20; - - optional bool use_smoltcp = 21; - optional bool disable_ipv6 = 47; - optional bool enable_kcp_proxy = 22; - optional bool disable_kcp_input = 23; - optional bool disable_p2p = 24; - optional bool bind_device = 25; - optional bool no_tun = 26; - - optional bool enable_exit_node = 27; - optional bool relay_all_peer_rpc = 28; - optional bool multi_thread = 29; - optional bool enable_relay_network_whitelist = 30; - repeated string relay_network_whitelist = 31; - optional bool enable_manual_routes = 32; - repeated string routes = 33; - repeated string exit_nodes = 34; - optional bool proxy_forward_by_system = 35; - optional bool disable_encryption = 36; - optional bool enable_socks5 = 37; - optional int32 socks5_port = 38; - optional bool disable_udp_hole_punching = 39; - optional int32 mtu = 40; - repeated string mapped_listeners = 41; - - optional bool enable_magic_dns = 42; - optional bool enable_private_mode = 43; - - repeated string rpc_portal_whitelists = 44; - - optional bool enable_quic_proxy = 45; - optional bool disable_quic_input = 46; - repeated PortForwardConfig port_forwards = 48; -} - -message PortForwardConfig { - string bind_ip = 1; - uint32 bind_port = 2; - string dst_ip = 3; - uint32 dst_port = 4; - string proto = 5; -} - -message MyNodeInfo { - common.Ipv4Inet virtual_ipv4 = 1; - string hostname = 2; - string version = 3; - peer_rpc.GetIpListResponse ips = 4; - common.StunInfo stun_info = 5; - repeated common.Url listeners = 6; - optional string vpn_portal_cfg = 7; -} - -message NetworkInstanceRunningInfo { - string dev_name = 1; - MyNodeInfo my_node_info = 2; - repeated string events = 3; - repeated cli.Route routes = 4; - repeated cli.PeerInfo peers = 5; - repeated cli.PeerRoutePair peer_route_pairs = 6; - bool running = 7; - optional string error_msg = 8; -} - -message NetworkInstanceRunningInfoMap { - map map = 1; -} - message HeartbeatRequest { - common.UUID machine_id = 1; - common.UUID inst_id = 2; - string user_token = 3; + common.UUID machine_id = 1; + common.UUID inst_id = 2; + string user_token = 3; - string easytier_version = 4; - string report_time = 5; - string hostname = 6; + string easytier_version = 4; + string report_time = 5; + string hostname = 6; - repeated common.UUID running_network_instances = 7; + repeated common.UUID running_network_instances = 7; } -message HeartbeatResponse { -} +message HeartbeatResponse {} service WebServerService { - rpc Heartbeat(HeartbeatRequest) returns (HeartbeatResponse) {} -} - -message ValidateConfigRequest { - NetworkConfig config = 1; -} - -message ValidateConfigResponse { - string toml_config = 1; -} - -message RunNetworkInstanceRequest { - common.UUID inst_id = 1; - NetworkConfig config = 2; -} - -message RunNetworkInstanceResponse { - common.UUID inst_id = 1; -} - -message RetainNetworkInstanceRequest { - repeated common.UUID inst_ids = 1; -} - -message RetainNetworkInstanceResponse { - repeated common.UUID remain_inst_ids = 1; -} - -message CollectNetworkInfoRequest { - repeated common.UUID inst_ids = 1; -} - -message CollectNetworkInfoResponse { - NetworkInstanceRunningInfoMap info = 1; -} - -message ListNetworkInstanceRequest { -} - -message ListNetworkInstanceResponse { - repeated common.UUID inst_ids = 1; -} - -message DeleteNetworkInstanceRequest { - repeated common.UUID inst_ids = 1; -} - -message DeleteNetworkInstanceResponse { - repeated common.UUID remain_inst_ids = 1; -} - -service WebClientService { - rpc ValidateConfig(ValidateConfigRequest) returns (ValidateConfigResponse) {} - rpc RunNetworkInstance(RunNetworkInstanceRequest) returns (RunNetworkInstanceResponse) {} - rpc RetainNetworkInstance(RetainNetworkInstanceRequest) returns (RetainNetworkInstanceResponse) {} - rpc CollectNetworkInfo(CollectNetworkInfoRequest) returns (CollectNetworkInfoResponse) {} - rpc ListNetworkInstance(ListNetworkInstanceRequest) returns (ListNetworkInstanceResponse) {} - rpc DeleteNetworkInstance(DeleteNetworkInstanceRequest) returns (DeleteNetworkInstanceResponse) {} -} + rpc Heartbeat(HeartbeatRequest) returns (HeartbeatResponse); +} \ No newline at end of file diff --git a/easytier/src/rpc_service/acl_manage.rs b/easytier/src/rpc_service/acl_manage.rs new file mode 100644 index 000000000..452375fb5 --- /dev/null +++ b/easytier/src/rpc_service/acl_manage.rs @@ -0,0 +1,50 @@ +use std::sync::Arc; + +use crate::{ + instance_manager::NetworkInstanceManager, + proto::{ + api::instance::{ + AclManageRpc, GetAclStatsRequest, GetAclStatsResponse, GetWhitelistRequest, + GetWhitelistResponse, + }, + rpc_types::controller::BaseController, + }, +}; + +#[derive(Clone)] +pub struct AclManageRpcService { + instance_manager: Arc, +} + +impl AclManageRpcService { + pub fn new(instance_manager: Arc) -> Self { + Self { instance_manager } + } +} + +#[async_trait::async_trait] +impl AclManageRpc for AclManageRpcService { + type Controller = BaseController; + + async fn get_acl_stats( + &self, + ctrl: Self::Controller, + req: GetAclStatsRequest, + ) -> crate::proto::rpc_types::error::Result { + super::get_instance_service(&self.instance_manager, &req.instance)? + .get_acl_manage_service() + .get_acl_stats(ctrl, req) + .await + } + + async fn get_whitelist( + &self, + ctrl: Self::Controller, + req: GetWhitelistRequest, + ) -> crate::proto::rpc_types::error::Result { + super::get_instance_service(&self.instance_manager, &req.instance)? + .get_acl_manage_service() + .get_whitelist(ctrl, req) + .await + } +} diff --git a/easytier/src/rpc_service/api.rs b/easytier/src/rpc_service/api.rs new file mode 100644 index 000000000..6de125d94 --- /dev/null +++ b/easytier/src/rpc_service/api.rs @@ -0,0 +1,164 @@ +use std::{net::SocketAddr, sync::Arc}; + +use anyhow::Context; +use cidr::IpCidr; + +use crate::{ + instance::instance::InstanceRpcServerHook, + instance_manager::NetworkInstanceManager, + proto::{ + api::{ + config::ConfigRpcServer, + instance::{ + AclManageRpcServer, ConnectorManageRpcServer, MappedListenerManageRpcServer, + PeerManageRpcServer, PortForwardManageRpcServer, StatsRpcServer, TcpProxyRpcServer, + VpnPortalRpcServer, + }, + logger::LoggerRpcServer, + manage::WebClientServiceServer, + }, + rpc_impl::{service_registry::ServiceRegistry, standalone::StandAloneServer}, + rpc_types::error::Error, + }, + rpc_service::{ + acl_manage::AclManageRpcService, config::ConfigRpcService, + connector_manage::ConnectorManageRpcService, instance_manage::InstanceManageRpcService, + logger::LoggerRpcService, mapped_listener_manage::MappedListenerManageRpcService, + peer_manage::PeerManageRpcService, port_forward_manage::PortForwardManageRpcService, + proxy::TcpProxyRpcService, stats::StatsRpcService, vpn_portal::VpnPortalRpcService, + }, + tunnel::tcp::TcpTunnelListener, +}; + +pub struct ApiRpcServer { + rpc_server: StandAloneServer, +} + +impl ApiRpcServer { + pub fn new( + rpc_portal: Option, + rpc_portal_whitelist: Option>, + instance_manager: Arc, + ) -> anyhow::Result { + let mut rpc_server = StandAloneServer::new(TcpTunnelListener::new( + format!("tcp://{}", parse_rpc_portal(rpc_portal)?) + .parse() + .context("failed to parse rpc portal address")?, + )); + rpc_server.set_hook(Arc::new(InstanceRpcServerHook::new(rpc_portal_whitelist))); + register_api_rpc_service(&instance_manager, rpc_server.registry()); + Ok(Self { rpc_server }) + } + + pub async fn serve(mut self) -> Result { + self.rpc_server.serve().await?; + Ok(self) + } +} + +impl Drop for ApiRpcServer { + fn drop(&mut self) { + self.rpc_server.registry().unregister_all(); + } +} + +fn register_api_rpc_service( + instance_manager: &Arc, + registry: &ServiceRegistry, +) { + registry.register( + PeerManageRpcServer::new(PeerManageRpcService::new(instance_manager.clone())), + "", + ); + + registry.register( + ConnectorManageRpcServer::new(ConnectorManageRpcService::new(instance_manager.clone())), + "", + ); + + registry.register( + MappedListenerManageRpcServer::new(MappedListenerManageRpcService::new( + instance_manager.clone(), + )), + "", + ); + + registry.register( + VpnPortalRpcServer::new(VpnPortalRpcService::new(instance_manager.clone())), + "", + ); + + for client_type in ["tcp", "kcp_src", "kcp_dst", "quic_src", "quic_dst"] { + registry.register( + TcpProxyRpcServer::new(TcpProxyRpcService::new( + instance_manager.clone(), + client_type, + )), + client_type, + ); + } + + registry.register( + AclManageRpcServer::new(AclManageRpcService::new(instance_manager.clone())), + "", + ); + + registry.register( + PortForwardManageRpcServer::new(PortForwardManageRpcService::new(instance_manager.clone())), + "", + ); + + registry.register( + StatsRpcServer::new(StatsRpcService::new(instance_manager.clone())), + "", + ); + + registry.register(LoggerRpcServer::new(LoggerRpcService), ""); + + registry.register( + ConfigRpcServer::new(ConfigRpcService::new(instance_manager.clone())), + "", + ); + + registry.register( + WebClientServiceServer::new(InstanceManageRpcService::new(instance_manager.clone())), + "", + ); +} + +fn parse_rpc_portal(rpc_portal: Option) -> anyhow::Result { + if let Some(Ok(port)) = rpc_portal.as_ref().map(|s| s.parse::()) { + Ok(SocketAddr::from(([0, 0, 0, 0], port))) + } else { + let mut rpc_addr = rpc_portal + .map(|addr| { + addr.parse::() + .context("failed to parse rpc portal address") + }) + .transpose()?; + select_proper_rpc_port(&mut rpc_addr)?; + rpc_addr.ok_or_else(|| anyhow::anyhow!("failed to parse rpc portal address")) + } +} + +fn select_proper_rpc_port(addr: &mut Option) -> anyhow::Result<()> { + match addr { + None => { + *addr = Some(SocketAddr::from(([0, 0, 0, 0], 0))); + select_proper_rpc_port(addr)?; + Ok(()) + } + Some(addr) => { + if addr.port() == 0 { + let Some(port) = crate::utils::find_free_tcp_port(15888..15900) else { + tracing::warn!( + "No free port found for RPC portal, skipping setting RPC portal" + ); + return Err(anyhow::anyhow!("No free port found for RPC portal")); + }; + addr.set_port(port); + } + Ok(()) + } + } +} diff --git a/easytier/src/rpc_service/config.rs b/easytier/src/rpc_service/config.rs new file mode 100644 index 000000000..1f1b92148 --- /dev/null +++ b/easytier/src/rpc_service/config.rs @@ -0,0 +1,49 @@ +use std::sync::Arc; + +use crate::{ + instance_manager::NetworkInstanceManager, + proto::{ + api::config::{ + ConfigRpc, GetConfigRequest, GetConfigResponse, PatchConfigRequest, PatchConfigResponse, + }, + rpc_types::{self, controller::BaseController}, + }, +}; + +#[derive(Clone)] +pub struct ConfigRpcService { + instance_manager: Arc, +} + +impl ConfigRpcService { + pub fn new(instance_manager: Arc) -> Self { + Self { instance_manager } + } +} + +#[async_trait::async_trait] +impl ConfigRpc for ConfigRpcService { + type Controller = BaseController; + + async fn patch_config( + &self, + ctrl: Self::Controller, + input: PatchConfigRequest, + ) -> Result { + super::get_instance_service(&self.instance_manager, &input.instance)? + .get_config_service() + .patch_config(ctrl, input) + .await + } + + async fn get_config( + &self, + ctrl: Self::Controller, + input: GetConfigRequest, + ) -> Result { + super::get_instance_service(&self.instance_manager, &input.instance)? + .get_config_service() + .get_config(ctrl, input) + .await + } +} diff --git a/easytier/src/rpc_service/connector_manage.rs b/easytier/src/rpc_service/connector_manage.rs new file mode 100644 index 000000000..6030159df --- /dev/null +++ b/easytier/src/rpc_service/connector_manage.rs @@ -0,0 +1,36 @@ +use std::sync::Arc; + +use crate::{ + instance_manager::NetworkInstanceManager, + proto::{ + api::instance::{ConnectorManageRpc, ListConnectorRequest, ListConnectorResponse}, + rpc_types::controller::BaseController, + }, +}; + +#[derive(Clone)] +pub struct ConnectorManageRpcService { + instance_manager: Arc, +} + +impl ConnectorManageRpcService { + pub fn new(instance_manager: Arc) -> Self { + Self { instance_manager } + } +} + +#[async_trait::async_trait] +impl ConnectorManageRpc for ConnectorManageRpcService { + type Controller = BaseController; + + async fn list_connector( + &self, + ctrl: Self::Controller, + req: ListConnectorRequest, + ) -> crate::proto::rpc_types::error::Result { + super::get_instance_service(&self.instance_manager, &req.instance)? + .get_connector_manage_service() + .list_connector(ctrl, req) + .await + } +} diff --git a/easytier/src/rpc_service/instance_manage.rs b/easytier/src/rpc_service/instance_manage.rs new file mode 100644 index 000000000..9cab1f95a --- /dev/null +++ b/easytier/src/rpc_service/instance_manage.rs @@ -0,0 +1,136 @@ +use std::sync::Arc; + +use crate::{ + common::config::ConfigLoader, + instance_manager::NetworkInstanceManager, + launcher::ConfigSource, + proto::{ + api::manage::*, + rpc_types::{self, controller::BaseController}, + }, +}; + +#[derive(Clone)] +pub struct InstanceManageRpcService { + manager: Arc, +} + +impl InstanceManageRpcService { + pub fn new(manager: Arc) -> Self { + Self { manager } + } +} + +#[async_trait::async_trait] +impl WebClientService for InstanceManageRpcService { + type Controller = BaseController; + + async fn validate_config( + &self, + _: BaseController, + req: ValidateConfigRequest, + ) -> Result { + let toml_config = req.config.unwrap_or_default().gen_config()?.dump(); + Ok(ValidateConfigResponse { toml_config }) + } + + async fn run_network_instance( + &self, + _: BaseController, + req: RunNetworkInstanceRequest, + ) -> Result { + if req.config.is_none() { + return Err(anyhow::anyhow!("config is required").into()); + } + let cfg = req.config.unwrap().gen_config()?; + let id = cfg.get_id(); + if let Some(inst_id) = req.inst_id { + cfg.set_id(inst_id.into()); + } + self.manager.run_network_instance(cfg, ConfigSource::Web)?; + println!("instance {} started", id); + Ok(RunNetworkInstanceResponse { + inst_id: Some(id.into()), + }) + } + + async fn retain_network_instance( + &self, + _: BaseController, + req: RetainNetworkInstanceRequest, + ) -> Result { + let remain = self + .manager + .retain_network_instance(req.inst_ids.into_iter().map(Into::into).collect())?; + println!("instance {:?} retained", remain); + Ok(RetainNetworkInstanceResponse { + remain_inst_ids: remain.iter().map(|item| (*item).into()).collect(), + }) + } + + async fn collect_network_info( + &self, + _: BaseController, + req: CollectNetworkInfoRequest, + ) -> Result { + let mut ret = NetworkInstanceRunningInfoMap { + map: self + .manager + .collect_network_infos() + .await? + .into_iter() + .map(|(k, v)| (k.to_string(), v)) + .collect(), + }; + let include_inst_ids = req + .inst_ids + .iter() + .cloned() + .map(|id| id.to_string()) + .collect::>(); + if !include_inst_ids.is_empty() { + let mut to_remove = Vec::new(); + for (k, _) in ret.map.iter() { + if !include_inst_ids.contains(k) { + to_remove.push(k.clone()); + } + } + + for k in to_remove { + ret.map.remove(&k); + } + } + Ok(CollectNetworkInfoResponse { info: Some(ret) }) + } + + // rpc ListNetworkInstance(ListNetworkInstanceRequest) returns (ListNetworkInstanceResponse) {} + async fn list_network_instance( + &self, + _: BaseController, + _: ListNetworkInstanceRequest, + ) -> Result { + Ok(ListNetworkInstanceResponse { + inst_ids: self + .manager + .list_network_instance_ids() + .into_iter() + .map(Into::into) + .collect(), + }) + } + + // rpc DeleteNetworkInstance(DeleteNetworkInstanceRequest) returns (DeleteNetworkInstanceResponse) {} + async fn delete_network_instance( + &self, + _: BaseController, + req: DeleteNetworkInstanceRequest, + ) -> Result { + let remain_inst_ids = self + .manager + .delete_network_instance(req.inst_ids.into_iter().map(Into::into).collect())?; + println!("instance {:?} retained", remain_inst_ids); + Ok(DeleteNetworkInstanceResponse { + remain_inst_ids: remain_inst_ids.into_iter().map(Into::into).collect(), + }) + } +} diff --git a/easytier/src/rpc_service/logger.rs b/easytier/src/rpc_service/logger.rs new file mode 100644 index 000000000..d71886394 --- /dev/null +++ b/easytier/src/rpc_service/logger.rs @@ -0,0 +1,105 @@ +use std::sync::{mpsc::Sender, Mutex, OnceLock}; + +use crate::proto::{ + api::logger::{ + GetLoggerConfigRequest, GetLoggerConfigResponse, LogLevel, LoggerRpc, + SetLoggerConfigRequest, SetLoggerConfigResponse, + }, + rpc_types::{self, controller::BaseController}, +}; + +pub static LOGGER_LEVEL_SENDER: std::sync::OnceLock>> = OnceLock::new(); +pub static CURRENT_LOG_LEVEL: std::sync::OnceLock> = OnceLock::new(); + +#[derive(Clone, Default)] +pub struct LoggerRpcService; + +impl LoggerRpcService { + fn log_level_to_string(level: LogLevel) -> String { + match level { + LogLevel::Disabled => "off".to_string(), + LogLevel::Error => "error".to_string(), + LogLevel::Warning => "warn".to_string(), + LogLevel::Info => "info".to_string(), + LogLevel::Debug => "debug".to_string(), + LogLevel::Trace => "trace".to_string(), + } + } + + fn string_to_log_level(level_str: &str) -> LogLevel { + match level_str.to_lowercase().as_str() { + "off" | "disabled" => LogLevel::Disabled, + "error" => LogLevel::Error, + "warn" | "warning" => LogLevel::Warning, + "info" => LogLevel::Info, + "debug" => LogLevel::Debug, + "trace" => LogLevel::Trace, + _ => LogLevel::Info, // 默认为 Info 级别 + } + } +} + +#[async_trait::async_trait] +impl LoggerRpc for LoggerRpcService { + type Controller = BaseController; + + async fn set_logger_config( + &self, + _: BaseController, + request: SetLoggerConfigRequest, + ) -> Result { + let level_str = Self::log_level_to_string(request.level()); + + // 更新当前日志级别 + if let Some(current_level) = CURRENT_LOG_LEVEL.get() { + if let Ok(mut level) = current_level.lock() { + *level = level_str.clone(); + } + } + + // 发送新的日志级别到 logger 重载器 + if let Some(sender) = LOGGER_LEVEL_SENDER.get() { + if let Ok(sender) = sender.lock() { + if let Err(e) = sender.send(level_str) { + tracing::warn!("Failed to send new log level to reloader: {}", e); + return Err(rpc_types::error::Error::ExecutionError(anyhow::anyhow!( + "Failed to update log level: {}", + e + ))); + } + } else { + return Err(rpc_types::error::Error::ExecutionError(anyhow::anyhow!( + "Logger sender is not available" + ))); + } + } else { + return Err(rpc_types::error::Error::ExecutionError(anyhow::anyhow!( + "Logger reloader is not initialized" + ))); + } + + Ok(SetLoggerConfigResponse {}) + } + + async fn get_logger_config( + &self, + _: BaseController, + _request: GetLoggerConfigRequest, + ) -> Result { + let current_level_str = if let Some(current_level) = CURRENT_LOG_LEVEL.get() { + if let Ok(level) = current_level.lock() { + level.clone() + } else { + "info".to_string() // 默认级别 + } + } else { + "info".to_string() // 默认级别 + }; + + let level = Self::string_to_log_level(¤t_level_str); + + Ok(GetLoggerConfigResponse { + level: level.into(), + }) + } +} diff --git a/easytier/src/rpc_service/mapped_listener_manage.rs b/easytier/src/rpc_service/mapped_listener_manage.rs new file mode 100644 index 000000000..394376346 --- /dev/null +++ b/easytier/src/rpc_service/mapped_listener_manage.rs @@ -0,0 +1,38 @@ +use std::sync::Arc; + +use crate::{ + instance_manager::NetworkInstanceManager, + proto::{ + api::instance::{ + ListMappedListenerRequest, ListMappedListenerResponse, MappedListenerManageRpc, + }, + rpc_types::controller::BaseController, + }, +}; + +#[derive(Clone)] +pub struct MappedListenerManageRpcService { + instance_manager: Arc, +} + +impl MappedListenerManageRpcService { + pub fn new(instance_manager: Arc) -> Self { + Self { instance_manager } + } +} + +#[async_trait::async_trait] +impl MappedListenerManageRpc for MappedListenerManageRpcService { + type Controller = BaseController; + + async fn list_mapped_listener( + &self, + ctrl: Self::Controller, + req: ListMappedListenerRequest, + ) -> crate::proto::rpc_types::error::Result { + super::get_instance_service(&self.instance_manager, &req.instance)? + .get_mapped_listener_manage_service() + .list_mapped_listener(ctrl, req) + .await + } +} diff --git a/easytier/src/rpc_service/mod.rs b/easytier/src/rpc_service/mod.rs new file mode 100644 index 000000000..d1fa8841b --- /dev/null +++ b/easytier/src/rpc_service/mod.rs @@ -0,0 +1,108 @@ +mod acl_manage; +mod api; +mod config; +mod connector_manage; +mod mapped_listener_manage; +mod peer_manage; +mod port_forward_manage; +mod proxy; +mod stats; +mod vpn_portal; + +pub mod instance_manage; +pub mod logger; + +pub type ApiRpcServer = self::api::ApiRpcServer; + +pub trait InstanceRpcService: Sync + Send { + fn get_peer_manage_service( + &self, + ) -> &dyn crate::proto::api::instance::PeerManageRpc< + Controller = crate::proto::rpc_types::controller::BaseController, + >; + fn get_connector_manage_service( + &self, + ) -> &dyn crate::proto::api::instance::ConnectorManageRpc< + Controller = crate::proto::rpc_types::controller::BaseController, + >; + fn get_mapped_listener_manage_service( + &self, + ) -> &dyn crate::proto::api::instance::MappedListenerManageRpc< + Controller = crate::proto::rpc_types::controller::BaseController, + >; + fn get_vpn_portal_service( + &self, + ) -> &dyn crate::proto::api::instance::VpnPortalRpc< + Controller = crate::proto::rpc_types::controller::BaseController, + >; + fn get_proxy_service( + &self, + client_type: &str, + ) -> Option< + std::sync::Arc< + dyn crate::proto::api::instance::TcpProxyRpc< + Controller = crate::proto::rpc_types::controller::BaseController, + > + Send + + Sync, + >, + >; + fn get_acl_manage_service( + &self, + ) -> &dyn crate::proto::api::instance::AclManageRpc< + Controller = crate::proto::rpc_types::controller::BaseController, + >; + fn get_port_forward_manage_service( + &self, + ) -> &dyn crate::proto::api::instance::PortForwardManageRpc< + Controller = crate::proto::rpc_types::controller::BaseController, + >; + fn get_stats_service( + &self, + ) -> &dyn crate::proto::api::instance::StatsRpc< + Controller = crate::proto::rpc_types::controller::BaseController, + >; + fn get_config_service( + &self, + ) -> &dyn crate::proto::api::config::ConfigRpc< + Controller = crate::proto::rpc_types::controller::BaseController, + >; +} + +fn get_instance_service( + instance_manager: &std::sync::Arc, + identifier: &Option, +) -> Result, anyhow::Error> { + use crate::proto::api; + let selector = identifier.as_ref().and_then(|s| s.selector.as_ref()); + + let id = if let Some(api::instance::instance_identifier::Selector::Id(id)) = selector { + (*id).into() + } else { + let ids = instance_manager.filter_network_instance(|_, i| { + if let Some(api::instance::instance_identifier::Selector::InstanceSelector(selector)) = + selector + { + if let Some(name) = selector.name.as_ref() { + if i.get_inst_name() != *name { + return false; + } + } + } + true + }); + match ids.len() { + 0 => return Err(anyhow::anyhow!("No instance matches the selector")), + 1 => ids[0], + _ => { + return Err(anyhow::anyhow!( + "{} instances match the selector, please specify the instance ID", + ids.len() + )) + } + } + }; + + instance_manager + .get_instance_service(&id) + .ok_or_else(|| anyhow::anyhow!("Instance not found or API service not available")) +} diff --git a/easytier/src/rpc_service/peer_manage.rs b/easytier/src/rpc_service/peer_manage.rs new file mode 100644 index 000000000..274e32588 --- /dev/null +++ b/easytier/src/rpc_service/peer_manage.rs @@ -0,0 +1,102 @@ +use std::sync::Arc; + +use crate::{ + instance_manager::NetworkInstanceManager, + proto::{ + api::instance::{self, ListPeerRequest, ListPeerResponse, PeerManageRpc}, + rpc_types::controller::BaseController, + }, +}; + +#[derive(Clone)] +pub struct PeerManageRpcService { + instance_manager: Arc, +} + +impl PeerManageRpcService { + pub fn new(instance_manager: Arc) -> Self { + Self { instance_manager } + } +} + +#[async_trait::async_trait] +impl PeerManageRpc for PeerManageRpcService { + type Controller = BaseController; + + async fn list_peer( + &self, + ctrl: Self::Controller, + req: ListPeerRequest, + ) -> crate::proto::rpc_types::error::Result { + super::get_instance_service(&self.instance_manager, &req.instance)? + .get_peer_manage_service() + .list_peer(ctrl, req) + .await + } + + async fn list_route( + &self, + ctrl: Self::Controller, + req: crate::proto::api::instance::ListRouteRequest, + ) -> crate::proto::rpc_types::error::Result { + super::get_instance_service(&self.instance_manager, &req.instance)? + .get_peer_manage_service() + .list_route(ctrl, req) + .await + } + + async fn dump_route( + &self, + ctrl: Self::Controller, + req: crate::proto::api::instance::DumpRouteRequest, + ) -> crate::proto::rpc_types::error::Result { + super::get_instance_service(&self.instance_manager, &req.instance)? + .get_peer_manage_service() + .dump_route(ctrl, req) + .await + } + + async fn list_foreign_network( + &self, + ctrl: Self::Controller, + req: crate::proto::api::instance::ListForeignNetworkRequest, + ) -> crate::proto::rpc_types::error::Result { + super::get_instance_service(&self.instance_manager, &req.instance)? + .get_peer_manage_service() + .list_foreign_network(ctrl, req) + .await + } + + async fn list_global_foreign_network( + &self, + ctrl: Self::Controller, + req: crate::proto::api::instance::ListGlobalForeignNetworkRequest, + ) -> crate::proto::rpc_types::error::Result { + super::get_instance_service(&self.instance_manager, &req.instance)? + .get_peer_manage_service() + .list_global_foreign_network(ctrl, req) + .await + } + + async fn get_foreign_network_summary( + &self, + ctrl: Self::Controller, + req: crate::proto::api::instance::GetForeignNetworkSummaryRequest, + ) -> crate::proto::rpc_types::error::Result { + super::get_instance_service(&self.instance_manager, &req.instance)? + .get_peer_manage_service() + .get_foreign_network_summary(ctrl, req) + .await + } + + async fn show_node_info( + &self, + ctrl: Self::Controller, + req: crate::proto::api::instance::ShowNodeInfoRequest, + ) -> crate::proto::rpc_types::error::Result { + super::get_instance_service(&self.instance_manager, &req.instance)? + .get_peer_manage_service() + .show_node_info(ctrl, req) + .await + } +} diff --git a/easytier/src/rpc_service/port_forward_manage.rs b/easytier/src/rpc_service/port_forward_manage.rs new file mode 100644 index 000000000..58726279d --- /dev/null +++ b/easytier/src/rpc_service/port_forward_manage.rs @@ -0,0 +1,36 @@ +use std::sync::Arc; + +use crate::{ + instance_manager::NetworkInstanceManager, + proto::{ + api::instance::{ListPortForwardRequest, ListPortForwardResponse, PortForwardManageRpc}, + rpc_types::controller::BaseController, + }, +}; + +#[derive(Clone)] +pub struct PortForwardManageRpcService { + instance_manager: Arc, +} + +impl PortForwardManageRpcService { + pub fn new(instance_manager: Arc) -> Self { + Self { instance_manager } + } +} + +#[async_trait::async_trait] +impl PortForwardManageRpc for PortForwardManageRpcService { + type Controller = BaseController; + + async fn list_port_forward( + &self, + ctrl: Self::Controller, + req: ListPortForwardRequest, + ) -> crate::proto::rpc_types::error::Result { + super::get_instance_service(&self.instance_manager, &req.instance)? + .get_port_forward_manage_service() + .list_port_forward(ctrl, req) + .await + } +} diff --git a/easytier/src/rpc_service/proxy.rs b/easytier/src/rpc_service/proxy.rs new file mode 100644 index 000000000..d92fb5d88 --- /dev/null +++ b/easytier/src/rpc_service/proxy.rs @@ -0,0 +1,41 @@ +use std::sync::Arc; + +use crate::{ + instance_manager::NetworkInstanceManager, + proto::{ + api::instance::{ListTcpProxyEntryRequest, ListTcpProxyEntryResponse, TcpProxyRpc}, + rpc_types::controller::BaseController, + }, +}; + +#[derive(Clone)] +pub struct TcpProxyRpcService { + instance_manager: Arc, + client_type: &'static str, +} + +impl TcpProxyRpcService { + pub fn new(instance_manager: Arc, client_type: &'static str) -> Self { + Self { + instance_manager, + client_type, + } + } +} + +#[async_trait::async_trait] +impl TcpProxyRpc for TcpProxyRpcService { + type Controller = BaseController; + + async fn list_tcp_proxy_entry( + &self, + ctrl: Self::Controller, + req: ListTcpProxyEntryRequest, + ) -> crate::proto::rpc_types::error::Result { + super::get_instance_service(&self.instance_manager, &req.instance)? + .get_proxy_service(self.client_type) + .ok_or_else(|| anyhow::anyhow!("TCP proxy service not found for {}", self.client_type))? + .list_tcp_proxy_entry(ctrl, req) + .await + } +} diff --git a/easytier/src/rpc_service/stats.rs b/easytier/src/rpc_service/stats.rs new file mode 100644 index 000000000..da6e00658 --- /dev/null +++ b/easytier/src/rpc_service/stats.rs @@ -0,0 +1,50 @@ +use std::sync::Arc; + +use crate::{ + instance_manager::NetworkInstanceManager, + proto::{ + api::instance::{ + GetPrometheusStatsRequest, GetPrometheusStatsResponse, GetStatsRequest, + GetStatsResponse, StatsRpc, + }, + rpc_types::controller::BaseController, + }, +}; + +#[derive(Clone)] +pub struct StatsRpcService { + instance_manager: Arc, +} + +impl StatsRpcService { + pub fn new(instance_manager: Arc) -> Self { + Self { instance_manager } + } +} + +#[async_trait::async_trait] +impl StatsRpc for StatsRpcService { + type Controller = BaseController; + + async fn get_stats( + &self, + ctrl: Self::Controller, + req: GetStatsRequest, + ) -> crate::proto::rpc_types::error::Result { + super::get_instance_service(&self.instance_manager, &req.instance)? + .get_stats_service() + .get_stats(ctrl, req) + .await + } + + async fn get_prometheus_stats( + &self, + ctrl: Self::Controller, + req: GetPrometheusStatsRequest, + ) -> crate::proto::rpc_types::error::Result { + super::get_instance_service(&self.instance_manager, &req.instance)? + .get_stats_service() + .get_prometheus_stats(ctrl, req) + .await + } +} diff --git a/easytier/src/rpc_service/vpn_portal.rs b/easytier/src/rpc_service/vpn_portal.rs new file mode 100644 index 000000000..e09694d67 --- /dev/null +++ b/easytier/src/rpc_service/vpn_portal.rs @@ -0,0 +1,36 @@ +use std::sync::Arc; + +use crate::{ + instance_manager::NetworkInstanceManager, + proto::{ + api::instance::{GetVpnPortalInfoRequest, GetVpnPortalInfoResponse, VpnPortalRpc}, + rpc_types::controller::BaseController, + }, +}; + +#[derive(Clone)] +pub struct VpnPortalRpcService { + instance_manager: Arc, +} + +impl VpnPortalRpcService { + pub fn new(instance_manager: Arc) -> Self { + Self { instance_manager } + } +} + +#[async_trait::async_trait] +impl VpnPortalRpc for VpnPortalRpcService { + type Controller = BaseController; + + async fn get_vpn_portal_info( + &self, + ctrl: Self::Controller, + req: GetVpnPortalInfoRequest, + ) -> crate::proto::rpc_types::error::Result { + super::get_instance_service(&self.instance_manager, &req.instance)? + .get_vpn_portal_service() + .get_vpn_portal_info(ctrl, req) + .await + } +} diff --git a/easytier/src/tests/mod.rs b/easytier/src/tests/mod.rs index 4c81fad90..7996fe32d 100644 --- a/easytier/src/tests/mod.rs +++ b/easytier/src/tests/mod.rs @@ -25,7 +25,7 @@ pub fn del_netns(name: &str) { .output(); } -pub fn create_netns(name: &str, ipv4: &str) { +pub fn create_netns(name: &str, ipv4: &str, ipv6: &str) { // create netns let _ = std::process::Command::new("ip") .args(["netns", "add", name]) @@ -76,20 +76,22 @@ pub fn create_netns(name: &str, ipv4: &str) { .output() .unwrap(); - let _ = std::process::Command::new("ip") - .args([ - "netns", - "exec", - name, - "ip", - "addr", - "add", - ipv4, - "dev", - get_guest_veth_name(name), - ]) - .output() - .unwrap(); + for ip in [ipv4, ipv6] { + let _ = std::process::Command::new("ip") + .args([ + "netns", + "exec", + name, + "ip", + "addr", + "add", + ip, + "dev", + get_guest_veth_name(name), + ]) + .output() + .unwrap(); + } } pub fn prepare_bridge(name: &str) { @@ -130,7 +132,7 @@ pub fn enable_log() { .init(); } -fn check_route(ipv4: &str, dst_peer_id: PeerId, routes: Vec) { +fn check_route(ipv4: &str, dst_peer_id: PeerId, routes: Vec) { let mut found = false; for r in routes.iter() { if r.ipv4_addr == Some(ipv4.parse().unwrap()) { @@ -145,6 +147,21 @@ fn check_route(ipv4: &str, dst_peer_id: PeerId, routes: Vec, + peer_id: PeerId, + checker: impl Fn(&crate::proto::api::instance::Route) -> bool, +) { + let mut found = false; + for r in routes.iter() { + if r.peer_id == peer_id { + found = true; + assert!(checker(r), "{:?}", routes); + } + } + assert!(found, "routes: {:?}, dst_peer_id: {}", routes, peer_id); +} + async fn wait_proxy_route_appear( mgr: &std::sync::Arc, ipv4: &str, diff --git a/easytier/src/tests/three_node.rs b/easytier/src/tests/three_node.rs index 3c529e680..4837de2f2 100644 --- a/easytier/src/tests/three_node.rs +++ b/easytier/src/tests/three_node.rs @@ -15,9 +15,10 @@ use crate::{ common::{ config::{ConfigLoader, NetworkIdentity, PortForwardConfig, TomlConfigLoader}, netns::{NetNS, ROOT_NETNS_NAME}, + stats_manager::{LabelType, MetricName}, }, instance::instance::Instance, - proto::common::CompressionAlgoPb, + proto::{api::instance::TcpProxyEntryTransportType, common::CompressionAlgoPb}, tunnel::{ common::tests::{_tunnel_bench_netns, wait_for_condition}, ring::RingTunnelConnector, @@ -39,10 +40,10 @@ pub fn prepare_linux_namespaces() { del_netns("net_c"); del_netns("net_d"); - create_netns("net_a", "10.1.1.1/24"); - create_netns("net_b", "10.1.1.2/24"); - create_netns("net_c", "10.1.2.3/24"); - create_netns("net_d", "10.1.2.4/24"); + create_netns("net_a", "10.1.1.1/24", "fd11::1/64"); + create_netns("net_b", "10.1.1.2/24", "fd11::2/64"); + create_netns("net_c", "10.1.2.3/24", "fd12::3/64"); + create_netns("net_d", "10.1.2.4/24", "fd12::4/64"); prepare_bridge("br_a"); prepare_bridge("br_b"); @@ -340,11 +341,12 @@ pub async fn basic_three_node_test( drop_insts(insts).await; } -async fn subnet_proxy_test_udp(target_ip: &str) { +async fn subnet_proxy_test_udp(listen_ip: &str, target_ip: &str) { use crate::tunnel::{common::tests::_tunnel_pingpong_netns, udp::UdpTunnelListener}; use rand::Rng; - let udp_listener = UdpTunnelListener::new("udp://10.1.2.4:22233".parse().unwrap()); + let udp_listener = + UdpTunnelListener::new(format!("udp://{}:22233", listen_ip).parse().unwrap()); let udp_connector = UdpTunnelConnector::new(format!("udp://{}:22233", target_ip).parse().unwrap()); @@ -352,17 +354,24 @@ async fn subnet_proxy_test_udp(target_ip: &str) { let mut buf = vec![0; 7 * 1024]; rand::thread_rng().fill(&mut buf[..]); + let ns_name = if target_ip == "10.144.144.3" { + "net_c" + } else { + "net_d" + }; + _tunnel_pingpong_netns( udp_listener, udp_connector, - NetNS::new(Some("net_d".into())), + NetNS::new(Some(ns_name.into())), NetNS::new(Some("net_a".into())), buf, ) .await; // no fragment - let udp_listener = UdpTunnelListener::new("udp://10.1.2.4:22233".parse().unwrap()); + let udp_listener = + UdpTunnelListener::new(format!("udp://{}:22233", listen_ip).parse().unwrap()); let udp_connector = UdpTunnelConnector::new(format!("udp://{}:22233", target_ip).parse().unwrap()); @@ -372,77 +381,34 @@ async fn subnet_proxy_test_udp(target_ip: &str) { _tunnel_pingpong_netns( udp_listener, udp_connector, - NetNS::new(Some("net_d".into())), - NetNS::new(Some("net_a".into())), - buf, - ) - .await; - - // connect to virtual ip (no tun mode) - - let udp_listener = UdpTunnelListener::new("udp://0.0.0.0:22234".parse().unwrap()); - let udp_connector = UdpTunnelConnector::new("udp://10.144.144.3:22234".parse().unwrap()); - // NOTE: this should not excced udp tunnel max buffer size - let mut buf = vec![0; 7 * 1024]; - rand::thread_rng().fill(&mut buf[..]); - - _tunnel_pingpong_netns( - udp_listener, - udp_connector, - NetNS::new(Some("net_c".into())), - NetNS::new(Some("net_a".into())), - buf, - ) - .await; - - // no fragment - let udp_listener = UdpTunnelListener::new("udp://0.0.0.0:22235".parse().unwrap()); - let udp_connector = UdpTunnelConnector::new("udp://10.144.144.3:22235".parse().unwrap()); - - let mut buf = vec![0; 1024]; - rand::thread_rng().fill(&mut buf[..]); - - _tunnel_pingpong_netns( - udp_listener, - udp_connector, - NetNS::new(Some("net_c".into())), + NetNS::new(Some(ns_name.into())), NetNS::new(Some("net_a".into())), buf, ) .await; } -async fn subnet_proxy_test_tcp(target_ip: &str) { +async fn subnet_proxy_test_tcp(listen_ip: &str, connect_ip: &str) { use crate::tunnel::{common::tests::_tunnel_pingpong_netns, tcp::TcpTunnelListener}; use rand::Rng; - let tcp_listener = TcpTunnelListener::new("tcp://10.1.2.4:22223".parse().unwrap()); + let tcp_listener = TcpTunnelListener::new(format!("tcp://{listen_ip}:22223").parse().unwrap()); let tcp_connector = - TcpTunnelConnector::new(format!("tcp://{}:22223", target_ip).parse().unwrap()); + TcpTunnelConnector::new(format!("tcp://{}:22223", connect_ip).parse().unwrap()); let mut buf = vec![0; 32]; rand::thread_rng().fill(&mut buf[..]); - _tunnel_pingpong_netns( - tcp_listener, - tcp_connector, - NetNS::new(Some("net_d".into())), - NetNS::new(Some("net_a".into())), - buf, - ) - .await; - - // connect to virtual ip (no tun mode) - let tcp_listener = TcpTunnelListener::new("tcp://0.0.0.0:22223".parse().unwrap()); - let tcp_connector = TcpTunnelConnector::new("tcp://10.144.144.3:22223".parse().unwrap()); - - let mut buf = vec![0; 32]; - rand::thread_rng().fill(&mut buf[..]); + let ns_name = if connect_ip == "10.144.144.3" { + "net_c" + } else { + "net_d" + }; _tunnel_pingpong_netns( tcp_listener, tcp_connector, - NetNS::new(Some("net_c".into())), + NetNS::new(Some(ns_name.into())), NetNS::new(Some("net_a".into())), buf, ) @@ -461,19 +427,53 @@ async fn subnet_proxy_test_icmp(target_ip: &str) { Duration::from_secs(5), ) .await; +} - // connect to virtual ip (no tun mode) - wait_for_condition( - || async { ping_test("net_a", "10.144.144.3", None).await }, - Duration::from_secs(5), +#[tokio::test] +pub async fn quic_proxy() { + let insts = init_three_node_ex( + "udp", + |cfg| { + if cfg.get_inst_name() == "inst3" { + cfg.add_proxy_cidr("10.1.2.0/24".parse().unwrap(), None) + .unwrap(); + } else if cfg.get_inst_name() == "inst1" { + let mut flags = cfg.get_flags(); + flags.enable_quic_proxy = true; + cfg.set_flags(flags); + } + cfg + }, + false, ) .await; - wait_for_condition( - || async { ping_test("net_a", "10.144.144.3", Some(5 * 1024)).await }, - Duration::from_secs(5), + assert_eq!(insts[2].get_global_ctx().config.get_proxy_cidrs().len(), 1); + + wait_proxy_route_appear( + &insts[0].get_peer_manager(), + "10.144.144.3/24", + insts[2].peer_id(), + "10.1.2.0/24", ) .await; + + let target_ip = "10.1.2.4"; + + subnet_proxy_test_icmp(target_ip).await; + subnet_proxy_test_icmp("10.144.144.3").await; + subnet_proxy_test_tcp(target_ip, target_ip).await; + subnet_proxy_test_tcp("0.0.0.0", "10.144.144.3").await; + + let metrics = insts[0] + .get_global_ctx() + .stats_manager() + .get_metrics_by_prefix(&MetricName::TcpProxyConnect.to_string()); + assert_eq!(metrics.len(), 2); + assert_eq!(1, metrics[0].value); + assert_eq!(1, metrics[1].value); + + drop_insts(insts).await; } #[tokio::test] @@ -583,10 +583,64 @@ pub async fn subnet_proxy_three_node_test( ) .await; - for target_ip in ["10.1.3.4", "10.1.2.4"].iter() { + for target_ip in ["10.1.3.4", "10.1.2.4", "10.144.144.3"] { subnet_proxy_test_icmp(target_ip).await; - subnet_proxy_test_tcp(target_ip).await; - subnet_proxy_test_udp(target_ip).await; + let listen_ip = if target_ip == "10.144.144.3" { + "0.0.0.0" + } else { + "10.1.2.4" + }; + subnet_proxy_test_tcp(listen_ip, target_ip).await; + subnet_proxy_test_udp(listen_ip, target_ip).await; + } + + if enable_kcp_proxy && !disable_kcp_input { + let metrics = insts[0] + .get_global_ctx() + .stats_manager() + .get_metrics_by_prefix(&MetricName::TcpProxyConnect.to_string()); + assert_eq!(metrics.len(), 3); + for metric in metrics { + assert_eq!(1, metric.value); + assert!(metric.labels.labels().iter().any(|l| { + let t = + LabelType::Protocol(TcpProxyEntryTransportType::Kcp.as_str_name().to_string()); + t.key() == l.key && t.value() == l.value + })); + } + } else if enable_quic_proxy && !disable_quic_input { + let metrics = insts[0] + .get_global_ctx() + .stats_manager() + .get_metrics_by_prefix(&MetricName::TcpProxyConnect.to_string()); + assert_eq!(metrics.len(), 3); + for metric in metrics { + assert_eq!(1, metric.value); + assert!(metric.labels.labels().iter().any(|l| { + let t = + LabelType::Protocol(TcpProxyEntryTransportType::Quic.as_str_name().to_string()); + t.key() == l.key && t.value() == l.value + })); + } + } else { + // tcp subnet proxy + let metrics = insts[2] + .get_global_ctx() + .stats_manager() + .get_metrics_by_prefix(&MetricName::TcpProxyConnect.to_string()); + if no_tun { + assert_eq!(metrics.len(), 3); + } else { + assert_eq!(metrics.len(), 2); + } + for metric in metrics { + assert_eq!(1, metric.value); + assert!(metric.labels.labels().iter().any(|l| { + let t = + LabelType::Protocol(TcpProxyEntryTransportType::Tcp.as_str_name().to_string()); + t.key() == l.key && t.value() == l.value + })); + } } drop_insts(insts).await; @@ -910,10 +964,18 @@ fn run_wireguard_client( } #[cfg(feature = "wireguard")] +#[rstest::rstest] #[tokio::test] #[serial_test::serial] -pub async fn wireguard_vpn_portal() { +pub async fn wireguard_vpn_portal(#[values(true, false)] test_v6: bool) { let mut insts = init_three_node("tcp").await; + + if test_v6 { + ping6_test("net_d", "fd12::3", None).await; + } else { + ping_test("net_d", "10.1.2.3", None).await; + } + let net_ns = NetNS::new(Some("net_d".into())); let _g = net_ns.guard(); insts[2] @@ -925,11 +987,17 @@ pub async fn wireguard_vpn_portal() { }); insts[2].run_vpn_portal().await.unwrap(); + let dst_socket_addr = if test_v6 { + "[fd12::3]:22121".parse().unwrap() + } else { + "10.1.2.3:22121".parse().unwrap() + }; + let net_ns = NetNS::new(Some("net_d".into())); let _g = net_ns.guard(); let wg_cfg = get_wg_config_for_portal(&insts[2].get_global_ctx().get_network_identity()); run_wireguard_client( - "10.1.2.3:22121".parse().unwrap(), + dst_socket_addr, Key::try_from(wg_cfg.my_public_key()).unwrap(), Key::try_from(wg_cfg.peer_secret_key()).unwrap(), vec!["10.14.14.0/24".to_string(), "10.144.144.0/24".to_string()], @@ -1818,3 +1886,511 @@ pub async fn acl_rule_test_subnet_proxy( drop_insts(insts).await; } + +#[rstest::rstest] +#[tokio::test] +#[serial_test::serial] +pub async fn acl_group_base_test( + #[values("tcp", "udp")] protocol: &str, + #[values(true, false)] enable_kcp_proxy: bool, + #[values(true, false)] enable_quic_proxy: bool, +) { + use crate::tunnel::{ + common::tests::_tunnel_pingpong_netns_with_timeout, + tcp::{TcpTunnelConnector, TcpTunnelListener}, + udp::{UdpTunnelConnector, UdpTunnelListener}, + TunnelConnector, TunnelListener, + }; + use rand::Rng; + + // 构造 ACL 配置,包含组信息 + use crate::proto::acl::*; + + // 设置组信息 + let group_declares = vec![ + GroupIdentity { + group_name: "admin".to_string(), + group_secret: "admin-secret".to_string(), + }, + GroupIdentity { + group_name: "user".to_string(), + group_secret: "user-secret".to_string(), + }, + ]; + + let mut chain = Chain { + name: "group_acl_test".to_string(), + chain_type: ChainType::Inbound as i32, + enabled: true, + default_action: Action::Drop as i32, + ..Default::default() + }; + + // 规则1: 允许admin组访问所有端口 + let admin_allow_rule = Rule { + name: "allow_admin_all".to_string(), + priority: 300, + enabled: true, + action: Action::Allow as i32, + protocol: Protocol::Any as i32, + source_groups: vec!["admin".to_string()], + stateful: true, + ..Default::default() + }; + chain.rules.push(admin_allow_rule); + + // 规则2: 允许user组访问8080端口 + let user_8080_rule = Rule { + name: "allow_user_8080".to_string(), + priority: 200, + enabled: true, + action: Action::Allow as i32, + protocol: Protocol::Any as i32, + source_groups: vec!["user".to_string()], + ports: vec!["8080".to_string()], + stateful: true, + ..Default::default() + }; + chain.rules.push(user_8080_rule); + + let acl_admin = Acl { + acl_v1: Some(AclV1 { + group: Some(GroupInfo { + declares: group_declares.clone(), + members: vec!["admin".to_string()], + }), + ..AclV1::default() + }), + }; + + let acl_user = Acl { + acl_v1: Some(AclV1 { + group: Some(GroupInfo { + declares: group_declares.clone(), + members: vec!["user".to_string()], + }), + ..AclV1::default() + }), + }; + + let acl_target = Acl { + acl_v1: Some(AclV1 { + chains: vec![chain.clone()], + group: Some(GroupInfo { + declares: group_declares.clone(), + members: vec![], + }), + }), + }; + + let insts = init_three_node_ex( + protocol, + move |cfg| { + match cfg.get_inst_name().as_str() { + "inst1" => { + cfg.set_acl(Some(acl_admin.clone())); + } + "inst2" => { + cfg.set_acl(Some(acl_user.clone())); + } + "inst3" => { + cfg.set_acl(Some(acl_target.clone())); + } + _ => {} + } + + let mut flags = cfg.get_flags(); + flags.enable_kcp_proxy = enable_kcp_proxy; + flags.enable_quic_proxy = enable_quic_proxy; + cfg.set_flags(flags); + + cfg + }, + false, + ) + .await; + + println!("Testing group-based ACL rules..."); + + let make_listener = |port: u16| -> Box { + match protocol { + "tcp" => Box::new(TcpTunnelListener::new( + format!("tcp://0.0.0.0:{}", port).parse().unwrap(), + )), + "udp" => Box::new(UdpTunnelListener::new( + format!("udp://0.0.0.0:{}", port).parse().unwrap(), + )), + _ => panic!("unsupported protocol: {}", protocol), + } + }; + + let make_connector = |port: u16| -> Box { + match protocol { + "tcp" => Box::new(TcpTunnelConnector::new( + format!("tcp://10.144.144.3:{}", port).parse().unwrap(), + )), + "udp" => Box::new(UdpTunnelConnector::new( + format!("udp://10.144.144.3:{}", port).parse().unwrap(), + )), + _ => panic!("unsupported protocol: {}", protocol), + } + }; + + // 构造测试数据 + let mut buf = vec![0; 32]; + rand::thread_rng().fill(&mut buf[..]); + + // 测试1: inst1 (admin组) 访问8080 - 应该成功 + let result = _tunnel_pingpong_netns_with_timeout( + make_listener(8080), + make_connector(8080), + NetNS::new(Some("net_c".into())), + NetNS::new(Some("net_a".into())), + buf.clone(), + std::time::Duration::from_millis(30000), + ) + .await; + assert!( + result.is_ok(), + "Admin group access to port 8080 should be allowed (protocol={})", + protocol + ); + println!( + "✓ Admin group access to port 8080 succeeded ({})\n", + protocol + ); + + // 测试2: inst1 (admin组) 访问8081 - 应该成功 + let result = _tunnel_pingpong_netns_with_timeout( + make_listener(8081), + make_connector(8081), + NetNS::new(Some("net_c".into())), + NetNS::new(Some("net_a".into())), + buf.clone(), + std::time::Duration::from_millis(30000), + ) + .await; + assert!( + result.is_ok(), + "Admin group access to port 8081 should be allowed (protocol={})", + protocol + ); + println!( + "✓ Admin group access to port 8081 succeeded ({})\n", + protocol + ); + + // 测试3: inst2 (user组) 访问8080 - 应该成功 + let result = _tunnel_pingpong_netns_with_timeout( + make_listener(8080), + make_connector(8080), + NetNS::new(Some("net_c".into())), + NetNS::new(Some("net_b".into())), + buf.clone(), + std::time::Duration::from_millis(30000), + ) + .await; + assert!( + result.is_ok(), + "User group access to port 8080 should be allowed (protocol={})", + protocol + ); + println!( + "✓ User group access to port 8080 succeeded ({})\n", + protocol + ); + + // 测试4: inst2 (user组) 访问8081 - 应该失败 + let result = _tunnel_pingpong_netns_with_timeout( + make_listener(8081), + make_connector(8081), + NetNS::new(Some("net_c".into())), + NetNS::new(Some("net_b".into())), + buf.clone(), + std::time::Duration::from_millis(200), + ) + .await; + assert!( + result.is_err(), + "User group access to port 8081 should be blocked (protocol={})", + protocol + ); + println!( + "✓ User group access to port 8081 blocked as expected ({})\n", + protocol + ); + + let stats = insts[2].get_global_ctx().get_acl_filter().get_stats(); + println!("ACL stats after group {} tests: {:?}", protocol, stats); + + println!("✓ All group-based ACL tests completed successfully"); + + drop_insts(insts).await; +} + +#[rstest::rstest] +#[tokio::test] +#[serial_test::serial] +pub async fn acl_group_self_test( + #[values("tcp", "udp")] protocol: &str, + #[values(true, false)] enable_kcp_proxy: bool, + #[values(true, false)] enable_quic_proxy: bool, +) { + use crate::tunnel::{ + common::tests::_tunnel_pingpong_netns_with_timeout, + tcp::{TcpTunnelConnector, TcpTunnelListener}, + udp::{UdpTunnelConnector, UdpTunnelListener}, + TunnelConnector, TunnelListener, + }; + use rand::Rng; + + // 构造 ACL 配置,包含组信息 + use crate::proto::acl::*; + + // 设置组信息 + let group_declares = vec![GroupIdentity { + group_name: "admin".to_string(), + group_secret: "admin-secret".to_string(), + }]; + + let mut chain = Chain { + name: "group_acl_test".to_string(), + chain_type: ChainType::Inbound as i32, + enabled: true, + default_action: Action::Drop as i32, + ..Default::default() + }; + + // 规则1: 允许admin组访问admin组 + let admin_allow_rule = Rule { + name: "allow_admin_admin".to_string(), + priority: 300, + enabled: true, + action: Action::Allow as i32, + protocol: Protocol::Any as i32, + source_groups: vec!["admin".to_string()], + destination_groups: vec!["admin".to_string()], + stateful: true, + ..Default::default() + }; + chain.rules.push(admin_allow_rule); + + let acl_admin = Acl { + acl_v1: Some(AclV1 { + chains: vec![chain.clone()], + group: Some(GroupInfo { + declares: group_declares.clone(), + members: vec!["admin".to_string()], + }), + }), + }; + + let acl_common = Acl { + acl_v1: Some(AclV1 { + chains: vec![chain.clone()], + group: Some(GroupInfo { + declares: group_declares.clone(), + members: vec![], + }), + }), + }; + + let insts = init_three_node_ex( + protocol, + move |cfg| { + match cfg.get_inst_name().as_str() { + "inst1" => { + cfg.set_acl(Some(acl_admin.clone())); + } + "inst2" => { + cfg.set_acl(Some(acl_common.clone())); + } + "inst3" => { + cfg.set_acl(Some(acl_admin.clone())); + } + _ => {} + } + + let mut flags = cfg.get_flags(); + flags.enable_kcp_proxy = enable_kcp_proxy; + flags.enable_quic_proxy = enable_quic_proxy; + cfg.set_flags(flags); + + cfg + }, + false, + ) + .await; + + println!("Testing group-based ACL rules..."); + + let make_listener = |port: u16| -> Box { + match protocol { + "tcp" => Box::new(TcpTunnelListener::new( + format!("tcp://0.0.0.0:{}", port).parse().unwrap(), + )), + "udp" => Box::new(UdpTunnelListener::new( + format!("udp://0.0.0.0:{}", port).parse().unwrap(), + )), + _ => panic!("unsupported protocol: {}", protocol), + } + }; + + let make_connector = |port: u16| -> Box { + match protocol { + "tcp" => Box::new(TcpTunnelConnector::new( + format!("tcp://10.144.144.3:{}", port).parse().unwrap(), + )), + "udp" => Box::new(UdpTunnelConnector::new( + format!("udp://10.144.144.3:{}", port).parse().unwrap(), + )), + _ => panic!("unsupported protocol: {}", protocol), + } + }; + + // 构造测试数据 + let mut buf = vec![0; 32]; + rand::thread_rng().fill(&mut buf[..]); + + // 测试1: inst1 (admin组) 访问inst3 (admin组) - 应该成功 + let result = _tunnel_pingpong_netns_with_timeout( + make_listener(8080), + make_connector(8080), + NetNS::new(Some("net_c".into())), + NetNS::new(Some("net_a".into())), + buf.clone(), + std::time::Duration::from_millis(30000), + ) + .await; + assert!( + result.is_ok(), + "Admin group access to Admin group should be allowed (protocol={})", + protocol + ); + println!( + "✓ Admin group access to Admin group succeeded ({})\n", + protocol + ); + + // 测试2: inst2 (无组) 访问inst3 (admin组) - 应该失败 + let result = _tunnel_pingpong_netns_with_timeout( + make_listener(8080), + make_connector(8080), + NetNS::new(Some("net_c".into())), + NetNS::new(Some("net_b".into())), + buf.clone(), + std::time::Duration::from_millis(200), + ) + .await; + assert!( + result.is_err(), + "None group access to inst3 (admin group) should be blocked (protocol={})", + protocol + ); + println!( + "✓ None group access to inst3 (admin group) blocked as expected ({})\n", + protocol + ); + + let stats = insts[2].get_global_ctx().get_acl_filter().get_stats(); + println!("ACL stats after group {} tests: {:?}", protocol, stats); + + println!("✓ All group-based ACL tests completed successfully"); + + drop_insts(insts).await; +} + +#[rstest::rstest] +#[tokio::test] +#[serial_test::serial] +pub async fn config_patch_test() { + use crate::proto::{ + api::config::{ + ConfigPatchAction, InstanceConfigPatch, PortForwardPatch, ProxyNetworkPatch, + }, + common::{PortForwardConfigPb, SocketType}, + }; + use crate::tunnel::common::tests::_tunnel_pingpong_netns_with_timeout; + + let insts = init_three_node("udp").await; + + check_route( + "10.144.144.2/24", + insts[1].peer_id(), + insts[0].get_peer_manager().list_routes().await, + ); + + check_route( + "10.144.144.3/24", + insts[2].peer_id(), + insts[0].get_peer_manager().list_routes().await, + ); + + // 测试1: 修改hostname、ip、子网代理 + let patch = InstanceConfigPatch { + hostname: Some("new_inst1".to_string()), + ipv4: Some("10.144.144.22/24".parse().unwrap()), + proxy_networks: vec![ProxyNetworkPatch { + action: ConfigPatchAction::Add as i32, + cidr: Some("10.144.145.0/24".parse().unwrap()), + mapped_cidr: None, + }], + ..Default::default() + }; + insts[1] + .get_config_patcher() + .apply_patch(patch) + .await + .unwrap(); + assert_eq!(insts[1].get_global_ctx().get_hostname(), "new_inst1"); + assert_eq!( + insts[1].get_global_ctx().get_ipv4().unwrap(), + "10.144.144.22/24".parse().unwrap() + ); + tokio::time::sleep(Duration::from_secs(1)).await; + check_route_ex( + insts[0].get_peer_manager().list_routes().await, + insts[1].peer_id(), + |r| { + assert_eq!(r.hostname, "new_inst1"); + assert_eq!(r.ipv4_addr, Some("10.144.144.22/24".parse().unwrap())); + assert_eq!(r.proxy_cidrs[0], "10.144.145.0/24"); + true + }, + ); + + // 测试2: 端口转发 + let patch = InstanceConfigPatch { + port_forwards: vec![PortForwardPatch { + action: ConfigPatchAction::Add as i32, + cfg: Some(PortForwardConfigPb { + bind_addr: Some("0.0.0.0:23458".parse::().unwrap().into()), + dst_addr: Some("10.144.144.3:23457".parse::().unwrap().into()), + socket_type: SocketType::Tcp as i32, + }), + }], + ..Default::default() + }; + insts[0] + .get_config_patcher() + .apply_patch(patch) + .await + .unwrap(); + + let mut buf = vec![0; 32]; + rand::thread_rng().fill(&mut buf[..]); + let tcp_listener = TcpTunnelListener::new("tcp://0.0.0.0:23457".parse().unwrap()); + let tcp_connector = TcpTunnelConnector::new("tcp://127.0.0.1:23458".parse().unwrap()); + let result = _tunnel_pingpong_netns_with_timeout( + tcp_listener, + tcp_connector, + NetNS::new(Some("net_c".into())), + NetNS::new(Some("net_a".into())), + buf.clone(), + std::time::Duration::from_millis(30000), + ) + .await; + assert!(result.is_ok(), "Port forward pingpong should succeed"); + + drop_insts(insts).await; +} diff --git a/easytier/src/tunnel/common.rs b/easytier/src/tunnel/common.rs index 8d26db78c..b5ac636d7 100644 --- a/easytier/src/tunnel/common.rs +++ b/easytier/src/tunnel/common.rs @@ -384,7 +384,11 @@ pub(crate) fn setup_sokcet2_ext( unsafe { let dev_idx = nix::libc::if_nametoindex(dev_name.as_str().as_ptr() as *const i8); tracing::warn!(?dev_idx, ?dev_name, "bind device"); - socket2_socket.bind_device_by_index_v4(std::num::NonZeroU32::new(dev_idx))?; + if bind_addr.is_ipv4() { + socket2_socket.bind_device_by_index_v4(std::num::NonZeroU32::new(dev_idx))?; + } else { + socket2_socket.bind_device_by_index_v6(std::num::NonZeroU32::new(dev_idx))?; + } tracing::warn!(?dev_idx, ?dev_name, "bind device doen"); } } @@ -560,6 +564,45 @@ pub mod tests { } } + pub(crate) async fn _tunnel_pingpong_netns_with_timeout( + listener: L, + connector: C, + l_netns: NetNS, + c_netns: NetNS, + buf: Vec, + timeout: std::time::Duration, + ) -> Result<(), anyhow::Error> + where + L: TunnelListener + Send + Sync + 'static, + C: TunnelConnector + Send + Sync + 'static, + { + let handle = tokio::spawn(async move { + _tunnel_pingpong_netns(listener, connector, l_netns, c_netns, buf).await; + }); + + match tokio::time::timeout(timeout, handle).await { + Ok(join_res) => match join_res { + Ok(_) => Ok(()), + Err(join_err) => { + if join_err.is_panic() { + let payload = join_err.into_panic(); + let msg = match payload.downcast::() { + Ok(s) => *s, + Err(payload) => match payload.downcast::<&str>() { + Ok(s) => (*s).to_string(), + Err(_) => "non-string panic payload".to_string(), + }, + }; + Err(anyhow::anyhow!("task panicked: {}", msg)) + } else { + Err(anyhow::anyhow!("task cancelled")) + } + } + }, + Err(elapsed) => Err(elapsed.into()), + } + } + pub(crate) async fn _tunnel_bench(listener: L, connector: C) where L: TunnelListener + Send + Sync + 'static, diff --git a/easytier/src/tunnel/packet_def.rs b/easytier/src/tunnel/packet_def.rs index 54c5f850c..7c0a4bb8c 100644 --- a/easytier/src/tunnel/packet_def.rs +++ b/easytier/src/tunnel/packet_def.rs @@ -679,6 +679,14 @@ impl ZCPacket { ZCPacketType::DummyTunnel, ) } + + pub fn get_src_peer_id(&self) -> Option { + self.peer_manager_header().map(|hdr| hdr.from_peer_id.get()) + } + + pub fn get_dst_peer_id(&self) -> Option { + self.peer_manager_header().map(|hdr| hdr.to_peer_id.get()) + } } #[cfg(test)] diff --git a/easytier/src/tunnel/quic.rs b/easytier/src/tunnel/quic.rs index 49695d960..bddfff10f 100644 --- a/easytier/src/tunnel/quic.rs +++ b/easytier/src/tunnel/quic.rs @@ -7,7 +7,7 @@ use std::{ }; use crate::tunnel::{ - common::{FramedReader, FramedWriter, TunnelWrapper}, + common::{setup_sokcet2, FramedReader, FramedWriter, TunnelWrapper}, TunnelInfo, }; use anyhow::Context; @@ -89,12 +89,20 @@ impl AsyncUdpSocket for NoGroAsyncUdpSocket { #[allow(unused)] pub fn make_server_endpoint(bind_addr: SocketAddr) -> Result<(Endpoint, Vec), Box> { let (server_config, server_cert) = configure_server()?; - let socket = std::net::UdpSocket::bind(bind_addr)?; + + let socket2_socket = socket2::Socket::new( + socket2::Domain::for_address(bind_addr), + socket2::Type::DGRAM, + Some(socket2::Protocol::UDP), + )?; + setup_sokcet2(&socket2_socket, &bind_addr)?; + let socket = std::net::UdpSocket::from(socket2_socket); + let runtime = quinn::default_runtime().ok_or_else(|| std::io::Error::other("no async runtime found"))?; let mut endpoint_config = EndpointConfig::default(); endpoint_config.max_udp_payload_size(1200)?; - let socket = NoGroAsyncUdpSocket { + let socket: NoGroAsyncUdpSocket = NoGroAsyncUdpSocket { inner: runtime.wrap_udp_socket(socket)?, }; let endpoint = Endpoint::new_with_abstract_socket( @@ -147,6 +155,36 @@ impl QUICTunnelListener { server_cert: None, } } + + async fn do_accept(&mut self) -> Result, super::TunnelError> { + // accept a single connection + let conn = self + .endpoint + .as_ref() + .unwrap() + .accept() + .await + .ok_or_else(|| anyhow::anyhow!("accept failed, no incoming"))?; + let conn = conn.await.with_context(|| "accept connection failed")?; + let remote_addr = conn.remote_address(); + let (w, r) = conn.accept_bi().await.with_context(|| "accept_bi failed")?; + + let arc_conn = Arc::new(ConnWrapper { conn }); + + let info = TunnelInfo { + tunnel_type: "quic".to_owned(), + local_addr: Some(self.local_url().into()), + remote_addr: Some( + super::build_url_from_socket_addr(&remote_addr.to_string(), "quic").into(), + ), + }; + + Ok(Box::new(TunnelWrapper::new( + FramedReader::new_with_associate_data(r, 2000, Some(Box::new(arc_conn.clone()))), + FramedWriter::new_with_associate_data(w, Some(Box::new(arc_conn))), + Some(info), + ))) + } } #[async_trait::async_trait] @@ -155,7 +193,8 @@ impl TunnelListener for QUICTunnelListener { let addr = check_scheme_and_get_socket_addr::(&self.addr, "quic", IpVersion::Both) .await?; - let (endpoint, server_cert) = make_server_endpoint(addr).unwrap(); + let (endpoint, server_cert) = make_server_endpoint(addr) + .map_err(|e| anyhow::anyhow!("make server endpoint error: {:?}", e))?; self.endpoint = Some(endpoint); self.server_cert = Some(server_cert); @@ -167,44 +206,15 @@ impl TunnelListener for QUICTunnelListener { } async fn accept(&mut self) -> Result, super::TunnelError> { - // accept a single connection - let conn = loop { - let Some(incoming_conn) = self.endpoint.as_ref().unwrap().accept().await else { - tokio::time::sleep(Duration::from_millis(100)).await; - continue; - }; - match incoming_conn.await { - Ok(conn) => { - tracing::info!( - "[server] connection accepted: addr={}", - conn.remote_address() - ); - break conn; - } + loop { + match self.do_accept().await { + Ok(ret) => return Ok(ret), Err(e) => { - tracing::error!("[server] accept connection failed: {:?}", e); - tokio::time::sleep(Duration::from_millis(100)).await; + tracing::warn!(?e, "accept fail"); + tokio::time::sleep(Duration::from_millis(1)).await; } } - }; - let remote_addr = conn.remote_address(); - let (w, r) = conn.accept_bi().await.with_context(|| "accept_bi failed")?; - - let arc_conn = Arc::new(ConnWrapper { conn }); - - let info = TunnelInfo { - tunnel_type: "quic".to_owned(), - local_addr: Some(self.local_url().into()), - remote_addr: Some( - super::build_url_from_socket_addr(&remote_addr.to_string(), "quic").into(), - ), - }; - - Ok(Box::new(TunnelWrapper::new( - FramedReader::new_with_associate_data(r, 2000, Some(Box::new(arc_conn.clone()))), - FramedWriter::new_with_associate_data(w, Some(Box::new(arc_conn))), - Some(info), - ))) + } } fn local_url(&self) -> url::Url { diff --git a/easytier/src/tunnel/wireguard.rs b/easytier/src/tunnel/wireguard.rs index 1d720b13d..96705f425 100644 --- a/easytier/src/tunnel/wireguard.rs +++ b/easytier/src/tunnel/wireguard.rs @@ -629,7 +629,10 @@ impl WgTunnelConnector { addr: SocketAddr, ) -> Result, super::TunnelError> { tracing::warn!("wg connect: {:?}", addr); - let local_addr = udp.local_addr().unwrap().to_string(); + let local_addr = udp + .local_addr() + .with_context(|| "Failed to get local addr")? + .to_string(); let mut wg_peer = WgPeer::new(Arc::new(udp), config.clone(), addr); let udp = wg_peer.udp_socket(); diff --git a/easytier/src/utils.rs b/easytier/src/utils.rs index a85ace31e..4e34f2c18 100644 --- a/easytier/src/utils.rs +++ b/easytier/src/utils.rs @@ -2,11 +2,15 @@ use std::{fs::OpenOptions, str::FromStr}; use anyhow::Context; use tracing::level_filters::LevelFilter; -use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, EnvFilter, Layer}; +use tracing_subscriber::{ + layer::SubscriberExt, util::SubscriberInitExt, EnvFilter, Layer, Registry, +}; -use crate::common::{config::LoggingConfigLoader, get_logger_timer_rfc3339}; +use crate::common::{ + config::LoggingConfigLoader, get_logger_timer_rfc3339, tracing_rolling_appender::*, +}; -pub type PeerRoutePair = crate::proto::cli::PeerRoutePair; +pub type PeerRoutePair = crate::proto::api::instance::PeerRoutePair; pub fn cost_to_str(cost: i32) -> String { if cost == 1 { @@ -27,6 +31,8 @@ pub fn init_logger( need_reload: bool, ) -> Result, anyhow::Error> { return Ok(None); + use crate::rpc_service::logger::{CURRENT_LOG_LEVEL, LOGGER_LEVEL_SENDER}; + let file_config = config.get_file_logger_config(); let file_level = file_config .level @@ -49,7 +55,12 @@ pub fn init_logger( if need_reload { let (sender, recver) = std::sync::mpsc::channel(); - ret_sender = Some(sender); + ret_sender = Some(sender.clone()); + + // 初始化全局状态 + let _ = LOGGER_LEVEL_SENDER.set(std::sync::Mutex::new(sender)); + let _ = CURRENT_LOG_LEVEL.set(std::sync::Mutex::new(file_level.to_string())); + std::thread::spawn(move || { println!("Start log filter reloader"); while let Ok(lf) = recver.recv() { @@ -71,15 +82,25 @@ pub fn init_logger( }); } - let file_appender = tracing_appender::rolling::Builder::new() - .rotation(tracing_appender::rolling::Rotation::DAILY) - .max_log_files(5) - .filename_prefix(file_config.file.unwrap_or("easytier".to_string())) - .filename_suffix("log") - .build(file_config.dir.unwrap_or("./".to_string())) - .with_context(|| "failed to initialize rolling file appender")?; + let dir = file_config.dir.as_deref().unwrap_or("."); + let file = file_config.file.as_deref().unwrap_or("easytier.log"); + let path = std::path::Path::new(dir).join(file); + let path_str = path.to_string_lossy().into_owned(); + + let builder = RollingFileAppenderBase::builder(); + let file_appender = builder + .filename(path_str) + .condition_daily() + .max_filecount(file_config.count.unwrap_or(10)) + .condition_max_file_size(file_config.size_mb.unwrap_or(100) * 1024 * 1024) + .build() + .unwrap(); + + let wrapper = FileAppenderWrapper::new(file_appender); + + // Create a simple wrapper that implements MakeWriter file_layer = Some( - l.with_writer(file_appender) + l.with_writer(wrapper) .with_timer(get_logger_timer_rfc3339()) .with_filter(file_filter), ); @@ -103,10 +124,22 @@ pub fn init_logger( .with_writer(std::io::stderr) .with_filter(console_filter); - tracing_subscriber::Registry::default() - .with(console_layer) - .with(file_layer) - .init(); + let registry = Registry::default(); + + #[cfg(not(feature = "tracing"))] + { + registry.with(console_layer).with(file_layer).init(); + } + + #[cfg(feature = "tracing")] + { + let console_subscriber_layer = console_subscriber::ConsoleLayer::builder().spawn(); + registry + .with(console_layer) + .with(file_layer) + .with(console_subscriber_layer) + .init(); + } Ok(ret_sender) } @@ -118,7 +151,7 @@ pub fn utf8_or_gbk_to_string(s: &[u8]) -> String { utf8_str } else { // 如果解码失败,则尝试使用GBK解码 - if let Ok(gbk_str) = GBK.decode(&s, DecoderTrap::Strict) { + if let Ok(gbk_str) = GBK.decode(s, DecoderTrap::Strict) { gbk_str } else { String::from_utf8_lossy(s).to_string() @@ -222,6 +255,11 @@ pub fn find_free_tcp_port(mut range: std::ops::Range) -> Option { range.find(|&port| check_tcp_available(port)) } +pub fn weak_upgrade(weak: &std::sync::Weak) -> anyhow::Result> { + weak.upgrade() + .ok_or_else(|| anyhow::anyhow!("{} not available", std::any::type_name::())) +} + #[cfg(test)] mod tests { use crate::common::config::{self}; diff --git a/easytier/src/vpn_portal/wireguard.rs b/easytier/src/vpn_portal/wireguard.rs index f4d94d7d8..a87108911 100644 --- a/easytier/src/vpn_portal/wireguard.rs +++ b/easytier/src/vpn_portal/wireguard.rs @@ -1,5 +1,5 @@ use std::{ - net::{IpAddr, Ipv4Addr, SocketAddr}, + net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV6}, sync::Arc, }; @@ -49,7 +49,7 @@ struct WireGuardImpl { global_ctx: ArcGlobalCtx, peer_mgr: Arc, wg_config: WgConfig, - listenr_addr: SocketAddr, + listener_addr: SocketAddr, wg_peer_ip_table: WgPeerIpTable, @@ -62,13 +62,13 @@ impl WireGuardImpl { let wg_config = get_wg_config_for_portal(&nid); let vpn_cfg = global_ctx.config.get_vpn_portal_config().unwrap(); - let listenr_addr = vpn_cfg.wireguard_listen; + let listener_addr = vpn_cfg.wireguard_listen; Self { global_ctx, peer_mgr, wg_config, - listenr_addr, + listener_addr, wg_peer_ip_table: Arc::new(DashMap::new()), tasks: Arc::new(std::sync::Mutex::new(JoinSet::new())), } @@ -209,12 +209,11 @@ impl WireGuardImpl { .await; } - #[tracing::instrument(skip(self), err(level = Level::WARN))] - async fn start(&self) -> anyhow::Result<()> { - let mut l = WgTunnelListener::new( - format!("wg://{}", self.listenr_addr).parse().unwrap(), - self.wg_config.clone(), - ); + async fn start_listener(&self, listener_addr: &SocketAddr) -> anyhow::Result<()> { + let mut listener_url = url::Url::parse("wg://0.0.0.0:0").unwrap(); + listener_url.set_port(Some(listener_addr.port())).unwrap(); + listener_url.set_ip_host(listener_addr.ip()).unwrap(); + let mut l = WgTunnelListener::new(listener_url.clone(), self.wg_config.clone()); tracing::info!("Wireguard VPN Portal Starting"); @@ -224,9 +223,6 @@ impl WireGuardImpl { .await .with_context(|| "Failed to start wireguard listener for vpn portal")?; } - - join_joinset_background(self.tasks.clone(), "wireguard".to_string()); - let tasks = Arc::downgrade(&self.tasks.clone()); let peer_mgr = self.peer_mgr.clone(); let wg_peer_ip_table = self.wg_peer_ip_table.clone(); @@ -243,6 +239,32 @@ impl WireGuardImpl { } }); + self.global_ctx + .issue_event(GlobalCtxEvent::VpnPortalStarted(listener_url.to_string())); + + Ok(()) + } + + #[tracing::instrument(skip(self), err(level = Level::WARN))] + async fn start(&self) -> anyhow::Result<()> { + tracing::info!("Wireguard VPN Portal Starting"); + + self.start_listener(&self.listener_addr).await?; + // if binding to v4 unspecified, also start a listener on v6 unspecified + if let SocketAddr::V4(v4) = &self.listener_addr { + if v4.ip().is_unspecified() { + let _ = self + .start_listener(&SocketAddr::V6(SocketAddrV6::new( + Ipv6Addr::UNSPECIFIED, + v4.port(), + 0, + 0, + ))) + .await; + } + }; + + join_joinset_background(self.tasks.clone(), "wireguard".to_string()); self.start_pipeline_processor().await; Ok(()) @@ -324,7 +346,7 @@ PersistentKeepalive = 25 "#, peer_secret_key = BASE64_STANDARD.encode(cfg.peer_secret_key()), my_public_key = BASE64_STANDARD.encode(cfg.my_public_key()), - listenr_addr = self.inner.as_ref().unwrap().listenr_addr, + listenr_addr = self.inner.as_ref().unwrap().listener_addr, allow_ips = allow_ips, address = client_cidr.first_address().to_string() + "/32", ); diff --git a/easytier/src/web_client/controller.rs b/easytier/src/web_client/controller.rs index 01f632703..d681e1340 100644 --- a/easytier/src/web_client/controller.rs +++ b/easytier/src/web_client/controller.rs @@ -1,31 +1,22 @@ +use std::sync::Arc; + use crate::{ - common::config::ConfigLoader, instance_manager::NetworkInstanceManager, - launcher::ConfigSource, - proto::{ - rpc_types::{self, controller::BaseController}, - web::{ - CollectNetworkInfoRequest, CollectNetworkInfoResponse, DeleteNetworkInstanceRequest, - DeleteNetworkInstanceResponse, ListNetworkInstanceRequest, ListNetworkInstanceResponse, - NetworkInstanceRunningInfoMap, RetainNetworkInstanceRequest, - RetainNetworkInstanceResponse, RunNetworkInstanceRequest, RunNetworkInstanceResponse, - ValidateConfigRequest, ValidateConfigResponse, WebClientService, - }, - } + rpc_service::instance_manage::InstanceManageRpcService, }; pub struct Controller { token: String, hostname: String, - manager: NetworkInstanceManager, + manager: Arc, } impl Controller { - pub fn new(token: String, hostname: String) -> Self { + pub fn new(token: String, hostname: String, manager: Arc) -> Self { Controller { token, hostname, - manager: NetworkInstanceManager::new(), + manager, } } @@ -40,117 +31,8 @@ impl Controller { pub fn hostname(&self) -> String { self.hostname.clone() } -} - -#[async_trait::async_trait] -impl WebClientService for Controller { - type Controller = BaseController; - - async fn validate_config( - &self, - _: BaseController, - req: ValidateConfigRequest, - ) -> Result { - let toml_config = req.config.unwrap_or_default().gen_config()?.dump(); - Ok(ValidateConfigResponse { toml_config }) - } - - async fn run_network_instance( - &self, - _: BaseController, - req: RunNetworkInstanceRequest, - ) -> Result { - if req.config.is_none() { - return Err(anyhow::anyhow!("config is required").into()); - } - let cfg = req.config.unwrap().gen_config()?; - let id = cfg.get_id(); - if let Some(inst_id) = req.inst_id { - cfg.set_id(inst_id.into()); - } - self.manager.run_network_instance(cfg, ConfigSource::Web)?; - println!("instance {} started", id); - Ok(RunNetworkInstanceResponse { - inst_id: Some(id.into()), - }) - } - - async fn retain_network_instance( - &self, - _: BaseController, - req: RetainNetworkInstanceRequest, - ) -> Result { - let remain = self - .manager - .retain_network_instance(req.inst_ids.into_iter().map(Into::into).collect())?; - println!("instance {:?} retained", remain); - Ok(RetainNetworkInstanceResponse { - remain_inst_ids: remain.iter().map(|item| (*item).into()).collect(), - }) - } - - async fn collect_network_info( - &self, - _: BaseController, - req: CollectNetworkInfoRequest, - ) -> Result { - let mut ret = NetworkInstanceRunningInfoMap { - map: self - .manager - .collect_network_infos()? - .into_iter() - .map(|(k, v)| (k.to_string(), v)) - .collect(), - }; - let include_inst_ids = req - .inst_ids - .iter() - .cloned() - .map(|id| id.to_string()) - .collect::>(); - if !include_inst_ids.is_empty() { - let mut to_remove = Vec::new(); - for (k, _) in ret.map.iter() { - if !include_inst_ids.contains(k) { - to_remove.push(k.clone()); - } - } - - for k in to_remove { - ret.map.remove(&k); - } - } - Ok(CollectNetworkInfoResponse { info: Some(ret) }) - } - - // rpc ListNetworkInstance(ListNetworkInstanceRequest) returns (ListNetworkInstanceResponse) {} - async fn list_network_instance( - &self, - _: BaseController, - _: ListNetworkInstanceRequest, - ) -> Result { - Ok(ListNetworkInstanceResponse { - inst_ids: self - .manager - .list_network_instance_ids() - .into_iter() - .map(Into::into) - .collect(), - }) - } - // rpc DeleteNetworkInstance(DeleteNetworkInstanceRequest) returns (DeleteNetworkInstanceResponse) {} - async fn delete_network_instance( - &self, - _: BaseController, - req: DeleteNetworkInstanceRequest, - ) -> Result { - let remain_inst_ids = self - .manager - .delete_network_instance(req.inst_ids.into_iter().map(Into::into).collect())?; - println!("instance {:?} retained", remain_inst_ids); - Ok(DeleteNetworkInstanceResponse { - remain_inst_ids: remain_inst_ids.into_iter().map(Into::into).collect(), - }) + pub fn get_rpc_service(&self) -> InstanceManageRpcService { + InstanceManageRpcService::new(self.manager.clone()) } } diff --git a/easytier/src/web_client/mod.rs b/easytier/src/web_client/mod.rs index beeeaefaf..14748ab15 100644 --- a/easytier/src/web_client/mod.rs +++ b/easytier/src/web_client/mod.rs @@ -1,6 +1,9 @@ use std::sync::Arc; -use crate::{common::scoped_task::ScopedTask, tunnel::TunnelConnector}; +use crate::{ + common::scoped_task::ScopedTask, instance_manager::NetworkInstanceManager, + tunnel::TunnelConnector, +}; pub mod controller; pub mod session; @@ -15,10 +18,12 @@ impl WebClient { connector: T, token: S, hostname: H, + manager: Arc, ) -> Self { let controller = Arc::new(controller::Controller::new( token.to_string(), hostname.to_string(), + manager, )); let controller_clone = controller.clone(); diff --git a/easytier/src/web_client/session.rs b/easytier/src/web_client/session.rs index 72b4b7208..fdec67e8f 100644 --- a/easytier/src/web_client/session.rs +++ b/easytier/src/web_client/session.rs @@ -9,12 +9,10 @@ use tokio::{ use crate::{ common::{constants::EASYTIER_VERSION, get_machine_id}, proto::{ + api::manage::WebClientServiceServer, rpc_impl::bidirect::BidirectRpcManager, rpc_types::controller::BaseController, - web::{ - HeartbeatRequest, HeartbeatResponse, WebClientServiceServer, - WebServerServiceClientFactory, - }, + web::{HeartbeatRequest, HeartbeatResponse, WebServerServiceClientFactory}, }, tunnel::Tunnel, }; @@ -41,10 +39,10 @@ impl Session { let rpc_mgr = BidirectRpcManager::new(); rpc_mgr.run_with_tunnel(tunnel); - rpc_mgr - .rpc_server() - .registry() - .register(WebClientServiceServer::new(controller.clone()), ""); + rpc_mgr.rpc_server().registry().register( + WebClientServiceServer::new(controller.get_rpc_service()), + "", + ); let mut tasks: JoinSet<()> = JoinSet::new(); let heartbeat_ctx = diff --git a/flake.lock b/flake.lock index a4cb51bb9..955154dfa 100644 --- a/flake.lock +++ b/flake.lock @@ -20,11 +20,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1753429684, - "narHash": "sha256-9h7+4/53cSfQ/uA3pSvCaBepmZaz/dLlLVJnbQ+SJjk=", + "lastModified": 1754725699, + "narHash": "sha256-iAcj9T/Y+3DBy2J0N+yF9XQQQ8IEb5swLFzs23CdP88=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "7fd36ee82c0275fb545775cc5e4d30542899511d", + "rev": "85dbfc7aaf52ecb755f87e577ddbe6dbbdbc1054", "type": "github" }, "original": { @@ -48,11 +48,11 @@ ] }, "locked": { - "lastModified": 1753671061, - "narHash": "sha256-IU4eBWfe9h2QejJYST+EAlhg8a1H6mh9gbcmWgZ2/mQ=", + "lastModified": 1754966322, + "narHash": "sha256-7f/LH60DnjjQVKbXAsHIniGaU7ixVM7eWU3hyjT24YI=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "40065d17ee4dbec3ded8ca61236132aede843fab", + "rev": "7c13cec2e3828d964b9980d0ffd680bd8d4dce90", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index b4e2009fc..0f5f49c89 100644 --- a/flake.nix +++ b/flake.nix @@ -10,47 +10,101 @@ }; }; - outputs = { self, nixpkgs, flake-utils, rust-overlay, ... }: - flake-utils.lib.eachDefaultSystem (system: + outputs = + { + self, + nixpkgs, + flake-utils, + rust-overlay, + ... + }: + flake-utils.lib.eachDefaultSystem ( + system: let overlays = [ (import rust-overlay) ]; pkgs = import nixpkgs { inherit system overlays; + config = { + licenseAccepted = true; + allowUnfree = true; + }; }; rustVersion = "1.89.0"; - rust = pkgs.rust-bin.stable.${rustVersion}.default.override{ - extensions = [ "rust-src" "rust-analyzer" ]; + makeRust = + features: + pkgs.rust-bin.stable.${rustVersion}.default.override { + extensions = [ + "rust-src" + "rust-analyzer" + ] + ++ (if builtins.elem "android" features then android.rust.extensions else [ ]); + + targets = if builtins.elem "android" features then android.rust.targets else [ ]; + }; + + android = import ./android.nix { + inherit pkgs system nixpkgs; }; + + makeShell = + features: + let + hasFeature = feature: builtins.elem feature features; + withFeature = feature: pkgList: if hasFeature feature then pkgList else [ ]; + flattenPaths = xs: builtins.concatLists (map (p: if builtins.isList p then p else [ p ]) xs); + rust = makeRust features; + in + pkgs.mkShell (rec { + nativeBuildInputs = + with pkgs; + ( + [ + rust + protobuf + clang + pkg-config + bridge-utils # for three node test + ] + ++ (withFeature "web" [ + nodejs_22 + pnpm + ]) + ++ (withFeature "android" android.packages) + ); + + buildInputs = with pkgs; ([ + zstd + openssl + libclang + llvmPackages.libclang + libsoup_3 + webkitgtk_4_1 + ]); + + RUST_SRC_PATH = "${rust}/lib/rustlib/src/rust/library"; + LIBCLANG_PATH = "${pkgs.libclang.lib}/lib"; + BINDGEN_EXTRA_CLANG_ARGS = "-I${pkgs.clang}/resource-root/include"; + LD_LIBRARY_PATH = pkgs.lib.makeLibraryPath (flattenPaths (buildInputs ++ nativeBuildInputs)); + ZSTD_SYS_USE_PKG_CONFIG = true; + KCP_SYS_EXTRA_HEADER_PATH = "${pkgs.libclang.lib}/lib/clang/19/include:${pkgs.glibc.dev}/include"; + } + // (if hasFeature "android" then android.envVars else { })); in { - devShells.default = pkgs.mkShell rec { - nativeBuildInputs = with pkgs; [ - rust - protobuf - clang - pkg-config - - # web - nodejs_22 - pnpm + devShells = { + default = makeShell [ ]; + core = makeShell [ ]; + web = makeShell [ "web" ]; + gui = makeShell [ "gui" ]; + android = makeShell [ + "android" + "web" ]; - buildInputs = with pkgs; [ - zstd - openssl - libclang - llvmPackages.libclang - - # gui - webkitgtk_4_1 - libsoup_3 + full = makeShell [ + "web" + "gui" + "android" ]; - - RUST_SRC_PATH = "${rust}/lib/rustlib/src/rust/library"; - LIBCLANG_PATH = "${pkgs.libclang.lib}/lib"; - BINDGEN_EXTRA_CLANG_ARGS = "-I${pkgs.clang}/resource-root/include"; - LD_LIBRARY_PATH = pkgs.lib.makeLibraryPath (buildInputs ++ nativeBuildInputs); - ZSTD_SYS_USE_PKG_CONFIG = true; - KCP_SYS_EXTRA_HEADER_PATH = "${pkgs.libclang.lib}/lib/clang/19/include:${pkgs.glibc.dev}/include"; }; } ); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0e29dad55..a3d93b729 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,9 +8,9 @@ importers: easytier-gui: dependencies: - '@primevue/themes': - specifier: 4.3.3 - version: 4.3.3 + '@primeuix/themes': + specifier: ^1.2.3 + version: 1.2.3 '@tauri-apps/plugin-autostart': specifier: 2.0.0 version: 2.0.0 @@ -28,10 +28,7 @@ importers: version: 2.3.0 '@vueuse/core': specifier: ^11.2.0 - version: 11.2.0(vue@3.5.12(typescript@5.6.3)) - aura: - specifier: link:@primevue\themes\aura - version: link:@primevue/themes/aura + version: 11.3.0(vue@3.5.21(typescript@5.6.3)) easytier-frontend-lib: specifier: workspace:* version: link:../easytier-web/frontend-lib @@ -40,29 +37,29 @@ importers: version: 1.5.1 pinia: specifier: ^2.2.4 - version: 2.2.6(typescript@5.6.3)(vue@3.5.12(typescript@5.6.3)) + version: 2.3.1(typescript@5.6.3)(vue@3.5.21(typescript@5.6.3)) primevue: - specifier: 4.3.3 - version: 4.3.3(vue@3.5.12(typescript@5.6.3)) + specifier: ^4.3.9 + version: 4.3.9(vue@3.5.21(typescript@5.6.3)) tauri-plugin-vpnservice-api: specifier: workspace:* version: link:../tauri-plugin-vpnservice vue: specifier: ^3.5.12 - version: 3.5.12(typescript@5.6.3) + version: 3.5.21(typescript@5.6.3) vue-router: specifier: ^4.4.5 - version: 4.4.5(vue@3.5.12(typescript@5.6.3)) + version: 4.5.1(vue@3.5.21(typescript@5.6.3)) devDependencies: '@antfu/eslint-config': specifier: ^3.7.3 - version: 3.8.0(@typescript-eslint/utils@8.13.0(eslint@9.14.0(jiti@2.4.0))(typescript@5.6.3))(@vue/compiler-sfc@3.5.12)(eslint-plugin-format@0.1.2(eslint@9.14.0(jiti@2.4.0)))(eslint@9.14.0(jiti@2.4.0))(typescript@5.6.3) + version: 3.16.0(@typescript-eslint/utils@8.42.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.6.3))(@vue/compiler-sfc@3.5.21)(eslint-plugin-format@0.1.3(eslint@9.35.0(jiti@2.5.1)))(eslint@9.35.0(jiti@2.5.1))(typescript@5.6.3) '@intlify/unplugin-vue-i18n': specifier: ^5.2.0 - version: 5.2.0(@vue/compiler-dom@3.5.12)(eslint@9.14.0(jiti@2.4.0))(rollup@4.24.3)(typescript@5.6.3)(vue-i18n@10.0.4(vue@3.5.12(typescript@5.6.3)))(vue@3.5.12(typescript@5.6.3)) + version: 5.3.1(@vue/compiler-dom@3.5.21)(eslint@9.35.0(jiti@2.5.1))(rollup@4.50.1)(typescript@5.6.3)(vue-i18n@10.0.8(vue@3.5.21(typescript@5.6.3)))(vue@3.5.21(typescript@5.6.3)) '@primevue/auto-import-resolver': - specifier: 4.3.3 - version: 4.3.3 + specifier: 4.3.9 + version: 4.3.9 '@tauri-apps/api': specifier: 2.7.0 version: 2.7.0 @@ -74,34 +71,34 @@ importers: version: 7.2.2 '@types/node': specifier: ^22.7.4 - version: 22.8.6 + version: 22.18.1 '@types/uuid': specifier: ^10.0.0 version: 10.0.0 '@vitejs/plugin-vue': specifier: ^5.1.4 - version: 5.1.4(vite@5.4.10(@types/node@22.8.6))(vue@3.5.12(typescript@5.6.3)) + version: 5.2.4(vite@5.4.19(@types/node@22.18.1))(vue@3.5.21(typescript@5.6.3)) '@vue-macros/volar': specifier: 0.30.5 - version: 0.30.5(rollup@4.24.3)(typescript@5.6.3)(vue-tsc@2.1.10(typescript@5.6.3))(vue@3.5.12(typescript@5.6.3)) + version: 0.30.5(rollup@4.50.1)(typescript@5.6.3)(vue-tsc@2.2.12(typescript@5.6.3))(vue@3.5.21(typescript@5.6.3)) autoprefixer: specifier: ^10.4.20 - version: 10.4.20(postcss@8.4.47) + version: 10.4.21(postcss@8.5.6) cidr-tools: specifier: ^11.0.2 - version: 11.0.2 + version: 11.0.3 default-gateway: specifier: ^7.2.2 version: 7.2.2 eslint: specifier: ^9.12.0 - version: 9.14.0(jiti@2.4.0) + version: 9.35.0(jiti@2.5.1) eslint-plugin-format: specifier: ^0.1.2 - version: 0.1.2(eslint@9.14.0(jiti@2.4.0)) + version: 0.1.3(eslint@9.35.0(jiti@2.5.1)) postcss: specifier: ^8.4.47 - version: 8.4.47 + version: 8.5.6 tailwindcss: specifier: '=3.4.17' version: 3.4.17 @@ -110,83 +107,83 @@ importers: version: 5.6.3 unplugin-auto-import: specifier: ^0.18.3 - version: 0.18.3(@vueuse/core@11.2.0(vue@3.5.12(typescript@5.6.3)))(rollup@4.24.3) + version: 0.18.6(@vueuse/core@11.3.0(vue@3.5.21(typescript@5.6.3)))(rollup@4.50.1) unplugin-vue-components: specifier: ^0.27.4 - version: 0.27.4(@babel/parser@7.26.2)(rollup@4.24.3)(vue@3.5.12(typescript@5.6.3)) + version: 0.27.5(@babel/parser@7.28.4)(rollup@4.50.1)(vue@3.5.21(typescript@5.6.3)) unplugin-vue-macros: specifier: ^2.13.3 - version: 2.13.3(@vueuse/core@11.2.0(vue@3.5.12(typescript@5.6.3)))(esbuild@0.23.1)(rollup@4.24.3)(typescript@5.6.3)(vite@5.4.10(@types/node@22.8.6))(vue-tsc@2.1.10(typescript@5.6.3))(vue@3.5.12(typescript@5.6.3)) + version: 2.14.5(@vueuse/core@11.3.0(vue@3.5.21(typescript@5.6.3)))(esbuild@0.25.9)(rollup@4.50.1)(typescript@5.6.3)(vite@5.4.19(@types/node@22.18.1))(vue-tsc@2.2.12(typescript@5.6.3))(vue@3.5.21(typescript@5.6.3)) unplugin-vue-markdown: specifier: ^0.26.2 - version: 0.26.2(rollup@4.24.3)(vite@5.4.10(@types/node@22.8.6)) + version: 0.26.3(rollup@4.50.1)(vite@5.4.19(@types/node@22.18.1)) unplugin-vue-router: specifier: ^0.10.8 - version: 0.10.8(rollup@4.24.3)(vue-router@4.4.5(vue@3.5.12(typescript@5.6.3)))(vue@3.5.12(typescript@5.6.3)) + version: 0.10.9(rollup@4.50.1)(vue-router@4.5.1(vue@3.5.21(typescript@5.6.3)))(vue@3.5.21(typescript@5.6.3)) uuid: specifier: ^10.0.0 version: 10.0.0 vite: specifier: ^5.4.8 - version: 5.4.10(@types/node@22.8.6) + version: 5.4.19(@types/node@22.18.1) vite-plugin-vue-devtools: specifier: ^7.4.6 - version: 7.6.3(rollup@4.24.3)(vite@5.4.10(@types/node@22.8.6))(vue@3.5.12(typescript@5.6.3)) + version: 7.7.7(rollup@4.50.1)(vite@5.4.19(@types/node@22.18.1))(vue@3.5.21(typescript@5.6.3)) vite-plugin-vue-layouts: specifier: ^0.11.0 - version: 0.11.0(vite@5.4.10(@types/node@22.8.6))(vue-router@4.4.5(vue@3.5.12(typescript@5.6.3)))(vue@3.5.12(typescript@5.6.3)) + version: 0.11.0(vite@5.4.19(@types/node@22.18.1))(vue-router@4.5.1(vue@3.5.21(typescript@5.6.3)))(vue@3.5.21(typescript@5.6.3)) vue-i18n: specifier: ^10.0.0 - version: 10.0.4(vue@3.5.12(typescript@5.6.3)) + version: 10.0.8(vue@3.5.21(typescript@5.6.3)) vue-tsc: specifier: ^2.1.10 - version: 2.1.10(typescript@5.6.3) + version: 2.2.12(typescript@5.6.3) easytier-web/frontend: dependencies: '@modyfi/vite-plugin-yaml': specifier: ^1.1.0 - version: 1.1.0(rollup@4.24.3)(vite@5.4.10(@types/node@22.8.6)) - '@primevue/themes': - specifier: 4.3.3 - version: 4.3.3 - aura: - specifier: link:@primevue/themes/aura - version: link:@primevue/themes/aura + version: 1.1.1(rollup@4.50.1)(vite@5.4.19(@types/node@22.18.1)) + '@primeuix/themes': + specifier: ^1.2.3 + version: 1.2.3 axios: specifier: ^1.7.7 - version: 1.7.7 + version: 1.11.0 easytier-frontend-lib: specifier: workspace:* version: link:../frontend-lib primevue: - specifier: 4.3.3 - version: 4.3.3(vue@3.5.12(typescript@5.6.3)) + specifier: ^4.3.9 + version: 4.3.9(vue@3.5.21(typescript@5.6.3)) tailwindcss-primeui: specifier: ^0.3.4 version: 0.3.4(tailwindcss@3.4.17) vue: specifier: ^3.5.12 - version: 3.5.12(typescript@5.6.3) + version: 3.5.21(typescript@5.6.3) vue-i18n: specifier: ^9.9.1 - version: 9.14.4(vue@3.5.12(typescript@5.6.3)) + version: 9.14.5(vue@3.5.21(typescript@5.6.3)) vue-router: specifier: '4' - version: 4.4.5(vue@3.5.12(typescript@5.6.3)) + version: 4.5.1(vue@3.5.21(typescript@5.6.3)) devDependencies: + '@primevue/auto-import-resolver': + specifier: 4.3.9 + version: 4.3.9 '@types/node': specifier: ^22.8.6 - version: 22.8.6 + version: 22.18.1 '@vitejs/plugin-vue': specifier: ^5.1.4 - version: 5.1.4(vite@5.4.10(@types/node@22.8.6))(vue@3.5.12(typescript@5.6.3)) + version: 5.2.4(vite@5.4.19(@types/node@22.18.1))(vue@3.5.21(typescript@5.6.3)) autoprefixer: specifier: ^10.4.20 - version: 10.4.20(postcss@8.4.47) + version: 10.4.21(postcss@8.5.6) postcss: specifier: ^8.4.47 - version: 8.4.47 + version: 8.5.6 tailwindcss: specifier: '=3.4.17' version: 3.4.17 @@ -195,31 +192,31 @@ importers: version: 5.6.3 vite: specifier: ^5.4.10 - version: 5.4.10(@types/node@22.8.6) + version: 5.4.19(@types/node@22.18.1) vite-plugin-singlefile: specifier: ^2.0.3 - version: 2.0.3(rollup@4.24.3)(vite@5.4.10(@types/node@22.8.6)) + version: 2.3.0(rollup@4.50.1)(vite@5.4.19(@types/node@22.18.1)) vue-tsc: specifier: ^2.1.10 - version: 2.1.10(typescript@5.6.3) + version: 2.2.12(typescript@5.6.3) easytier-web/frontend-lib: dependencies: - '@primevue/themes': - specifier: 4.3.3 - version: 4.3.3 + '@primeuix/themes': + specifier: ^1.2.3 + version: 1.2.3 '@vueuse/core': specifier: ^11.1.0 - version: 11.2.0(vue@3.5.12(typescript@5.6.3)) - aura: - specifier: link:@primevue\themes\aura - version: link:@primevue/themes/aura + version: 11.3.0(vue@3.5.21(typescript@5.6.3)) axios: specifier: ^1.7.7 - version: 1.7.7 + version: 1.11.0 + chart.js: + specifier: ^4.5.0 + version: 4.5.0 floating-vue: specifier: ^5.2 - version: 5.2.2(vue@3.5.12(typescript@5.6.3)) + version: 5.2.2(vue@3.5.21(typescript@5.6.3)) ip-num: specifier: 1.5.1 version: 1.5.1 @@ -227,8 +224,8 @@ importers: specifier: ^7.0.0 version: 7.0.0 primevue: - specifier: 4.3.3 - version: 4.3.3(vue@3.5.12(typescript@5.6.3)) + specifier: ^4.3.9 + version: 4.3.9(vue@3.5.21(typescript@5.6.3)) tailwindcss-primeui: specifier: ^0.3.4 version: 0.3.4(tailwindcss@3.4.17) @@ -237,35 +234,38 @@ importers: version: 1.3.1 uuid: specifier: ^11.0.2 - version: 11.0.2 + version: 11.1.0 vue: specifier: ^3.5.12 - version: 3.5.12(typescript@5.6.3) + version: 3.5.21(typescript@5.6.3) + vue-chartjs: + specifier: ^5.3.2 + version: 5.3.2(chart.js@4.5.0)(vue@3.5.21(typescript@5.6.3)) vue-i18n: specifier: ^10.0.4 - version: 10.0.4(vue@3.5.12(typescript@5.6.3)) + version: 10.0.8(vue@3.5.21(typescript@5.6.3)) devDependencies: '@modyfi/vite-plugin-yaml': specifier: ^1.1.0 - version: 1.1.0(rollup@4.24.3)(vite@5.4.10(@types/node@22.8.6)) + version: 1.1.1(rollup@4.50.1)(vite@5.4.19(@types/node@22.18.1)) '@types/node': specifier: ^22.8.6 - version: 22.8.6 + version: 22.18.1 '@vitejs/plugin-vue': specifier: ^5.1.4 - version: 5.1.4(vite@5.4.10(@types/node@22.8.6))(vue@3.5.12(typescript@5.6.3)) + version: 5.2.4(vite@5.4.19(@types/node@22.18.1))(vue@3.5.21(typescript@5.6.3)) autoprefixer: specifier: ^10.4.20 - version: 10.4.20(postcss@8.4.47) + version: 10.4.21(postcss@8.5.6) postcss: specifier: ^8.4.47 - version: 8.4.47 + version: 8.5.6 postcss-import: specifier: ^16.1.0 - version: 16.1.0(postcss@8.4.47) + version: 16.1.1(postcss@8.5.6) postcss-nested: specifier: ^7.0.2 - version: 7.0.2(postcss@8.4.47) + version: 7.0.2(postcss@8.5.6) tailwindcss: specifier: '=3.4.17' version: 3.4.17 @@ -274,13 +274,13 @@ importers: version: 5.6.3 vite: specifier: ^5.4.10 - version: 5.4.10(@types/node@22.8.6) + version: 5.4.19(@types/node@22.18.1) vite-plugin-dts: specifier: ^4.3.0 - version: 4.3.0(@types/node@22.8.6)(rollup@4.24.3)(typescript@5.6.3)(vite@5.4.10(@types/node@22.8.6)) + version: 4.5.4(@types/node@22.18.1)(rollup@4.50.1)(typescript@5.6.3)(vite@5.4.19(@types/node@22.18.1)) vue-tsc: specifier: ^2.1.10 - version: 2.1.10(typescript@5.6.3) + version: 2.2.12(typescript@5.6.3) tauri-plugin-vpnservice: dependencies: @@ -290,10 +290,10 @@ importers: devDependencies: '@rollup/plugin-typescript': specifier: ^11.1.6 - version: 11.1.6(rollup@4.24.3)(tslib@2.8.1)(typescript@5.6.3) + version: 11.1.6(rollup@4.50.1)(tslib@2.8.1)(typescript@5.6.3) rollup: specifier: ^4.20.0 - version: 4.24.3 + version: 4.50.1 tslib: specifier: ^2.6.3 version: 2.8.1 @@ -307,15 +307,11 @@ packages: resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} engines: {node: '>=10'} - '@ampproject/remapping@2.3.0': - resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} - engines: {node: '>=6.0.0'} - - '@antfu/eslint-config@3.8.0': - resolution: {integrity: sha512-O5QSufPHpKTm0wk1OQ5c2mOZVzCqYV3hIDrt5zt+cOWqiG8YXLPkSOD4fFwjomATtOuUbcLUwkcgY5dErM7aIw==} + '@antfu/eslint-config@3.16.0': + resolution: {integrity: sha512-g6RAXUMeow9vexoOMYwCpByY2xSDpAD78q+rvQLvVpY6MFcxFD/zmdrZGYa/yt7LizK86m17kIYKOGLJ3L8P0w==} hasBin: true peerDependencies: - '@eslint-react/eslint-plugin': ^1.5.8 + '@eslint-react/eslint-plugin': ^1.19.0 '@prettier/plugin-xml': ^3.4.1 '@unocss/eslint-plugin': '>=0.50.0' astro-eslint-parser: ^1.0.2 @@ -326,7 +322,7 @@ packages: eslint-plugin-react-refresh: ^0.4.4 eslint-plugin-solid: ^0.14.3 eslint-plugin-svelte: '>=2.35.1' - prettier-plugin-astro: ^0.13.0 + prettier-plugin-astro: ^0.14.0 prettier-plugin-slidev: ^1.0.5 svelte-eslint-parser: '>=0.37.0' peerDependenciesMeta: @@ -357,109 +353,116 @@ packages: svelte-eslint-parser: optional: true - '@antfu/install-pkg@0.4.1': - resolution: {integrity: sha512-T7yB5QNG29afhWVkVq7XeIMBa5U/vs9mX69YqayXypPRmYzUmzwnYltplHmPtZ4HPCn+sQKeXW8I47wCbuBOjw==} + '@antfu/install-pkg@1.1.0': + resolution: {integrity: sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ==} '@antfu/utils@0.7.10': resolution: {integrity: sha512-+562v9k4aI80m1+VuMHehNJWLOFjBnXn3tdOitzD0il5b7smkSBal4+a3oKiQTbrwMmN/TBUMDvbdoWDehgOww==} - '@babel/code-frame@7.26.2': - resolution: {integrity: sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==} + '@antfu/utils@8.1.1': + resolution: {integrity: sha512-Mex9nXf9vR6AhcXmMrlz/HVgYYZpVGJ6YlPgwl7UnaFpnshXs6EK/oa5Gpf3CzENMjkvEx2tQtntGnb7UtSTOQ==} + + '@babel/code-frame@7.27.1': + resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} engines: {node: '>=6.9.0'} - '@babel/compat-data@7.26.2': - resolution: {integrity: sha512-Z0WgzSEa+aUcdiJuCIqgujCshpMWgUpgOxXotrYPSA53hA3qopNaqcJpyr0hVb1FeWdnqFA35/fUtXgBK8srQg==} + '@babel/compat-data@7.28.4': + resolution: {integrity: sha512-YsmSKC29MJwf0gF8Rjjrg5LQCmyh+j/nD8/eP7f+BeoQTKYqs9RoWbjGOdy0+1Ekr68RJZMUOPVQaQisnIo4Rw==} engines: {node: '>=6.9.0'} - '@babel/core@7.26.0': - resolution: {integrity: sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==} + '@babel/core@7.28.4': + resolution: {integrity: sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==} engines: {node: '>=6.9.0'} - '@babel/generator@7.26.2': - resolution: {integrity: sha512-zevQbhbau95nkoxSq3f/DC/SC+EEOUZd3DYqfSkMhY2/wfSeaHV1Ew4vk8e+x8lja31IbyuUa2uQ3JONqKbysw==} + '@babel/generator@7.28.3': + resolution: {integrity: sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==} engines: {node: '>=6.9.0'} - '@babel/helper-annotate-as-pure@7.25.9': - resolution: {integrity: sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g==} + '@babel/helper-annotate-as-pure@7.27.3': + resolution: {integrity: sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==} engines: {node: '>=6.9.0'} - '@babel/helper-compilation-targets@7.25.9': - resolution: {integrity: sha512-j9Db8Suy6yV/VHa4qzrj9yZfZxhLWQdVnRlXxmKLYlhWUVB1sB2G5sxuWYXk/whHD9iW76PmNzxZ4UCnTQTVEQ==} + '@babel/helper-compilation-targets@7.27.2': + resolution: {integrity: sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==} engines: {node: '>=6.9.0'} - '@babel/helper-create-class-features-plugin@7.25.9': - resolution: {integrity: sha512-UTZQMvt0d/rSz6KI+qdu7GQze5TIajwTS++GUozlw8VBJDEOAqSXwm1WvmYEZwqdqSGQshRocPDqrt4HBZB3fQ==} + '@babel/helper-create-class-features-plugin@7.28.3': + resolution: {integrity: sha512-V9f6ZFIYSLNEbuGA/92uOvYsGCJNsuA8ESZ4ldc09bWk/j8H8TKiPw8Mk1eG6olpnO0ALHJmYfZvF4MEE4gajg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 - '@babel/helper-member-expression-to-functions@7.25.9': - resolution: {integrity: sha512-wbfdZ9w5vk0C0oyHqAJbc62+vet5prjj01jjJ8sKn3j9h3MQQlflEdXYvuqRWjHnM12coDEqiC1IRCi0U/EKwQ==} + '@babel/helper-globals@7.28.0': + resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==} engines: {node: '>=6.9.0'} - '@babel/helper-module-imports@7.25.9': - resolution: {integrity: sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==} + '@babel/helper-member-expression-to-functions@7.27.1': + resolution: {integrity: sha512-E5chM8eWjTp/aNoVpcbfM7mLxu9XGLWYise2eBKGQomAk/Mb4XoxyqXTZbuTohbsl8EKqdlMhnDI2CCLfcs9wA==} engines: {node: '>=6.9.0'} - '@babel/helper-module-transforms@7.26.0': - resolution: {integrity: sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==} + '@babel/helper-module-imports@7.27.1': + resolution: {integrity: sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-transforms@7.28.3': + resolution: {integrity: sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 - '@babel/helper-optimise-call-expression@7.25.9': - resolution: {integrity: sha512-FIpuNaz5ow8VyrYcnXQTDRGvV6tTjkNtCK/RYNDXGSLlUD6cBuQTSw43CShGxjvfBTfcUA/r6UhUCbtYqkhcuQ==} + '@babel/helper-optimise-call-expression@7.27.1': + resolution: {integrity: sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==} engines: {node: '>=6.9.0'} - '@babel/helper-plugin-utils@7.25.9': - resolution: {integrity: sha512-kSMlyUVdWe25rEsRGviIgOWnoT/nfABVWlqt9N19/dIPWViAOW2s9wznP5tURbs/IDuNk4gPy3YdYRgH3uxhBw==} + '@babel/helper-plugin-utils@7.27.1': + resolution: {integrity: sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==} engines: {node: '>=6.9.0'} - '@babel/helper-replace-supers@7.25.9': - resolution: {integrity: sha512-IiDqTOTBQy0sWyeXyGSC5TBJpGFXBkRynjBeXsvbhQFKj2viwJC76Epz35YLU1fpe/Am6Vppb7W7zM4fPQzLsQ==} + '@babel/helper-replace-supers@7.27.1': + resolution: {integrity: sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 - '@babel/helper-skip-transparent-expression-wrappers@7.25.9': - resolution: {integrity: sha512-K4Du3BFa3gvyhzgPcntrkDgZzQaq6uozzcpGbOO1OEJaI+EJdqWIMTLgFgQf6lrfiDFo5FU+BxKepI9RmZqahA==} + '@babel/helper-skip-transparent-expression-wrappers@7.27.1': + resolution: {integrity: sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==} engines: {node: '>=6.9.0'} - '@babel/helper-string-parser@7.25.9': - resolution: {integrity: sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==} + '@babel/helper-string-parser@7.27.1': + resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} engines: {node: '>=6.9.0'} - '@babel/helper-validator-identifier@7.25.9': - resolution: {integrity: sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==} + '@babel/helper-validator-identifier@7.27.1': + resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==} engines: {node: '>=6.9.0'} - '@babel/helper-validator-option@7.25.9': - resolution: {integrity: sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==} + '@babel/helper-validator-option@7.27.1': + resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} engines: {node: '>=6.9.0'} - '@babel/helpers@7.26.0': - resolution: {integrity: sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw==} + '@babel/helpers@7.28.4': + resolution: {integrity: sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==} engines: {node: '>=6.9.0'} - '@babel/parser@7.26.2': - resolution: {integrity: sha512-DWMCZH9WA4Maitz2q21SRKHo9QXZxkDsbNZoVD62gusNtNBBqDg9i7uOhASfTfIGNzW+O+r7+jAlM8dwphcJKQ==} + '@babel/parser@7.28.4': + resolution: {integrity: sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==} engines: {node: '>=6.0.0'} hasBin: true - '@babel/plugin-proposal-decorators@7.25.9': - resolution: {integrity: sha512-smkNLL/O1ezy9Nhy4CNosc4Va+1wo5w4gzSZeLe6y6dM4mmHfYOCPolXQPHQxonZCF+ZyebxN9vqOolkYrSn5g==} + '@babel/plugin-proposal-decorators@7.28.0': + resolution: {integrity: sha512-zOiZqvANjWDUaUS9xMxbMcK/Zccztbe/6ikvUXaG9nsPH3w6qh5UaPGAnirI/WhIbZ8m3OHU0ReyPrknG+ZKeg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-syntax-decorators@7.25.9': - resolution: {integrity: sha512-ryzI0McXUPJnRCvMo4lumIKZUzhYUO/ScI+Mz4YVaTLt04DHNSjEUjKVvbzQjZFLuod/cYEc07mJWhzl6v4DPg==} + '@babel/plugin-syntax-decorators@7.27.1': + resolution: {integrity: sha512-YMq8Z87Lhl8EGkmb0MwYkt36QnxC+fzCgrl66ereamPlYToRpIk5nUjKUY3QKLWq8mwUB1BgbeXcTJhZOCDg5A==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-syntax-import-attributes@7.26.0': - resolution: {integrity: sha512-e2dttdsJ1ZTpi3B9UYGLw41hifAubg19AtCu/2I/F1QNVclOBr1dYpTdmdyZ84Xiz43BS/tCUkMAZNLv12Pi+A==} + '@babel/plugin-syntax-import-attributes@7.27.1': + resolution: {integrity: sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -469,43 +472,41 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-syntax-jsx@7.25.9': - resolution: {integrity: sha512-ld6oezHQMZsZfp6pWtbjaNDF2tiiCYYDqQszHt5VV437lewP9aSi2Of99CK0D0XB21k7FLgnLcmQKyKzynfeAA==} + '@babel/plugin-syntax-jsx@7.27.1': + resolution: {integrity: sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-syntax-typescript@7.25.9': - resolution: {integrity: sha512-hjMgRy5hb8uJJjUcdWunWVcoi9bGpJp8p5Ol1229PoN6aytsLwNMgmdftO23wnCLMfVmTwZDWMPNq/D1SY60JQ==} + '@babel/plugin-syntax-typescript@7.27.1': + resolution: {integrity: sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-typescript@7.25.9': - resolution: {integrity: sha512-7PbZQZP50tzv2KGGnhh82GSyMB01yKY9scIjf1a+GfZCtInOWqUH5+1EBU4t9fyR5Oykkkc9vFTs4OHrhHXljQ==} + '@babel/plugin-transform-typescript@7.28.0': + resolution: {integrity: sha512-4AEiDEBPIZvLQaWlc9liCavE0xRM0dNca41WtBeM3jgFptfUOSG9z0uteLhq6+3rq+WB6jIvUwKDTpXEHPJ2Vg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/template@7.25.9': - resolution: {integrity: sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==} + '@babel/template@7.27.2': + resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==} engines: {node: '>=6.9.0'} - '@babel/traverse@7.25.9': - resolution: {integrity: sha512-ZCuvfwOwlz/bawvAuvcj8rrithP2/N55Tzz342AkTvq4qaWbGfmCk/tKhNaV2cthijKrPAA8SRJV5WWe7IBMJw==} + '@babel/traverse@7.28.4': + resolution: {integrity: sha512-YEzuboP2qvQavAcjgQNVgsvHIDv6ZpwXvcvjmyySP2DIMuByS/6ioU5G9pYrWHM6T2YDfc7xga9iNzYOs12CFQ==} engines: {node: '>=6.9.0'} - '@babel/types@7.26.0': - resolution: {integrity: sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==} + '@babel/types@7.28.4': + resolution: {integrity: sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==} engines: {node: '>=6.9.0'} - '@clack/core@0.3.4': - resolution: {integrity: sha512-H4hxZDXgHtWTwV3RAVenqcC4VbJZNegbBjlPvzOzCouXtS2y3sDvlO3IsbrPNWuLWPPlYVYPghQdSF64683Ldw==} + '@clack/core@0.4.1': + resolution: {integrity: sha512-Pxhij4UXg8KSr7rPek6Zowm+5M22rbd2g1nfojHJkxp5YkFqiZ2+YLEM/XGVIzvGOcM0nqjIFxrpDwWRZYWYjA==} - '@clack/prompts@0.7.0': - resolution: {integrity: sha512-0MhX9/B4iL6Re04jPrttDm+BsP8y6mS7byuv0BvXgdXhbV5PdlsHt55dvNsuBCPZ7xq1oTAOOuotR9NFbQyMSA==} - bundledDependencies: - - is-unicode-supported + '@clack/prompts@0.9.1': + resolution: {integrity: sha512-JIpyaboYZeWYlyP0H+OoPPxd6nqueG/CmN6ixBiNFsIDHREevjIf0n0Ohh5gr5C8pEDknzgvz+pIJ8dMhzWIeg==} '@dprint/formatter@0.3.0': resolution: {integrity: sha512-N9fxCxbaBOrDkteSOzaCqwWjso5iAe+WJPsHC021JfHNj2ThInPNEF13ORDKta3llq5D1TlclODCvOvipH7bWQ==} @@ -513,25 +514,21 @@ packages: '@dprint/markdown@0.17.8': resolution: {integrity: sha512-ukHFOg+RpG284aPdIg7iPrCYmMs3Dqy43S1ejybnwlJoFiW02b+6Bbr5cfZKFRYNP3dKGM86BqHEnMzBOyLvvA==} - '@dprint/toml@0.6.3': - resolution: {integrity: sha512-zQ42I53sb4WVHA+5yoY1t59Zk++Ot02AvUgtNKLzTT8mPyVqVChFcePa3on/xIoKEgH+RoepgPHzqfk9837YFw==} + '@dprint/toml@0.6.4': + resolution: {integrity: sha512-bZXIUjxr0LIuHWshZr/5mtUkOrnh0NKVZEF6ACojW5z7zkJu7s9sV2mMXm8XQDqN4cJzdHYUYzUyEGdfciaLJA==} - '@emnapi/core@1.3.1': - resolution: {integrity: sha512-pVGjBIt1Y6gg3EJN8jTcfpP/+uuRksIo055oE/OBkDNcjZqVbfkWCksG1Jp4yZnj3iKWyWX8fdG/j6UDYPbFog==} + '@emnapi/core@1.5.0': + resolution: {integrity: sha512-sbP8GzB1WDzacS8fgNPpHlp6C9VZe+SJP3F90W9rLemaQj2PzIuTEl1qDOYQf58YIpyjViI24y9aPWCjEzY2cg==} - '@emnapi/runtime@1.3.1': - resolution: {integrity: sha512-kEBmG8KyqtxJZv+ygbEim+KCGtIq1fC22Ms3S4ziXmYKm8uyoLX0MHONVKwp+9opg390VaKRNt4a7A9NwmpNhw==} + '@emnapi/runtime@1.5.0': + resolution: {integrity: sha512-97/BJ3iXHww3djw6hYIfErCZFee7qCtrneuLa20UXFCOTCfBM2cvQHjWJ2EG0s0MtdNwInarqCTz35i4wWXHsQ==} - '@emnapi/wasi-threads@1.0.1': - resolution: {integrity: sha512-iIBu7mwkq4UQGeMEM8bLwNK962nXdhodeScX4slfQnRhEMMzvYivHhutCIk8uojvmASXXPC2WNEjwxFWk72Oqw==} - - '@es-joy/jsdoccomment@0.48.0': - resolution: {integrity: sha512-G6QUWIcC+KvSwXNsJyDTHvqUdNoAVJPPgkc3+Uk4WBKqZvoXhlvazOgm9aL0HwihJLQf0l+tOE2UFzXBqCqgDw==} - engines: {node: '>=16'} + '@emnapi/wasi-threads@1.1.0': + resolution: {integrity: sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==} - '@es-joy/jsdoccomment@0.49.0': - resolution: {integrity: sha512-xjZTSFgECpb9Ohuk5yMX5RhUEbfeQcuOp8IF60e+wyzWEF0M5xeSgqsfLtvPEX8BIyOX9saZqzuGPmZ8oWc+5Q==} - engines: {node: '>=16'} + '@es-joy/jsdoccomment@0.50.2': + resolution: {integrity: sha512-YAdE/IJSpwbOTiaURNCKECdAwqrJuFiZhylmesBcIRawtYKnBR2wxPhoIewMg+Yu+QuYvHfJNReWpoxGBKOChA==} + engines: {node: '>=18'} '@esbuild/aix-ppc64@0.21.5': resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==} @@ -539,8 +536,8 @@ packages: cpu: [ppc64] os: [aix] - '@esbuild/aix-ppc64@0.23.1': - resolution: {integrity: sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==} + '@esbuild/aix-ppc64@0.25.9': + resolution: {integrity: sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA==} engines: {node: '>=18'} cpu: [ppc64] os: [aix] @@ -551,8 +548,8 @@ packages: cpu: [arm64] os: [android] - '@esbuild/android-arm64@0.23.1': - resolution: {integrity: sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==} + '@esbuild/android-arm64@0.25.9': + resolution: {integrity: sha512-IDrddSmpSv51ftWslJMvl3Q2ZT98fUSL2/rlUXuVqRXHCs5EUF1/f+jbjF5+NG9UffUDMCiTyh8iec7u8RlTLg==} engines: {node: '>=18'} cpu: [arm64] os: [android] @@ -563,8 +560,8 @@ packages: cpu: [arm] os: [android] - '@esbuild/android-arm@0.23.1': - resolution: {integrity: sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==} + '@esbuild/android-arm@0.25.9': + resolution: {integrity: sha512-5WNI1DaMtxQ7t7B6xa572XMXpHAaI/9Hnhk8lcxF4zVN4xstUgTlvuGDorBguKEnZO70qwEcLpfifMLoxiPqHQ==} engines: {node: '>=18'} cpu: [arm] os: [android] @@ -575,8 +572,8 @@ packages: cpu: [x64] os: [android] - '@esbuild/android-x64@0.23.1': - resolution: {integrity: sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==} + '@esbuild/android-x64@0.25.9': + resolution: {integrity: sha512-I853iMZ1hWZdNllhVZKm34f4wErd4lMyeV7BLzEExGEIZYsOzqDWDf+y082izYUE8gtJnYHdeDpN/6tUdwvfiw==} engines: {node: '>=18'} cpu: [x64] os: [android] @@ -587,8 +584,8 @@ packages: cpu: [arm64] os: [darwin] - '@esbuild/darwin-arm64@0.23.1': - resolution: {integrity: sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==} + '@esbuild/darwin-arm64@0.25.9': + resolution: {integrity: sha512-XIpIDMAjOELi/9PB30vEbVMs3GV1v2zkkPnuyRRURbhqjyzIINwj+nbQATh4H9GxUgH1kFsEyQMxwiLFKUS6Rg==} engines: {node: '>=18'} cpu: [arm64] os: [darwin] @@ -599,8 +596,8 @@ packages: cpu: [x64] os: [darwin] - '@esbuild/darwin-x64@0.23.1': - resolution: {integrity: sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==} + '@esbuild/darwin-x64@0.25.9': + resolution: {integrity: sha512-jhHfBzjYTA1IQu8VyrjCX4ApJDnH+ez+IYVEoJHeqJm9VhG9Dh2BYaJritkYK3vMaXrf7Ogr/0MQ8/MeIefsPQ==} engines: {node: '>=18'} cpu: [x64] os: [darwin] @@ -611,8 +608,8 @@ packages: cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-arm64@0.23.1': - resolution: {integrity: sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==} + '@esbuild/freebsd-arm64@0.25.9': + resolution: {integrity: sha512-z93DmbnY6fX9+KdD4Ue/H6sYs+bhFQJNCPZsi4XWJoYblUqT06MQUdBCpcSfuiN72AbqeBFu5LVQTjfXDE2A6Q==} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] @@ -623,8 +620,8 @@ packages: cpu: [x64] os: [freebsd] - '@esbuild/freebsd-x64@0.23.1': - resolution: {integrity: sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==} + '@esbuild/freebsd-x64@0.25.9': + resolution: {integrity: sha512-mrKX6H/vOyo5v71YfXWJxLVxgy1kyt1MQaD8wZJgJfG4gq4DpQGpgTB74e5yBeQdyMTbgxp0YtNj7NuHN0PoZg==} engines: {node: '>=18'} cpu: [x64] os: [freebsd] @@ -635,8 +632,8 @@ packages: cpu: [arm64] os: [linux] - '@esbuild/linux-arm64@0.23.1': - resolution: {integrity: sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==} + '@esbuild/linux-arm64@0.25.9': + resolution: {integrity: sha512-BlB7bIcLT3G26urh5Dmse7fiLmLXnRlopw4s8DalgZ8ef79Jj4aUcYbk90g8iCa2467HX8SAIidbL7gsqXHdRw==} engines: {node: '>=18'} cpu: [arm64] os: [linux] @@ -647,8 +644,8 @@ packages: cpu: [arm] os: [linux] - '@esbuild/linux-arm@0.23.1': - resolution: {integrity: sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==} + '@esbuild/linux-arm@0.25.9': + resolution: {integrity: sha512-HBU2Xv78SMgaydBmdor38lg8YDnFKSARg1Q6AT0/y2ezUAKiZvc211RDFHlEZRFNRVhcMamiToo7bDx3VEOYQw==} engines: {node: '>=18'} cpu: [arm] os: [linux] @@ -659,8 +656,8 @@ packages: cpu: [ia32] os: [linux] - '@esbuild/linux-ia32@0.23.1': - resolution: {integrity: sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==} + '@esbuild/linux-ia32@0.25.9': + resolution: {integrity: sha512-e7S3MOJPZGp2QW6AK6+Ly81rC7oOSerQ+P8L0ta4FhVi+/j/v2yZzx5CqqDaWjtPFfYz21Vi1S0auHrap3Ma3A==} engines: {node: '>=18'} cpu: [ia32] os: [linux] @@ -671,8 +668,8 @@ packages: cpu: [loong64] os: [linux] - '@esbuild/linux-loong64@0.23.1': - resolution: {integrity: sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==} + '@esbuild/linux-loong64@0.25.9': + resolution: {integrity: sha512-Sbe10Bnn0oUAB2AalYztvGcK+o6YFFA/9829PhOCUS9vkJElXGdphz0A3DbMdP8gmKkqPmPcMJmJOrI3VYB1JQ==} engines: {node: '>=18'} cpu: [loong64] os: [linux] @@ -683,8 +680,8 @@ packages: cpu: [mips64el] os: [linux] - '@esbuild/linux-mips64el@0.23.1': - resolution: {integrity: sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==} + '@esbuild/linux-mips64el@0.25.9': + resolution: {integrity: sha512-YcM5br0mVyZw2jcQeLIkhWtKPeVfAerES5PvOzaDxVtIyZ2NUBZKNLjC5z3/fUlDgT6w89VsxP2qzNipOaaDyA==} engines: {node: '>=18'} cpu: [mips64el] os: [linux] @@ -695,8 +692,8 @@ packages: cpu: [ppc64] os: [linux] - '@esbuild/linux-ppc64@0.23.1': - resolution: {integrity: sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==} + '@esbuild/linux-ppc64@0.25.9': + resolution: {integrity: sha512-++0HQvasdo20JytyDpFvQtNrEsAgNG2CY1CLMwGXfFTKGBGQT3bOeLSYE2l1fYdvML5KUuwn9Z8L1EWe2tzs1w==} engines: {node: '>=18'} cpu: [ppc64] os: [linux] @@ -707,8 +704,8 @@ packages: cpu: [riscv64] os: [linux] - '@esbuild/linux-riscv64@0.23.1': - resolution: {integrity: sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==} + '@esbuild/linux-riscv64@0.25.9': + resolution: {integrity: sha512-uNIBa279Y3fkjV+2cUjx36xkx7eSjb8IvnL01eXUKXez/CBHNRw5ekCGMPM0BcmqBxBcdgUWuUXmVWwm4CH9kg==} engines: {node: '>=18'} cpu: [riscv64] os: [linux] @@ -719,8 +716,8 @@ packages: cpu: [s390x] os: [linux] - '@esbuild/linux-s390x@0.23.1': - resolution: {integrity: sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==} + '@esbuild/linux-s390x@0.25.9': + resolution: {integrity: sha512-Mfiphvp3MjC/lctb+7D287Xw1DGzqJPb/J2aHHcHxflUo+8tmN/6d4k6I2yFR7BVo5/g7x2Monq4+Yew0EHRIA==} engines: {node: '>=18'} cpu: [s390x] os: [linux] @@ -731,26 +728,32 @@ packages: cpu: [x64] os: [linux] - '@esbuild/linux-x64@0.23.1': - resolution: {integrity: sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==} + '@esbuild/linux-x64@0.25.9': + resolution: {integrity: sha512-iSwByxzRe48YVkmpbgoxVzn76BXjlYFXC7NvLYq+b+kDjyyk30J0JY47DIn8z1MO3K0oSl9fZoRmZPQI4Hklzg==} engines: {node: '>=18'} cpu: [x64] os: [linux] + '@esbuild/netbsd-arm64@0.25.9': + resolution: {integrity: sha512-9jNJl6FqaUG+COdQMjSCGW4QiMHH88xWbvZ+kRVblZsWrkXlABuGdFJ1E9L7HK+T0Yqd4akKNa/lO0+jDxQD4Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + '@esbuild/netbsd-x64@0.21.5': resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==} engines: {node: '>=12'} cpu: [x64] os: [netbsd] - '@esbuild/netbsd-x64@0.23.1': - resolution: {integrity: sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==} + '@esbuild/netbsd-x64@0.25.9': + resolution: {integrity: sha512-RLLdkflmqRG8KanPGOU7Rpg829ZHu8nFy5Pqdi9U01VYtG9Y0zOG6Vr2z4/S+/3zIyOxiK6cCeYNWOFR9QP87g==} engines: {node: '>=18'} cpu: [x64] os: [netbsd] - '@esbuild/openbsd-arm64@0.23.1': - resolution: {integrity: sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==} + '@esbuild/openbsd-arm64@0.25.9': + resolution: {integrity: sha512-YaFBlPGeDasft5IIM+CQAhJAqS3St3nJzDEgsgFixcfZeyGPCd6eJBWzke5piZuZ7CtL656eOSYKk4Ls2C0FRQ==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] @@ -761,20 +764,26 @@ packages: cpu: [x64] os: [openbsd] - '@esbuild/openbsd-x64@0.23.1': - resolution: {integrity: sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==} + '@esbuild/openbsd-x64@0.25.9': + resolution: {integrity: sha512-1MkgTCuvMGWuqVtAvkpkXFmtL8XhWy+j4jaSO2wxfJtilVCi0ZE37b8uOdMItIHz4I6z1bWWtEX4CJwcKYLcuA==} engines: {node: '>=18'} cpu: [x64] os: [openbsd] + '@esbuild/openharmony-arm64@0.25.9': + resolution: {integrity: sha512-4Xd0xNiMVXKh6Fa7HEJQbrpP3m3DDn43jKxMjxLLRjWnRsfxjORYJlXPO4JNcXtOyfajXorRKY9NkOpTHptErg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + '@esbuild/sunos-x64@0.21.5': resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==} engines: {node: '>=12'} cpu: [x64] os: [sunos] - '@esbuild/sunos-x64@0.23.1': - resolution: {integrity: sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==} + '@esbuild/sunos-x64@0.25.9': + resolution: {integrity: sha512-WjH4s6hzo00nNezhp3wFIAfmGZ8U7KtrJNlFMRKxiI9mxEK1scOMAaa9i4crUtu+tBr+0IN6JCuAcSBJZfnphw==} engines: {node: '>=18'} cpu: [x64] os: [sunos] @@ -785,8 +794,8 @@ packages: cpu: [arm64] os: [win32] - '@esbuild/win32-arm64@0.23.1': - resolution: {integrity: sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==} + '@esbuild/win32-arm64@0.25.9': + resolution: {integrity: sha512-mGFrVJHmZiRqmP8xFOc6b84/7xa5y5YvR1x8djzXpJBSv/UsNK6aqec+6JDjConTgvvQefdGhFDAs2DLAds6gQ==} engines: {node: '>=18'} cpu: [arm64] os: [win32] @@ -797,8 +806,8 @@ packages: cpu: [ia32] os: [win32] - '@esbuild/win32-ia32@0.23.1': - resolution: {integrity: sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==} + '@esbuild/win32-ia32@0.25.9': + resolution: {integrity: sha512-b33gLVU2k11nVx1OhX3C8QQP6UHQK4ZtN56oFWvVXvz2VkDoe6fbG8TOgHFxEvqeqohmRnIHe5A1+HADk4OQww==} engines: {node: '>=18'} cpu: [ia32] os: [win32] @@ -809,20 +818,20 @@ packages: cpu: [x64] os: [win32] - '@esbuild/win32-x64@0.23.1': - resolution: {integrity: sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==} + '@esbuild/win32-x64@0.25.9': + resolution: {integrity: sha512-PPOl1mi6lpLNQxnGoyAfschAodRFYXJ+9fs6WHXz7CSWKbOqiMZsubC+BQsVKuul+3vKLuwTHsS2c2y9EoKwxQ==} engines: {node: '>=18'} cpu: [x64] os: [win32] - '@eslint-community/eslint-plugin-eslint-comments@4.4.1': - resolution: {integrity: sha512-lb/Z/MzbTf7CaVYM9WCFNQZ4L1yi3ev2fsFPF99h31ljhSEyUoyEsKsNWiU+qD1glbYTDJdqgyaLKtyTkkqtuQ==} + '@eslint-community/eslint-plugin-eslint-comments@4.5.0': + resolution: {integrity: sha512-MAhuTKlr4y/CE3WYX26raZjy+I/kS2PLKSzvfmDCGrBLTFHOYwqROZdr4XwPgXwX3K9rjzMr4pSmUWGnzsUyMg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0 - '@eslint-community/eslint-utils@4.4.1': - resolution: {integrity: sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==} + '@eslint-community/eslint-utils@4.8.0': + resolution: {integrity: sha512-MJQFqrZgcW0UNYLGOuQpey/oTN59vyWwplvCGZztn1cKz9agZPPYpJB7h2OMmuu7VLqkvEjN8feFZJmxNF9D+Q==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 @@ -831,70 +840,74 @@ packages: resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} - '@eslint/compat@1.2.2': - resolution: {integrity: sha512-jhgiIrsw+tRfcBQ4BFl2C3vCrIUw2trCY0cnDvGZpwTtKCEDmZhAtMfrEUP/KpnwM6PrO0T+Ltm+ccW74olG3Q==} + '@eslint/compat@1.3.2': + resolution: {integrity: sha512-jRNwzTbd6p2Rw4sZ1CgWRS8YMtqG15YyZf7zvb6gY2rB2u6n+2Z+ELW0GtL0fQgyl0pr4Y/BzBfng/BdsereRA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - eslint: ^9.10.0 + eslint: ^8.40 || 9 peerDependenciesMeta: eslint: optional: true - '@eslint/config-array@0.18.0': - resolution: {integrity: sha512-fTxvnS1sRMu3+JjXwJG0j/i4RT9u4qJ+lqS/yCGap4lH4zZGzQ7tu+xZqQmcMZq5OBZDL4QRxQzRjkWcGt8IVw==} + '@eslint/config-array@0.21.0': + resolution: {integrity: sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/config-helpers@0.3.1': + resolution: {integrity: sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/core@0.7.0': - resolution: {integrity: sha512-xp5Jirz5DyPYlPiKat8jaq0EmYvDXKKpzTbxXMpT9eqlRJkRKIz9AGMdlvYjih+im+QlhWrpvVjl8IPC/lHlUw==} + '@eslint/core@0.14.0': + resolution: {integrity: sha512-qIbV0/JZr7iSDjqAc60IqbLdsj9GDt16xQtWD+B78d/HAlvysGdZZ6rpJHGAc2T0FQx1X6thsSPdnoiGKdNtdg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/eslintrc@3.1.0': - resolution: {integrity: sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==} + '@eslint/core@0.15.2': + resolution: {integrity: sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/js@9.14.0': - resolution: {integrity: sha512-pFoEtFWCPyDOl+C6Ift+wC7Ro89otjigCf5vcuWqWgqNSQbRrpjSvdeE6ofLz4dHmyxD5f7gIdGT4+p36L6Twg==} + '@eslint/eslintrc@3.3.1': + resolution: {integrity: sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/markdown@6.2.1': - resolution: {integrity: sha512-cKVd110hG4ICHmWhIwZJfKmmJBvbiDWyrHODJknAtudKgZtlROGoLX9UEOA0o746zC0hCY4UV4vR+aOGW9S6JQ==} + '@eslint/js@9.35.0': + resolution: {integrity: sha512-30iXE9whjlILfWobBkNerJo+TXYsgVM5ERQwMcMKCHckHflCmf7wXDAHlARoWnh0s1U72WqlbeyE7iAcCzuCPw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/object-schema@2.1.4': - resolution: {integrity: sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==} + '@eslint/markdown@6.6.0': + resolution: {integrity: sha512-IsWPy2jU3gaQDlioDC4sT4I4kG1hX1OMWs/q2sWwJrPoMASHW/Z4SDw+6Aql6EsHejGbagYuJbFq9Zvx+Y1b1Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/plugin-kit@0.2.2': - resolution: {integrity: sha512-CXtq5nR4Su+2I47WPOlWud98Y5Lv8Kyxp2ukhgFx/eW6Blm18VXJO5WuQylPugRo8nbluoi6GvvxBLqHcvqUUw==} + '@eslint/object-schema@2.1.6': + resolution: {integrity: sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@floating-ui/core@1.6.8': - resolution: {integrity: sha512-7XJ9cPU+yI2QeLS+FCSlqNFZJq8arvswefkZrYI1yQBbftw6FyrZOxYSh+9S7z7TpeWlRt9zJ5IhM1WIL334jA==} + '@eslint/plugin-kit@0.3.5': + resolution: {integrity: sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@floating-ui/core@1.7.3': + resolution: {integrity: sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==} '@floating-ui/dom@1.1.1': resolution: {integrity: sha512-TpIO93+DIujg3g7SykEAGZMDtbJRrmnYRCNYSjJlvIbGhBjRSNTLVbNeDQBrzy9qDgUbiWdc7KA0uZHZ2tJmiw==} - '@floating-ui/utils@0.2.8': - resolution: {integrity: sha512-kym7SodPp8/wloecOpcmSnWJsK7M0E5Wg8UcFA+uO4B9s5d0ywXOEro/8HM9x0rW+TljRzul/14UYz3TleT3ig==} + '@floating-ui/utils@0.2.10': + resolution: {integrity: sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==} '@humanfs/core@0.19.1': resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} engines: {node: '>=18.18.0'} - '@humanfs/node@0.16.6': - resolution: {integrity: sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==} + '@humanfs/node@0.16.7': + resolution: {integrity: sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==} engines: {node: '>=18.18.0'} '@humanwhocodes/module-importer@1.0.1': resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} engines: {node: '>=12.22'} - '@humanwhocodes/retry@0.3.1': - resolution: {integrity: sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==} - engines: {node: '>=18.18'} - - '@humanwhocodes/retry@0.4.1': - resolution: {integrity: sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA==} + '@humanwhocodes/retry@0.4.3': + resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} engines: {node: '>=18.18'} '@intlify/bundle-utils@9.0.0': @@ -909,40 +922,44 @@ packages: vue-i18n: optional: true - '@intlify/core-base@10.0.4': - resolution: {integrity: sha512-GG428DkrrWCMhxRMRQZjuS7zmSUzarYcaHJqG9VB8dXAxw4iQDoKVQ7ChJRB6ZtsCsX3Jse1PEUlHrJiyQrOTg==} + '@intlify/core-base@10.0.8': + resolution: {integrity: sha512-FoHslNWSoHjdUBLy35bpm9PV/0LVI/DSv9L6Km6J2ad8r/mm0VaGg06C40FqlE8u2ADcGUM60lyoU7Myo4WNZQ==} engines: {node: '>= 16'} - '@intlify/core-base@9.14.4': - resolution: {integrity: sha512-vtZCt7NqWhKEtHa3SD/322DlgP5uR9MqWxnE0y8Q0tjDs9H5Lxhss+b5wv8rmuXRoHKLESNgw9d+EN9ybBbj9g==} + '@intlify/core-base@9.14.5': + resolution: {integrity: sha512-5ah5FqZG4pOoHjkvs8mjtv+gPKYU0zCISaYNjBNNqYiaITxW8ZtVih3GS/oTOqN8d9/mDLyrjD46GBApNxmlsA==} engines: {node: '>= 16'} - '@intlify/message-compiler@10.0.4': - resolution: {integrity: sha512-AFbhEo10DP095/45EauinQJ5hJ3rJUmuuqltGguvc3WsvezZN+g8qNHLGWKu60FHQVizMrQY7VJ+zVlBXlQQkQ==} + '@intlify/message-compiler@10.0.8': + resolution: {integrity: sha512-DV+sYXIkHVd5yVb2mL7br/NEUwzUoLBsMkV3H0InefWgmYa34NLZUvMCGi5oWX+Hqr2Y2qUxnVrnOWF4aBlgWg==} engines: {node: '>= 16'} '@intlify/message-compiler@12.0.0-alpha.3': resolution: {integrity: sha512-mDDTN3gfYOHhBnpnlby19UHyvMaOnzdlpsIrxUfs44R/vCATfn8pMOkE8PXD2t410xkocEj3FpDcC9XC/0v4Dg==} engines: {node: '>= 16'} - '@intlify/message-compiler@9.14.4': - resolution: {integrity: sha512-vcyCLiVRN628U38c3PbahrhbbXrckrM9zpy0KZVlDk2Z0OnGwv8uQNNXP3twwGtfLsCf4gu3ci6FMIZnPaqZsw==} + '@intlify/message-compiler@9.14.5': + resolution: {integrity: sha512-IHzgEu61/YIpQV5Pc3aRWScDcnFKWvQA9kigcINcCBXN8mbW+vk9SK+lDxA6STzKQsVJxUPg9ACC52pKKo3SVQ==} engines: {node: '>= 16'} - '@intlify/shared@10.0.4': - resolution: {integrity: sha512-ukFn0I01HsSgr3VYhYcvkTCLS7rGa0gw4A4AMpcy/A9xx/zRJy7PS2BElMXLwUazVFMAr5zuiTk3MQeoeGXaJg==} + '@intlify/shared@10.0.8': + resolution: {integrity: sha512-BcmHpb5bQyeVNrptC3UhzpBZB/YHHDoEREOUERrmF2BRxsyOEuRrq+Z96C/D4+2KJb8kuHiouzAei7BXlG0YYw==} + engines: {node: '>= 16'} + + '@intlify/shared@11.1.12': + resolution: {integrity: sha512-Om86EjuQtA69hdNj3GQec9ZC0L0vPSAnXzB3gP/gyJ7+mA7t06d9aOAiqMZ+xEOsumGP4eEBlfl8zF2LOTzf2A==} engines: {node: '>= 16'} '@intlify/shared@12.0.0-alpha.3': resolution: {integrity: sha512-ryaNYBvxQjyJUmVuBBg+HHUsmGnfxcEUPR0NCeG4/K9N2qtyFE35C80S15IN6iYFE2MGWLN7HfOSyg0MXZIc9w==} engines: {node: '>= 16'} - '@intlify/shared@9.14.4': - resolution: {integrity: sha512-P9zv6i1WvMc9qDBWvIgKkymjY2ptIiQ065PjDv7z7fDqH3J/HBRBN5IoiR46r/ujRcU7hCuSIZWvCAFCyuOYZA==} + '@intlify/shared@9.14.5': + resolution: {integrity: sha512-9gB+E53BYuAEMhbCAxVgG38EZrk59sxBtv3jSizNL2hEWlgjBjAw1AwpLHtNaeda12pe6W20OGEa0TwuMSRbyQ==} engines: {node: '>= 16'} - '@intlify/unplugin-vue-i18n@5.2.0': - resolution: {integrity: sha512-pmRiPY2Nj9mmSrixT69aO45XxGUr5fDBy/IIw4ajLlDTJm5TSmQKA5YNdsH0uxVDCPWy5tlQrF18hkDwI7UJvg==} + '@intlify/unplugin-vue-i18n@5.3.1': + resolution: {integrity: sha512-76huP8TpMOtBMLsYYIMLNbqMPXJ7+Q6xcjP6495h/pmbOQ7sw/DB8E0OFvDFeIZ2571a4ylzJnz+KMuYbAs1xA==} engines: {node: '>= 18'} peerDependencies: petite-vue-i18n: '*' @@ -972,57 +989,66 @@ packages: vue-i18n: optional: true + '@isaacs/balanced-match@4.0.1': + resolution: {integrity: sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==} + engines: {node: 20 || >=22} + + '@isaacs/brace-expansion@5.0.0': + resolution: {integrity: sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==} + engines: {node: 20 || >=22} + '@isaacs/cliui@8.0.2': resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} engines: {node: '>=12'} - '@jridgewell/gen-mapping@0.3.5': - resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==} - engines: {node: '>=6.0.0'} + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + + '@jridgewell/remapping@2.3.5': + resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} '@jridgewell/resolve-uri@3.1.2': resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} engines: {node: '>=6.0.0'} - '@jridgewell/set-array@1.2.1': - resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} - engines: {node: '>=6.0.0'} + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} - '@jridgewell/sourcemap-codec@1.5.0': - resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} + '@jridgewell/trace-mapping@0.3.30': + resolution: {integrity: sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==} - '@jridgewell/trace-mapping@0.3.25': - resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} + '@kurkle/color@0.3.4': + resolution: {integrity: sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w==} - '@mdit-vue/plugin-component@2.1.3': - resolution: {integrity: sha512-9AG17beCgpEw/4ldo/M6Y/1Rh4E1bqMmr/rCkWKmCAxy9tJz3lzY7HQJanyHMJufwsb3WL5Lp7Om/aPcQTZ9SA==} + '@mdit-vue/plugin-component@2.1.4': + resolution: {integrity: sha512-fiLbwcaE6gZE4c8Mkdkc4X38ltXh/EdnuPE1hepFT2dLiW6I4X8ho2Wq7nhYuT8RmV4OKlCFENwCuXlKcpV/sw==} - '@mdit-vue/plugin-frontmatter@2.1.3': - resolution: {integrity: sha512-KxsSCUVBEmn6sJcchSTiI5v9bWaoRxe68RBYRDGcSEY1GTnfQ5gQPMIsM48P4q1luLEIWurVGGrRu7u93//LDQ==} + '@mdit-vue/plugin-frontmatter@2.1.4': + resolution: {integrity: sha512-mOlavV176njnozIf0UZGFYymmQ2LK5S1rjrbJ1uGz4Df59tu0DQntdE7YZXqmJJA9MiSx7ViCTUQCNPKg7R8Ow==} - '@mdit-vue/types@2.1.0': - resolution: {integrity: sha512-TMBB/BQWVvwtpBdWD75rkZx4ZphQ6MN0O4QB2Bc0oI5PC2uE57QerhNxdRZ7cvBHE2iY2C+BUNUziCfJbjIRRA==} + '@mdit-vue/types@2.1.4': + resolution: {integrity: sha512-QiGNZslz+zXUs2X8D11UQhB4KAMZ0DZghvYxa7+1B+VMLcDtz//XHpWbcuexjzE3kBXSxIUTPH3eSQCa0puZHA==} - '@microsoft/api-extractor-model@7.29.8': - resolution: {integrity: sha512-t3Z/xcO6TRbMcnKGVMs4uMzv/gd5j0NhMiJIGjD4cJMeFJ1Hf8wnLSx37vxlRlL0GWlGJhnFgxvnaL6JlS+73g==} + '@microsoft/api-extractor-model@7.30.7': + resolution: {integrity: sha512-TBbmSI2/BHpfR9YhQA7nH0nqVmGgJ0xH0Ex4D99/qBDAUpnhA2oikGmdXanbw9AWWY/ExBYIpkmY8dBHdla3YQ==} - '@microsoft/api-extractor@7.47.11': - resolution: {integrity: sha512-lrudfbPub5wzBhymfFtgZKuBvXxoSIAdrvS2UbHjoMT2TjIEddq6Z13pcve7A03BAouw0x8sW8G4txdgfiSwpQ==} + '@microsoft/api-extractor@7.52.11': + resolution: {integrity: sha512-IKQ7bHg6f/Io3dQds6r9QPYk4q0OlR9A4nFDtNhUt3UUIhyitbxAqRN1CLjUVtk6IBk3xzyCMOdwwtIXQ7AlGg==} hasBin: true - '@microsoft/tsdoc-config@0.17.0': - resolution: {integrity: sha512-v/EYRXnCAIHxOHW+Plb6OWuUoMotxTN0GLatnpOb1xq0KuTNw/WI3pamJx/UbsoJP5k9MCw1QxvvhPcF9pH3Zg==} + '@microsoft/tsdoc-config@0.17.1': + resolution: {integrity: sha512-UtjIFe0C6oYgTnad4q1QP4qXwLhe6tIpNTRStJ2RZEPIkqQPREAwE5spzVxsdn9UaEMUqhh0AqSx3X4nWAKXWw==} - '@microsoft/tsdoc@0.15.0': - resolution: {integrity: sha512-HZpPoABogPvjeJOdzCOSJsXeL/SMCBgBZMVC3X3d7YYp2gf31MfxhUoYUNwf1ERPJOnQc0wkFn9trqI6ZEdZuA==} + '@microsoft/tsdoc@0.15.1': + resolution: {integrity: sha512-4aErSrCR/On/e5G2hDP0wjooqDdauzEbIq8hIkIe5pXV0rtWJZvdCEKL0ykZxex+IxIwBp0eGeV48hQN07dXtw==} - '@modyfi/vite-plugin-yaml@1.1.0': - resolution: {integrity: sha512-L26xfzkSo1yamODCAtk/ipVlL6OEw2bcJ92zunyHu8zxi7+meV0zefA9xscRMDCsMY8xL3C3wi3DhMiPxcbxbw==} + '@modyfi/vite-plugin-yaml@1.1.1': + resolution: {integrity: sha512-rEbfFNlMGLKpAYs2RsfLAhxCHFa6M4QKHHk0A4EYcCJAUwFtFO6qiEdLjUGUTtnRUxAC7GxxCa+ZbeUILSDvqQ==} peerDependencies: - vite: ^3.2.7 || ^4.0.5 || ^5.0.5 + vite: '>=3.2.7' - '@napi-rs/wasm-runtime@0.2.5': - resolution: {integrity: sha512-kwUxR7J9WLutBbulqg1dfOrMTwhMdXLdcGUhcbCcGwnPLt3gz19uHVdwH1syKVDbE022ZS2vZxOWflFLS0YTjw==} + '@napi-rs/wasm-runtime@0.2.12': + resolution: {integrity: sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==} '@nodelib/fs.scandir@2.1.5': resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} @@ -1036,58 +1062,58 @@ packages: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} - '@oxc-resolver/binding-darwin-arm64@2.0.1': - resolution: {integrity: sha512-0l180Q0aBPRj23FlY7heLKo4znwXZGyTirxhtQewUeqLVnTzdyOuqGGKrnlL2rMXyE9cEXqQ4ZODdzdmWO6QsQ==} + '@oxc-resolver/binding-darwin-arm64@4.2.0': + resolution: {integrity: sha512-DP+KY4nXRJvL5XayKda0P7NCjcP1zZ5x6RZznMM/bMPCBrjcYNG4XKV9v/EbkSq3Et24mEJFYOM55WmPxtqf0w==} cpu: [arm64] os: [darwin] - '@oxc-resolver/binding-darwin-x64@2.0.1': - resolution: {integrity: sha512-56icBBSx+5eM/+uV6KwsK66AJ8LIs3lMTH0Nw9ivVHRqos/eHIUigtlRzsEFF1CtOFCE4mC6p/kihiuBvQoVzA==} + '@oxc-resolver/binding-darwin-x64@4.2.0': + resolution: {integrity: sha512-k8wrYcZPE94Wq7QvLi7FVqdbnlg52L/J7dZOvdjmQaJN9zp2Gg/rhIXlXGf1yFqOC0NfiDIW0C4CpEat/zmw+Q==} cpu: [x64] os: [darwin] - '@oxc-resolver/binding-freebsd-x64@2.0.1': - resolution: {integrity: sha512-/tbk+uuplx77DOuctwz0nZxfWMRnpBBQei1BGfjABNWtCQxnABhuiGgDIRXHx5TzJZhbbspxcndPagW3NHGixg==} + '@oxc-resolver/binding-freebsd-x64@4.2.0': + resolution: {integrity: sha512-ozYwrwsJMBPCF6PEvO4UeGcV1klyV3raVMoZeGPElF0QQpWTiLiOc1CEN3U/H82ZVYWLMDLNPTmTOdsc3CELqA==} cpu: [x64] os: [freebsd] - '@oxc-resolver/binding-linux-arm-gnueabihf@2.0.1': - resolution: {integrity: sha512-DeALa9wNzaKpR3vqXOFANFNygYwcLXUB50CYNhX3oqBLNhp4U79N5mDhQOr00fsDhaYcsCkMtRnxBi9zQUkGzQ==} + '@oxc-resolver/binding-linux-arm-gnueabihf@4.2.0': + resolution: {integrity: sha512-3LjgnQBIrQywemSbVJvjCP+X6kcmChF1NRytgccbVCtOFocNh8JWtykdUnAbeJRY8SeM49QP0WtAPlEEdHMNTQ==} cpu: [arm] os: [linux] - '@oxc-resolver/binding-linux-arm64-gnu@2.0.1': - resolution: {integrity: sha512-dMHO0PG0KYVeLxCOsmE7HMsdVmvovYPp+2k00Mp/PTu9HqbmFPdYaWbbDs0g+tqePL8u+Q7+bEMX7gUFGPFP2Q==} + '@oxc-resolver/binding-linux-arm64-gnu@4.2.0': + resolution: {integrity: sha512-mMB1AvqzTH25rbUo1eRfvFzNqBopX6aRlDmO1fIVVzIWi6YJNKckxbkGaatez4hH/n86IR6aEdZFM3qBUjn3Tg==} cpu: [arm64] os: [linux] - '@oxc-resolver/binding-linux-arm64-musl@2.0.1': - resolution: {integrity: sha512-J0O5YbVZ+ioDH7e9VhHnqEEj140iHDB5KdxNIfRbPWa3nVuRrWDeBztk8239Yi1Mjb0tGp1m1arGU/XmlEar0A==} + '@oxc-resolver/binding-linux-arm64-musl@4.2.0': + resolution: {integrity: sha512-9oPBU8Yb35z15/14LzALn/8rRwwrtfe19l25N1MRZVSONGiOwfzWNqDNjWiDdyW+EUt/hlylmFOItZmreL6iIw==} cpu: [arm64] os: [linux] - '@oxc-resolver/binding-linux-x64-gnu@2.0.1': - resolution: {integrity: sha512-5nGiVH3R0p8SxHN47hd0bwtCDxdeWlQ6z1UO1U3wx6LfBwqSKbsuL1r5Z/+izBOEAtON1QQlpDBUaoCQMCyduQ==} + '@oxc-resolver/binding-linux-x64-gnu@4.2.0': + resolution: {integrity: sha512-8wU4fwHb0b45i0qMBJ24UYBEtaLyvYWUOqVVCn0SpQZ1mhWWC8dvD6+zIVAKRVex/cKdgzi3imXoKGIDqVEu9w==} cpu: [x64] os: [linux] - '@oxc-resolver/binding-linux-x64-musl@2.0.1': - resolution: {integrity: sha512-abG5WmO2ZgkQTTfRZItFatzo9d+KYuePJ68cp1sJ/gHlNTliE+/6XepTggmRm85+8ehexjjceldvNdj80j6Gjg==} + '@oxc-resolver/binding-linux-x64-musl@4.2.0': + resolution: {integrity: sha512-5CS2wlGxzESPJCj4NlNGr73QCku75VpGtkwNp8qJF4hLELKAzkoqIB0eBbcvNPg8m2rB7YeXb1u+puGUKXDhNQ==} cpu: [x64] os: [linux] - '@oxc-resolver/binding-wasm32-wasi@2.0.1': - resolution: {integrity: sha512-CES19bgvEETC1YkpdJSFFrprtZ5L7VaoxR5ZVNUJrpCAM6WVPMhjev2NXLfgzbaHOBJo6lL4jnTDCtRPwzoYQw==} + '@oxc-resolver/binding-wasm32-wasi@4.2.0': + resolution: {integrity: sha512-VOLpvmVAQZjvj/7Et/gYzW6yBqL9VKjLWOGaFiQ7cvTpY9R9d/1mrNKEuP3beDHF2si2fM5f2pl9bL+N4tvwiA==} engines: {node: '>=14.0.0'} cpu: [wasm32] - '@oxc-resolver/binding-win32-arm64-msvc@2.0.1': - resolution: {integrity: sha512-iXIQ6JNylPio31j+bAE900jYsp9qu4DKwST1ODok6RxMQkyKfJjc9xoD/WsN6gbh8VfOAAEiAL1uMsLdwGHagA==} + '@oxc-resolver/binding-win32-arm64-msvc@4.2.0': + resolution: {integrity: sha512-8tPj93hd1H5vXMtud1jN3C+prLZnvNzGw+BuyMer1+Z6RCQZHqn0XrfCalcuDOggKUYFagcKDdpdhv/CSW2/ZQ==} cpu: [arm64] os: [win32] - '@oxc-resolver/binding-win32-x64-msvc@2.0.1': - resolution: {integrity: sha512-hPUQ+dtWOFPWeemo0VK6DNH/NY9UjjBxAfpsjoCPOAcXpyuMQ3CsHFDwi2keNkPkCgfJSJRfffFvQysqF3fKKQ==} + '@oxc-resolver/binding-win32-x64-msvc@4.2.0': + resolution: {integrity: sha512-of3dYwB4RN825qq9kBu/79QPVXDZFb5S/opLtJScLqyRhI6owkFWV4P9VmFih8dfBh/7SImdvt/B4HQTF1fthg==} cpu: [x64] os: [win32] @@ -1095,48 +1121,51 @@ packages: resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} - '@pkgr/core@0.1.1': - resolution: {integrity: sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==} + '@pkgr/core@0.1.2': + resolution: {integrity: sha512-fdDH1LSGfZdTH2sxdpVMw31BanV28K/Gry0cVFxaNP77neJSkd82mM8ErPNYs9e+0O7SdHBLTDzDgwUuy18RnQ==} + engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} + + '@pkgr/core@0.2.9': + resolution: {integrity: sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==} engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} - '@polka/url@1.0.0-next.28': - resolution: {integrity: sha512-8LduaNlMZGwdZ6qWrKlfa+2M4gahzFkprZiAt2TF8uS0qQgBizKXpXURqvTJ4WtmupWxaLqjRb2UCTe72mu+Aw==} + '@polka/url@1.0.0-next.29': + resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==} - '@primeuix/styled@0.5.1': - resolution: {integrity: sha512-5Ftw/KSauDPClQ8F2qCyCUF7cIUEY4yLNikf0rKV7Vsb8zGYNK0dahQe7CChaR6M2Kn+NA2DSBSk76ZXqj6Uog==} + '@primeuix/styled@0.7.2': + resolution: {integrity: sha512-tIJ6byZezTYZ9YUICNSidQHOIQOQL3zeUgjwiX0JnBTK3+WCvy4DyCBcrJ94RtiX0WGFZSYNvaGaFkTo4jU8FQ==} engines: {node: '>=12.11.0'} - '@primeuix/styles@1.0.2': - resolution: {integrity: sha512-zOOzpm0ECp1gJJx0MWXGXszdkqX3pRM9HAk4swSMOBzVKe+eCV3dhVLv3SvIzB40NjUXmP/8DqgsYESjAYbBiw==} + '@primeuix/styles@1.2.3': + resolution: {integrity: sha512-+KwmQsLTYgVAqFADmO252btz40lstPML6r4QMNjxz4gLNCKVW3kPR0/aCouQx6/21+boXG1P68tu8Zk3FAKr2w==} - '@primeuix/themes@1.0.2': - resolution: {integrity: sha512-pm9Z6TsaV0Q7LBKFneXg2XgltA3huYG4KIMGYX9AzuebfwQEcW0Kf0xgsXt2XUu4ODEizuQgccG7xGS8NYPsxQ==} + '@primeuix/themes@1.2.3': + resolution: {integrity: sha512-GLAU2h6lhgln2w10EQalUQlgwbgQ0xZoIOLMNGfIvqU4O09L282P7rwKCKQksvAGAFt1GoO/Q1NgBSxnttr7iA==} - '@primeuix/utils@0.5.3': - resolution: {integrity: sha512-7SGh7734wcF1/uK6RzO6Z6CBjGQ97GDHfpyl2F1G/c7R0z9hkT/V72ypDo82AWcCS7Ta07oIjDpOCTkSVZuEGQ==} + '@primeuix/utils@0.6.1': + resolution: {integrity: sha512-tQL/ZOPgCdD+NTimlUmhyD0ey8J1XmpZE4hDHM+/fnuBicVVmlKOd5HpS748LcOVRUKbWjmEPdHX4hi5XZoC1Q==} engines: {node: '>=12.11.0'} - '@primevue/auto-import-resolver@4.3.3': - resolution: {integrity: sha512-CwQPlG8IzDySOwF8N0Q0rLv76awCH7SzYt+RHu1AO/HZEsdCorclIoJO6TrJYevenlhxialprRqpIV52McoRpw==} + '@primevue/auto-import-resolver@4.3.9': + resolution: {integrity: sha512-3zC5cmJSWFFWm376wgJ4Ntq0xXE2T6zPtfUwEf6XlAF65bRhq+9YTM/rgoArJd2OY9jOGgDcwOt7/Np37hVGiA==} engines: {node: '>=12.11.0'} - '@primevue/core@4.3.3': - resolution: {integrity: sha512-kSkN5oourG7eueoFPIqiNX3oDT/f0I5IRK3uOY/ytz+VzTZp5yuaCN0Nt42ZQpVXjDxMxDvUhIdaXVrjr58NhQ==} + '@primevue/core@4.3.9': + resolution: {integrity: sha512-P08MhVD8WrldbropVuiG25ku6il+v+cKKrspES6RijDc4NE/CrGMAwOlrdpOkpy7pcfTzqC9eIGBx5ifS28S5g==} engines: {node: '>=12.11.0'} peerDependencies: vue: ^3.5.0 - '@primevue/icons@4.3.3': - resolution: {integrity: sha512-ouQaxHyeFB6MSfEGGbjaK5Qv9efS1xZGetZoU5jcPm090MSYLFtroP1CuK3lZZAQals06TZ6T6qcoNukSHpK5w==} + '@primevue/icons@4.3.9': + resolution: {integrity: sha512-7EaVz7yS4KlAWppPsFEl9r2ia94LZStNjO2qiXolz0lvRYjZu6W66Or96sTr7GJhIIoE+qRnn3T9A/LeUmke6Q==} engines: {node: '>=12.11.0'} - '@primevue/metadata@4.3.3': - resolution: {integrity: sha512-R1IBTGsYsmOlAy4/dytJW699Iie9B8p8PqbIxDY0GWiMGk8O0tVBYfuCs50w0QtHqmThhLRtSS+eCSOP1ybwSg==} + '@primevue/metadata@4.3.9': + resolution: {integrity: sha512-Xt4mQNzdhz71hN+go53iCzE2vloKwTUkLpunzHup344ivJTZUYSlsszYFDGv4DEnqfkfRHHPBP3ZzHzgzE89jQ==} engines: {node: '>=12.11.0'} - '@primevue/themes@4.3.3': - resolution: {integrity: sha512-LiYlSXsHeA8DFm8+yGyiDFQc3SEQwHcESTN1/rV+rrZ+UPuPisHY9fNIGRFQKA5XUQPDTQDQjtwYGx25Jikwhg==} - engines: {node: '>=12.11.0'} + '@quansync/fs@0.1.5': + resolution: {integrity: sha512-lNS9hL2aS2NZgNW7BBj+6EBl4rOf8l+tQ0eRY6JWCI8jI2kc53gSoqbjojU0OnAWhzoXiOjFyGsHcDGePB3lhA==} '@rollup/plugin-typescript@11.1.6': resolution: {integrity: sha512-R92yOmIACgYdJ7dJ97p4K69I8gg6IEHt8M7dUBxN3W6nrO8uUxX5ixl0yU/N3aZTi8WhPuICvOHXQvF6FaykAA==} @@ -1160,8 +1189,8 @@ packages: rollup: optional: true - '@rollup/pluginutils@5.1.3': - resolution: {integrity: sha512-Pnsb6f32CD2W3uCaLZIzDmeFyQ2b8UWMFI7xtwUezpcGBDVDW6y9XgAWIlARiGAo6eNF5FK5aQTr0LFyNyqq5A==} + '@rollup/pluginutils@5.3.0': + resolution: {integrity: sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==} engines: {node: '>=14.0.0'} peerDependencies: rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 @@ -1169,98 +1198,113 @@ packages: rollup: optional: true - '@rollup/rollup-android-arm-eabi@4.24.3': - resolution: {integrity: sha512-ufb2CH2KfBWPJok95frEZZ82LtDl0A6QKTa8MoM+cWwDZvVGl5/jNb79pIhRvAalUu+7LD91VYR0nwRD799HkQ==} + '@rollup/rollup-android-arm-eabi@4.50.1': + resolution: {integrity: sha512-HJXwzoZN4eYTdD8bVV22DN8gsPCAj3V20NHKOs8ezfXanGpmVPR7kalUHd+Y31IJp9stdB87VKPFbsGY3H/2ag==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.24.3': - resolution: {integrity: sha512-iAHpft/eQk9vkWIV5t22V77d90CRofgR2006UiCjHcHJFVI1E0oBkQIAbz+pLtthFw3hWEmVB4ilxGyBf48i2Q==} + '@rollup/rollup-android-arm64@4.50.1': + resolution: {integrity: sha512-PZlsJVcjHfcH53mOImyt3bc97Ep3FJDXRpk9sMdGX0qgLmY0EIWxCag6EigerGhLVuL8lDVYNnSo8qnTElO4xw==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.24.3': - resolution: {integrity: sha512-QPW2YmkWLlvqmOa2OwrfqLJqkHm7kJCIMq9kOz40Zo9Ipi40kf9ONG5Sz76zszrmIZZ4hgRIkez69YnTHgEz1w==} + '@rollup/rollup-darwin-arm64@4.50.1': + resolution: {integrity: sha512-xc6i2AuWh++oGi4ylOFPmzJOEeAa2lJeGUGb4MudOtgfyyjr4UPNK+eEWTPLvmPJIY/pgw6ssFIox23SyrkkJw==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.24.3': - resolution: {integrity: sha512-KO0pN5x3+uZm1ZXeIfDqwcvnQ9UEGN8JX5ufhmgH5Lz4ujjZMAnxQygZAVGemFWn+ZZC0FQopruV4lqmGMshow==} + '@rollup/rollup-darwin-x64@4.50.1': + resolution: {integrity: sha512-2ofU89lEpDYhdLAbRdeyz/kX3Y2lpYc6ShRnDjY35bZhd2ipuDMDi6ZTQ9NIag94K28nFMofdnKeHR7BT0CATw==} cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.24.3': - resolution: {integrity: sha512-CsC+ZdIiZCZbBI+aRlWpYJMSWvVssPuWqrDy/zi9YfnatKKSLFCe6fjna1grHuo/nVaHG+kiglpRhyBQYRTK4A==} + '@rollup/rollup-freebsd-arm64@4.50.1': + resolution: {integrity: sha512-wOsE6H2u6PxsHY/BeFHA4VGQN3KUJFZp7QJBmDYI983fgxq5Th8FDkVuERb2l9vDMs1D5XhOrhBrnqcEY6l8ZA==} cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.24.3': - resolution: {integrity: sha512-F0nqiLThcfKvRQhZEzMIXOQG4EeX61im61VYL1jo4eBxv4aZRmpin6crnBJQ/nWnCsjH5F6J3W6Stdm0mBNqBg==} + '@rollup/rollup-freebsd-x64@4.50.1': + resolution: {integrity: sha512-A/xeqaHTlKbQggxCqispFAcNjycpUEHP52mwMQZUNqDUJFFYtPHCXS1VAG29uMlDzIVr+i00tSFWFLivMcoIBQ==} cpu: [x64] os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.24.3': - resolution: {integrity: sha512-KRSFHyE/RdxQ1CSeOIBVIAxStFC/hnBgVcaiCkQaVC+EYDtTe4X7z5tBkFyRoBgUGtB6Xg6t9t2kulnX6wJc6A==} + '@rollup/rollup-linux-arm-gnueabihf@4.50.1': + resolution: {integrity: sha512-54v4okehwl5TaSIkpp97rAHGp7t3ghinRd/vyC1iXqXMfjYUTm7TfYmCzXDoHUPTTf36L8pr0E7YsD3CfB3ZDg==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm-musleabihf@4.24.3': - resolution: {integrity: sha512-h6Q8MT+e05zP5BxEKz0vi0DhthLdrNEnspdLzkoFqGwnmOzakEHSlXfVyA4HJ322QtFy7biUAVFPvIDEDQa6rw==} + '@rollup/rollup-linux-arm-musleabihf@4.50.1': + resolution: {integrity: sha512-p/LaFyajPN/0PUHjv8TNyxLiA7RwmDoVY3flXHPSzqrGcIp/c2FjwPPP5++u87DGHtw+5kSH5bCJz0mvXngYxw==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm64-gnu@4.24.3': - resolution: {integrity: sha512-fKElSyXhXIJ9pqiYRqisfirIo2Z5pTTve5K438URf08fsypXrEkVmShkSfM8GJ1aUyvjakT+fn2W7Czlpd/0FQ==} + '@rollup/rollup-linux-arm64-gnu@4.50.1': + resolution: {integrity: sha512-2AbMhFFkTo6Ptna1zO7kAXXDLi7H9fGTbVaIq2AAYO7yzcAsuTNWPHhb2aTA6GPiP+JXh85Y8CiS54iZoj4opw==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-arm64-musl@4.24.3': - resolution: {integrity: sha512-YlddZSUk8G0px9/+V9PVilVDC6ydMz7WquxozToozSnfFK6wa6ne1ATUjUvjin09jp34p84milxlY5ikueoenw==} + '@rollup/rollup-linux-arm64-musl@4.50.1': + resolution: {integrity: sha512-Cgef+5aZwuvesQNw9eX7g19FfKX5/pQRIyhoXLCiBOrWopjo7ycfB292TX9MDcDijiuIJlx1IzJz3IoCPfqs9w==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-powerpc64le-gnu@4.24.3': - resolution: {integrity: sha512-yNaWw+GAO8JjVx3s3cMeG5Esz1cKVzz8PkTJSfYzE5u7A+NvGmbVFEHP+BikTIyYWuz0+DX9kaA3pH9Sqxp69g==} + '@rollup/rollup-linux-loongarch64-gnu@4.50.1': + resolution: {integrity: sha512-RPhTwWMzpYYrHrJAS7CmpdtHNKtt2Ueo+BlLBjfZEhYBhK00OsEqM08/7f+eohiF6poe0YRDDd8nAvwtE/Y62Q==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-ppc64-gnu@4.50.1': + resolution: {integrity: sha512-eSGMVQw9iekut62O7eBdbiccRguuDgiPMsw++BVUg+1K7WjZXHOg/YOT9SWMzPZA+w98G+Fa1VqJgHZOHHnY0Q==} cpu: [ppc64] os: [linux] - '@rollup/rollup-linux-riscv64-gnu@4.24.3': - resolution: {integrity: sha512-lWKNQfsbpv14ZCtM/HkjCTm4oWTKTfxPmr7iPfp3AHSqyoTz5AgLemYkWLwOBWc+XxBbrU9SCokZP0WlBZM9lA==} + '@rollup/rollup-linux-riscv64-gnu@4.50.1': + resolution: {integrity: sha512-S208ojx8a4ciIPrLgazF6AgdcNJzQE4+S9rsmOmDJkusvctii+ZvEuIC4v/xFqzbuP8yDjn73oBlNDgF6YGSXQ==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-riscv64-musl@4.50.1': + resolution: {integrity: sha512-3Ag8Ls1ggqkGUvSZWYcdgFwriy2lWo+0QlYgEFra/5JGtAd6C5Hw59oojx1DeqcA2Wds2ayRgvJ4qxVTzCHgzg==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-s390x-gnu@4.24.3': - resolution: {integrity: sha512-HoojGXTC2CgCcq0Woc/dn12wQUlkNyfH0I1ABK4Ni9YXyFQa86Fkt2Q0nqgLfbhkyfQ6003i3qQk9pLh/SpAYw==} + '@rollup/rollup-linux-s390x-gnu@4.50.1': + resolution: {integrity: sha512-t9YrKfaxCYe7l7ldFERE1BRg/4TATxIg+YieHQ966jwvo7ddHJxPj9cNFWLAzhkVsbBvNA4qTbPVNsZKBO4NSg==} cpu: [s390x] os: [linux] - '@rollup/rollup-linux-x64-gnu@4.24.3': - resolution: {integrity: sha512-mnEOh4iE4USSccBOtcrjF5nj+5/zm6NcNhbSEfR3Ot0pxBwvEn5QVUXcuOwwPkapDtGZ6pT02xLoPaNv06w7KQ==} + '@rollup/rollup-linux-x64-gnu@4.50.1': + resolution: {integrity: sha512-MCgtFB2+SVNuQmmjHf+wfI4CMxy3Tk8XjA5Z//A0AKD7QXUYFMQcns91K6dEHBvZPCnhJSyDWLApk40Iq/H3tA==} cpu: [x64] os: [linux] - '@rollup/rollup-linux-x64-musl@4.24.3': - resolution: {integrity: sha512-rMTzawBPimBQkG9NKpNHvquIUTQPzrnPxPbCY1Xt+mFkW7pshvyIS5kYgcf74goxXOQk0CP3EoOC1zcEezKXhw==} + '@rollup/rollup-linux-x64-musl@4.50.1': + resolution: {integrity: sha512-nEvqG+0jeRmqaUMuwzlfMKwcIVffy/9KGbAGyoa26iu6eSngAYQ512bMXuqqPrlTyfqdlB9FVINs93j534UJrg==} cpu: [x64] os: [linux] - '@rollup/rollup-win32-arm64-msvc@4.24.3': - resolution: {integrity: sha512-2lg1CE305xNvnH3SyiKwPVsTVLCg4TmNCF1z7PSHX2uZY2VbUpdkgAllVoISD7JO7zu+YynpWNSKAtOrX3AiuA==} + '@rollup/rollup-openharmony-arm64@4.50.1': + resolution: {integrity: sha512-RDsLm+phmT3MJd9SNxA9MNuEAO/J2fhW8GXk62G/B4G7sLVumNFbRwDL6v5NrESb48k+QMqdGbHgEtfU0LCpbA==} + cpu: [arm64] + os: [openharmony] + + '@rollup/rollup-win32-arm64-msvc@4.50.1': + resolution: {integrity: sha512-hpZB/TImk2FlAFAIsoElM3tLzq57uxnGYwplg6WDyAxbYczSi8O2eQ+H2Lx74504rwKtZ3N2g4bCUkiamzS6TQ==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.24.3': - resolution: {integrity: sha512-9SjYp1sPyxJsPWuhOCX6F4jUMXGbVVd5obVpoVEi8ClZqo52ViZewA6eFz85y8ezuOA+uJMP5A5zo6Oz4S5rVQ==} + '@rollup/rollup-win32-ia32-msvc@4.50.1': + resolution: {integrity: sha512-SXjv8JlbzKM0fTJidX4eVsH+Wmnp0/WcD8gJxIZyR6Gay5Qcsmdbi9zVtnbkGPG8v2vMR1AD06lGWy5FLMcG7A==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.24.3': - resolution: {integrity: sha512-HGZgRFFYrMrP3TJlq58nR1xy8zHKId25vhmm5S9jETEfDf6xybPxsavFTJaufe2zgOGYJBskGlj49CwtEuFhWQ==} + '@rollup/rollup-win32-x64-msvc@4.50.1': + resolution: {integrity: sha512-StxAO/8ts62KZVRAm4JZYq9+NqNsV7RvimNK+YM7ry//zebEH6meuugqW/P5OFUCjyQgui+9fUxT6d5NShvMvA==} cpu: [x64] os: [win32] - '@rushstack/node-core-library@5.9.0': - resolution: {integrity: sha512-MMsshEWkTbXqxqFxD4gcIUWQOCeBChlGczdZbHfqmNZQFLHB3yWxDFSMHFUdu2/OB9NUk7Awn5qRL+rws4HQNg==} + '@rushstack/node-core-library@5.14.0': + resolution: {integrity: sha512-eRong84/rwQUlATGFW3TMTYVyqL1vfW9Lf10PH+mVGfIb9HzU3h5AASNIw+axnBLjnD0n3rT5uQBwu9fvzATrg==} peerDependencies: '@types/node': '*' peerDependenciesMeta: @@ -1270,19 +1314,26 @@ packages: '@rushstack/rig-package@0.5.3': resolution: {integrity: sha512-olzSSjYrvCNxUFZowevC3uz8gvKr3WTpHQ7BkpjtRpA3wK+T0ybep/SRUMfr195gBzJm5gaXw0ZMgjIyHqJUow==} - '@rushstack/terminal@0.14.2': - resolution: {integrity: sha512-2fC1wqu1VCExKC0/L+0noVcFQEXEnoBOtCIex1TOjBzEDWcw8KzJjjj7aTP6mLxepG0XIyn9OufeFb6SFsa+sg==} + '@rushstack/terminal@0.15.4': + resolution: {integrity: sha512-OQSThV0itlwVNHV6thoXiAYZlQh4Fgvie2CzxFABsbO2MWQsI4zOh3LRNigYSTrmS+ba2j0B3EObakPzf/x6Zg==} peerDependencies: '@types/node': '*' peerDependenciesMeta: '@types/node': optional: true - '@rushstack/ts-command-line@4.23.0': - resolution: {integrity: sha512-jYREBtsxduPV6ptNq8jOKp9+yx0ld1Tb/Tkdnlj8gTjazl1sF3DwX2VbluyYrNd0meWIL0bNeer7WDf5tKFjaQ==} + '@rushstack/ts-command-line@5.0.2': + resolution: {integrity: sha512-+AkJDbu1GFMPIU8Sb7TLVXDv/Q7Mkvx+wAjEl8XiXVVq+p1FmWW6M3LYpJMmoHNckSofeMecgWg5lfMwNAAsEQ==} + + '@sec-ant/readable-stream@0.4.1': + resolution: {integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==} + + '@sindresorhus/merge-streams@4.0.0': + resolution: {integrity: sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==} + engines: {node: '>=18'} - '@stylistic/eslint-plugin@2.10.1': - resolution: {integrity: sha512-U+4yzNXElTf9q0kEfnloI9XbOyD4cnEQCxjUI94q0+W++0GAEQvJ/slwEj9lwjDHfGADRSr+Tco/z0XJvmDfCQ==} + '@stylistic/eslint-plugin@2.13.0': + resolution: {integrity: sha512-RnO1SaiCFHn666wNz2QfZEFxvmiNRqhzaMXHXxXXKt+MEP7aajlPxUSMIQpKAaJfverpovEYqjBOXDq6dDcaOQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: '>=8.40.0' @@ -1380,8 +1431,8 @@ packages: '@tauri-apps/plugin-shell@2.3.0': resolution: {integrity: sha512-6GIRxO2z64uxPX4CCTuhQzefvCC0ew7HjdBhMALiGw74vFBDY95VWueAHOHgNOMV4UOUAFupyidN9YulTe5xlA==} - '@tybys/wasm-util@0.9.0': - resolution: {integrity: sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw==} + '@tybys/wasm-util@0.10.0': + resolution: {integrity: sha512-VyyPYFlOMNylG45GoAe0xDoLwWuowvf92F9kySqzYh8vmYm7D2u4iUJKa1tOUpS70Ku13ASrOkS4ScXFsTaCNQ==} '@types/argparse@1.0.38': resolution: {integrity: sha512-ebDJ9b0e702Yr7pWgB0jzm+CX4Srzz8RcXtLJDJB+BSccqMa36uyH/zUsSYao5+BD1ytv3k3rPYCq4mAE1hsXA==} @@ -1392,8 +1443,8 @@ packages: '@types/default-gateway@7.2.2': resolution: {integrity: sha512-35C93fYQlnLKLASkMPoxRvok4fENwB3By9clRLd2I/08n/XRl0pCdf7EB17K5oMMwZu8NBYA8i66jH5r/LYBKA==} - '@types/estree@1.0.6': - resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==} + '@types/estree@1.0.8': + resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} @@ -1410,11 +1461,11 @@ packages: '@types/mdurl@2.0.0': resolution: {integrity: sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==} - '@types/ms@0.7.34': - resolution: {integrity: sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==} + '@types/ms@2.1.0': + resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} - '@types/node@22.8.6': - resolution: {integrity: sha512-tosuJYKrIqjQIlVCM4PEGxOmyg3FCPa/fViuJChnGeEIhjA46oy8FMVoF9su1/v8PNs2a8Q0iFNyOx0uOF91nw==} + '@types/node@22.18.1': + resolution: {integrity: sha512-rzSDyhn4cYznVG+PCzGe1lwuMYJrcBS1fc3JqSa2PvtABwWo+dZ1ij5OVok3tqfpEBCBoaR4d7upFJk73HRJDw==} '@types/normalize-package-data@2.4.4': resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==} @@ -1428,95 +1479,170 @@ packages: '@types/web-bluetooth@0.0.20': resolution: {integrity: sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow==} - '@typescript-eslint/eslint-plugin@8.13.0': - resolution: {integrity: sha512-nQtBLiZYMUPkclSeC3id+x4uVd1SGtHuElTxL++SfP47jR0zfkZBJHc+gL4qPsgTuypz0k8Y2GheaDYn6Gy3rg==} + '@typescript-eslint/eslint-plugin@8.42.0': + resolution: {integrity: sha512-Aq2dPqsQkxHOLfb2OPv43RnIvfj05nw8v/6n3B2NABIPpHnjQnaLo9QGMTvml+tv4korl/Cjfrb/BYhoL8UUTQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - '@typescript-eslint/parser': ^8.0.0 || ^8.0.0-alpha.0 + '@typescript-eslint/parser': ^8.42.0 eslint: ^8.57.0 || ^9.0.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true + typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/parser@8.13.0': - resolution: {integrity: sha512-w0xp+xGg8u/nONcGw1UXAr6cjCPU1w0XVyBs6Zqaj5eLmxkKQAByTdV/uGgNN5tVvN/kKpoQlP2cL7R+ajZZIQ==} + '@typescript-eslint/parser@8.42.0': + resolution: {integrity: sha512-r1XG74QgShUgXph1BYseJ+KZd17bKQib/yF3SR+demvytiRXrwd12Blnz5eYGm8tXaeRdd4x88MlfwldHoudGg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true + typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/scope-manager@7.18.0': - resolution: {integrity: sha512-jjhdIE/FPF2B7Z1uzc6i3oWKbGcHb87Qw7AWj6jmEqNOfDFbJWtjt/XfwCpvNkpGWlcJaog5vTR+VV8+w9JflA==} - engines: {node: ^18.18.0 || >=20.0.0} + '@typescript-eslint/project-service@8.42.0': + resolution: {integrity: sha512-vfVpLHAhbPjilrabtOSNcUDmBboQNrJUiNAGoImkZKnMjs2TIcWG33s4Ds0wY3/50aZmTMqJa6PiwkwezaAklg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/scope-manager@8.13.0': - resolution: {integrity: sha512-XsGWww0odcUT0gJoBZ1DeulY1+jkaHUciUq4jKNv4cpInbvvrtDoyBH9rE/n2V29wQJPk8iCH1wipra9BhmiMA==} + '@typescript-eslint/scope-manager@8.42.0': + resolution: {integrity: sha512-51+x9o78NBAVgQzOPd17DkNTnIzJ8T/O2dmMBLoK9qbY0Gm52XJcdJcCl18ExBMiHo6jPMErUQWUv5RLE51zJw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/type-utils@8.13.0': - resolution: {integrity: sha512-Rqnn6xXTR316fP4D2pohZenJnp+NwQ1mo7/JM+J1LWZENSLkJI8ID8QNtlvFeb0HnFSK94D6q0cnMX6SbE5/vA==} + '@typescript-eslint/tsconfig-utils@8.42.0': + resolution: {integrity: sha512-kHeFUOdwAJfUmYKjR3CLgZSglGHjbNTi1H8sTYRYV2xX6eNz4RyJ2LIgsDLKf8Yi0/GL1WZAC/DgZBeBft8QAQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - - '@typescript-eslint/types@7.18.0': - resolution: {integrity: sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==} - engines: {node: ^18.18.0 || >=20.0.0} + typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/types@8.13.0': - resolution: {integrity: sha512-4cyFErJetFLckcThRUFdReWJjVsPCqyBlJTi6IDEpc1GWCIIZRFxVppjWLIMcQhNGhdWJJRYFHpHoDWvMlDzng==} + '@typescript-eslint/type-utils@8.42.0': + resolution: {integrity: sha512-9KChw92sbPTYVFw3JLRH1ockhyR3zqqn9lQXol3/YbI6jVxzWoGcT3AsAW0mu1MY0gYtsXnUGV/AKpkAj5tVlQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@typescript-eslint/typescript-estree@7.18.0': - resolution: {integrity: sha512-aP1v/BSPnnyhMHts8cf1qQ6Q1IFwwRvAQGRvBFkWlo3/lH29OXA3Pts+c10nxRxIBrDnoMqzhgdwVe5f2D6OzA==} - engines: {node: ^18.18.0 || >=20.0.0} peerDependencies: - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/types@8.42.0': + resolution: {integrity: sha512-LdtAWMiFmbRLNP7JNeY0SqEtJvGMYSzfiWBSmx+VSZ1CH+1zyl8Mmw1TT39OrtsRvIYShjJWzTDMPWZJCpwBlw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/typescript-estree@8.13.0': - resolution: {integrity: sha512-v7SCIGmVsRK2Cy/LTLGN22uea6SaUIlpBcO/gnMGT/7zPtxp90bphcGf4fyrCQl3ZtiBKqVTG32hb668oIYy1g==} + '@typescript-eslint/typescript-estree@8.42.0': + resolution: {integrity: sha512-ku/uYtT4QXY8sl9EDJETD27o3Ewdi72hcXg1ah/kkUgBvAYHLwj2ofswFFNXS+FL5G+AGkxBtvGt8pFBHKlHsQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true + typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/utils@8.13.0': - resolution: {integrity: sha512-A1EeYOND6Uv250nybnLZapeXpYMl8tkzYUxqmoKAWnI4sei3ihf2XdZVd+vVOmHGcp3t+P7yRrNsyyiXTvShFQ==} + '@typescript-eslint/utils@8.42.0': + resolution: {integrity: sha512-JnIzu7H3RH5BrKC4NoZqRfmjqCIS1u3hGZltDYJgkVdqAezl4L9d1ZLw+36huCujtSBSAirGINF/S4UxOcR+/g==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/visitor-keys@7.18.0': - resolution: {integrity: sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg==} - engines: {node: ^18.18.0 || >=20.0.0} - - '@typescript-eslint/visitor-keys@8.13.0': - resolution: {integrity: sha512-7N/+lztJqH4Mrf0lb10R/CbI1EaAMMGyF5y0oJvFoAhafwgiRA7TXyd8TFn8FC8k5y2dTsYogg238qavRGNnlw==} + '@typescript-eslint/visitor-keys@8.42.0': + resolution: {integrity: sha512-3WbiuzoEowaEn8RSnhJBrxSwX8ULYE9CXaPepS2C2W3NSA5NNIvBaslpBSBElPq0UGr0xVJlXFWOAKIkyylydQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@vitejs/plugin-vue@5.1.4': - resolution: {integrity: sha512-N2XSI2n3sQqp5w7Y/AN/L2XDjBIRGqXko+eDp42sydYSBeJuSm5a1sLf8zakmo8u7tA8NmBgoDLA1HeOESjp9A==} + '@unrs/resolver-binding-android-arm-eabi@1.11.1': + resolution: {integrity: sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==} + cpu: [arm] + os: [android] + + '@unrs/resolver-binding-android-arm64@1.11.1': + resolution: {integrity: sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g==} + cpu: [arm64] + os: [android] + + '@unrs/resolver-binding-darwin-arm64@1.11.1': + resolution: {integrity: sha512-gPVA1UjRu1Y/IsB/dQEsp2V1pm44Of6+LWvbLc9SDk1c2KhhDRDBUkQCYVWe6f26uJb3fOK8saWMgtX8IrMk3g==} + cpu: [arm64] + os: [darwin] + + '@unrs/resolver-binding-darwin-x64@1.11.1': + resolution: {integrity: sha512-cFzP7rWKd3lZaCsDze07QX1SC24lO8mPty9vdP+YVa3MGdVgPmFc59317b2ioXtgCMKGiCLxJ4HQs62oz6GfRQ==} + cpu: [x64] + os: [darwin] + + '@unrs/resolver-binding-freebsd-x64@1.11.1': + resolution: {integrity: sha512-fqtGgak3zX4DCB6PFpsH5+Kmt/8CIi4Bry4rb1ho6Av2QHTREM+47y282Uqiu3ZRF5IQioJQ5qWRV6jduA+iGw==} + cpu: [x64] + os: [freebsd] + + '@unrs/resolver-binding-linux-arm-gnueabihf@1.11.1': + resolution: {integrity: sha512-u92mvlcYtp9MRKmP+ZvMmtPN34+/3lMHlyMj7wXJDeXxuM0Vgzz0+PPJNsro1m3IZPYChIkn944wW8TYgGKFHw==} + cpu: [arm] + os: [linux] + + '@unrs/resolver-binding-linux-arm-musleabihf@1.11.1': + resolution: {integrity: sha512-cINaoY2z7LVCrfHkIcmvj7osTOtm6VVT16b5oQdS4beibX2SYBwgYLmqhBjA1t51CarSaBuX5YNsWLjsqfW5Cw==} + cpu: [arm] + os: [linux] + + '@unrs/resolver-binding-linux-arm64-gnu@1.11.1': + resolution: {integrity: sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==} + cpu: [arm64] + os: [linux] + + '@unrs/resolver-binding-linux-arm64-musl@1.11.1': + resolution: {integrity: sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==} + cpu: [arm64] + os: [linux] + + '@unrs/resolver-binding-linux-ppc64-gnu@1.11.1': + resolution: {integrity: sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==} + cpu: [ppc64] + os: [linux] + + '@unrs/resolver-binding-linux-riscv64-gnu@1.11.1': + resolution: {integrity: sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==} + cpu: [riscv64] + os: [linux] + + '@unrs/resolver-binding-linux-riscv64-musl@1.11.1': + resolution: {integrity: sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==} + cpu: [riscv64] + os: [linux] + + '@unrs/resolver-binding-linux-s390x-gnu@1.11.1': + resolution: {integrity: sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==} + cpu: [s390x] + os: [linux] + + '@unrs/resolver-binding-linux-x64-gnu@1.11.1': + resolution: {integrity: sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==} + cpu: [x64] + os: [linux] + + '@unrs/resolver-binding-linux-x64-musl@1.11.1': + resolution: {integrity: sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==} + cpu: [x64] + os: [linux] + + '@unrs/resolver-binding-wasm32-wasi@1.11.1': + resolution: {integrity: sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + + '@unrs/resolver-binding-win32-arm64-msvc@1.11.1': + resolution: {integrity: sha512-nRcz5Il4ln0kMhfL8S3hLkxI85BXs3o8EYoattsJNdsX4YUU89iOkVn7g0VHSRxFuVMdM4Q1jEpIId1Ihim/Uw==} + cpu: [arm64] + os: [win32] + + '@unrs/resolver-binding-win32-ia32-msvc@1.11.1': + resolution: {integrity: sha512-DCEI6t5i1NmAZp6pFonpD5m7i6aFrpofcp4LA2i8IIq60Jyo28hamKBxNrZcyOwVOZkgsRp9O2sXWBWP8MnvIQ==} + cpu: [ia32] + os: [win32] + + '@unrs/resolver-binding-win32-x64-msvc@1.11.1': + resolution: {integrity: sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g==} + cpu: [x64] + os: [win32] + + '@vitejs/plugin-vue@5.2.4': + resolution: {integrity: sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA==} engines: {node: ^18.0.0 || >=20.0.0} peerDependencies: - vite: ^5.0.0 + vite: ^5.0.0 || ^6.0.0 vue: ^3.2.25 - '@vitest/eslint-plugin@1.1.7': - resolution: {integrity: sha512-pTWGW3y6lH2ukCuuffpan6kFxG6nIuoesbhMiQxskyQMRcCN5t9SXsKrNHvEw3p8wcCsgJoRqFZVkOTn6TjclA==} + '@vitest/eslint-plugin@1.3.9': + resolution: {integrity: sha512-wsNe7xy44ovm/h9ISDkDNcv0aOnUsaOYDqan2y6qCFAUQ0odFr6df/+FdGKHZN+mCM+SvIDWoXuvm5T5V3Kh6w==} peerDependencies: - '@typescript-eslint/utils': '>= 8.0' eslint: '>= 8.57.0' typescript: '>= 5.0.0' vitest: '*' @@ -1526,29 +1652,42 @@ packages: vitest: optional: true - '@volar/language-core@2.4.8': - resolution: {integrity: sha512-K/GxMOXGq997bO00cdFhTNuR85xPxj0BEEAy+BaqqayTmy9Tmhfgmq2wpJcVspRhcwfgPoE2/mEJa26emUhG/g==} + '@volar/language-core@2.4.15': + resolution: {integrity: sha512-3VHw+QZU0ZG9IuQmzT68IyN4hZNd9GchGPhbD9+pa8CVv7rnoOZwo7T8weIbrRmihqy3ATpdfXFnqRrfPVK6CA==} + + '@volar/language-core@2.4.23': + resolution: {integrity: sha512-hEEd5ET/oSmBC6pi1j6NaNYRWoAiDhINbT8rmwtINugR39loROSlufGdYMF9TaKGfz+ViGs1Idi3mAhnuPcoGQ==} + + '@volar/source-map@2.4.15': + resolution: {integrity: sha512-CPbMWlUN6hVZJYGcU/GSoHu4EnCHiLaXI9n8c9la6RaI9W5JHX+NqG+GSQcB0JdC2FIBLdZJwGsfKyBB71VlTg==} - '@volar/source-map@2.4.8': - resolution: {integrity: sha512-jeWJBkC/WivdelMwxKkpFL811uH/jJ1kVxa+c7OvG48DXc3VrP7pplSWPP2W1dLMqBxD+awRlg55FQQfiup4cA==} + '@volar/source-map@2.4.23': + resolution: {integrity: sha512-Z1Uc8IB57Lm6k7q6KIDu/p+JWtf3xsXJqAX/5r18hYOTpJyBn0KXUR8oTJ4WFYOcDzWC9n3IflGgHowx6U6z9Q==} - '@volar/typescript@2.4.8': - resolution: {integrity: sha512-6xkIYJ5xxghVBhVywMoPMidDDAFT1OoQeXwa27HSgJ6AiIKRe61RXLoik+14Z7r0JvnblXVsjsRLmCr42SGzqg==} + '@volar/typescript@2.4.15': + resolution: {integrity: sha512-2aZ8i0cqPGjXb4BhkMsPYDkkuc2ZQ6yOpqwAuNwUoncELqoy5fRgOQtLR9gB0g902iS0NAkvpIzs27geVyVdPg==} - '@vue-macros/api@0.13.0': - resolution: {integrity: sha512-BIdiZ6awmyLHlC5VGmU44JqoUb2J+6ED3BT66ICF96Z8474yN5kOjqOsoHvbVIjY/F/peuWLD9BKRFVuj37Nyg==} + '@volar/typescript@2.4.23': + resolution: {integrity: sha512-lAB5zJghWxVPqfcStmAP1ZqQacMpe90UrP5RJ3arDyrhy4aCUQqmxPPLB2PWDKugvylmO41ljK7vZ+t6INMTag==} + + '@vue-macros/api@0.13.4': + resolution: {integrity: sha512-owQSbo1sVzMBZpu8MJ6GiSxwBDMSOgqBIajZj1HOj6U8wTHk/F55X77I02PZi+/TXgGdGSVK2OsiV8dOLgiCcg==} engines: {node: '>=16.14.0'} - '@vue-macros/better-define@1.11.0': - resolution: {integrity: sha512-GcCv/U5cml0/3gG4KKyWldpq5pKMLMRjWmDLMijzZmEnNtqJBQAzshlfLaV+ZRAXydn+FjrQMm4punHuUTIfaA==} + '@vue-macros/better-define@1.11.4': + resolution: {integrity: sha512-0VSKuNHLJTVKUj/eh9PL/BYmbHAJTPKIpCf1iXx1fOjhPExeGKaGZJf1Awk4/Qx8NGVa9xytEZYqKh+cw3r4OA==} engines: {node: '>=16.14.0'} '@vue-macros/boolean-prop@0.5.2': resolution: {integrity: sha512-D3sP0ejVDuksT5IavAq5JeYtBvCRBmVUqjwqFaQ76OzHss3hC8FuN8SJkiAeNBFBtiYHHKpVAXg26CwVvziCmw==} engines: {node: '>=16.14.0'} - '@vue-macros/chain-call@0.4.2': - resolution: {integrity: sha512-HcQCeuGbJnqtyM0ZIM6SoFuK01XlQGRIKAJ2Ty7tWsiB3+V4FKqG8RxOMS29MV+RoLajA47yi1Dd9gYSf4GV0w==} + '@vue-macros/boolean-prop@0.5.5': + resolution: {integrity: sha512-FfsIPefse634+jtqKC4AN3VUZ0OjndWqAlkOepV8h1UQ1pJnPk6DD87HhxGGtDuzOX9cKrMobvGHcPoqidQzMA==} + engines: {node: '>=16.14.0'} + + '@vue-macros/chain-call@0.4.5': + resolution: {integrity: sha512-5Fpt0malmMuO4aL6sO5F16EJ2pW+kqwZHLEWDHDPgCH7zWvpH2NbeEauu0HPPImD2Ym+9d+YaEM0CULYMrPNyQ==} engines: {node: '>=16.14.0'} '@vue-macros/common@1.15.0': @@ -1560,18 +1699,31 @@ packages: vue: optional: true + '@vue-macros/common@1.16.1': + resolution: {integrity: sha512-Pn/AWMTjoMYuquepLZP813BIcq8DTZiNCoaceuNlvaYuOTd8DqBZWc5u0uOMQZMInwME1mdSmmBAcTluiV9Jtg==} + engines: {node: '>=16.14.0'} + peerDependencies: + vue: ^2.7.0 || ^3.2.25 + peerDependenciesMeta: + vue: + optional: true + '@vue-macros/config@0.5.0': resolution: {integrity: sha512-wJcqHU71ZNXu4C4k/zmR9e4l0EaF08gnTd2YxZQqSYdV9ltAaqip22gp1R03X/Yz1/u/R+/yJap1s5jy8Zew5A==} engines: {node: '>=16.14.0'} - '@vue-macros/define-emit@0.5.1': - resolution: {integrity: sha512-4j/HcxPMhpKgXrRZQR/aRDnRWHgpI8ZBrtVOi84e8jq1VTFwaKytjCHzk89S0/KQjj1NcrulmjjPR8q9KfP1lQ==} + '@vue-macros/config@0.6.1': + resolution: {integrity: sha512-iQ1+QpgcvqCcgzRuoK46L1C1Z29hXVq8Zb90Mryfizafkl2dxfUqBQV6AytV7+jhCIjJPtN2laGIRownNti8+Q==} + engines: {node: '>=16.14.0'} + + '@vue-macros/define-emit@0.5.4': + resolution: {integrity: sha512-LBRiBOfaGrRlCdiicVkbSRVzriabrHfF7NDf8g2FT2WSl4vXXKXEDGj5qvG7WCbDTVClDmUBPreOx/zeKIMmdg==} engines: {node: '>=16.14.0'} peerDependencies: vue: ^2.7.0 || ^3.2.25 - '@vue-macros/define-models@1.3.2': - resolution: {integrity: sha512-pIzIDFjvLEfv/j1cR/gms1HCYNv/m9pJb3x9Q2ji8UVTTYYOoYwJFcxEM9xsOnSNtmR/u3SuGqv00OrhdE7E9g==} + '@vue-macros/define-models@1.3.5': + resolution: {integrity: sha512-XFUG498vLmzavLHYmZdiFKT+cN5bYDuVEOfG4hsVAdOoflGqBcRhZmnr9b2M/Y90olULq8AZY7xSnWx9Vqyerw==} engines: {node: '>=16.14.0'} peerDependencies: '@vueuse/core': '>=9.0.0' @@ -1579,116 +1731,133 @@ packages: '@vueuse/core': optional: true - '@vue-macros/define-prop@0.6.1': - resolution: {integrity: sha512-BIgyJScNQ/UptdLChqn5j177web4JFlhRTil+BFVhnKfVhi6CwMSZHhMVTSoKGJ1WwhUJA1l2LjxmaW+dlPNEA==} + '@vue-macros/define-prop@0.6.5': + resolution: {integrity: sha512-9/xJHCvuAYBe77qPXdjOENa0KUweKpUWpUSYul8COPreOqKKVULCxeKFM9zv9ervlpT5g9s4JD83tm7dIV9+NQ==} engines: {node: '>=16.14.0'} peerDependencies: vue: ^2.7.0 || ^3.2.25 - '@vue-macros/define-props-refs@1.3.2': - resolution: {integrity: sha512-njE4Tu2RiI2XbJqWvNt4/fpCoc4EUwFeqxHgltt1EXRq7qQmUB2KrzWxjGdhJwPsf5WtuMQ8o/GaIgW2/dtSTQ==} + '@vue-macros/define-props-refs@1.3.5': + resolution: {integrity: sha512-DpvGrIsjM+BGbtkadJspKq3Y2oa/ryXghx3N/VZ4AvnKDmBFTRBG9epU6NKoKJNTvXq87232qv2PTfrT3S5xQQ==} engines: {node: '>=16.14.0'} peerDependencies: vue: ^2.7.0 || ^3.2.25 - '@vue-macros/define-props@4.0.3': - resolution: {integrity: sha512-zPACrwxkNIi+lPjRsPoFWnUXxsZIFMLF2yOPnrBpzTWe4sAaqOkc/TtQUW9fsyrnUTGWhC5ggy6bK6njIrh8vg==} + '@vue-macros/define-props@4.0.6': + resolution: {integrity: sha512-cfFg84z9/qa0HNpkubERQOcBBkLo2Y9RpI8BXq/tl4gceuR6++ycIgqZZMSxoaLdet0VnDv+CMRz3yHGVSClKw==} engines: {node: '>=16.14.0'} peerDependencies: - '@vue-macros/reactivity-transform': ^1.1.3 + '@vue-macros/reactivity-transform': ^1.1.6 vue: ^2.7.0 || ^3.2.25 - '@vue-macros/define-render@1.6.2': - resolution: {integrity: sha512-hgE13SjZJg4Q2HDt4FEj0p6ORkI1mRFRwOxTMzOKH7oV3YnT5VFDUPxz+xMD6Y2rNSlnXy2pf/f1/dJ6XRGRqQ==} + '@vue-macros/define-render@1.6.6': + resolution: {integrity: sha512-EIc1mZ+SJ8eohtLYSzHU4zlGqOZDPYqCIaRUutwIL6EAcIv0/GskO6s3gZzrnrA0K8fNj1AwBWjXktO4p6RcgQ==} engines: {node: '>=16.14.0'} peerDependencies: vue: ^2.7.0 || ^3.0.0 - '@vue-macros/define-slots@1.2.2': - resolution: {integrity: sha512-4JoOIJxBWDbeqT9QSwQokivftxUBQBqWc5UJj4uoeJ7KXoarq9Hl4ixaPG3fsCKzJyrmiWYH6Re83j3NCBoD5w==} + '@vue-macros/define-slots@1.2.6': + resolution: {integrity: sha512-2IFysgXkKVMJqRm6lXEiamB5DBFMcEZBKVXU0s+CRLnN6CJ4kN0oOLlaHyNhe0Dj/jtBVCriDqeIT25AQA3bDQ==} engines: {node: '>=16.14.0'} peerDependencies: vue: ^2.7.0 || ^3.0.0 - '@vue-macros/define-stylex@0.2.0': - resolution: {integrity: sha512-GCdtIeIo87mRKpfgC8Z1Wgyis0d8zyv1xsrz2eJAS4bLmm+zDxjopBTOWpnJQpwJh599ExkYQj1L4eMEE1/xAw==} + '@vue-macros/define-stylex@0.2.3': + resolution: {integrity: sha512-UDFK7k4yHuJI9umUrjMbfM9jNUZamV5nlnSXRORz0wA2ybbQ5MbjEPAviwAlvKmy/I+rWL5dbLD8QdpHoTkBPQ==} engines: {node: '>=16.14.0'} - '@vue-macros/devtools@0.4.0': - resolution: {integrity: sha512-767WYNXNZqyarp92FkcSGxk5twi1S8QqmwG8UiplIExzSAG2tA2Hria/MQP4vth9/gh8hjekib6ipOjoCDZUpw==} + '@vue-macros/devtools@0.4.1': + resolution: {integrity: sha512-bsNFXYZpLT6wiqBiJ5Ej4n76b/mV/S6y+R9Djd3r9smr7BneYcNtYuIFZU3BeQKP6+Zb+QEXPvp7jWhM4nQG+w==} engines: {node: '>=16.14.0'} peerDependencies: - vite: ^4.0.0 || ^5.0.0-0 + vite: ^4.0.0 || ^5.0.0-0 || ^6.0.0 peerDependenciesMeta: vite: optional: true - '@vue-macros/export-expose@0.3.2': - resolution: {integrity: sha512-ESI6nxyv4TLXrA6UznfJVGi1rer8q1EBZRc3vtg0u5jyZFhB6W+rXlxVL6NJ1l6WkRizNkRQSW4mRDNj0qBTbA==} + '@vue-macros/export-expose@0.3.5': + resolution: {integrity: sha512-X84DWs0vhnPrM1zVIhHNtS2hAPJcSLGVzpdfJwPtW2L3FqVj25/9cW3UBV6Oa6pt+0+upZUwgxftOA5Tn4Dmjw==} engines: {node: '>=16.14.0'} peerDependencies: vue: ^2.7.0 || ^3.2.25 - '@vue-macros/export-props@0.6.2': - resolution: {integrity: sha512-eqqQCZ9lD00NNUPOWmerUE5LX8PB44LKpP1ufrHGGZqllRowLynT95uFHZidz9hNz+ocp8wyecngUqQSmrvVEg==} + '@vue-macros/export-props@0.6.5': + resolution: {integrity: sha512-NfHl526bVRRPX1sIaSdnCU81Tne0tqqCiSlvxZsiRKwKkI/eudF8EDqVOzPu9jtXbsZxtT331XdBjPFxjRlapA==} engines: {node: '>=16.14.0'} peerDependencies: vue: ^2.7.0 || ^3.2.25 - '@vue-macros/export-render@0.3.2': - resolution: {integrity: sha512-M3+T32HSLIoCXpJkyolA8HgLiKGKvxRt0zIP4pu4wKWNKsLIHpaeVh5c5K+wq2GBsvoninZZbmEEiHvZP6zaNg==} + '@vue-macros/export-render@0.3.5': + resolution: {integrity: sha512-OQGLrYEVNS2daouty2yM1mnz6fduiE0swpsRhrWf6aEBbT3kqkgT+hSBgBoVBrjRaLJVm6WO0sNQXqQeXQGgJQ==} engines: {node: '>=16.14.0'} peerDependencies: vue: ^2.7.0 || ^3.2.25 - '@vue-macros/hoist-static@1.6.2': - resolution: {integrity: sha512-B1xmM1/PCQeIq45Q50uDcmG1e7zsMGanKS+BePY0FT8SHu9/DD39eb7d3FoDKvkPhRy7ooEAtzSiv7KwWZKUXw==} + '@vue-macros/hoist-static@1.7.0': + resolution: {integrity: sha512-qIKU0xLzZ4Woo5JfLR6eZwiCj/QXee7GmGqVPZquR5Nrnbf5PvkAJeirX3Wlizjgvg+snmkz1dOg+80qcYooTQ==} engines: {node: '>=16.14.0'} - '@vue-macros/jsx-directive@0.9.2': - resolution: {integrity: sha512-6triZEaIuqTTTcUQ49YtGmIkMxCXDrCLkC2DvAZBnN1RgWxhRuDNi9xC/z/haMMDN9UyvwQB9OHtFISasdgl1g==} + '@vue-macros/jsx-directive@0.10.6': + resolution: {integrity: sha512-I7vfvd5sWxlnWYUpHLRrpfs4S6Piz5Ef+zlFRdfqZRq00KiUWJd/m//Xv0vd8ORR3CEu6bbQVDXXxVGh+2mhKQ==} engines: {node: '>=16.14.0'} - '@vue-macros/named-template@0.5.2': - resolution: {integrity: sha512-tM8tPpSWVf+VmqLyuZnmEMX0GqL89FqcNPUJOhwNkABkpJylQ+0fON35Gk7nnUh2NJ2ksiRguS/5Ru1OferR4Q==} + '@vue-macros/named-template@0.5.5': + resolution: {integrity: sha512-wKPxZC3wqUpahGat9bFpIzZOrzrsh7P7Evz5IAZjIsv25HzzFlxN6Lmd7WGn2XXBjV1ZAUsMlCtmCBlIxX8RzQ==} engines: {node: '>=16.14.0'} - '@vue-macros/reactivity-transform@1.1.3': - resolution: {integrity: sha512-qPqwTv1ZugoDUE7+bpkWl0cdSSTBAT+ogvIUVaNsd+bjWBpdnAC2nEb/HPQeJ/tlXICxbZTCykp8fa002WSVJQ==} + '@vue-macros/reactivity-transform@1.1.6': + resolution: {integrity: sha512-yicxeIdSuV9IXFCbRwHbM7hy4yUB5qYXf8dxvm/ITE3vhZkVV7omLoQPdUA0zGc/ldSwXfYL3Ul3xnms7EBiCQ==} engines: {node: '>=16.14.0'} peerDependencies: vue: ^2.7.0 || ^3.2.25 - '@vue-macros/script-lang@0.2.2': - resolution: {integrity: sha512-w9vNXE8jnzIT98DPynyJToAmZx2b/J3KidmNx5X/TrNyNrGUuLaOkbZBaNyk2V5NGNTNqNdt0E8m+DH6JbZZ1Q==} + '@vue-macros/script-lang@0.2.5': + resolution: {integrity: sha512-2twUdHbDRT1wm1zF8kem04D0MXWHd5+OHP/5hy8zb2g0QfXWTOQSlq/n9Xh1fO/XWYpaipKV8XMOKehfqfHtjg==} engines: {node: '>=16.14.0'} peerDependencies: vue: ^2.7.0 || ^3.0.0 - '@vue-macros/setup-block@0.4.2': - resolution: {integrity: sha512-Pr+mOOJZc3su3TO4F43gZyzuG1oqSDlNOs02kjM6G0/H8O/6iFcdWAIBesbEPu+KT84OO02SlOAvH0v9foisiw==} + '@vue-macros/setup-block@0.4.5': + resolution: {integrity: sha512-xmAHTwYu9igrwuUrKgN4CckGeR9aaIgjmylOdaVtg28ZPxhbQ+VDLnYlN3tabOoHFeZD/L7CCA6z+fZGuLcsCw==} engines: {node: '>=16.14.0'} - '@vue-macros/setup-component@0.18.2': - resolution: {integrity: sha512-254cJ4uRAoMjNyvYNdvEzaRguoZX5+k2HqhGtq70/lyabP2RdvMmJNEtWaNCgLxwKormogTnHyxeCZl8DauwOQ==} + '@vue-macros/setup-component@0.18.5': + resolution: {integrity: sha512-Op1IIQX+AthQ5SSmm26DbZEeXpiFQfwi0vi8nwkAq24C6WlBLv/QUqsnq+D2o/0+t1sCDzLHPY5Y5oZpxu9FLw==} engines: {node: '>=16.14.0'} - '@vue-macros/setup-sfc@0.18.2': - resolution: {integrity: sha512-uRK2iUECpgwD2Dr1g7cPG4NK10/Th5qZoL50lKzuA+EYw2pC45h6AELre29bE4Ft0vhDGgxgo4UfOUmi/I8oWQ==} + '@vue-macros/setup-sfc@0.18.5': + resolution: {integrity: sha512-J4M2qXOOb1jeeShq6WpC4LRngLP3/SAQdOK8XxioaILe/UCIuty6QWjxbmoz4im6Ol1pNS44dQNKa5gOsxUusg==} engines: {node: '>=16.14.0'} '@vue-macros/short-bind@1.1.2': resolution: {integrity: sha512-qfZx6SU1lDo9/Ala/6B9Z2K+++0NIQd/4PiV9flu7FvJItdc9ypBlYPnK6RUNJiFP8BeHU6rjq1b6n7GH9qVrA==} engines: {node: '>=16.14.0'} - '@vue-macros/short-emits@1.6.2': - resolution: {integrity: sha512-FdlLVvrtjmHqkR80ZrXjjfkCSDk6HcSqXRpIJw1nNx45YTqfMQrSyGxKR/3BJZGBq70Rh5GdJuNmfmFeMe1IDQ==} + '@vue-macros/short-bind@1.1.5': + resolution: {integrity: sha512-PSm30G05Asa6hLrGN90D3yWquCCEYupZ2eq7TVP0F/DVlRHYBn5vjngOcU3jdTSqRdeMLoqzFRr7G6nzqtiPcQ==} + engines: {node: '>=16.14.0'} + + '@vue-macros/short-emits@1.6.5': + resolution: {integrity: sha512-o1fAnavDmybqBxp5uwqMEBHOLmjdHTdH8nKYNLegZwUGhYpRmLsVdq6dSWkGOGDodwCnqc1I/tfFIFdQPkgcLA==} engines: {node: '>=16.14.0'} '@vue-macros/short-vmodel@1.5.2': resolution: {integrity: sha512-lhNTAfrHCcA3Wdh05L6eTH+fdq4g1lMkDwKVwal6wUEz/Z1YLhTxNmEwNbHFWtKRlcY7APmNPMrJ9ktwJKjm3g==} engines: {node: '>=16.14.0'} + '@vue-macros/short-vmodel@1.5.5': + resolution: {integrity: sha512-EYEf0f3QU8csOxgBsGiu4tOblOnBKiLFiYaZ3g72ER+6PwJ7kF2fLhHwdA6H/4RL+VEpSOFSTAazpZa4lCed+Q==} + engines: {node: '>=16.14.0'} + + '@vue-macros/volar@0.30.15': + resolution: {integrity: sha512-CU2/XTH1Md06bpE+Opc8LDnY9t06tX8V2daZTWemsNb2NxxzRE+5Xj+EUGR/pG3R9dDXAZ7kQfERiIgO+dAb8w==} + engines: {node: '>=16.14.0'} + peerDependencies: + vue-tsc: 2.1.10 + peerDependenciesMeta: + vue-tsc: + optional: true + '@vue-macros/volar@0.30.5': resolution: {integrity: sha512-uK6Buuypg3x2S135w4S82jURSMMEKHqgj9F+sZ/mLDr7XjfFr4ynwlTM4zpuERHupNQGlqzE0UN3nIIs2owW0g==} engines: {node: '>=16.14.0'} @@ -1698,33 +1867,33 @@ packages: vue-tsc: optional: true - '@vue/babel-helper-vue-transform-on@1.2.5': - resolution: {integrity: sha512-lOz4t39ZdmU4DJAa2hwPYmKc8EsuGa2U0L9KaZaOJUt0UwQNjNA3AZTq6uEivhOKhhG1Wvy96SvYBoFmCg3uuw==} + '@vue/babel-helper-vue-transform-on@1.5.0': + resolution: {integrity: sha512-0dAYkerNhhHutHZ34JtTl2czVQHUNWv6xEbkdF5W+Yrv5pCWsqjeORdOgbtW2I9gWlt+wBmVn+ttqN9ZxR5tzA==} - '@vue/babel-plugin-jsx@1.2.5': - resolution: {integrity: sha512-zTrNmOd4939H9KsRIGmmzn3q2zvv1mjxkYZHgqHZgDrXz5B1Q3WyGEjO2f+JrmKghvl1JIRcvo63LgM1kH5zFg==} + '@vue/babel-plugin-jsx@1.5.0': + resolution: {integrity: sha512-mneBhw1oOqCd2247O0Yw/mRwC9jIGACAJUlawkmMBiNmL4dGA2eMzuNZVNqOUfYTa6vqmND4CtOPzmEEEqLKFw==} peerDependencies: '@babel/core': ^7.0.0-0 peerDependenciesMeta: '@babel/core': optional: true - '@vue/babel-plugin-resolve-type@1.2.5': - resolution: {integrity: sha512-U/ibkQrf5sx0XXRnUZD1mo5F7PkpKyTbfXM3a3rC4YnUz6crHEz9Jg09jzzL6QYlXNto/9CePdOg/c87O4Nlfg==} + '@vue/babel-plugin-resolve-type@1.5.0': + resolution: {integrity: sha512-Wm/60o+53JwJODm4Knz47dxJnLDJ9FnKnGZJbUUf8nQRAtt6P+undLUAVU3Ha33LxOJe6IPoifRQ6F/0RrU31w==} peerDependencies: '@babel/core': ^7.0.0-0 - '@vue/compiler-core@3.5.12': - resolution: {integrity: sha512-ISyBTRMmMYagUxhcpyEH0hpXRd/KqDU4ymofPgl2XAkY9ZhQ+h0ovEZJIiPop13UmR/54oA2cgMDjgroRelaEw==} + '@vue/compiler-core@3.5.21': + resolution: {integrity: sha512-8i+LZ0vf6ZgII5Z9XmUvrCyEzocvWT+TeR2VBUVlzIH6Tyv57E20mPZ1bCS+tbejgUgmjrEh7q/0F0bibskAmw==} - '@vue/compiler-dom@3.5.12': - resolution: {integrity: sha512-9G6PbJ03uwxLHKQ3P42cMTi85lDRvGLB2rSGOiQqtXELat6uI4n8cNz9yjfVHRPIu+MsK6TE418Giruvgptckg==} + '@vue/compiler-dom@3.5.21': + resolution: {integrity: sha512-jNtbu/u97wiyEBJlJ9kmdw7tAr5Vy0Aj5CgQmo+6pxWNQhXZDPsRr1UWPN4v3Zf82s2H3kF51IbzZ4jMWAgPlQ==} - '@vue/compiler-sfc@3.5.12': - resolution: {integrity: sha512-2k973OGo2JuAa5+ZlekuQJtitI5CgLMOwgl94BzMCsKZCX/xiqzJYzapl4opFogKHqwJk34vfsaKpfEhd1k5nw==} + '@vue/compiler-sfc@3.5.21': + resolution: {integrity: sha512-SXlyk6I5eUGBd2v8Ie7tF6ADHE9kCR6mBEuPyH1nUZ0h6Xx6nZI29i12sJKQmzbDyr2tUHMhhTt51Z6blbkTTQ==} - '@vue/compiler-ssr@3.5.12': - resolution: {integrity: sha512-eLwc7v6bfGBSM7wZOGPmRavSWzNFF6+PdRhE+VFJhNCgHiF8AM7ccoqcv5kBXA2eWUfigD7byekvf/JsOfKvPA==} + '@vue/compiler-ssr@3.5.21': + resolution: {integrity: sha512-vKQ5olH5edFZdf5ZrlEgSO1j1DMA4u23TVK5XR1uMhvwnYvVdDF0nHXJUblL/GvzlShQbjhZZ2uvYmDlAbgo9w==} '@vue/compiler-vue2@2.7.16': resolution: {integrity: sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A==} @@ -1732,16 +1901,16 @@ packages: '@vue/devtools-api@6.6.4': resolution: {integrity: sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==} - '@vue/devtools-core@7.6.3': - resolution: {integrity: sha512-C7FOuh3Z+EmXXzDU9eRjHQL7zW7/CFovM6yCNNpUb+zXxhrn4fiqTum+a3gNau9DuzYfEtQXwZ9F7MeK0JKYVw==} + '@vue/devtools-core@7.7.7': + resolution: {integrity: sha512-9z9TLbfC+AjAi1PQyWX+OErjIaJmdFlbDHcD+cAMYKY6Bh5VlsAtCeGyRMrXwIlMEQPukvnWt3gZBLwTAIMKzQ==} peerDependencies: vue: ^3.0.0 - '@vue/devtools-kit@7.6.3': - resolution: {integrity: sha512-ETsFc8GlOp04rSFN79tB2TpVloWfsSx9BoCSElV3w3CaJTSBfz42KsIi5Ka+dNTJs1jY7QVLTDeoBmUGgA9h2A==} + '@vue/devtools-kit@7.7.7': + resolution: {integrity: sha512-wgoZtxcTta65cnZ1Q6MbAfePVFxfM+gq0saaeytoph7nEa7yMXoi6sCPy4ufO111B9msnw0VOWjPEFCXuAKRHA==} - '@vue/devtools-shared@7.6.3': - resolution: {integrity: sha512-wJW5QF27i16+sNQIaes8QoEZg1eqEgF83GkiPUlEQe9k7ZoHXHV7PRrnrxOKem42sIHPU813J2V/ZK1uqTJe6g==} + '@vue/devtools-shared@7.7.7': + resolution: {integrity: sha512-+udSj47aRl5aKb0memBvcUG9koarqnxNM5yjuREvqwK6T3ap4mn3Zqqc17QrBFTqSMjr3HK1cvStEZpMDpfdyw==} '@vue/language-core@2.1.10': resolution: {integrity: sha512-DAI289d0K3AB5TUG3xDp9OuQ71CnrujQwJrQnfuZDwo6eGNf0UoRlPuaVNO+Zrn65PC3j0oB2i7mNmVPggeGeQ==} @@ -1759,39 +1928,55 @@ packages: typescript: optional: true - '@vue/reactivity@3.5.12': - resolution: {integrity: sha512-UzaN3Da7xnJXdz4Okb/BGbAaomRHc3RdoWqTzlvd9+WBR5m3J39J1fGcHes7U3za0ruYn/iYy/a1euhMEHvTAg==} + '@vue/language-core@2.2.0': + resolution: {integrity: sha512-O1ZZFaaBGkKbsRfnVH1ifOK1/1BUkyK+3SQsfnh6PmMmD4qJcTU8godCeA96jjDRTL6zgnK7YzCHfaUlH2r0Mw==} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@vue/language-core@2.2.12': + resolution: {integrity: sha512-IsGljWbKGU1MZpBPN+BvPAdr55YPkj2nB/TBNGNC32Vy2qLG25DYu/NBN2vNtZqdRbTRjaoYrahLrToim2NanA==} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@vue/reactivity@3.5.21': + resolution: {integrity: sha512-3ah7sa+Cwr9iiYEERt9JfZKPw4A2UlbY8RbbnH2mGCE8NwHkhmlZt2VsH0oDA3P08X3jJd29ohBDtX+TbD9AsA==} - '@vue/runtime-core@3.5.12': - resolution: {integrity: sha512-hrMUYV6tpocr3TL3Ad8DqxOdpDe4zuQY4HPY3X/VRh+L2myQO8MFXPAMarIOSGNu0bFAjh1yBkMPXZBqCk62Uw==} + '@vue/runtime-core@3.5.21': + resolution: {integrity: sha512-+DplQlRS4MXfIf9gfD1BOJpk5RSyGgGXD/R+cumhe8jdjUcq/qlxDawQlSI8hCKupBlvM+3eS1se5xW+SuNAwA==} - '@vue/runtime-dom@3.5.12': - resolution: {integrity: sha512-q8VFxR9A2MRfBr6/55Q3umyoN7ya836FzRXajPB6/Vvuv0zOPL+qltd9rIMzG/DbRLAIlREmnLsplEF/kotXKA==} + '@vue/runtime-dom@3.5.21': + resolution: {integrity: sha512-3M2DZsOFwM5qI15wrMmNF5RJe1+ARijt2HM3TbzBbPSuBHOQpoidE+Pa+XEaVN+czbHf81ETRoG1ltztP2em8w==} - '@vue/server-renderer@3.5.12': - resolution: {integrity: sha512-I3QoeDDeEPZm8yR28JtY+rk880Oqmj43hreIBVTicisFTx/Dl7JpG72g/X7YF8hnQD3IFhkky5i2bPonwrTVPg==} + '@vue/server-renderer@3.5.21': + resolution: {integrity: sha512-qr8AqgD3DJPJcGvLcJKQo2tAc8OnXRcfxhOJCPF+fcfn5bBGz7VCcO7t+qETOPxpWK1mgysXvVT/j+xWaHeMWA==} peerDependencies: - vue: 3.5.12 + vue: 3.5.21 - '@vue/shared@3.5.12': - resolution: {integrity: sha512-L2RPSAwUFbgZH20etwrXyVyCBu9OxRSi8T/38QsvnkJyvq2LufW2lDCOzm7t/U9C1mkhJGWYfCuFBCmIuNivrg==} + '@vue/shared@3.5.21': + resolution: {integrity: sha512-+2k1EQpnYuVuu3N7atWyG3/xoFWIVJZq4Mz8XNOdScFI0etES75fbny/oU4lKWk/577P1zmg0ioYvpGEDZ3DLw==} - '@vueuse/core@11.2.0': - resolution: {integrity: sha512-JIUwRcOqOWzcdu1dGlfW04kaJhW3EXnnjJJfLTtddJanymTL7lF1C0+dVVZ/siLfc73mWn+cGP1PE1PKPruRSA==} + '@vueuse/core@11.3.0': + resolution: {integrity: sha512-7OC4Rl1f9G8IT6rUfi9JrKiXy4bfmHhZ5x2Ceojy0jnd3mHNEvV4JaRygH362ror6/NZ+Nl+n13LPzGiPN8cKA==} - '@vueuse/metadata@11.2.0': - resolution: {integrity: sha512-L0ZmtRmNx+ZW95DmrgD6vn484gSpVeRbgpWevFKXwqqQxW9hnSi2Ppuh2BzMjnbv4aJRiIw8tQatXT9uOB23dQ==} + '@vueuse/metadata@11.3.0': + resolution: {integrity: sha512-pwDnDspTqtTo2HwfLw4Rp6yywuuBdYnPYDq+mO38ZYKGebCUQC/nVj/PXSiK9HX5otxLz8Fn7ECPbjiRz2CC3g==} - '@vueuse/shared@11.2.0': - resolution: {integrity: sha512-VxFjie0EanOudYSgMErxXfq6fo8vhr5ICI+BuE3I9FnX7ePllEsVrRQ7O6Q1TLgApeLuPKcHQxAXpP+KnlrJsg==} + '@vueuse/shared@11.3.0': + resolution: {integrity: sha512-P8gSSWQeucH5821ek2mn/ciCk+MS/zoRKqdQIM3bHq6p7GXDAJLmnRRKmF5F65sAVJIfzQlwR3aDzwCn10s8hA==} acorn-jsx@5.3.2: resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 - acorn@8.14.0: - resolution: {integrity: sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==} + acorn@8.15.0: + resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} engines: {node: '>=0.4.0'} hasBin: true @@ -1820,15 +2005,21 @@ packages: ajv@8.13.0: resolution: {integrity: sha512-PRA911Blj99jR5RMeTunVbNXMF6Lp4vZXnk5GQjcnUWUTsrXtekg/pnmFFI2u/I36Y/2bITGS30GZCXei6uNkA==} - alien-signals@0.2.0: - resolution: {integrity: sha512-StlonZhBBrsPPwrDjiPAiVTf/rolxffLxVPT60Qv/t88BZ81BvUVzHgGqEFvJ1ii8HXtm1+zU2Icr59tfWEcag==} + alien-signals@0.2.2: + resolution: {integrity: sha512-cZIRkbERILsBOXTQmMrxc9hgpxglstn69zm+F1ARf4aPAzdAFYd6sBq87ErO0Fj3DV94tglcyHG5kQz9nDC/8A==} + + alien-signals@0.4.14: + resolution: {integrity: sha512-itUAVzhczTmP2U5yX67xVpsbbOiquusbWVyA9N+sy6+r6YVbFkahXvNCeEPWEOMhwDYwbVbGHFkVL03N9I5g+Q==} + + alien-signals@1.0.13: + resolution: {integrity: sha512-OGj9yyTnJEttvzhTUWuscOvtqxq5vrhF7vL9oS0xJ2mK0ItPYP1/y+vCFebfxoEyAz0++1AIwJ5CMr+Fk3nDmg==} ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} - ansi-regex@6.1.0: - resolution: {integrity: sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==} + ansi-regex@6.2.0: + resolution: {integrity: sha512-TKY5pyBkHyADOPYlRT9Lx6F544mPl0vS5Ew7BJ45hA08Q+t3GjbueLliBWN3sMICk6+y7HdyxSzC4bWS8baBdg==} engines: {node: '>=12'} ansi-styles@4.3.0: @@ -1859,12 +2050,8 @@ packages: argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} - array-union@2.1.0: - resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} - engines: {node: '>=8'} - - ast-kit@1.3.1: - resolution: {integrity: sha512-3bIRV4s/cNAee2rKjuvYdoG+0CMqtOIgCvWrJL6zG8R0fDyMwYzStspX5JqXPbdMzM+qxHZ6g2rMHKhr3HkPlQ==} + ast-kit@1.4.3: + resolution: {integrity: sha512-MdJqjpodkS5J149zN0Po+HPshkTdUyrvF7CKTafUgv69vBSPtncrj+3IiUgqdd7ElIEkbeXCsEouBUwLrw9Ilg==} engines: {node: '>=16.14.0'} ast-walker-scope@0.6.2: @@ -1874,15 +2061,15 @@ packages: asynckit@0.4.0: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} - autoprefixer@10.4.20: - resolution: {integrity: sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==} + autoprefixer@10.4.21: + resolution: {integrity: sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==} engines: {node: ^10 || ^12 || >=14} hasBin: true peerDependencies: postcss: ^8.1.0 - axios@1.7.7: - resolution: {integrity: sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==} + axios@1.11.0: + resolution: {integrity: sha512-1Lx3WLFQWm3ooKDYZD1eXmoGO9fxYQjrycfHFC8P0sCfQVXyROp0p9PFWBehewBOdCwHc+f/b8I0fMto5eSfwA==} balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} @@ -1891,24 +2078,24 @@ packages: resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} engines: {node: '>=8'} - birpc@0.2.19: - resolution: {integrity: sha512-5WeXXAvTmitV1RqJFppT5QtUiz2p1mRSYU000Jkft5ZUCLJIk4uQriYNO50HknxKwM6jd8utNc66K1qGIwwWBQ==} + birpc@2.5.0: + resolution: {integrity: sha512-VSWO/W6nNQdyP520F1mhf+Lc2f8pjGQOtoHHm7Ze8Go1kX7akpVIrtTa0fn+HB0QJEDVacl6aO08YE0PgXfdnQ==} boolbase@1.0.0: resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} - brace-expansion@1.1.11: - resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + brace-expansion@1.1.12: + resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} - brace-expansion@2.0.1: - resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} + brace-expansion@2.0.2: + resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} braces@3.0.3: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} - browserslist@4.24.2: - resolution: {integrity: sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg==} + browserslist@4.25.4: + resolution: {integrity: sha512-4jYpcjabC606xJ3kw2QwGEZKX0Aw7sgQdZCvIK9dhVSPh76BKo+C+btT1RRofH7B+8iNpEbgGNVWiLki5q93yg==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true @@ -1920,12 +2107,16 @@ packages: resolution: {integrity: sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==} engines: {node: '>=18'} - bundle-require@5.0.0: - resolution: {integrity: sha512-GuziW3fSSmopcx4KRymQEJVbZUfqlCqcq7dvs6TYwKRZiegK/2buMxQTPs6MGlNv50wms1699qYO54R8XfRX4w==} + bundle-require@5.1.0: + resolution: {integrity: sha512-3WrrOuZiyaaZPWiEt4G3+IffISVC9HYlWueJEBWED4ZH4aIAC2PnkdnuRrR94M+w6yGWn4AglWtJtBI8YqvgoA==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} peerDependencies: esbuild: '>=0.18' + call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} + callsites@3.1.0: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} @@ -1934,8 +2125,8 @@ packages: resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==} engines: {node: '>= 6'} - caniuse-lite@1.0.30001677: - resolution: {integrity: sha512-fmfjsOlJUpMWu+mAAtZZZHz7UEwsUxIIvu1TJfO1HqFQvB/B+ii0xr9B5HpbZY/mC4XZ8SvjHJqtAY6pDPQEog==} + caniuse-lite@1.0.30001741: + resolution: {integrity: sha512-QGUGitqsc8ARjLdgAfxETDhRbJ0REsP6O3I96TAth/mVjh2cYzN2u+3AzPP3aVSm2FehEItaJw1xd+IGBXWeSw==} ccount@2.0.1: resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} @@ -1947,16 +2138,20 @@ packages: character-entities@2.0.2: resolution: {integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==} + chart.js@4.5.0: + resolution: {integrity: sha512-aYeC/jDgSEx8SHWZvANYMioYMZ2KX02W6f6uVfyteuCGcadDLcYVHdfdygsTQkQ4TKn5lghoojAsPj5pu0SnvQ==} + engines: {pnpm: '>=8'} + chokidar@3.6.0: resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} engines: {node: '>= 8.10.0'} - ci-info@4.0.0: - resolution: {integrity: sha512-TdHqgGf9odd8SXNuxtUBVx8Nv+qZOejE6qyqiy5NtbYYQOeFa6zmHkxlPzmaLxWWHsU6nJmB7AETdVPi+2NBUg==} + ci-info@4.3.0: + resolution: {integrity: sha512-l+2bNRMiQgcfILUi33labAZYIWlH1kWDp+ecNo5iisRKrbm0xcRyCww71/YU0Fkw0mAFpz9bJayXPjey6vkmaQ==} engines: {node: '>=8'} - cidr-tools@11.0.2: - resolution: {integrity: sha512-OLeM9EOXybbhMsGGBNRLCMjn8e+wFOXARIShF/sZwmJLsxWywqfE0By4BMftT6BFWpbcETWpW7TfM2KGCtrZDg==} + cidr-tools@11.0.3: + resolution: {integrity: sha512-7p0rp7B2P+nZfBkJlrQzUMDyUHeYK2h/XCJY80VUl1v5oxwLxQjZMy39BXVOXugwAX67l0oJ/QQ6OhANgUtUbw==} engines: {node: '>=18'} clean-regexp@1.0.0: @@ -1998,6 +2193,9 @@ packages: confbox@0.1.8: resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==} + confbox@0.2.2: + resolution: {integrity: sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==} + convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} @@ -2005,11 +2203,11 @@ packages: resolution: {integrity: sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w==} engines: {node: '>=12.13'} - core-js-compat@3.39.0: - resolution: {integrity: sha512-VgEUx3VwlExr5no0tXlBt+silBvhTryPwCXRI2Id1PN8WTKu7MreethvddqOubrYxkFdv/RnYrqlv1sFNAUelw==} + core-js-compat@3.45.1: + resolution: {integrity: sha512-tqTt5T4PzsMIZ430XGviK4vzYSoeNJ6CXODi6c/voxOT6IZqBht5/EKaSNnYiEjjRYxjVz7DQIsOsY0XNi8PIA==} - cross-spawn@7.0.3: - resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} cssesc@3.0.0: @@ -2023,16 +2221,8 @@ packages: de-indent@1.0.2: resolution: {integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==} - debug@3.2.7: - resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true - - debug@4.3.7: - resolution: {integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==} + debug@4.4.1: + resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==} engines: {node: '>=6.0'} peerDependencies: supports-color: '*' @@ -2040,8 +2230,8 @@ packages: supports-color: optional: true - decode-named-character-reference@1.0.2: - resolution: {integrity: sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==} + decode-named-character-reference@1.2.0: + resolution: {integrity: sha512-c6fcElNV6ShtZXmsgNgFFV5tVX2PaV4g+MOAkb8eXHvn6sryJBrZa9r0zV6+dtTyoCKxtDy5tyQ5ZwQuidtd+Q==} deep-is@0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} @@ -2079,22 +2269,18 @@ packages: didyoumean@1.2.2: resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} - dir-glob@3.0.1: - resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} - engines: {node: '>=8'} - dlv@1.1.3: resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==} - doctrine@3.0.0: - resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} - engines: {node: '>=6.0.0'} + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} - electron-to-chromium@1.5.50: - resolution: {integrity: sha512-eMVObiUQ2LdgeO1F/ySTXsvqvxb6ZH2zPGaMYsWzRDdOddUa77tdmI0ltg+L16UpbWdhPmuF3wIQYyQq65WfZw==} + electron-to-chromium@1.5.214: + resolution: {integrity: sha512-TpvUNdha+X3ybfU78NoQatKvQEm1oq3lf2QbnmCEdw+Bd9RuIAY+hJTvq1avzHM0f7EJfnH3vbCnbzKzisc/9Q==} emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} @@ -2102,8 +2288,8 @@ packages: emoji-regex@9.2.2: resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} - enhanced-resolve@5.17.1: - resolution: {integrity: sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==} + enhanced-resolve@5.18.3: + resolution: {integrity: sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==} engines: {node: '>=10.13.0'} entities@4.5.0: @@ -2116,16 +2302,29 @@ packages: error-stack-parser-es@0.1.5: resolution: {integrity: sha512-xHku1X40RO+fO8yJ8Wh2f2rZWVjqyhb1zgq1yZ8aZRQkv6OOKhKWRUaht3eSCUbAOBaKIgM+ykwFLE+QUxgGeg==} - es-module-lexer@1.5.4: - resolution: {integrity: sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==} + es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + es-object-atoms@1.1.1: + resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} + engines: {node: '>= 0.4'} + + es-set-tostringtag@2.1.0: + resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} + engines: {node: '>= 0.4'} esbuild@0.21.5: resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} engines: {node: '>=12'} hasBin: true - esbuild@0.23.1: - resolution: {integrity: sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==} + esbuild@0.25.9: + resolution: {integrity: sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g==} engines: {node: '>=18'} hasBin: true @@ -2156,43 +2355,60 @@ packages: peerDependencies: eslint: '>=6.0.0' - eslint-compat-utils@0.6.0: - resolution: {integrity: sha512-1vVBdI/HLS6HTHVQCJGlN+LOF0w1Rs/WB9se23mQr84cRM0iMM8PulMFFhQdQ1BvS0cGwjpis4xziI91Rk0l6g==} + eslint-compat-utils@0.6.5: + resolution: {integrity: sha512-vAUHYzue4YAa2hNACjB8HvUQj5yehAZgiClyFVVom9cP8z5NSFq3PwB/TtJslN2zAMgRX6FCFCjYBbQh71g5RQ==} engines: {node: '>=12'} peerDependencies: eslint: '>=6.0.0' - eslint-config-flat-gitignore@0.3.0: - resolution: {integrity: sha512-0Ndxo4qGhcewjTzw52TK06Mc00aDtHNTdeeW2JfONgDcLkRO/n/BteMRzNVpLQYxdCC/dFEilfM9fjjpGIJ9Og==} + eslint-config-flat-gitignore@1.0.1: + resolution: {integrity: sha512-wjBmJ8TAb67G2or/gBp/H62uCIkDCjpCmlGPSG41/7QagUjMgh+iegVB3gY8eNYhTAmecjKtclT4wGAjHz5yWA==} peerDependencies: eslint: ^9.5.0 - eslint-flat-config-utils@0.4.0: - resolution: {integrity: sha512-kfd5kQZC+BMO0YwTol6zxjKX1zAsk8JfSAopbKjKqmENTJcew+yBejuvccAg37cvOrN0Mh+DVbeyznuNWEjt4A==} + eslint-flat-config-utils@1.1.0: + resolution: {integrity: sha512-W49wz7yQJGRfg4QSV3nwdO/fYcWetiSKhLV5YykfQMcqnIATNpoS7EPdINhLB9P3fmdjNmFtOgZjiKnCndWAnw==} eslint-formatting-reporter@0.0.0: resolution: {integrity: sha512-k9RdyTqxqN/wNYVaTk/ds5B5rA8lgoAmvceYN7bcZMBwU7TuXx5ntewJv81eF3pIL/CiJE+pJZm36llG8yhyyw==} peerDependencies: eslint: '>=8.40.0' - eslint-import-resolver-node@0.3.9: - resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==} + eslint-import-context@0.1.9: + resolution: {integrity: sha512-K9Hb+yRaGAGUbwjhFNHvSmmkZs9+zbuoe3kFQ4V1wYjrepUFYM2dZAfNtjbbj3qsPfUfsA68Bx/ICWQMi+C8Eg==} + engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} + peerDependencies: + unrs-resolver: ^1.0.0 + peerDependenciesMeta: + unrs-resolver: + optional: true + + eslint-json-compat-utils@0.2.1: + resolution: {integrity: sha512-YzEodbDyW8DX8bImKhAcCeu/L31Dd/70Bidx2Qex9OFUtgzXLqtfWL4Hr5fM/aCCB8QUZLuJur0S9k6UfgFkfg==} + engines: {node: '>=12'} + peerDependencies: + '@eslint/json': '*' + eslint: '*' + jsonc-eslint-parser: ^2.4.0 + peerDependenciesMeta: + '@eslint/json': + optional: true - eslint-merge-processors@0.1.0: - resolution: {integrity: sha512-IvRXXtEajLeyssvW4wJcZ2etxkR9mUf4zpNwgI+m/Uac9RfXHskuJefkHUcawVzePnd6xp24enp5jfgdHzjRdQ==} + eslint-merge-processors@1.0.0: + resolution: {integrity: sha512-4GybyHmhXtT7/W8RAouQzNM0791sYasJCTYHIAYjuiJvbNFY0jMKkoESREhX+mjX37dxiN6v4EqhZ1nc0tJF7A==} peerDependencies: eslint: '*' - eslint-parser-plain@0.1.0: - resolution: {integrity: sha512-oOeA6FWU0UJT/Rxc3XF5Cq0nbIZbylm7j8+plqq0CZoE6m4u32OXJrR+9iy4srGMmF6v6pmgvP1zPxSRIGh3sg==} + eslint-parser-plain@0.1.1: + resolution: {integrity: sha512-KRgd6wuxH4U8kczqPp+Oyk4irThIhHWxgFgLDtpgjUGVIS3wGrJntvZW/p6hHq1T4FOwnOtCNkvAI4Kr+mQ/Hw==} eslint-plugin-antfu@2.7.0: resolution: {integrity: sha512-gZM3jq3ouqaoHmUNszb1Zo2Ux7RckSvkGksjLWz9ipBYGSv1EwwBETN6AdiUXn+RpVHXTbEMPAPlXJazcA6+iA==} peerDependencies: eslint: '*' - eslint-plugin-command@0.2.6: - resolution: {integrity: sha512-T0bHZ1oblW1xUHUVoBKZJR2osSNNGkfZuK4iqboNwuNS/M7tdp3pmURaJtTi/XDzitxaQ02lvOdFH0mUd5QLvQ==} + eslint-plugin-command@2.1.0: + resolution: {integrity: sha512-S3gvDSCRHLdRG7NYaevLvGA0g/txOju7NEB2di7SE80NtbCwsvpi/fft045YuTZpOzqCRUfuye39raldmpXXYQ==} peerDependencies: eslint: '*' @@ -2202,31 +2418,38 @@ packages: peerDependencies: eslint: '>=8' - eslint-plugin-format@0.1.2: - resolution: {integrity: sha512-ZrcO3aiumgJ6ENAv65IWkPjtW77ML/5mp0YrRK0jdvvaZJb+4kKWbaQTMr/XbJo6CtELRmCApAziEKh7L2NbdQ==} + eslint-plugin-format@0.1.3: + resolution: {integrity: sha512-vTmshuv1iMfmcM1HADnyhae5MBBGlJZBZyZ+ybtXCEzRe3nRhUvLX+6rAvsEfcdK6a2pqpLs/F530dXKvfQqYQ==} peerDependencies: eslint: ^8.40.0 || ^9.0.0 - eslint-plugin-import-x@4.4.0: - resolution: {integrity: sha512-me58aWTjdkPtgmOzPe+uP0bebpN5etH4bJRnYzy85Rn9g/3QyASg6kTCqdwNzyaJRqMI2ii2o8s01P2LZpREHg==} + eslint-plugin-import-x@4.16.1: + resolution: {integrity: sha512-vPZZsiOKaBAIATpFE2uMI4w5IRwdv/FpQ+qZZMR4E+PeOcM4OeoEbqxRMnywdxP19TyB/3h6QBB0EWon7letSQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: + '@typescript-eslint/utils': ^8.0.0 eslint: ^8.57.0 || ^9.0.0 + eslint-import-resolver-node: '*' + peerDependenciesMeta: + '@typescript-eslint/utils': + optional: true + eslint-import-resolver-node: + optional: true - eslint-plugin-jsdoc@50.4.3: - resolution: {integrity: sha512-uWtwFxGRv6B8sU63HZM5dAGDhgsatb+LONwmILZJhdRALLOkCX2HFZhdL/Kw2ls8SQMAVEfK+LmnEfxInRN8HA==} + eslint-plugin-jsdoc@50.8.0: + resolution: {integrity: sha512-UyGb5755LMFWPrZTEqqvTJ3urLz1iqj+bYOHFNag+sw3NvaMWP9K2z+uIn37XfNALmQLQyrBlJ5mkiVPL7ADEg==} engines: {node: '>=18'} peerDependencies: eslint: ^7.0.0 || ^8.0.0 || ^9.0.0 - eslint-plugin-jsonc@2.17.0: - resolution: {integrity: sha512-wvifOtlIGDx6IFqVpuavPYLRA0yCoaFpoIUOW46rgS2F91brwCyWbEDXjrNrsThZ6rImTuDH9Biu5XHxaaL1qA==} + eslint-plugin-jsonc@2.20.1: + resolution: {integrity: sha512-gUzIwQHXx7ZPypUoadcyRi4WbHW2TPixDr0kqQ4miuJBU0emJmyGTlnaT3Og9X2a8R1CDayN9BFSq5weGWbTng==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: '>=6.0.0' - eslint-plugin-n@17.13.1: - resolution: {integrity: sha512-97qzhk1z3DdSJNCqT45EslwCu5+LB9GDadSyBItgKUfGsXAmN/aa7LRQ0ZxHffUxUzvgbTPJL27/pE9ZQWHy7A==} + eslint-plugin-n@17.21.3: + resolution: {integrity: sha512-MtxYjDZhMQgsWRm/4xYLL0i2EhusWT7itDxlJ80l1NND2AL2Vi5Mvneqv/ikG9+zpran0VsVRXTEHrpLmUZRNw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: '>=8.23.0' @@ -2235,45 +2458,32 @@ packages: resolution: {integrity: sha512-brcKcxGnISN2CcVhXJ/kEQlNa0MEfGRtwKtWA16SkqXHKitaKIMrfemJKLKX1YqDU5C/5JY3PvZXd5jEW04e0Q==} engines: {node: '>=5.0.0'} - eslint-plugin-perfectionist@3.9.1: - resolution: {integrity: sha512-9WRzf6XaAxF4Oi5t/3TqKP5zUjERhasHmLFHin2Yw6ZAp/EP/EVA2dr3BhQrrHWCm5SzTMZf0FcjDnBkO2xFkA==} + eslint-plugin-perfectionist@4.15.0: + resolution: {integrity: sha512-pC7PgoXyDnEXe14xvRUhBII8A3zRgggKqJFx2a82fjrItDs1BSI7zdZnQtM2yQvcyod6/ujmzb7ejKPx8lZTnw==} engines: {node: ^18.0.0 || >=20.0.0} peerDependencies: - astro-eslint-parser: ^1.0.2 - eslint: '>=8.0.0' - svelte: '>=3.0.0' - svelte-eslint-parser: ^0.41.1 - vue-eslint-parser: '>=9.0.0' - peerDependenciesMeta: - astro-eslint-parser: - optional: true - svelte: - optional: true - svelte-eslint-parser: - optional: true - vue-eslint-parser: - optional: true + eslint: '>=8.45.0' - eslint-plugin-regexp@2.6.0: - resolution: {integrity: sha512-FCL851+kislsTEQEMioAlpDuK5+E5vs0hi1bF8cFlPlHcEjeRhuAzEsGikXRreE+0j4WhW2uO54MqTjXtYOi3A==} + eslint-plugin-regexp@2.10.0: + resolution: {integrity: sha512-ovzQT8ESVn5oOe5a7gIDPD5v9bCSjIFJu57sVPDqgPRXicQzOnYfFN21WoQBQF18vrhT5o7UMKFwJQVVjyJ0ng==} engines: {node: ^18 || >=20} peerDependencies: eslint: '>=8.44.0' - eslint-plugin-toml@0.11.1: - resolution: {integrity: sha512-Y1WuMSzfZpeMIrmlP1nUh3kT8p96mThIq4NnHrYUhg10IKQgGfBZjAWnrg9fBqguiX4iFps/x/3Hb5TxBisfdw==} + eslint-plugin-toml@0.12.0: + resolution: {integrity: sha512-+/wVObA9DVhwZB1nG83D2OAQRrcQZXy+drqUnFJKymqnmbnbfg/UPmEMCKrJNcEboUGxUjYrJlgy+/Y930mURQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: '>=6.0.0' - eslint-plugin-unicorn@56.0.0: - resolution: {integrity: sha512-aXpddVz/PQMmd69uxO98PA4iidiVNvA0xOtbpUoz1WhBd4RxOQQYqN618v68drY0hmy5uU2jy1bheKEVWBjlPw==} + eslint-plugin-unicorn@56.0.1: + resolution: {integrity: sha512-FwVV0Uwf8XPfVnKSGpMg7NtlZh0G0gBarCaFcMUOoqPxXryxdYxTRRv4kH6B9TFCVIrjRXG+emcxIk2ayZilog==} engines: {node: '>=18.18'} peerDependencies: eslint: '>=8.56.0' - eslint-plugin-unused-imports@4.1.4: - resolution: {integrity: sha512-YptD6IzQjDardkl0POxnnRBhU1OEePMV0nd6siHaRBbd+lyh6NAhFEobiznKU7kTsSsDeSD62Pe7kAM1b7dAZQ==} + eslint-plugin-unused-imports@4.2.0: + resolution: {integrity: sha512-hLbJ2/wnjKq4kGA9AUaExVFIbNzyxYdVo49QZmKCnhk5pc9wcYRbfgLHvWJ8tnsdcseGhoUAddm9gn/lt+d74w==} peerDependencies: '@typescript-eslint/eslint-plugin': ^8.0.0-0 || ^7.0.0 || ^6.0.0 || ^5.0.0 eslint: ^9.0.0 || ^8.0.0 @@ -2281,20 +2491,20 @@ packages: '@typescript-eslint/eslint-plugin': optional: true - eslint-plugin-vue@9.30.0: - resolution: {integrity: sha512-CyqlRgShvljFkOeYK8wN5frh/OGTvkj1S7wlr2Q2pUvwq+X5VYiLd6ZjujpgSgLnys2W8qrBLkXQ41SUYaoPIQ==} + eslint-plugin-vue@9.33.0: + resolution: {integrity: sha512-174lJKuNsuDIlLpjeXc5E2Tss8P44uIimAfGD0b90k0NoirJqpG7stLuU9Vp/9ioTOrQdWVREc4mRd1BD+CvGw==} engines: {node: ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.2.0 || ^7.0.0 || ^8.0.0 || ^9.0.0 - eslint-plugin-yml@1.15.0: - resolution: {integrity: sha512-leC8APYVOsKyWUlvRwVhewytK5wS70BfMqIaUplFstRfzCoVp0YoEroV4cUEvQrBj93tQ3M9LcjO/ewr6D4kjA==} + eslint-plugin-yml@1.18.0: + resolution: {integrity: sha512-9NtbhHRN2NJa/s3uHchO3qVVZw0vyOIvWlXWGaKCr/6l3Go62wsvJK5byiI6ZoYztDsow4GnS69BZD3GnqH3hA==} engines: {node: ^14.17.0 || >=16.0.0} peerDependencies: eslint: '>=6.0.0' - eslint-processor-vue-blocks@0.1.2: - resolution: {integrity: sha512-PfpJ4uKHnqeL/fXUnzYkOax3aIenlwewXRX8jFinA1a2yCFnLgMuiH3xvCgvHHUlV2xJWQHbCTdiJWGwb3NqpQ==} + eslint-processor-vue-blocks@1.0.0: + resolution: {integrity: sha512-q+Wn9bCml65NwYtuINVCE5dUqZa/uVoY4jfc8qEDwWbcGqdRyfJJmAONNZsreA4Q9EJqjYGjk8Hk1QuwAktgkw==} peerDependencies: '@vue/compiler-sfc': ^3.3.0 eslint: ^8.50.0 || ^9.0.0 @@ -2303,20 +2513,20 @@ packages: resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - eslint-scope@8.2.0: - resolution: {integrity: sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A==} + eslint-scope@8.4.0: + resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} eslint-visitor-keys@3.4.3: resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - eslint-visitor-keys@4.2.0: - resolution: {integrity: sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==} + eslint-visitor-keys@4.2.1: + resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - eslint@9.14.0: - resolution: {integrity: sha512-c2FHsVBr87lnUtjP4Yhvk4yEhKrQavGafRA/Se1ouse8PfbfC/Qh9Mxa00yWsZRlqeUB9raXip0aiiUZkgnr9g==} + eslint@9.35.0: + resolution: {integrity: sha512-QePbBFMJFjgmlE+cXAlbHZbHpdFVS2E/6vzCy7aKlebddvl1vadiC4JFV5u/wqTkNUwEV8WrQi257jf5f06hrg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} hasBin: true peerDependencies: @@ -2325,8 +2535,8 @@ packages: jiti: optional: true - espree@10.3.0: - resolution: {integrity: sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==} + espree@10.4.0: + resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} espree@9.6.1: @@ -2364,9 +2574,12 @@ packages: resolution: {integrity: sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA==} engines: {node: ^14.18.0 || ^16.14.0 || >=18.0.0} - execa@8.0.1: - resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==} - engines: {node: '>=16.17'} + execa@9.6.0: + resolution: {integrity: sha512-jpWzZ1ZhwUmeWRhS7Qv3mhpOhLfwI+uAX4e5fOcXqwMR7EcJ0pj2kV1CVzHVMX/LphnKWD3LObjZCoJ71lKpHw==} + engines: {node: ^18.19.0 || >=20.5.0} + + exsolve@1.0.7: + resolution: {integrity: sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw==} extend-shallow@2.0.1: resolution: {integrity: sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==} @@ -2378,8 +2591,8 @@ packages: fast-diff@1.3.0: resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==} - fast-glob@3.3.2: - resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} + fast-glob@3.3.3: + resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} engines: {node: '>=8.6.0'} fast-json-stable-stringify@2.1.0: @@ -2388,8 +2601,15 @@ packages: fast-levenshtein@2.0.6: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} - fastq@1.17.1: - resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} + fastq@1.19.1: + resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} + + fault@2.0.1: + resolution: {integrity: sha512-WtySTkS4OKev5JtpHXnib4Gxiurzh5NCGvWrFaZ34m6JehfTUhKZvn9njTfw48t6JumVQOmrKqpmGcdwxnhqBQ==} + + figures@6.1.0: + resolution: {integrity: sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==} + engines: {node: '>=18'} file-entry-cache@8.0.0: resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} @@ -2399,10 +2619,6 @@ packages: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} - find-up-simple@1.0.0: - resolution: {integrity: sha512-q7Us7kcjj2VMePAa02hDAF6d+MzsdsAWEwYyOpwUtlerRBkOEPBCRZrAV4XfcSN8fHAgaD0hP7miwoay6DCprw==} - engines: {node: '>=18'} - find-up@4.1.0: resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} engines: {node: '>=8'} @@ -2415,8 +2631,8 @@ packages: resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} engines: {node: '>=16'} - flatted@3.3.1: - resolution: {integrity: sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==} + flatted@3.3.3: + resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} floating-vue@5.2.2: resolution: {integrity: sha512-afW+h2CFafo+7Y9Lvw/xsqjaQlKLdJV7h1fCHfcYQ1C4SVMlu7OAekqWgu5d4SgvkBVU0pVpLlVsrSTBURFRkg==} @@ -2427,8 +2643,8 @@ packages: '@nuxt/kit': optional: true - follow-redirects@1.15.9: - resolution: {integrity: sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==} + follow-redirects@1.15.11: + resolution: {integrity: sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==} engines: {node: '>=4.0'} peerDependencies: debug: '*' @@ -2436,25 +2652,25 @@ packages: debug: optional: true - foreground-child@3.3.0: - resolution: {integrity: sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==} + foreground-child@3.3.1: + resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} engines: {node: '>=14'} - form-data@4.0.1: - resolution: {integrity: sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==} + form-data@4.0.4: + resolution: {integrity: sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==} engines: {node: '>= 6'} + format@0.2.2: + resolution: {integrity: sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==} + engines: {node: '>=0.4.x'} + fraction.js@4.3.7: resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==} - fs-extra@11.2.0: - resolution: {integrity: sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==} + fs-extra@11.3.1: + resolution: {integrity: sha512-eXvGGwZ5CL17ZSwHWd3bbgk7UUpF6IFHtP57NYYakPvHOs8GDgDe5KJI36jIJzDkJ6eJjuzRA8eBQb6SkKue0g==} engines: {node: '>=14.14'} - fs-extra@7.0.1: - resolution: {integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==} - engines: {node: '>=6 <7 || >=8'} - fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -2471,16 +2687,27 @@ packages: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} engines: {node: 6.* || 8.* || >= 10.*} + get-intrinsic@1.3.0: + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + engines: {node: '>= 0.4'} + + get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} + get-stream@6.0.1: resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} engines: {node: '>=10'} - get-stream@8.0.1: - resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==} - engines: {node: '>=16'} + get-stream@9.0.1: + resolution: {integrity: sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==} + engines: {node: '>=18'} - get-tsconfig@4.8.1: - resolution: {integrity: sha512-k9PN+cFBmaLWtVz29SkUoqU5O0slLuHJXt/2P+tMVFT+phsSGXGkp9t3rQIqdz0e+06EHNGs3oM6ZX1s2zHxRg==} + get-tsconfig@4.10.1: + resolution: {integrity: sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==} + + github-slugger@2.0.0: + resolution: {integrity: sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==} glob-parent@5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} @@ -2494,10 +2721,6 @@ packages: resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} hasBin: true - globals@11.12.0: - resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} - engines: {node: '>=4'} - globals@13.24.0: resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==} engines: {node: '>=8'} @@ -2506,13 +2729,16 @@ packages: resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} engines: {node: '>=18'} - globals@15.12.0: - resolution: {integrity: sha512-1+gLErljJFhbOVyaetcwJiJ4+eLe45S2E7P5UiZ9xGfeq3ATQf5DOv9G7MH3gGbKQLkzmNh2DxfZwLdw+j6oTQ==} + globals@15.15.0: + resolution: {integrity: sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==} engines: {node: '>=18'} - globby@11.1.0: - resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} - engines: {node: '>=10'} + globrex@0.1.2: + resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==} + + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} @@ -2528,6 +2754,14 @@ packages: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} + has-symbols@1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} + engines: {node: '>= 0.4'} + + has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} + hasown@2.0.2: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} @@ -2542,32 +2776,32 @@ packages: hosted-git-info@2.8.9: resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==} - html-tags@3.3.1: - resolution: {integrity: sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==} - engines: {node: '>=8'} - human-signals@4.3.1: resolution: {integrity: sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==} engines: {node: '>=14.18.0'} - human-signals@5.0.0: - resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==} - engines: {node: '>=16.17.0'} + human-signals@8.0.1: + resolution: {integrity: sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ==} + engines: {node: '>=18.18.0'} ignore@5.3.2: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} - import-fresh@3.3.0: - resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} + ignore@7.0.5: + resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} + engines: {node: '>= 4'} + + import-fresh@3.3.1: + resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} engines: {node: '>=6'} import-lazy@4.0.0: resolution: {integrity: sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==} engines: {node: '>=8'} - importx@0.5.0: - resolution: {integrity: sha512-qROz3rSOjQYclmEQAajH9RhBuqpAGHM+5CNd9fk+TsF4JKmQsAI1egafW8XZZv8vARCo4nAmmt5d0eI2B8GUsA==} + importx@0.5.2: + resolution: {integrity: sha512-YEwlK86Ml5WiTxN/ECUYC5U7jd1CisAVw7ya4i9ZppBoHfFkT2+hChhr3PE2fYxUKLkNyivxEQpa5Ruil1LJBQ==} imurmurhash@0.1.4: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} @@ -2577,8 +2811,8 @@ packages: resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} engines: {node: '>=8'} - ip-bigint@8.2.0: - resolution: {integrity: sha512-46EAEKzGNxojH5JaGEeCix49tL4h1W8ia5mhogZ68HroVAfyLj1E+SFFid4GuyK0mdIKjwcAITLqwg1wlkx2iQ==} + ip-bigint@8.2.2: + resolution: {integrity: sha512-wPoOpHigOtoY29UCFA0L82cJVFcT7M+TsrgipUVpFw7HV9LpLEuNXCymt3623jzHPlIZzFaCyaVf9VACssFYew==} engines: {node: '>=18'} ip-num@1.5.1: @@ -2595,8 +2829,8 @@ packages: resolution: {integrity: sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==} engines: {node: '>=6'} - is-core-module@2.15.1: - resolution: {integrity: sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==} + is-core-module@2.16.1: + resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} engines: {node: '>= 0.4'} is-docker@3.0.0: @@ -2629,10 +2863,22 @@ packages: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} + is-plain-obj@4.1.0: + resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} + engines: {node: '>=12'} + is-stream@3.0.0: resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + is-stream@4.0.1: + resolution: {integrity: sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==} + engines: {node: '>=18'} + + is-unicode-supported@2.1.0: + resolution: {integrity: sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==} + engines: {node: '>=18'} + is-what@4.1.16: resolution: {integrity: sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==} engines: {node: '>=12.13'} @@ -2647,12 +2893,12 @@ packages: jackspeak@3.4.3: resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} - jiti@1.21.6: - resolution: {integrity: sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==} + jiti@1.21.7: + resolution: {integrity: sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==} hasBin: true - jiti@2.4.0: - resolution: {integrity: sha512-H5UpaUI+aHOqZXlYOaFP/8AzKsg+guWu+Pr3Y8i7+Y3zr1aXAvCvTAQ1RxSc6oVD8R8c7brgNtTVP91E7upH/g==} + jiti@2.5.1: + resolution: {integrity: sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w==} hasBin: true jju@1.4.0: @@ -2661,8 +2907,8 @@ packages: js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} - js-tokens@9.0.0: - resolution: {integrity: sha512-WriZw1luRMlmV3LGJaR6QOJjWwgLUTf89OwT2lUOyjX2dJGBwgmIkbcz+7WFZjrZM635JOIR517++e/67CP9dQ==} + js-tokens@9.0.1: + resolution: {integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==} js-yaml@3.14.1: resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} @@ -2676,12 +2922,16 @@ packages: resolution: {integrity: sha512-Hicd6JK5Njt2QB6XYFS7ok9e37O8AYk3jTcppG4YVQnYjOemymvTcmc7OWsmq/Qqj5TdRFO5/x/tIPmBeRtGHg==} engines: {node: '>=12.0.0'} + jsdoc-type-pratt-parser@4.8.0: + resolution: {integrity: sha512-iZ8Bdb84lWRuGHamRXFyML07r21pcwBrLkHEuHgEY5UbCouBwv7ECknDRKzsQIXMiqpPymqtIf8TC/shYKB5rw==} + engines: {node: '>=12.0.0'} + jsesc@0.5.0: resolution: {integrity: sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==} hasBin: true - jsesc@3.0.2: - resolution: {integrity: sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==} + jsesc@3.1.0: + resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} engines: {node: '>=6'} hasBin: true @@ -2709,11 +2959,8 @@ packages: resolution: {integrity: sha512-WYDyuc/uFcGp6YtM2H0uKmUwieOuzeE/5YocFJLnLfclZ4inf3mRn8ZVy1s7Hxji7Jxm6Ss8gqpexD/GlKoGgg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - jsonfile@4.0.0: - resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} - - jsonfile@6.1.0: - resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} + jsonfile@6.2.0: + resolution: {integrity: sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==} keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} @@ -2743,8 +2990,12 @@ packages: resolution: {integrity: sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - local-pkg@0.5.0: - resolution: {integrity: sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg==} + local-pkg@0.5.1: + resolution: {integrity: sha512-9rrA30MRRP3gBD3HTGnC6cDFpaE1kVDWxWgqWJUN0RvDNAo+Nz/9GxB+nHOH0ifbVFy0hSA1V6vFDvnx54lTEQ==} + engines: {node: '>=14'} + + local-pkg@1.1.2: + resolution: {integrity: sha512-arhlxbFRmoQHl33a0Zkle/YWlmNwoyt6QNZEIJcqNbdrsix5Lvc4HyyI3EnwxTYlZYc32EbYrQ8SzEZ7dqgg9A==} engines: {node: '>=14'} locate-path@5.0.0: @@ -2774,15 +3025,19 @@ packages: resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} engines: {node: '>=10'} - magic-string-ast@0.6.2: - resolution: {integrity: sha512-oN3Bcd7ZVt+0VGEs7402qR/tjgjbM7kPlH/z7ufJnzTLVBzXJITRHOJiwMmmYMgZfdoWQsfQcY+iKlxiBppnMA==} + magic-string-ast@0.6.3: + resolution: {integrity: sha512-C9sgUzVZtUtzCBoMdYtwrIRQ4IucGRFGgdhkjL7PXsVfPYmTuWtewqzk7dlipaCMWH/gOYehW9rgMoa4Oebtpw==} engines: {node: '>=16.14.0'} - magic-string@0.30.12: - resolution: {integrity: sha512-Ea8I3sQMVXr8JhN4z+H/d8zwo+tYDgHE9+5G4Wnrwhs0gaK9fXTKx0Tw5Xwsd/bCPTTZNRAdpyzvoeORe9LYpw==} + magic-string-ast@0.7.1: + resolution: {integrity: sha512-ub9iytsEbT7Yw/Pd29mSo/cNQpaEu67zR1VVcXDiYjSFwzeBxNdTd0FMnSslLQXiRj8uGPzwsaoefrMD5XAmdw==} + engines: {node: '>=16.14.0'} + + magic-string@0.30.18: + resolution: {integrity: sha512-yi8swmWbO17qHhwIBNeeZxTceJMeBvWJaId6dyvTSOwTipqeHhMhOrz6513r1sOKnpvQ7zkhlG8tPrpilwTxHQ==} - make-synchronized@0.2.9: - resolution: {integrity: sha512-4wczOs8SLuEdpEvp3vGo83wh8rjJ78UsIk7DIX5fxdfmfMJGog4bQzxfvOwq7Q3yCHLC4jp1urPHIxRS/A93gA==} + make-synchronized@0.2.10: + resolution: {integrity: sha512-7NTbfv+5oJJdjHRPW4j4P/n7sYeu7mrBTZLVHD5ACSyFPRObPhsZAIoR/75SlVl20x/g7PIP75FBBHqSJ2FPuA==} markdown-it@14.1.0: resolution: {integrity: sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==} @@ -2791,17 +3046,24 @@ packages: markdown-table@3.0.4: resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==} - mdast-util-find-and-replace@3.0.1: - resolution: {integrity: sha512-SG21kZHGC3XRTSUhtofZkBzZTJNM5ecCi0SK2IMKmSXR8vO3peL+kb1O0z7Zl83jKtutG4k5Wv/W7V3/YHvzPA==} + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + + mdast-util-find-and-replace@3.0.2: + resolution: {integrity: sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==} mdast-util-from-markdown@2.0.2: resolution: {integrity: sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA==} + mdast-util-frontmatter@2.0.1: + resolution: {integrity: sha512-LRqI9+wdgC25P0URIJY9vwocIzCcksduHQ9OF2joxQoyTNVduwLAFUzjoopuRJbJAReaKrNQKAZKL3uCMugWJA==} + mdast-util-gfm-autolink-literal@2.0.1: resolution: {integrity: sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==} - mdast-util-gfm-footnote@2.0.0: - resolution: {integrity: sha512-5jOT2boTSVkMnQ7LTrd6n/18kqwjmuYqo7JUPe+tRCY6O7dAuTFMtTPauYYrMPpox9hlN0uOx/FL8XvEfG9/mQ==} + mdast-util-gfm-footnote@2.1.0: + resolution: {integrity: sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==} mdast-util-gfm-strikethrough@2.0.0: resolution: {integrity: sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==} @@ -2812,8 +3074,8 @@ packages: mdast-util-gfm-task-list-item@2.0.0: resolution: {integrity: sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==} - mdast-util-gfm@3.0.0: - resolution: {integrity: sha512-dgQEX5Amaq+DuUqf26jJqSK9qgixgd6rYDHAv4aTBuA92cTknZlKpPfa86Z/s8Dj8xsAQpFfBmPUHWJBWqS4Bw==} + mdast-util-gfm@3.1.0: + resolution: {integrity: sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==} mdast-util-phrasing@4.1.0: resolution: {integrity: sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==} @@ -2834,8 +3096,11 @@ packages: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} - micromark-core-commonmark@2.0.1: - resolution: {integrity: sha512-CUQyKr1e///ZODyD1U3xit6zXwy1a8q2a1S1HKtIlmgvurrEpaw/Y9y6KSIbF8P59cn/NjzHyO+Q2fAyYLQrAA==} + micromark-core-commonmark@2.0.3: + resolution: {integrity: sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==} + + micromark-extension-frontmatter@2.0.0: + resolution: {integrity: sha512-C4AkuM3dA58cgZha7zVnuVxBhDsbttIMiytjgsM2XbHAB2faRVaHRle40558FBN+DJcrLNCoqG5mlrpdU4cRtg==} micromark-extension-gfm-autolink-literal@2.1.0: resolution: {integrity: sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==} @@ -2846,8 +3111,8 @@ packages: micromark-extension-gfm-strikethrough@2.1.0: resolution: {integrity: sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==} - micromark-extension-gfm-table@2.1.0: - resolution: {integrity: sha512-Ub2ncQv+fwD70/l4ou27b4YzfNaCJOvyX4HxXU15m7mpYY+rjuWzsLIPZHJL253Z643RpbcP1oeIJlQ/SKW67g==} + micromark-extension-gfm-table@2.1.1: + resolution: {integrity: sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==} micromark-extension-gfm-tagfilter@2.0.0: resolution: {integrity: sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==} @@ -2858,65 +3123,65 @@ packages: micromark-extension-gfm@3.0.0: resolution: {integrity: sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==} - micromark-factory-destination@2.0.0: - resolution: {integrity: sha512-j9DGrQLm/Uhl2tCzcbLhy5kXsgkHUrjJHg4fFAeoMRwJmJerT9aw4FEhIbZStWN8A3qMwOp1uzHr4UL8AInxtA==} + micromark-factory-destination@2.0.1: + resolution: {integrity: sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==} - micromark-factory-label@2.0.0: - resolution: {integrity: sha512-RR3i96ohZGde//4WSe/dJsxOX6vxIg9TimLAS3i4EhBAFx8Sm5SmqVfR8E87DPSR31nEAjZfbt91OMZWcNgdZw==} + micromark-factory-label@2.0.1: + resolution: {integrity: sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==} - micromark-factory-space@2.0.0: - resolution: {integrity: sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==} + micromark-factory-space@2.0.1: + resolution: {integrity: sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==} - micromark-factory-title@2.0.0: - resolution: {integrity: sha512-jY8CSxmpWLOxS+t8W+FG3Xigc0RDQA9bKMY/EwILvsesiRniiVMejYTE4wumNc2f4UbAa4WsHqe3J1QS1sli+A==} + micromark-factory-title@2.0.1: + resolution: {integrity: sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==} - micromark-factory-whitespace@2.0.0: - resolution: {integrity: sha512-28kbwaBjc5yAI1XadbdPYHX/eDnqaUFVikLwrO7FDnKG7lpgxnvk/XGRhX/PN0mOZ+dBSZ+LgunHS+6tYQAzhA==} + micromark-factory-whitespace@2.0.1: + resolution: {integrity: sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==} - micromark-util-character@2.1.0: - resolution: {integrity: sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==} + micromark-util-character@2.1.1: + resolution: {integrity: sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==} - micromark-util-chunked@2.0.0: - resolution: {integrity: sha512-anK8SWmNphkXdaKgz5hJvGa7l00qmcaUQoMYsBwDlSKFKjc6gjGXPDw3FNL3Nbwq5L8gE+RCbGqTw49FK5Qyvg==} + micromark-util-chunked@2.0.1: + resolution: {integrity: sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==} - micromark-util-classify-character@2.0.0: - resolution: {integrity: sha512-S0ze2R9GH+fu41FA7pbSqNWObo/kzwf8rN/+IGlW/4tC6oACOs8B++bh+i9bVyNnwCcuksbFwsBme5OCKXCwIw==} + micromark-util-classify-character@2.0.1: + resolution: {integrity: sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==} - micromark-util-combine-extensions@2.0.0: - resolution: {integrity: sha512-vZZio48k7ON0fVS3CUgFatWHoKbbLTK/rT7pzpJ4Bjp5JjkZeasRfrS9wsBdDJK2cJLHMckXZdzPSSr1B8a4oQ==} + micromark-util-combine-extensions@2.0.1: + resolution: {integrity: sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==} - micromark-util-decode-numeric-character-reference@2.0.1: - resolution: {integrity: sha512-bmkNc7z8Wn6kgjZmVHOX3SowGmVdhYS7yBpMnuMnPzDq/6xwVA604DuOXMZTO1lvq01g+Adfa0pE2UKGlxL1XQ==} + micromark-util-decode-numeric-character-reference@2.0.2: + resolution: {integrity: sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==} - micromark-util-decode-string@2.0.0: - resolution: {integrity: sha512-r4Sc6leeUTn3P6gk20aFMj2ntPwn6qpDZqWvYmAG6NgvFTIlj4WtrAudLi65qYoaGdXYViXYw2pkmn7QnIFasA==} + micromark-util-decode-string@2.0.1: + resolution: {integrity: sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==} - micromark-util-encode@2.0.0: - resolution: {integrity: sha512-pS+ROfCXAGLWCOc8egcBvT0kf27GoWMqtdarNfDcjb6YLuV5cM3ioG45Ys2qOVqeqSbjaKg72vU+Wby3eddPsA==} + micromark-util-encode@2.0.1: + resolution: {integrity: sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==} - micromark-util-html-tag-name@2.0.0: - resolution: {integrity: sha512-xNn4Pqkj2puRhKdKTm8t1YHC/BAjx6CEwRFXntTaRf/x16aqka6ouVoutm+QdkISTlT7e2zU7U4ZdlDLJd2Mcw==} + micromark-util-html-tag-name@2.0.1: + resolution: {integrity: sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==} - micromark-util-normalize-identifier@2.0.0: - resolution: {integrity: sha512-2xhYT0sfo85FMrUPtHcPo2rrp1lwbDEEzpx7jiH2xXJLqBuy4H0GgXk5ToU8IEwoROtXuL8ND0ttVa4rNqYK3w==} + micromark-util-normalize-identifier@2.0.1: + resolution: {integrity: sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==} - micromark-util-resolve-all@2.0.0: - resolution: {integrity: sha512-6KU6qO7DZ7GJkaCgwBNtplXCvGkJToU86ybBAUdavvgsCiG8lSSvYxr9MhwmQ+udpzywHsl4RpGJsYWG1pDOcA==} + micromark-util-resolve-all@2.0.1: + resolution: {integrity: sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==} - micromark-util-sanitize-uri@2.0.0: - resolution: {integrity: sha512-WhYv5UEcZrbAtlsnPuChHUAsu/iBPOVaEVsntLBIdpibO0ddy8OzavZz3iL2xVvBZOpolujSliP65Kq0/7KIYw==} + micromark-util-sanitize-uri@2.0.1: + resolution: {integrity: sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==} - micromark-util-subtokenize@2.0.1: - resolution: {integrity: sha512-jZNtiFl/1aY73yS3UGQkutD0UbhTt68qnRpw2Pifmz5wV9h8gOVsN70v+Lq/f1rKaU/W8pxRe8y8Q9FX1AOe1Q==} + micromark-util-subtokenize@2.1.0: + resolution: {integrity: sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==} - micromark-util-symbol@2.0.0: - resolution: {integrity: sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==} + micromark-util-symbol@2.0.1: + resolution: {integrity: sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==} - micromark-util-types@2.0.0: - resolution: {integrity: sha512-oNh6S2WMHWRZrmutsRmDDfkzKtxF+bc2VxLC9dvtrDIRFln627VsFP6fLMgTryGDljgLPjkrzQSDcPrjPyDJ5w==} + micromark-util-types@2.0.2: + resolution: {integrity: sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==} - micromark@4.0.0: - resolution: {integrity: sha512-o/sd0nMof8kYff+TqcDx3VSrgBTcZpSvYcAHIfHhv5VAuNmisCxjhx6YmxS8PFEpb9z5WKWKPdzf0jM23ro3RQ==} + micromark@4.0.2: + resolution: {integrity: sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==} micromatch@4.0.8: resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} @@ -2938,8 +3203,9 @@ packages: resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} engines: {node: '>=4'} - minimatch@3.0.8: - resolution: {integrity: sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q==} + minimatch@10.0.3: + resolution: {integrity: sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw==} + engines: {node: 20 || >=22} minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} @@ -2955,11 +3221,11 @@ packages: mitt@3.0.1: resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==} - mlly@1.7.2: - resolution: {integrity: sha512-tN3dvVHYVz4DhSXinXIk7u9syPYaJvio118uomkovAtWBT+RdbP6Lfh/5Lvo519YMmwBafwlh20IPTXIStscpA==} + mlly@1.8.0: + resolution: {integrity: sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g==} - mrmime@2.0.0: - resolution: {integrity: sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw==} + mrmime@2.0.1: + resolution: {integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==} engines: {node: '>=10'} ms@2.1.3: @@ -2971,19 +3237,30 @@ packages: mz@2.7.0: resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} - nanoid@3.3.7: - resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} + nanoid@3.3.11: + resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true - natural-compare-lite@1.4.0: - resolution: {integrity: sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==} + nanoid@5.1.5: + resolution: {integrity: sha512-Ir/+ZpE9fDsNH0hQ3C68uyThDXzYcim2EqcZ8zn8Chtt1iylPT9xXJB0kPCnqzgcEGikO9RxSrh63MsmVCU7Fw==} + engines: {node: ^18 || >=20} + hasBin: true + + napi-postinstall@0.3.3: + resolution: {integrity: sha512-uTp172LLXSxuSYHv/kou+f6KW3SMppU9ivthaVTXian9sOt3XM/zHYHpRZiLgQoxeWfYUnslNWQHF1+G71xcow==} + engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} + hasBin: true natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} - node-releases@2.0.18: - resolution: {integrity: sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==} + natural-orderby@5.0.0: + resolution: {integrity: sha512-kKHJhxwpR/Okycz4HhQKKlhWe4ASEfPgkSWNmKFHd7+ezuQlxkA5cM3+XkBPvm1gmHen3w53qsYAv+8GwRrBlg==} + engines: {node: '>=18'} + + node-releases@2.0.20: + resolution: {integrity: sha512-7gK6zSXEH6neM212JgfYFXe+GmZQM+fia5SsusuBIUgnPheLFBmIPhtFoAQRj8/7wASYQnbDlHPVwY0BefoFgA==} normalize-package-data@2.5.0: resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==} @@ -3000,6 +3277,10 @@ packages: resolution: {integrity: sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + npm-run-path@6.0.0: + resolution: {integrity: sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==} + engines: {node: '>=18'} + nth-check@2.1.1: resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} @@ -3015,16 +3296,16 @@ packages: resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} engines: {node: '>=12'} - open@10.1.0: - resolution: {integrity: sha512-mnkeQ1qP5Ue2wd+aivTD3NHd/lZ96Lu0jgf0pwktLPtx6cTZiH7tyeGRRHs0zX0rbrahXPnXlUnbeXyaBBuIaw==} + open@10.2.0: + resolution: {integrity: sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==} engines: {node: '>=18'} optionator@0.9.4: resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} engines: {node: '>= 0.8.0'} - oxc-resolver@2.0.1: - resolution: {integrity: sha512-xEbYdEGwafn+Y2GTyW0BGC3iIjJZXl+fxrIkyheew5mZrDODmPXJf2qwsa1ocBeVUC51g9e835vNZ9tRR5fYCg==} + oxc-resolver@4.2.0: + resolution: {integrity: sha512-x9bzmn1rQRu2cRT6dC6qOCKyStDVubXsf5H3UloUG/UFjzufmNu8DHTxafYDaSlA9Y+rorD+EnmF7sWSaFdd7g==} p-limit@2.3.0: resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} @@ -3049,8 +3330,8 @@ packages: package-json-from-dist@1.0.1: resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} - package-manager-detector@0.2.2: - resolution: {integrity: sha512-VgXbyrSNsml4eHWIvxxG/nTL4wgybMTXCV2Un/+yEc3aDKKU6nQBZjbeP3Pl3qm9Qg92X/1ng4ffvCeD/zwHgg==} + package-manager-detector@1.3.0: + resolution: {integrity: sha512-ZsEbbZORsyHuO00lY1kV3/t72yp6Ysay6Pd17ZAlNGuGwmWDLCJxFpRs0IzfXfj1o4icJOkUEioexFHzyPurSQ==} parent-module@1.0.1: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} @@ -3060,14 +3341,20 @@ packages: resolution: {integrity: sha512-RmVuCHWsfu0QPNW+mraxh/xjQVw/lhUCUru8Zni3Ctq3AoMhpDTq0OVdKS6iesd6Kqb7viCV3isAL43dciOSog==} engines: {node: '>=14'} - parse-imports@2.2.1: - resolution: {integrity: sha512-OL/zLggRp8mFhKL0rNORUTR4yBYujK/uU+xZL+/0Rgm2QE4nLO9v8PzEweSJEbMGKmDRjJE4R3IMJlL2di4JeQ==} - engines: {node: '>= 18'} + parse-imports-exports@0.2.4: + resolution: {integrity: sha512-4s6vd6dx1AotCx/RCI2m7t7GCh5bDRUtGNvRfHSP2wbBQdMi67pPe7mtzmgwcaQ8VKK/6IB7Glfyu3qdZJPybQ==} parse-json@5.2.0: resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} engines: {node: '>=8'} + parse-ms@4.0.0: + resolution: {integrity: sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==} + engines: {node: '>=18'} + + parse-statements@1.0.11: + resolution: {integrity: sha512-HlsyYdMBnbPQ9Jr/VgJ1YF4scnldvJpJxCVx6KgqPL4dxppsWrJHCIIxQXMJrqGnsRkNPATbeMJ8Yxu7JMsYcA==} + path-browserify@1.0.1: resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} @@ -3090,13 +3377,12 @@ packages: resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} engines: {node: '>=16 || 14 >=14.18'} - path-type@4.0.0: - resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} - engines: {node: '>=8'} - pathe@1.1.2: resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} + pathe@2.0.3: + resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + perfect-debounce@1.0.0: resolution: {integrity: sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==} @@ -3107,32 +3393,32 @@ packages: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} - picomatch@4.0.2: - resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==} + picomatch@4.0.3: + resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} engines: {node: '>=12'} pify@2.3.0: resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} engines: {node: '>=0.10.0'} - pinia@2.2.6: - resolution: {integrity: sha512-vIsR8JkDN5Ga2vAxqOE2cJj4VtsHnzpR1Fz30kClxlh0yCHfec6uoMeM3e/ddqmwFUejK3NlrcQa/shnpyT4hA==} + pinia@2.3.1: + resolution: {integrity: sha512-khUlZSwt9xXCaTbbxFYBKDc/bWAGWJjOgvxETwkTN7KRm66EeT1ZdZj6i2ceh9sP2Pzqsbc704r2yngBrxBVug==} peerDependencies: - '@vue/composition-api': ^1.4.0 typescript: '>=4.4.4' - vue: ^2.6.14 || ^3.5.11 + vue: ^2.7.0 || ^3.5.11 peerDependenciesMeta: - '@vue/composition-api': - optional: true typescript: optional: true - pirates@4.0.6: - resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==} + pirates@4.0.7: + resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==} engines: {node: '>= 6'} - pkg-types@1.2.1: - resolution: {integrity: sha512-sQoqa8alT3nHjGuTjuKgOnvjo4cljkufdtLMnO2LBP/wRwuDlo1tkaEdMxCRhyGRPacv/ztlZgDPm2b7FAmEvw==} + pkg-types@1.3.1: + resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==} + + pkg-types@2.3.0: + resolution: {integrity: sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==} pluralize@8.0.0: resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==} @@ -3144,8 +3430,8 @@ packages: peerDependencies: postcss: ^8.0.0 - postcss-import@16.1.0: - resolution: {integrity: sha512-7hsAZ4xGXl4MW+OKEWCnF6T5jqBw80/EE9aXg1r2yyn1RsVEU8EtKXbijEODa+rg7iih4bKf7vlvTGYR4CnPNg==} + postcss-import@16.1.1: + resolution: {integrity: sha512-2xVS1NCZAfjtVdvXiyegxzJ447GyqCeEI5V7ApgQVOWnros1p5lGNovJNapwPpMombyFBfqDwt7AD3n2l0KOfQ==} engines: {node: '>=18.0.0'} peerDependencies: postcss: ^8.0.0 @@ -3184,15 +3470,15 @@ packages: resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==} engines: {node: '>=4'} - postcss-selector-parser@7.0.0: - resolution: {integrity: sha512-9RbEr1Y7FFfptd/1eEdntyjMwLeghW1bHX9GWjXo19vx4ytPQhANltvVxDggzJl7mnWM+dX28kb6cyS/4iQjlQ==} + postcss-selector-parser@7.1.0: + resolution: {integrity: sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==} engines: {node: '>=4'} postcss-value-parser@4.2.0: resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} - postcss@8.4.47: - resolution: {integrity: sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==} + postcss@8.5.6: + resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} engines: {node: ^10 || ^12 || >=14} prelude-ls@1.2.1: @@ -3203,16 +3489,20 @@ packages: resolution: {integrity: sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==} engines: {node: '>=6.0.0'} - prettier@3.3.3: - resolution: {integrity: sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==} + prettier@3.6.2: + resolution: {integrity: sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==} engines: {node: '>=14'} hasBin: true + pretty-ms@9.2.0: + resolution: {integrity: sha512-4yf0QO/sllf/1zbZWYnvWw3NxCQwLXKzIj0G849LSufP15BXKM0rbD2Z3wVnkMfjdn/CB0Dpp444gYAACdsplg==} + engines: {node: '>=18'} + primeicons@7.0.0: resolution: {integrity: sha512-jK3Et9UzwzTsd6tzl2RmwrVY/b8raJ3QZLzoDACj+oTJ0oX7L9Hy+XnVwgo4QVKlKpnP/Ur13SXV/pVh4LzaDw==} - primevue@4.3.3: - resolution: {integrity: sha512-nooYVoEz5CdP3EhUkD6c3qTdRmpLHZh75fBynkUkl46K8y5rksHTjdSISiDijwTA5STQIOkyqLb+RM+HQ6nC1Q==} + primevue@4.3.9: + resolution: {integrity: sha512-qEAm/ZwtfBJAxHBhmXxsZ4NM6StRDeMVg8146HBn1xZ+0wiYhhJXH4uEttBTBhUgRhmZ00n6bANiL+ib9ylNjg==} engines: {node: '>=12.11.0'} proxy-from-env@1.1.0: @@ -3226,6 +3516,9 @@ packages: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} + quansync@0.2.11: + resolution: {integrity: sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==} + queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} @@ -3275,19 +3568,20 @@ packages: resolve-pkg-maps@1.0.0: resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} - resolve@1.22.8: - resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} + resolve@1.22.10: + resolution: {integrity: sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==} + engines: {node: '>= 0.4'} hasBin: true - reusify@1.0.4: - resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} + reusify@1.1.0: + resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} rfdc@1.4.1: resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} - rollup@4.24.3: - resolution: {integrity: sha512-HBW896xR5HGmoksbi3JBDtmVzWiPAYqp7wip50hjQ67JbDz61nyoMPdqu1DvVW9asYb2M65Z20ZHsyJCMqMyDg==} + rollup@4.50.1: + resolution: {integrity: sha512-78E9voJHwnXQMiQdiqswVLZwJIzdBKJ1GdI5Zx6XwoFKUIk09/sSrr+05QFzvYb8q6Y9pPV45zzDuYa3907TZA==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true @@ -3322,8 +3616,8 @@ packages: engines: {node: '>=10'} hasBin: true - semver@7.6.3: - resolution: {integrity: sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==} + semver@7.7.2: + resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==} engines: {node: '>=10'} hasBin: true @@ -3342,24 +3636,13 @@ packages: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} engines: {node: '>=14'} - sirv@2.0.4: - resolution: {integrity: sha512-94Bdh3cC2PKrbgSOUqTiGPWVZeSiXfKOVZNJniWoqrWrRkB1CJzBU3NEbiTsPcYy1lDsANA/THzS+9WBiy5nfQ==} - engines: {node: '>= 10'} - - sirv@3.0.0: - resolution: {integrity: sha512-BPwJGUeDaDCHihkORDchNyyTvWFhcusy1XMmhEVTQTwGeybFbp8YEmB+njbPnth1FibULBSBVwCQni25XlCUDg==} + sirv@3.0.2: + resolution: {integrity: sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==} engines: {node: '>=18'} sisteransi@1.0.5: resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} - slash@3.0.0: - resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} - engines: {node: '>=8'} - - slashes@3.0.12: - resolution: {integrity: sha512-Q9VME8WyGkc7pJf6QEkj3wE+2CnvZMI+XJhwdTPR8Z/kWQRXi7boAWLDibRPyHRTUTPx5FaU7MsyrjI3yLB4HA==} - source-map-js@1.2.1: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} @@ -3380,8 +3663,8 @@ packages: spdx-expression-parse@4.0.0: resolution: {integrity: sha512-Clya5JIij/7C6bRR22+tnGXbc4VKlibKSVj2iHvVeX5iMW7s1SIQlqu699JkODJJIhh/pUu8L0/VLh8xflD+LQ==} - spdx-license-ids@3.0.20: - resolution: {integrity: sha512-jg25NiDV/1fLtSgEgyvVyDunvaNHbuwF9lfNV17gSmPFAlYzdfNBlLtLzXTevwkPj7DhGbmN9VnmJIgLnhvaBw==} + spdx-license-ids@3.0.22: + resolution: {integrity: sha512-4PRT4nh1EImPbt2jASOKHX7PB7I+e4IWNLvkKFDxNhJlfjbYlleYQh285Z/3mPTHSAK/AvdMmw5BNNuYH8ShgQ==} speakingurl@14.0.1: resolution: {integrity: sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==} @@ -3390,8 +3673,9 @@ packages: sprintf-js@1.0.3: resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} - stable-hash@0.0.4: - resolution: {integrity: sha512-LjdcbuBeLcdETCrPn9i8AYAZ1eCtu4ECAWtP7UleOiZ9LzVxRzzUZEoZ8zB24nhkQnDWyET0I+3sWokSDS3E7g==} + stable-hash-x@0.2.0: + resolution: {integrity: sha512-o3yWv49B/o4QZk5ZcsALc6t0+eCelPc44zZsLtCQnZPDwFpDYSWcDnrv2TtMmMbQ7uKo3J0HTURCqckw23czNQ==} + engines: {node: '>=12.0.0'} string-argv@0.3.2: resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==} @@ -3421,6 +3705,10 @@ packages: resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==} engines: {node: '>=12'} + strip-final-newline@4.0.0: + resolution: {integrity: sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==} + engines: {node: '>=18'} + strip-indent@3.0.0: resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} engines: {node: '>=8'} @@ -3429,16 +3717,16 @@ packages: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} - strip-literal@2.1.0: - resolution: {integrity: sha512-Op+UycaUt/8FbN/Z2TWPBLge3jWrP3xj10f3fnYxf052bKuS3EKs1ZQcVGjnEMdsNVAM+plXRdmjrZ/KgG3Skw==} + strip-literal@2.1.1: + resolution: {integrity: sha512-631UJ6O00eNGfMiWG78ck80dfBab8X6IVFB51jZK5Icd7XAs60Z5y7QdSd/wGIklnWvRbUNloVzhOKKmutxQ6Q==} sucrase@3.35.0: resolution: {integrity: sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==} engines: {node: '>=16 || 14 >=14.17'} hasBin: true - superjson@2.2.1: - resolution: {integrity: sha512-8iGv75BYOa0xRJHK5vRLEjE2H/i4lulTjzpUXic3Eg8akftYjkmQDa8JARQ42rlczXyFR3IeRoeFCc7RxHsYZA==} + superjson@2.2.2: + resolution: {integrity: sha512-5JRxVqC8I8NuOUjzBbvVJAKNM8qoVuH0O77h4WInc/qC2q5IreqKxYwgkga3PfA22OayK2ikceb/B26dztPl+Q==} engines: {node: '>=16'} supports-color@7.2.0: @@ -3453,15 +3741,12 @@ packages: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} - svg-tags@1.0.0: - resolution: {integrity: sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==} - - synckit@0.6.2: - resolution: {integrity: sha512-Vhf+bUa//YSTYKseDiiEuQmhGCoIF3CVBhunm3r/DQnYiGT4JssmnKQc44BIyOZRK2pKjXXAgbhfmbeoC9CJpA==} - engines: {node: '>=12.20'} + synckit@0.11.11: + resolution: {integrity: sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw==} + engines: {node: ^14.18.0 || >=16.0.0} - synckit@0.9.2: - resolution: {integrity: sha512-vrozgXDQwYO72vHjUb/HnFbQx1exDjoKzqx23aXEg2a9VIg2TSFZ8FmeZpTjUCFMYw7mpX4BE2SFu8wI7asYsw==} + synckit@0.9.3: + resolution: {integrity: sha512-JJoOEKTfL1urb1mDoEblhD9NhEbWmq9jHEMEnxoC4ujUaZ4itA8vKgwkFAyNClgxplLi9tsUKX+EduK0p/l7sg==} engines: {node: ^14.18.0 || >=16.0.0} tailwindcss-primeui@0.3.4: @@ -3474,13 +3759,10 @@ packages: engines: {node: '>=14.0.0'} hasBin: true - tapable@2.2.1: - resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==} + tapable@2.2.3: + resolution: {integrity: sha512-ZL6DDuAlRlLGghwcfmSn9sK3Hr6ArtyudlSAiCqQ6IfE+b+HHbydbYDIG15IfS5do+7XQQBdBiubF/cV2dnDzg==} engines: {node: '>=6'} - text-table@0.2.0: - resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} - thenify-all@1.6.0: resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} engines: {node: '>=0.8'} @@ -3488,8 +3770,8 @@ packages: thenify@3.3.1: resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} - tinyexec@0.3.1: - resolution: {integrity: sha512-WiCJLEECkO18gwqIp6+hJg0//p23HXp4S+gGtAKu3mI2F2/sXC4FvHvXvB0zJVVaTPhx1/tOwdbRsa1sOBIKqQ==} + tinyexec@1.0.1: + resolution: {integrity: sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw==} to-regex-range@5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} @@ -3507,15 +3789,23 @@ packages: resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} engines: {node: '>=6'} - ts-api-utils@1.4.0: - resolution: {integrity: sha512-032cPxaEKwM+GT3vA5JXNzIaizx388rhsSW79vGRNGXfRRAdEAn2mvk36PvK5HnOchyWZ7afLEXqYCvPCrzuzQ==} - engines: {node: '>=16'} + ts-api-utils@2.1.0: + resolution: {integrity: sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==} + engines: {node: '>=18.12'} + peerDependencies: + typescript: '>=4.8.4' + + ts-declaration-location@1.0.7: + resolution: {integrity: sha512-EDyGAwH1gO0Ausm9gV6T2nUvBgXT5kGoCMJPllOaooZ+4VvJiKBdZE7wK18N1deEowhcUptS+5GXZK8U/fvpwA==} peerDependencies: - typescript: '>=4.2.0' + typescript: '>=4.0.0' ts-interface-checker@0.1.13: resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} + ts-macro@0.1.35: + resolution: {integrity: sha512-cMPJUCH8VsH9s9FANjL1r/SrkV2T6CKBjgWqgP2XGiS+y/zBBwmw0C3C31M4LqrLEjb8djgUMDV18vQ4Dr+/mg==} + ts-md5@1.3.1: resolution: {integrity: sha512-DiwiXfwvcTeZ5wCE0z+2A9EseZsztaiZtGrtSaY5JOD7ekPnR/GoIVD5gXZAlK9Na9Kvpo9Waz5rW64WKAWApg==} engines: {node: '>=12'} @@ -3523,8 +3813,8 @@ packages: tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} - tsx@4.19.2: - resolution: {integrity: sha512-pOUl6Vo2LUq/bSa8S5q7b91cgNSjctn9ugq/+Mvow99qW6x/UZYwzxy/3NmqoT66eHYfCVvFvACC58UBPFf28g==} + tsx@4.20.5: + resolution: {integrity: sha512-+wKjMNU9w/EaQayHXb7WA7ZaHY6hN8WgfvHNQ3t1PnU91/7O8TcTnIhCDYTZwnt8JsO9IBqZ30Ln1r7pPF52Aw==} engines: {node: '>=18.0.0'} hasBin: true @@ -3544,30 +3834,37 @@ packages: resolution: {integrity: sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==} engines: {node: '>=8'} - typescript@5.4.2: - resolution: {integrity: sha512-+2/g0Fds1ERlP6JsakQQDXjZdZMM+rqpamFZJEKh4kwTIn3iDkgKtby0CeNd5ATNZ4Ry1ax15TMx0W2V+miizQ==} + typescript@5.6.3: + resolution: {integrity: sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==} engines: {node: '>=14.17'} hasBin: true - typescript@5.6.3: - resolution: {integrity: sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==} + typescript@5.8.2: + resolution: {integrity: sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==} engines: {node: '>=14.17'} hasBin: true uc.micro@2.1.0: resolution: {integrity: sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==} - ufo@1.5.4: - resolution: {integrity: sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ==} + ufo@1.6.1: + resolution: {integrity: sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==} + + unconfig@0.6.1: + resolution: {integrity: sha512-cVU+/sPloZqOyJEAfNwnQSFCzFrZm85vcVkryH7lnlB/PiTycUkAjt5Ds79cfIshGOZ+M5v3PBDnKgpmlE5DtA==} - unconfig@0.6.0: - resolution: {integrity: sha512-4C67J0nIF2QwSXty2kW3zZx1pMZ3iXabylvJWWgHybWVUcMf9pxwsngoQt0gC+AVstRywFqrRBp3qOXJayhpOw==} + unconfig@7.3.3: + resolution: {integrity: sha512-QCkQoOnJF8L107gxfHL0uavn7WD9b3dpBcFX6HtfQYmjw2YzWxGuFQ0N0J6tE9oguCBJn9KOvfqYDCMPHIZrBA==} - undici-types@6.19.8: - resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==} + undici-types@6.21.0: + resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} - unimport@3.13.1: - resolution: {integrity: sha512-nNrVzcs93yrZQOW77qnyOVHtb68LegvhYFwxFMfuuWScmwQmyVCG/NBuN8tYsaGzgQUVYv34E/af+Cc9u4og4A==} + unicorn-magic@0.3.0: + resolution: {integrity: sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==} + engines: {node: '>=18'} + + unimport@3.14.6: + resolution: {integrity: sha512-CYvbDaTT04Rh8bmD8jz3WPmHYZRG/NnvYVzwD6V1YAlvvKROlAeNDUBhkBGzNav2RKaeuXvlWYaa1V4Lfi/O0g==} unist-util-is@6.0.0: resolution: {integrity: sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==} @@ -3581,16 +3878,12 @@ packages: unist-util-visit@5.0.0: resolution: {integrity: sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==} - universalify@0.1.2: - resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} - engines: {node: '>= 4.0.0'} - universalify@2.0.1: resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} engines: {node: '>= 10.0.0'} - unplugin-auto-import@0.18.3: - resolution: {integrity: sha512-q3FUtGQjYA2e+kb1WumyiQMjHM27MrTQ05QfVwtLRVhyYe+KF6TblBYaEX9L6Z0EibsqaXAiW+RFfkcQpfaXzg==} + unplugin-auto-import@0.18.6: + resolution: {integrity: sha512-LMFzX5DtkTj/3wZuyG5bgKBoJ7WSgzqSGJ8ppDRdlvPh45mx6t6w3OcbExQi53n3xF5MYkNGPNR/HYOL95KL2A==} engines: {node: '>=14'} peerDependencies: '@nuxt/kit': ^3.2.2 @@ -3601,14 +3894,15 @@ packages: '@vueuse/core': optional: true - unplugin-combine@1.0.3: - resolution: {integrity: sha512-vCpXdYCTcGwRGv7iF/COh7dupqyIrRxwe5kTKF3ZiVnO4toyvU+tpoTj570Bf9SpJG4JspGnfjcZIU6SBIKryA==} + unplugin-combine@1.2.1: + resolution: {integrity: sha512-qGkXjQo8yTq5QknP8f8p8/Aw3BJKqclTbTe8de0pC6exHzpoPBnH69Eztf00G2oc50IaIlV7KX/g4cKgzCq9BA==} engines: {node: '>=16.14.0'} peerDependencies: '@rspack/core': '*' esbuild: '>=0.13' rolldown: '*' rollup: ^3.2.0 || ^4.0.0 + unplugin: ^1.0.0 || ^2.0.0 vite: ^2.3.0 || ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0-0 webpack: 4 || 5 peerDependenciesMeta: @@ -3620,13 +3914,15 @@ packages: optional: true rollup: optional: true + unplugin: + optional: true vite: optional: true webpack: optional: true - unplugin-vue-components@0.27.4: - resolution: {integrity: sha512-1XVl5iXG7P1UrOMnaj2ogYa5YTq8aoh5jwDPQhemwO/OrXW+lPQKDXd1hMz15qxQPxgb/XXlbgo3HQ2rLEbmXQ==} + unplugin-vue-components@0.27.5: + resolution: {integrity: sha512-m9j4goBeNwXyNN8oZHHxvIIYiG8FQ9UfmKWeNllpDvhU7btKNNELGPt+o3mckQKuPwrE7e0PvCsx+IWuDSD9Vg==} engines: {node: '>=14'} peerDependencies: '@babel/parser': ^7.15.8 @@ -3638,40 +3934,42 @@ packages: '@nuxt/kit': optional: true - unplugin-vue-define-options@1.5.2: - resolution: {integrity: sha512-Ghp32GLCNo94olEwpRiSvF3a2fEIfgvw5YJFTWIofaf1i50f7bgZA65AFwioYRG1+Bg0xl4ch4zRXc+KzNrJfA==} + unplugin-vue-define-options@1.5.5: + resolution: {integrity: sha512-V50sWbpoADsjyVgovxewoLo2IDW0zfgHJbKiAl2EdZT8OL3g3h1Mz3QKoAAu09i8+LnkDatIEQMgBVeHHxWXNg==} engines: {node: '>=16.14.0'} - unplugin-vue-macros@2.13.3: - resolution: {integrity: sha512-zjcrh+qmQLvq0iEUEhtNc7PsGDpX/C7XS0OVdePIhgRJx7ZwY7Lpy1aI1SLyeiKA27XhHt2E21SzxFFEoCbb5A==} + unplugin-vue-macros@2.14.5: + resolution: {integrity: sha512-jlZhsr26/wreKBrkX6BM21Mpm9DbS6H2H0aMrd3gu/wabA3YWUj/t+zqZD5Y5yShaTKO/03yJjb5BfPck9mPtw==} engines: {node: '>=16.14.0'} peerDependencies: vue: ^2.7.0 || ^3.2.25 - unplugin-vue-markdown@0.26.2: - resolution: {integrity: sha512-FjmhLZ+RRx7PFmfBCTwNUZLAj0Y9z0y/j79rTgYuXH9u+K6tZBFB+GpFFBm+4yMQ0la3MNCl7KHbaSvfna2bEA==} + unplugin-vue-markdown@0.26.3: + resolution: {integrity: sha512-F70u5BuXLn/08jlcp2iUmU60yBLxRwvUZQ4Ys6y9TPS+VkEqlVBXYHc+1dHjycQZK13LAsMWN3FofeXJlJpzdg==} peerDependencies: - vite: ^2.0.0 || ^3.0.0-0 || ^4.0.0-0 || ^5.0.0-0 + vite: ^2.0.0 || ^3.0.0-0 || ^4.0.0-0 || ^5.0.0-0 || ^6.0.0 - unplugin-vue-router@0.10.8: - resolution: {integrity: sha512-xi+eLweYAqolIoTRSmumbi6Yx0z5M0PLvl+NFNVWHJgmE2ByJG1SZbrn+TqyuDtIyln20KKgq8tqmL7aLoiFjw==} + unplugin-vue-router@0.10.9: + resolution: {integrity: sha512-DXmC0GMcROOnCmN56GRvi1bkkG1BnVs4xJqNvucBUeZkmB245URvtxOfbo3H6q4SOUQQbLPYWd6InzvjRh363A==} peerDependencies: vue-router: ^4.4.0 peerDependenciesMeta: vue-router: optional: true - unplugin@1.15.0: - resolution: {integrity: sha512-jTPIs63W+DUEDW207ztbaoO7cQ4p5aVaB823LSlxpsFEU3Mykwxf3ZGC/wzxFJeZlASZYgVrWeo7LgOrqJZ8RA==} + unplugin@1.16.1: + resolution: {integrity: sha512-4/u/j4FrCKdi17jaxuJA0jClGxB1AvU2hw/IuayPc4ay1XGaJs/rbb4v5WKwAjNifjmXK9PIFyuPiaK8azyR9w==} engines: {node: '>=14.0.0'} - peerDependencies: - webpack-sources: ^3 - peerDependenciesMeta: - webpack-sources: - optional: true - update-browserslist-db@1.1.1: - resolution: {integrity: sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==} + unplugin@2.0.0-beta.1: + resolution: {integrity: sha512-2qzQo5LN2DmUZXkWDHvGKLF5BP0WN+KthD6aPnPJ8plRBIjv4lh5O07eYcSxgO2znNw9s4MNhEO1sB+JDllDbQ==} + engines: {node: '>=18.12.0'} + + unrs-resolver@1.11.1: + resolution: {integrity: sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==} + + update-browserslist-db@1.1.3: + resolution: {integrity: sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==} hasBin: true peerDependencies: browserslist: '>= 4.21.0' @@ -3686,21 +3984,20 @@ packages: resolution: {integrity: sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==} hasBin: true - uuid@11.0.2: - resolution: {integrity: sha512-14FfcOJmqdjbBPdDjFQyk/SdT4NySW4eM0zcG+HqbHP5jzuH56xO3J1DGhgs/cEMCfwYi3HQI1gnTO62iaG+tQ==} + uuid@11.1.0: + resolution: {integrity: sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==} hasBin: true validate-npm-package-license@3.0.4: resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} - vite-hot-client@0.2.3: - resolution: {integrity: sha512-rOGAV7rUlUHX89fP2p2v0A2WWvV3QMX2UYq0fRqsWSvFvev4atHWqjwGoKaZT1VTKyLGk533ecu3eyd0o59CAg==} + vite-hot-client@2.1.0: + resolution: {integrity: sha512-7SpgZmU7R+dDnSmvXE1mfDtnHLHQSisdySVR7lO8ceAXvM0otZeuQQ6C8LrS5d/aYyP/QZ0hI0L+dIPrm4YlFQ==} peerDependencies: - vite: ^2.6.0 || ^3.0.0 || ^4.0.0 || ^5.0.0-0 + vite: ^2.6.0 || ^3.0.0 || ^4.0.0 || ^5.0.0-0 || ^6.0.0-0 || ^7.0.0-0 - vite-plugin-dts@4.3.0: - resolution: {integrity: sha512-LkBJh9IbLwL6/rxh0C1/bOurDrIEmRE7joC+jFdOEEciAFPbpEKOLSAr5nNh5R7CJ45cMbksTrFfy52szzC5eA==} - engines: {node: ^14.18.0 || >=16.0.0} + vite-plugin-dts@4.5.4: + resolution: {integrity: sha512-d4sOM8M/8z7vRXHHq/ebbblfaxENjogAAekcfcDCCwAyvGqnPrc7f4NZbvItS+g4WTgerW0xDwSz5qz11JT3vg==} peerDependencies: typescript: '*' vite: '*' @@ -3708,33 +4005,33 @@ packages: vite: optional: true - vite-plugin-inspect@0.8.7: - resolution: {integrity: sha512-/XXou3MVc13A5O9/2Nd6xczjrUwt7ZyI9h8pTnUMkr5SshLcb0PJUOVq2V+XVkdeU4njsqAtmK87THZuO2coGA==} + vite-plugin-inspect@0.8.9: + resolution: {integrity: sha512-22/8qn+LYonzibb1VeFZmISdVao5kC22jmEKm24vfFE8siEn47EpVcCLYMv6iKOYMJfjSvSJfueOwcFCkUnV3A==} engines: {node: '>=14'} peerDependencies: '@nuxt/kit': '*' - vite: ^3.1.0 || ^4.0.0 || ^5.0.0-0 + vite: ^3.1.0 || ^4.0.0 || ^5.0.0-0 || ^6.0.1 peerDependenciesMeta: '@nuxt/kit': optional: true - vite-plugin-singlefile@2.0.3: - resolution: {integrity: sha512-OEBEwdX8nCGPSdtaB1D7rryYnT+YfPTS8ojL1TDyeUF+bWDCTfRriQqw6T0vl9EbKI/KMg7szN3awst6cLrKkA==} + vite-plugin-singlefile@2.3.0: + resolution: {integrity: sha512-DAcHzYypM0CasNLSz/WG0VdKOCxGHErfrjOoyIPiNxTPTGmO6rRD/te93n1YL/s+miXq66ipF1brMBikf99c6A==} engines: {node: '>18.0.0'} peerDependencies: - rollup: ^4.24.3 - vite: ^5.4.10 + rollup: ^4.44.1 + vite: ^5.4.11 || ^6.0.0 || ^7.0.0 - vite-plugin-vue-devtools@7.6.3: - resolution: {integrity: sha512-p1rZMKzreWqxj9U05RaxY1vDoOhGYhA6iX8vKfo4nD6jqTmVoGjjk+U1g5HYwwTCdr/eck3kzO2f4gnPCjqVKA==} + vite-plugin-vue-devtools@7.7.7: + resolution: {integrity: sha512-d0fIh3wRcgSlr4Vz7bAk4va1MkdqhQgj9ANE/rBhsAjOnRfTLs2ocjFMvSUOsv6SRRXU9G+VM7yMgqDb6yI4iQ==} engines: {node: '>=v14.21.3'} peerDependencies: - vite: ^3.1.0 || ^4.0.0-0 || ^5.0.0-0 + vite: ^3.1.0 || ^4.0.0-0 || ^5.0.0-0 || ^6.0.0-0 || ^7.0.0-0 - vite-plugin-vue-inspector@5.2.0: - resolution: {integrity: sha512-wWxyb9XAtaIvV/Lr7cqB1HIzmHZFVUJsTNm3yAxkS87dgh/Ky4qr2wDEWNxF23fdhVa3jQ8MZREpr4XyiuaRqA==} + vite-plugin-vue-inspector@5.3.2: + resolution: {integrity: sha512-YvEKooQcSiBTAs0DoYLfefNja9bLgkFM7NI2b07bE2SruuvX0MEa9cMaxjKVMkeCp5Nz9FRIdcN1rOdFVBeL6Q==} peerDependencies: - vite: ^3.0.0-0 || ^4.0.0-0 || ^5.0.0-0 + vite: ^3.0.0-0 || ^4.0.0-0 || ^5.0.0-0 || ^6.0.0-0 || ^7.0.0-0 vite-plugin-vue-layouts@0.11.0: resolution: {integrity: sha512-uh6NW7lt+aOXujK4eHfiNbeo55K9OTuB7fnv+5RVc4OBn/cZull6ThXdYH03JzKanUfgt6QZ37NbbtJ0og59qw==} @@ -3743,8 +4040,8 @@ packages: vue: ^3.2.4 vue-router: ^4.0.11 - vite@5.4.10: - resolution: {integrity: sha512-1hvaPshuPUtxeQ0hsVH3Mud0ZanOLwVTneA1EgbAM5LhaZEqyPWGRQ7BtaMvUrTDeEaC8pxtj6a6jku3x4z6SQ==} + vite@5.4.19: + resolution: {integrity: sha512-qO3aKv3HoQC8QKiNSTuUM1l9o/XX3+c+VTgLHbJWHZGeTPVAg2XwazI9UWzoxjIJCGCV2zU60uqMzjeLZuULqA==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true peerDependencies: @@ -3774,8 +4071,14 @@ packages: terser: optional: true - vscode-uri@3.0.8: - resolution: {integrity: sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw==} + vscode-uri@3.1.0: + resolution: {integrity: sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==} + + vue-chartjs@5.3.2: + resolution: {integrity: sha512-NrkbRRoYshbXbWqJkTN6InoDVwVb90C0R7eAVgMWcB9dPikbruaOoTFjFYHE/+tNPdIe6qdLCDjfjPHQ0fw4jw==} + peerDependencies: + chart.js: ^4.1.1 + vue: ^3.0.0-0 || ^2.7.0 vue-demi@0.14.10: resolution: {integrity: sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==} @@ -3794,14 +4097,14 @@ packages: peerDependencies: eslint: '>=6.0.0' - vue-i18n@10.0.4: - resolution: {integrity: sha512-1xkzVxqBLk2ZFOmeI+B5r1J7aD/WtNJ4j9k2mcFcQo5BnOmHBmD7z4/oZohh96AAaRZ4Q7mNQvxc9h+aT+Md3w==} + vue-i18n@10.0.8: + resolution: {integrity: sha512-mIjy4utxMz9lMMo6G9vYePv7gUFt4ztOMhY9/4czDJxZ26xPeJ49MAGa9wBAE3XuXbYCrtVPmPxNjej7JJJkZQ==} engines: {node: '>= 16'} peerDependencies: vue: ^3.0.0 - vue-i18n@9.14.4: - resolution: {integrity: sha512-B934C8yUyWLT0EMud3DySrwSUJI7ZNiWYsEEz2gknTthqKiG4dzWE/WSa8AzCuSQzwBEv4HtG1jZDhgzPfWSKQ==} + vue-i18n@9.14.5: + resolution: {integrity: sha512-0jQ9Em3ymWngyiIkj0+c/k7WgaPO+TNzjKSNq9BvBQaKJECqn9cd9fL4tkDhB5G1QBskGl9YxxbDAhgbFtpe2g==} engines: {node: '>= 16'} peerDependencies: vue: ^3.0.0 @@ -3811,19 +4114,19 @@ packages: peerDependencies: vue: ^3.0.0 - vue-router@4.4.5: - resolution: {integrity: sha512-4fKZygS8cH1yCyuabAXGUAsyi1b2/o/OKgu/RUb+znIYOxPRxdkytJEx+0wGcpBE1pX6vUgh5jwWOKRGvuA/7Q==} + vue-router@4.5.1: + resolution: {integrity: sha512-ogAF3P97NPm8fJsE4by9dwSYtDwXIY1nFY9T6DyQnGHd1E2Da94w9JIolpe42LJGIl0DwOHBi8TcRPlPGwbTtw==} peerDependencies: vue: ^3.2.0 - vue-tsc@2.1.10: - resolution: {integrity: sha512-RBNSfaaRHcN5uqVqJSZh++Gy/YUzryuv9u1aFWhsammDJXNtUiJMNoJ747lZcQ68wUQFx6E73y4FY3D8E7FGMA==} + vue-tsc@2.2.12: + resolution: {integrity: sha512-P7OP77b2h/Pmk+lZdJ0YWs+5tJ6J2+uOQPo7tlBnY44QqQSPYvS0qVT4wqDJgwrZaLe47etJLLQRFia71GYITw==} hasBin: true peerDependencies: typescript: '>=5.0.0' - vue@3.5.12: - resolution: {integrity: sha512-CLVZtXtn2ItBIi/zHZ0Sg1Xkb7+PU32bJJ8Bmy7ts3jxXTcbfsEfBivFYYWz1Hur+lalqGAh65Coin0r+HRUfg==} + vue@3.5.21: + resolution: {integrity: sha512-xxf9rum9KtOdwdRkiApWL+9hZEMWE90FHh8yS1+KJAiWYh+iGWV1FquPjoO9VUHQ+VIhsCXNNyZ5Sf4++RVZBA==} peerDependencies: typescript: '*' peerDependenciesMeta: @@ -3850,6 +4153,10 @@ packages: resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} engines: {node: '>=12'} + wsl-utils@0.1.0: + resolution: {integrity: sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==} + engines: {node: '>=18'} + xml-name-validator@4.0.0: resolution: {integrity: sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==} engines: {node: '>=12'} @@ -3864,13 +4171,13 @@ packages: yallist@4.0.0: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} - yaml-eslint-parser@1.2.3: - resolution: {integrity: sha512-4wZWvE398hCP7O8n3nXKu/vdq1HcH01ixYlCREaJL5NUMwQ0g3MaGFUBNSlmBtKmhbtVG/Cm6lyYmSVTEVil8A==} + yaml-eslint-parser@1.3.0: + resolution: {integrity: sha512-E/+VitOorXSLiAqtTd7Yqax0/pAS3xaYMP+AUUJGOK1OZG3rhcj9fcJOM5HJ2VrP1FrStVCWr1muTfQCdj4tAA==} engines: {node: ^14.17.0 || >=16.0.0} - yaml@2.6.0: - resolution: {integrity: sha512-a6ae//JvKDEra2kdi1qzCyrJW/WZCgFi8ydDV+eXExl95t+5R+ijnqHJbz9tmMh8FUjx3iv2fCQ4dclAQlO2UQ==} - engines: {node: '>= 14'} + yaml@2.8.1: + resolution: {integrity: sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==} + engines: {node: '>= 14.6'} hasBin: true yargs-parser@21.1.1: @@ -3885,6 +4192,10 @@ packages: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} + yoctocolors@2.1.2: + resolution: {integrity: sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==} + engines: {node: '>=18'} + zwitch@2.0.4: resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} @@ -3892,263 +4203,263 @@ snapshots: '@alloc/quick-lru@5.2.0': {} - '@ampproject/remapping@2.3.0': - dependencies: - '@jridgewell/gen-mapping': 0.3.5 - '@jridgewell/trace-mapping': 0.3.25 - - '@antfu/eslint-config@3.8.0(@typescript-eslint/utils@8.13.0(eslint@9.14.0(jiti@2.4.0))(typescript@5.6.3))(@vue/compiler-sfc@3.5.12)(eslint-plugin-format@0.1.2(eslint@9.14.0(jiti@2.4.0)))(eslint@9.14.0(jiti@2.4.0))(typescript@5.6.3)': - dependencies: - '@antfu/install-pkg': 0.4.1 - '@clack/prompts': 0.7.0 - '@eslint-community/eslint-plugin-eslint-comments': 4.4.1(eslint@9.14.0(jiti@2.4.0)) - '@eslint/markdown': 6.2.1 - '@stylistic/eslint-plugin': 2.10.1(eslint@9.14.0(jiti@2.4.0))(typescript@5.6.3) - '@typescript-eslint/eslint-plugin': 8.13.0(@typescript-eslint/parser@8.13.0(eslint@9.14.0(jiti@2.4.0))(typescript@5.6.3))(eslint@9.14.0(jiti@2.4.0))(typescript@5.6.3) - '@typescript-eslint/parser': 8.13.0(eslint@9.14.0(jiti@2.4.0))(typescript@5.6.3) - '@vitest/eslint-plugin': 1.1.7(@typescript-eslint/utils@8.13.0(eslint@9.14.0(jiti@2.4.0))(typescript@5.6.3))(eslint@9.14.0(jiti@2.4.0))(typescript@5.6.3) - eslint: 9.14.0(jiti@2.4.0) - eslint-config-flat-gitignore: 0.3.0(eslint@9.14.0(jiti@2.4.0)) - eslint-flat-config-utils: 0.4.0 - eslint-merge-processors: 0.1.0(eslint@9.14.0(jiti@2.4.0)) - eslint-plugin-antfu: 2.7.0(eslint@9.14.0(jiti@2.4.0)) - eslint-plugin-command: 0.2.6(eslint@9.14.0(jiti@2.4.0)) - eslint-plugin-import-x: 4.4.0(eslint@9.14.0(jiti@2.4.0))(typescript@5.6.3) - eslint-plugin-jsdoc: 50.4.3(eslint@9.14.0(jiti@2.4.0)) - eslint-plugin-jsonc: 2.17.0(eslint@9.14.0(jiti@2.4.0)) - eslint-plugin-n: 17.13.1(eslint@9.14.0(jiti@2.4.0)) + '@antfu/eslint-config@3.16.0(@typescript-eslint/utils@8.42.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.6.3))(@vue/compiler-sfc@3.5.21)(eslint-plugin-format@0.1.3(eslint@9.35.0(jiti@2.5.1)))(eslint@9.35.0(jiti@2.5.1))(typescript@5.6.3)': + dependencies: + '@antfu/install-pkg': 1.1.0 + '@clack/prompts': 0.9.1 + '@eslint-community/eslint-plugin-eslint-comments': 4.5.0(eslint@9.35.0(jiti@2.5.1)) + '@eslint/markdown': 6.6.0 + '@stylistic/eslint-plugin': 2.13.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.6.3) + '@typescript-eslint/eslint-plugin': 8.42.0(@typescript-eslint/parser@8.42.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.6.3))(eslint@9.35.0(jiti@2.5.1))(typescript@5.6.3) + '@typescript-eslint/parser': 8.42.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.6.3) + '@vitest/eslint-plugin': 1.3.9(eslint@9.35.0(jiti@2.5.1))(typescript@5.6.3) + eslint: 9.35.0(jiti@2.5.1) + eslint-config-flat-gitignore: 1.0.1(eslint@9.35.0(jiti@2.5.1)) + eslint-flat-config-utils: 1.1.0 + eslint-merge-processors: 1.0.0(eslint@9.35.0(jiti@2.5.1)) + eslint-plugin-antfu: 2.7.0(eslint@9.35.0(jiti@2.5.1)) + eslint-plugin-command: 2.1.0(eslint@9.35.0(jiti@2.5.1)) + eslint-plugin-import-x: 4.16.1(@typescript-eslint/utils@8.42.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.6.3))(eslint@9.35.0(jiti@2.5.1)) + eslint-plugin-jsdoc: 50.8.0(eslint@9.35.0(jiti@2.5.1)) + eslint-plugin-jsonc: 2.20.1(eslint@9.35.0(jiti@2.5.1)) + eslint-plugin-n: 17.21.3(eslint@9.35.0(jiti@2.5.1))(typescript@5.6.3) eslint-plugin-no-only-tests: 3.3.0 - eslint-plugin-perfectionist: 3.9.1(eslint@9.14.0(jiti@2.4.0))(typescript@5.6.3)(vue-eslint-parser@9.4.3(eslint@9.14.0(jiti@2.4.0))) - eslint-plugin-regexp: 2.6.0(eslint@9.14.0(jiti@2.4.0)) - eslint-plugin-toml: 0.11.1(eslint@9.14.0(jiti@2.4.0)) - eslint-plugin-unicorn: 56.0.0(eslint@9.14.0(jiti@2.4.0)) - eslint-plugin-unused-imports: 4.1.4(@typescript-eslint/eslint-plugin@8.13.0(@typescript-eslint/parser@8.13.0(eslint@9.14.0(jiti@2.4.0))(typescript@5.6.3))(eslint@9.14.0(jiti@2.4.0))(typescript@5.6.3))(eslint@9.14.0(jiti@2.4.0)) - eslint-plugin-vue: 9.30.0(eslint@9.14.0(jiti@2.4.0)) - eslint-plugin-yml: 1.15.0(eslint@9.14.0(jiti@2.4.0)) - eslint-processor-vue-blocks: 0.1.2(@vue/compiler-sfc@3.5.12)(eslint@9.14.0(jiti@2.4.0)) - globals: 15.12.0 + eslint-plugin-perfectionist: 4.15.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.6.3) + eslint-plugin-regexp: 2.10.0(eslint@9.35.0(jiti@2.5.1)) + eslint-plugin-toml: 0.12.0(eslint@9.35.0(jiti@2.5.1)) + eslint-plugin-unicorn: 56.0.1(eslint@9.35.0(jiti@2.5.1)) + eslint-plugin-unused-imports: 4.2.0(@typescript-eslint/eslint-plugin@8.42.0(@typescript-eslint/parser@8.42.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.6.3))(eslint@9.35.0(jiti@2.5.1))(typescript@5.6.3))(eslint@9.35.0(jiti@2.5.1)) + eslint-plugin-vue: 9.33.0(eslint@9.35.0(jiti@2.5.1)) + eslint-plugin-yml: 1.18.0(eslint@9.35.0(jiti@2.5.1)) + eslint-processor-vue-blocks: 1.0.0(@vue/compiler-sfc@3.5.21)(eslint@9.35.0(jiti@2.5.1)) + globals: 15.15.0 jsonc-eslint-parser: 2.4.0 - local-pkg: 0.5.0 + local-pkg: 1.1.2 parse-gitignore: 2.0.0 picocolors: 1.1.1 toml-eslint-parser: 0.10.0 - vue-eslint-parser: 9.4.3(eslint@9.14.0(jiti@2.4.0)) - yaml-eslint-parser: 1.2.3 + vue-eslint-parser: 9.4.3(eslint@9.35.0(jiti@2.5.1)) + yaml-eslint-parser: 1.3.0 yargs: 17.7.2 optionalDependencies: - eslint-plugin-format: 0.1.2(eslint@9.14.0(jiti@2.4.0)) + eslint-plugin-format: 0.1.3(eslint@9.35.0(jiti@2.5.1)) transitivePeerDependencies: + - '@eslint/json' - '@typescript-eslint/utils' - '@vue/compiler-sfc' + - eslint-import-resolver-node - supports-color - - svelte - typescript - vitest - '@antfu/install-pkg@0.4.1': + '@antfu/install-pkg@1.1.0': dependencies: - package-manager-detector: 0.2.2 - tinyexec: 0.3.1 + package-manager-detector: 1.3.0 + tinyexec: 1.0.1 '@antfu/utils@0.7.10': {} - '@babel/code-frame@7.26.2': + '@antfu/utils@8.1.1': {} + + '@babel/code-frame@7.27.1': dependencies: - '@babel/helper-validator-identifier': 7.25.9 + '@babel/helper-validator-identifier': 7.27.1 js-tokens: 4.0.0 picocolors: 1.1.1 - '@babel/compat-data@7.26.2': {} + '@babel/compat-data@7.28.4': {} - '@babel/core@7.26.0': + '@babel/core@7.28.4': dependencies: - '@ampproject/remapping': 2.3.0 - '@babel/code-frame': 7.26.2 - '@babel/generator': 7.26.2 - '@babel/helper-compilation-targets': 7.25.9 - '@babel/helper-module-transforms': 7.26.0(@babel/core@7.26.0) - '@babel/helpers': 7.26.0 - '@babel/parser': 7.26.2 - '@babel/template': 7.25.9 - '@babel/traverse': 7.25.9 - '@babel/types': 7.26.0 + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.28.3 + '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.4) + '@babel/helpers': 7.28.4 + '@babel/parser': 7.28.4 + '@babel/template': 7.27.2 + '@babel/traverse': 7.28.4 + '@babel/types': 7.28.4 + '@jridgewell/remapping': 2.3.5 convert-source-map: 2.0.0 - debug: 4.3.7 + debug: 4.4.1 gensync: 1.0.0-beta.2 json5: 2.2.3 semver: 6.3.1 transitivePeerDependencies: - supports-color - '@babel/generator@7.26.2': + '@babel/generator@7.28.3': dependencies: - '@babel/parser': 7.26.2 - '@babel/types': 7.26.0 - '@jridgewell/gen-mapping': 0.3.5 - '@jridgewell/trace-mapping': 0.3.25 - jsesc: 3.0.2 + '@babel/parser': 7.28.4 + '@babel/types': 7.28.4 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.30 + jsesc: 3.1.0 - '@babel/helper-annotate-as-pure@7.25.9': + '@babel/helper-annotate-as-pure@7.27.3': dependencies: - '@babel/types': 7.26.0 + '@babel/types': 7.28.4 - '@babel/helper-compilation-targets@7.25.9': + '@babel/helper-compilation-targets@7.27.2': dependencies: - '@babel/compat-data': 7.26.2 - '@babel/helper-validator-option': 7.25.9 - browserslist: 4.24.2 + '@babel/compat-data': 7.28.4 + '@babel/helper-validator-option': 7.27.1 + browserslist: 4.25.4 lru-cache: 5.1.1 semver: 6.3.1 - '@babel/helper-create-class-features-plugin@7.25.9(@babel/core@7.26.0)': + '@babel/helper-create-class-features-plugin@7.28.3(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.26.0 - '@babel/helper-annotate-as-pure': 7.25.9 - '@babel/helper-member-expression-to-functions': 7.25.9 - '@babel/helper-optimise-call-expression': 7.25.9 - '@babel/helper-replace-supers': 7.25.9(@babel/core@7.26.0) - '@babel/helper-skip-transparent-expression-wrappers': 7.25.9 - '@babel/traverse': 7.25.9 + '@babel/core': 7.28.4 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-member-expression-to-functions': 7.27.1 + '@babel/helper-optimise-call-expression': 7.27.1 + '@babel/helper-replace-supers': 7.27.1(@babel/core@7.28.4) + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + '@babel/traverse': 7.28.4 semver: 6.3.1 transitivePeerDependencies: - supports-color - '@babel/helper-member-expression-to-functions@7.25.9': + '@babel/helper-globals@7.28.0': {} + + '@babel/helper-member-expression-to-functions@7.27.1': dependencies: - '@babel/traverse': 7.25.9 - '@babel/types': 7.26.0 + '@babel/traverse': 7.28.4 + '@babel/types': 7.28.4 transitivePeerDependencies: - supports-color - '@babel/helper-module-imports@7.25.9': + '@babel/helper-module-imports@7.27.1': dependencies: - '@babel/traverse': 7.25.9 - '@babel/types': 7.26.0 + '@babel/traverse': 7.28.4 + '@babel/types': 7.28.4 transitivePeerDependencies: - supports-color - '@babel/helper-module-transforms@7.26.0(@babel/core@7.26.0)': + '@babel/helper-module-transforms@7.28.3(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.26.0 - '@babel/helper-module-imports': 7.25.9 - '@babel/helper-validator-identifier': 7.25.9 - '@babel/traverse': 7.25.9 + '@babel/core': 7.28.4 + '@babel/helper-module-imports': 7.27.1 + '@babel/helper-validator-identifier': 7.27.1 + '@babel/traverse': 7.28.4 transitivePeerDependencies: - supports-color - '@babel/helper-optimise-call-expression@7.25.9': + '@babel/helper-optimise-call-expression@7.27.1': dependencies: - '@babel/types': 7.26.0 + '@babel/types': 7.28.4 - '@babel/helper-plugin-utils@7.25.9': {} + '@babel/helper-plugin-utils@7.27.1': {} - '@babel/helper-replace-supers@7.25.9(@babel/core@7.26.0)': + '@babel/helper-replace-supers@7.27.1(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.26.0 - '@babel/helper-member-expression-to-functions': 7.25.9 - '@babel/helper-optimise-call-expression': 7.25.9 - '@babel/traverse': 7.25.9 + '@babel/core': 7.28.4 + '@babel/helper-member-expression-to-functions': 7.27.1 + '@babel/helper-optimise-call-expression': 7.27.1 + '@babel/traverse': 7.28.4 transitivePeerDependencies: - supports-color - '@babel/helper-skip-transparent-expression-wrappers@7.25.9': + '@babel/helper-skip-transparent-expression-wrappers@7.27.1': dependencies: - '@babel/traverse': 7.25.9 - '@babel/types': 7.26.0 + '@babel/traverse': 7.28.4 + '@babel/types': 7.28.4 transitivePeerDependencies: - supports-color - '@babel/helper-string-parser@7.25.9': {} + '@babel/helper-string-parser@7.27.1': {} - '@babel/helper-validator-identifier@7.25.9': {} + '@babel/helper-validator-identifier@7.27.1': {} - '@babel/helper-validator-option@7.25.9': {} + '@babel/helper-validator-option@7.27.1': {} - '@babel/helpers@7.26.0': + '@babel/helpers@7.28.4': dependencies: - '@babel/template': 7.25.9 - '@babel/types': 7.26.0 + '@babel/template': 7.27.2 + '@babel/types': 7.28.4 - '@babel/parser@7.26.2': + '@babel/parser@7.28.4': dependencies: - '@babel/types': 7.26.0 + '@babel/types': 7.28.4 - '@babel/plugin-proposal-decorators@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-proposal-decorators@7.28.0(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.26.0 - '@babel/helper-create-class-features-plugin': 7.25.9(@babel/core@7.26.0) - '@babel/helper-plugin-utils': 7.25.9 - '@babel/plugin-syntax-decorators': 7.25.9(@babel/core@7.26.0) + '@babel/core': 7.28.4 + '@babel/helper-create-class-features-plugin': 7.28.3(@babel/core@7.28.4) + '@babel/helper-plugin-utils': 7.27.1 + '@babel/plugin-syntax-decorators': 7.27.1(@babel/core@7.28.4) transitivePeerDependencies: - supports-color - '@babel/plugin-syntax-decorators@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-syntax-decorators@7.27.1(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-import-attributes@7.26.0(@babel/core@7.26.0)': + '@babel/plugin-syntax-import-attributes@7.27.1(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.26.0)': + '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-jsx@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-syntax-jsx@7.27.1(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-typescript@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-syntax-typescript@7.27.1(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-typescript@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-transform-typescript@7.28.0(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.26.0 - '@babel/helper-annotate-as-pure': 7.25.9 - '@babel/helper-create-class-features-plugin': 7.25.9(@babel/core@7.26.0) - '@babel/helper-plugin-utils': 7.25.9 - '@babel/helper-skip-transparent-expression-wrappers': 7.25.9 - '@babel/plugin-syntax-typescript': 7.25.9(@babel/core@7.26.0) + '@babel/core': 7.28.4 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-create-class-features-plugin': 7.28.3(@babel/core@7.28.4) + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + '@babel/plugin-syntax-typescript': 7.27.1(@babel/core@7.28.4) transitivePeerDependencies: - supports-color - '@babel/template@7.25.9': + '@babel/template@7.27.2': dependencies: - '@babel/code-frame': 7.26.2 - '@babel/parser': 7.26.2 - '@babel/types': 7.26.0 + '@babel/code-frame': 7.27.1 + '@babel/parser': 7.28.4 + '@babel/types': 7.28.4 - '@babel/traverse@7.25.9': + '@babel/traverse@7.28.4': dependencies: - '@babel/code-frame': 7.26.2 - '@babel/generator': 7.26.2 - '@babel/parser': 7.26.2 - '@babel/template': 7.25.9 - '@babel/types': 7.26.0 - debug: 4.3.7 - globals: 11.12.0 + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.28.3 + '@babel/helper-globals': 7.28.0 + '@babel/parser': 7.28.4 + '@babel/template': 7.27.2 + '@babel/types': 7.28.4 + debug: 4.4.1 transitivePeerDependencies: - supports-color - '@babel/types@7.26.0': + '@babel/types@7.28.4': dependencies: - '@babel/helper-string-parser': 7.25.9 - '@babel/helper-validator-identifier': 7.25.9 + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.27.1 - '@clack/core@0.3.4': + '@clack/core@0.4.1': dependencies: picocolors: 1.1.1 sisteransi: 1.0.5 - '@clack/prompts@0.7.0': + '@clack/prompts@0.9.1': dependencies: - '@clack/core': 0.3.4 + '@clack/core': 0.4.1 picocolors: 1.1.1 sisteransi: 1.0.5 @@ -4156,32 +4467,28 @@ snapshots: '@dprint/markdown@0.17.8': {} - '@dprint/toml@0.6.3': {} + '@dprint/toml@0.6.4': {} - '@emnapi/core@1.3.1': + '@emnapi/core@1.5.0': dependencies: - '@emnapi/wasi-threads': 1.0.1 + '@emnapi/wasi-threads': 1.1.0 tslib: 2.8.1 optional: true - '@emnapi/runtime@1.3.1': + '@emnapi/runtime@1.5.0': dependencies: tslib: 2.8.1 optional: true - '@emnapi/wasi-threads@1.0.1': + '@emnapi/wasi-threads@1.1.0': dependencies: tslib: 2.8.1 optional: true - '@es-joy/jsdoccomment@0.48.0': - dependencies: - comment-parser: 1.4.1 - esquery: 1.6.0 - jsdoc-type-pratt-parser: 4.1.0 - - '@es-joy/jsdoccomment@0.49.0': + '@es-joy/jsdoccomment@0.50.2': dependencies: + '@types/estree': 1.0.8 + '@typescript-eslint/types': 8.42.0 comment-parser: 1.4.1 esquery: 1.6.0 jsdoc-type-pratt-parser: 4.1.0 @@ -4189,252 +4496,269 @@ snapshots: '@esbuild/aix-ppc64@0.21.5': optional: true - '@esbuild/aix-ppc64@0.23.1': + '@esbuild/aix-ppc64@0.25.9': optional: true '@esbuild/android-arm64@0.21.5': optional: true - '@esbuild/android-arm64@0.23.1': + '@esbuild/android-arm64@0.25.9': optional: true '@esbuild/android-arm@0.21.5': optional: true - '@esbuild/android-arm@0.23.1': + '@esbuild/android-arm@0.25.9': optional: true '@esbuild/android-x64@0.21.5': optional: true - '@esbuild/android-x64@0.23.1': + '@esbuild/android-x64@0.25.9': optional: true '@esbuild/darwin-arm64@0.21.5': optional: true - '@esbuild/darwin-arm64@0.23.1': + '@esbuild/darwin-arm64@0.25.9': optional: true '@esbuild/darwin-x64@0.21.5': optional: true - '@esbuild/darwin-x64@0.23.1': + '@esbuild/darwin-x64@0.25.9': optional: true '@esbuild/freebsd-arm64@0.21.5': optional: true - '@esbuild/freebsd-arm64@0.23.1': + '@esbuild/freebsd-arm64@0.25.9': optional: true '@esbuild/freebsd-x64@0.21.5': optional: true - '@esbuild/freebsd-x64@0.23.1': + '@esbuild/freebsd-x64@0.25.9': optional: true '@esbuild/linux-arm64@0.21.5': optional: true - '@esbuild/linux-arm64@0.23.1': + '@esbuild/linux-arm64@0.25.9': optional: true '@esbuild/linux-arm@0.21.5': optional: true - '@esbuild/linux-arm@0.23.1': + '@esbuild/linux-arm@0.25.9': optional: true '@esbuild/linux-ia32@0.21.5': optional: true - '@esbuild/linux-ia32@0.23.1': + '@esbuild/linux-ia32@0.25.9': optional: true '@esbuild/linux-loong64@0.21.5': optional: true - '@esbuild/linux-loong64@0.23.1': + '@esbuild/linux-loong64@0.25.9': optional: true '@esbuild/linux-mips64el@0.21.5': optional: true - '@esbuild/linux-mips64el@0.23.1': + '@esbuild/linux-mips64el@0.25.9': optional: true '@esbuild/linux-ppc64@0.21.5': optional: true - '@esbuild/linux-ppc64@0.23.1': + '@esbuild/linux-ppc64@0.25.9': optional: true '@esbuild/linux-riscv64@0.21.5': optional: true - '@esbuild/linux-riscv64@0.23.1': + '@esbuild/linux-riscv64@0.25.9': optional: true '@esbuild/linux-s390x@0.21.5': optional: true - '@esbuild/linux-s390x@0.23.1': + '@esbuild/linux-s390x@0.25.9': optional: true '@esbuild/linux-x64@0.21.5': optional: true - '@esbuild/linux-x64@0.23.1': + '@esbuild/linux-x64@0.25.9': + optional: true + + '@esbuild/netbsd-arm64@0.25.9': optional: true '@esbuild/netbsd-x64@0.21.5': optional: true - '@esbuild/netbsd-x64@0.23.1': + '@esbuild/netbsd-x64@0.25.9': optional: true - '@esbuild/openbsd-arm64@0.23.1': + '@esbuild/openbsd-arm64@0.25.9': optional: true '@esbuild/openbsd-x64@0.21.5': optional: true - '@esbuild/openbsd-x64@0.23.1': + '@esbuild/openbsd-x64@0.25.9': + optional: true + + '@esbuild/openharmony-arm64@0.25.9': optional: true '@esbuild/sunos-x64@0.21.5': optional: true - '@esbuild/sunos-x64@0.23.1': + '@esbuild/sunos-x64@0.25.9': optional: true '@esbuild/win32-arm64@0.21.5': optional: true - '@esbuild/win32-arm64@0.23.1': + '@esbuild/win32-arm64@0.25.9': optional: true '@esbuild/win32-ia32@0.21.5': optional: true - '@esbuild/win32-ia32@0.23.1': + '@esbuild/win32-ia32@0.25.9': optional: true '@esbuild/win32-x64@0.21.5': optional: true - '@esbuild/win32-x64@0.23.1': + '@esbuild/win32-x64@0.25.9': optional: true - '@eslint-community/eslint-plugin-eslint-comments@4.4.1(eslint@9.14.0(jiti@2.4.0))': + '@eslint-community/eslint-plugin-eslint-comments@4.5.0(eslint@9.35.0(jiti@2.5.1))': dependencies: escape-string-regexp: 4.0.0 - eslint: 9.14.0(jiti@2.4.0) + eslint: 9.35.0(jiti@2.5.1) ignore: 5.3.2 - '@eslint-community/eslint-utils@4.4.1(eslint@9.14.0(jiti@2.4.0))': + '@eslint-community/eslint-utils@4.8.0(eslint@9.35.0(jiti@2.5.1))': dependencies: - eslint: 9.14.0(jiti@2.4.0) + eslint: 9.35.0(jiti@2.5.1) eslint-visitor-keys: 3.4.3 '@eslint-community/regexpp@4.12.1': {} - '@eslint/compat@1.2.2(eslint@9.14.0(jiti@2.4.0))': + '@eslint/compat@1.3.2(eslint@9.35.0(jiti@2.5.1))': optionalDependencies: - eslint: 9.14.0(jiti@2.4.0) + eslint: 9.35.0(jiti@2.5.1) - '@eslint/config-array@0.18.0': + '@eslint/config-array@0.21.0': dependencies: - '@eslint/object-schema': 2.1.4 - debug: 4.3.7 + '@eslint/object-schema': 2.1.6 + debug: 4.4.1 minimatch: 3.1.2 transitivePeerDependencies: - supports-color - '@eslint/core@0.7.0': {} + '@eslint/config-helpers@0.3.1': {} + + '@eslint/core@0.14.0': + dependencies: + '@types/json-schema': 7.0.15 + + '@eslint/core@0.15.2': + dependencies: + '@types/json-schema': 7.0.15 - '@eslint/eslintrc@3.1.0': + '@eslint/eslintrc@3.3.1': dependencies: ajv: 6.12.6 - debug: 4.3.7 - espree: 10.3.0 + debug: 4.4.1 + espree: 10.4.0 globals: 14.0.0 ignore: 5.3.2 - import-fresh: 3.3.0 + import-fresh: 3.3.1 js-yaml: 4.1.0 minimatch: 3.1.2 strip-json-comments: 3.1.1 transitivePeerDependencies: - supports-color - '@eslint/js@9.14.0': {} + '@eslint/js@9.35.0': {} - '@eslint/markdown@6.2.1': + '@eslint/markdown@6.6.0': dependencies: - '@eslint/plugin-kit': 0.2.2 + '@eslint/core': 0.14.0 + '@eslint/plugin-kit': 0.3.5 + github-slugger: 2.0.0 mdast-util-from-markdown: 2.0.2 - mdast-util-gfm: 3.0.0 + mdast-util-frontmatter: 2.0.1 + mdast-util-gfm: 3.1.0 + micromark-extension-frontmatter: 2.0.0 micromark-extension-gfm: 3.0.0 transitivePeerDependencies: - supports-color - '@eslint/object-schema@2.1.4': {} + '@eslint/object-schema@2.1.6': {} - '@eslint/plugin-kit@0.2.2': + '@eslint/plugin-kit@0.3.5': dependencies: + '@eslint/core': 0.15.2 levn: 0.4.1 - '@floating-ui/core@1.6.8': + '@floating-ui/core@1.7.3': dependencies: - '@floating-ui/utils': 0.2.8 + '@floating-ui/utils': 0.2.10 '@floating-ui/dom@1.1.1': dependencies: - '@floating-ui/core': 1.6.8 + '@floating-ui/core': 1.7.3 - '@floating-ui/utils@0.2.8': {} + '@floating-ui/utils@0.2.10': {} '@humanfs/core@0.19.1': {} - '@humanfs/node@0.16.6': + '@humanfs/node@0.16.7': dependencies: '@humanfs/core': 0.19.1 - '@humanwhocodes/retry': 0.3.1 + '@humanwhocodes/retry': 0.4.3 '@humanwhocodes/module-importer@1.0.1': {} - '@humanwhocodes/retry@0.3.1': {} - - '@humanwhocodes/retry@0.4.1': {} + '@humanwhocodes/retry@0.4.3': {} - '@intlify/bundle-utils@9.0.0(vue-i18n@10.0.4(vue@3.5.12(typescript@5.6.3)))': + '@intlify/bundle-utils@9.0.0(vue-i18n@10.0.8(vue@3.5.21(typescript@5.6.3)))': dependencies: '@intlify/message-compiler': 12.0.0-alpha.3 '@intlify/shared': 12.0.0-alpha.3 - acorn: 8.14.0 + acorn: 8.15.0 escodegen: 2.1.0 estree-walker: 2.0.2 jsonc-eslint-parser: 2.4.0 - mlly: 1.7.2 + mlly: 1.8.0 source-map-js: 1.2.1 - yaml-eslint-parser: 1.2.3 + yaml-eslint-parser: 1.3.0 optionalDependencies: - vue-i18n: 10.0.4(vue@3.5.12(typescript@5.6.3)) + vue-i18n: 10.0.8(vue@3.5.21(typescript@5.6.3)) - '@intlify/core-base@10.0.4': + '@intlify/core-base@10.0.8': dependencies: - '@intlify/message-compiler': 10.0.4 - '@intlify/shared': 10.0.4 + '@intlify/message-compiler': 10.0.8 + '@intlify/shared': 10.0.8 - '@intlify/core-base@9.14.4': + '@intlify/core-base@9.14.5': dependencies: - '@intlify/message-compiler': 9.14.4 - '@intlify/shared': 9.14.4 + '@intlify/message-compiler': 9.14.5 + '@intlify/shared': 9.14.5 - '@intlify/message-compiler@10.0.4': + '@intlify/message-compiler@10.0.8': dependencies: - '@intlify/shared': 10.0.4 + '@intlify/shared': 10.0.8 source-map-js: 1.2.1 '@intlify/message-compiler@12.0.0-alpha.3': @@ -4442,53 +4766,60 @@ snapshots: '@intlify/shared': 12.0.0-alpha.3 source-map-js: 1.2.1 - '@intlify/message-compiler@9.14.4': + '@intlify/message-compiler@9.14.5': dependencies: - '@intlify/shared': 9.14.4 + '@intlify/shared': 9.14.5 source-map-js: 1.2.1 - '@intlify/shared@10.0.4': {} + '@intlify/shared@10.0.8': {} + + '@intlify/shared@11.1.12': {} '@intlify/shared@12.0.0-alpha.3': {} - '@intlify/shared@9.14.4': {} + '@intlify/shared@9.14.5': {} - '@intlify/unplugin-vue-i18n@5.2.0(@vue/compiler-dom@3.5.12)(eslint@9.14.0(jiti@2.4.0))(rollup@4.24.3)(typescript@5.6.3)(vue-i18n@10.0.4(vue@3.5.12(typescript@5.6.3)))(vue@3.5.12(typescript@5.6.3))': + '@intlify/unplugin-vue-i18n@5.3.1(@vue/compiler-dom@3.5.21)(eslint@9.35.0(jiti@2.5.1))(rollup@4.50.1)(typescript@5.6.3)(vue-i18n@10.0.8(vue@3.5.21(typescript@5.6.3)))(vue@3.5.21(typescript@5.6.3))': dependencies: - '@eslint-community/eslint-utils': 4.4.1(eslint@9.14.0(jiti@2.4.0)) - '@intlify/bundle-utils': 9.0.0(vue-i18n@10.0.4(vue@3.5.12(typescript@5.6.3))) - '@intlify/shared': 12.0.0-alpha.3 - '@intlify/vue-i18n-extensions': 7.0.0(@intlify/shared@12.0.0-alpha.3)(@vue/compiler-dom@3.5.12)(vue-i18n@10.0.4(vue@3.5.12(typescript@5.6.3)))(vue@3.5.12(typescript@5.6.3)) - '@rollup/pluginutils': 5.1.3(rollup@4.24.3) - '@typescript-eslint/scope-manager': 7.18.0 - '@typescript-eslint/typescript-estree': 7.18.0(typescript@5.6.3) - debug: 4.3.7 - fast-glob: 3.3.2 + '@eslint-community/eslint-utils': 4.8.0(eslint@9.35.0(jiti@2.5.1)) + '@intlify/bundle-utils': 9.0.0(vue-i18n@10.0.8(vue@3.5.21(typescript@5.6.3))) + '@intlify/shared': 11.1.12 + '@intlify/vue-i18n-extensions': 7.0.0(@intlify/shared@11.1.12)(@vue/compiler-dom@3.5.21)(vue-i18n@10.0.8(vue@3.5.21(typescript@5.6.3)))(vue@3.5.21(typescript@5.6.3)) + '@rollup/pluginutils': 5.3.0(rollup@4.50.1) + '@typescript-eslint/scope-manager': 8.42.0 + '@typescript-eslint/typescript-estree': 8.42.0(typescript@5.6.3) + debug: 4.4.1 + fast-glob: 3.3.3 js-yaml: 4.1.0 json5: 2.2.3 pathe: 1.1.2 picocolors: 1.1.1 source-map-js: 1.2.1 - unplugin: 1.15.0 - vue: 3.5.12(typescript@5.6.3) + unplugin: 1.16.1 + vue: 3.5.21(typescript@5.6.3) optionalDependencies: - vue-i18n: 10.0.4(vue@3.5.12(typescript@5.6.3)) + vue-i18n: 10.0.8(vue@3.5.21(typescript@5.6.3)) transitivePeerDependencies: - '@vue/compiler-dom' - eslint - rollup - supports-color - typescript - - webpack-sources - '@intlify/vue-i18n-extensions@7.0.0(@intlify/shared@12.0.0-alpha.3)(@vue/compiler-dom@3.5.12)(vue-i18n@10.0.4(vue@3.5.12(typescript@5.6.3)))(vue@3.5.12(typescript@5.6.3))': + '@intlify/vue-i18n-extensions@7.0.0(@intlify/shared@11.1.12)(@vue/compiler-dom@3.5.21)(vue-i18n@10.0.8(vue@3.5.21(typescript@5.6.3)))(vue@3.5.21(typescript@5.6.3))': dependencies: - '@babel/parser': 7.26.2 + '@babel/parser': 7.28.4 optionalDependencies: - '@intlify/shared': 12.0.0-alpha.3 - '@vue/compiler-dom': 3.5.12 - vue: 3.5.12(typescript@5.6.3) - vue-i18n: 10.0.4(vue@3.5.12(typescript@5.6.3)) + '@intlify/shared': 11.1.12 + '@vue/compiler-dom': 3.5.21 + vue: 3.5.21(typescript@5.6.3) + vue-i18n: 10.0.8(vue@3.5.21(typescript@5.6.3)) + + '@isaacs/balanced-match@4.0.1': {} + + '@isaacs/brace-expansion@5.0.0': + dependencies: + '@isaacs/balanced-match': 4.0.1 '@isaacs/cliui@8.0.2': dependencies: @@ -4499,86 +4830,90 @@ snapshots: wrap-ansi: 8.1.0 wrap-ansi-cjs: wrap-ansi@7.0.0 - '@jridgewell/gen-mapping@0.3.5': + '@jridgewell/gen-mapping@0.3.13': dependencies: - '@jridgewell/set-array': 1.2.1 - '@jridgewell/sourcemap-codec': 1.5.0 - '@jridgewell/trace-mapping': 0.3.25 + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.30 - '@jridgewell/resolve-uri@3.1.2': {} + '@jridgewell/remapping@2.3.5': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.30 - '@jridgewell/set-array@1.2.1': {} + '@jridgewell/resolve-uri@3.1.2': {} - '@jridgewell/sourcemap-codec@1.5.0': {} + '@jridgewell/sourcemap-codec@1.5.5': {} - '@jridgewell/trace-mapping@0.3.25': + '@jridgewell/trace-mapping@0.3.30': dependencies: '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.5.0 + '@jridgewell/sourcemap-codec': 1.5.5 - '@mdit-vue/plugin-component@2.1.3': + '@kurkle/color@0.3.4': {} + + '@mdit-vue/plugin-component@2.1.4': dependencies: '@types/markdown-it': 14.1.2 markdown-it: 14.1.0 - '@mdit-vue/plugin-frontmatter@2.1.3': + '@mdit-vue/plugin-frontmatter@2.1.4': dependencies: - '@mdit-vue/types': 2.1.0 + '@mdit-vue/types': 2.1.4 '@types/markdown-it': 14.1.2 gray-matter: 4.0.3 markdown-it: 14.1.0 - '@mdit-vue/types@2.1.0': {} + '@mdit-vue/types@2.1.4': {} - '@microsoft/api-extractor-model@7.29.8(@types/node@22.8.6)': + '@microsoft/api-extractor-model@7.30.7(@types/node@22.18.1)': dependencies: - '@microsoft/tsdoc': 0.15.0 - '@microsoft/tsdoc-config': 0.17.0 - '@rushstack/node-core-library': 5.9.0(@types/node@22.8.6) + '@microsoft/tsdoc': 0.15.1 + '@microsoft/tsdoc-config': 0.17.1 + '@rushstack/node-core-library': 5.14.0(@types/node@22.18.1) transitivePeerDependencies: - '@types/node' - '@microsoft/api-extractor@7.47.11(@types/node@22.8.6)': + '@microsoft/api-extractor@7.52.11(@types/node@22.18.1)': dependencies: - '@microsoft/api-extractor-model': 7.29.8(@types/node@22.8.6) - '@microsoft/tsdoc': 0.15.0 - '@microsoft/tsdoc-config': 0.17.0 - '@rushstack/node-core-library': 5.9.0(@types/node@22.8.6) + '@microsoft/api-extractor-model': 7.30.7(@types/node@22.18.1) + '@microsoft/tsdoc': 0.15.1 + '@microsoft/tsdoc-config': 0.17.1 + '@rushstack/node-core-library': 5.14.0(@types/node@22.18.1) '@rushstack/rig-package': 0.5.3 - '@rushstack/terminal': 0.14.2(@types/node@22.8.6) - '@rushstack/ts-command-line': 4.23.0(@types/node@22.8.6) + '@rushstack/terminal': 0.15.4(@types/node@22.18.1) + '@rushstack/ts-command-line': 5.0.2(@types/node@22.18.1) lodash: 4.17.21 - minimatch: 3.0.8 - resolve: 1.22.8 + minimatch: 10.0.3 + resolve: 1.22.10 semver: 7.5.4 source-map: 0.6.1 - typescript: 5.4.2 + typescript: 5.8.2 transitivePeerDependencies: - '@types/node' - '@microsoft/tsdoc-config@0.17.0': + '@microsoft/tsdoc-config@0.17.1': dependencies: - '@microsoft/tsdoc': 0.15.0 + '@microsoft/tsdoc': 0.15.1 ajv: 8.12.0 jju: 1.4.0 - resolve: 1.22.8 + resolve: 1.22.10 - '@microsoft/tsdoc@0.15.0': {} + '@microsoft/tsdoc@0.15.1': {} - '@modyfi/vite-plugin-yaml@1.1.0(rollup@4.24.3)(vite@5.4.10(@types/node@22.8.6))': + '@modyfi/vite-plugin-yaml@1.1.1(rollup@4.50.1)(vite@5.4.19(@types/node@22.18.1))': dependencies: - '@rollup/pluginutils': 5.1.0(rollup@4.24.3) + '@rollup/pluginutils': 5.1.0(rollup@4.50.1) js-yaml: 4.1.0 tosource: 2.0.0-alpha.3 - vite: 5.4.10(@types/node@22.8.6) + vite: 5.4.19(@types/node@22.18.1) transitivePeerDependencies: - rollup - '@napi-rs/wasm-runtime@0.2.5': + '@napi-rs/wasm-runtime@0.2.12': dependencies: - '@emnapi/core': 1.3.1 - '@emnapi/runtime': 1.3.1 - '@tybys/wasm-util': 0.9.0 + '@emnapi/core': 1.5.0 + '@emnapi/runtime': 1.5.0 + '@tybys/wasm-util': 0.10.0 optional: true '@nodelib/fs.scandir@2.1.5': @@ -4591,209 +4926,223 @@ snapshots: '@nodelib/fs.walk@1.2.8': dependencies: '@nodelib/fs.scandir': 2.1.5 - fastq: 1.17.1 + fastq: 1.19.1 - '@oxc-resolver/binding-darwin-arm64@2.0.1': + '@oxc-resolver/binding-darwin-arm64@4.2.0': optional: true - '@oxc-resolver/binding-darwin-x64@2.0.1': + '@oxc-resolver/binding-darwin-x64@4.2.0': optional: true - '@oxc-resolver/binding-freebsd-x64@2.0.1': + '@oxc-resolver/binding-freebsd-x64@4.2.0': optional: true - '@oxc-resolver/binding-linux-arm-gnueabihf@2.0.1': + '@oxc-resolver/binding-linux-arm-gnueabihf@4.2.0': optional: true - '@oxc-resolver/binding-linux-arm64-gnu@2.0.1': + '@oxc-resolver/binding-linux-arm64-gnu@4.2.0': optional: true - '@oxc-resolver/binding-linux-arm64-musl@2.0.1': + '@oxc-resolver/binding-linux-arm64-musl@4.2.0': optional: true - '@oxc-resolver/binding-linux-x64-gnu@2.0.1': + '@oxc-resolver/binding-linux-x64-gnu@4.2.0': optional: true - '@oxc-resolver/binding-linux-x64-musl@2.0.1': + '@oxc-resolver/binding-linux-x64-musl@4.2.0': optional: true - '@oxc-resolver/binding-wasm32-wasi@2.0.1': + '@oxc-resolver/binding-wasm32-wasi@4.2.0': dependencies: - '@napi-rs/wasm-runtime': 0.2.5 + '@napi-rs/wasm-runtime': 0.2.12 optional: true - '@oxc-resolver/binding-win32-arm64-msvc@2.0.1': + '@oxc-resolver/binding-win32-arm64-msvc@4.2.0': optional: true - '@oxc-resolver/binding-win32-x64-msvc@2.0.1': + '@oxc-resolver/binding-win32-x64-msvc@4.2.0': optional: true '@pkgjs/parseargs@0.11.0': optional: true - '@pkgr/core@0.1.1': {} + '@pkgr/core@0.1.2': {} + + '@pkgr/core@0.2.9': {} - '@polka/url@1.0.0-next.28': {} + '@polka/url@1.0.0-next.29': {} - '@primeuix/styled@0.5.1': + '@primeuix/styled@0.7.2': dependencies: - '@primeuix/utils': 0.5.3 + '@primeuix/utils': 0.6.1 - '@primeuix/styles@1.0.2': + '@primeuix/styles@1.2.3': dependencies: - '@primeuix/styled': 0.5.1 + '@primeuix/styled': 0.7.2 - '@primeuix/themes@1.0.2': + '@primeuix/themes@1.2.3': dependencies: - '@primeuix/styled': 0.5.1 + '@primeuix/styled': 0.7.2 - '@primeuix/utils@0.5.3': {} + '@primeuix/utils@0.6.1': {} - '@primevue/auto-import-resolver@4.3.3': + '@primevue/auto-import-resolver@4.3.9': dependencies: - '@primevue/metadata': 4.3.3 + '@primevue/metadata': 4.3.9 - '@primevue/core@4.3.3(vue@3.5.12(typescript@5.6.3))': + '@primevue/core@4.3.9(vue@3.5.21(typescript@5.6.3))': dependencies: - '@primeuix/styled': 0.5.1 - '@primeuix/utils': 0.5.3 - vue: 3.5.12(typescript@5.6.3) + '@primeuix/styled': 0.7.2 + '@primeuix/utils': 0.6.1 + vue: 3.5.21(typescript@5.6.3) - '@primevue/icons@4.3.3(vue@3.5.12(typescript@5.6.3))': + '@primevue/icons@4.3.9(vue@3.5.21(typescript@5.6.3))': dependencies: - '@primeuix/utils': 0.5.3 - '@primevue/core': 4.3.3(vue@3.5.12(typescript@5.6.3)) + '@primeuix/utils': 0.6.1 + '@primevue/core': 4.3.9(vue@3.5.21(typescript@5.6.3)) transitivePeerDependencies: - vue - '@primevue/metadata@4.3.3': {} + '@primevue/metadata@4.3.9': {} - '@primevue/themes@4.3.3': + '@quansync/fs@0.1.5': dependencies: - '@primeuix/styled': 0.5.1 - '@primeuix/themes': 1.0.2 + quansync: 0.2.11 - '@rollup/plugin-typescript@11.1.6(rollup@4.24.3)(tslib@2.8.1)(typescript@5.6.3)': + '@rollup/plugin-typescript@11.1.6(rollup@4.50.1)(tslib@2.8.1)(typescript@5.6.3)': dependencies: - '@rollup/pluginutils': 5.1.3(rollup@4.24.3) - resolve: 1.22.8 + '@rollup/pluginutils': 5.3.0(rollup@4.50.1) + resolve: 1.22.10 typescript: 5.6.3 optionalDependencies: - rollup: 4.24.3 + rollup: 4.50.1 tslib: 2.8.1 - '@rollup/pluginutils@5.1.0(rollup@4.24.3)': + '@rollup/pluginutils@5.1.0(rollup@4.50.1)': dependencies: - '@types/estree': 1.0.6 + '@types/estree': 1.0.8 estree-walker: 2.0.2 picomatch: 2.3.1 optionalDependencies: - rollup: 4.24.3 + rollup: 4.50.1 - '@rollup/pluginutils@5.1.3(rollup@4.24.3)': + '@rollup/pluginutils@5.3.0(rollup@4.50.1)': dependencies: - '@types/estree': 1.0.6 + '@types/estree': 1.0.8 estree-walker: 2.0.2 - picomatch: 4.0.2 + picomatch: 4.0.3 optionalDependencies: - rollup: 4.24.3 + rollup: 4.50.1 + + '@rollup/rollup-android-arm-eabi@4.50.1': + optional: true + + '@rollup/rollup-android-arm64@4.50.1': + optional: true - '@rollup/rollup-android-arm-eabi@4.24.3': + '@rollup/rollup-darwin-arm64@4.50.1': optional: true - '@rollup/rollup-android-arm64@4.24.3': + '@rollup/rollup-darwin-x64@4.50.1': optional: true - '@rollup/rollup-darwin-arm64@4.24.3': + '@rollup/rollup-freebsd-arm64@4.50.1': optional: true - '@rollup/rollup-darwin-x64@4.24.3': + '@rollup/rollup-freebsd-x64@4.50.1': optional: true - '@rollup/rollup-freebsd-arm64@4.24.3': + '@rollup/rollup-linux-arm-gnueabihf@4.50.1': optional: true - '@rollup/rollup-freebsd-x64@4.24.3': + '@rollup/rollup-linux-arm-musleabihf@4.50.1': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.24.3': + '@rollup/rollup-linux-arm64-gnu@4.50.1': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.24.3': + '@rollup/rollup-linux-arm64-musl@4.50.1': optional: true - '@rollup/rollup-linux-arm64-gnu@4.24.3': + '@rollup/rollup-linux-loongarch64-gnu@4.50.1': optional: true - '@rollup/rollup-linux-arm64-musl@4.24.3': + '@rollup/rollup-linux-ppc64-gnu@4.50.1': optional: true - '@rollup/rollup-linux-powerpc64le-gnu@4.24.3': + '@rollup/rollup-linux-riscv64-gnu@4.50.1': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.24.3': + '@rollup/rollup-linux-riscv64-musl@4.50.1': optional: true - '@rollup/rollup-linux-s390x-gnu@4.24.3': + '@rollup/rollup-linux-s390x-gnu@4.50.1': optional: true - '@rollup/rollup-linux-x64-gnu@4.24.3': + '@rollup/rollup-linux-x64-gnu@4.50.1': optional: true - '@rollup/rollup-linux-x64-musl@4.24.3': + '@rollup/rollup-linux-x64-musl@4.50.1': optional: true - '@rollup/rollup-win32-arm64-msvc@4.24.3': + '@rollup/rollup-openharmony-arm64@4.50.1': optional: true - '@rollup/rollup-win32-ia32-msvc@4.24.3': + '@rollup/rollup-win32-arm64-msvc@4.50.1': optional: true - '@rollup/rollup-win32-x64-msvc@4.24.3': + '@rollup/rollup-win32-ia32-msvc@4.50.1': optional: true - '@rushstack/node-core-library@5.9.0(@types/node@22.8.6)': + '@rollup/rollup-win32-x64-msvc@4.50.1': + optional: true + + '@rushstack/node-core-library@5.14.0(@types/node@22.18.1)': dependencies: ajv: 8.13.0 ajv-draft-04: 1.0.0(ajv@8.13.0) ajv-formats: 3.0.1(ajv@8.13.0) - fs-extra: 7.0.1 + fs-extra: 11.3.1 import-lazy: 4.0.0 jju: 1.4.0 - resolve: 1.22.8 + resolve: 1.22.10 semver: 7.5.4 optionalDependencies: - '@types/node': 22.8.6 + '@types/node': 22.18.1 '@rushstack/rig-package@0.5.3': dependencies: - resolve: 1.22.8 + resolve: 1.22.10 strip-json-comments: 3.1.1 - '@rushstack/terminal@0.14.2(@types/node@22.8.6)': + '@rushstack/terminal@0.15.4(@types/node@22.18.1)': dependencies: - '@rushstack/node-core-library': 5.9.0(@types/node@22.8.6) + '@rushstack/node-core-library': 5.14.0(@types/node@22.18.1) supports-color: 8.1.1 optionalDependencies: - '@types/node': 22.8.6 + '@types/node': 22.18.1 - '@rushstack/ts-command-line@4.23.0(@types/node@22.8.6)': + '@rushstack/ts-command-line@5.0.2(@types/node@22.18.1)': dependencies: - '@rushstack/terminal': 0.14.2(@types/node@22.8.6) + '@rushstack/terminal': 0.15.4(@types/node@22.18.1) '@types/argparse': 1.0.38 argparse: 1.0.10 string-argv: 0.3.2 transitivePeerDependencies: - '@types/node' - '@stylistic/eslint-plugin@2.10.1(eslint@9.14.0(jiti@2.4.0))(typescript@5.6.3)': + '@sec-ant/readable-stream@0.4.1': {} + + '@sindresorhus/merge-streams@4.0.0': {} + + '@stylistic/eslint-plugin@2.13.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.6.3)': dependencies: - '@typescript-eslint/utils': 8.13.0(eslint@9.14.0(jiti@2.4.0))(typescript@5.6.3) - eslint: 9.14.0(jiti@2.4.0) - eslint-visitor-keys: 4.2.0 - espree: 10.3.0 + '@typescript-eslint/utils': 8.42.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.6.3) + eslint: 9.35.0(jiti@2.5.1) + eslint-visitor-keys: 4.2.1 + espree: 10.4.0 estraverse: 5.3.0 - picomatch: 4.0.2 + picomatch: 4.0.3 transitivePeerDependencies: - supports-color - typescript @@ -4869,7 +5218,7 @@ snapshots: dependencies: '@tauri-apps/api': 2.7.0 - '@tybys/wasm-util@0.9.0': + '@tybys/wasm-util@0.10.0': dependencies: tslib: 2.8.1 optional: true @@ -4878,11 +5227,11 @@ snapshots: '@types/debug@4.1.12': dependencies: - '@types/ms': 0.7.34 + '@types/ms': 2.1.0 '@types/default-gateway@7.2.2': {} - '@types/estree@1.0.6': {} + '@types/estree@1.0.8': {} '@types/json-schema@7.0.15': {} @@ -4899,11 +5248,11 @@ snapshots: '@types/mdurl@2.0.0': {} - '@types/ms@0.7.34': {} + '@types/ms@2.1.0': {} - '@types/node@22.8.6': + '@types/node@22.18.1': dependencies: - undici-types: 6.19.8 + undici-types: 6.21.0 '@types/normalize-package-data@2.4.4': {} @@ -4913,492 +5262,550 @@ snapshots: '@types/web-bluetooth@0.0.20': {} - '@typescript-eslint/eslint-plugin@8.13.0(@typescript-eslint/parser@8.13.0(eslint@9.14.0(jiti@2.4.0))(typescript@5.6.3))(eslint@9.14.0(jiti@2.4.0))(typescript@5.6.3)': + '@typescript-eslint/eslint-plugin@8.42.0(@typescript-eslint/parser@8.42.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.6.3))(eslint@9.35.0(jiti@2.5.1))(typescript@5.6.3)': dependencies: '@eslint-community/regexpp': 4.12.1 - '@typescript-eslint/parser': 8.13.0(eslint@9.14.0(jiti@2.4.0))(typescript@5.6.3) - '@typescript-eslint/scope-manager': 8.13.0 - '@typescript-eslint/type-utils': 8.13.0(eslint@9.14.0(jiti@2.4.0))(typescript@5.6.3) - '@typescript-eslint/utils': 8.13.0(eslint@9.14.0(jiti@2.4.0))(typescript@5.6.3) - '@typescript-eslint/visitor-keys': 8.13.0 - eslint: 9.14.0(jiti@2.4.0) + '@typescript-eslint/parser': 8.42.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.6.3) + '@typescript-eslint/scope-manager': 8.42.0 + '@typescript-eslint/type-utils': 8.42.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.6.3) + '@typescript-eslint/utils': 8.42.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.6.3) + '@typescript-eslint/visitor-keys': 8.42.0 + eslint: 9.35.0(jiti@2.5.1) graphemer: 1.4.0 - ignore: 5.3.2 + ignore: 7.0.5 natural-compare: 1.4.0 - ts-api-utils: 1.4.0(typescript@5.6.3) - optionalDependencies: + ts-api-utils: 2.1.0(typescript@5.6.3) typescript: 5.6.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.13.0(eslint@9.14.0(jiti@2.4.0))(typescript@5.6.3)': + '@typescript-eslint/parser@8.42.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.6.3)': dependencies: - '@typescript-eslint/scope-manager': 8.13.0 - '@typescript-eslint/types': 8.13.0 - '@typescript-eslint/typescript-estree': 8.13.0(typescript@5.6.3) - '@typescript-eslint/visitor-keys': 8.13.0 - debug: 4.3.7 - eslint: 9.14.0(jiti@2.4.0) - optionalDependencies: + '@typescript-eslint/scope-manager': 8.42.0 + '@typescript-eslint/types': 8.42.0 + '@typescript-eslint/typescript-estree': 8.42.0(typescript@5.6.3) + '@typescript-eslint/visitor-keys': 8.42.0 + debug: 4.4.1 + eslint: 9.35.0(jiti@2.5.1) typescript: 5.6.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/scope-manager@7.18.0': - dependencies: - '@typescript-eslint/types': 7.18.0 - '@typescript-eslint/visitor-keys': 7.18.0 - - '@typescript-eslint/scope-manager@8.13.0': + '@typescript-eslint/project-service@8.42.0(typescript@5.6.3)': dependencies: - '@typescript-eslint/types': 8.13.0 - '@typescript-eslint/visitor-keys': 8.13.0 - - '@typescript-eslint/type-utils@8.13.0(eslint@9.14.0(jiti@2.4.0))(typescript@5.6.3)': - dependencies: - '@typescript-eslint/typescript-estree': 8.13.0(typescript@5.6.3) - '@typescript-eslint/utils': 8.13.0(eslint@9.14.0(jiti@2.4.0))(typescript@5.6.3) - debug: 4.3.7 - ts-api-utils: 1.4.0(typescript@5.6.3) - optionalDependencies: + '@typescript-eslint/tsconfig-utils': 8.42.0(typescript@5.6.3) + '@typescript-eslint/types': 8.42.0 + debug: 4.4.1 typescript: 5.6.3 transitivePeerDependencies: - - eslint - supports-color - '@typescript-eslint/types@7.18.0': {} + '@typescript-eslint/scope-manager@8.42.0': + dependencies: + '@typescript-eslint/types': 8.42.0 + '@typescript-eslint/visitor-keys': 8.42.0 - '@typescript-eslint/types@8.13.0': {} + '@typescript-eslint/tsconfig-utils@8.42.0(typescript@5.6.3)': + dependencies: + typescript: 5.6.3 - '@typescript-eslint/typescript-estree@7.18.0(typescript@5.6.3)': + '@typescript-eslint/type-utils@8.42.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.6.3)': dependencies: - '@typescript-eslint/types': 7.18.0 - '@typescript-eslint/visitor-keys': 7.18.0 - debug: 4.3.7 - globby: 11.1.0 - is-glob: 4.0.3 - minimatch: 9.0.5 - semver: 7.6.3 - ts-api-utils: 1.4.0(typescript@5.6.3) - optionalDependencies: + '@typescript-eslint/types': 8.42.0 + '@typescript-eslint/typescript-estree': 8.42.0(typescript@5.6.3) + '@typescript-eslint/utils': 8.42.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.6.3) + debug: 4.4.1 + eslint: 9.35.0(jiti@2.5.1) + ts-api-utils: 2.1.0(typescript@5.6.3) typescript: 5.6.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/typescript-estree@8.13.0(typescript@5.6.3)': + '@typescript-eslint/types@8.42.0': {} + + '@typescript-eslint/typescript-estree@8.42.0(typescript@5.6.3)': dependencies: - '@typescript-eslint/types': 8.13.0 - '@typescript-eslint/visitor-keys': 8.13.0 - debug: 4.3.7 - fast-glob: 3.3.2 + '@typescript-eslint/project-service': 8.42.0(typescript@5.6.3) + '@typescript-eslint/tsconfig-utils': 8.42.0(typescript@5.6.3) + '@typescript-eslint/types': 8.42.0 + '@typescript-eslint/visitor-keys': 8.42.0 + debug: 4.4.1 + fast-glob: 3.3.3 is-glob: 4.0.3 minimatch: 9.0.5 - semver: 7.6.3 - ts-api-utils: 1.4.0(typescript@5.6.3) - optionalDependencies: + semver: 7.7.2 + ts-api-utils: 2.1.0(typescript@5.6.3) typescript: 5.6.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.13.0(eslint@9.14.0(jiti@2.4.0))(typescript@5.6.3)': + '@typescript-eslint/utils@8.42.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.6.3)': dependencies: - '@eslint-community/eslint-utils': 4.4.1(eslint@9.14.0(jiti@2.4.0)) - '@typescript-eslint/scope-manager': 8.13.0 - '@typescript-eslint/types': 8.13.0 - '@typescript-eslint/typescript-estree': 8.13.0(typescript@5.6.3) - eslint: 9.14.0(jiti@2.4.0) + '@eslint-community/eslint-utils': 4.8.0(eslint@9.35.0(jiti@2.5.1)) + '@typescript-eslint/scope-manager': 8.42.0 + '@typescript-eslint/types': 8.42.0 + '@typescript-eslint/typescript-estree': 8.42.0(typescript@5.6.3) + eslint: 9.35.0(jiti@2.5.1) + typescript: 5.6.3 transitivePeerDependencies: - supports-color - - typescript - '@typescript-eslint/visitor-keys@7.18.0': + '@typescript-eslint/visitor-keys@8.42.0': dependencies: - '@typescript-eslint/types': 7.18.0 - eslint-visitor-keys: 3.4.3 + '@typescript-eslint/types': 8.42.0 + eslint-visitor-keys: 4.2.1 + + '@unrs/resolver-binding-android-arm-eabi@1.11.1': + optional: true + + '@unrs/resolver-binding-android-arm64@1.11.1': + optional: true + + '@unrs/resolver-binding-darwin-arm64@1.11.1': + optional: true + + '@unrs/resolver-binding-darwin-x64@1.11.1': + optional: true - '@typescript-eslint/visitor-keys@8.13.0': + '@unrs/resolver-binding-freebsd-x64@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-arm-gnueabihf@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-arm-musleabihf@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-arm64-gnu@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-arm64-musl@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-ppc64-gnu@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-riscv64-gnu@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-riscv64-musl@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-s390x-gnu@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-x64-gnu@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-x64-musl@1.11.1': + optional: true + + '@unrs/resolver-binding-wasm32-wasi@1.11.1': dependencies: - '@typescript-eslint/types': 8.13.0 - eslint-visitor-keys: 3.4.3 + '@napi-rs/wasm-runtime': 0.2.12 + optional: true - '@vitejs/plugin-vue@5.1.4(vite@5.4.10(@types/node@22.8.6))(vue@3.5.12(typescript@5.6.3))': + '@unrs/resolver-binding-win32-arm64-msvc@1.11.1': + optional: true + + '@unrs/resolver-binding-win32-ia32-msvc@1.11.1': + optional: true + + '@unrs/resolver-binding-win32-x64-msvc@1.11.1': + optional: true + + '@vitejs/plugin-vue@5.2.4(vite@5.4.19(@types/node@22.18.1))(vue@3.5.21(typescript@5.6.3))': dependencies: - vite: 5.4.10(@types/node@22.8.6) - vue: 3.5.12(typescript@5.6.3) + vite: 5.4.19(@types/node@22.18.1) + vue: 3.5.21(typescript@5.6.3) - '@vitest/eslint-plugin@1.1.7(@typescript-eslint/utils@8.13.0(eslint@9.14.0(jiti@2.4.0))(typescript@5.6.3))(eslint@9.14.0(jiti@2.4.0))(typescript@5.6.3)': + '@vitest/eslint-plugin@1.3.9(eslint@9.35.0(jiti@2.5.1))(typescript@5.6.3)': dependencies: - '@typescript-eslint/utils': 8.13.0(eslint@9.14.0(jiti@2.4.0))(typescript@5.6.3) - eslint: 9.14.0(jiti@2.4.0) + '@typescript-eslint/scope-manager': 8.42.0 + '@typescript-eslint/utils': 8.42.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.6.3) + eslint: 9.35.0(jiti@2.5.1) optionalDependencies: typescript: 5.6.3 + transitivePeerDependencies: + - supports-color - '@volar/language-core@2.4.8': + '@volar/language-core@2.4.15': dependencies: - '@volar/source-map': 2.4.8 + '@volar/source-map': 2.4.15 - '@volar/source-map@2.4.8': {} + '@volar/language-core@2.4.23': + dependencies: + '@volar/source-map': 2.4.23 + + '@volar/source-map@2.4.15': {} + + '@volar/source-map@2.4.23': {} + + '@volar/typescript@2.4.15': + dependencies: + '@volar/language-core': 2.4.15 + path-browserify: 1.0.1 + vscode-uri: 3.1.0 - '@volar/typescript@2.4.8': + '@volar/typescript@2.4.23': dependencies: - '@volar/language-core': 2.4.8 + '@volar/language-core': 2.4.23 path-browserify: 1.0.1 - vscode-uri: 3.0.8 + vscode-uri: 3.1.0 - '@vue-macros/api@0.13.0(rollup@4.24.3)(vue@3.5.12(typescript@5.6.3))': + '@vue-macros/api@0.13.4(vue@3.5.21(typescript@5.6.3))': dependencies: - '@babel/types': 7.26.0 - '@vue-macros/common': 1.15.0(rollup@4.24.3)(vue@3.5.12(typescript@5.6.3)) - oxc-resolver: 2.0.1 + '@vue-macros/common': 1.16.1(vue@3.5.21(typescript@5.6.3)) + oxc-resolver: 4.2.0 transitivePeerDependencies: - - rollup - vue - '@vue-macros/better-define@1.11.0(rollup@4.24.3)(vue@3.5.12(typescript@5.6.3))': + '@vue-macros/better-define@1.11.4(vue@3.5.21(typescript@5.6.3))': dependencies: - '@vue-macros/api': 0.13.0(rollup@4.24.3)(vue@3.5.12(typescript@5.6.3)) - '@vue-macros/common': 1.15.0(rollup@4.24.3)(vue@3.5.12(typescript@5.6.3)) - unplugin: 1.15.0 + '@vue-macros/api': 0.13.4(vue@3.5.21(typescript@5.6.3)) + '@vue-macros/common': 1.16.1(vue@3.5.21(typescript@5.6.3)) + unplugin: 1.16.1 transitivePeerDependencies: - - rollup - vue - - webpack-sources - '@vue-macros/boolean-prop@0.5.2(rollup@4.24.3)(vue@3.5.12(typescript@5.6.3))': + '@vue-macros/boolean-prop@0.5.2(rollup@4.50.1)(vue@3.5.21(typescript@5.6.3))': dependencies: - '@vue-macros/common': 1.15.0(rollup@4.24.3)(vue@3.5.12(typescript@5.6.3)) - '@vue/compiler-core': 3.5.12 + '@vue-macros/common': 1.15.0(rollup@4.50.1)(vue@3.5.21(typescript@5.6.3)) + '@vue/compiler-core': 3.5.21 transitivePeerDependencies: - rollup - vue - '@vue-macros/chain-call@0.4.2(rollup@4.24.3)(vue@3.5.12(typescript@5.6.3))': + '@vue-macros/boolean-prop@0.5.5(vue@3.5.21(typescript@5.6.3))': dependencies: - '@vue-macros/common': 1.15.0(rollup@4.24.3)(vue@3.5.12(typescript@5.6.3)) - unplugin: 1.15.0 + '@vue-macros/common': 1.16.1(vue@3.5.21(typescript@5.6.3)) + '@vue/compiler-core': 3.5.21 + transitivePeerDependencies: + - vue + + '@vue-macros/chain-call@0.4.5(vue@3.5.21(typescript@5.6.3))': + dependencies: + '@vue-macros/common': 1.16.1(vue@3.5.21(typescript@5.6.3)) + unplugin: 1.16.1 transitivePeerDependencies: - - rollup - vue - - webpack-sources - '@vue-macros/common@1.15.0(rollup@4.24.3)(vue@3.5.12(typescript@5.6.3))': + '@vue-macros/common@1.15.0(rollup@4.50.1)(vue@3.5.21(typescript@5.6.3))': dependencies: - '@babel/types': 7.26.0 - '@rollup/pluginutils': 5.1.3(rollup@4.24.3) - '@vue/compiler-sfc': 3.5.12 - ast-kit: 1.3.1 - local-pkg: 0.5.0 - magic-string-ast: 0.6.2 + '@babel/types': 7.28.4 + '@rollup/pluginutils': 5.3.0(rollup@4.50.1) + '@vue/compiler-sfc': 3.5.21 + ast-kit: 1.4.3 + local-pkg: 0.5.1 + magic-string-ast: 0.6.3 optionalDependencies: - vue: 3.5.12(typescript@5.6.3) + vue: 3.5.21(typescript@5.6.3) transitivePeerDependencies: - rollup - '@vue-macros/config@0.5.0(rollup@4.24.3)(vue@3.5.12(typescript@5.6.3))': + '@vue-macros/common@1.16.1(vue@3.5.21(typescript@5.6.3))': + dependencies: + '@vue/compiler-sfc': 3.5.21 + ast-kit: 1.4.3 + local-pkg: 1.1.2 + magic-string-ast: 0.7.1 + pathe: 2.0.3 + picomatch: 4.0.3 + optionalDependencies: + vue: 3.5.21(typescript@5.6.3) + + '@vue-macros/config@0.5.0(rollup@4.50.1)(vue@3.5.21(typescript@5.6.3))': dependencies: - '@vue-macros/common': 1.15.0(rollup@4.24.3)(vue@3.5.12(typescript@5.6.3)) - make-synchronized: 0.2.9 - unconfig: 0.6.0 + '@vue-macros/common': 1.15.0(rollup@4.50.1)(vue@3.5.21(typescript@5.6.3)) + make-synchronized: 0.2.10 + unconfig: 0.6.1 transitivePeerDependencies: - rollup - supports-color - vue - '@vue-macros/define-emit@0.5.1(rollup@4.24.3)(vue@3.5.12(typescript@5.6.3))': + '@vue-macros/config@0.6.1(vue@3.5.21(typescript@5.6.3))': dependencies: - '@vue-macros/common': 1.15.0(rollup@4.24.3)(vue@3.5.12(typescript@5.6.3)) - unplugin: 1.15.0 - vue: 3.5.12(typescript@5.6.3) + '@vue-macros/common': 1.16.1(vue@3.5.21(typescript@5.6.3)) + make-synchronized: 0.2.10 + unconfig: 7.3.3 transitivePeerDependencies: - - rollup - - webpack-sources + - vue - '@vue-macros/define-models@1.3.2(@vueuse/core@11.2.0(vue@3.5.12(typescript@5.6.3)))(rollup@4.24.3)(vue@3.5.12(typescript@5.6.3))': + '@vue-macros/define-emit@0.5.4(vue@3.5.21(typescript@5.6.3))': dependencies: - '@vue-macros/common': 1.15.0(rollup@4.24.3)(vue@3.5.12(typescript@5.6.3)) + '@vue-macros/common': 1.16.1(vue@3.5.21(typescript@5.6.3)) + unplugin: 1.16.1 + vue: 3.5.21(typescript@5.6.3) + + '@vue-macros/define-models@1.3.5(@vueuse/core@11.3.0(vue@3.5.21(typescript@5.6.3)))(vue@3.5.21(typescript@5.6.3))': + dependencies: + '@vue-macros/common': 1.16.1(vue@3.5.21(typescript@5.6.3)) ast-walker-scope: 0.6.2 - unplugin: 1.15.0 + unplugin: 1.16.1 optionalDependencies: - '@vueuse/core': 11.2.0(vue@3.5.12(typescript@5.6.3)) + '@vueuse/core': 11.3.0(vue@3.5.21(typescript@5.6.3)) transitivePeerDependencies: - - rollup - vue - - webpack-sources - '@vue-macros/define-prop@0.6.1(rollup@4.24.3)(vue@3.5.12(typescript@5.6.3))': + '@vue-macros/define-prop@0.6.5(vue@3.5.21(typescript@5.6.3))': dependencies: - '@vue-macros/api': 0.13.0(rollup@4.24.3)(vue@3.5.12(typescript@5.6.3)) - '@vue-macros/common': 1.15.0(rollup@4.24.3)(vue@3.5.12(typescript@5.6.3)) - unplugin: 1.15.0 - vue: 3.5.12(typescript@5.6.3) - transitivePeerDependencies: - - rollup - - webpack-sources + '@vue-macros/api': 0.13.4(vue@3.5.21(typescript@5.6.3)) + '@vue-macros/common': 1.16.1(vue@3.5.21(typescript@5.6.3)) + unplugin: 1.16.1 + vue: 3.5.21(typescript@5.6.3) - '@vue-macros/define-props-refs@1.3.2(rollup@4.24.3)(vue@3.5.12(typescript@5.6.3))': + '@vue-macros/define-props-refs@1.3.5(vue@3.5.21(typescript@5.6.3))': dependencies: - '@vue-macros/common': 1.15.0(rollup@4.24.3)(vue@3.5.12(typescript@5.6.3)) - unplugin: 1.15.0 - vue: 3.5.12(typescript@5.6.3) - transitivePeerDependencies: - - rollup - - webpack-sources + '@vue-macros/common': 1.16.1(vue@3.5.21(typescript@5.6.3)) + unplugin: 1.16.1 + vue: 3.5.21(typescript@5.6.3) - '@vue-macros/define-props@4.0.3(@vue-macros/reactivity-transform@1.1.3(rollup@4.24.3)(vue@3.5.12(typescript@5.6.3)))(rollup@4.24.3)(vue@3.5.12(typescript@5.6.3))': + '@vue-macros/define-props@4.0.6(@vue-macros/reactivity-transform@1.1.6(vue@3.5.21(typescript@5.6.3)))(vue@3.5.21(typescript@5.6.3))': dependencies: - '@vue-macros/common': 1.15.0(rollup@4.24.3)(vue@3.5.12(typescript@5.6.3)) - '@vue-macros/reactivity-transform': 1.1.3(rollup@4.24.3)(vue@3.5.12(typescript@5.6.3)) - unplugin: 1.15.0 - vue: 3.5.12(typescript@5.6.3) - transitivePeerDependencies: - - rollup - - webpack-sources + '@vue-macros/common': 1.16.1(vue@3.5.21(typescript@5.6.3)) + '@vue-macros/reactivity-transform': 1.1.6(vue@3.5.21(typescript@5.6.3)) + unplugin: 1.16.1 + vue: 3.5.21(typescript@5.6.3) - '@vue-macros/define-render@1.6.2(rollup@4.24.3)(vue@3.5.12(typescript@5.6.3))': + '@vue-macros/define-render@1.6.6(vue@3.5.21(typescript@5.6.3))': dependencies: - '@vue-macros/common': 1.15.0(rollup@4.24.3)(vue@3.5.12(typescript@5.6.3)) - unplugin: 1.15.0 - vue: 3.5.12(typescript@5.6.3) - transitivePeerDependencies: - - rollup - - webpack-sources + '@vue-macros/common': 1.16.1(vue@3.5.21(typescript@5.6.3)) + unplugin: 1.16.1 + vue: 3.5.21(typescript@5.6.3) - '@vue-macros/define-slots@1.2.2(rollup@4.24.3)(vue@3.5.12(typescript@5.6.3))': + '@vue-macros/define-slots@1.2.6(vue@3.5.21(typescript@5.6.3))': dependencies: - '@vue-macros/common': 1.15.0(rollup@4.24.3)(vue@3.5.12(typescript@5.6.3)) - unplugin: 1.15.0 - vue: 3.5.12(typescript@5.6.3) - transitivePeerDependencies: - - rollup - - webpack-sources + '@vue-macros/common': 1.16.1(vue@3.5.21(typescript@5.6.3)) + unplugin: 1.16.1 + vue: 3.5.21(typescript@5.6.3) - '@vue-macros/define-stylex@0.2.0(rollup@4.24.3)(vue@3.5.12(typescript@5.6.3))': + '@vue-macros/define-stylex@0.2.3(vue@3.5.21(typescript@5.6.3))': dependencies: - '@vue-macros/common': 1.15.0(rollup@4.24.3)(vue@3.5.12(typescript@5.6.3)) - '@vue/compiler-dom': 3.5.12 - unplugin: 1.15.0 + '@vue-macros/common': 1.16.1(vue@3.5.21(typescript@5.6.3)) + '@vue/compiler-dom': 3.5.21 + unplugin: 1.16.1 transitivePeerDependencies: - - rollup - vue - - webpack-sources - '@vue-macros/devtools@0.4.0(typescript@5.6.3)(vite@5.4.10(@types/node@22.8.6))': + '@vue-macros/devtools@0.4.1(typescript@5.6.3)(vite@5.4.19(@types/node@22.18.1))': dependencies: - sirv: 2.0.4 - vue: 3.5.12(typescript@5.6.3) + sirv: 3.0.2 + vue: 3.5.21(typescript@5.6.3) optionalDependencies: - vite: 5.4.10(@types/node@22.8.6) + vite: 5.4.19(@types/node@22.18.1) transitivePeerDependencies: - typescript - '@vue-macros/export-expose@0.3.2(rollup@4.24.3)(vue@3.5.12(typescript@5.6.3))': + '@vue-macros/export-expose@0.3.5(vue@3.5.21(typescript@5.6.3))': dependencies: - '@vue-macros/common': 1.15.0(rollup@4.24.3)(vue@3.5.12(typescript@5.6.3)) - '@vue/compiler-sfc': 3.5.12 - unplugin: 1.15.0 - vue: 3.5.12(typescript@5.6.3) - transitivePeerDependencies: - - rollup - - webpack-sources + '@vue-macros/common': 1.16.1(vue@3.5.21(typescript@5.6.3)) + '@vue/compiler-sfc': 3.5.21 + unplugin: 1.16.1 + vue: 3.5.21(typescript@5.6.3) - '@vue-macros/export-props@0.6.2(rollup@4.24.3)(vue@3.5.12(typescript@5.6.3))': + '@vue-macros/export-props@0.6.5(vue@3.5.21(typescript@5.6.3))': dependencies: - '@vue-macros/common': 1.15.0(rollup@4.24.3)(vue@3.5.12(typescript@5.6.3)) - unplugin: 1.15.0 - vue: 3.5.12(typescript@5.6.3) - transitivePeerDependencies: - - rollup - - webpack-sources + '@vue-macros/common': 1.16.1(vue@3.5.21(typescript@5.6.3)) + unplugin: 1.16.1 + vue: 3.5.21(typescript@5.6.3) - '@vue-macros/export-render@0.3.2(rollup@4.24.3)(vue@3.5.12(typescript@5.6.3))': + '@vue-macros/export-render@0.3.5(vue@3.5.21(typescript@5.6.3))': dependencies: - '@vue-macros/common': 1.15.0(rollup@4.24.3)(vue@3.5.12(typescript@5.6.3)) - unplugin: 1.15.0 - vue: 3.5.12(typescript@5.6.3) - transitivePeerDependencies: - - rollup - - webpack-sources + '@vue-macros/common': 1.16.1(vue@3.5.21(typescript@5.6.3)) + unplugin: 1.16.1 + vue: 3.5.21(typescript@5.6.3) - '@vue-macros/hoist-static@1.6.2(rollup@4.24.3)(vue@3.5.12(typescript@5.6.3))': + '@vue-macros/hoist-static@1.7.0(vue@3.5.21(typescript@5.6.3))': dependencies: - '@vue-macros/common': 1.15.0(rollup@4.24.3)(vue@3.5.12(typescript@5.6.3)) - unplugin: 1.15.0 + '@vue-macros/common': 1.16.1(vue@3.5.21(typescript@5.6.3)) + unplugin: 1.16.1 transitivePeerDependencies: - - rollup - vue - - webpack-sources - '@vue-macros/jsx-directive@0.9.2(rollup@4.24.3)(typescript@5.6.3)': + '@vue-macros/jsx-directive@0.10.6(typescript@5.6.3)': dependencies: - '@vue-macros/common': 1.15.0(rollup@4.24.3)(vue@3.5.12(typescript@5.6.3)) - unplugin: 1.15.0 - vue: 3.5.12(typescript@5.6.3) + '@vue-macros/common': 1.16.1(vue@3.5.21(typescript@5.6.3)) + '@vue/compiler-sfc': 3.5.21 + unplugin: 1.16.1 + vue: 3.5.21(typescript@5.6.3) transitivePeerDependencies: - - rollup - typescript - - webpack-sources - '@vue-macros/named-template@0.5.2(rollup@4.24.3)(vue@3.5.12(typescript@5.6.3))': + '@vue-macros/named-template@0.5.5(vue@3.5.21(typescript@5.6.3))': dependencies: - '@vue-macros/common': 1.15.0(rollup@4.24.3)(vue@3.5.12(typescript@5.6.3)) - '@vue/compiler-dom': 3.5.12 - unplugin: 1.15.0 + '@vue-macros/common': 1.16.1(vue@3.5.21(typescript@5.6.3)) + '@vue/compiler-dom': 3.5.21 + unplugin: 1.16.1 transitivePeerDependencies: - - rollup - vue - - webpack-sources - '@vue-macros/reactivity-transform@1.1.3(rollup@4.24.3)(vue@3.5.12(typescript@5.6.3))': + '@vue-macros/reactivity-transform@1.1.6(vue@3.5.21(typescript@5.6.3))': + dependencies: + '@babel/parser': 7.28.4 + '@vue-macros/common': 1.16.1(vue@3.5.21(typescript@5.6.3)) + '@vue/compiler-core': 3.5.21 + '@vue/shared': 3.5.21 + magic-string: 0.30.18 + unplugin: 1.16.1 + vue: 3.5.21(typescript@5.6.3) + + '@vue-macros/script-lang@0.2.5(vue@3.5.21(typescript@5.6.3))': dependencies: - '@babel/parser': 7.26.2 - '@vue-macros/common': 1.15.0(rollup@4.24.3)(vue@3.5.12(typescript@5.6.3)) - '@vue/compiler-core': 3.5.12 - '@vue/shared': 3.5.12 - magic-string: 0.30.12 - unplugin: 1.15.0 - vue: 3.5.12(typescript@5.6.3) + '@vue-macros/common': 1.16.1(vue@3.5.21(typescript@5.6.3)) + unplugin: 1.16.1 + vue: 3.5.21(typescript@5.6.3) + + '@vue-macros/setup-block@0.4.5(vue@3.5.21(typescript@5.6.3))': + dependencies: + '@vue-macros/common': 1.16.1(vue@3.5.21(typescript@5.6.3)) + '@vue/compiler-dom': 3.5.21 + unplugin: 1.16.1 transitivePeerDependencies: - - rollup - - webpack-sources + - vue - '@vue-macros/script-lang@0.2.2(rollup@4.24.3)(vue@3.5.12(typescript@5.6.3))': + '@vue-macros/setup-component@0.18.5(vue@3.5.21(typescript@5.6.3))': dependencies: - '@vue-macros/common': 1.15.0(rollup@4.24.3)(vue@3.5.12(typescript@5.6.3)) - unplugin: 1.15.0 - vue: 3.5.12(typescript@5.6.3) + '@vue-macros/common': 1.16.1(vue@3.5.21(typescript@5.6.3)) + unplugin: 1.16.1 transitivePeerDependencies: - - rollup - - webpack-sources + - vue - '@vue-macros/setup-block@0.4.2(rollup@4.24.3)(vue@3.5.12(typescript@5.6.3))': + '@vue-macros/setup-sfc@0.18.5(vue@3.5.21(typescript@5.6.3))': dependencies: - '@vue-macros/common': 1.15.0(rollup@4.24.3)(vue@3.5.12(typescript@5.6.3)) - '@vue/compiler-dom': 3.5.12 - unplugin: 1.15.0 + '@vue-macros/common': 1.16.1(vue@3.5.21(typescript@5.6.3)) + unplugin: 1.16.1 transitivePeerDependencies: - - rollup - vue - - webpack-sources - '@vue-macros/setup-component@0.18.2(rollup@4.24.3)(vue@3.5.12(typescript@5.6.3))': + '@vue-macros/short-bind@1.1.2(rollup@4.50.1)(vue@3.5.21(typescript@5.6.3))': dependencies: - '@vue-macros/common': 1.15.0(rollup@4.24.3)(vue@3.5.12(typescript@5.6.3)) - unplugin: 1.15.0 + '@vue-macros/common': 1.15.0(rollup@4.50.1)(vue@3.5.21(typescript@5.6.3)) + '@vue/compiler-core': 3.5.21 transitivePeerDependencies: - rollup - vue - - webpack-sources - '@vue-macros/setup-sfc@0.18.2(rollup@4.24.3)(vue@3.5.12(typescript@5.6.3))': + '@vue-macros/short-bind@1.1.5(vue@3.5.21(typescript@5.6.3))': dependencies: - '@vue-macros/common': 1.15.0(rollup@4.24.3)(vue@3.5.12(typescript@5.6.3)) - unplugin: 1.15.0 + '@vue-macros/common': 1.16.1(vue@3.5.21(typescript@5.6.3)) + '@vue/compiler-core': 3.5.21 transitivePeerDependencies: - - rollup - vue - - webpack-sources - '@vue-macros/short-bind@1.1.2(rollup@4.24.3)(vue@3.5.12(typescript@5.6.3))': + '@vue-macros/short-emits@1.6.5(vue@3.5.21(typescript@5.6.3))': dependencies: - '@vue-macros/common': 1.15.0(rollup@4.24.3)(vue@3.5.12(typescript@5.6.3)) - '@vue/compiler-core': 3.5.12 + '@vue-macros/common': 1.16.1(vue@3.5.21(typescript@5.6.3)) + unplugin: 1.16.1 transitivePeerDependencies: - - rollup - vue - '@vue-macros/short-emits@1.6.2(rollup@4.24.3)(vue@3.5.12(typescript@5.6.3))': + '@vue-macros/short-vmodel@1.5.2(rollup@4.50.1)(vue@3.5.21(typescript@5.6.3))': dependencies: - '@vue-macros/common': 1.15.0(rollup@4.24.3)(vue@3.5.12(typescript@5.6.3)) - unplugin: 1.15.0 + '@vue-macros/common': 1.15.0(rollup@4.50.1)(vue@3.5.21(typescript@5.6.3)) + '@vue/compiler-core': 3.5.21 transitivePeerDependencies: - rollup - vue - - webpack-sources - '@vue-macros/short-vmodel@1.5.2(rollup@4.24.3)(vue@3.5.12(typescript@5.6.3))': + '@vue-macros/short-vmodel@1.5.5(vue@3.5.21(typescript@5.6.3))': dependencies: - '@vue-macros/common': 1.15.0(rollup@4.24.3)(vue@3.5.12(typescript@5.6.3)) - '@vue/compiler-core': 3.5.12 + '@vue-macros/common': 1.16.1(vue@3.5.21(typescript@5.6.3)) + '@vue/compiler-core': 3.5.21 transitivePeerDependencies: - - rollup - vue - '@vue-macros/volar@0.30.5(rollup@4.24.3)(typescript@5.6.3)(vue-tsc@2.1.10(typescript@5.6.3))(vue@3.5.12(typescript@5.6.3))': + '@vue-macros/volar@0.30.15(typescript@5.6.3)(vue-tsc@2.2.12(typescript@5.6.3))(vue@3.5.21(typescript@5.6.3))': + dependencies: + '@vue-macros/boolean-prop': 0.5.5(vue@3.5.21(typescript@5.6.3)) + '@vue-macros/common': 1.16.1(vue@3.5.21(typescript@5.6.3)) + '@vue-macros/config': 0.6.1(vue@3.5.21(typescript@5.6.3)) + '@vue-macros/short-bind': 1.1.5(vue@3.5.21(typescript@5.6.3)) + '@vue-macros/short-vmodel': 1.5.5(vue@3.5.21(typescript@5.6.3)) + '@vue/language-core': 2.1.10(typescript@5.6.3) + muggle-string: 0.4.1 + ts-macro: 0.1.35 + optionalDependencies: + vue-tsc: 2.2.12(typescript@5.6.3) + transitivePeerDependencies: + - typescript + - vue + + '@vue-macros/volar@0.30.5(rollup@4.50.1)(typescript@5.6.3)(vue-tsc@2.2.12(typescript@5.6.3))(vue@3.5.21(typescript@5.6.3))': dependencies: - '@vue-macros/boolean-prop': 0.5.2(rollup@4.24.3)(vue@3.5.12(typescript@5.6.3)) - '@vue-macros/common': 1.15.0(rollup@4.24.3)(vue@3.5.12(typescript@5.6.3)) - '@vue-macros/config': 0.5.0(rollup@4.24.3)(vue@3.5.12(typescript@5.6.3)) - '@vue-macros/short-bind': 1.1.2(rollup@4.24.3)(vue@3.5.12(typescript@5.6.3)) - '@vue-macros/short-vmodel': 1.5.2(rollup@4.24.3)(vue@3.5.12(typescript@5.6.3)) + '@vue-macros/boolean-prop': 0.5.2(rollup@4.50.1)(vue@3.5.21(typescript@5.6.3)) + '@vue-macros/common': 1.15.0(rollup@4.50.1)(vue@3.5.21(typescript@5.6.3)) + '@vue-macros/config': 0.5.0(rollup@4.50.1)(vue@3.5.21(typescript@5.6.3)) + '@vue-macros/short-bind': 1.1.2(rollup@4.50.1)(vue@3.5.21(typescript@5.6.3)) + '@vue-macros/short-vmodel': 1.5.2(rollup@4.50.1)(vue@3.5.21(typescript@5.6.3)) '@vue/language-core': 2.1.6(typescript@5.6.3) muggle-string: 0.4.1 optionalDependencies: - vue-tsc: 2.1.10(typescript@5.6.3) + vue-tsc: 2.2.12(typescript@5.6.3) transitivePeerDependencies: - rollup - supports-color - typescript - vue - '@vue/babel-helper-vue-transform-on@1.2.5': {} + '@vue/babel-helper-vue-transform-on@1.5.0': {} - '@vue/babel-plugin-jsx@1.2.5(@babel/core@7.26.0)': + '@vue/babel-plugin-jsx@1.5.0(@babel/core@7.28.4)': dependencies: - '@babel/helper-module-imports': 7.25.9 - '@babel/helper-plugin-utils': 7.25.9 - '@babel/plugin-syntax-jsx': 7.25.9(@babel/core@7.26.0) - '@babel/template': 7.25.9 - '@babel/traverse': 7.25.9 - '@babel/types': 7.26.0 - '@vue/babel-helper-vue-transform-on': 1.2.5 - '@vue/babel-plugin-resolve-type': 1.2.5(@babel/core@7.26.0) - html-tags: 3.3.1 - svg-tags: 1.0.0 + '@babel/helper-module-imports': 7.27.1 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.28.4) + '@babel/template': 7.27.2 + '@babel/traverse': 7.28.4 + '@babel/types': 7.28.4 + '@vue/babel-helper-vue-transform-on': 1.5.0 + '@vue/babel-plugin-resolve-type': 1.5.0(@babel/core@7.28.4) + '@vue/shared': 3.5.21 optionalDependencies: - '@babel/core': 7.26.0 + '@babel/core': 7.28.4 transitivePeerDependencies: - supports-color - '@vue/babel-plugin-resolve-type@1.2.5(@babel/core@7.26.0)': + '@vue/babel-plugin-resolve-type@1.5.0(@babel/core@7.28.4)': dependencies: - '@babel/code-frame': 7.26.2 - '@babel/core': 7.26.0 - '@babel/helper-module-imports': 7.25.9 - '@babel/helper-plugin-utils': 7.25.9 - '@babel/parser': 7.26.2 - '@vue/compiler-sfc': 3.5.12 + '@babel/code-frame': 7.27.1 + '@babel/core': 7.28.4 + '@babel/helper-module-imports': 7.27.1 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/parser': 7.28.4 + '@vue/compiler-sfc': 3.5.21 transitivePeerDependencies: - supports-color - '@vue/compiler-core@3.5.12': + '@vue/compiler-core@3.5.21': dependencies: - '@babel/parser': 7.26.2 - '@vue/shared': 3.5.12 + '@babel/parser': 7.28.4 + '@vue/shared': 3.5.21 entities: 4.5.0 estree-walker: 2.0.2 source-map-js: 1.2.1 - '@vue/compiler-dom@3.5.12': + '@vue/compiler-dom@3.5.21': dependencies: - '@vue/compiler-core': 3.5.12 - '@vue/shared': 3.5.12 + '@vue/compiler-core': 3.5.21 + '@vue/shared': 3.5.21 - '@vue/compiler-sfc@3.5.12': + '@vue/compiler-sfc@3.5.21': dependencies: - '@babel/parser': 7.26.2 - '@vue/compiler-core': 3.5.12 - '@vue/compiler-dom': 3.5.12 - '@vue/compiler-ssr': 3.5.12 - '@vue/shared': 3.5.12 + '@babel/parser': 7.28.4 + '@vue/compiler-core': 3.5.21 + '@vue/compiler-dom': 3.5.21 + '@vue/compiler-ssr': 3.5.21 + '@vue/shared': 3.5.21 estree-walker: 2.0.2 - magic-string: 0.30.12 - postcss: 8.4.47 + magic-string: 0.30.18 + postcss: 8.5.6 source-map-js: 1.2.1 - '@vue/compiler-ssr@3.5.12': + '@vue/compiler-ssr@3.5.21': dependencies: - '@vue/compiler-dom': 3.5.12 - '@vue/shared': 3.5.12 + '@vue/compiler-dom': 3.5.21 + '@vue/shared': 3.5.21 '@vue/compiler-vue2@2.7.16': dependencies: @@ -5407,39 +5814,39 @@ snapshots: '@vue/devtools-api@6.6.4': {} - '@vue/devtools-core@7.6.3(vite@5.4.10(@types/node@22.8.6))(vue@3.5.12(typescript@5.6.3))': + '@vue/devtools-core@7.7.7(vite@5.4.19(@types/node@22.18.1))(vue@3.5.21(typescript@5.6.3))': dependencies: - '@vue/devtools-kit': 7.6.3 - '@vue/devtools-shared': 7.6.3 + '@vue/devtools-kit': 7.7.7 + '@vue/devtools-shared': 7.7.7 mitt: 3.0.1 - nanoid: 3.3.7 - pathe: 1.1.2 - vite-hot-client: 0.2.3(vite@5.4.10(@types/node@22.8.6)) - vue: 3.5.12(typescript@5.6.3) + nanoid: 5.1.5 + pathe: 2.0.3 + vite-hot-client: 2.1.0(vite@5.4.19(@types/node@22.18.1)) + vue: 3.5.21(typescript@5.6.3) transitivePeerDependencies: - vite - '@vue/devtools-kit@7.6.3': + '@vue/devtools-kit@7.7.7': dependencies: - '@vue/devtools-shared': 7.6.3 - birpc: 0.2.19 + '@vue/devtools-shared': 7.7.7 + birpc: 2.5.0 hookable: 5.5.3 mitt: 3.0.1 perfect-debounce: 1.0.0 speakingurl: 14.0.1 - superjson: 2.2.1 + superjson: 2.2.2 - '@vue/devtools-shared@7.6.3': + '@vue/devtools-shared@7.7.7': dependencies: rfdc: 1.4.1 '@vue/language-core@2.1.10(typescript@5.6.3)': dependencies: - '@volar/language-core': 2.4.8 - '@vue/compiler-dom': 3.5.12 + '@volar/language-core': 2.4.23 + '@vue/compiler-dom': 3.5.21 '@vue/compiler-vue2': 2.7.16 - '@vue/shared': 3.5.12 - alien-signals: 0.2.0 + '@vue/shared': 3.5.21 + alien-signals: 0.2.2 minimatch: 9.0.5 muggle-string: 0.4.1 path-browserify: 1.0.1 @@ -5448,10 +5855,10 @@ snapshots: '@vue/language-core@2.1.6(typescript@5.6.3)': dependencies: - '@volar/language-core': 2.4.8 - '@vue/compiler-dom': 3.5.12 + '@volar/language-core': 2.4.23 + '@vue/compiler-dom': 3.5.21 '@vue/compiler-vue2': 2.7.16 - '@vue/shared': 3.5.12 + '@vue/shared': 3.5.21 computeds: 0.0.1 minimatch: 9.0.5 muggle-string: 0.4.1 @@ -5459,54 +5866,80 @@ snapshots: optionalDependencies: typescript: 5.6.3 - '@vue/reactivity@3.5.12': + '@vue/language-core@2.2.0(typescript@5.6.3)': dependencies: - '@vue/shared': 3.5.12 + '@volar/language-core': 2.4.23 + '@vue/compiler-dom': 3.5.21 + '@vue/compiler-vue2': 2.7.16 + '@vue/shared': 3.5.21 + alien-signals: 0.4.14 + minimatch: 9.0.5 + muggle-string: 0.4.1 + path-browserify: 1.0.1 + optionalDependencies: + typescript: 5.6.3 - '@vue/runtime-core@3.5.12': + '@vue/language-core@2.2.12(typescript@5.6.3)': dependencies: - '@vue/reactivity': 3.5.12 - '@vue/shared': 3.5.12 + '@volar/language-core': 2.4.15 + '@vue/compiler-dom': 3.5.21 + '@vue/compiler-vue2': 2.7.16 + '@vue/shared': 3.5.21 + alien-signals: 1.0.13 + minimatch: 9.0.5 + muggle-string: 0.4.1 + path-browserify: 1.0.1 + optionalDependencies: + typescript: 5.6.3 + + '@vue/reactivity@3.5.21': + dependencies: + '@vue/shared': 3.5.21 - '@vue/runtime-dom@3.5.12': + '@vue/runtime-core@3.5.21': dependencies: - '@vue/reactivity': 3.5.12 - '@vue/runtime-core': 3.5.12 - '@vue/shared': 3.5.12 + '@vue/reactivity': 3.5.21 + '@vue/shared': 3.5.21 + + '@vue/runtime-dom@3.5.21': + dependencies: + '@vue/reactivity': 3.5.21 + '@vue/runtime-core': 3.5.21 + '@vue/shared': 3.5.21 csstype: 3.1.3 - '@vue/server-renderer@3.5.12(vue@3.5.12(typescript@5.6.3))': + '@vue/server-renderer@3.5.21(vue@3.5.21(typescript@5.6.3))': dependencies: - '@vue/compiler-ssr': 3.5.12 - '@vue/shared': 3.5.12 - vue: 3.5.12(typescript@5.6.3) + '@vue/compiler-ssr': 3.5.21 + '@vue/shared': 3.5.21 + vue: 3.5.21(typescript@5.6.3) - '@vue/shared@3.5.12': {} + '@vue/shared@3.5.21': {} - '@vueuse/core@11.2.0(vue@3.5.12(typescript@5.6.3))': + '@vueuse/core@11.3.0(vue@3.5.21(typescript@5.6.3))': dependencies: '@types/web-bluetooth': 0.0.20 - '@vueuse/metadata': 11.2.0 - '@vueuse/shared': 11.2.0(vue@3.5.12(typescript@5.6.3)) - vue-demi: 0.14.10(vue@3.5.12(typescript@5.6.3)) + '@vueuse/metadata': 11.3.0 + '@vueuse/shared': 11.3.0(vue@3.5.21(typescript@5.6.3)) + vue-demi: 0.14.10(vue@3.5.21(typescript@5.6.3)) transitivePeerDependencies: - '@vue/composition-api' - vue - '@vueuse/metadata@11.2.0': {} + '@vueuse/metadata@11.3.0': {} - '@vueuse/shared@11.2.0(vue@3.5.12(typescript@5.6.3))': + '@vueuse/shared@11.3.0(vue@3.5.21(typescript@5.6.3))': dependencies: - vue-demi: 0.14.10(vue@3.5.12(typescript@5.6.3)) + vue-demi: 0.14.10(vue@3.5.21(typescript@5.6.3)) transitivePeerDependencies: - '@vue/composition-api' - vue - acorn-jsx@5.3.2(acorn@8.14.0): + acorn-jsx@5.3.2(acorn@8.15.0): dependencies: - acorn: 8.14.0 + acorn: 8.15.0 - acorn@8.14.0: {} + acorn@8.15.0: {} ajv-draft-04@1.0.0(ajv@8.13.0): optionalDependencies: @@ -5537,11 +5970,15 @@ snapshots: require-from-string: 2.0.2 uri-js: 4.4.1 - alien-signals@0.2.0: {} + alien-signals@0.2.2: {} + + alien-signals@0.4.14: {} + + alien-signals@1.0.13: {} ansi-regex@5.0.1: {} - ansi-regex@6.1.0: {} + ansi-regex@6.2.0: {} ansi-styles@4.3.0: dependencies: @@ -5566,34 +6003,32 @@ snapshots: argparse@2.0.1: {} - array-union@2.1.0: {} - - ast-kit@1.3.1: + ast-kit@1.4.3: dependencies: - '@babel/parser': 7.26.2 - pathe: 1.1.2 + '@babel/parser': 7.28.4 + pathe: 2.0.3 ast-walker-scope@0.6.2: dependencies: - '@babel/parser': 7.26.2 - ast-kit: 1.3.1 + '@babel/parser': 7.28.4 + ast-kit: 1.4.3 asynckit@0.4.0: {} - autoprefixer@10.4.20(postcss@8.4.47): + autoprefixer@10.4.21(postcss@8.5.6): dependencies: - browserslist: 4.24.2 - caniuse-lite: 1.0.30001677 + browserslist: 4.25.4 + caniuse-lite: 1.0.30001741 fraction.js: 4.3.7 normalize-range: 0.1.2 picocolors: 1.1.1 - postcss: 8.4.47 + postcss: 8.5.6 postcss-value-parser: 4.2.0 - axios@1.7.7: + axios@1.11.0: dependencies: - follow-redirects: 1.15.9 - form-data: 4.0.1 + follow-redirects: 1.15.11 + form-data: 4.0.4 proxy-from-env: 1.1.0 transitivePeerDependencies: - debug @@ -5602,16 +6037,16 @@ snapshots: binary-extensions@2.3.0: {} - birpc@0.2.19: {} + birpc@2.5.0: {} boolbase@1.0.0: {} - brace-expansion@1.1.11: + brace-expansion@1.1.12: dependencies: balanced-match: 1.0.2 concat-map: 0.0.1 - brace-expansion@2.0.1: + brace-expansion@2.0.2: dependencies: balanced-match: 1.0.2 @@ -5619,12 +6054,12 @@ snapshots: dependencies: fill-range: 7.1.1 - browserslist@4.24.2: + browserslist@4.25.4: dependencies: - caniuse-lite: 1.0.30001677 - electron-to-chromium: 1.5.50 - node-releases: 2.0.18 - update-browserslist-db: 1.1.1(browserslist@4.24.2) + caniuse-lite: 1.0.30001741 + electron-to-chromium: 1.5.214 + node-releases: 2.0.20 + update-browserslist-db: 1.1.3(browserslist@4.25.4) builtin-modules@3.3.0: {} @@ -5632,16 +6067,21 @@ snapshots: dependencies: run-applescript: 7.0.0 - bundle-require@5.0.0(esbuild@0.23.1): + bundle-require@5.1.0(esbuild@0.25.9): dependencies: - esbuild: 0.23.1 + esbuild: 0.25.9 load-tsconfig: 0.2.5 + call-bind-apply-helpers@1.0.2: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + callsites@3.1.0: {} camelcase-css@2.0.1: {} - caniuse-lite@1.0.30001677: {} + caniuse-lite@1.0.30001741: {} ccount@2.0.1: {} @@ -5652,6 +6092,10 @@ snapshots: character-entities@2.0.2: {} + chart.js@4.5.0: + dependencies: + '@kurkle/color': 0.3.4 + chokidar@3.6.0: dependencies: anymatch: 3.1.3 @@ -5664,11 +6108,11 @@ snapshots: optionalDependencies: fsevents: 2.3.3 - ci-info@4.0.0: {} + ci-info@4.3.0: {} - cidr-tools@11.0.2: + cidr-tools@11.0.3: dependencies: - ip-bigint: 8.2.0 + ip-bigint: 8.2.2 clean-regexp@1.0.0: dependencies: @@ -5702,17 +6146,19 @@ snapshots: confbox@0.1.8: {} + confbox@0.2.2: {} + convert-source-map@2.0.0: {} copy-anything@3.0.5: dependencies: is-what: 4.1.16 - core-js-compat@3.39.0: + core-js-compat@3.45.1: dependencies: - browserslist: 4.24.2 + browserslist: 4.25.4 - cross-spawn@7.0.3: + cross-spawn@7.0.6: dependencies: path-key: 3.1.1 shebang-command: 2.0.0 @@ -5724,15 +6170,11 @@ snapshots: de-indent@1.0.2: {} - debug@3.2.7: + debug@4.4.1: dependencies: ms: 2.1.3 - debug@4.3.7: - dependencies: - ms: 2.1.3 - - decode-named-character-reference@1.0.2: + decode-named-character-reference@1.2.0: dependencies: character-entities: 2.0.2 @@ -5763,28 +6205,26 @@ snapshots: didyoumean@1.2.2: {} - dir-glob@3.0.1: - dependencies: - path-type: 4.0.0 - dlv@1.1.3: {} - doctrine@3.0.0: + dunder-proto@1.0.1: dependencies: - esutils: 2.0.3 + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 eastasianwidth@0.2.0: {} - electron-to-chromium@1.5.50: {} + electron-to-chromium@1.5.214: {} emoji-regex@8.0.0: {} emoji-regex@9.2.2: {} - enhanced-resolve@5.17.1: + enhanced-resolve@5.18.3: dependencies: graceful-fs: 4.2.11 - tapable: 2.2.1 + tapable: 2.2.3 entities@4.5.0: {} @@ -5794,7 +6234,20 @@ snapshots: error-stack-parser-es@0.1.5: {} - es-module-lexer@1.5.4: {} + es-define-property@1.0.1: {} + + es-errors@1.3.0: {} + + es-object-atoms@1.1.1: + dependencies: + es-errors: 1.3.0 + + es-set-tostringtag@2.1.0: + dependencies: + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + has-tostringtag: 1.0.2 + hasown: 2.0.2 esbuild@0.21.5: optionalDependencies: @@ -5822,32 +6275,34 @@ snapshots: '@esbuild/win32-ia32': 0.21.5 '@esbuild/win32-x64': 0.21.5 - esbuild@0.23.1: + esbuild@0.25.9: optionalDependencies: - '@esbuild/aix-ppc64': 0.23.1 - '@esbuild/android-arm': 0.23.1 - '@esbuild/android-arm64': 0.23.1 - '@esbuild/android-x64': 0.23.1 - '@esbuild/darwin-arm64': 0.23.1 - '@esbuild/darwin-x64': 0.23.1 - '@esbuild/freebsd-arm64': 0.23.1 - '@esbuild/freebsd-x64': 0.23.1 - '@esbuild/linux-arm': 0.23.1 - '@esbuild/linux-arm64': 0.23.1 - '@esbuild/linux-ia32': 0.23.1 - '@esbuild/linux-loong64': 0.23.1 - '@esbuild/linux-mips64el': 0.23.1 - '@esbuild/linux-ppc64': 0.23.1 - '@esbuild/linux-riscv64': 0.23.1 - '@esbuild/linux-s390x': 0.23.1 - '@esbuild/linux-x64': 0.23.1 - '@esbuild/netbsd-x64': 0.23.1 - '@esbuild/openbsd-arm64': 0.23.1 - '@esbuild/openbsd-x64': 0.23.1 - '@esbuild/sunos-x64': 0.23.1 - '@esbuild/win32-arm64': 0.23.1 - '@esbuild/win32-ia32': 0.23.1 - '@esbuild/win32-x64': 0.23.1 + '@esbuild/aix-ppc64': 0.25.9 + '@esbuild/android-arm': 0.25.9 + '@esbuild/android-arm64': 0.25.9 + '@esbuild/android-x64': 0.25.9 + '@esbuild/darwin-arm64': 0.25.9 + '@esbuild/darwin-x64': 0.25.9 + '@esbuild/freebsd-arm64': 0.25.9 + '@esbuild/freebsd-x64': 0.25.9 + '@esbuild/linux-arm': 0.25.9 + '@esbuild/linux-arm64': 0.25.9 + '@esbuild/linux-ia32': 0.25.9 + '@esbuild/linux-loong64': 0.25.9 + '@esbuild/linux-mips64el': 0.25.9 + '@esbuild/linux-ppc64': 0.25.9 + '@esbuild/linux-riscv64': 0.25.9 + '@esbuild/linux-s390x': 0.25.9 + '@esbuild/linux-x64': 0.25.9 + '@esbuild/netbsd-arm64': 0.25.9 + '@esbuild/netbsd-x64': 0.25.9 + '@esbuild/openbsd-arm64': 0.25.9 + '@esbuild/openbsd-x64': 0.25.9 + '@esbuild/openharmony-arm64': 0.25.9 + '@esbuild/sunos-x64': 0.25.9 + '@esbuild/win32-arm64': 0.25.9 + '@esbuild/win32-ia32': 0.25.9 + '@esbuild/win32-x64': 0.25.9 escalade@3.2.0: {} @@ -5865,258 +6320,265 @@ snapshots: optionalDependencies: source-map: 0.6.1 - eslint-compat-utils@0.5.1(eslint@9.14.0(jiti@2.4.0)): + eslint-compat-utils@0.5.1(eslint@9.35.0(jiti@2.5.1)): dependencies: - eslint: 9.14.0(jiti@2.4.0) - semver: 7.6.3 + eslint: 9.35.0(jiti@2.5.1) + semver: 7.7.2 - eslint-compat-utils@0.6.0(eslint@9.14.0(jiti@2.4.0)): + eslint-compat-utils@0.6.5(eslint@9.35.0(jiti@2.5.1)): dependencies: - eslint: 9.14.0(jiti@2.4.0) - semver: 7.6.3 + eslint: 9.35.0(jiti@2.5.1) + semver: 7.7.2 - eslint-config-flat-gitignore@0.3.0(eslint@9.14.0(jiti@2.4.0)): + eslint-config-flat-gitignore@1.0.1(eslint@9.35.0(jiti@2.5.1)): dependencies: - '@eslint/compat': 1.2.2(eslint@9.14.0(jiti@2.4.0)) - eslint: 9.14.0(jiti@2.4.0) - find-up-simple: 1.0.0 + '@eslint/compat': 1.3.2(eslint@9.35.0(jiti@2.5.1)) + eslint: 9.35.0(jiti@2.5.1) - eslint-flat-config-utils@0.4.0: + eslint-flat-config-utils@1.1.0: dependencies: - pathe: 1.1.2 + pathe: 2.0.3 - eslint-formatting-reporter@0.0.0(eslint@9.14.0(jiti@2.4.0)): + eslint-formatting-reporter@0.0.0(eslint@9.35.0(jiti@2.5.1)): dependencies: - eslint: 9.14.0(jiti@2.4.0) + eslint: 9.35.0(jiti@2.5.1) prettier-linter-helpers: 1.0.0 - eslint-import-resolver-node@0.3.9: + eslint-import-context@0.1.9(unrs-resolver@1.11.1): dependencies: - debug: 3.2.7 - is-core-module: 2.15.1 - resolve: 1.22.8 - transitivePeerDependencies: - - supports-color + get-tsconfig: 4.10.1 + stable-hash-x: 0.2.0 + optionalDependencies: + unrs-resolver: 1.11.1 - eslint-merge-processors@0.1.0(eslint@9.14.0(jiti@2.4.0)): + eslint-json-compat-utils@0.2.1(eslint@9.35.0(jiti@2.5.1))(jsonc-eslint-parser@2.4.0): dependencies: - eslint: 9.14.0(jiti@2.4.0) + eslint: 9.35.0(jiti@2.5.1) + esquery: 1.6.0 + jsonc-eslint-parser: 2.4.0 - eslint-parser-plain@0.1.0: {} + eslint-merge-processors@1.0.0(eslint@9.35.0(jiti@2.5.1)): + dependencies: + eslint: 9.35.0(jiti@2.5.1) - eslint-plugin-antfu@2.7.0(eslint@9.14.0(jiti@2.4.0)): + eslint-parser-plain@0.1.1: {} + + eslint-plugin-antfu@2.7.0(eslint@9.35.0(jiti@2.5.1)): dependencies: '@antfu/utils': 0.7.10 - eslint: 9.14.0(jiti@2.4.0) + eslint: 9.35.0(jiti@2.5.1) - eslint-plugin-command@0.2.6(eslint@9.14.0(jiti@2.4.0)): + eslint-plugin-command@2.1.0(eslint@9.35.0(jiti@2.5.1)): dependencies: - '@es-joy/jsdoccomment': 0.48.0 - eslint: 9.14.0(jiti@2.4.0) + '@es-joy/jsdoccomment': 0.50.2 + eslint: 9.35.0(jiti@2.5.1) - eslint-plugin-es-x@7.8.0(eslint@9.14.0(jiti@2.4.0)): + eslint-plugin-es-x@7.8.0(eslint@9.35.0(jiti@2.5.1)): dependencies: - '@eslint-community/eslint-utils': 4.4.1(eslint@9.14.0(jiti@2.4.0)) + '@eslint-community/eslint-utils': 4.8.0(eslint@9.35.0(jiti@2.5.1)) '@eslint-community/regexpp': 4.12.1 - eslint: 9.14.0(jiti@2.4.0) - eslint-compat-utils: 0.5.1(eslint@9.14.0(jiti@2.4.0)) + eslint: 9.35.0(jiti@2.5.1) + eslint-compat-utils: 0.5.1(eslint@9.35.0(jiti@2.5.1)) - eslint-plugin-format@0.1.2(eslint@9.14.0(jiti@2.4.0)): + eslint-plugin-format@0.1.3(eslint@9.35.0(jiti@2.5.1)): dependencies: '@dprint/formatter': 0.3.0 '@dprint/markdown': 0.17.8 - '@dprint/toml': 0.6.3 - eslint: 9.14.0(jiti@2.4.0) - eslint-formatting-reporter: 0.0.0(eslint@9.14.0(jiti@2.4.0)) - eslint-parser-plain: 0.1.0 - prettier: 3.3.3 - synckit: 0.9.2 - - eslint-plugin-import-x@4.4.0(eslint@9.14.0(jiti@2.4.0))(typescript@5.6.3): - dependencies: - '@typescript-eslint/utils': 8.13.0(eslint@9.14.0(jiti@2.4.0))(typescript@5.6.3) - debug: 4.3.7 - doctrine: 3.0.0 - eslint: 9.14.0(jiti@2.4.0) - eslint-import-resolver-node: 0.3.9 - get-tsconfig: 4.8.1 + '@dprint/toml': 0.6.4 + eslint: 9.35.0(jiti@2.5.1) + eslint-formatting-reporter: 0.0.0(eslint@9.35.0(jiti@2.5.1)) + eslint-parser-plain: 0.1.1 + prettier: 3.6.2 + synckit: 0.9.3 + + eslint-plugin-import-x@4.16.1(@typescript-eslint/utils@8.42.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.6.3))(eslint@9.35.0(jiti@2.5.1)): + dependencies: + '@typescript-eslint/types': 8.42.0 + comment-parser: 1.4.1 + debug: 4.4.1 + eslint: 9.35.0(jiti@2.5.1) + eslint-import-context: 0.1.9(unrs-resolver@1.11.1) is-glob: 4.0.3 - minimatch: 9.0.5 - semver: 7.6.3 - stable-hash: 0.0.4 - tslib: 2.8.1 + minimatch: 10.0.3 + semver: 7.7.2 + stable-hash-x: 0.2.0 + unrs-resolver: 1.11.1 + optionalDependencies: + '@typescript-eslint/utils': 8.42.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.6.3) transitivePeerDependencies: - supports-color - - typescript - eslint-plugin-jsdoc@50.4.3(eslint@9.14.0(jiti@2.4.0)): + eslint-plugin-jsdoc@50.8.0(eslint@9.35.0(jiti@2.5.1)): dependencies: - '@es-joy/jsdoccomment': 0.49.0 + '@es-joy/jsdoccomment': 0.50.2 are-docs-informative: 0.0.2 comment-parser: 1.4.1 - debug: 4.3.7 + debug: 4.4.1 escape-string-regexp: 4.0.0 - eslint: 9.14.0(jiti@2.4.0) - espree: 10.3.0 + eslint: 9.35.0(jiti@2.5.1) + espree: 10.4.0 esquery: 1.6.0 - parse-imports: 2.2.1 - semver: 7.6.3 + parse-imports-exports: 0.2.4 + semver: 7.7.2 spdx-expression-parse: 4.0.0 - synckit: 0.9.2 transitivePeerDependencies: - supports-color - eslint-plugin-jsonc@2.17.0(eslint@9.14.0(jiti@2.4.0)): + eslint-plugin-jsonc@2.20.1(eslint@9.35.0(jiti@2.5.1)): dependencies: - '@eslint-community/eslint-utils': 4.4.1(eslint@9.14.0(jiti@2.4.0)) - eslint: 9.14.0(jiti@2.4.0) - eslint-compat-utils: 0.6.0(eslint@9.14.0(jiti@2.4.0)) - espree: 9.6.1 + '@eslint-community/eslint-utils': 4.8.0(eslint@9.35.0(jiti@2.5.1)) + eslint: 9.35.0(jiti@2.5.1) + eslint-compat-utils: 0.6.5(eslint@9.35.0(jiti@2.5.1)) + eslint-json-compat-utils: 0.2.1(eslint@9.35.0(jiti@2.5.1))(jsonc-eslint-parser@2.4.0) + espree: 10.4.0 graphemer: 1.4.0 jsonc-eslint-parser: 2.4.0 natural-compare: 1.4.0 - synckit: 0.6.2 + synckit: 0.11.11 + transitivePeerDependencies: + - '@eslint/json' - eslint-plugin-n@17.13.1(eslint@9.14.0(jiti@2.4.0)): + eslint-plugin-n@17.21.3(eslint@9.35.0(jiti@2.5.1))(typescript@5.6.3): dependencies: - '@eslint-community/eslint-utils': 4.4.1(eslint@9.14.0(jiti@2.4.0)) - enhanced-resolve: 5.17.1 - eslint: 9.14.0(jiti@2.4.0) - eslint-plugin-es-x: 7.8.0(eslint@9.14.0(jiti@2.4.0)) - get-tsconfig: 4.8.1 - globals: 15.12.0 + '@eslint-community/eslint-utils': 4.8.0(eslint@9.35.0(jiti@2.5.1)) + enhanced-resolve: 5.18.3 + eslint: 9.35.0(jiti@2.5.1) + eslint-plugin-es-x: 7.8.0(eslint@9.35.0(jiti@2.5.1)) + get-tsconfig: 4.10.1 + globals: 15.15.0 + globrex: 0.1.2 ignore: 5.3.2 - minimatch: 9.0.5 - semver: 7.6.3 + semver: 7.7.2 + ts-declaration-location: 1.0.7(typescript@5.6.3) + transitivePeerDependencies: + - typescript eslint-plugin-no-only-tests@3.3.0: {} - eslint-plugin-perfectionist@3.9.1(eslint@9.14.0(jiti@2.4.0))(typescript@5.6.3)(vue-eslint-parser@9.4.3(eslint@9.14.0(jiti@2.4.0))): + eslint-plugin-perfectionist@4.15.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.6.3): dependencies: - '@typescript-eslint/types': 8.13.0 - '@typescript-eslint/utils': 8.13.0(eslint@9.14.0(jiti@2.4.0))(typescript@5.6.3) - eslint: 9.14.0(jiti@2.4.0) - minimatch: 9.0.5 - natural-compare-lite: 1.4.0 - optionalDependencies: - vue-eslint-parser: 9.4.3(eslint@9.14.0(jiti@2.4.0)) + '@typescript-eslint/types': 8.42.0 + '@typescript-eslint/utils': 8.42.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.6.3) + eslint: 9.35.0(jiti@2.5.1) + natural-orderby: 5.0.0 transitivePeerDependencies: - supports-color - typescript - eslint-plugin-regexp@2.6.0(eslint@9.14.0(jiti@2.4.0)): + eslint-plugin-regexp@2.10.0(eslint@9.35.0(jiti@2.5.1)): dependencies: - '@eslint-community/eslint-utils': 4.4.1(eslint@9.14.0(jiti@2.4.0)) + '@eslint-community/eslint-utils': 4.8.0(eslint@9.35.0(jiti@2.5.1)) '@eslint-community/regexpp': 4.12.1 comment-parser: 1.4.1 - eslint: 9.14.0(jiti@2.4.0) - jsdoc-type-pratt-parser: 4.1.0 + eslint: 9.35.0(jiti@2.5.1) + jsdoc-type-pratt-parser: 4.8.0 refa: 0.12.1 regexp-ast-analysis: 0.7.1 scslre: 0.3.0 - eslint-plugin-toml@0.11.1(eslint@9.14.0(jiti@2.4.0)): + eslint-plugin-toml@0.12.0(eslint@9.35.0(jiti@2.5.1)): dependencies: - debug: 4.3.7 - eslint: 9.14.0(jiti@2.4.0) - eslint-compat-utils: 0.5.1(eslint@9.14.0(jiti@2.4.0)) + debug: 4.4.1 + eslint: 9.35.0(jiti@2.5.1) + eslint-compat-utils: 0.6.5(eslint@9.35.0(jiti@2.5.1)) lodash: 4.17.21 toml-eslint-parser: 0.10.0 transitivePeerDependencies: - supports-color - eslint-plugin-unicorn@56.0.0(eslint@9.14.0(jiti@2.4.0)): + eslint-plugin-unicorn@56.0.1(eslint@9.35.0(jiti@2.5.1)): dependencies: - '@babel/helper-validator-identifier': 7.25.9 - '@eslint-community/eslint-utils': 4.4.1(eslint@9.14.0(jiti@2.4.0)) - ci-info: 4.0.0 + '@babel/helper-validator-identifier': 7.27.1 + '@eslint-community/eslint-utils': 4.8.0(eslint@9.35.0(jiti@2.5.1)) + ci-info: 4.3.0 clean-regexp: 1.0.0 - core-js-compat: 3.39.0 - eslint: 9.14.0(jiti@2.4.0) + core-js-compat: 3.45.1 + eslint: 9.35.0(jiti@2.5.1) esquery: 1.6.0 - globals: 15.12.0 + globals: 15.15.0 indent-string: 4.0.0 is-builtin-module: 3.2.1 - jsesc: 3.0.2 + jsesc: 3.1.0 pluralize: 8.0.0 read-pkg-up: 7.0.1 regexp-tree: 0.1.27 regjsparser: 0.10.0 - semver: 7.6.3 + semver: 7.7.2 strip-indent: 3.0.0 - eslint-plugin-unused-imports@4.1.4(@typescript-eslint/eslint-plugin@8.13.0(@typescript-eslint/parser@8.13.0(eslint@9.14.0(jiti@2.4.0))(typescript@5.6.3))(eslint@9.14.0(jiti@2.4.0))(typescript@5.6.3))(eslint@9.14.0(jiti@2.4.0)): + eslint-plugin-unused-imports@4.2.0(@typescript-eslint/eslint-plugin@8.42.0(@typescript-eslint/parser@8.42.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.6.3))(eslint@9.35.0(jiti@2.5.1))(typescript@5.6.3))(eslint@9.35.0(jiti@2.5.1)): dependencies: - eslint: 9.14.0(jiti@2.4.0) + eslint: 9.35.0(jiti@2.5.1) optionalDependencies: - '@typescript-eslint/eslint-plugin': 8.13.0(@typescript-eslint/parser@8.13.0(eslint@9.14.0(jiti@2.4.0))(typescript@5.6.3))(eslint@9.14.0(jiti@2.4.0))(typescript@5.6.3) + '@typescript-eslint/eslint-plugin': 8.42.0(@typescript-eslint/parser@8.42.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.6.3))(eslint@9.35.0(jiti@2.5.1))(typescript@5.6.3) - eslint-plugin-vue@9.30.0(eslint@9.14.0(jiti@2.4.0)): + eslint-plugin-vue@9.33.0(eslint@9.35.0(jiti@2.5.1)): dependencies: - '@eslint-community/eslint-utils': 4.4.1(eslint@9.14.0(jiti@2.4.0)) - eslint: 9.14.0(jiti@2.4.0) + '@eslint-community/eslint-utils': 4.8.0(eslint@9.35.0(jiti@2.5.1)) + eslint: 9.35.0(jiti@2.5.1) globals: 13.24.0 natural-compare: 1.4.0 nth-check: 2.1.1 postcss-selector-parser: 6.1.2 - semver: 7.6.3 - vue-eslint-parser: 9.4.3(eslint@9.14.0(jiti@2.4.0)) + semver: 7.7.2 + vue-eslint-parser: 9.4.3(eslint@9.35.0(jiti@2.5.1)) xml-name-validator: 4.0.0 transitivePeerDependencies: - supports-color - eslint-plugin-yml@1.15.0(eslint@9.14.0(jiti@2.4.0)): + eslint-plugin-yml@1.18.0(eslint@9.35.0(jiti@2.5.1)): dependencies: - debug: 4.3.7 - eslint: 9.14.0(jiti@2.4.0) - eslint-compat-utils: 0.5.1(eslint@9.14.0(jiti@2.4.0)) - lodash: 4.17.21 + debug: 4.4.1 + escape-string-regexp: 4.0.0 + eslint: 9.35.0(jiti@2.5.1) + eslint-compat-utils: 0.6.5(eslint@9.35.0(jiti@2.5.1)) natural-compare: 1.4.0 - yaml-eslint-parser: 1.2.3 + yaml-eslint-parser: 1.3.0 transitivePeerDependencies: - supports-color - eslint-processor-vue-blocks@0.1.2(@vue/compiler-sfc@3.5.12)(eslint@9.14.0(jiti@2.4.0)): + eslint-processor-vue-blocks@1.0.0(@vue/compiler-sfc@3.5.21)(eslint@9.35.0(jiti@2.5.1)): dependencies: - '@vue/compiler-sfc': 3.5.12 - eslint: 9.14.0(jiti@2.4.0) + '@vue/compiler-sfc': 3.5.21 + eslint: 9.35.0(jiti@2.5.1) eslint-scope@7.2.2: dependencies: esrecurse: 4.3.0 estraverse: 5.3.0 - eslint-scope@8.2.0: + eslint-scope@8.4.0: dependencies: esrecurse: 4.3.0 estraverse: 5.3.0 eslint-visitor-keys@3.4.3: {} - eslint-visitor-keys@4.2.0: {} + eslint-visitor-keys@4.2.1: {} - eslint@9.14.0(jiti@2.4.0): + eslint@9.35.0(jiti@2.5.1): dependencies: - '@eslint-community/eslint-utils': 4.4.1(eslint@9.14.0(jiti@2.4.0)) + '@eslint-community/eslint-utils': 4.8.0(eslint@9.35.0(jiti@2.5.1)) '@eslint-community/regexpp': 4.12.1 - '@eslint/config-array': 0.18.0 - '@eslint/core': 0.7.0 - '@eslint/eslintrc': 3.1.0 - '@eslint/js': 9.14.0 - '@eslint/plugin-kit': 0.2.2 - '@humanfs/node': 0.16.6 + '@eslint/config-array': 0.21.0 + '@eslint/config-helpers': 0.3.1 + '@eslint/core': 0.15.2 + '@eslint/eslintrc': 3.3.1 + '@eslint/js': 9.35.0 + '@eslint/plugin-kit': 0.3.5 + '@humanfs/node': 0.16.7 '@humanwhocodes/module-importer': 1.0.1 - '@humanwhocodes/retry': 0.4.1 - '@types/estree': 1.0.6 + '@humanwhocodes/retry': 0.4.3 + '@types/estree': 1.0.8 '@types/json-schema': 7.0.15 ajv: 6.12.6 chalk: 4.1.2 - cross-spawn: 7.0.3 - debug: 4.3.7 + cross-spawn: 7.0.6 + debug: 4.4.1 escape-string-regexp: 4.0.0 - eslint-scope: 8.2.0 - eslint-visitor-keys: 4.2.0 - espree: 10.3.0 + eslint-scope: 8.4.0 + eslint-visitor-keys: 4.2.1 + espree: 10.4.0 esquery: 1.6.0 esutils: 2.0.3 fast-deep-equal: 3.1.3 @@ -6131,22 +6593,21 @@ snapshots: minimatch: 3.1.2 natural-compare: 1.4.0 optionator: 0.9.4 - text-table: 0.2.0 optionalDependencies: - jiti: 2.4.0 + jiti: 2.5.1 transitivePeerDependencies: - supports-color - espree@10.3.0: + espree@10.4.0: dependencies: - acorn: 8.14.0 - acorn-jsx: 5.3.2(acorn@8.14.0) - eslint-visitor-keys: 4.2.0 + acorn: 8.15.0 + acorn-jsx: 5.3.2(acorn@8.15.0) + eslint-visitor-keys: 4.2.1 espree@9.6.1: dependencies: - acorn: 8.14.0 - acorn-jsx: 5.3.2(acorn@8.14.0) + acorn: 8.15.0 + acorn-jsx: 5.3.2(acorn@8.15.0) eslint-visitor-keys: 3.4.3 esprima@4.0.1: {} @@ -6165,13 +6626,13 @@ snapshots: estree-walker@3.0.3: dependencies: - '@types/estree': 1.0.6 + '@types/estree': 1.0.8 esutils@2.0.3: {} execa@7.2.0: dependencies: - cross-spawn: 7.0.3 + cross-spawn: 7.0.6 get-stream: 6.0.1 human-signals: 4.3.1 is-stream: 3.0.0 @@ -6181,17 +6642,22 @@ snapshots: signal-exit: 3.0.7 strip-final-newline: 3.0.0 - execa@8.0.1: - dependencies: - cross-spawn: 7.0.3 - get-stream: 8.0.1 - human-signals: 5.0.0 - is-stream: 3.0.0 - merge-stream: 2.0.0 - npm-run-path: 5.3.0 - onetime: 6.0.0 + execa@9.6.0: + dependencies: + '@sindresorhus/merge-streams': 4.0.0 + cross-spawn: 7.0.6 + figures: 6.1.0 + get-stream: 9.0.1 + human-signals: 8.0.1 + is-plain-obj: 4.1.0 + is-stream: 4.0.1 + npm-run-path: 6.0.0 + pretty-ms: 9.2.0 signal-exit: 4.1.0 - strip-final-newline: 3.0.0 + strip-final-newline: 4.0.0 + yoctocolors: 2.1.2 + + exsolve@1.0.7: {} extend-shallow@2.0.1: dependencies: @@ -6201,7 +6667,7 @@ snapshots: fast-diff@1.3.0: {} - fast-glob@3.3.2: + fast-glob@3.3.3: dependencies: '@nodelib/fs.stat': 2.0.5 '@nodelib/fs.walk': 1.2.8 @@ -6213,9 +6679,17 @@ snapshots: fast-levenshtein@2.0.6: {} - fastq@1.17.1: + fastq@1.19.1: dependencies: - reusify: 1.0.4 + reusify: 1.1.0 + + fault@2.0.1: + dependencies: + format: 0.2.2 + + figures@6.1.0: + dependencies: + is-unicode-supported: 2.1.0 file-entry-cache@8.0.0: dependencies: @@ -6225,8 +6699,6 @@ snapshots: dependencies: to-regex-range: 5.0.1 - find-up-simple@1.0.0: {} - find-up@4.1.0: dependencies: locate-path: 5.0.0 @@ -6239,44 +6711,42 @@ snapshots: flat-cache@4.0.1: dependencies: - flatted: 3.3.1 + flatted: 3.3.3 keyv: 4.5.4 - flatted@3.3.1: {} + flatted@3.3.3: {} - floating-vue@5.2.2(vue@3.5.12(typescript@5.6.3)): + floating-vue@5.2.2(vue@3.5.21(typescript@5.6.3)): dependencies: '@floating-ui/dom': 1.1.1 - vue: 3.5.12(typescript@5.6.3) - vue-resize: 2.0.0-alpha.1(vue@3.5.12(typescript@5.6.3)) + vue: 3.5.21(typescript@5.6.3) + vue-resize: 2.0.0-alpha.1(vue@3.5.21(typescript@5.6.3)) - follow-redirects@1.15.9: {} + follow-redirects@1.15.11: {} - foreground-child@3.3.0: + foreground-child@3.3.1: dependencies: - cross-spawn: 7.0.3 + cross-spawn: 7.0.6 signal-exit: 4.1.0 - form-data@4.0.1: + form-data@4.0.4: dependencies: asynckit: 0.4.0 combined-stream: 1.0.8 + es-set-tostringtag: 2.1.0 + hasown: 2.0.2 mime-types: 2.1.35 + format@0.2.2: {} + fraction.js@4.3.7: {} - fs-extra@11.2.0: + fs-extra@11.3.1: dependencies: graceful-fs: 4.2.11 - jsonfile: 6.1.0 + jsonfile: 6.2.0 universalify: 2.0.1 - fs-extra@7.0.1: - dependencies: - graceful-fs: 4.2.11 - jsonfile: 4.0.0 - universalify: 0.1.2 - fsevents@2.3.3: optional: true @@ -6286,14 +6756,37 @@ snapshots: get-caller-file@2.0.5: {} + get-intrinsic@1.3.0: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + function-bind: 1.1.2 + get-proto: 1.0.1 + gopd: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.2 + math-intrinsics: 1.1.0 + + get-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.1 + get-stream@6.0.1: {} - get-stream@8.0.1: {} + get-stream@9.0.1: + dependencies: + '@sec-ant/readable-stream': 0.4.1 + is-stream: 4.0.1 - get-tsconfig@4.8.1: + get-tsconfig@4.10.1: dependencies: resolve-pkg-maps: 1.0.0 + github-slugger@2.0.0: {} + glob-parent@5.1.2: dependencies: is-glob: 4.0.3 @@ -6304,31 +6797,24 @@ snapshots: glob@10.4.5: dependencies: - foreground-child: 3.3.0 + foreground-child: 3.3.1 jackspeak: 3.4.3 minimatch: 9.0.5 minipass: 7.1.2 package-json-from-dist: 1.0.1 path-scurry: 1.11.1 - globals@11.12.0: {} - globals@13.24.0: dependencies: type-fest: 0.20.2 globals@14.0.0: {} - globals@15.12.0: {} + globals@15.15.0: {} - globby@11.1.0: - dependencies: - array-union: 2.1.0 - dir-glob: 3.0.1 - fast-glob: 3.3.2 - ignore: 5.3.2 - merge2: 1.4.1 - slash: 3.0.0 + globrex@0.1.2: {} + + gopd@1.2.0: {} graceful-fs@4.2.11: {} @@ -6343,6 +6829,12 @@ snapshots: has-flag@4.0.0: {} + has-symbols@1.1.0: {} + + has-tostringtag@1.0.2: + dependencies: + has-symbols: 1.1.0 + hasown@2.0.2: dependencies: function-bind: 1.1.2 @@ -6353,29 +6845,29 @@ snapshots: hosted-git-info@2.8.9: {} - html-tags@3.3.1: {} - human-signals@4.3.1: {} - human-signals@5.0.0: {} + human-signals@8.0.1: {} ignore@5.3.2: {} - import-fresh@3.3.0: + ignore@7.0.5: {} + + import-fresh@3.3.1: dependencies: parent-module: 1.0.1 resolve-from: 4.0.0 import-lazy@4.0.0: {} - importx@0.5.0: + importx@0.5.2: dependencies: - bundle-require: 5.0.0(esbuild@0.23.1) - debug: 4.3.7 - esbuild: 0.23.1 - jiti: 2.4.0 - pathe: 1.1.2 - tsx: 4.19.2 + bundle-require: 5.1.0(esbuild@0.25.9) + debug: 4.4.1 + esbuild: 0.25.9 + jiti: 2.5.1 + pathe: 2.0.3 + tsx: 4.20.5 transitivePeerDependencies: - supports-color @@ -6383,7 +6875,7 @@ snapshots: indent-string@4.0.0: {} - ip-bigint@8.2.0: {} + ip-bigint@8.2.2: {} ip-num@1.5.1: {} @@ -6397,7 +6889,7 @@ snapshots: dependencies: builtin-modules: 3.3.0 - is-core-module@2.15.1: + is-core-module@2.16.1: dependencies: hasown: 2.0.2 @@ -6419,8 +6911,14 @@ snapshots: is-number@7.0.0: {} + is-plain-obj@4.1.0: {} + is-stream@3.0.0: {} + is-stream@4.0.1: {} + + is-unicode-supported@2.1.0: {} + is-what@4.1.16: {} is-wsl@3.1.0: @@ -6435,15 +6933,15 @@ snapshots: optionalDependencies: '@pkgjs/parseargs': 0.11.0 - jiti@1.21.6: {} + jiti@1.21.7: {} - jiti@2.4.0: {} + jiti@2.5.1: {} jju@1.4.0: {} js-tokens@4.0.0: {} - js-tokens@9.0.0: {} + js-tokens@9.0.1: {} js-yaml@3.14.1: dependencies: @@ -6456,9 +6954,11 @@ snapshots: jsdoc-type-pratt-parser@4.1.0: {} + jsdoc-type-pratt-parser@4.8.0: {} + jsesc@0.5.0: {} - jsesc@3.0.2: {} + jsesc@3.1.0: {} json-buffer@3.0.1: {} @@ -6474,16 +6974,12 @@ snapshots: jsonc-eslint-parser@2.4.0: dependencies: - acorn: 8.14.0 + acorn: 8.15.0 eslint-visitor-keys: 3.4.3 espree: 9.6.1 - semver: 7.6.3 - - jsonfile@4.0.0: - optionalDependencies: - graceful-fs: 4.2.11 + semver: 7.7.2 - jsonfile@6.1.0: + jsonfile@6.2.0: dependencies: universalify: 2.0.1 optionalDependencies: @@ -6512,10 +7008,16 @@ snapshots: load-tsconfig@0.2.5: {} - local-pkg@0.5.0: + local-pkg@0.5.1: + dependencies: + mlly: 1.8.0 + pkg-types: 1.3.1 + + local-pkg@1.1.2: dependencies: - mlly: 1.7.2 - pkg-types: 1.2.1 + mlly: 1.8.0 + pkg-types: 2.3.0 + quansync: 0.2.11 locate-path@5.0.0: dependencies: @@ -6541,15 +7043,19 @@ snapshots: dependencies: yallist: 4.0.0 - magic-string-ast@0.6.2: + magic-string-ast@0.6.3: + dependencies: + magic-string: 0.30.18 + + magic-string-ast@0.7.1: dependencies: - magic-string: 0.30.12 + magic-string: 0.30.18 - magic-string@0.30.12: + magic-string@0.30.18: dependencies: - '@jridgewell/sourcemap-codec': 1.5.0 + '@jridgewell/sourcemap-codec': 1.5.5 - make-synchronized@0.2.9: {} + make-synchronized@0.2.10: {} markdown-it@14.1.0: dependencies: @@ -6562,7 +7068,9 @@ snapshots: markdown-table@3.0.4: {} - mdast-util-find-and-replace@3.0.1: + math-intrinsics@1.1.0: {} + + mdast-util-find-and-replace@3.0.2: dependencies: '@types/mdast': 4.0.4 escape-string-regexp: 5.0.0 @@ -6573,34 +7081,45 @@ snapshots: dependencies: '@types/mdast': 4.0.4 '@types/unist': 3.0.3 - decode-named-character-reference: 1.0.2 + decode-named-character-reference: 1.2.0 devlop: 1.1.0 mdast-util-to-string: 4.0.0 - micromark: 4.0.0 - micromark-util-decode-numeric-character-reference: 2.0.1 - micromark-util-decode-string: 2.0.0 - micromark-util-normalize-identifier: 2.0.0 - micromark-util-symbol: 2.0.0 - micromark-util-types: 2.0.0 + micromark: 4.0.2 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-decode-string: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 unist-util-stringify-position: 4.0.0 transitivePeerDependencies: - supports-color + mdast-util-frontmatter@2.0.1: + dependencies: + '@types/mdast': 4.0.4 + devlop: 1.1.0 + escape-string-regexp: 5.0.0 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + micromark-extension-frontmatter: 2.0.0 + transitivePeerDependencies: + - supports-color + mdast-util-gfm-autolink-literal@2.0.1: dependencies: '@types/mdast': 4.0.4 ccount: 2.0.1 devlop: 1.1.0 - mdast-util-find-and-replace: 3.0.1 - micromark-util-character: 2.1.0 + mdast-util-find-and-replace: 3.0.2 + micromark-util-character: 2.1.1 - mdast-util-gfm-footnote@2.0.0: + mdast-util-gfm-footnote@2.1.0: dependencies: '@types/mdast': 4.0.4 devlop: 1.1.0 mdast-util-from-markdown: 2.0.2 mdast-util-to-markdown: 2.1.2 - micromark-util-normalize-identifier: 2.0.0 + micromark-util-normalize-identifier: 2.0.1 transitivePeerDependencies: - supports-color @@ -6631,11 +7150,11 @@ snapshots: transitivePeerDependencies: - supports-color - mdast-util-gfm@3.0.0: + mdast-util-gfm@3.1.0: dependencies: mdast-util-from-markdown: 2.0.2 mdast-util-gfm-autolink-literal: 2.0.1 - mdast-util-gfm-footnote: 2.0.0 + mdast-util-gfm-footnote: 2.1.0 mdast-util-gfm-strikethrough: 2.0.0 mdast-util-gfm-table: 2.0.0 mdast-util-gfm-task-list-item: 2.0.0 @@ -6655,8 +7174,8 @@ snapshots: longest-streak: 3.1.0 mdast-util-phrasing: 4.1.0 mdast-util-to-string: 4.0.0 - micromark-util-classify-character: 2.0.0 - micromark-util-decode-string: 2.0.0 + micromark-util-classify-character: 2.0.1 + micromark-util-decode-string: 2.0.1 unist-util-visit: 5.0.0 zwitch: 2.0.4 @@ -6670,194 +7189,201 @@ snapshots: merge2@1.4.1: {} - micromark-core-commonmark@2.0.1: + micromark-core-commonmark@2.0.3: dependencies: - decode-named-character-reference: 1.0.2 + decode-named-character-reference: 1.2.0 devlop: 1.1.0 - micromark-factory-destination: 2.0.0 - micromark-factory-label: 2.0.0 - micromark-factory-space: 2.0.0 - micromark-factory-title: 2.0.0 - micromark-factory-whitespace: 2.0.0 - micromark-util-character: 2.1.0 - micromark-util-chunked: 2.0.0 - micromark-util-classify-character: 2.0.0 - micromark-util-html-tag-name: 2.0.0 - micromark-util-normalize-identifier: 2.0.0 - micromark-util-resolve-all: 2.0.0 - micromark-util-subtokenize: 2.0.1 - micromark-util-symbol: 2.0.0 - micromark-util-types: 2.0.0 + micromark-factory-destination: 2.0.1 + micromark-factory-label: 2.0.1 + micromark-factory-space: 2.0.1 + micromark-factory-title: 2.0.1 + micromark-factory-whitespace: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-chunked: 2.0.1 + micromark-util-classify-character: 2.0.1 + micromark-util-html-tag-name: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-subtokenize: 2.1.0 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-frontmatter@2.0.0: + dependencies: + fault: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 micromark-extension-gfm-autolink-literal@2.1.0: dependencies: - micromark-util-character: 2.1.0 - micromark-util-sanitize-uri: 2.0.0 - micromark-util-symbol: 2.0.0 - micromark-util-types: 2.0.0 + micromark-util-character: 2.1.1 + micromark-util-sanitize-uri: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 micromark-extension-gfm-footnote@2.1.0: dependencies: devlop: 1.1.0 - micromark-core-commonmark: 2.0.1 - micromark-factory-space: 2.0.0 - micromark-util-character: 2.1.0 - micromark-util-normalize-identifier: 2.0.0 - micromark-util-sanitize-uri: 2.0.0 - micromark-util-symbol: 2.0.0 - micromark-util-types: 2.0.0 + micromark-core-commonmark: 2.0.3 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-sanitize-uri: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 micromark-extension-gfm-strikethrough@2.1.0: dependencies: devlop: 1.1.0 - micromark-util-chunked: 2.0.0 - micromark-util-classify-character: 2.0.0 - micromark-util-resolve-all: 2.0.0 - micromark-util-symbol: 2.0.0 - micromark-util-types: 2.0.0 + micromark-util-chunked: 2.0.1 + micromark-util-classify-character: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 - micromark-extension-gfm-table@2.1.0: + micromark-extension-gfm-table@2.1.1: dependencies: devlop: 1.1.0 - micromark-factory-space: 2.0.0 - micromark-util-character: 2.1.0 - micromark-util-symbol: 2.0.0 - micromark-util-types: 2.0.0 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 micromark-extension-gfm-tagfilter@2.0.0: dependencies: - micromark-util-types: 2.0.0 + micromark-util-types: 2.0.2 micromark-extension-gfm-task-list-item@2.1.0: dependencies: devlop: 1.1.0 - micromark-factory-space: 2.0.0 - micromark-util-character: 2.1.0 - micromark-util-symbol: 2.0.0 - micromark-util-types: 2.0.0 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 micromark-extension-gfm@3.0.0: dependencies: micromark-extension-gfm-autolink-literal: 2.1.0 micromark-extension-gfm-footnote: 2.1.0 micromark-extension-gfm-strikethrough: 2.1.0 - micromark-extension-gfm-table: 2.1.0 + micromark-extension-gfm-table: 2.1.1 micromark-extension-gfm-tagfilter: 2.0.0 micromark-extension-gfm-task-list-item: 2.1.0 - micromark-util-combine-extensions: 2.0.0 - micromark-util-types: 2.0.0 + micromark-util-combine-extensions: 2.0.1 + micromark-util-types: 2.0.2 - micromark-factory-destination@2.0.0: + micromark-factory-destination@2.0.1: dependencies: - micromark-util-character: 2.1.0 - micromark-util-symbol: 2.0.0 - micromark-util-types: 2.0.0 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 - micromark-factory-label@2.0.0: + micromark-factory-label@2.0.1: dependencies: devlop: 1.1.0 - micromark-util-character: 2.1.0 - micromark-util-symbol: 2.0.0 - micromark-util-types: 2.0.0 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 - micromark-factory-space@2.0.0: + micromark-factory-space@2.0.1: dependencies: - micromark-util-character: 2.1.0 - micromark-util-types: 2.0.0 + micromark-util-character: 2.1.1 + micromark-util-types: 2.0.2 - micromark-factory-title@2.0.0: + micromark-factory-title@2.0.1: dependencies: - micromark-factory-space: 2.0.0 - micromark-util-character: 2.1.0 - micromark-util-symbol: 2.0.0 - micromark-util-types: 2.0.0 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 - micromark-factory-whitespace@2.0.0: + micromark-factory-whitespace@2.0.1: dependencies: - micromark-factory-space: 2.0.0 - micromark-util-character: 2.1.0 - micromark-util-symbol: 2.0.0 - micromark-util-types: 2.0.0 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 - micromark-util-character@2.1.0: + micromark-util-character@2.1.1: dependencies: - micromark-util-symbol: 2.0.0 - micromark-util-types: 2.0.0 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 - micromark-util-chunked@2.0.0: + micromark-util-chunked@2.0.1: dependencies: - micromark-util-symbol: 2.0.0 + micromark-util-symbol: 2.0.1 - micromark-util-classify-character@2.0.0: + micromark-util-classify-character@2.0.1: dependencies: - micromark-util-character: 2.1.0 - micromark-util-symbol: 2.0.0 - micromark-util-types: 2.0.0 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 - micromark-util-combine-extensions@2.0.0: + micromark-util-combine-extensions@2.0.1: dependencies: - micromark-util-chunked: 2.0.0 - micromark-util-types: 2.0.0 + micromark-util-chunked: 2.0.1 + micromark-util-types: 2.0.2 - micromark-util-decode-numeric-character-reference@2.0.1: + micromark-util-decode-numeric-character-reference@2.0.2: dependencies: - micromark-util-symbol: 2.0.0 + micromark-util-symbol: 2.0.1 - micromark-util-decode-string@2.0.0: + micromark-util-decode-string@2.0.1: dependencies: - decode-named-character-reference: 1.0.2 - micromark-util-character: 2.1.0 - micromark-util-decode-numeric-character-reference: 2.0.1 - micromark-util-symbol: 2.0.0 + decode-named-character-reference: 1.2.0 + micromark-util-character: 2.1.1 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-symbol: 2.0.1 - micromark-util-encode@2.0.0: {} + micromark-util-encode@2.0.1: {} - micromark-util-html-tag-name@2.0.0: {} + micromark-util-html-tag-name@2.0.1: {} - micromark-util-normalize-identifier@2.0.0: + micromark-util-normalize-identifier@2.0.1: dependencies: - micromark-util-symbol: 2.0.0 + micromark-util-symbol: 2.0.1 - micromark-util-resolve-all@2.0.0: + micromark-util-resolve-all@2.0.1: dependencies: - micromark-util-types: 2.0.0 + micromark-util-types: 2.0.2 - micromark-util-sanitize-uri@2.0.0: + micromark-util-sanitize-uri@2.0.1: dependencies: - micromark-util-character: 2.1.0 - micromark-util-encode: 2.0.0 - micromark-util-symbol: 2.0.0 + micromark-util-character: 2.1.1 + micromark-util-encode: 2.0.1 + micromark-util-symbol: 2.0.1 - micromark-util-subtokenize@2.0.1: + micromark-util-subtokenize@2.1.0: dependencies: devlop: 1.1.0 - micromark-util-chunked: 2.0.0 - micromark-util-symbol: 2.0.0 - micromark-util-types: 2.0.0 + micromark-util-chunked: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 - micromark-util-symbol@2.0.0: {} + micromark-util-symbol@2.0.1: {} - micromark-util-types@2.0.0: {} + micromark-util-types@2.0.2: {} - micromark@4.0.0: + micromark@4.0.2: dependencies: '@types/debug': 4.1.12 - debug: 4.3.7 - decode-named-character-reference: 1.0.2 + debug: 4.4.1 + decode-named-character-reference: 1.2.0 devlop: 1.1.0 - micromark-core-commonmark: 2.0.1 - micromark-factory-space: 2.0.0 - micromark-util-character: 2.1.0 - micromark-util-chunked: 2.0.0 - micromark-util-combine-extensions: 2.0.0 - micromark-util-decode-numeric-character-reference: 2.0.1 - micromark-util-encode: 2.0.0 - micromark-util-normalize-identifier: 2.0.0 - micromark-util-resolve-all: 2.0.0 - micromark-util-sanitize-uri: 2.0.0 - micromark-util-subtokenize: 2.0.1 - micromark-util-symbol: 2.0.0 - micromark-util-types: 2.0.0 + micromark-core-commonmark: 2.0.3 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-chunked: 2.0.1 + micromark-util-combine-extensions: 2.0.1 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-encode: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-sanitize-uri: 2.0.1 + micromark-util-subtokenize: 2.1.0 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 transitivePeerDependencies: - supports-color @@ -6876,30 +7402,30 @@ snapshots: min-indent@1.0.1: {} - minimatch@3.0.8: + minimatch@10.0.3: dependencies: - brace-expansion: 1.1.11 + '@isaacs/brace-expansion': 5.0.0 minimatch@3.1.2: dependencies: - brace-expansion: 1.1.11 + brace-expansion: 1.1.12 minimatch@9.0.5: dependencies: - brace-expansion: 2.0.1 + brace-expansion: 2.0.2 minipass@7.1.2: {} mitt@3.0.1: {} - mlly@1.7.2: + mlly@1.8.0: dependencies: - acorn: 8.14.0 - pathe: 1.1.2 - pkg-types: 1.2.1 - ufo: 1.5.4 + acorn: 8.15.0 + pathe: 2.0.3 + pkg-types: 1.3.1 + ufo: 1.6.1 - mrmime@2.0.0: {} + mrmime@2.0.1: {} ms@2.1.3: {} @@ -6911,18 +7437,22 @@ snapshots: object-assign: 4.1.1 thenify-all: 1.6.0 - nanoid@3.3.7: {} + nanoid@3.3.11: {} + + nanoid@5.1.5: {} - natural-compare-lite@1.4.0: {} + napi-postinstall@0.3.3: {} natural-compare@1.4.0: {} - node-releases@2.0.18: {} + natural-orderby@5.0.0: {} + + node-releases@2.0.20: {} normalize-package-data@2.5.0: dependencies: hosted-git-info: 2.8.9 - resolve: 1.22.8 + resolve: 1.22.10 semver: 5.7.2 validate-npm-package-license: 3.0.4 @@ -6934,6 +7464,11 @@ snapshots: dependencies: path-key: 4.0.0 + npm-run-path@6.0.0: + dependencies: + path-key: 4.0.0 + unicorn-magic: 0.3.0 + nth-check@2.1.1: dependencies: boolbase: 1.0.0 @@ -6946,12 +7481,12 @@ snapshots: dependencies: mimic-fn: 4.0.0 - open@10.1.0: + open@10.2.0: dependencies: default-browser: 5.2.1 define-lazy-prop: 3.0.0 is-inside-container: 1.0.0 - is-wsl: 3.1.0 + wsl-utils: 0.1.0 optionator@0.9.4: dependencies: @@ -6962,19 +7497,19 @@ snapshots: type-check: 0.4.0 word-wrap: 1.2.5 - oxc-resolver@2.0.1: + oxc-resolver@4.2.0: optionalDependencies: - '@oxc-resolver/binding-darwin-arm64': 2.0.1 - '@oxc-resolver/binding-darwin-x64': 2.0.1 - '@oxc-resolver/binding-freebsd-x64': 2.0.1 - '@oxc-resolver/binding-linux-arm-gnueabihf': 2.0.1 - '@oxc-resolver/binding-linux-arm64-gnu': 2.0.1 - '@oxc-resolver/binding-linux-arm64-musl': 2.0.1 - '@oxc-resolver/binding-linux-x64-gnu': 2.0.1 - '@oxc-resolver/binding-linux-x64-musl': 2.0.1 - '@oxc-resolver/binding-wasm32-wasi': 2.0.1 - '@oxc-resolver/binding-win32-arm64-msvc': 2.0.1 - '@oxc-resolver/binding-win32-x64-msvc': 2.0.1 + '@oxc-resolver/binding-darwin-arm64': 4.2.0 + '@oxc-resolver/binding-darwin-x64': 4.2.0 + '@oxc-resolver/binding-freebsd-x64': 4.2.0 + '@oxc-resolver/binding-linux-arm-gnueabihf': 4.2.0 + '@oxc-resolver/binding-linux-arm64-gnu': 4.2.0 + '@oxc-resolver/binding-linux-arm64-musl': 4.2.0 + '@oxc-resolver/binding-linux-x64-gnu': 4.2.0 + '@oxc-resolver/binding-linux-x64-musl': 4.2.0 + '@oxc-resolver/binding-wasm32-wasi': 4.2.0 + '@oxc-resolver/binding-win32-arm64-msvc': 4.2.0 + '@oxc-resolver/binding-win32-x64-msvc': 4.2.0 p-limit@2.3.0: dependencies: @@ -6996,7 +7531,7 @@ snapshots: package-json-from-dist@1.0.1: {} - package-manager-detector@0.2.2: {} + package-manager-detector@1.3.0: {} parent-module@1.0.1: dependencies: @@ -7004,18 +7539,21 @@ snapshots: parse-gitignore@2.0.0: {} - parse-imports@2.2.1: + parse-imports-exports@0.2.4: dependencies: - es-module-lexer: 1.5.4 - slashes: 3.0.12 + parse-statements: 1.0.11 parse-json@5.2.0: dependencies: - '@babel/code-frame': 7.26.2 + '@babel/code-frame': 7.27.1 error-ex: 1.3.2 json-parse-even-better-errors: 2.3.1 lines-and-columns: 1.2.4 + parse-ms@4.0.0: {} + + parse-statements@1.0.11: {} + path-browserify@1.0.1: {} path-exists@4.0.0: {} @@ -7031,89 +7569,97 @@ snapshots: lru-cache: 10.4.3 minipass: 7.1.2 - path-type@4.0.0: {} - pathe@1.1.2: {} + pathe@2.0.3: {} + perfect-debounce@1.0.0: {} picocolors@1.1.1: {} picomatch@2.3.1: {} - picomatch@4.0.2: {} + picomatch@4.0.3: {} pify@2.3.0: {} - pinia@2.2.6(typescript@5.6.3)(vue@3.5.12(typescript@5.6.3)): + pinia@2.3.1(typescript@5.6.3)(vue@3.5.21(typescript@5.6.3)): dependencies: '@vue/devtools-api': 6.6.4 - vue: 3.5.12(typescript@5.6.3) - vue-demi: 0.14.10(vue@3.5.12(typescript@5.6.3)) + vue: 3.5.21(typescript@5.6.3) + vue-demi: 0.14.10(vue@3.5.21(typescript@5.6.3)) optionalDependencies: typescript: 5.6.3 + transitivePeerDependencies: + - '@vue/composition-api' - pirates@4.0.6: {} + pirates@4.0.7: {} - pkg-types@1.2.1: + pkg-types@1.3.1: dependencies: confbox: 0.1.8 - mlly: 1.7.2 - pathe: 1.1.2 + mlly: 1.8.0 + pathe: 2.0.3 + + pkg-types@2.3.0: + dependencies: + confbox: 0.2.2 + exsolve: 1.0.7 + pathe: 2.0.3 pluralize@8.0.0: {} - postcss-import@15.1.0(postcss@8.4.47): + postcss-import@15.1.0(postcss@8.5.6): dependencies: - postcss: 8.4.47 + postcss: 8.5.6 postcss-value-parser: 4.2.0 read-cache: 1.0.0 - resolve: 1.22.8 + resolve: 1.22.10 - postcss-import@16.1.0(postcss@8.4.47): + postcss-import@16.1.1(postcss@8.5.6): dependencies: - postcss: 8.4.47 + postcss: 8.5.6 postcss-value-parser: 4.2.0 read-cache: 1.0.0 - resolve: 1.22.8 + resolve: 1.22.10 - postcss-js@4.0.1(postcss@8.4.47): + postcss-js@4.0.1(postcss@8.5.6): dependencies: camelcase-css: 2.0.1 - postcss: 8.4.47 + postcss: 8.5.6 - postcss-load-config@4.0.2(postcss@8.4.47): + postcss-load-config@4.0.2(postcss@8.5.6): dependencies: lilconfig: 3.1.3 - yaml: 2.6.0 + yaml: 2.8.1 optionalDependencies: - postcss: 8.4.47 + postcss: 8.5.6 - postcss-nested@6.2.0(postcss@8.4.47): + postcss-nested@6.2.0(postcss@8.5.6): dependencies: - postcss: 8.4.47 + postcss: 8.5.6 postcss-selector-parser: 6.1.2 - postcss-nested@7.0.2(postcss@8.4.47): + postcss-nested@7.0.2(postcss@8.5.6): dependencies: - postcss: 8.4.47 - postcss-selector-parser: 7.0.0 + postcss: 8.5.6 + postcss-selector-parser: 7.1.0 postcss-selector-parser@6.1.2: dependencies: cssesc: 3.0.0 util-deprecate: 1.0.2 - postcss-selector-parser@7.0.0: + postcss-selector-parser@7.1.0: dependencies: cssesc: 3.0.0 util-deprecate: 1.0.2 postcss-value-parser@4.2.0: {} - postcss@8.4.47: + postcss@8.5.6: dependencies: - nanoid: 3.3.7 + nanoid: 3.3.11 picocolors: 1.1.1 source-map-js: 1.2.1 @@ -7123,17 +7669,21 @@ snapshots: dependencies: fast-diff: 1.3.0 - prettier@3.3.3: {} + prettier@3.6.2: {} + + pretty-ms@9.2.0: + dependencies: + parse-ms: 4.0.0 primeicons@7.0.0: {} - primevue@4.3.3(vue@3.5.12(typescript@5.6.3)): + primevue@4.3.9(vue@3.5.21(typescript@5.6.3)): dependencies: - '@primeuix/styled': 0.5.1 - '@primeuix/styles': 1.0.2 - '@primeuix/utils': 0.5.3 - '@primevue/core': 4.3.3(vue@3.5.12(typescript@5.6.3)) - '@primevue/icons': 4.3.3(vue@3.5.12(typescript@5.6.3)) + '@primeuix/styled': 0.7.2 + '@primeuix/styles': 1.2.3 + '@primeuix/utils': 0.6.1 + '@primevue/core': 4.3.9(vue@3.5.21(typescript@5.6.3)) + '@primevue/icons': 4.3.9(vue@3.5.21(typescript@5.6.3)) transitivePeerDependencies: - vue @@ -7143,6 +7693,8 @@ snapshots: punycode@2.3.1: {} + quansync@0.2.11: {} + queue-microtask@1.2.3: {} read-cache@1.0.0: @@ -7189,38 +7741,41 @@ snapshots: resolve-pkg-maps@1.0.0: {} - resolve@1.22.8: + resolve@1.22.10: dependencies: - is-core-module: 2.15.1 + is-core-module: 2.16.1 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 - reusify@1.0.4: {} + reusify@1.1.0: {} rfdc@1.4.1: {} - rollup@4.24.3: + rollup@4.50.1: dependencies: - '@types/estree': 1.0.6 + '@types/estree': 1.0.8 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.24.3 - '@rollup/rollup-android-arm64': 4.24.3 - '@rollup/rollup-darwin-arm64': 4.24.3 - '@rollup/rollup-darwin-x64': 4.24.3 - '@rollup/rollup-freebsd-arm64': 4.24.3 - '@rollup/rollup-freebsd-x64': 4.24.3 - '@rollup/rollup-linux-arm-gnueabihf': 4.24.3 - '@rollup/rollup-linux-arm-musleabihf': 4.24.3 - '@rollup/rollup-linux-arm64-gnu': 4.24.3 - '@rollup/rollup-linux-arm64-musl': 4.24.3 - '@rollup/rollup-linux-powerpc64le-gnu': 4.24.3 - '@rollup/rollup-linux-riscv64-gnu': 4.24.3 - '@rollup/rollup-linux-s390x-gnu': 4.24.3 - '@rollup/rollup-linux-x64-gnu': 4.24.3 - '@rollup/rollup-linux-x64-musl': 4.24.3 - '@rollup/rollup-win32-arm64-msvc': 4.24.3 - '@rollup/rollup-win32-ia32-msvc': 4.24.3 - '@rollup/rollup-win32-x64-msvc': 4.24.3 + '@rollup/rollup-android-arm-eabi': 4.50.1 + '@rollup/rollup-android-arm64': 4.50.1 + '@rollup/rollup-darwin-arm64': 4.50.1 + '@rollup/rollup-darwin-x64': 4.50.1 + '@rollup/rollup-freebsd-arm64': 4.50.1 + '@rollup/rollup-freebsd-x64': 4.50.1 + '@rollup/rollup-linux-arm-gnueabihf': 4.50.1 + '@rollup/rollup-linux-arm-musleabihf': 4.50.1 + '@rollup/rollup-linux-arm64-gnu': 4.50.1 + '@rollup/rollup-linux-arm64-musl': 4.50.1 + '@rollup/rollup-linux-loongarch64-gnu': 4.50.1 + '@rollup/rollup-linux-ppc64-gnu': 4.50.1 + '@rollup/rollup-linux-riscv64-gnu': 4.50.1 + '@rollup/rollup-linux-riscv64-musl': 4.50.1 + '@rollup/rollup-linux-s390x-gnu': 4.50.1 + '@rollup/rollup-linux-x64-gnu': 4.50.1 + '@rollup/rollup-linux-x64-musl': 4.50.1 + '@rollup/rollup-openharmony-arm64': 4.50.1 + '@rollup/rollup-win32-arm64-msvc': 4.50.1 + '@rollup/rollup-win32-ia32-msvc': 4.50.1 + '@rollup/rollup-win32-x64-msvc': 4.50.1 fsevents: 2.3.3 run-applescript@7.0.0: {} @@ -7250,7 +7805,7 @@ snapshots: dependencies: lru-cache: 6.0.0 - semver@7.6.3: {} + semver@7.7.2: {} shebang-command@2.0.0: dependencies: @@ -7262,24 +7817,14 @@ snapshots: signal-exit@4.1.0: {} - sirv@2.0.4: + sirv@3.0.2: dependencies: - '@polka/url': 1.0.0-next.28 - mrmime: 2.0.0 - totalist: 3.0.1 - - sirv@3.0.0: - dependencies: - '@polka/url': 1.0.0-next.28 - mrmime: 2.0.0 + '@polka/url': 1.0.0-next.29 + mrmime: 2.0.1 totalist: 3.0.1 sisteransi@1.0.5: {} - slash@3.0.0: {} - - slashes@3.0.12: {} - source-map-js@1.2.1: {} source-map@0.6.1: {} @@ -7287,27 +7832,27 @@ snapshots: spdx-correct@3.2.0: dependencies: spdx-expression-parse: 3.0.1 - spdx-license-ids: 3.0.20 + spdx-license-ids: 3.0.22 spdx-exceptions@2.5.0: {} spdx-expression-parse@3.0.1: dependencies: spdx-exceptions: 2.5.0 - spdx-license-ids: 3.0.20 + spdx-license-ids: 3.0.22 spdx-expression-parse@4.0.0: dependencies: spdx-exceptions: 2.5.0 - spdx-license-ids: 3.0.20 + spdx-license-ids: 3.0.22 - spdx-license-ids@3.0.20: {} + spdx-license-ids@3.0.22: {} speakingurl@14.0.1: {} sprintf-js@1.0.3: {} - stable-hash@0.0.4: {} + stable-hash-x@0.2.0: {} string-argv@0.3.2: {} @@ -7329,33 +7874,35 @@ snapshots: strip-ansi@7.1.0: dependencies: - ansi-regex: 6.1.0 + ansi-regex: 6.2.0 strip-bom-string@1.0.0: {} strip-final-newline@3.0.0: {} + strip-final-newline@4.0.0: {} + strip-indent@3.0.0: dependencies: min-indent: 1.0.1 strip-json-comments@3.1.1: {} - strip-literal@2.1.0: + strip-literal@2.1.1: dependencies: - js-tokens: 9.0.0 + js-tokens: 9.0.1 sucrase@3.35.0: dependencies: - '@jridgewell/gen-mapping': 0.3.5 + '@jridgewell/gen-mapping': 0.3.13 commander: 4.1.1 glob: 10.4.5 lines-and-columns: 1.2.4 mz: 2.7.0 - pirates: 4.0.6 + pirates: 4.0.7 ts-interface-checker: 0.1.13 - superjson@2.2.1: + superjson@2.2.2: dependencies: copy-anything: 3.0.5 @@ -7369,15 +7916,13 @@ snapshots: supports-preserve-symlinks-flag@1.0.0: {} - svg-tags@1.0.0: {} - - synckit@0.6.2: + synckit@0.11.11: dependencies: - tslib: 2.8.1 + '@pkgr/core': 0.2.9 - synckit@0.9.2: + synckit@0.9.3: dependencies: - '@pkgr/core': 0.1.1 + '@pkgr/core': 0.1.2 tslib: 2.8.1 tailwindcss-primeui@0.3.4(tailwindcss@3.4.17): @@ -7391,29 +7936,27 @@ snapshots: chokidar: 3.6.0 didyoumean: 1.2.2 dlv: 1.1.3 - fast-glob: 3.3.2 + fast-glob: 3.3.3 glob-parent: 6.0.2 is-glob: 4.0.3 - jiti: 1.21.6 + jiti: 1.21.7 lilconfig: 3.1.3 micromatch: 4.0.8 normalize-path: 3.0.0 object-hash: 3.0.0 picocolors: 1.1.1 - postcss: 8.4.47 - postcss-import: 15.1.0(postcss@8.4.47) - postcss-js: 4.0.1(postcss@8.4.47) - postcss-load-config: 4.0.2(postcss@8.4.47) - postcss-nested: 6.2.0(postcss@8.4.47) + postcss: 8.5.6 + postcss-import: 15.1.0(postcss@8.5.6) + postcss-js: 4.0.1(postcss@8.5.6) + postcss-load-config: 4.0.2(postcss@8.5.6) + postcss-nested: 6.2.0(postcss@8.5.6) postcss-selector-parser: 6.1.2 - resolve: 1.22.8 + resolve: 1.22.10 sucrase: 3.35.0 transitivePeerDependencies: - ts-node - tapable@2.2.1: {} - - text-table@0.2.0: {} + tapable@2.2.3: {} thenify-all@1.6.0: dependencies: @@ -7423,7 +7966,7 @@ snapshots: dependencies: any-promise: 1.3.0 - tinyexec@0.3.1: {} + tinyexec@1.0.1: {} to-regex-range@5.0.1: dependencies: @@ -7437,20 +7980,29 @@ snapshots: totalist@3.0.1: {} - ts-api-utils@1.4.0(typescript@5.6.3): + ts-api-utils@2.1.0(typescript@5.6.3): + dependencies: + typescript: 5.6.3 + + ts-declaration-location@1.0.7(typescript@5.6.3): dependencies: + picomatch: 4.0.3 typescript: 5.6.3 ts-interface-checker@0.1.13: {} + ts-macro@0.1.35: + dependencies: + muggle-string: 0.4.1 + ts-md5@1.3.1: {} tslib@2.8.1: {} - tsx@4.19.2: + tsx@4.20.5: dependencies: - esbuild: 0.23.1 - get-tsconfig: 4.8.1 + esbuild: 0.25.9 + get-tsconfig: 4.10.1 optionalDependencies: fsevents: 2.3.3 @@ -7464,42 +8016,51 @@ snapshots: type-fest@0.8.1: {} - typescript@5.4.2: {} - typescript@5.6.3: {} + typescript@5.8.2: {} + uc.micro@2.1.0: {} - ufo@1.5.4: {} + ufo@1.6.1: {} - unconfig@0.6.0: + unconfig@0.6.1: dependencies: - '@antfu/utils': 0.7.10 + '@antfu/utils': 8.1.1 defu: 6.1.4 - importx: 0.5.0 + importx: 0.5.2 transitivePeerDependencies: - supports-color - undici-types@6.19.8: {} + unconfig@7.3.3: + dependencies: + '@quansync/fs': 0.1.5 + defu: 6.1.4 + jiti: 2.5.1 + quansync: 0.2.11 + + undici-types@6.21.0: {} + + unicorn-magic@0.3.0: {} - unimport@3.13.1(rollup@4.24.3): + unimport@3.14.6(rollup@4.50.1): dependencies: - '@rollup/pluginutils': 5.1.3(rollup@4.24.3) - acorn: 8.14.0 + '@rollup/pluginutils': 5.3.0(rollup@4.50.1) + acorn: 8.15.0 escape-string-regexp: 5.0.0 estree-walker: 3.0.3 - fast-glob: 3.3.2 - local-pkg: 0.5.0 - magic-string: 0.30.12 - mlly: 1.7.2 - pathe: 1.1.2 - pkg-types: 1.2.1 + fast-glob: 3.3.3 + local-pkg: 1.1.2 + magic-string: 0.30.18 + mlly: 1.8.0 + pathe: 2.0.3 + picomatch: 4.0.3 + pkg-types: 1.3.1 scule: 1.3.0 - strip-literal: 2.1.0 - unplugin: 1.15.0 + strip-literal: 2.1.1 + unplugin: 1.16.1 transitivePeerDependencies: - rollup - - webpack-sources unist-util-is@6.0.0: dependencies: @@ -7520,160 +8081,175 @@ snapshots: unist-util-is: 6.0.0 unist-util-visit-parents: 6.0.1 - universalify@0.1.2: {} - universalify@2.0.1: {} - unplugin-auto-import@0.18.3(@vueuse/core@11.2.0(vue@3.5.12(typescript@5.6.3)))(rollup@4.24.3): + unplugin-auto-import@0.18.6(@vueuse/core@11.3.0(vue@3.5.21(typescript@5.6.3)))(rollup@4.50.1): dependencies: '@antfu/utils': 0.7.10 - '@rollup/pluginutils': 5.1.3(rollup@4.24.3) - fast-glob: 3.3.2 - local-pkg: 0.5.0 - magic-string: 0.30.12 + '@rollup/pluginutils': 5.3.0(rollup@4.50.1) + fast-glob: 3.3.3 + local-pkg: 0.5.1 + magic-string: 0.30.18 minimatch: 9.0.5 - unimport: 3.13.1(rollup@4.24.3) - unplugin: 1.15.0 + unimport: 3.14.6(rollup@4.50.1) + unplugin: 1.16.1 optionalDependencies: - '@vueuse/core': 11.2.0(vue@3.5.12(typescript@5.6.3)) + '@vueuse/core': 11.3.0(vue@3.5.21(typescript@5.6.3)) transitivePeerDependencies: - rollup - - webpack-sources - unplugin-combine@1.0.3(esbuild@0.23.1)(rollup@4.24.3)(vite@5.4.10(@types/node@22.8.6)): - dependencies: - '@antfu/utils': 0.7.10 - unplugin: 1.15.0 + unplugin-combine@1.2.1(esbuild@0.25.9)(rollup@4.50.1)(unplugin@1.16.1)(vite@5.4.19(@types/node@22.18.1)): optionalDependencies: - esbuild: 0.23.1 - rollup: 4.24.3 - vite: 5.4.10(@types/node@22.8.6) - transitivePeerDependencies: - - webpack-sources + esbuild: 0.25.9 + rollup: 4.50.1 + unplugin: 1.16.1 + vite: 5.4.19(@types/node@22.18.1) - unplugin-vue-components@0.27.4(@babel/parser@7.26.2)(rollup@4.24.3)(vue@3.5.12(typescript@5.6.3)): + unplugin-vue-components@0.27.5(@babel/parser@7.28.4)(rollup@4.50.1)(vue@3.5.21(typescript@5.6.3)): dependencies: '@antfu/utils': 0.7.10 - '@rollup/pluginutils': 5.1.3(rollup@4.24.3) + '@rollup/pluginutils': 5.3.0(rollup@4.50.1) chokidar: 3.6.0 - debug: 4.3.7 - fast-glob: 3.3.2 - local-pkg: 0.5.0 - magic-string: 0.30.12 + debug: 4.4.1 + fast-glob: 3.3.3 + local-pkg: 0.5.1 + magic-string: 0.30.18 minimatch: 9.0.5 - mlly: 1.7.2 - unplugin: 1.15.0 - vue: 3.5.12(typescript@5.6.3) + mlly: 1.8.0 + unplugin: 1.16.1 + vue: 3.5.21(typescript@5.6.3) optionalDependencies: - '@babel/parser': 7.26.2 + '@babel/parser': 7.28.4 transitivePeerDependencies: - rollup - supports-color - - webpack-sources - unplugin-vue-define-options@1.5.2(rollup@4.24.3)(vue@3.5.12(typescript@5.6.3)): + unplugin-vue-define-options@1.5.5(vue@3.5.21(typescript@5.6.3)): dependencies: - '@vue-macros/common': 1.15.0(rollup@4.24.3)(vue@3.5.12(typescript@5.6.3)) + '@vue-macros/common': 1.16.1(vue@3.5.21(typescript@5.6.3)) ast-walker-scope: 0.6.2 - unplugin: 1.15.0 + unplugin: 1.16.1 transitivePeerDependencies: - - rollup - vue - - webpack-sources - - unplugin-vue-macros@2.13.3(@vueuse/core@11.2.0(vue@3.5.12(typescript@5.6.3)))(esbuild@0.23.1)(rollup@4.24.3)(typescript@5.6.3)(vite@5.4.10(@types/node@22.8.6))(vue-tsc@2.1.10(typescript@5.6.3))(vue@3.5.12(typescript@5.6.3)): - dependencies: - '@vue-macros/better-define': 1.11.0(rollup@4.24.3)(vue@3.5.12(typescript@5.6.3)) - '@vue-macros/boolean-prop': 0.5.2(rollup@4.24.3)(vue@3.5.12(typescript@5.6.3)) - '@vue-macros/chain-call': 0.4.2(rollup@4.24.3)(vue@3.5.12(typescript@5.6.3)) - '@vue-macros/common': 1.15.0(rollup@4.24.3)(vue@3.5.12(typescript@5.6.3)) - '@vue-macros/config': 0.5.0(rollup@4.24.3)(vue@3.5.12(typescript@5.6.3)) - '@vue-macros/define-emit': 0.5.1(rollup@4.24.3)(vue@3.5.12(typescript@5.6.3)) - '@vue-macros/define-models': 1.3.2(@vueuse/core@11.2.0(vue@3.5.12(typescript@5.6.3)))(rollup@4.24.3)(vue@3.5.12(typescript@5.6.3)) - '@vue-macros/define-prop': 0.6.1(rollup@4.24.3)(vue@3.5.12(typescript@5.6.3)) - '@vue-macros/define-props': 4.0.3(@vue-macros/reactivity-transform@1.1.3(rollup@4.24.3)(vue@3.5.12(typescript@5.6.3)))(rollup@4.24.3)(vue@3.5.12(typescript@5.6.3)) - '@vue-macros/define-props-refs': 1.3.2(rollup@4.24.3)(vue@3.5.12(typescript@5.6.3)) - '@vue-macros/define-render': 1.6.2(rollup@4.24.3)(vue@3.5.12(typescript@5.6.3)) - '@vue-macros/define-slots': 1.2.2(rollup@4.24.3)(vue@3.5.12(typescript@5.6.3)) - '@vue-macros/define-stylex': 0.2.0(rollup@4.24.3)(vue@3.5.12(typescript@5.6.3)) - '@vue-macros/devtools': 0.4.0(typescript@5.6.3)(vite@5.4.10(@types/node@22.8.6)) - '@vue-macros/export-expose': 0.3.2(rollup@4.24.3)(vue@3.5.12(typescript@5.6.3)) - '@vue-macros/export-props': 0.6.2(rollup@4.24.3)(vue@3.5.12(typescript@5.6.3)) - '@vue-macros/export-render': 0.3.2(rollup@4.24.3)(vue@3.5.12(typescript@5.6.3)) - '@vue-macros/hoist-static': 1.6.2(rollup@4.24.3)(vue@3.5.12(typescript@5.6.3)) - '@vue-macros/jsx-directive': 0.9.2(rollup@4.24.3)(typescript@5.6.3) - '@vue-macros/named-template': 0.5.2(rollup@4.24.3)(vue@3.5.12(typescript@5.6.3)) - '@vue-macros/reactivity-transform': 1.1.3(rollup@4.24.3)(vue@3.5.12(typescript@5.6.3)) - '@vue-macros/script-lang': 0.2.2(rollup@4.24.3)(vue@3.5.12(typescript@5.6.3)) - '@vue-macros/setup-block': 0.4.2(rollup@4.24.3)(vue@3.5.12(typescript@5.6.3)) - '@vue-macros/setup-component': 0.18.2(rollup@4.24.3)(vue@3.5.12(typescript@5.6.3)) - '@vue-macros/setup-sfc': 0.18.2(rollup@4.24.3)(vue@3.5.12(typescript@5.6.3)) - '@vue-macros/short-bind': 1.1.2(rollup@4.24.3)(vue@3.5.12(typescript@5.6.3)) - '@vue-macros/short-emits': 1.6.2(rollup@4.24.3)(vue@3.5.12(typescript@5.6.3)) - '@vue-macros/short-vmodel': 1.5.2(rollup@4.24.3)(vue@3.5.12(typescript@5.6.3)) - '@vue-macros/volar': 0.30.5(rollup@4.24.3)(typescript@5.6.3)(vue-tsc@2.1.10(typescript@5.6.3))(vue@3.5.12(typescript@5.6.3)) - unplugin: 1.15.0 - unplugin-combine: 1.0.3(esbuild@0.23.1)(rollup@4.24.3)(vite@5.4.10(@types/node@22.8.6)) - unplugin-vue-define-options: 1.5.2(rollup@4.24.3)(vue@3.5.12(typescript@5.6.3)) - vue: 3.5.12(typescript@5.6.3) + + unplugin-vue-macros@2.14.5(@vueuse/core@11.3.0(vue@3.5.21(typescript@5.6.3)))(esbuild@0.25.9)(rollup@4.50.1)(typescript@5.6.3)(vite@5.4.19(@types/node@22.18.1))(vue-tsc@2.2.12(typescript@5.6.3))(vue@3.5.21(typescript@5.6.3)): + dependencies: + '@vue-macros/better-define': 1.11.4(vue@3.5.21(typescript@5.6.3)) + '@vue-macros/boolean-prop': 0.5.5(vue@3.5.21(typescript@5.6.3)) + '@vue-macros/chain-call': 0.4.5(vue@3.5.21(typescript@5.6.3)) + '@vue-macros/common': 1.16.1(vue@3.5.21(typescript@5.6.3)) + '@vue-macros/config': 0.6.1(vue@3.5.21(typescript@5.6.3)) + '@vue-macros/define-emit': 0.5.4(vue@3.5.21(typescript@5.6.3)) + '@vue-macros/define-models': 1.3.5(@vueuse/core@11.3.0(vue@3.5.21(typescript@5.6.3)))(vue@3.5.21(typescript@5.6.3)) + '@vue-macros/define-prop': 0.6.5(vue@3.5.21(typescript@5.6.3)) + '@vue-macros/define-props': 4.0.6(@vue-macros/reactivity-transform@1.1.6(vue@3.5.21(typescript@5.6.3)))(vue@3.5.21(typescript@5.6.3)) + '@vue-macros/define-props-refs': 1.3.5(vue@3.5.21(typescript@5.6.3)) + '@vue-macros/define-render': 1.6.6(vue@3.5.21(typescript@5.6.3)) + '@vue-macros/define-slots': 1.2.6(vue@3.5.21(typescript@5.6.3)) + '@vue-macros/define-stylex': 0.2.3(vue@3.5.21(typescript@5.6.3)) + '@vue-macros/devtools': 0.4.1(typescript@5.6.3)(vite@5.4.19(@types/node@22.18.1)) + '@vue-macros/export-expose': 0.3.5(vue@3.5.21(typescript@5.6.3)) + '@vue-macros/export-props': 0.6.5(vue@3.5.21(typescript@5.6.3)) + '@vue-macros/export-render': 0.3.5(vue@3.5.21(typescript@5.6.3)) + '@vue-macros/hoist-static': 1.7.0(vue@3.5.21(typescript@5.6.3)) + '@vue-macros/jsx-directive': 0.10.6(typescript@5.6.3) + '@vue-macros/named-template': 0.5.5(vue@3.5.21(typescript@5.6.3)) + '@vue-macros/reactivity-transform': 1.1.6(vue@3.5.21(typescript@5.6.3)) + '@vue-macros/script-lang': 0.2.5(vue@3.5.21(typescript@5.6.3)) + '@vue-macros/setup-block': 0.4.5(vue@3.5.21(typescript@5.6.3)) + '@vue-macros/setup-component': 0.18.5(vue@3.5.21(typescript@5.6.3)) + '@vue-macros/setup-sfc': 0.18.5(vue@3.5.21(typescript@5.6.3)) + '@vue-macros/short-bind': 1.1.5(vue@3.5.21(typescript@5.6.3)) + '@vue-macros/short-emits': 1.6.5(vue@3.5.21(typescript@5.6.3)) + '@vue-macros/short-vmodel': 1.5.5(vue@3.5.21(typescript@5.6.3)) + '@vue-macros/volar': 0.30.15(typescript@5.6.3)(vue-tsc@2.2.12(typescript@5.6.3))(vue@3.5.21(typescript@5.6.3)) + unplugin: 1.16.1 + unplugin-combine: 1.2.1(esbuild@0.25.9)(rollup@4.50.1)(unplugin@1.16.1)(vite@5.4.19(@types/node@22.18.1)) + unplugin-vue-define-options: 1.5.5(vue@3.5.21(typescript@5.6.3)) + vue: 3.5.21(typescript@5.6.3) transitivePeerDependencies: - '@rspack/core' - '@vueuse/core' - esbuild - rolldown - rollup - - supports-color - typescript - vite - vue-tsc - webpack - - webpack-sources - unplugin-vue-markdown@0.26.2(rollup@4.24.3)(vite@5.4.10(@types/node@22.8.6)): + unplugin-vue-markdown@0.26.3(rollup@4.50.1)(vite@5.4.19(@types/node@22.18.1)): dependencies: - '@mdit-vue/plugin-component': 2.1.3 - '@mdit-vue/plugin-frontmatter': 2.1.3 - '@mdit-vue/types': 2.1.0 - '@rollup/pluginutils': 5.1.3(rollup@4.24.3) + '@mdit-vue/plugin-component': 2.1.4 + '@mdit-vue/plugin-frontmatter': 2.1.4 + '@mdit-vue/types': 2.1.4 + '@rollup/pluginutils': 5.3.0(rollup@4.50.1) '@types/markdown-it': 14.1.2 markdown-it: 14.1.0 - unplugin: 1.15.0 - vite: 5.4.10(@types/node@22.8.6) + unplugin: 1.16.1 + vite: 5.4.19(@types/node@22.18.1) transitivePeerDependencies: - rollup - - webpack-sources - unplugin-vue-router@0.10.8(rollup@4.24.3)(vue-router@4.4.5(vue@3.5.12(typescript@5.6.3)))(vue@3.5.12(typescript@5.6.3)): + unplugin-vue-router@0.10.9(rollup@4.50.1)(vue-router@4.5.1(vue@3.5.21(typescript@5.6.3)))(vue@3.5.21(typescript@5.6.3)): dependencies: - '@babel/types': 7.26.0 - '@rollup/pluginutils': 5.1.3(rollup@4.24.3) - '@vue-macros/common': 1.15.0(rollup@4.24.3)(vue@3.5.12(typescript@5.6.3)) + '@babel/types': 7.28.4 + '@rollup/pluginutils': 5.3.0(rollup@4.50.1) + '@vue-macros/common': 1.16.1(vue@3.5.21(typescript@5.6.3)) ast-walker-scope: 0.6.2 chokidar: 3.6.0 - fast-glob: 3.3.2 + fast-glob: 3.3.3 json5: 2.2.3 - local-pkg: 0.5.0 - magic-string: 0.30.12 - mlly: 1.7.2 + local-pkg: 0.5.1 + magic-string: 0.30.18 + mlly: 1.8.0 pathe: 1.1.2 scule: 1.3.0 - unplugin: 1.15.0 - yaml: 2.6.0 + unplugin: 2.0.0-beta.1 + yaml: 2.8.1 optionalDependencies: - vue-router: 4.4.5(vue@3.5.12(typescript@5.6.3)) + vue-router: 4.5.1(vue@3.5.21(typescript@5.6.3)) transitivePeerDependencies: - rollup - vue - - webpack-sources - unplugin@1.15.0: + unplugin@1.16.1: dependencies: - acorn: 8.14.0 + acorn: 8.15.0 webpack-virtual-modules: 0.6.2 - update-browserslist-db@1.1.1(browserslist@4.24.2): + unplugin@2.0.0-beta.1: dependencies: - browserslist: 4.24.2 + acorn: 8.15.0 + webpack-virtual-modules: 0.6.2 + + unrs-resolver@1.11.1: + dependencies: + napi-postinstall: 0.3.3 + optionalDependencies: + '@unrs/resolver-binding-android-arm-eabi': 1.11.1 + '@unrs/resolver-binding-android-arm64': 1.11.1 + '@unrs/resolver-binding-darwin-arm64': 1.11.1 + '@unrs/resolver-binding-darwin-x64': 1.11.1 + '@unrs/resolver-binding-freebsd-x64': 1.11.1 + '@unrs/resolver-binding-linux-arm-gnueabihf': 1.11.1 + '@unrs/resolver-binding-linux-arm-musleabihf': 1.11.1 + '@unrs/resolver-binding-linux-arm64-gnu': 1.11.1 + '@unrs/resolver-binding-linux-arm64-musl': 1.11.1 + '@unrs/resolver-binding-linux-ppc64-gnu': 1.11.1 + '@unrs/resolver-binding-linux-riscv64-gnu': 1.11.1 + '@unrs/resolver-binding-linux-riscv64-musl': 1.11.1 + '@unrs/resolver-binding-linux-s390x-gnu': 1.11.1 + '@unrs/resolver-binding-linux-x64-gnu': 1.11.1 + '@unrs/resolver-binding-linux-x64-musl': 1.11.1 + '@unrs/resolver-binding-wasm32-wasi': 1.11.1 + '@unrs/resolver-binding-win32-arm64-msvc': 1.11.1 + '@unrs/resolver-binding-win32-ia32-msvc': 1.11.1 + '@unrs/resolver-binding-win32-x64-msvc': 1.11.1 + + update-browserslist-db@1.1.3(browserslist@4.25.4): + dependencies: + browserslist: 4.25.4 escalade: 3.2.0 picocolors: 1.1.1 @@ -7685,164 +8261,168 @@ snapshots: uuid@10.0.0: {} - uuid@11.0.2: {} + uuid@11.1.0: {} validate-npm-package-license@3.0.4: dependencies: spdx-correct: 3.2.0 spdx-expression-parse: 3.0.1 - vite-hot-client@0.2.3(vite@5.4.10(@types/node@22.8.6)): + vite-hot-client@2.1.0(vite@5.4.19(@types/node@22.18.1)): dependencies: - vite: 5.4.10(@types/node@22.8.6) + vite: 5.4.19(@types/node@22.18.1) - vite-plugin-dts@4.3.0(@types/node@22.8.6)(rollup@4.24.3)(typescript@5.6.3)(vite@5.4.10(@types/node@22.8.6)): + vite-plugin-dts@4.5.4(@types/node@22.18.1)(rollup@4.50.1)(typescript@5.6.3)(vite@5.4.19(@types/node@22.18.1)): dependencies: - '@microsoft/api-extractor': 7.47.11(@types/node@22.8.6) - '@rollup/pluginutils': 5.1.3(rollup@4.24.3) - '@volar/typescript': 2.4.8 - '@vue/language-core': 2.1.6(typescript@5.6.3) + '@microsoft/api-extractor': 7.52.11(@types/node@22.18.1) + '@rollup/pluginutils': 5.3.0(rollup@4.50.1) + '@volar/typescript': 2.4.23 + '@vue/language-core': 2.2.0(typescript@5.6.3) compare-versions: 6.1.1 - debug: 4.3.7 + debug: 4.4.1 kolorist: 1.8.0 - local-pkg: 0.5.0 - magic-string: 0.30.12 + local-pkg: 1.1.2 + magic-string: 0.30.18 typescript: 5.6.3 optionalDependencies: - vite: 5.4.10(@types/node@22.8.6) + vite: 5.4.19(@types/node@22.18.1) transitivePeerDependencies: - '@types/node' - rollup - supports-color - vite-plugin-inspect@0.8.7(rollup@4.24.3)(vite@5.4.10(@types/node@22.8.6)): + vite-plugin-inspect@0.8.9(rollup@4.50.1)(vite@5.4.19(@types/node@22.18.1)): dependencies: '@antfu/utils': 0.7.10 - '@rollup/pluginutils': 5.1.3(rollup@4.24.3) - debug: 4.3.7 + '@rollup/pluginutils': 5.3.0(rollup@4.50.1) + debug: 4.4.1 error-stack-parser-es: 0.1.5 - fs-extra: 11.2.0 - open: 10.1.0 + fs-extra: 11.3.1 + open: 10.2.0 perfect-debounce: 1.0.0 picocolors: 1.1.1 - sirv: 2.0.4 - vite: 5.4.10(@types/node@22.8.6) + sirv: 3.0.2 + vite: 5.4.19(@types/node@22.18.1) transitivePeerDependencies: - rollup - supports-color - vite-plugin-singlefile@2.0.3(rollup@4.24.3)(vite@5.4.10(@types/node@22.8.6)): + vite-plugin-singlefile@2.3.0(rollup@4.50.1)(vite@5.4.19(@types/node@22.18.1)): dependencies: micromatch: 4.0.8 - rollup: 4.24.3 - vite: 5.4.10(@types/node@22.8.6) - - vite-plugin-vue-devtools@7.6.3(rollup@4.24.3)(vite@5.4.10(@types/node@22.8.6))(vue@3.5.12(typescript@5.6.3)): - dependencies: - '@vue/devtools-core': 7.6.3(vite@5.4.10(@types/node@22.8.6))(vue@3.5.12(typescript@5.6.3)) - '@vue/devtools-kit': 7.6.3 - '@vue/devtools-shared': 7.6.3 - execa: 8.0.1 - sirv: 3.0.0 - vite: 5.4.10(@types/node@22.8.6) - vite-plugin-inspect: 0.8.7(rollup@4.24.3)(vite@5.4.10(@types/node@22.8.6)) - vite-plugin-vue-inspector: 5.2.0(vite@5.4.10(@types/node@22.8.6)) + rollup: 4.50.1 + vite: 5.4.19(@types/node@22.18.1) + + vite-plugin-vue-devtools@7.7.7(rollup@4.50.1)(vite@5.4.19(@types/node@22.18.1))(vue@3.5.21(typescript@5.6.3)): + dependencies: + '@vue/devtools-core': 7.7.7(vite@5.4.19(@types/node@22.18.1))(vue@3.5.21(typescript@5.6.3)) + '@vue/devtools-kit': 7.7.7 + '@vue/devtools-shared': 7.7.7 + execa: 9.6.0 + sirv: 3.0.2 + vite: 5.4.19(@types/node@22.18.1) + vite-plugin-inspect: 0.8.9(rollup@4.50.1)(vite@5.4.19(@types/node@22.18.1)) + vite-plugin-vue-inspector: 5.3.2(vite@5.4.19(@types/node@22.18.1)) transitivePeerDependencies: - '@nuxt/kit' - rollup - supports-color - vue - vite-plugin-vue-inspector@5.2.0(vite@5.4.10(@types/node@22.8.6)): + vite-plugin-vue-inspector@5.3.2(vite@5.4.19(@types/node@22.18.1)): dependencies: - '@babel/core': 7.26.0 - '@babel/plugin-proposal-decorators': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-syntax-import-attributes': 7.26.0(@babel/core@7.26.0) - '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.26.0) - '@babel/plugin-transform-typescript': 7.25.9(@babel/core@7.26.0) - '@vue/babel-plugin-jsx': 1.2.5(@babel/core@7.26.0) - '@vue/compiler-dom': 3.5.12 + '@babel/core': 7.28.4 + '@babel/plugin-proposal-decorators': 7.28.0(@babel/core@7.28.4) + '@babel/plugin-syntax-import-attributes': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.28.4) + '@babel/plugin-transform-typescript': 7.28.0(@babel/core@7.28.4) + '@vue/babel-plugin-jsx': 1.5.0(@babel/core@7.28.4) + '@vue/compiler-dom': 3.5.21 kolorist: 1.8.0 - magic-string: 0.30.12 - vite: 5.4.10(@types/node@22.8.6) + magic-string: 0.30.18 + vite: 5.4.19(@types/node@22.18.1) transitivePeerDependencies: - supports-color - vite-plugin-vue-layouts@0.11.0(vite@5.4.10(@types/node@22.8.6))(vue-router@4.4.5(vue@3.5.12(typescript@5.6.3)))(vue@3.5.12(typescript@5.6.3)): + vite-plugin-vue-layouts@0.11.0(vite@5.4.19(@types/node@22.18.1))(vue-router@4.5.1(vue@3.5.21(typescript@5.6.3)))(vue@3.5.21(typescript@5.6.3)): dependencies: - debug: 4.3.7 - fast-glob: 3.3.2 - vite: 5.4.10(@types/node@22.8.6) - vue: 3.5.12(typescript@5.6.3) - vue-router: 4.4.5(vue@3.5.12(typescript@5.6.3)) + debug: 4.4.1 + fast-glob: 3.3.3 + vite: 5.4.19(@types/node@22.18.1) + vue: 3.5.21(typescript@5.6.3) + vue-router: 4.5.1(vue@3.5.21(typescript@5.6.3)) transitivePeerDependencies: - supports-color - vite@5.4.10(@types/node@22.8.6): + vite@5.4.19(@types/node@22.18.1): dependencies: esbuild: 0.21.5 - postcss: 8.4.47 - rollup: 4.24.3 + postcss: 8.5.6 + rollup: 4.50.1 optionalDependencies: - '@types/node': 22.8.6 + '@types/node': 22.18.1 fsevents: 2.3.3 - vscode-uri@3.0.8: {} + vscode-uri@3.1.0: {} + + vue-chartjs@5.3.2(chart.js@4.5.0)(vue@3.5.21(typescript@5.6.3)): + dependencies: + chart.js: 4.5.0 + vue: 3.5.21(typescript@5.6.3) - vue-demi@0.14.10(vue@3.5.12(typescript@5.6.3)): + vue-demi@0.14.10(vue@3.5.21(typescript@5.6.3)): dependencies: - vue: 3.5.12(typescript@5.6.3) + vue: 3.5.21(typescript@5.6.3) - vue-eslint-parser@9.4.3(eslint@9.14.0(jiti@2.4.0)): + vue-eslint-parser@9.4.3(eslint@9.35.0(jiti@2.5.1)): dependencies: - debug: 4.3.7 - eslint: 9.14.0(jiti@2.4.0) + debug: 4.4.1 + eslint: 9.35.0(jiti@2.5.1) eslint-scope: 7.2.2 eslint-visitor-keys: 3.4.3 espree: 9.6.1 esquery: 1.6.0 lodash: 4.17.21 - semver: 7.6.3 + semver: 7.7.2 transitivePeerDependencies: - supports-color - vue-i18n@10.0.4(vue@3.5.12(typescript@5.6.3)): + vue-i18n@10.0.8(vue@3.5.21(typescript@5.6.3)): dependencies: - '@intlify/core-base': 10.0.4 - '@intlify/shared': 10.0.4 + '@intlify/core-base': 10.0.8 + '@intlify/shared': 10.0.8 '@vue/devtools-api': 6.6.4 - vue: 3.5.12(typescript@5.6.3) + vue: 3.5.21(typescript@5.6.3) - vue-i18n@9.14.4(vue@3.5.12(typescript@5.6.3)): + vue-i18n@9.14.5(vue@3.5.21(typescript@5.6.3)): dependencies: - '@intlify/core-base': 9.14.4 - '@intlify/shared': 9.14.4 + '@intlify/core-base': 9.14.5 + '@intlify/shared': 9.14.5 '@vue/devtools-api': 6.6.4 - vue: 3.5.12(typescript@5.6.3) + vue: 3.5.21(typescript@5.6.3) - vue-resize@2.0.0-alpha.1(vue@3.5.12(typescript@5.6.3)): + vue-resize@2.0.0-alpha.1(vue@3.5.21(typescript@5.6.3)): dependencies: - vue: 3.5.12(typescript@5.6.3) + vue: 3.5.21(typescript@5.6.3) - vue-router@4.4.5(vue@3.5.12(typescript@5.6.3)): + vue-router@4.5.1(vue@3.5.21(typescript@5.6.3)): dependencies: '@vue/devtools-api': 6.6.4 - vue: 3.5.12(typescript@5.6.3) + vue: 3.5.21(typescript@5.6.3) - vue-tsc@2.1.10(typescript@5.6.3): + vue-tsc@2.2.12(typescript@5.6.3): dependencies: - '@volar/typescript': 2.4.8 - '@vue/language-core': 2.1.10(typescript@5.6.3) - semver: 7.6.3 + '@volar/typescript': 2.4.15 + '@vue/language-core': 2.2.12(typescript@5.6.3) typescript: 5.6.3 - vue@3.5.12(typescript@5.6.3): + vue@3.5.21(typescript@5.6.3): dependencies: - '@vue/compiler-dom': 3.5.12 - '@vue/compiler-sfc': 3.5.12 - '@vue/runtime-dom': 3.5.12 - '@vue/server-renderer': 3.5.12(vue@3.5.12(typescript@5.6.3)) - '@vue/shared': 3.5.12 + '@vue/compiler-dom': 3.5.21 + '@vue/compiler-sfc': 3.5.21 + '@vue/runtime-dom': 3.5.21 + '@vue/server-renderer': 3.5.21(vue@3.5.21(typescript@5.6.3)) + '@vue/shared': 3.5.21 optionalDependencies: typescript: 5.6.3 @@ -7866,6 +8446,10 @@ snapshots: string-width: 5.1.2 strip-ansi: 7.1.0 + wsl-utils@0.1.0: + dependencies: + is-wsl: 3.1.0 + xml-name-validator@4.0.0: {} y18n@5.0.8: {} @@ -7874,13 +8458,12 @@ snapshots: yallist@4.0.0: {} - yaml-eslint-parser@1.2.3: + yaml-eslint-parser@1.3.0: dependencies: eslint-visitor-keys: 3.4.3 - lodash: 4.17.21 - yaml: 2.6.0 + yaml: 2.8.1 - yaml@2.6.0: {} + yaml@2.8.1: {} yargs-parser@21.1.1: {} @@ -7896,4 +8479,6 @@ snapshots: yocto-queue@0.1.0: {} + yoctocolors@2.1.2: {} + zwitch@2.0.4: {}