diff --git a/.sqlx/query-a5a6769dd15af8a3e004b68334960da8185970c57bde31c413f638880c6bb4db.json b/.sqlx/query-a5a6769dd15af8a3e004b68334960da8185970c57bde31c413f638880c6bb4db.json new file mode 100644 index 0000000000..e3c2de5318 --- /dev/null +++ b/.sqlx/query-a5a6769dd15af8a3e004b68334960da8185970c57bde31c413f638880c6bb4db.json @@ -0,0 +1,15 @@ +{ + "db_name": "PostgreSQL", + "query": "DELETE FROM \"gateway\" WHERE id = $1 AND network_id = $2", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Int8", + "Int8" + ] + }, + "nullable": [] + }, + "hash": "a5a6769dd15af8a3e004b68334960da8185970c57bde31c413f638880c6bb4db" +} diff --git a/Cargo.lock b/Cargo.lock index 8aa34e9b9c..78a6e9c86f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -284,9 +284,9 @@ checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "axum" -version = "0.8.7" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b098575ebe77cb6d14fc7f32749631a6e44edbef6b796f89b020e99ba20d425" +checksum = "8b52af3cb4058c895d37317bb27508dccc8e5f2d39454016b297bf4a400597b8" dependencies = [ "axum-core", "bytes", @@ -328,9 +328,9 @@ dependencies = [ [[package]] name = "axum-core" -version = "0.5.5" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59446ce19cd142f8833f856eb31f3eb097812d1479ab224f54d72428ca21ea22" +checksum = "08c78f31d7b1291f7ee735c1c6780ccde7785daae9a9206026862dab7d8792d1" dependencies = [ "bytes", "futures-core", @@ -347,15 +347,16 @@ dependencies = [ [[package]] name = "axum-extra" -version = "0.10.3" +version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9963ff19f40c6102c76756ef0a46004c0d58957d87259fc9208ff8441c12ab96" +checksum = "fef252edff26ddba56bbcdf2ee3307b8129acb86f5749b68990c168a6fcc9c76" dependencies = [ "axum", "axum-core", "bytes", "cookie", "form_urlencoded", + "futures-core", "futures-util", "headers", "http", @@ -363,7 +364,6 @@ dependencies = [ "http-body-util", "mime", "pin-project-lite", - "rustversion", "serde_core", "serde_html_form", "serde_path_to_error", @@ -384,12 +384,6 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "022dfe9eb35f19ebbcb51e0b40a5ab759f46ad60cadf7297e0bd085afb50e076" -[[package]] -name = "base64" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" - [[package]] name = "base64" version = "0.21.7" @@ -574,9 +568,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.49" +version = "1.2.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90583009037521a116abf44494efecd645ba48b6622457080f080b85544e2215" +checksum = "7a0aeaff4ff1a90589618835a598e545176939b97874f7abc7851caa0618f203" dependencies = [ "find-msvc-tools", "jobserver", @@ -1108,7 +1102,6 @@ dependencies = [ "defguard_event_router", "defguard_mail", "defguard_proxy_manager", - "defguard_session_manager", "defguard_version", "dotenvy", "secrecy", @@ -1139,7 +1132,7 @@ dependencies = [ "rsa", "secrecy", "serde", - "serde_cbor_2 0.12.0-dev", + "serde_cbor_2", "sqlx", "struct-patch", "thiserror 2.0.17", @@ -1159,7 +1152,6 @@ version = "0.0.0" dependencies = [ "ammonia", "anyhow", - "argon2", "axum", "axum-client-ip", "axum-extra", @@ -1196,7 +1188,6 @@ dependencies = [ "secrecy", "semver", "serde", - "serde_cbor_2 0.12.0-dev", "serde_json", "serde_qs", "serde_urlencoded", @@ -1294,9 +1285,7 @@ dependencies = [ name = "defguard_proxy_manager" version = "0.0.0" dependencies = [ - "anyhow", "axum", - "chrono", "defguard_common", "defguard_core", "defguard_mail", @@ -1439,18 +1428,18 @@ dependencies = [ [[package]] name = "derive_more" -version = "2.1.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10b768e943bed7bf2cab53df09f4bc34bfd217cdb57d971e769874c9a6710618" +checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134" dependencies = [ "derive_more-impl", ] [[package]] name = "derive_more-impl" -version = "2.1.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d286bfdaf75e988b4a78e013ecd79c581e06399ab53fbacd2d916c2f904f30b" +checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" dependencies = [ "convert_case 0.10.0", "proc-macro2", @@ -1550,9 +1539,9 @@ dependencies = [ [[package]] name = "dtoa" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6add3b8cff394282be81f3fc1a0605db594ed69890078ca6e2cab1c408bcf04" +checksum = "4c3cf4824e2d5f025c7b531afcb2325364084a16806f6d47fbc1f5fbd9960590" [[package]] name = "dtoa-short" @@ -1743,9 +1732,9 @@ checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" [[package]] name = "find-msvc-tools" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844" +checksum = "645cbb3a84e60b7531617d5ae4e57f7e27308f6445f5abf653209ea76dec8dff" [[package]] name = "fixedbitset" @@ -2061,12 +2050,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "half" -version = "1.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b43ede17f21864e81be2fa654110bf1e793774238d86ef8555c37e6519c0403" - [[package]] name = "half" version = "2.7.1" @@ -2572,9 +2555,9 @@ dependencies = [ [[package]] name = "iri-string" -version = "0.7.9" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f867b9d1d896b67beb18518eda36fdb77a32ea590de864f1325b294a6d14397" +checksum = "c91338f0783edbd6195decb37bae672fd3b165faffb89bf7b9e6942f8b1a731a" dependencies = [ "memchr", "serde", @@ -2606,9 +2589,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.15" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" +checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" [[package]] name = "jobserver" @@ -2632,17 +2615,16 @@ dependencies = [ [[package]] name = "jsonwebkey" -version = "0.3.5" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c57c852b14147e2bd58c14fde40398864453403ef632b1101db130282ee6e2cc" +checksum = "ba781c43eb46c3bbf5bfda541139eed9a52b78d7c3aa74d516918885ecd63c40" dependencies = [ - "base64 0.13.1", - "bitflags 1.3.2", - "generic-array", + "base64 0.22.1", + "bitflags 2.10.0", "num-bigint", "serde", "serde_json", - "thiserror 1.0.69", + "thiserror 2.0.17", "yasna", "zeroize", ] @@ -2696,9 +2678,9 @@ dependencies = [ [[package]] name = "lber" -version = "0.4.2" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2df7f9fd9f64cf8f59e1a4a0753fe7d575a5b38d3d7ac5758dcee9357d83ef0a" +checksum = "cbcf559624bfd9fe8d488329a8959766335a43a9b8b2cdd6a2c379fca02909a5" dependencies = [ "bytes", "nom 7.1.3", @@ -2706,21 +2688,20 @@ dependencies = [ [[package]] name = "ldap3" -version = "0.11.5" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "166199a8207874a275144c8a94ff6eed5fcbf5c52303e4d9b4d53a0c7ac76554" +checksum = "01fe89f5e7cfb7e4701e3a38ff9f00358e026a9aee940355d88ee9d81e5c7503" dependencies = [ "async-trait", "bytes", "futures", "futures-util", - "lazy_static", "lber", "log", "native-tls", "nom 7.1.3", "percent-encoding", - "thiserror 1.0.69", + "thiserror 2.0.17", "tokio", "tokio-native-tls", "tokio-stream", @@ -2782,13 +2763,13 @@ checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" [[package]] name = "libredox" -version = "0.1.11" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df15f6eac291ed1cf25865b1ee60399f57e7c227e7f51bdbd4c5270396a9ed50" +checksum = "3d0b95e02c851351f877147b7deea7b1afb1df71b63aa5f8270716e0c5720616" dependencies = [ "bitflags 2.10.0", "libc", - "redox_syscall 0.6.0", + "redox_syscall 0.7.0", ] [[package]] @@ -2803,9 +2784,9 @@ dependencies = [ [[package]] name = "libz-rs-sys" -version = "0.5.4" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15413ef615ad868d4d65dce091cb233b229419c7c0c4bcaa746c0901c49ff39c" +checksum = "c10501e7805cee23da17c7790e59df2870c0d4043ec6d03f67d31e2b53e77415" dependencies = [ "zlib-rs", ] @@ -3007,7 +2988,7 @@ dependencies = [ "libc", "log", "openssl", - "openssl-probe", + "openssl-probe 0.1.6", "openssl-sys", "schannel", "security-framework 2.11.1", @@ -3462,6 +3443,12 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" +[[package]] +name = "openssl-probe" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f50d9b3dabb09ecd771ad0aa242ca6894994c130308ca3d7684634df8037391" + [[package]] name = "openssl-sys" version = "0.9.111" @@ -3725,7 +3712,7 @@ dependencies = [ "curve25519-dalek", "cx448", "derive_builder", - "derive_more 2.1.0", + "derive_more 2.1.1", "des", "digest", "dsa", @@ -3948,9 +3935,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.103" +version = "1.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" +checksum = "9695f8df41bb4f3d222c95a67532365f569318332d03d5f3f67f37b20e6ebdf0" dependencies = [ "unicode-ident", ] @@ -4215,9 +4202,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.6.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec96166dafa0886eb81fe1c0a388bece180fbef2135f97c1e2cf8302e74b43b5" +checksum = "49f3fe0889e69e2ae9e41f4d6c4c0181701d00e4697b356fb1f74173a5e0ee27" dependencies = [ "bitflags 2.10.0", ] @@ -4273,9 +4260,9 @@ checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" [[package]] name = "reqwest" -version = "0.12.26" +version = "0.12.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b4c14b2d9afca6a60277086b0cc6a6ae0b568f6f7916c943a8cdc79f8be240f" +checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" dependencies = [ "base64 0.22.1", "bytes", @@ -4446,9 +4433,9 @@ dependencies = [ [[package]] name = "rustix" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" +checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34" dependencies = [ "bitflags 2.10.0", "errno", @@ -4474,11 +4461,11 @@ dependencies = [ [[package]] name = "rustls-native-certs" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9980d917ebb0c0536119ba501e90834767bffc3d60641457fd84a1f3fd337923" +checksum = "612460d5f7bea540c490b2b6395d8e34a953e52b491accd6c86c8164c5932a63" dependencies = [ - "openssl-probe", + "openssl-probe 0.2.0", "rustls-pki-types", "schannel", "security-framework 3.5.1", @@ -4513,9 +4500,9 @@ checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] name = "ryu" -version = "1.0.20" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" +checksum = "a50f4cf475b65d88e057964e0e9bb1f0aa9bbb2036dc65c64596b42932536984" [[package]] name = "same-file" @@ -4549,9 +4536,9 @@ dependencies = [ [[package]] name = "schemars" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9558e172d4e8533736ba97870c4b2cd63f84b382a3d6eb063da41b91cce17289" +checksum = "54e910108742c57a770f492731f99be216a52fadd361b06c8fb59d74ccc267d2" dependencies = [ "dyn-clone", "ref-cast", @@ -4666,23 +4653,13 @@ dependencies = [ "serde_core", ] -[[package]] -name = "serde_cbor_2" -version = "0.12.0-dev" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b46d75f449e01f1eddbe9b00f432d616fbbd899b809c837d0fbc380496a0dd55" -dependencies = [ - "half 1.8.3", - "serde", -] - [[package]] name = "serde_cbor_2" version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34aec2709de9078e077090abd848e967abab63c9fb3fdb5d4799ad359d8d482c" dependencies = [ - "half 2.7.1", + "half", "serde", ] @@ -4721,15 +4698,15 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.145" +version = "1.0.148" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" +checksum = "3084b546a1dd6289475996f182a22aba973866ea8e8b02c51d9f46b1336a22da" dependencies = [ "itoa", "memchr", - "ryu", "serde", "serde_core", + "zmij", ] [[package]] @@ -4787,7 +4764,7 @@ dependencies = [ "indexmap 1.9.3", "indexmap 2.12.1", "schemars 0.9.0", - "schemars 1.1.0", + "schemars 1.2.0", "serde_core", "serde_json", "serde_with_macros", @@ -5380,9 +5357,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.111" +version = "2.0.112" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87" +checksum = "21f182278bf2d2bcb3c88b1b08a37df029d71ce3d3ae26168e3c653b213b99d4" dependencies = [ "proc-macro2", "quote", @@ -5438,9 +5415,9 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "tempfile" -version = "3.23.0" +version = "3.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16" +checksum = "655da9c7eb6305c55742045d5a8d2037996d61d8de95806335c7c86ce0f82e9c" dependencies = [ "fastrand", "getrandom 0.3.4", @@ -6368,7 +6345,7 @@ dependencies = [ "openssl-sys", "serde", "serde_bytes", - "serde_cbor_2 0.13.0", + "serde_cbor_2", "serde_json", "thiserror 1.0.69", "tokio", @@ -6411,7 +6388,7 @@ dependencies = [ "rand 0.9.2", "rand_chacha 0.9.0", "serde", - "serde_cbor_2 0.13.0", + "serde_cbor_2", "serde_json", "thiserror 1.0.69", "tracing", @@ -6825,9 +6802,9 @@ dependencies = [ [[package]] name = "yasna" -version = "0.4.0" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e262a29d0e61ccf2b6190d7050d4b237535fc76ce4c1210d9caa316f71dffa75" +checksum = "e17bb3549cc1321ae1296b9cdc2698e2b6cb1992adfa19a8c72e5b7a738f44cd" dependencies = [ "num-bigint", ] @@ -6907,9 +6884,9 @@ dependencies = [ [[package]] name = "zeroize_derive" -version = "1.4.2" +version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +checksum = "85a5b4158499876c763cb03bc4e49185d3cccbabb15b33c627f7884f43db852e" dependencies = [ "proc-macro2", "quote", @@ -6965,9 +6942,15 @@ dependencies = [ [[package]] name = "zlib-rs" -version = "0.5.4" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40990edd51aae2c2b6907af74ffb635029d5788228222c4bb811e9351c0caad3" + +[[package]] +name = "zmij" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51f936044d677be1a1168fae1d03b583a285a5dd9d8cbf7b24c23aa1fc775235" +checksum = "de9211a9f64b825911bdf0240f58b7a8dac217fe260fc61f080a07f61372fbd5" [[package]] name = "zopfli" diff --git a/Cargo.toml b/Cargo.toml index cdbbb13016..1774605390 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,7 +28,7 @@ anyhow = "1.0" argon2 = { version = "0.5", features = ["std"] } axum = "0.8" axum-client-ip = "0.7" -axum-extra = { version = "0.10", features = [ +axum-extra = { version = "0.12", features = [ "cookie-private", "typed-header", "query", @@ -45,9 +45,9 @@ clap = { version = "4.5", features = ["derive", "env"] } humantime = "2.1" # match version used by sqlx ipnetwork = "0.20" -jsonwebkey = { version = "0.3", features = ["pkcs-convert"] } +jsonwebkey = { version = "0.4", features = ["pkcs-convert"] } jsonwebtoken = "9.3" -ldap3 = { version = "0.11", default-features = false, features = ["tls"] } +ldap3 = { version = "0.12", default-features = false, features = ["tls"] } lettre = { version = "0.11", features = ["tokio1-native-tls"] } matches = "0.1" md4 = "0.10" @@ -68,7 +68,7 @@ semver = { version = "1.0", features = ["serde"] } secrecy = { version = "0.10", features = ["serde"] } serde = { version = "1.0", features = ["derive"] } # match version from webauthn-rs-core -serde_cbor = { version = "0.12.0-dev", package = "serde_cbor_2" } +serde_cbor = { version = "0.13", package = "serde_cbor_2" } serde_json = "1.0" serde_urlencoded = "0.7" sha-1 = "0.10" @@ -110,12 +110,11 @@ tower-http = { version = "0.6", features = ["fs", "trace", "set-header"] } tracing = "0.1" tracing-subscriber = { version = "0.3", features = ["env-filter"] } trait-variant = "0.1" -uaparser = "0.6" # openapi utoipa = { version = "5", features = ["axum_extras", "chrono", "uuid"] } utoipa-swagger-ui = { version = "9", features = ["axum", "vendored"] } uuid = { version = "1.9", features = ["v4"] } -webauthn-authenticator-rs = { version = "0.5" } +webauthn-authenticator-rs = "0.5" webauthn-rs = { version = "0.5", features = [ "danger-allow-state-serialisation", ] } diff --git a/crates/defguard/Cargo.toml b/crates/defguard/Cargo.toml index edc03cd2eb..c6125e672c 100644 --- a/crates/defguard/Cargo.toml +++ b/crates/defguard/Cargo.toml @@ -15,7 +15,6 @@ defguard_event_router = { workspace = true } defguard_event_logger = { workspace = true } defguard_mail = { workspace = true } defguard_proxy_manager = { workspace = true } -defguard_session_manager = { workspace = true } defguard_version = { workspace = true } # external dependencies diff --git a/crates/defguard/src/main.rs b/crates/defguard/src/main.rs index 0a7d54c499..99de576016 100644 --- a/crates/defguard/src/main.rs +++ b/crates/defguard/src/main.rs @@ -28,9 +28,7 @@ use defguard_core::{ events::{ApiEvent, BidiStreamEvent, GrpcEvent, InternalEvent}, grpc::{ WorkerState, - gateway::{ - client_state::ClientMap, events::GatewayEvent, map::GatewayMap, run_grpc_gateway_stream, - }, + gateway::{client_state::ClientMap, events::GatewayEvent, run_grpc_gateway_stream}, run_grpc_server, }, init_dev_env, init_vpn_location, run_web_server, @@ -118,7 +116,6 @@ async fn main() -> Result<(), anyhow::Error> { // let (peer_stats_tx, peer_stats_rx) = unbounded_channel::(); let worker_state = Arc::new(Mutex::new(WorkerState::new(webhook_tx.clone()))); - let gateway_state = Arc::new(Mutex::new(GatewayMap::new())); let client_state = Arc::new(Mutex::new(ClientMap::new())); let incompatible_components: Arc> = Arc::default(); @@ -184,7 +181,6 @@ async fn main() -> Result<(), anyhow::Error> { ) => error!("gRPC server returned early: {res:?}"), res = run_web_server( worker_state, - gateway_state, webhook_tx, webhook_rx, wireguard_tx.clone(), diff --git a/crates/defguard_common/src/csv.rs b/crates/defguard_common/src/csv.rs index 1695bafaba..613ca3ee16 100644 --- a/crates/defguard_common/src/csv.rs +++ b/crates/defguard_common/src/csv.rs @@ -4,7 +4,7 @@ pub trait AsCsv { impl AsCsv for I where - I: ?Sized + std::iter::IntoIterator, + I: ?Sized + IntoIterator, for<'a> &'a I: IntoIterator, T: ToString, { diff --git a/crates/defguard_common/src/db/models/device.rs b/crates/defguard_common/src/db/models/device.rs index abd3b17d8b..c6ae162cf7 100644 --- a/crates/defguard_common/src/db/models/device.rs +++ b/crates/defguard_common/src/db/models/device.rs @@ -560,14 +560,8 @@ impl Device { wireguard_network_device: &WireguardNetworkDevice, ) -> String { let dns = match &network.dns { - Some(dns) => { - if dns.is_empty() { - String::new() - } else { - format!("DNS = {dns}") - } - } - None => String::new(), + Some(dns) if !dns.is_empty() => format!("DNS = {dns}"), + _ => String::new(), }; let allowed_ips = if network.allowed_ips.is_empty() { diff --git a/crates/defguard_core/src/db/models/gateway.rs b/crates/defguard_common/src/db/models/gateway.rs similarity index 74% rename from crates/defguard_core/src/db/models/gateway.rs rename to crates/defguard_common/src/db/models/gateway.rs index 5d6221f6b4..bab5cff05f 100644 --- a/crates/defguard_core/src/db/models/gateway.rs +++ b/crates/defguard_common/src/db/models/gateway.rs @@ -2,12 +2,13 @@ use std::fmt; use chrono::{NaiveDateTime, Utc}; use model_derive::Model; +use serde::Deserialize; use sqlx::{PgExecutor, query, query_as}; -use defguard_common::db::{Id, NoId}; +use crate::db::{Id, NoId}; -#[derive(Clone, Debug, Deserialize, Model, PartialEq, Serialize)] -pub(crate) struct Gateway { +#[derive(Deserialize, Model)] +pub struct Gateway { pub id: I, pub network_id: Id, pub url: String, @@ -16,9 +17,21 @@ pub(crate) struct Gateway { pub disconnected_at: Option, } +impl Gateway { + pub fn is_connected(&self) -> bool { + if let (Some(connected_at), Some(disconnected_at)) = + (self.connected_at, self.disconnected_at) + { + disconnected_at <= connected_at + } else { + self.connected_at.is_some() + } + } +} + impl Gateway { #[must_use] - pub(crate) fn new>(network_id: Id, url: S) -> Self { + pub fn new>(network_id: Id, url: S) -> Self { Self { id: NoId, network_id, @@ -31,7 +44,7 @@ impl Gateway { } impl Gateway { - pub(crate) async fn find_by_network_id<'e, E>( + pub async fn find_by_network_id<'e, E>( executor: E, network_id: Id, ) -> Result, sqlx::Error> @@ -48,7 +61,7 @@ impl Gateway { } /// Update `hostname` and set `connected_at` to the current time and save it to the database. - pub(crate) async fn touch_connected<'e, E>( + pub async fn touch_connected<'e, E>( &mut self, executor: E, hostname: String, @@ -71,7 +84,7 @@ impl Gateway { } /// Set `disconnected_at` to the current time and save it to the database. - pub(crate) async fn touch_disconnected<'e, E>(&mut self, executor: E) -> Result<(), sqlx::Error> + pub async fn touch_disconnected<'e, E>(&mut self, executor: E) -> Result<(), sqlx::Error> where E: PgExecutor<'e>, { @@ -87,14 +100,19 @@ impl Gateway { Ok(()) } - pub(crate) fn is_connected(&self) -> bool { - if let (Some(connected_at), Some(disconnected_at)) = - (self.connected_at, self.disconnected_at) - { - disconnected_at <= connected_at - } else { - self.connected_at.is_some() - } + pub async fn delete_by_id<'e, E>(executor: E, id: Id, network_id: Id) -> Result<(), sqlx::Error> + where + E: PgExecutor<'e>, + { + sqlx::query!( + "DELETE FROM \"gateway\" WHERE id = $1 AND network_id = $2", + id, + network_id + ) + .execute(executor) + .await?; + + Ok(()) } } diff --git a/crates/defguard_common/src/db/models/group.rs b/crates/defguard_common/src/db/models/group.rs index 7415e2f897..8acd6cab65 100644 --- a/crates/defguard_common/src/db/models/group.rs +++ b/crates/defguard_common/src/db/models/group.rs @@ -1,11 +1,12 @@ use std::fmt; -use crate::db::{Id, NoId, models::user::User}; use model_derive::Model; use serde::Serialize; use sqlx::{Error as SqlxError, FromRow, PgExecutor, query, query_as, query_scalar}; use utoipa::ToSchema; +use crate::db::{Id, NoId, models::user::User}; + #[derive(Debug)] pub enum Permission { IsAdmin, @@ -30,8 +31,8 @@ impl Default for Group { fn default() -> Self { Self { id: NoId, - name: Default::default(), - is_admin: Default::default(), + name: String::default(), + is_admin: bool::default(), } } } diff --git a/crates/defguard_common/src/db/models/mod.rs b/crates/defguard_common/src/db/models/mod.rs index 653e62d2ab..9908ea120e 100644 --- a/crates/defguard_common/src/db/models/mod.rs +++ b/crates/defguard_common/src/db/models/mod.rs @@ -4,6 +4,7 @@ pub mod biometric_auth; pub mod device; pub mod device_login; pub mod error; +pub mod gateway; pub mod group; pub mod mfa_info; pub mod oauth2authorizedapp; diff --git a/crates/defguard_common/src/db/models/oauth2client.rs b/crates/defguard_common/src/db/models/oauth2client.rs index 37256afc74..bec7be9f44 100644 --- a/crates/defguard_common/src/db/models/oauth2client.rs +++ b/crates/defguard_common/src/db/models/oauth2client.rs @@ -1,10 +1,11 @@ +use model_derive::Model; +use serde::{Deserialize, Serialize}; +use sqlx::{Error as SqlxError, PgExecutor, PgPool, query_as}; + use crate::{ db::{Id, NoId, models::OAuth2Token}, random::gen_alphanumeric, }; -use model_derive::Model; -use serde::{Deserialize, Serialize}; -use sqlx::{Error as SqlxError, PgExecutor, PgPool, query_as}; #[derive(Clone, Debug, Deserialize, Model, Serialize, PartialEq)] pub struct OAuth2Client { @@ -105,6 +106,7 @@ impl OAuth2Client { } /// Checks if `url` matches client config (ignoring trailing slashes). + #[must_use] pub fn contains_redirect_url(&self, url: &str) -> bool { let url_trimmed = url.trim_end_matches('/'); diff --git a/crates/defguard_common/src/db/models/wireguard.rs b/crates/defguard_common/src/db/models/wireguard.rs index 82b9076097..d1aab82c32 100644 --- a/crates/defguard_common/src/db/models/wireguard.rs +++ b/crates/defguard_common/src/db/models/wireguard.rs @@ -21,19 +21,15 @@ use utoipa::ToSchema; use x25519_dalek::{PublicKey, StaticSecret}; use super::{ + ModelError, device::{Device, DeviceError, DeviceType, WireguardNetworkDevice}, + group::{Group, Permission}, user::User, + wireguard_peer_stats::WireguardPeerStats, }; use crate::{ auth::claims::{Claims, ClaimsType}, - db::{ - Id, NoId, - models::{ - ModelError, - group::{Group, Permission}, - wireguard_peer_stats::WireguardPeerStats, - }, - }, + db::{Id, NoId}, types::user_info::UserInfo, }; @@ -1081,15 +1077,6 @@ impl Default for WireguardNetwork { } } -#[derive(Serialize, ToSchema)] -pub struct WireguardNetworkInfo { - #[serde(flatten)] - pub network: WireguardNetwork, - pub connected: bool, - // pub gateways: Vec, - pub allowed_groups: Vec, -} - #[derive(Clone, Serialize, Deserialize, PartialEq)] pub struct WireguardStatsRow { pub collected_at: Option, diff --git a/crates/defguard_common/src/db/models/wireguard_peer_stats.rs b/crates/defguard_common/src/db/models/wireguard_peer_stats.rs index c0eb5a04f3..902f20028a 100644 --- a/crates/defguard_common/src/db/models/wireguard_peer_stats.rs +++ b/crates/defguard_common/src/db/models/wireguard_peer_stats.rs @@ -137,6 +137,7 @@ impl WireguardPeerStats { /// Remove port part from `endpoint`. /// IPv4: a.b.c.d:p -> a.b.c.d /// IPv6: [x::y:z]:p -> x::y:z + #[must_use] pub fn endpoint_without_port(&self) -> Option { self.endpoint.as_ref().and_then(|endpoint| { let mut addr = endpoint.rsplit_once(':')?.0; @@ -151,6 +152,7 @@ impl WireguardPeerStats { /// Returns a `Vec` of `allowed_ips` without a CIDR mask. /// Non-parsable addresses are omitted. + #[must_use] pub fn trim_allowed_ips(&self) -> Vec { let Some(allowed_ips) = &self.allowed_ips else { return Vec::new(); diff --git a/crates/defguard_common/src/utils.rs b/crates/defguard_common/src/utils.rs index 5dc46c1911..a80460de0b 100644 --- a/crates/defguard_common/src/utils.rs +++ b/crates/defguard_common/src/utils.rs @@ -2,6 +2,7 @@ use ipnetwork::IpNetwork; /// Parse a string with comma-separated IP addresses. /// Invalid addresses will be silently ignored. +#[must_use] pub fn parse_address_list(ips: &str) -> Vec { ips.split(',') .filter_map(|ip| ip.trim().parse().ok()) @@ -11,6 +12,7 @@ pub fn parse_address_list(ips: &str) -> Vec { /// Parse a string with comma-separated IP network addresses. /// Host bits will be stripped. /// Invalid addresses will be silently ignored. +#[must_use] pub fn parse_network_address_list(ips: &str) -> Vec { ips.split(',') .filter_map(|ip| ip.trim().parse().ok()) diff --git a/crates/defguard_core/Cargo.toml b/crates/defguard_core/Cargo.toml index 2d57b13188..31c179484f 100644 --- a/crates/defguard_core/Cargo.toml +++ b/crates/defguard_core/Cargo.toml @@ -18,12 +18,12 @@ model_derive = { workspace = true } # external dependencies anyhow = { workspace = true } -argon2 = { workspace = true } axum = { workspace = true } axum-client-ip = { workspace = true } axum-extra = { workspace = true } base32 = { workspace = true } base64 = { workspace = true } +bytes = { workspace = true } chrono = { workspace = true } humantime = { workspace = true } # match version used by sqlx @@ -46,8 +46,6 @@ rust-ini = { workspace = true } secrecy = { workspace = true } semver = { workspace = true } serde = { workspace = true } -# match version from webauthn-rs-core -serde_cbor = { workspace = true } serde_json = { workspace = true } serde_urlencoded = { workspace = true } sha-1 = { workspace = true } @@ -55,6 +53,8 @@ sha256 = { workspace = true } sqlx = { workspace = true } ssh-key = { workspace = true } struct-patch = { workspace = true } +strum = { workspace = true } +strum_macros = { workspace = true } tera = { workspace = true } thiserror = { workspace = true } # match axum-extra -> cookies @@ -69,7 +69,6 @@ totp-lite = { workspace = true } tower-http = { workspace = true } tracing = { workspace = true } trait-variant = { workspace = true } -uaparser = { workspace = true } # openapi utoipa = { workspace = true } utoipa-swagger-ui = { workspace = true } @@ -78,19 +77,15 @@ webauthn-authenticator-rs = { workspace = true } webauthn-rs = { workspace = true } webauthn-rs-proto = { workspace = true } x25519-dalek = { workspace = true } -strum = { workspace = true } -strum_macros = { workspace = true } -bytes = { workspace = true } -tower = "0.5" +ammonia = "4.1" regex = "1.10" -ammonia = "4.1.1" +tower = "0.5" +uaparser = "0.6" [dev-dependencies] -bytes = "1.6" claims.workspace = true hyper-util = "0.1" matches.workspace = true -regex = "1.10" reqwest = { version = "0.12", features = [ "cookies", "json", @@ -99,7 +94,6 @@ reqwest = { version = "0.12", features = [ "stream", ], default-features = false } serde_qs = "0.15" -tower = "0.5" webauthn-authenticator-rs = { version = "0.5", features = ["softpasskey"] } [build-dependencies] diff --git a/crates/defguard_core/src/db/models/mod.rs b/crates/defguard_core/src/db/models/mod.rs index 1f23833749..b6efe1b578 100644 --- a/crates/defguard_core/src/db/models/mod.rs +++ b/crates/defguard_core/src/db/models/mod.rs @@ -1,4 +1,3 @@ pub mod activity_log; pub mod enrollment; -pub mod gateway; pub mod webhook; diff --git a/crates/defguard_core/src/enterprise/activity_log_stream/http_stream.rs b/crates/defguard_core/src/enterprise/activity_log_stream/http_stream.rs index 64593c021a..491d70d9c3 100644 --- a/crates/defguard_core/src/enterprise/activity_log_stream/http_stream.rs +++ b/crates/defguard_core/src/enterprise/activity_log_stream/http_stream.rs @@ -51,20 +51,23 @@ pub(super) async fn run_http_stream_task( let body: String = { let text = &response.text().await; match text { - Ok(body) => body.to_string(), + Ok(body) => body.clone(), Err(_) => "Body decoding failed".to_string(), } }; - error!("Activity log stream ({stream_name}) response code {0}. Body: {1}", status_code, body); + error!("Activity log stream ({stream_name}) response code \ + {status_code}. Body: {body}"); } }, Err(e) => { - error!("Activity log stream {stream_name} failed to send messages. Reason: {e}"); + error!("Activity log stream {stream_name} failed to send messages. \ + Reason: {e}"); } } }, Err(e) => { - error!("Receiving activity log stream message failed ! Reason: {}", e.to_string()); + error!("Receiving activity log stream message failed! Reason: {}", + e.to_string()); break; } } diff --git a/crates/defguard_core/src/enterprise/db/models/acl.rs b/crates/defguard_core/src/enterprise/db/models/acl.rs index 605f147828..f6fbe70ede 100644 --- a/crates/defguard_core/src/enterprise/db/models/acl.rs +++ b/crates/defguard_core/src/enterprise/db/models/acl.rs @@ -561,7 +561,7 @@ pub fn parse_ports(ports: &str) -> Result, AclError> { } _ => { error!("Failed to parse ports string: \"{ports}\""); - return Err(AclError::InvalidPortsFormat(ports.to_string())); + return Err(AclError::InvalidPortsFormat(ports.clone())); } } } diff --git a/crates/defguard_core/src/enterprise/directory_sync/mod.rs b/crates/defguard_core/src/enterprise/directory_sync/mod.rs index 236db1b2c8..ad3fec42a7 100644 --- a/crates/defguard_core/src/enterprise/directory_sync/mod.rs +++ b/crates/defguard_core/src/enterprise/directory_sync/mod.rs @@ -713,7 +713,7 @@ async fn sync_all_users_state( directory_user.email.clone(), details.phone_number.clone(), ); - user.openid_sub = directory_user.id.clone(); + user.openid_sub.clone_from(&directory_user.id); let new_user = user.save(&mut *transaction).await?; created_users.push(new_user); } diff --git a/crates/defguard_core/src/enterprise/handlers/openid_login.rs b/crates/defguard_core/src/enterprise/handlers/openid_login.rs index 855e52695d..ff370b093e 100644 --- a/crates/defguard_core/src/enterprise/handlers/openid_login.rs +++ b/crates/defguard_core/src/enterprise/handlers/openid_login.rs @@ -173,8 +173,8 @@ pub async fn make_oidc_client( WebError, > { let provider_metadata = get_provider_metadata(&provider.base_url).await?; - let client_id = ClientId::new(provider.client_id.to_string()); - let client_secret = ClientSecret::new(provider.client_secret.to_string()); + let client_id = ClientId::new(provider.client_id.clone()); + let client_secret = ClientSecret::new(provider.client_secret.clone()); let core_client = CoreClient::from_provider_metadata( provider_metadata, client_id.clone(), @@ -442,7 +442,7 @@ pub async fn user_from_claims( }; let mut user = User::new( - username.to_string(), + username.clone(), None, family_name.to_string(), given_name.to_string(), diff --git a/crates/defguard_core/src/enterprise/mod.rs b/crates/defguard_core/src/enterprise/mod.rs index 7cc7699b1c..485ebe83d2 100644 --- a/crates/defguard_core/src/enterprise/mod.rs +++ b/crates/defguard_core/src/enterprise/mod.rs @@ -16,11 +16,13 @@ use limits::get_counts; use crate::enterprise::license::LicenseTier; /// Helper function to gate features which require a base license (Team or Business tier) +#[must_use] pub fn is_business_license_active() -> bool { is_license_tier_active(LicenseTier::Business) } /// Helper function to gate features which require an Enterprise tier license +#[must_use] pub fn is_enterprise_license_active() -> bool { is_license_tier_active(LicenseTier::Enterprise) } diff --git a/crates/defguard_core/src/error.rs b/crates/defguard_core/src/error.rs index 84aadb2ab3..d0dd38abf5 100644 --- a/crates/defguard_core/src/error.rs +++ b/crates/defguard_core/src/error.rs @@ -4,7 +4,6 @@ use defguard_common::db::models::{ user::UserError, }; use defguard_mail::templates::TemplateError; -use sqlx::error::Error as SqlxError; use thiserror::Error; use tokio::sync::mpsc::error::SendError; use utoipa::ToSchema; @@ -14,10 +13,9 @@ use crate::{ db::models::enrollment::TokenError, enterprise::{ activity_log_stream::error::ActivityLogStreamError, db::models::acl::AclError, - firewall::FirewallError, ldap::error::LdapError, license::LicenseError, + firewall::FirewallError, license::LicenseError, }, events::ApiEvent, - grpc::gateway::map::GatewayMapError, location_management::LocationManagementError, }; @@ -26,22 +24,16 @@ use crate::{ pub enum WebError { #[error("GRPC error: {0}")] Grpc(String), - #[error("LDAP error: {0}")] - Ldap(String), #[error("Webauthn registration error: {0}")] WebauthnRegistration(String), #[error("Email MFA error: {0}")] EmailMfa(String), - #[error("Incorrect username: {0}")] - IncorrectUsername(String), #[error("Object not found: {0}")] ObjectNotFound(String), #[error("Object already exists: {0}")] ObjectAlreadyExists(String), #[error("Serialization error: {0}")] Serialization(String), - #[error("Deserialization error: {0}")] - Deserialization(String), #[error("Authorization error: {0}")] Authorization(String), #[error("Authentication error")] @@ -67,8 +59,6 @@ pub enum WebError { #[error(transparent)] #[schema(value_type=Object)] TemplateError(#[from] TemplateError), - #[error("Server config missing")] - ServerConfigMissing, #[error("License error: {0}")] #[schema(value_type=Object)] LicenseError(#[from] LicenseError), @@ -100,14 +90,8 @@ impl From for WebError { } } -impl From for WebError { - fn from(error: LdapError) -> Self { - Self::Ldap(error.to_string()) - } -} - -impl From for WebError { - fn from(error: SqlxError) -> Self { +impl From for WebError { + fn from(error: sqlx::Error) -> Self { Self::DbError(error.to_string()) } } @@ -129,19 +113,6 @@ impl From for WebError { } } -impl From for WebError { - fn from(error: GatewayMapError) -> Self { - match error { - GatewayMapError::NotFound(_, _) - | GatewayMapError::NetworkNotFound(_) - | GatewayMapError::UidNotFound(_) => Self::ObjectNotFound(error.to_string()), - GatewayMapError::RemoveActive(_) => Self::BadRequest(error.to_string()), - GatewayMapError::ConfigError => Self::ServerConfigMissing, - GatewayMapError::SettingsError => Self::DbError(error.to_string()), - } - } -} - impl From for WebError { fn from(error: WireguardNetworkError) -> Self { match error { @@ -160,7 +131,7 @@ impl From for WebError { impl From for WebError { fn from(err: TokenError) -> Self { - error!("{}", err); + error!("{err}"); match err { TokenError::DbError(msg) => WebError::DbError(msg.to_string()), TokenError::NotFound | TokenError::UserNotFound | TokenError::AdminNotFound => { @@ -194,7 +165,7 @@ impl From for WebError { impl From for WebError { fn from(err: UserError) -> Self { - error!("{}", err); + error!("{err}"); match err { UserError::InvalidMfaState { username: _ } | UserError::DbError(_) => { WebError::Http(StatusCode::INTERNAL_SERVER_ERROR) @@ -206,7 +177,7 @@ impl From for WebError { impl From for WebError { fn from(err: LocationManagementError) -> Self { - error!("{}", err); + error!("{err}"); match err { LocationManagementError::FirewallError(firewall_error) => firewall_error.into(), LocationManagementError::DbError(error) => error.into(), diff --git a/crates/defguard_core/src/grpc/gateway/handler.rs b/crates/defguard_core/src/grpc/gateway/handler.rs index 42921ff880..51995484f5 100644 --- a/crates/defguard_core/src/grpc/gateway/handler.rs +++ b/crates/defguard_core/src/grpc/gateway/handler.rs @@ -13,7 +13,10 @@ use defguard_common::{ auth::claims::Claims, db::{ Id, NoId, - models::{Device, User, WireguardNetwork, wireguard_peer_stats::WireguardPeerStats}, + models::{ + Device, User, WireguardNetwork, gateway::Gateway, + wireguard_peer_stats::WireguardPeerStats, + }, }, }; use defguard_mail::Mail; @@ -33,13 +36,11 @@ use tokio::{ use tokio_stream::wrappers::UnboundedReceiverStream; use tonic::{ Code, Status, - metadata::MetadataMap, transport::{ClientTlsConfig, Endpoint}, }; use crate::{ ClaimsType, - db::models::gateway::Gateway, enterprise::firewall::try_get_location_firewall_config, grpc::{ ClientMap, GrpcEvent, TEN_SECS, @@ -112,32 +113,6 @@ impl GatewayHandler { }) } - // Parse network ID from Gateway request metadata from intercepted information from JWT token. - fn get_network_id_from_metadata(metadata: &MetadataMap) -> Option { - if let Some(ascii_value) = metadata.get("gateway_network_id") { - if let Ok(slice) = ascii_value.clone().to_str() { - if let Ok(id) = slice.parse::() { - return Some(id); - } - } - } - None - } - - // Extract Gateway hostname from request headers. - fn get_gateway_hostname(metadata: &MetadataMap) -> Option { - if let Some(ascii_value) = metadata.get("hostname") { - let Ok(hostname) = ascii_value.to_str() else { - error!("Failed to parse Gateway hostname from request metadata"); - return None; - }; - Some(hostname.into()) - } else { - error!("Gateway hostname not found in request metadata"); - None - } - } - /// Send network and VPN configuration to Gateway. async fn send_configuration( &self, diff --git a/crates/defguard_core/src/grpc/gateway/map.rs b/crates/defguard_core/src/grpc/gateway/map.rs deleted file mode 100644 index 60b5ab49d0..0000000000 --- a/crates/defguard_core/src/grpc/gateway/map.rs +++ /dev/null @@ -1,222 +0,0 @@ -use std::collections::HashMap; - -use chrono::Utc; -use defguard_common::db::Id; -use defguard_mail::Mail; -use defguard_version::tracing::VersionInfo; -use semver::Version; -use sqlx::PgPool; -use thiserror::Error; -use tokio::sync::mpsc::UnboundedSender; -use uuid::Uuid; - -use super::state::GatewayState; - -/// Helper struct used to handle gateway state. Gateways are grouped by network. -type GatewayHostname = String; -#[derive(Debug, Serialize)] -pub struct GatewayMap(HashMap>); - -#[derive(Debug, Error)] -pub enum GatewayMapError { - #[error("Gateway {1} for network {0} not found")] - NotFound(i64, GatewayHostname), - #[error("Network {0} not found")] - NetworkNotFound(i64), - #[error("Gateway with UID {0} not found")] - UidNotFound(Uuid), - #[error("Cannot remove. Gateway with UID {0} is still active")] - RemoveActive(Uuid), - #[error("Config missing")] - ConfigError, - #[error("Failed to get current settings")] - SettingsError, -} - -impl GatewayMap { - #[must_use] - pub fn new() -> Self { - Self(HashMap::new()) - } - - #[must_use] - pub fn is_empty(&self) -> bool { - self.0.is_empty() - } - - /// Add a new gateway to the map. - /// This is meant to be called when Gateway requests a config as a sort of "registration". - pub(crate) fn add_gateway( - &mut self, - network_id: Id, - network_name: &str, - hostname: String, - name: Option, - mail_tx: UnboundedSender, - version: Version, - ) { - info!("Adding gateway {hostname} with to gateway map for network {network_id}",); - let gateway_state = - GatewayState::new(network_id, network_name, &hostname, name, mail_tx, version); - - if let Some(network_gateway_map) = self.0.get_mut(&network_id) { - network_gateway_map.entry(hostname).or_insert(gateway_state); - } else { - // no map for a given network exists yet - let mut network_gateway_map = HashMap::new(); - network_gateway_map.insert(hostname, gateway_state); - self.0.insert(network_id, network_gateway_map); - } - } - - /// Remove gateway from the map. - pub(crate) fn remove_gateway( - &mut self, - network_id: Id, - uid: Uuid, - ) -> Result<(), GatewayMapError> { - debug!("Removing gateway from network {network_id}"); - if let Some(network_gateway_map) = self.0.get_mut(&network_id) { - // find gateway by uuid - let hostname = match network_gateway_map - .iter() - .find(|(_address, state)| state.uid == uid) - { - None => { - error!("Failed to find gateway with UID {uid}"); - return Err(GatewayMapError::UidNotFound(uid)); - } - Some((hostname, state)) => { - if state.connected { - error!("Cannot remove. Gateway with UID {uid} is still active"); - return Err(GatewayMapError::RemoveActive(uid)); - } - hostname.clone() - } - }; - // remove matching gateway - network_gateway_map.remove(&hostname) - } else { - // no map for a given network exists yet - error!("Network {network_id} not found in gateway map"); - return Err(GatewayMapError::NetworkNotFound(network_id)); - }; - info!("Gateway with UID {uid} removed from network {network_id}"); - Ok(()) - } - - /// Change gateway status to connected. - /// Assume that the gateway is already present in the map. - pub(crate) fn connect_gateway( - &mut self, - network_id: Id, - hostname: &str, - pool: &PgPool, - ) -> Result<(), GatewayMapError> { - debug!("Connecting gateway {hostname} in network {network_id}"); - if let Some(network_gateway_map) = self.0.get_mut(&network_id) { - if let Some(state) = network_gateway_map.get_mut(hostname) { - // check if a gateway is reconnecting to avoid sending notifications on initial - // connection - let is_reconnecting = state.disconnected_at.is_some(); - state.connected = true; - state.disconnected_at = None; - state.connected_at = Some(Utc::now().naive_utc()); - state.cancel_pending_disconnect_notification(); - if is_reconnecting { - state.handle_reconnect_notification(pool); - } - debug!( - "Gateway {hostname} found in gateway map, current state: {:?}", - state - ); - } else { - error!("Gateway {hostname} not found in gateway map for network {network_id}"); - return Err(GatewayMapError::NotFound(network_id, hostname.into())); - } - } else { - // no map for a given network exists yet - error!("Network {network_id} not found in gateway map"); - return Err(GatewayMapError::NetworkNotFound(network_id)); - } - info!("Gateway {hostname} connected in network {network_id}"); - Ok(()) - } - - /// Change gateway status to disconnected. - pub(crate) fn disconnect_gateway( - &mut self, - network_id: Id, - hostname: String, - pool: &PgPool, - ) -> Result<(), GatewayMapError> { - debug!("Disconnecting gateway {hostname} in network {network_id}"); - if let Some(network_gateway_map) = self.0.get_mut(&network_id) { - if let Some(state) = network_gateway_map.get_mut(&hostname) { - state.connected = false; - state.disconnected_at = Some(Utc::now().naive_utc()); - state.handle_disconnect_notification(pool); - debug!("Gateway {hostname} found in gateway map, current state: {state:?}"); - info!("Gateway {hostname} disconnected in network {network_id}"); - return Ok(()); - } - } - let err = GatewayMapError::NotFound(network_id, hostname); - error!("Gateway disconnect failed: {err}"); - Err(err) - } - - /// Return `true` if at least one gateway in a given network is connected. - #[must_use] - pub(crate) fn connected(&self, network_id: Id) -> bool { - match self.0.get(&network_id) { - Some(network_gateway_map) => network_gateway_map - .values() - .any(|gateway| gateway.connected), - None => false, - } - } - - /// Return a list of all statuses of all gateways for a given network. - #[must_use] - pub fn get_network_gateway_status(&self, network_id: Id) -> Vec { - if let Some(network_gateway_map) = self.0.get(&network_id) { - network_gateway_map.values().cloned().collect() - } else { - Vec::new() - } - } - - /// Flattens the inner `HashMap` into `Vec`. - /// - /// Since key information in inner HashMap is within `GatewayState` it's simpler to consume it - /// as `Vec` on web. - /// - /// # Returns - /// `HashMap>` from `GatewayMap` - #[must_use] - pub(crate) fn as_flattened(&self) -> HashMap> { - self.0 - .iter() - .map(|(id, inner_map)| { - let states: Vec = inner_map.values().cloned().collect(); - (*id, states) - }) - .collect() - } - - #[allow(dead_code)] - #[must_use] - pub(crate) fn all_states_as_version_info(&self) -> Vec { - self.0 - .values() - .flat_map(|inner_map| inner_map.values().map(GatewayState::as_version_info)) - .collect() - } -} - -impl Default for GatewayMap { - fn default() -> Self { - Self::new() - } -} diff --git a/crates/defguard_core/src/grpc/gateway/mod.rs b/crates/defguard_core/src/grpc/gateway/mod.rs index 4aaff653be..80356acd1e 100644 --- a/crates/defguard_core/src/grpc/gateway/mod.rs +++ b/crates/defguard_core/src/grpc/gateway/mod.rs @@ -1,28 +1,21 @@ use std::{ collections::HashMap, net::IpAddr, - sync::{Arc, Mutex, mpsc::Receiver}, - thread::JoinHandle, + sync::{Arc, Mutex}, }; -use chrono::{DateTime, Utc}; use defguard_common::{ config::server_config, db::{ - ChangeNotification, Id, NoId, TriggerOperation, - models::{ - Device, User, WireguardNetwork, wireguard::ServiceLocationMode, - wireguard_peer_stats::WireguardPeerStats, - }, + ChangeNotification, Id, TriggerOperation, + models::{WireguardNetwork, gateway::Gateway, wireguard::ServiceLocationMode}, }, }; use defguard_mail::Mail; use defguard_proto::{ enterprise::firewall::FirewallConfig, - gateway::{Configuration, CoreResponse, Peer, PeerStats, Update, core_response, update}, + gateway::{Configuration, CoreResponse, Peer, Update, core_response, update}, }; -use defguard_version::version_info_from_metadata; -use semver::Version; use sqlx::{PgExecutor, PgPool, postgres::PgListener, query}; use thiserror::Error; use tokio::{ @@ -32,24 +25,19 @@ use tokio::{ }, task::{AbortHandle, JoinSet}, }; -use tonic::{Code, Status, metadata::MetadataMap}; +use tonic::{Code, Status}; use crate::{ - db::models::gateway::Gateway, enterprise::is_enterprise_license_active, events::{GrpcEvent, GrpcRequestContext}, - grpc::gateway::{ - client_state::ClientMap, events::GatewayEvent, handler::GatewayHandler, map::GatewayMap, - }, + grpc::gateway::{client_state::ClientMap, events::GatewayEvent, handler::GatewayHandler}, }; pub mod client_state; pub mod events; pub(crate) mod handler; -pub mod map; -pub(crate) mod state; -#[cfg(test)] -mod tests; +// #[cfg(test)] +// mod tests; #[cfg(test)] pub(super) static TONIC_SOCKET: &str = "tonic.sock"; @@ -64,7 +52,7 @@ pub fn send_wireguard_event(event: GatewayEvent, wg_tx: &Sender) { } } -/// Sends multiple events to be handled by gateway GRPC server +/// Sends multiple events to be handled by gateway gRPC server. /// /// If you want to use it inside the API context, use [`crate::AppState::send_multiple_wireguard_events`] instead pub fn send_multiple_wireguard_events(events: Vec, wg_tx: &Sender) { @@ -74,31 +62,31 @@ pub fn send_multiple_wireguard_events(events: Vec, wg_tx: &Sender< } } -/// Helper used to convert peer stats coming from gRPC client -/// into an internal representation -fn protos_into_internal_stats( - proto_stats: PeerStats, - location_id: Id, - device_id: Id, -) -> WireguardPeerStats { - let endpoint = match proto_stats.endpoint { - endpoint if endpoint.is_empty() => None, - _ => Some(proto_stats.endpoint), - }; - WireguardPeerStats { - id: NoId, - network: location_id, - endpoint, - device_id, - collected_at: Utc::now().naive_utc(), - upload: proto_stats.upload as i64, - download: proto_stats.download as i64, - latest_handshake: DateTime::from_timestamp(proto_stats.latest_handshake as i64, 0) - .unwrap_or_default() - .naive_utc(), - allowed_ips: Some(proto_stats.allowed_ips), - } -} +// Helper used to convert peer stats coming from gRPC client +// into an internal representation +// fn protos_into_internal_stats( +// proto_stats: PeerStats, +// location_id: Id, +// device_id: Id, +// ) -> WireguardPeerStats { +// let endpoint = match proto_stats.endpoint { +// endpoint if endpoint.is_empty() => None, +// _ => Some(proto_stats.endpoint), +// }; +// WireguardPeerStats { +// id: NoId, +// network: location_id, +// endpoint, +// device_id, +// collected_at: Utc::now().naive_utc(), +// upload: proto_stats.upload as i64, +// download: proto_stats.download as i64, +// latest_handshake: DateTime::from_timestamp(proto_stats.latest_handshake as i64, 0) +// .unwrap_or_default() +// .naive_utc(), +// allowed_ips: Some(proto_stats.allowed_ips), +// } +// } #[allow(clippy::large_enum_variant)] #[derive(Debug, Error)] @@ -115,16 +103,8 @@ impl From for Status { } } -pub struct GatewayServer { - pool: PgPool, - gateway_state: Arc>, - client_state: Arc>, - wireguard_tx: Sender, - mail_tx: UnboundedSender, - grpc_event_tx: UnboundedSender, -} - -/// If this location is marked as a service location, checks if all requirements are met for it to function: +/// If this location is marked as a service location, checks if all requirements are met for it to +/// function: /// - Enterprise is enabled #[must_use] pub fn should_prevent_service_location_usage(location: &WireguardNetwork) -> bool { @@ -149,7 +129,7 @@ where { debug!("Fetching all peers for network {}", location.id); - if should_prevent_service_location_usage(&location) { + if should_prevent_service_location_usage(location) { warn!( "Tried to use service location {} with disabled enterprise features. No clients \ will be allowed to connect.", @@ -200,169 +180,6 @@ where Ok(result) } -/// Utility struct encapsulating commonly extracted metadata fields during gRPC communication. -struct GatewayMetadata { - network_id: Id, - hostname: String, - version: Version, - // info: String, -} - -impl GatewayServer { - /// Create new gateway server instance - #[must_use] - pub fn new( - pool: PgPool, - gateway_state: Arc>, - client_state: Arc>, - wireguard_tx: Sender, - mail_tx: UnboundedSender, - grpc_event_tx: UnboundedSender, - ) -> Self { - Self { - pool, - gateway_state, - client_state, - wireguard_tx, - mail_tx, - grpc_event_tx, - } - } - - fn get_network_id(metadata: &MetadataMap) -> Result { - match Self::get_network_id_from_metadata(metadata) { - Some(m) => Ok(m), - None => Err(Status::new( - Code::Internal, - "Network ID was not found in metadata", - )), - } - } - - // parse network id from gateway request metadata from intercepted information from JWT token - fn get_network_id_from_metadata(metadata: &MetadataMap) -> Option { - if let Some(ascii_value) = metadata.get("gateway_network_id") { - if let Ok(slice) = ascii_value.clone().to_str() { - if let Ok(id) = slice.parse::() { - return Some(id); - } - } - } - None - } - - // extract gateway hostname from request headers - fn get_gateway_hostname(metadata: &MetadataMap) -> Result { - match metadata.get("hostname") { - Some(ascii_value) => { - let hostname = ascii_value.to_str().map_err(|_| { - Status::new( - Code::Internal, - "Failed to parse gateway hostname from request metadata", - ) - })?; - Ok(hostname.into()) - } - None => Err(Status::new( - Code::Internal, - "Gateway hostname not found in request metadata", - )), - } - } - - pub fn get_client_state_guard( - &self, - ) -> Result, GatewayServerError> { - let client_state = self - .client_state - .lock() - .map_err(|_| GatewayServerError::ClientStateMutexError)?; - debug!("Current VPN client state map: {client_state:?}"); - Ok(client_state) - } - - fn emit_event(&self, event: GrpcEvent) -> Result<(), GatewayServerError> { - Ok(self.grpc_event_tx.send(event)?) - } - - /// Helper method to fetch `Device` info from DB by pubkey and return appropriate errors - async fn fetch_device_from_db(&self, public_key: &str) -> Result>, Status> { - let device = Device::find_by_pubkey(&self.pool, public_key) - .await - .map_err(|err| { - error!("Failed to retrieve device with public key {public_key}: {err}",); - Status::new( - Code::Internal, - format!("Failed to retrieve device with public key {public_key}: {err}",), - ) - })?; - - Ok(device) - } - - /// Helper method to fetch `WireguardNetwork` info from DB and return appropriate errors - async fn fetch_location_from_db( - &self, - location_id: Id, - ) -> Result, Status> { - let location = match WireguardNetwork::find_by_id(&self.pool, location_id).await { - Ok(Some(location)) => location, - Ok(None) => { - error!("Location {location_id} not found"); - return Err(Status::new( - Code::Internal, - format!("Location {location_id} not found"), - )); - } - Err(err) => { - error!("Failed to retrieve location {location_id}: {err}",); - return Err(Status::new( - Code::Internal, - format!("Failed to retrieve location {location_id}: {err}",), - )); - } - }; - Ok(location) - } - - /// Helper method to fetch `User` info from DB and return appropriate errors - async fn fetch_user_from_db(&self, user_id: Id, public_key: &str) -> Result, Status> { - let user = match User::find_by_id(&self.pool, user_id).await { - Ok(Some(user)) => user, - Ok(None) => { - error!("User {user_id} assigned to device with public key {public_key} not found"); - return Err(Status::new( - Code::Internal, - format!("User assigned to device with public key {public_key} not found"), - )); - } - Err(err) => { - error!( - "Failed to retrieve user {user_id} for device with public key {public_key}: {err}", - ); - return Err(Status::new( - Code::Internal, - format!( - "Failed to retrieve user for device with public key {public_key}: {err}", - ), - )); - } - }; - - Ok(user) - } - - /// Utility function extracting metadata fields during gRPC communication. - fn extract_metadata(metadata: &MetadataMap) -> Result { - let (version, _info) = version_info_from_metadata(metadata); - Ok(GatewayMetadata { - network_id: Self::get_network_id(metadata)?, - hostname: Self::get_gateway_hostname(metadata)?, - version, - }) - } -} - fn gen_config( network: &WireguardNetwork, peers: Vec, @@ -516,7 +333,6 @@ impl GatewayUpdatesHandler { GatewayEvent::NetworkCreated(network_id, network) => { if network_id == self.network_id { self.send_network_update(&network, Vec::new(), None, 0) - .await } else { Ok(()) } @@ -528,9 +344,8 @@ impl GatewayUpdatesHandler { maybe_firewall_config, ) => { if network_id == self.network_id { - let result = self - .send_network_update(&network, peers, maybe_firewall_config, 1) - .await; + let result = + self.send_network_update(&network, peers, maybe_firewall_config, 1); // update stored network data self.network = network; result @@ -540,7 +355,7 @@ impl GatewayUpdatesHandler { } GatewayEvent::NetworkDeleted(network_id, network_name) => { if network_id == self.network_id { - self.send_network_delete(&network_name).await + self.send_network_delete(&network_name) } else { Ok(()) } @@ -576,7 +391,6 @@ impl GatewayUpdatesHandler { }, 0, ) - .await } None => Ok(()), } @@ -612,7 +426,6 @@ impl GatewayUpdatesHandler { }, 1, ) - .await } None => Ok(()), } @@ -624,20 +437,20 @@ impl GatewayUpdatesHandler { .iter() .find(|info| info.network_id == self.network_id) { - Some(_) => self.send_peer_delete(&device.device.wireguard_pubkey).await, + Some(_) => self.send_peer_delete(&device.device.wireguard_pubkey), None => Ok(()), } } GatewayEvent::FirewallConfigChanged(location_id, firewall_config) => { if location_id == self.network_id { - self.send_firewall_update(firewall_config).await + self.send_firewall_update(firewall_config) } else { Ok(()) } } GatewayEvent::FirewallDisabled(location_id) => { if location_id == self.network_id { - self.send_firewall_disable().await + self.send_firewall_disable() } else { Ok(()) } @@ -654,7 +467,7 @@ impl GatewayUpdatesHandler { } /// Sends updated network configuration - async fn send_network_update( + fn send_network_update( &self, network: &WireguardNetwork, peers: Vec, @@ -689,7 +502,7 @@ impl GatewayUpdatesHandler { } /// Sends delete network command to gateway - async fn send_network_delete(&self, network_name: &str) -> Result<(), Status> { + fn send_network_delete(&self, network_name: &str) -> Result<(), Status> { debug!( "Sending network delete command for network {}", self.network @@ -720,7 +533,7 @@ impl GatewayUpdatesHandler { } /// Send update peer command to gateway - async fn send_peer_update(&self, peer: Peer, update_type: i32) -> Result<(), Status> { + fn send_peer_update(&self, peer: Peer, update_type: i32) -> Result<(), Status> { debug!("Sending peer update for network {}", self.network); if let Err(err) = self.tx.send(CoreResponse { id: 0, @@ -730,7 +543,8 @@ impl GatewayUpdatesHandler { })), }) { let msg = format!( - "Failed to send peer update for network {}, update type: {update_type} ({}), error: {err}", + "Failed to send peer update for network {}, update type: {update_type} ({}), \ + error: {err}", self.network, if update_type == 0 { "CREATE" } else { "MODIFY" }, ); @@ -742,7 +556,7 @@ impl GatewayUpdatesHandler { } /// Send delete peer command to gateway - async fn send_peer_delete(&self, peer_pubkey: &str) -> Result<(), Status> { + fn send_peer_delete(&self, peer_pubkey: &str) -> Result<(), Status> { debug!("Sending peer delete for network {}", self.network); if let Err(err) = self.tx.send(CoreResponse { id: 0, @@ -757,7 +571,8 @@ impl GatewayUpdatesHandler { })), }) { let msg = format!( - "Failed to send peer update for network {}, peer {peer_pubkey}, update type: 2 (DELETE), error: {err}", + "Failed to send peer update for network {}, peer {peer_pubkey}, update type: 2 \ + (DELETE), error: {err}", self.network, ); error!(msg); @@ -768,7 +583,7 @@ impl GatewayUpdatesHandler { } /// Send firewall config update command to gateway - async fn send_firewall_update(&self, firewall_config: FirewallConfig) -> Result<(), Status> { + fn send_firewall_update(&self, firewall_config: FirewallConfig) -> Result<(), Status> { debug!( "Sending firewall config update for network {} with config {firewall_config:?}", self.network @@ -792,7 +607,7 @@ impl GatewayUpdatesHandler { } /// Send firewall disable command to gateway - async fn send_firewall_disable(&self) -> Result<(), Status> { + fn send_firewall_disable(&self) -> Result<(), Status> { debug!( "Sending firewall disable command for network {}", self.network @@ -816,59 +631,6 @@ impl GatewayUpdatesHandler { } } -pub struct GatewayUpdatesStream { - task_handle: JoinHandle<()>, - rx: Receiver>, - network_id: Id, - gateway_hostname: String, - gateway_state: Arc>, - pool: PgPool, -} - -impl GatewayUpdatesStream { - #[must_use] - pub fn new( - task_handle: JoinHandle<()>, - rx: Receiver>, - network_id: Id, - gateway_hostname: String, - gateway_state: Arc>, - pool: PgPool, - ) -> Self { - Self { - task_handle, - rx, - network_id, - gateway_hostname, - gateway_state, - pool, - } - } -} - -// impl Stream for GatewayUpdatesStream { -// type Item = Result; - -// fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { -// Pin::new(&mut self.rx).poll_recv(cx) -// } -// } - -// impl Drop for GatewayUpdatesStream { -// fn drop(&mut self) { -// info!("Client disconnected"); -// // terminate update task -// self.task_handle.abort(); -// // update gateway state -// // TODO: possibly use a oneshot channel instead -// self.gateway_state -// .lock() -// .unwrap() -// .disconnect_gateway(self.network_id, self.gateway_hostname.clone(), &self.pool) -// .expect("Unable to disconnect gateway."); -// } -// } - // #[tonic::async_trait] // impl gateway_service_server::GatewayService for GatewayServer { // type UpdatesStream = GatewayUpdatesStream; diff --git a/crates/defguard_core/src/grpc/gateway/state.rs b/crates/defguard_core/src/grpc/gateway/state.rs index a628810f3f..4a412414ab 100644 --- a/crates/defguard_core/src/grpc/gateway/state.rs +++ b/crates/defguard_core/src/grpc/gateway/state.rs @@ -166,7 +166,6 @@ impl GatewayState { } } - #[allow(dead_code)] pub(super) fn as_version_info(&self) -> VersionInfo { VersionInfo { component: Some(DefguardComponent::Gateway), diff --git a/crates/defguard_core/src/grpc/gateway/tests.rs b/crates/defguard_core/src/grpc/gateway/tests.rs index c3c209c9a0..52dbfdff18 100644 --- a/crates/defguard_core/src/grpc/gateway/tests.rs +++ b/crates/defguard_core/src/grpc/gateway/tests.rs @@ -14,17 +14,17 @@ use tokio_stream::wrappers::{UnboundedReceiverStream, UnixListenerStream}; use tonic::{Request, Response, Status, Streaming, transport::Server}; use defguard_common::db::{ - models::wireguard::{LocationMfaMode, ServiceLocationMode, WireguardNetwork}, + models::{ + gateway::Gateway, + wireguard::{LocationMfaMode, ServiceLocationMode, WireguardNetwork}, + }, setup_pool, }; use defguard_mail::Mail; use defguard_proto::gateway::{CoreRequest, CoreResponse, gateway_server}; use super::{TONIC_SOCKET, handler::GatewayHandler}; -use crate::{ - db::models::gateway::Gateway, - grpc::{ClientMap, GrpcEvent, gateway::events::GatewayEvent}, -}; +use crate::grpc::{ClientMap, GrpcEvent, gateway::events::GatewayEvent}; // TODO: move to "gateway" repo. struct FakeGateway; diff --git a/crates/defguard_core/src/grpc/mod.rs b/crates/defguard_core/src/grpc/mod.rs index 4072df66d0..73edecd08d 100644 --- a/crates/defguard_core/src/grpc/mod.rs +++ b/crates/defguard_core/src/grpc/mod.rs @@ -30,7 +30,6 @@ use crate::{ events::GrpcEvent, grpc::gateway::client_state::ClientMap, server_config, - version::MIN_GATEWAY_VERSION, }; mod auth; diff --git a/crates/defguard_core/src/handlers/mod.rs b/crates/defguard_core/src/handlers/mod.rs index 070709e6a2..c18017ba92 100644 --- a/crates/defguard_core/src/handlers/mod.rs +++ b/crates/defguard_core/src/handlers/mod.rs @@ -68,9 +68,6 @@ impl ApiResponse { impl From for ApiResponse { fn from(web_error: WebError) -> ApiResponse { match web_error { - WebError::Deserialization(msg) => { - ApiResponse::new(json!({"msg": msg}), StatusCode::BAD_REQUEST) - } WebError::ObjectNotFound(msg) => { ApiResponse::new(json!({ "msg": msg }), StatusCode::NOT_FOUND) } @@ -88,11 +85,9 @@ impl From for ApiResponse { } WebError::DbError(_) | WebError::Grpc(_) - | WebError::Ldap(_) | WebError::WebauthnRegistration(_) | WebError::Serialization(_) | WebError::ModelError(_) - | WebError::ServerConfigMissing | WebError::EmailMfa(_) | WebError::ClientIpError | WebError::FirewallError(_) @@ -164,8 +159,7 @@ impl From for ApiResponse { json!({ "msg": "Too many login attempts" }), StatusCode::TOO_MANY_REQUESTS, ), - WebError::IncorrectUsername(msg) - | WebError::PubkeyValidation(msg) + WebError::PubkeyValidation(msg) | WebError::PubkeyExists(msg) | WebError::BadRequest(msg) => { error!(msg); diff --git a/crates/defguard_core/src/handlers/openid_flow.rs b/crates/defguard_core/src/handlers/openid_flow.rs index 6a2da694cf..ab3f87e9a6 100644 --- a/crates/defguard_core/src/handlers/openid_flow.rs +++ b/crates/defguard_core/src/handlers/openid_flow.rs @@ -581,7 +581,7 @@ pub async fn secure_authorization( send_new_device_ocid_login_email( &session_info.user.email, - oauth2client.name.to_string(), + oauth2client.name.clone(), &appstate.mail_tx, &session_info.session.into(), ) diff --git a/crates/defguard_core/src/handlers/wireguard.rs b/crates/defguard_core/src/handlers/wireguard.rs index d5dfa01a5c..4660a4b7f5 100644 --- a/crates/defguard_core/src/handlers/wireguard.rs +++ b/crates/defguard_core/src/handlers/wireguard.rs @@ -1,4 +1,8 @@ -use std::{collections::HashSet, net::IpAddr, str::FromStr}; +use std::{ + collections::{HashMap, HashSet}, + net::IpAddr, + str::FromStr, +}; use axum::{ extract::{Json, Path, Query, State}, @@ -12,10 +16,11 @@ use defguard_common::{ models::{ Device, DeviceConfig, DeviceNetworkInfo, DeviceType, WireguardNetwork, device::{AddDevice, DeviceInfo, ModifyDevice, WireguardNetworkDevice}, + gateway::Gateway, wireguard::{ DateTimeAggregation, LocationMfaMode, MappedDevice, ServiceLocationMode, - WireguardDeviceStatsRow, WireguardNetworkInfo, WireguardNetworkStats, - WireguardUserStatsRow, networks_stats, + WireguardDeviceStatsRow, WireguardNetworkStats, WireguardUserStatsRow, + networks_stats, }, }, }, @@ -49,6 +54,41 @@ use crate::{ wg_config::{ImportedDevice, parse_wireguard_config}, }; +#[derive(Serialize, ToSchema)] +pub(crate) struct GatewayInfo { + id: Id, + network_id: Id, + url: String, + hostname: Option, + connected_at: Option, + disconnected_at: Option, + connected: bool, +} + +impl From> for GatewayInfo { + fn from(gateway: Gateway) -> Self { + let connected = gateway.is_connected(); + Self { + id: gateway.id, + network_id: gateway.network_id, + url: gateway.url, + hostname: gateway.hostname, + connected_at: gateway.connected_at, + disconnected_at: gateway.disconnected_at, + connected, + } + } +} + +#[derive(Serialize, ToSchema)] +pub(crate) struct WireguardNetworkInfo { + #[serde(flatten)] + network: WireguardNetwork, + connected: bool, + gateways: Vec, + allowed_groups: Vec, +} + #[derive(Deserialize, Serialize, ToSchema)] pub struct WireguardNetworkData { pub name: String, @@ -127,21 +167,21 @@ impl WireguardNetworkData { } } -// Used in process of importing network from WireGuard config -#[derive(Serialize, Deserialize, Debug, Clone)] -pub struct MappedDevices { - pub devices: Vec, +// Used in process of importing network from WireGuard config. +#[derive(Deserialize)] +pub(crate) struct MappedDevices { + devices: Vec, } #[derive(Deserialize)] -pub struct ImportNetworkData { - pub name: String, - pub endpoint: String, - pub config: String, - pub allowed_groups: Vec, +pub(crate) struct ImportNetworkData { + name: String, + endpoint: String, + config: String, + allowed_groups: Vec, } -#[derive(Serialize, Deserialize)] +#[derive(Deserialize, Serialize)] pub struct ImportedNetworkData { pub network: WireguardNetwork, pub devices: Vec, @@ -296,15 +336,14 @@ pub(crate) async fn modify_network( network.peer_disconnect_threshold = data.peer_disconnect_threshold; network.acl_enabled = data.acl_enabled; network.acl_default_allow = data.acl_default_allow; - network.service_location_mode = match data.location_mfa_mode { - LocationMfaMode::Disabled => data.service_location_mode, - _ => { - warn!( - "Disabling service location mode for location {} because location MFA is enabled", - network.name - ); - ServiceLocationMode::Disabled - } + network.service_location_mode = if data.location_mfa_mode == LocationMfaMode::Disabled { + data.service_location_mode + } else { + warn!( + "Disabling service location mode for location {} because location MFA is enabled", + network.name + ); + ServiceLocationMode::Disabled }; network.location_mfa_mode = data.location_mfa_mode; @@ -429,16 +468,14 @@ pub(crate) async fn list_networks(_role: AdminRole, State(appstate): State { let allowed_groups = network.fetch_allowed_groups(&appstate.pool).await?; + let gateways = Gateway::find_by_network_id(&appstate.pool, network_id).await?; let network_info = WireguardNetworkInfo { network, connected: false, // FIXME: was: gateway_state.connected(network_id), - // gateways: gateway_state.get_network_gateway_status(network_id), + gateways: gateways.into_iter().map(Into::into).collect(), allowed_groups, }; ApiResponse { @@ -505,48 +543,115 @@ pub(crate) async fn network_details( /// Returns state of gateways in a given network /// /// # Returns -/// Returns `Vec` for requested network -pub(crate) async fn gateway_status(Path(network_id): Path, _role: AdminRole) -> ApiResult { +/// Returns `Vec` for requested network. +pub(crate) async fn gateway_status( + Path(network_id): Path, + _role: AdminRole, + State(appstate): State, +) -> ApiResult { debug!("Displaying gateway status for network {network_id}"); - // TODO: fetch gateways from db + let gateways = Gateway::find_by_network_id(&appstate.pool, network_id) + .await? + .into_iter() + .map(GatewayInfo::from) + .collect::>(); debug!("Displayed gateway status for network {network_id}"); - // Ok(ApiResponse { - // json: json!(gateway_state.get_network_gateway_status(network_id)), - // status: StatusCode::OK, - // }) - Ok(ApiResponse::default()) + Ok(ApiResponse { + json: json!(gateways), + status: StatusCode::OK, + }) } /// Returns state of gateways for all networks /// -/// Returns current state of gateways as `HashMap>` where key is an id of `WireguardNetwork` -pub(crate) async fn all_gateways_status(_role: AdminRole) -> ApiResult { +/// Returns current state of gateways as `HashMap>` where key is ID of +/// `WireguardNetwork`. +pub(crate) async fn all_gateways_status( + _role: AdminRole, + State(appstate): State, +) -> ApiResult { debug!("Displaying gateways status for all networks."); - // let flattened = (*gateway_state).as_flattened(); - // Ok(ApiResponse { - // json: json!(flattened), - // status: StatusCode::OK, - // }) - Ok(ApiResponse::default()) + let mut map = HashMap::new(); + let gateways = Gateway::all(&appstate.pool).await?; + for gateway in gateways { + let entry: &mut Vec = map.entry(gateway.network_id).or_default(); + entry.push(gateway.into()); + } + + Ok(ApiResponse { + json: json!(map), + status: StatusCode::OK, + }) } +#[derive(Deserialize)] +pub(crate) struct GatewayData { + url: String, +} + +/// Add gateway (POST). +pub(crate) async fn add_gateway( + Path(network_id): Path, + _role: AdminRole, + State(appstate): State, + Json(data): Json, +) -> ApiResult { + debug!("Adding gateway in network {network_id}"); + + let gateway = Gateway::new(network_id, data.url) + .save(&appstate.pool) + .await?; + + info!("Added gateway in network {network_id}"); + + Ok(ApiResponse { + json: json!(GatewayInfo::from(gateway)), + status: StatusCode::CREATED, + }) +} + +/// Change gateway (PUT). +pub(crate) async fn change_gateway( + Path((network_id, gateway_id)): Path<(Id, Id)>, + _role: AdminRole, + State(appstate): State, + Json(data): Json, +) -> ApiResult { + debug!("Changing gateway {gateway_id} in network {network_id}"); + + if let Some(mut gateway) = Gateway::find_by_id(&appstate.pool, gateway_id).await? { + if gateway.network_id == network_id { + gateway.url = data.url; + gateway.save(&appstate.pool).await?; + info!("Changed gateway"); + return Ok(ApiResponse { + json: json!(GatewayInfo::from(gateway)), + status: StatusCode::OK, + }); + } + } + + info!("Changed gateway {gateway_id} in network {network_id}"); + + Ok(ApiResponse { + json: Value::Null, + status: StatusCode::NOT_FOUND, + }) +} + +/// Remove gateway (DELETE). pub(crate) async fn remove_gateway( - Path((network_id, gateway_id)): Path<(i64, String)>, + Path((network_id, gateway_id)): Path<(Id, Id)>, _role: AdminRole, + State(appstate): State, ) -> ApiResult { debug!("Removing gateway {gateway_id} in network {network_id}"); - // TODO: fetch gateways from db - - // gateway_state.remove_gateway( - // network_id, - // Uuid::from_str(&gateway_id) - // .map_err(|_| WebError::Http(StatusCode::INTERNAL_SERVER_ERROR))?, - // )?; + Gateway::delete_by_id(&appstate.pool, gateway_id, network_id).await?; info!("Removed gateway {gateway_id} in network {network_id}"); @@ -665,7 +770,7 @@ pub(crate) async fn add_user_devices( // assign IPs and generate configs for each network #[derive(Serialize, ToSchema)] -pub struct AddDeviceResult { +pub(crate) struct AddDeviceResult { configs: Vec, device: Device, } @@ -1357,7 +1462,7 @@ fn get_aggregation(from: NaiveDateTime) -> Result, } @@ -1372,9 +1477,9 @@ impl QueryFrom { } #[derive(Serialize)] -pub struct DevicesStatsResponse { - pub user_devices: Vec, - pub network_devices: Vec, +pub(crate) struct DevicesStatsResponse { + user_devices: Vec, + network_devices: Vec, } /// Returns network statistics for users and their devices diff --git a/crates/defguard_core/src/lib.rs b/crates/defguard_core/src/lib.rs index 5005fe003d..81b022e53a 100644 --- a/crates/defguard_core/src/lib.rs +++ b/crates/defguard_core/src/lib.rs @@ -101,7 +101,7 @@ use self::{ appstate::AppState, auth::failed_login::FailedLoginMap, db::AppEvent, - grpc::{WorkerState, gateway::map::GatewayMap}, + grpc::WorkerState, handlers::{ app_info::get_app_info, auth::{ @@ -150,7 +150,9 @@ use self::{ }, }; use crate::{ - grpc::gateway::events::GatewayEvent, location_management::sync_location_allowed_devices, + grpc::gateway::events::GatewayEvent, + handlers::wireguard::{add_gateway, change_gateway}, + location_management::sync_location_allowed_devices, version::IncompatibleComponents, }; @@ -348,7 +350,6 @@ pub fn build_webapp( wireguard_tx: Sender, mail_tx: UnboundedSender, worker_state: Arc>, - gateway_state: Arc>, pool: PgPool, failed_logins: Arc>, event_tx: UnboundedSender, @@ -621,9 +622,10 @@ pub fn build_webapp( .get(network_details), ) .route("/network/{network_id}/gateways", get(gateway_status)) + .route("/network/{network_id}/gateways", post(add_gateway)) .route( "/network/{network_id}/gateways/{gateway_id}", - delete(remove_gateway), + put(change_gateway).delete(remove_gateway), ) .route("/network/{network_id}/devices", post(add_user_devices)) .route( @@ -641,8 +643,7 @@ pub fn build_webapp( "/network/{location_id}/snat/{user_id}", put(modify_snat_binding).delete(delete_snat_binding), ) - .route("/outdated", get(outdated_components)) - .layer(Extension(gateway_state)), + .route("/outdated", get(outdated_components)), ); let webapp = webapp.nest( @@ -694,7 +695,6 @@ pub fn build_webapp( #[instrument(skip_all)] pub async fn run_web_server( worker_state: Arc>, - gateway_state: Arc>, webhook_tx: UnboundedSender, webhook_rx: UnboundedReceiver, wireguard_tx: Sender, @@ -710,7 +710,6 @@ pub async fn run_web_server( wireguard_tx, mail_tx, worker_state, - gateway_state, pool, failed_logins, event_tx, diff --git a/crates/defguard_core/src/utility_thread.rs b/crates/defguard_core/src/utility_thread.rs index 66d81f534c..5697c5cfb8 100644 --- a/crates/defguard_core/src/utility_thread.rs +++ b/crates/defguard_core/src/utility_thread.rs @@ -185,7 +185,12 @@ async fn enterprise_status_check( .expect("ACL-enabled location must have firewall config"); // Handle service location update or just update the firewall - if location.service_location_mode != ServiceLocationMode::Disabled { + if location.service_location_mode == ServiceLocationMode::Disabled { + wireguard_tx.send(GatewayEvent::FirewallConfigChanged( + location.id, + firewall_config, + ))?; + } else { let new_peers = get_location_allowed_peers(&location, &mut *transaction).await?; wireguard_tx.send(GatewayEvent::NetworkModified( @@ -194,11 +199,6 @@ async fn enterprise_status_check( new_peers, Some(firewall_config), ))?; - } else { - wireguard_tx.send(GatewayEvent::FirewallConfigChanged( - location.id, - firewall_config, - ))?; } } transaction.commit().await?; @@ -206,10 +206,13 @@ async fn enterprise_status_check( // handle switch from enabled -> disabled debug!("Disabling gateway firewall configuration for ACL-enabled locations"); for location in locations { - if location.service_location_mode != ServiceLocationMode::Disabled { + if location.service_location_mode == ServiceLocationMode::Disabled { + debug!("Disabling gateway firewall configuration for location {location:?}"); + wireguard_tx.send(GatewayEvent::FirewallDisabled(location.id))?; + } else { debug!( - "Disabling gateway firewall configuration and service location client connections \ - for location {location:?}" + "Disabling gateway firewall configuration and service location client \ + connections for location {location}" ); wireguard_tx.send(GatewayEvent::NetworkModified( location.id, @@ -218,9 +221,6 @@ async fn enterprise_status_check( Vec::new(), None, ))?; - } else { - debug!("Disabling gateway firewall configuration for location {location:?}"); - wireguard_tx.send(GatewayEvent::FirewallDisabled(location.id))?; } } } diff --git a/crates/defguard_core/tests/integration/api/common/mod.rs b/crates/defguard_core/tests/integration/api/common/mod.rs index f74a24da4b..12fbd499ca 100644 --- a/crates/defguard_core/tests/integration/api/common/mod.rs +++ b/crates/defguard_core/tests/integration/api/common/mod.rs @@ -20,10 +20,7 @@ use defguard_core::{ db::AppEvent, enterprise::license::{License, LicenseTier, set_cached_license}, events::ApiEvent, - grpc::{ - WorkerState, - gateway::{events::GatewayEvent, map::GatewayMap}, - }, + grpc::{WorkerState, gateway::events::GatewayEvent}, handlers::{Auth, user::UserDetails}, }; use defguard_mail::Mail; @@ -89,7 +86,6 @@ pub(crate) async fn make_base_client( let worker_state = Arc::new(Mutex::new(WorkerState::new(tx.clone()))); let (wg_tx, wg_rx) = broadcast::channel::(16); let (mail_tx, mail_rx) = unbounded_channel::(); - let gateway_state = Arc::new(Mutex::new(GatewayMap::new())); let failed_logins = FailedLoginMap::new(); let failed_logins = Arc::new(Mutex::new(failed_logins)); @@ -135,7 +131,6 @@ pub(crate) async fn make_base_client( wg_tx, mail_tx, worker_state, - gateway_state, pool, failed_logins, api_event_tx, diff --git a/crates/defguard_core/tests/integration/grpc/common/mod.rs b/crates/defguard_core/tests/integration/grpc/common/mod.rs index 4b63e3d6d6..0e3b0e75a6 100644 --- a/crates/defguard_core/tests/integration/grpc/common/mod.rs +++ b/crates/defguard_core/tests/integration/grpc/common/mod.rs @@ -34,7 +34,6 @@ pub struct TestGrpcServer { grpc_server_task_handle: JoinHandle<()>, pub grpc_event_rx: UnboundedReceiver, wireguard_tx: Sender, - gateway_state: Arc>, client_state: Arc>, pub client_channel: Channel, } @@ -46,7 +45,6 @@ impl TestGrpcServer { grpc_router: Router, grpc_event_rx: UnboundedReceiver, wireguard_tx: Sender, - gateway_state: Arc>, client_state: Arc>, client_channel: Channel, ) -> Self { @@ -63,18 +61,11 @@ impl TestGrpcServer { grpc_server_task_handle, grpc_event_rx, wireguard_tx, - gateway_state, client_state, client_channel, } } - pub fn get_gateway_map(&self) -> std::sync::MutexGuard<'_, GatewayMap> { - self.gateway_state - .lock() - .expect("failed to acquire lock on gateway state") - } - pub fn get_client_map(&self) -> std::sync::MutexGuard<'_, ClientMap> { self.client_state .lock() @@ -128,7 +119,6 @@ pub(crate) async fn make_grpc_test_server(pool: &PgPool) -> TestGrpcServer { let worker_state = Arc::new(Mutex::new(WorkerState::new(app_event_tx.clone()))); let (wg_tx, _wg_rx) = broadcast::channel::(16); let (mail_tx, _mail_rx) = unbounded_channel::(); - let gateway_state = Arc::new(Mutex::new(GatewayMap::new())); let client_state = Arc::new(Mutex::new(ClientMap::new())); let failed_logins = FailedLoginMap::new(); @@ -153,22 +143,16 @@ pub(crate) async fn make_grpc_test_server(pool: &PgPool) -> TestGrpcServer { set_cached_license(Some(license)); let server = Server::builder(); - let grpc_router = build_grpc_service_router( - server, - pool.clone(), - worker_state, - mail_tx, - failed_logins, - ) - .await - .unwrap(); + let grpc_router = + build_grpc_service_router(server, pool.clone(), worker_state, mail_tx, failed_logins) + .await + .unwrap(); TestGrpcServer::new( server_stream, grpc_router, grpc_event_rx, wg_tx, - gateway_state, client_state, client_channel, ) diff --git a/crates/defguard_core/user_agent_header_regexes.yaml b/crates/defguard_core/user_agent_header_regexes.yaml index bdef390459..8cfda16704 100644 --- a/crates/defguard_core/user_agent_header_regexes.yaml +++ b/crates/defguard_core/user_agent_header_regexes.yaml @@ -308,11 +308,16 @@ user_agent_parsers: - regex: (Ecosia) android@(\d+)(?:\.(\d+)|)(?:\.(\d+)|)(?:\.(\d+)|) family_replacement: Ecosia Android - regex: (VivoBrowser)\/(\d+)\.(\d+)\.(\d+)(?:\.(\d+)|) - - regex: (HiBrowser)\/v(\d+)\.(\d+)\.(\d+)\.(\d+) + - regex: (H[Ii]Browser)\/v(\d+)\.(\d+)\.(\d+)\.(\d+) + family_replacement: HiBrowser + - regex: (bdhonorbrowser)/(\d+)\.(\d+)\.(\d+)\.(\d+) + family_replacement: Honor Browser - regex: (weibo)__(\d+)\.(\d+)\.(\d+) family_replacement: Weibo - regex: (WeiboliteiOS|WeiboIntliOS) family_replacement: Weibo + - regex: (PHX)/(\d+)\.(\d+) + family_replacement: Phoenix Browser - regex: Version/.{1,300}(Chrome)/(\d+)\.(\d+)\.(\d+)\.(\d+) family_replacement: Chrome Mobile WebView - regex: ; wv\).{1,300}(Chrome)/(\d+)\.(\d+)\.(\d+)\.(\d+) @@ -395,6 +400,12 @@ user_agent_parsers: family_replacement: JiSu Browser - regex: (Wolvic)/(\d+)\.(\d+)\.(\d+) family_replacement: Wolvic Browser + - regex: (HeyTapBrowser)/(\d+)\.(\d+)\.(\d+)\.(\d+) + family_replacement: HeyTap Browser + - regex: (Thano)/(\d+)\.(\d+) + family_replacement: SmartTV WebBrowser + - regex: (MicroMessenger)/(\d+)\.(\d+)\.(\d+)\.(\d+) + family_replacement: WeChat Browser - regex: (Slack_SSB)/(\d+)\.(\d+)\.(\d+) family_replacement: Slack Desktop Client - regex: (HipChat)/?(\d+|) diff --git a/crates/defguard_proxy_manager/Cargo.toml b/crates/defguard_proxy_manager/Cargo.toml index 63580a5b12..18d4acd0b5 100644 --- a/crates/defguard_proxy_manager/Cargo.toml +++ b/crates/defguard_proxy_manager/Cargo.toml @@ -19,9 +19,7 @@ reqwest.workspace = true semver.workspace = true tokio-stream.workspace = true -anyhow.workspace = true axum.workspace = true -chrono.workspace = true sqlx.workspace = true thiserror.workspace = true tokio.workspace = true diff --git a/crates/defguard_proxy_manager/src/lib.rs b/crates/defguard_proxy_manager/src/lib.rs index 7e4f34e9c1..f71b9b2890 100644 --- a/crates/defguard_proxy_manager/src/lib.rs +++ b/crates/defguard_proxy_manager/src/lib.rs @@ -157,7 +157,7 @@ impl ProxyOrchestrator { pool, tx, incompatible_components, - router: Default::default(), + router: Arc::default(), } } @@ -174,7 +174,7 @@ impl ProxyOrchestrator { let proxies = vec![Proxy::new( self.pool.clone(), Uri::from_str(url)?, - self.tx.clone(), + &self.tx, Arc::clone(&self.router), )?]; let mut tasks = JoinSet::>::new(); @@ -202,6 +202,7 @@ pub struct ProxyTxSet { } impl ProxyTxSet { + #[must_use] pub fn new( wireguard: Sender, mail: UnboundedSender, @@ -236,7 +237,7 @@ impl Proxy { pub fn new( pool: PgPool, uri: Uri, - tx: ProxyTxSet, + tx: &ProxyTxSet, router: Arc>, ) -> Result { let endpoint = Endpoint::from(uri); @@ -261,13 +262,13 @@ impl Proxy { }; // Instantiate gRPC servers. - let services = ProxyServices::new(pool.clone(), tx); + let services = ProxyServices::new(&pool, tx); Ok(Self { pool, endpoint, - router, services, + router, }) } @@ -790,7 +791,7 @@ impl Proxy { } } else { let _ = tx.send(req); - }; + } } Err(err) => { error!("Disconnected from proxy at {}: {err}", self.endpoint.uri()); @@ -819,7 +820,7 @@ struct ProxyServices { } impl ProxyServices { - pub fn new(pool: PgPool, tx: ProxyTxSet) -> Self { + pub fn new(pool: &PgPool, tx: &ProxyTxSet) -> Self { let enrollment = EnrollmentServer::new( pool.clone(), tx.wireguard.clone(), diff --git a/crates/defguard_proxy_manager/src/password_reset.rs b/crates/defguard_proxy_manager/src/password_reset.rs index e27bc4776e..3c27dbd384 100644 --- a/crates/defguard_proxy_manager/src/password_reset.rs +++ b/crates/defguard_proxy_manager/src/password_reset.rs @@ -109,7 +109,7 @@ impl PasswordResetServer { let email = request.email; - let user = User::find_by_email(&self.pool, email.to_string().as_str()) + let user = User::find_by_email(&self.pool, email.clone().as_str()) .await .map_err(|_| { error!("Failed to fetch user by email: {email}"); diff --git a/web/package.json b/web/package.json index c9c38ee422..8c4cde03bc 100644 --- a/web/package.json +++ b/web/package.json @@ -56,8 +56,8 @@ "@react-rxjs/core": "^0.10.8", "@stablelib/base64": "^2.0.1", "@stablelib/x25519": "^2.0.1", - "@tanstack/query-core": "^5.90.10", - "@tanstack/react-query": "^5.90.10", + "@tanstack/query-core": "^5.90.16", + "@tanstack/react-query": "^5.90.16", "@tanstack/react-virtual": "3.13.12", "@tanstack/virtual-core": "3.13.12", "@use-gesture/react": "^10.3.1", @@ -76,27 +76,27 @@ "fuse.js": "^7.1.0", "get-text-width": "^1.0.3", "hex-rgb": "^5.0.0", - "html-react-parser": "^5.2.10", - "humanize-duration": "^3.33.1", - "ipaddr.js": "^2.2.0", + "html-react-parser": "^5.2.11", + "humanize-duration": "^3.33.2", + "ipaddr.js": "^2.3.0", "itertools": "^2.5.0", "js-base64": "^3.7.8", - "lodash-es": "^4.17.21", + "lodash-es": "^4.17.22", "merge-refs": "^2.0.0", "millify": "^6.1.0", - "motion": "^12.23.24", + "motion": "^12.23.26", "numbro": "^2.5.0", "qrcode": "^1.5.4", - "qs": "^6.14.0", + "qs": "^6.14.1", "radash": "^12.1.1", - "react": "^19.2.0", + "react": "^19.2.3", "react-click-away-listener": "^2.4.0", - "react-datepicker": "^8.9.0", - "react-dom": "^19.2.0", - "react-hook-form": "^7.66.0", + "react-datepicker": "^8.10.0", + "react-dom": "^19.2.3", + "react-hook-form": "^7.69.0", "react-idle-timer": "^5.7.2", "react-intersection-observer": "^9.16.0", - "react-is": "^19.2.0", + "react-is": "^19.2.3", "react-loading-skeleton": "^3.5.0", "react-markdown": "^10.1.0", "react-qr-code": "^2.0.18", @@ -105,7 +105,7 @@ "react-router-dom": "^6.30.2", "react-tracked": "^2.0.1", "react-virtualized-auto-sizer": "^1.0.26", - "recharts": "^3.4.1", + "recharts": "^3.6.0", "rehype-external-links": "^3.0.0", "rehype-raw": "^7.0.0", "rehype-sanitize": "^6.0.0", @@ -115,7 +115,7 @@ "typesafe-i18n": "^5.26.2", "use-breakpoint": "^4.0.10", "zod": "^3.25.76", - "zustand": "^5.0.8" + "zustand": "^5.0.9" }, "devDependencies": { "@babel/core": "^7.28.5", @@ -123,31 +123,31 @@ "@csstools/css-parser-algorithms": "^3.0.5", "@csstools/css-tokenizer": "^3.0.4", "@hookform/devtools": "^4.4.0", - "@tanstack/react-query-devtools": "^5.90.2", + "@tanstack/react-query-devtools": "^5.91.2", "@types/byte-size": "^8.1.2", "@types/file-saver": "^2.0.7", "@types/humanize-duration": "^3.27.4", "@types/lodash-es": "^4.17.12", - "@types/node": "^24.10.1", + "@types/node": "^24.10.4", "@types/qs": "^6.14.0", - "@types/react": "^19.2.5", + "@types/react": "^19.2.7", "@types/react-dom": "^19.2.3", "@types/react-router-dom": "^5.3.3", "@vitejs/plugin-react-swc": "^4.2.2", - "@vitest/ui": "^4.0.14", - "autoprefixer": "^10.4.22", + "@vitest/ui": "^4.0.16", + "autoprefixer": "^10.4.23", "concurrently": "^9.2.1", "dotenv": "^17.2.3", "esbuild": "^0.25.12", "globals": "^16.5.0", "postcss": "^8.5.6", - "prettier": "^3.6.2", + "prettier": "^3.7.4", "sass": "~1.70.0", "standard-version": "^9.5.0", "type-fest": "^4.41.0", "typescript": "~5.9.3", - "vite": "^7.2.2", + "vite": "^7.3.0", "vite-plugin-package-version": "^1.1.0", - "vitest": "^4.0.14" + "vitest": "^4.0.16" } } diff --git a/web/pnpm-lock.yaml b/web/pnpm-lock.yaml index 7e1b612c19..00ba7fd4ab 100644 --- a/web/pnpm-lock.yaml +++ b/web/pnpm-lock.yaml @@ -13,19 +13,19 @@ importers: dependencies: '@floating-ui/react': specifier: ^0.27.16 - version: 0.27.16(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + version: 0.27.16(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@github/webauthn-json': specifier: ^2.1.1 version: 2.1.1 '@hookform/resolvers': specifier: ^5.2.2 - version: 5.2.2(react-hook-form@7.66.0(react@19.2.0)) + version: 5.2.2(react-hook-form@7.69.0(react@19.2.3)) '@react-hook/resize-observer': specifier: ^2.0.2 - version: 2.0.2(react@19.2.0) + version: 2.0.2(react@19.2.3) '@react-rxjs/core': specifier: ^0.10.8 - version: 0.10.8(react@19.2.0)(rxjs@7.8.2) + version: 0.10.8(react@19.2.3)(rxjs@7.8.2) '@stablelib/base64': specifier: ^2.0.1 version: 2.0.1 @@ -33,20 +33,20 @@ importers: specifier: ^2.0.1 version: 2.0.1 '@tanstack/query-core': - specifier: ^5.90.10 - version: 5.90.10 + specifier: ^5.90.16 + version: 5.90.16 '@tanstack/react-query': - specifier: ^5.90.10 - version: 5.90.10(react@19.2.0) + specifier: ^5.90.16 + version: 5.90.16(react@19.2.3) '@tanstack/react-virtual': specifier: 3.13.12 - version: 3.13.12(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + version: 3.13.12(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@tanstack/virtual-core': specifier: 3.13.12 version: 3.13.12 '@use-gesture/react': specifier: ^10.3.1 - version: 10.3.1(react@19.2.0) + version: 10.3.1(react@19.2.3) axios: specifier: ^1.13.2 version: 1.13.2 @@ -93,14 +93,14 @@ importers: specifier: ^5.0.0 version: 5.0.0 html-react-parser: - specifier: ^5.2.10 - version: 5.2.10(@types/react@19.2.5)(react@19.2.0) + specifier: ^5.2.11 + version: 5.2.11(@types/react@19.2.7)(react@19.2.3) humanize-duration: - specifier: ^3.33.1 - version: 3.33.1 + specifier: ^3.33.2 + version: 3.33.2 ipaddr.js: - specifier: ^2.2.0 - version: 2.2.0 + specifier: ^2.3.0 + version: 2.3.0 itertools: specifier: ^2.5.0 version: 2.5.0 @@ -108,17 +108,17 @@ importers: specifier: ^3.7.8 version: 3.7.8 lodash-es: - specifier: ^4.17.21 - version: 4.17.21 + specifier: ^4.17.22 + version: 4.17.22 merge-refs: specifier: ^2.0.0 - version: 2.0.0(@types/react@19.2.5) + version: 2.0.0(@types/react@19.2.7) millify: specifier: ^6.1.0 version: 6.1.0 motion: - specifier: ^12.23.24 - version: 12.23.24(@emotion/is-prop-valid@1.4.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + specifier: ^12.23.26 + version: 12.23.26(@emotion/is-prop-valid@1.4.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) numbro: specifier: ^2.5.0 version: 2.5.0 @@ -126,62 +126,62 @@ importers: specifier: ^1.5.4 version: 1.5.4 qs: - specifier: ^6.14.0 - version: 6.14.0 + specifier: ^6.14.1 + version: 6.14.1 radash: specifier: ^12.1.1 version: 12.1.1 react: - specifier: ^19.2.0 - version: 19.2.0 + specifier: ^19.2.3 + version: 19.2.3 react-click-away-listener: specifier: ^2.4.0 - version: 2.4.0(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + version: 2.4.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) react-datepicker: - specifier: ^8.9.0 - version: 8.9.0(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + specifier: ^8.10.0 + version: 8.10.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) react-dom: - specifier: ^19.2.0 - version: 19.2.0(react@19.2.0) + specifier: ^19.2.3 + version: 19.2.3(react@19.2.3) react-hook-form: - specifier: ^7.66.0 - version: 7.66.0(react@19.2.0) + specifier: ^7.69.0 + version: 7.69.0(react@19.2.3) react-idle-timer: specifier: ^5.7.2 - version: 5.7.2(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + version: 5.7.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) react-intersection-observer: specifier: ^9.16.0 - version: 9.16.0(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + version: 9.16.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) react-is: - specifier: ^19.2.0 - version: 19.2.0 + specifier: ^19.2.3 + version: 19.2.3 react-loading-skeleton: specifier: ^3.5.0 - version: 3.5.0(react@19.2.0) + version: 3.5.0(react@19.2.3) react-markdown: specifier: ^10.1.0 - version: 10.1.0(@types/react@19.2.5)(react@19.2.0) + version: 10.1.0(@types/react@19.2.7)(react@19.2.3) react-qr-code: specifier: ^2.0.18 - version: 2.0.18(react@19.2.0) + version: 2.0.18(react@19.2.3) react-resize-detector: specifier: ^12.3.0 - version: 12.3.0(react@19.2.0) + version: 12.3.0(react@19.2.3) react-router: specifier: ^6.30.2 - version: 6.30.2(react@19.2.0) + version: 6.30.2(react@19.2.3) react-router-dom: specifier: ^6.30.2 - version: 6.30.2(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + version: 6.30.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) react-tracked: specifier: ^2.0.1 - version: 2.0.1(react@19.2.0)(scheduler@0.26.0) + version: 2.0.1(react@19.2.3)(scheduler@0.26.0) react-virtualized-auto-sizer: specifier: ^1.0.26 - version: 1.0.26(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + version: 1.0.26(react-dom@19.2.3(react@19.2.3))(react@19.2.3) recharts: - specifier: ^3.4.1 - version: 3.4.1(@types/react@19.2.5)(react-dom@19.2.0(react@19.2.0))(react-is@19.2.0)(react@19.2.0)(redux@5.0.1) + specifier: ^3.6.0 + version: 3.6.0(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react-is@19.2.3)(react@19.2.3)(redux@5.0.1) rehype-external-links: specifier: ^3.0.0 version: 3.0.0 @@ -205,13 +205,13 @@ importers: version: 5.26.2(typescript@5.9.3) use-breakpoint: specifier: ^4.0.10 - version: 4.0.10(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + version: 4.0.10(react-dom@19.2.3(react@19.2.3))(react@19.2.3) zod: specifier: ^3.25.76 version: 3.25.76 zustand: - specifier: ^5.0.8 - version: 5.0.8(@types/react@19.2.5)(immer@10.2.0)(react@19.2.0)(use-sync-external-store@1.6.0(react@19.2.0)) + specifier: ^5.0.9 + version: 5.0.9(@types/react@19.2.7)(immer@11.1.3)(react@19.2.3)(use-sync-external-store@1.6.0(react@19.2.3)) devDependencies: '@babel/core': specifier: ^7.28.5 @@ -227,10 +227,10 @@ importers: version: 3.0.4 '@hookform/devtools': specifier: ^4.4.0 - version: 4.4.0(@types/react@19.2.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + version: 4.4.0(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@tanstack/react-query-devtools': - specifier: ^5.90.2 - version: 5.90.2(@tanstack/react-query@5.90.10(react@19.2.0))(react@19.2.0) + specifier: ^5.91.2 + version: 5.91.2(@tanstack/react-query@5.90.16(react@19.2.3))(react@19.2.3) '@types/byte-size': specifier: ^8.1.2 version: 8.1.2 @@ -244,29 +244,29 @@ importers: specifier: ^4.17.12 version: 4.17.12 '@types/node': - specifier: ^24.10.1 - version: 24.10.1 + specifier: ^24.10.4 + version: 24.10.4 '@types/qs': specifier: ^6.14.0 version: 6.14.0 '@types/react': - specifier: ^19.2.5 - version: 19.2.5 + specifier: ^19.2.7 + version: 19.2.7 '@types/react-dom': specifier: ^19.2.3 - version: 19.2.3(@types/react@19.2.5) + version: 19.2.3(@types/react@19.2.7) '@types/react-router-dom': specifier: ^5.3.3 version: 5.3.3 '@vitejs/plugin-react-swc': specifier: ^4.2.2 - version: 4.2.2(vite@7.2.2(@types/node@24.10.1)(jiti@2.4.2)(sass@1.70.0)(terser@5.37.0)(yaml@2.6.1)) + version: 4.2.2(vite@7.3.0(@types/node@24.10.4)(jiti@2.4.2)(sass@1.70.0)(terser@5.37.0)(yaml@2.6.1)) '@vitest/ui': - specifier: ^4.0.14 - version: 4.0.14(vitest@4.0.14) + specifier: ^4.0.16 + version: 4.0.16(vitest@4.0.16) autoprefixer: - specifier: ^10.4.22 - version: 10.4.22(postcss@8.5.6) + specifier: ^10.4.23 + version: 10.4.23(postcss@8.5.6) concurrently: specifier: ^9.2.1 version: 9.2.1 @@ -283,8 +283,8 @@ importers: specifier: ^8.5.6 version: 8.5.6 prettier: - specifier: ^3.6.2 - version: 3.6.2 + specifier: ^3.7.4 + version: 3.7.4 sass: specifier: ~1.70.0 version: 1.70.0 @@ -298,14 +298,14 @@ importers: specifier: ~5.9.3 version: 5.9.3 vite: - specifier: ^7.2.2 - version: 7.2.2(@types/node@24.10.1)(jiti@2.4.2)(sass@1.70.0)(terser@5.37.0)(yaml@2.6.1) + specifier: ^7.3.0 + version: 7.3.0(@types/node@24.10.4)(jiti@2.4.2)(sass@1.70.0)(terser@5.37.0)(yaml@2.6.1) vite-plugin-package-version: specifier: ^1.1.0 - version: 1.1.0(vite@7.2.2(@types/node@24.10.1)(jiti@2.4.2)(sass@1.70.0)(terser@5.37.0)(yaml@2.6.1)) + version: 1.1.0(vite@7.3.0(@types/node@24.10.4)(jiti@2.4.2)(sass@1.70.0)(terser@5.37.0)(yaml@2.6.1)) vitest: - specifier: ^4.0.14 - version: 4.0.14(@types/node@24.10.1)(@vitest/ui@4.0.14)(jiti@2.4.2)(sass@1.70.0)(terser@5.37.0)(yaml@2.6.1) + specifier: ^4.0.16 + version: 4.0.16(@types/node@24.10.4)(@vitest/ui@4.0.16)(jiti@2.4.2)(sass@1.70.0)(terser@5.37.0)(yaml@2.6.1) packages: @@ -503,156 +503,312 @@ packages: cpu: [ppc64] os: [aix] + '@esbuild/aix-ppc64@0.27.2': + resolution: {integrity: sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + '@esbuild/android-arm64@0.25.12': resolution: {integrity: sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==} engines: {node: '>=18'} cpu: [arm64] os: [android] + '@esbuild/android-arm64@0.27.2': + resolution: {integrity: sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + '@esbuild/android-arm@0.25.12': resolution: {integrity: sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==} engines: {node: '>=18'} cpu: [arm] os: [android] + '@esbuild/android-arm@0.27.2': + resolution: {integrity: sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + '@esbuild/android-x64@0.25.12': resolution: {integrity: sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==} engines: {node: '>=18'} cpu: [x64] os: [android] + '@esbuild/android-x64@0.27.2': + resolution: {integrity: sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + '@esbuild/darwin-arm64@0.25.12': resolution: {integrity: sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==} engines: {node: '>=18'} cpu: [arm64] os: [darwin] + '@esbuild/darwin-arm64@0.27.2': + resolution: {integrity: sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + '@esbuild/darwin-x64@0.25.12': resolution: {integrity: sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==} engines: {node: '>=18'} cpu: [x64] os: [darwin] + '@esbuild/darwin-x64@0.27.2': + resolution: {integrity: sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + '@esbuild/freebsd-arm64@0.25.12': resolution: {integrity: sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] + '@esbuild/freebsd-arm64@0.27.2': + resolution: {integrity: sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + '@esbuild/freebsd-x64@0.25.12': resolution: {integrity: sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==} engines: {node: '>=18'} cpu: [x64] os: [freebsd] + '@esbuild/freebsd-x64@0.27.2': + resolution: {integrity: sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + '@esbuild/linux-arm64@0.25.12': resolution: {integrity: sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==} engines: {node: '>=18'} cpu: [arm64] os: [linux] + '@esbuild/linux-arm64@0.27.2': + resolution: {integrity: sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + '@esbuild/linux-arm@0.25.12': resolution: {integrity: sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==} engines: {node: '>=18'} cpu: [arm] os: [linux] + '@esbuild/linux-arm@0.27.2': + resolution: {integrity: sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + '@esbuild/linux-ia32@0.25.12': resolution: {integrity: sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==} engines: {node: '>=18'} cpu: [ia32] os: [linux] + '@esbuild/linux-ia32@0.27.2': + resolution: {integrity: sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + '@esbuild/linux-loong64@0.25.12': resolution: {integrity: sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==} engines: {node: '>=18'} cpu: [loong64] os: [linux] + '@esbuild/linux-loong64@0.27.2': + resolution: {integrity: sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + '@esbuild/linux-mips64el@0.25.12': resolution: {integrity: sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==} engines: {node: '>=18'} cpu: [mips64el] os: [linux] + '@esbuild/linux-mips64el@0.27.2': + resolution: {integrity: sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + '@esbuild/linux-ppc64@0.25.12': resolution: {integrity: sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==} engines: {node: '>=18'} cpu: [ppc64] os: [linux] + '@esbuild/linux-ppc64@0.27.2': + resolution: {integrity: sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + '@esbuild/linux-riscv64@0.25.12': resolution: {integrity: sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==} engines: {node: '>=18'} cpu: [riscv64] os: [linux] + '@esbuild/linux-riscv64@0.27.2': + resolution: {integrity: sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + '@esbuild/linux-s390x@0.25.12': resolution: {integrity: sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==} engines: {node: '>=18'} cpu: [s390x] os: [linux] + '@esbuild/linux-s390x@0.27.2': + resolution: {integrity: sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + '@esbuild/linux-x64@0.25.12': resolution: {integrity: sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==} engines: {node: '>=18'} cpu: [x64] os: [linux] + '@esbuild/linux-x64@0.27.2': + resolution: {integrity: sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + '@esbuild/netbsd-arm64@0.25.12': resolution: {integrity: sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==} engines: {node: '>=18'} cpu: [arm64] os: [netbsd] + '@esbuild/netbsd-arm64@0.27.2': + resolution: {integrity: sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + '@esbuild/netbsd-x64@0.25.12': resolution: {integrity: sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==} engines: {node: '>=18'} cpu: [x64] os: [netbsd] + '@esbuild/netbsd-x64@0.27.2': + resolution: {integrity: sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + '@esbuild/openbsd-arm64@0.25.12': resolution: {integrity: sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] + '@esbuild/openbsd-arm64@0.27.2': + resolution: {integrity: sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + '@esbuild/openbsd-x64@0.25.12': resolution: {integrity: sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==} engines: {node: '>=18'} cpu: [x64] os: [openbsd] + '@esbuild/openbsd-x64@0.27.2': + resolution: {integrity: sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + '@esbuild/openharmony-arm64@0.25.12': resolution: {integrity: sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==} engines: {node: '>=18'} cpu: [arm64] os: [openharmony] + '@esbuild/openharmony-arm64@0.27.2': + resolution: {integrity: sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + '@esbuild/sunos-x64@0.25.12': resolution: {integrity: sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==} engines: {node: '>=18'} cpu: [x64] os: [sunos] + '@esbuild/sunos-x64@0.27.2': + resolution: {integrity: sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + '@esbuild/win32-arm64@0.25.12': resolution: {integrity: sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==} engines: {node: '>=18'} cpu: [arm64] os: [win32] + '@esbuild/win32-arm64@0.27.2': + resolution: {integrity: sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + '@esbuild/win32-ia32@0.25.12': resolution: {integrity: sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==} engines: {node: '>=18'} cpu: [ia32] os: [win32] + '@esbuild/win32-ia32@0.27.2': + resolution: {integrity: sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + '@esbuild/win32-x64@0.25.12': resolution: {integrity: sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==} engines: {node: '>=18'} cpu: [x64] os: [win32] + '@esbuild/win32-x64@0.27.2': + resolution: {integrity: sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + '@floating-ui/core@1.7.3': resolution: {integrity: sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==} @@ -737,8 +893,8 @@ packages: react: '>=16.8.0' rxjs: '>=7' - '@reduxjs/toolkit@2.10.1': - resolution: {integrity: sha512-/U17EXQ9Do9Yx4DlNGU6eVNfZvFJfYpUtRRdLf19PbPjdWBxNlxGZXywQZ1p1Nz8nMkWplTI7iD/23m07nolDA==} + '@reduxjs/toolkit@2.11.2': + resolution: {integrity: sha512-Kd6kAHTA6/nUpp8mySPqj3en3dm0tdMIgbttnQ1xFMVpufoj+ADi8pXLBsd4xzTRHQa7t/Jv8W5UnCuW4kuWMQ==} peerDependencies: react: ^16.9.0 || ^17.0.0 || ^18 || ^19 react-redux: ^7.2.1 || ^8.1.3 || ^9.0.0 @@ -755,113 +911,113 @@ packages: '@rolldown/pluginutils@1.0.0-beta.47': resolution: {integrity: sha512-8QagwMH3kNCuzD8EWL8R2YPW5e4OrHNSAHRFDdmFqEwEaD/KcNKjVoumo+gP2vW5eKB2UPbM6vTYiGZX0ixLnw==} - '@rollup/rollup-android-arm-eabi@4.53.2': - resolution: {integrity: sha512-yDPzwsgiFO26RJA4nZo8I+xqzh7sJTZIWQOxn+/XOdPE31lAvLIYCKqjV+lNH/vxE2L2iH3plKxDCRK6i+CwhA==} + '@rollup/rollup-android-arm-eabi@4.54.0': + resolution: {integrity: sha512-OywsdRHrFvCdvsewAInDKCNyR3laPA2mc9bRYJ6LBp5IyvF3fvXbbNR0bSzHlZVFtn6E0xw2oZlyjg4rKCVcng==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.53.2': - resolution: {integrity: sha512-k8FontTxIE7b0/OGKeSN5B6j25EuppBcWM33Z19JoVT7UTXFSo3D9CdU39wGTeb29NO3XxpMNauh09B+Ibw+9g==} + '@rollup/rollup-android-arm64@4.54.0': + resolution: {integrity: sha512-Skx39Uv+u7H224Af+bDgNinitlmHyQX1K/atIA32JP3JQw6hVODX5tkbi2zof/E69M1qH2UoN3Xdxgs90mmNYw==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.53.2': - resolution: {integrity: sha512-A6s4gJpomNBtJ2yioj8bflM2oogDwzUiMl2yNJ2v9E7++sHrSrsQ29fOfn5DM/iCzpWcebNYEdXpaK4tr2RhfQ==} + '@rollup/rollup-darwin-arm64@4.54.0': + resolution: {integrity: sha512-k43D4qta/+6Fq+nCDhhv9yP2HdeKeP56QrUUTW7E6PhZP1US6NDqpJj4MY0jBHlJivVJD5P8NxrjuobZBJTCRw==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.53.2': - resolution: {integrity: sha512-e6XqVmXlHrBlG56obu9gDRPW3O3hLxpwHpLsBJvuI8qqnsrtSZ9ERoWUXtPOkY8c78WghyPHZdmPhHLWNdAGEw==} + '@rollup/rollup-darwin-x64@4.54.0': + resolution: {integrity: sha512-cOo7biqwkpawslEfox5Vs8/qj83M/aZCSSNIWpVzfU2CYHa2G3P1UN5WF01RdTHSgCkri7XOlTdtk17BezlV3A==} cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.53.2': - resolution: {integrity: sha512-v0E9lJW8VsrwPux5Qe5CwmH/CF/2mQs6xU1MF3nmUxmZUCHazCjLgYvToOk+YuuUqLQBio1qkkREhxhc656ViA==} + '@rollup/rollup-freebsd-arm64@4.54.0': + resolution: {integrity: sha512-miSvuFkmvFbgJ1BevMa4CPCFt5MPGw094knM64W9I0giUIMMmRYcGW/JWZDriaw/k1kOBtsWh1z6nIFV1vPNtA==} cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.53.2': - resolution: {integrity: sha512-ClAmAPx3ZCHtp6ysl4XEhWU69GUB1D+s7G9YjHGhIGCSrsg00nEGRRZHmINYxkdoJehde8VIsDC5t9C0gb6yqA==} + '@rollup/rollup-freebsd-x64@4.54.0': + resolution: {integrity: sha512-KGXIs55+b/ZfZsq9aR026tmr/+7tq6VG6MsnrvF4H8VhwflTIuYh+LFUlIsRdQSgrgmtM3fVATzEAj4hBQlaqQ==} cpu: [x64] os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.53.2': - resolution: {integrity: sha512-EPlb95nUsz6Dd9Qy13fI5kUPXNSljaG9FiJ4YUGU1O/Q77i5DYFW5KR8g1OzTcdZUqQQ1KdDqsTohdFVwCwjqg==} + '@rollup/rollup-linux-arm-gnueabihf@4.54.0': + resolution: {integrity: sha512-EHMUcDwhtdRGlXZsGSIuXSYwD5kOT9NVnx9sqzYiwAc91wfYOE1g1djOEDseZJKKqtHAHGwnGPQu3kytmfaXLQ==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm-musleabihf@4.53.2': - resolution: {integrity: sha512-BOmnVW+khAUX+YZvNfa0tGTEMVVEerOxN0pDk2E6N6DsEIa2Ctj48FOMfNDdrwinocKaC7YXUZ1pHlKpnkja/Q==} + '@rollup/rollup-linux-arm-musleabihf@4.54.0': + resolution: {integrity: sha512-+pBrqEjaakN2ySv5RVrj/qLytYhPKEUwk+e3SFU5jTLHIcAtqh2rLrd/OkbNuHJpsBgxsD8ccJt5ga/SeG0JmA==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm64-gnu@4.53.2': - resolution: {integrity: sha512-Xt2byDZ+6OVNuREgBXr4+CZDJtrVso5woFtpKdGPhpTPHcNG7D8YXeQzpNbFRxzTVqJf7kvPMCub/pcGUWgBjA==} + '@rollup/rollup-linux-arm64-gnu@4.54.0': + resolution: {integrity: sha512-NSqc7rE9wuUaRBsBp5ckQ5CVz5aIRKCwsoa6WMF7G01sX3/qHUw/z4pv+D+ahL1EIKy6Enpcnz1RY8pf7bjwng==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-arm64-musl@4.53.2': - resolution: {integrity: sha512-+LdZSldy/I9N8+klim/Y1HsKbJ3BbInHav5qE9Iy77dtHC/pibw1SR/fXlWyAk0ThnpRKoODwnAuSjqxFRDHUQ==} + '@rollup/rollup-linux-arm64-musl@4.54.0': + resolution: {integrity: sha512-gr5vDbg3Bakga5kbdpqx81m2n9IX8M6gIMlQQIXiLTNeQW6CucvuInJ91EuCJ/JYvc+rcLLsDFcfAD1K7fMofg==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-loong64-gnu@4.53.2': - resolution: {integrity: sha512-8ms8sjmyc1jWJS6WdNSA23rEfdjWB30LH8Wqj0Cqvv7qSHnvw6kgMMXRdop6hkmGPlyYBdRPkjJnj3KCUHV/uQ==} + '@rollup/rollup-linux-loong64-gnu@4.54.0': + resolution: {integrity: sha512-gsrtB1NA3ZYj2vq0Rzkylo9ylCtW/PhpLEivlgWe0bpgtX5+9j9EZa0wtZiCjgu6zmSeZWyI/e2YRX1URozpIw==} cpu: [loong64] os: [linux] - '@rollup/rollup-linux-ppc64-gnu@4.53.2': - resolution: {integrity: sha512-3HRQLUQbpBDMmzoxPJYd3W6vrVHOo2cVW8RUo87Xz0JPJcBLBr5kZ1pGcQAhdZgX9VV7NbGNipah1omKKe23/g==} + '@rollup/rollup-linux-ppc64-gnu@4.54.0': + resolution: {integrity: sha512-y3qNOfTBStmFNq+t4s7Tmc9hW2ENtPg8FeUD/VShI7rKxNW7O4fFeaYbMsd3tpFlIg1Q8IapFgy7Q9i2BqeBvA==} cpu: [ppc64] os: [linux] - '@rollup/rollup-linux-riscv64-gnu@4.53.2': - resolution: {integrity: sha512-fMjKi+ojnmIvhk34gZP94vjogXNNUKMEYs+EDaB/5TG/wUkoeua7p7VCHnE6T2Tx+iaghAqQX8teQzcvrYpaQA==} + '@rollup/rollup-linux-riscv64-gnu@4.54.0': + resolution: {integrity: sha512-89sepv7h2lIVPsFma8iwmccN7Yjjtgz0Rj/Ou6fEqg3HDhpCa+Et+YSufy27i6b0Wav69Qv4WBNl3Rs6pwhebQ==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-riscv64-musl@4.53.2': - resolution: {integrity: sha512-XuGFGU+VwUUV5kLvoAdi0Wz5Xbh2SrjIxCtZj6Wq8MDp4bflb/+ThZsVxokM7n0pcbkEr2h5/pzqzDYI7cCgLQ==} + '@rollup/rollup-linux-riscv64-musl@4.54.0': + resolution: {integrity: sha512-ZcU77ieh0M2Q8Ur7D5X7KvK+UxbXeDHwiOt/CPSBTI1fBmeDMivW0dPkdqkT4rOgDjrDDBUed9x4EgraIKoR2A==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-s390x-gnu@4.53.2': - resolution: {integrity: sha512-w6yjZF0P+NGzWR3AXWX9zc0DNEGdtvykB03uhonSHMRa+oWA6novflo2WaJr6JZakG2ucsyb+rvhrKac6NIy+w==} + '@rollup/rollup-linux-s390x-gnu@4.54.0': + resolution: {integrity: sha512-2AdWy5RdDF5+4YfG/YesGDDtbyJlC9LHmL6rZw6FurBJ5n4vFGupsOBGfwMRjBYH7qRQowT8D/U4LoSvVwOhSQ==} cpu: [s390x] os: [linux] - '@rollup/rollup-linux-x64-gnu@4.53.2': - resolution: {integrity: sha512-yo8d6tdfdeBArzC7T/PnHd7OypfI9cbuZzPnzLJIyKYFhAQ8SvlkKtKBMbXDxe1h03Rcr7u++nFS7tqXz87Gtw==} + '@rollup/rollup-linux-x64-gnu@4.54.0': + resolution: {integrity: sha512-WGt5J8Ij/rvyqpFexxk3ffKqqbLf9AqrTBbWDk7ApGUzaIs6V+s2s84kAxklFwmMF/vBNGrVdYgbblCOFFezMQ==} cpu: [x64] os: [linux] - '@rollup/rollup-linux-x64-musl@4.53.2': - resolution: {integrity: sha512-ah59c1YkCxKExPP8O9PwOvs+XRLKwh/mV+3YdKqQ5AMQ0r4M4ZDuOrpWkUaqO7fzAHdINzV9tEVu8vNw48z0lA==} + '@rollup/rollup-linux-x64-musl@4.54.0': + resolution: {integrity: sha512-JzQmb38ATzHjxlPHuTH6tE7ojnMKM2kYNzt44LO/jJi8BpceEC8QuXYA908n8r3CNuG/B3BV8VR3Hi1rYtmPiw==} cpu: [x64] os: [linux] - '@rollup/rollup-openharmony-arm64@4.53.2': - resolution: {integrity: sha512-4VEd19Wmhr+Zy7hbUsFZ6YXEiP48hE//KPLCSVNY5RMGX2/7HZ+QkN55a3atM1C/BZCGIgqN+xrVgtdak2S9+A==} + '@rollup/rollup-openharmony-arm64@4.54.0': + resolution: {integrity: sha512-huT3fd0iC7jigGh7n3q/+lfPcXxBi+om/Rs3yiFxjvSxbSB6aohDFXbWvlspaqjeOh+hx7DDHS+5Es5qRkWkZg==} cpu: [arm64] os: [openharmony] - '@rollup/rollup-win32-arm64-msvc@4.53.2': - resolution: {integrity: sha512-IlbHFYc/pQCgew/d5fslcy1KEaYVCJ44G8pajugd8VoOEI8ODhtb/j8XMhLpwHCMB3yk2J07ctup10gpw2nyMA==} + '@rollup/rollup-win32-arm64-msvc@4.54.0': + resolution: {integrity: sha512-c2V0W1bsKIKfbLMBu/WGBz6Yci8nJ/ZJdheE0EwB73N3MvHYKiKGs3mVilX4Gs70eGeDaMqEob25Tw2Gb9Nqyw==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.53.2': - resolution: {integrity: sha512-lNlPEGgdUfSzdCWU176ku/dQRnA7W+Gp8d+cWv73jYrb8uT7HTVVxq62DUYxjbaByuf1Yk0RIIAbDzp+CnOTFg==} + '@rollup/rollup-win32-ia32-msvc@4.54.0': + resolution: {integrity: sha512-woEHgqQqDCkAzrDhvDipnSirm5vxUXtSKDYTVpZG3nUdW/VVB5VdCYA2iReSj/u3yCZzXID4kuKG7OynPnB3WQ==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-gnu@4.53.2': - resolution: {integrity: sha512-S6YojNVrHybQis2lYov1sd+uj7K0Q05NxHcGktuMMdIQ2VixGwAfbJ23NnlvvVV1bdpR2m5MsNBViHJKcA4ADw==} + '@rollup/rollup-win32-x64-gnu@4.54.0': + resolution: {integrity: sha512-dzAc53LOuFvHwbCEOS0rPbXp6SIhAf2txMP5p6mGyOXXw5mWY8NGGbPMPrs4P1WItkfApDathBj/NzMLUZ9rtQ==} cpu: [x64] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.53.2': - resolution: {integrity: sha512-k+/Rkcyx//P6fetPoLMb8pBeqJBNGx81uuf7iljX9++yNBVRDQgD04L+SVXmXmh5ZP4/WOp4mWF0kmi06PW2tA==} + '@rollup/rollup-win32-x64-msvc@4.54.0': + resolution: {integrity: sha512-hYT5d3YNdSh3mbCU1gwQyPgQd3T2ne0A3KG8KSBdav5TiBg6eInVmV+TeR5uHufiIgSFg0XsOWGW5/RhNcSvPg==} cpu: [x64] os: [win32] @@ -894,74 +1050,74 @@ packages: '@stablelib/x25519@2.0.1': resolution: {integrity: sha512-qi04HS2puHaBf50kM/kes5QcZFGsx8yF0YmCjLCOa/LPmnBaKEKX9ZR82OnnCwMn72YH13R/bBZgr/UP0aPFfA==} - '@standard-schema/spec@1.0.0': - resolution: {integrity: sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==} + '@standard-schema/spec@1.1.0': + resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} '@standard-schema/utils@0.3.0': resolution: {integrity: sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==} - '@swc/core-darwin-arm64@1.15.2': - resolution: {integrity: sha512-Ghyz4RJv4zyXzrUC1B2MLQBbppIB5c4jMZJybX2ebdEQAvryEKp3gq1kBksCNsatKGmEgXul88SETU19sMWcrw==} + '@swc/core-darwin-arm64@1.15.8': + resolution: {integrity: sha512-M9cK5GwyWWRkRGwwCbREuj6r8jKdES/haCZ3Xckgkl8MUQJZA3XB7IXXK1IXRNeLjg6m7cnoMICpXv1v1hlJOg==} engines: {node: '>=10'} cpu: [arm64] os: [darwin] - '@swc/core-darwin-x64@1.15.2': - resolution: {integrity: sha512-7n/PGJOcL2QoptzL42L5xFFfXY5rFxLHnuz1foU+4ruUTG8x2IebGhtwVTpaDN8ShEv2UZObBlT1rrXTba15Zw==} + '@swc/core-darwin-x64@1.15.8': + resolution: {integrity: sha512-j47DasuOvXl80sKJHSi2X25l44CMc3VDhlJwA7oewC1nV1VsSzwX+KOwE5tLnfORvVJJyeiXgJORNYg4jeIjYQ==} engines: {node: '>=10'} cpu: [x64] os: [darwin] - '@swc/core-linux-arm-gnueabihf@1.15.2': - resolution: {integrity: sha512-ZUQVCfRJ9wimuxkStRSlLwqX4TEDmv6/J+E6FicGkQ6ssLMWoKDy0cAo93HiWt/TWEee5vFhFaSQYzCuBEGO6A==} + '@swc/core-linux-arm-gnueabihf@1.15.8': + resolution: {integrity: sha512-siAzDENu2rUbwr9+fayWa26r5A9fol1iORG53HWxQL1J8ym4k7xt9eME0dMPXlYZDytK5r9sW8zEA10F2U3Xwg==} engines: {node: '>=10'} cpu: [arm] os: [linux] - '@swc/core-linux-arm64-gnu@1.15.2': - resolution: {integrity: sha512-GZh3pYBmfnpQ+JIg+TqLuz+pM+Mjsk5VOzi8nwKn/m+GvQBsxD5ectRtxuWUxMGNG8h0lMy4SnHRqdK3/iJl7A==} + '@swc/core-linux-arm64-gnu@1.15.8': + resolution: {integrity: sha512-o+1y5u6k2FfPYbTRUPvurwzNt5qd0NTumCTFscCNuBksycloXY16J8L+SMW5QRX59n4Hp9EmFa3vpvNHRVv1+Q==} engines: {node: '>=10'} cpu: [arm64] os: [linux] - '@swc/core-linux-arm64-musl@1.15.2': - resolution: {integrity: sha512-5av6VYZZeneiYIodwzGMlnyVakpuYZryGzFIbgu1XP8wVylZxduEzup4eP8atiMDFmIm+s4wn8GySJmYqeJC0A==} + '@swc/core-linux-arm64-musl@1.15.8': + resolution: {integrity: sha512-koiCqL09EwOP1S2RShCI7NbsQuG6r2brTqUYE7pV7kZm9O17wZ0LSz22m6gVibpwEnw8jI3IE1yYsQTVpluALw==} engines: {node: '>=10'} cpu: [arm64] os: [linux] - '@swc/core-linux-x64-gnu@1.15.2': - resolution: {integrity: sha512-1nO/UfdCLuT/uE/7oB3EZgTeZDCIa6nL72cFEpdegnqpJVNDI6Qb8U4g/4lfVPkmHq2lvxQ0L+n+JdgaZLhrRA==} + '@swc/core-linux-x64-gnu@1.15.8': + resolution: {integrity: sha512-4p6lOMU3bC+Vd5ARtKJ/FxpIC5G8v3XLoPEZ5s7mLR8h7411HWC/LmTXDHcrSXRC55zvAVia1eldy6zDLz8iFQ==} engines: {node: '>=10'} cpu: [x64] os: [linux] - '@swc/core-linux-x64-musl@1.15.2': - resolution: {integrity: sha512-Ksfrb0Tx310kr+TLiUOvB/I80lyZ3lSOp6cM18zmNRT/92NB4mW8oX2Jo7K4eVEI2JWyaQUAFubDSha2Q+439A==} + '@swc/core-linux-x64-musl@1.15.8': + resolution: {integrity: sha512-z3XBnbrZAL+6xDGAhJoN4lOueIxC/8rGrJ9tg+fEaeqLEuAtHSW2QHDHxDwkxZMjuF/pZ6MUTjHjbp8wLbuRLA==} engines: {node: '>=10'} cpu: [x64] os: [linux] - '@swc/core-win32-arm64-msvc@1.15.2': - resolution: {integrity: sha512-IzUb5RlMUY0r1A9IuJrQ7Tbts1wWb73/zXVXT8VhewbHGoNlBKE0qUhKMED6Tv4wDF+pmbtUJmKXDthytAvLmg==} + '@swc/core-win32-arm64-msvc@1.15.8': + resolution: {integrity: sha512-djQPJ9Rh9vP8GTS/Df3hcc6XP6xnG5c8qsngWId/BLA9oX6C7UzCPAn74BG/wGb9a6j4w3RINuoaieJB3t+7iQ==} engines: {node: '>=10'} cpu: [arm64] os: [win32] - '@swc/core-win32-ia32-msvc@1.15.2': - resolution: {integrity: sha512-kCATEzuY2LP9AlbU2uScjcVhgnCAkRdu62vbce17Ro5kxEHxYWcugkveyBRS3AqZGtwAKYbMAuNloer9LS/hpw==} + '@swc/core-win32-ia32-msvc@1.15.8': + resolution: {integrity: sha512-/wfAgxORg2VBaUoFdytcVBVCgf1isWZIEXB9MZEUty4wwK93M/PxAkjifOho9RN3WrM3inPLabICRCEgdHpKKQ==} engines: {node: '>=10'} cpu: [ia32] os: [win32] - '@swc/core-win32-x64-msvc@1.15.2': - resolution: {integrity: sha512-iJaHeYCF4jTn7OEKSa3KRiuVFIVYts8jYjNmCdyz1u5g8HRyTDISD76r8+ljEOgm36oviRQvcXaw6LFp1m0yyA==} + '@swc/core-win32-x64-msvc@1.15.8': + resolution: {integrity: sha512-GpMePrh9Sl4d61o4KAHOOv5is5+zt6BEXCOCgs/H0FLGeii7j9bWDE8ExvKFy2GRRZVNR1ugsnzaGWHKM6kuzA==} engines: {node: '>=10'} cpu: [x64] os: [win32] - '@swc/core@1.15.2': - resolution: {integrity: sha512-OQm+yJdXxvSjqGeaWhP6Ia264ogifwAO7Q12uTDVYj/Ks4jBTI4JknlcjDRAXtRhqbWsfbZyK/5RtuIPyptk3w==} + '@swc/core@1.15.8': + resolution: {integrity: sha512-T8keoJjXaSUoVBCIjgL6wAnhADIb09GOELzKg10CjNg+vLX48P93SME6jTfte9MZIm5m+Il57H3rTSk/0kzDUw==} engines: {node: '>=10'} peerDependencies: '@swc/helpers': '>=0.5.17' @@ -975,20 +1131,20 @@ packages: '@swc/types@0.1.25': resolution: {integrity: sha512-iAoY/qRhNH8a/hBvm3zKj9qQ4oc2+3w1unPJa2XvTK3XjeLXtzcCingVPw/9e5mn1+0yPqxcBGp9Jf0pkfMb1g==} - '@tanstack/query-core@5.90.10': - resolution: {integrity: sha512-EhZVFu9rl7GfRNuJLJ3Y7wtbTnENsvzp+YpcAV7kCYiXni1v8qZh++lpw4ch4rrwC0u/EZRnBHIehzCGzwXDSQ==} + '@tanstack/query-core@5.90.16': + resolution: {integrity: sha512-MvtWckSVufs/ja463/K4PyJeqT+HMlJWtw6PrCpywznd2NSgO3m4KwO9RqbFqGg6iDE8vVMFWMeQI4Io3eEYww==} - '@tanstack/query-devtools@5.90.1': - resolution: {integrity: sha512-GtINOPjPUH0OegJExZ70UahT9ykmAhmtNVcmtdnOZbxLwT7R5OmRztR5Ahe3/Cu7LArEmR6/588tAycuaWb1xQ==} + '@tanstack/query-devtools@5.92.0': + resolution: {integrity: sha512-N8D27KH1vEpVacvZgJL27xC6yPFUy0Zkezn5gnB3L3gRCxlDeSuiya7fKge8Y91uMTnC8aSxBQhcK6ocY7alpQ==} - '@tanstack/react-query-devtools@5.90.2': - resolution: {integrity: sha512-vAXJzZuBXtCQtrY3F/yUNJCV4obT/A/n81kb3+YqLbro5Z2+phdAbceO+deU3ywPw8B42oyJlp4FhO0SoivDFQ==} + '@tanstack/react-query-devtools@5.91.2': + resolution: {integrity: sha512-ZJ1503ay5fFeEYFUdo7LMNFzZryi6B0Cacrgr2h1JRkvikK1khgIq6Nq2EcblqEdIlgB/r7XDW8f8DQ89RuUgg==} peerDependencies: - '@tanstack/react-query': ^5.90.2 + '@tanstack/react-query': ^5.90.14 react: ^18 || ^19 - '@tanstack/react-query@5.90.10': - resolution: {integrity: sha512-BKLss9Y8PQ9IUjPYQiv3/Zmlx92uxffUOX8ZZNoQlCIZBJPT5M+GOMQj7xislvVQ6l1BstBjcX0XB/aHfFYVNw==} + '@tanstack/react-query@5.90.16': + resolution: {integrity: sha512-bpMGOmV4OPmif7TNMteU/Ehf/hoC0Kf98PDc0F4BZkFrEapRMEqI/V6YS0lyzwSV6PQpY1y4xxArUIfBW5LVxQ==} peerDependencies: react: ^18 || ^19 @@ -1061,8 +1217,8 @@ packages: '@types/lodash-es@4.17.12': resolution: {integrity: sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==} - '@types/lodash@4.17.20': - resolution: {integrity: sha512-H3MHACvFUEiujabxhaI/ImO6gUrd8oOurg7LQtS7mbwIXA/cUqWrvBsaeJ23aZEPk1TAYkurjfMbSELfoCXlGA==} + '@types/lodash@4.17.21': + resolution: {integrity: sha512-FOvQ0YPD5NOfPgMzJihoT+Za5pdkDJWcbpuj1DjaKZIr/gxodQjY/uWEFlTNqW2ugXHUiL8lRQgw63dzKHZdeQ==} '@types/mdast@4.0.4': resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==} @@ -1073,8 +1229,8 @@ packages: '@types/ms@2.1.0': resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} - '@types/node@24.10.1': - resolution: {integrity: sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==} + '@types/node@24.10.4': + resolution: {integrity: sha512-vnDVpYPMzs4wunl27jHrfmwojOGKya0xyM3sH+UE5iv5uPS6vX7UIoh6m+vQc5LGBq52HBKPIn/zcSZVzeDEZg==} '@types/normalize-package-data@2.4.4': resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==} @@ -1096,8 +1252,8 @@ packages: '@types/react-router@5.1.20': resolution: {integrity: sha512-jGjmu/ZqS7FjSH6owMcD5qpq19+1RS9DeVRqfl1FeBMxTDQAGwlMWOcs52NDoXaNKyG3d1cYQFMs9rCrb88o9Q==} - '@types/react@19.2.5': - resolution: {integrity: sha512-keKxkZMqnDicuvFoJbzrhbtdLSPhj/rZThDlKWCDbgXmUg0rEUFtRssDXKYmtXluZlIqiC5VqkCgRwzuyLHKHw==} + '@types/react@19.2.7': + resolution: {integrity: sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==} '@types/unist@2.0.11': resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==} @@ -1125,11 +1281,11 @@ packages: peerDependencies: vite: ^4 || ^5 || ^6 || ^7 - '@vitest/expect@4.0.14': - resolution: {integrity: sha512-RHk63V3zvRiYOWAV0rGEBRO820ce17hz7cI2kDmEdfQsBjT2luEKB5tCOc91u1oSQoUOZkSv3ZyzkdkSLD7lKw==} + '@vitest/expect@4.0.16': + resolution: {integrity: sha512-eshqULT2It7McaJkQGLkPjPjNph+uevROGuIMJdG3V+0BSR2w9u6J9Lwu+E8cK5TETlfou8GRijhafIMhXsimA==} - '@vitest/mocker@4.0.14': - resolution: {integrity: sha512-RzS5NujlCzeRPF1MK7MXLiEFpkIXeMdQ+rN3Kk3tDI9j0mtbr7Nmuq67tpkOJQpgyClbOltCXMjLZicJHsH5Cg==} + '@vitest/mocker@4.0.16': + resolution: {integrity: sha512-yb6k4AZxJTB+q9ycAvsoxGn+j/po0UaPgajllBgt1PzoMAAmJGYFdDk0uCcRcxb3BrME34I6u8gHZTQlkqSZpg==} peerDependencies: msw: ^2.4.9 vite: ^6.0.0 || ^7.0.0-0 @@ -1139,25 +1295,25 @@ packages: vite: optional: true - '@vitest/pretty-format@4.0.14': - resolution: {integrity: sha512-SOYPgujB6TITcJxgd3wmsLl+wZv+fy3av2PpiPpsWPZ6J1ySUYfScfpIt2Yv56ShJXR2MOA6q2KjKHN4EpdyRQ==} + '@vitest/pretty-format@4.0.16': + resolution: {integrity: sha512-eNCYNsSty9xJKi/UdVD8Ou16alu7AYiS2fCPRs0b1OdhJiV89buAXQLpTbe+X8V9L6qrs9CqyvU7OaAopJYPsA==} - '@vitest/runner@4.0.14': - resolution: {integrity: sha512-BsAIk3FAqxICqREbX8SetIteT8PiaUL/tgJjmhxJhCsigmzzH8xeadtp7LRnTpCVzvf0ib9BgAfKJHuhNllKLw==} + '@vitest/runner@4.0.16': + resolution: {integrity: sha512-VWEDm5Wv9xEo80ctjORcTQRJ539EGPB3Pb9ApvVRAY1U/WkHXmmYISqU5E79uCwcW7xYUV38gwZD+RV755fu3Q==} - '@vitest/snapshot@4.0.14': - resolution: {integrity: sha512-aQVBfT1PMzDSA16Y3Fp45a0q8nKexx6N5Amw3MX55BeTeZpoC08fGqEZqVmPcqN0ueZsuUQ9rriPMhZ3Mu19Ag==} + '@vitest/snapshot@4.0.16': + resolution: {integrity: sha512-sf6NcrYhYBsSYefxnry+DR8n3UV4xWZwWxYbCJUt2YdvtqzSPR7VfGrY0zsv090DAbjFZsi7ZaMi1KnSRyK1XA==} - '@vitest/spy@4.0.14': - resolution: {integrity: sha512-JmAZT1UtZooO0tpY3GRyiC/8W7dCs05UOq9rfsUUgEZEdq+DuHLmWhPsrTt0TiW7WYeL/hXpaE07AZ2RCk44hg==} + '@vitest/spy@4.0.16': + resolution: {integrity: sha512-4jIOWjKP0ZUaEmJm00E0cOBLU+5WE0BpeNr3XN6TEF05ltro6NJqHWxXD0kA8/Zc8Nh23AT8WQxwNG+WeROupw==} - '@vitest/ui@4.0.14': - resolution: {integrity: sha512-fvDz8o7SQpFLoSBo6Cudv+fE85/fPCkwTnLAN85M+Jv7k59w2mSIjT9Q5px7XwGrmYqqKBEYxh/09IBGd1E7AQ==} + '@vitest/ui@4.0.16': + resolution: {integrity: sha512-rkoPH+RqWopVxDnCBE/ysIdfQ2A7j1eDmW8tCxxrR9nnFBa9jKf86VgsSAzxBd1x+ny0GC4JgiD3SNfRHv3pOg==} peerDependencies: - vitest: 4.0.14 + vitest: 4.0.16 - '@vitest/utils@4.0.14': - resolution: {integrity: sha512-hLqXZKAWNg8pI+SQXyXxWCTOpA3MvsqcbVeNgSi8x/CSN2wi26dSzn1wrOhmCmFjEvN9p8/kLFRHa6PI8jHazw==} + '@vitest/utils@4.0.16': + resolution: {integrity: sha512-h8z9yYhV3e1LEfaQ3zdypIrnAg/9hguReGZoS7Gl0aBG5xgA410zBqECqmaF/+RkTggRsfnzc1XaAHA6bmUufA==} JSONStream@1.3.5: resolution: {integrity: sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==} @@ -1201,8 +1357,8 @@ packages: asynckit@0.4.0: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} - autoprefixer@10.4.22: - resolution: {integrity: sha512-ARe0v/t9gO28Bznv6GgqARmVqcWOV3mfgUPn9becPHMiD3o9BwlRgaeccZnwTpZ7Zwqrm+c1sUSsMxIzQzc8Xg==} + autoprefixer@10.4.23: + resolution: {integrity: sha512-YYTXSFulfwytnjAPlw8QHncHJmlvFKtczb8InXaAx9Q0LbfDnfEYDE55omerIJKihhmU61Ft+cAOSzQVaBUmeA==} engines: {node: ^10 || ^12 || >=14} hasBin: true peerDependencies: @@ -1221,8 +1377,8 @@ packages: balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} - baseline-browser-mapping@2.8.29: - resolution: {integrity: sha512-sXdt2elaVnhpDNRDz+1BDx1JQoJRuNk7oVlAlbGiFkLikHCAQiccexF/9e91zVi6RCgqspl04aP+6Cnl9zRLrA==} + baseline-browser-mapping@2.9.11: + resolution: {integrity: sha512-Sg0xJUNDU1sJNGdfGWhVHX0kkZ+HWcvmVymJbj6NSgZZmW/8S9Y2HQ5euytnIgakgxN6papOAWiwDo1ctFDcoQ==} hasBin: true bignumber.js@9.3.1: @@ -1239,8 +1395,8 @@ packages: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} - browserslist@4.28.0: - resolution: {integrity: sha512-tbydkR/CxfMwelN0vwdP/pLkDwyAASZ+VfWm4EOwlB6SWhx1sYnWLqo8N5j0rAzPfzfRaxt0mM/4wPU/Su84RQ==} + browserslist@4.28.1: + resolution: {integrity: sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true @@ -1276,14 +1432,14 @@ packages: resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} engines: {node: '>=6'} - caniuse-lite@1.0.30001755: - resolution: {integrity: sha512-44V+Jm6ctPj7R52Na4TLi3Zri4dWUljJd+RDm+j8LtNCc/ihLCT+X1TzoOAkRETEWqjuLnh9581Tl80FvK7jVA==} + caniuse-lite@1.0.30001762: + resolution: {integrity: sha512-PxZwGNvH7Ak8WX5iXzoK1KPZttBXNPuaOvI2ZYU7NrlM+d9Ov+TUvlLOBNGzVXAntMSMMlJPd+jY6ovrVjSmUw==} ccount@2.0.1: resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} - chai@6.2.1: - resolution: {integrity: sha512-p4Z49OGG5W/WBCPSS/dH3jQ73kD6tiMmUM+bckNK6Jr5JHMG3k9bg/BvKR8lKmtVBKmOiuVaV2ws8s9oSbwysg==} + chai@6.2.2: + resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==} engines: {node: '>=18'} chalk@2.4.2: @@ -1593,8 +1749,8 @@ packages: resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} engines: {node: '>= 0.4'} - electron-to-chromium@1.5.254: - resolution: {integrity: sha512-DcUsWpVhv9svsKRxnSCZ86SjD+sp32SGidNB37KpqXJncp1mfUgKbHvBomE89WJDbfVKw1mdv5+ikrvd43r+Bg==} + electron-to-chromium@1.5.267: + resolution: {integrity: sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==} emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} @@ -1629,14 +1785,19 @@ packages: resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} engines: {node: '>= 0.4'} - es-toolkit@1.42.0: - resolution: {integrity: sha512-SLHIyY7VfDJBM8clz4+T2oquwTQxEzu263AyhVK4jREOAwJ+8eebaa4wM3nlvnAqhDrMm2EsA6hWHaQsMPQ1nA==} + es-toolkit@1.43.0: + resolution: {integrity: sha512-SKCT8AsWvYzBBuUqMk4NPwFlSdqLpJwmy6AP322ERn8W2YLIB6JBXnwMI2Qsh2gfphT3q7EKAxKb23cvFHFwKA==} esbuild@0.25.12: resolution: {integrity: sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==} engines: {node: '>=18'} hasBin: true + esbuild@0.27.2: + resolution: {integrity: sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==} + engines: {node: '>=18'} + hasBin: true + escalade@3.2.0: resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} engines: {node: '>=6'} @@ -1662,8 +1823,8 @@ packages: resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} engines: {node: '>=0.8.x'} - expect-type@1.2.2: - resolution: {integrity: sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA==} + expect-type@1.3.0: + resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==} engines: {node: '>=12.0.0'} extend@3.0.2: @@ -1733,8 +1894,8 @@ packages: fraction.js@5.3.4: resolution: {integrity: sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==} - framer-motion@12.23.24: - resolution: {integrity: sha512-HMi5HRoRCTou+3fb3h9oTLyJGBxHfW+HnNE25tAXOvVx/IvwMHK0cx7IR4a2ZU6sh3IX1Z+4ts32PcYBOqka8w==} + framer-motion@12.23.26: + resolution: {integrity: sha512-cPcIhgR42xBn1Uj+PzOyheMtZ73H927+uWPDVhUMqxy8UHt6Okavb6xIz9J/phFUHUj0OncR6UvMfJTXoc/LKA==} peerDependencies: '@emotion/is-prop-valid': '*' react: ^18.0.0 || ^19.0.0 @@ -1862,8 +2023,8 @@ packages: hast-util-to-jsx-runtime@2.3.6: resolution: {integrity: sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==} - hast-util-to-parse5@8.0.0: - resolution: {integrity: sha512-3KKrV5ZVI8if87DVSi1vDeByYrkGzg4mEfeu4alwgmmIeARiBLKCZS2uw5Gb6nU9x9Yufyj3iudm6i7nl52PFw==} + hast-util-to-parse5@8.0.1: + resolution: {integrity: sha512-MlWT6Pjt4CG9lFCjiz4BH7l9wmrMkfkJYCxFwKQic8+RTZgWPuWxwAfjJElsXkex7DJjfSJsQIt931ilUgmwdA==} hast-util-whitespace@3.0.0: resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==} @@ -1888,8 +2049,8 @@ packages: html-dom-parser@5.1.2: resolution: {integrity: sha512-9nD3Rj3/FuQt83AgIa1Y3ruzspwFFA54AJbQnohXN+K6fL1/bhcDQJJY5Ne4L4A163ADQFVESd/0TLyNoV0mfg==} - html-react-parser@5.2.10: - resolution: {integrity: sha512-DjOLloguuDA+Ed7Q7PKhvMQmCl2+Yk/pfvvca68fvn15QFBbL4uHGxXwoXQ4sqS0UyuRH2lJb0S8yZCL3lvehQ==} + html-react-parser@5.2.11: + resolution: {integrity: sha512-WnSQVn/D1UTj64nSz5y8MriL+MrbsZH80Ytr1oqKqs8DGZnphWY1R1pl3t7TY3rpqTSu+FHA21P80lrsmrdNBA==} peerDependencies: '@types/react': 0.14 || 15 || 16 || 17 || 18 || 19 react: 0.14 || 15 || 16 || 17 || 18 || 19 @@ -1906,12 +2067,15 @@ packages: htmlparser2@10.0.0: resolution: {integrity: sha512-TwAZM+zE5Tq3lrEHvOlvwgj1XLWQCtaaibSN11Q+gGBAS7Y1uZSWwXXRe4iF6OXnaq1riyQAPFOBtYc77Mxq0g==} - humanize-duration@3.33.1: - resolution: {integrity: sha512-hwzSCymnRdFx9YdRkQQ0OYequXiVAV6ZGQA2uzocwB0F4309Ke6pO8dg0P8LHhRQJyVjGteRTAA/zNfEcpXn8A==} + humanize-duration@3.33.2: + resolution: {integrity: sha512-K7Ny/ULO1hDm2nnhvAY+SJV1skxFb61fd073SG1IWJl+D44ULrruCuTyjHKjBVVcSuTlnY99DKtgEG39CM5QOQ==} immer@10.2.0: resolution: {integrity: sha512-d/+XTN3zfODyjr89gM3mPq1WNX2B8pYsu7eORitdwyA2sBubnTl3laYlBk4sXY5FUa5qTZGBDPJICVbvqzjlbw==} + immer@11.1.3: + resolution: {integrity: sha512-6jQTc5z0KJFtr1UgFpIL3N9XSC3saRaI9PwWtzM2pSqkNGtiNkYY2OSwkOGDK2XcTRcLb1pi/aNkKZz0nxVH4Q==} + immutable@4.3.7: resolution: {integrity: sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw==} @@ -1936,8 +2100,8 @@ packages: resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==} engines: {node: '>=12'} - ipaddr.js@2.2.0: - resolution: {integrity: sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==} + ipaddr.js@2.3.0: + resolution: {integrity: sha512-Zv/pA+ciVFbCSBBjGfaKUya/CcGmUHzTydLMaTwrUUEM2DIEO3iZvueGxmacvmN50fGpGVKeTXpb2LcYQxeVdg==} engines: {node: '>= 10'} is-absolute-url@4.0.1: @@ -2070,8 +2234,8 @@ packages: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} - lodash-es@4.17.21: - resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==} + lodash-es@4.17.22: + resolution: {integrity: sha512-XEawp1t0gxSi9x01glktRZ5HDy0HXqrM0x5pXQM98EaI0NxO6jVM7omDOxsuEo5UIASAnm2bRp1Jt/e0a2XU8Q==} lodash.ismatch@4.4.0: resolution: {integrity: sha512-fPMfXjGQEV9Xsq/8MTSgUf255gawYRbjwMyDbcvDhXgV7enSZA0hynz6vMPnpAb5iONEzBHBPsT+0zes5Z301g==} @@ -2243,8 +2407,8 @@ packages: motion-utils@12.23.6: resolution: {integrity: sha512-eAWoPgr4eFEOFfg2WjIsMoqJTW6Z8MTUCgn/GZ3VRpClWBdnbjryiA3ZSNLyxCTmCQx4RmYX6jX1iWHbenUPNQ==} - motion@12.23.24: - resolution: {integrity: sha512-Rc5E7oe2YZ72N//S3QXGzbnXgqNrTESv8KKxABR20q2FLch9gHLo0JLyYo2hZ238bZ9Gx6cWhj9VO0IgwbMjCw==} + motion@12.23.26: + resolution: {integrity: sha512-Ll8XhVxY8LXMVYTCfme27WH2GjBrCIzY4+ndr5QKxsK+YwCtOi2B/oBi5jcIbik5doXuWT/4KKDOVAZJkeY5VQ==} peerDependencies: '@emotion/is-prop-valid': '*' react: ^18.0.0 || ^19.0.0 @@ -2289,10 +2453,6 @@ packages: 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'} - numbro@2.5.0: resolution: {integrity: sha512-xDcctDimhzko/e+y+Q2/8i3qNC9Svw1QgOkSkQoO0kIPI473tR9QRbo2KP88Ty9p8WbPy+3OpTaAIzehtuHq+A==} @@ -2413,8 +2573,8 @@ packages: resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} engines: {node: ^10 || ^12 || >=14} - prettier@3.6.2: - resolution: {integrity: sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==} + prettier@3.7.4: + resolution: {integrity: sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==} engines: {node: '>=14'} hasBin: true @@ -2424,9 +2584,6 @@ packages: prop-types@15.8.1: resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} - property-information@6.5.0: - resolution: {integrity: sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==} - property-information@7.1.0: resolution: {integrity: sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==} @@ -2452,8 +2609,8 @@ packages: engines: {node: '>=10.13.0'} hasBin: true - qs@6.14.0: - resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==} + qs@6.14.1: + resolution: {integrity: sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==} engines: {node: '>=0.6'} quick-lru@4.0.1: @@ -2470,19 +2627,19 @@ packages: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - react-datepicker@8.9.0: - resolution: {integrity: sha512-yoRsGxjqVRjk8iUBssrW9jcinTeyP9mAfTpuzdKvlESOUjdrY0sfDTzIZWJAn38jvNcxW1dnDmW1CinjiFdxYQ==} + react-datepicker@8.10.0: + resolution: {integrity: sha512-JIXuA+g+qP3c4MVJpx24o7n1gnv3WV/8A/D6964HucY1FlSEc30+ITPNUfbKZXYHl5rruCtxYCwi2lzn7gaz7g==} peerDependencies: react: ^16.9.0 || ^17 || ^18 || ^19 || ^19.0.0-rc react-dom: ^16.9.0 || ^17 || ^18 || ^19 || ^19.0.0-rc - react-dom@19.2.0: - resolution: {integrity: sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ==} + react-dom@19.2.3: + resolution: {integrity: sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==} peerDependencies: - react: ^19.2.0 + react: ^19.2.3 - react-hook-form@7.66.0: - resolution: {integrity: sha512-xXBqsWGKrY46ZqaHDo+ZUYiMUgi8suYu5kdrS20EG8KiL7VRQitEbNjm+UcrDYrNi1YLyfpmAeGjCZYXLT9YBw==} + react-hook-form@7.69.0: + resolution: {integrity: sha512-yt6ZGME9f4F6WHwevrvpAjh42HMvocuSnSIHUGycBqXIJdhqGSPQzTpGF+1NLREk/58IdPxEMfPcFCjlMhclGw==} engines: {node: '>=18.0.0'} peerDependencies: react: ^16.8.0 || ^17 || ^18 || ^19 @@ -2505,8 +2662,8 @@ packages: react-is@16.13.1: resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} - react-is@19.2.0: - resolution: {integrity: sha512-x3Ax3kNSMIIkyVYhWPyO09bu0uttcAIoecO/um/rKGQ4EltYWVYtyiGkS/3xMynrbVQdS69Jhlv8FXUEZehlzA==} + react-is@19.2.3: + resolution: {integrity: sha512-qJNJfu81ByyabuG7hPFEbXqNcWSU3+eVus+KJs+0ncpGfMyYdvSmxiJxbWR65lYi1I+/0HBcliO029gc4F+PnA==} react-loading-skeleton@3.5.0: resolution: {integrity: sha512-gxxSyLbrEAdXTKgfbpBEFZCO/P153DnqSCQau2+o6lNy1jgMRr2MmRmOzMmyrwSaSYLRB8g7b0waYPmUjz7IhQ==} @@ -2574,8 +2731,8 @@ packages: react: ^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0 || ^19.0.0 react-dom: ^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0 || ^19.0.0 - react@19.2.0: - resolution: {integrity: sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==} + react@19.2.3: + resolution: {integrity: sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==} engines: {node: '>=0.10.0'} read-pkg-up@3.0.0: @@ -2605,8 +2762,8 @@ packages: resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} engines: {node: '>=8.10.0'} - recharts@3.4.1: - resolution: {integrity: sha512-35kYg6JoOgwq8sE4rhYkVWwa6aAIgOtT+Ob0gitnShjwUwZmhrmy7Jco/5kJNF4PnLXgt9Hwq+geEMS+WrjU1g==} + recharts@3.6.0: + resolution: {integrity: sha512-L5bjxvQRAe26RlToBAziKUB7whaGKEwD3znoM6fz3DrTowCIC/FnJYnuq1GEzB8Zv2kdTfaxQfi5GoH0tBinyg==} engines: {node: '>=18'} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 @@ -2659,8 +2816,8 @@ packages: engines: {node: '>= 0.4'} hasBin: true - rollup@4.53.2: - resolution: {integrity: sha512-MHngMYwGJVi6Fmnk6ISmnk7JAHRNF0UkuucA0CUW3N3a4KnONPEZz+vUanQP/ZC/iY1Qkf3bwPWzyY84wEks1g==} + rollup@4.54.0: + resolution: {integrity: sha512-3nk8Y3a9Ea8szgKhinMlGMhGMw89mqule3KWczxhIzqudyHdCIOHw8WJlj/r329fACjKLEh13ZSk7oE22kyeIw==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true @@ -2828,8 +2985,8 @@ packages: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} - tabbable@6.3.0: - resolution: {integrity: sha512-EIHvdY5bPLuWForiR/AN2Bxngzpuwn1is4asboytXtpTgsArc+WmSJKVLlhdh71u7jFcryDqB2A8lQvj78MkyQ==} + tabbable@6.4.0: + resolution: {integrity: sha512-05PUHKSNE8ou2dwIxTngl4EzcnsCDZGJ/iCLtDflR/SHB/ny14rXc+qU5P4mG9JkusiV7EivzY9Mhm55AzAvCg==} terser@5.37.0: resolution: {integrity: sha512-B8wRRkmre4ERucLM/uXx4MOV5cbnOlVAqUst+1+iLKPI0dOgFO28f84ptoQt9HEI537PMzfYa/d+GEPKTRXmYA==} @@ -2918,8 +3075,9 @@ packages: tinybench@2.9.0: resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} - tinyexec@0.3.2: - resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} + tinyexec@1.0.2: + resolution: {integrity: sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==} + engines: {node: '>=18'} tinyglobby@0.2.15: resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} @@ -3010,8 +3168,8 @@ packages: unist-util-visit@5.0.0: resolution: {integrity: sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==} - update-browserslist-db@1.1.4: - resolution: {integrity: sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A==} + update-browserslist-db@1.2.3: + resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==} hasBin: true peerDependencies: browserslist: '>= 4.21.0' @@ -3066,8 +3224,8 @@ packages: peerDependencies: vite: '>=2.0.0-beta.69' - vite@7.2.2: - resolution: {integrity: sha512-BxAKBWmIbrDgrokdGZH1IgkIk/5mMHDreLDmCJ0qpyJaAteP8NvMhkwr/ZCQNqNH97bw/dANTE9PDzqwJghfMQ==} + vite@7.3.0: + resolution: {integrity: sha512-dZwN5L1VlUBewiP6H9s2+B3e3Jg96D0vzN+Ry73sOefebhYr9f94wwkMNN/9ouoU8pV1BqA1d1zGk8928cx0rg==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: @@ -3106,18 +3264,18 @@ packages: yaml: optional: true - vitest@4.0.14: - resolution: {integrity: sha512-d9B2J9Cm9dN9+6nxMnnNJKJCtcyKfnHj15N6YNJfaFHRLua/d3sRKU9RuKmO9mB0XdFtUizlxfz/VPbd3OxGhw==} + vitest@4.0.16: + resolution: {integrity: sha512-E4t7DJ9pESL6E3I8nFjPa4xGUd3PmiWDLsDztS2qXSJWfHtbQnwAWylaBvSNY48I3vr8PTqIZlyK8TE3V3CA4Q==} engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} hasBin: true peerDependencies: '@edge-runtime/vm': '*' '@opentelemetry/api': ^1.9.0 '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 - '@vitest/browser-playwright': 4.0.14 - '@vitest/browser-preview': 4.0.14 - '@vitest/browser-webdriverio': 4.0.14 - '@vitest/ui': 4.0.14 + '@vitest/browser-playwright': 4.0.16 + '@vitest/browser-preview': 4.0.16 + '@vitest/browser-webdriverio': 4.0.16 + '@vitest/ui': 4.0.16 happy-dom: '*' jsdom: '*' peerDependenciesMeta: @@ -3219,8 +3377,8 @@ packages: zod@3.25.76: resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} - zustand@5.0.8: - resolution: {integrity: sha512-gyPKpIaxY9XcO2vSMrLbiER7QMAMGOQZVRdJ6Zi782jkbzZygq5GI9nG8g+sMgitRtndwaBSl7uiqC49o1SSiw==} + zustand@5.0.9: + resolution: {integrity: sha512-ALBtUj0AfjJt3uNRQoL1tL2tMvj6Gp/6e39dnfT6uzpelGru8v1tPOGBzayOWbPJvujM8JojDk3E1LxeFisBNg==} engines: {node: '>=12.20.0'} peerDependencies: '@types/react': '>=18.0.0' @@ -3282,7 +3440,7 @@ snapshots: dependencies: '@babel/compat-data': 7.28.5 '@babel/helper-validator-option': 7.27.1 - browserslist: 4.28.0 + browserslist: 4.28.1 lru-cache: 5.1.1 semver: 6.3.1 @@ -3417,19 +3575,19 @@ snapshots: '@emotion/memoize@0.9.0': {} - '@emotion/react@11.14.0(@types/react@19.2.5)(react@19.2.0)': + '@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.3)': dependencies: '@babel/runtime': 7.28.4 '@emotion/babel-plugin': 11.13.5 '@emotion/cache': 11.14.0 '@emotion/serialize': 1.3.3 - '@emotion/use-insertion-effect-with-fallbacks': 1.2.0(react@19.2.0) + '@emotion/use-insertion-effect-with-fallbacks': 1.2.0(react@19.2.3) '@emotion/utils': 1.4.2 '@emotion/weak-memoize': 0.4.0 hoist-non-react-statics: 3.3.2 - react: 19.2.0 + react: 19.2.3 optionalDependencies: - '@types/react': 19.2.5 + '@types/react': 19.2.7 transitivePeerDependencies: - supports-color @@ -3443,26 +3601,26 @@ snapshots: '@emotion/sheet@1.4.0': {} - '@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.5)(react@19.2.0))(@types/react@19.2.5)(react@19.2.0)': + '@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.3))(@types/react@19.2.7)(react@19.2.3)': dependencies: '@babel/runtime': 7.28.4 '@emotion/babel-plugin': 11.13.5 '@emotion/is-prop-valid': 1.4.0 - '@emotion/react': 11.14.0(@types/react@19.2.5)(react@19.2.0) + '@emotion/react': 11.14.0(@types/react@19.2.7)(react@19.2.3) '@emotion/serialize': 1.3.3 - '@emotion/use-insertion-effect-with-fallbacks': 1.2.0(react@19.2.0) + '@emotion/use-insertion-effect-with-fallbacks': 1.2.0(react@19.2.3) '@emotion/utils': 1.4.2 - react: 19.2.0 + react: 19.2.3 optionalDependencies: - '@types/react': 19.2.5 + '@types/react': 19.2.7 transitivePeerDependencies: - supports-color '@emotion/unitless@0.10.0': {} - '@emotion/use-insertion-effect-with-fallbacks@1.2.0(react@19.2.0)': + '@emotion/use-insertion-effect-with-fallbacks@1.2.0(react@19.2.3)': dependencies: - react: 19.2.0 + react: 19.2.3 '@emotion/utils@1.4.2': {} @@ -3471,81 +3629,159 @@ snapshots: '@esbuild/aix-ppc64@0.25.12': optional: true + '@esbuild/aix-ppc64@0.27.2': + optional: true + '@esbuild/android-arm64@0.25.12': optional: true + '@esbuild/android-arm64@0.27.2': + optional: true + '@esbuild/android-arm@0.25.12': optional: true + '@esbuild/android-arm@0.27.2': + optional: true + '@esbuild/android-x64@0.25.12': optional: true + '@esbuild/android-x64@0.27.2': + optional: true + '@esbuild/darwin-arm64@0.25.12': optional: true + '@esbuild/darwin-arm64@0.27.2': + optional: true + '@esbuild/darwin-x64@0.25.12': optional: true + '@esbuild/darwin-x64@0.27.2': + optional: true + '@esbuild/freebsd-arm64@0.25.12': optional: true + '@esbuild/freebsd-arm64@0.27.2': + optional: true + '@esbuild/freebsd-x64@0.25.12': optional: true + '@esbuild/freebsd-x64@0.27.2': + optional: true + '@esbuild/linux-arm64@0.25.12': optional: true + '@esbuild/linux-arm64@0.27.2': + optional: true + '@esbuild/linux-arm@0.25.12': optional: true + '@esbuild/linux-arm@0.27.2': + optional: true + '@esbuild/linux-ia32@0.25.12': optional: true + '@esbuild/linux-ia32@0.27.2': + optional: true + '@esbuild/linux-loong64@0.25.12': optional: true + '@esbuild/linux-loong64@0.27.2': + optional: true + '@esbuild/linux-mips64el@0.25.12': optional: true + '@esbuild/linux-mips64el@0.27.2': + optional: true + '@esbuild/linux-ppc64@0.25.12': optional: true + '@esbuild/linux-ppc64@0.27.2': + optional: true + '@esbuild/linux-riscv64@0.25.12': optional: true + '@esbuild/linux-riscv64@0.27.2': + optional: true + '@esbuild/linux-s390x@0.25.12': optional: true + '@esbuild/linux-s390x@0.27.2': + optional: true + '@esbuild/linux-x64@0.25.12': optional: true + '@esbuild/linux-x64@0.27.2': + optional: true + '@esbuild/netbsd-arm64@0.25.12': optional: true + '@esbuild/netbsd-arm64@0.27.2': + optional: true + '@esbuild/netbsd-x64@0.25.12': optional: true + '@esbuild/netbsd-x64@0.27.2': + optional: true + '@esbuild/openbsd-arm64@0.25.12': optional: true + '@esbuild/openbsd-arm64@0.27.2': + optional: true + '@esbuild/openbsd-x64@0.25.12': optional: true + '@esbuild/openbsd-x64@0.27.2': + optional: true + '@esbuild/openharmony-arm64@0.25.12': optional: true + '@esbuild/openharmony-arm64@0.27.2': + optional: true + '@esbuild/sunos-x64@0.25.12': optional: true + '@esbuild/sunos-x64@0.27.2': + optional: true + '@esbuild/win32-arm64@0.25.12': optional: true + '@esbuild/win32-arm64@0.27.2': + optional: true + '@esbuild/win32-ia32@0.25.12': optional: true + '@esbuild/win32-ia32@0.27.2': + optional: true + '@esbuild/win32-x64@0.25.12': optional: true + '@esbuild/win32-x64@0.27.2': + optional: true + '@floating-ui/core@1.7.3': dependencies: '@floating-ui/utils': 0.2.10 @@ -3555,44 +3791,44 @@ snapshots: '@floating-ui/core': 1.7.3 '@floating-ui/utils': 0.2.10 - '@floating-ui/react-dom@2.1.6(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + '@floating-ui/react-dom@2.1.6(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: '@floating-ui/dom': 1.7.4 - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) - '@floating-ui/react@0.27.16(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + '@floating-ui/react@0.27.16(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: - '@floating-ui/react-dom': 2.1.6(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@floating-ui/react-dom': 2.1.6(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@floating-ui/utils': 0.2.10 - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - tabbable: 6.3.0 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + tabbable: 6.4.0 '@floating-ui/utils@0.2.10': {} '@github/webauthn-json@2.1.1': {} - '@hookform/devtools@4.4.0(@types/react@19.2.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + '@hookform/devtools@4.4.0(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: - '@emotion/react': 11.14.0(@types/react@19.2.5)(react@19.2.0) - '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.2.5)(react@19.2.0))(@types/react@19.2.5)(react@19.2.0) - '@types/lodash': 4.17.20 - little-state-machine: 4.8.1(react@19.2.0) + '@emotion/react': 11.14.0(@types/react@19.2.7)(react@19.2.3) + '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.3))(@types/react@19.2.7)(react@19.2.3) + '@types/lodash': 4.17.21 + little-state-machine: 4.8.1(react@19.2.3) lodash: 4.17.21 - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - react-simple-animate: 3.5.3(react-dom@19.2.0(react@19.2.0)) - use-deep-compare-effect: 1.8.1(react@19.2.0) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + react-simple-animate: 3.5.3(react-dom@19.2.3(react@19.2.3)) + use-deep-compare-effect: 1.8.1(react@19.2.3) uuid: 8.3.2 transitivePeerDependencies: - '@types/react' - supports-color - '@hookform/resolvers@5.2.2(react-hook-form@7.66.0(react@19.2.0))': + '@hookform/resolvers@5.2.2(react-hook-form@7.69.0(react@19.2.3))': dependencies: '@standard-schema/utils': 0.3.0 - react-hook-form: 7.66.0(react@19.2.0) + react-hook-form: 7.69.0(react@19.2.3) '@hutson/parse-repository-url@3.0.2': {} @@ -3623,107 +3859,107 @@ snapshots: '@polka/url@1.0.0-next.29': {} - '@react-hook/latest@1.0.3(react@19.2.0)': + '@react-hook/latest@1.0.3(react@19.2.3)': dependencies: - react: 19.2.0 + react: 19.2.3 - '@react-hook/passive-layout-effect@1.2.1(react@19.2.0)': + '@react-hook/passive-layout-effect@1.2.1(react@19.2.3)': dependencies: - react: 19.2.0 + react: 19.2.3 - '@react-hook/resize-observer@2.0.2(react@19.2.0)': + '@react-hook/resize-observer@2.0.2(react@19.2.3)': dependencies: - '@react-hook/latest': 1.0.3(react@19.2.0) - '@react-hook/passive-layout-effect': 1.2.1(react@19.2.0) - react: 19.2.0 + '@react-hook/latest': 1.0.3(react@19.2.3) + '@react-hook/passive-layout-effect': 1.2.1(react@19.2.3) + react: 19.2.3 - '@react-rxjs/core@0.10.8(react@19.2.0)(rxjs@7.8.2)': + '@react-rxjs/core@0.10.8(react@19.2.3)(rxjs@7.8.2)': dependencies: '@rx-state/core': 0.1.4(rxjs@7.8.2) - react: 19.2.0 + react: 19.2.3 rxjs: 7.8.2 - use-sync-external-store: 1.6.0(react@19.2.0) + use-sync-external-store: 1.6.0(react@19.2.3) - '@reduxjs/toolkit@2.10.1(react-redux@9.2.0(@types/react@19.2.5)(react@19.2.0)(redux@5.0.1))(react@19.2.0)': + '@reduxjs/toolkit@2.11.2(react-redux@9.2.0(@types/react@19.2.7)(react@19.2.3)(redux@5.0.1))(react@19.2.3)': dependencies: - '@standard-schema/spec': 1.0.0 + '@standard-schema/spec': 1.1.0 '@standard-schema/utils': 0.3.0 - immer: 10.2.0 + immer: 11.1.3 redux: 5.0.1 redux-thunk: 3.1.0(redux@5.0.1) reselect: 5.1.1 optionalDependencies: - react: 19.2.0 - react-redux: 9.2.0(@types/react@19.2.5)(react@19.2.0)(redux@5.0.1) + react: 19.2.3 + react-redux: 9.2.0(@types/react@19.2.7)(react@19.2.3)(redux@5.0.1) '@remix-run/router@1.23.1': {} '@rolldown/pluginutils@1.0.0-beta.47': {} - '@rollup/rollup-android-arm-eabi@4.53.2': + '@rollup/rollup-android-arm-eabi@4.54.0': optional: true - '@rollup/rollup-android-arm64@4.53.2': + '@rollup/rollup-android-arm64@4.54.0': optional: true - '@rollup/rollup-darwin-arm64@4.53.2': + '@rollup/rollup-darwin-arm64@4.54.0': optional: true - '@rollup/rollup-darwin-x64@4.53.2': + '@rollup/rollup-darwin-x64@4.54.0': optional: true - '@rollup/rollup-freebsd-arm64@4.53.2': + '@rollup/rollup-freebsd-arm64@4.54.0': optional: true - '@rollup/rollup-freebsd-x64@4.53.2': + '@rollup/rollup-freebsd-x64@4.54.0': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.53.2': + '@rollup/rollup-linux-arm-gnueabihf@4.54.0': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.53.2': + '@rollup/rollup-linux-arm-musleabihf@4.54.0': optional: true - '@rollup/rollup-linux-arm64-gnu@4.53.2': + '@rollup/rollup-linux-arm64-gnu@4.54.0': optional: true - '@rollup/rollup-linux-arm64-musl@4.53.2': + '@rollup/rollup-linux-arm64-musl@4.54.0': optional: true - '@rollup/rollup-linux-loong64-gnu@4.53.2': + '@rollup/rollup-linux-loong64-gnu@4.54.0': optional: true - '@rollup/rollup-linux-ppc64-gnu@4.53.2': + '@rollup/rollup-linux-ppc64-gnu@4.54.0': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.53.2': + '@rollup/rollup-linux-riscv64-gnu@4.54.0': optional: true - '@rollup/rollup-linux-riscv64-musl@4.53.2': + '@rollup/rollup-linux-riscv64-musl@4.54.0': optional: true - '@rollup/rollup-linux-s390x-gnu@4.53.2': + '@rollup/rollup-linux-s390x-gnu@4.54.0': optional: true - '@rollup/rollup-linux-x64-gnu@4.53.2': + '@rollup/rollup-linux-x64-gnu@4.54.0': optional: true - '@rollup/rollup-linux-x64-musl@4.53.2': + '@rollup/rollup-linux-x64-musl@4.54.0': optional: true - '@rollup/rollup-openharmony-arm64@4.53.2': + '@rollup/rollup-openharmony-arm64@4.54.0': optional: true - '@rollup/rollup-win32-arm64-msvc@4.53.2': + '@rollup/rollup-win32-arm64-msvc@4.54.0': optional: true - '@rollup/rollup-win32-ia32-msvc@4.53.2': + '@rollup/rollup-win32-ia32-msvc@4.54.0': optional: true - '@rollup/rollup-win32-x64-gnu@4.53.2': + '@rollup/rollup-win32-x64-gnu@4.54.0': optional: true - '@rollup/rollup-win32-x64-msvc@4.53.2': + '@rollup/rollup-win32-x64-msvc@4.54.0': optional: true '@rx-state/core@0.1.4(rxjs@7.8.2)': @@ -3757,55 +3993,55 @@ snapshots: '@stablelib/random': 2.0.1 '@stablelib/wipe': 2.0.1 - '@standard-schema/spec@1.0.0': {} + '@standard-schema/spec@1.1.0': {} '@standard-schema/utils@0.3.0': {} - '@swc/core-darwin-arm64@1.15.2': + '@swc/core-darwin-arm64@1.15.8': optional: true - '@swc/core-darwin-x64@1.15.2': + '@swc/core-darwin-x64@1.15.8': optional: true - '@swc/core-linux-arm-gnueabihf@1.15.2': + '@swc/core-linux-arm-gnueabihf@1.15.8': optional: true - '@swc/core-linux-arm64-gnu@1.15.2': + '@swc/core-linux-arm64-gnu@1.15.8': optional: true - '@swc/core-linux-arm64-musl@1.15.2': + '@swc/core-linux-arm64-musl@1.15.8': optional: true - '@swc/core-linux-x64-gnu@1.15.2': + '@swc/core-linux-x64-gnu@1.15.8': optional: true - '@swc/core-linux-x64-musl@1.15.2': + '@swc/core-linux-x64-musl@1.15.8': optional: true - '@swc/core-win32-arm64-msvc@1.15.2': + '@swc/core-win32-arm64-msvc@1.15.8': optional: true - '@swc/core-win32-ia32-msvc@1.15.2': + '@swc/core-win32-ia32-msvc@1.15.8': optional: true - '@swc/core-win32-x64-msvc@1.15.2': + '@swc/core-win32-x64-msvc@1.15.8': optional: true - '@swc/core@1.15.2': + '@swc/core@1.15.8': dependencies: '@swc/counter': 0.1.3 '@swc/types': 0.1.25 optionalDependencies: - '@swc/core-darwin-arm64': 1.15.2 - '@swc/core-darwin-x64': 1.15.2 - '@swc/core-linux-arm-gnueabihf': 1.15.2 - '@swc/core-linux-arm64-gnu': 1.15.2 - '@swc/core-linux-arm64-musl': 1.15.2 - '@swc/core-linux-x64-gnu': 1.15.2 - '@swc/core-linux-x64-musl': 1.15.2 - '@swc/core-win32-arm64-msvc': 1.15.2 - '@swc/core-win32-ia32-msvc': 1.15.2 - '@swc/core-win32-x64-msvc': 1.15.2 + '@swc/core-darwin-arm64': 1.15.8 + '@swc/core-darwin-x64': 1.15.8 + '@swc/core-linux-arm-gnueabihf': 1.15.8 + '@swc/core-linux-arm64-gnu': 1.15.8 + '@swc/core-linux-arm64-musl': 1.15.8 + '@swc/core-linux-x64-gnu': 1.15.8 + '@swc/core-linux-x64-musl': 1.15.8 + '@swc/core-win32-arm64-msvc': 1.15.8 + '@swc/core-win32-ia32-msvc': 1.15.8 + '@swc/core-win32-x64-msvc': 1.15.8 '@swc/counter@0.1.3': {} @@ -3813,26 +4049,26 @@ snapshots: dependencies: '@swc/counter': 0.1.3 - '@tanstack/query-core@5.90.10': {} + '@tanstack/query-core@5.90.16': {} - '@tanstack/query-devtools@5.90.1': {} + '@tanstack/query-devtools@5.92.0': {} - '@tanstack/react-query-devtools@5.90.2(@tanstack/react-query@5.90.10(react@19.2.0))(react@19.2.0)': + '@tanstack/react-query-devtools@5.91.2(@tanstack/react-query@5.90.16(react@19.2.3))(react@19.2.3)': dependencies: - '@tanstack/query-devtools': 5.90.1 - '@tanstack/react-query': 5.90.10(react@19.2.0) - react: 19.2.0 + '@tanstack/query-devtools': 5.92.0 + '@tanstack/react-query': 5.90.16(react@19.2.3) + react: 19.2.3 - '@tanstack/react-query@5.90.10(react@19.2.0)': + '@tanstack/react-query@5.90.16(react@19.2.3)': dependencies: - '@tanstack/query-core': 5.90.10 - react: 19.2.0 + '@tanstack/query-core': 5.90.16 + react: 19.2.3 - '@tanstack/react-virtual@3.13.12(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + '@tanstack/react-virtual@3.13.12(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: '@tanstack/virtual-core': 3.13.12 - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) '@tanstack/virtual-core@3.13.12': {} @@ -3891,9 +4127,9 @@ snapshots: '@types/lodash-es@4.17.12': dependencies: - '@types/lodash': 4.17.20 + '@types/lodash': 4.17.21 - '@types/lodash@4.17.20': {} + '@types/lodash@4.17.21': {} '@types/mdast@4.0.4': dependencies: @@ -3903,7 +4139,7 @@ snapshots: '@types/ms@2.1.0': {} - '@types/node@24.10.1': + '@types/node@24.10.4': dependencies: undici-types: 7.16.0 @@ -3913,22 +4149,22 @@ snapshots: '@types/qs@6.14.0': {} - '@types/react-dom@19.2.3(@types/react@19.2.5)': + '@types/react-dom@19.2.3(@types/react@19.2.7)': dependencies: - '@types/react': 19.2.5 + '@types/react': 19.2.7 '@types/react-router-dom@5.3.3': dependencies: '@types/history': 4.7.11 - '@types/react': 19.2.5 + '@types/react': 19.2.7 '@types/react-router': 5.1.20 '@types/react-router@5.1.20': dependencies: '@types/history': 4.7.11 - '@types/react': 19.2.5 + '@types/react': 19.2.7 - '@types/react@19.2.5': + '@types/react@19.2.7': dependencies: csstype: 3.2.3 @@ -3942,67 +4178,67 @@ snapshots: '@use-gesture/core@10.3.1': {} - '@use-gesture/react@10.3.1(react@19.2.0)': + '@use-gesture/react@10.3.1(react@19.2.3)': dependencies: '@use-gesture/core': 10.3.1 - react: 19.2.0 + react: 19.2.3 - '@vitejs/plugin-react-swc@4.2.2(vite@7.2.2(@types/node@24.10.1)(jiti@2.4.2)(sass@1.70.0)(terser@5.37.0)(yaml@2.6.1))': + '@vitejs/plugin-react-swc@4.2.2(vite@7.3.0(@types/node@24.10.4)(jiti@2.4.2)(sass@1.70.0)(terser@5.37.0)(yaml@2.6.1))': dependencies: '@rolldown/pluginutils': 1.0.0-beta.47 - '@swc/core': 1.15.2 - vite: 7.2.2(@types/node@24.10.1)(jiti@2.4.2)(sass@1.70.0)(terser@5.37.0)(yaml@2.6.1) + '@swc/core': 1.15.8 + vite: 7.3.0(@types/node@24.10.4)(jiti@2.4.2)(sass@1.70.0)(terser@5.37.0)(yaml@2.6.1) transitivePeerDependencies: - '@swc/helpers' - '@vitest/expect@4.0.14': + '@vitest/expect@4.0.16': dependencies: - '@standard-schema/spec': 1.0.0 + '@standard-schema/spec': 1.1.0 '@types/chai': 5.2.3 - '@vitest/spy': 4.0.14 - '@vitest/utils': 4.0.14 - chai: 6.2.1 + '@vitest/spy': 4.0.16 + '@vitest/utils': 4.0.16 + chai: 6.2.2 tinyrainbow: 3.0.3 - '@vitest/mocker@4.0.14(vite@7.2.2(@types/node@24.10.1)(jiti@2.4.2)(sass@1.70.0)(terser@5.37.0)(yaml@2.6.1))': + '@vitest/mocker@4.0.16(vite@7.3.0(@types/node@24.10.4)(jiti@2.4.2)(sass@1.70.0)(terser@5.37.0)(yaml@2.6.1))': dependencies: - '@vitest/spy': 4.0.14 + '@vitest/spy': 4.0.16 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 7.2.2(@types/node@24.10.1)(jiti@2.4.2)(sass@1.70.0)(terser@5.37.0)(yaml@2.6.1) + vite: 7.3.0(@types/node@24.10.4)(jiti@2.4.2)(sass@1.70.0)(terser@5.37.0)(yaml@2.6.1) - '@vitest/pretty-format@4.0.14': + '@vitest/pretty-format@4.0.16': dependencies: tinyrainbow: 3.0.3 - '@vitest/runner@4.0.14': + '@vitest/runner@4.0.16': dependencies: - '@vitest/utils': 4.0.14 + '@vitest/utils': 4.0.16 pathe: 2.0.3 - '@vitest/snapshot@4.0.14': + '@vitest/snapshot@4.0.16': dependencies: - '@vitest/pretty-format': 4.0.14 + '@vitest/pretty-format': 4.0.16 magic-string: 0.30.21 pathe: 2.0.3 - '@vitest/spy@4.0.14': {} + '@vitest/spy@4.0.16': {} - '@vitest/ui@4.0.14(vitest@4.0.14)': + '@vitest/ui@4.0.16(vitest@4.0.16)': dependencies: - '@vitest/utils': 4.0.14 + '@vitest/utils': 4.0.16 fflate: 0.8.2 flatted: 3.3.3 pathe: 2.0.3 sirv: 3.0.2 tinyglobby: 0.2.15 tinyrainbow: 3.0.3 - vitest: 4.0.14(@types/node@24.10.1)(@vitest/ui@4.0.14)(jiti@2.4.2)(sass@1.70.0)(terser@5.37.0)(yaml@2.6.1) + vitest: 4.0.16(@types/node@24.10.4)(@vitest/ui@4.0.16)(jiti@2.4.2)(sass@1.70.0)(terser@5.37.0)(yaml@2.6.1) - '@vitest/utils@4.0.14': + '@vitest/utils@4.0.16': dependencies: - '@vitest/pretty-format': 4.0.14 + '@vitest/pretty-format': 4.0.16 tinyrainbow: 3.0.3 JSONStream@1.3.5: @@ -4038,12 +4274,11 @@ snapshots: asynckit@0.4.0: {} - autoprefixer@10.4.22(postcss@8.5.6): + autoprefixer@10.4.23(postcss@8.5.6): dependencies: - browserslist: 4.28.0 - caniuse-lite: 1.0.30001755 + browserslist: 4.28.1 + caniuse-lite: 1.0.30001762 fraction.js: 5.3.4 - normalize-range: 0.1.2 picocolors: 1.1.1 postcss: 8.5.6 postcss-value-parser: 4.2.0 @@ -4066,7 +4301,7 @@ snapshots: balanced-match@1.0.2: {} - baseline-browser-mapping@2.8.29: {} + baseline-browser-mapping@2.9.11: {} bignumber.js@9.3.1: {} @@ -4081,13 +4316,13 @@ snapshots: dependencies: fill-range: 7.1.1 - browserslist@4.28.0: + browserslist@4.28.1: dependencies: - baseline-browser-mapping: 2.8.29 - caniuse-lite: 1.0.30001755 - electron-to-chromium: 1.5.254 + baseline-browser-mapping: 2.9.11 + caniuse-lite: 1.0.30001762 + electron-to-chromium: 1.5.267 node-releases: 2.0.27 - update-browserslist-db: 1.1.4(browserslist@4.28.0) + update-browserslist-db: 1.2.3(browserslist@4.28.1) buffer-from@1.1.2: {} @@ -4113,11 +4348,11 @@ snapshots: camelcase@5.3.1: {} - caniuse-lite@1.0.30001755: {} + caniuse-lite@1.0.30001762: {} ccount@2.0.1: {} - chai@6.2.1: {} + chai@6.2.2: {} chalk@2.4.2: dependencies: @@ -4464,7 +4699,7 @@ snapshots: es-errors: 1.3.0 gopd: 1.2.0 - electron-to-chromium@1.5.254: {} + electron-to-chromium@1.5.267: {} emoji-regex@8.0.0: {} @@ -4493,7 +4728,7 @@ snapshots: has-tostringtag: 1.0.2 hasown: 2.0.2 - es-toolkit@1.42.0: {} + es-toolkit@1.43.0: {} esbuild@0.25.12: optionalDependencies: @@ -4524,6 +4759,35 @@ snapshots: '@esbuild/win32-ia32': 0.25.12 '@esbuild/win32-x64': 0.25.12 + esbuild@0.27.2: + optionalDependencies: + '@esbuild/aix-ppc64': 0.27.2 + '@esbuild/android-arm': 0.27.2 + '@esbuild/android-arm64': 0.27.2 + '@esbuild/android-x64': 0.27.2 + '@esbuild/darwin-arm64': 0.27.2 + '@esbuild/darwin-x64': 0.27.2 + '@esbuild/freebsd-arm64': 0.27.2 + '@esbuild/freebsd-x64': 0.27.2 + '@esbuild/linux-arm': 0.27.2 + '@esbuild/linux-arm64': 0.27.2 + '@esbuild/linux-ia32': 0.27.2 + '@esbuild/linux-loong64': 0.27.2 + '@esbuild/linux-mips64el': 0.27.2 + '@esbuild/linux-ppc64': 0.27.2 + '@esbuild/linux-riscv64': 0.27.2 + '@esbuild/linux-s390x': 0.27.2 + '@esbuild/linux-x64': 0.27.2 + '@esbuild/netbsd-arm64': 0.27.2 + '@esbuild/netbsd-x64': 0.27.2 + '@esbuild/openbsd-arm64': 0.27.2 + '@esbuild/openbsd-x64': 0.27.2 + '@esbuild/openharmony-arm64': 0.27.2 + '@esbuild/sunos-x64': 0.27.2 + '@esbuild/win32-arm64': 0.27.2 + '@esbuild/win32-ia32': 0.27.2 + '@esbuild/win32-x64': 0.27.2 + escalade@3.2.0: {} escape-string-regexp@1.0.5: {} @@ -4540,7 +4804,7 @@ snapshots: events@3.3.0: {} - expect-type@1.2.2: {} + expect-type@1.3.0: {} extend@3.0.2: {} @@ -4596,15 +4860,15 @@ snapshots: fraction.js@5.3.4: {} - framer-motion@12.23.24(@emotion/is-prop-valid@1.4.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0): + framer-motion@12.23.26(@emotion/is-prop-valid@1.4.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3): dependencies: motion-dom: 12.23.23 motion-utils: 12.23.6 tslib: 2.8.1 optionalDependencies: '@emotion/is-prop-valid': 1.4.0 - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) fsevents@2.3.3: optional: true @@ -4726,7 +4990,7 @@ snapshots: '@types/unist': 3.0.3 '@ungap/structured-clone': 1.3.0 hast-util-from-parse5: 8.0.3 - hast-util-to-parse5: 8.0.0 + hast-util-to-parse5: 8.0.1 html-void-elements: 3.0.0 mdast-util-to-hast: 13.2.1 parse5: 7.3.0 @@ -4762,12 +5026,12 @@ snapshots: transitivePeerDependencies: - supports-color - hast-util-to-parse5@8.0.0: + hast-util-to-parse5@8.0.1: dependencies: '@types/hast': 3.0.4 comma-separated-tokens: 2.0.3 devlop: 1.1.0 - property-information: 6.5.0 + property-information: 7.1.0 space-separated-tokens: 2.0.2 web-namespaces: 2.0.1 zwitch: 2.0.4 @@ -4801,15 +5065,15 @@ snapshots: domhandler: 5.0.3 htmlparser2: 10.0.0 - html-react-parser@5.2.10(@types/react@19.2.5)(react@19.2.0): + html-react-parser@5.2.11(@types/react@19.2.7)(react@19.2.3): dependencies: domhandler: 5.0.3 html-dom-parser: 5.1.2 - react: 19.2.0 + react: 19.2.3 react-property: 2.0.2 style-to-js: 1.1.21 optionalDependencies: - '@types/react': 19.2.5 + '@types/react': 19.2.7 html-url-attributes@3.0.1: {} @@ -4822,10 +5086,12 @@ snapshots: domutils: 3.2.2 entities: 6.0.1 - humanize-duration@3.33.1: {} + humanize-duration@3.33.2: {} immer@10.2.0: {} + immer@11.1.3: {} + immutable@4.3.7: {} import-fresh@3.3.1: @@ -4843,7 +5109,7 @@ snapshots: internmap@2.0.3: {} - ipaddr.js@2.2.0: {} + ipaddr.js@2.3.0: {} is-absolute-url@4.0.1: {} @@ -4915,9 +5181,9 @@ snapshots: lines-and-columns@1.2.4: {} - little-state-machine@4.8.1(react@19.2.0): + little-state-machine@4.8.1(react@19.2.3): dependencies: - react: 19.2.0 + react: 19.2.3 load-json-file@4.0.0: dependencies: @@ -4944,7 +5210,7 @@ snapshots: dependencies: p-locate: 5.0.0 - lodash-es@4.17.21: {} + lodash-es@4.17.22: {} lodash.ismatch@4.4.0: {} @@ -5077,9 +5343,9 @@ snapshots: type-fest: 0.18.1 yargs-parser: 20.2.9 - merge-refs@2.0.0(@types/react@19.2.5): + merge-refs@2.0.0(@types/react@19.2.7): optionalDependencies: - '@types/react': 19.2.5 + '@types/react': 19.2.7 micromark-core-commonmark@2.0.3: dependencies: @@ -5246,14 +5512,14 @@ snapshots: motion-utils@12.23.6: {} - motion@12.23.24(@emotion/is-prop-valid@1.4.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0): + motion@12.23.26(@emotion/is-prop-valid@1.4.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3): dependencies: - framer-motion: 12.23.24(@emotion/is-prop-valid@1.4.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + framer-motion: 12.23.26(@emotion/is-prop-valid@1.4.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) tslib: 2.8.1 optionalDependencies: '@emotion/is-prop-valid': 1.4.0 - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) mrmime@2.0.1: {} @@ -5283,8 +5549,6 @@ snapshots: normalize-path@3.0.0: {} - normalize-range@0.1.2: {} - numbro@2.5.0: dependencies: bignumber.js: 9.3.1 @@ -5391,7 +5655,7 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 - prettier@3.6.2: {} + prettier@3.7.4: {} process-nextick-args@2.0.1: {} @@ -5401,8 +5665,6 @@ snapshots: object-assign: 4.1.1 react-is: 16.13.1 - property-information@6.5.0: {} - property-information@7.1.0: {} proxy-compare@3.0.1: {} @@ -5419,7 +5681,7 @@ snapshots: pngjs: 5.0.0 yargs: 15.4.1 - qs@6.14.0: + qs@6.14.1: dependencies: side-channel: 1.1.0 @@ -5427,57 +5689,57 @@ snapshots: radash@12.1.1: {} - react-click-away-listener@2.4.0(react-dom@19.2.0(react@19.2.0))(react@19.2.0): + react-click-away-listener@2.4.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3): dependencies: - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) - react-datepicker@8.9.0(react-dom@19.2.0(react@19.2.0))(react@19.2.0): + react-datepicker@8.10.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3): dependencies: - '@floating-ui/react': 0.27.16(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@floating-ui/react': 0.27.16(react-dom@19.2.3(react@19.2.3))(react@19.2.3) clsx: 2.1.1 date-fns: 4.1.0 - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) - react-dom@19.2.0(react@19.2.0): + react-dom@19.2.3(react@19.2.3): dependencies: - react: 19.2.0 + react: 19.2.3 scheduler: 0.27.0 - react-hook-form@7.66.0(react@19.2.0): + react-hook-form@7.69.0(react@19.2.3): dependencies: - react: 19.2.0 + react: 19.2.3 - react-idle-timer@5.7.2(react-dom@19.2.0(react@19.2.0))(react@19.2.0): + react-idle-timer@5.7.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3): dependencies: - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) - react-intersection-observer@9.16.0(react-dom@19.2.0(react@19.2.0))(react@19.2.0): + react-intersection-observer@9.16.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3): dependencies: - react: 19.2.0 + react: 19.2.3 optionalDependencies: - react-dom: 19.2.0(react@19.2.0) + react-dom: 19.2.3(react@19.2.3) react-is@16.13.1: {} - react-is@19.2.0: {} + react-is@19.2.3: {} - react-loading-skeleton@3.5.0(react@19.2.0): + react-loading-skeleton@3.5.0(react@19.2.3): dependencies: - react: 19.2.0 + react: 19.2.3 - react-markdown@10.1.0(@types/react@19.2.5)(react@19.2.0): + react-markdown@10.1.0(@types/react@19.2.7)(react@19.2.3): dependencies: '@types/hast': 3.0.4 '@types/mdast': 4.0.4 - '@types/react': 19.2.5 + '@types/react': 19.2.7 devlop: 1.1.0 hast-util-to-jsx-runtime: 2.3.6 html-url-attributes: 3.0.1 mdast-util-to-hast: 13.2.1 - react: 19.2.0 + react: 19.2.3 remark-parse: 11.0.0 remark-rehype: 11.1.2 unified: 11.0.5 @@ -5488,55 +5750,55 @@ snapshots: react-property@2.0.2: {} - react-qr-code@2.0.18(react@19.2.0): + react-qr-code@2.0.18(react@19.2.3): dependencies: prop-types: 15.8.1 qr.js: 0.0.0 - react: 19.2.0 + react: 19.2.3 - react-redux@9.2.0(@types/react@19.2.5)(react@19.2.0)(redux@5.0.1): + react-redux@9.2.0(@types/react@19.2.7)(react@19.2.3)(redux@5.0.1): dependencies: '@types/use-sync-external-store': 0.0.6 - react: 19.2.0 - use-sync-external-store: 1.6.0(react@19.2.0) + react: 19.2.3 + use-sync-external-store: 1.6.0(react@19.2.3) optionalDependencies: - '@types/react': 19.2.5 + '@types/react': 19.2.7 redux: 5.0.1 - react-resize-detector@12.3.0(react@19.2.0): + react-resize-detector@12.3.0(react@19.2.3): dependencies: - es-toolkit: 1.42.0 - react: 19.2.0 + es-toolkit: 1.43.0 + react: 19.2.3 - react-router-dom@6.30.2(react-dom@19.2.0(react@19.2.0))(react@19.2.0): + react-router-dom@6.30.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3): dependencies: '@remix-run/router': 1.23.1 - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - react-router: 6.30.2(react@19.2.0) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + react-router: 6.30.2(react@19.2.3) - react-router@6.30.2(react@19.2.0): + react-router@6.30.2(react@19.2.3): dependencies: '@remix-run/router': 1.23.1 - react: 19.2.0 + react: 19.2.3 - react-simple-animate@3.5.3(react-dom@19.2.0(react@19.2.0)): + react-simple-animate@3.5.3(react-dom@19.2.3(react@19.2.3)): dependencies: - react-dom: 19.2.0(react@19.2.0) + react-dom: 19.2.3(react@19.2.3) - react-tracked@2.0.1(react@19.2.0)(scheduler@0.26.0): + react-tracked@2.0.1(react@19.2.3)(scheduler@0.26.0): dependencies: proxy-compare: 3.0.1 - react: 19.2.0 + react: 19.2.3 scheduler: 0.26.0 - use-context-selector: 2.0.0(react@19.2.0)(scheduler@0.26.0) + use-context-selector: 2.0.0(react@19.2.3)(scheduler@0.26.0) - react-virtualized-auto-sizer@1.0.26(react-dom@19.2.0(react@19.2.0))(react@19.2.0): + react-virtualized-auto-sizer@1.0.26(react-dom@19.2.3(react@19.2.3))(react@19.2.3): dependencies: - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) - react@19.2.0: {} + react@19.2.3: {} read-pkg-up@3.0.0: dependencies: @@ -5582,21 +5844,21 @@ snapshots: dependencies: picomatch: 2.3.1 - recharts@3.4.1(@types/react@19.2.5)(react-dom@19.2.0(react@19.2.0))(react-is@19.2.0)(react@19.2.0)(redux@5.0.1): + recharts@3.6.0(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react-is@19.2.3)(react@19.2.3)(redux@5.0.1): dependencies: - '@reduxjs/toolkit': 2.10.1(react-redux@9.2.0(@types/react@19.2.5)(react@19.2.0)(redux@5.0.1))(react@19.2.0) + '@reduxjs/toolkit': 2.11.2(react-redux@9.2.0(@types/react@19.2.7)(react@19.2.3)(redux@5.0.1))(react@19.2.3) clsx: 2.1.1 decimal.js-light: 2.5.1 - es-toolkit: 1.42.0 + es-toolkit: 1.43.0 eventemitter3: 5.0.1 immer: 10.2.0 - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - react-is: 19.2.0 - react-redux: 9.2.0(@types/react@19.2.5)(react@19.2.0)(redux@5.0.1) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + react-is: 19.2.3 + react-redux: 9.2.0(@types/react@19.2.7)(react@19.2.3)(redux@5.0.1) reselect: 5.1.1 tiny-invariant: 1.3.3 - use-sync-external-store: 1.6.0(react@19.2.0) + use-sync-external-store: 1.6.0(react@19.2.3) victory-vendor: 37.3.6 transitivePeerDependencies: - '@types/react' @@ -5664,32 +5926,32 @@ snapshots: path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 - rollup@4.53.2: + rollup@4.54.0: dependencies: '@types/estree': 1.0.8 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.53.2 - '@rollup/rollup-android-arm64': 4.53.2 - '@rollup/rollup-darwin-arm64': 4.53.2 - '@rollup/rollup-darwin-x64': 4.53.2 - '@rollup/rollup-freebsd-arm64': 4.53.2 - '@rollup/rollup-freebsd-x64': 4.53.2 - '@rollup/rollup-linux-arm-gnueabihf': 4.53.2 - '@rollup/rollup-linux-arm-musleabihf': 4.53.2 - '@rollup/rollup-linux-arm64-gnu': 4.53.2 - '@rollup/rollup-linux-arm64-musl': 4.53.2 - '@rollup/rollup-linux-loong64-gnu': 4.53.2 - '@rollup/rollup-linux-ppc64-gnu': 4.53.2 - '@rollup/rollup-linux-riscv64-gnu': 4.53.2 - '@rollup/rollup-linux-riscv64-musl': 4.53.2 - '@rollup/rollup-linux-s390x-gnu': 4.53.2 - '@rollup/rollup-linux-x64-gnu': 4.53.2 - '@rollup/rollup-linux-x64-musl': 4.53.2 - '@rollup/rollup-openharmony-arm64': 4.53.2 - '@rollup/rollup-win32-arm64-msvc': 4.53.2 - '@rollup/rollup-win32-ia32-msvc': 4.53.2 - '@rollup/rollup-win32-x64-gnu': 4.53.2 - '@rollup/rollup-win32-x64-msvc': 4.53.2 + '@rollup/rollup-android-arm-eabi': 4.54.0 + '@rollup/rollup-android-arm64': 4.54.0 + '@rollup/rollup-darwin-arm64': 4.54.0 + '@rollup/rollup-darwin-x64': 4.54.0 + '@rollup/rollup-freebsd-arm64': 4.54.0 + '@rollup/rollup-freebsd-x64': 4.54.0 + '@rollup/rollup-linux-arm-gnueabihf': 4.54.0 + '@rollup/rollup-linux-arm-musleabihf': 4.54.0 + '@rollup/rollup-linux-arm64-gnu': 4.54.0 + '@rollup/rollup-linux-arm64-musl': 4.54.0 + '@rollup/rollup-linux-loong64-gnu': 4.54.0 + '@rollup/rollup-linux-ppc64-gnu': 4.54.0 + '@rollup/rollup-linux-riscv64-gnu': 4.54.0 + '@rollup/rollup-linux-riscv64-musl': 4.54.0 + '@rollup/rollup-linux-s390x-gnu': 4.54.0 + '@rollup/rollup-linux-x64-gnu': 4.54.0 + '@rollup/rollup-linux-x64-musl': 4.54.0 + '@rollup/rollup-openharmony-arm64': 4.54.0 + '@rollup/rollup-win32-arm64-msvc': 4.54.0 + '@rollup/rollup-win32-ia32-msvc': 4.54.0 + '@rollup/rollup-win32-x64-gnu': 4.54.0 + '@rollup/rollup-win32-x64-msvc': 4.54.0 fsevents: 2.3.3 rxjs@7.8.2: @@ -5868,7 +6130,7 @@ snapshots: supports-preserve-symlinks-flag@1.0.0: {} - tabbable@6.3.0: {} + tabbable@6.4.0: {} terser@5.37.0: dependencies: @@ -5988,7 +6250,7 @@ snapshots: tinybench@2.9.0: {} - tinyexec@0.3.2: {} + tinyexec@1.0.2: {} tinyglobby@0.2.15: dependencies: @@ -6067,31 +6329,31 @@ snapshots: unist-util-is: 6.0.1 unist-util-visit-parents: 6.0.2 - update-browserslist-db@1.1.4(browserslist@4.28.0): + update-browserslist-db@1.2.3(browserslist@4.28.1): dependencies: - browserslist: 4.28.0 + browserslist: 4.28.1 escalade: 3.2.0 picocolors: 1.1.1 - use-breakpoint@4.0.10(react-dom@19.2.0(react@19.2.0))(react@19.2.0): + use-breakpoint@4.0.10(react-dom@19.2.3(react@19.2.3))(react@19.2.3): dependencies: - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) - use-context-selector@2.0.0(react@19.2.0)(scheduler@0.26.0): + use-context-selector@2.0.0(react@19.2.3)(scheduler@0.26.0): dependencies: - react: 19.2.0 + react: 19.2.3 scheduler: 0.26.0 - use-deep-compare-effect@1.8.1(react@19.2.0): + use-deep-compare-effect@1.8.1(react@19.2.3): dependencies: '@babel/runtime': 7.28.4 dequal: 2.0.3 - react: 19.2.0 + react: 19.2.3 - use-sync-external-store@1.6.0(react@19.2.0): + use-sync-external-store@1.6.0(react@19.2.3): dependencies: - react: 19.2.0 + react: 19.2.3 util-deprecate@1.0.2: {} @@ -6134,51 +6396,51 @@ snapshots: d3-time: 3.1.0 d3-timer: 3.0.1 - vite-plugin-package-version@1.1.0(vite@7.2.2(@types/node@24.10.1)(jiti@2.4.2)(sass@1.70.0)(terser@5.37.0)(yaml@2.6.1)): + vite-plugin-package-version@1.1.0(vite@7.3.0(@types/node@24.10.4)(jiti@2.4.2)(sass@1.70.0)(terser@5.37.0)(yaml@2.6.1)): dependencies: - vite: 7.2.2(@types/node@24.10.1)(jiti@2.4.2)(sass@1.70.0)(terser@5.37.0)(yaml@2.6.1) + vite: 7.3.0(@types/node@24.10.4)(jiti@2.4.2)(sass@1.70.0)(terser@5.37.0)(yaml@2.6.1) - vite@7.2.2(@types/node@24.10.1)(jiti@2.4.2)(sass@1.70.0)(terser@5.37.0)(yaml@2.6.1): + vite@7.3.0(@types/node@24.10.4)(jiti@2.4.2)(sass@1.70.0)(terser@5.37.0)(yaml@2.6.1): dependencies: - esbuild: 0.25.12 + esbuild: 0.27.2 fdir: 6.5.0(picomatch@4.0.3) picomatch: 4.0.3 postcss: 8.5.6 - rollup: 4.53.2 + rollup: 4.54.0 tinyglobby: 0.2.15 optionalDependencies: - '@types/node': 24.10.1 + '@types/node': 24.10.4 fsevents: 2.3.3 jiti: 2.4.2 sass: 1.70.0 terser: 5.37.0 yaml: 2.6.1 - vitest@4.0.14(@types/node@24.10.1)(@vitest/ui@4.0.14)(jiti@2.4.2)(sass@1.70.0)(terser@5.37.0)(yaml@2.6.1): + vitest@4.0.16(@types/node@24.10.4)(@vitest/ui@4.0.16)(jiti@2.4.2)(sass@1.70.0)(terser@5.37.0)(yaml@2.6.1): dependencies: - '@vitest/expect': 4.0.14 - '@vitest/mocker': 4.0.14(vite@7.2.2(@types/node@24.10.1)(jiti@2.4.2)(sass@1.70.0)(terser@5.37.0)(yaml@2.6.1)) - '@vitest/pretty-format': 4.0.14 - '@vitest/runner': 4.0.14 - '@vitest/snapshot': 4.0.14 - '@vitest/spy': 4.0.14 - '@vitest/utils': 4.0.14 + '@vitest/expect': 4.0.16 + '@vitest/mocker': 4.0.16(vite@7.3.0(@types/node@24.10.4)(jiti@2.4.2)(sass@1.70.0)(terser@5.37.0)(yaml@2.6.1)) + '@vitest/pretty-format': 4.0.16 + '@vitest/runner': 4.0.16 + '@vitest/snapshot': 4.0.16 + '@vitest/spy': 4.0.16 + '@vitest/utils': 4.0.16 es-module-lexer: 1.7.0 - expect-type: 1.2.2 + expect-type: 1.3.0 magic-string: 0.30.21 obug: 2.1.1 pathe: 2.0.3 picomatch: 4.0.3 std-env: 3.10.0 tinybench: 2.9.0 - tinyexec: 0.3.2 + tinyexec: 1.0.2 tinyglobby: 0.2.15 tinyrainbow: 3.0.3 - vite: 7.2.2(@types/node@24.10.1)(jiti@2.4.2)(sass@1.70.0)(terser@5.37.0)(yaml@2.6.1) + vite: 7.3.0(@types/node@24.10.4)(jiti@2.4.2)(sass@1.70.0)(terser@5.37.0)(yaml@2.6.1) why-is-node-running: 2.3.0 optionalDependencies: - '@types/node': 24.10.1 - '@vitest/ui': 4.0.14(vitest@4.0.14) + '@types/node': 24.10.4 + '@vitest/ui': 4.0.16(vitest@4.0.16) transitivePeerDependencies: - jiti - less @@ -6277,11 +6539,11 @@ snapshots: zod@3.25.76: {} - zustand@5.0.8(@types/react@19.2.5)(immer@10.2.0)(react@19.2.0)(use-sync-external-store@1.6.0(react@19.2.0)): + zustand@5.0.9(@types/react@19.2.7)(immer@11.1.3)(react@19.2.3)(use-sync-external-store@1.6.0(react@19.2.3)): optionalDependencies: - '@types/react': 19.2.5 - immer: 10.2.0 - react: 19.2.0 - use-sync-external-store: 1.6.0(react@19.2.0) + '@types/react': 19.2.7 + immer: 11.1.3 + react: 19.2.3 + use-sync-external-store: 1.6.0(react@19.2.3) zwitch@2.0.4: {}