mirror of
https://github.com/clash-verge-rev/clash-verge-rev.git
synced 2026-11-08 02:52:07 +08:00
Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6271560e34 | ||
|
|
46941f9d4d |
Generated
+47
-12
@@ -159,9 +159,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "arc-swap"
|
name = "arc-swap"
|
||||||
version = "1.9.3"
|
version = "1.9.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "380962b3d93b99642e50b9febf72c071e97e8b32d86eae9cf4ef582bb960ae6e"
|
checksum = "db4c94f57a570fce5d381be3289b3f79aef6332d72948fe82ab5772e423ccf92"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"rustversion",
|
"rustversion",
|
||||||
]
|
]
|
||||||
@@ -1119,7 +1119,7 @@ dependencies = [
|
|||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
"reqwest 0.13.5",
|
"reqwest 0.13.5",
|
||||||
"reqwest_dav",
|
"reqwest_dav",
|
||||||
"rfd",
|
"rfd 0.18.0",
|
||||||
"runas",
|
"runas",
|
||||||
"rustls",
|
"rustls",
|
||||||
"scopeguard",
|
"scopeguard",
|
||||||
@@ -2403,9 +2403,9 @@ checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "flate2"
|
name = "flate2"
|
||||||
version = "1.1.10"
|
version = "1.1.11"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6e634e2e0ebac1ee034020da1ca582e17ffe4e0f5e985823721e168928136dcb"
|
checksum = "626d4928326b646e70f65ed36fc7cf9f206bdb68a0019ee7c6b4709da4e084c4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"crc32fast",
|
"crc32fast",
|
||||||
"miniz_oxide 0.9.1",
|
"miniz_oxide 0.9.1",
|
||||||
@@ -4069,9 +4069,9 @@ checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lzma-rust2"
|
name = "lzma-rust2"
|
||||||
version = "0.24.0"
|
version = "0.25.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "490750560fa7851fbbf5d3ab1a8750f09350ed361ed8aed85b1e1e40587812ee"
|
checksum = "ef1d73c59a56771343712b22ea5615092c612c16ba291c0979e3105dfeec4890"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"sha2 0.11.1",
|
"sha2 0.11.1",
|
||||||
]
|
]
|
||||||
@@ -4537,6 +4537,7 @@ dependencies = [
|
|||||||
"objc2-core-video",
|
"objc2-core-video",
|
||||||
"objc2-foundation",
|
"objc2-foundation",
|
||||||
"objc2-quartz-core",
|
"objc2-quartz-core",
|
||||||
|
"objc2-uniform-type-identifiers",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -4758,6 +4759,16 @@ dependencies = [
|
|||||||
"objc2-user-notifications",
|
"objc2-user-notifications",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "objc2-uniform-type-identifiers"
|
||||||
|
version = "0.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7902ac02859fc1f7045f8b598c63f1ae0cc7efeaa06a9bc9f3d9a3c955974fa4"
|
||||||
|
dependencies = [
|
||||||
|
"objc2",
|
||||||
|
"objc2-foundation",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "objc2-user-notifications"
|
name = "objc2-user-notifications"
|
||||||
version = "0.3.2"
|
version = "0.3.2"
|
||||||
@@ -5933,6 +5944,30 @@ dependencies = [
|
|||||||
"windows-sys 0.61.2",
|
"windows-sys 0.61.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rfd"
|
||||||
|
version = "0.18.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c4b9d4857b10297ffb0f64cae711e983a7aeaee561848d56d429d3858bd8ed93"
|
||||||
|
dependencies = [
|
||||||
|
"block2",
|
||||||
|
"dispatch2",
|
||||||
|
"js-sys",
|
||||||
|
"libc",
|
||||||
|
"log",
|
||||||
|
"objc2",
|
||||||
|
"objc2-app-kit",
|
||||||
|
"objc2-core-foundation",
|
||||||
|
"objc2-foundation",
|
||||||
|
"objc2-uniform-type-identifiers",
|
||||||
|
"percent-encoding",
|
||||||
|
"raw-window-handle",
|
||||||
|
"wasm-bindgen",
|
||||||
|
"wasm-bindgen-futures",
|
||||||
|
"web-sys",
|
||||||
|
"windows-sys 0.61.2",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ring"
|
name = "ring"
|
||||||
version = "0.17.14"
|
version = "0.17.14"
|
||||||
@@ -7426,7 +7461,7 @@ checksum = "ce0fb9c2b6d34a9720627ed930ebc8af26f61e648695aecaeb03d5c33c5896f5"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"log",
|
"log",
|
||||||
"raw-window-handle",
|
"raw-window-handle",
|
||||||
"rfd",
|
"rfd 0.17.2",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"tauri",
|
"tauri",
|
||||||
@@ -10069,9 +10104,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zip"
|
name = "zip"
|
||||||
version = "9.3.0"
|
version = "9.4.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "eaeb7f16d2605b49aef586996ded29e65642fab7259a3077e537351df6d80426"
|
checksum = "23f17fbc870bfac28c6228aed7d6e6df352e26bf7015c4313727e16a7a01ce5e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aes",
|
"aes",
|
||||||
"bzip2",
|
"bzip2",
|
||||||
@@ -10099,9 +10134,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zlib-rs"
|
name = "zlib-rs"
|
||||||
version = "0.6.6"
|
version = "0.6.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b142a20ec14a91d5bc708c1dc21b080c550113d8aa77afa29635673a65dd02c5"
|
checksum = "b268e58e7c693d7c271f93ffc4ba3b380412554231c85bf61ca7af91042a4112"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zmij"
|
name = "zmij"
|
||||||
|
|||||||
+2
-2
@@ -79,7 +79,7 @@
|
|||||||
"react-dom": "19.3.1",
|
"react-dom": "19.3.1",
|
||||||
"react-error-boundary": "6.1.6",
|
"react-error-boundary": "6.1.6",
|
||||||
"react-hook-form": "^7.88.0",
|
"react-hook-form": "^7.88.0",
|
||||||
"react-i18next": "17.1.10",
|
"react-i18next": "17.1.13",
|
||||||
"react-markdown": "10.1.0",
|
"react-markdown": "10.1.0",
|
||||||
"react-router": "^8.4.0",
|
"react-router": "^8.4.0",
|
||||||
"rehype-raw": "^7.0.0",
|
"rehype-raw": "^7.0.0",
|
||||||
@@ -132,7 +132,7 @@
|
|||||||
"vite": "^8.3.0",
|
"vite": "^8.3.0",
|
||||||
"vite-plugin-svgr": "^5.2.0",
|
"vite-plugin-svgr": "^5.2.0",
|
||||||
"vitest": "^5.0.1",
|
"vitest": "^5.0.1",
|
||||||
"webdriverio": "10.1.0",
|
"webdriverio": "10.2.0",
|
||||||
"ws": "8.22.1"
|
"ws": "8.22.1"
|
||||||
},
|
},
|
||||||
"lint-staged": {
|
"lint-staged": {
|
||||||
|
|||||||
Generated
+102
-82
@@ -275,8 +275,8 @@ importers:
|
|||||||
specifier: ^7.88.0
|
specifier: ^7.88.0
|
||||||
version: 7.88.0(react@19.3.1)
|
version: 7.88.0(react@19.3.1)
|
||||||
react-i18next:
|
react-i18next:
|
||||||
specifier: 17.1.10
|
specifier: 17.1.13
|
||||||
version: 17.1.10(i18next@26.4.2(typescript@6.0.3))(react-dom@19.3.1(react@19.3.1))(react@19.3.1)(typescript@6.0.3)
|
version: 17.1.13(i18next@26.4.2(typescript@6.0.3))(react-dom@19.3.1(react@19.3.1))(react@19.3.1)(typescript@6.0.3)
|
||||||
react-markdown:
|
react-markdown:
|
||||||
specifier: 10.1.0
|
specifier: 10.1.0
|
||||||
version: 10.1.0(@types/react@19.3.0)(react@19.3.1)(supports-color@7.2.0)
|
version: 10.1.0(@types/react@19.3.0)(react@19.3.1)(supports-color@7.2.0)
|
||||||
@@ -340,7 +340,7 @@ importers:
|
|||||||
version: 6.1.1(vite@8.3.3(@types/node@26.6.5)(esbuild@0.28.2)(jiti@2.7.0)(sass@1.104.1)(terser@5.51.2)(yaml@2.9.1))
|
version: 6.1.1(vite@8.3.3(@types/node@26.6.5)(esbuild@0.28.2)(jiti@2.7.0)(sass@1.104.1)(terser@5.51.2)(yaml@2.9.1))
|
||||||
'@wdio/tauri-service':
|
'@wdio/tauri-service':
|
||||||
specifier: 1.5.1
|
specifier: 1.5.1
|
||||||
version: 1.5.1(expect-webdriverio@6.1.0)(supports-color@7.2.0)(webdriverio@10.1.0(supports-color@7.2.0)(yauzl@2.10.0))
|
version: 1.5.1(expect-webdriverio@6.1.0)(supports-color@7.2.0)(webdriverio@10.2.0(supports-color@7.2.0)(yauzl@2.10.0))
|
||||||
adm-zip:
|
adm-zip:
|
||||||
specifier: ^0.6.1
|
specifier: ^0.6.1
|
||||||
version: 0.6.1
|
version: 0.6.1
|
||||||
@@ -429,8 +429,8 @@ importers:
|
|||||||
specifier: ^5.0.1
|
specifier: ^5.0.1
|
||||||
version: 5.0.1(@types/node@26.6.5)(vite@8.3.3(@types/node@26.6.5)(esbuild@0.28.2)(jiti@2.7.0)(sass@1.104.1)(terser@5.51.2)(yaml@2.9.1))
|
version: 5.0.1(@types/node@26.6.5)(vite@8.3.3(@types/node@26.6.5)(esbuild@0.28.2)(jiti@2.7.0)(sass@1.104.1)(terser@5.51.2)(yaml@2.9.1))
|
||||||
webdriverio:
|
webdriverio:
|
||||||
specifier: 10.1.0
|
specifier: 10.2.0
|
||||||
version: 10.1.0(supports-color@7.2.0)(yauzl@2.10.0)
|
version: 10.2.0(supports-color@7.2.0)(yauzl@2.10.0)
|
||||||
ws:
|
ws:
|
||||||
specifier: 8.22.1
|
specifier: 8.22.1
|
||||||
version: 8.22.1
|
version: 8.22.1
|
||||||
@@ -1381,6 +1381,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==}
|
resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==}
|
||||||
engines: {node: '>=18.0.0'}
|
engines: {node: '>=18.0.0'}
|
||||||
|
|
||||||
|
'@isaacs/nonrecursive-glob-match@1.1.0':
|
||||||
|
resolution: {integrity: sha512-GPm/Dbvk5RLmhRnxXIa7nTpK0xtl4E0wj0MslsuNiJ1FIROU+2+9lgUC/dgF7Ey4QCWhfEsgxilMRMCdHPfDfw==}
|
||||||
|
|
||||||
'@jest/diff-sequences@30.5.0':
|
'@jest/diff-sequences@30.5.0':
|
||||||
resolution: {integrity: sha512-OsqBjHXCn8cadasoAZBP6nWYvMsRhpMzGXTpxJ5aO04NlbdhIz+FVe3q49l0AwVhsz/cEmIpBes6gAFl1/dWQg==}
|
resolution: {integrity: sha512-OsqBjHXCn8cadasoAZBP6nWYvMsRhpMzGXTpxJ5aO04NlbdhIz+FVe3q49l0AwVhsz/cEmIpBes6gAFl1/dWQg==}
|
||||||
engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
|
engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
|
||||||
@@ -2598,9 +2601,9 @@ packages:
|
|||||||
'@vscode/l10n@0.0.18':
|
'@vscode/l10n@0.0.18':
|
||||||
resolution: {integrity: sha512-KYSIHVmslkaCDyw013pphY+d7x1qV8IZupYfeIfzNA+nsaWHbn5uPuQRvdRFsa9zFzGeudPuoGoZ1Op4jrJXIQ==}
|
resolution: {integrity: sha512-KYSIHVmslkaCDyw013pphY+d7x1qV8IZupYfeIfzNA+nsaWHbn5uPuQRvdRFsa9zFzGeudPuoGoZ1Op4jrJXIQ==}
|
||||||
|
|
||||||
'@wdio/config@10.1.0':
|
'@wdio/config@10.2.0':
|
||||||
resolution: {integrity: sha512-V4G0uIhLTlSJRKLfr0ypu3SJLL90+LCuo9niBgqTmpNpJz4K3nwK2Mz//JuAKmz/OVXPb1HxJlrm2snfSOI2WQ==}
|
resolution: {integrity: sha512-dCsMVpgmyL8fLRQVcxVyYXBG/pgtJeY897+LUxU/Vw+JvJOV2iTycHn5xUD5EehbNsstfxgJwkLCVunhJcbXdg==}
|
||||||
engines: {node: '>=22.19.0'}
|
engines: {node: '>=22.23.4 <23 || >=24.22.1 <25 || >=26.12.1'}
|
||||||
|
|
||||||
'@wdio/globals@9.31.3':
|
'@wdio/globals@9.31.3':
|
||||||
resolution: {integrity: sha512-ZzmRKUlwUBahNoNxHbAzst+9dNInEjLQc7Z3c86OhDRxAf3Ivw8k2rc56UCbyYiZnkd1dOpuA91zFiTrUNNORw==}
|
resolution: {integrity: sha512-ZzmRKUlwUBahNoNxHbAzst+9dNInEjLQc7Z3c86OhDRxAf3Ivw8k2rc56UCbyYiZnkd1dOpuA91zFiTrUNNORw==}
|
||||||
@@ -2609,9 +2612,9 @@ packages:
|
|||||||
expect-webdriverio: ^6.0.9
|
expect-webdriverio: ^6.0.9
|
||||||
webdriverio: ^9.28.0
|
webdriverio: ^9.28.0
|
||||||
|
|
||||||
'@wdio/logger@10.1.0':
|
'@wdio/logger@10.2.0':
|
||||||
resolution: {integrity: sha512-zZn9TMaxzTAHOGX5fLTs9E8Gw436l6xSFh+wZyXfRpgnzgeS5W/U0I6pBAjsvMn8rr5HAJC8Vy+WhiUxmbKsvQ==}
|
resolution: {integrity: sha512-C7qpFSA+Zuu/6xbx+ALoEeOiyIWlD02/Llb8ufjypCOpe9rAy77JgAHeQlKYWg7aitRQRt7aG2oeix0mNhTk5A==}
|
||||||
engines: {node: '>=22.19.0'}
|
engines: {node: '>=22.23.4 <23 || >=24.22.1 <25 || >=26.12.1'}
|
||||||
|
|
||||||
'@wdio/logger@9.29.1':
|
'@wdio/logger@9.29.1':
|
||||||
resolution: {integrity: sha512-0ZAEIo6PNyMIJPlOGkIgyOJUjcd0pC8/QHlVAAe1c91/IcjZ1X+k0yidXHaboJdN7dq1XPUacmhRdtua0U5EZg==}
|
resolution: {integrity: sha512-0ZAEIo6PNyMIJPlOGkIgyOJUjcd0pC8/QHlVAAe1c91/IcjZ1X+k0yidXHaboJdN7dq1XPUacmhRdtua0U5EZg==}
|
||||||
@@ -2638,13 +2641,13 @@ packages:
|
|||||||
tsx:
|
tsx:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@wdio/protocols@10.0.2':
|
'@wdio/protocols@10.2.0':
|
||||||
resolution: {integrity: sha512-ewuQuu+niq/ZpV1TLn904nDA4F9n4LLu9X1U8Juwwm1ahq09dpEP8lEytDnpaD0f21uIx//mFBKmBAjSexMdEA==}
|
resolution: {integrity: sha512-HuSsvpv7hV8hbhFdlMraz7c+Bpu9O4FiKg2XdztZupj59wx0c7n7YPnZRGEY5yW3/wRt9PfqWkBI0EO81Y+Zqw==}
|
||||||
engines: {node: '>=22.19.0'}
|
engines: {node: '>=22.23.4 <23 || >=24.22.1 <25 || >=26.12.1'}
|
||||||
|
|
||||||
'@wdio/repl@10.0.0':
|
'@wdio/repl@10.2.0':
|
||||||
resolution: {integrity: sha512-wAC9ksdWWv2E3Sj86eNKxB3IUt0gEyw9rgDGAWjk/GWgi6K3e+/n3erGNweI9JMJyC/+RNe0qHgVPQpRJMRVpg==}
|
resolution: {integrity: sha512-bVx6C9c1LgAzOyWevg796NwghExw28oPLp0NlfYGc0gErxO8wtpvLJGwCYLXQ/V372b2en5H4OZ05QXoGmXOWw==}
|
||||||
engines: {node: '>=22.19.0'}
|
engines: {node: '>=22.23.4 <23 || >=24.22.1 <25 || >=26.12.1'}
|
||||||
|
|
||||||
'@wdio/reporter@9.31.2':
|
'@wdio/reporter@9.31.2':
|
||||||
resolution: {integrity: sha512-3cKE6vCOsro/Ep9dGTIbo3FBn3WNvJ++BIn32lFd9m19pFdjuwlVgLtPXHb9jWNCJs740h5AIUcVkcscqUYhAA==}
|
resolution: {integrity: sha512-3cKE6vCOsro/Ep9dGTIbo3FBn3WNvJ++BIn32lFd9m19pFdjuwlVgLtPXHb9jWNCJs740h5AIUcVkcscqUYhAA==}
|
||||||
@@ -2660,17 +2663,17 @@ packages:
|
|||||||
peerDependencies:
|
peerDependencies:
|
||||||
webdriverio: ^9.0.0
|
webdriverio: ^9.0.0
|
||||||
|
|
||||||
'@wdio/types@10.1.0':
|
'@wdio/types@10.2.0':
|
||||||
resolution: {integrity: sha512-MxGhIjP74w2xaIhqtzBfrtzDc41qyb9cMqvHgdIDC7MbaREJxpUd/paAg8qmouIph7hBp8MruXap/BPKlVXmVA==}
|
resolution: {integrity: sha512-a9qwqHzf0t2ctrXs+qUEhobH/VK0PmeoErCBWdN6DRxhTP8YiXQco4wjTj1z7H4k5xwZYxMT0KC0QzN/e4B73Q==}
|
||||||
engines: {node: '>=22.19.0'}
|
engines: {node: '>=22.23.4 <23 || >=24.22.1 <25 || >=26.12.1'}
|
||||||
|
|
||||||
'@wdio/types@9.31.2':
|
'@wdio/types@9.31.2':
|
||||||
resolution: {integrity: sha512-lQKaiQDCJJ6VC1K/cKzaAxCpwB9pL1U0VbmX2uGksMxO4EDbNuLvIxWryuUDJ7OaSYTjxILTKYFErdbT/xEikg==}
|
resolution: {integrity: sha512-lQKaiQDCJJ6VC1K/cKzaAxCpwB9pL1U0VbmX2uGksMxO4EDbNuLvIxWryuUDJ7OaSYTjxILTKYFErdbT/xEikg==}
|
||||||
engines: {node: '>=18.20.0'}
|
engines: {node: '>=18.20.0'}
|
||||||
|
|
||||||
'@wdio/utils@10.1.0':
|
'@wdio/utils@10.2.0':
|
||||||
resolution: {integrity: sha512-BABJY+L4JidOD2bfQe3icuRZ0jQeUimTwUEe66I4BHVGWwaa5QBeVU+JZmdNQLjMA9q+Nk385aL96Mb1/PbKYw==}
|
resolution: {integrity: sha512-C4ok/WatCBV3gHmIYGjj4Aa43tQTD53eTFs3okpKxuc1wmFy/1zDFOYLDjeTq4xSFygoRBI+ZMyc9LBEiok3ng==}
|
||||||
engines: {node: '>=22.19.0'}
|
engines: {node: '>=22.23.4 <23 || >=24.22.1 <25 || >=26.12.1'}
|
||||||
|
|
||||||
'@zip.js/zip.js@2.24.0':
|
'@zip.js/zip.js@2.24.0':
|
||||||
resolution: {integrity: sha512-WlaVKzdqBK+RF3bdiXZWbdCnvGB6PdO4IHC1mn/2Yfl3bASgqUNegnU3+PNg61iecwoPfUi5BgLUPqBukXpyWw==}
|
resolution: {integrity: sha512-WlaVKzdqBK+RF3bdiXZWbdCnvGB6PdO4IHC1mn/2Yfl3bASgqUNegnU3+PNg61iecwoPfUi5BgLUPqBukXpyWw==}
|
||||||
@@ -3086,6 +3089,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-G7Cqgaelq68XHJNGlZ7lrNQyhZGsFqpwtGFexqUv4IQdjKoSYF7ipZ9UuTJZUSQXFj/XaoBLuEVIVqr8EJngEQ==}
|
resolution: {integrity: sha512-G7Cqgaelq68XHJNGlZ7lrNQyhZGsFqpwtGFexqUv4IQdjKoSYF7ipZ9UuTJZUSQXFj/XaoBLuEVIVqr8EJngEQ==}
|
||||||
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
|
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
|
||||||
|
|
||||||
|
decamelize@7.0.0:
|
||||||
|
resolution: {integrity: sha512-uT/0KzZIx9u4b5vW3HLWtSYwjLTuY2QFVY3xNmfXXUmTqnfgzfT8DEA2gY93go5sXEjnbqP66KcURdVxm0rDXA==}
|
||||||
|
engines: {node: '>=22'}
|
||||||
|
|
||||||
decode-named-character-reference@1.3.0:
|
decode-named-character-reference@1.3.0:
|
||||||
resolution: {integrity: sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==}
|
resolution: {integrity: sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==}
|
||||||
|
|
||||||
@@ -3580,8 +3587,8 @@ packages:
|
|||||||
resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==}
|
resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==}
|
||||||
engines: {node: '>=10.13.0'}
|
engines: {node: '>=10.13.0'}
|
||||||
|
|
||||||
glob@13.0.6:
|
glob@14.0.4:
|
||||||
resolution: {integrity: sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==}
|
resolution: {integrity: sha512-mMiwNKoJZ4HAIRVJxotO6LsNRReA30gQwP9E2/p94tfLv+jygCNpKDjhhSL6OLY6LE371EZm5EAf7RpYuUqlsw==}
|
||||||
engines: {node: 18 || 20 || >=22}
|
engines: {node: 18 || 20 || >=22}
|
||||||
|
|
||||||
globals@17.12.0:
|
globals@17.12.0:
|
||||||
@@ -4121,6 +4128,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==}
|
resolution: {integrity: sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==}
|
||||||
engines: {node: 18 || 20 || >=22}
|
engines: {node: 18 || 20 || >=22}
|
||||||
|
|
||||||
|
minimatch@11.0.5:
|
||||||
|
resolution: {integrity: sha512-snPlVppFcYWfLLFQGhDxUyBcHNtMlWmn3jtj7pEaO+DP8pqL0IvAzXhMYmn1z9ASNtgmwLBdFxSf4tMUB0OTKA==}
|
||||||
|
engines: {node: 20 || >=22}
|
||||||
|
|
||||||
minipass@7.1.3:
|
minipass@7.1.3:
|
||||||
resolution: {integrity: sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==}
|
resolution: {integrity: sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==}
|
||||||
engines: {node: '>=16 || 14 >=14.17'}
|
engines: {node: '>=16 || 14 >=14.17'}
|
||||||
@@ -4278,8 +4289,8 @@ packages:
|
|||||||
path-parse@1.0.7:
|
path-parse@1.0.7:
|
||||||
resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
|
resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
|
||||||
|
|
||||||
path-scurry@2.0.2:
|
path-scurry@3.0.0:
|
||||||
resolution: {integrity: sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==}
|
resolution: {integrity: sha512-1pTbQqH1wIwMi/eleQ5leFTTCkzFsPcgXNjwOA8YGGW/W4/kHT3dssE7Z6NH+zuqNLOzOB0uaD61yl7cAcVhgg==}
|
||||||
engines: {node: 18 || 20 || >=22}
|
engines: {node: 18 || 20 || >=22}
|
||||||
|
|
||||||
path-type@4.0.0:
|
path-type@4.0.0:
|
||||||
@@ -4388,8 +4399,8 @@ packages:
|
|||||||
peerDependencies:
|
peerDependencies:
|
||||||
react: ^16.8.0 || ^17 || ^18 || ^19
|
react: ^16.8.0 || ^17 || ^18 || ^19
|
||||||
|
|
||||||
react-i18next@17.1.10:
|
react-i18next@17.1.13:
|
||||||
resolution: {integrity: sha512-XuUuA/rxukE4hXuR//PN308DuSQkWqKNNWBUj0dDTR087l3+6WLid3NC/w55ALrXA/dtWb7lychJ4rQLR3DCDg==}
|
resolution: {integrity: sha512-ZxhOSlyKSKPtbUNPLwYMmE3w3bhoc0jaZJZ6bc7wGOT6cLbrbs//6m01HxCLtFdSjHvFpwFwoPhxxzIlHSs9cw==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
i18next: '>= 26.2.0'
|
i18next: '>= 26.2.0'
|
||||||
react: '>= 16.8.0'
|
react: '>= 16.8.0'
|
||||||
@@ -5016,13 +5027,13 @@ packages:
|
|||||||
web-namespaces@2.0.1:
|
web-namespaces@2.0.1:
|
||||||
resolution: {integrity: sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==}
|
resolution: {integrity: sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==}
|
||||||
|
|
||||||
webdriver@10.1.0:
|
webdriver@10.2.0:
|
||||||
resolution: {integrity: sha512-W3lNUfcjsD5T62U7HackcsfKefrBoUect9PkyObhuByjPFjR+JwmkWAjbFYHlATpZwFZN5d5h28VBanG4BUe4g==}
|
resolution: {integrity: sha512-RV2+j+CAgJgJb59QliIqMik2DmP4l9dW38tDSmzWdilonCAxM5fKZAydpBt6CNlToG0jqpZ9nL3Mo6YnQh/xqw==}
|
||||||
engines: {node: '>=22.19.0'}
|
engines: {node: '>=22.23.4 <23 || >=24.22.1 <25 || >=26.12.1'}
|
||||||
|
|
||||||
webdriverio@10.1.0:
|
webdriverio@10.2.0:
|
||||||
resolution: {integrity: sha512-RDfW0GkHFM/ItdKEw8YvmLgcb5szUlJOKEVUoRgIeeQQ4+a7UMVavC6TVK438wNI32IHu0R1MoMrSW/6DRWYmQ==}
|
resolution: {integrity: sha512-efmXasjo/+tWN+C5HCqsaPIJDJLH7smua1aeizL/DoEDg7f4YYVLgD9Zhav9DYrrouag9C04Mmj6bo6a2KA3hA==}
|
||||||
engines: {node: '>=22.19.0'}
|
engines: {node: '>=22.23.4 <23 || >=24.22.1 <25 || >=26.12.1'}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
puppeteer-core: '>=24 <26'
|
puppeteer-core: '>=24 <26'
|
||||||
peerDependenciesMeta:
|
peerDependenciesMeta:
|
||||||
@@ -6242,6 +6253,8 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
minipass: 7.1.3
|
minipass: 7.1.3
|
||||||
|
|
||||||
|
'@isaacs/nonrecursive-glob-match@1.1.0': {}
|
||||||
|
|
||||||
'@jest/diff-sequences@30.5.0': {}
|
'@jest/diff-sequences@30.5.0': {}
|
||||||
|
|
||||||
'@jest/expect-utils@30.5.2':
|
'@jest/expect-utils@30.5.2':
|
||||||
@@ -7220,13 +7233,13 @@ snapshots:
|
|||||||
|
|
||||||
'@vscode/l10n@0.0.18': {}
|
'@vscode/l10n@0.0.18': {}
|
||||||
|
|
||||||
'@wdio/config@10.1.0(supports-color@7.2.0)(yauzl@2.10.0)':
|
'@wdio/config@10.2.0(supports-color@7.2.0)(yauzl@2.10.0)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@wdio/logger': 10.1.0
|
'@wdio/logger': 10.2.0
|
||||||
'@wdio/types': 10.1.0
|
'@wdio/types': 10.2.0
|
||||||
'@wdio/utils': 10.1.0(supports-color@7.2.0)(yauzl@2.10.0)
|
'@wdio/utils': 10.2.0(supports-color@7.2.0)(yauzl@2.10.0)
|
||||||
deepmerge-ts: 8.0.2
|
deepmerge-ts: 8.0.2
|
||||||
glob: 13.0.6
|
glob: 14.0.4
|
||||||
import-meta-resolve: 4.2.0
|
import-meta-resolve: 4.2.0
|
||||||
jiti: 2.7.0
|
jiti: 2.7.0
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
@@ -7234,12 +7247,12 @@ snapshots:
|
|||||||
- supports-color
|
- supports-color
|
||||||
- yauzl
|
- yauzl
|
||||||
|
|
||||||
'@wdio/globals@9.31.3(expect-webdriverio@6.1.0)(webdriverio@10.1.0(supports-color@7.2.0)(yauzl@2.10.0))':
|
'@wdio/globals@9.31.3(expect-webdriverio@6.1.0)(webdriverio@10.2.0(supports-color@7.2.0)(yauzl@2.10.0))':
|
||||||
dependencies:
|
dependencies:
|
||||||
expect-webdriverio: 6.1.0(@wdio/globals@9.31.3)(@wdio/logger@9.29.1)(webdriverio@10.1.0(supports-color@7.2.0)(yauzl@2.10.0))
|
expect-webdriverio: 6.1.0(@wdio/globals@9.31.3)(@wdio/logger@9.29.1)(webdriverio@10.2.0(supports-color@7.2.0)(yauzl@2.10.0))
|
||||||
webdriverio: 10.1.0(supports-color@7.2.0)(yauzl@2.10.0)
|
webdriverio: 10.2.0(supports-color@7.2.0)(yauzl@2.10.0)
|
||||||
|
|
||||||
'@wdio/logger@10.1.0':
|
'@wdio/logger@10.2.0':
|
||||||
dependencies:
|
dependencies:
|
||||||
chalk: 6.0.1
|
chalk: 6.0.1
|
||||||
loglevel: 1.9.2
|
loglevel: 1.9.2
|
||||||
@@ -7283,9 +7296,9 @@ snapshots:
|
|||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
'@wdio/protocols@10.0.2': {}
|
'@wdio/protocols@10.2.0': {}
|
||||||
|
|
||||||
'@wdio/repl@10.0.0':
|
'@wdio/repl@10.2.0':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/node': 22.20.5
|
'@types/node': 22.20.5
|
||||||
|
|
||||||
@@ -7305,9 +7318,9 @@ snapshots:
|
|||||||
easy-table: 1.2.0
|
easy-table: 1.2.0
|
||||||
pretty-ms: 9.3.1
|
pretty-ms: 9.3.1
|
||||||
|
|
||||||
'@wdio/tauri-service@1.5.1(expect-webdriverio@6.1.0)(supports-color@7.2.0)(webdriverio@10.1.0(supports-color@7.2.0)(yauzl@2.10.0))':
|
'@wdio/tauri-service@1.5.1(expect-webdriverio@6.1.0)(supports-color@7.2.0)(webdriverio@10.2.0(supports-color@7.2.0)(yauzl@2.10.0))':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@wdio/globals': 9.31.3(expect-webdriverio@6.1.0)(webdriverio@10.1.0(supports-color@7.2.0)(yauzl@2.10.0))
|
'@wdio/globals': 9.31.3(expect-webdriverio@6.1.0)(webdriverio@10.2.0(supports-color@7.2.0)(yauzl@2.10.0))
|
||||||
'@wdio/logger': 9.29.1
|
'@wdio/logger': 9.29.1
|
||||||
'@wdio/native-core': 1.3.1(supports-color@7.2.0)
|
'@wdio/native-core': 1.3.1(supports-color@7.2.0)
|
||||||
'@wdio/native-spy': 1.4.1
|
'@wdio/native-spy': 1.4.1
|
||||||
@@ -7318,13 +7331,13 @@ snapshots:
|
|||||||
debug: 4.4.3(supports-color@7.2.0)
|
debug: 4.4.3(supports-color@7.2.0)
|
||||||
get-port: 7.2.0
|
get-port: 7.2.0
|
||||||
tslib: 2.8.1
|
tslib: 2.8.1
|
||||||
webdriverio: 10.1.0(supports-color@7.2.0)(yauzl@2.10.0)
|
webdriverio: 10.2.0(supports-color@7.2.0)(yauzl@2.10.0)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- expect-webdriverio
|
- expect-webdriverio
|
||||||
- supports-color
|
- supports-color
|
||||||
- tsx
|
- tsx
|
||||||
|
|
||||||
'@wdio/types@10.1.0':
|
'@wdio/types@10.2.0':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/node': 22.20.5
|
'@types/node': 22.20.5
|
||||||
|
|
||||||
@@ -7332,18 +7345,18 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
'@types/node': 20.19.43
|
'@types/node': 20.19.43
|
||||||
|
|
||||||
'@wdio/utils@10.1.0(supports-color@7.2.0)(yauzl@2.10.0)':
|
'@wdio/utils@10.2.0(supports-color@7.2.0)(yauzl@2.10.0)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@puppeteer/browsers': 3.2.3(yauzl@2.10.0)
|
'@puppeteer/browsers': 3.2.3(yauzl@2.10.0)
|
||||||
'@types/split2': 4.2.3
|
'@types/split2': 4.2.3
|
||||||
'@wdio/logger': 10.1.0
|
'@wdio/logger': 10.2.0
|
||||||
'@wdio/types': 10.1.0
|
'@wdio/types': 10.2.0
|
||||||
aria-query: 5.3.2
|
aria-query: 5.3.2
|
||||||
decamelize: 6.0.1
|
decamelize: 7.0.0
|
||||||
deepmerge-ts: 8.0.2
|
deepmerge-ts: 8.0.2
|
||||||
edgedriver: 8.0.0(@wdio/logger@10.1.0)
|
edgedriver: 8.0.0(@wdio/logger@10.2.0)
|
||||||
electron-to-chromium: 1.5.444
|
electron-to-chromium: 1.5.444
|
||||||
geckodriver: 8.0.0(@wdio/logger@10.1.0)
|
geckodriver: 8.0.0(@wdio/logger@10.2.0)
|
||||||
get-port: 7.2.0
|
get-port: 7.2.0
|
||||||
import-meta-resolve: 4.2.0
|
import-meta-resolve: 4.2.0
|
||||||
locate-app: 2.5.0
|
locate-app: 2.5.0
|
||||||
@@ -7754,6 +7767,8 @@ snapshots:
|
|||||||
|
|
||||||
decamelize@6.0.1: {}
|
decamelize@6.0.1: {}
|
||||||
|
|
||||||
|
decamelize@7.0.0: {}
|
||||||
|
|
||||||
decode-named-character-reference@1.3.0:
|
decode-named-character-reference@1.3.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
character-entities: 2.0.2
|
character-entities: 2.0.2
|
||||||
@@ -7825,9 +7840,9 @@ snapshots:
|
|||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
wcwidth: 1.0.1
|
wcwidth: 1.0.1
|
||||||
|
|
||||||
edgedriver@8.0.0(@wdio/logger@10.1.0):
|
edgedriver@8.0.0(@wdio/logger@10.2.0):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@wdio/logger': 10.1.0
|
'@wdio/logger': 10.2.0
|
||||||
'@zip.js/zip.js': 2.24.0
|
'@zip.js/zip.js': 2.24.0
|
||||||
decamelize: 6.0.1
|
decamelize: 6.0.1
|
||||||
undici: 8.12.3
|
undici: 8.12.3
|
||||||
@@ -8218,15 +8233,15 @@ snapshots:
|
|||||||
|
|
||||||
expect-type@1.4.0: {}
|
expect-type@1.4.0: {}
|
||||||
|
|
||||||
expect-webdriverio@6.1.0(@wdio/globals@9.31.3)(@wdio/logger@9.29.1)(webdriverio@10.1.0(supports-color@7.2.0)(yauzl@2.10.0)):
|
expect-webdriverio@6.1.0(@wdio/globals@9.31.3)(@wdio/logger@9.29.1)(webdriverio@10.2.0(supports-color@7.2.0)(yauzl@2.10.0)):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@vitest/snapshot': 4.1.11
|
'@vitest/snapshot': 4.1.11
|
||||||
'@wdio/globals': 9.31.3(expect-webdriverio@6.1.0)(webdriverio@10.1.0(supports-color@7.2.0)(yauzl@2.10.0))
|
'@wdio/globals': 9.31.3(expect-webdriverio@6.1.0)(webdriverio@10.2.0(supports-color@7.2.0)(yauzl@2.10.0))
|
||||||
'@wdio/logger': 9.29.1
|
'@wdio/logger': 9.29.1
|
||||||
deep-eql: 5.0.2
|
deep-eql: 5.0.2
|
||||||
expect: 30.5.2
|
expect: 30.5.2
|
||||||
jest-matcher-utils: 30.5.2
|
jest-matcher-utils: 30.5.2
|
||||||
webdriverio: 10.1.0(supports-color@7.2.0)(yauzl@2.10.0)
|
webdriverio: 10.2.0(supports-color@7.2.0)(yauzl@2.10.0)
|
||||||
|
|
||||||
expect@30.5.2:
|
expect@30.5.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -8325,9 +8340,9 @@ snapshots:
|
|||||||
|
|
||||||
function-bind@1.1.2: {}
|
function-bind@1.1.2: {}
|
||||||
|
|
||||||
geckodriver@8.0.0(@wdio/logger@10.1.0):
|
geckodriver@8.0.0(@wdio/logger@10.2.0):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@wdio/logger': 10.1.0
|
'@wdio/logger': 10.2.0
|
||||||
'@zip.js/zip.js': 2.24.0
|
'@zip.js/zip.js': 2.24.0
|
||||||
decamelize: 6.0.1
|
decamelize: 6.0.1
|
||||||
modern-tar: 0.8.5
|
modern-tar: 0.8.5
|
||||||
@@ -8367,11 +8382,11 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
is-glob: 4.0.3
|
is-glob: 4.0.3
|
||||||
|
|
||||||
glob@13.0.6:
|
glob@14.0.4:
|
||||||
dependencies:
|
dependencies:
|
||||||
minimatch: 10.2.6
|
minimatch: 11.0.5
|
||||||
minipass: 7.1.3
|
minipass: 7.1.3
|
||||||
path-scurry: 2.0.2
|
path-scurry: 3.0.0
|
||||||
|
|
||||||
globals@17.12.0: {}
|
globals@17.12.0: {}
|
||||||
|
|
||||||
@@ -9063,6 +9078,11 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
brace-expansion: 5.0.12
|
brace-expansion: 5.0.12
|
||||||
|
|
||||||
|
minimatch@11.0.5:
|
||||||
|
dependencies:
|
||||||
|
'@isaacs/nonrecursive-glob-match': 1.1.0
|
||||||
|
brace-expansion: 5.0.12
|
||||||
|
|
||||||
minipass@7.1.3: {}
|
minipass@7.1.3: {}
|
||||||
|
|
||||||
minizlib@3.1.0:
|
minizlib@3.1.0:
|
||||||
@@ -9258,7 +9278,7 @@ snapshots:
|
|||||||
|
|
||||||
path-parse@1.0.7: {}
|
path-parse@1.0.7: {}
|
||||||
|
|
||||||
path-scurry@2.0.2:
|
path-scurry@3.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
lru-cache: 11.5.3
|
lru-cache: 11.5.3
|
||||||
minipass: 7.1.3
|
minipass: 7.1.3
|
||||||
@@ -9342,7 +9362,7 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
react: 19.3.1
|
react: 19.3.1
|
||||||
|
|
||||||
react-i18next@17.1.10(i18next@26.4.2(typescript@6.0.3))(react-dom@19.3.1(react@19.3.1))(react@19.3.1)(typescript@6.0.3):
|
react-i18next@17.1.13(i18next@26.4.2(typescript@6.0.3))(react-dom@19.3.1(react@19.3.1))(react@19.3.1)(typescript@6.0.3):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/runtime': 7.29.7
|
'@babel/runtime': 7.29.7
|
||||||
html-parse-stringify: 4.0.1
|
html-parse-stringify: 4.0.1
|
||||||
@@ -9975,15 +9995,15 @@ snapshots:
|
|||||||
|
|
||||||
web-namespaces@2.0.1: {}
|
web-namespaces@2.0.1: {}
|
||||||
|
|
||||||
webdriver@10.1.0(supports-color@7.2.0)(yauzl@2.10.0):
|
webdriver@10.2.0(supports-color@7.2.0)(yauzl@2.10.0):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/node': 22.20.5
|
'@types/node': 22.20.5
|
||||||
'@types/ws': 8.18.1
|
'@types/ws': 8.18.1
|
||||||
'@wdio/config': 10.1.0(supports-color@7.2.0)(yauzl@2.10.0)
|
'@wdio/config': 10.2.0(supports-color@7.2.0)(yauzl@2.10.0)
|
||||||
'@wdio/logger': 10.1.0
|
'@wdio/logger': 10.2.0
|
||||||
'@wdio/protocols': 10.0.2
|
'@wdio/protocols': 10.2.0
|
||||||
'@wdio/types': 10.1.0
|
'@wdio/types': 10.2.0
|
||||||
'@wdio/utils': 10.1.0(supports-color@7.2.0)(yauzl@2.10.0)
|
'@wdio/utils': 10.2.0(supports-color@7.2.0)(yauzl@2.10.0)
|
||||||
deepmerge-ts: 8.0.2
|
deepmerge-ts: 8.0.2
|
||||||
https-proxy-agent: 9.1.0(supports-color@7.2.0)
|
https-proxy-agent: 9.1.0(supports-color@7.2.0)
|
||||||
undici: 8.12.3
|
undici: 8.12.3
|
||||||
@@ -9996,16 +10016,16 @@ snapshots:
|
|||||||
- utf-8-validate
|
- utf-8-validate
|
||||||
- yauzl
|
- yauzl
|
||||||
|
|
||||||
webdriverio@10.1.0(supports-color@7.2.0)(yauzl@2.10.0):
|
webdriverio@10.2.0(supports-color@7.2.0)(yauzl@2.10.0):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/node': 22.20.5
|
'@types/node': 22.20.5
|
||||||
'@types/sinonjs__fake-timers': 15.0.1
|
'@types/sinonjs__fake-timers': 15.0.1
|
||||||
'@wdio/config': 10.1.0(supports-color@7.2.0)(yauzl@2.10.0)
|
'@wdio/config': 10.2.0(supports-color@7.2.0)(yauzl@2.10.0)
|
||||||
'@wdio/logger': 10.1.0
|
'@wdio/logger': 10.2.0
|
||||||
'@wdio/protocols': 10.0.2
|
'@wdio/protocols': 10.2.0
|
||||||
'@wdio/repl': 10.0.0
|
'@wdio/repl': 10.2.0
|
||||||
'@wdio/types': 10.1.0
|
'@wdio/types': 10.2.0
|
||||||
'@wdio/utils': 10.1.0(supports-color@7.2.0)(yauzl@2.10.0)
|
'@wdio/utils': 10.2.0(supports-color@7.2.0)(yauzl@2.10.0)
|
||||||
archiver: 8.0.0
|
archiver: 8.0.0
|
||||||
aria-query: 5.3.2
|
aria-query: 5.3.2
|
||||||
cheerio: 1.2.0
|
cheerio: 1.2.0
|
||||||
@@ -10020,7 +10040,7 @@ snapshots:
|
|||||||
rgb2hex: 0.2.5
|
rgb2hex: 0.2.5
|
||||||
serialize-error: 13.0.2
|
serialize-error: 13.0.2
|
||||||
urlpattern-polyfill: 10.1.0
|
urlpattern-polyfill: 10.1.0
|
||||||
webdriver: 10.1.0(supports-color@7.2.0)(yauzl@2.10.0)
|
webdriver: 10.2.0(supports-color@7.2.0)(yauzl@2.10.0)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- bare-abort-controller
|
- bare-abort-controller
|
||||||
- bare-buffer
|
- bare-buffer
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ smartstring = { workspace = true, features = ["serde"] }
|
|||||||
socket2 = { workspace = true }
|
socket2 = { workspace = true }
|
||||||
warp = { version = "0.4.3", features = ["server"] }
|
warp = { version = "0.4.3", features = ["server"] }
|
||||||
open = "5.4.5"
|
open = "5.4.5"
|
||||||
rfd = { version = "0.17.2", default-features = false, features = ["common-controls-v6"] }
|
rfd = { version = "0.18.0", default-features = false, features = ["common-controls-v6"] }
|
||||||
dirs = "7.0.0"
|
dirs = "7.0.0"
|
||||||
dunce = "1.0.5"
|
dunce = "1.0.5"
|
||||||
nanoid = "0.5"
|
nanoid = "0.5"
|
||||||
@@ -76,8 +76,8 @@ tauri-plugin-dialog = "2.8.4"
|
|||||||
tauri-plugin-fs = "2.7.1"
|
tauri-plugin-fs = "2.7.1"
|
||||||
tauri-plugin-deep-link = "2.6.3"
|
tauri-plugin-deep-link = "2.6.3"
|
||||||
tauri-plugin-window-state = "2.5.0"
|
tauri-plugin-window-state = "2.5.0"
|
||||||
zip = "9.3.0"
|
zip = "9.4.1"
|
||||||
flate2 = "1.1.10"
|
flate2 = "1.1.11"
|
||||||
reqwest_dav = "0.3.3"
|
reqwest_dav = "0.3.3"
|
||||||
aes-gcm = { version = "0.11.1" }
|
aes-gcm = { version = "0.11.1" }
|
||||||
base64 = "0.23.1"
|
base64 = "0.23.1"
|
||||||
@@ -96,7 +96,7 @@ async-trait = "0.1.92"
|
|||||||
clash_verge_service_ipc = { git = "https://github.com/clash-verge-rev/clash-verge-service-ipc.git", tag = "v2.7.7", version = "2.7.7", features = [
|
clash_verge_service_ipc = { git = "https://github.com/clash-verge-rev/clash-verge-service-ipc.git", tag = "v2.7.7", version = "2.7.7", features = [
|
||||||
"client",
|
"client",
|
||||||
] }
|
] }
|
||||||
arc-swap = "1.9.3"
|
arc-swap = "1.9.4"
|
||||||
tokio-rustls = "0.26.6"
|
tokio-rustls = "0.26.6"
|
||||||
rustls = { version = "0.23.46", features = ["ring"] }
|
rustls = { version = "0.23.46", features = ["ring"] }
|
||||||
webpki-roots = "1.0.11"
|
webpki-roots = "1.0.11"
|
||||||
|
|||||||
@@ -5,10 +5,16 @@ use crate::{
|
|||||||
core::{
|
core::{
|
||||||
handle::Handle,
|
handle::Handle,
|
||||||
proxy_view::{GroupScope, ProxyViewBuilder, ProxyViewInput, ProxyViewV1},
|
proxy_view::{GroupScope, ProxyViewBuilder, ProxyViewInput, ProxyViewV1},
|
||||||
|
tray::Tray,
|
||||||
},
|
},
|
||||||
|
process::AsyncHandler,
|
||||||
};
|
};
|
||||||
|
use clash_verge_logging::{Type, logging};
|
||||||
use serde_yaml_ng::Mapping;
|
use serde_yaml_ng::Mapping;
|
||||||
use std::collections::{BTreeMap, HashSet};
|
use std::{
|
||||||
|
collections::{BTreeMap, HashSet},
|
||||||
|
sync::atomic::{AtomicBool, Ordering},
|
||||||
|
};
|
||||||
|
|
||||||
/// Merges one selection pair into fresh backend state to avoid stale-list overwrites.
|
/// Merges one selection pair into fresh backend state to avoid stale-list overwrites.
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
@@ -21,12 +27,15 @@ pub async fn record_selected_node(group_name: String, node: String) -> CmdResult
|
|||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
#[specta::specta]
|
#[specta::specta]
|
||||||
pub async fn switch_proxy_node(group_name: String, proxy_name: String, unfix: bool) -> CmdResult<()> {
|
pub async fn forget_selected_node(group_name: String) -> CmdResult<()> {
|
||||||
crate::feat::switch_proxy_node(&group_name, &proxy_name, unfix)
|
crate::config::profiles::forget_selected_node(&group_name)
|
||||||
.await
|
.await
|
||||||
.stringify_err()
|
.stringify_err()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static TRAY_SYNC_RUNNING: AtomicBool = AtomicBool::new(false);
|
||||||
|
static TRAY_SYNC_PENDING: AtomicBool = AtomicBool::new(false);
|
||||||
|
|
||||||
fn runtime_group_order(config: Option<&Mapping>) -> Vec<String> {
|
fn runtime_group_order(config: Option<&Mapping>) -> Vec<String> {
|
||||||
let mut seen = HashSet::new();
|
let mut seen = HashSet::new();
|
||||||
|
|
||||||
@@ -115,3 +124,48 @@ pub async fn get_proxy_view() -> CmdResult<ProxyViewV1> {
|
|||||||
providers: providers.ok(),
|
providers: providers.ok(),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
#[specta::specta]
|
||||||
|
pub async fn sync_tray_proxy_selection() -> CmdResult<()> {
|
||||||
|
if TRAY_SYNC_RUNNING
|
||||||
|
.compare_exchange(false, true, Ordering::AcqRel, Ordering::Acquire)
|
||||||
|
.is_ok()
|
||||||
|
{
|
||||||
|
AsyncHandler::spawn(move || async move {
|
||||||
|
run_tray_sync_loop().await;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
TRAY_SYNC_PENDING.store(true, Ordering::Release);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn run_tray_sync_loop() {
|
||||||
|
// Permanent watcher: keeps syncing until a pass finds no pending sync request.
|
||||||
|
loop {
|
||||||
|
match Tray::global().update_menu().await {
|
||||||
|
Ok(_) => {
|
||||||
|
logging!(debug, Type::Cmd, "Tray proxy selection synced successfully");
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
logging!(error, Type::Cmd, "Failed to sync tray proxy selection: {e:#}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !TRAY_SYNC_PENDING.swap(false, Ordering::AcqRel) {
|
||||||
|
TRAY_SYNC_RUNNING.store(false, Ordering::Release);
|
||||||
|
|
||||||
|
if TRAY_SYNC_PENDING.swap(false, Ordering::AcqRel)
|
||||||
|
&& TRAY_SYNC_RUNNING
|
||||||
|
.compare_exchange(false, true, Ordering::AcqRel, Ordering::Acquire)
|
||||||
|
.is_ok()
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1000,9 +1000,7 @@ fn on_menu_event(_: &AppHandle, event: MenuEvent) {
|
|||||||
Some((g, p)) => (g, p),
|
Some((g, p)) => (g, p),
|
||||||
None => return,
|
None => return,
|
||||||
};
|
};
|
||||||
if let Err(err) = feat::switch_proxy_node(group_name, proxy_name, false).await {
|
feat::switch_proxy_node(group_name, proxy_name).await;
|
||||||
logging!(error, Type::Tray, "Failed to switch proxy: {err:#}");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
logging!(debug, Type::Tray, "Unhandled tray menu event: {:?}", event.id);
|
logging!(debug, Type::Tray, "Unhandled tray menu event: {:?}", event.id);
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ use crate::{
|
|||||||
cmd,
|
cmd,
|
||||||
config::{Config, PrfItem, PrfOption, profiles::profiles_update_item_safe},
|
config::{Config, PrfItem, PrfOption, profiles::profiles_update_item_safe},
|
||||||
core::{CoreManager, handle, tray, validate::ValidationOutcome},
|
core::{CoreManager, handle, tray, validate::ValidationOutcome},
|
||||||
process::AsyncHandler,
|
|
||||||
utils::help::{mask_err, mask_url},
|
utils::help::{mask_err, mask_url},
|
||||||
};
|
};
|
||||||
use anyhow::{Result, bail};
|
use anyhow::{Result, bail};
|
||||||
@@ -22,85 +21,37 @@ pub async fn toggle_proxy_profile(profile_index: String) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn switch_proxy_node(group_name: &str, proxy_name: &str, unfix: bool) -> Result<()> {
|
/// Tell the profile which node this group is on now.
|
||||||
let mihomo = handle::Handle::mihomo();
|
///
|
||||||
let previous_proxy = if Config::verge().await.data_arc().auto_close_connection.unwrap_or(false) {
|
/// The core is already switched by the time this runs, so failing to record is not a reason to
|
||||||
match mihomo.get_proxy_by_name(group_name).await {
|
/// report the switch as failed — but it is worth a log line, because what the profile holds is
|
||||||
Ok(group) => group.now.filter(|name| !name.is_empty()),
|
/// what gets re-applied the next time a core starts.
|
||||||
Err(err) => {
|
async fn record_switched_node(group_name: &str, proxy_name: &str) {
|
||||||
logging!(
|
if let Err(error) = crate::config::profiles::record_selected_node(group_name, proxy_name).await {
|
||||||
warn,
|
|
||||||
Type::Core,
|
|
||||||
"Failed to read previous proxy selection for {group_name}: {err}"
|
|
||||||
);
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
if unfix {
|
|
||||||
mihomo.unfixed_proxy(group_name).await?;
|
|
||||||
} else {
|
|
||||||
mihomo.select_node_for_group(group_name, proxy_name).await?;
|
|
||||||
}
|
|
||||||
|
|
||||||
let save_result = if unfix {
|
|
||||||
crate::config::profiles::forget_selected_node(group_name).await
|
|
||||||
} else {
|
|
||||||
crate::config::profiles::record_selected_node(group_name, proxy_name).await
|
|
||||||
};
|
|
||||||
// The core selection is already applied; persistence failure must not report it as failed.
|
|
||||||
if let Err(error) = save_result {
|
|
||||||
logging!(
|
logging!(
|
||||||
warn,
|
warn,
|
||||||
Type::Config,
|
Type::Config,
|
||||||
"Failed to persist proxy selection for {group_name}: {error:#}"
|
"切换代理成功但未能记录到配置: {} -> {}, 错误: {error:#}",
|
||||||
|
group_name,
|
||||||
|
proxy_name
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
announce(Refresh::Proxies);
|
}
|
||||||
|
|
||||||
if let Some(previous_proxy) = previous_proxy {
|
pub async fn switch_proxy_node(group_name: &str, proxy_name: &str) {
|
||||||
AsyncHandler::spawn(move || async move {
|
match handle::Handle::mihomo()
|
||||||
let connections = match mihomo.get_connections().await {
|
.select_node_for_group(group_name, proxy_name)
|
||||||
Ok(connections) => connections,
|
.await
|
||||||
Err(err) => {
|
{
|
||||||
logging!(
|
Ok(_) => {
|
||||||
warn,
|
record_switched_node(group_name, proxy_name).await;
|
||||||
Type::Core,
|
announce(Refresh::Proxies);
|
||||||
"Failed to read connections after switching proxy: {err}"
|
let _ = tray::Tray::global().update_menu().await;
|
||||||
);
|
}
|
||||||
return;
|
Err(err) => {
|
||||||
}
|
logging!(error, Type::Tray, "切换代理失败: {err:?}");
|
||||||
};
|
}
|
||||||
futures::future::join_all(
|
|
||||||
connections
|
|
||||||
.connections
|
|
||||||
.unwrap_or_default()
|
|
||||||
.into_iter()
|
|
||||||
.filter(|connection| connection.chains.contains(&previous_proxy))
|
|
||||||
.map(|connection| async move {
|
|
||||||
if let Err(err) = mihomo.close_connection(&connection.id).await {
|
|
||||||
logging!(
|
|
||||||
warn,
|
|
||||||
Type::Core,
|
|
||||||
"Failed to close connection {} after switching proxy: {err}",
|
|
||||||
connection.id
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
if let Err(err) = tray::Tray::global().update_menu().await {
|
|
||||||
logging!(
|
|
||||||
warn,
|
|
||||||
Type::Tray,
|
|
||||||
"Failed to refresh tray after switching proxy: {err:#}"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn should_update_profile(uid: &String, ignore_auto_update: bool) -> Result<Option<(String, Option<PrfOption>)>> {
|
async fn should_update_profile(uid: &String, ignore_auto_update: bool) -> Result<Option<(String, Option<PrfOption>)>> {
|
||||||
|
|||||||
@@ -45,8 +45,9 @@ pub fn builder() -> tauri_specta::Builder<tauri::Wry> {
|
|||||||
cmd::update_proxy_chain_config_in_runtime,
|
cmd::update_proxy_chain_config_in_runtime,
|
||||||
cmd::invoke_uwp_tool,
|
cmd::invoke_uwp_tool,
|
||||||
cmd::copy_clash_env,
|
cmd::copy_clash_env,
|
||||||
|
cmd::sync_tray_proxy_selection,
|
||||||
cmd::record_selected_node,
|
cmd::record_selected_node,
|
||||||
cmd::switch_proxy_node,
|
cmd::forget_selected_node,
|
||||||
cmd::save_dns_config,
|
cmd::save_dns_config,
|
||||||
cmd::apply_dns_config,
|
cmd::apply_dns_config,
|
||||||
cmd::set_dns_override,
|
cmd::set_dns_override,
|
||||||
|
|||||||
@@ -606,6 +606,9 @@ export const CurrentProxyCard = () => {
|
|||||||
)
|
)
|
||||||
|
|
||||||
const { select } = useProxySelection({
|
const { select } = useProxySelection({
|
||||||
|
onSuccess: () => {
|
||||||
|
refreshProxy()
|
||||||
|
},
|
||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
console.error('代理切换失败', error)
|
console.error('代理切换失败', error)
|
||||||
refreshProxy()
|
refreshProxy()
|
||||||
@@ -752,9 +755,10 @@ export const CurrentProxyCard = () => {
|
|||||||
if (!selectedGroup || !option || !isInteractableMember(option.member)) {
|
if (!selectedGroup || !option || !isInteractableMember(option.member)) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
const previousProxy = selectedGroup.now
|
||||||
const nextName = option.member.ref.name
|
const nextName = option.member.ref.name
|
||||||
// The profile selection is the durable source across core restarts and run modes.
|
// The profile selection is the durable source across core restarts and run modes.
|
||||||
select(selectedGroup.name, nextName, selectedGroup.fixed!)
|
select(selectedGroup.name, nextName, previousProxy!, selectedGroup.fixed!)
|
||||||
},
|
},
|
||||||
[isDirectMode, select, selectedGroup, unsortedProxyOptions],
|
[isDirectMode, select, selectedGroup, unsortedProxyOptions],
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -470,6 +470,9 @@ function NormalProxyGroups(props: { mode: string }) {
|
|||||||
}, [handleScroll])
|
}, [handleScroll])
|
||||||
|
|
||||||
const { select } = useProxySelection({
|
const { select } = useProxySelection({
|
||||||
|
onSuccess: () => {
|
||||||
|
onProxies()
|
||||||
|
},
|
||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
console.error('代理切换失败', error)
|
console.error('代理切换失败', error)
|
||||||
onProxies()
|
onProxies()
|
||||||
@@ -484,12 +487,12 @@ function NormalProxyGroups(props: { mode: string }) {
|
|||||||
if (group.name === globalGroupName) {
|
if (group.name === globalGroupName) {
|
||||||
pendingRevealRef.current =
|
pendingRevealRef.current =
|
||||||
member.kind === 'group' ? member.ref.name : null
|
member.kind === 'group' ? member.ref.name : null
|
||||||
// The backend selection event refreshes the view.
|
// The selection refetches the view, so skip onHeadState's refetch.
|
||||||
if (member.kind === 'group') {
|
if (member.kind === 'group') {
|
||||||
setHeadState(member.ref.name, { open: true })
|
setHeadState(member.ref.name, { open: true })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
select(group.name, member.ref.name, group.fixed!)
|
select(group.name, member.ref.name, group.now!, group.fixed!)
|
||||||
},
|
},
|
||||||
[globalGroupName, select, setHeadState],
|
[globalGroupName, select, setHeadState],
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,35 +1,51 @@
|
|||||||
|
import { selectNodeForGroup } from 'tauri-plugin-mihomo-api'
|
||||||
import { expect, it, vi } from 'vitest'
|
import { expect, it, vi } from 'vitest'
|
||||||
|
|
||||||
import { switchProxyNode } from '@/services/cmds'
|
|
||||||
|
|
||||||
import { useProxySelection } from './use-proxy-selection'
|
import { useProxySelection } from './use-proxy-selection'
|
||||||
|
|
||||||
|
const record = vi.hoisted(() => vi.fn())
|
||||||
vi.mock('react', () => ({
|
vi.mock('react', () => ({
|
||||||
useCallback: (callback: unknown) => callback,
|
useCallback: (callback: unknown) => callback,
|
||||||
useRef: (current: unknown) => ({ current }),
|
useRef: (current: unknown) => ({ current }),
|
||||||
}))
|
}))
|
||||||
vi.mock('@/services/cmds', () => ({ switchProxyNode: vi.fn() }))
|
vi.mock('@/hooks/use-record-selection', () => ({
|
||||||
|
useRecordSelection: () => record,
|
||||||
|
useForgetSelection: () => vi.fn(),
|
||||||
|
}))
|
||||||
|
vi.mock('@/hooks/use-verge', () => ({ useVerge: () => ({ verge: {} }) }))
|
||||||
|
vi.mock('tauri-plugin-mihomo-api', () => ({
|
||||||
|
selectNodeForGroup: vi.fn(),
|
||||||
|
unfixedProxy: vi.fn(),
|
||||||
|
}))
|
||||||
|
vi.mock('@/services/cmds', () => ({ syncTrayProxySelection: vi.fn() }))
|
||||||
vi.mock('@/services/notice-service', () => ({ showNotice: { error: vi.fn() } }))
|
vi.mock('@/services/notice-service', () => ({ showNotice: { error: vi.fn() } }))
|
||||||
vi.mock('@/services/query-client', () => ({ revalidateQueries: vi.fn() }))
|
vi.mock('@/services/query-client', () => ({ revalidateQueries: vi.fn() }))
|
||||||
|
|
||||||
it('requests independent group selections while another group is pending', async () => {
|
it('applies and records independent group selections while another group is pending', async () => {
|
||||||
let finishFirst!: () => void
|
let finishFirst!: () => void
|
||||||
vi.mocked(switchProxyNode)
|
vi.mocked(selectNodeForGroup)
|
||||||
.mockImplementationOnce(
|
.mockImplementationOnce(
|
||||||
() =>
|
() =>
|
||||||
new Promise((resolve) => {
|
new Promise((resolve) => {
|
||||||
finishFirst = () => resolve(null)
|
finishFirst = resolve
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.mockResolvedValueOnce(null)
|
.mockResolvedValueOnce(undefined)
|
||||||
const first = useProxySelection()
|
const firstSuccess = vi.fn()
|
||||||
const second = useProxySelection()
|
const secondSuccess = vi.fn()
|
||||||
|
const first = useProxySelection({ onSuccess: firstSuccess })
|
||||||
|
const second = useProxySelection({ onSuccess: secondSuccess })
|
||||||
first.select('Group A', 'Node A')
|
first.select('Group A', 'Node A')
|
||||||
second.select('Group B', 'Node B', 'Node B')
|
second.select('Group B', 'Node B')
|
||||||
await vi.waitFor(() =>
|
await vi.waitFor(() =>
|
||||||
expect(switchProxyNode).toHaveBeenCalledWith('Group B', 'Node B', true),
|
expect(record).toHaveBeenCalledWith('Group B', 'Node B'),
|
||||||
)
|
)
|
||||||
expect(switchProxyNode).toHaveBeenCalledWith('Group A', 'Node A', false)
|
expect(selectNodeForGroup).toHaveBeenCalledTimes(2)
|
||||||
expect(switchProxyNode).toHaveBeenCalledTimes(2)
|
expect(firstSuccess).not.toHaveBeenCalled()
|
||||||
|
expect(secondSuccess).toHaveBeenCalledOnce()
|
||||||
finishFirst()
|
finishFirst()
|
||||||
|
await vi.waitFor(() =>
|
||||||
|
expect(record).toHaveBeenCalledWith('Group A', 'Node A'),
|
||||||
|
)
|
||||||
|
expect(firstSuccess).toHaveBeenCalledOnce()
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,39 +1,121 @@
|
|||||||
import { useCallback, useRef } from 'react'
|
import { useCallback, useRef } from 'react'
|
||||||
|
import {
|
||||||
|
closeConnection,
|
||||||
|
getConnections,
|
||||||
|
selectNodeForGroup,
|
||||||
|
unfixedProxy,
|
||||||
|
} from 'tauri-plugin-mihomo-api'
|
||||||
|
|
||||||
import { switchProxyNode } from '@/services/cmds'
|
import {
|
||||||
|
useForgetSelection,
|
||||||
|
useRecordSelection,
|
||||||
|
} from '@/hooks/use-record-selection'
|
||||||
|
import { useVerge } from '@/hooks/use-verge'
|
||||||
|
import { syncTrayProxySelection } from '@/services/cmds'
|
||||||
import { mutate } from '@/services/mutate'
|
import { mutate } from '@/services/mutate'
|
||||||
|
import { debugLog } from '@/utils/debug'
|
||||||
|
|
||||||
|
// 缓存连接清理
|
||||||
|
const cleanupConnections = async (previousProxy: string) => {
|
||||||
|
try {
|
||||||
|
const { connections } = await getConnections()
|
||||||
|
const cleanupPromises = (connections ?? [])
|
||||||
|
.filter((conn) => conn.chains.includes(previousProxy))
|
||||||
|
.map((conn) => closeConnection(conn.id))
|
||||||
|
|
||||||
|
if (cleanupPromises.length > 0) {
|
||||||
|
await Promise.allSettled(cleanupPromises)
|
||||||
|
debugLog(`[ProxySelection] 清理了 ${cleanupPromises.length} 个连接`)
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.warn('[ProxySelection] 连接清理失败:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
interface ProxySelectionOptions {
|
interface ProxySelectionOptions {
|
||||||
|
onSuccess?: () => void
|
||||||
onError?: (error: any) => void
|
onError?: (error: any) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ProxyChangeRequest {
|
interface ProxyChangeRequest {
|
||||||
groupName: string
|
groupName: string
|
||||||
proxyName: string
|
proxyName: string
|
||||||
|
previousProxy?: string
|
||||||
fixed?: string
|
fixed?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 代理选择 Hook
|
||||||
export const useProxySelection = (options: ProxySelectionOptions = {}) => {
|
export const useProxySelection = (options: ProxySelectionOptions = {}) => {
|
||||||
|
const recordSelection = useRecordSelection()
|
||||||
|
const forgetSelection = useForgetSelection()
|
||||||
|
const { verge } = useVerge()
|
||||||
const pendingRequestRef = useRef<ProxyChangeRequest | null>(null)
|
const pendingRequestRef = useRef<ProxyChangeRequest | null>(null)
|
||||||
const isProcessingRef = useRef(false)
|
const isProcessingRef = useRef(false)
|
||||||
const { onError } = options
|
|
||||||
|
const { onSuccess, onError } = options
|
||||||
|
|
||||||
|
const autoCloseConnection = verge?.auto_close_connection ?? false
|
||||||
|
|
||||||
|
// 切换节点
|
||||||
|
const syncTraySelection = useCallback(() => {
|
||||||
|
mutate(() => syncTrayProxySelection(), {
|
||||||
|
id: 'sync-tray-selection',
|
||||||
|
errorNotice: false,
|
||||||
|
}).catch((error) => {
|
||||||
|
console.error('[ProxySelection] 托盘状态同步失败:', error)
|
||||||
|
})
|
||||||
|
}, [])
|
||||||
|
|
||||||
const executeChange = useCallback(
|
const executeChange = useCallback(
|
||||||
async ({ groupName, proxyName, fixed }: ProxyChangeRequest) => {
|
async (request: ProxyChangeRequest) => {
|
||||||
|
const { groupName, proxyName, previousProxy, fixed } = request
|
||||||
|
const isFixedProxy = fixed === proxyName
|
||||||
|
if (isFixedProxy) {
|
||||||
|
debugLog(`[ProxySelection] 代理取消固定: ${groupName} -> ${proxyName}`)
|
||||||
|
} else {
|
||||||
|
debugLog(`[ProxySelection] 代理切换: ${groupName} -> ${proxyName}`)
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await mutate(
|
const result = await mutate(
|
||||||
() => switchProxyNode(groupName, proxyName, fixed === proxyName),
|
() =>
|
||||||
|
isFixedProxy
|
||||||
|
? unfixedProxy(groupName)
|
||||||
|
: selectNodeForGroup(groupName, proxyName),
|
||||||
{ id: `select-proxy-node:${groupName}`, errorNotice: false },
|
{ id: `select-proxy-node:${groupName}`, errorNotice: false },
|
||||||
)
|
)
|
||||||
|
if (!result.ok) return
|
||||||
|
onSuccess?.()
|
||||||
|
syncTraySelection()
|
||||||
|
if (isFixedProxy) {
|
||||||
|
await forgetSelection(groupName)
|
||||||
|
debugLog(`[ProxySelection] 代理和状态同步完成: ${groupName}`)
|
||||||
|
} else {
|
||||||
|
await recordSelection(groupName, proxyName)
|
||||||
|
debugLog(
|
||||||
|
`[ProxySelection] 代理和状态同步完成: ${groupName} -> ${proxyName}`,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (autoCloseConnection && previousProxy) {
|
||||||
|
void cleanupConnections(previousProxy)
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(
|
console.error(
|
||||||
`[ProxySelection] Failed to switch proxy: ${groupName} -> ${proxyName}`,
|
`[ProxySelection] 代理切换失败: ${groupName} -> ${proxyName}`,
|
||||||
error,
|
error,
|
||||||
)
|
)
|
||||||
onError?.(error)
|
onError?.(error)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[onError],
|
[
|
||||||
|
autoCloseConnection,
|
||||||
|
forgetSelection,
|
||||||
|
onError,
|
||||||
|
onSuccess,
|
||||||
|
recordSelection,
|
||||||
|
syncTraySelection,
|
||||||
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
const flushChangeQueue = useCallback(async () => {
|
const flushChangeQueue = useCallback(async () => {
|
||||||
@@ -55,10 +137,16 @@ export const useProxySelection = (options: ProxySelectionOptions = {}) => {
|
|||||||
}, [executeChange])
|
}, [executeChange])
|
||||||
|
|
||||||
const select = useCallback(
|
const select = useCallback(
|
||||||
(groupName: string, proxyName: string, fixed?: string) => {
|
(
|
||||||
|
groupName: string,
|
||||||
|
proxyName: string,
|
||||||
|
previousProxy?: string,
|
||||||
|
fixed?: string,
|
||||||
|
) => {
|
||||||
pendingRequestRef.current = {
|
pendingRequestRef.current = {
|
||||||
groupName,
|
groupName,
|
||||||
proxyName,
|
proxyName,
|
||||||
|
previousProxy,
|
||||||
fixed,
|
fixed,
|
||||||
}
|
}
|
||||||
void flushChangeQueue()
|
void flushChangeQueue()
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { useCallback } from 'react'
|
import { useCallback } from 'react'
|
||||||
|
|
||||||
import { recordSelectedNode } from '@/services/cmds'
|
import { forgetSelectedNode, recordSelectedNode } from '@/services/cmds'
|
||||||
import { mutate } from '@/services/mutate'
|
import { mutate } from '@/services/mutate'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -19,3 +19,16 @@ export const useRecordSelection = () => {
|
|||||||
}
|
}
|
||||||
}, [])
|
}, [])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const useForgetSelection = () => {
|
||||||
|
return useCallback(async (groupName: string) => {
|
||||||
|
try {
|
||||||
|
await mutate(() => forgetSelectedNode(groupName), {
|
||||||
|
id: `persist-selected-node:${groupName}`,
|
||||||
|
errorNotice: false,
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
console.error('[Selection] 清除代理选择失败:', error)
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
}
|
||||||
|
|||||||
@@ -78,11 +78,13 @@ export const commands = {
|
|||||||
}),
|
}),
|
||||||
invokeUwpTool: () => __TAURI_INVOKE<null>('invoke_uwp_tool'),
|
invokeUwpTool: () => __TAURI_INVOKE<null>('invoke_uwp_tool'),
|
||||||
copyClashEnv: () => __TAURI_INVOKE<null>('copy_clash_env'),
|
copyClashEnv: () => __TAURI_INVOKE<null>('copy_clash_env'),
|
||||||
|
syncTrayProxySelection: () =>
|
||||||
|
__TAURI_INVOKE<null>('sync_tray_proxy_selection'),
|
||||||
/** Merges one selection pair into fresh backend state to avoid stale-list overwrites. */
|
/** Merges one selection pair into fresh backend state to avoid stale-list overwrites. */
|
||||||
recordSelectedNode: (groupName: string, node: string) =>
|
recordSelectedNode: (groupName: string, node: string) =>
|
||||||
__TAURI_INVOKE<null>('record_selected_node', { groupName, node }),
|
__TAURI_INVOKE<null>('record_selected_node', { groupName, node }),
|
||||||
switchProxyNode: (groupName: string, proxyName: string, unfix: boolean) =>
|
forgetSelectedNode: (groupName: string) =>
|
||||||
__TAURI_INVOKE<null>('switch_proxy_node', { groupName, proxyName, unfix }),
|
__TAURI_INVOKE<null>('forget_selected_node', { groupName }),
|
||||||
saveDnsConfig: (dnsConfig: YamlMapping) =>
|
saveDnsConfig: (dnsConfig: YamlMapping) =>
|
||||||
__TAURI_INVOKE<null>('save_dns_config', { dnsConfig }),
|
__TAURI_INVOKE<null>('save_dns_config', { dnsConfig }),
|
||||||
applyDnsConfig: (apply: boolean) =>
|
applyDnsConfig: (apply: boolean) =>
|
||||||
|
|||||||
@@ -29,11 +29,14 @@ export interface BusHandlerDeps {
|
|||||||
/**
|
/**
|
||||||
* Pure per-event handler table for the bus. Handlers may trigger documented
|
* Pure per-event handler table for the bus. Handlers may trigger documented
|
||||||
* side effects (SWR revalidation, notice toasts) but write store state only
|
* side effects (SWR revalidation, notice toasts) but write store state only
|
||||||
* through dispatched pure actions. Created exactly once per bus mount.
|
* through dispatched pure actions. Created exactly once per bus mount; the
|
||||||
|
* throttles below live in the returned closure, mirroring the previous
|
||||||
|
* scattered listeners.
|
||||||
*/
|
*/
|
||||||
export const createEventBus = (deps: BusHandlerDeps) => {
|
export const createEventBus = (deps: BusHandlerDeps) => {
|
||||||
let lastProfileId: string | null = null
|
let lastProfileId: string | null = null
|
||||||
let lastProfileChangeTime = 0
|
let lastProfileChangeTime = 0
|
||||||
|
let lastProxyRefreshTime = 0
|
||||||
const refreshThrottle = 800
|
const refreshThrottle = 800
|
||||||
|
|
||||||
const handleProfileChanged = (newProfileId: string) => {
|
const handleProfileChanged = (newProfileId: string) => {
|
||||||
@@ -49,6 +52,13 @@ export const createEventBus = (deps: BusHandlerDeps) => {
|
|||||||
deps.revalidateKeys(['getProfiles'])
|
deps.revalidateKeys(['getProfiles'])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleRefreshProxyConfig = () => {
|
||||||
|
const now = Date.now()
|
||||||
|
if (now - lastProxyRefreshTime <= refreshThrottle) return
|
||||||
|
lastProxyRefreshTime = now
|
||||||
|
deps.revalidateKeys(['getProxyView'])
|
||||||
|
}
|
||||||
|
|
||||||
const handlers: BusHandlers = {
|
const handlers: BusHandlers = {
|
||||||
'verge://refresh-clash-config': () =>
|
'verge://refresh-clash-config': () =>
|
||||||
deps.revalidateKeys([
|
deps.revalidateKeys([
|
||||||
@@ -64,7 +74,7 @@ export const createEventBus = (deps: BusHandlerDeps) => {
|
|||||||
'verge://refresh-verge-config': () =>
|
'verge://refresh-verge-config': () =>
|
||||||
deps.revalidateKeys(['getVergeConfig', 'getSystemProxy', 'getAutoProxy']),
|
deps.revalidateKeys(['getVergeConfig', 'getSystemProxy', 'getAutoProxy']),
|
||||||
'verge://refresh-profiles': () => deps.revalidateKeys(['getProfiles']),
|
'verge://refresh-profiles': () => deps.revalidateKeys(['getProfiles']),
|
||||||
'verge://refresh-proxy-config': () => deps.revalidateKeys(['getProxyView']),
|
'verge://refresh-proxy-config': handleRefreshProxyConfig,
|
||||||
'verge://notice-message': (payload) => deps.handleNotice(payload),
|
'verge://notice-message': (payload) => deps.handleNotice(payload),
|
||||||
'profile-changed': handleProfileChanged,
|
'profile-changed': handleProfileChanged,
|
||||||
'verge://timer-updated': (uid) =>
|
'verge://timer-updated': (uid) =>
|
||||||
|
|||||||
@@ -152,17 +152,17 @@ export async function patchClashMode(payload: string) {
|
|||||||
return commands.patchClashMode(payload)
|
return commands.patchClashMode(payload)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function syncTrayProxySelection() {
|
||||||
|
return commands.syncTrayProxySelection()
|
||||||
|
}
|
||||||
|
|
||||||
/** Sends one selection pair so the backend can merge against current profile state. */
|
/** Sends one selection pair so the backend can merge against current profile state. */
|
||||||
export async function recordSelectedNode(groupName: string, node: string) {
|
export async function recordSelectedNode(groupName: string, node: string) {
|
||||||
return commands.recordSelectedNode(groupName, node)
|
return commands.recordSelectedNode(groupName, node)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function switchProxyNode(
|
export async function forgetSelectedNode(groupName: string) {
|
||||||
groupName: string,
|
return commands.forgetSelectedNode(groupName)
|
||||||
proxyName: string,
|
|
||||||
unfix: boolean,
|
|
||||||
) {
|
|
||||||
return commands.switchProxyNode(groupName, proxyName, unfix)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getProxyView(): Promise<ProxyViewV1> {
|
export async function getProxyView(): Promise<ProxyViewV1> {
|
||||||
|
|||||||
Reference in New Issue
Block a user