From cdddb44099e363c12c3622200e98bf255950ef52 Mon Sep 17 00:00:00 2001 From: Tommy Verrall Date: Tue, 1 Apr 2025 17:06:21 +0200 Subject: [PATCH 01/48] migrate to v2 - lots to check and do --- nym-wallet/Cargo.lock | 2748 +++++++++---- nym-wallet/package.json | 10 +- nym-wallet/src-tauri/Cargo.toml | 8 +- .../src-tauri/capabilities/migrated.json | 11 + .../src-tauri/gen/schemas/capabilities.json | 1 + .../src-tauri/gen/schemas/desktop-schema.json | 2279 ++++++++++ .../src-tauri/gen/schemas/macOS-schema.json | 2279 ++++++++++ nym-wallet/src-tauri/src/config/mod.rs | 288 +- nym-wallet/src-tauri/src/log.rs | 4 +- nym-wallet/src-tauri/src/main.rs | 34 +- nym-wallet/src-tauri/src/menu.rs | 31 +- .../src-tauri/src/operations/app/version.rs | 55 +- .../src-tauri/src/operations/app/window.rs | 8 +- .../src-tauri/src/operations/help/log.rs | 12 +- .../src-tauri/src/wallet_storage/mod.rs | 3654 +++++++++-------- nym-wallet/src-tauri/tauri.conf.json | 113 +- nym-wallet/src/components/CopyToClipboard.tsx | 4 +- .../src/components/Settings/AppVersion.tsx | 4 +- .../src/pages/internal-docs/DocEntry.tsx | 2 +- nym-wallet/src/requests/wrapper.ts | 2 +- nym-wallet/src/utils/common.ts | 3 +- yarn.lock | 2041 +-------- 22 files changed, 8942 insertions(+), 4649 deletions(-) create mode 100644 nym-wallet/src-tauri/capabilities/migrated.json create mode 100644 nym-wallet/src-tauri/gen/schemas/capabilities.json create mode 100644 nym-wallet/src-tauri/gen/schemas/desktop-schema.json create mode 100644 nym-wallet/src-tauri/gen/schemas/macOS-schema.json diff --git a/nym-wallet/Cargo.lock b/nym-wallet/Cargo.lock index d5f95eb219..e451c0c1e8 100644 --- a/nym-wallet/Cargo.lock +++ b/nym-wallet/Cargo.lock @@ -100,6 +100,21 @@ version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + [[package]] name = "anstream" version = "0.6.4" @@ -154,6 +169,34 @@ version = "1.0.72" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b13c32d80ecc7ab747b80c3784bce54ee8a7a0cc4fbda9bf4cda2cf6fe90854" +[[package]] +name = "arbitrary" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223" +dependencies = [ + "derive_arbitrary", +] + +[[package]] +name = "arboard" +version = "3.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df099ccb16cd014ff054ac1bf392c67feeef57164b05c42f037cd40f5d4357f4" +dependencies = [ + "clipboard-win", + "core-graphics 0.23.1", + "image", + "log", + "objc2 0.5.2", + "objc2-app-kit 0.2.2", + "objc2-foundation 0.2.2", + "parking_lot", + "windows-sys 0.48.0", + "wl-clipboard-rs", + "x11rb", +] + [[package]] name = "argon2" version = "0.5.1" @@ -313,26 +356,25 @@ dependencies = [ [[package]] name = "atk" -version = "0.15.1" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c3d816ce6f0e2909a96830d6911c2aff044370b1ef92d7f267b43bae5addedd" +checksum = "241b621213072e993be4f6f3a9e4b45f65b7e6faad43001be957184b7bb1824b" dependencies = [ "atk-sys", - "bitflags 1.3.2", "glib", "libc", ] [[package]] name = "atk-sys" -version = "0.15.1" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58aeb089fb698e06db8089971c7ee317ab9644bade33383f63631437b03aafb6" +checksum = "c5e48b684b0ca77d2bbadeef17424c2ea3c897d44d566a1617e7e8f30614d086" dependencies = [ "glib-sys", "gobject-sys", "libc", - "system-deps 6.1.1", + "system-deps", ] [[package]] @@ -341,22 +383,6 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" -[[package]] -name = "attohttpc" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fcf00bc6d5abb29b5f97e3c61a90b6d3caa12f3faf897d4a3e3607c050a35a7" -dependencies = [ - "flate2", - "http 0.2.9", - "log", - "native-tls", - "serde", - "serde_json", - "serde_urlencoded", - "url", -] - [[package]] name = "atty" version = "0.2.14" @@ -479,9 +505,12 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.4.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" +checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" +dependencies = [ + "serde", +] [[package]] name = "blake2" @@ -492,12 +521,6 @@ dependencies = [ "digest 0.10.7", ] -[[package]] -name = "block" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" - [[package]] name = "block-buffer" version = "0.9.0" @@ -516,6 +539,24 @@ dependencies = [ "generic-array", ] +[[package]] +name = "block2" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c132eebf10f5cad5289222520a4a058514204aed6d791f1cf4fe8088b82d15f" +dependencies = [ + "objc2 0.5.2", +] + +[[package]] +name = "block2" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d59b4c170e16f0405a2e95aff44432a0d41aa97675f3d52623effe95792a037" +dependencies = [ + "objc2 0.6.0", +] + [[package]] name = "bls12_381" version = "0.8.0" @@ -546,7 +587,18 @@ checksum = "a1a0b1dbcc8ae29329621f8d4f0d835787c1c38bb1401979b49d13b0b305ff68" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", - "brotli-decompressor", + "brotli-decompressor 2.3.4", +] + +[[package]] +name = "brotli" +version = "7.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc97b8f16f944bba54f0433f07e30be199b6dc2bd25937444bbad560bcea29bd" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", + "brotli-decompressor 4.0.2", ] [[package]] @@ -559,6 +611,16 @@ dependencies = [ "alloc-stdlib", ] +[[package]] +name = "brotli-decompressor" +version = "4.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74fa05ad7d803d413eb8380983b092cbbaf9a85f151b871360e7b00cd7060b37" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", +] + [[package]] name = "bs58" version = "0.5.1" @@ -569,16 +631,6 @@ dependencies = [ "tinyvec", ] -[[package]] -name = "bstr" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6798148dccfbff0fae41c7574d2fa8f1ef3492fba0face179de5d8d447d67b05" -dependencies = [ - "memchr", - "serde", -] - [[package]] name = "bumpalo" version = "3.13.0" @@ -597,6 +649,12 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" +[[package]] +name = "byteorder-lite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" + [[package]] name = "bytes" version = "1.10.1" @@ -608,26 +666,27 @@ dependencies = [ [[package]] name = "cairo-rs" -version = "0.15.12" +version = "0.18.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c76ee391b03d35510d9fa917357c7f1855bd9a6659c95a1b392e33f49b3369bc" +checksum = "8ca26ef0159422fb77631dc9d17b102f253b876fe1586b03b803e63a309b4ee2" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.9.0", "cairo-sys-rs", "glib", "libc", + "once_cell", "thiserror 1.0.64", ] [[package]] name = "cairo-sys-rs" -version = "0.15.1" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c55d429bef56ac9172d25fecb85dc8068307d17acd74b377866b7a1ef25d3c8" +checksum = "685c9fa8e590b8b3d678873528d83411db17242a73fccaed827770ea0fedda51" dependencies = [ "glib-sys", "libc", - "system-deps 6.1.1", + "system-deps", ] [[package]] @@ -663,13 +722,27 @@ dependencies = [ ] [[package]] -name = "cargo_toml" -version = "0.13.3" +name = "cargo_metadata" +version = "0.19.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497049e9477329f8f6a559972ee42e117487d01d1e8c2cc9f836ea6fa23a9e1a" +checksum = "dd5eb614ed4c27c5d706420e4320fbe3216ab31fa1c33cd8246ac36dae4479ba" +dependencies = [ + "camino", + "cargo-platform", + "semver", + "serde", + "serde_json", + "thiserror 2.0.11", +] + +[[package]] +name = "cargo_toml" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02260d489095346e5cafd04dea8e8cb54d1d74fcd759022a9b72986ebe9a1257" dependencies = [ "serde", - "toml 0.5.11", + "toml 0.8.20", ] [[package]] @@ -708,12 +781,14 @@ dependencies = [ ] [[package]] -name = "cfg-expr" -version = "0.9.1" +name = "cfb" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3431df59f28accaf4cb4eed4a9acc66bea3f3c3753aa6cdc2f024174ef232af7" +checksum = "d38f2da7a0a2c4ccf0065be06397cc26a81f4e528be095826eee9d4adbb8c60f" dependencies = [ - "smallvec", + "byteorder", + "fnv", + "uuid 1.10.0", ] [[package]] @@ -732,6 +807,31 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "cfg_aliases" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" + +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + +[[package]] +name = "chrono" +version = "0.4.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a7964611d71df112cb1730f2ee67324fcf4d0fc6606acbbe9bfe06df124637c" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "num-traits", + "serde", + "windows-link", +] + [[package]] name = "cipher" version = "0.4.4" @@ -761,7 +861,7 @@ dependencies = [ "anstream", "anstyle", "clap_lex", - "strsim", + "strsim 0.10.0", ] [[package]] @@ -783,42 +883,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" [[package]] -name = "cocoa" -version = "0.24.1" +name = "clipboard-win" +version = "5.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f425db7937052c684daec3bd6375c8abe2d146dca4b8b143d6db777c39138f3a" +checksum = "15efe7a882b08f34e38556b14f2fb3daa98769d06c7f0c1b076dfd0d983bc892" dependencies = [ - "bitflags 1.3.2", - "block", - "cocoa-foundation", - "core-foundation", - "core-graphics", - "foreign-types", - "libc", - "objc", + "error-code", ] -[[package]] -name = "cocoa-foundation" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "931d3837c286f56e3c58423ce4eba12d08db2374461a785c86f672b08b5650d6" -dependencies = [ - "bitflags 1.3.2", - "block", - "core-foundation", - "core-graphics-types", - "foreign-types", - "libc", - "objc", -] - -[[package]] -name = "color_quant" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" - [[package]] name = "colorchoice" version = "1.0.0" @@ -843,7 +915,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c" dependencies = [ "lazy_static", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -874,6 +946,16 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" +[[package]] +name = "cookie" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747" +dependencies = [ + "time", + "version_check", +] + [[package]] name = "core-foundation" version = "0.9.3" @@ -885,21 +967,44 @@ dependencies = [ ] [[package]] -name = "core-foundation-sys" -version = "0.8.4" +name = "core-foundation" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" +checksum = "b55271e5c8c478ad3f38ad24ef34923091e0548492a266d19b3c0b4d82574c63" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "core-graphics" -version = "0.22.3" +version = "0.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2581bbab3b8ffc6fcbd550bf46c355135d16e9ff2a6ea032ad6b9bf1d7efe4fb" +checksum = "970a29baf4110c26fedbc7f82107d42c23f7e88e404c4577ed73fe99ff85a212" dependencies = [ "bitflags 1.3.2", - "core-foundation", - "core-graphics-types", - "foreign-types", + "core-foundation 0.9.3", + "core-graphics-types 0.1.2", + "foreign-types 0.5.0", + "libc", +] + +[[package]] +name = "core-graphics" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa95a34622365fa5bbf40b20b75dba8dfa8c94c734aea8ac9a5ca38af14316f1" +dependencies = [ + "bitflags 2.9.0", + "core-foundation 0.10.0", + "core-graphics-types 0.2.0", + "foreign-types 0.5.0", "libc", ] @@ -910,7 +1015,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2bb142d41022986c1d8ff29103a1411c8a3dfad3552f87a4f8dc50d61d4f4e33" dependencies = [ "bitflags 1.3.2", - "core-foundation", + "core-foundation 0.9.3", + "libc", +] + +[[package]] +name = "core-graphics-types" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb" +dependencies = [ + "bitflags 2.9.0", + "core-foundation 0.10.0", "libc", ] @@ -1045,9 +1161,9 @@ dependencies = [ [[package]] name = "crc32fast" -version = "1.3.2" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" dependencies = [ "cfg-if", ] @@ -1147,6 +1263,16 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "ctor" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a2785755761f3ddc1492979ce1e48d2c00d09311c39e4466429188f3dd6501" +dependencies = [ + "quote", + "syn 2.0.96", +] + [[package]] name = "ctr" version = "0.9.2" @@ -1298,8 +1424,18 @@ version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c" dependencies = [ - "darling_core", - "darling_macro", + "darling_core 0.13.4", + "darling_macro 0.13.4", +] + +[[package]] +name = "darling" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" +dependencies = [ + "darling_core 0.20.11", + "darling_macro 0.20.11", ] [[package]] @@ -1312,21 +1448,46 @@ dependencies = [ "ident_case", "proc-macro2", "quote", - "strsim", + "strsim 0.10.0", "syn 1.0.109", ] +[[package]] +name = "darling_core" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim 0.11.1", + "syn 2.0.96", +] + [[package]] name = "darling_macro" version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835" dependencies = [ - "darling_core", + "darling_core 0.13.4", "quote", "syn 1.0.109", ] +[[package]] +name = "darling_macro" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" +dependencies = [ + "darling_core 0.20.11", + "quote", + "syn 2.0.96", +] + [[package]] name = "data-encoding" version = "2.7.0" @@ -1364,6 +1525,28 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "derive-new" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d150dea618e920167e5973d70ae6ece4385b7164e0d799fe7c122dd0a5d912ad" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", +] + +[[package]] +name = "derive_arbitrary" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30542c1ad912e0e3d22a1935c290e12e8a29d704a420177a31faad4a601a0800" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", +] + [[package]] name = "derive_more" version = "0.99.17" @@ -1438,13 +1621,12 @@ dependencies = [ ] [[package]] -name = "dirs-next" -version = "2.0.0" +name = "dirs" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" +checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e" dependencies = [ - "cfg-if", - "dirs-sys-next", + "dirs-sys 0.5.0", ] [[package]] @@ -1454,7 +1636,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" dependencies = [ "libc", - "redox_users", + "redox_users 0.4.3", "winapi", ] @@ -1466,19 +1648,20 @@ checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" dependencies = [ "libc", "option-ext", - "redox_users", + "redox_users 0.4.3", "windows-sys 0.48.0", ] [[package]] -name = "dirs-sys-next" -version = "0.1.2" +name = "dirs-sys" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" +checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" dependencies = [ "libc", - "redox_users", - "winapi", + "option-ext", + "redox_users 0.5.0", + "windows-sys 0.59.0", ] [[package]] @@ -1498,12 +1681,50 @@ dependencies = [ "syn 2.0.96", ] +[[package]] +name = "dlopen2" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1297103d2bbaea85724fcee6294c2d50b1081f9ad47d0f6f6f61eda65315a6" +dependencies = [ + "dlopen2_derive", + "libc", + "once_cell", + "winapi", +] + +[[package]] +name = "dlopen2_derive" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2b99bf03862d7f545ebc28ddd33a665b50865f4dfd84031a393823879bd4c54" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", +] + [[package]] name = "dotenvy" version = "0.15.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" +[[package]] +name = "downcast-rs" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" + +[[package]] +name = "dpi" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f25c0e292a7ca6d6498557ff1df68f32c99850012b6ea401cf8daf771f22ff53" +dependencies = [ + "serde", +] + [[package]] name = "dtoa" version = "1.0.9" @@ -1626,6 +1847,20 @@ dependencies = [ "zeroize", ] +[[package]] +name = "embed-resource" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fbc6e0d8e0c03a655b53ca813f0463d2c956bc4db8138dbc89f120b066551e3" +dependencies = [ + "cc", + "memchr", + "rustc_version", + "toml 0.8.20", + "vswhom", + "winreg 0.52.0", +] + [[package]] name = "embed_plist" version = "1.2.2" @@ -1673,26 +1908,31 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] -name = "errno" -version = "0.3.2" +name = "erased-serde" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b30f669a7961ef1631673d2766cc92f52d64f7ef354d4fe0ddfd30ed52f0f4f" +checksum = "e004d887f51fcb9fef17317a2f3525c887d8aa3f4f50fed920816a688284a5b7" dependencies = [ - "errno-dragonfly", - "libc", - "windows-sys 0.48.0", + "serde", + "typeid", ] [[package]] -name = "errno-dragonfly" -version = "0.1.2" +name = "errno" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" dependencies = [ - "cc", "libc", + "windows-sys 0.59.0", ] +[[package]] +name = "error-code" +version = "3.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5d9305ccc6942a704f4335694ecd3de2ea531b114ac2d51f5f843750787a92f" + [[package]] name = "eyre" version = "0.6.12" @@ -1705,15 +1945,15 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.0.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] name = "fdeflate" -version = "0.3.0" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d329bdeac514ee06249dabc27877490f17f5d371ec693360768b838e19f3ae10" +checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c" dependencies = [ "simd-adler32", ] @@ -1756,16 +1996,22 @@ dependencies = [ [[package]] name = "filetime" -version = "0.2.22" +version = "0.2.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4029edd3e734da6fe05b6cd7bd2960760a616bd2ddd0d59a0124746d6272af0" +checksum = "35c0522e981e68cbfa8c3f978441a5f34b30b96e146b33cd3359176b50fe8586" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.3.5", - "windows-sys 0.48.0", + "libredox", + "windows-sys 0.59.0", ] +[[package]] +name = "fixedbitset" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" + [[package]] name = "flate2" version = "1.1.0" @@ -1798,7 +2044,28 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" dependencies = [ - "foreign-types-shared", + "foreign-types-shared 0.1.1", +] + +[[package]] +name = "foreign-types" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" +dependencies = [ + "foreign-types-macros", + "foreign-types-shared 0.3.1", +] + +[[package]] +name = "foreign-types-macros" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", ] [[package]] @@ -1807,6 +2074,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" +[[package]] +name = "foreign-types-shared" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" + [[package]] name = "form_urlencoded" version = "1.2.1" @@ -1926,11 +2199,10 @@ dependencies = [ [[package]] name = "gdk" -version = "0.15.4" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6e05c1f572ab0e1f15be94217f0dc29088c248b14f792a5ff0af0d84bcda9e8" +checksum = "d9f245958c627ac99d8e529166f9823fb3b838d1d41fd2b297af3075093c2691" dependencies = [ - "bitflags 1.3.2", "cairo-rs", "gdk-pixbuf", "gdk-sys", @@ -1942,35 +2214,35 @@ dependencies = [ [[package]] name = "gdk-pixbuf" -version = "0.15.11" +version = "0.18.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad38dd9cc8b099cceecdf41375bb6d481b1b5a7cd5cd603e10a69a9383f8619a" +checksum = "50e1f5f1b0bfb830d6ccc8066d18db35c487b1b2b1e8589b5dfe9f07e8defaec" dependencies = [ - "bitflags 1.3.2", "gdk-pixbuf-sys", "gio", "glib", "libc", + "once_cell", ] [[package]] name = "gdk-pixbuf-sys" -version = "0.15.10" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "140b2f5378256527150350a8346dbdb08fadc13453a7a2d73aecd5fab3c402a7" +checksum = "3f9839ea644ed9c97a34d129ad56d38a25e6756f99f3a88e15cd39c20629caf7" dependencies = [ "gio-sys", "glib-sys", "gobject-sys", "libc", - "system-deps 6.1.1", + "system-deps", ] [[package]] name = "gdk-sys" -version = "0.15.1" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32e7a08c1e8f06f4177fb7e51a777b8c1689f743a7bc11ea91d44d2226073a88" +checksum = "5c2d13f38594ac1e66619e188c6d5a1adb98d11b2fcf7894fc416ad76aa2f3f7" dependencies = [ "cairo-sys-rs", "gdk-pixbuf-sys", @@ -1980,33 +2252,48 @@ dependencies = [ "libc", "pango-sys", "pkg-config", - "system-deps 6.1.1", + "system-deps", ] [[package]] -name = "gdkx11-sys" -version = "0.15.1" +name = "gdkwayland-sys" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4b7f8c7a84b407aa9b143877e267e848ff34106578b64d1e0a24bf550716178" +checksum = "140071d506d223f7572b9f09b5e155afbd77428cd5cc7af8f2694c41d98dfe69" dependencies = [ "gdk-sys", "glib-sys", + "gobject-sys", + "libc", + "pkg-config", + "system-deps", +] + +[[package]] +name = "gdkx11" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3caa00e14351bebbc8183b3c36690327eb77c49abc2268dd4bd36b856db3fbfe" +dependencies = [ + "gdk", + "gdkx11-sys", + "gio", + "glib", "libc", - "system-deps 6.1.1", "x11", ] [[package]] -name = "generator" -version = "0.7.5" +name = "gdkx11-sys" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cc16584ff22b460a382b7feec54b23d2908d858152e5739a120b949293bd74e" +checksum = "6e2e7445fe01ac26f11601db260dd8608fe172514eb63b3b5e261ea6b0f4428d" dependencies = [ - "cc", + "gdk-sys", + "glib-sys", "libc", - "log", - "rustversion", - "windows 0.48.0", + "system-deps", + "x11", ] [[package]] @@ -2020,6 +2307,16 @@ dependencies = [ "zeroize", ] +[[package]] +name = "gethostname" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0176e0459c2e4a1fe232f984bca6890e681076abb9934f6cea7c326f3fc47818" +dependencies = [ + "libc", + "windows-targets 0.48.1", +] + [[package]] name = "getrandom" version = "0.1.16" @@ -2044,6 +2341,18 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "getrandom" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasi 0.14.2+wasi-0.2.4", +] + [[package]] name = "getset" version = "0.1.5" @@ -2074,49 +2383,54 @@ checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" [[package]] name = "gio" -version = "0.15.12" +version = "0.18.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68fdbc90312d462781a395f7a16d96a2b379bb6ef8cd6310a2df272771c4283b" +checksum = "d4fc8f532f87b79cbc51a79748f16a6828fb784be93145a322fa14d06d354c73" dependencies = [ - "bitflags 1.3.2", "futures-channel", "futures-core", "futures-io", + "futures-util", "gio-sys", "glib", "libc", "once_cell", + "pin-project-lite", + "smallvec", "thiserror 1.0.64", ] [[package]] name = "gio-sys" -version = "0.15.10" +version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32157a475271e2c4a023382e9cab31c4584ee30a97da41d3c4e9fdd605abcf8d" +checksum = "37566df850baf5e4cb0dfb78af2e4b9898d817ed9263d1090a2df958c64737d2" dependencies = [ "glib-sys", "gobject-sys", "libc", - "system-deps 6.1.1", + "system-deps", "winapi", ] [[package]] name = "glib" -version = "0.15.12" +version = "0.18.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edb0306fbad0ab5428b0ca674a23893db909a98582969c9b537be4ced78c505d" +checksum = "233daaf6e83ae6a12a52055f568f9d7cf4671dabb78ff9560ab6da230ce00ee5" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.9.0", "futures-channel", "futures-core", "futures-executor", "futures-task", + "futures-util", + "gio-sys", "glib-macros", "glib-sys", "gobject-sys", "libc", + "memchr", "once_cell", "smallvec", "thiserror 1.0.64", @@ -2124,27 +2438,26 @@ dependencies = [ [[package]] name = "glib-macros" -version = "0.15.13" +version = "0.18.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10c6ae9f6fa26f4fb2ac16b528d138d971ead56141de489f8111e259b9df3c4a" +checksum = "0bb0228f477c0900c880fd78c8759b95c7636dbd7842707f49e132378aa2acdc" dependencies = [ - "anyhow", "heck 0.4.1", - "proc-macro-crate", + "proc-macro-crate 2.0.0", "proc-macro-error", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.96", ] [[package]] name = "glib-sys" -version = "0.15.10" +version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef4b192f8e65e9cf76cbf4ea71fa8e3be4a0e18ffe3d68b8da6836974cc5bad4" +checksum = "063ce2eb6a8d0ea93d2bf8ba1957e78dbab6be1c2220dd3daca57d5a9d869898" dependencies = [ "libc", - "system-deps 6.1.1", + "system-deps", ] [[package]] @@ -2153,28 +2466,15 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" -[[package]] -name = "globset" -version = "0.4.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "759c97c1e17c55525b57192c06a267cda0ac5210b222d6b82189a2338fa1c13d" -dependencies = [ - "aho-corasick", - "bstr", - "fnv", - "log", - "regex", -] - [[package]] name = "gobject-sys" -version = "0.15.10" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d57ce44246becd17153bd035ab4d32cfee096a657fc01f2231c9278378d1e0a" +checksum = "0850127b514d1c4a4654ead6dedadb18198999985908e6ffe4436f53c785ce44" dependencies = [ "glib-sys", "libc", - "system-deps 6.1.1", + "system-deps", ] [[package]] @@ -2190,12 +2490,11 @@ dependencies = [ [[package]] name = "gtk" -version = "0.15.5" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92e3004a2d5d6d8b5057d2b57b3712c9529b62e82c77f25c1fecde1fd5c23bd0" +checksum = "fd56fb197bfc42bd5d2751f4f017d44ff59fbb58140c6b49f9b3b2bdab08506a" dependencies = [ "atk", - "bitflags 1.3.2", "cairo-rs", "field-offset", "futures-channel", @@ -2206,16 +2505,15 @@ dependencies = [ "gtk-sys", "gtk3-macros", "libc", - "once_cell", "pango", "pkg-config", ] [[package]] name = "gtk-sys" -version = "0.15.3" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5bc2f0587cba247f60246a0ca11fe25fb733eabc3de12d1965fc07efab87c84" +checksum = "8f29a1c21c59553eb7dd40e918be54dccd60c52b049b75119d5d96ce6b624414" dependencies = [ "atk-sys", "cairo-sys-rs", @@ -2226,21 +2524,20 @@ dependencies = [ "gobject-sys", "libc", "pango-sys", - "system-deps 6.1.1", + "system-deps", ] [[package]] name = "gtk3-macros" -version = "0.15.6" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "684c0456c086e8e7e9af73ec5b84e35938df394712054550e81558d21c44ab0d" +checksum = "52ff3c5b21f14f0736fed6dcfc0bfb4225ebf5725f3c0209edeec181e4d73e9d" dependencies = [ - "anyhow", - "proc-macro-crate", + "proc-macro-crate 1.3.1", "proc-macro-error", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.96", ] [[package]] @@ -2451,7 +2748,21 @@ checksum = "e5c13fb08e5d4dfc151ee5e88bae63f7773d61852f3bdc73c9f4b9e1bde03148" dependencies = [ "log", "mac", - "markup5ever", + "markup5ever 0.10.1", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "html5ever" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bea68cab48b8459f17cf1c944c67ddc572d272d9f2b274140f223ecb1da4a3b7" +dependencies = [ + "log", + "mac", + "markup5ever 0.11.0", "proc-macro2", "quote", "syn 1.0.109", @@ -2513,12 +2824,6 @@ dependencies = [ "pin-project-lite", ] -[[package]] -name = "http-range" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21dec9db110f5f872ed9699c3ecf50cf16f423502706ba5c72462e28d3157573" - [[package]] name = "httparse" version = "1.8.0" @@ -2667,6 +2972,29 @@ dependencies = [ "tracing", ] +[[package]] +name = "iana-time-zone" +version = "0.1.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core 0.52.0", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + [[package]] name = "ico" version = "0.2.0" @@ -2677,6 +3005,16 @@ dependencies = [ "png", ] +[[package]] +name = "ico" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc50b891e4acf8fe0e71ef88ec43ad82ee07b3810ad09de10f1d01f072ed4b98" +dependencies = [ + "byteorder", + "png", +] + [[package]] name = "icu_collections" version = "1.5.0" @@ -2832,34 +3170,17 @@ dependencies = [ "icu_properties", ] -[[package]] -name = "ignore" -version = "0.4.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbe7873dab538a9a44ad79ede1faf5f30d49f9a5c883ddbab48bce81b64b7492" -dependencies = [ - "globset", - "lazy_static", - "log", - "memchr", - "regex", - "same-file", - "thread_local", - "walkdir", - "winapi-util", -] - [[package]] name = "image" -version = "0.24.7" +version = "0.25.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f3dfdbdd72063086ff443e297b61695500514b1e41095b6fb9a5ab48a70a711" +checksum = "db35664ce6b9810857a38a906215e75a9c879f0696556a39f59c62829710251a" dependencies = [ "bytemuck", - "byteorder", - "color_quant", - "num-rational", + "byteorder-lite", "num-traits", + "png", + "tiff", ] [[package]] @@ -2896,7 +3217,16 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "20b2b533137b9cad970793453d4f921c2e91312a6d88b1085c07bc15fc51bb3b" dependencies = [ - "cfb", + "cfb 0.6.1", +] + +[[package]] +name = "infer" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a588916bfdfd92e71cacef98a63d9b1f0d74d6599980d11894290e7ddefffcf7" +dependencies = [ + "cfb 0.7.3", ] [[package]] @@ -2908,15 +3238,6 @@ dependencies = [ "generic-array", ] -[[package]] -name = "instant" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" -dependencies = [ - "cfg-if", -] - [[package]] name = "ipconfig" version = "0.3.2" @@ -2935,6 +3256,15 @@ version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6" +[[package]] +name = "is-docker" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "928bae27f42bc99b60d9ac7334e3a21d10ad8f1835a4e12ec3ec0464765ed1b3" +dependencies = [ + "once_cell", +] + [[package]] name = "is-terminal" version = "0.4.9" @@ -2942,10 +3272,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" dependencies = [ "hermit-abi 0.3.9", - "rustix", + "rustix 0.38.44", "windows-sys 0.48.0", ] +[[package]] +name = "is-wsl" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "173609498df190136aa7dea1a91db051746d339e18476eed5ca40521f02d7aa5" +dependencies = [ + "is-docker", + "once_cell", +] + [[package]] name = "itertools" version = "0.10.5" @@ -2987,9 +3327,9 @@ checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" [[package]] name = "javascriptcore-rs" -version = "0.16.0" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf053e7843f2812ff03ef5afe34bb9c06ffee120385caad4f6b9967fcd37d41c" +checksum = "ca5671e9ffce8ffba57afc24070e906da7fc4b1ba66f2cabebf61bf2ea257fcc" dependencies = [ "bitflags 1.3.2", "glib", @@ -2998,28 +3338,30 @@ dependencies = [ [[package]] name = "javascriptcore-rs-sys" -version = "0.4.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "905fbb87419c5cde6e3269537e4ea7d46431f3008c5d057e915ef3f115e7793c" +checksum = "af1be78d14ffa4b75b66df31840478fef72b51f8c2465d4ca7c194da9f7a5124" dependencies = [ "glib-sys", "gobject-sys", "libc", - "system-deps 5.0.0", + "system-deps", ] [[package]] name = "jni" -version = "0.20.0" +version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "039022cdf4d7b1cf548d31f60ae783138e5fd42013f6271049d7df7afadef96c" +checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" dependencies = [ "cesu8", + "cfg-if", "combine", "jni-sys", "log", "thiserror 1.0.64", "walkdir", + "windows-sys 0.45.0", ] [[package]] @@ -3028,6 +3370,12 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" +[[package]] +name = "jpeg-decoder" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0" + [[package]] name = "js-sys" version = "0.3.64" @@ -3048,6 +3396,28 @@ dependencies = [ "treediff", ] +[[package]] +name = "json-patch" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "863726d7afb6bc2590eeff7135d923545e5e964f004c2ccf8716c25e70a86f08" +dependencies = [ + "jsonptr", + "serde", + "serde_json", + "thiserror 1.0.64", +] + +[[package]] +name = "jsonptr" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dea2b27dd239b2556ed7a25ba842fe47fd602e7fc7433c2a8d6106d4d9edd70" +dependencies = [ + "serde", + "serde_json", +] + [[package]] name = "k256" version = "0.13.4" @@ -3062,6 +3432,17 @@ dependencies = [ "signature", ] +[[package]] +name = "keyboard-types" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b750dcadc39a09dbadd74e118f6dd6598df77fa01df0cfcdc52c28dece74528a" +dependencies = [ + "bitflags 2.9.0", + "serde", + "unicode-segmentation", +] + [[package]] name = "kuchiki" version = "0.8.1" @@ -3069,7 +3450,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ea8e9c6e031377cff82ee3001dc8026cdf431ed4e2e6b51f98ab8c73484a358" dependencies = [ "cssparser", - "html5ever", + "html5ever 0.25.2", + "matches", + "selectors", +] + +[[package]] +name = "kuchikiki" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f29e4755b7b995046f510a7520c42b2fed58b77bd94d5a87a8eb43d2fd126da8" +dependencies = [ + "cssparser", + "html5ever 0.26.0", + "indexmap 1.9.3", "matches", "selectors", ] @@ -3080,12 +3474,57 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +[[package]] +name = "libappindicator" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03589b9607c868cc7ae54c0b2a22c8dc03dd41692d48f2d7df73615c6a95dc0a" +dependencies = [ + "glib", + "gtk", + "gtk-sys", + "libappindicator-sys", + "log", +] + +[[package]] +name = "libappindicator-sys" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e9ec52138abedcc58dc17a7c6c0c00a2bdb4f3427c7f63fa97fd0d859155caf" +dependencies = [ + "gtk-sys", + "libloading", + "once_cell", +] + [[package]] name = "libc" version = "0.2.169" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" +[[package]] +name = "libloading" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" +dependencies = [ + "cfg-if", + "winapi", +] + +[[package]] +name = "libredox" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +dependencies = [ + "bitflags 2.9.0", + "libc", + "redox_syscall 0.5.10", +] + [[package]] name = "line-wrap" version = "0.1.1" @@ -3103,9 +3542,15 @@ checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" [[package]] name = "linux-raw-sys" -version = "0.4.5" +version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57bcfdad1b858c2db7c38303a6d2ad4dfaf5eb53dfeb0910128b2c26d6158503" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" + +[[package]] +name = "linux-raw-sys" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe7db12097d22ec582439daf8618b8fdd1a7bef6270e9af3b1ebcd30893cf413" [[package]] name = "litemap" @@ -3132,21 +3577,6 @@ dependencies = [ "serde", ] -[[package]] -name = "loom" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff50ecb28bb86013e935fb6683ab1f6d3a20016f123c76fd4c27470076ac30f5" -dependencies = [ - "cfg-if", - "generator", - "scoped-tls", - "serde", - "serde_json", - "tracing", - "tracing-subscriber", -] - [[package]] name = "lru-cache" version = "0.1.2" @@ -3162,15 +3592,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" -[[package]] -name = "malloc_buf" -version = "0.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" -dependencies = [ - "libc", -] - [[package]] name = "markup5ever" version = "0.10.1" @@ -3179,7 +3600,21 @@ checksum = "a24f40fb03852d1cdd84330cddcaf98e9ec08a7b7768e952fad3b4cf048ec8fd" dependencies = [ "log", "phf 0.8.0", - "phf_codegen", + "phf_codegen 0.8.0", + "string_cache", + "string_cache_codegen", + "tendril", +] + +[[package]] +name = "markup5ever" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2629bb1404f3d34c2e921f21fd34ba00b206124c81f65c50b43b6aaefeb016" +dependencies = [ + "log", + "phf 0.10.1", + "phf_codegen 0.10.0", "string_cache", "string_cache_codegen", "tendril", @@ -3191,15 +3626,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" -[[package]] -name = "matchers" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" -dependencies = [ - "regex-automata 0.1.10", -] - [[package]] name = "matches" version = "0.1.10" @@ -3228,10 +3654,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] -name = "minisign-verify" +name = "minimal-lexical" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "933dca44d65cdd53b355d0b73d380a2ff5da71f87f036053188bf1eab6a19881" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "minisign-verify" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6367d84fb54d4242af283086402907277715b8fe46976963af5ebf173f8efba3" [[package]] name = "miniz_oxide" @@ -3240,7 +3672,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" dependencies = [ "adler", - "simd-adler32", ] [[package]] @@ -3250,6 +3681,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e3e04debbb59698c15bacbb6d93584a8c0ca9cc3213cb423d31f760d8843ce5" dependencies = [ "adler2", + "simd-adler32", ] [[package]] @@ -3264,6 +3696,27 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "muda" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4de14a9b5d569ca68d7c891d613b390cf5ab4f851c77aaa2f9e435555d3d9492" +dependencies = [ + "crossbeam-channel", + "dpi", + "gtk", + "keyboard-types", + "objc2 0.6.0", + "objc2-app-kit 0.3.0", + "objc2-core-foundation", + "objc2-foundation 0.3.0", + "once_cell", + "png", + "serde", + "thiserror 2.0.11", + "windows-sys 0.59.0", +] + [[package]] name = "native-tls" version = "0.2.11" @@ -3284,14 +3737,16 @@ dependencies = [ [[package]] name = "ndk" -version = "0.6.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2032c77e030ddee34a6787a64166008da93f6a352b629261d0fee232b8742dd4" +checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.9.0", "jni-sys", + "log", "ndk-sys", "num_enum", + "raw-window-handle", "thiserror 1.0.64", ] @@ -3303,9 +3758,9 @@ checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" [[package]] name = "ndk-sys" -version = "0.3.0" +version = "0.6.0+11769913" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e5a6ae77c8ee183dcbbba6150e2e6b9f3f4196a7666c02a715a95692ec1fa97" +checksum = "ee6cda3051665f1fb8d9e08fc35c96d5a244fb1be711a03b71118828afc9a873" dependencies = [ "jni-sys", ] @@ -3316,6 +3771,18 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54" +[[package]] +name = "nix" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4" +dependencies = [ + "bitflags 2.9.0", + "cfg-if", + "cfg_aliases 0.1.1", + "libc", +] + [[package]] name = "nodrop" version = "0.1.14" @@ -3323,13 +3790,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" [[package]] -name = "nu-ansi-term" -version = "0.46.0" +name = "nom" +version = "7.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" dependencies = [ - "overload", - "winapi", + "memchr", + "minimal-lexical", ] [[package]] @@ -3357,17 +3824,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "num-rational" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - [[package]] name = "num-traits" version = "0.2.19" @@ -3379,23 +3835,23 @@ dependencies = [ [[package]] name = "num_enum" -version = "0.5.11" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" +checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179" dependencies = [ "num_enum_derive", ] [[package]] name = "num_enum_derive" -version = "0.5.11" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" +checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" dependencies = [ - "proc-macro-crate", + "proc-macro-crate 2.0.0", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.96", ] [[package]] @@ -3653,7 +4109,7 @@ dependencies = [ name = "nym-network-defaults" version = "0.1.0" dependencies = [ - "cargo_metadata", + "cargo_metadata 0.18.1", "dotenvy", "log", "regex", @@ -3916,8 +4372,12 @@ dependencies = [ "tap", "tauri", "tauri-build", - "tauri-codegen", - "tauri-macros", + "tauri-codegen 1.2.1", + "tauri-macros 1.2.1", + "tauri-plugin-clipboard-manager", + "tauri-plugin-process", + "tauri-plugin-shell", + "tauri-plugin-updater", "tempfile", "thiserror 1.0.64", "time", @@ -3929,42 +4389,267 @@ dependencies = [ ] [[package]] -name = "objc" -version = "0.2.7" +name = "objc-sys" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" +checksum = "cdb91bdd390c7ce1a8607f35f3ca7151b65afc0ff5ff3b34fa350f7d7c7e4310" + +[[package]] +name = "objc2" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46a785d4eeff09c14c487497c162e92766fbb3e4059a71840cecc03d9a50b804" dependencies = [ - "malloc_buf", - "objc_exception", + "objc-sys", + "objc2-encode", ] [[package]] -name = "objc-foundation" +name = "objc2" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3531f65190d9cff863b77a99857e74c314dd16bf56c538c4b57c7cbc3f3a6e59" +dependencies = [ + "objc2-encode", + "objc2-exception-helper", +] + +[[package]] +name = "objc2-app-kit" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff" +dependencies = [ + "bitflags 2.9.0", + "block2 0.5.1", + "libc", + "objc2 0.5.2", + "objc2-core-data 0.2.2", + "objc2-core-image 0.2.2", + "objc2-foundation 0.2.2", + "objc2-quartz-core 0.2.2", +] + +[[package]] +name = "objc2-app-kit" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5906f93257178e2f7ae069efb89fbd6ee94f0592740b5f8a1512ca498814d0fb" +dependencies = [ + "bitflags 2.9.0", + "block2 0.6.0", + "libc", + "objc2 0.6.0", + "objc2-cloud-kit", + "objc2-core-data 0.3.0", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-core-image 0.3.0", + "objc2-foundation 0.3.0", + "objc2-quartz-core 0.3.0", +] + +[[package]] +name = "objc2-cloud-kit" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c1948a9be5f469deadbd6bcb86ad7ff9e47b4f632380139722f7d9840c0d42c" +dependencies = [ + "bitflags 2.9.0", + "objc2 0.6.0", + "objc2-foundation 0.3.0", +] + +[[package]] +name = "objc2-core-data" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef" +dependencies = [ + "bitflags 2.9.0", + "block2 0.5.1", + "objc2 0.5.2", + "objc2-foundation 0.2.2", +] + +[[package]] +name = "objc2-core-data" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f860f8e841f6d32f754836f51e6bc7777cd7e7053cf18528233f6811d3eceb4" +dependencies = [ + "bitflags 2.9.0", + "objc2 0.6.0", + "objc2-foundation 0.3.0", +] + +[[package]] +name = "objc2-core-foundation" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daeaf60f25471d26948a1c2f840e3f7d86f4109e3af4e8e4b5cd70c39690d925" +dependencies = [ + "bitflags 2.9.0", + "objc2 0.6.0", +] + +[[package]] +name = "objc2-core-graphics" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dca602628b65356b6513290a21a6405b4d4027b8b250f0b98dddbb28b7de02" +dependencies = [ + "bitflags 2.9.0", + "objc2 0.6.0", + "objc2-core-foundation", + "objc2-io-surface", +] + +[[package]] +name = "objc2-core-image" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55260963a527c99f1819c4f8e3b47fe04f9650694ef348ffd2227e8196d34c80" +dependencies = [ + "block2 0.5.1", + "objc2 0.5.2", + "objc2-foundation 0.2.2", + "objc2-metal", +] + +[[package]] +name = "objc2-core-image" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ffa6bea72bf42c78b0b34e89c0bafac877d5f80bf91e159a5d96ea7f693ca56" +dependencies = [ + "objc2 0.6.0", + "objc2-foundation 0.3.0", +] + +[[package]] +name = "objc2-encode" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" + +[[package]] +name = "objc2-exception-helper" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1add1b659e36c9607c7aab864a76c7a4c2760cd0cd2e120f3fb8b952c7e22bf9" -dependencies = [ - "block", - "objc", - "objc_id", -] - -[[package]] -name = "objc_exception" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad970fb455818ad6cba4c122ad012fae53ae8b4795f86378bce65e4f6bab2ca4" +checksum = "c7a1c5fbb72d7735b076bb47b578523aedc40f3c439bea6dfd595c089d79d98a" dependencies = [ "cc", ] [[package]] -name = "objc_id" -version = "0.1.1" +name = "objc2-foundation" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c92d4ddb4bd7b50d730c215ff871754d0da6b2178849f8a2a2ab69712d0c073b" +checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" dependencies = [ - "objc", + "bitflags 2.9.0", + "block2 0.5.1", + "libc", + "objc2 0.5.2", +] + +[[package]] +name = "objc2-foundation" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a21c6c9014b82c39515db5b396f91645182611c97d24637cf56ac01e5f8d998" +dependencies = [ + "bitflags 2.9.0", + "block2 0.6.0", + "libc", + "objc2 0.6.0", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-io-surface" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "161a8b87e32610086e1a7a9e9ec39f84459db7b3a0881c1f16ca5a2605581c19" +dependencies = [ + "bitflags 2.9.0", + "objc2 0.6.0", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-metal" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" +dependencies = [ + "bitflags 2.9.0", + "block2 0.5.1", + "objc2 0.5.2", + "objc2-foundation 0.2.2", +] + +[[package]] +name = "objc2-osa-kit" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1ac59da3ceebc4a82179b35dc550431ad9458f9cc326e053f49ba371ce76c5a" +dependencies = [ + "bitflags 2.9.0", + "objc2 0.6.0", + "objc2-app-kit 0.3.0", + "objc2-foundation 0.3.0", +] + +[[package]] +name = "objc2-quartz-core" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a" +dependencies = [ + "bitflags 2.9.0", + "block2 0.5.1", + "objc2 0.5.2", + "objc2-foundation 0.2.2", + "objc2-metal", +] + +[[package]] +name = "objc2-quartz-core" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fb3794501bb1bee12f08dcad8c61f2a5875791ad1c6f47faa71a0f033f20071" +dependencies = [ + "bitflags 2.9.0", + "objc2 0.6.0", + "objc2-foundation 0.3.0", +] + +[[package]] +name = "objc2-ui-kit" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "777a571be14a42a3990d4ebedaeb8b54cd17377ec21b92e8200ac03797b3bee1" +dependencies = [ + "bitflags 2.9.0", + "objc2 0.6.0", + "objc2-core-foundation", + "objc2-foundation 0.3.0", +] + +[[package]] +name = "objc2-web-kit" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b717127e4014b0f9f3e8bba3d3f2acec81f1bde01f656823036e823ed2c94dce" +dependencies = [ + "bitflags 2.9.0", + "block2 0.6.0", + "objc2 0.6.0", + "objc2-app-kit 0.3.0", + "objc2-core-foundation", + "objc2-foundation 0.3.0", ] [[package]] @@ -3990,12 +4675,14 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "open" -version = "3.2.0" +version = "5.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2078c0039e6a54a0c42c28faa984e115fb4c2d5bf2208f77d1961002df8576f8" +checksum = "e2483562e62ea94312f3576a7aca397306df7990b8d89033e18766744377ef95" dependencies = [ + "dunce", + "is-wsl", + "libc", "pathdiff", - "windows-sys 0.42.0", ] [[package]] @@ -4004,9 +4691,9 @@ version = "0.10.70" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61cfb4e166a8bb8c9b55c500bc2308550148ece889be90f609377e58140f42c6" dependencies = [ - "bitflags 2.4.0", + "bitflags 2.9.0", "cfg-if", - "foreign-types", + "foreign-types 0.3.2", "libc", "once_cell", "openssl-macros", @@ -4049,10 +4736,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" [[package]] -name = "overload" -version = "0.1.1" +name = "os_pipe" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" +checksum = "5ffd2b0a5634335b135d5728d84c5e0fd726954b87111f7506a61c502280d982" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + +[[package]] +name = "osakit" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "732c71caeaa72c065bb69d7ea08717bd3f4863a4f451402fc9513e29dbd5261b" +dependencies = [ + "objc2 0.6.0", + "objc2-foundation 0.3.0", + "objc2-osa-kit", + "serde", + "serde_json", + "thiserror 2.0.11", +] [[package]] name = "p256" @@ -4077,11 +4782,11 @@ dependencies = [ [[package]] name = "pango" -version = "0.15.10" +version = "0.18.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22e4045548659aee5313bde6c582b0d83a627b7904dd20dc2d9ef0895d414e4f" +checksum = "7ca27ec1eb0457ab26f3036ea52229edbdb74dee1edd29063f5b9b010e7ebee4" dependencies = [ - "bitflags 1.3.2", + "gio", "glib", "libc", "once_cell", @@ -4090,14 +4795,14 @@ dependencies = [ [[package]] name = "pango-sys" -version = "0.15.10" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2a00081cde4661982ed91d80ef437c20eacaf6aa1a5962c0279ae194662c3aa" +checksum = "436737e391a843e5933d6d9aa102cb126d501e815b83601365a948a518555dc5" dependencies = [ "glib-sys", "gobject-sys", "libc", - "system-deps 6.1.1", + "system-deps", ] [[package]] @@ -4142,9 +4847,9 @@ checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" [[package]] name = "pathdiff" -version = "0.2.1" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" +checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3" [[package]] name = "peg" @@ -4234,6 +4939,16 @@ dependencies = [ "sha2 0.10.8", ] +[[package]] +name = "petgraph" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" +dependencies = [ + "fixedbitset", + "indexmap 2.5.0", +] + [[package]] name = "phf" version = "0.8.0" @@ -4276,6 +4991,16 @@ dependencies = [ "phf_shared 0.8.0", ] +[[package]] +name = "phf_codegen" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb1c3a8bc4dd4e5cfce29b44ffc14bedd2ee294559a294e2a4d4c9e9a6a13cd" +dependencies = [ + "phf_generator 0.10.0", + "phf_shared 0.10.0", +] + [[package]] name = "phf_generator" version = "0.8.0" @@ -4431,22 +5156,22 @@ dependencies = [ "base64 0.21.4", "indexmap 1.9.3", "line-wrap", - "quick-xml", + "quick-xml 0.29.0", "serde", "time", ] [[package]] name = "png" -version = "0.17.9" +version = "0.17.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59871cc5b6cce7eaccca5a802b4173377a1c2ba90654246789a8fa2334426d11" +checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526" dependencies = [ "bitflags 1.3.2", "crc32fast", "fdeflate", "flate2", - "miniz_oxide 0.7.1", + "miniz_oxide 0.8.5", ] [[package]] @@ -4508,6 +5233,15 @@ dependencies = [ "toml_edit 0.19.14", ] +[[package]] +name = "proc-macro-crate" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e8366a6159044a37876a2b9817124296703c586a5c92e2c53751fa06d8d43e8" +dependencies = [ + "toml_edit 0.20.7", +] + [[package]] name = "proc-macro-error" version = "1.0.4" @@ -4613,6 +5347,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "quick-xml" +version = "0.37.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4ce8c88de324ff838700f36fb6ab86c96df0e3c4ab6ef3a9b2044465cce1369" +dependencies = [ + "memchr", +] + [[package]] name = "quote" version = "1.0.35" @@ -4622,6 +5365,12 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "r-efi" +version = "5.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" + [[package]] name = "rand" version = "0.7.3" @@ -4705,9 +5454,9 @@ dependencies = [ [[package]] name = "raw-window-handle" -version = "0.5.2" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9" +checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" [[package]] name = "rayon" @@ -4747,6 +5496,15 @@ dependencies = [ "bitflags 1.3.2", ] +[[package]] +name = "redox_syscall" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b8c0c260b63a8219631167be35e6a988e9554dbd323f8bd08439c8ed1302bd1" +dependencies = [ + "bitflags 2.9.0", +] + [[package]] name = "redox_users" version = "0.4.3" @@ -4758,6 +5516,17 @@ dependencies = [ "thiserror 1.0.64", ] +[[package]] +name = "redox_users" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd6f9d3d47bdd2ad6945c5015a226ec6155d0bcdfd8f7cd29f86b71f8de99d2b" +dependencies = [ + "getrandom 0.2.10", + "libredox", + "thiserror 2.0.11", +] + [[package]] name = "regex" version = "1.11.1" @@ -4766,17 +5535,8 @@ checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.9", - "regex-syntax 0.8.5", -] - -[[package]] -name = "regex-automata" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" -dependencies = [ - "regex-syntax 0.6.29", + "regex-automata", + "regex-syntax", ] [[package]] @@ -4787,15 +5547,9 @@ checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.8.5", + "regex-syntax", ] -[[package]] -name = "regex-syntax" -version = "0.6.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" - [[package]] name = "regex-syntax" version = "0.8.5" @@ -4886,6 +5640,7 @@ dependencies = [ "url", "wasm-bindgen", "wasm-bindgen-futures", + "wasm-streams", "web-sys", "webpki-roots 0.26.1", "winreg 0.52.0", @@ -4911,30 +5666,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "rfd" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0149778bd99b6959285b0933288206090c50e2327f47a9c463bfdbf45c8823ea" -dependencies = [ - "block", - "dispatch", - "glib-sys", - "gobject-sys", - "gtk-sys", - "js-sys", - "lazy_static", - "log", - "objc", - "objc-foundation", - "objc_id", - "raw-window-handle", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "windows 0.37.0", -] - [[package]] name = "ring" version = "0.17.13" @@ -5006,15 +5737,28 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.8" +version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19ed4fa021d81c8392ce04db050a3da9a60299050b7ae1cf482d862b54a7218f" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags 2.4.0", + "bitflags 2.9.0", "errno", "libc", - "linux-raw-sys", - "windows-sys 0.48.0", + "linux-raw-sys 0.4.15", + "windows-sys 0.59.0", +] + +[[package]] +name = "rustix" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a04a32fb43fcdef85b977ce7f77a150805e4b2ea1f2656898d4a547dde78df6" +dependencies = [ + "bitflags 2.9.0", + "errno", + "libc", + "linux-raw-sys 0.9.3", + "windows-sys 0.59.0", ] [[package]] @@ -5148,6 +5892,8 @@ dependencies = [ "schemars_derive", "serde", "serde_json", + "url", + "uuid 1.10.0", ] [[package]] @@ -5162,12 +5908,6 @@ dependencies = [ "syn 2.0.96", ] -[[package]] -name = "scoped-tls" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" - [[package]] name = "scopeguard" version = "1.2.0" @@ -5224,7 +5964,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" dependencies = [ "bitflags 1.3.2", - "core-foundation", + "core-foundation 0.9.3", "core-foundation-sys", "libc", "security-framework-sys", @@ -5253,7 +5993,7 @@ dependencies = [ "log", "matches", "phf 0.8.0", - "phf_codegen", + "phf_codegen 0.8.0", "precomputed-hash", "servo_arc", "smallvec", @@ -5287,6 +6027,17 @@ dependencies = [ "serde", ] +[[package]] +name = "serde-untagged" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "299d9c19d7d466db4ab10addd5703e4c615dec2a5a16dbbafe191045e87ee66e" +dependencies = [ + "erased-serde", + "serde", + "typeid", +] + [[package]] name = "serde_bytes" version = "0.11.17" @@ -5369,7 +6120,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "678b5a069e50bf00ecd22d0cd8ddf7c236f68581b03db652061ed5eb13a312ff" dependencies = [ "serde", - "serde_with_macros", + "serde_with_macros 1.5.2", +] + +[[package]] +name = "serde_with" +version = "3.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6b6f7f2fcb69f747921f79f3926bd1e203fce4fef62c268dd3abfb6d86029aa" +dependencies = [ + "base64 0.22.1", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.5.0", + "serde", + "serde_derive", + "serde_json", + "serde_with_macros 3.12.0", + "time", ] [[package]] @@ -5378,12 +6147,24 @@ version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e182d6ec6f05393cc0e5ed1bf81ad6db3a8feedf8ee515ecdd369809bcce8082" dependencies = [ - "darling", + "darling 0.13.4", "proc-macro2", "quote", "syn 1.0.109", ] +[[package]] +name = "serde_with_macros" +version = "3.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d00caa5193a3c8362ac2b73be6b9e768aa5a4b2f721d8f4b339600c3cb51f8e" +dependencies = [ + "darling 0.20.11", + "proc-macro2", + "quote", + "syn 2.0.96", +] + [[package]] name = "serdect" version = "0.2.0" @@ -5461,12 +6242,13 @@ dependencies = [ ] [[package]] -name = "sharded-slab" -version = "0.1.4" +name = "shared_child" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31" +checksum = "09fa9338aed9a1df411814a5b2252f7cd206c55ae9bf2fa763f8de84603aa60c" dependencies = [ - "lazy_static", + "libc", + "windows-sys 0.59.0", ] [[package]] @@ -5548,31 +6330,51 @@ dependencies = [ ] [[package]] -name = "soup2" -version = "0.2.1" +name = "softbuffer" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2b4d76501d8ba387cf0fefbe055c3e0a59891d09f0f995ae4e4b16f6b60f3c0" +checksum = "18051cdd562e792cad055119e0cdb2cfc137e44e3987532e0f9659a77931bb08" dependencies = [ - "bitflags 1.3.2", - "gio", - "glib", - "libc", - "once_cell", - "soup2-sys", + "bytemuck", + "cfg_aliases 0.2.1", + "core-graphics 0.24.0", + "foreign-types 0.5.0", + "js-sys", + "log", + "objc2 0.5.2", + "objc2-foundation 0.2.2", + "objc2-quartz-core 0.2.2", + "raw-window-handle", + "redox_syscall 0.5.10", + "wasm-bindgen", + "web-sys", + "windows-sys 0.59.0", ] [[package]] -name = "soup2-sys" -version = "0.2.0" +name = "soup3" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "009ef427103fcb17f802871647a7fa6c60cbb654b4c4e4c0ac60a31c5f6dc9cf" +checksum = "471f924a40f31251afc77450e781cb26d55c0b650842efafc9c6cbd2f7cc4f9f" +dependencies = [ + "futures-channel", + "gio", + "glib", + "libc", + "soup3-sys", +] + +[[package]] +name = "soup3-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ebe8950a680a12f24f15ebe1bf70db7af98ad242d9db43596ad3108aab86c27" dependencies = [ - "bitflags 1.3.2", "gio-sys", "glib-sys", "gobject-sys", "libc", - "system-deps 5.0.0", + "system-deps", ] [[package]] @@ -5591,15 +6393,6 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" -[[package]] -name = "state" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbe866e1e51e8260c9eed836a042a5e7f6726bb2b411dffeaa712e19c388f23b" -dependencies = [ - "loom", -] - [[package]] name = "static_assertions" version = "1.1.0" @@ -5638,6 +6431,12 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + [[package]] name = "strum" version = "0.23.0" @@ -5703,6 +6502,17 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "734676eb262c623cec13c3155096e08d1f8f29adce39ba17948b18dad1e54142" +[[package]] +name = "swift-rs" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4057c98e2e852d51fdcfca832aac7b571f6b351ad159f9eda5db1655f8d0c4d7" +dependencies = [ + "base64 0.21.4", + "serde", + "serde_json", +] + [[package]] name = "syn" version = "1.0.109" @@ -5749,7 +6559,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" dependencies = [ "bitflags 1.3.2", - "core-foundation", + "core-foundation 0.9.3", "system-configuration-sys", ] @@ -5763,56 +6573,35 @@ dependencies = [ "libc", ] -[[package]] -name = "system-deps" -version = "5.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18db855554db7bd0e73e06cf7ba3df39f97812cb11d3f75e71c39bf45171797e" -dependencies = [ - "cfg-expr 0.9.1", - "heck 0.3.3", - "pkg-config", - "toml 0.5.11", - "version-compare 0.0.11", -] - [[package]] name = "system-deps" version = "6.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "30c2de8a4d8f4b823d634affc9cd2a74ec98c53a756f317e529a48046cbf71f3" dependencies = [ - "cfg-expr 0.15.4", + "cfg-expr", "heck 0.4.1", "pkg-config", "toml 0.7.6", - "version-compare 0.1.1", + "version-compare", ] [[package]] name = "tao" -version = "0.15.8" +version = "0.32.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac8e6399427c8494f9849b58694754d7cc741293348a6836b6c8d2c5aa82d8e6" +checksum = "63c8b1020610b9138dd7b1e06cf259ae91aa05c30f3bd0d6b42a03997b92dec1" dependencies = [ - "bitflags 1.3.2", - "cairo-rs", - "cc", - "cocoa", - "core-foundation", - "core-graphics", + "bitflags 2.9.0", + "core-foundation 0.10.0", + "core-graphics 0.24.0", "crossbeam-channel", "dispatch", - "gdk", - "gdk-pixbuf", - "gdk-sys", + "dlopen2", + "dpi", + "gdkwayland-sys", "gdkx11-sys", - "gio", - "glib", - "glib-sys", "gtk", - "image", - "instant", "jni", "lazy_static", "libc", @@ -5820,21 +6609,33 @@ dependencies = [ "ndk", "ndk-context", "ndk-sys", - "objc", + "objc2 0.6.0", + "objc2-app-kit 0.3.0", + "objc2-foundation 0.3.0", "once_cell", "parking_lot", - "paste", - "png", "raw-window-handle", "scopeguard", - "serde", + "tao-macros", "unicode-segmentation", - "uuid 1.10.0", - "windows 0.39.0", - "windows-implement", + "url", + "windows 0.60.0", + "windows-core 0.60.1", + "windows-version", "x11-dl", ] +[[package]] +name = "tao-macros" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4e16beb8b2ac17db28eab8bca40e62dbfbb34c0fcdc6d9826b11b7b5d047dfd" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", +] + [[package]] name = "tap" version = "1.0.1" @@ -5843,9 +6644,9 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "tar" -version = "0.4.40" +version = "0.4.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b16afcea1f22891c49a00c751c7b63b2233284064f11a200fc624137c51e2ddb" +checksum = "1d863878d212c87a19c1a610eb53bb01fe12951c0501cf5a0d65f724914a667a" dependencies = [ "filetime", "libc", @@ -5860,71 +6661,74 @@ checksum = "9d0e916b1148c8e263850e1ebcbd046f333e0683c724876bb0da63ea4373dc8a" [[package]] name = "tauri" -version = "1.2.3" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b48820ee3bb6a5031a83b2b6e11f8630bdc5a2f68cb841ab8ebc7a15a916679" +checksum = "511dd38065a5d3b36c33cdba4362b99a40a5103bebcd4aebb930717e7c8ba292" dependencies = [ "anyhow", - "attohttpc", - "base64 0.13.1", - "cocoa", - "dirs-next", + "bytes", + "dirs 6.0.0", + "dunce", "embed_plist", - "encoding_rs", - "flate2", "futures-util", - "glib", + "getrandom 0.2.10", "glob", "gtk", - "heck 0.4.1", - "http 0.2.9", - "ignore", - "minisign-verify", - "objc", - "once_cell", - "open", + "heck 0.5.0", + "http 1.1.0", + "jni", + "libc", + "log", + "mime", + "muda", + "objc2 0.6.0", + "objc2-app-kit 0.3.0", + "objc2-foundation 0.3.0", "percent-encoding", - "rand 0.8.5", + "plist", "raw-window-handle", - "regex", - "rfd", - "semver", + "reqwest 0.12.4", "serde", "serde_json", "serde_repr", "serialize-to-javascript", - "state", - "tar", - "tauri-macros", + "swift-rs", + "tauri-build", + "tauri-macros 2.1.0", "tauri-runtime", "tauri-runtime-wry", - "tauri-utils", - "tempfile", - "thiserror 1.0.64", - "time", + "tauri-utils 2.3.0", + "thiserror 2.0.11", "tokio", + "tray-icon", "url", - "uuid 1.10.0", + "urlpattern", "webkit2gtk", "webview2-com", - "windows 0.39.0", - "zip", + "window-vibrancy", + "windows 0.60.0", ] [[package]] name = "tauri-build" -version = "1.2.1" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8807c85d656b2b93927c19fe5a5f1f1f348f96c2de8b90763b3c2d561511f9b4" +checksum = "7ffa8732a66f90903f5a585215f3cf1e87988d0359bc88c18a502efe7572c1de" dependencies = [ "anyhow", "cargo_toml", - "heck 0.4.1", - "json-patch", + "dirs 6.0.0", + "glob", + "heck 0.5.0", + "json-patch 3.0.1", + "schemars", "semver", + "serde", "serde_json", - "tauri-utils", - "winres", + "tauri-utils 2.3.0", + "tauri-winres", + "toml 0.8.20", + "walkdir", ] [[package]] @@ -5934,25 +6738,51 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14388d484b6b1b5dc0f6a7d6cc6433b3b230bec85eaa576adcdf3f9fafa49251" dependencies = [ "base64 0.13.1", - "brotli", - "ico", - "json-patch", + "brotli 3.3.4", + "ico 0.2.0", + "json-patch 0.2.7", "plist", "png", "proc-macro2", "quote", - "regex", "semver", "serde", "serde_json", "sha2 0.10.8", - "tauri-utils", + "tauri-utils 1.2.1", "thiserror 1.0.64", "time", "uuid 1.10.0", "walkdir", ] +[[package]] +name = "tauri-codegen" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c266a247f14d63f40c6282c2653a8bac5cc3d482ca562a003a88513653ea817a" +dependencies = [ + "base64 0.22.1", + "brotli 7.0.0", + "ico 0.4.0", + "json-patch 3.0.1", + "plist", + "png", + "proc-macro2", + "quote", + "semver", + "serde", + "serde_json", + "sha2 0.10.8", + "syn 2.0.96", + "tauri-utils 2.3.0", + "thiserror 2.0.11", + "time", + "url", + "uuid 1.10.0", + "walkdir", +] + [[package]] name = "tauri-macros" version = "1.2.1" @@ -5963,47 +6793,163 @@ dependencies = [ "proc-macro2", "quote", "syn 1.0.109", - "tauri-codegen", - "tauri-utils", + "tauri-codegen 1.2.1", + "tauri-utils 1.2.1", +] + +[[package]] +name = "tauri-macros" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f47a1cf94b3bd6c4dc37dce1a43fc96120ff29a91757f0ab3cf713c7ad846e7c" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "syn 2.0.96", + "tauri-codegen 2.1.0", + "tauri-utils 2.3.0", +] + +[[package]] +name = "tauri-plugin" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9972871fcbddf16618f70412d965d4d845cd4b76d03fff168709961ef71e5cdf" +dependencies = [ + "anyhow", + "glob", + "plist", + "schemars", + "serde", + "serde_json", + "tauri-utils 2.3.0", + "toml 0.8.20", + "walkdir", +] + +[[package]] +name = "tauri-plugin-clipboard-manager" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ab4cb42fdf745229b768802e9180920a4be63122cf87ed1c879103f7609d98e" +dependencies = [ + "arboard", + "log", + "serde", + "serde_json", + "tauri", + "tauri-plugin", + "thiserror 2.0.11", +] + +[[package]] +name = "tauri-plugin-process" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40cc553ab29581c8c43dfa5fb0c9d5aee8ba962ad3b42908eea26c79610441b7" +dependencies = [ + "tauri", + "tauri-plugin", +] + +[[package]] +name = "tauri-plugin-shell" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb2c50a63e60fb8925956cc5b7569f4b750ac197a4d39f13b8dd46ea8e2bad79" +dependencies = [ + "encoding_rs", + "log", + "open", + "os_pipe", + "regex", + "schemars", + "serde", + "serde_json", + "shared_child", + "tauri", + "tauri-plugin", + "thiserror 2.0.11", + "tokio", +] + +[[package]] +name = "tauri-plugin-updater" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31bfcfb4a8318008d2108ccfba439d8263cf48867baabf372cb0e9f24771896" +dependencies = [ + "base64 0.22.1", + "dirs 6.0.0", + "flate2", + "futures-util", + "http 1.1.0", + "infer 0.19.0", + "log", + "minisign-verify", + "osakit", + "percent-encoding", + "reqwest 0.12.4", + "semver", + "serde", + "serde_json", + "tar", + "tauri", + "tauri-plugin", + "tempfile", + "thiserror 2.0.11", + "time", + "tokio", + "url", + "windows-sys 0.59.0", + "zip", ] [[package]] name = "tauri-runtime" -version = "0.12.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c507d954d08ac8705d235bc70ec6975b9054fb95ff7823af72dbb04186596f3b" +checksum = "9e9c7bce5153f1ca7bc45eba37349b31ba50e975e28edc8b5766c5ec02b0b63a" dependencies = [ + "cookie", + "dpi", "gtk", - "http 0.2.9", - "http-range", - "rand 0.8.5", + "http 1.1.0", + "jni", "raw-window-handle", "serde", "serde_json", - "tauri-utils", - "thiserror 1.0.64", - "uuid 1.10.0", - "webview2-com", - "windows 0.39.0", + "tauri-utils 2.3.0", + "thiserror 2.0.11", + "url", + "windows 0.60.0", ] [[package]] name = "tauri-runtime-wry" -version = "0.12.2" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36b1c5764a41a13176a4599b5b7bd0881bea7d94dfe45e1e755f789b98317e30" +checksum = "087188020fd6facb8578fe9b38e81fa0fe5fb85744c73da51a299f94a530a1e3" dependencies = [ - "cocoa", "gtk", + "http 1.1.0", + "jni", + "log", + "objc2 0.6.0", + "objc2-app-kit 0.3.0", + "objc2-foundation 0.3.0", + "once_cell", "percent-encoding", - "rand 0.8.5", "raw-window-handle", + "softbuffer", + "tao", "tauri-runtime", - "tauri-utils", - "uuid 1.10.0", + "tauri-utils 2.3.0", + "url", "webkit2gtk", "webview2-com", - "windows 0.39.0", + "windows 0.60.0", "wry", ] @@ -6013,13 +6959,12 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5abbc109a6eb45127956ffcc26ef0e875d160150ac16cfa45d26a6b2871686f1" dependencies = [ - "brotli", - "ctor", - "glob", + "brotli 3.3.4", + "ctor 0.1.26", "heck 0.4.1", - "html5ever", - "infer", - "json-patch", + "html5ever 0.25.2", + "infer 0.7.0", + "json-patch 0.2.7", "kuchiki", "memchr", "phf 0.10.1", @@ -6028,24 +6973,71 @@ dependencies = [ "semver", "serde", "serde_json", - "serde_with", + "serde_with 1.14.0", "thiserror 1.0.64", "url", - "walkdir", "windows 0.39.0", ] [[package]] -name = "tempfile" -version = "3.7.1" +name = "tauri-utils" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc02fddf48964c42031a0b3fe0428320ecf3a73c401040fc0096f97794310651" +checksum = "82dcced4014e59af9790cc22f5d271df3be09ecd6728ec68861642553c8d01b7" +dependencies = [ + "anyhow", + "brotli 7.0.0", + "cargo_metadata 0.19.2", + "ctor 0.2.9", + "dunce", + "glob", + "html5ever 0.26.0", + "http 1.1.0", + "infer 0.19.0", + "json-patch 3.0.1", + "kuchikiki", + "log", + "memchr", + "phf 0.11.3", + "proc-macro2", + "quote", + "regex", + "schemars", + "semver", + "serde", + "serde-untagged", + "serde_json", + "serde_with 3.12.0", + "swift-rs", + "thiserror 2.0.11", + "toml 0.8.20", + "url", + "urlpattern", + "uuid 1.10.0", + "walkdir", +] + +[[package]] +name = "tauri-winres" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56eaa45f707bedf34d19312c26d350bc0f3c59a47e58e8adbeecdc850d2c13a0" +dependencies = [ + "embed-resource", + "toml 0.8.20", +] + +[[package]] +name = "tempfile" +version = "3.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7437ac7763b9b123ccf33c338a5cc1bac6f69b45a136c19bdd8a65e3916435bf" dependencies = [ - "cfg-if", "fastrand", - "redox_syscall 0.3.5", - "rustix", - "windows-sys 0.48.0", + "getrandom 0.3.2", + "once_cell", + "rustix 1.0.4", + "windows-sys 0.59.0", ] [[package]] @@ -6207,13 +7199,14 @@ dependencies = [ ] [[package]] -name = "thread_local" -version = "1.1.7" +name = "tiff" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" +checksum = "ba1310fcea54c6a9a4fd1aad794ecc02c31682f6bfbecdf460bf19533eed1e3e" dependencies = [ - "cfg-if", - "once_cell", + "flate2", + "jpeg-decoder", + "weezl", ] [[package]] @@ -6403,6 +7396,17 @@ dependencies = [ "winnow 0.5.10", ] +[[package]] +name = "toml_edit" +version = "0.20.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70f427fce4d84c72b5b732388bf4a9f4531b53f74e2887e3ecb2481f68f66d81" +dependencies = [ + "indexmap 2.5.0", + "toml_datetime", + "winnow 0.5.10", +] + [[package]] name = "toml_edit" version = "0.22.23" @@ -6474,36 +7478,41 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" dependencies = [ "once_cell", - "valuable", ] [[package]] -name = "tracing-log" -version = "0.1.3" +name = "tray-icon" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922" +checksum = "d433764348e7084bad2c5ea22c96c71b61b17afe3a11645710f533bd72b6a2b5" dependencies = [ - "lazy_static", - "log", - "tracing-core", -] - -[[package]] -name = "tracing-subscriber" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30a651bc37f915e81f087d86e62a18eec5f79550c7faff886f7090b4ea757c77" -dependencies = [ - "matchers", - "nu-ansi-term", + "crossbeam-channel", + "dirs 6.0.0", + "libappindicator", + "muda", + "objc2 0.6.0", + "objc2-app-kit 0.3.0", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-foundation 0.3.0", "once_cell", - "regex", - "sharded-slab", - "smallvec", - "thread_local", - "tracing", - "tracing-core", - "tracing-log", + "png", + "serde", + "thiserror 2.0.11", + "windows-sys 0.59.0", +] + +[[package]] +name = "tree_magic_mini" +version = "3.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aac5e8971f245c3389a5a76e648bfc80803ae066a1243a75db0064d7c1129d63" +dependencies = [ + "fnv", + "memchr", + "nom", + "once_cell", + "petgraph", ] [[package]] @@ -6544,6 +7553,12 @@ dependencies = [ "termcolor", ] +[[package]] +name = "typeid" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c" + [[package]] name = "typenum" version = "1.16.0" @@ -6556,6 +7571,47 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" +[[package]] +name = "unic-char-property" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221" +dependencies = [ + "unic-char-range", +] + +[[package]] +name = "unic-char-range" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc" + +[[package]] +name = "unic-common" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc" + +[[package]] +name = "unic-ucd-ident" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e230a37c0381caa9219d67cf063aa3a375ffed5bf541a452db16e744bdab6987" +dependencies = [ + "unic-char-property", + "unic-char-range", + "unic-ucd-version", +] + +[[package]] +name = "unic-ucd-version" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4" +dependencies = [ + "unic-common", +] + [[package]] name = "unicode-bidi" version = "0.3.13" @@ -6579,9 +7635,9 @@ dependencies = [ [[package]] name = "unicode-segmentation" -version = "1.10.1" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" [[package]] name = "unicode-xid" @@ -6617,6 +7673,18 @@ dependencies = [ "serde", ] +[[package]] +name = "urlpattern" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70acd30e3aa1450bc2eece896ce2ad0d178e9c079493819301573dae3c37ba6d" +dependencies = [ + "regex", + "serde", + "unic-ucd-ident", + "url", +] + [[package]] name = "utf-8" version = "0.7.6" @@ -6677,14 +7745,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" dependencies = [ "getrandom 0.2.10", + "serde", ] -[[package]] -name = "valuable" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" - [[package]] name = "vcpkg" version = "0.2.15" @@ -6698,7 +7761,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e27d6bdd219887a9eadd19e1c34f32e47fa332301184935c6d9bca26f3cca525" dependencies = [ "anyhow", - "cargo_metadata", + "cargo_metadata 0.18.1", "cfg-if", "regex", "rustc_version", @@ -6706,12 +7769,6 @@ dependencies = [ "time", ] -[[package]] -name = "version-compare" -version = "0.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c18c859eead79d8b95d09e4678566e8d70105c4e7b251f707a03df32442661b" - [[package]] name = "version-compare" version = "0.1.1" @@ -6724,6 +7781,26 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "vswhom" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be979b7f07507105799e854203b470ff7c78a1639e330a58f183b5fea574608b" +dependencies = [ + "libc", + "vswhom-sys", +] + +[[package]] +name = "vswhom-sys" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb067e4cbd1ff067d1df46c9194b5de0e98efd2810bbc95c5d5e5f25a3231150" +dependencies = [ + "cc", + "libc", +] + [[package]] name = "walkdir" version = "2.3.3" @@ -6755,6 +7832,15 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasi" +version = "0.14.2+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +dependencies = [ + "wit-bindgen-rt", +] + [[package]] name = "wasm-bindgen" version = "0.2.87" @@ -6821,6 +7907,19 @@ version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" +[[package]] +name = "wasm-streams" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e072d4e72f700fb3443d8fe94a39315df013eef1104903cdb0a2abd322bbecd" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + [[package]] name = "wasmtimer" version = "0.4.1" @@ -6835,6 +7934,76 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "wayland-backend" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7208998eaa3870dad37ec8836979581506e0c5c64c20c9e79e9d2a10d6f47bf" +dependencies = [ + "cc", + "downcast-rs", + "rustix 0.38.44", + "smallvec", + "wayland-sys", +] + +[[package]] +name = "wayland-client" +version = "0.31.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2120de3d33638aaef5b9f4472bff75f07c56379cf76ea320bd3a3d65ecaf73f" +dependencies = [ + "bitflags 2.9.0", + "rustix 0.38.44", + "wayland-backend", + "wayland-scanner", +] + +[[package]] +name = "wayland-protocols" +version = "0.31.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f81f365b8b4a97f422ac0e8737c438024b5951734506b0e1d775c73030561f4" +dependencies = [ + "bitflags 2.9.0", + "wayland-backend", + "wayland-client", + "wayland-scanner", +] + +[[package]] +name = "wayland-protocols-wlr" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad1f61b76b6c2d8742e10f9ba5c3737f6530b4c243132c2a2ccc8aa96fe25cd6" +dependencies = [ + "bitflags 2.9.0", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-scanner", +] + +[[package]] +name = "wayland-scanner" +version = "0.31.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "896fdafd5d28145fce7958917d69f2fd44469b1d4e861cb5961bcbeebc6d1484" +dependencies = [ + "proc-macro2", + "quick-xml 0.37.4", + "quote", +] + +[[package]] +name = "wayland-sys" +version = "0.31.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbcebb399c77d5aa9fa5db874806ee7b4eba4e73650948e8f93963f128896615" +dependencies = [ + "pkg-config", +] + [[package]] name = "web-sys" version = "0.3.64" @@ -6847,9 +8016,9 @@ dependencies = [ [[package]] name = "webkit2gtk" -version = "0.18.2" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8f859735e4a452aeb28c6c56a852967a8a76c8eb1cc32dbf931ad28a13d6370" +checksum = "76b1bc1e54c581da1e9f179d0b38512ba358fb1af2d634a1affe42e37172361a" dependencies = [ "bitflags 1.3.2", "cairo-rs", @@ -6865,20 +8034,18 @@ dependencies = [ "javascriptcore-rs", "libc", "once_cell", - "soup2", + "soup3", "webkit2gtk-sys", ] [[package]] name = "webkit2gtk-sys" -version = "0.18.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d76ca6ecc47aeba01ec61e480139dda143796abcae6f83bcddf50d6b5b1dcf3" +checksum = "62daa38afc514d1f8f12b8693d30d5993ff77ced33ce30cd04deebc267a6d57c" dependencies = [ - "atk-sys", "bitflags 1.3.2", "cairo-sys-rs", - "gdk-pixbuf-sys", "gdk-sys", "gio-sys", "glib-sys", @@ -6886,10 +8053,9 @@ dependencies = [ "gtk-sys", "javascriptcore-rs-sys", "libc", - "pango-sys", "pkg-config", - "soup2-sys", - "system-deps 6.1.1", + "soup3-sys", + "system-deps", ] [[package]] @@ -6909,42 +8075,46 @@ dependencies = [ [[package]] name = "webview2-com" -version = "0.19.1" +version = "0.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4a769c9f1a64a8734bde70caafac2b96cada12cd4aefa49196b3a386b8b4178" +checksum = "b0d606f600e5272b514dbb66539dd068211cc20155be8d3958201b4b5bd79ed3" dependencies = [ "webview2-com-macros", "webview2-com-sys", - "windows 0.39.0", - "windows-implement", + "windows 0.60.0", + "windows-core 0.60.1", + "windows-implement 0.59.0", + "windows-interface", ] [[package]] name = "webview2-com-macros" -version = "0.6.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaebe196c01691db62e9e4ca52c5ef1e4fd837dcae27dae3ada599b5a8fd05ac" +checksum = "1d228f15bba3b9d56dde8bddbee66fa24545bd17b48d5128ccf4a8742b18e431" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.96", ] [[package]] name = "webview2-com-sys" -version = "0.19.0" +version = "0.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aac48ef20ddf657755fdcda8dfed2a7b4fc7e4581acce6fe9b88c3d64f29dee7" +checksum = "bfb27fccd3c27f68e9a6af1bcf48c2d82534b8675b83608a4d81446d095a17ac" dependencies = [ - "regex", - "serde", - "serde_json", - "thiserror 1.0.64", - "windows 0.39.0", - "windows-bindgen", - "windows-metadata", + "thiserror 2.0.11", + "windows 0.60.0", + "windows-core 0.60.1", ] +[[package]] +name = "weezl" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082" + [[package]] name = "widestring" version = "1.1.0" @@ -6983,16 +8153,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] -name = "windows" -version = "0.37.0" +name = "window-vibrancy" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57b543186b344cc61c85b5aab0d2e3adf4e0f99bc076eff9aa5927bcc0b8a647" +checksum = "d9bec5a31f3f9362f2258fd0e9c9dd61a9ca432e7306cc78c444258f0dce9a9c" dependencies = [ - "windows_aarch64_msvc 0.37.0", - "windows_i686_gnu 0.37.0", - "windows_i686_msvc 0.37.0", - "windows_x86_64_gnu 0.37.0", - "windows_x86_64_msvc 0.37.0", + "objc2 0.6.0", + "objc2-app-kit 0.3.0", + "objc2-core-foundation", + "objc2-foundation 0.3.0", + "raw-window-handle", + "windows-sys 0.59.0", + "windows-version", ] [[package]] @@ -7001,7 +8173,7 @@ version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1c4bd0a50ac6020f65184721f758dba47bb9fbc2133df715ec74a237b26794a" dependencies = [ - "windows-implement", + "windows-implement 0.39.0", "windows_aarch64_msvc 0.39.0", "windows_i686_gnu 0.39.0", "windows_i686_msvc 0.39.0", @@ -7011,21 +8183,56 @@ dependencies = [ [[package]] name = "windows" -version = "0.48.0" +version = "0.60.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" +checksum = "ddf874e74c7a99773e62b1c671427abf01a425e77c3d3fb9fb1e4883ea934529" dependencies = [ - "windows-targets 0.48.1", + "windows-collections", + "windows-core 0.60.1", + "windows-future", + "windows-link", + "windows-numerics", ] [[package]] -name = "windows-bindgen" -version = "0.39.0" +name = "windows-collections" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68003dbd0e38abc0fb85b939240f4bce37c43a5981d3df37ccbaaa981b47cb41" +checksum = "5467f79cc1ba3f52ebb2ed41dbb459b8e7db636cc3429458d9a852e15bc24dec" dependencies = [ - "windows-metadata", - "windows-tokens", + "windows-core 0.60.1", +] + +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-core" +version = "0.60.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca21a92a9cae9bf4ccae5cf8368dce0837100ddf6e6d57936749e85f152f6247" +dependencies = [ + "windows-implement 0.59.0", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-future" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a787db4595e7eb80239b74ce8babfb1363d8e343ab072f2ffe901400c03349f0" +dependencies = [ + "windows-core 0.60.1", + "windows-link", ] [[package]] @@ -7039,24 +8246,68 @@ dependencies = [ ] [[package]] -name = "windows-metadata" -version = "0.39.0" +name = "windows-implement" +version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ee5e275231f07c6e240d14f34e1b635bf1faa1c76c57cfd59a5cdb9848e4278" +checksum = "83577b051e2f49a058c308f17f273b570a6a758386fc291b5f6a934dd84e48c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", +] + +[[package]] +name = "windows-interface" +version = "0.59.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", +] + +[[package]] +name = "windows-link" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" + +[[package]] +name = "windows-numerics" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "005dea54e2f6499f2cee279b8f703b3cf3b5734a2d8d21867c8f44003182eeed" +dependencies = [ + "windows-core 0.60.1", + "windows-link", +] + +[[package]] +name = "windows-result" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87fa48cc5d406560701792be122a10132491cff9d0aeb23583cc2dcafc847319" +dependencies = [ + "windows-link", +] [[package]] name = "windows-sys" -version = "0.42.0" +version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", + "windows-targets 0.42.2", ] [[package]] @@ -7077,6 +8328,30 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + [[package]] name = "windows-targets" version = "0.48.1" @@ -7114,6 +8389,15 @@ version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f838de2fe15fe6bac988e74b798f26499a8b21a9d97edec321e79b28d1d7f597" +[[package]] +name = "windows-version" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e04a5c6627e310a23ad2358483286c7df260c964eb2d003d8efd6d0f4e79265c" +dependencies = [ + "windows-link", +] + [[package]] name = "windows_aarch64_gnullvm" version = "0.42.2" @@ -7132,12 +8416,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" -[[package]] -name = "windows_aarch64_msvc" -version = "0.37.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2623277cb2d1c216ba3b578c0f3cf9cdebeddb6e66b1b218bb33596ea7769c3a" - [[package]] name = "windows_aarch64_msvc" version = "0.39.0" @@ -7162,12 +8440,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" -[[package]] -name = "windows_i686_gnu" -version = "0.37.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3925fd0b0b804730d44d4b6278c50f9699703ec49bcd628020f46f4ba07d9e1" - [[package]] name = "windows_i686_gnu" version = "0.39.0" @@ -7198,12 +8470,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" -[[package]] -name = "windows_i686_msvc" -version = "0.37.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce907ac74fe331b524c1298683efbf598bb031bc84d5e274db2083696d07c57c" - [[package]] name = "windows_i686_msvc" version = "0.39.0" @@ -7228,12 +8494,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" -[[package]] -name = "windows_x86_64_gnu" -version = "0.37.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2babfba0828f2e6b32457d5341427dcbb577ceef556273229959ac23a10af33d" - [[package]] name = "windows_x86_64_gnu" version = "0.39.0" @@ -7276,12 +8536,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" -[[package]] -name = "windows_x86_64_msvc" -version = "0.37.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4dd6dc7df2d84cf7b33822ed5b86318fb1781948e9663bacd047fc9dd52259d" - [[package]] name = "windows_x86_64_msvc" version = "0.39.0" @@ -7345,12 +8599,32 @@ dependencies = [ ] [[package]] -name = "winres" -version = "0.1.12" +name = "wit-bindgen-rt" +version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b68db261ef59e9e52806f688020631e987592bd83619edccda9c47d42cde4f6c" +checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" dependencies = [ - "toml 0.5.11", + "bitflags 2.9.0", +] + +[[package]] +name = "wl-clipboard-rs" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12b41773911497b18ca8553c3daaf8ec9fe9819caf93d451d3055f69de028adb" +dependencies = [ + "derive-new", + "libc", + "log", + "nix", + "os_pipe", + "tempfile", + "thiserror 1.0.64", + "tree_magic_mini", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-protocols-wlr", ] [[package]] @@ -7367,40 +8641,46 @@ checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" [[package]] name = "wry" -version = "0.23.4" +version = "0.50.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c1ad8e2424f554cc5bdebe8aa374ef5b433feff817aebabca0389961fc7ef98" +checksum = "b19b78efae8b853c6c817e8752fc1dbf9cab8a8ffe9c30f399bd750ccf0f0730" dependencies = [ - "base64 0.13.1", - "block", - "cocoa", - "core-graphics", + "base64 0.22.1", + "block2 0.6.0", + "cookie", "crossbeam-channel", + "dpi", "dunce", - "gdk", - "gio", - "glib", + "gdkx11", "gtk", - "html5ever", - "http 0.2.9", - "kuchiki", + "html5ever 0.26.0", + "http 1.1.0", + "javascriptcore-rs", + "jni", + "kuchikiki", "libc", - "log", - "objc", - "objc_id", + "ndk", + "objc2 0.6.0", + "objc2-app-kit 0.3.0", + "objc2-core-foundation", + "objc2-foundation 0.3.0", + "objc2-ui-kit", + "objc2-web-kit", "once_cell", - "serde", - "serde_json", + "percent-encoding", + "raw-window-handle", "sha2 0.10.8", - "soup2", - "tao", - "thiserror 1.0.64", + "soup3", + "tao-macros", + "thiserror 2.0.11", "url", "webkit2gtk", "webkit2gtk-sys", "webview2-com", - "windows 0.39.0", - "windows-implement", + "windows 0.60.0", + "windows-core 0.60.1", + "windows-version", + "x11-dl", ] [[package]] @@ -7424,6 +8704,23 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "x11rb" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d91ffca73ee7f68ce055750bf9f6eca0780b8c85eff9bc046a3b0da41755e12" +dependencies = [ + "gethostname", + "rustix 0.38.44", + "x11rb-protocol", +] + +[[package]] +name = "x11rb-protocol" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec107c4503ea0b4a98ef47356329af139c0a4f7750e621cf2973cd3385ebcb3d" + [[package]] name = "x25519-dalek" version = "2.0.0" @@ -7438,11 +8735,12 @@ dependencies = [ [[package]] name = "xattr" -version = "1.0.1" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4686009f71ff3e5c4dbcf1a282d0a44db3f021ba69350cd42086b3e5f1c6985" +checksum = "0d65cbf2f12c15564212d48f4e3dfb87923d25d611f2aed18f4cb23f0413d89e" dependencies = [ "libc", + "rustix 1.0.4", ] [[package]] @@ -7555,11 +8853,13 @@ dependencies = [ [[package]] name = "zip" -version = "0.6.6" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "760394e246e4c28189f19d488c058bf16f564016aefac5d32bb1f3b51d5e9261" +checksum = "27c03817464f64e23f6f37574b4fdc8cf65925b5bfd2b0f2aedf959791941f88" dependencies = [ - "byteorder", + "arbitrary", "crc32fast", "crossbeam-utils", + "indexmap 2.5.0", + "memchr", ] diff --git a/nym-wallet/package.json b/nym-wallet/package.json index 341da85fb1..6584e1bf07 100644 --- a/nym-wallet/package.json +++ b/nym-wallet/package.json @@ -21,6 +21,7 @@ "webpack:prod": "yarn webpack --progress --config webpack.prod.js" }, "dependencies": { + "@babel/helper-simple-access": "^7.25.9", "@emotion/react": "^11.7.0", "@emotion/styled": "^11.6.0", "@hookform/resolvers": "^2.8.0", @@ -33,7 +34,10 @@ "@nymproject/react": "^1.0.0", "@nymproject/types": "^1.0.0", "@storybook/react": "^6.5.15", - "@tauri-apps/api": "^1.2.0", + "@tauri-apps/api": "^2.4.0", + "@tauri-apps/cli": "^2.4.0", + "@tauri-apps/plugin-clipboard-manager": "^2.2.2", + "@tauri-apps/plugin-updater": "^2.0.0", "@tauri-apps/tauri-forage": "^1.0.0-beta.2", "big.js": "^6.2.1", "bs58": "^4.0.1", @@ -67,7 +71,7 @@ "@pmmmwh/react-refresh-webpack-plugin": "^0.5.4", "@storybook/react": "^6.5.15", "@svgr/webpack": "^6.1.1", - "@tauri-apps/cli": "^1.0.5", + "@tauri-apps/cli": "^2.4.0", "@testing-library/jest-dom": "^5.14.1", "@testing-library/react": "^12.0.0", "@types/big.js": "^6.1.6", @@ -125,4 +129,4 @@ "webpack-merge": "^5.8.0" }, "private": false -} \ No newline at end of file +} diff --git a/nym-wallet/src-tauri/Cargo.toml b/nym-wallet/src-tauri/Cargo.toml index 28d878b799..e394276220 100644 --- a/nym-wallet/src-tauri/Cargo.toml +++ b/nym-wallet/src-tauri/Cargo.toml @@ -13,13 +13,17 @@ rust-version = "1.76" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [build-dependencies] -tauri-build = { version = "=1.2.1", features = [] } +tauri-build = { version = "2", features = [] } tauri-codegen = "=1.2.1" tauri-macros = "=1.2.1" [dependencies] async-trait = "0.1.68" +tauri-plugin-updater = "2" +tauri-plugin-clipboard-manager = "2" +tauri-plugin-shell = "2" +tauri-plugin-process = "2" bip39 = { version = "2.0.0", features = ["zeroize", "rand"] } cfg-if = "1.0.0" colored = "2.0" @@ -38,7 +42,7 @@ serde_json = "1.0" serde_repr = "0.1" strum = { version = "0.23", features = ["derive"] } tap = "1" -tauri = { version = "=1.2.3", features = ["clipboard-all", "shell-open", "updater", "window-maximize", "window-print"] } +tauri = { version = "2", features = [] } #tendermint-rpc = "0.23.0" time = { version = "0.3.30", features = ["local-offset"] } thiserror = "1.0" diff --git a/nym-wallet/src-tauri/capabilities/migrated.json b/nym-wallet/src-tauri/capabilities/migrated.json new file mode 100644 index 0000000000..b55bbbbd82 --- /dev/null +++ b/nym-wallet/src-tauri/capabilities/migrated.json @@ -0,0 +1,11 @@ +{ + "identifier": "migrated", + "description": "permissions that were migrated from v1", + "local": true, + "windows": [ + "main" + ], + "permissions": [ + "core:default" + ] +} \ No newline at end of file diff --git a/nym-wallet/src-tauri/gen/schemas/capabilities.json b/nym-wallet/src-tauri/gen/schemas/capabilities.json new file mode 100644 index 0000000000..0ea61d3fbc --- /dev/null +++ b/nym-wallet/src-tauri/gen/schemas/capabilities.json @@ -0,0 +1 @@ +{"migrated":{"identifier":"migrated","description":"permissions that were migrated from v1","local":true,"windows":["main"],"permissions":["core:default"]}} \ No newline at end of file diff --git a/nym-wallet/src-tauri/gen/schemas/desktop-schema.json b/nym-wallet/src-tauri/gen/schemas/desktop-schema.json new file mode 100644 index 0000000000..5ff69983d7 --- /dev/null +++ b/nym-wallet/src-tauri/gen/schemas/desktop-schema.json @@ -0,0 +1,2279 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "CapabilityFile", + "description": "Capability formats accepted in a capability file.", + "anyOf": [ + { + "description": "A single capability.", + "allOf": [ + { + "$ref": "#/definitions/Capability" + } + ] + }, + { + "description": "A list of capabilities.", + "type": "array", + "items": { + "$ref": "#/definitions/Capability" + } + }, + { + "description": "A list of capabilities.", + "type": "object", + "required": [ + "capabilities" + ], + "properties": { + "capabilities": { + "description": "The list of capabilities.", + "type": "array", + "items": { + "$ref": "#/definitions/Capability" + } + } + } + } + ], + "definitions": { + "Capability": { + "description": "A grouping and boundary mechanism developers can use to isolate access to the IPC layer.\n\nIt controls application windows' and webviews' fine grained access to the Tauri core, application, or plugin commands. If a webview or its window is not matching any capability then it has no access to the IPC layer at all.\n\nThis can be done to create groups of windows, based on their required system access, which can reduce impact of frontend vulnerabilities in less privileged windows. Windows can be added to a capability by exact name (e.g. `main-window`) or glob patterns like `*` or `admin-*`. A Window can have none, one, or multiple associated capabilities.\n\n## Example\n\n```json { \"identifier\": \"main-user-files-write\", \"description\": \"This capability allows the `main` window on macOS and Windows access to `filesystem` write related commands and `dialog` commands to enable programatic access to files selected by the user.\", \"windows\": [ \"main\" ], \"permissions\": [ \"core:default\", \"dialog:open\", { \"identifier\": \"fs:allow-write-text-file\", \"allow\": [{ \"path\": \"$HOME/test.txt\" }] }, ], \"platforms\": [\"macOS\",\"windows\"] } ```", + "type": "object", + "required": [ + "identifier", + "permissions" + ], + "properties": { + "identifier": { + "description": "Identifier of the capability.\n\n## Example\n\n`main-user-files-write`", + "type": "string" + }, + "description": { + "description": "Description of what the capability is intended to allow on associated windows.\n\nIt should contain a description of what the grouped permissions should allow.\n\n## Example\n\nThis capability allows the `main` window access to `filesystem` write related commands and `dialog` commands to enable programatic access to files selected by the user.", + "default": "", + "type": "string" + }, + "remote": { + "description": "Configure remote URLs that can use the capability permissions.\n\nThis setting is optional and defaults to not being set, as our default use case is that the content is served from our local application.\n\n:::caution Make sure you understand the security implications of providing remote sources with local system access. :::\n\n## Example\n\n```json { \"urls\": [\"https://*.mydomain.dev\"] } ```", + "anyOf": [ + { + "$ref": "#/definitions/CapabilityRemote" + }, + { + "type": "null" + } + ] + }, + "local": { + "description": "Whether this capability is enabled for local app URLs or not. Defaults to `true`.", + "default": true, + "type": "boolean" + }, + "windows": { + "description": "List of windows that are affected by this capability. Can be a glob pattern.\n\nIf a window label matches any of the patterns in this list, the capability will be enabled on all the webviews of that window, regardless of the value of [`Self::webviews`].\n\nOn multiwebview windows, prefer specifying [`Self::webviews`] and omitting [`Self::windows`] for a fine grained access control.\n\n## Example\n\n`[\"main\"]`", + "type": "array", + "items": { + "type": "string" + } + }, + "webviews": { + "description": "List of webviews that are affected by this capability. Can be a glob pattern.\n\nThe capability will be enabled on all the webviews whose label matches any of the patterns in this list, regardless of whether the webview's window label matches a pattern in [`Self::windows`].\n\n## Example\n\n`[\"sub-webview-one\", \"sub-webview-two\"]`", + "type": "array", + "items": { + "type": "string" + } + }, + "permissions": { + "description": "List of permissions attached to this capability.\n\nMust include the plugin name as prefix in the form of `${plugin-name}:${permission-name}`. For commands directly implemented in the application itself only `${permission-name}` is required.\n\n## Example\n\n```json [ \"core:default\", \"shell:allow-open\", \"dialog:open\", { \"identifier\": \"fs:allow-write-text-file\", \"allow\": [{ \"path\": \"$HOME/test.txt\" }] } ] ```", + "type": "array", + "items": { + "$ref": "#/definitions/PermissionEntry" + }, + "uniqueItems": true + }, + "platforms": { + "description": "Limit which target platforms this capability applies to.\n\nBy default all platforms are targeted.\n\n## Example\n\n`[\"macOS\",\"windows\"]`", + "type": [ + "array", + "null" + ], + "items": { + "$ref": "#/definitions/Target" + } + } + } + }, + "CapabilityRemote": { + "description": "Configuration for remote URLs that are associated with the capability.", + "type": "object", + "required": [ + "urls" + ], + "properties": { + "urls": { + "description": "Remote domains this capability refers to using the [URLPattern standard](https://urlpattern.spec.whatwg.org/).\n\n## Examples\n\n- \"https://*.mydomain.dev\": allows subdomains of mydomain.dev - \"https://mydomain.dev/api/*\": allows any subpath of mydomain.dev/api", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "PermissionEntry": { + "description": "An entry for a permission value in a [`Capability`] can be either a raw permission [`Identifier`] or an object that references a permission and extends its scope.", + "anyOf": [ + { + "description": "Reference a permission or permission set by identifier.", + "allOf": [ + { + "$ref": "#/definitions/Identifier" + } + ] + }, + { + "description": "Reference a permission or permission set by identifier and extends its scope.", + "type": "object", + "allOf": [ + { + "if": { + "properties": { + "identifier": { + "anyOf": [ + { + "description": "This permission set configures which\nshell functionality is exposed by default.\n\n#### Granted Permissions\n\nIt allows to use the `open` functionality without any specific\nscope pre-configured. It will allow opening `http(s)://`,\n`tel:` and `mailto:` links.\n", + "type": "string", + "const": "shell:default" + }, + { + "description": "Enables the execute command without any pre-configured scope.", + "type": "string", + "const": "shell:allow-execute" + }, + { + "description": "Enables the kill command without any pre-configured scope.", + "type": "string", + "const": "shell:allow-kill" + }, + { + "description": "Enables the open command without any pre-configured scope.", + "type": "string", + "const": "shell:allow-open" + }, + { + "description": "Enables the spawn command without any pre-configured scope.", + "type": "string", + "const": "shell:allow-spawn" + }, + { + "description": "Enables the stdin_write command without any pre-configured scope.", + "type": "string", + "const": "shell:allow-stdin-write" + }, + { + "description": "Denies the execute command without any pre-configured scope.", + "type": "string", + "const": "shell:deny-execute" + }, + { + "description": "Denies the kill command without any pre-configured scope.", + "type": "string", + "const": "shell:deny-kill" + }, + { + "description": "Denies the open command without any pre-configured scope.", + "type": "string", + "const": "shell:deny-open" + }, + { + "description": "Denies the spawn command without any pre-configured scope.", + "type": "string", + "const": "shell:deny-spawn" + }, + { + "description": "Denies the stdin_write command without any pre-configured scope.", + "type": "string", + "const": "shell:deny-stdin-write" + } + ] + } + } + }, + "then": { + "properties": { + "allow": { + "items": { + "title": "ShellScopeEntry", + "description": "Shell scope entry.", + "anyOf": [ + { + "type": "object", + "required": [ + "cmd", + "name" + ], + "properties": { + "args": { + "description": "The allowed arguments for the command execution.", + "allOf": [ + { + "$ref": "#/definitions/ShellScopeEntryAllowedArgs" + } + ] + }, + "cmd": { + "description": "The command name. It can start with a variable that resolves to a system base directory. The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`, `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`, `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`, `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.", + "type": "string" + }, + "name": { + "description": "The name for this allowed shell command configuration.\n\nThis name will be used inside of the webview API to call this command along with any specified arguments.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "name", + "sidecar" + ], + "properties": { + "args": { + "description": "The allowed arguments for the command execution.", + "allOf": [ + { + "$ref": "#/definitions/ShellScopeEntryAllowedArgs" + } + ] + }, + "name": { + "description": "The name for this allowed shell command configuration.\n\nThis name will be used inside of the webview API to call this command along with any specified arguments.", + "type": "string" + }, + "sidecar": { + "description": "If this command is a sidecar command.", + "type": "boolean" + } + }, + "additionalProperties": false + } + ] + } + }, + "deny": { + "items": { + "title": "ShellScopeEntry", + "description": "Shell scope entry.", + "anyOf": [ + { + "type": "object", + "required": [ + "cmd", + "name" + ], + "properties": { + "args": { + "description": "The allowed arguments for the command execution.", + "allOf": [ + { + "$ref": "#/definitions/ShellScopeEntryAllowedArgs" + } + ] + }, + "cmd": { + "description": "The command name. It can start with a variable that resolves to a system base directory. The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`, `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`, `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`, `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.", + "type": "string" + }, + "name": { + "description": "The name for this allowed shell command configuration.\n\nThis name will be used inside of the webview API to call this command along with any specified arguments.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "name", + "sidecar" + ], + "properties": { + "args": { + "description": "The allowed arguments for the command execution.", + "allOf": [ + { + "$ref": "#/definitions/ShellScopeEntryAllowedArgs" + } + ] + }, + "name": { + "description": "The name for this allowed shell command configuration.\n\nThis name will be used inside of the webview API to call this command along with any specified arguments.", + "type": "string" + }, + "sidecar": { + "description": "If this command is a sidecar command.", + "type": "boolean" + } + }, + "additionalProperties": false + } + ] + } + } + } + }, + "properties": { + "identifier": { + "description": "Identifier of the permission or permission set.", + "allOf": [ + { + "$ref": "#/definitions/Identifier" + } + ] + } + } + }, + { + "properties": { + "identifier": { + "description": "Identifier of the permission or permission set.", + "allOf": [ + { + "$ref": "#/definitions/Identifier" + } + ] + }, + "allow": { + "description": "Data that defines what is allowed by the scope.", + "type": [ + "array", + "null" + ], + "items": { + "$ref": "#/definitions/Value" + } + }, + "deny": { + "description": "Data that defines what is denied by the scope. This should be prioritized by validation logic.", + "type": [ + "array", + "null" + ], + "items": { + "$ref": "#/definitions/Value" + } + } + } + } + ], + "required": [ + "identifier" + ] + } + ] + }, + "Identifier": { + "description": "Permission identifier", + "oneOf": [ + { + "description": "No features are enabled by default, as we believe\nthe clipboard can be inherently dangerous and it is \napplication specific if read and/or write access is needed.\n\nClipboard interaction needs to be explicitly enabled.\n", + "type": "string", + "const": "clipboard-manager:default" + }, + { + "description": "Enables the clear command without any pre-configured scope.", + "type": "string", + "const": "clipboard-manager:allow-clear" + }, + { + "description": "Enables the read_image command without any pre-configured scope.", + "type": "string", + "const": "clipboard-manager:allow-read-image" + }, + { + "description": "Enables the read_text command without any pre-configured scope.", + "type": "string", + "const": "clipboard-manager:allow-read-text" + }, + { + "description": "Enables the write_html command without any pre-configured scope.", + "type": "string", + "const": "clipboard-manager:allow-write-html" + }, + { + "description": "Enables the write_image command without any pre-configured scope.", + "type": "string", + "const": "clipboard-manager:allow-write-image" + }, + { + "description": "Enables the write_text command without any pre-configured scope.", + "type": "string", + "const": "clipboard-manager:allow-write-text" + }, + { + "description": "Denies the clear command without any pre-configured scope.", + "type": "string", + "const": "clipboard-manager:deny-clear" + }, + { + "description": "Denies the read_image command without any pre-configured scope.", + "type": "string", + "const": "clipboard-manager:deny-read-image" + }, + { + "description": "Denies the read_text command without any pre-configured scope.", + "type": "string", + "const": "clipboard-manager:deny-read-text" + }, + { + "description": "Denies the write_html command without any pre-configured scope.", + "type": "string", + "const": "clipboard-manager:deny-write-html" + }, + { + "description": "Denies the write_image command without any pre-configured scope.", + "type": "string", + "const": "clipboard-manager:deny-write-image" + }, + { + "description": "Denies the write_text command without any pre-configured scope.", + "type": "string", + "const": "clipboard-manager:deny-write-text" + }, + { + "description": "Default core plugins set which includes:\n- 'core:path:default'\n- 'core:event:default'\n- 'core:window:default'\n- 'core:webview:default'\n- 'core:app:default'\n- 'core:image:default'\n- 'core:resources:default'\n- 'core:menu:default'\n- 'core:tray:default'\n", + "type": "string", + "const": "core:default" + }, + { + "description": "Default permissions for the plugin.", + "type": "string", + "const": "core:app:default" + }, + { + "description": "Enables the app_hide command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-app-hide" + }, + { + "description": "Enables the app_show command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-app-show" + }, + { + "description": "Enables the default_window_icon command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-default-window-icon" + }, + { + "description": "Enables the fetch_data_store_identifiers command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-fetch-data-store-identifiers" + }, + { + "description": "Enables the identifier command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-identifier" + }, + { + "description": "Enables the name command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-name" + }, + { + "description": "Enables the remove_data_store command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-remove-data-store" + }, + { + "description": "Enables the set_app_theme command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-set-app-theme" + }, + { + "description": "Enables the tauri_version command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-tauri-version" + }, + { + "description": "Enables the version command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-version" + }, + { + "description": "Denies the app_hide command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-app-hide" + }, + { + "description": "Denies the app_show command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-app-show" + }, + { + "description": "Denies the default_window_icon command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-default-window-icon" + }, + { + "description": "Denies the fetch_data_store_identifiers command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-fetch-data-store-identifiers" + }, + { + "description": "Denies the identifier command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-identifier" + }, + { + "description": "Denies the name command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-name" + }, + { + "description": "Denies the remove_data_store command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-remove-data-store" + }, + { + "description": "Denies the set_app_theme command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-set-app-theme" + }, + { + "description": "Denies the tauri_version command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-tauri-version" + }, + { + "description": "Denies the version command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-version" + }, + { + "description": "Default permissions for the plugin.", + "type": "string", + "const": "core:event:default" + }, + { + "description": "Enables the emit command without any pre-configured scope.", + "type": "string", + "const": "core:event:allow-emit" + }, + { + "description": "Enables the emit_to command without any pre-configured scope.", + "type": "string", + "const": "core:event:allow-emit-to" + }, + { + "description": "Enables the listen command without any pre-configured scope.", + "type": "string", + "const": "core:event:allow-listen" + }, + { + "description": "Enables the unlisten command without any pre-configured scope.", + "type": "string", + "const": "core:event:allow-unlisten" + }, + { + "description": "Denies the emit command without any pre-configured scope.", + "type": "string", + "const": "core:event:deny-emit" + }, + { + "description": "Denies the emit_to command without any pre-configured scope.", + "type": "string", + "const": "core:event:deny-emit-to" + }, + { + "description": "Denies the listen command without any pre-configured scope.", + "type": "string", + "const": "core:event:deny-listen" + }, + { + "description": "Denies the unlisten command without any pre-configured scope.", + "type": "string", + "const": "core:event:deny-unlisten" + }, + { + "description": "Default permissions for the plugin.", + "type": "string", + "const": "core:image:default" + }, + { + "description": "Enables the from_bytes command without any pre-configured scope.", + "type": "string", + "const": "core:image:allow-from-bytes" + }, + { + "description": "Enables the from_path command without any pre-configured scope.", + "type": "string", + "const": "core:image:allow-from-path" + }, + { + "description": "Enables the new command without any pre-configured scope.", + "type": "string", + "const": "core:image:allow-new" + }, + { + "description": "Enables the rgba command without any pre-configured scope.", + "type": "string", + "const": "core:image:allow-rgba" + }, + { + "description": "Enables the size command without any pre-configured scope.", + "type": "string", + "const": "core:image:allow-size" + }, + { + "description": "Denies the from_bytes command without any pre-configured scope.", + "type": "string", + "const": "core:image:deny-from-bytes" + }, + { + "description": "Denies the from_path command without any pre-configured scope.", + "type": "string", + "const": "core:image:deny-from-path" + }, + { + "description": "Denies the new command without any pre-configured scope.", + "type": "string", + "const": "core:image:deny-new" + }, + { + "description": "Denies the rgba command without any pre-configured scope.", + "type": "string", + "const": "core:image:deny-rgba" + }, + { + "description": "Denies the size command without any pre-configured scope.", + "type": "string", + "const": "core:image:deny-size" + }, + { + "description": "Default permissions for the plugin.", + "type": "string", + "const": "core:menu:default" + }, + { + "description": "Enables the append command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-append" + }, + { + "description": "Enables the create_default command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-create-default" + }, + { + "description": "Enables the get command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-get" + }, + { + "description": "Enables the insert command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-insert" + }, + { + "description": "Enables the is_checked command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-is-checked" + }, + { + "description": "Enables the is_enabled command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-is-enabled" + }, + { + "description": "Enables the items command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-items" + }, + { + "description": "Enables the new command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-new" + }, + { + "description": "Enables the popup command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-popup" + }, + { + "description": "Enables the prepend command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-prepend" + }, + { + "description": "Enables the remove command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-remove" + }, + { + "description": "Enables the remove_at command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-remove-at" + }, + { + "description": "Enables the set_accelerator command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-set-accelerator" + }, + { + "description": "Enables the set_as_app_menu command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-set-as-app-menu" + }, + { + "description": "Enables the set_as_help_menu_for_nsapp command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-set-as-help-menu-for-nsapp" + }, + { + "description": "Enables the set_as_window_menu command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-set-as-window-menu" + }, + { + "description": "Enables the set_as_windows_menu_for_nsapp command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-set-as-windows-menu-for-nsapp" + }, + { + "description": "Enables the set_checked command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-set-checked" + }, + { + "description": "Enables the set_enabled command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-set-enabled" + }, + { + "description": "Enables the set_icon command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-set-icon" + }, + { + "description": "Enables the set_text command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-set-text" + }, + { + "description": "Enables the text command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-text" + }, + { + "description": "Denies the append command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-append" + }, + { + "description": "Denies the create_default command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-create-default" + }, + { + "description": "Denies the get command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-get" + }, + { + "description": "Denies the insert command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-insert" + }, + { + "description": "Denies the is_checked command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-is-checked" + }, + { + "description": "Denies the is_enabled command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-is-enabled" + }, + { + "description": "Denies the items command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-items" + }, + { + "description": "Denies the new command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-new" + }, + { + "description": "Denies the popup command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-popup" + }, + { + "description": "Denies the prepend command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-prepend" + }, + { + "description": "Denies the remove command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-remove" + }, + { + "description": "Denies the remove_at command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-remove-at" + }, + { + "description": "Denies the set_accelerator command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-set-accelerator" + }, + { + "description": "Denies the set_as_app_menu command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-set-as-app-menu" + }, + { + "description": "Denies the set_as_help_menu_for_nsapp command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-set-as-help-menu-for-nsapp" + }, + { + "description": "Denies the set_as_window_menu command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-set-as-window-menu" + }, + { + "description": "Denies the set_as_windows_menu_for_nsapp command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-set-as-windows-menu-for-nsapp" + }, + { + "description": "Denies the set_checked command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-set-checked" + }, + { + "description": "Denies the set_enabled command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-set-enabled" + }, + { + "description": "Denies the set_icon command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-set-icon" + }, + { + "description": "Denies the set_text command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-set-text" + }, + { + "description": "Denies the text command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-text" + }, + { + "description": "Default permissions for the plugin.", + "type": "string", + "const": "core:path:default" + }, + { + "description": "Enables the basename command without any pre-configured scope.", + "type": "string", + "const": "core:path:allow-basename" + }, + { + "description": "Enables the dirname command without any pre-configured scope.", + "type": "string", + "const": "core:path:allow-dirname" + }, + { + "description": "Enables the extname command without any pre-configured scope.", + "type": "string", + "const": "core:path:allow-extname" + }, + { + "description": "Enables the is_absolute command without any pre-configured scope.", + "type": "string", + "const": "core:path:allow-is-absolute" + }, + { + "description": "Enables the join command without any pre-configured scope.", + "type": "string", + "const": "core:path:allow-join" + }, + { + "description": "Enables the normalize command without any pre-configured scope.", + "type": "string", + "const": "core:path:allow-normalize" + }, + { + "description": "Enables the resolve command without any pre-configured scope.", + "type": "string", + "const": "core:path:allow-resolve" + }, + { + "description": "Enables the resolve_directory command without any pre-configured scope.", + "type": "string", + "const": "core:path:allow-resolve-directory" + }, + { + "description": "Denies the basename command without any pre-configured scope.", + "type": "string", + "const": "core:path:deny-basename" + }, + { + "description": "Denies the dirname command without any pre-configured scope.", + "type": "string", + "const": "core:path:deny-dirname" + }, + { + "description": "Denies the extname command without any pre-configured scope.", + "type": "string", + "const": "core:path:deny-extname" + }, + { + "description": "Denies the is_absolute command without any pre-configured scope.", + "type": "string", + "const": "core:path:deny-is-absolute" + }, + { + "description": "Denies the join command without any pre-configured scope.", + "type": "string", + "const": "core:path:deny-join" + }, + { + "description": "Denies the normalize command without any pre-configured scope.", + "type": "string", + "const": "core:path:deny-normalize" + }, + { + "description": "Denies the resolve command without any pre-configured scope.", + "type": "string", + "const": "core:path:deny-resolve" + }, + { + "description": "Denies the resolve_directory command without any pre-configured scope.", + "type": "string", + "const": "core:path:deny-resolve-directory" + }, + { + "description": "Default permissions for the plugin.", + "type": "string", + "const": "core:resources:default" + }, + { + "description": "Enables the close command without any pre-configured scope.", + "type": "string", + "const": "core:resources:allow-close" + }, + { + "description": "Denies the close command without any pre-configured scope.", + "type": "string", + "const": "core:resources:deny-close" + }, + { + "description": "Default permissions for the plugin.", + "type": "string", + "const": "core:tray:default" + }, + { + "description": "Enables the get_by_id command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-get-by-id" + }, + { + "description": "Enables the new command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-new" + }, + { + "description": "Enables the remove_by_id command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-remove-by-id" + }, + { + "description": "Enables the set_icon command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-set-icon" + }, + { + "description": "Enables the set_icon_as_template command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-set-icon-as-template" + }, + { + "description": "Enables the set_menu command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-set-menu" + }, + { + "description": "Enables the set_show_menu_on_left_click command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-set-show-menu-on-left-click" + }, + { + "description": "Enables the set_temp_dir_path command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-set-temp-dir-path" + }, + { + "description": "Enables the set_title command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-set-title" + }, + { + "description": "Enables the set_tooltip command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-set-tooltip" + }, + { + "description": "Enables the set_visible command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-set-visible" + }, + { + "description": "Denies the get_by_id command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-get-by-id" + }, + { + "description": "Denies the new command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-new" + }, + { + "description": "Denies the remove_by_id command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-remove-by-id" + }, + { + "description": "Denies the set_icon command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-set-icon" + }, + { + "description": "Denies the set_icon_as_template command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-set-icon-as-template" + }, + { + "description": "Denies the set_menu command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-set-menu" + }, + { + "description": "Denies the set_show_menu_on_left_click command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-set-show-menu-on-left-click" + }, + { + "description": "Denies the set_temp_dir_path command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-set-temp-dir-path" + }, + { + "description": "Denies the set_title command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-set-title" + }, + { + "description": "Denies the set_tooltip command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-set-tooltip" + }, + { + "description": "Denies the set_visible command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-set-visible" + }, + { + "description": "Default permissions for the plugin.", + "type": "string", + "const": "core:webview:default" + }, + { + "description": "Enables the clear_all_browsing_data command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-clear-all-browsing-data" + }, + { + "description": "Enables the create_webview command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-create-webview" + }, + { + "description": "Enables the create_webview_window command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-create-webview-window" + }, + { + "description": "Enables the get_all_webviews command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-get-all-webviews" + }, + { + "description": "Enables the internal_toggle_devtools command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-internal-toggle-devtools" + }, + { + "description": "Enables the print command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-print" + }, + { + "description": "Enables the reparent command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-reparent" + }, + { + "description": "Enables the set_webview_background_color command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-set-webview-background-color" + }, + { + "description": "Enables the set_webview_focus command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-set-webview-focus" + }, + { + "description": "Enables the set_webview_position command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-set-webview-position" + }, + { + "description": "Enables the set_webview_size command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-set-webview-size" + }, + { + "description": "Enables the set_webview_zoom command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-set-webview-zoom" + }, + { + "description": "Enables the webview_close command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-webview-close" + }, + { + "description": "Enables the webview_hide command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-webview-hide" + }, + { + "description": "Enables the webview_position command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-webview-position" + }, + { + "description": "Enables the webview_show command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-webview-show" + }, + { + "description": "Enables the webview_size command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-webview-size" + }, + { + "description": "Denies the clear_all_browsing_data command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-clear-all-browsing-data" + }, + { + "description": "Denies the create_webview command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-create-webview" + }, + { + "description": "Denies the create_webview_window command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-create-webview-window" + }, + { + "description": "Denies the get_all_webviews command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-get-all-webviews" + }, + { + "description": "Denies the internal_toggle_devtools command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-internal-toggle-devtools" + }, + { + "description": "Denies the print command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-print" + }, + { + "description": "Denies the reparent command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-reparent" + }, + { + "description": "Denies the set_webview_background_color command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-set-webview-background-color" + }, + { + "description": "Denies the set_webview_focus command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-set-webview-focus" + }, + { + "description": "Denies the set_webview_position command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-set-webview-position" + }, + { + "description": "Denies the set_webview_size command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-set-webview-size" + }, + { + "description": "Denies the set_webview_zoom command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-set-webview-zoom" + }, + { + "description": "Denies the webview_close command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-webview-close" + }, + { + "description": "Denies the webview_hide command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-webview-hide" + }, + { + "description": "Denies the webview_position command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-webview-position" + }, + { + "description": "Denies the webview_show command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-webview-show" + }, + { + "description": "Denies the webview_size command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-webview-size" + }, + { + "description": "Default permissions for the plugin.", + "type": "string", + "const": "core:window:default" + }, + { + "description": "Enables the available_monitors command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-available-monitors" + }, + { + "description": "Enables the center command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-center" + }, + { + "description": "Enables the close command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-close" + }, + { + "description": "Enables the create command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-create" + }, + { + "description": "Enables the current_monitor command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-current-monitor" + }, + { + "description": "Enables the cursor_position command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-cursor-position" + }, + { + "description": "Enables the destroy command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-destroy" + }, + { + "description": "Enables the get_all_windows command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-get-all-windows" + }, + { + "description": "Enables the hide command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-hide" + }, + { + "description": "Enables the inner_position command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-inner-position" + }, + { + "description": "Enables the inner_size command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-inner-size" + }, + { + "description": "Enables the internal_toggle_maximize command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-internal-toggle-maximize" + }, + { + "description": "Enables the is_always_on_top command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-always-on-top" + }, + { + "description": "Enables the is_closable command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-closable" + }, + { + "description": "Enables the is_decorated command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-decorated" + }, + { + "description": "Enables the is_enabled command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-enabled" + }, + { + "description": "Enables the is_focused command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-focused" + }, + { + "description": "Enables the is_fullscreen command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-fullscreen" + }, + { + "description": "Enables the is_maximizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-maximizable" + }, + { + "description": "Enables the is_maximized command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-maximized" + }, + { + "description": "Enables the is_minimizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-minimizable" + }, + { + "description": "Enables the is_minimized command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-minimized" + }, + { + "description": "Enables the is_resizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-resizable" + }, + { + "description": "Enables the is_visible command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-visible" + }, + { + "description": "Enables the maximize command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-maximize" + }, + { + "description": "Enables the minimize command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-minimize" + }, + { + "description": "Enables the monitor_from_point command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-monitor-from-point" + }, + { + "description": "Enables the outer_position command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-outer-position" + }, + { + "description": "Enables the outer_size command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-outer-size" + }, + { + "description": "Enables the primary_monitor command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-primary-monitor" + }, + { + "description": "Enables the request_user_attention command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-request-user-attention" + }, + { + "description": "Enables the scale_factor command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-scale-factor" + }, + { + "description": "Enables the set_always_on_bottom command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-always-on-bottom" + }, + { + "description": "Enables the set_always_on_top command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-always-on-top" + }, + { + "description": "Enables the set_background_color command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-background-color" + }, + { + "description": "Enables the set_badge_count command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-badge-count" + }, + { + "description": "Enables the set_badge_label command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-badge-label" + }, + { + "description": "Enables the set_closable command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-closable" + }, + { + "description": "Enables the set_content_protected command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-content-protected" + }, + { + "description": "Enables the set_cursor_grab command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-cursor-grab" + }, + { + "description": "Enables the set_cursor_icon command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-cursor-icon" + }, + { + "description": "Enables the set_cursor_position command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-cursor-position" + }, + { + "description": "Enables the set_cursor_visible command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-cursor-visible" + }, + { + "description": "Enables the set_decorations command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-decorations" + }, + { + "description": "Enables the set_effects command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-effects" + }, + { + "description": "Enables the set_enabled command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-enabled" + }, + { + "description": "Enables the set_focus command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-focus" + }, + { + "description": "Enables the set_fullscreen command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-fullscreen" + }, + { + "description": "Enables the set_icon command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-icon" + }, + { + "description": "Enables the set_ignore_cursor_events command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-ignore-cursor-events" + }, + { + "description": "Enables the set_max_size command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-max-size" + }, + { + "description": "Enables the set_maximizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-maximizable" + }, + { + "description": "Enables the set_min_size command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-min-size" + }, + { + "description": "Enables the set_minimizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-minimizable" + }, + { + "description": "Enables the set_overlay_icon command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-overlay-icon" + }, + { + "description": "Enables the set_position command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-position" + }, + { + "description": "Enables the set_progress_bar command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-progress-bar" + }, + { + "description": "Enables the set_resizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-resizable" + }, + { + "description": "Enables the set_shadow command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-shadow" + }, + { + "description": "Enables the set_size command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-size" + }, + { + "description": "Enables the set_size_constraints command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-size-constraints" + }, + { + "description": "Enables the set_skip_taskbar command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-skip-taskbar" + }, + { + "description": "Enables the set_theme command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-theme" + }, + { + "description": "Enables the set_title command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-title" + }, + { + "description": "Enables the set_title_bar_style command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-title-bar-style" + }, + { + "description": "Enables the set_visible_on_all_workspaces command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-visible-on-all-workspaces" + }, + { + "description": "Enables the show command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-show" + }, + { + "description": "Enables the start_dragging command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-start-dragging" + }, + { + "description": "Enables the start_resize_dragging command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-start-resize-dragging" + }, + { + "description": "Enables the theme command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-theme" + }, + { + "description": "Enables the title command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-title" + }, + { + "description": "Enables the toggle_maximize command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-toggle-maximize" + }, + { + "description": "Enables the unmaximize command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-unmaximize" + }, + { + "description": "Enables the unminimize command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-unminimize" + }, + { + "description": "Denies the available_monitors command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-available-monitors" + }, + { + "description": "Denies the center command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-center" + }, + { + "description": "Denies the close command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-close" + }, + { + "description": "Denies the create command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-create" + }, + { + "description": "Denies the current_monitor command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-current-monitor" + }, + { + "description": "Denies the cursor_position command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-cursor-position" + }, + { + "description": "Denies the destroy command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-destroy" + }, + { + "description": "Denies the get_all_windows command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-get-all-windows" + }, + { + "description": "Denies the hide command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-hide" + }, + { + "description": "Denies the inner_position command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-inner-position" + }, + { + "description": "Denies the inner_size command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-inner-size" + }, + { + "description": "Denies the internal_toggle_maximize command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-internal-toggle-maximize" + }, + { + "description": "Denies the is_always_on_top command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-always-on-top" + }, + { + "description": "Denies the is_closable command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-closable" + }, + { + "description": "Denies the is_decorated command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-decorated" + }, + { + "description": "Denies the is_enabled command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-enabled" + }, + { + "description": "Denies the is_focused command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-focused" + }, + { + "description": "Denies the is_fullscreen command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-fullscreen" + }, + { + "description": "Denies the is_maximizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-maximizable" + }, + { + "description": "Denies the is_maximized command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-maximized" + }, + { + "description": "Denies the is_minimizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-minimizable" + }, + { + "description": "Denies the is_minimized command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-minimized" + }, + { + "description": "Denies the is_resizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-resizable" + }, + { + "description": "Denies the is_visible command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-visible" + }, + { + "description": "Denies the maximize command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-maximize" + }, + { + "description": "Denies the minimize command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-minimize" + }, + { + "description": "Denies the monitor_from_point command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-monitor-from-point" + }, + { + "description": "Denies the outer_position command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-outer-position" + }, + { + "description": "Denies the outer_size command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-outer-size" + }, + { + "description": "Denies the primary_monitor command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-primary-monitor" + }, + { + "description": "Denies the request_user_attention command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-request-user-attention" + }, + { + "description": "Denies the scale_factor command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-scale-factor" + }, + { + "description": "Denies the set_always_on_bottom command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-always-on-bottom" + }, + { + "description": "Denies the set_always_on_top command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-always-on-top" + }, + { + "description": "Denies the set_background_color command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-background-color" + }, + { + "description": "Denies the set_badge_count command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-badge-count" + }, + { + "description": "Denies the set_badge_label command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-badge-label" + }, + { + "description": "Denies the set_closable command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-closable" + }, + { + "description": "Denies the set_content_protected command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-content-protected" + }, + { + "description": "Denies the set_cursor_grab command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-cursor-grab" + }, + { + "description": "Denies the set_cursor_icon command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-cursor-icon" + }, + { + "description": "Denies the set_cursor_position command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-cursor-position" + }, + { + "description": "Denies the set_cursor_visible command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-cursor-visible" + }, + { + "description": "Denies the set_decorations command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-decorations" + }, + { + "description": "Denies the set_effects command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-effects" + }, + { + "description": "Denies the set_enabled command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-enabled" + }, + { + "description": "Denies the set_focus command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-focus" + }, + { + "description": "Denies the set_fullscreen command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-fullscreen" + }, + { + "description": "Denies the set_icon command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-icon" + }, + { + "description": "Denies the set_ignore_cursor_events command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-ignore-cursor-events" + }, + { + "description": "Denies the set_max_size command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-max-size" + }, + { + "description": "Denies the set_maximizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-maximizable" + }, + { + "description": "Denies the set_min_size command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-min-size" + }, + { + "description": "Denies the set_minimizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-minimizable" + }, + { + "description": "Denies the set_overlay_icon command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-overlay-icon" + }, + { + "description": "Denies the set_position command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-position" + }, + { + "description": "Denies the set_progress_bar command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-progress-bar" + }, + { + "description": "Denies the set_resizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-resizable" + }, + { + "description": "Denies the set_shadow command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-shadow" + }, + { + "description": "Denies the set_size command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-size" + }, + { + "description": "Denies the set_size_constraints command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-size-constraints" + }, + { + "description": "Denies the set_skip_taskbar command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-skip-taskbar" + }, + { + "description": "Denies the set_theme command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-theme" + }, + { + "description": "Denies the set_title command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-title" + }, + { + "description": "Denies the set_title_bar_style command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-title-bar-style" + }, + { + "description": "Denies the set_visible_on_all_workspaces command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-visible-on-all-workspaces" + }, + { + "description": "Denies the show command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-show" + }, + { + "description": "Denies the start_dragging command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-start-dragging" + }, + { + "description": "Denies the start_resize_dragging command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-start-resize-dragging" + }, + { + "description": "Denies the theme command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-theme" + }, + { + "description": "Denies the title command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-title" + }, + { + "description": "Denies the toggle_maximize command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-toggle-maximize" + }, + { + "description": "Denies the unmaximize command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-unmaximize" + }, + { + "description": "Denies the unminimize command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-unminimize" + }, + { + "description": "This permission set configures which\nprocess feeatures are by default exposed.\n\n#### Granted Permissions\n\nThis enables to quit via `allow-exit` and restart via `allow-restart`\nthe application.\n", + "type": "string", + "const": "process:default" + }, + { + "description": "Enables the exit command without any pre-configured scope.", + "type": "string", + "const": "process:allow-exit" + }, + { + "description": "Enables the restart command without any pre-configured scope.", + "type": "string", + "const": "process:allow-restart" + }, + { + "description": "Denies the exit command without any pre-configured scope.", + "type": "string", + "const": "process:deny-exit" + }, + { + "description": "Denies the restart command without any pre-configured scope.", + "type": "string", + "const": "process:deny-restart" + }, + { + "description": "This permission set configures which\nshell functionality is exposed by default.\n\n#### Granted Permissions\n\nIt allows to use the `open` functionality without any specific\nscope pre-configured. It will allow opening `http(s)://`,\n`tel:` and `mailto:` links.\n", + "type": "string", + "const": "shell:default" + }, + { + "description": "Enables the execute command without any pre-configured scope.", + "type": "string", + "const": "shell:allow-execute" + }, + { + "description": "Enables the kill command without any pre-configured scope.", + "type": "string", + "const": "shell:allow-kill" + }, + { + "description": "Enables the open command without any pre-configured scope.", + "type": "string", + "const": "shell:allow-open" + }, + { + "description": "Enables the spawn command without any pre-configured scope.", + "type": "string", + "const": "shell:allow-spawn" + }, + { + "description": "Enables the stdin_write command without any pre-configured scope.", + "type": "string", + "const": "shell:allow-stdin-write" + }, + { + "description": "Denies the execute command without any pre-configured scope.", + "type": "string", + "const": "shell:deny-execute" + }, + { + "description": "Denies the kill command without any pre-configured scope.", + "type": "string", + "const": "shell:deny-kill" + }, + { + "description": "Denies the open command without any pre-configured scope.", + "type": "string", + "const": "shell:deny-open" + }, + { + "description": "Denies the spawn command without any pre-configured scope.", + "type": "string", + "const": "shell:deny-spawn" + }, + { + "description": "Denies the stdin_write command without any pre-configured scope.", + "type": "string", + "const": "shell:deny-stdin-write" + }, + { + "description": "This permission set configures which kind of\nupdater functions are exposed to the frontend.\n\n#### Granted Permissions\n\nThe full workflow from checking for updates to installing them\nis enabled.\n\n", + "type": "string", + "const": "updater:default" + }, + { + "description": "Enables the check command without any pre-configured scope.", + "type": "string", + "const": "updater:allow-check" + }, + { + "description": "Enables the download command without any pre-configured scope.", + "type": "string", + "const": "updater:allow-download" + }, + { + "description": "Enables the download_and_install command without any pre-configured scope.", + "type": "string", + "const": "updater:allow-download-and-install" + }, + { + "description": "Enables the install command without any pre-configured scope.", + "type": "string", + "const": "updater:allow-install" + }, + { + "description": "Denies the check command without any pre-configured scope.", + "type": "string", + "const": "updater:deny-check" + }, + { + "description": "Denies the download command without any pre-configured scope.", + "type": "string", + "const": "updater:deny-download" + }, + { + "description": "Denies the download_and_install command without any pre-configured scope.", + "type": "string", + "const": "updater:deny-download-and-install" + }, + { + "description": "Denies the install command without any pre-configured scope.", + "type": "string", + "const": "updater:deny-install" + } + ] + }, + "Value": { + "description": "All supported ACL values.", + "anyOf": [ + { + "description": "Represents a null JSON value.", + "type": "null" + }, + { + "description": "Represents a [`bool`].", + "type": "boolean" + }, + { + "description": "Represents a valid ACL [`Number`].", + "allOf": [ + { + "$ref": "#/definitions/Number" + } + ] + }, + { + "description": "Represents a [`String`].", + "type": "string" + }, + { + "description": "Represents a list of other [`Value`]s.", + "type": "array", + "items": { + "$ref": "#/definitions/Value" + } + }, + { + "description": "Represents a map of [`String`] keys to [`Value`]s.", + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/Value" + } + } + ] + }, + "Number": { + "description": "A valid ACL number.", + "anyOf": [ + { + "description": "Represents an [`i64`].", + "type": "integer", + "format": "int64" + }, + { + "description": "Represents a [`f64`].", + "type": "number", + "format": "double" + } + ] + }, + "Target": { + "description": "Platform target.", + "oneOf": [ + { + "description": "MacOS.", + "type": "string", + "enum": [ + "macOS" + ] + }, + { + "description": "Windows.", + "type": "string", + "enum": [ + "windows" + ] + }, + { + "description": "Linux.", + "type": "string", + "enum": [ + "linux" + ] + }, + { + "description": "Android.", + "type": "string", + "enum": [ + "android" + ] + }, + { + "description": "iOS.", + "type": "string", + "enum": [ + "iOS" + ] + } + ] + }, + "ShellScopeEntryAllowedArg": { + "description": "A command argument allowed to be executed by the webview API.", + "anyOf": [ + { + "description": "A non-configurable argument that is passed to the command in the order it was specified.", + "type": "string" + }, + { + "description": "A variable that is set while calling the command from the webview API.", + "type": "object", + "required": [ + "validator" + ], + "properties": { + "raw": { + "description": "Marks the validator as a raw regex, meaning the plugin should not make any modification at runtime.\n\nThis means the regex will not match on the entire string by default, which might be exploited if your regex allow unexpected input to be considered valid. When using this option, make sure your regex is correct.", + "default": false, + "type": "boolean" + }, + "validator": { + "description": "[regex] validator to require passed values to conform to an expected input.\n\nThis will require the argument value passed to this variable to match the `validator` regex before it will be executed.\n\nThe regex string is by default surrounded by `^...$` to match the full string. For example the `https?://\\w+` regex would be registered as `^https?://\\w+$`.\n\n[regex]: ", + "type": "string" + } + }, + "additionalProperties": false + } + ] + }, + "ShellScopeEntryAllowedArgs": { + "description": "A set of command arguments allowed to be executed by the webview API.\n\nA value of `true` will allow any arguments to be passed to the command. `false` will disable all arguments. A list of [`ShellScopeEntryAllowedArg`] will set those arguments as the only valid arguments to be passed to the attached command configuration.", + "anyOf": [ + { + "description": "Use a simple boolean to allow all or disable all arguments to this command configuration.", + "type": "boolean" + }, + { + "description": "A specific set of [`ShellScopeEntryAllowedArg`] that are valid to call for the command configuration.", + "type": "array", + "items": { + "$ref": "#/definitions/ShellScopeEntryAllowedArg" + } + } + ] + } + } +} \ No newline at end of file diff --git a/nym-wallet/src-tauri/gen/schemas/macOS-schema.json b/nym-wallet/src-tauri/gen/schemas/macOS-schema.json new file mode 100644 index 0000000000..5ff69983d7 --- /dev/null +++ b/nym-wallet/src-tauri/gen/schemas/macOS-schema.json @@ -0,0 +1,2279 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "CapabilityFile", + "description": "Capability formats accepted in a capability file.", + "anyOf": [ + { + "description": "A single capability.", + "allOf": [ + { + "$ref": "#/definitions/Capability" + } + ] + }, + { + "description": "A list of capabilities.", + "type": "array", + "items": { + "$ref": "#/definitions/Capability" + } + }, + { + "description": "A list of capabilities.", + "type": "object", + "required": [ + "capabilities" + ], + "properties": { + "capabilities": { + "description": "The list of capabilities.", + "type": "array", + "items": { + "$ref": "#/definitions/Capability" + } + } + } + } + ], + "definitions": { + "Capability": { + "description": "A grouping and boundary mechanism developers can use to isolate access to the IPC layer.\n\nIt controls application windows' and webviews' fine grained access to the Tauri core, application, or plugin commands. If a webview or its window is not matching any capability then it has no access to the IPC layer at all.\n\nThis can be done to create groups of windows, based on their required system access, which can reduce impact of frontend vulnerabilities in less privileged windows. Windows can be added to a capability by exact name (e.g. `main-window`) or glob patterns like `*` or `admin-*`. A Window can have none, one, or multiple associated capabilities.\n\n## Example\n\n```json { \"identifier\": \"main-user-files-write\", \"description\": \"This capability allows the `main` window on macOS and Windows access to `filesystem` write related commands and `dialog` commands to enable programatic access to files selected by the user.\", \"windows\": [ \"main\" ], \"permissions\": [ \"core:default\", \"dialog:open\", { \"identifier\": \"fs:allow-write-text-file\", \"allow\": [{ \"path\": \"$HOME/test.txt\" }] }, ], \"platforms\": [\"macOS\",\"windows\"] } ```", + "type": "object", + "required": [ + "identifier", + "permissions" + ], + "properties": { + "identifier": { + "description": "Identifier of the capability.\n\n## Example\n\n`main-user-files-write`", + "type": "string" + }, + "description": { + "description": "Description of what the capability is intended to allow on associated windows.\n\nIt should contain a description of what the grouped permissions should allow.\n\n## Example\n\nThis capability allows the `main` window access to `filesystem` write related commands and `dialog` commands to enable programatic access to files selected by the user.", + "default": "", + "type": "string" + }, + "remote": { + "description": "Configure remote URLs that can use the capability permissions.\n\nThis setting is optional and defaults to not being set, as our default use case is that the content is served from our local application.\n\n:::caution Make sure you understand the security implications of providing remote sources with local system access. :::\n\n## Example\n\n```json { \"urls\": [\"https://*.mydomain.dev\"] } ```", + "anyOf": [ + { + "$ref": "#/definitions/CapabilityRemote" + }, + { + "type": "null" + } + ] + }, + "local": { + "description": "Whether this capability is enabled for local app URLs or not. Defaults to `true`.", + "default": true, + "type": "boolean" + }, + "windows": { + "description": "List of windows that are affected by this capability. Can be a glob pattern.\n\nIf a window label matches any of the patterns in this list, the capability will be enabled on all the webviews of that window, regardless of the value of [`Self::webviews`].\n\nOn multiwebview windows, prefer specifying [`Self::webviews`] and omitting [`Self::windows`] for a fine grained access control.\n\n## Example\n\n`[\"main\"]`", + "type": "array", + "items": { + "type": "string" + } + }, + "webviews": { + "description": "List of webviews that are affected by this capability. Can be a glob pattern.\n\nThe capability will be enabled on all the webviews whose label matches any of the patterns in this list, regardless of whether the webview's window label matches a pattern in [`Self::windows`].\n\n## Example\n\n`[\"sub-webview-one\", \"sub-webview-two\"]`", + "type": "array", + "items": { + "type": "string" + } + }, + "permissions": { + "description": "List of permissions attached to this capability.\n\nMust include the plugin name as prefix in the form of `${plugin-name}:${permission-name}`. For commands directly implemented in the application itself only `${permission-name}` is required.\n\n## Example\n\n```json [ \"core:default\", \"shell:allow-open\", \"dialog:open\", { \"identifier\": \"fs:allow-write-text-file\", \"allow\": [{ \"path\": \"$HOME/test.txt\" }] } ] ```", + "type": "array", + "items": { + "$ref": "#/definitions/PermissionEntry" + }, + "uniqueItems": true + }, + "platforms": { + "description": "Limit which target platforms this capability applies to.\n\nBy default all platforms are targeted.\n\n## Example\n\n`[\"macOS\",\"windows\"]`", + "type": [ + "array", + "null" + ], + "items": { + "$ref": "#/definitions/Target" + } + } + } + }, + "CapabilityRemote": { + "description": "Configuration for remote URLs that are associated with the capability.", + "type": "object", + "required": [ + "urls" + ], + "properties": { + "urls": { + "description": "Remote domains this capability refers to using the [URLPattern standard](https://urlpattern.spec.whatwg.org/).\n\n## Examples\n\n- \"https://*.mydomain.dev\": allows subdomains of mydomain.dev - \"https://mydomain.dev/api/*\": allows any subpath of mydomain.dev/api", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "PermissionEntry": { + "description": "An entry for a permission value in a [`Capability`] can be either a raw permission [`Identifier`] or an object that references a permission and extends its scope.", + "anyOf": [ + { + "description": "Reference a permission or permission set by identifier.", + "allOf": [ + { + "$ref": "#/definitions/Identifier" + } + ] + }, + { + "description": "Reference a permission or permission set by identifier and extends its scope.", + "type": "object", + "allOf": [ + { + "if": { + "properties": { + "identifier": { + "anyOf": [ + { + "description": "This permission set configures which\nshell functionality is exposed by default.\n\n#### Granted Permissions\n\nIt allows to use the `open` functionality without any specific\nscope pre-configured. It will allow opening `http(s)://`,\n`tel:` and `mailto:` links.\n", + "type": "string", + "const": "shell:default" + }, + { + "description": "Enables the execute command without any pre-configured scope.", + "type": "string", + "const": "shell:allow-execute" + }, + { + "description": "Enables the kill command without any pre-configured scope.", + "type": "string", + "const": "shell:allow-kill" + }, + { + "description": "Enables the open command without any pre-configured scope.", + "type": "string", + "const": "shell:allow-open" + }, + { + "description": "Enables the spawn command without any pre-configured scope.", + "type": "string", + "const": "shell:allow-spawn" + }, + { + "description": "Enables the stdin_write command without any pre-configured scope.", + "type": "string", + "const": "shell:allow-stdin-write" + }, + { + "description": "Denies the execute command without any pre-configured scope.", + "type": "string", + "const": "shell:deny-execute" + }, + { + "description": "Denies the kill command without any pre-configured scope.", + "type": "string", + "const": "shell:deny-kill" + }, + { + "description": "Denies the open command without any pre-configured scope.", + "type": "string", + "const": "shell:deny-open" + }, + { + "description": "Denies the spawn command without any pre-configured scope.", + "type": "string", + "const": "shell:deny-spawn" + }, + { + "description": "Denies the stdin_write command without any pre-configured scope.", + "type": "string", + "const": "shell:deny-stdin-write" + } + ] + } + } + }, + "then": { + "properties": { + "allow": { + "items": { + "title": "ShellScopeEntry", + "description": "Shell scope entry.", + "anyOf": [ + { + "type": "object", + "required": [ + "cmd", + "name" + ], + "properties": { + "args": { + "description": "The allowed arguments for the command execution.", + "allOf": [ + { + "$ref": "#/definitions/ShellScopeEntryAllowedArgs" + } + ] + }, + "cmd": { + "description": "The command name. It can start with a variable that resolves to a system base directory. The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`, `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`, `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`, `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.", + "type": "string" + }, + "name": { + "description": "The name for this allowed shell command configuration.\n\nThis name will be used inside of the webview API to call this command along with any specified arguments.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "name", + "sidecar" + ], + "properties": { + "args": { + "description": "The allowed arguments for the command execution.", + "allOf": [ + { + "$ref": "#/definitions/ShellScopeEntryAllowedArgs" + } + ] + }, + "name": { + "description": "The name for this allowed shell command configuration.\n\nThis name will be used inside of the webview API to call this command along with any specified arguments.", + "type": "string" + }, + "sidecar": { + "description": "If this command is a sidecar command.", + "type": "boolean" + } + }, + "additionalProperties": false + } + ] + } + }, + "deny": { + "items": { + "title": "ShellScopeEntry", + "description": "Shell scope entry.", + "anyOf": [ + { + "type": "object", + "required": [ + "cmd", + "name" + ], + "properties": { + "args": { + "description": "The allowed arguments for the command execution.", + "allOf": [ + { + "$ref": "#/definitions/ShellScopeEntryAllowedArgs" + } + ] + }, + "cmd": { + "description": "The command name. It can start with a variable that resolves to a system base directory. The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`, `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`, `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`, `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.", + "type": "string" + }, + "name": { + "description": "The name for this allowed shell command configuration.\n\nThis name will be used inside of the webview API to call this command along with any specified arguments.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "name", + "sidecar" + ], + "properties": { + "args": { + "description": "The allowed arguments for the command execution.", + "allOf": [ + { + "$ref": "#/definitions/ShellScopeEntryAllowedArgs" + } + ] + }, + "name": { + "description": "The name for this allowed shell command configuration.\n\nThis name will be used inside of the webview API to call this command along with any specified arguments.", + "type": "string" + }, + "sidecar": { + "description": "If this command is a sidecar command.", + "type": "boolean" + } + }, + "additionalProperties": false + } + ] + } + } + } + }, + "properties": { + "identifier": { + "description": "Identifier of the permission or permission set.", + "allOf": [ + { + "$ref": "#/definitions/Identifier" + } + ] + } + } + }, + { + "properties": { + "identifier": { + "description": "Identifier of the permission or permission set.", + "allOf": [ + { + "$ref": "#/definitions/Identifier" + } + ] + }, + "allow": { + "description": "Data that defines what is allowed by the scope.", + "type": [ + "array", + "null" + ], + "items": { + "$ref": "#/definitions/Value" + } + }, + "deny": { + "description": "Data that defines what is denied by the scope. This should be prioritized by validation logic.", + "type": [ + "array", + "null" + ], + "items": { + "$ref": "#/definitions/Value" + } + } + } + } + ], + "required": [ + "identifier" + ] + } + ] + }, + "Identifier": { + "description": "Permission identifier", + "oneOf": [ + { + "description": "No features are enabled by default, as we believe\nthe clipboard can be inherently dangerous and it is \napplication specific if read and/or write access is needed.\n\nClipboard interaction needs to be explicitly enabled.\n", + "type": "string", + "const": "clipboard-manager:default" + }, + { + "description": "Enables the clear command without any pre-configured scope.", + "type": "string", + "const": "clipboard-manager:allow-clear" + }, + { + "description": "Enables the read_image command without any pre-configured scope.", + "type": "string", + "const": "clipboard-manager:allow-read-image" + }, + { + "description": "Enables the read_text command without any pre-configured scope.", + "type": "string", + "const": "clipboard-manager:allow-read-text" + }, + { + "description": "Enables the write_html command without any pre-configured scope.", + "type": "string", + "const": "clipboard-manager:allow-write-html" + }, + { + "description": "Enables the write_image command without any pre-configured scope.", + "type": "string", + "const": "clipboard-manager:allow-write-image" + }, + { + "description": "Enables the write_text command without any pre-configured scope.", + "type": "string", + "const": "clipboard-manager:allow-write-text" + }, + { + "description": "Denies the clear command without any pre-configured scope.", + "type": "string", + "const": "clipboard-manager:deny-clear" + }, + { + "description": "Denies the read_image command without any pre-configured scope.", + "type": "string", + "const": "clipboard-manager:deny-read-image" + }, + { + "description": "Denies the read_text command without any pre-configured scope.", + "type": "string", + "const": "clipboard-manager:deny-read-text" + }, + { + "description": "Denies the write_html command without any pre-configured scope.", + "type": "string", + "const": "clipboard-manager:deny-write-html" + }, + { + "description": "Denies the write_image command without any pre-configured scope.", + "type": "string", + "const": "clipboard-manager:deny-write-image" + }, + { + "description": "Denies the write_text command without any pre-configured scope.", + "type": "string", + "const": "clipboard-manager:deny-write-text" + }, + { + "description": "Default core plugins set which includes:\n- 'core:path:default'\n- 'core:event:default'\n- 'core:window:default'\n- 'core:webview:default'\n- 'core:app:default'\n- 'core:image:default'\n- 'core:resources:default'\n- 'core:menu:default'\n- 'core:tray:default'\n", + "type": "string", + "const": "core:default" + }, + { + "description": "Default permissions for the plugin.", + "type": "string", + "const": "core:app:default" + }, + { + "description": "Enables the app_hide command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-app-hide" + }, + { + "description": "Enables the app_show command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-app-show" + }, + { + "description": "Enables the default_window_icon command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-default-window-icon" + }, + { + "description": "Enables the fetch_data_store_identifiers command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-fetch-data-store-identifiers" + }, + { + "description": "Enables the identifier command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-identifier" + }, + { + "description": "Enables the name command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-name" + }, + { + "description": "Enables the remove_data_store command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-remove-data-store" + }, + { + "description": "Enables the set_app_theme command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-set-app-theme" + }, + { + "description": "Enables the tauri_version command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-tauri-version" + }, + { + "description": "Enables the version command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-version" + }, + { + "description": "Denies the app_hide command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-app-hide" + }, + { + "description": "Denies the app_show command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-app-show" + }, + { + "description": "Denies the default_window_icon command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-default-window-icon" + }, + { + "description": "Denies the fetch_data_store_identifiers command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-fetch-data-store-identifiers" + }, + { + "description": "Denies the identifier command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-identifier" + }, + { + "description": "Denies the name command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-name" + }, + { + "description": "Denies the remove_data_store command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-remove-data-store" + }, + { + "description": "Denies the set_app_theme command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-set-app-theme" + }, + { + "description": "Denies the tauri_version command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-tauri-version" + }, + { + "description": "Denies the version command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-version" + }, + { + "description": "Default permissions for the plugin.", + "type": "string", + "const": "core:event:default" + }, + { + "description": "Enables the emit command without any pre-configured scope.", + "type": "string", + "const": "core:event:allow-emit" + }, + { + "description": "Enables the emit_to command without any pre-configured scope.", + "type": "string", + "const": "core:event:allow-emit-to" + }, + { + "description": "Enables the listen command without any pre-configured scope.", + "type": "string", + "const": "core:event:allow-listen" + }, + { + "description": "Enables the unlisten command without any pre-configured scope.", + "type": "string", + "const": "core:event:allow-unlisten" + }, + { + "description": "Denies the emit command without any pre-configured scope.", + "type": "string", + "const": "core:event:deny-emit" + }, + { + "description": "Denies the emit_to command without any pre-configured scope.", + "type": "string", + "const": "core:event:deny-emit-to" + }, + { + "description": "Denies the listen command without any pre-configured scope.", + "type": "string", + "const": "core:event:deny-listen" + }, + { + "description": "Denies the unlisten command without any pre-configured scope.", + "type": "string", + "const": "core:event:deny-unlisten" + }, + { + "description": "Default permissions for the plugin.", + "type": "string", + "const": "core:image:default" + }, + { + "description": "Enables the from_bytes command without any pre-configured scope.", + "type": "string", + "const": "core:image:allow-from-bytes" + }, + { + "description": "Enables the from_path command without any pre-configured scope.", + "type": "string", + "const": "core:image:allow-from-path" + }, + { + "description": "Enables the new command without any pre-configured scope.", + "type": "string", + "const": "core:image:allow-new" + }, + { + "description": "Enables the rgba command without any pre-configured scope.", + "type": "string", + "const": "core:image:allow-rgba" + }, + { + "description": "Enables the size command without any pre-configured scope.", + "type": "string", + "const": "core:image:allow-size" + }, + { + "description": "Denies the from_bytes command without any pre-configured scope.", + "type": "string", + "const": "core:image:deny-from-bytes" + }, + { + "description": "Denies the from_path command without any pre-configured scope.", + "type": "string", + "const": "core:image:deny-from-path" + }, + { + "description": "Denies the new command without any pre-configured scope.", + "type": "string", + "const": "core:image:deny-new" + }, + { + "description": "Denies the rgba command without any pre-configured scope.", + "type": "string", + "const": "core:image:deny-rgba" + }, + { + "description": "Denies the size command without any pre-configured scope.", + "type": "string", + "const": "core:image:deny-size" + }, + { + "description": "Default permissions for the plugin.", + "type": "string", + "const": "core:menu:default" + }, + { + "description": "Enables the append command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-append" + }, + { + "description": "Enables the create_default command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-create-default" + }, + { + "description": "Enables the get command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-get" + }, + { + "description": "Enables the insert command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-insert" + }, + { + "description": "Enables the is_checked command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-is-checked" + }, + { + "description": "Enables the is_enabled command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-is-enabled" + }, + { + "description": "Enables the items command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-items" + }, + { + "description": "Enables the new command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-new" + }, + { + "description": "Enables the popup command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-popup" + }, + { + "description": "Enables the prepend command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-prepend" + }, + { + "description": "Enables the remove command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-remove" + }, + { + "description": "Enables the remove_at command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-remove-at" + }, + { + "description": "Enables the set_accelerator command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-set-accelerator" + }, + { + "description": "Enables the set_as_app_menu command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-set-as-app-menu" + }, + { + "description": "Enables the set_as_help_menu_for_nsapp command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-set-as-help-menu-for-nsapp" + }, + { + "description": "Enables the set_as_window_menu command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-set-as-window-menu" + }, + { + "description": "Enables the set_as_windows_menu_for_nsapp command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-set-as-windows-menu-for-nsapp" + }, + { + "description": "Enables the set_checked command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-set-checked" + }, + { + "description": "Enables the set_enabled command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-set-enabled" + }, + { + "description": "Enables the set_icon command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-set-icon" + }, + { + "description": "Enables the set_text command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-set-text" + }, + { + "description": "Enables the text command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-text" + }, + { + "description": "Denies the append command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-append" + }, + { + "description": "Denies the create_default command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-create-default" + }, + { + "description": "Denies the get command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-get" + }, + { + "description": "Denies the insert command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-insert" + }, + { + "description": "Denies the is_checked command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-is-checked" + }, + { + "description": "Denies the is_enabled command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-is-enabled" + }, + { + "description": "Denies the items command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-items" + }, + { + "description": "Denies the new command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-new" + }, + { + "description": "Denies the popup command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-popup" + }, + { + "description": "Denies the prepend command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-prepend" + }, + { + "description": "Denies the remove command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-remove" + }, + { + "description": "Denies the remove_at command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-remove-at" + }, + { + "description": "Denies the set_accelerator command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-set-accelerator" + }, + { + "description": "Denies the set_as_app_menu command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-set-as-app-menu" + }, + { + "description": "Denies the set_as_help_menu_for_nsapp command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-set-as-help-menu-for-nsapp" + }, + { + "description": "Denies the set_as_window_menu command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-set-as-window-menu" + }, + { + "description": "Denies the set_as_windows_menu_for_nsapp command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-set-as-windows-menu-for-nsapp" + }, + { + "description": "Denies the set_checked command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-set-checked" + }, + { + "description": "Denies the set_enabled command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-set-enabled" + }, + { + "description": "Denies the set_icon command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-set-icon" + }, + { + "description": "Denies the set_text command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-set-text" + }, + { + "description": "Denies the text command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-text" + }, + { + "description": "Default permissions for the plugin.", + "type": "string", + "const": "core:path:default" + }, + { + "description": "Enables the basename command without any pre-configured scope.", + "type": "string", + "const": "core:path:allow-basename" + }, + { + "description": "Enables the dirname command without any pre-configured scope.", + "type": "string", + "const": "core:path:allow-dirname" + }, + { + "description": "Enables the extname command without any pre-configured scope.", + "type": "string", + "const": "core:path:allow-extname" + }, + { + "description": "Enables the is_absolute command without any pre-configured scope.", + "type": "string", + "const": "core:path:allow-is-absolute" + }, + { + "description": "Enables the join command without any pre-configured scope.", + "type": "string", + "const": "core:path:allow-join" + }, + { + "description": "Enables the normalize command without any pre-configured scope.", + "type": "string", + "const": "core:path:allow-normalize" + }, + { + "description": "Enables the resolve command without any pre-configured scope.", + "type": "string", + "const": "core:path:allow-resolve" + }, + { + "description": "Enables the resolve_directory command without any pre-configured scope.", + "type": "string", + "const": "core:path:allow-resolve-directory" + }, + { + "description": "Denies the basename command without any pre-configured scope.", + "type": "string", + "const": "core:path:deny-basename" + }, + { + "description": "Denies the dirname command without any pre-configured scope.", + "type": "string", + "const": "core:path:deny-dirname" + }, + { + "description": "Denies the extname command without any pre-configured scope.", + "type": "string", + "const": "core:path:deny-extname" + }, + { + "description": "Denies the is_absolute command without any pre-configured scope.", + "type": "string", + "const": "core:path:deny-is-absolute" + }, + { + "description": "Denies the join command without any pre-configured scope.", + "type": "string", + "const": "core:path:deny-join" + }, + { + "description": "Denies the normalize command without any pre-configured scope.", + "type": "string", + "const": "core:path:deny-normalize" + }, + { + "description": "Denies the resolve command without any pre-configured scope.", + "type": "string", + "const": "core:path:deny-resolve" + }, + { + "description": "Denies the resolve_directory command without any pre-configured scope.", + "type": "string", + "const": "core:path:deny-resolve-directory" + }, + { + "description": "Default permissions for the plugin.", + "type": "string", + "const": "core:resources:default" + }, + { + "description": "Enables the close command without any pre-configured scope.", + "type": "string", + "const": "core:resources:allow-close" + }, + { + "description": "Denies the close command without any pre-configured scope.", + "type": "string", + "const": "core:resources:deny-close" + }, + { + "description": "Default permissions for the plugin.", + "type": "string", + "const": "core:tray:default" + }, + { + "description": "Enables the get_by_id command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-get-by-id" + }, + { + "description": "Enables the new command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-new" + }, + { + "description": "Enables the remove_by_id command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-remove-by-id" + }, + { + "description": "Enables the set_icon command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-set-icon" + }, + { + "description": "Enables the set_icon_as_template command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-set-icon-as-template" + }, + { + "description": "Enables the set_menu command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-set-menu" + }, + { + "description": "Enables the set_show_menu_on_left_click command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-set-show-menu-on-left-click" + }, + { + "description": "Enables the set_temp_dir_path command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-set-temp-dir-path" + }, + { + "description": "Enables the set_title command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-set-title" + }, + { + "description": "Enables the set_tooltip command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-set-tooltip" + }, + { + "description": "Enables the set_visible command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-set-visible" + }, + { + "description": "Denies the get_by_id command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-get-by-id" + }, + { + "description": "Denies the new command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-new" + }, + { + "description": "Denies the remove_by_id command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-remove-by-id" + }, + { + "description": "Denies the set_icon command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-set-icon" + }, + { + "description": "Denies the set_icon_as_template command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-set-icon-as-template" + }, + { + "description": "Denies the set_menu command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-set-menu" + }, + { + "description": "Denies the set_show_menu_on_left_click command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-set-show-menu-on-left-click" + }, + { + "description": "Denies the set_temp_dir_path command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-set-temp-dir-path" + }, + { + "description": "Denies the set_title command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-set-title" + }, + { + "description": "Denies the set_tooltip command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-set-tooltip" + }, + { + "description": "Denies the set_visible command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-set-visible" + }, + { + "description": "Default permissions for the plugin.", + "type": "string", + "const": "core:webview:default" + }, + { + "description": "Enables the clear_all_browsing_data command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-clear-all-browsing-data" + }, + { + "description": "Enables the create_webview command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-create-webview" + }, + { + "description": "Enables the create_webview_window command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-create-webview-window" + }, + { + "description": "Enables the get_all_webviews command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-get-all-webviews" + }, + { + "description": "Enables the internal_toggle_devtools command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-internal-toggle-devtools" + }, + { + "description": "Enables the print command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-print" + }, + { + "description": "Enables the reparent command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-reparent" + }, + { + "description": "Enables the set_webview_background_color command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-set-webview-background-color" + }, + { + "description": "Enables the set_webview_focus command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-set-webview-focus" + }, + { + "description": "Enables the set_webview_position command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-set-webview-position" + }, + { + "description": "Enables the set_webview_size command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-set-webview-size" + }, + { + "description": "Enables the set_webview_zoom command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-set-webview-zoom" + }, + { + "description": "Enables the webview_close command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-webview-close" + }, + { + "description": "Enables the webview_hide command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-webview-hide" + }, + { + "description": "Enables the webview_position command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-webview-position" + }, + { + "description": "Enables the webview_show command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-webview-show" + }, + { + "description": "Enables the webview_size command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-webview-size" + }, + { + "description": "Denies the clear_all_browsing_data command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-clear-all-browsing-data" + }, + { + "description": "Denies the create_webview command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-create-webview" + }, + { + "description": "Denies the create_webview_window command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-create-webview-window" + }, + { + "description": "Denies the get_all_webviews command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-get-all-webviews" + }, + { + "description": "Denies the internal_toggle_devtools command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-internal-toggle-devtools" + }, + { + "description": "Denies the print command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-print" + }, + { + "description": "Denies the reparent command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-reparent" + }, + { + "description": "Denies the set_webview_background_color command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-set-webview-background-color" + }, + { + "description": "Denies the set_webview_focus command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-set-webview-focus" + }, + { + "description": "Denies the set_webview_position command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-set-webview-position" + }, + { + "description": "Denies the set_webview_size command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-set-webview-size" + }, + { + "description": "Denies the set_webview_zoom command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-set-webview-zoom" + }, + { + "description": "Denies the webview_close command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-webview-close" + }, + { + "description": "Denies the webview_hide command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-webview-hide" + }, + { + "description": "Denies the webview_position command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-webview-position" + }, + { + "description": "Denies the webview_show command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-webview-show" + }, + { + "description": "Denies the webview_size command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-webview-size" + }, + { + "description": "Default permissions for the plugin.", + "type": "string", + "const": "core:window:default" + }, + { + "description": "Enables the available_monitors command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-available-monitors" + }, + { + "description": "Enables the center command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-center" + }, + { + "description": "Enables the close command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-close" + }, + { + "description": "Enables the create command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-create" + }, + { + "description": "Enables the current_monitor command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-current-monitor" + }, + { + "description": "Enables the cursor_position command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-cursor-position" + }, + { + "description": "Enables the destroy command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-destroy" + }, + { + "description": "Enables the get_all_windows command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-get-all-windows" + }, + { + "description": "Enables the hide command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-hide" + }, + { + "description": "Enables the inner_position command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-inner-position" + }, + { + "description": "Enables the inner_size command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-inner-size" + }, + { + "description": "Enables the internal_toggle_maximize command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-internal-toggle-maximize" + }, + { + "description": "Enables the is_always_on_top command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-always-on-top" + }, + { + "description": "Enables the is_closable command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-closable" + }, + { + "description": "Enables the is_decorated command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-decorated" + }, + { + "description": "Enables the is_enabled command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-enabled" + }, + { + "description": "Enables the is_focused command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-focused" + }, + { + "description": "Enables the is_fullscreen command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-fullscreen" + }, + { + "description": "Enables the is_maximizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-maximizable" + }, + { + "description": "Enables the is_maximized command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-maximized" + }, + { + "description": "Enables the is_minimizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-minimizable" + }, + { + "description": "Enables the is_minimized command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-minimized" + }, + { + "description": "Enables the is_resizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-resizable" + }, + { + "description": "Enables the is_visible command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-visible" + }, + { + "description": "Enables the maximize command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-maximize" + }, + { + "description": "Enables the minimize command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-minimize" + }, + { + "description": "Enables the monitor_from_point command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-monitor-from-point" + }, + { + "description": "Enables the outer_position command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-outer-position" + }, + { + "description": "Enables the outer_size command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-outer-size" + }, + { + "description": "Enables the primary_monitor command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-primary-monitor" + }, + { + "description": "Enables the request_user_attention command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-request-user-attention" + }, + { + "description": "Enables the scale_factor command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-scale-factor" + }, + { + "description": "Enables the set_always_on_bottom command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-always-on-bottom" + }, + { + "description": "Enables the set_always_on_top command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-always-on-top" + }, + { + "description": "Enables the set_background_color command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-background-color" + }, + { + "description": "Enables the set_badge_count command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-badge-count" + }, + { + "description": "Enables the set_badge_label command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-badge-label" + }, + { + "description": "Enables the set_closable command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-closable" + }, + { + "description": "Enables the set_content_protected command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-content-protected" + }, + { + "description": "Enables the set_cursor_grab command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-cursor-grab" + }, + { + "description": "Enables the set_cursor_icon command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-cursor-icon" + }, + { + "description": "Enables the set_cursor_position command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-cursor-position" + }, + { + "description": "Enables the set_cursor_visible command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-cursor-visible" + }, + { + "description": "Enables the set_decorations command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-decorations" + }, + { + "description": "Enables the set_effects command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-effects" + }, + { + "description": "Enables the set_enabled command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-enabled" + }, + { + "description": "Enables the set_focus command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-focus" + }, + { + "description": "Enables the set_fullscreen command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-fullscreen" + }, + { + "description": "Enables the set_icon command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-icon" + }, + { + "description": "Enables the set_ignore_cursor_events command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-ignore-cursor-events" + }, + { + "description": "Enables the set_max_size command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-max-size" + }, + { + "description": "Enables the set_maximizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-maximizable" + }, + { + "description": "Enables the set_min_size command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-min-size" + }, + { + "description": "Enables the set_minimizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-minimizable" + }, + { + "description": "Enables the set_overlay_icon command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-overlay-icon" + }, + { + "description": "Enables the set_position command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-position" + }, + { + "description": "Enables the set_progress_bar command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-progress-bar" + }, + { + "description": "Enables the set_resizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-resizable" + }, + { + "description": "Enables the set_shadow command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-shadow" + }, + { + "description": "Enables the set_size command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-size" + }, + { + "description": "Enables the set_size_constraints command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-size-constraints" + }, + { + "description": "Enables the set_skip_taskbar command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-skip-taskbar" + }, + { + "description": "Enables the set_theme command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-theme" + }, + { + "description": "Enables the set_title command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-title" + }, + { + "description": "Enables the set_title_bar_style command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-title-bar-style" + }, + { + "description": "Enables the set_visible_on_all_workspaces command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-visible-on-all-workspaces" + }, + { + "description": "Enables the show command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-show" + }, + { + "description": "Enables the start_dragging command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-start-dragging" + }, + { + "description": "Enables the start_resize_dragging command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-start-resize-dragging" + }, + { + "description": "Enables the theme command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-theme" + }, + { + "description": "Enables the title command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-title" + }, + { + "description": "Enables the toggle_maximize command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-toggle-maximize" + }, + { + "description": "Enables the unmaximize command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-unmaximize" + }, + { + "description": "Enables the unminimize command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-unminimize" + }, + { + "description": "Denies the available_monitors command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-available-monitors" + }, + { + "description": "Denies the center command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-center" + }, + { + "description": "Denies the close command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-close" + }, + { + "description": "Denies the create command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-create" + }, + { + "description": "Denies the current_monitor command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-current-monitor" + }, + { + "description": "Denies the cursor_position command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-cursor-position" + }, + { + "description": "Denies the destroy command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-destroy" + }, + { + "description": "Denies the get_all_windows command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-get-all-windows" + }, + { + "description": "Denies the hide command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-hide" + }, + { + "description": "Denies the inner_position command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-inner-position" + }, + { + "description": "Denies the inner_size command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-inner-size" + }, + { + "description": "Denies the internal_toggle_maximize command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-internal-toggle-maximize" + }, + { + "description": "Denies the is_always_on_top command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-always-on-top" + }, + { + "description": "Denies the is_closable command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-closable" + }, + { + "description": "Denies the is_decorated command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-decorated" + }, + { + "description": "Denies the is_enabled command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-enabled" + }, + { + "description": "Denies the is_focused command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-focused" + }, + { + "description": "Denies the is_fullscreen command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-fullscreen" + }, + { + "description": "Denies the is_maximizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-maximizable" + }, + { + "description": "Denies the is_maximized command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-maximized" + }, + { + "description": "Denies the is_minimizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-minimizable" + }, + { + "description": "Denies the is_minimized command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-minimized" + }, + { + "description": "Denies the is_resizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-resizable" + }, + { + "description": "Denies the is_visible command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-visible" + }, + { + "description": "Denies the maximize command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-maximize" + }, + { + "description": "Denies the minimize command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-minimize" + }, + { + "description": "Denies the monitor_from_point command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-monitor-from-point" + }, + { + "description": "Denies the outer_position command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-outer-position" + }, + { + "description": "Denies the outer_size command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-outer-size" + }, + { + "description": "Denies the primary_monitor command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-primary-monitor" + }, + { + "description": "Denies the request_user_attention command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-request-user-attention" + }, + { + "description": "Denies the scale_factor command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-scale-factor" + }, + { + "description": "Denies the set_always_on_bottom command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-always-on-bottom" + }, + { + "description": "Denies the set_always_on_top command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-always-on-top" + }, + { + "description": "Denies the set_background_color command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-background-color" + }, + { + "description": "Denies the set_badge_count command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-badge-count" + }, + { + "description": "Denies the set_badge_label command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-badge-label" + }, + { + "description": "Denies the set_closable command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-closable" + }, + { + "description": "Denies the set_content_protected command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-content-protected" + }, + { + "description": "Denies the set_cursor_grab command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-cursor-grab" + }, + { + "description": "Denies the set_cursor_icon command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-cursor-icon" + }, + { + "description": "Denies the set_cursor_position command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-cursor-position" + }, + { + "description": "Denies the set_cursor_visible command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-cursor-visible" + }, + { + "description": "Denies the set_decorations command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-decorations" + }, + { + "description": "Denies the set_effects command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-effects" + }, + { + "description": "Denies the set_enabled command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-enabled" + }, + { + "description": "Denies the set_focus command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-focus" + }, + { + "description": "Denies the set_fullscreen command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-fullscreen" + }, + { + "description": "Denies the set_icon command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-icon" + }, + { + "description": "Denies the set_ignore_cursor_events command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-ignore-cursor-events" + }, + { + "description": "Denies the set_max_size command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-max-size" + }, + { + "description": "Denies the set_maximizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-maximizable" + }, + { + "description": "Denies the set_min_size command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-min-size" + }, + { + "description": "Denies the set_minimizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-minimizable" + }, + { + "description": "Denies the set_overlay_icon command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-overlay-icon" + }, + { + "description": "Denies the set_position command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-position" + }, + { + "description": "Denies the set_progress_bar command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-progress-bar" + }, + { + "description": "Denies the set_resizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-resizable" + }, + { + "description": "Denies the set_shadow command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-shadow" + }, + { + "description": "Denies the set_size command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-size" + }, + { + "description": "Denies the set_size_constraints command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-size-constraints" + }, + { + "description": "Denies the set_skip_taskbar command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-skip-taskbar" + }, + { + "description": "Denies the set_theme command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-theme" + }, + { + "description": "Denies the set_title command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-title" + }, + { + "description": "Denies the set_title_bar_style command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-title-bar-style" + }, + { + "description": "Denies the set_visible_on_all_workspaces command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-visible-on-all-workspaces" + }, + { + "description": "Denies the show command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-show" + }, + { + "description": "Denies the start_dragging command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-start-dragging" + }, + { + "description": "Denies the start_resize_dragging command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-start-resize-dragging" + }, + { + "description": "Denies the theme command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-theme" + }, + { + "description": "Denies the title command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-title" + }, + { + "description": "Denies the toggle_maximize command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-toggle-maximize" + }, + { + "description": "Denies the unmaximize command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-unmaximize" + }, + { + "description": "Denies the unminimize command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-unminimize" + }, + { + "description": "This permission set configures which\nprocess feeatures are by default exposed.\n\n#### Granted Permissions\n\nThis enables to quit via `allow-exit` and restart via `allow-restart`\nthe application.\n", + "type": "string", + "const": "process:default" + }, + { + "description": "Enables the exit command without any pre-configured scope.", + "type": "string", + "const": "process:allow-exit" + }, + { + "description": "Enables the restart command without any pre-configured scope.", + "type": "string", + "const": "process:allow-restart" + }, + { + "description": "Denies the exit command without any pre-configured scope.", + "type": "string", + "const": "process:deny-exit" + }, + { + "description": "Denies the restart command without any pre-configured scope.", + "type": "string", + "const": "process:deny-restart" + }, + { + "description": "This permission set configures which\nshell functionality is exposed by default.\n\n#### Granted Permissions\n\nIt allows to use the `open` functionality without any specific\nscope pre-configured. It will allow opening `http(s)://`,\n`tel:` and `mailto:` links.\n", + "type": "string", + "const": "shell:default" + }, + { + "description": "Enables the execute command without any pre-configured scope.", + "type": "string", + "const": "shell:allow-execute" + }, + { + "description": "Enables the kill command without any pre-configured scope.", + "type": "string", + "const": "shell:allow-kill" + }, + { + "description": "Enables the open command without any pre-configured scope.", + "type": "string", + "const": "shell:allow-open" + }, + { + "description": "Enables the spawn command without any pre-configured scope.", + "type": "string", + "const": "shell:allow-spawn" + }, + { + "description": "Enables the stdin_write command without any pre-configured scope.", + "type": "string", + "const": "shell:allow-stdin-write" + }, + { + "description": "Denies the execute command without any pre-configured scope.", + "type": "string", + "const": "shell:deny-execute" + }, + { + "description": "Denies the kill command without any pre-configured scope.", + "type": "string", + "const": "shell:deny-kill" + }, + { + "description": "Denies the open command without any pre-configured scope.", + "type": "string", + "const": "shell:deny-open" + }, + { + "description": "Denies the spawn command without any pre-configured scope.", + "type": "string", + "const": "shell:deny-spawn" + }, + { + "description": "Denies the stdin_write command without any pre-configured scope.", + "type": "string", + "const": "shell:deny-stdin-write" + }, + { + "description": "This permission set configures which kind of\nupdater functions are exposed to the frontend.\n\n#### Granted Permissions\n\nThe full workflow from checking for updates to installing them\nis enabled.\n\n", + "type": "string", + "const": "updater:default" + }, + { + "description": "Enables the check command without any pre-configured scope.", + "type": "string", + "const": "updater:allow-check" + }, + { + "description": "Enables the download command without any pre-configured scope.", + "type": "string", + "const": "updater:allow-download" + }, + { + "description": "Enables the download_and_install command without any pre-configured scope.", + "type": "string", + "const": "updater:allow-download-and-install" + }, + { + "description": "Enables the install command without any pre-configured scope.", + "type": "string", + "const": "updater:allow-install" + }, + { + "description": "Denies the check command without any pre-configured scope.", + "type": "string", + "const": "updater:deny-check" + }, + { + "description": "Denies the download command without any pre-configured scope.", + "type": "string", + "const": "updater:deny-download" + }, + { + "description": "Denies the download_and_install command without any pre-configured scope.", + "type": "string", + "const": "updater:deny-download-and-install" + }, + { + "description": "Denies the install command without any pre-configured scope.", + "type": "string", + "const": "updater:deny-install" + } + ] + }, + "Value": { + "description": "All supported ACL values.", + "anyOf": [ + { + "description": "Represents a null JSON value.", + "type": "null" + }, + { + "description": "Represents a [`bool`].", + "type": "boolean" + }, + { + "description": "Represents a valid ACL [`Number`].", + "allOf": [ + { + "$ref": "#/definitions/Number" + } + ] + }, + { + "description": "Represents a [`String`].", + "type": "string" + }, + { + "description": "Represents a list of other [`Value`]s.", + "type": "array", + "items": { + "$ref": "#/definitions/Value" + } + }, + { + "description": "Represents a map of [`String`] keys to [`Value`]s.", + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/Value" + } + } + ] + }, + "Number": { + "description": "A valid ACL number.", + "anyOf": [ + { + "description": "Represents an [`i64`].", + "type": "integer", + "format": "int64" + }, + { + "description": "Represents a [`f64`].", + "type": "number", + "format": "double" + } + ] + }, + "Target": { + "description": "Platform target.", + "oneOf": [ + { + "description": "MacOS.", + "type": "string", + "enum": [ + "macOS" + ] + }, + { + "description": "Windows.", + "type": "string", + "enum": [ + "windows" + ] + }, + { + "description": "Linux.", + "type": "string", + "enum": [ + "linux" + ] + }, + { + "description": "Android.", + "type": "string", + "enum": [ + "android" + ] + }, + { + "description": "iOS.", + "type": "string", + "enum": [ + "iOS" + ] + } + ] + }, + "ShellScopeEntryAllowedArg": { + "description": "A command argument allowed to be executed by the webview API.", + "anyOf": [ + { + "description": "A non-configurable argument that is passed to the command in the order it was specified.", + "type": "string" + }, + { + "description": "A variable that is set while calling the command from the webview API.", + "type": "object", + "required": [ + "validator" + ], + "properties": { + "raw": { + "description": "Marks the validator as a raw regex, meaning the plugin should not make any modification at runtime.\n\nThis means the regex will not match on the entire string by default, which might be exploited if your regex allow unexpected input to be considered valid. When using this option, make sure your regex is correct.", + "default": false, + "type": "boolean" + }, + "validator": { + "description": "[regex] validator to require passed values to conform to an expected input.\n\nThis will require the argument value passed to this variable to match the `validator` regex before it will be executed.\n\nThe regex string is by default surrounded by `^...$` to match the full string. For example the `https?://\\w+` regex would be registered as `^https?://\\w+$`.\n\n[regex]: ", + "type": "string" + } + }, + "additionalProperties": false + } + ] + }, + "ShellScopeEntryAllowedArgs": { + "description": "A set of command arguments allowed to be executed by the webview API.\n\nA value of `true` will allow any arguments to be passed to the command. `false` will disable all arguments. A list of [`ShellScopeEntryAllowedArg`] will set those arguments as the only valid arguments to be passed to the attached command configuration.", + "anyOf": [ + { + "description": "Use a simple boolean to allow all or disable all arguments to this command configuration.", + "type": "boolean" + }, + { + "description": "A specific set of [`ShellScopeEntryAllowedArg`] that are valid to call for the command configuration.", + "type": "array", + "items": { + "$ref": "#/definitions/ShellScopeEntryAllowedArg" + } + } + ] + } + } +} \ No newline at end of file diff --git a/nym-wallet/src-tauri/src/config/mod.rs b/nym-wallet/src-tauri/src/config/mod.rs index 1ba8c2166a..87014b1b50 100644 --- a/nym-wallet/src-tauri/src/config/mod.rs +++ b/nym-wallet/src-tauri/src/config/mod.rs @@ -11,6 +11,7 @@ use nym_validator_client::nyxd::AccountId as CosmosAccountId; use serde::de::DeserializeOwned; use serde::{Deserialize, Serialize}; use strum::IntoEnumIterator; +use tauri::Manager; use url::Url; use nym_config::defaults::{DenomDetailsOwned, NymNetworkDetails, ValidatorDetails}; @@ -104,32 +105,35 @@ impl NetworkConfig { } impl Config { - fn root_directory() -> PathBuf { - tauri::api::path::config_dir().expect("Failed to get config directory") + fn root_directory(app_handle: &tauri::AppHandle) -> PathBuf { + app_handle + .path() + .local_data_dir() + .expect("Failed to get config directory") } - fn config_directory() -> PathBuf { - Self::root_directory().join(CONFIG_DIR_NAME) + fn config_directory(app_handle: &tauri::AppHandle) -> PathBuf { + Self::root_directory(app_handle).join(CONFIG_DIR_NAME) } - fn config_file_path(network: Option) -> PathBuf { + fn config_file_path(app_handle: &tauri::AppHandle, network: Option) -> PathBuf { if let Some(network) = network { let network_filename = format!("{}.toml", network.as_key()); - Self::config_directory().join(network_filename) + Self::config_directory(app_handle).join(network_filename) } else { - Self::config_directory().join(CONFIG_FILENAME) + Self::config_directory(app_handle).join(CONFIG_FILENAME) } } - pub fn save_to_files(&self) -> io::Result<()> { + pub fn save_to_files(&self, app_handle: &tauri::AppHandle) -> io::Result<()> { log::trace!("Config::save_to_file"); // Make sure the whole directory structure actually exists - fs::create_dir_all(Self::config_directory())?; + fs::create_dir_all(Self::config_directory(app_handle))?; // Global config if let Some(global) = &self.global { - let location = Self::config_file_path(None); + let location = Self::config_file_path(app_handle, None); match toml::to_string_pretty(&global) .map_err(|toml_err| io::Error::new(io::ErrorKind::Other, toml_err)) @@ -150,7 +154,7 @@ impl Config { } }; - let location = Self::config_file_path(Some(network)); + let location = Self::config_file_path(app_handle, Some(network)); match toml::to_string_pretty(config) .map_err(|toml_err| io::Error::new(io::ErrorKind::Other, toml_err)) .map(|toml| fs::write(location.clone(), toml)) @@ -162,10 +166,10 @@ impl Config { Ok(()) } - pub fn load_from_files() -> Self { + pub fn load_from_files(app_handle: &tauri::AppHandle) -> Self { // Global let global = { - let file = Self::config_file_path(None); + let file = Self::config_file_path(app_handle, None); match load_from_file::(file.clone()) { Ok(global) => { log::debug!("Loaded from file {:#?}", file); @@ -181,7 +185,7 @@ impl Config { // One file per network let mut networks = HashMap::new(); for network in WalletNetwork::iter() { - let file = Self::config_file_path(Some(network)); + let file = Self::config_file_path(app_handle, Some(network)); match load_from_file::(file.clone()) { Ok(config) => { log::trace!("Loaded from file {:#?}", file); @@ -261,6 +265,8 @@ impl Config { } } + //////// + pub fn select_nyxd_url(&mut self, nyxd_url: Url, network: WalletNetwork) { if let Some(net) = self.networks.get_mut(&network.as_key()) { net.selected_nyxd_url = Some(nyxd_url); @@ -531,145 +537,145 @@ impl From for NetworkDetails { } } -#[cfg(test)] -mod tests { - use super::*; +// #[cfg(test)] +// mod tests { +// use super::*; - fn test_config() -> Config { - let netconfig = NetworkConfig { - selected_nyxd_url: None, - selected_api_url: Some("https://my_api_url.com".parse().unwrap()), +// fn test_config() -> Config { +// let netconfig = NetworkConfig { +// selected_nyxd_url: None, +// selected_api_url: Some("https://my_api_url.com".parse().unwrap()), - nyxd_urls: Some(vec![ - ValidatorConfigEntry { - nyxd_url: "https://foo".parse().unwrap(), - nyxd_name: Some("FooName".to_string()), - api_url: None, - }, - ValidatorConfigEntry { - nyxd_url: "https://bar".parse().unwrap(), - nyxd_name: None, - api_url: Some("https://bar/api".parse().unwrap()), - }, - ValidatorConfigEntry { - nyxd_url: "https://baz".parse().unwrap(), - nyxd_name: None, - api_url: Some("https://baz/api".parse().unwrap()), - }, - ]), - ..NetworkConfig::default() - }; +// nyxd_urls: Some(vec![ +// ValidatorConfigEntry { +// nyxd_url: "https://foo".parse().unwrap(), +// nyxd_name: Some("FooName".to_string()), +// api_url: None, +// }, +// ValidatorConfigEntry { +// nyxd_url: "https://bar".parse().unwrap(), +// nyxd_name: None, +// api_url: Some("https://bar/api".parse().unwrap()), +// }, +// ValidatorConfigEntry { +// nyxd_url: "https://baz".parse().unwrap(), +// nyxd_name: None, +// api_url: Some("https://baz/api".parse().unwrap()), +// }, +// ]), +// ..NetworkConfig::default() +// }; - Config { - base: Base::default(), - global: Some(GlobalConfig::default()), - networks: [(WalletNetwork::MAINNET.as_key(), netconfig)] - .into_iter() - .collect(), - } - } +// Config { +// base: Base::default(), +// global: Some(GlobalConfig::default()), +// networks: [(WalletNetwork::MAINNET.as_key(), netconfig)] +// .into_iter() +// .collect(), +// } +// } - #[test] - fn serialize_to_toml() { - let config = test_config(); - let netconfig = &config.networks[&WalletNetwork::MAINNET.as_key()]; - assert_eq!( - toml::to_string_pretty(netconfig).unwrap(), - r#"version = 1 -selected_api_url = 'https://my_api_url.com/' +// #[test] +// fn serialize_to_toml() { +// let config = test_config(); +// let netconfig = &config.networks[&WalletNetwork::MAINNET.as_key()]; +// assert_eq!( +// toml::to_string_pretty(netconfig).unwrap(), +// r#"version = 1 +// selected_api_url = 'https://my_api_url.com/' -[[nyxd_urls]] -nyxd_url = 'https://foo/' -nyxd_name = 'FooName' +// [[nyxd_urls]] +// nyxd_url = 'https://foo/' +// nyxd_name = 'FooName' -[[nyxd_urls]] -nyxd_url = 'https://bar/' -api_url = 'https://bar/api' +// [[nyxd_urls]] +// nyxd_url = 'https://bar/' +// api_url = 'https://bar/api' -[[nyxd_urls]] -nyxd_url = 'https://baz/' -api_url = 'https://baz/api' -"# - ); - } +// [[nyxd_urls]] +// nyxd_url = 'https://baz/' +// api_url = 'https://baz/api' +// "# +// ); +// } - #[test] - fn serialize_to_json() { - let config = test_config(); - let netconfig = &config.networks[&WalletNetwork::MAINNET.as_key()]; - println!("{}", serde_json::to_string_pretty(netconfig).unwrap()); - assert_eq!( - serde_json::to_string_pretty(netconfig).unwrap(), - r#"{ - "version": 1, - "selected_nyxd_url": null, - "default_nyxd_url": null, - "selected_api_url": "https://my_api_url.com/", - "nyxd_urls": [ - { - "nyxd_url": "https://foo/", - "nyxd_name": "FooName", - "api_url": null - }, - { - "nyxd_url": "https://bar/", - "nyxd_name": null, - "api_url": "https://bar/api" - }, - { - "nyxd_url": "https://baz/", - "nyxd_name": null, - "api_url": "https://baz/api" - } - ] -}"# - ); - } +// #[test] +// fn serialize_to_json() { +// let config = test_config(); +// let netconfig = &config.networks[&WalletNetwork::MAINNET.as_key()]; +// println!("{}", serde_json::to_string_pretty(netconfig).unwrap()); +// assert_eq!( +// serde_json::to_string_pretty(netconfig).unwrap(), +// r#"{ +// "version": 1, +// "selected_nyxd_url": null, +// "default_nyxd_url": null, +// "selected_api_url": "https://my_api_url.com/", +// "nyxd_urls": [ +// { +// "nyxd_url": "https://foo/", +// "nyxd_name": "FooName", +// "api_url": null +// }, +// { +// "nyxd_url": "https://bar/", +// "nyxd_name": null, +// "api_url": "https://bar/api" +// }, +// { +// "nyxd_url": "https://baz/", +// "nyxd_name": null, +// "api_url": "https://baz/api" +// } +// ] +// }"# +// ); +// } - #[test] - fn serialize_and_deserialize_to_toml() { - let config = test_config(); - let netconfig = &config.networks[&WalletNetwork::MAINNET.as_key()]; - let config_str = toml::to_string_pretty(netconfig).unwrap(); - let config_from_toml: NetworkConfig = toml::from_str(&config_str).unwrap(); - assert_eq!(netconfig, &config_from_toml); - } +// #[test] +// fn serialize_and_deserialize_to_toml() { +// let config = test_config(); +// let netconfig = &config.networks[&WalletNetwork::MAINNET.as_key()]; +// let config_str = toml::to_string_pretty(netconfig).unwrap(); +// let config_from_toml: NetworkConfig = toml::from_str(&config_str).unwrap(); +// assert_eq!(netconfig, &config_from_toml); +// } - #[test] - fn get_urls_parsed_from_config() { - let config = test_config(); +// #[test] +// fn get_urls_parsed_from_config() { +// let config = test_config(); - let nyxd_url = config - .get_configured_validators(WalletNetwork::MAINNET) - .next() - .map(|v| v.nyxd_url) - .unwrap(); - assert_eq!(nyxd_url.as_ref(), "https://foo/"); +// let nyxd_url = config +// .get_configured_validators(WalletNetwork::MAINNET) +// .next() +// .map(|v| v.nyxd_url) +// .unwrap(); +// assert_eq!(nyxd_url.as_ref(), "https://foo/"); - // The first entry is missing an API URL - let api_url = config - .get_configured_validators(WalletNetwork::MAINNET) - .next() - .and_then(|v| v.api_url); - assert_eq!(api_url, None); - } +// // The first entry is missing an API URL +// let api_url = config +// .get_configured_validators(WalletNetwork::MAINNET) +// .next() +// .and_then(|v| v.api_url); +// assert_eq!(api_url, None); +// } - #[test] - fn get_urls_from_defaults() { - let config = Config::default(); +// #[test] +// fn get_urls_from_defaults() { +// let config = Config::default(); - let nyxd_url = config - .get_base_validators(WalletNetwork::MAINNET) - .next() - .map(|v| v.nyxd_url) - .unwrap(); - assert_eq!(nyxd_url.as_ref(), "https://rpc.nymtech.net/"); +// let nyxd_url = config +// .get_base_validators(WalletNetwork::MAINNET) +// .next() +// .map(|v| v.nyxd_url) +// .unwrap(); +// assert_eq!(nyxd_url.as_ref(), "https://rpc.nymtech.net/"); - let api_url = config - .get_base_validators(WalletNetwork::MAINNET) - .next() - .and_then(|v| v.api_url) - .unwrap(); - assert_eq!(api_url.as_ref(), "https://validator.nymtech.net/api/",); - } -} +// let api_url = config +// .get_base_validators(WalletNetwork::MAINNET) +// .next() +// .and_then(|v| v.api_url) +// .unwrap(); +// assert_eq!(api_url.as_ref(), "https://validator.nymtech.net/api/",); +// } +// } diff --git a/nym-wallet/src-tauri/src/log.rs b/nym-wallet/src-tauri/src/log.rs index 6c9eb072da..d7642291e3 100644 --- a/nym-wallet/src-tauri/src/log.rs +++ b/nym-wallet/src-tauri/src/log.rs @@ -3,7 +3,7 @@ use std::str::FromStr; use fern::colors::{Color, ColoredLevelConfig}; use serde::Serialize; use serde_repr::{Deserialize_repr, Serialize_repr}; -use tauri::Manager; +use tauri::Emitter; use time::{format_description, OffsetDateTime}; fn formatted_time() -> String { @@ -61,7 +61,7 @@ pub fn setup_logging(app_handle: tauri::AppHandle) -> Result<(), log::SetLoggerE message: record.args().to_string(), level: record.level().into(), }; - app_handle.emit_all("log://log", msg).unwrap(); + app_handle.emit("log://log", msg).unwrap(); })); base_config diff --git a/nym-wallet/src-tauri/src/main.rs b/nym-wallet/src-tauri/src/main.rs index 6b3c6a1b27..0f9aa2bcdc 100644 --- a/nym-wallet/src-tauri/src/main.rs +++ b/nym-wallet/src-tauri/src/main.rs @@ -3,11 +3,11 @@ windows_subsystem = "windows" )] -use tauri::{Manager, Menu}; - use nym_mixnet_contract_common::{Gateway, MixNode}; +use tauri::menu::{MenuBuilder, MenuItemBuilder, SubmenuBuilder}; +use tauri::Manager; -use crate::menu::AddDefaultSubmenus; +use crate::menu::SHOW_LOG_WINDOW; use crate::operations::app; use crate::operations::help; use crate::operations::mixnet; @@ -210,13 +210,31 @@ fn main() { app::react::set_react_state, app::react::get_react_state, ]) - .menu(Menu::os_default(&context.package_info().name).add_default_app_submenus()) - .on_menu_event(|event| { - if event.menu_item_id() == menu::SHOW_LOG_WINDOW { - let _r = help::log::help_log_toggle_window(event.window().app_handle()); + .menu(|app| { + // Create a menu builder + let menu_builder = MenuBuilder::new(app); + if ::std::env::var("NYM_WALLET_ENABLE_LOG").is_ok() { + let help_text = MenuItemBuilder::with_id(SHOW_LOG_WINDOW, "Show logs") + .build(app) + .expect("Failed to create menu item"); + + let submenu = SubmenuBuilder::new(app, "Help") + .items(&[&help_text]) + .build() + .expect("Failed to create help submenu"); + + menu_builder.item(&submenu).build() + } else { + // Build a default menu without the submenu + menu_builder.build() } }) - .setup(|app| Ok(log::setup_logging(app.app_handle())?)) + .on_menu_event(|app, event| { + if event.id() == SHOW_LOG_WINDOW { + let _r = help::log::help_log_toggle_window(app.app_handle().clone()); + } + }) + .setup(|app| Ok(log::setup_logging(app.app_handle().clone())?)) .run(context) .expect("error while running tauri application"); } diff --git a/nym-wallet/src-tauri/src/menu.rs b/nym-wallet/src-tauri/src/menu.rs index b38e73a639..618944ca13 100644 --- a/nym-wallet/src-tauri/src/menu.rs +++ b/nym-wallet/src-tauri/src/menu.rs @@ -1,5 +1,5 @@ -use tauri::Menu; -use tauri::{CustomMenuItem, Submenu}; +use tauri::menu::Menu; +use tauri::menu::{MenuBuilder, MenuItemBuilder, SubmenuBuilder}; pub const SHOW_LOG_WINDOW: &str = "show_log_window"; @@ -7,16 +7,29 @@ pub trait AddDefaultSubmenus { fn add_default_app_submenus(self) -> Self; } -impl AddDefaultSubmenus for Menu { +impl AddDefaultSubmenus for Menu { #[allow(dead_code)] fn add_default_app_submenus(self) -> Self { if ::std::env::var("NYM_WALLET_ENABLE_LOG").is_ok() { - let submenu = Submenu::new( - "Help", - Menu::new().add_item(CustomMenuItem::new(SHOW_LOG_WINDOW, "Show logs")), - ); - return self.add_submenu(submenu); + let app_handle = self.app_handle(); + + let help_text = MenuItemBuilder::with_id(SHOW_LOG_WINDOW, "Show logs") + .build(app_handle) + .expect("Failed to create menu item"); + + let submenu = SubmenuBuilder::new(app_handle, "Help") + .items(&[&help_text]) + .build() + .expect("Failed to create help submenu"); + + let menu_builder = MenuBuilder::new(app_handle); + + match menu_builder.item(&submenu).build() { + Ok(new_menu) => new_menu, + Err(_) => self, + } + } else { + self } - self } } diff --git a/nym-wallet/src-tauri/src/operations/app/version.rs b/nym-wallet/src-tauri/src/operations/app/version.rs index d9d475e931..d904449cf0 100644 --- a/nym-wallet/src-tauri/src/operations/app/version.rs +++ b/nym-wallet/src-tauri/src/operations/app/version.rs @@ -3,27 +3,46 @@ use crate::error::BackendError; use nym_wallet_types::app::AppVersion; +use tauri_plugin_updater::UpdaterExt; #[tauri::command] pub async fn check_version(handle: tauri::AppHandle) -> Result { log::info!(">>> Getting app version info"); - let res = tauri::updater::builder(handle) - .check() - .await - .map(|u| AppVersion { - current_version: u.current_version().to_string(), - latest_version: u.latest_version().to_owned(), - is_update_available: u.is_update_available(), + + let updater = handle.updater().map_err(|e| { + log::error!("Failed to get updater: {}", e); + BackendError::CheckAppVersionError + })?; + + // Then check for updates + let update_info = updater.check().await.map_err(|e| { + log::error!("An error occurred while checking for app update {}", e); + BackendError::CheckAppVersionError + })?; + + // Process the result + if let Some(update) = update_info { + log::debug!( + "<<< update available: [true], current version {}, latest version {}", + update.current_version, + update.version + ); + Ok(AppVersion { + current_version: update.current_version.to_string(), + latest_version: update.version, + is_update_available: true, }) - .map_err(|e| { - log::error!("An error ocurred while checking for app update {}", e); - BackendError::CheckAppVersionError - })?; - log::debug!( - "<<< update available: [{}], current version {}, latest version {}", - res.is_update_available, - res.current_version, - res.latest_version - ); - Ok(res) + } else { + // No update available + let current_version = handle.package_info().version.to_string(); + log::debug!( + "<<< update available: [false], current version {}", + current_version + ); + Ok(AppVersion { + current_version: current_version.clone(), + latest_version: current_version, + is_update_available: false, + }) + } } diff --git a/nym-wallet/src-tauri/src/operations/app/window.rs b/nym-wallet/src-tauri/src/operations/app/window.rs index ce30536fe0..d15fde65cf 100644 --- a/nym-wallet/src-tauri/src/operations/app/window.rs +++ b/nym-wallet/src-tauri/src/operations/app/window.rs @@ -26,10 +26,10 @@ async fn create_window( ) -> Result<(), BackendError> { // create the new window first, to stop the app process from exiting log::info!("Creating {} window...", new_window_label); - match tauri::WindowBuilder::new( + match tauri::WebviewWindowBuilder::new( &app_handle, - new_window_label, - tauri::WindowUrl::App(new_window_url.into()), + "main", + tauri::WebviewUrl::App(new_window_url.into()), ) .title("Nym Wallet") .build() @@ -49,7 +49,7 @@ async fn create_window( } // close the old window - match app_handle.windows().get(try_close_window_label) { + match app_handle.get_webview_window(try_close_window_label) { Some(try_close_window) => { if let Err(err) = try_close_window.close() { log::error!("Could not close window: {err}") diff --git a/nym-wallet/src-tauri/src/operations/help/log.rs b/nym-wallet/src-tauri/src/operations/help/log.rs index c4c2357304..42f15fb02f 100644 --- a/nym-wallet/src-tauri/src/operations/help/log.rs +++ b/nym-wallet/src-tauri/src/operations/help/log.rs @@ -3,7 +3,7 @@ use tauri::Manager; #[tauri::command] pub fn help_log_toggle_window(app_handle: tauri::AppHandle) -> Result<(), BackendError> { - if let Some(current_log_window) = app_handle.windows().get("log") { + if let Some(current_log_window) = app_handle.get_webview_window("log") { log::info!("Closing log window..."); if let Err(err) = current_log_window.close() { log::error!("Unable to close log window: {err}"); @@ -12,9 +12,13 @@ pub fn help_log_toggle_window(app_handle: tauri::AppHandle) -> Result<(), Backen } log::info!("Creating log window..."); - match tauri::WindowBuilder::new(&app_handle, "log", tauri::WindowUrl::App("log.html".into())) - .title("Nym Wallet Logs") - .build() + match tauri::WebviewWindowBuilder::new( + &app_handle, + "log", + tauri::WebviewUrl::App("log.html".into()), + ) + .title("Nym Wallet Logs") + .build() { Ok(window) => { if let Err(err) = window.set_focus() { diff --git a/nym-wallet/src-tauri/src/wallet_storage/mod.rs b/nym-wallet/src-tauri/src/wallet_storage/mod.rs index 14d295a8a4..c620351417 100644 --- a/nym-wallet/src-tauri/src/wallet_storage/mod.rs +++ b/nym-wallet/src-tauri/src/wallet_storage/mod.rs @@ -19,6 +19,7 @@ use nym_validator_client::nyxd::bip32::DerivationPath; use std::ffi::OsString; use std::fs::{self, create_dir_all, OpenOptions}; use std::path::{Path, PathBuf}; +use tauri::Manager; use time::OffsetDateTime; #[cfg(test)] @@ -37,14 +38,18 @@ pub(crate) const DEFAULT_LOGIN_ID: &str = "default"; /// this name. pub(crate) const DEFAULT_FIRST_ACCOUNT_NAME: &str = "Account 1"; -fn get_storage_directory() -> Result { - tauri::api::path::local_data_dir() +fn get_storage_directory(app_handle: &tauri::AppHandle) -> Result { + app_handle + .path() + .local_data_dir() .map(|dir| dir.join(STORAGE_DIR_NAME)) .ok_or(BackendError::UnknownStorageDirectory) } -pub(crate) fn wallet_login_filepath() -> Result { - get_storage_directory().map(|dir| dir.join(WALLET_INFO_FILENAME)) +pub(crate) fn wallet_login_filepath( + app_handle: &tauri::AppHandle, +) -> Result { + get_storage_directory(app_handle).map(|dir| dir.join(WALLET_INFO_FILENAME)) } fn write_to_file(filepath: &Path, wallet: &StoredWallet) -> Result<(), BackendError> { @@ -59,8 +64,10 @@ fn write_to_file(filepath: &Path, wallet: &StoredWallet) -> Result<(), BackendEr /// Load stored wallet file #[allow(unused)] -pub(crate) fn load_existing_wallet() -> Result { - let store_dir = get_storage_directory()?; +pub(crate) fn load_existing_wallet( + app_handle: &tauri::AppHandle, +) -> Result { + let store_dir = get_storage_directory(app_handle)?; let filepath = store_dir.join(WALLET_INFO_FILENAME); load_existing_wallet_at_file(&filepath) } @@ -77,10 +84,11 @@ fn load_existing_wallet_at_file(filepath: &Path) -> Result Result { - let store_dir = get_storage_directory()?; + let store_dir = get_storage_directory(app_handle)?; let filepath = store_dir.join(WALLET_INFO_FILENAME); load_existing_login_at_file(&filepath, id, password) } @@ -468,1819 +476,1819 @@ fn rename_account_in_login_at_file( write_to_file(filepath, &stored_wallet) } -#[cfg(test)] -mod tests { - use crate::wallet_storage::account_data::WalletAccount; - - use super::*; - use nym_config::defaults::COSMOS_DERIVATION_PATH; - use std::str::FromStr; - use tempfile::tempdir; - - #[test] - fn trying_to_load_nonexistant_wallet_fails() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let id1 = LoginId::new("first".to_string()); - let password = UserPassword::new("password".to_string()); - - assert!(matches!( - load_existing_wallet_at_file(&wallet_file), - Err(BackendError::WalletFileNotFound), - )); - assert!(matches!( - load_existing_login_at_file(&wallet_file, &id1, &password), - Err(BackendError::WalletFileNotFound), - )); - remove_login_at_file(&wallet_file, &id1).unwrap_err(); - } - - #[test] - fn store_single_login() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - - store_login_at_file(&wallet_file, account1, hd_path, id1.clone(), &password).unwrap(); - - let stored_wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); - assert_eq!(stored_wallet.len(), 1); - - let login = stored_wallet.get_encrypted_login_by_index(0).unwrap(); - assert_eq!(login.id, id1); - - // some actual ciphertext was saved - assert!(!login.account.ciphertext().is_empty()); - } - - #[test] - fn store_single_login_with_multi() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - - store_login_with_multiple_accounts_at_file( - &wallet_file, - account1, - cosmos_hd_path, - id1.clone(), - &password, - ) - .unwrap(); - - let stored_wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); - assert_eq!(stored_wallet.len(), 1); - - let login = stored_wallet.get_encrypted_login_by_index(0).unwrap(); - assert_eq!(login.id, id1); - - // some actual ciphertext was saved - assert!(!login.account.ciphertext().is_empty()); - } - - #[test] - fn store_single_login_with_multi_then_update_pwd_and_load() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let new_password = UserPassword::new("new_password".to_string()); - let id1 = LoginId::new("first".to_string()); - - store_login_with_multiple_accounts_at_file( - &wallet_file, - account1, - cosmos_hd_path, - id1.clone(), - &password, - ) - .unwrap(); - - update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); - - let stored_wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); - assert_eq!(stored_wallet.len(), 1); - - let login = stored_wallet.get_encrypted_login_by_index(0).unwrap(); - assert_eq!(login.id, id1); - - // some actual ciphertext was saved - assert!(!login.account.ciphertext().is_empty()); - } - - #[test] - fn store_twice_for_the_same_id_fails() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - - // Store the first login - store_login_at_file( - &wallet_file, - account1.clone(), - hd_path.clone(), - id1.clone(), - &password, - ) - .unwrap(); - - // and storing the same id again fails - assert!(matches!( - store_login_at_file(&wallet_file, account1, hd_path, id1, &password,), - Err(BackendError::WalletLoginIdAlreadyExists), - )); - } - - #[test] - fn store_twice_for_the_same_id_fails_with_multiple() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - - // Store the first login - store_login_with_multiple_accounts_at_file( - &wallet_file, - account1.clone(), - hd_path.clone(), - id1.clone(), - &password, - ) - .unwrap(); - - // and storing the same id again fails - assert!(matches!( - store_login_with_multiple_accounts_at_file( - &wallet_file, - account1, - hd_path, - id1, - &password, - ), - Err(BackendError::WalletLoginIdAlreadyExists), - )); - } - - #[test] - fn load_with_wrong_password_fails() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let bad_password = UserPassword::new("bad-password".to_string()); - let id1 = LoginId::new("first".to_string()); - - store_login_at_file(&wallet_file, account1, hd_path, id1.clone(), &password).unwrap(); - - // Trying to load it with wrong password now fails - assert!(matches!( - load_existing_login_at_file(&wallet_file, &id1, &bad_password), - Err(BackendError::StoreCipherError { .. }), - )); - } - - #[test] - fn load_with_wrong_password_fails_with_multi() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let bad_password = UserPassword::new("bad-password".to_string()); - let id1 = LoginId::new("first".to_string()); - - store_login_with_multiple_accounts_at_file( - &wallet_file, - account1, - hd_path, - id1.clone(), - &password, - ) - .unwrap(); - - // Trying to load it with wrong password now fails - assert!(matches!( - load_existing_login_at_file(&wallet_file, &id1, &bad_password), - Err(BackendError::StoreCipherError { .. }), - )); - } - - #[test] - fn load_with_wrong_id_fails() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - let id2 = LoginId::new("second".to_string()); - - store_login_at_file(&wallet_file, account1, hd_path, id1, &password).unwrap(); - - // Trying to load with the wrong id - assert!(matches!( - load_existing_login_at_file(&wallet_file, &id2, &password), - Err(BackendError::WalletNoSuchLoginId), - )); - } - - #[test] - fn load_with_wrong_id_fails_with_multi() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - let id2 = LoginId::new("second".to_string()); - - store_login_with_multiple_accounts_at_file(&wallet_file, account1, hd_path, id1, &password) - .unwrap(); - - // Trying to load with the wrong id - assert!(matches!( - load_existing_login_at_file(&wallet_file, &id2, &password), - Err(BackendError::WalletNoSuchLoginId), - )); - } - - #[test] - fn store_and_load_a_single_login() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - - store_login_at_file( - &wallet_file, - account1.clone(), - hd_path.clone(), - id1.clone(), - &password, - ) - .unwrap(); - - let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); - let acc = loaded_login.as_mnemonic_account().unwrap(); - assert_eq!(&account1, acc.mnemonic()); - assert_eq!(&hd_path, acc.hd_path()); - } - - #[test] - fn store_a_single_login_then_update_pwd_and_load() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let new_password = UserPassword::new("new_password".to_string()); - let id1 = LoginId::new("first".to_string()); - - store_login_at_file( - &wallet_file, - account1.clone(), - hd_path.clone(), - id1.clone(), - &password, - ) - .unwrap(); - - update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); - - let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &new_password).unwrap(); - let acc = loaded_login.as_mnemonic_account().unwrap(); - assert_eq!(&account1, acc.mnemonic()); - assert_eq!(&hd_path, acc.hd_path()); - } - - #[test] - fn store_a_single_login_then_update_pwd_with_identical_pwd_is_noop_but_okay() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - - store_login_at_file( - &wallet_file, - account1.clone(), - hd_path.clone(), - id1.clone(), - &password, - ) - .unwrap(); - - update_encrypted_logins_at_file(&wallet_file, &password, &password).unwrap(); - - let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); - let acc = loaded_login.as_mnemonic_account().unwrap(); - assert_eq!(&account1, acc.mnemonic()); - assert_eq!(&hd_path, acc.hd_path()); - } - - #[test] - fn store_a_single_login_then_update_pwd_with_wrong_current_pwd_fails() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let new_password = UserPassword::new("new_password".to_string()); - let wrong_password = UserPassword::new("wrong_password".to_string()); - let id1 = LoginId::new("first".to_string()); - - store_login_at_file(&wallet_file, account1, hd_path, id1, &password).unwrap(); - - let err = update_encrypted_logins_at_file(&wallet_file, &wrong_password, &new_password) - .unwrap_err(); - assert!(matches!(err, BackendError::StoreCipherError { .. })); - } - - #[test] - fn store_a_single_login_then_update_pwd_and_load_with_wrong_pwd_fails() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let new_password = UserPassword::new("new_password".to_string()); - let id1 = LoginId::new("first".to_string()); - - store_login_at_file(&wallet_file, account1, hd_path, id1.clone(), &password).unwrap(); - - update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); - - let err = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap_err(); - assert!(matches!(err, BackendError::StoreCipherError { .. })); - } - - #[test] - fn store_and_load_a_single_login_with_multi() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let acc1 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - - store_login_with_multiple_accounts_at_file( - &wallet_file, - acc1.clone(), - hd_path.clone(), - id1.clone(), - &password, - ) - .unwrap(); - - let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); - let accounts = loaded_login.as_multiple_accounts().unwrap(); - assert_eq!(accounts.len(), 1); - let account = accounts - .get_account(&DEFAULT_FIRST_ACCOUNT_NAME.into()) - .unwrap(); - assert_eq!(account.id().as_ref(), DEFAULT_FIRST_ACCOUNT_NAME); - assert_eq!(account.mnemonic(), &acc1); - assert_eq!(account.hd_path(), &hd_path); - } - - #[test] - fn store_a_single_login_with_multi_then_update_pwd_and_load() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let acc1 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let new_password = UserPassword::new("new_password".to_string()); - let id1 = LoginId::new("first".to_string()); - - store_login_with_multiple_accounts_at_file( - &wallet_file, - acc1.clone(), - hd_path.clone(), - id1.clone(), - &password, - ) - .unwrap(); - - update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); - - let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &new_password).unwrap(); - let accounts = loaded_login.as_multiple_accounts().unwrap(); - assert_eq!(accounts.len(), 1); - let account = accounts - .get_account(&DEFAULT_FIRST_ACCOUNT_NAME.into()) - .unwrap(); - assert_eq!(account.id().as_ref(), DEFAULT_FIRST_ACCOUNT_NAME); - assert_eq!(account.mnemonic(), &acc1); - assert_eq!(account.hd_path(), &hd_path); - } - - #[test] - fn store_a_single_login_with_multi_then_update_pwd_with_wrong_current_pwd_fails() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let acc1 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let new_password = UserPassword::new("new_password".to_string()); - let wrong_password = UserPassword::new("wrong_password".to_string()); - let id1 = LoginId::new("first".to_string()); - - store_login_with_multiple_accounts_at_file(&wallet_file, acc1, hd_path, id1, &password) - .unwrap(); - - let err = update_encrypted_logins_at_file(&wallet_file, &wrong_password, &new_password) - .unwrap_err(); - assert!(matches!(err, BackendError::StoreCipherError { .. })); - } - - #[test] - fn store_a_single_login_with_multi_then_update_pwd_and_load_with_wrong_pwd_fails() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let acc1 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let new_password = UserPassword::new("new_password".to_string()); - let id1 = LoginId::new("first".to_string()); - - store_login_with_multiple_accounts_at_file( - &wallet_file, - acc1, - hd_path, - id1.clone(), - &password, - ) - .unwrap(); - - update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); - - let err = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap_err(); - assert!(matches!(err, BackendError::StoreCipherError { .. })); - } - - #[test] - fn store_a_second_login_with_a_different_password_fails() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let account2 = Mnemonic::generate(24).unwrap(); - let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let bad_password = UserPassword::new("bad-password".to_string()); - let id1 = LoginId::new("first".to_string()); - let id2 = LoginId::new("second".to_string()); - - store_login_at_file( - &wallet_file, - account1, - cosmos_hd_path.clone(), - id1, - &password, - ) - .unwrap(); - - // Can't store a second login if you use different password - assert!(matches!( - store_login_at_file(&wallet_file, account2, cosmos_hd_path, id2, &bad_password), - Err(BackendError::WalletDifferentPasswordDetected), - )); - } - - #[test] - fn store_a_second_login_with_a_different_password_fails_with_multi() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let account2 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let bad_password = UserPassword::new("bad-password".to_string()); - let id1 = LoginId::new("first".to_string()); - let id2 = LoginId::new("second".to_string()); - - store_login_with_multiple_accounts_at_file( - &wallet_file, - account1, - hd_path.clone(), - id1, - &password, - ) - .unwrap(); - - // Can't store a second login if you use different password - assert!(matches!( - store_login_with_multiple_accounts_at_file( - &wallet_file, - account2, - hd_path, - id2, - &bad_password - ), - Err(BackendError::WalletDifferentPasswordDetected), - )); - } - - #[test] - fn store_two_mnemonic_accounts_gives_different_salts_and_iv() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let account2 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let different_hd_path: DerivationPath = "m".parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - let id2 = LoginId::new("second".to_string()); - - // Store the first account - store_login_at_file(&wallet_file, account1, hd_path, id1, &password).unwrap(); - - let stored_wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); - let encrypted_blob = &stored_wallet - .get_encrypted_login_by_index(0) - .unwrap() - .account; - - // keep track of salt and iv for future assertion - let original_iv = encrypted_blob.iv().to_vec(); - let original_salt = encrypted_blob.salt().to_vec(); - - // Add an extra account - store_login_at_file(&wallet_file, account2, different_hd_path, id2, &password).unwrap(); - - let loaded_accounts = load_existing_wallet_at_file(&wallet_file).unwrap(); - assert_eq!(loaded_accounts.len(), 2); - let encrypted_blob = &loaded_accounts - .get_encrypted_login_by_index(1) - .unwrap() - .account; - - // fresh IV and salt are used - assert_ne!(original_iv, encrypted_blob.iv()); - assert_ne!(original_salt, encrypted_blob.salt()); - } - - #[test] - fn store_two_mnemonic_accounts_using_two_logins() { - let store_dir = tempdir().unwrap(); - let wallet = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let account2 = Mnemonic::generate(24).unwrap(); - let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let different_hd_path: DerivationPath = "m".parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - let id2 = LoginId::new("second".to_string()); - - // Store the first account - store_login_at_file( - &wallet, - account1.clone(), - cosmos_hd_path.clone(), - id1.clone(), - &password, - ) - .unwrap(); - - let login = load_existing_login_at_file(&wallet, &id1, &password).unwrap(); - let acc = login.as_mnemonic_account().unwrap(); - assert_eq!(&account1, acc.mnemonic()); - assert_eq!(&cosmos_hd_path, acc.hd_path()); - - // Add an extra account - store_login_at_file( - &wallet, - account2.clone(), - different_hd_path.clone(), - id2.clone(), - &password, - ) - .unwrap(); - - // first account should be unchanged - let loaded_login = load_existing_login_at_file(&wallet, &id1, &password).unwrap(); - let acc1 = loaded_login.as_mnemonic_account().unwrap(); - assert_eq!(&account1, acc1.mnemonic()); - assert_eq!(&cosmos_hd_path, acc1.hd_path()); - - let loaded_login = load_existing_login_at_file(&wallet, &id2, &password).unwrap(); - let acc2 = loaded_login.as_mnemonic_account().unwrap(); - assert_eq!(&account2, acc2.mnemonic()); - assert_eq!(&different_hd_path, acc2.hd_path()); - } - - #[test] - fn store_two_mnemonic_accounts_using_two_logins_then_update_pwd_and_load() { - let store_dir = tempdir().unwrap(); - let wallet = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let account2 = Mnemonic::generate(24).unwrap(); - let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let different_hd_path: DerivationPath = "m".parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let new_password = UserPassword::new("new_password".to_string()); - let id1 = LoginId::new("first".to_string()); - let id2 = LoginId::new("second".to_string()); - - // Store the first login with an account - store_login_at_file( - &wallet, - account1.clone(), - cosmos_hd_path.clone(), - id1.clone(), - &password, - ) - .unwrap(); - - let login = load_existing_login_at_file(&wallet, &id1, &password).unwrap(); - let acc = login.as_mnemonic_account().unwrap(); - assert_eq!(&account1, acc.mnemonic()); - assert_eq!(&cosmos_hd_path, acc.hd_path()); - - // Store a second login, also with an account - store_login_at_file( - &wallet, - account2.clone(), - different_hd_path.clone(), - id2.clone(), - &password, - ) - .unwrap(); - - update_encrypted_logins_at_file(&wallet, &password, &new_password).unwrap(); - - // first account should be unchanged - let loaded_login = load_existing_login_at_file(&wallet, &id1, &new_password).unwrap(); - let acc1 = loaded_login.as_mnemonic_account().unwrap(); - assert_eq!(&account1, acc1.mnemonic()); - assert_eq!(&cosmos_hd_path, acc1.hd_path()); - - let loaded_login = load_existing_login_at_file(&wallet, &id2, &new_password).unwrap(); - let acc2 = loaded_login.as_mnemonic_account().unwrap(); - assert_eq!(&account2, acc2.mnemonic()); - assert_eq!(&different_hd_path, acc2.hd_path()); - } - - #[test] - fn store_one_mnemonic_account_and_one_multi_account() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let account2 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let different_hd_path: DerivationPath = "m".parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - let id2 = LoginId::new("second".to_string()); - - // Store the first account - store_login_at_file( - &wallet_file, - account1.clone(), - hd_path.clone(), - id1.clone(), - &password, - ) - .unwrap(); - - let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); - let acc = loaded_login.as_mnemonic_account().unwrap(); - assert_eq!(&account1, acc.mnemonic()); - assert_eq!(&hd_path, acc.hd_path()); - - // Add an extra account - store_login_with_multiple_accounts_at_file( - &wallet_file, - account2.clone(), - different_hd_path.clone(), - id2.clone(), - &password, - ) - .unwrap(); - - // first account should be unchanged - let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); - let acc1 = loaded_login.as_mnemonic_account().unwrap(); - assert_eq!(&account1, acc1.mnemonic()); - assert_eq!(&hd_path, acc1.hd_path()); - - let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); - let acc2 = loaded_login.as_multiple_accounts().unwrap(); - assert_eq!(acc2.len(), 1); - let account = acc2 - .get_account(&DEFAULT_FIRST_ACCOUNT_NAME.into()) - .unwrap(); - assert_eq!(account.id().as_ref(), DEFAULT_FIRST_ACCOUNT_NAME); - assert_eq!(account.mnemonic(), &account2); - assert_eq!(account.hd_path(), &different_hd_path); - } - - #[test] - fn remove_non_existent_id_fails() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - let id2 = LoginId::new("second".to_string()); - - store_login_with_multiple_accounts_at_file(&wallet_file, account1, hd_path, id1, &password) - .unwrap(); - - // Fails to delete non-existent id in the wallet - assert!(matches!( - remove_login_at_file(&wallet_file, &id2), - Err(BackendError::WalletNoSuchLoginId), - )); - } - - #[test] - fn store_and_remove_wallet_login_information() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let account2 = Mnemonic::generate(24).unwrap(); - let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let different_hd_path: DerivationPath = "m".parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - let id2 = LoginId::new("second".to_string()); - - // Store two accounts with two different passwords - store_login_at_file( - &wallet_file, - account1.clone(), - cosmos_hd_path.clone(), - id1.clone(), - &password, - ) - .unwrap(); - store_login_at_file( - &wallet_file, - account2.clone(), - different_hd_path.clone(), - id2.clone(), - &password, - ) - .unwrap(); - - // Load and compare - let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); - let acc1 = loaded_login.as_mnemonic_account().unwrap(); - assert_eq!(&account1, acc1.mnemonic()); - assert_eq!(&cosmos_hd_path, acc1.hd_path()); - - let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); - let acc2 = loaded_login.as_mnemonic_account().unwrap(); - assert_eq!(&account2, acc2.mnemonic()); - assert_eq!(&different_hd_path, acc2.hd_path()); - - // Delete the second account - remove_login_at_file(&wallet_file, &id2).unwrap(); - - // The first account should be unchanged - let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); - let acc1 = loaded_login.as_mnemonic_account().unwrap(); - assert_eq!(&account1, acc1.mnemonic()); - assert_eq!(&cosmos_hd_path, acc1.hd_path()); - - // And we can't load the second one anymore - assert!(matches!( - load_existing_login_at_file(&wallet_file, &id2, &password), - Err(BackendError::WalletNoSuchLoginId), - )); - - // Delete the first account - assert!(wallet_file.exists()); - remove_login_at_file(&wallet_file, &id1).unwrap(); - - // The file should now be removed - assert!(!wallet_file.exists()); - - // And trying to load when the file is gone fails - assert!(matches!( - load_existing_login_at_file(&wallet_file, &id1, &password), - Err(BackendError::WalletFileNotFound), - )); - } - - #[test] - fn append_account_converts_the_type() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let account2 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - let id2 = AccountId::new("second".to_string()); - - store_login_at_file( - &wallet_file, - account1.clone(), - hd_path.clone(), - id1.clone(), - &password, - ) - .unwrap(); - - // Check that it's there as the correct non-multiple type - let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); - let acc = loaded_login.as_mnemonic_account().unwrap(); - assert_eq!(acc.mnemonic(), &account1); - assert_eq!(acc.hd_path(), &hd_path); - - append_account_to_login_at_file( - &wallet_file, - account2.clone(), - hd_path.clone(), - id1.clone(), - id2.clone(), - &password, - ) - .unwrap(); - - // Check that it is now multiple mnemonic type - let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); - let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); - let expected = vec![ - WalletAccount::new(id1.into(), MnemonicAccount::new(account1, hd_path.clone())), - WalletAccount::new(id2, MnemonicAccount::new(account2, hd_path)), - ] - .into(); - assert_eq!(loaded_accounts, &expected); - } - - #[test] - fn append_accounts_to_existing_login() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let account2 = Mnemonic::generate(24).unwrap(); - let account3 = Mnemonic::generate(24).unwrap(); - let account4 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - let id2 = LoginId::new("second".to_string()); - let id3 = AccountId::new("third".to_string()); - let id4 = AccountId::new("fourth".to_string()); - - store_login_at_file( - &wallet_file, - account1.clone(), - hd_path.clone(), - id1.clone(), - &password, - ) - .unwrap(); - - store_login_at_file( - &wallet_file, - account2.clone(), - hd_path.clone(), - id2.clone(), - &password, - ) - .unwrap(); - - // Check that it's there as the correct non-multiple type - let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); - let acc2 = loaded_login.as_mnemonic_account().unwrap(); - assert_eq!(acc2.mnemonic(), &account2); - assert_eq!(acc2.hd_path(), &hd_path); - - // Add a third and fourth mnenonic grouped together with the second one - append_account_to_login_at_file( - &wallet_file, - account3.clone(), - hd_path.clone(), - id2.clone(), - id3.clone(), - &password, - ) - .unwrap(); - append_account_to_login_at_file( - &wallet_file, - account4.clone(), - hd_path.clone(), - id2.clone(), - id4.clone(), - &password, - ) - .unwrap(); - - // Check that we can load all four - let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); - let acc1 = loaded_login.as_mnemonic_account().unwrap(); - assert_eq!(acc1.mnemonic(), &account1); - assert_eq!(acc1.hd_path(), &hd_path); - - let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); - let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); - let expected = vec![ - WalletAccount::new(id2.into(), MnemonicAccount::new(account2, hd_path.clone())), - WalletAccount::new(id3, MnemonicAccount::new(account3, hd_path.clone())), - WalletAccount::new(id4, MnemonicAccount::new(account4, hd_path)), - ] - .into(); - assert_eq!(loaded_accounts, &expected); - } - - #[test] - fn append_accounts_to_existing_login_with_multi() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let account2 = Mnemonic::generate(24).unwrap(); - let account3 = Mnemonic::generate(24).unwrap(); - let account4 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - let id2 = LoginId::new("second".to_string()); - let id3 = AccountId::new("third".to_string()); - let id4 = AccountId::new("fourth".to_string()); - - store_login_with_multiple_accounts_at_file( - &wallet_file, - account1.clone(), - hd_path.clone(), - id1.clone(), - &password, - ) - .unwrap(); - - store_login_with_multiple_accounts_at_file( - &wallet_file, - account2.clone(), - hd_path.clone(), - id2.clone(), - &password, - ) - .unwrap(); - - // Add a third and fourth mnenonic grouped together with the second one - append_account_to_login_at_file( - &wallet_file, - account3.clone(), - hd_path.clone(), - id2.clone(), - id3.clone(), - &password, - ) - .unwrap(); - append_account_to_login_at_file( - &wallet_file, - account4.clone(), - hd_path.clone(), - id2.clone(), - id4.clone(), - &password, - ) - .unwrap(); - - // Check that we can load all four - let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); - let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); - let expected = vec![WalletAccount::new( - DEFAULT_FIRST_ACCOUNT_NAME.into(), - MnemonicAccount::new(account1, hd_path.clone()), - )] - .into(); - assert_eq!(loaded_accounts, &expected); - - let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); - let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); - let expected = vec![ - WalletAccount::new( - DEFAULT_FIRST_ACCOUNT_NAME.into(), - MnemonicAccount::new(account2, hd_path.clone()), - ), - WalletAccount::new(id3, MnemonicAccount::new(account3, hd_path.clone())), - WalletAccount::new(id4, MnemonicAccount::new(account4, hd_path)), - ] - .into(); - assert_eq!(loaded_accounts, &expected); - } - - #[test] - fn append_account_to_existing_login_with_multi_then_update_pwd_and_load() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let account2 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let new_password = UserPassword::new("new_password".to_string()); - let login_id = LoginId::new("first".to_string()); - let appended_account = AccountId::new("second".to_string()); - - store_login_with_multiple_accounts_at_file( - &wallet_file, - account1.clone(), - hd_path.clone(), - login_id.clone(), - &password, - ) - .unwrap(); - - // Append a second mnenonic to the same login - append_account_to_login_at_file( - &wallet_file, - account2.clone(), - hd_path.clone(), - login_id.clone(), - appended_account.clone(), - &password, - ) - .unwrap(); - - // Update the password - update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); - - // Expect that we can load these 2 accounts with the new password - let loaded_login = - load_existing_login_at_file(&wallet_file, &login_id, &new_password).unwrap(); - let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); - let expected = vec![ - WalletAccount::new( - DEFAULT_FIRST_ACCOUNT_NAME.into(), - MnemonicAccount::new(account1, hd_path.clone()), - ), - WalletAccount::new(appended_account, MnemonicAccount::new(account2, hd_path)), - ] - .into(); - assert_eq!(loaded_accounts, &expected); - - // Expect that trying to load these 2 accounts with the old password fails - let err = load_existing_login_at_file(&wallet_file, &login_id, &password).unwrap_err(); - assert!(matches!(err, BackendError::StoreCipherError { .. })); - } - - #[test] - fn append_the_same_mnemonic_twice_fails() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - let id2 = AccountId::new("second".to_string()); - - store_login_with_multiple_accounts_at_file( - &wallet_file, - account1.clone(), - hd_path.clone(), - id1.clone(), - &password, - ) - .unwrap(); - - assert!(matches!( - append_account_to_login_at_file(&wallet_file, account1, hd_path, id1, id2, &password), - Err(BackendError::WalletMnemonicAlreadyExistsInWalletLogin), - )) - } - - #[test] - fn append_the_same_account_name_twice_fails() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let mnemonic1 = Mnemonic::generate(24).unwrap(); - let mnemonic2 = Mnemonic::generate(24).unwrap(); - let mnemonic3 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - // The top-level login id. NOTE: the first account id is always set to default. - let login_id = LoginId::new("my_login_id".to_string()); - - // Store the first account under login_id. The first account id is always set to default - // name. - store_login_with_multiple_accounts_at_file( - &wallet_file, - mnemonic1, - hd_path.clone(), - login_id.clone(), - &password, - ) - .unwrap(); - - // Append another account (account2) to the same login (login_id) - let account2 = AccountId::new("account_2".to_string()); - - append_account_to_login_at_file( - &wallet_file, - mnemonic2, - hd_path.clone(), - login_id.clone(), - account2.clone(), - &password, - ) - .unwrap(); - - // Appending the third account, with same account id will fail - assert!(matches!( - append_account_to_login_at_file( - &wallet_file, - mnemonic3, - hd_path, - login_id, - account2, - &password, - ), - Err(BackendError::WalletAccountIdAlreadyExistsInWalletLogin), - )) - } - - #[test] - fn delete_the_same_account_twice_for_a_login_fails() { - let store_dir = tempdir().unwrap(); - let wallet = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let account2 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - let id2 = AccountId::new("second".to_string()); - - store_login_at_file(&wallet, account1, hd_path.clone(), id1.clone(), &password).unwrap(); - - append_account_to_login_at_file( - &wallet, - account2, - hd_path, - id1.clone(), - id2.clone(), - &password, - ) - .unwrap(); - - remove_account_from_login_at_file(&wallet, &id1, &id2, &password).unwrap(); - - assert!(matches!( - remove_account_from_login_at_file(&wallet, &id1, &id2, &password), - Err(BackendError::WalletNoSuchAccountIdInWalletLogin), - )); - } - - #[test] - fn delete_the_same_account_twice_for_a_login_fails_with_multi() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let account2 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - let id2 = AccountId::new("second".to_string()); - - store_login_with_multiple_accounts_at_file( - &wallet_file, - account1, - hd_path.clone(), - id1.clone(), - &password, - ) - .unwrap(); - - append_account_to_login_at_file( - &wallet_file, - account2, - hd_path, - id1.clone(), - id2.clone(), - &password, - ) - .unwrap(); - - remove_account_from_login_at_file(&wallet_file, &id1, &id2, &password).unwrap(); - - assert!(matches!( - remove_account_from_login_at_file(&wallet_file, &id1, &id2, &password), - Err(BackendError::WalletNoSuchAccountIdInWalletLogin), - )); - } - - #[test] - fn delete_appended_account_doesnt_affect_others() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let account2 = Mnemonic::generate(24).unwrap(); - let account3 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - let id2 = LoginId::new("second".to_string()); - let id3 = AccountId::new("third".to_string()); - - store_login_at_file( - &wallet_file, - account1, - hd_path.clone(), - id1.clone(), - &password, - ) - .unwrap(); - - store_login_at_file( - &wallet_file, - account2.clone(), - hd_path.clone(), - id2.clone(), - &password, - ) - .unwrap(); - - append_account_to_login_at_file( - &wallet_file, - account3.clone(), - hd_path.clone(), - id2.clone(), - id3.clone(), - &password, - ) - .unwrap(); - - remove_login_at_file(&wallet_file, &id1).unwrap(); - - // The second login one is still there - let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); - let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); - let expected = vec![ - WalletAccount::new(id2.into(), MnemonicAccount::new(account2, hd_path.clone())), - WalletAccount::new(id3, MnemonicAccount::new(account3, hd_path)), - ] - .into(); - assert_eq!(loaded_accounts, &expected); - } - - #[test] - fn remove_all_accounts_for_a_login_removes_the_file_when_empty() { - let store_dir = tempdir().unwrap(); - let wallet = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let account2 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - let id2 = AccountId::new("second".to_string()); - - store_login_with_multiple_accounts_at_file( - &wallet, - account1, - hd_path.clone(), - id1.clone(), - &password, - ) - .unwrap(); - - append_account_to_login_at_file( - &wallet, - account2, - hd_path, - id1.clone(), - id2.clone(), - &password, - ) - .unwrap(); - - remove_account_from_login_at_file( - &wallet, - &id1, - &DEFAULT_FIRST_ACCOUNT_NAME.into(), - &password, - ) - .unwrap(); - remove_account_from_login_at_file(&wallet, &id1, &id2, &password).unwrap(); - - // The file should now be removed - assert!(!wallet.exists()); - - // And trying to load when the file is gone fails - assert!(matches!( - load_existing_login_at_file(&wallet, &id1, &password), - Err(BackendError::WalletFileNotFound), - )); - } - - #[test] - fn remove_all_accounts_for_a_login_removes_that_login() { - let store_dir = tempdir().unwrap(); - let wallet = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let account2 = Mnemonic::generate(24).unwrap(); - let account3 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - let id2 = AccountId::new("second".to_string()); - let id3 = LoginId::new("third".to_string()); - - store_login_with_multiple_accounts_at_file( - &wallet, - account1, - hd_path.clone(), - id1.clone(), - &password, - ) - .unwrap(); - - append_account_to_login_at_file( - &wallet, - account2, - hd_path.clone(), - id1.clone(), - id2.clone(), - &password, - ) - .unwrap(); - - store_login_with_multiple_accounts_at_file( - &wallet, - account3.clone(), - hd_path.clone(), - id3.clone(), - &password, - ) - .unwrap(); - - remove_account_from_login_at_file( - &wallet, - &id1, - &DEFAULT_FIRST_ACCOUNT_NAME.into(), - &password, - ) - .unwrap(); - remove_account_from_login_at_file(&wallet, &id1, &id2, &password).unwrap(); - - // And trying to load when the file is gone fails - assert!(matches!( - load_existing_login_at_file(&wallet, &id1, &password), - Err(BackendError::WalletNoSuchLoginId), - )); - - // The other login is still there - let loaded_login = load_existing_login_at_file(&wallet, &id3, &password).unwrap(); - let acc3 = loaded_login.as_multiple_accounts().unwrap(); - let expected = vec![WalletAccount::new( - DEFAULT_FIRST_ACCOUNT_NAME.into(), - MnemonicAccount::new(account3, hd_path), - )] - .into(); - assert_eq!(acc3, &expected); - } - - #[test] - fn append_accounts_and_remove_appended_accounts() { - let store_dir = tempdir().unwrap(); - let wallet = store_dir.path().join(WALLET_INFO_FILENAME); - let acc1 = Mnemonic::generate(24).unwrap(); - let acc2 = Mnemonic::generate(24).unwrap(); - let acc3 = Mnemonic::generate(24).unwrap(); - let acc4 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - let id2 = LoginId::new("second".to_string()); - let id3 = AccountId::new("third".to_string()); - let id4 = AccountId::new("fourth".to_string()); - - store_login_at_file( - &wallet, - acc1.clone(), - hd_path.clone(), - id1.clone(), - &password, - ) - .unwrap(); - - store_login_at_file( - &wallet, - acc2.clone(), - hd_path.clone(), - id2.clone(), - &password, - ) - .unwrap(); - - // Add a third and fourth mnenonic grouped together with the second one - append_account_to_login_at_file( - &wallet, - acc3, - hd_path.clone(), - id2.clone(), - id3.clone(), - &password, - ) - .unwrap(); - append_account_to_login_at_file( - &wallet, - acc4.clone(), - hd_path.clone(), - id2.clone(), - id4.clone(), - &password, - ) - .unwrap(); - - // Delete the third mnemonic, from the second login entry - remove_account_from_login_at_file(&wallet, &id2, &id3, &password).unwrap(); - - // Check that we can still load the other accounts - let loaded_login = load_existing_login_at_file(&wallet, &id2, &password).unwrap(); - let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); - let expected = vec![ - WalletAccount::new( - id2.clone().into(), - MnemonicAccount::new(acc2, hd_path.clone()), - ), - WalletAccount::new(id4.clone(), MnemonicAccount::new(acc4, hd_path.clone())), - ] - .into(); - assert_eq!(loaded_accounts, &expected); - - // Delete the second and fourth mnemonic from the second login entry removes the login entry - remove_account_from_login_at_file(&wallet, &id2, &id2.clone().into(), &password).unwrap(); - remove_account_from_login_at_file(&wallet, &id2, &id4, &password).unwrap(); - assert!(matches!( - load_existing_login_at_file(&wallet, &id2, &password), - Err(BackendError::WalletNoSuchLoginId), - )); - - // The first login is still available - let loaded_login = load_existing_login_at_file(&wallet, &id1, &password).unwrap(); - let account = loaded_login.as_mnemonic_account().unwrap(); - assert_eq!(account.mnemonic(), &acc1); - assert_eq!(account.hd_path(), &hd_path); - } - - #[test] - fn rename_first_account_in_login() { - let store_dir = tempdir().unwrap(); - let wallet = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let login_id = LoginId::new("first".to_string()); - - store_login_with_multiple_accounts_at_file( - &wallet, - account1.clone(), - hd_path.clone(), - login_id.clone(), - &password, - ) - .unwrap(); - - let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); - let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); - let expected = vec![WalletAccount::new( - DEFAULT_FIRST_ACCOUNT_NAME.into(), - MnemonicAccount::new(account1.clone(), hd_path.clone()), - )] - .into(); - assert_eq!(loaded_accounts, &expected); - - let renamed_account = AccountId::new("new_first".to_string()); - - rename_account_in_login_at_file( - &wallet, - &login_id, - &DEFAULT_FIRST_ACCOUNT_NAME.into(), - &renamed_account, - &password, - ) - .unwrap(); - - let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); - let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); - let expected = vec![WalletAccount::new( - renamed_account, - MnemonicAccount::new(account1, hd_path), - )] - .into(); - assert_eq!(loaded_accounts, &expected); - } - - #[test] - fn rename_one_account_in_login_with_two_accounts() { - let store_dir = tempdir().unwrap(); - let wallet = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let account2 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let login_id = LoginId::new("first".to_string()); - let account_id2 = AccountId::new("second".to_string()); - let renamed_account_id2 = AccountId::new("new_second".to_string()); - - store_login_with_multiple_accounts_at_file( - &wallet, - account1.clone(), - hd_path.clone(), - login_id.clone(), - &password, - ) - .unwrap(); - - append_account_to_login_at_file( - &wallet, - account2.clone(), - hd_path.clone(), - login_id.clone(), - account_id2.clone(), - &password, - ) - .unwrap(); - - // Load and confirm - let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); - let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); - let expected = vec![ - WalletAccount::new( - DEFAULT_FIRST_ACCOUNT_NAME.into(), - MnemonicAccount::new(account1.clone(), hd_path.clone()), - ), - WalletAccount::new( - account_id2.clone(), - MnemonicAccount::new(account2.clone(), hd_path.clone()), - ), - ] - .into(); - assert_eq!(loaded_accounts, &expected); - - // Rename the second account to a new name - rename_account_in_login_at_file( - &wallet, - &login_id, - &account_id2, - &renamed_account_id2, - &password, - ) - .unwrap(); - - // Load and confirm - let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); - let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); - let expected = vec![ - WalletAccount::new( - DEFAULT_FIRST_ACCOUNT_NAME.into(), - MnemonicAccount::new(account1, hd_path.clone()), - ), - WalletAccount::new(renamed_account_id2, MnemonicAccount::new(account2, hd_path)), - ] - .into(); - assert_eq!(loaded_accounts, &expected); - } - - #[test] - fn rename_account_into_existing_account_id_fails() { - let store_dir = tempdir().unwrap(); - let wallet = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let account2 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let login_id = LoginId::new("first".to_string()); - let account_id2 = AccountId::new("second".to_string()); - let renamed_account_id2 = DEFAULT_FIRST_ACCOUNT_NAME.into(); - - store_login_with_multiple_accounts_at_file( - &wallet, - account1.clone(), - hd_path.clone(), - login_id.clone(), - &password, - ) - .unwrap(); - - append_account_to_login_at_file( - &wallet, - account2.clone(), - hd_path.clone(), - login_id.clone(), - account_id2.clone(), - &password, - ) - .unwrap(); - - // Load and confirm - let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); - let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); - let expected = vec![ - WalletAccount::new( - DEFAULT_FIRST_ACCOUNT_NAME.into(), - MnemonicAccount::new(account1.clone(), hd_path.clone()), - ), - WalletAccount::new( - account_id2.clone(), - MnemonicAccount::new(account2.clone(), hd_path.clone()), - ), - ] - .into(); - assert_eq!(loaded_accounts, &expected); - - // Rename the second account to the name of the first one fails - assert!(matches!( - rename_account_in_login_at_file( - &wallet, - &login_id, - &account_id2, - &renamed_account_id2, - &password, - ), - Err(BackendError::WalletAccountIdAlreadyExistsInWalletLogin) - )); - - // Load and confirm nothing was changed - let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); - let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); - let expected = vec![ - WalletAccount::new( - DEFAULT_FIRST_ACCOUNT_NAME.into(), - MnemonicAccount::new(account1, hd_path.clone()), - ), - WalletAccount::new(account_id2, MnemonicAccount::new(account2, hd_path)), - ] - .into(); - assert_eq!(loaded_accounts, &expected); - } - - // Test to that decrypts a stored file from the repo, to make sure we are able to decrypt stored - // wallets created with older versions. - #[test] - fn decrypt_stored_wallet() { - const SAVED_WALLET: &str = "src/wallet_storage/test-data/saved-wallet.json"; - let wallet_file = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(SAVED_WALLET); - - let wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); - - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let bad_password = UserPassword::new("bad-password".to_string()); - let id1 = LoginId::new("first".to_string()); - let id2 = LoginId::new("second".to_string()); - - assert!(!wallet.password_can_decrypt_all(&bad_password)); - assert!(wallet.password_can_decrypt_all(&password)); - - let acc1 = wallet.decrypt_login(&id1, &password).unwrap(); - let acc2 = wallet.decrypt_login(&id2, &password).unwrap(); - - assert!(matches!(acc1, StoredLogin::Mnemonic(_))); - assert!(matches!(acc2, StoredLogin::Mnemonic(_))); - - let expected_acc1 = bip39::Mnemonic::from_str("country mean universe text phone begin deputy reject result good cram illness common cluster proud swamp digital patrol spread bar face december base kick").unwrap(); - let expected_acc2 = bip39::Mnemonic::from_str("home mansion start quiz dress decide hint second dragon sunny juice always steak real minimum art rival skin draw total pulp foot goddess agent").unwrap(); - - assert_eq!( - acc1.as_mnemonic_account().unwrap().mnemonic(), - &expected_acc1 - ); - assert_eq!(acc1.as_mnemonic_account().unwrap().hd_path(), &hd_path,); - - assert_eq!( - acc2.as_mnemonic_account().unwrap().mnemonic(), - &expected_acc2 - ); - assert_eq!(acc2.as_mnemonic_account().unwrap().hd_path(), &hd_path,); - } - - #[test] - fn decrypt_stored_wallet_1_0_4() { - const SAVED_WALLET: &str = "src/wallet_storage/test-data/saved-wallet-1.0.4.json"; - let wallet_file = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(SAVED_WALLET); - - let wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); - - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password11!".to_string()); - let bad_password = UserPassword::new("bad-password".to_string()); - let login_id = LoginId::new("default".to_string()); - - assert!(!wallet.password_can_decrypt_all(&bad_password)); - assert!(wallet.password_can_decrypt_all(&password)); - - let acc1 = wallet.decrypt_login(&login_id, &password).unwrap(); - - assert!(matches!(acc1, StoredLogin::Mnemonic(_))); - - let expected_acc1 = bip39::Mnemonic::from_str("arrow capable abstract industry elevator nominee december piece hotel feed lounge web faint sword veteran bundle hour page actual laptop horror gold test warrior").unwrap(); - - assert_eq!( - acc1.as_mnemonic_account().unwrap().mnemonic(), - &expected_acc1 - ); - assert_eq!(acc1.as_mnemonic_account().unwrap().hd_path(), &hd_path,); - } - - #[test] - fn decrypt_stored_wallet_1_0_5_with_multiple_accounts() { - const SAVED_WALLET: &str = "src/wallet_storage/test-data/saved-wallet-1.0.5.json"; - let wallet_file = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(SAVED_WALLET); - - let wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); - - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password11!".to_string()); - let bad_password = UserPassword::new("bad-password".to_string()); - let login_id = LoginId::new("default".to_string()); - - assert!(!wallet.password_can_decrypt_all(&bad_password)); - assert!(wallet.password_can_decrypt_all(&password)); - - let login = wallet.decrypt_login(&login_id, &password).unwrap(); - - assert!(matches!(login, StoredLogin::Multiple(_))); - - let login = login.as_multiple_accounts().unwrap(); - assert_eq!(login.len(), 4); - - let expected_mn1 = bip39::Mnemonic::from_str("arrow capable abstract industry elevator nominee december piece hotel feed lounge web faint sword veteran bundle hour page actual laptop horror gold test warrior").unwrap(); - let expected_mn2 = bip39::Mnemonic::from_str("border hurt skull lunar goddess second danger game dismiss exhaust oven thumb dog drama onion false orchard spice tent next predict invite cherry green").unwrap(); - let expected_mn3 = bip39::Mnemonic::from_str("gentle crowd rule snap girl urge flat jump winner cluster night sand museum stock grunt quick tree acquire traffic major awake tag rack peasant").unwrap(); - let expected_mn4 = bip39::Mnemonic::from_str("debris blue skin annual inhale text border rigid spatial lesson coconut yard horn crystal control survey version vote hawk neck frame arrive oblige width").unwrap(); - - let expected = vec![ - WalletAccount::new( - "default".into(), - MnemonicAccount::new(expected_mn1, hd_path.clone()), - ), - WalletAccount::new( - "account2".into(), - MnemonicAccount::new(expected_mn2, hd_path.clone()), - ), - WalletAccount::new( - "foobar".into(), - MnemonicAccount::new(expected_mn3, hd_path.clone()), - ), - WalletAccount::new("42".into(), MnemonicAccount::new(expected_mn4, hd_path)), - ] - .into(); - - assert_eq!(login, &expected); - } - - #[test] - fn append_filename() { - let wallet_file = PathBuf::from("/tmp/saved-wallet.json"); - let timestamp = OsString::from("42"); - #[cfg(target_family = "unix")] - assert_eq!( - append_timestamp_to_filename(wallet_file.clone(), timestamp.clone(), None) - .unwrap() - .into_os_string() - .into_string() - .unwrap(), - "/tmp/saved-wallet-42.json".to_string(), - ); - #[cfg(not(target_family = "unix"))] - assert_eq!( - append_timestamp_to_filename(wallet_file.clone(), timestamp.clone(), None) - .unwrap() - .into_os_string() - .into_string() - .unwrap(), - r"/tmp\saved-wallet-42.json".to_string(), - ); - - #[cfg(target_family = "unix")] - assert_eq!( - append_timestamp_to_filename(wallet_file, timestamp, Some(3)) - .unwrap() - .into_os_string() - .into_string() - .unwrap(), - "/tmp/saved-wallet-42-3.json".to_string(), - ); - #[cfg(not(target_family = "unix"))] - assert_eq!( - append_timestamp_to_filename(wallet_file, timestamp, Some(3)) - .unwrap() - .into_os_string() - .into_string() - .unwrap(), - r"/tmp\saved-wallet-42-3.json".to_string(), - ); - } -} +// #[cfg(test)] +// mod tests { +// use crate::wallet_storage::account_data::WalletAccount; + +// use super::*; +// use nym_config::defaults::COSMOS_DERIVATION_PATH; +// use std::str::FromStr; +// use tempfile::tempdir; + +// #[test] +// fn trying_to_load_nonexistant_wallet_fails() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let id1 = LoginId::new("first".to_string()); +// let password = UserPassword::new("password".to_string()); + +// assert!(matches!( +// load_existing_wallet_at_file(&wallet_file), +// Err(BackendError::WalletFileNotFound), +// )); +// assert!(matches!( +// load_existing_login_at_file(&wallet_file, &id1, &password), +// Err(BackendError::WalletFileNotFound), +// )); +// remove_login_at_file(&wallet_file, &id1).unwrap_err(); +// } + +// #[test] +// fn store_single_login() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); + +// store_login_at_file(&wallet_file, account1, hd_path, id1.clone(), &password).unwrap(); + +// let stored_wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); +// assert_eq!(stored_wallet.len(), 1); + +// let login = stored_wallet.get_encrypted_login_by_index(0).unwrap(); +// assert_eq!(login.id, id1); + +// // some actual ciphertext was saved +// assert!(!login.account.ciphertext().is_empty()); +// } + +// #[test] +// fn store_single_login_with_multi() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); + +// store_login_with_multiple_accounts_at_file( +// &wallet_file, +// account1, +// cosmos_hd_path, +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// let stored_wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); +// assert_eq!(stored_wallet.len(), 1); + +// let login = stored_wallet.get_encrypted_login_by_index(0).unwrap(); +// assert_eq!(login.id, id1); + +// // some actual ciphertext was saved +// assert!(!login.account.ciphertext().is_empty()); +// } + +// #[test] +// fn store_single_login_with_multi_then_update_pwd_and_load() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let new_password = UserPassword::new("new_password".to_string()); +// let id1 = LoginId::new("first".to_string()); + +// store_login_with_multiple_accounts_at_file( +// &wallet_file, +// account1, +// cosmos_hd_path, +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); + +// let stored_wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); +// assert_eq!(stored_wallet.len(), 1); + +// let login = stored_wallet.get_encrypted_login_by_index(0).unwrap(); +// assert_eq!(login.id, id1); + +// // some actual ciphertext was saved +// assert!(!login.account.ciphertext().is_empty()); +// } + +// #[test] +// fn store_twice_for_the_same_id_fails() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); + +// // Store the first login +// store_login_at_file( +// &wallet_file, +// account1.clone(), +// hd_path.clone(), +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// // and storing the same id again fails +// assert!(matches!( +// store_login_at_file(&wallet_file, account1, hd_path, id1, &password,), +// Err(BackendError::WalletLoginIdAlreadyExists), +// )); +// } + +// #[test] +// fn store_twice_for_the_same_id_fails_with_multiple() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); + +// // Store the first login +// store_login_with_multiple_accounts_at_file( +// &wallet_file, +// account1.clone(), +// hd_path.clone(), +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// // and storing the same id again fails +// assert!(matches!( +// store_login_with_multiple_accounts_at_file( +// &wallet_file, +// account1, +// hd_path, +// id1, +// &password, +// ), +// Err(BackendError::WalletLoginIdAlreadyExists), +// )); +// } + +// #[test] +// fn load_with_wrong_password_fails() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let bad_password = UserPassword::new("bad-password".to_string()); +// let id1 = LoginId::new("first".to_string()); + +// store_login_at_file(&wallet_file, account1, hd_path, id1.clone(), &password).unwrap(); + +// // Trying to load it with wrong password now fails +// assert!(matches!( +// load_existing_login_at_file(&wallet_file, &id1, &bad_password), +// Err(BackendError::StoreCipherError { .. }), +// )); +// } + +// #[test] +// fn load_with_wrong_password_fails_with_multi() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let bad_password = UserPassword::new("bad-password".to_string()); +// let id1 = LoginId::new("first".to_string()); + +// store_login_with_multiple_accounts_at_file( +// &wallet_file, +// account1, +// hd_path, +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// // Trying to load it with wrong password now fails +// assert!(matches!( +// load_existing_login_at_file(&wallet_file, &id1, &bad_password), +// Err(BackendError::StoreCipherError { .. }), +// )); +// } + +// #[test] +// fn load_with_wrong_id_fails() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); +// let id2 = LoginId::new("second".to_string()); + +// store_login_at_file(&wallet_file, account1, hd_path, id1, &password).unwrap(); + +// // Trying to load with the wrong id +// assert!(matches!( +// load_existing_login_at_file(&wallet_file, &id2, &password), +// Err(BackendError::WalletNoSuchLoginId), +// )); +// } + +// #[test] +// fn load_with_wrong_id_fails_with_multi() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); +// let id2 = LoginId::new("second".to_string()); + +// store_login_with_multiple_accounts_at_file(&wallet_file, account1, hd_path, id1, &password) +// .unwrap(); + +// // Trying to load with the wrong id +// assert!(matches!( +// load_existing_login_at_file(&wallet_file, &id2, &password), +// Err(BackendError::WalletNoSuchLoginId), +// )); +// } + +// #[test] +// fn store_and_load_a_single_login() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); + +// store_login_at_file( +// &wallet_file, +// account1.clone(), +// hd_path.clone(), +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); +// let acc = loaded_login.as_mnemonic_account().unwrap(); +// assert_eq!(&account1, acc.mnemonic()); +// assert_eq!(&hd_path, acc.hd_path()); +// } + +// #[test] +// fn store_a_single_login_then_update_pwd_and_load() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let new_password = UserPassword::new("new_password".to_string()); +// let id1 = LoginId::new("first".to_string()); + +// store_login_at_file( +// &wallet_file, +// account1.clone(), +// hd_path.clone(), +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); + +// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &new_password).unwrap(); +// let acc = loaded_login.as_mnemonic_account().unwrap(); +// assert_eq!(&account1, acc.mnemonic()); +// assert_eq!(&hd_path, acc.hd_path()); +// } + +// #[test] +// fn store_a_single_login_then_update_pwd_with_identical_pwd_is_noop_but_okay() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); + +// store_login_at_file( +// &wallet_file, +// account1.clone(), +// hd_path.clone(), +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// update_encrypted_logins_at_file(&wallet_file, &password, &password).unwrap(); + +// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); +// let acc = loaded_login.as_mnemonic_account().unwrap(); +// assert_eq!(&account1, acc.mnemonic()); +// assert_eq!(&hd_path, acc.hd_path()); +// } + +// #[test] +// fn store_a_single_login_then_update_pwd_with_wrong_current_pwd_fails() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let new_password = UserPassword::new("new_password".to_string()); +// let wrong_password = UserPassword::new("wrong_password".to_string()); +// let id1 = LoginId::new("first".to_string()); + +// store_login_at_file(&wallet_file, account1, hd_path, id1, &password).unwrap(); + +// let err = update_encrypted_logins_at_file(&wallet_file, &wrong_password, &new_password) +// .unwrap_err(); +// assert!(matches!(err, BackendError::StoreCipherError { .. })); +// } + +// #[test] +// fn store_a_single_login_then_update_pwd_and_load_with_wrong_pwd_fails() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let new_password = UserPassword::new("new_password".to_string()); +// let id1 = LoginId::new("first".to_string()); + +// store_login_at_file(&wallet_file, account1, hd_path, id1.clone(), &password).unwrap(); + +// update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); + +// let err = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap_err(); +// assert!(matches!(err, BackendError::StoreCipherError { .. })); +// } + +// #[test] +// fn store_and_load_a_single_login_with_multi() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let acc1 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); + +// store_login_with_multiple_accounts_at_file( +// &wallet_file, +// acc1.clone(), +// hd_path.clone(), +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); +// let accounts = loaded_login.as_multiple_accounts().unwrap(); +// assert_eq!(accounts.len(), 1); +// let account = accounts +// .get_account(&DEFAULT_FIRST_ACCOUNT_NAME.into()) +// .unwrap(); +// assert_eq!(account.id().as_ref(), DEFAULT_FIRST_ACCOUNT_NAME); +// assert_eq!(account.mnemonic(), &acc1); +// assert_eq!(account.hd_path(), &hd_path); +// } + +// #[test] +// fn store_a_single_login_with_multi_then_update_pwd_and_load() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let acc1 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let new_password = UserPassword::new("new_password".to_string()); +// let id1 = LoginId::new("first".to_string()); + +// store_login_with_multiple_accounts_at_file( +// &wallet_file, +// acc1.clone(), +// hd_path.clone(), +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); + +// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &new_password).unwrap(); +// let accounts = loaded_login.as_multiple_accounts().unwrap(); +// assert_eq!(accounts.len(), 1); +// let account = accounts +// .get_account(&DEFAULT_FIRST_ACCOUNT_NAME.into()) +// .unwrap(); +// assert_eq!(account.id().as_ref(), DEFAULT_FIRST_ACCOUNT_NAME); +// assert_eq!(account.mnemonic(), &acc1); +// assert_eq!(account.hd_path(), &hd_path); +// } + +// #[test] +// fn store_a_single_login_with_multi_then_update_pwd_with_wrong_current_pwd_fails() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let acc1 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let new_password = UserPassword::new("new_password".to_string()); +// let wrong_password = UserPassword::new("wrong_password".to_string()); +// let id1 = LoginId::new("first".to_string()); + +// store_login_with_multiple_accounts_at_file(&wallet_file, acc1, hd_path, id1, &password) +// .unwrap(); + +// let err = update_encrypted_logins_at_file(&wallet_file, &wrong_password, &new_password) +// .unwrap_err(); +// assert!(matches!(err, BackendError::StoreCipherError { .. })); +// } + +// #[test] +// fn store_a_single_login_with_multi_then_update_pwd_and_load_with_wrong_pwd_fails() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let acc1 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let new_password = UserPassword::new("new_password".to_string()); +// let id1 = LoginId::new("first".to_string()); + +// store_login_with_multiple_accounts_at_file( +// &wallet_file, +// acc1, +// hd_path, +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); + +// let err = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap_err(); +// assert!(matches!(err, BackendError::StoreCipherError { .. })); +// } + +// #[test] +// fn store_a_second_login_with_a_different_password_fails() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let account2 = Mnemonic::generate(24).unwrap(); +// let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let bad_password = UserPassword::new("bad-password".to_string()); +// let id1 = LoginId::new("first".to_string()); +// let id2 = LoginId::new("second".to_string()); + +// store_login_at_file( +// &wallet_file, +// account1, +// cosmos_hd_path.clone(), +// id1, +// &password, +// ) +// .unwrap(); + +// // Can't store a second login if you use different password +// assert!(matches!( +// store_login_at_file(&wallet_file, account2, cosmos_hd_path, id2, &bad_password), +// Err(BackendError::WalletDifferentPasswordDetected), +// )); +// } + +// #[test] +// fn store_a_second_login_with_a_different_password_fails_with_multi() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let account2 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let bad_password = UserPassword::new("bad-password".to_string()); +// let id1 = LoginId::new("first".to_string()); +// let id2 = LoginId::new("second".to_string()); + +// store_login_with_multiple_accounts_at_file( +// &wallet_file, +// account1, +// hd_path.clone(), +// id1, +// &password, +// ) +// .unwrap(); + +// // Can't store a second login if you use different password +// assert!(matches!( +// store_login_with_multiple_accounts_at_file( +// &wallet_file, +// account2, +// hd_path, +// id2, +// &bad_password +// ), +// Err(BackendError::WalletDifferentPasswordDetected), +// )); +// } + +// #[test] +// fn store_two_mnemonic_accounts_gives_different_salts_and_iv() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let account2 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let different_hd_path: DerivationPath = "m".parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); +// let id2 = LoginId::new("second".to_string()); + +// // Store the first account +// store_login_at_file(&wallet_file, account1, hd_path, id1, &password).unwrap(); + +// let stored_wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); +// let encrypted_blob = &stored_wallet +// .get_encrypted_login_by_index(0) +// .unwrap() +// .account; + +// // keep track of salt and iv for future assertion +// let original_iv = encrypted_blob.iv().to_vec(); +// let original_salt = encrypted_blob.salt().to_vec(); + +// // Add an extra account +// store_login_at_file(&wallet_file, account2, different_hd_path, id2, &password).unwrap(); + +// let loaded_accounts = load_existing_wallet_at_file(&wallet_file).unwrap(); +// assert_eq!(loaded_accounts.len(), 2); +// let encrypted_blob = &loaded_accounts +// .get_encrypted_login_by_index(1) +// .unwrap() +// .account; + +// // fresh IV and salt are used +// assert_ne!(original_iv, encrypted_blob.iv()); +// assert_ne!(original_salt, encrypted_blob.salt()); +// } + +// #[test] +// fn store_two_mnemonic_accounts_using_two_logins() { +// let store_dir = tempdir().unwrap(); +// let wallet = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let account2 = Mnemonic::generate(24).unwrap(); +// let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let different_hd_path: DerivationPath = "m".parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); +// let id2 = LoginId::new("second".to_string()); + +// // Store the first account +// store_login_at_file( +// &wallet, +// account1.clone(), +// cosmos_hd_path.clone(), +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// let login = load_existing_login_at_file(&wallet, &id1, &password).unwrap(); +// let acc = login.as_mnemonic_account().unwrap(); +// assert_eq!(&account1, acc.mnemonic()); +// assert_eq!(&cosmos_hd_path, acc.hd_path()); + +// // Add an extra account +// store_login_at_file( +// &wallet, +// account2.clone(), +// different_hd_path.clone(), +// id2.clone(), +// &password, +// ) +// .unwrap(); + +// // first account should be unchanged +// let loaded_login = load_existing_login_at_file(&wallet, &id1, &password).unwrap(); +// let acc1 = loaded_login.as_mnemonic_account().unwrap(); +// assert_eq!(&account1, acc1.mnemonic()); +// assert_eq!(&cosmos_hd_path, acc1.hd_path()); + +// let loaded_login = load_existing_login_at_file(&wallet, &id2, &password).unwrap(); +// let acc2 = loaded_login.as_mnemonic_account().unwrap(); +// assert_eq!(&account2, acc2.mnemonic()); +// assert_eq!(&different_hd_path, acc2.hd_path()); +// } + +// #[test] +// fn store_two_mnemonic_accounts_using_two_logins_then_update_pwd_and_load() { +// let store_dir = tempdir().unwrap(); +// let wallet = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let account2 = Mnemonic::generate(24).unwrap(); +// let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let different_hd_path: DerivationPath = "m".parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let new_password = UserPassword::new("new_password".to_string()); +// let id1 = LoginId::new("first".to_string()); +// let id2 = LoginId::new("second".to_string()); + +// // Store the first login with an account +// store_login_at_file( +// &wallet, +// account1.clone(), +// cosmos_hd_path.clone(), +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// let login = load_existing_login_at_file(&wallet, &id1, &password).unwrap(); +// let acc = login.as_mnemonic_account().unwrap(); +// assert_eq!(&account1, acc.mnemonic()); +// assert_eq!(&cosmos_hd_path, acc.hd_path()); + +// // Store a second login, also with an account +// store_login_at_file( +// &wallet, +// account2.clone(), +// different_hd_path.clone(), +// id2.clone(), +// &password, +// ) +// .unwrap(); + +// update_encrypted_logins_at_file(&wallet, &password, &new_password).unwrap(); + +// // first account should be unchanged +// let loaded_login = load_existing_login_at_file(&wallet, &id1, &new_password).unwrap(); +// let acc1 = loaded_login.as_mnemonic_account().unwrap(); +// assert_eq!(&account1, acc1.mnemonic()); +// assert_eq!(&cosmos_hd_path, acc1.hd_path()); + +// let loaded_login = load_existing_login_at_file(&wallet, &id2, &new_password).unwrap(); +// let acc2 = loaded_login.as_mnemonic_account().unwrap(); +// assert_eq!(&account2, acc2.mnemonic()); +// assert_eq!(&different_hd_path, acc2.hd_path()); +// } + +// #[test] +// fn store_one_mnemonic_account_and_one_multi_account() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let account2 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let different_hd_path: DerivationPath = "m".parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); +// let id2 = LoginId::new("second".to_string()); + +// // Store the first account +// store_login_at_file( +// &wallet_file, +// account1.clone(), +// hd_path.clone(), +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); +// let acc = loaded_login.as_mnemonic_account().unwrap(); +// assert_eq!(&account1, acc.mnemonic()); +// assert_eq!(&hd_path, acc.hd_path()); + +// // Add an extra account +// store_login_with_multiple_accounts_at_file( +// &wallet_file, +// account2.clone(), +// different_hd_path.clone(), +// id2.clone(), +// &password, +// ) +// .unwrap(); + +// // first account should be unchanged +// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); +// let acc1 = loaded_login.as_mnemonic_account().unwrap(); +// assert_eq!(&account1, acc1.mnemonic()); +// assert_eq!(&hd_path, acc1.hd_path()); + +// let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); +// let acc2 = loaded_login.as_multiple_accounts().unwrap(); +// assert_eq!(acc2.len(), 1); +// let account = acc2 +// .get_account(&DEFAULT_FIRST_ACCOUNT_NAME.into()) +// .unwrap(); +// assert_eq!(account.id().as_ref(), DEFAULT_FIRST_ACCOUNT_NAME); +// assert_eq!(account.mnemonic(), &account2); +// assert_eq!(account.hd_path(), &different_hd_path); +// } + +// #[test] +// fn remove_non_existent_id_fails() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); +// let id2 = LoginId::new("second".to_string()); + +// store_login_with_multiple_accounts_at_file(&wallet_file, account1, hd_path, id1, &password) +// .unwrap(); + +// // Fails to delete non-existent id in the wallet +// assert!(matches!( +// remove_login_at_file(&wallet_file, &id2), +// Err(BackendError::WalletNoSuchLoginId), +// )); +// } + +// #[test] +// fn store_and_remove_wallet_login_information() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let account2 = Mnemonic::generate(24).unwrap(); +// let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let different_hd_path: DerivationPath = "m".parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); +// let id2 = LoginId::new("second".to_string()); + +// // Store two accounts with two different passwords +// store_login_at_file( +// &wallet_file, +// account1.clone(), +// cosmos_hd_path.clone(), +// id1.clone(), +// &password, +// ) +// .unwrap(); +// store_login_at_file( +// &wallet_file, +// account2.clone(), +// different_hd_path.clone(), +// id2.clone(), +// &password, +// ) +// .unwrap(); + +// // Load and compare +// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); +// let acc1 = loaded_login.as_mnemonic_account().unwrap(); +// assert_eq!(&account1, acc1.mnemonic()); +// assert_eq!(&cosmos_hd_path, acc1.hd_path()); + +// let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); +// let acc2 = loaded_login.as_mnemonic_account().unwrap(); +// assert_eq!(&account2, acc2.mnemonic()); +// assert_eq!(&different_hd_path, acc2.hd_path()); + +// // Delete the second account +// remove_login_at_file(&wallet_file, &id2).unwrap(); + +// // The first account should be unchanged +// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); +// let acc1 = loaded_login.as_mnemonic_account().unwrap(); +// assert_eq!(&account1, acc1.mnemonic()); +// assert_eq!(&cosmos_hd_path, acc1.hd_path()); + +// // And we can't load the second one anymore +// assert!(matches!( +// load_existing_login_at_file(&wallet_file, &id2, &password), +// Err(BackendError::WalletNoSuchLoginId), +// )); + +// // Delete the first account +// assert!(wallet_file.exists()); +// remove_login_at_file(&wallet_file, &id1).unwrap(); + +// // The file should now be removed +// assert!(!wallet_file.exists()); + +// // And trying to load when the file is gone fails +// assert!(matches!( +// load_existing_login_at_file(&wallet_file, &id1, &password), +// Err(BackendError::WalletFileNotFound), +// )); +// } + +// #[test] +// fn append_account_converts_the_type() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let account2 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); +// let id2 = AccountId::new("second".to_string()); + +// store_login_at_file( +// &wallet_file, +// account1.clone(), +// hd_path.clone(), +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// // Check that it's there as the correct non-multiple type +// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); +// let acc = loaded_login.as_mnemonic_account().unwrap(); +// assert_eq!(acc.mnemonic(), &account1); +// assert_eq!(acc.hd_path(), &hd_path); + +// append_account_to_login_at_file( +// &wallet_file, +// account2.clone(), +// hd_path.clone(), +// id1.clone(), +// id2.clone(), +// &password, +// ) +// .unwrap(); + +// // Check that it is now multiple mnemonic type +// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); +// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); +// let expected = vec![ +// WalletAccount::new(id1.into(), MnemonicAccount::new(account1, hd_path.clone())), +// WalletAccount::new(id2, MnemonicAccount::new(account2, hd_path)), +// ] +// .into(); +// assert_eq!(loaded_accounts, &expected); +// } + +// #[test] +// fn append_accounts_to_existing_login() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let account2 = Mnemonic::generate(24).unwrap(); +// let account3 = Mnemonic::generate(24).unwrap(); +// let account4 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); +// let id2 = LoginId::new("second".to_string()); +// let id3 = AccountId::new("third".to_string()); +// let id4 = AccountId::new("fourth".to_string()); + +// store_login_at_file( +// &wallet_file, +// account1.clone(), +// hd_path.clone(), +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// store_login_at_file( +// &wallet_file, +// account2.clone(), +// hd_path.clone(), +// id2.clone(), +// &password, +// ) +// .unwrap(); + +// // Check that it's there as the correct non-multiple type +// let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); +// let acc2 = loaded_login.as_mnemonic_account().unwrap(); +// assert_eq!(acc2.mnemonic(), &account2); +// assert_eq!(acc2.hd_path(), &hd_path); + +// // Add a third and fourth mnenonic grouped together with the second one +// append_account_to_login_at_file( +// &wallet_file, +// account3.clone(), +// hd_path.clone(), +// id2.clone(), +// id3.clone(), +// &password, +// ) +// .unwrap(); +// append_account_to_login_at_file( +// &wallet_file, +// account4.clone(), +// hd_path.clone(), +// id2.clone(), +// id4.clone(), +// &password, +// ) +// .unwrap(); + +// // Check that we can load all four +// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); +// let acc1 = loaded_login.as_mnemonic_account().unwrap(); +// assert_eq!(acc1.mnemonic(), &account1); +// assert_eq!(acc1.hd_path(), &hd_path); + +// let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); +// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); +// let expected = vec![ +// WalletAccount::new(id2.into(), MnemonicAccount::new(account2, hd_path.clone())), +// WalletAccount::new(id3, MnemonicAccount::new(account3, hd_path.clone())), +// WalletAccount::new(id4, MnemonicAccount::new(account4, hd_path)), +// ] +// .into(); +// assert_eq!(loaded_accounts, &expected); +// } + +// #[test] +// fn append_accounts_to_existing_login_with_multi() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let account2 = Mnemonic::generate(24).unwrap(); +// let account3 = Mnemonic::generate(24).unwrap(); +// let account4 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); +// let id2 = LoginId::new("second".to_string()); +// let id3 = AccountId::new("third".to_string()); +// let id4 = AccountId::new("fourth".to_string()); + +// store_login_with_multiple_accounts_at_file( +// &wallet_file, +// account1.clone(), +// hd_path.clone(), +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// store_login_with_multiple_accounts_at_file( +// &wallet_file, +// account2.clone(), +// hd_path.clone(), +// id2.clone(), +// &password, +// ) +// .unwrap(); + +// // Add a third and fourth mnenonic grouped together with the second one +// append_account_to_login_at_file( +// &wallet_file, +// account3.clone(), +// hd_path.clone(), +// id2.clone(), +// id3.clone(), +// &password, +// ) +// .unwrap(); +// append_account_to_login_at_file( +// &wallet_file, +// account4.clone(), +// hd_path.clone(), +// id2.clone(), +// id4.clone(), +// &password, +// ) +// .unwrap(); + +// // Check that we can load all four +// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); +// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); +// let expected = vec![WalletAccount::new( +// DEFAULT_FIRST_ACCOUNT_NAME.into(), +// MnemonicAccount::new(account1, hd_path.clone()), +// )] +// .into(); +// assert_eq!(loaded_accounts, &expected); + +// let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); +// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); +// let expected = vec![ +// WalletAccount::new( +// DEFAULT_FIRST_ACCOUNT_NAME.into(), +// MnemonicAccount::new(account2, hd_path.clone()), +// ), +// WalletAccount::new(id3, MnemonicAccount::new(account3, hd_path.clone())), +// WalletAccount::new(id4, MnemonicAccount::new(account4, hd_path)), +// ] +// .into(); +// assert_eq!(loaded_accounts, &expected); +// } + +// #[test] +// fn append_account_to_existing_login_with_multi_then_update_pwd_and_load() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let account2 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let new_password = UserPassword::new("new_password".to_string()); +// let login_id = LoginId::new("first".to_string()); +// let appended_account = AccountId::new("second".to_string()); + +// store_login_with_multiple_accounts_at_file( +// &wallet_file, +// account1.clone(), +// hd_path.clone(), +// login_id.clone(), +// &password, +// ) +// .unwrap(); + +// // Append a second mnenonic to the same login +// append_account_to_login_at_file( +// &wallet_file, +// account2.clone(), +// hd_path.clone(), +// login_id.clone(), +// appended_account.clone(), +// &password, +// ) +// .unwrap(); + +// // Update the password +// update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); + +// // Expect that we can load these 2 accounts with the new password +// let loaded_login = +// load_existing_login_at_file(&wallet_file, &login_id, &new_password).unwrap(); +// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); +// let expected = vec![ +// WalletAccount::new( +// DEFAULT_FIRST_ACCOUNT_NAME.into(), +// MnemonicAccount::new(account1, hd_path.clone()), +// ), +// WalletAccount::new(appended_account, MnemonicAccount::new(account2, hd_path)), +// ] +// .into(); +// assert_eq!(loaded_accounts, &expected); + +// // Expect that trying to load these 2 accounts with the old password fails +// let err = load_existing_login_at_file(&wallet_file, &login_id, &password).unwrap_err(); +// assert!(matches!(err, BackendError::StoreCipherError { .. })); +// } + +// #[test] +// fn append_the_same_mnemonic_twice_fails() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); +// let id2 = AccountId::new("second".to_string()); + +// store_login_with_multiple_accounts_at_file( +// &wallet_file, +// account1.clone(), +// hd_path.clone(), +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// assert!(matches!( +// append_account_to_login_at_file(&wallet_file, account1, hd_path, id1, id2, &password), +// Err(BackendError::WalletMnemonicAlreadyExistsInWalletLogin), +// )) +// } + +// #[test] +// fn append_the_same_account_name_twice_fails() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let mnemonic1 = Mnemonic::generate(24).unwrap(); +// let mnemonic2 = Mnemonic::generate(24).unwrap(); +// let mnemonic3 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// // The top-level login id. NOTE: the first account id is always set to default. +// let login_id = LoginId::new("my_login_id".to_string()); + +// // Store the first account under login_id. The first account id is always set to default +// // name. +// store_login_with_multiple_accounts_at_file( +// &wallet_file, +// mnemonic1, +// hd_path.clone(), +// login_id.clone(), +// &password, +// ) +// .unwrap(); + +// // Append another account (account2) to the same login (login_id) +// let account2 = AccountId::new("account_2".to_string()); + +// append_account_to_login_at_file( +// &wallet_file, +// mnemonic2, +// hd_path.clone(), +// login_id.clone(), +// account2.clone(), +// &password, +// ) +// .unwrap(); + +// // Appending the third account, with same account id will fail +// assert!(matches!( +// append_account_to_login_at_file( +// &wallet_file, +// mnemonic3, +// hd_path, +// login_id, +// account2, +// &password, +// ), +// Err(BackendError::WalletAccountIdAlreadyExistsInWalletLogin), +// )) +// } + +// #[test] +// fn delete_the_same_account_twice_for_a_login_fails() { +// let store_dir = tempdir().unwrap(); +// let wallet = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let account2 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); +// let id2 = AccountId::new("second".to_string()); + +// store_login_at_file(&wallet, account1, hd_path.clone(), id1.clone(), &password).unwrap(); + +// append_account_to_login_at_file( +// &wallet, +// account2, +// hd_path, +// id1.clone(), +// id2.clone(), +// &password, +// ) +// .unwrap(); + +// remove_account_from_login_at_file(&wallet, &id1, &id2, &password).unwrap(); + +// assert!(matches!( +// remove_account_from_login_at_file(&wallet, &id1, &id2, &password), +// Err(BackendError::WalletNoSuchAccountIdInWalletLogin), +// )); +// } + +// #[test] +// fn delete_the_same_account_twice_for_a_login_fails_with_multi() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let account2 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); +// let id2 = AccountId::new("second".to_string()); + +// store_login_with_multiple_accounts_at_file( +// &wallet_file, +// account1, +// hd_path.clone(), +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// append_account_to_login_at_file( +// &wallet_file, +// account2, +// hd_path, +// id1.clone(), +// id2.clone(), +// &password, +// ) +// .unwrap(); + +// remove_account_from_login_at_file(&wallet_file, &id1, &id2, &password).unwrap(); + +// assert!(matches!( +// remove_account_from_login_at_file(&wallet_file, &id1, &id2, &password), +// Err(BackendError::WalletNoSuchAccountIdInWalletLogin), +// )); +// } + +// #[test] +// fn delete_appended_account_doesnt_affect_others() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let account2 = Mnemonic::generate(24).unwrap(); +// let account3 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); +// let id2 = LoginId::new("second".to_string()); +// let id3 = AccountId::new("third".to_string()); + +// store_login_at_file( +// &wallet_file, +// account1, +// hd_path.clone(), +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// store_login_at_file( +// &wallet_file, +// account2.clone(), +// hd_path.clone(), +// id2.clone(), +// &password, +// ) +// .unwrap(); + +// append_account_to_login_at_file( +// &wallet_file, +// account3.clone(), +// hd_path.clone(), +// id2.clone(), +// id3.clone(), +// &password, +// ) +// .unwrap(); + +// remove_login_at_file(&wallet_file, &id1).unwrap(); + +// // The second login one is still there +// let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); +// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); +// let expected = vec![ +// WalletAccount::new(id2.into(), MnemonicAccount::new(account2, hd_path.clone())), +// WalletAccount::new(id3, MnemonicAccount::new(account3, hd_path)), +// ] +// .into(); +// assert_eq!(loaded_accounts, &expected); +// } + +// #[test] +// fn remove_all_accounts_for_a_login_removes_the_file_when_empty() { +// let store_dir = tempdir().unwrap(); +// let wallet = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let account2 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); +// let id2 = AccountId::new("second".to_string()); + +// store_login_with_multiple_accounts_at_file( +// &wallet, +// account1, +// hd_path.clone(), +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// append_account_to_login_at_file( +// &wallet, +// account2, +// hd_path, +// id1.clone(), +// id2.clone(), +// &password, +// ) +// .unwrap(); + +// remove_account_from_login_at_file( +// &wallet, +// &id1, +// &DEFAULT_FIRST_ACCOUNT_NAME.into(), +// &password, +// ) +// .unwrap(); +// remove_account_from_login_at_file(&wallet, &id1, &id2, &password).unwrap(); + +// // The file should now be removed +// assert!(!wallet.exists()); + +// // And trying to load when the file is gone fails +// assert!(matches!( +// load_existing_login_at_file(&wallet, &id1, &password), +// Err(BackendError::WalletFileNotFound), +// )); +// } + +// #[test] +// fn remove_all_accounts_for_a_login_removes_that_login() { +// let store_dir = tempdir().unwrap(); +// let wallet = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let account2 = Mnemonic::generate(24).unwrap(); +// let account3 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); +// let id2 = AccountId::new("second".to_string()); +// let id3 = LoginId::new("third".to_string()); + +// store_login_with_multiple_accounts_at_file( +// &wallet, +// account1, +// hd_path.clone(), +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// append_account_to_login_at_file( +// &wallet, +// account2, +// hd_path.clone(), +// id1.clone(), +// id2.clone(), +// &password, +// ) +// .unwrap(); + +// store_login_with_multiple_accounts_at_file( +// &wallet, +// account3.clone(), +// hd_path.clone(), +// id3.clone(), +// &password, +// ) +// .unwrap(); + +// remove_account_from_login_at_file( +// &wallet, +// &id1, +// &DEFAULT_FIRST_ACCOUNT_NAME.into(), +// &password, +// ) +// .unwrap(); +// remove_account_from_login_at_file(&wallet, &id1, &id2, &password).unwrap(); + +// // And trying to load when the file is gone fails +// assert!(matches!( +// load_existing_login_at_file(&wallet, &id1, &password), +// Err(BackendError::WalletNoSuchLoginId), +// )); + +// // The other login is still there +// let loaded_login = load_existing_login_at_file(&wallet, &id3, &password).unwrap(); +// let acc3 = loaded_login.as_multiple_accounts().unwrap(); +// let expected = vec![WalletAccount::new( +// DEFAULT_FIRST_ACCOUNT_NAME.into(), +// MnemonicAccount::new(account3, hd_path), +// )] +// .into(); +// assert_eq!(acc3, &expected); +// } + +// #[test] +// fn append_accounts_and_remove_appended_accounts() { +// let store_dir = tempdir().unwrap(); +// let wallet = store_dir.path().join(WALLET_INFO_FILENAME); +// let acc1 = Mnemonic::generate(24).unwrap(); +// let acc2 = Mnemonic::generate(24).unwrap(); +// let acc3 = Mnemonic::generate(24).unwrap(); +// let acc4 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); +// let id2 = LoginId::new("second".to_string()); +// let id3 = AccountId::new("third".to_string()); +// let id4 = AccountId::new("fourth".to_string()); + +// store_login_at_file( +// &wallet, +// acc1.clone(), +// hd_path.clone(), +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// store_login_at_file( +// &wallet, +// acc2.clone(), +// hd_path.clone(), +// id2.clone(), +// &password, +// ) +// .unwrap(); + +// // Add a third and fourth mnenonic grouped together with the second one +// append_account_to_login_at_file( +// &wallet, +// acc3, +// hd_path.clone(), +// id2.clone(), +// id3.clone(), +// &password, +// ) +// .unwrap(); +// append_account_to_login_at_file( +// &wallet, +// acc4.clone(), +// hd_path.clone(), +// id2.clone(), +// id4.clone(), +// &password, +// ) +// .unwrap(); + +// // Delete the third mnemonic, from the second login entry +// remove_account_from_login_at_file(&wallet, &id2, &id3, &password).unwrap(); + +// // Check that we can still load the other accounts +// let loaded_login = load_existing_login_at_file(&wallet, &id2, &password).unwrap(); +// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); +// let expected = vec![ +// WalletAccount::new( +// id2.clone().into(), +// MnemonicAccount::new(acc2, hd_path.clone()), +// ), +// WalletAccount::new(id4.clone(), MnemonicAccount::new(acc4, hd_path.clone())), +// ] +// .into(); +// assert_eq!(loaded_accounts, &expected); + +// // Delete the second and fourth mnemonic from the second login entry removes the login entry +// remove_account_from_login_at_file(&wallet, &id2, &id2.clone().into(), &password).unwrap(); +// remove_account_from_login_at_file(&wallet, &id2, &id4, &password).unwrap(); +// assert!(matches!( +// load_existing_login_at_file(&wallet, &id2, &password), +// Err(BackendError::WalletNoSuchLoginId), +// )); + +// // The first login is still available +// let loaded_login = load_existing_login_at_file(&wallet, &id1, &password).unwrap(); +// let account = loaded_login.as_mnemonic_account().unwrap(); +// assert_eq!(account.mnemonic(), &acc1); +// assert_eq!(account.hd_path(), &hd_path); +// } + +// #[test] +// fn rename_first_account_in_login() { +// let store_dir = tempdir().unwrap(); +// let wallet = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let login_id = LoginId::new("first".to_string()); + +// store_login_with_multiple_accounts_at_file( +// &wallet, +// account1.clone(), +// hd_path.clone(), +// login_id.clone(), +// &password, +// ) +// .unwrap(); + +// let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); +// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); +// let expected = vec![WalletAccount::new( +// DEFAULT_FIRST_ACCOUNT_NAME.into(), +// MnemonicAccount::new(account1.clone(), hd_path.clone()), +// )] +// .into(); +// assert_eq!(loaded_accounts, &expected); + +// let renamed_account = AccountId::new("new_first".to_string()); + +// rename_account_in_login_at_file( +// &wallet, +// &login_id, +// &DEFAULT_FIRST_ACCOUNT_NAME.into(), +// &renamed_account, +// &password, +// ) +// .unwrap(); + +// let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); +// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); +// let expected = vec![WalletAccount::new( +// renamed_account, +// MnemonicAccount::new(account1, hd_path), +// )] +// .into(); +// assert_eq!(loaded_accounts, &expected); +// } + +// #[test] +// fn rename_one_account_in_login_with_two_accounts() { +// let store_dir = tempdir().unwrap(); +// let wallet = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let account2 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let login_id = LoginId::new("first".to_string()); +// let account_id2 = AccountId::new("second".to_string()); +// let renamed_account_id2 = AccountId::new("new_second".to_string()); + +// store_login_with_multiple_accounts_at_file( +// &wallet, +// account1.clone(), +// hd_path.clone(), +// login_id.clone(), +// &password, +// ) +// .unwrap(); + +// append_account_to_login_at_file( +// &wallet, +// account2.clone(), +// hd_path.clone(), +// login_id.clone(), +// account_id2.clone(), +// &password, +// ) +// .unwrap(); + +// // Load and confirm +// let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); +// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); +// let expected = vec![ +// WalletAccount::new( +// DEFAULT_FIRST_ACCOUNT_NAME.into(), +// MnemonicAccount::new(account1.clone(), hd_path.clone()), +// ), +// WalletAccount::new( +// account_id2.clone(), +// MnemonicAccount::new(account2.clone(), hd_path.clone()), +// ), +// ] +// .into(); +// assert_eq!(loaded_accounts, &expected); + +// // Rename the second account to a new name +// rename_account_in_login_at_file( +// &wallet, +// &login_id, +// &account_id2, +// &renamed_account_id2, +// &password, +// ) +// .unwrap(); + +// // Load and confirm +// let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); +// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); +// let expected = vec![ +// WalletAccount::new( +// DEFAULT_FIRST_ACCOUNT_NAME.into(), +// MnemonicAccount::new(account1, hd_path.clone()), +// ), +// WalletAccount::new(renamed_account_id2, MnemonicAccount::new(account2, hd_path)), +// ] +// .into(); +// assert_eq!(loaded_accounts, &expected); +// } + +// #[test] +// fn rename_account_into_existing_account_id_fails() { +// let store_dir = tempdir().unwrap(); +// let wallet = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let account2 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let login_id = LoginId::new("first".to_string()); +// let account_id2 = AccountId::new("second".to_string()); +// let renamed_account_id2 = DEFAULT_FIRST_ACCOUNT_NAME.into(); + +// store_login_with_multiple_accounts_at_file( +// &wallet, +// account1.clone(), +// hd_path.clone(), +// login_id.clone(), +// &password, +// ) +// .unwrap(); + +// append_account_to_login_at_file( +// &wallet, +// account2.clone(), +// hd_path.clone(), +// login_id.clone(), +// account_id2.clone(), +// &password, +// ) +// .unwrap(); + +// // Load and confirm +// let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); +// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); +// let expected = vec![ +// WalletAccount::new( +// DEFAULT_FIRST_ACCOUNT_NAME.into(), +// MnemonicAccount::new(account1.clone(), hd_path.clone()), +// ), +// WalletAccount::new( +// account_id2.clone(), +// MnemonicAccount::new(account2.clone(), hd_path.clone()), +// ), +// ] +// .into(); +// assert_eq!(loaded_accounts, &expected); + +// // Rename the second account to the name of the first one fails +// assert!(matches!( +// rename_account_in_login_at_file( +// &wallet, +// &login_id, +// &account_id2, +// &renamed_account_id2, +// &password, +// ), +// Err(BackendError::WalletAccountIdAlreadyExistsInWalletLogin) +// )); + +// // Load and confirm nothing was changed +// let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); +// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); +// let expected = vec![ +// WalletAccount::new( +// DEFAULT_FIRST_ACCOUNT_NAME.into(), +// MnemonicAccount::new(account1, hd_path.clone()), +// ), +// WalletAccount::new(account_id2, MnemonicAccount::new(account2, hd_path)), +// ] +// .into(); +// assert_eq!(loaded_accounts, &expected); +// } + +// // Test to that decrypts a stored file from the repo, to make sure we are able to decrypt stored +// // wallets created with older versions. +// #[test] +// fn decrypt_stored_wallet() { +// const SAVED_WALLET: &str = "src/wallet_storage/test-data/saved-wallet.json"; +// let wallet_file = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(SAVED_WALLET); + +// let wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); + +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let bad_password = UserPassword::new("bad-password".to_string()); +// let id1 = LoginId::new("first".to_string()); +// let id2 = LoginId::new("second".to_string()); + +// assert!(!wallet.password_can_decrypt_all(&bad_password)); +// assert!(wallet.password_can_decrypt_all(&password)); + +// let acc1 = wallet.decrypt_login(&id1, &password).unwrap(); +// let acc2 = wallet.decrypt_login(&id2, &password).unwrap(); + +// assert!(matches!(acc1, StoredLogin::Mnemonic(_))); +// assert!(matches!(acc2, StoredLogin::Mnemonic(_))); + +// let expected_acc1 = bip39::Mnemonic::from_str("country mean universe text phone begin deputy reject result good cram illness common cluster proud swamp digital patrol spread bar face december base kick").unwrap(); +// let expected_acc2 = bip39::Mnemonic::from_str("home mansion start quiz dress decide hint second dragon sunny juice always steak real minimum art rival skin draw total pulp foot goddess agent").unwrap(); + +// assert_eq!( +// acc1.as_mnemonic_account().unwrap().mnemonic(), +// &expected_acc1 +// ); +// assert_eq!(acc1.as_mnemonic_account().unwrap().hd_path(), &hd_path,); + +// assert_eq!( +// acc2.as_mnemonic_account().unwrap().mnemonic(), +// &expected_acc2 +// ); +// assert_eq!(acc2.as_mnemonic_account().unwrap().hd_path(), &hd_path,); +// } + +// #[test] +// fn decrypt_stored_wallet_1_0_4() { +// const SAVED_WALLET: &str = "src/wallet_storage/test-data/saved-wallet-1.0.4.json"; +// let wallet_file = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(SAVED_WALLET); + +// let wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); + +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password11!".to_string()); +// let bad_password = UserPassword::new("bad-password".to_string()); +// let login_id = LoginId::new("default".to_string()); + +// assert!(!wallet.password_can_decrypt_all(&bad_password)); +// assert!(wallet.password_can_decrypt_all(&password)); + +// let acc1 = wallet.decrypt_login(&login_id, &password).unwrap(); + +// assert!(matches!(acc1, StoredLogin::Mnemonic(_))); + +// let expected_acc1 = bip39::Mnemonic::from_str("arrow capable abstract industry elevator nominee december piece hotel feed lounge web faint sword veteran bundle hour page actual laptop horror gold test warrior").unwrap(); + +// assert_eq!( +// acc1.as_mnemonic_account().unwrap().mnemonic(), +// &expected_acc1 +// ); +// assert_eq!(acc1.as_mnemonic_account().unwrap().hd_path(), &hd_path,); +// } + +// #[test] +// fn decrypt_stored_wallet_1_0_5_with_multiple_accounts() { +// const SAVED_WALLET: &str = "src/wallet_storage/test-data/saved-wallet-1.0.5.json"; +// let wallet_file = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(SAVED_WALLET); + +// let wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); + +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password11!".to_string()); +// let bad_password = UserPassword::new("bad-password".to_string()); +// let login_id = LoginId::new("default".to_string()); + +// assert!(!wallet.password_can_decrypt_all(&bad_password)); +// assert!(wallet.password_can_decrypt_all(&password)); + +// let login = wallet.decrypt_login(&login_id, &password).unwrap(); + +// assert!(matches!(login, StoredLogin::Multiple(_))); + +// let login = login.as_multiple_accounts().unwrap(); +// assert_eq!(login.len(), 4); + +// let expected_mn1 = bip39::Mnemonic::from_str("arrow capable abstract industry elevator nominee december piece hotel feed lounge web faint sword veteran bundle hour page actual laptop horror gold test warrior").unwrap(); +// let expected_mn2 = bip39::Mnemonic::from_str("border hurt skull lunar goddess second danger game dismiss exhaust oven thumb dog drama onion false orchard spice tent next predict invite cherry green").unwrap(); +// let expected_mn3 = bip39::Mnemonic::from_str("gentle crowd rule snap girl urge flat jump winner cluster night sand museum stock grunt quick tree acquire traffic major awake tag rack peasant").unwrap(); +// let expected_mn4 = bip39::Mnemonic::from_str("debris blue skin annual inhale text border rigid spatial lesson coconut yard horn crystal control survey version vote hawk neck frame arrive oblige width").unwrap(); + +// let expected = vec![ +// WalletAccount::new( +// "default".into(), +// MnemonicAccount::new(expected_mn1, hd_path.clone()), +// ), +// WalletAccount::new( +// "account2".into(), +// MnemonicAccount::new(expected_mn2, hd_path.clone()), +// ), +// WalletAccount::new( +// "foobar".into(), +// MnemonicAccount::new(expected_mn3, hd_path.clone()), +// ), +// WalletAccount::new("42".into(), MnemonicAccount::new(expected_mn4, hd_path)), +// ] +// .into(); + +// assert_eq!(login, &expected); +// } + +// #[test] +// fn append_filename() { +// let wallet_file = PathBuf::from("/tmp/saved-wallet.json"); +// let timestamp = OsString::from("42"); +// #[cfg(target_family = "unix")] +// assert_eq!( +// append_timestamp_to_filename(wallet_file.clone(), timestamp.clone(), None) +// .unwrap() +// .into_os_string() +// .into_string() +// .unwrap(), +// "/tmp/saved-wallet-42.json".to_string(), +// ); +// #[cfg(not(target_family = "unix"))] +// assert_eq!( +// append_timestamp_to_filename(wallet_file.clone(), timestamp.clone(), None) +// .unwrap() +// .into_os_string() +// .into_string() +// .unwrap(), +// r"/tmp\saved-wallet-42.json".to_string(), +// ); + +// #[cfg(target_family = "unix")] +// assert_eq!( +// append_timestamp_to_filename(wallet_file, timestamp, Some(3)) +// .unwrap() +// .into_os_string() +// .into_string() +// .unwrap(), +// "/tmp/saved-wallet-42-3.json".to_string(), +// ); +// #[cfg(not(target_family = "unix"))] +// assert_eq!( +// append_timestamp_to_filename(wallet_file, timestamp, Some(3)) +// .unwrap() +// .into_os_string() +// .into_string() +// .unwrap(), +// r"/tmp\saved-wallet-42-3.json".to_string(), +// ); +// } +// } diff --git a/nym-wallet/src-tauri/tauri.conf.json b/nym-wallet/src-tauri/tauri.conf.json index e493efdfed..5843d192dd 100644 --- a/nym-wallet/src-tauri/tauri.conf.json +++ b/nym-wallet/src-tauri/tauri.conf.json @@ -1,78 +1,69 @@ { - "package": { - "productName": "nym-wallet", - "version": "1.2.15" - }, - "build": { - "distDir": "../dist", - "devPath": "http://localhost:9000", - "beforeDevCommand": "", - "beforeBuildCommand": "" - }, - "tauri": { - "bundle": { - "active": true, - "targets": "all", - "identifier": "net.nymtech.wallet", - "icon": [ - "icons/32x32.png", - "icons/128x128.png", - "icons/128x128@2x.png", - "icons/icon.icns", - "icons/icon.ico" - ], - "resources": [], - "externalBin": [], - "copyright": "Copyright © 2021-2023 Nym Technologies SA", - "category": "Business", - "shortDescription": "Nym desktop wallet allows you to manage your NYM tokens", - "longDescription": "", + "bundle": { + "active": true, + "targets": "all", + "windows": { + "certificateThumbprint": "6DB77B1F529A0804FE0E6843A3EB8A8CECFFD408", + "digestAlgorithm": "sha256", + "timestampUrl": "http://timestamp.comodoca.com" + }, + "icon": [ + "icons/32x32.png", + "icons/128x128.png", + "icons/128x128@2x.png", + "icons/icon.icns", + "icons/icon.ico" + ], + "resources": [], + "externalBin": [], + "copyright": "Copyright © 2021-2025 Nym Technologies SA", + "category": "Business", + "shortDescription": "Nym desktop wallet allows you to manage your NYM tokens", + "longDescription": "", + "macOS": { + "frameworks": [], + "minimumSystemVersion": "", + "exceptionDomain": "", + "signingIdentity": "Developer ID Application: Nym Technologies SA (VW5DZLFHM5)", + "entitlements": null + }, + "linux": { "deb": { "depends": [] - }, - "macOS": { - "frameworks": [], - "minimumSystemVersion": "", - "exceptionDomain": "", - "signingIdentity": "Developer ID Application: Nym Technologies SA (VW5DZLFHM5)", - "entitlements": null - }, - "windows": { - "certificateThumbprint": "6DB77B1F529A0804FE0E6843A3EB8A8CECFFD408", - "digestAlgorithm": "sha256", - "timestampUrl": "http://timestamp.comodoca.com" } }, + "createUpdaterArtifacts": "v1Compatible" + }, + "build": { + "beforeBuildCommand": "", + "frontendDist": "../dist", + "beforeDevCommand": "", + "devUrl": "http://localhost:9000" + }, + "productName": "nym-wallet", + "mainBinaryName": "nym-wallet", + "version": "1.2.15", + "identifier": "net.nymtech.wallet", + "plugins": { "updater": { - "active": true, + "pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IENCNzQ2M0E5N0VFODE2NApSV1JrZ2U2WE9rYTNETTg1OTBKdE5uWUEra0hML2syOVUvQ2lxZmFZRzZ1T3NWbGM0eVRzUTVhVwo=", "endpoints": [ "https://nymtech.net/.wellknown/wallet/updater.json" - ], - "dialog": true, - "pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IENCNzQ2M0E5N0VFODE2NApSV1JrZ2U2WE9rYTNETTg1OTBKdE5uWUEra0hML2syOVUvQ2lxZmFZRzZ1T3NWbGM0eVRzUTVhVwo=" - }, - "allowlist": { - "window": { - "maximize": true, - "print": true - }, - "clipboard": { - "all": true - }, - "shell": { - "open": true - } + ] + } + }, + "app": { + "security": { + "csp": "default-src blob: data: filesystem: ws: wss: http: https: tauri: 'unsafe-eval' 'unsafe-inline' 'self' img-src: 'self'; connect-src ipc: http://ipc.localhost" }, "windows": [ { "title": "Nym Wallet", "width": 1268, "height": 768, - "resizable": true + "resizable": true, + "useHttpsScheme": true } - ], - "security": { - "csp": "default-src blob: data: filesystem: ws: wss: http: https: tauri: 'unsafe-eval' 'unsafe-inline' 'self' img-src: 'self'" - } + ] } } \ No newline at end of file diff --git a/nym-wallet/src/components/CopyToClipboard.tsx b/nym-wallet/src/components/CopyToClipboard.tsx index d6de5cab9b..a58f8ff859 100644 --- a/nym-wallet/src/components/CopyToClipboard.tsx +++ b/nym-wallet/src/components/CopyToClipboard.tsx @@ -1,15 +1,15 @@ import React, { useEffect, useState } from 'react'; import { Button, IconButton, Tooltip } from '@mui/material'; import { Check, ContentCopy } from '@mui/icons-material'; -import { clipboard } from '@tauri-apps/api'; import { Console } from '../utils/console'; +import { writeText, readText } from '@tauri-apps/plugin-clipboard-manager'; export const CopyToClipboard = ({ text = '', iconButton }: { text?: string; iconButton?: boolean }) => { const [copied, setCopied] = useState(false); const handleCopy = async (_text: string) => { try { - await clipboard.writeText(_text); + await writeText(_text); setCopied(true); } catch (e) { Console.error(`failed to copy: ${e}`); diff --git a/nym-wallet/src/components/Settings/AppVersion.tsx b/nym-wallet/src/components/Settings/AppVersion.tsx index e968a0696c..28e4dcc824 100644 --- a/nym-wallet/src/components/Settings/AppVersion.tsx +++ b/nym-wallet/src/components/Settings/AppVersion.tsx @@ -1,6 +1,6 @@ import React, { useContext, useEffect, useState } from 'react'; import { Button, Stack, Typography } from '@mui/material'; -import { checkUpdate } from '@tauri-apps/api/updater'; +import { check } from '@tauri-apps/plugin-updater'; import { AppContext } from '../../context'; import { checkVersion } from '../../requests'; import { Console } from '../../utils/console'; @@ -28,7 +28,7 @@ const AppVersion = () => { try { // despite the name, this will spawn an external native window with // an embedded "download and update the Wallet" flow - checkUpdate(); + check(); } catch (e) { Console.error(e); } diff --git a/nym-wallet/src/pages/internal-docs/DocEntry.tsx b/nym-wallet/src/pages/internal-docs/DocEntry.tsx index 22374cc92c..8f6a3231c3 100644 --- a/nym-wallet/src/pages/internal-docs/DocEntry.tsx +++ b/nym-wallet/src/pages/internal-docs/DocEntry.tsx @@ -1,7 +1,7 @@ /* eslint-disable react/destructuring-assignment */ import React from 'react'; import { Button, Card, CardContent, TextField } from '@mui/material'; -import { invoke } from '@tauri-apps/api'; +import { invoke } from '@tauri-apps/api/core'; interface DocEntryProps { function: FunctionDef; diff --git a/nym-wallet/src/requests/wrapper.ts b/nym-wallet/src/requests/wrapper.ts index 5c9cb97f3a..6041bed1e4 100644 --- a/nym-wallet/src/requests/wrapper.ts +++ b/nym-wallet/src/requests/wrapper.ts @@ -1,4 +1,4 @@ -import { invoke } from '@tauri-apps/api'; +import { invoke } from '@tauri-apps/api/core'; import { config } from '../config'; import { Console } from '../utils/console'; diff --git a/nym-wallet/src/utils/common.ts b/nym-wallet/src/utils/common.ts index ed5d7453cd..f2da75f177 100644 --- a/nym-wallet/src/utils/common.ts +++ b/nym-wallet/src/utils/common.ts @@ -1,4 +1,4 @@ -import { appWindow } from '@tauri-apps/api/window'; +import { getCurrentWebviewWindow } from '@tauri-apps/api/webviewWindow'; import bs58 from 'bs58'; import Big from 'big.js'; import { valid } from 'semver'; @@ -14,6 +14,7 @@ import { userBalance, } from '../requests'; import { Console } from './console'; +const appWindow = getCurrentWebviewWindow() export const validateKey = (key: string, bytesLength: number): boolean => { // it must be a valid base58 key diff --git a/yarn.lock b/yarn.lock index ac67ada74f..843efd355b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -15,17 +15,6 @@ "@jridgewell/gen-mapping" "^0.3.5" "@jridgewell/trace-mapping" "^0.3.24" -"@asamuzakjp/css-color@^3.1.1": - version "3.1.1" - resolved "https://registry.yarnpkg.com/@asamuzakjp/css-color/-/css-color-3.1.1.tgz#41a612834dafd9353b89855b37baa8a03fb67bf2" - integrity sha512-hpRD68SV2OMcZCsrbdkccTw5FXjNDLo5OuqSHyHZfwweGsDWZwDJ2+gONyNAbazZclobMirACLw0lk8WVxIqxA== - dependencies: - "@csstools/css-calc" "^2.1.2" - "@csstools/css-color-parser" "^3.0.8" - "@csstools/css-parser-algorithms" "^3.0.4" - "@csstools/css-tokenizer" "^3.0.3" - lru-cache "^10.4.3" - "@babel/code-frame@7.12.11": version "7.12.11" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.12.11.tgz#f4ad435aa263db935b8f10f2c552d23fb716a63f" @@ -226,6 +215,14 @@ "@babel/helper-optimise-call-expression" "^7.25.9" "@babel/traverse" "^7.26.5" +"@babel/helper-simple-access@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.25.9.tgz#6d51783299884a2c74618d6ef0f86820ec2e7739" + integrity sha512-c6WHXuiaRsJTyHYLJV75t9IqsmTbItYfdj99PnzYGQZkYKvan5/2jKJ7gu31J3/BJ/A18grImSPModuyG/Eo0Q== + dependencies: + "@babel/traverse" "^7.25.9" + "@babel/types" "^7.25.9" + "@babel/helper-skip-transparent-expression-wrappers@^7.20.0", "@babel/helper-skip-transparent-expression-wrappers@^7.25.9": version "7.25.9" resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.25.9.tgz#0b2e1b62d560d6b1954893fd2b705dc17c91f0c9" @@ -1148,7 +1145,7 @@ dependencies: regenerator-runtime "^0.13.2" -"@babel/runtime@^7.0.0", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.15.4", "@babel/runtime@^7.17.8", "@babel/runtime@^7.18.3", "@babel/runtime@^7.18.9", "@babel/runtime@^7.21.0", "@babel/runtime@^7.23.2", "@babel/runtime@^7.23.9", "@babel/runtime@^7.24.0", "@babel/runtime@^7.25.0", "@babel/runtime@^7.25.7", "@babel/runtime@^7.26.0", "@babel/runtime@^7.26.10", "@babel/runtime@^7.3.1", "@babel/runtime@^7.5.0", "@babel/runtime@^7.5.5", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.3", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2": +"@babel/runtime@^7.0.0", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.15.4", "@babel/runtime@^7.17.8", "@babel/runtime@^7.18.3", "@babel/runtime@^7.18.9", "@babel/runtime@^7.21.0", "@babel/runtime@^7.23.9", "@babel/runtime@^7.24.0", "@babel/runtime@^7.25.7", "@babel/runtime@^7.26.0", "@babel/runtime@^7.3.1", "@babel/runtime@^7.5.0", "@babel/runtime@^7.5.5", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.3", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2": version "7.26.10" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.26.10.tgz#a07b4d8fa27af131a633d7b3524db803eb4764c2" integrity sha512-2WJMeRQPHKSPemqk/awGrAiuFfzBmOIPXKizAsVhWH9YJqLZ0H+HS4c8loHGgW6utJ3E/ejXQUsiGaQy2NZ9Fw== @@ -1202,60 +1199,6 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== -"@biomejs/biome@1.9.4": - version "1.9.4" - resolved "https://registry.yarnpkg.com/@biomejs/biome/-/biome-1.9.4.tgz#89766281cbc3a0aae865a7ff13d6aaffea2842bf" - integrity sha512-1rkd7G70+o9KkTn5KLmDYXihGoTaIGO9PIIN2ZB7UJxFrWw04CZHPYiMRjYsaDvVV7hP1dYNRLxSANLaBFGpog== - optionalDependencies: - "@biomejs/cli-darwin-arm64" "1.9.4" - "@biomejs/cli-darwin-x64" "1.9.4" - "@biomejs/cli-linux-arm64" "1.9.4" - "@biomejs/cli-linux-arm64-musl" "1.9.4" - "@biomejs/cli-linux-x64" "1.9.4" - "@biomejs/cli-linux-x64-musl" "1.9.4" - "@biomejs/cli-win32-arm64" "1.9.4" - "@biomejs/cli-win32-x64" "1.9.4" - -"@biomejs/cli-darwin-arm64@1.9.4": - version "1.9.4" - resolved "https://registry.yarnpkg.com/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-1.9.4.tgz#dfa376d23a54a2d8f17133c92f23c1bf2e62509f" - integrity sha512-bFBsPWrNvkdKrNCYeAp+xo2HecOGPAy9WyNyB/jKnnedgzl4W4Hb9ZMzYNbf8dMCGmUdSavlYHiR01QaYR58cw== - -"@biomejs/cli-darwin-x64@1.9.4": - version "1.9.4" - resolved "https://registry.yarnpkg.com/@biomejs/cli-darwin-x64/-/cli-darwin-x64-1.9.4.tgz#eafc2ce3849d385fc02238aad1ca4a73395a64d9" - integrity sha512-ngYBh/+bEedqkSevPVhLP4QfVPCpb+4BBe2p7Xs32dBgs7rh9nY2AIYUL6BgLw1JVXV8GlpKmb/hNiuIxfPfZg== - -"@biomejs/cli-linux-arm64-musl@1.9.4": - version "1.9.4" - resolved "https://registry.yarnpkg.com/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-1.9.4.tgz#d780c3e01758fc90f3268357e3f19163d1f84fca" - integrity sha512-v665Ct9WCRjGa8+kTr0CzApU0+XXtRgwmzIf1SeKSGAv+2scAlW6JR5PMFo6FzqqZ64Po79cKODKf3/AAmECqA== - -"@biomejs/cli-linux-arm64@1.9.4": - version "1.9.4" - resolved "https://registry.yarnpkg.com/@biomejs/cli-linux-arm64/-/cli-linux-arm64-1.9.4.tgz#8ed1dd0e89419a4b66a47f95aefb8c46ae6041c9" - integrity sha512-fJIW0+LYujdjUgJJuwesP4EjIBl/N/TcOX3IvIHJQNsAqvV2CHIogsmA94BPG6jZATS4Hi+xv4SkBBQSt1N4/g== - -"@biomejs/cli-linux-x64-musl@1.9.4": - version "1.9.4" - resolved "https://registry.yarnpkg.com/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-1.9.4.tgz#f36982b966bd671a36671e1de4417963d7db15fb" - integrity sha512-gEhi/jSBhZ2m6wjV530Yy8+fNqG8PAinM3oV7CyO+6c3CEh16Eizm21uHVsyVBEB6RIM8JHIl6AGYCv6Q6Q9Tg== - -"@biomejs/cli-linux-x64@1.9.4": - version "1.9.4" - resolved "https://registry.yarnpkg.com/@biomejs/cli-linux-x64/-/cli-linux-x64-1.9.4.tgz#a0a7f56680c76b8034ddc149dbf398bdd3a462e8" - integrity sha512-lRCJv/Vi3Vlwmbd6K+oQ0KhLHMAysN8lXoCI7XeHlxaajk06u7G+UsFSO01NAs5iYuWKmVZjmiOzJ0OJmGsMwg== - -"@biomejs/cli-win32-arm64@1.9.4": - version "1.9.4" - resolved "https://registry.yarnpkg.com/@biomejs/cli-win32-arm64/-/cli-win32-arm64-1.9.4.tgz#e2ef4e0084e76b7e26f0fc887c5ef1265ea56200" - integrity sha512-tlbhLk+WXZmgwoIKwHIHEBZUwxml7bRJgk0X2sPyNR3S93cdRq6XulAZRQJ17FYGGzWne0fgrXBKpl7l4M87Hg== - -"@biomejs/cli-win32-x64@1.9.4": - version "1.9.4" - resolved "https://registry.yarnpkg.com/@biomejs/cli-win32-x64/-/cli-win32-x64-1.9.4.tgz#4c7afa90e3970213599b4095e62f87e5972b2340" - integrity sha512-8Y5wMhVIPaWe6jw2H+KlEm4wP/f7EW3810ZLmDlrEEy5KvBsb9ECEfu/kMWD484ijfQ8+nIi0giMgu9g1UAuuA== - "@chain-registry/client@^1.49.11": version "1.53.94" resolved "https://registry.yarnpkg.com/@chain-registry/client/-/client-1.53.94.tgz#cbee6721abb8a7cdbf54383c3c7746c0b2e32a46" @@ -1288,7 +1231,7 @@ resolved "https://registry.yarnpkg.com/@chain-registry/types/-/types-0.46.15.tgz#f4c0219fb7060d97cb224b55f349adb1d436aac9" integrity sha512-gzf+pkAbEZ7fKuTuwmrEAEcx1K/BNrKuCnB9s+WwSo9Ad/3s+GV5LOXcOOxjjHh9Mrs9kvnxKvzKjOwWu8gDJw== -"@chain-registry/types@^0.50.36", "@chain-registry/types@^0.50.94": +"@chain-registry/types@^0.50.94": version "0.50.94" resolved "https://registry.yarnpkg.com/@chain-registry/types/-/types-0.50.94.tgz#9c9cdeb45b122dae116f19ba04418ad6a183e154" integrity sha512-D9PvO09UTjdKD+MmHFNSP//vfWXVEt1ILXOBGvZfi9kBy1a5xM4tCLuH/DH6kX5hjZHGxFmcBUwvYwh1kUJc7w== @@ -1767,7 +1710,7 @@ nock "13.5.4" uuid "^9.0.1" -"@cosmos-kit/keplr-extension@^2.14.0", "@cosmos-kit/keplr-extension@^2.7.9": +"@cosmos-kit/keplr-extension@^2.7.9": version "2.14.6" resolved "https://registry.yarnpkg.com/@cosmos-kit/keplr-extension/-/keplr-extension-2.14.6.tgz#4956500d1b6fe7a0ca39b7e77a2df5232b21ca6f" integrity sha512-oebXKrcwTcKCAjU05Jx4bS2gkBPF7oXqhvHReaCgxyMx+D6xHOORI7a70BxJIUbZZjTslAfgDN8k2/Xcj5/PIQ== @@ -1786,7 +1729,7 @@ "@cosmos-kit/core" "^2.15.5" "@dao-dao/cosmiframe" "^1.0.0-rc.1" -"@cosmos-kit/react@^2.10.11", "@cosmos-kit/react@^2.20.1": +"@cosmos-kit/react@^2.10.11": version "2.21.7" resolved "https://registry.yarnpkg.com/@cosmos-kit/react/-/react-2.21.7.tgz#009cb60f6604bc3894d4946c9ce038a2039c6166" integrity sha512-7LByV1LpwtXR2ohW4OOLd7lHS6jVs2AWvgMx8I8Y+B7y7k6YU1MU63dnMqKyV2JqfSv9GpeCa+Xhadd35kcMrw== @@ -1803,34 +1746,6 @@ dependencies: "@jridgewell/trace-mapping" "0.3.9" -"@csstools/color-helpers@^5.0.2": - version "5.0.2" - resolved "https://registry.yarnpkg.com/@csstools/color-helpers/-/color-helpers-5.0.2.tgz#82592c9a7c2b83c293d9161894e2a6471feb97b8" - integrity sha512-JqWH1vsgdGcw2RR6VliXXdA0/59LttzlU8UlRT/iUUsEeWfYq8I+K0yhihEUTTHLRm1EXvpsCx3083EU15ecsA== - -"@csstools/css-calc@^2.1.2": - version "2.1.2" - resolved "https://registry.yarnpkg.com/@csstools/css-calc/-/css-calc-2.1.2.tgz#bffd55f002dab119b76d4023f95cd943e6c8c11e" - integrity sha512-TklMyb3uBB28b5uQdxjReG4L80NxAqgrECqLZFQbyLekwwlcDDS8r3f07DKqeo8C4926Br0gf/ZDe17Zv4wIuw== - -"@csstools/css-color-parser@^3.0.8": - version "3.0.8" - resolved "https://registry.yarnpkg.com/@csstools/css-color-parser/-/css-color-parser-3.0.8.tgz#5fe9322920851450bf5e065c2b0e731b9e165394" - integrity sha512-pdwotQjCCnRPuNi06jFuP68cykU1f3ZWExLe/8MQ1LOs8Xq+fTkYgd+2V8mWUWMrOn9iS2HftPVaMZDaXzGbhQ== - dependencies: - "@csstools/color-helpers" "^5.0.2" - "@csstools/css-calc" "^2.1.2" - -"@csstools/css-parser-algorithms@^3.0.4": - version "3.0.4" - resolved "https://registry.yarnpkg.com/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.4.tgz#74426e93bd1c4dcab3e441f5cc7ba4fb35d94356" - integrity sha512-Up7rBoV77rv29d3uKHUIVubz1BTcgyUK72IvCQAbfbMv584xHcGKCKbWh7i8hPrRJ7qU4Y8IO3IY9m+iTB7P3A== - -"@csstools/css-tokenizer@^3.0.3": - version "3.0.3" - resolved "https://registry.yarnpkg.com/@csstools/css-tokenizer/-/css-tokenizer-3.0.3.tgz#a5502c8539265fecbd873c1e395a890339f119c2" - integrity sha512-UJnjoFsmxfKUdNYdWgOB0mWUypuLvAfQPH1+pyvRJs6euowbFkFC6P13w1l8mJyi3vxYMxc9kld5jZEGRQs6bw== - "@dao-dao/cosmiframe@^1.0.0-rc.1": version "1.0.0-rc.1" resolved "https://registry.yarnpkg.com/@dao-dao/cosmiframe/-/cosmiframe-1.0.0-rc.1.tgz#51d4d1801605b8abfd01ae0425c797b6b876991e" @@ -1956,7 +1871,7 @@ resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.9.0.tgz#745969d649977776b43fc7648c556aaa462b4102" integrity sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ== -"@emotion/react@^11.13.5", "@emotion/react@^11.4.1", "@emotion/react@^11.7.0": +"@emotion/react@^11.4.1", "@emotion/react@^11.7.0": version "11.14.0" resolved "https://registry.yarnpkg.com/@emotion/react/-/react-11.14.0.tgz#cfaae35ebc67dd9ef4ea2e9acc6cd29e157dd05d" integrity sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA== @@ -1986,7 +1901,7 @@ resolved "https://registry.yarnpkg.com/@emotion/sheet/-/sheet-1.4.0.tgz#c9299c34d248bc26e82563735f78953d2efca83c" integrity sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg== -"@emotion/styled@^11.13.5", "@emotion/styled@^11.3.0", "@emotion/styled@^11.6.0": +"@emotion/styled@^11.3.0", "@emotion/styled@^11.6.0": version "11.14.0" resolved "https://registry.yarnpkg.com/@emotion/styled/-/styled-11.14.0.tgz#f47ca7219b1a295186d7661583376fcea95f0ff3" integrity sha512-XxfOnXFffatap2IyCeJyNov3kiDQWoR08gPUQxvbL7fxKryGBKUZUkG6Hz48DZwVrJSVh9sJboyV1Ds4OW6SgA== @@ -2018,14 +1933,14 @@ resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz#5e13fac887f08c44f76b0ccaf3370eb00fec9bb6" integrity sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg== -"@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0": +"@eslint-community/eslint-utils@^4.2.0": version "4.5.1" resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.5.1.tgz#b0fc7e06d0c94f801537fd4237edc2706d3b8e4c" integrity sha512-soEIOALTfTK6EjmKMMoLugwaP0rzkad90iIWd1hMO9ARkSAyjfMfkRRhLvD5qH7vvM0Cg72pieUfR6yh6XxC4w== dependencies: eslint-visitor-keys "^3.4.3" -"@eslint-community/regexpp@^4.10.0", "@eslint-community/regexpp@^4.4.0", "@eslint-community/regexpp@^4.6.1": +"@eslint-community/regexpp@^4.4.0", "@eslint-community/regexpp@^4.6.1": version "4.12.1" resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.12.1.tgz#cfc6cffe39df390a3841cde2abccf92eaa7ae0e0" integrity sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ== @@ -2381,7 +2296,7 @@ resolved "https://registry.yarnpkg.com/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.5.tgz#56f00962ff0c4e0eb93d34a047d29fa995e3e342" integrity sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg== -"@interchain-ui/react@^1.14.2", "@interchain-ui/react@^1.26.1": +"@interchain-ui/react@^1.14.2": version "1.26.2" resolved "https://registry.yarnpkg.com/@interchain-ui/react/-/react-1.26.2.tgz#bb38916362cb48c220c40dabf94d5cb60214aeab" integrity sha512-AstIeKU+CvZwXR1LOeeiepCIK5QWpScTZKPpR6JCSfJCYuXox5qmim/dv57t3mlJReLiPX1R+Cy+0zGrxhX14Q== @@ -3017,12 +2932,7 @@ resolved "https://registry.yarnpkg.com/@mui/core-downloads-tracker/-/core-downloads-tracker-5.16.14.tgz#e6536f1b6caa873f7915fbf9703fdc840a5a98d9" integrity sha512-sbjXW+BBSvmzn61XyTMun899E7nGPTXwqD9drm1jBUAvWEhJpPFIRxwQQiATWZnd9rvdxtnhhdsDxEGWI0jxqA== -"@mui/core-downloads-tracker@^6.4.8": - version "6.4.8" - resolved "https://registry.yarnpkg.com/@mui/core-downloads-tracker/-/core-downloads-tracker-6.4.8.tgz#ffabbdce0e7b341e9c2906bb06a00b34c9d15e05" - integrity sha512-vjP4+A1ybyCRhDZC7r5EPWu/gLseFZxaGyPdDl94vzVvk6Yj6gahdaqcjbhkaCrJjdZj90m3VioltWPAnWF/zw== - -"@mui/icons-material@^5.0.0", "@mui/icons-material@^5.16.11", "@mui/icons-material@^5.2.0": +"@mui/icons-material@^5.0.0", "@mui/icons-material@^5.2.0": version "5.16.14" resolved "https://registry.yarnpkg.com/@mui/icons-material/-/icons-material-5.16.14.tgz#4fdecb05c15b1696f6f668fc29d549875544c892" integrity sha512-heL4S+EawrP61xMXBm59QH6HODsu0gxtZi5JtnXF2r+rghzyU/3Uftlt1ij8rmJh+cFdKTQug1L9KkZB5JgpMQ== @@ -3042,13 +2952,6 @@ clsx "^2.1.0" prop-types "^15.8.1" -"@mui/material-nextjs@^6.1.9": - version "6.4.3" - resolved "https://registry.yarnpkg.com/@mui/material-nextjs/-/material-nextjs-6.4.3.tgz#516eec4b6ca7d79f196f5c8b1b7f91e69e5c95ab" - integrity sha512-4ZRLrcD1HeWpvY8c7MrKYKuaUSobtvqcLYeEfGh/x5ezzPgKizhl7C0jpVVEgf6g+C9OgOGbhLTVfks7Y2IBAQ== - dependencies: - "@babel/runtime" "^7.26.0" - "@mui/material@^5.0.1", "@mui/material@^5.2.2": version "5.16.14" resolved "https://registry.yarnpkg.com/@mui/material/-/material-5.16.14.tgz#da8a75822f039d8c1b0ab7fb4146d767c2f9248a" @@ -3067,24 +2970,6 @@ react-is "^19.0.0" react-transition-group "^4.4.5" -"@mui/material@^6.1.10": - version "6.4.8" - resolved "https://registry.yarnpkg.com/@mui/material/-/material-6.4.8.tgz#3da961a35b778246832db22fab98c49adbccda77" - integrity sha512-5S9UTjKZZBd9GfbcYh/nYfD9cv6OXmj5Y7NgKYfk7JcSoshp8/pW5zP4wecRiroBSZX8wcrywSgogpVNO+5W0Q== - dependencies: - "@babel/runtime" "^7.26.0" - "@mui/core-downloads-tracker" "^6.4.8" - "@mui/system" "^6.4.8" - "@mui/types" "~7.2.24" - "@mui/utils" "^6.4.8" - "@popperjs/core" "^2.11.8" - "@types/react-transition-group" "^4.4.12" - clsx "^2.1.1" - csstype "^3.1.3" - prop-types "^15.8.1" - react-is "^19.0.0" - react-transition-group "^4.4.5" - "@mui/private-theming@^5.16.14": version "5.16.14" resolved "https://registry.yarnpkg.com/@mui/private-theming/-/private-theming-5.16.14.tgz#7ad2b8a8fe0417f9fdfd05011806b9cb33c1a20a" @@ -3094,15 +2979,6 @@ "@mui/utils" "^5.16.14" prop-types "^15.8.1" -"@mui/private-theming@^6.4.8": - version "6.4.8" - resolved "https://registry.yarnpkg.com/@mui/private-theming/-/private-theming-6.4.8.tgz#210d1a1ab4501f6cf31f1abdf1b0a37fbdfc2991" - integrity sha512-sWwQoNSn6elsPTAtSqCf+w5aaGoh7AASURNmpy+QTTD/zwJ0Jgwt0ZaaP6mXq2IcgHxYnYloM/+vJgHPMkRKTQ== - dependencies: - "@babel/runtime" "^7.26.0" - "@mui/utils" "^6.4.8" - prop-types "^15.8.1" - "@mui/styled-engine@^5.16.14": version "5.16.14" resolved "https://registry.yarnpkg.com/@mui/styled-engine/-/styled-engine-5.16.14.tgz#f90fef5b4f8ebf11d48e1b1df8854a45bb31a9f5" @@ -3113,18 +2989,6 @@ csstype "^3.1.3" prop-types "^15.8.1" -"@mui/styled-engine@^6.4.8": - version "6.4.8" - resolved "https://registry.yarnpkg.com/@mui/styled-engine/-/styled-engine-6.4.8.tgz#a454115ab349cf729510c107f2564fd1e23c385a" - integrity sha512-oyjx1b1FvUCI85ZMO4trrjNxGm90eLN3Ohy0AP/SqK5gWvRQg1677UjNf7t6iETOKAleHctJjuq0B3aXO2gtmw== - dependencies: - "@babel/runtime" "^7.26.0" - "@emotion/cache" "^11.13.5" - "@emotion/serialize" "^1.3.3" - "@emotion/sheet" "^1.4.0" - csstype "^3.1.3" - prop-types "^15.8.1" - "@mui/styles@^5.0.1", "@mui/styles@^5.2.2": version "5.16.14" resolved "https://registry.yarnpkg.com/@mui/styles/-/styles-5.16.14.tgz#a9482d0287ae83636c146817ba99f7dde6fcdaca" @@ -3162,20 +3026,6 @@ csstype "^3.1.3" prop-types "^15.8.1" -"@mui/system@^6.4.8": - version "6.4.8" - resolved "https://registry.yarnpkg.com/@mui/system/-/system-6.4.8.tgz#e4ecb65677c2268959effad1f4f885d9b836c09b" - integrity sha512-gV7iBHoqlsIenU2BP0wq14BefRoZcASZ/4LeyuQglayBl+DfLX5rEd3EYR3J409V2EZpR0NOM1LATAGlNk2cyA== - dependencies: - "@babel/runtime" "^7.26.0" - "@mui/private-theming" "^6.4.8" - "@mui/styled-engine" "^6.4.8" - "@mui/types" "~7.2.24" - "@mui/utils" "^6.4.8" - clsx "^2.1.1" - csstype "^3.1.3" - prop-types "^15.8.1" - "@mui/types@^7.2.14", "@mui/types@^7.2.15", "@mui/types@~7.2.24": version "7.2.24" resolved "https://registry.yarnpkg.com/@mui/types/-/types-7.2.24.tgz#5eff63129d9c29d80bbf2d2e561bd0690314dec2" @@ -3277,19 +3127,6 @@ prop-types "^15.8.1" react-transition-group "^4.4.5" -"@mui/x-date-pickers@^7.23.2": - version "7.28.0" - resolved "https://registry.yarnpkg.com/@mui/x-date-pickers/-/x-date-pickers-7.28.0.tgz#1daa089722b7b3b7458ad9af1ef39ae5ec9a9918" - integrity sha512-m1bfkZLOw3cMogeh6q92SjykVmLzfptnz3ZTgAlFKV7UBnVFuGUITvmwbgTZ1Mz3FmLVnGUQYUpZWw0ZnoghNA== - dependencies: - "@babel/runtime" "^7.25.7" - "@mui/utils" "^5.16.6 || ^6.0.0 || ^7.0.0 || ^7.0.0-beta" - "@mui/x-internals" "7.28.0" - "@types/react-transition-group" "^4.4.11" - clsx "^2.1.1" - prop-types "^15.8.1" - react-transition-group "^4.4.5" - "@mui/x-internals@7.28.0": version "7.28.0" resolved "https://registry.yarnpkg.com/@mui/x-internals/-/x-internals-7.28.0.tgz#b0a04f4c0f53f2f91d13a46f357f731b77c832c5" @@ -3320,15 +3157,10 @@ "@emnapi/runtime" "^1.3.1" "@tybys/wasm-util" "^0.9.0" -"@next/env@14.1.4": - version "14.1.4" - resolved "https://registry.yarnpkg.com/@next/env/-/env-14.1.4.tgz#432e80651733fbd67230bf262aee28be65252674" - integrity sha512-e7X7bbn3Z6DWnDi75UWn+REgAbLEqxI8Tq2pkFOFAMpWAWApz/YCUhtWMWn410h8Q2fYiYL7Yg5OlxMOCfFjJQ== - -"@next/env@15.2.2": - version "15.2.2" - resolved "https://registry.yarnpkg.com/@next/env/-/env-15.2.2.tgz#6345352365a811c523cecf284874ff489b675e59" - integrity sha512-yWgopCfA9XDR8ZH3taB5nRKtKJ1Q5fYsTOuYkzIIoS8TJ0UAUKAGF73JnGszbjk2ufAQDj6mDdgsJAFx5CLtYQ== +"@next/env@14.2.21": + version "14.2.21" + resolved "https://registry.yarnpkg.com/@next/env/-/env-14.2.21.tgz#09ff0813d29c596397e141205d4f5fd5c236bdd0" + integrity sha512-lXcwcJd5oR01tggjWJ6SrNNYFGuOOMB9c251wUNkjCpkoXOPkDeF/15c3mnVlBqrW4JJXb2kVxDFhC4GduJt2A== "@next/eslint-plugin-next@14.1.4": version "14.1.4" @@ -3337,213 +3169,50 @@ dependencies: glob "10.3.10" -"@next/eslint-plugin-next@15.0.3": - version "15.0.3" - resolved "https://registry.yarnpkg.com/@next/eslint-plugin-next/-/eslint-plugin-next-15.0.3.tgz#ce953098036d462f6901e423cc6445fc165b78c4" - integrity sha512-3Ln/nHq2V+v8uIaxCR6YfYo7ceRgZNXfTd3yW1ukTaFbO+/I8jNakrjYWODvG9BuR2v5kgVtH/C8r0i11quOgw== - dependencies: - fast-glob "3.3.1" +"@next/swc-darwin-arm64@14.2.21": + version "14.2.21" + resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.21.tgz#32a31992aace1440981df9cf7cb3af7845d94fec" + integrity sha512-HwEjcKsXtvszXz5q5Z7wCtrHeTTDSTgAbocz45PHMUjU3fBYInfvhR+ZhavDRUYLonm53aHZbB09QtJVJj8T7g== -"@next/swc-darwin-arm64@14.1.4": - version "14.1.4" - resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.1.4.tgz#a3bca0dc4393ac4cf3169bbf24df63441de66bb7" - integrity sha512-ubmUkbmW65nIAOmoxT1IROZdmmJMmdYvXIe8211send9ZYJu+SqxSnJM4TrPj9wmL6g9Atvj0S/2cFmMSS99jg== +"@next/swc-darwin-x64@14.2.21": + version "14.2.21" + resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.21.tgz#5ab4b3f6685b6b52f810d0f5cf6e471480ddffdb" + integrity sha512-TSAA2ROgNzm4FhKbTbyJOBrsREOMVdDIltZ6aZiKvCi/v0UwFmwigBGeqXDA97TFMpR3LNNpw52CbVelkoQBxA== -"@next/swc-darwin-arm64@15.2.2": - version "15.2.2" - resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.2.2.tgz#d3131279d4815ed7604eba7bf0063e0b8bfd2135" - integrity sha512-HNBRnz+bkZ+KfyOExpUxTMR0Ow8nkkcE6IlsdEa9W/rI7gefud19+Sn1xYKwB9pdCdxIP1lPru/ZfjfA+iT8pw== +"@next/swc-linux-arm64-gnu@14.2.21": + version "14.2.21" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.21.tgz#8a0e1fa887aef19ca218af2af515d0a5ee67ba3f" + integrity sha512-0Dqjn0pEUz3JG+AImpnMMW/m8hRtl1GQCNbO66V1yp6RswSTiKmnHf3pTX6xMdJYSemf3O4Q9ykiL0jymu0TuA== -"@next/swc-darwin-x64@14.1.4": - version "14.1.4" - resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-14.1.4.tgz#ba3683d4e2d30099f3f2864dd7349a4d9f440140" - integrity sha512-b0Xo1ELj3u7IkZWAKcJPJEhBop117U78l70nfoQGo4xUSvv0PJSTaV4U9xQBLvZlnjsYkc8RwQN1HoH/oQmLlQ== +"@next/swc-linux-arm64-musl@14.2.21": + version "14.2.21" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.21.tgz#ddad844406b42fa8965fe11250abc85c1fe0fd05" + integrity sha512-Ggfw5qnMXldscVntwnjfaQs5GbBbjioV4B4loP+bjqNEb42fzZlAaK+ldL0jm2CTJga9LynBMhekNfV8W4+HBw== -"@next/swc-darwin-x64@15.2.2": - version "15.2.2" - resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-15.2.2.tgz#8562f6b51177aa30f3a6e95a8b3e0e176a6cb04f" - integrity sha512-mJOUwp7al63tDpLpEFpKwwg5jwvtL1lhRW2fI1Aog0nYCPAhxbJsaZKdoVyPZCy8MYf/iQVNDuk/+i29iLCzIA== +"@next/swc-linux-x64-gnu@14.2.21": + version "14.2.21" + resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.21.tgz#db55fd666f9ba27718f65caa54b622a912cdd16b" + integrity sha512-uokj0lubN1WoSa5KKdThVPRffGyiWlm/vCc/cMkWOQHw69Qt0X1o3b2PyLLx8ANqlefILZh1EdfLRz9gVpG6tg== -"@next/swc-linux-arm64-gnu@14.1.4": - version "14.1.4" - resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.1.4.tgz#3519969293f16379954b7e196deb0c1eecbb2f8b" - integrity sha512-457G0hcLrdYA/u1O2XkRMsDKId5VKe3uKPvrKVOyuARa6nXrdhJOOYU9hkKKyQTMru1B8qEP78IAhf/1XnVqKA== +"@next/swc-linux-x64-musl@14.2.21": + version "14.2.21" + resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.21.tgz#dddb850353624efcd58c4c4e30ad8a1aab379642" + integrity sha512-iAEBPzWNbciah4+0yI4s7Pce6BIoxTQ0AGCkxn/UBuzJFkYyJt71MadYQkjPqCQCJAFQ26sYh7MOKdU+VQFgPg== -"@next/swc-linux-arm64-gnu@15.2.2": - version "15.2.2" - resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.2.2.tgz#c2466cffc541f39ae4d435087c81f5899d401903" - integrity sha512-5ZZ0Zwy3SgMr7MfWtRE7cQWVssfOvxYfD9O7XHM7KM4nrf5EOeqwq67ZXDgo86LVmffgsu5tPO57EeFKRnrfSQ== +"@next/swc-win32-arm64-msvc@14.2.21": + version "14.2.21" + resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.21.tgz#290012ee57b196d3d2d04853e6bf0179cae9fbaf" + integrity sha512-plykgB3vL2hB4Z32W3ktsfqyuyGAPxqwiyrAi2Mr8LlEUhNn9VgkiAl5hODSBpzIfWweX3er1f5uNpGDygfQVQ== -"@next/swc-linux-arm64-musl@14.1.4": - version "14.1.4" - resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.1.4.tgz#4bb3196bd402b3f84cf5373ff1021f547264d62f" - integrity sha512-l/kMG+z6MB+fKA9KdtyprkTQ1ihlJcBh66cf0HvqGP+rXBbOXX0dpJatjZbHeunvEHoBBS69GYQG5ry78JMy3g== +"@next/swc-win32-ia32-msvc@14.2.21": + version "14.2.21" + resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.21.tgz#c959135a78cab18cca588d11d1e33bcf199590d4" + integrity sha512-w5bacz4Vxqrh06BjWgua3Yf7EMDb8iMcVhNrNx8KnJXt8t+Uu0Zg4JHLDL/T7DkTCEEfKXO/Er1fcfWxn2xfPA== -"@next/swc-linux-arm64-musl@15.2.2": - version "15.2.2" - resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.2.2.tgz#24ed87fe47325597e986bd01c427efae53d6b677" - integrity sha512-cgKWBuFMLlJ4TWcFHl1KOaVVUAF8vy4qEvX5KsNd0Yj5mhu989QFCq1WjuaEbv/tO1ZpsQI6h/0YR8bLwEi+nA== - -"@next/swc-linux-x64-gnu@14.1.4": - version "14.1.4" - resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.1.4.tgz#1b3372c98c83dcdab946cdb4ee06e068b8139ba3" - integrity sha512-BapIFZ3ZRnvQ1uWbmqEGJuPT9cgLwvKtxhK/L2t4QYO7l+/DxXuIGjvp1x8rvfa/x1FFSsipERZK70pewbtJtw== - -"@next/swc-linux-x64-gnu@15.2.2": - version "15.2.2" - resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.2.2.tgz#0240f44530ca39c1fc899fabe8f33aa35d7e77da" - integrity sha512-c3kWSOSsVL8rcNBBfOq1+/j2PKs2nsMwJUV4icUxRgGBwUOfppeh7YhN5s79enBQFU+8xRgVatFkhHU1QW7yUA== - -"@next/swc-linux-x64-musl@14.1.4": - version "14.1.4" - resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.1.4.tgz#8459088bdc872648ff78f121db596f2533df5808" - integrity sha512-mqVxTwk4XuBl49qn2A5UmzFImoL1iLm0KQQwtdRJRKl21ylQwwGCxJtIYo2rbfkZHoSKlh/YgztY0qH3wG1xIg== - -"@next/swc-linux-x64-musl@15.2.2": - version "15.2.2" - resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.2.2.tgz#cca6ce8e9ec89afaa618673f0f8658682c30ff56" - integrity sha512-PXTW9PLTxdNlVYgPJ0equojcq1kNu5NtwcNjRjHAB+/sdoKZ+X8FBu70fdJFadkxFIGekQTyRvPMFF+SOJaQjw== - -"@next/swc-win32-arm64-msvc@14.1.4": - version "14.1.4" - resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.1.4.tgz#84280a08c00cc3be24ddd3a12f4617b108e6dea6" - integrity sha512-xzxF4ErcumXjO2Pvg/wVGrtr9QQJLk3IyQX1ddAC/fi6/5jZCZ9xpuL9Tzc4KPWMFq8GGWFVDMshZOdHGdkvag== - -"@next/swc-win32-arm64-msvc@15.2.2": - version "15.2.2" - resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.2.2.tgz#08f621602ed53fa21ddf6f656160b9186860be15" - integrity sha512-nG644Es5llSGEcTaXhnGWR/aThM/hIaz0jx4MDg4gWC8GfTCp8eDBWZ77CVuv2ha/uL9Ce+nPTfYkSLG67/sHg== - -"@next/swc-win32-ia32-msvc@14.1.4": - version "14.1.4" - resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.1.4.tgz#23ff7f4bd0a27177428669ef6fa5c3923c738031" - integrity sha512-WZiz8OdbkpRw6/IU/lredZWKKZopUMhcI2F+XiMAcPja0uZYdMTZQRoQ0WZcvinn9xZAidimE7tN9W5v9Yyfyw== - -"@next/swc-win32-x64-msvc@14.1.4": - version "14.1.4" - resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.1.4.tgz#bccf5beccfde66d6c66fa4e2509118c796385eda" - integrity sha512-4Rto21sPfw555sZ/XNLqfxDUNeLhNYGO2dlPqsnuCg8N8a2a9u1ltqBOPQ4vj1Gf7eJC0W2hHG2eYUHuiXgY2w== - -"@next/swc-win32-x64-msvc@15.2.2": - version "15.2.2" - resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.2.2.tgz#2072d69374f8c944134a5c5a80ce03ff84254cfa" - integrity sha512-52nWy65S/R6/kejz3jpvHAjZDPKIbEQu4x9jDBzmB9jJfuOy5rspjKu4u77+fI4M/WzLXrrQd57hlFGzz1ubcQ== - -"@nivo/annotations@0.88.0": - version "0.88.0" - resolved "https://registry.yarnpkg.com/@nivo/annotations/-/annotations-0.88.0.tgz#96830d735331dea2b60e66ce3971ef43b45cca8c" - integrity sha512-NXE+1oIUn+EGWMQpnpeRMLgi2wyuzhGDoJQY4OUHissCUiNotid2oNQ/PXJwN0toiu+/j9SyhzI32xr70OPi7Q== - dependencies: - "@nivo/colors" "0.88.0" - "@nivo/core" "0.88.0" - "@react-spring/web" "9.4.5 || ^9.7.2" - lodash "^4.17.21" - -"@nivo/axes@0.88.0": - version "0.88.0" - resolved "https://registry.yarnpkg.com/@nivo/axes/-/axes-0.88.0.tgz#b773efb217fc0faedbb5f8bc458f4b78820e2761" - integrity sha512-jF7aIxzTNayV5cI1J/b9Q1FfpMBxTXGk3OwSigXMSfYWlliskDn2u0qGRLiYhuXFdQAWIp4oXsO1GcAQ0eRVdg== - dependencies: - "@nivo/core" "0.88.0" - "@nivo/scales" "0.88.0" - "@react-spring/web" "9.4.5 || ^9.7.2" - "@types/d3-format" "^1.4.1" - "@types/d3-time-format" "^2.3.1" - d3-format "^1.4.4" - d3-time-format "^3.0.0" - -"@nivo/colors@0.88.0": - version "0.88.0" - resolved "https://registry.yarnpkg.com/@nivo/colors/-/colors-0.88.0.tgz#2790ac0607381800270f2902e4d957e65e2cad70" - integrity sha512-IZ+leYIqAlo7dyLHmsQwujanfRgXyoQ5H7PU3RWLEn1PP0zxDKLgEjFEDADpDauuslh2Tx0L81GNkWR6QSP0Mw== - dependencies: - "@nivo/core" "0.88.0" - "@types/d3-color" "^3.0.0" - "@types/d3-scale" "^4.0.8" - "@types/d3-scale-chromatic" "^3.0.0" - "@types/prop-types" "^15.7.2" - d3-color "^3.1.0" - d3-scale "^4.0.2" - d3-scale-chromatic "^3.0.0" - lodash "^4.17.21" - prop-types "^15.7.2" - -"@nivo/core@0.88.0": - version "0.88.0" - resolved "https://registry.yarnpkg.com/@nivo/core/-/core-0.88.0.tgz#ec79c7d63311473f15a463fd78274d9971a52848" - integrity sha512-XjUkA5MmwjLP38bdrJwn36Gj7T5SYMKD55LYQp/1nIJPdxqJ38dUfE4XyBDfIEgfP6yrHOihw3C63cUdnUBoiw== - dependencies: - "@nivo/tooltip" "0.88.0" - "@react-spring/web" "9.4.5 || ^9.7.2" - "@types/d3-shape" "^3.1.6" - d3-color "^3.1.0" - d3-format "^1.4.4" - d3-interpolate "^3.0.1" - d3-scale "^4.0.2" - d3-scale-chromatic "^3.0.0" - d3-shape "^3.2.0" - d3-time-format "^3.0.0" - lodash "^4.17.21" - prop-types "^15.7.2" - -"@nivo/legends@0.88.0": - version "0.88.0" - resolved "https://registry.yarnpkg.com/@nivo/legends/-/legends-0.88.0.tgz#a6007492048358a14bd061472a117ed34e7b0c81" - integrity sha512-d4DF9pHbD8LmGJlp/Gp1cF4e8y2wfQTcw3jVhbZj9zkb7ZWB7JfeF60VHRfbXNux9bjQ9U78/SssQqueVDPEmg== - dependencies: - "@nivo/colors" "0.88.0" - "@nivo/core" "0.88.0" - "@types/d3-scale" "^4.0.8" - d3-scale "^4.0.2" - -"@nivo/line@^0.88.0": - version "0.88.0" - resolved "https://registry.yarnpkg.com/@nivo/line/-/line-0.88.0.tgz#145b194f2c1bff2dd6071ab46fe114f9c4237811" - integrity sha512-hFTyZ3BdAZvq2HwdwMj2SJGUeodjEW+7DLtFMIIoVIxmjZlAs3z533HcJ9cJd3it928fDm8SF/rgHs0TztYf9Q== - dependencies: - "@nivo/annotations" "0.88.0" - "@nivo/axes" "0.88.0" - "@nivo/colors" "0.88.0" - "@nivo/core" "0.88.0" - "@nivo/legends" "0.88.0" - "@nivo/scales" "0.88.0" - "@nivo/tooltip" "0.88.0" - "@nivo/voronoi" "0.88.0" - "@react-spring/web" "9.4.5 || ^9.7.2" - d3-shape "^3.2.0" - -"@nivo/scales@0.88.0": - version "0.88.0" - resolved "https://registry.yarnpkg.com/@nivo/scales/-/scales-0.88.0.tgz#c69110b5ab504debb80a5a01a7824780af241ffb" - integrity sha512-HbpxkQp6tHCltZ1yDGeqdLcaJl5ze54NPjurfGtx/Uq+H5IQoBd4Tln49bUar5CsFAMsXw8yF1HQvASr7I1SIA== - dependencies: - "@types/d3-scale" "^4.0.8" - "@types/d3-time" "^1.1.1" - "@types/d3-time-format" "^3.0.0" - d3-scale "^4.0.2" - d3-time "^1.0.11" - d3-time-format "^3.0.0" - lodash "^4.17.21" - -"@nivo/tooltip@0.88.0": - version "0.88.0" - resolved "https://registry.yarnpkg.com/@nivo/tooltip/-/tooltip-0.88.0.tgz#b98348c0d617fea09c1bbddccc443c12ca697620" - integrity sha512-iEjVfQA8gumAzg/yUinjTwswygCkE5Iwuo8opwnrbpNIqMrleBV+EAKIgB0PrzepIoW8CFG/SJhoiRfbU8jhOw== - dependencies: - "@nivo/core" "0.88.0" - "@react-spring/web" "9.4.5 || ^9.7.2" - -"@nivo/voronoi@0.88.0": - version "0.88.0" - resolved "https://registry.yarnpkg.com/@nivo/voronoi/-/voronoi-0.88.0.tgz#029f984ccb6e72b415f398e1bd74c572960197ae" - integrity sha512-MyiNLvODthFoMjQ7Wjp693nogbTmVEx8Yn/7QkJhyPQbFyyA37TF/D1a/ox4h2OslXtP6K9QFN+42gB/zu7ixw== - dependencies: - "@nivo/core" "0.88.0" - "@nivo/tooltip" "0.88.0" - "@types/d3-delaunay" "^6.0.4" - "@types/d3-scale" "^4.0.8" - d3-delaunay "^6.0.4" - d3-scale "^4.0.2" +"@next/swc-win32-x64-msvc@14.2.21": + version "14.2.21" + resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.21.tgz#21ff892286555b90538a7d1b505ea21a005d6ead" + integrity sha512-sT6+llIkzpsexGYZq8cjjthRyRGe5cJVhqh12FmlbxHqna6zsDDK8UNaV7g41T6atFHCJUPeLb3uyAwrBwy0NA== "@noble/hashes@^1", "@noble/hashes@^1.0.0", "@noble/hashes@^1.2.0": version "1.7.1" @@ -3748,11 +3417,6 @@ resolved "https://registry.yarnpkg.com/@nymproject/contract-clients/-/contract-clients-1.2.4-rc.1.tgz#10b385f63d8b33beea6bf47d948fc7cc0c6c2357" integrity sha512-WMiEIHNxc9oTZ8GxAE8278ifpNJ2AIZHEw4JifN1WRhn2XViYaDDJaPCA3o0p+WGRySmqdurHmployr3iUEz1Q== -"@nymproject/contract-clients@^1.4.1": - version "1.4.1" - resolved "https://registry.yarnpkg.com/@nymproject/contract-clients/-/contract-clients-1.4.1.tgz#ae2644387b518eb13e8825fa8021d4c81ffe7852" - integrity sha512-HuJZ4Hv+Rl6ZZEtCHKgurNLJapM+QQRJlGkevFH2a4UdqUqF9omUkUi3AVes4679dPoSFgvA7plyVSDBdbgV6w== - "@nymproject/node-tester@^1.0.0", "@nymproject/node-tester@^1.2.3": version "1.2.3" resolved "https://registry.yarnpkg.com/@nymproject/node-tester/-/node-tester-1.2.3.tgz#79fbde8b69e2d1180eed897557c4610a1aa1038f" @@ -4649,7 +4313,7 @@ resolved "https://registry.yarnpkg.com/@react-spring/types/-/types-9.7.5.tgz#e5dd180f3ed985b44fd2cd2f32aa9203752ef3e8" integrity sha512-HVj7LrZ4ReHWBimBvu2SKND3cDVUPWKLqRTmWe/fNY6o1owGOX0cAHbdPDTMelgBlVbrTKrre6lFkhqGZErK/g== -"@react-spring/web@9.4.5 || ^9.7.2", "@react-spring/web@^9.7.5": +"@react-spring/web@^9.7.5": version "9.7.5" resolved "https://registry.yarnpkg.com/@react-spring/web/-/web-9.7.5.tgz#7d7782560b3a6fb9066b52824690da738605de80" integrity sha512-lmvqGwpe+CSttsWNZVr+Dg62adtKhauGwLyGE/RRyZ8AAMLgb9x3NDMA5RMElXo+IMyTkPp7nxTB8ZQlmhb6JQ== @@ -5196,7 +4860,7 @@ resolved "https://registry.yarnpkg.com/@rtsao/scc/-/scc-1.1.0.tgz#927dd2fae9bc3361403ac2c7a00c32ddce9ad7e8" integrity sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g== -"@rushstack/eslint-patch@^1.10.3", "@rushstack/eslint-patch@^1.3.3": +"@rushstack/eslint-patch@^1.3.3": version "1.11.0" resolved "https://registry.yarnpkg.com/@rushstack/eslint-patch/-/eslint-patch-1.11.0.tgz#75dce8e972f90bba488e2b0cc677fb233aa357ab" integrity sha512-zxnHvoMQVqewTJr/W4pKjF0bMGiKJv1WX7bSrkl46Hg0QjESbzBROWK0Wg4RphzSOS5Jiy7eFimmM3UgMrMZbQ== @@ -6345,25 +6009,26 @@ "@svgr/plugin-jsx" "^6.5.1" "@svgr/plugin-svgo" "^6.5.1" -"@swc/counter@0.1.3": +"@swc/counter@^0.1.3": version "0.1.3" resolved "https://registry.yarnpkg.com/@swc/counter/-/counter-0.1.3.tgz#cc7463bd02949611c6329596fccd2b0ec782b0e9" integrity sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ== -"@swc/helpers@0.5.15", "@swc/helpers@^0.5.0": +"@swc/helpers@0.5.5": + version "0.5.5" + resolved "https://registry.yarnpkg.com/@swc/helpers/-/helpers-0.5.5.tgz#12689df71bfc9b21c4f4ca00ae55f2f16c8b77c0" + integrity sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A== + dependencies: + "@swc/counter" "^0.1.3" + tslib "^2.4.0" + +"@swc/helpers@^0.5.0": version "0.5.15" resolved "https://registry.yarnpkg.com/@swc/helpers/-/helpers-0.5.15.tgz#79efab344c5819ecf83a43f3f9f811fc84b516d7" integrity sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g== dependencies: tslib "^2.8.0" -"@swc/helpers@0.5.2": - version "0.5.2" - resolved "https://registry.yarnpkg.com/@swc/helpers/-/helpers-0.5.2.tgz#85ea0c76450b61ad7d10a37050289eded783c27d" - integrity sha512-E4KcWTpoLHqwPHLxidpOqQbcrZVgi0rsmmZXUle1jXmJfuIf/UWpczUJ7MZZ5tlxytgJXyp0w4PGkkeLiuIdZw== - dependencies: - tslib "^2.4.0" - "@tanstack/match-sorter-utils@8.19.4": version "8.19.4" resolved "https://registry.yarnpkg.com/@tanstack/match-sorter-utils/-/match-sorter-utils-8.19.4.tgz#dacf772b5d94f4684f10dbeb2518cf72dccab8a5" @@ -6371,35 +6036,6 @@ dependencies: remove-accents "0.5.0" -"@tanstack/query-core@5.68.0": - version "5.68.0" - resolved "https://registry.yarnpkg.com/@tanstack/query-core/-/query-core-5.68.0.tgz#3765573de58741c68fb80b128d3e3ffb4d80cb68" - integrity sha512-r8rFYYo8/sY/LNaOqX84h12w7EQev4abFXDWy4UoDVUJzJ5d9Fbmb8ayTi7ScG+V0ap44SF3vNs/45mkzDGyGw== - -"@tanstack/query-devtools@5.67.2": - version "5.67.2" - resolved "https://registry.yarnpkg.com/@tanstack/query-devtools/-/query-devtools-5.67.2.tgz#890ae9913bd21d3969c7fd85c68b1bd1500cfc57" - integrity sha512-O4QXFFd7xqp6EX7sdvc9tsVO8nm4lpWBqwpgjpVLW5g7IeOY6VnS/xvs/YzbRhBVkKTMaJMOUGU7NhSX+YGoNg== - -"@tanstack/react-query-devtools@^5.64.2": - version "5.68.0" - resolved "https://registry.yarnpkg.com/@tanstack/react-query-devtools/-/react-query-devtools-5.68.0.tgz#83a76ae279796e0c2105cd760ef27e93bee79155" - integrity sha512-h9ArHkfa7SD5eGnJ9h+9M5uYWBdeVeY+WalrtGLCAtJJvHx6/RrtbbzxeoEQbPyx3f0kPcwJ58DGQ+7CBXelpg== - dependencies: - "@tanstack/query-devtools" "5.67.2" - -"@tanstack/react-query-next-experimental@^5.66.0": - version "5.68.0" - resolved "https://registry.yarnpkg.com/@tanstack/react-query-next-experimental/-/react-query-next-experimental-5.68.0.tgz#72ceeb510b0a50058d8612c5ba892b8aff1978be" - integrity sha512-nkmxjo7CbURBjDMX9MwNEj5Us8iG6ClK2fHtLUlnZJlkrIjDPPfCjp6Eh4otZjNXLPSXb92AWJWmPZSavCbM5A== - -"@tanstack/react-query@^5.64.2": - version "5.68.0" - resolved "https://registry.yarnpkg.com/@tanstack/react-query/-/react-query-5.68.0.tgz#f7b242eb800ce7c36b4edbe26c9ba8478eb18826" - integrity sha512-mMOdGDKlwTP/WV72QqSNf4PAMeoBp/DqBHQ222wBfb51Looi8QUqnCnb9O98ZgvNISmy6fzxRGBJdZ+9IBvX2Q== - dependencies: - "@tanstack/query-core" "5.68.0" - "@tanstack/react-table@8.20.5": version "8.20.5" resolved "https://registry.yarnpkg.com/@tanstack/react-table/-/react-table-8.20.5.tgz#19987d101e1ea25ef5406dce4352cab3932449d8" @@ -6407,20 +6043,6 @@ dependencies: "@tanstack/table-core" "8.20.5" -"@tanstack/react-table@8.20.6": - version "8.20.6" - resolved "https://registry.yarnpkg.com/@tanstack/react-table/-/react-table-8.20.6.tgz#a1f3103327aa59aa621931f4087a7604a21054d0" - integrity sha512-w0jluT718MrOKthRcr2xsjqzx+oEM7B7s/XXyfs19ll++hlId3fjTm+B2zrR3ijpANpkzBAr15j1XGVOMxpggQ== - dependencies: - "@tanstack/table-core" "8.20.5" - -"@tanstack/react-table@^8.20.6": - version "8.21.2" - resolved "https://registry.yarnpkg.com/@tanstack/react-table/-/react-table-8.21.2.tgz#6a7fce828b64547e33f4606ada8114db496007cc" - integrity sha512-11tNlEDTdIhMJba2RBH+ecJ9l1zgS2kjmexDPAraulc8jeNA4xocSNeyzextT0XJyASil4XsCYlJmf5jEWAtYg== - dependencies: - "@tanstack/table-core" "8.21.2" - "@tanstack/react-virtual@3.10.6": version "3.10.6" resolved "https://registry.yarnpkg.com/@tanstack/react-virtual/-/react-virtual-3.10.6.tgz#f90f97d50a8d83dcd3c3a2d425aadbb55d4837db" @@ -6428,13 +6050,6 @@ dependencies: "@tanstack/virtual-core" "3.10.6" -"@tanstack/react-virtual@3.11.2": - version "3.11.2" - resolved "https://registry.yarnpkg.com/@tanstack/react-virtual/-/react-virtual-3.11.2.tgz#d6b9bd999c181f0a2edce270c87a2febead04322" - integrity sha512-OuFzMXPF4+xZgx8UzJha0AieuMihhhaWG0tCqpp6tDzlFwOmNBPYMuLOtMJ1Tr4pXLHmgjcWhG6RlknY2oNTdQ== - dependencies: - "@tanstack/virtual-core" "3.11.2" - "@tanstack/react-virtual@^3.10.5": version "3.13.4" resolved "https://registry.yarnpkg.com/@tanstack/react-virtual/-/react-virtual-3.13.4.tgz#1188d4e452c8c7569cf42d0f605018ed67941e5c" @@ -6447,98 +6062,111 @@ resolved "https://registry.yarnpkg.com/@tanstack/table-core/-/table-core-8.20.5.tgz#3974f0b090bed11243d4107283824167a395cf1d" integrity sha512-P9dF7XbibHph2PFRz8gfBKEXEY/HJPOhym8CHmjF8y3q5mWpKx9xtZapXQUWCgkqvsK0R46Azuz+VaxD4Xl+Tg== -"@tanstack/table-core@8.21.2": - version "8.21.2" - resolved "https://registry.yarnpkg.com/@tanstack/table-core/-/table-core-8.21.2.tgz#dd57595a1773652bb6fb437e90a5f5386a49fd7e" - integrity sha512-uvXk/U4cBiFMxt+p9/G7yUWI/UbHYbyghLCjlpWZ3mLeIZiUBSKcUnw9UnKkdRz7Z/N4UBuFLWQdJCjUe7HjvA== - "@tanstack/virtual-core@3.10.6": version "3.10.6" resolved "https://registry.yarnpkg.com/@tanstack/virtual-core/-/virtual-core-3.10.6.tgz#babe3989b2344a5f12fc64129f9bbed5d3402999" integrity sha512-1giLc4dzgEKLMx5pgKjL6HlG5fjZMgCjzlKAlpr7yoUtetVPELgER1NtephAI910nMwfPTHNyWKSFmJdHkz2Cw== -"@tanstack/virtual-core@3.11.2": - version "3.11.2" - resolved "https://registry.yarnpkg.com/@tanstack/virtual-core/-/virtual-core-3.11.2.tgz#00409e743ac4eea9afe5b7708594d5fcebb00212" - integrity sha512-vTtpNt7mKCiZ1pwU9hfKPhpdVO2sVzFQsxoVBGtOSHxlrRRzYr8iQ2TlwbAcRYCcEiZ9ECAM8kBzH0v2+VzfKw== - "@tanstack/virtual-core@3.13.4": version "3.13.4" resolved "https://registry.yarnpkg.com/@tanstack/virtual-core/-/virtual-core-3.13.4.tgz#31ac7710d102ed59463f922ab088dde5a9e62010" integrity sha512-fNGO9fjjSLns87tlcto106enQQLycCKR4DPNpgq3djP5IdcPFdPAmaKjsgzIeRhH7hWrELgW12hYnRthS5kLUw== -"@tauri-apps/api@^1.2.0", "@tauri-apps/api@^1.5.1": +"@tauri-apps/api@^1.5.1": version "1.6.0" resolved "https://registry.yarnpkg.com/@tauri-apps/api/-/api-1.6.0.tgz#745b7e4e26782c3b2ad9510d558fa5bb2cf29186" integrity sha512-rqI++FWClU5I2UBp4HXFvl+sBWkdigBkxnpJDQUWttNyG7IZP4FwQGhTNL5EOw0vI8i6eSAJ5frLqO7n7jbJdg== -"@tauri-apps/cli-darwin-arm64@1.6.3": - version "1.6.3" - resolved "https://registry.yarnpkg.com/@tauri-apps/cli-darwin-arm64/-/cli-darwin-arm64-1.6.3.tgz#a204b9c686c88d774b7a67e0344cf660a0704558" - integrity sha512-fQN6IYSL8bG4NvkdKE4sAGF4dF/QqqQq4hOAU+t8ksOzHJr0hUlJYfncFeJYutr/MMkdF7hYKadSb0j5EE9r0A== +"@tauri-apps/api@^2.0.0", "@tauri-apps/api@^2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@tauri-apps/api/-/api-2.4.0.tgz#1b93976a129383ad914e979402431a63045c6329" + integrity sha512-F1zXTsmwcCp+ocg6fbzD/YL0OHeSG1eynCag1UNlX2tD5+dlXy7eRbTu9cAcscPjcR7Nix7by2wiv/+VfWUieg== -"@tauri-apps/cli-darwin-x64@1.6.3": - version "1.6.3" - resolved "https://registry.yarnpkg.com/@tauri-apps/cli-darwin-x64/-/cli-darwin-x64-1.6.3.tgz#2486f54c0b3beddf9f007b76d0b31d4da7b80e5d" - integrity sha512-1yTXZzLajKAYINJOJhZfmMhCzweHSgKQ3bEgJSn6t+1vFkOgY8Yx4oFgWcybrrWI5J1ZLZAl47+LPOY81dLcyA== +"@tauri-apps/cli-darwin-arm64@2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@tauri-apps/cli-darwin-arm64/-/cli-darwin-arm64-2.4.0.tgz#f727421a1a41538ba046faf98fb24e7703418e0b" + integrity sha512-MVzYrahJBgDyzUJ2gNEU8H+0oCVEucN115+CVorFnidHcJ6DtDRMCaKLkpjOZNfJyag1WQ25fu7imvZSe0mz/g== -"@tauri-apps/cli-linux-arm-gnueabihf@1.6.3": - version "1.6.3" - resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-arm-gnueabihf/-/cli-linux-arm-gnueabihf-1.6.3.tgz#53066f4e8292f33c1967ab5732ec9c53a0fe8531" - integrity sha512-CjTEr9r9xgjcvos09AQw8QMRPuH152B1jvlZt4PfAsyJNPFigzuwed5/SF7XAd8bFikA7zArP4UT12RdBxrx7w== +"@tauri-apps/cli-darwin-x64@2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@tauri-apps/cli-darwin-x64/-/cli-darwin-x64-2.4.0.tgz#0fd659ed5a34dc7077bcea66c4cc00808bc2f58d" + integrity sha512-/4IdbWv6IWSuBn0WVe5JkiSIP1gZhXCZRcumSsYq3ZmOlq4BqXeXT36Oig5mlDnS/2/UpNS94kd8gOA1DNdIeQ== -"@tauri-apps/cli-linux-arm64-gnu@1.6.3": - version "1.6.3" - resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-arm64-gnu/-/cli-linux-arm64-gnu-1.6.3.tgz#e204925e9f229d36cf8af17df59545ca88becfeb" - integrity sha512-G9EUUS4M8M/Jz1UKZqvJmQQCKOzgTb8/0jZKvfBuGfh5AjFBu8LHvlFpwkKVm1l4951Xg4ulUp6P9Q7WRJ9XSA== +"@tauri-apps/cli-linux-arm-gnueabihf@2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-arm-gnueabihf/-/cli-linux-arm-gnueabihf-2.4.0.tgz#b7fa0bf87184caa751d1f92db5d320586f918291" + integrity sha512-rOjlk3Vd6R847LXds4pOAFKUL5NVdSWlaiQvr4H9WDUwIWWoxnj7SQfpryTtElDb2wV7a0BNaOCXCpyFEAXjkw== -"@tauri-apps/cli-linux-arm64-musl@1.6.3": - version "1.6.3" - resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-arm64-musl/-/cli-linux-arm64-musl-1.6.3.tgz#a3753b2fa8d3c68bd8ebdd4af08d4b9a83bbf127" - integrity sha512-MuBTHJyNpZRbPVG8IZBN8+Zs7aKqwD22tkWVBcL1yOGL4zNNTJlkfL+zs5qxRnHlUsn6YAlbW/5HKocfpxVwBw== +"@tauri-apps/cli-linux-arm64-gnu@2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-arm64-gnu/-/cli-linux-arm64-gnu-2.4.0.tgz#12cea428829a635de446c6695da4b2ce7e5f5ea9" + integrity sha512-X/uCwao6R/weWT2y4f3JKJMeUsujo9H4nBMAv9RZhRsz93n9Amw9ETavAOP11pyhl57YkasvKTCRQN6FwsaoXg== -"@tauri-apps/cli-linux-x64-gnu@1.6.3": - version "1.6.3" - resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-x64-gnu/-/cli-linux-x64-gnu-1.6.3.tgz#9a22f12f310ed2af9ff46cc6251203f6d2b81aee" - integrity sha512-Uvi7M+NK3tAjCZEY1WGel+dFlzJmqcvu3KND+nqa22762NFmOuBIZ4KJR/IQHfpEYqKFNUhJfCGnpUDfiC3Oxg== +"@tauri-apps/cli-linux-arm64-musl@2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.4.0.tgz#4c9e3826c5254b2c70192f8560c5064c65e741c2" + integrity sha512-GhvQtrTjadW3eLSmfrSfybSqgJMZzUXC+0WqDzFovLug3a1a1go0m9QK9YGvYLkyEpTY5zRxLtwv+tbZXN4tZw== -"@tauri-apps/cli-linux-x64-musl@1.6.3": - version "1.6.3" - resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-x64-musl/-/cli-linux-x64-musl-1.6.3.tgz#c5b022f26c869b4877898589baf8dabf87a79096" - integrity sha512-rc6B342C0ra8VezB/OJom9j/N+9oW4VRA4qMxS2f4bHY2B/z3J9NPOe6GOILeg4v/CV62ojkLsC3/K/CeF3fqQ== +"@tauri-apps/cli-linux-riscv64-gnu@2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-riscv64-gnu/-/cli-linux-riscv64-gnu-2.4.0.tgz#39a8fed2f60cd34d55695a1583e9a8dfa33f98f0" + integrity sha512-NgeNihQ9uHS/ibMWLge5VA/BgsS/g8VPSVtCp8DSPyub3bBuCy79A8V+bzNKlMOiDVrqK4vQ//FS9kSxoJOtXw== -"@tauri-apps/cli-win32-arm64-msvc@1.6.3": - version "1.6.3" - resolved "https://registry.yarnpkg.com/@tauri-apps/cli-win32-arm64-msvc/-/cli-win32-arm64-msvc-1.6.3.tgz#5a871f96a7d58da5adacae07c848e5a0f3e82286" - integrity sha512-cSH2qOBYuYC4UVIFtrc1YsGfc5tfYrotoHrpTvRjUGu0VywvmyNk82+ZsHEnWZ2UHmu3l3lXIGRqSWveLln0xg== +"@tauri-apps/cli-linux-x64-gnu@2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-x64-gnu/-/cli-linux-x64-gnu-2.4.0.tgz#e4fc5751fe578cd14cff172791d2839bd2b5f970" + integrity sha512-ebRmV2HLIVms1KlNNueQCp3OrXBv6cimU3mYEh5NbZ8dH88f2sF46dFCyPq8Qr/Zti4qPEaAArVG7RYFjfECPw== -"@tauri-apps/cli-win32-ia32-msvc@1.6.3": - version "1.6.3" - resolved "https://registry.yarnpkg.com/@tauri-apps/cli-win32-ia32-msvc/-/cli-win32-ia32-msvc-1.6.3.tgz#cbec8e197a1cf0a63d329661ee45dcd7a31d05eb" - integrity sha512-T8V6SJQqE4PSWmYBl0ChQVmS6AR2hXFHURH2DwAhgSGSQ6uBXgwlYFcfIeQpBQA727K2Eq8X2hGfvmoySyHMRw== +"@tauri-apps/cli-linux-x64-musl@2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-x64-musl/-/cli-linux-x64-musl-2.4.0.tgz#b136eb34dbfeddf078d3a2023086da9a0b39499e" + integrity sha512-FOp2cBFyq5LnUr3he95Z99PQm3nCSJv2GZNeH7UqmUpeHwdcYuhBERU7C+8VDJJPR98Q5fkcoV00Pc4nw0v5KQ== -"@tauri-apps/cli-win32-x64-msvc@1.6.3": - version "1.6.3" - resolved "https://registry.yarnpkg.com/@tauri-apps/cli-win32-x64-msvc/-/cli-win32-x64-msvc-1.6.3.tgz#a78f5ccabbaca1d7ac3ce2acab2b1bd0276b53c4" - integrity sha512-HUkWZ+lYHI/Gjkh2QjHD/OBDpqLVmvjZGpLK9losur1Eg974Jip6k+vsoTUxQBCBDfj30eDBct9E1FvXOspWeg== +"@tauri-apps/cli-win32-arm64-msvc@2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@tauri-apps/cli-win32-arm64-msvc/-/cli-win32-arm64-msvc-2.4.0.tgz#18bba98ba67690a1fb76c540a9df01c7f220d68a" + integrity sha512-SVf1wDagYsaFM+mpUYKmjNveKodcUSGPEM27WmMW4Enh6aXGzTJi4IYOE3GEFOJF1BpRNscslwE1Rd064kfpcg== -"@tauri-apps/cli@^1.0.5": - version "1.6.3" - resolved "https://registry.yarnpkg.com/@tauri-apps/cli/-/cli-1.6.3.tgz#75e23dea0b67774fc6f150d637a0dbecf1f9592c" - integrity sha512-q46umd6QLRKDd4Gg6WyZBGa2fWvk0pbeUA5vFomm4uOs1/17LIciHv2iQ4UD+2Yv5H7AO8YiE1t50V0POiEGEw== - dependencies: - semver ">=7.5.2" +"@tauri-apps/cli-win32-ia32-msvc@2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@tauri-apps/cli-win32-ia32-msvc/-/cli-win32-ia32-msvc-2.4.0.tgz#cd5b3a1d3fda1dae254922cece455e394afaf43d" + integrity sha512-j+fOFVeSSejk9hrUePY7bJuaYr+80xr+ftjXzxCj0CS0d2oSbq+lT8/zS514WemJk9e9yxUus+2ke/Ng17wkkQ== + +"@tauri-apps/cli-win32-x64-msvc@2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@tauri-apps/cli-win32-x64-msvc/-/cli-win32-x64-msvc-2.4.0.tgz#d509a54c6cdf1436978dcf853f8ec3d903573799" + integrity sha512-nv84b3a8eI5Y7ksTLBKjjvtr9NOlAGGGo7OJbjKT+xZLdiPOZ0nJ2cT+4IdfnNAZ33pKJugAfuj1fBvQKeTy0w== + +"@tauri-apps/cli@^2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@tauri-apps/cli/-/cli-2.4.0.tgz#d1174ee62f5d982a2c6e65101c53e6763e40b2d6" + integrity sha512-Esg7s20tuSULd2YF3lmtMa1vF7yr5eh/TlBHXjEyrC+XSD9aBxHVoXb6oz7oKybDY9Jf9OiBa0bf2PbybcmOLA== optionalDependencies: - "@tauri-apps/cli-darwin-arm64" "1.6.3" - "@tauri-apps/cli-darwin-x64" "1.6.3" - "@tauri-apps/cli-linux-arm-gnueabihf" "1.6.3" - "@tauri-apps/cli-linux-arm64-gnu" "1.6.3" - "@tauri-apps/cli-linux-arm64-musl" "1.6.3" - "@tauri-apps/cli-linux-x64-gnu" "1.6.3" - "@tauri-apps/cli-linux-x64-musl" "1.6.3" - "@tauri-apps/cli-win32-arm64-msvc" "1.6.3" - "@tauri-apps/cli-win32-ia32-msvc" "1.6.3" - "@tauri-apps/cli-win32-x64-msvc" "1.6.3" + "@tauri-apps/cli-darwin-arm64" "2.4.0" + "@tauri-apps/cli-darwin-x64" "2.4.0" + "@tauri-apps/cli-linux-arm-gnueabihf" "2.4.0" + "@tauri-apps/cli-linux-arm64-gnu" "2.4.0" + "@tauri-apps/cli-linux-arm64-musl" "2.4.0" + "@tauri-apps/cli-linux-riscv64-gnu" "2.4.0" + "@tauri-apps/cli-linux-x64-gnu" "2.4.0" + "@tauri-apps/cli-linux-x64-musl" "2.4.0" + "@tauri-apps/cli-win32-arm64-msvc" "2.4.0" + "@tauri-apps/cli-win32-ia32-msvc" "2.4.0" + "@tauri-apps/cli-win32-x64-msvc" "2.4.0" + +"@tauri-apps/plugin-clipboard-manager@^2.2.2": + version "2.2.2" + resolved "https://registry.yarnpkg.com/@tauri-apps/plugin-clipboard-manager/-/plugin-clipboard-manager-2.2.2.tgz#19559f2b80dd6c692b4e7b884515b5b693ca9791" + integrity sha512-bZvDLMqfcNmsw7Ag8I49jlaCjdpDvvlJHnpp6P+Gg/3xtpSERdwlDxm7cKGbs2mj46dsw4AuG3RoAgcpwgioUA== + dependencies: + "@tauri-apps/api" "^2.0.0" + +"@tauri-apps/plugin-updater@^2.6.1": + version "2.6.1" + resolved "https://registry.yarnpkg.com/@tauri-apps/plugin-updater/-/plugin-updater-2.6.1.tgz#ce6665c1ab77fff96092283702dbe2777c3b295d" + integrity sha512-iiOevw4kc12Ok99J9KthXwUqwPv1sYjG+tNEDZqPmwvOmIq7s58nKMRz6NJPKXT4U16NzMPffFcP/LUOsz6c4A== + dependencies: + "@tauri-apps/api" "^2.0.0" "@tauri-apps/tauri-forage@^1.0.0-beta.2": version "1.0.0-beta.2" @@ -6736,7 +6364,7 @@ resolved "https://registry.yarnpkg.com/@types/d3-array/-/d3-array-3.2.1.tgz#1f6658e3d2006c4fceac53fde464166859f8b8c5" integrity sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg== -"@types/d3-color@*", "@types/d3-color@^3.0.0", "@types/d3-color@^3.1.3": +"@types/d3-color@*", "@types/d3-color@^3.1.3": version "3.1.3" resolved "https://registry.yarnpkg.com/@types/d3-color/-/d3-color-3.1.3.tgz#368c961a18de721da8200e80bf3943fb53136af2" integrity sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A== @@ -6768,11 +6396,6 @@ dependencies: "@types/d3-dsv" "*" -"@types/d3-format@^1.4.1": - version "1.4.5" - resolved "https://registry.yarnpkg.com/@types/d3-format/-/d3-format-1.4.5.tgz#6392303c2ca3c287c3a1a2046455cd0a0bd50bbe" - integrity sha512-mLxrC1MSWupOSncXN/HOlWUAAIffAEBaI4+PKy2uMPsKe4FNZlk7qrbTjmzJXITQQqBHivaks4Td18azgqnotA== - "@types/d3-geo@^1": version "1.12.7" resolved "https://registry.yarnpkg.com/@types/d3-geo/-/d3-geo-1.12.7.tgz#595680bf9b9525031cef956ed80baf38b8f3b4be" @@ -6806,11 +6429,6 @@ resolved "https://registry.yarnpkg.com/@types/d3-path/-/d3-path-3.1.1.tgz#f632b380c3aca1dba8e34aa049bcd6a4af23df8a" integrity sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg== -"@types/d3-scale-chromatic@^3.0.0": - version "3.1.0" - resolved "https://registry.yarnpkg.com/@types/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz#dc6d4f9a98376f18ea50bad6c39537f1b5463c39" - integrity sha512-iWMJgwkK7yTRmWqRB5plb1kadXyQ5Sj8V/zYlFGMUBbIPKQScw+Dku9cAAMgJG+z5GYDoMjWGLVOvjghDEFnKQ== - "@types/d3-scale@^4.0.1", "@types/d3-scale@^4.0.2", "@types/d3-scale@^4.0.8": version "4.0.9" resolved "https://registry.yarnpkg.com/@types/d3-scale/-/d3-scale-4.0.9.tgz#57a2f707242e6fe1de81ad7bfcccaaf606179afb" @@ -6830,26 +6448,11 @@ dependencies: "@types/d3-path" "*" -"@types/d3-time-format@^2.3.1": - version "2.3.4" - resolved "https://registry.yarnpkg.com/@types/d3-time-format/-/d3-time-format-2.3.4.tgz#544af5184df8b3fc4d9b42b14058789acee2905e" - integrity sha512-xdDXbpVO74EvadI3UDxjxTdR6QIxm1FKzEA/+F8tL4GWWUg/hgvBqf6chql64U5A9ZUGWo7pEu4eNlyLwbKdhg== - -"@types/d3-time-format@^3.0.0": - version "3.0.4" - resolved "https://registry.yarnpkg.com/@types/d3-time-format/-/d3-time-format-3.0.4.tgz#f972bdd7be1048184577cf235a44721a78c6bb4b" - integrity sha512-or9DiDnYI1h38J9hxKEsw513+KVuFbEVhl7qdxcaudoiqWWepapUen+2vAriFGexr6W5+P4l9+HJrB39GG+oRg== - "@types/d3-time@*", "@types/d3-time@^3.0.0", "@types/d3-time@^3.0.3": version "3.0.4" resolved "https://registry.yarnpkg.com/@types/d3-time/-/d3-time-3.0.4.tgz#8472feecd639691450dd8000eb33edd444e1323f" integrity sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g== -"@types/d3-time@^1.1.1": - version "1.1.4" - resolved "https://registry.yarnpkg.com/@types/d3-time/-/d3-time-1.1.4.tgz#20da4b75c537a940e7319b75717c67a2e499515a" - integrity sha512-JIvy2HjRInE+TXOmIGN5LCmeO0hkFZx5f9FZ7kiN+D+YTcc8pptsiLiuHsvwxwC7VVKmJ2ExHUgNlAiV7vQM9g== - "@types/d3-timer@^3.0.0": version "3.0.2" resolved "https://registry.yarnpkg.com/@types/d3-timer/-/d3-timer-3.0.2.tgz#70bbda77dc23aa727413e22e214afa3f0e852f70" @@ -6863,13 +6466,6 @@ "@types/d3-interpolate" "^1" "@types/d3-selection" "^1" -"@types/debug@^4.0.0": - version "4.1.12" - resolved "https://registry.yarnpkg.com/@types/debug/-/debug-4.1.12.tgz#a155f21690871953410df4b6b6f53187f0500917" - integrity sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ== - dependencies: - "@types/ms" "*" - "@types/eslint-scope@^3.7.7": version "3.7.7" resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.7.tgz#3108bd5f18b0cdb277c867b3dd449c9ed7079ac5" @@ -6886,13 +6482,6 @@ "@types/estree" "*" "@types/json-schema" "*" -"@types/estree-jsx@^1.0.0": - version "1.0.5" - resolved "https://registry.yarnpkg.com/@types/estree-jsx/-/estree-jsx-1.0.5.tgz#858a88ea20f34fe65111f005a689fa1ebf70dc18" - integrity sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg== - dependencies: - "@types/estree" "*" - "@types/estree@*", "@types/estree@^1.0.0", "@types/estree@^1.0.6": version "1.0.6" resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.6.tgz#628effeeae2064a1b4e79f78e81d87b7e5fc7b50" @@ -6990,13 +6579,6 @@ dependencies: "@types/unist" "^2" -"@types/hast@^3.0.0": - version "3.0.4" - resolved "https://registry.yarnpkg.com/@types/hast/-/hast-3.0.4.tgz#1d6b39993b82cea6ad783945b0508c25903e15aa" - integrity sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ== - dependencies: - "@types/unist" "*" - "@types/html-minifier-terser@^5.0.0": version "5.1.2" resolved "https://registry.yarnpkg.com/@types/html-minifier-terser/-/html-minifier-terser-5.1.2.tgz#693b316ad323ea97eed6b38ed1a3cc02b1672b57" @@ -7086,13 +6668,6 @@ dependencies: "@types/unist" "^2" -"@types/mdast@^4.0.0": - version "4.0.4" - resolved "https://registry.yarnpkg.com/@types/mdast/-/mdast-4.0.4.tgz#7ccf72edd2f1aa7dd3437e180c64373585804dd6" - integrity sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA== - dependencies: - "@types/unist" "*" - "@types/mime@^1": version "1.3.5" resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.5.tgz#1ef302e01cf7d2b5a0fa526790c9123bf1d06690" @@ -7113,11 +6688,6 @@ resolved "https://registry.yarnpkg.com/@types/minimist/-/minimist-1.2.5.tgz#ec10755e871497bcd83efe927e43ec46e8c0747e" integrity sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag== -"@types/ms@*": - version "2.1.0" - resolved "https://registry.yarnpkg.com/@types/ms/-/ms-2.1.0.tgz#052aa67a48eccc4309d7f0191b7e41434b90bb78" - integrity sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA== - "@types/node-fetch@^2.5.7": version "2.6.12" resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.6.12.tgz#8ab5c3ef8330f13100a7479e2cd56d3386830a03" @@ -7194,7 +6764,7 @@ resolved "https://registry.yarnpkg.com/@types/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz#ee1bd8c9f7a01b3445786aad0ef23aba5f511a44" integrity sha512-nj39q0wAIdhwn7DGUyT9irmsKK1tV0bd5WFEhgpqNTMFZ8cE+jieuTphCW0tfdm47S2zVT5mr09B28b1chmQMA== -"@types/prop-types@*", "@types/prop-types@^15.7.12", "@types/prop-types@^15.7.14", "@types/prop-types@^15.7.2": +"@types/prop-types@*", "@types/prop-types@^15.7.12", "@types/prop-types@^15.7.14": version "15.7.14" resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.14.tgz#1433419d73b2a7ebfc6918dcefd2ec0d5cd698f2" integrity sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ== @@ -7206,7 +6776,7 @@ dependencies: "@types/react" "*" -"@types/qs@*", "@types/qs@^6.9.18", "@types/qs@^6.9.5": +"@types/qs@*", "@types/qs@^6.9.5": version "6.9.18" resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.18.tgz#877292caa91f7c1b213032b34626505b746624c2" integrity sha512-kK7dgTYDyGqS+e2Q4aK9X3D7q234CIZ1Bv0q/7Z5IwRDoADNU81xXJK/YVyLbLTZCoIwUoDoffFeF+p/eIklAA== @@ -7243,7 +6813,7 @@ dependencies: react-tooltip "*" -"@types/react-transition-group@^4.4.10", "@types/react-transition-group@^4.4.11", "@types/react-transition-group@^4.4.12": +"@types/react-transition-group@^4.4.10", "@types/react-transition-group@^4.4.11": version "4.4.12" resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-4.4.12.tgz#b5d76568485b02a307238270bfe96cb51ee2a044" integrity sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w== @@ -7346,11 +6916,6 @@ dependencies: "@types/geojson" "*" -"@types/trusted-types@^2.0.7": - version "2.0.7" - resolved "https://registry.yarnpkg.com/@types/trusted-types/-/trusted-types-2.0.7.tgz#baccb07a970b91707df3a3e8ba6896c57ead2d11" - integrity sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw== - "@types/uglify-js@*": version "3.17.5" resolved "https://registry.yarnpkg.com/@types/uglify-js/-/uglify-js-3.17.5.tgz#905ce03a3cbbf2e31cbefcbc68d15497ee2e17df" @@ -7358,11 +6923,6 @@ dependencies: source-map "^0.6.1" -"@types/unist@*", "@types/unist@^3.0.0": - version "3.0.3" - resolved "https://registry.yarnpkg.com/@types/unist/-/unist-3.0.3.tgz#acaab0f919ce69cce629c2d4ed2eb4adc1b6c20c" - integrity sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q== - "@types/unist@^2", "@types/unist@^2.0.0", "@types/unist@^2.0.2", "@types/unist@^2.0.3": version "2.0.11" resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.11.tgz#11af57b127e32487774841f7a4e54eab166d03c4" @@ -7453,21 +7013,6 @@ semver "^7.3.7" tsutils "^3.21.0" -"@typescript-eslint/eslint-plugin@^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0": - version "8.26.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.26.1.tgz#3e48eb847924161843b092c87a9b65176b53782f" - integrity sha512-2X3mwqsj9Bd3Ciz508ZUtoQQYpOhU/kWoUqIf49H8Z0+Vbh6UF/y0OEYp0Q0axOGzaBGs7QxRwq0knSQ8khQNA== - dependencies: - "@eslint-community/regexpp" "^4.10.0" - "@typescript-eslint/scope-manager" "8.26.1" - "@typescript-eslint/type-utils" "8.26.1" - "@typescript-eslint/utils" "8.26.1" - "@typescript-eslint/visitor-keys" "8.26.1" - graphemer "^1.4.0" - ignore "^5.3.1" - natural-compare "^1.4.0" - ts-api-utils "^2.0.1" - "@typescript-eslint/experimental-utils@^5.3.0": version "5.62.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-5.62.0.tgz#14559bf73383a308026b427a4a6129bae2146741" @@ -7496,17 +7041,6 @@ "@typescript-eslint/visitor-keys" "6.21.0" debug "^4.3.4" -"@typescript-eslint/parser@^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0": - version "8.26.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.26.1.tgz#0e2f915a497519fc43f52cf2ecbfa607ff56f72e" - integrity sha512-w6HZUV4NWxqd8BdeFf81t07d7/YV9s7TCWrQQbG5uhuvGUAW+fq1usZ1Hmz9UPNLniFnD8GLSsDpjP0hm1S4lQ== - dependencies: - "@typescript-eslint/scope-manager" "8.26.1" - "@typescript-eslint/types" "8.26.1" - "@typescript-eslint/typescript-estree" "8.26.1" - "@typescript-eslint/visitor-keys" "8.26.1" - debug "^4.3.4" - "@typescript-eslint/scope-manager@5.62.0": version "5.62.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz#d9457ccc6a0b8d6b37d0eb252a23022478c5460c" @@ -7523,14 +7057,6 @@ "@typescript-eslint/types" "6.21.0" "@typescript-eslint/visitor-keys" "6.21.0" -"@typescript-eslint/scope-manager@8.26.1": - version "8.26.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.26.1.tgz#5e6ad0ac258ccf79462e91c3f43a3f1f7f31a6cc" - integrity sha512-6EIvbE5cNER8sqBu6V7+KeMZIC1664d2Yjt+B9EWUXrsyWpxx4lEZrmvxgSKRC6gX+efDL/UY9OpPZ267io3mg== - dependencies: - "@typescript-eslint/types" "8.26.1" - "@typescript-eslint/visitor-keys" "8.26.1" - "@typescript-eslint/type-utils@5.62.0": version "5.62.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.62.0.tgz#286f0389c41681376cdad96b309cedd17d70346a" @@ -7541,16 +7067,6 @@ debug "^4.3.4" tsutils "^3.21.0" -"@typescript-eslint/type-utils@8.26.1": - version "8.26.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.26.1.tgz#462f0bae09de72ac6e8e1af2ebe588c23224d7f8" - integrity sha512-Kcj/TagJLwoY/5w9JGEFV0dclQdyqw9+VMndxOJKtoFSjfZhLXhYjzsQEeyza03rwHx2vFEGvrJWJBXKleRvZg== - dependencies: - "@typescript-eslint/typescript-estree" "8.26.1" - "@typescript-eslint/utils" "8.26.1" - debug "^4.3.4" - ts-api-utils "^2.0.1" - "@typescript-eslint/types@5.62.0": version "5.62.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.62.0.tgz#258607e60effa309f067608931c3df6fed41fd2f" @@ -7561,11 +7077,6 @@ resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-6.21.0.tgz#205724c5123a8fef7ecd195075fa6e85bac3436d" integrity sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg== -"@typescript-eslint/types@8.26.1": - version "8.26.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.26.1.tgz#d5978721670cff263348d5062773389231a64132" - integrity sha512-n4THUQW27VmQMx+3P+B0Yptl7ydfceUj4ON/AQILAASwgYdZ/2dhfymRMh5egRUrvK5lSmaOm77Ry+lmXPOgBQ== - "@typescript-eslint/typescript-estree@5.62.0": version "5.62.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz#7d17794b77fabcac615d6a48fb143330d962eb9b" @@ -7593,20 +7104,6 @@ semver "^7.5.4" ts-api-utils "^1.0.1" -"@typescript-eslint/typescript-estree@8.26.1": - version "8.26.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.26.1.tgz#eb0e4ce31753683d83be53441a409fd5f0b34afd" - integrity sha512-yUwPpUHDgdrv1QJ7YQal3cMVBGWfnuCdKbXw1yyjArax3353rEJP1ZA+4F8nOlQ3RfS2hUN/wze3nlY+ZOhvoA== - dependencies: - "@typescript-eslint/types" "8.26.1" - "@typescript-eslint/visitor-keys" "8.26.1" - debug "^4.3.4" - fast-glob "^3.3.2" - is-glob "^4.0.3" - minimatch "^9.0.4" - semver "^7.6.0" - ts-api-utils "^2.0.1" - "@typescript-eslint/utils@5.62.0", "@typescript-eslint/utils@^5.10.0": version "5.62.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.62.0.tgz#141e809c71636e4a75daa39faed2fb5f4b10df86" @@ -7621,16 +7118,6 @@ eslint-scope "^5.1.1" semver "^7.3.7" -"@typescript-eslint/utils@8.26.1": - version "8.26.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.26.1.tgz#54cc58469955f25577f659753b71a0e117a0539f" - integrity sha512-V4Urxa/XtSUroUrnI7q6yUTD3hDtfJ2jzVfeT3VK0ciizfK2q/zGC0iDh1lFMUZR8cImRrep6/q0xd/1ZGPQpg== - dependencies: - "@eslint-community/eslint-utils" "^4.4.0" - "@typescript-eslint/scope-manager" "8.26.1" - "@typescript-eslint/types" "8.26.1" - "@typescript-eslint/typescript-estree" "8.26.1" - "@typescript-eslint/visitor-keys@5.62.0": version "5.62.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz#2174011917ce582875954ffe2f6912d5931e353e" @@ -7647,20 +7134,7 @@ "@typescript-eslint/types" "6.21.0" eslint-visitor-keys "^3.4.1" -"@typescript-eslint/visitor-keys@8.26.1": - version "8.26.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.26.1.tgz#c5267fcc82795cf10280363023837deacad2647c" - integrity sha512-AjOC3zfnxd6S4Eiy3jwktJPclqhFHNyd8L6Gycf9WUPoKZpgM5PjkxY1X7uSy61xVpiJDhhk7XT2NVsN3ALTWg== - dependencies: - "@typescript-eslint/types" "8.26.1" - eslint-visitor-keys "^4.2.0" - -"@uidotdev/usehooks@^2.4.1": - version "2.4.1" - resolved "https://registry.yarnpkg.com/@uidotdev/usehooks/-/usehooks-2.4.1.tgz#4b733eaeae09a7be143c6c9ca158b56cc1ea75bf" - integrity sha512-1I+RwWyS+kdv3Mv0Vmc+p0dPYH0DTRAo04HLyXReYBL9AeseDWUJyi4THuksBJcu9F0Pih69Ak150VDnqbVnXg== - -"@ungap/structured-clone@^1.0.0", "@ungap/structured-clone@^1.2.0": +"@ungap/structured-clone@^1.2.0": version "1.3.0" resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.3.0.tgz#d06bbb384ebcf6c505fde1c3d0ed4ddffe0aaff8" integrity sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g== @@ -8242,11 +7716,6 @@ agent-base@6, agent-base@^6.0.2: dependencies: debug "4" -agent-base@^7.1.0, agent-base@^7.1.2: - version "7.1.3" - resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.3.tgz#29435eb821bc4194633a5b89e5bc4703bafc25a1" - integrity sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw== - agentkeepalive@^4.2.1: version "4.6.0" resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.6.0.tgz#35f73e94b3f40bf65f105219c623ad19c136ea6a" @@ -8966,11 +8435,6 @@ bail@^1.0.0: resolved "https://registry.yarnpkg.com/bail/-/bail-1.0.5.tgz#b6fa133404a392cbc1f8c4bf63f5953351e7a776" integrity sha512-xFbRxM1tahm08yHBP16MMjVUAvDaBMD38zsM9EMAUN61omwLmKlOpB/Zku5QkjZ8TZ4vn53pj+t518cH0S03RQ== -bail@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/bail/-/bail-2.0.2.tgz#d26f5cd8fe5d6f832a31517b9f7c356040ba6d5d" - integrity sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw== - balanced-match@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" @@ -9641,12 +9105,7 @@ ccount@^1.0.0: resolved "https://registry.yarnpkg.com/ccount/-/ccount-1.1.0.tgz#246687debb6014735131be8abab2d93898f8d043" integrity sha512-vlNK021QdI7PNeiUh/lKkC/mNHHfV0m/Ad5JoI0TYtlBnJAslM/JIkm/tGC88bkLIwO6OQ5uV6ztS6kVAtCDlg== -ccount@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/ccount/-/ccount-2.0.1.tgz#17a3bf82302e0870d6da43a01311a8bc02a3ecf5" - integrity sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg== - -chain-registry@^1.29.1, chain-registry@^1.69.64: +chain-registry@^1.29.1: version "1.69.155" resolved "https://registry.yarnpkg.com/chain-registry/-/chain-registry-1.69.155.tgz#72077fba2a0e39cf241854aca6745f585261d032" integrity sha512-d6bQWd7xSmp31pNMSam1bApqO7oCjLeoodipRLQNJL1C9svNquAv+MseNI9O5rf+cpb9kIpvrqa6NaAOt0i8WQ== @@ -9691,41 +9150,21 @@ char-regex@^1.0.2: resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf" integrity sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw== -character-entities-html4@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/character-entities-html4/-/character-entities-html4-2.1.0.tgz#1f1adb940c971a4b22ba39ddca6b618dc6e56b2b" - integrity sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA== - character-entities-legacy@^1.0.0: version "1.1.4" resolved "https://registry.yarnpkg.com/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz#94bc1845dce70a5bb9d2ecc748725661293d8fc1" integrity sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA== -character-entities-legacy@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz#76bc83a90738901d7bc223a9e93759fdd560125b" - integrity sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ== - character-entities@^1.0.0: version "1.2.4" resolved "https://registry.yarnpkg.com/character-entities/-/character-entities-1.2.4.tgz#e12c3939b7eaf4e5b15e7ad4c5e28e1d48c5b16b" integrity sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw== -character-entities@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/character-entities/-/character-entities-2.0.2.tgz#2d09c2e72cd9523076ccb21157dff66ad43fcc22" - integrity sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ== - character-reference-invalid@^1.0.0: version "1.1.4" resolved "https://registry.yarnpkg.com/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz#083329cda0eae272ab3dbbf37e9a382c13af1560" integrity sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg== -character-reference-invalid@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz#85c66b041e43b47210faf401278abf808ac45cb9" - integrity sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw== - chardet@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" @@ -9825,11 +9264,6 @@ classnames@^2.3.0: resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.5.1.tgz#ba774c614be0f016da105c858e7159eae8e7687b" integrity sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow== -cldr-compact-number@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/cldr-compact-number/-/cldr-compact-number-0.4.0.tgz#d74bb8cedab92ca63032423a1bd26aaaf76b9b2c" - integrity sha512-f/Wz46qoQrfhF2Mq4TcsSbIXMaB70LhioR4fJAbYi5nTnczh1LnSsWuBOXuyKTwFsyrMCRnPFr0OHjl0XB6y9A== - clean-css@^4.2.3: version "4.2.4" resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-4.2.4.tgz#733bf46eba4e607c6891ea57c24a989356831178" @@ -10062,11 +9496,6 @@ comma-separated-tokens@^1.0.0: resolved "https://registry.yarnpkg.com/comma-separated-tokens/-/comma-separated-tokens-1.0.8.tgz#632b80b6117867a158f1080ad498b2fbe7e3f5ea" integrity sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw== -comma-separated-tokens@^2.0.0: - version "2.0.3" - resolved "https://registry.yarnpkg.com/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz#4e89c9458acb61bc8fef19f4529973b2392839ee" - integrity sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg== - commander@2, commander@^2.19.0, commander@^2.20.0: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" @@ -10589,17 +10018,6 @@ css-select@^4.1.3: domutils "^2.8.0" nth-check "^2.0.1" -css-select@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/css-select/-/css-select-5.1.0.tgz#b8ebd6554c3637ccc76688804ad3f6a6fdaea8a6" - integrity sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg== - dependencies: - boolbase "^1.0.0" - css-what "^6.1.0" - domhandler "^5.0.2" - domutils "^3.0.1" - nth-check "^2.0.1" - css-tree@^1.1.2, css-tree@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.1.3.tgz#eb4870fb6fd7707327ec95c2ff2ab09b5e8db91d" @@ -10608,22 +10026,6 @@ css-tree@^1.1.2, css-tree@^1.1.3: mdn-data "2.0.14" source-map "^0.6.1" -css-tree@^2.3.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-2.3.1.tgz#10264ce1e5442e8572fc82fbe490644ff54b5c20" - integrity sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw== - dependencies: - mdn-data "2.0.30" - source-map-js "^1.0.1" - -css-tree@~2.2.0: - version "2.2.1" - resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-2.2.1.tgz#36115d382d60afd271e377f9c5f67d02bd48c032" - integrity sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA== - dependencies: - mdn-data "2.0.28" - source-map-js "^1.0.1" - css-vendor@^2.0.8: version "2.0.8" resolved "https://registry.yarnpkg.com/css-vendor/-/css-vendor-2.0.8.tgz#e47f91d3bd3117d49180a3c935e62e3d9f7f449d" @@ -10703,13 +10105,6 @@ csso@^4.2.0: dependencies: css-tree "^1.1.2" -csso@^5.0.5: - version "5.0.5" - resolved "https://registry.yarnpkg.com/csso/-/csso-5.0.5.tgz#f9b7fe6cc6ac0b7d90781bb16d5e9874303e2ca6" - integrity sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ== - dependencies: - css-tree "~2.2.0" - cssom@^0.4.4: version "0.4.4" resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.4.4.tgz#5a66cf93d2d0b661d80bf6a44fb65f5c2e4e0a10" @@ -10727,14 +10122,6 @@ cssstyle@^2.3.0: dependencies: cssom "~0.3.6" -cssstyle@^4.2.1: - version "4.3.0" - resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-4.3.0.tgz#83db22d1aec8eb7e5ecd812b4d14a17fb3dd243d" - integrity sha512-6r0NiY0xizYqfBvWp1G7WXJ06/bZyrk7Dc6PHql82C/pKGUTKu4yAX4Y8JPamb1ob9nBKuxWzCGTRuGwU3yxJQ== - dependencies: - "@asamuzakjp/css-color" "^3.1.1" - rrweb-cssom "^0.8.0" - csstype@^3.0.2, csstype@^3.0.7, csstype@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.3.tgz#d80ff294d114fb0e6ac500fbf85b60137d7eff81" @@ -10752,13 +10139,6 @@ cyclist@^1.0.1: resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-1.0.2.tgz#673b5f233bf34d8e602b949429f8171d9121bea3" integrity sha512-0sVXIohTfLqVIW3kb/0n6IiWF3Ifj5nm2XaSrLq2DI6fKIGa2fYAZdk917rUneaeLVpYfFcyXE2ft0fe3remsA== -d3-array@2, d3-array@^2.5.0: - version "2.12.1" - resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-2.12.1.tgz#e20b41aafcdffdf5d50928004ececf815a465e81" - integrity sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ== - dependencies: - internmap "^1.0.0" - "d3-array@2 - 3", "d3-array@2.10.0 - 3", d3-array@^3.1.6: version "3.2.4" resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-3.2.4.tgz#15fec33b237f97ac5d7c986dc77da273a8ed0bb5" @@ -10766,6 +10146,13 @@ d3-array@2, d3-array@^2.5.0: dependencies: internmap "1 - 2" +d3-array@^2.5.0: + version "2.12.1" + resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-2.12.1.tgz#e20b41aafcdffdf5d50928004ececf815a465e81" + integrity sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ== + dependencies: + internmap "^1.0.0" + "d3-color@1 - 2": version "2.0.0" resolved "https://registry.yarnpkg.com/d3-color/-/d3-color-2.0.0.tgz#8d625cab42ed9b8f601a1760a389f7ea9189d62e" @@ -10811,11 +10198,6 @@ d3-ease@^3.0.1: resolved "https://registry.yarnpkg.com/d3-format/-/d3-format-3.1.0.tgz#9260e23a28ea5cb109e93b21a06e24e2ebd55641" integrity sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA== -d3-format@^1.4.4: - version "1.4.5" - resolved "https://registry.yarnpkg.com/d3-format/-/d3-format-1.4.5.tgz#374f2ba1320e3717eb74a9356c67daee17a7edb4" - integrity sha512-J0piedu6Z8iB6TbIGfZgDzfXxUFN3qQRMofy2oPdXzQibYGqPB/9iMcxr/TGalU+2RsyDO+U4f33id8tbnSRMQ== - d3-geo@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/d3-geo/-/d3-geo-2.0.2.tgz#c065c1b71fe8c5f1be657e5f43d9bdd010383c40" @@ -10830,7 +10212,7 @@ d3-geo@^2.0.1: dependencies: d3-color "1 - 2" -"d3-interpolate@1 - 3", "d3-interpolate@1.2.0 - 3", d3-interpolate@^3.0.1: +"d3-interpolate@1.2.0 - 3", d3-interpolate@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/d3-interpolate/-/d3-interpolate-3.0.1.tgz#3c47aa5b32c5b3dfb56ef3fd4342078a632b400d" integrity sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g== @@ -10842,14 +10224,6 @@ d3-path@^3.1.0: resolved "https://registry.yarnpkg.com/d3-path/-/d3-path-3.1.0.tgz#22df939032fb5a71ae8b1800d61ddb7851c42526" integrity sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ== -d3-scale-chromatic@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz#34c39da298b23c20e02f1a4b239bd0f22e7f1314" - integrity sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ== - dependencies: - d3-color "1 - 3" - d3-interpolate "1 - 3" - d3-scale@^4.0.0, d3-scale@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/d3-scale/-/d3-scale-4.0.2.tgz#82b38e8e8ff7080764f8dcec77bd4be393689396" @@ -10880,20 +10254,6 @@ d3-shape@^3.1.0, d3-shape@^3.2.0: dependencies: d3-time "1 - 3" -d3-time-format@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/d3-time-format/-/d3-time-format-3.0.0.tgz#df8056c83659e01f20ac5da5fdeae7c08d5f1bb6" - integrity sha512-UXJh6EKsHBTjopVqZBhFysQcoXSv/5yLONZvkQ5Kk3qbwiUYkdX17Xa1PT6U1ZWXGGfB1ey5L8dKMlFq2DO0Ag== - dependencies: - d3-time "1 - 2" - -"d3-time@1 - 2": - version "2.1.1" - resolved "https://registry.yarnpkg.com/d3-time/-/d3-time-2.1.1.tgz#e9d8a8a88691f4548e68ca085e5ff956724a6682" - integrity sha512-/eIQe/eR4kCQwq7yxi7z4c6qEXf2IYGcjoWB5OOQy4Tq9Uv39/947qlDcN2TLkiTzQWzvnsuYPB9TrWaNfipKQ== - dependencies: - d3-array "2" - "d3-time@1 - 3", "d3-time@2.1.1 - 3", d3-time@^3.0.0, d3-time@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/d3-time/-/d3-time-3.1.0.tgz#9310db56e992e3c0175e1ef385e545e48a9bb5c7" @@ -10901,11 +10261,6 @@ d3-time-format@^3.0.0: dependencies: d3-array "2 - 3" -d3-time@^1.0.11: - version "1.1.0" - resolved "https://registry.yarnpkg.com/d3-time/-/d3-time-1.1.0.tgz#b1e19d307dae9c900b7e5b25ffc5dcc249a8a0f1" - integrity sha512-Xh0isrZ5rPYYdqhAVk8VLnMEidhz5aP7htAADH6MfzgmmicPkTo8LhkLxci61/lCB7n7UmE3bN0leRt+qvkLxA== - "d3-timer@1 - 2": version "2.0.0" resolved "https://registry.yarnpkg.com/d3-timer/-/d3-timer-2.0.0.tgz#055edb1d170cfe31ab2da8968deee940b56623e6" @@ -10957,14 +10312,6 @@ data-urls@^2.0.0: whatwg-mimetype "^2.3.0" whatwg-url "^8.0.0" -data-urls@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-5.0.0.tgz#2f76906bce1824429ffecb6920f45a0b30f00dde" - integrity sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg== - dependencies: - whatwg-mimetype "^4.0.0" - whatwg-url "^14.0.0" - data-view-buffer@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/data-view-buffer/-/data-view-buffer-1.0.2.tgz#211a03ba95ecaf7798a8c7198d79536211f88570" @@ -10999,11 +10346,6 @@ date-fns@^2.24.0, date-fns@^2.28.0: dependencies: "@babel/runtime" "^7.21.0" -date-fns@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-4.1.0.tgz#64b3d83fff5aa80438f5b1a633c2e83b8a1c2d14" - integrity sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg== - dateformat@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae" @@ -11016,7 +10358,7 @@ debug@2.6.9, debug@^2.2.0, debug@^2.3.3: dependencies: ms "2.0.0" -debug@4, debug@^4.0.0, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4, debug@^4.3.5, debug@^4.4.0: +debug@4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4, debug@^4.3.5, debug@^4.4.0: version "4.4.0" resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.0.tgz#2b3f2aea2ffeb776477460267377dc8710faba8a" integrity sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA== @@ -11053,18 +10395,11 @@ decimal.js-light@^2.4.1: resolved "https://registry.yarnpkg.com/decimal.js-light/-/decimal.js-light-2.5.1.tgz#134fd32508f19e208f4fb2f8dac0d2626a867934" integrity sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg== -decimal.js@10, decimal.js@^10.2.1, decimal.js@^10.4.3: +decimal.js@10, decimal.js@^10.2.1: version "10.5.0" resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.5.0.tgz#0f371c7cf6c4898ce0afb09836db73cd82010f22" integrity sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw== -decode-named-character-reference@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/decode-named-character-reference/-/decode-named-character-reference-1.1.0.tgz#5d6ce68792808901210dac42a8e9853511e2b8bf" - integrity sha512-Wy+JTSbFThEOXQIR2L6mxJvEs+veIzpmqD7ynWxMXGpnk3smkHQOp6forLdHsKpAMW9iJpaBBIxz285t1n1C3w== - dependencies: - character-entities "^2.0.0" - decode-uri-component@^0.2.0: version "0.2.2" resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.2.tgz#e69dbe25d37941171dd540e024c444cd5188e1e9" @@ -11242,11 +10577,6 @@ deprecation@^2.0.0: resolved "https://registry.yarnpkg.com/deprecation/-/deprecation-2.3.1.tgz#6368cbdb40abf3373b525ac87e4a260c3a700919" integrity sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ== -dequal@^2.0.0: - version "2.0.3" - resolved "https://registry.yarnpkg.com/dequal/-/dequal-2.0.3.tgz#2644214f1997d39ed0ee0ece72335490a7ac67be" - integrity sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA== - des.js@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.1.0.tgz#1d37f5766f3bbff4ee9638e871a8768c173b81da" @@ -11307,13 +10637,6 @@ detect-port@^1.3.0: address "^1.0.1" debug "4" -devlop@^1.0.0, devlop@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/devlop/-/devlop-1.1.0.tgz#4db7c2ca4dc6e0e834c30be70c94bbc976dc7018" - integrity sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA== - dependencies: - dequal "^2.0.0" - diacritics@1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/diacritics/-/diacritics-1.3.0.tgz#3efa87323ebb863e6696cebb0082d48ff3d6f7a1" @@ -11422,15 +10745,6 @@ dom-serializer@^1.0.1: domhandler "^4.2.0" entities "^2.0.0" -dom-serializer@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-2.0.0.tgz#e41b802e1eedf9f6cae183ce5e622d789d7d8e53" - integrity sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg== - dependencies: - domelementtype "^2.3.0" - domhandler "^5.0.2" - entities "^4.2.0" - dom-walk@^0.1.0: version "0.1.2" resolved "https://registry.yarnpkg.com/dom-walk/-/dom-walk-0.1.2.tgz#0c548bef048f4d1f2a97249002236060daa3fd84" @@ -11441,7 +10755,7 @@ domain-browser@^1.1.1: resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.2.0.tgz#3d31f50191a6749dd1375a7f522e823d42e54eda" integrity sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA== -domelementtype@^2.0.1, domelementtype@^2.2.0, domelementtype@^2.3.0: +domelementtype@^2.0.1, domelementtype@^2.2.0: version "2.3.0" resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.3.0.tgz#5c45e8e869952626331d7aab326d01daf65d589d" integrity sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw== @@ -11460,20 +10774,6 @@ domhandler@^4.0.0, domhandler@^4.2.0, domhandler@^4.3.1: dependencies: domelementtype "^2.2.0" -domhandler@^5.0.2, domhandler@^5.0.3: - version "5.0.3" - resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-5.0.3.tgz#cc385f7f751f1d1fc650c21374804254538c7d31" - integrity sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w== - dependencies: - domelementtype "^2.3.0" - -dompurify@^3.2.4: - version "3.2.4" - resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-3.2.4.tgz#af5a5a11407524431456cf18836c55d13441cd8e" - integrity sha512-ysFSFEDVduQpyhzAob/kkuJjf5zWkZD8/A9ywSp1byueyuCfHamrCBa14/Oc2iiB0e51B+NpxSl5gmzn+Ms/mg== - optionalDependencies: - "@types/trusted-types" "^2.0.7" - domutils@^2.5.2, domutils@^2.8.0: version "2.8.0" resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.8.0.tgz#4437def5db6e2d1f5d6ee859bd95ca7d02048135" @@ -11483,15 +10783,6 @@ domutils@^2.5.2, domutils@^2.8.0: domelementtype "^2.2.0" domhandler "^4.2.0" -domutils@^3.0.1: - version "3.2.2" - resolved "https://registry.yarnpkg.com/domutils/-/domutils-3.2.2.tgz#edbfe2b668b0c1d97c24baf0f1062b132221bc78" - integrity sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw== - dependencies: - dom-serializer "^2.0.0" - domelementtype "^2.3.0" - domhandler "^5.0.3" - dot-case@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/dot-case/-/dot-case-3.0.4.tgz#9b2b670d00a431667a8a75ba29cd1b98809ce751" @@ -11705,7 +10996,7 @@ entities@^2.0.0: resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55" integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A== -entities@^4.2.0, entities@^4.4.0, entities@^4.5.0: +entities@^4.4.0: version "4.5.0" resolved "https://registry.yarnpkg.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48" integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw== @@ -11985,22 +11276,6 @@ eslint-config-next@14.1.4: eslint-plugin-react "^7.33.2" eslint-plugin-react-hooks "^4.5.0 || 5.0.0-canary-7118f5dd7-20230705" -eslint-config-next@15.0.3: - version "15.0.3" - resolved "https://registry.yarnpkg.com/eslint-config-next/-/eslint-config-next-15.0.3.tgz#b483585260d5e55050d4ab87e053c88089ae12ee" - integrity sha512-IGP2DdQQrgjcr4mwFPve4DrCqo7CVVez1WoYY47XwKSrYO4hC0Dlb+iJA60i0YfICOzgNADIb8r28BpQ5Zs0wg== - dependencies: - "@next/eslint-plugin-next" "15.0.3" - "@rushstack/eslint-patch" "^1.10.3" - "@typescript-eslint/eslint-plugin" "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0" - "@typescript-eslint/parser" "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0" - eslint-import-resolver-node "^0.3.6" - eslint-import-resolver-typescript "^3.5.2" - eslint-plugin-import "^2.31.0" - eslint-plugin-jsx-a11y "^6.10.0" - eslint-plugin-react "^7.35.0" - eslint-plugin-react-hooks "^5.0.0" - eslint-config-prettier@^8.3.0, eslint-config-prettier@^8.5.0: version "8.10.0" resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.10.0.tgz#3a06a662130807e2502fc3ff8b4143d8a0658e11" @@ -12043,7 +11318,7 @@ eslint-module-utils@^2.12.0: dependencies: debug "^3.2.7" -eslint-plugin-import@^2.25.4, eslint-plugin-import@^2.28.1, eslint-plugin-import@^2.31.0: +eslint-plugin-import@^2.25.4, eslint-plugin-import@^2.28.1: version "2.31.0" resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.31.0.tgz#310ce7e720ca1d9c0bb3f69adfd1c6bdd7d9e0e7" integrity sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A== @@ -12075,7 +11350,7 @@ eslint-plugin-jest@^26.1.1: dependencies: "@typescript-eslint/utils" "^5.10.0" -eslint-plugin-jsx-a11y@^6.10.0, eslint-plugin-jsx-a11y@^6.5.1, eslint-plugin-jsx-a11y@^6.7.1: +eslint-plugin-jsx-a11y@^6.5.1, eslint-plugin-jsx-a11y@^6.7.1: version "6.10.2" resolved "https://registry.yarnpkg.com/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.10.2.tgz#d2812bb23bf1ab4665f1718ea442e8372e638483" integrity sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q== @@ -12122,12 +11397,7 @@ eslint-plugin-react-hooks@^4.3.0: resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.0.0-canary-7118f5dd7-20230705.tgz#4d55c50e186f1a2b0636433d2b0b2f592ddbccfd" integrity sha512-AZYbMo/NW9chdL7vk6HQzQhT+PvTAEVqWk9ziruUoW2kAOcN5qNyelv70e0F1VNQAbvutOC9oc+xfWycI9FxDw== -eslint-plugin-react-hooks@^5.0.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.2.0.tgz#1be0080901e6ac31ce7971beed3d3ec0a423d9e3" - integrity sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg== - -eslint-plugin-react@^7.29.2, eslint-plugin-react@^7.33.2, eslint-plugin-react@^7.35.0: +eslint-plugin-react@^7.29.2, eslint-plugin-react@^7.33.2: version "7.37.4" resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.37.4.tgz#1b6c80b6175b6ae4b26055ae4d55d04c414c7181" integrity sha512-BGP0jRmfYyvOyvMoRX/uoUeW+GqNj9y16bPQzqAHf3AYII/tDs+jMN0dBVkl88/OZwNGwrVFxE7riHsXVfy/LQ== @@ -12213,11 +11483,6 @@ eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4 resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== -eslint-visitor-keys@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz#687bacb2af884fcdda8a6e7d65c606f46a14cd45" - integrity sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw== - eslint@^7.18.0: version "7.32.0" resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.32.0.tgz#c6d328a14be3fb08c8d1d21e12c02fdb7a2a812d" @@ -12364,11 +11629,6 @@ estree-to-babel@^3.1.0: "@babel/types" "^7.2.0" c8 "^7.6.0" -estree-util-is-identifier-name@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz#0b5ef4c4ff13508b34dcd01ecfa945f61fce5dbd" - integrity sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg== - estree-walker@^0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-0.6.1.tgz#53049143f40c6eb918b23671d1fe3219f3a1b362" @@ -12605,17 +11865,6 @@ fast-equals@^5.0.1: resolved "https://registry.yarnpkg.com/fast-equals/-/fast-equals-5.2.2.tgz#885d7bfb079fac0ce0e8450374bce29e9b742484" integrity sha512-V7/RktU11J3I36Nwq2JnZEM7tNm17eBJz+u25qdxBZeCKiX6BkVSZQjwWIr+IobgnZy+ag73tTZgZi7tr0LrBw== -fast-glob@3.3.1: - version "3.3.1" - resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.1.tgz#784b4e897340f3dbbef17413b3f11acf03c874c4" - integrity sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg== - dependencies: - "@nodelib/fs.stat" "^2.0.2" - "@nodelib/fs.walk" "^1.2.3" - glob-parent "^5.1.2" - merge2 "^1.3.0" - micromatch "^4.0.4" - fast-glob@^2.2.6: version "2.2.7" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-2.2.7.tgz#6953857c3afa475fff92ee6015d52da70a4cd39d" @@ -12628,7 +11877,7 @@ fast-glob@^2.2.6: merge2 "^1.2.3" micromatch "^3.1.10" -fast-glob@^3.2.9, fast-glob@^3.3.2: +fast-glob@^3.2.9: version "3.3.3" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.3.tgz#d06d585ce8dba90a16b0505c543c3ccfb3aeb818" integrity sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg== @@ -12986,7 +12235,7 @@ form-data@^3.0.0: es-set-tostringtag "^2.1.0" mime-types "^2.1.35" -form-data@^4.0.0, form-data@^4.0.1: +form-data@^4.0.0: version "4.0.2" resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.2.tgz#35cabbdd30c3ce73deb2c42d3c8d3ed9ca51794c" integrity sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w== @@ -13692,27 +12941,6 @@ hast-util-raw@6.0.1: xtend "^4.0.0" zwitch "^1.0.0" -hast-util-to-jsx-runtime@^2.0.0: - version "2.3.6" - resolved "https://registry.yarnpkg.com/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.6.tgz#ff31897aae59f62232e21594eac7ef6b63333e98" - integrity sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg== - dependencies: - "@types/estree" "^1.0.0" - "@types/hast" "^3.0.0" - "@types/unist" "^3.0.0" - comma-separated-tokens "^2.0.0" - devlop "^1.0.0" - estree-util-is-identifier-name "^3.0.0" - hast-util-whitespace "^3.0.0" - mdast-util-mdx-expression "^2.0.0" - mdast-util-mdx-jsx "^3.0.0" - mdast-util-mdxjs-esm "^2.0.0" - property-information "^7.0.0" - space-separated-tokens "^2.0.0" - style-to-js "^1.0.0" - unist-util-position "^5.0.0" - vfile-message "^4.0.0" - hast-util-to-parse5@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/hast-util-to-parse5/-/hast-util-to-parse5-6.0.0.tgz#1ec44650b631d72952066cea9b1445df699f8479" @@ -13724,13 +12952,6 @@ hast-util-to-parse5@^6.0.0: xtend "^4.0.0" zwitch "^1.0.0" -hast-util-whitespace@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz#7778ed9d3c92dd9e8c5c8f648a49c21fc51cb621" - integrity sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw== - dependencies: - "@types/hast" "^3.0.0" - hastscript@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/hastscript/-/hastscript-6.0.0.tgz#e8768d7eac56c3fdeac8a92830d58e811e5bf640" @@ -13757,11 +12978,6 @@ highlight-words@1.2.2: resolved "https://registry.yarnpkg.com/highlight-words/-/highlight-words-1.2.2.tgz#9875b75d11814d7356b24f23feeb7d77761fa867" integrity sha512-Mf4xfPXYm8Ay1wTibCrHpNWeR2nUMynMVFkXCi4mbl+TEgmNOe+I4hV7W3OCZcSvzGL6kupaqpfHOemliMTGxQ== -highlight-words@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/highlight-words/-/highlight-words-2.0.0.tgz#06853d68f1f7c8e59d6ef2dd072fe2f64fc93936" - integrity sha512-If5n+IhSBRXTScE7wl16VPmd+44Vy7kof24EdqhjsZsDuHikpv1OCagVcJFpB4fS4UPUniedlWqrjIO8vWOsIQ== - hmac-drbg@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" @@ -13821,13 +13037,6 @@ html-encoding-sniffer@^2.0.1: dependencies: whatwg-encoding "^1.0.5" -html-encoding-sniffer@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz#696df529a7cfd82446369dc5193e590a3735b448" - integrity sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ== - dependencies: - whatwg-encoding "^3.1.1" - html-entities@^2.1.0, html-entities@^2.3.2: version "2.5.2" resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-2.5.2.tgz#201a3cf95d3a15be7099521620d19dfb4f65359f" @@ -13864,23 +13073,11 @@ html-minifier-terser@^6.0.2: relateurl "^0.2.7" terser "^5.10.0" -html-parse-stringify@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz#dfc1017347ce9f77c8141a507f233040c59c55d2" - integrity sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg== - dependencies: - void-elements "3.1.0" - html-tags@^3.1.0: version "3.3.1" resolved "https://registry.yarnpkg.com/html-tags/-/html-tags-3.3.1.tgz#a04026a18c882e4bba8a01a3d39cfe465d40b5ce" integrity sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ== -html-url-attributes@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/html-url-attributes/-/html-url-attributes-3.0.1.tgz#83b052cd5e437071b756cd74ae70f708870c2d87" - integrity sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ== - html-void-elements@^1.0.0: version "1.0.5" resolved "https://registry.yarnpkg.com/html-void-elements/-/html-void-elements-1.0.5.tgz#ce9159494e86d95e45795b166c2021c2cfca4483" @@ -13976,14 +13173,6 @@ http-proxy-agent@^5.0.0: agent-base "6" debug "4" -http-proxy-agent@^7.0.2: - version "7.0.2" - resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz#9a8b1f246866c028509486585f62b8f2c18c270e" - integrity sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig== - dependencies: - agent-base "^7.1.0" - debug "^4.3.4" - http-proxy-middleware@^2.0.3, http-proxy-middleware@^2.0.6: version "2.0.7" resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-2.0.7.tgz#915f236d92ae98ef48278a95dedf17e991936ec6" @@ -14017,14 +13206,6 @@ https-proxy-agent@^5.0.0: agent-base "6" debug "4" -https-proxy-agent@^7.0.6: - version "7.0.6" - resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz#da8dfeac7da130b05c2ba4b59c9b6cd66611a6b9" - integrity sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw== - dependencies: - agent-base "^7.1.2" - debug "4" - human-signals@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" @@ -14049,20 +13230,6 @@ i18n-iso-countries@^6.8.0: dependencies: diacritics "1.3.0" -i18next-resources-to-backend@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/i18next-resources-to-backend/-/i18next-resources-to-backend-1.2.1.tgz#fded121e63e3139ce839c9901b9449dbbea7351d" - integrity sha512-okHbVA+HZ7n1/76MsfhPqDou0fptl2dAlhRDu2ideXloRRduzHsqDOznJBef+R3DFZnbvWoBW+KxJ7fnFjd6Yw== - dependencies: - "@babel/runtime" "^7.23.2" - -i18next@^24.2.2: - version "24.2.3" - resolved "https://registry.yarnpkg.com/i18next/-/i18next-24.2.3.tgz#3a05f72615cbd7c00d7e348667e2aabef1df753b" - integrity sha512-lfbf80OzkocvX7nmZtu7nSTNbrTYR52sLWxPtlXX1zAhVw8WEnFk4puUkCR4B1dNQwbSpEHHHemcZu//7EcB7A== - dependencies: - "@babel/runtime" "^7.26.10" - iconv-lite@0.4.24, iconv-lite@^0.4.24: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" @@ -14070,7 +13237,7 @@ iconv-lite@0.4.24, iconv-lite@^0.4.24: dependencies: safer-buffer ">= 2.1.2 < 3" -iconv-lite@0.6.3, iconv-lite@^0.6.2: +iconv-lite@^0.6.2: version "0.6.3" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== @@ -14128,7 +13295,7 @@ ignore@^4.0.3, ignore@^4.0.6: resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== -ignore@^5.0.4, ignore@^5.2.0, ignore@^5.3.1: +ignore@^5.0.4, ignore@^5.2.0: version "5.3.2" resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5" integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g== @@ -14230,11 +13397,6 @@ inline-style-parser@0.1.1: resolved "https://registry.yarnpkg.com/inline-style-parser/-/inline-style-parser-0.1.1.tgz#ec8a3b429274e9c0a1f1c4ffa9453a7fef72cea1" integrity sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q== -inline-style-parser@0.2.4: - version "0.2.4" - resolved "https://registry.yarnpkg.com/inline-style-parser/-/inline-style-parser-0.2.4.tgz#f4af5fe72e612839fcd453d989a586566d695f22" - integrity sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q== - inquirer@^8.2.4: version "8.2.6" resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-8.2.6.tgz#733b74888195d8d400a67ac332011b5fae5ea562" @@ -14335,11 +13497,6 @@ is-alphabetical@1.0.4, is-alphabetical@^1.0.0: resolved "https://registry.yarnpkg.com/is-alphabetical/-/is-alphabetical-1.0.4.tgz#9e7d6b94916be22153745d184c298cbf986a686d" integrity sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg== -is-alphabetical@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/is-alphabetical/-/is-alphabetical-2.0.1.tgz#01072053ea7c1036df3c7d19a6daaec7f19e789b" - integrity sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ== - is-alphanumerical@^1.0.0: version "1.0.4" resolved "https://registry.yarnpkg.com/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz#7eb9a2431f855f6b1ef1a78e326df515696c4dbf" @@ -14348,14 +13505,6 @@ is-alphanumerical@^1.0.0: is-alphabetical "^1.0.0" is-decimal "^1.0.0" -is-alphanumerical@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz#7c03fbe96e3e931113e57f964b0a368cc2dfd875" - integrity sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw== - dependencies: - is-alphabetical "^2.0.0" - is-decimal "^2.0.0" - is-arguments@^1.1.1: version "1.2.0" resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.2.0.tgz#ad58c6aecf563b78ef2bf04df540da8f5d7d8e1b" @@ -14495,11 +13644,6 @@ is-decimal@^1.0.0: resolved "https://registry.yarnpkg.com/is-decimal/-/is-decimal-1.0.4.tgz#65a3a5958a1c5b63a706e1b333d7cd9f630d3fa5" integrity sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw== -is-decimal@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/is-decimal/-/is-decimal-2.0.1.tgz#9469d2dc190d0214fd87d78b78caecc0cc14eef7" - integrity sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A== - is-descriptor@^0.1.0: version "0.1.7" resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.7.tgz#2727eb61fd789dcd5bdf0ed4569f551d2fe3be33" @@ -14602,11 +13746,6 @@ is-hexadecimal@^1.0.0: resolved "https://registry.yarnpkg.com/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz#cc35c97588da4bd49a8eedd6bc4082d44dcb23a7" integrity sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw== -is-hexadecimal@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz#86b5bf668fca307498d319dfc03289d781a90027" - integrity sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg== - is-in-browser@^1.0.2, is-in-browser@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/is-in-browser/-/is-in-browser-1.1.3.tgz#56ff4db683a078c6082eb95dad7dc62e1d04f835" @@ -14701,11 +13840,6 @@ is-plain-obj@^3.0.0: resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-3.0.0.tgz#af6f2ea14ac5a646183a5bbdb5baabbc156ad9d7" integrity sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA== -is-plain-obj@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-4.1.0.tgz#d65025edec3657ce032fd7db63c97883eaed71f0" - integrity sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg== - is-plain-object@5.0.0, is-plain-object@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344" @@ -14904,14 +14038,6 @@ isobject@^4.0.0: resolved "https://registry.yarnpkg.com/isobject/-/isobject-4.0.0.tgz#3f1c9155e73b192022a80819bacd0343711697b0" integrity sha512-S/2fF5wH8SJA/kmwr6HYhK/RI/OkhD84k8ntalo0iJjZikgq1XFvR5M8NPT1x5F7fBwCG3qHfnzeP/Vh/ZxCUA== -isomorphic-dompurify@^2.21.0: - version "2.22.0" - resolved "https://registry.yarnpkg.com/isomorphic-dompurify/-/isomorphic-dompurify-2.22.0.tgz#675b751bb80dc1e96a5c6760bbc6396b03f4786d" - integrity sha512-A2xsDNST1yB94rErEnwqlzSvGllCJ4e8lDMe1OWBH2hvpfc/2qzgMEiDshTO1HwO+PIDTiYeOc7ZDB7Ds49BOg== - dependencies: - dompurify "^3.2.4" - jsdom "^26.0.0" - isomorphic-unfetch@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/isomorphic-unfetch/-/isomorphic-unfetch-3.1.0.tgz#87341d5f4f7b63843d468438128cb087b7c3e98f" @@ -15663,33 +14789,6 @@ jsdom@^16.6.0: ws "^7.4.6" xml-name-validator "^3.0.0" -jsdom@^26.0.0: - version "26.0.0" - resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-26.0.0.tgz#446dd1ad8cfc50df7e714e58f1f972c1763b354c" - integrity sha512-BZYDGVAIriBWTpIxYzrXjv3E/4u8+/pSG5bQdIYCbNCGOvsPkDQfTVLAIXAf9ETdCpduCVTkDe2NNZ8NIwUVzw== - dependencies: - cssstyle "^4.2.1" - data-urls "^5.0.0" - decimal.js "^10.4.3" - form-data "^4.0.1" - html-encoding-sniffer "^4.0.0" - http-proxy-agent "^7.0.2" - https-proxy-agent "^7.0.6" - is-potential-custom-element-name "^1.0.1" - nwsapi "^2.2.16" - parse5 "^7.2.1" - rrweb-cssom "^0.8.0" - saxes "^6.0.0" - symbol-tree "^3.2.4" - tough-cookie "^5.0.0" - w3c-xmlserializer "^5.0.0" - webidl-conversions "^7.0.0" - whatwg-encoding "^3.1.1" - whatwg-mimetype "^4.0.0" - whatwg-url "^14.1.0" - ws "^8.18.0" - xml-name-validator "^5.0.0" - jsesc@^3.0.2: version "3.1.0" resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-3.1.0.tgz#74d335a234f67ed19907fdadfac7ccf9d409825d" @@ -15933,72 +15032,6 @@ lazy-universal-dotenv@^3.0.1: dotenv "^8.0.0" dotenv-expand "^5.1.0" -lefthook-darwin-arm64@1.11.3: - version "1.11.3" - resolved "https://registry.yarnpkg.com/lefthook-darwin-arm64/-/lefthook-darwin-arm64-1.11.3.tgz#adb4c44d4f82a07f97e09deca36fb706d51514d5" - integrity sha512-IYzAOf8Qwqk7q+LoRyy7kSk9vzpUZ5wb/vLzEAH/F86Vay9AUaWe1f2pzeLwFg18qEc1QNklT69h9p/uLQMojA== - -lefthook-darwin-x64@1.11.3: - version "1.11.3" - resolved "https://registry.yarnpkg.com/lefthook-darwin-x64/-/lefthook-darwin-x64-1.11.3.tgz#b77c231371f6f60ddaf097d500a4aae5c374eb80" - integrity sha512-z/Wp7UMjE1Vyl+x9sjN3NvN6qKdwgHl+cDf98MKKDg/WyPE5XnzqLm9rLLJgImjyClfH7ptTfZxEyhTG3M3XvQ== - -lefthook-freebsd-arm64@1.11.3: - version "1.11.3" - resolved "https://registry.yarnpkg.com/lefthook-freebsd-arm64/-/lefthook-freebsd-arm64-1.11.3.tgz#27665ae8ab91c40a137d2f5a68ba84594988c120" - integrity sha512-QevwQ7lrv5wBCkk7LLTzT5KR3Bk/5nttSxT1UH2o0EsgirS/c2K5xSgQmV6m3CiZYuCe2Pja4BSIwN3zt17SMw== - -lefthook-freebsd-x64@1.11.3: - version "1.11.3" - resolved "https://registry.yarnpkg.com/lefthook-freebsd-x64/-/lefthook-freebsd-x64-1.11.3.tgz#30f5f398af759212dd68b8b5307229d04ce5c26c" - integrity sha512-PYbcyNgdJJ4J2pEO9Ss4oYo5yq4vmQGTKm3RTYbRx4viSWR65hvKCP0C4LnIqspMvmR05SJi2bqe7UBP2t60EA== - -lefthook-linux-arm64@1.11.3: - version "1.11.3" - resolved "https://registry.yarnpkg.com/lefthook-linux-arm64/-/lefthook-linux-arm64-1.11.3.tgz#ac338808e1c560935704473707e7540171170618" - integrity sha512-0pBMBAoafOAEg345eOPozsmRjWR0zCr6k+m5ZxwRBZbZx1bQFDqBakQ3TpFCphhcykmgFyaa1KeZJZUOrEsezA== - -lefthook-linux-x64@1.11.3: - version "1.11.3" - resolved "https://registry.yarnpkg.com/lefthook-linux-x64/-/lefthook-linux-x64-1.11.3.tgz#eea29dc603f34822c57f47bd9777bfa0e1ffa223" - integrity sha512-eiezheZ/bisBCMB2Ur0mctug/RDFyu39B5wzoE8y4z0W1yw6jHGrWMJ4Y8+5qKZ7fmdZg+7YPuMHZ2eFxOnhQA== - -lefthook-openbsd-arm64@1.11.3: - version "1.11.3" - resolved "https://registry.yarnpkg.com/lefthook-openbsd-arm64/-/lefthook-openbsd-arm64-1.11.3.tgz#b6eae9109a1a7520392317d635810e43cabe1b89" - integrity sha512-DRLTzXdtCj/TizpLcGSqXcnrqvgxeXgn/6nqzclIGqNdKCsNXDzpI0D3sP13Vwwmyoqv2etoTak2IHqZiXZDqg== - -lefthook-openbsd-x64@1.11.3: - version "1.11.3" - resolved "https://registry.yarnpkg.com/lefthook-openbsd-x64/-/lefthook-openbsd-x64-1.11.3.tgz#4b681975fe17ad9615b92310ca094c4d292aa5ec" - integrity sha512-l7om+ZjWpYrVZyDuElwnucZhEqa7YfwlRaKBenkBxEh2zMje8O6Zodeuma1KmyDbSFvnvEjARo/Ejiot4gLXEw== - -lefthook-windows-arm64@1.11.3: - version "1.11.3" - resolved "https://registry.yarnpkg.com/lefthook-windows-arm64/-/lefthook-windows-arm64-1.11.3.tgz#0cf029605d0c2983bf287269b8347aed460561c5" - integrity sha512-X0iTrql2gfPAkU2dzRwuHWgW5RcqCPbzJtKQ41X6Y/F7iQacRknmuYUGyC81funSvzGAsvlusMVLUvaFjIKnbA== - -lefthook-windows-x64@1.11.3: - version "1.11.3" - resolved "https://registry.yarnpkg.com/lefthook-windows-x64/-/lefthook-windows-x64-1.11.3.tgz#6baac87f6fb36885dea0c360452049dd8d6dad44" - integrity sha512-F+ORMn6YJXoS0EXU5LtN1FgV4QX9rC9LucZEkRmK6sKmS7hcb9IHpyb7siRGytArYzJvXVjPbxPBNSBdN4egZQ== - -lefthook@^1.8.5: - version "1.11.3" - resolved "https://registry.yarnpkg.com/lefthook/-/lefthook-1.11.3.tgz#d4c23eade3f9d6c32900c356555ca6380c601513" - integrity sha512-HJp37y62j3j8qzAOODWuUJl4ysLwsDvCTBV6odr3jIRHR/a5e+tI14VQGIBcpK9ysqC3pGWyW5Rp9Jv1YDubyw== - optionalDependencies: - lefthook-darwin-arm64 "1.11.3" - lefthook-darwin-x64 "1.11.3" - lefthook-freebsd-arm64 "1.11.3" - lefthook-freebsd-x64 "1.11.3" - lefthook-linux-arm64 "1.11.3" - lefthook-linux-x64 "1.11.3" - lefthook-openbsd-arm64 "1.11.3" - lefthook-openbsd-x64 "1.11.3" - lefthook-windows-arm64 "1.11.3" - lefthook-windows-x64 "1.11.3" - lerna@^7.3.0: version "7.4.2" resolved "https://registry.yarnpkg.com/lerna/-/lerna-7.4.2.tgz#03497125d7b7c8d463eebfe17a701b16bde2ad09" @@ -16325,11 +15358,6 @@ long@^4.0.0: resolved "https://registry.yarnpkg.com/long/-/long-4.0.0.tgz#9a7b71cfb7d361a194ea555241c92f7468d5bf28" integrity sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA== -longest-streak@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/longest-streak/-/longest-streak-3.1.0.tgz#62fa67cd958742a1574af9f39866364102d90cd4" - integrity sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g== - loose-envify@^1.1.0, loose-envify@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" @@ -16548,16 +15576,6 @@ material-react-table@^2.12.1: "@tanstack/react-virtual" "3.10.6" highlight-words "1.2.2" -material-react-table@^3.0.3: - version "3.2.1" - resolved "https://registry.yarnpkg.com/material-react-table/-/material-react-table-3.2.1.tgz#56f595755cab3b669b399999fed9eb305fbb6dd7" - integrity sha512-sQtTf7bETpkPN2Hm5BVtz89wrfXCVQguz6XlwMChSnfKFO5QCKAJJC5aSIKnUc3S0AvTz/k/ILi00FnnY1Gixw== - dependencies: - "@tanstack/match-sorter-utils" "8.19.4" - "@tanstack/react-table" "8.20.6" - "@tanstack/react-virtual" "3.11.2" - highlight-words "2.0.0" - math-intrinsics@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz#a0dd74be81e2aa5c2f27e65ce283605ee4e2b7f9" @@ -16586,74 +15604,6 @@ mdast-util-definitions@^4.0.0: dependencies: unist-util-visit "^2.0.0" -mdast-util-from-markdown@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.2.tgz#4850390ca7cf17413a9b9a0fbefcd1bc0eb4160a" - integrity sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA== - dependencies: - "@types/mdast" "^4.0.0" - "@types/unist" "^3.0.0" - decode-named-character-reference "^1.0.0" - devlop "^1.0.0" - mdast-util-to-string "^4.0.0" - micromark "^4.0.0" - micromark-util-decode-numeric-character-reference "^2.0.0" - micromark-util-decode-string "^2.0.0" - micromark-util-normalize-identifier "^2.0.0" - micromark-util-symbol "^2.0.0" - micromark-util-types "^2.0.0" - unist-util-stringify-position "^4.0.0" - -mdast-util-mdx-expression@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.1.tgz#43f0abac9adc756e2086f63822a38c8d3c3a5096" - integrity sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ== - dependencies: - "@types/estree-jsx" "^1.0.0" - "@types/hast" "^3.0.0" - "@types/mdast" "^4.0.0" - devlop "^1.0.0" - mdast-util-from-markdown "^2.0.0" - mdast-util-to-markdown "^2.0.0" - -mdast-util-mdx-jsx@^3.0.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.2.0.tgz#fd04c67a2a7499efb905a8a5c578dddc9fdada0d" - integrity sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q== - dependencies: - "@types/estree-jsx" "^1.0.0" - "@types/hast" "^3.0.0" - "@types/mdast" "^4.0.0" - "@types/unist" "^3.0.0" - ccount "^2.0.0" - devlop "^1.1.0" - mdast-util-from-markdown "^2.0.0" - mdast-util-to-markdown "^2.0.0" - parse-entities "^4.0.0" - stringify-entities "^4.0.0" - unist-util-stringify-position "^4.0.0" - vfile-message "^4.0.0" - -mdast-util-mdxjs-esm@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-2.0.1.tgz#019cfbe757ad62dd557db35a695e7314bcc9fa97" - integrity sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg== - dependencies: - "@types/estree-jsx" "^1.0.0" - "@types/hast" "^3.0.0" - "@types/mdast" "^4.0.0" - devlop "^1.0.0" - mdast-util-from-markdown "^2.0.0" - mdast-util-to-markdown "^2.0.0" - -mdast-util-phrasing@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz#7cc0a8dec30eaf04b7b1a9661a92adb3382aa6e3" - integrity sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w== - dependencies: - "@types/mdast" "^4.0.0" - unist-util-is "^6.0.0" - mdast-util-to-hast@10.0.1: version "10.0.1" resolved "https://registry.yarnpkg.com/mdast-util-to-hast/-/mdast-util-to-hast-10.0.1.tgz#0cfc82089494c52d46eb0e3edb7a4eb2aea021eb" @@ -16668,63 +15618,16 @@ mdast-util-to-hast@10.0.1: unist-util-position "^3.0.0" unist-util-visit "^2.0.0" -mdast-util-to-hast@^13.0.0: - version "13.2.0" - resolved "https://registry.yarnpkg.com/mdast-util-to-hast/-/mdast-util-to-hast-13.2.0.tgz#5ca58e5b921cc0a3ded1bc02eed79a4fe4fe41f4" - integrity sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA== - dependencies: - "@types/hast" "^3.0.0" - "@types/mdast" "^4.0.0" - "@ungap/structured-clone" "^1.0.0" - devlop "^1.0.0" - micromark-util-sanitize-uri "^2.0.0" - trim-lines "^3.0.0" - unist-util-position "^5.0.0" - unist-util-visit "^5.0.0" - vfile "^6.0.0" - -mdast-util-to-markdown@^2.0.0: - version "2.1.2" - resolved "https://registry.yarnpkg.com/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.2.tgz#f910ffe60897f04bb4b7e7ee434486f76288361b" - integrity sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA== - dependencies: - "@types/mdast" "^4.0.0" - "@types/unist" "^3.0.0" - longest-streak "^3.0.0" - mdast-util-phrasing "^4.0.0" - mdast-util-to-string "^4.0.0" - micromark-util-classify-character "^2.0.0" - micromark-util-decode-string "^2.0.0" - unist-util-visit "^5.0.0" - zwitch "^2.0.0" - mdast-util-to-string@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/mdast-util-to-string/-/mdast-util-to-string-1.1.0.tgz#27055500103f51637bd07d01da01eb1967a43527" integrity sha512-jVU0Nr2B9X3MU4tSK7JP1CMkSvOj7X5l/GboG1tKRw52lLF1x2Ju92Ms9tNetCcbfX3hzlM73zYo2NKkWSfF/A== -mdast-util-to-string@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz#7a5121475556a04e7eddeb67b264aae79d312814" - integrity sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg== - dependencies: - "@types/mdast" "^4.0.0" - mdn-data@2.0.14: version "2.0.14" resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.14.tgz#7113fc4281917d63ce29b43446f701e68c25ba50" integrity sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow== -mdn-data@2.0.28: - version "2.0.28" - resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.28.tgz#5ec48e7bef120654539069e1ae4ddc81ca490eba" - integrity sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g== - -mdn-data@2.0.30: - version "2.0.30" - resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.30.tgz#ce4df6f80af6cfbe218ecd5c552ba13c4dfa08cc" - integrity sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA== - mdurl@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e" @@ -16843,200 +15746,6 @@ microevent.ts@~0.1.1: resolved "https://registry.yarnpkg.com/microevent.ts/-/microevent.ts-0.1.1.tgz#70b09b83f43df5172d0205a63025bce0f7357fa0" integrity sha512-jo1OfR4TaEwd5HOrt5+tAZ9mqT4jmpNAusXtyfNzqVm9uiSYFZlKM1wYL4oU7azZW/PxQW53wM0S6OR1JHNa2g== -micromark-core-commonmark@^2.0.0: - version "2.0.3" - resolved "https://registry.yarnpkg.com/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz#c691630e485021a68cf28dbc2b2ca27ebf678cd4" - integrity sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg== - dependencies: - decode-named-character-reference "^1.0.0" - devlop "^1.0.0" - micromark-factory-destination "^2.0.0" - micromark-factory-label "^2.0.0" - micromark-factory-space "^2.0.0" - micromark-factory-title "^2.0.0" - micromark-factory-whitespace "^2.0.0" - micromark-util-character "^2.0.0" - micromark-util-chunked "^2.0.0" - micromark-util-classify-character "^2.0.0" - micromark-util-html-tag-name "^2.0.0" - micromark-util-normalize-identifier "^2.0.0" - micromark-util-resolve-all "^2.0.0" - micromark-util-subtokenize "^2.0.0" - micromark-util-symbol "^2.0.0" - micromark-util-types "^2.0.0" - -micromark-factory-destination@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz#8fef8e0f7081f0474fbdd92deb50c990a0264639" - integrity sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA== - dependencies: - micromark-util-character "^2.0.0" - micromark-util-symbol "^2.0.0" - micromark-util-types "^2.0.0" - -micromark-factory-label@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz#5267efa97f1e5254efc7f20b459a38cb21058ba1" - integrity sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg== - dependencies: - devlop "^1.0.0" - micromark-util-character "^2.0.0" - micromark-util-symbol "^2.0.0" - micromark-util-types "^2.0.0" - -micromark-factory-space@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz#36d0212e962b2b3121f8525fc7a3c7c029f334fc" - integrity sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg== - dependencies: - micromark-util-character "^2.0.0" - micromark-util-types "^2.0.0" - -micromark-factory-title@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz#237e4aa5d58a95863f01032d9ee9b090f1de6e94" - integrity sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw== - dependencies: - micromark-factory-space "^2.0.0" - micromark-util-character "^2.0.0" - micromark-util-symbol "^2.0.0" - micromark-util-types "^2.0.0" - -micromark-factory-whitespace@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz#06b26b2983c4d27bfcc657b33e25134d4868b0b1" - integrity sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ== - dependencies: - micromark-factory-space "^2.0.0" - micromark-util-character "^2.0.0" - micromark-util-symbol "^2.0.0" - micromark-util-types "^2.0.0" - -micromark-util-character@^2.0.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/micromark-util-character/-/micromark-util-character-2.1.1.tgz#2f987831a40d4c510ac261e89852c4e9703ccda6" - integrity sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q== - dependencies: - micromark-util-symbol "^2.0.0" - micromark-util-types "^2.0.0" - -micromark-util-chunked@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz#47fbcd93471a3fccab86cff03847fc3552db1051" - integrity sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA== - dependencies: - micromark-util-symbol "^2.0.0" - -micromark-util-classify-character@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz#d399faf9c45ca14c8b4be98b1ea481bced87b629" - integrity sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q== - dependencies: - micromark-util-character "^2.0.0" - micromark-util-symbol "^2.0.0" - micromark-util-types "^2.0.0" - -micromark-util-combine-extensions@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz#2a0f490ab08bff5cc2fd5eec6dd0ca04f89b30a9" - integrity sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg== - dependencies: - micromark-util-chunked "^2.0.0" - micromark-util-types "^2.0.0" - -micromark-util-decode-numeric-character-reference@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz#fcf15b660979388e6f118cdb6bf7d79d73d26fe5" - integrity sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw== - dependencies: - micromark-util-symbol "^2.0.0" - -micromark-util-decode-string@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz#6cb99582e5d271e84efca8e61a807994d7161eb2" - integrity sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ== - dependencies: - decode-named-character-reference "^1.0.0" - micromark-util-character "^2.0.0" - micromark-util-decode-numeric-character-reference "^2.0.0" - micromark-util-symbol "^2.0.0" - -micromark-util-encode@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz#0d51d1c095551cfaac368326963cf55f15f540b8" - integrity sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw== - -micromark-util-html-tag-name@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz#e40403096481986b41c106627f98f72d4d10b825" - integrity sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA== - -micromark-util-normalize-identifier@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz#c30d77b2e832acf6526f8bf1aa47bc9c9438c16d" - integrity sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q== - dependencies: - micromark-util-symbol "^2.0.0" - -micromark-util-resolve-all@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz#e1a2d62cdd237230a2ae11839027b19381e31e8b" - integrity sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg== - dependencies: - micromark-util-types "^2.0.0" - -micromark-util-sanitize-uri@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz#ab89789b818a58752b73d6b55238621b7faa8fd7" - integrity sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ== - dependencies: - micromark-util-character "^2.0.0" - micromark-util-encode "^2.0.0" - micromark-util-symbol "^2.0.0" - -micromark-util-subtokenize@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz#d8ade5ba0f3197a1cf6a2999fbbfe6357a1a19ee" - integrity sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA== - dependencies: - devlop "^1.0.0" - micromark-util-chunked "^2.0.0" - micromark-util-symbol "^2.0.0" - micromark-util-types "^2.0.0" - -micromark-util-symbol@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz#e5da494e8eb2b071a0d08fb34f6cefec6c0a19b8" - integrity sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q== - -micromark-util-types@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/micromark-util-types/-/micromark-util-types-2.0.2.tgz#f00225f5f5a0ebc3254f96c36b6605c4b393908e" - integrity sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA== - -micromark@^4.0.0: - version "4.0.2" - resolved "https://registry.yarnpkg.com/micromark/-/micromark-4.0.2.tgz#91395a3e1884a198e62116e33c9c568e39936fdb" - integrity sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA== - dependencies: - "@types/debug" "^4.0.0" - debug "^4.0.0" - decode-named-character-reference "^1.0.0" - devlop "^1.0.0" - micromark-core-commonmark "^2.0.0" - micromark-factory-space "^2.0.0" - micromark-util-character "^2.0.0" - micromark-util-chunked "^2.0.0" - micromark-util-combine-extensions "^2.0.0" - micromark-util-decode-numeric-character-reference "^2.0.0" - micromark-util-encode "^2.0.0" - micromark-util-normalize-identifier "^2.0.0" - micromark-util-resolve-all "^2.0.0" - micromark-util-sanitize-uri "^2.0.0" - micromark-util-subtokenize "^2.0.0" - micromark-util-symbol "^2.0.0" - micromark-util-types "^2.0.0" - micromatch@^3.1.10, micromatch@^3.1.4: version "3.1.10" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" @@ -17485,51 +16194,28 @@ nested-error-stacks@^2.0.0, nested-error-stacks@^2.1.0: resolved "https://registry.yarnpkg.com/nested-error-stacks/-/nested-error-stacks-2.1.1.tgz#26c8a3cee6cc05fbcf1e333cd2fc3e003326c0b5" integrity sha512-9iN1ka/9zmX1ZvLV9ewJYEk9h7RyRRtqdK0woXcqohu8EWIerfPUjYJPg0ULy0UqP7cslmdGc8xKDJcojlKiaw== -next@14.1.4: - version "14.1.4" - resolved "https://registry.yarnpkg.com/next/-/next-14.1.4.tgz#203310f7310578563fd5c961f0db4729ce7a502d" - integrity sha512-1WTaXeSrUwlz/XcnhGTY7+8eiaFvdet5z9u3V2jb+Ek1vFo0VhHKSAIJvDWfQpttWjnyw14kBeq28TPq7bTeEQ== +next@14.2.21: + version "14.2.21" + resolved "https://registry.yarnpkg.com/next/-/next-14.2.21.tgz#f6da9e2abba1a0e4ca7a5273825daf06632554ba" + integrity sha512-rZmLwucLHr3/zfDMYbJXbw0ZeoBpirxkXuvsJbk7UPorvPYZhP7vq7aHbKnU7dQNCYIimRrbB2pp3xmf+wsYUg== dependencies: - "@next/env" "14.1.4" - "@swc/helpers" "0.5.2" + "@next/env" "14.2.21" + "@swc/helpers" "0.5.5" busboy "1.6.0" caniuse-lite "^1.0.30001579" graceful-fs "^4.2.11" postcss "8.4.31" styled-jsx "5.1.1" optionalDependencies: - "@next/swc-darwin-arm64" "14.1.4" - "@next/swc-darwin-x64" "14.1.4" - "@next/swc-linux-arm64-gnu" "14.1.4" - "@next/swc-linux-arm64-musl" "14.1.4" - "@next/swc-linux-x64-gnu" "14.1.4" - "@next/swc-linux-x64-musl" "14.1.4" - "@next/swc-win32-arm64-msvc" "14.1.4" - "@next/swc-win32-ia32-msvc" "14.1.4" - "@next/swc-win32-x64-msvc" "14.1.4" - -next@^15.2.0: - version "15.2.2" - resolved "https://registry.yarnpkg.com/next/-/next-15.2.2.tgz#e3941a0e0e76cfe1880b57452807489e0546e3a2" - integrity sha512-dgp8Kcx5XZRjMw2KNwBtUzhngRaURPioxoNIVl5BOyJbhi9CUgEtKDO7fx5wh8Z8vOVX1nYZ9meawJoRrlASYA== - dependencies: - "@next/env" "15.2.2" - "@swc/counter" "0.1.3" - "@swc/helpers" "0.5.15" - busboy "1.6.0" - caniuse-lite "^1.0.30001579" - postcss "8.4.31" - styled-jsx "5.1.6" - optionalDependencies: - "@next/swc-darwin-arm64" "15.2.2" - "@next/swc-darwin-x64" "15.2.2" - "@next/swc-linux-arm64-gnu" "15.2.2" - "@next/swc-linux-arm64-musl" "15.2.2" - "@next/swc-linux-x64-gnu" "15.2.2" - "@next/swc-linux-x64-musl" "15.2.2" - "@next/swc-win32-arm64-msvc" "15.2.2" - "@next/swc-win32-x64-msvc" "15.2.2" - sharp "^0.33.5" + "@next/swc-darwin-arm64" "14.2.21" + "@next/swc-darwin-x64" "14.2.21" + "@next/swc-linux-arm64-gnu" "14.2.21" + "@next/swc-linux-arm64-musl" "14.2.21" + "@next/swc-linux-x64-gnu" "14.2.21" + "@next/swc-linux-x64-musl" "14.2.21" + "@next/swc-win32-arm64-msvc" "14.2.21" + "@next/swc-win32-ia32-msvc" "14.2.21" + "@next/swc-win32-x64-msvc" "14.2.21" nice-try@^1.0.4: version "1.0.5" @@ -17899,7 +16585,7 @@ num2fraction@^1.2.2: resolved "https://registry.yarnpkg.com/num2fraction/-/num2fraction-1.2.2.tgz#6f682b6a027a4e9ddfa4564cd2589d1d4e669ede" integrity sha512-Y1wZESM7VUThYY+4W+X4ySH2maqcA+p7UR+w8VWNWVAd6lwuXXWz/w/Cz43J/dI2I+PS6wD5N+bJUF+gjWvIqg== -nwsapi@^2.2.0, nwsapi@^2.2.16: +nwsapi@^2.2.0: version "2.2.18" resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.18.tgz#3c4d7927e1ef4d042d319438ecfda6cd81b7ee41" integrity sha512-p1TRH/edngVEHVbwqWnxUViEmq5znDvyB+Sik5cmuLpGOIfDf/39zLiq3swPF8Vakqn+gvNiOQAZu8djYlQILA== @@ -18134,18 +16820,6 @@ open@^8.0.9, open@^8.4.0: is-docker "^2.1.1" is-wsl "^2.2.0" -openapi-fetch@^0.13.4: - version "0.13.5" - resolved "https://registry.yarnpkg.com/openapi-fetch/-/openapi-fetch-0.13.5.tgz#805606860d85b8ba8c2e7cb36ea30b473d8065d9" - integrity sha512-AQK8T9GSKFREFlN1DBXTYsLjs7YV2tZcJ7zUWxbjMoQmj8dDSFRrzhLCbHPZWA1TMV3vACqfCxLEZcwf2wxV6Q== - dependencies: - openapi-typescript-helpers "^0.0.15" - -openapi-typescript-helpers@^0.0.15: - version "0.0.15" - resolved "https://registry.yarnpkg.com/openapi-typescript-helpers/-/openapi-typescript-helpers-0.0.15.tgz#96ffa762a5e01ef66a661b163d5f1109ed1967ed" - integrity sha512-opyTPaunsklCBpTK8JGef6mfPhLSnyy5a0IN9vKtx3+4aExf+KxEqYwIy3hqkedXIB97u357uLMJsOnm3GVjsw== - optionator@^0.9.1, optionator@^0.9.3: version "0.9.4" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.4.tgz#7ea1c1a5d91d764fb282139c88fe11e182a3a734" @@ -18445,19 +17119,6 @@ parse-entities@^2.0.0: is-decimal "^1.0.0" is-hexadecimal "^1.0.0" -parse-entities@^4.0.0: - version "4.0.2" - resolved "https://registry.yarnpkg.com/parse-entities/-/parse-entities-4.0.2.tgz#61d46f5ed28e4ee62e9ddc43d6b010188443f159" - integrity sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw== - dependencies: - "@types/unist" "^2.0.0" - character-entities-legacy "^3.0.0" - character-reference-invalid "^2.0.0" - decode-named-character-reference "^1.0.0" - is-alphanumerical "^2.0.0" - is-decimal "^2.0.0" - is-hexadecimal "^2.0.0" - parse-json@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9" @@ -18502,13 +17163,6 @@ parse5@6.0.1, parse5@^6.0.0, parse5@^6.0.1: resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw== -parse5@^7.2.1: - version "7.2.1" - resolved "https://registry.yarnpkg.com/parse5/-/parse5-7.2.1.tgz#8928f55915e6125f430cc44309765bf17556a33a" - integrity sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ== - dependencies: - entities "^4.5.0" - parseurl@~1.3.2, parseurl@~1.3.3: version "1.3.3" resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" @@ -19262,11 +17916,6 @@ property-information@^5.0.0, property-information@^5.3.0: dependencies: xtend "^4.0.0" -property-information@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/property-information/-/property-information-7.0.0.tgz#3508a6d6b0b8eb3ca6eb2c6623b164d2ed2ab112" - integrity sha512-7D/qOz/+Y4X/rzSB6jKxKUsQnphO046ei8qxG59mtM3RG3DHgTK81HrxrmoDVINJb8NKT5ZsRbwHvQ6B68Iyhg== - protobufjs@^6.11.2, protobufjs@^6.8.8, protobufjs@~6.11.2, protobufjs@~6.11.3: version "6.11.4" resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-6.11.4.tgz#29a412c38bf70d89e537b6d02d904a6f448173aa" @@ -19401,11 +18050,6 @@ qrcode.react@^1.0.1: prop-types "^15.6.0" qr.js "0.0.0" -qrcode.react@^4.1.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/qrcode.react/-/qrcode.react-4.2.0.tgz#1bce8363f348197d145c0da640929a24c83cbca3" - integrity sha512-QpgqWi8rD9DsS9EP3z7BT+5lY5SFhsqGjpgW5DY/i3mK4M9DTBNz3ErMi8BWYEfI3L0d8GIbGmcdFAS1uIRGjA== - qs@6.13.0: version "6.13.0" resolved "https://registry.yarnpkg.com/qs/-/qs-6.13.0.tgz#6ca3bd58439f7e245655798997787b0d88a51906" @@ -19413,7 +18057,7 @@ qs@6.13.0: dependencies: side-channel "^1.0.6" -qs@^6.10.0, qs@^6.12.3, qs@^6.14.0: +qs@^6.10.0, qs@^6.12.3: version "6.14.0" resolved "https://registry.yarnpkg.com/qs/-/qs-6.14.0.tgz#c63fa40680d2c5c941412a0e899c89af60c0a930" integrity sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w== @@ -19577,7 +18221,7 @@ react-docgen@^5.0.0: node-dir "^0.1.10" strip-indent "^3.0.0" -react-dom@^18, react-dom@^18.0.0, react-dom@^18.2.0: +react-dom@^18, react-dom@^18.2.0: version "18.3.1" resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.3.1.tgz#c2265d79511b57d479b3dd3fdfa51536494c5cb4" integrity sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw== @@ -19620,14 +18264,6 @@ react-hook-form@^7.14.2: resolved "https://registry.yarnpkg.com/react-hook-form/-/react-hook-form-7.54.2.tgz#8c26ed54c71628dff57ccd3c074b1dd377cfb211" integrity sha512-eHpAUgUjWbZocoQYUHposymRb4ZP6d0uwUnooL2uOybA9/3tPUvoAKqEWK1WaSiTxxOfTpffNZP7QwlnM3/gEg== -react-i18next@^15.4.0: - version "15.4.1" - resolved "https://registry.yarnpkg.com/react-i18next/-/react-i18next-15.4.1.tgz#33f3e89c2f6c68e2bfcbf9aa59986ad42fe78758" - integrity sha512-ahGab+IaSgZmNPYXdV1n+OYky95TGpFwnKRflX/16dY04DsYYKHtVLjeny7sBSCREEcoMbAgSkFiGLF5g5Oofw== - dependencies: - "@babel/runtime" "^7.25.0" - html-parse-stringify "^3.0.1" - react-identicons@^1.2.5: version "1.2.5" resolved "https://registry.yarnpkg.com/react-identicons/-/react-identicons-1.2.5.tgz#3502249e49d88f4e3500092694410a984bb102fa" @@ -19667,33 +18303,11 @@ react-load-script@^0.0.6: resolved "https://registry.yarnpkg.com/react-load-script/-/react-load-script-0.0.6.tgz#db6851236aaa25bb622677a2eb51dad4f8d2c258" integrity sha512-aRGxDGP9VoLxcsaYvKWIW+LRrMOzz2eEcubTS4NvQPPugjk2VvMhow0wWTkSl7RxookomD1MwcP4l5UStg5ShQ== -react-markdown@^9.0.3: - version "9.1.0" - resolved "https://registry.yarnpkg.com/react-markdown/-/react-markdown-9.1.0.tgz#606bd74c6af131ba382a7c1282ff506708ed2e26" - integrity sha512-xaijuJB0kzGiUdG7nc2MOMDUDBWPyGAjZtUrow9XxUeua8IqeP+VlIfAZ3bphpcLTnSZXz6z9jcVC/TCwbfgdw== - dependencies: - "@types/hast" "^3.0.0" - "@types/mdast" "^4.0.0" - devlop "^1.0.0" - hast-util-to-jsx-runtime "^2.0.0" - html-url-attributes "^3.0.0" - mdast-util-to-hast "^13.0.0" - remark-parse "^11.0.0" - remark-rehype "^11.0.0" - unified "^11.0.0" - unist-util-visit "^5.0.0" - vfile "^6.0.0" - react-merge-refs@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/react-merge-refs/-/react-merge-refs-1.1.0.tgz#73d88b892c6c68cbb7a66e0800faa374f4c38b06" integrity sha512-alTKsjEL0dKH/ru1Iyn7vliS2QRcBp9zZPGoWxUOvRGWPUYgjo+V01is7p04It6KhgrzhJGnIj9GgX8W4bZoCQ== -react-random-avatars@^1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/react-random-avatars/-/react-random-avatars-1.3.1.tgz#cfea11dd80262939656e4134438fa76eb2d95371" - integrity sha512-HyMGoWniK2D5CRthoA1he4c6LVFIX/yBjp4qarEXN/DsgJ+1R6M5gJwYhTBoATO+3itrGCAGnKZNqimG1sDjAA== - react-refresh-typescript@^2.0.2: version "2.0.10" resolved "https://registry.yarnpkg.com/react-refresh-typescript/-/react-refresh-typescript-2.0.10.tgz#8dfe40d2a51866926d97fb7150d139e70d649eb2" @@ -19801,16 +18415,7 @@ react-transition-group@^4.4.5: loose-envify "^1.4.0" prop-types "^15.6.2" -react-world-flags@^1.6.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/react-world-flags/-/react-world-flags-1.6.0.tgz#7b075aa9be8a1b3789451a9df2cf491294e6fbae" - integrity sha512-eutSeAy5YKoVh14js/JUCSlA6EBk1n4k+bDaV+NkNB50VhnG+f4QDTpYycnTUTsZ5cqw/saPmk0Z4Fa0VVZ1Iw== - dependencies: - svg-country-flags "^1.2.10" - svgo "^3.0.2" - world-countries "^5.0.0" - -react@^18, react@^18.0.0, react@^18.2.0: +react@^18, react@^18.2.0: version "18.3.1" resolved "https://registry.yarnpkg.com/react/-/react-18.3.1.tgz#49ab892009c53933625bd16b2533fc754cab2891" integrity sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ== @@ -20153,27 +18758,6 @@ remark-parse@8.0.3: vfile-location "^3.0.0" xtend "^4.0.1" -remark-parse@^11.0.0: - version "11.0.0" - resolved "https://registry.yarnpkg.com/remark-parse/-/remark-parse-11.0.0.tgz#aa60743fcb37ebf6b069204eb4da304e40db45a1" - integrity sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA== - dependencies: - "@types/mdast" "^4.0.0" - mdast-util-from-markdown "^2.0.0" - micromark-util-types "^2.0.0" - unified "^11.0.0" - -remark-rehype@^11.0.0: - version "11.1.1" - resolved "https://registry.yarnpkg.com/remark-rehype/-/remark-rehype-11.1.1.tgz#f864dd2947889a11997c0a2667cd6b38f685bca7" - integrity sha512-g/osARvjkBXb6Wo0XvAeXQohVta8i84ACbenPpoSsxTOQH/Ae0/RGP4WZgnMH5pMLpsj4FG7OHmcIcXxpza8eQ== - dependencies: - "@types/hast" "^3.0.0" - "@types/mdast" "^4.0.0" - mdast-util-to-hast "^13.0.0" - unified "^11.0.0" - vfile "^6.0.0" - remark-slug@^6.0.0: version "6.1.0" resolved "https://registry.yarnpkg.com/remark-slug/-/remark-slug-6.1.0.tgz#0503268d5f0c4ecb1f33315c00465ccdd97923ce" @@ -20420,11 +19004,6 @@ rollup@^3.17.2, rollup@^3.2.1: optionalDependencies: fsevents "~2.3.2" -rrweb-cssom@^0.8.0: - version "0.8.0" - resolved "https://registry.yarnpkg.com/rrweb-cssom/-/rrweb-cssom-0.8.0.tgz#3021d1b4352fbf3b614aaeed0bc0d5739abe0bc2" - integrity sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw== - rspack-resolver@^1.1.0: version "1.1.2" resolved "https://registry.yarnpkg.com/rspack-resolver/-/rspack-resolver-1.1.2.tgz#33b91858f7ae962d086ab00cbf66b8a4b8e9b570" @@ -20560,13 +19139,6 @@ saxes@^5.0.1: dependencies: xmlchars "^2.2.0" -saxes@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/saxes/-/saxes-6.0.0.tgz#fe5b4a4768df4f14a201b1ba6a65c1f3d9988cc5" - integrity sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA== - dependencies: - xmlchars "^2.2.0" - scheduler@^0.23.2: version "0.23.2" resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.2.tgz#414ba64a3b282892e944cf2108ecc078d115cdc3" @@ -20645,7 +19217,7 @@ semver@7.5.3: dependencies: lru-cache "^6.0.0" -semver@7.x, semver@>=7.5.2, semver@^7.0.0, semver@^7.1.1, semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8, semver@^7.5.0, semver@^7.5.3, semver@^7.5.4, semver@^7.6.0, semver@^7.6.3: +semver@7.x, semver@^7.0.0, semver@^7.1.1, semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8, semver@^7.5.0, semver@^7.5.3, semver@^7.5.4, semver@^7.6.3: version "7.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.1.tgz#abd5098d82b18c6c81f6074ff2647fd3e7220c9f" integrity sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA== @@ -20810,7 +19382,7 @@ shallow-clone@^3.0.0: dependencies: kind-of "^6.0.2" -sharp@^0.33.1, sharp@^0.33.5: +sharp@^0.33.1: version "0.33.5" resolved "https://registry.yarnpkg.com/sharp/-/sharp-0.33.5.tgz#13e0e4130cc309d6a9497596715240b2ec0c594e" integrity sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw== @@ -21061,7 +19633,7 @@ source-list-map@^2.0.0: resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34" integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw== -source-map-js@^1.0.1, source-map-js@^1.0.2, source-map-js@^1.2.1: +source-map-js@^1.0.2, source-map-js@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46" integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA== @@ -21115,11 +19687,6 @@ space-separated-tokens@^1.0.0: resolved "https://registry.yarnpkg.com/space-separated-tokens/-/space-separated-tokens-1.1.5.tgz#85f32c3d10d9682007e917414ddc5c26d1aa6899" integrity sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA== -space-separated-tokens@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz#1ecd9d2350a3844572c3f4a312bceb018348859f" - integrity sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q== - spdx-correct@^3.0.0: version "3.2.0" resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.2.0.tgz#4f5ab0668f0059e34f9c00dce331784a12de4e9c" @@ -21478,14 +20045,6 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -stringify-entities@^4.0.0: - version "4.0.4" - resolved "https://registry.yarnpkg.com/stringify-entities/-/stringify-entities-4.0.4.tgz#b3b79ef5f277cc4ac73caeb0236c5ba939b3a4f3" - integrity sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg== - dependencies: - character-entities-html4 "^2.0.0" - character-entities-legacy "^3.0.0" - "strip-ansi-cjs@npm:strip-ansi@^6.0.1": version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" @@ -21590,13 +20149,6 @@ style-loader@^3.3.1: resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-3.3.4.tgz#f30f786c36db03a45cbd55b6a70d930c479090e7" integrity sha512-0WqXzrsMTyb8yjZJHDqwmnwRJvhALK9LfRtRc6B4UTWe8AijYLZYZ9thuJTZc2VfQWINADW/j+LiJnfy2RoC1w== -style-to-js@^1.0.0: - version "1.1.16" - resolved "https://registry.yarnpkg.com/style-to-js/-/style-to-js-1.1.16.tgz#e6bd6cd29e250bcf8fa5e6591d07ced7575dbe7a" - integrity sha512-/Q6ld50hKYPH3d/r6nr117TZkHR0w0kGGIVfpG9N6D8NymRPM9RqCUv4pRpJ62E5DqOYx2AFpbZMyCPnjQCnOw== - dependencies: - style-to-object "1.0.8" - style-to-object@0.3.0, style-to-object@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/style-to-object/-/style-to-object-0.3.0.tgz#b1b790d205991cc783801967214979ee19a76e46" @@ -21604,13 +20156,6 @@ style-to-object@0.3.0, style-to-object@^0.3.0: dependencies: inline-style-parser "0.1.1" -style-to-object@1.0.8: - version "1.0.8" - resolved "https://registry.yarnpkg.com/style-to-object/-/style-to-object-1.0.8.tgz#67a29bca47eaa587db18118d68f9d95955e81292" - integrity sha512-xT47I/Eo0rwJmaXC4oilDGDWLohVhR6o/xAQcPQN8q6QBuZVL8qMYL85kLmST5cPjAorwvqIA4qXTRQoYHaL6g== - dependencies: - inline-style-parser "0.2.4" - styled-jsx@5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/styled-jsx/-/styled-jsx-5.1.1.tgz#839a1c3aaacc4e735fed0781b8619ea5d0009d1f" @@ -21618,13 +20163,6 @@ styled-jsx@5.1.1: dependencies: client-only "0.0.1" -styled-jsx@5.1.6: - version "5.1.6" - resolved "https://registry.yarnpkg.com/styled-jsx/-/styled-jsx-5.1.6.tgz#83b90c077e6c6a80f7f5e8781d0f311b2fe41499" - integrity sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA== - dependencies: - client-only "0.0.1" - stylehacks@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/stylehacks/-/stylehacks-5.1.1.tgz#7934a34eb59d7152149fa69d6e9e56f2fc34bcc9" @@ -21672,11 +20210,6 @@ supports-preserve-symlinks-flag@^1.0.0: resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== -svg-country-flags@^1.2.10: - version "1.2.10" - resolved "https://registry.yarnpkg.com/svg-country-flags/-/svg-country-flags-1.2.10.tgz#b1b94e9f08f065d7b3d0ff9ab359898edd4dd3b0" - integrity sha512-xrqwo0TYf/h2cfPvGpjdSuSguUbri4vNNizBnwzoZnX0xGo3O5nGJMlbYEp7NOYcnPGBm6LE2axqDWSB847bLw== - svg-parser@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/svg-parser/-/svg-parser-2.0.4.tgz#fdc2e29e13951736140b76cb122c8ee6630eb6b5" @@ -21695,19 +20228,6 @@ svgo@^2.7.0, svgo@^2.8.0: picocolors "^1.0.0" stable "^0.1.8" -svgo@^3.0.2: - version "3.3.2" - resolved "https://registry.yarnpkg.com/svgo/-/svgo-3.3.2.tgz#ad58002652dffbb5986fc9716afe52d869ecbda8" - integrity sha512-OoohrmuUlBs8B8o6MB2Aevn+pRIH9zDALSR+6hhqVfa6fRwG/Qw9VUMSMW9VNg2CFc/MTIfabtdOVl9ODIJjpw== - dependencies: - "@trysound/sax" "0.2.0" - commander "^7.2.0" - css-select "^5.1.0" - css-tree "^2.3.1" - css-what "^6.1.0" - csso "^5.0.5" - picocolors "^1.0.0" - symbol-observable@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-2.0.3.tgz#5b521d3d07a43c351055fa43b8355b62d33fd16a" @@ -21985,18 +20505,6 @@ tinyrainbow@^1.2.0: resolved "https://registry.yarnpkg.com/tinyrainbow/-/tinyrainbow-1.2.0.tgz#5c57d2fc0fb3d1afd78465c33ca885d04f02abb5" integrity sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ== -tldts-core@^6.1.84: - version "6.1.84" - resolved "https://registry.yarnpkg.com/tldts-core/-/tldts-core-6.1.84.tgz#f8ac2af9969bf9c2f7a99fa05d9c667b5e5b768c" - integrity sha512-NaQa1W76W2aCGjXybvnMYzGSM4x8fvG2AN/pla7qxcg0ZHbooOPhA8kctmOZUDfZyhDL27OGNbwAeig8P4p1vg== - -tldts@^6.1.32: - version "6.1.84" - resolved "https://registry.yarnpkg.com/tldts/-/tldts-6.1.84.tgz#fb58b1ceb70972a1ecd683606cea3d06c78f7238" - integrity sha512-aRGIbCIF3teodtUFAYSdQONVmDRy21REM3o6JnqWn5ZkQBJJ4gHxhw6OfwQ+WkSAi3ASamrS4N4nyazWx6uTYg== - dependencies: - tldts-core "^6.1.84" - tmp@^0.0.33: version "0.0.33" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" @@ -22088,13 +20596,6 @@ tough-cookie@^4.0.0: universalify "^0.2.0" url-parse "^1.5.3" -tough-cookie@^5.0.0: - version "5.1.2" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-5.1.2.tgz#66d774b4a1d9e12dc75089725af3ac75ec31bed7" - integrity sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A== - dependencies: - tldts "^6.1.32" - tr46@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/tr46/-/tr46-2.1.0.tgz#fa87aa81ca5d5941da8cbf1f9b749dc969a4e240" @@ -22102,23 +20603,11 @@ tr46@^2.1.0: dependencies: punycode "^2.1.1" -tr46@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/tr46/-/tr46-5.1.0.tgz#4a077922360ae807e172075ce5beb79b36e4a101" - integrity sha512-IUWnUK7ADYR5Sl1fZlO1INDUhVhatWl7BtJWsIhwJ0UAK7ilzzIa8uIqOO/aYVWHZPJkKbEL+362wrzoeRF7bw== - dependencies: - punycode "^2.3.1" - tr46@~0.0.3: version "0.0.3" resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== -trim-lines@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/trim-lines/-/trim-lines-3.0.1.tgz#d802e332a07df861c48802c04321017b1bd87338" - integrity sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg== - trim-newlines@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613" @@ -22144,21 +20633,11 @@ trough@^1.0.0: resolved "https://registry.yarnpkg.com/trough/-/trough-1.0.5.tgz#b8b639cefad7d0bb2abd37d433ff8293efa5f406" integrity sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA== -trough@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/trough/-/trough-2.2.0.tgz#94a60bd6bd375c152c1df911a4b11d5b0256f50f" - integrity sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw== - ts-api-utils@^1.0.1: version "1.4.3" resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.4.3.tgz#bfc2215fe6528fecab2b0fba570a2e8a4263b064" integrity sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw== -ts-api-utils@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-2.0.1.tgz#660729385b625b939aaa58054f45c058f33f10cd" - integrity sha512-dnlgjFSVetynI8nzgJ+qF62efpglpWRk8isUEWZGWlJYySCTD6aKvbUDu+zbPeDakk3bg5H4XpitHukgfL1m9w== - ts-dedent@^2.0.0, ts-dedent@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/ts-dedent/-/ts-dedent-2.2.0.tgz#39e4bd297cd036292ae2394eb3412be63f563bb5" @@ -22538,19 +21017,6 @@ unified@9.2.0: trough "^1.0.0" vfile "^4.0.0" -unified@^11.0.0: - version "11.0.5" - resolved "https://registry.yarnpkg.com/unified/-/unified-11.0.5.tgz#f66677610a5c0a9ee90cab2b8d4d66037026d9e1" - integrity sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA== - dependencies: - "@types/unist" "^3.0.0" - bail "^2.0.0" - devlop "^1.0.0" - extend "^3.0.0" - is-plain-obj "^4.0.0" - trough "^2.0.0" - vfile "^6.0.0" - union-value@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847" @@ -22618,25 +21084,11 @@ unist-util-is@^4.0.0: resolved "https://registry.yarnpkg.com/unist-util-is/-/unist-util-is-4.1.0.tgz#976e5f462a7a5de73d94b706bac1b90671b57797" integrity sha512-ZOQSsnce92GrxSqlnEEseX0gi7GH9zTJZ0p9dtu87WRb/37mMPO2Ilx1s/t9vBHrFhbgweUwb+t7cIn5dxPhZg== -unist-util-is@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/unist-util-is/-/unist-util-is-6.0.0.tgz#b775956486aff107a9ded971d996c173374be424" - integrity sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw== - dependencies: - "@types/unist" "^3.0.0" - unist-util-position@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/unist-util-position/-/unist-util-position-3.1.0.tgz#1c42ee6301f8d52f47d14f62bbdb796571fa2d47" integrity sha512-w+PkwCbYSFw8vpgWD0v7zRCl1FpY3fjDSQ3/N/wNd9Ffa4gPi8+4keqt99N3XW6F99t/mUzp2xAhNmfKWp95QA== -unist-util-position@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/unist-util-position/-/unist-util-position-5.0.0.tgz#678f20ab5ca1207a97d7ea8a388373c9cf896be4" - integrity sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA== - dependencies: - "@types/unist" "^3.0.0" - unist-util-remove-position@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/unist-util-remove-position/-/unist-util-remove-position-2.0.1.tgz#5d19ca79fdba712301999b2b73553ca8f3b352cc" @@ -22658,13 +21110,6 @@ unist-util-stringify-position@^2.0.0: dependencies: "@types/unist" "^2.0.2" -unist-util-stringify-position@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz#449c6e21a880e0855bf5aabadeb3a740314abac2" - integrity sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ== - dependencies: - "@types/unist" "^3.0.0" - unist-util-visit-parents@^3.0.0: version "3.1.1" resolved "https://registry.yarnpkg.com/unist-util-visit-parents/-/unist-util-visit-parents-3.1.1.tgz#65a6ce698f78a6b0f56aa0e88f13801886cdaef6" @@ -22673,14 +21118,6 @@ unist-util-visit-parents@^3.0.0: "@types/unist" "^2.0.0" unist-util-is "^4.0.0" -unist-util-visit-parents@^6.0.0: - version "6.0.1" - resolved "https://registry.yarnpkg.com/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz#4d5f85755c3b8f0dc69e21eca5d6d82d22162815" - integrity sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw== - dependencies: - "@types/unist" "^3.0.0" - unist-util-is "^6.0.0" - unist-util-visit@2.0.3, unist-util-visit@^2.0.0: version "2.0.3" resolved "https://registry.yarnpkg.com/unist-util-visit/-/unist-util-visit-2.0.3.tgz#c3703893146df47203bb8a9795af47d7b971208c" @@ -22690,15 +21127,6 @@ unist-util-visit@2.0.3, unist-util-visit@^2.0.0: unist-util-is "^4.0.0" unist-util-visit-parents "^3.0.0" -unist-util-visit@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/unist-util-visit/-/unist-util-visit-5.0.0.tgz#a7de1f31f72ffd3519ea71814cccf5fd6a9217d6" - integrity sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg== - dependencies: - "@types/unist" "^3.0.0" - unist-util-is "^6.0.0" - unist-util-visit-parents "^6.0.0" - universal-user-agent@^6.0.0: version "6.0.1" resolved "https://registry.yarnpkg.com/universal-user-agent/-/universal-user-agent-6.0.1.tgz#15f20f55da3c930c57bddbf1734c6654d5fd35aa" @@ -22965,14 +21393,6 @@ vfile-message@^2.0.0: "@types/unist" "^2.0.0" unist-util-stringify-position "^2.0.0" -vfile-message@^4.0.0: - version "4.0.2" - resolved "https://registry.yarnpkg.com/vfile-message/-/vfile-message-4.0.2.tgz#c883c9f677c72c166362fd635f21fc165a7d1181" - integrity sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw== - dependencies: - "@types/unist" "^3.0.0" - unist-util-stringify-position "^4.0.0" - vfile@^4.0.0: version "4.2.1" resolved "https://registry.yarnpkg.com/vfile/-/vfile-4.2.1.tgz#03f1dce28fc625c625bc6514350fbdb00fa9e624" @@ -22983,14 +21403,6 @@ vfile@^4.0.0: unist-util-stringify-position "^2.0.0" vfile-message "^2.0.0" -vfile@^6.0.0: - version "6.0.3" - resolved "https://registry.yarnpkg.com/vfile/-/vfile-6.0.3.tgz#3652ab1c496531852bf55a6bac57af981ebc38ab" - integrity sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q== - dependencies: - "@types/unist" "^3.0.0" - vfile-message "^4.0.0" - victory-vendor@^36.6.8: version "36.9.2" resolved "https://registry.yarnpkg.com/victory-vendor/-/victory-vendor-36.9.2.tgz#668b02a448fa4ea0f788dbf4228b7e64669ff801" @@ -23016,11 +21428,6 @@ vm-browserify@^1.0.1: resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0" integrity sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ== -void-elements@3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-3.1.0.tgz#614f7fbf8d801f0bb5f0661f5b2f5785750e4f09" - integrity sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w== - vscode-oniguruma@^1.6.1: version "1.7.0" resolved "https://registry.yarnpkg.com/vscode-oniguruma/-/vscode-oniguruma-1.7.0.tgz#439bfad8fe71abd7798338d1cd3dc53a8beea94b" @@ -23045,13 +21452,6 @@ w3c-xmlserializer@^2.0.0: dependencies: xml-name-validator "^3.0.0" -w3c-xmlserializer@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz#f925ba26855158594d907313cedd1476c5967f6c" - integrity sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA== - dependencies: - xml-name-validator "^5.0.0" - walker@^1.0.7, walker@~1.0.5: version "1.0.8" resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.8.tgz#bd498db477afe573dc04185f011d3ab8a8d7653f" @@ -23119,11 +21519,6 @@ webidl-conversions@^6.1.0: resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-6.1.0.tgz#9111b4d7ea80acd40f5270d666621afa78b69514" integrity sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w== -webidl-conversions@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-7.0.0.tgz#256b4e1882be7debbf01d05f0aa2039778ea080a" - integrity sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g== - webpack-cli@^4.8.0: version "4.10.0" resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-4.10.0.tgz#37c1d69c8d85214c5a65e589378f53aec64dab31" @@ -23354,31 +21749,11 @@ whatwg-encoding@^1.0.5: dependencies: iconv-lite "0.4.24" -whatwg-encoding@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz#d0f4ef769905d426e1688f3e34381a99b60b76e5" - integrity sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ== - dependencies: - iconv-lite "0.6.3" - whatwg-mimetype@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf" integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g== -whatwg-mimetype@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz#bc1bf94a985dc50388d54a9258ac405c3ca2fc0a" - integrity sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg== - -whatwg-url@^14.0.0, whatwg-url@^14.1.0: - version "14.2.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-14.2.0.tgz#4ee02d5d725155dae004f6ae95c73e7ef5d95663" - integrity sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw== - dependencies: - tr46 "^5.1.0" - webidl-conversions "^7.0.0" - whatwg-url@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" @@ -23525,11 +21900,6 @@ workerpool@^6.5.1: resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.5.1.tgz#060f73b39d0caf97c6db64da004cd01b4c099544" integrity sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA== -world-countries@^5.0.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/world-countries/-/world-countries-5.1.0.tgz#ea6b6156d9ef80c9ae6ff1e238d11b7ebd90351f" - integrity sha512-CXR6EBvTbArDlDDIWU3gfKb7Qk0ck2WNZ234b/A0vuecPzIfzzxH+O6Ejnvg1sT8XuiZjVlzOH0h08ZtaO7g0w== - "wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" @@ -23624,7 +21994,7 @@ ws@^7, ws@^7.4.6: resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.10.tgz#58b5c20dc281633f6c19113f39b349bd8bd558d9" integrity sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ== -ws@^8.13.0, ws@^8.18.0, ws@^8.2.3: +ws@^8.13.0, ws@^8.2.3: version "8.18.1" resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.1.tgz#ea131d3784e1dfdff91adb0a4a116b127515e3cb" integrity sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w== @@ -23641,11 +22011,6 @@ xml-name-validator@^3.0.0: resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a" integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw== -xml-name-validator@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-5.0.0.tgz#82be9b957f7afdacf961e5980f1bf227c0bf7673" - integrity sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg== - xml2js@^0.6.1: version "0.6.2" resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.6.2.tgz#dd0b630083aa09c161e25a4d0901e2b2a929b499" @@ -23781,11 +22146,6 @@ yup@^0.32.9: property-expr "^2.0.4" toposort "^2.0.2" -zod@^3.24.1: - version "3.24.2" - resolved "https://registry.yarnpkg.com/zod/-/zod-3.24.2.tgz#8efa74126287c675e92f46871cfc8d15c34372b3" - integrity sha512-lY7CDW43ECgW9u1TcT3IoXHflywfVqDYze4waEz812jR/bZ8FHDsl7pFQoSZTz5N+2NqRXs8GBwnAwo3ZNxqhQ== - zustand@^4.5.5: version "4.5.6" resolved "https://registry.yarnpkg.com/zustand/-/zustand-4.5.6.tgz#6857d52af44874a79fb3408c9473f78367255c96" @@ -23798,11 +22158,6 @@ zwitch@^1.0.0: resolved "https://registry.yarnpkg.com/zwitch/-/zwitch-1.0.5.tgz#d11d7381ffed16b742f6af7b3f223d5cd9fe9920" integrity sha512-V50KMwwzqJV0NpZIZFwfOD5/lyny3WlSzRiXgA0G7VUnRlqttta1L6UQIHzd6EuBY/cHGfwTIck7w1yH6Q5zUw== -zwitch@^2.0.0: - version "2.0.4" - resolved "https://registry.yarnpkg.com/zwitch/-/zwitch-2.0.4.tgz#c827d4b0acb76fc3e685a4c6ec2902d51070e9d7" - integrity sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A== - zxcvbn@^4.4.2: version "4.4.2" resolved "https://registry.yarnpkg.com/zxcvbn/-/zxcvbn-4.4.2.tgz#28ec17cf09743edcab056ddd8b1b06262cc73c30" From 9a4293a5b9b798c41a023053e4324201f374460b Mon Sep 17 00:00:00 2001 From: Tommy Verrall Date: Wed, 2 Apr 2025 08:53:40 +0200 Subject: [PATCH 02/48] add the base points back in - now i've reverted back to the original two here, as the compiler is failing around `tauri::api::path` however, looking into the new design for the path resolver in tower this tasks, requires pratically changing the whole wallet_strorage and config set up - it seems pretty straight forward https://v2.tauri.app/start/migrate/from-tauri-1/#migrate-path-to-tauri-manager - however, I would need a second set of eyes on this --- nym-wallet/src-tauri/src/config/mod.rs | 288 +- .../src-tauri/src/wallet_storage/mod.rs | 3654 ++++++++--------- 2 files changed, 1964 insertions(+), 1978 deletions(-) diff --git a/nym-wallet/src-tauri/src/config/mod.rs b/nym-wallet/src-tauri/src/config/mod.rs index 87014b1b50..1f79075157 100644 --- a/nym-wallet/src-tauri/src/config/mod.rs +++ b/nym-wallet/src-tauri/src/config/mod.rs @@ -11,7 +11,6 @@ use nym_validator_client::nyxd::AccountId as CosmosAccountId; use serde::de::DeserializeOwned; use serde::{Deserialize, Serialize}; use strum::IntoEnumIterator; -use tauri::Manager; use url::Url; use nym_config::defaults::{DenomDetailsOwned, NymNetworkDetails, ValidatorDetails}; @@ -105,35 +104,32 @@ impl NetworkConfig { } impl Config { - fn root_directory(app_handle: &tauri::AppHandle) -> PathBuf { - app_handle - .path() - .local_data_dir() - .expect("Failed to get config directory") + fn root_directory() -> PathBuf { + tauri::api::path::config_dir().expect("Failed to get config directory") } - fn config_directory(app_handle: &tauri::AppHandle) -> PathBuf { - Self::root_directory(app_handle).join(CONFIG_DIR_NAME) + fn config_directory() -> PathBuf { + Self::root_directory().join(CONFIG_DIR_NAME) } - fn config_file_path(app_handle: &tauri::AppHandle, network: Option) -> PathBuf { + fn config_file_path(network: Option) -> PathBuf { if let Some(network) = network { let network_filename = format!("{}.toml", network.as_key()); - Self::config_directory(app_handle).join(network_filename) + Self::config_directory().join(network_filename) } else { - Self::config_directory(app_handle).join(CONFIG_FILENAME) + Self::config_directory().join(CONFIG_FILENAME) } } - pub fn save_to_files(&self, app_handle: &tauri::AppHandle) -> io::Result<()> { + pub fn save_to_files(&self) -> io::Result<()> { log::trace!("Config::save_to_file"); // Make sure the whole directory structure actually exists - fs::create_dir_all(Self::config_directory(app_handle))?; + fs::create_dir_all(Self::config_directory())?; // Global config if let Some(global) = &self.global { - let location = Self::config_file_path(app_handle, None); + let location = Self::config_file_path(None); match toml::to_string_pretty(&global) .map_err(|toml_err| io::Error::new(io::ErrorKind::Other, toml_err)) @@ -154,7 +150,7 @@ impl Config { } }; - let location = Self::config_file_path(app_handle, Some(network)); + let location = Self::config_file_path(Some(network)); match toml::to_string_pretty(config) .map_err(|toml_err| io::Error::new(io::ErrorKind::Other, toml_err)) .map(|toml| fs::write(location.clone(), toml)) @@ -166,10 +162,10 @@ impl Config { Ok(()) } - pub fn load_from_files(app_handle: &tauri::AppHandle) -> Self { + pub fn load_from_files() -> Self { // Global let global = { - let file = Self::config_file_path(app_handle, None); + let file = Self::config_file_path(None); match load_from_file::(file.clone()) { Ok(global) => { log::debug!("Loaded from file {:#?}", file); @@ -185,7 +181,7 @@ impl Config { // One file per network let mut networks = HashMap::new(); for network in WalletNetwork::iter() { - let file = Self::config_file_path(app_handle, Some(network)); + let file = Self::config_file_path(Some(network)); match load_from_file::(file.clone()) { Ok(config) => { log::trace!("Loaded from file {:#?}", file); @@ -265,8 +261,6 @@ impl Config { } } - //////// - pub fn select_nyxd_url(&mut self, nyxd_url: Url, network: WalletNetwork) { if let Some(net) = self.networks.get_mut(&network.as_key()) { net.selected_nyxd_url = Some(nyxd_url); @@ -537,145 +531,145 @@ impl From for NetworkDetails { } } -// #[cfg(test)] -// mod tests { -// use super::*; +#[cfg(test)] +mod tests { + use super::*; -// fn test_config() -> Config { -// let netconfig = NetworkConfig { -// selected_nyxd_url: None, -// selected_api_url: Some("https://my_api_url.com".parse().unwrap()), + fn test_config() -> Config { + let netconfig = NetworkConfig { + selected_nyxd_url: None, + selected_api_url: Some("https://my_api_url.com".parse().unwrap()), -// nyxd_urls: Some(vec![ -// ValidatorConfigEntry { -// nyxd_url: "https://foo".parse().unwrap(), -// nyxd_name: Some("FooName".to_string()), -// api_url: None, -// }, -// ValidatorConfigEntry { -// nyxd_url: "https://bar".parse().unwrap(), -// nyxd_name: None, -// api_url: Some("https://bar/api".parse().unwrap()), -// }, -// ValidatorConfigEntry { -// nyxd_url: "https://baz".parse().unwrap(), -// nyxd_name: None, -// api_url: Some("https://baz/api".parse().unwrap()), -// }, -// ]), -// ..NetworkConfig::default() -// }; + nyxd_urls: Some(vec![ + ValidatorConfigEntry { + nyxd_url: "https://foo".parse().unwrap(), + nyxd_name: Some("FooName".to_string()), + api_url: None, + }, + ValidatorConfigEntry { + nyxd_url: "https://bar".parse().unwrap(), + nyxd_name: None, + api_url: Some("https://bar/api".parse().unwrap()), + }, + ValidatorConfigEntry { + nyxd_url: "https://baz".parse().unwrap(), + nyxd_name: None, + api_url: Some("https://baz/api".parse().unwrap()), + }, + ]), + ..NetworkConfig::default() + }; -// Config { -// base: Base::default(), -// global: Some(GlobalConfig::default()), -// networks: [(WalletNetwork::MAINNET.as_key(), netconfig)] -// .into_iter() -// .collect(), -// } -// } + Config { + base: Base::default(), + global: Some(GlobalConfig::default()), + networks: [(WalletNetwork::MAINNET.as_key(), netconfig)] + .into_iter() + .collect(), + } + } -// #[test] -// fn serialize_to_toml() { -// let config = test_config(); -// let netconfig = &config.networks[&WalletNetwork::MAINNET.as_key()]; -// assert_eq!( -// toml::to_string_pretty(netconfig).unwrap(), -// r#"version = 1 -// selected_api_url = 'https://my_api_url.com/' + #[test] + fn serialize_to_toml() { + let config = test_config(); + let netconfig = &config.networks[&WalletNetwork::MAINNET.as_key()]; + assert_eq!( + toml::to_string_pretty(netconfig).unwrap(), + r#"version = 1 +selected_api_url = 'https://my_api_url.com/' -// [[nyxd_urls]] -// nyxd_url = 'https://foo/' -// nyxd_name = 'FooName' +[[nyxd_urls]] +nyxd_url = 'https://foo/' +nyxd_name = 'FooName' -// [[nyxd_urls]] -// nyxd_url = 'https://bar/' -// api_url = 'https://bar/api' +[[nyxd_urls]] +nyxd_url = 'https://bar/' +api_url = 'https://bar/api' -// [[nyxd_urls]] -// nyxd_url = 'https://baz/' -// api_url = 'https://baz/api' -// "# -// ); -// } +[[nyxd_urls]] +nyxd_url = 'https://baz/' +api_url = 'https://baz/api' +"# + ); + } -// #[test] -// fn serialize_to_json() { -// let config = test_config(); -// let netconfig = &config.networks[&WalletNetwork::MAINNET.as_key()]; -// println!("{}", serde_json::to_string_pretty(netconfig).unwrap()); -// assert_eq!( -// serde_json::to_string_pretty(netconfig).unwrap(), -// r#"{ -// "version": 1, -// "selected_nyxd_url": null, -// "default_nyxd_url": null, -// "selected_api_url": "https://my_api_url.com/", -// "nyxd_urls": [ -// { -// "nyxd_url": "https://foo/", -// "nyxd_name": "FooName", -// "api_url": null -// }, -// { -// "nyxd_url": "https://bar/", -// "nyxd_name": null, -// "api_url": "https://bar/api" -// }, -// { -// "nyxd_url": "https://baz/", -// "nyxd_name": null, -// "api_url": "https://baz/api" -// } -// ] -// }"# -// ); -// } + #[test] + fn serialize_to_json() { + let config = test_config(); + let netconfig = &config.networks[&WalletNetwork::MAINNET.as_key()]; + println!("{}", serde_json::to_string_pretty(netconfig).unwrap()); + assert_eq!( + serde_json::to_string_pretty(netconfig).unwrap(), + r#"{ + "version": 1, + "selected_nyxd_url": null, + "default_nyxd_url": null, + "selected_api_url": "https://my_api_url.com/", + "nyxd_urls": [ + { + "nyxd_url": "https://foo/", + "nyxd_name": "FooName", + "api_url": null + }, + { + "nyxd_url": "https://bar/", + "nyxd_name": null, + "api_url": "https://bar/api" + }, + { + "nyxd_url": "https://baz/", + "nyxd_name": null, + "api_url": "https://baz/api" + } + ] +}"# + ); + } -// #[test] -// fn serialize_and_deserialize_to_toml() { -// let config = test_config(); -// let netconfig = &config.networks[&WalletNetwork::MAINNET.as_key()]; -// let config_str = toml::to_string_pretty(netconfig).unwrap(); -// let config_from_toml: NetworkConfig = toml::from_str(&config_str).unwrap(); -// assert_eq!(netconfig, &config_from_toml); -// } + #[test] + fn serialize_and_deserialize_to_toml() { + let config = test_config(); + let netconfig = &config.networks[&WalletNetwork::MAINNET.as_key()]; + let config_str = toml::to_string_pretty(netconfig).unwrap(); + let config_from_toml: NetworkConfig = toml::from_str(&config_str).unwrap(); + assert_eq!(netconfig, &config_from_toml); + } -// #[test] -// fn get_urls_parsed_from_config() { -// let config = test_config(); + #[test] + fn get_urls_parsed_from_config() { + let config = test_config(); -// let nyxd_url = config -// .get_configured_validators(WalletNetwork::MAINNET) -// .next() -// .map(|v| v.nyxd_url) -// .unwrap(); -// assert_eq!(nyxd_url.as_ref(), "https://foo/"); + let nyxd_url = config + .get_configured_validators(WalletNetwork::MAINNET) + .next() + .map(|v| v.nyxd_url) + .unwrap(); + assert_eq!(nyxd_url.as_ref(), "https://foo/"); -// // The first entry is missing an API URL -// let api_url = config -// .get_configured_validators(WalletNetwork::MAINNET) -// .next() -// .and_then(|v| v.api_url); -// assert_eq!(api_url, None); -// } + // The first entry is missing an API URL + let api_url = config + .get_configured_validators(WalletNetwork::MAINNET) + .next() + .and_then(|v| v.api_url); + assert_eq!(api_url, None); + } -// #[test] -// fn get_urls_from_defaults() { -// let config = Config::default(); + #[test] + fn get_urls_from_defaults() { + let config = Config::default(); -// let nyxd_url = config -// .get_base_validators(WalletNetwork::MAINNET) -// .next() -// .map(|v| v.nyxd_url) -// .unwrap(); -// assert_eq!(nyxd_url.as_ref(), "https://rpc.nymtech.net/"); + let nyxd_url = config + .get_base_validators(WalletNetwork::MAINNET) + .next() + .map(|v| v.nyxd_url) + .unwrap(); + assert_eq!(nyxd_url.as_ref(), "https://rpc.nymtech.net/"); -// let api_url = config -// .get_base_validators(WalletNetwork::MAINNET) -// .next() -// .and_then(|v| v.api_url) -// .unwrap(); -// assert_eq!(api_url.as_ref(), "https://validator.nymtech.net/api/",); -// } -// } + let api_url = config + .get_base_validators(WalletNetwork::MAINNET) + .next() + .and_then(|v| v.api_url) + .unwrap(); + assert_eq!(api_url.as_ref(), "https://validator.nymtech.net/api/",); + } +} \ No newline at end of file diff --git a/nym-wallet/src-tauri/src/wallet_storage/mod.rs b/nym-wallet/src-tauri/src/wallet_storage/mod.rs index c620351417..115823da4a 100644 --- a/nym-wallet/src-tauri/src/wallet_storage/mod.rs +++ b/nym-wallet/src-tauri/src/wallet_storage/mod.rs @@ -19,7 +19,6 @@ use nym_validator_client::nyxd::bip32::DerivationPath; use std::ffi::OsString; use std::fs::{self, create_dir_all, OpenOptions}; use std::path::{Path, PathBuf}; -use tauri::Manager; use time::OffsetDateTime; #[cfg(test)] @@ -38,18 +37,14 @@ pub(crate) const DEFAULT_LOGIN_ID: &str = "default"; /// this name. pub(crate) const DEFAULT_FIRST_ACCOUNT_NAME: &str = "Account 1"; -fn get_storage_directory(app_handle: &tauri::AppHandle) -> Result { - app_handle - .path() - .local_data_dir() +fn get_storage_directory() -> Result { + tauri::api::path::local_data_dir() .map(|dir| dir.join(STORAGE_DIR_NAME)) .ok_or(BackendError::UnknownStorageDirectory) } -pub(crate) fn wallet_login_filepath( - app_handle: &tauri::AppHandle, -) -> Result { - get_storage_directory(app_handle).map(|dir| dir.join(WALLET_INFO_FILENAME)) +pub(crate) fn wallet_login_filepath() -> Result { + get_storage_directory().map(|dir| dir.join(WALLET_INFO_FILENAME)) } fn write_to_file(filepath: &Path, wallet: &StoredWallet) -> Result<(), BackendError> { @@ -64,10 +59,8 @@ fn write_to_file(filepath: &Path, wallet: &StoredWallet) -> Result<(), BackendEr /// Load stored wallet file #[allow(unused)] -pub(crate) fn load_existing_wallet( - app_handle: &tauri::AppHandle, -) -> Result { - let store_dir = get_storage_directory(app_handle)?; +pub(crate) fn load_existing_wallet() -> Result { + let store_dir = get_storage_directory()?; let filepath = store_dir.join(WALLET_INFO_FILENAME); load_existing_wallet_at_file(&filepath) } @@ -84,11 +77,10 @@ fn load_existing_wallet_at_file(filepath: &Path) -> Result Result { - let store_dir = get_storage_directory(app_handle)?; + let store_dir = get_storage_directory()?; let filepath = store_dir.join(WALLET_INFO_FILENAME); load_existing_login_at_file(&filepath, id, password) } @@ -476,1819 +468,1819 @@ fn rename_account_in_login_at_file( write_to_file(filepath, &stored_wallet) } -// #[cfg(test)] -// mod tests { -// use crate::wallet_storage::account_data::WalletAccount; - -// use super::*; -// use nym_config::defaults::COSMOS_DERIVATION_PATH; -// use std::str::FromStr; -// use tempfile::tempdir; - -// #[test] -// fn trying_to_load_nonexistant_wallet_fails() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let id1 = LoginId::new("first".to_string()); -// let password = UserPassword::new("password".to_string()); - -// assert!(matches!( -// load_existing_wallet_at_file(&wallet_file), -// Err(BackendError::WalletFileNotFound), -// )); -// assert!(matches!( -// load_existing_login_at_file(&wallet_file, &id1, &password), -// Err(BackendError::WalletFileNotFound), -// )); -// remove_login_at_file(&wallet_file, &id1).unwrap_err(); -// } - -// #[test] -// fn store_single_login() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let id1 = LoginId::new("first".to_string()); - -// store_login_at_file(&wallet_file, account1, hd_path, id1.clone(), &password).unwrap(); - -// let stored_wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); -// assert_eq!(stored_wallet.len(), 1); - -// let login = stored_wallet.get_encrypted_login_by_index(0).unwrap(); -// assert_eq!(login.id, id1); - -// // some actual ciphertext was saved -// assert!(!login.account.ciphertext().is_empty()); -// } - -// #[test] -// fn store_single_login_with_multi() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let id1 = LoginId::new("first".to_string()); - -// store_login_with_multiple_accounts_at_file( -// &wallet_file, -// account1, -// cosmos_hd_path, -// id1.clone(), -// &password, -// ) -// .unwrap(); - -// let stored_wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); -// assert_eq!(stored_wallet.len(), 1); - -// let login = stored_wallet.get_encrypted_login_by_index(0).unwrap(); -// assert_eq!(login.id, id1); - -// // some actual ciphertext was saved -// assert!(!login.account.ciphertext().is_empty()); -// } - -// #[test] -// fn store_single_login_with_multi_then_update_pwd_and_load() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let new_password = UserPassword::new("new_password".to_string()); -// let id1 = LoginId::new("first".to_string()); - -// store_login_with_multiple_accounts_at_file( -// &wallet_file, -// account1, -// cosmos_hd_path, -// id1.clone(), -// &password, -// ) -// .unwrap(); - -// update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); - -// let stored_wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); -// assert_eq!(stored_wallet.len(), 1); - -// let login = stored_wallet.get_encrypted_login_by_index(0).unwrap(); -// assert_eq!(login.id, id1); - -// // some actual ciphertext was saved -// assert!(!login.account.ciphertext().is_empty()); -// } - -// #[test] -// fn store_twice_for_the_same_id_fails() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let id1 = LoginId::new("first".to_string()); - -// // Store the first login -// store_login_at_file( -// &wallet_file, -// account1.clone(), -// hd_path.clone(), -// id1.clone(), -// &password, -// ) -// .unwrap(); - -// // and storing the same id again fails -// assert!(matches!( -// store_login_at_file(&wallet_file, account1, hd_path, id1, &password,), -// Err(BackendError::WalletLoginIdAlreadyExists), -// )); -// } - -// #[test] -// fn store_twice_for_the_same_id_fails_with_multiple() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let id1 = LoginId::new("first".to_string()); - -// // Store the first login -// store_login_with_multiple_accounts_at_file( -// &wallet_file, -// account1.clone(), -// hd_path.clone(), -// id1.clone(), -// &password, -// ) -// .unwrap(); - -// // and storing the same id again fails -// assert!(matches!( -// store_login_with_multiple_accounts_at_file( -// &wallet_file, -// account1, -// hd_path, -// id1, -// &password, -// ), -// Err(BackendError::WalletLoginIdAlreadyExists), -// )); -// } - -// #[test] -// fn load_with_wrong_password_fails() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let bad_password = UserPassword::new("bad-password".to_string()); -// let id1 = LoginId::new("first".to_string()); - -// store_login_at_file(&wallet_file, account1, hd_path, id1.clone(), &password).unwrap(); - -// // Trying to load it with wrong password now fails -// assert!(matches!( -// load_existing_login_at_file(&wallet_file, &id1, &bad_password), -// Err(BackendError::StoreCipherError { .. }), -// )); -// } - -// #[test] -// fn load_with_wrong_password_fails_with_multi() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let bad_password = UserPassword::new("bad-password".to_string()); -// let id1 = LoginId::new("first".to_string()); - -// store_login_with_multiple_accounts_at_file( -// &wallet_file, -// account1, -// hd_path, -// id1.clone(), -// &password, -// ) -// .unwrap(); - -// // Trying to load it with wrong password now fails -// assert!(matches!( -// load_existing_login_at_file(&wallet_file, &id1, &bad_password), -// Err(BackendError::StoreCipherError { .. }), -// )); -// } - -// #[test] -// fn load_with_wrong_id_fails() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let id1 = LoginId::new("first".to_string()); -// let id2 = LoginId::new("second".to_string()); - -// store_login_at_file(&wallet_file, account1, hd_path, id1, &password).unwrap(); - -// // Trying to load with the wrong id -// assert!(matches!( -// load_existing_login_at_file(&wallet_file, &id2, &password), -// Err(BackendError::WalletNoSuchLoginId), -// )); -// } - -// #[test] -// fn load_with_wrong_id_fails_with_multi() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let id1 = LoginId::new("first".to_string()); -// let id2 = LoginId::new("second".to_string()); - -// store_login_with_multiple_accounts_at_file(&wallet_file, account1, hd_path, id1, &password) -// .unwrap(); - -// // Trying to load with the wrong id -// assert!(matches!( -// load_existing_login_at_file(&wallet_file, &id2, &password), -// Err(BackendError::WalletNoSuchLoginId), -// )); -// } - -// #[test] -// fn store_and_load_a_single_login() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let id1 = LoginId::new("first".to_string()); - -// store_login_at_file( -// &wallet_file, -// account1.clone(), -// hd_path.clone(), -// id1.clone(), -// &password, -// ) -// .unwrap(); - -// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); -// let acc = loaded_login.as_mnemonic_account().unwrap(); -// assert_eq!(&account1, acc.mnemonic()); -// assert_eq!(&hd_path, acc.hd_path()); -// } - -// #[test] -// fn store_a_single_login_then_update_pwd_and_load() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let new_password = UserPassword::new("new_password".to_string()); -// let id1 = LoginId::new("first".to_string()); - -// store_login_at_file( -// &wallet_file, -// account1.clone(), -// hd_path.clone(), -// id1.clone(), -// &password, -// ) -// .unwrap(); - -// update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); - -// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &new_password).unwrap(); -// let acc = loaded_login.as_mnemonic_account().unwrap(); -// assert_eq!(&account1, acc.mnemonic()); -// assert_eq!(&hd_path, acc.hd_path()); -// } - -// #[test] -// fn store_a_single_login_then_update_pwd_with_identical_pwd_is_noop_but_okay() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let id1 = LoginId::new("first".to_string()); - -// store_login_at_file( -// &wallet_file, -// account1.clone(), -// hd_path.clone(), -// id1.clone(), -// &password, -// ) -// .unwrap(); - -// update_encrypted_logins_at_file(&wallet_file, &password, &password).unwrap(); - -// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); -// let acc = loaded_login.as_mnemonic_account().unwrap(); -// assert_eq!(&account1, acc.mnemonic()); -// assert_eq!(&hd_path, acc.hd_path()); -// } - -// #[test] -// fn store_a_single_login_then_update_pwd_with_wrong_current_pwd_fails() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let new_password = UserPassword::new("new_password".to_string()); -// let wrong_password = UserPassword::new("wrong_password".to_string()); -// let id1 = LoginId::new("first".to_string()); - -// store_login_at_file(&wallet_file, account1, hd_path, id1, &password).unwrap(); - -// let err = update_encrypted_logins_at_file(&wallet_file, &wrong_password, &new_password) -// .unwrap_err(); -// assert!(matches!(err, BackendError::StoreCipherError { .. })); -// } - -// #[test] -// fn store_a_single_login_then_update_pwd_and_load_with_wrong_pwd_fails() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let new_password = UserPassword::new("new_password".to_string()); -// let id1 = LoginId::new("first".to_string()); - -// store_login_at_file(&wallet_file, account1, hd_path, id1.clone(), &password).unwrap(); - -// update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); - -// let err = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap_err(); -// assert!(matches!(err, BackendError::StoreCipherError { .. })); -// } - -// #[test] -// fn store_and_load_a_single_login_with_multi() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let acc1 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let id1 = LoginId::new("first".to_string()); - -// store_login_with_multiple_accounts_at_file( -// &wallet_file, -// acc1.clone(), -// hd_path.clone(), -// id1.clone(), -// &password, -// ) -// .unwrap(); - -// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); -// let accounts = loaded_login.as_multiple_accounts().unwrap(); -// assert_eq!(accounts.len(), 1); -// let account = accounts -// .get_account(&DEFAULT_FIRST_ACCOUNT_NAME.into()) -// .unwrap(); -// assert_eq!(account.id().as_ref(), DEFAULT_FIRST_ACCOUNT_NAME); -// assert_eq!(account.mnemonic(), &acc1); -// assert_eq!(account.hd_path(), &hd_path); -// } - -// #[test] -// fn store_a_single_login_with_multi_then_update_pwd_and_load() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let acc1 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let new_password = UserPassword::new("new_password".to_string()); -// let id1 = LoginId::new("first".to_string()); - -// store_login_with_multiple_accounts_at_file( -// &wallet_file, -// acc1.clone(), -// hd_path.clone(), -// id1.clone(), -// &password, -// ) -// .unwrap(); - -// update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); - -// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &new_password).unwrap(); -// let accounts = loaded_login.as_multiple_accounts().unwrap(); -// assert_eq!(accounts.len(), 1); -// let account = accounts -// .get_account(&DEFAULT_FIRST_ACCOUNT_NAME.into()) -// .unwrap(); -// assert_eq!(account.id().as_ref(), DEFAULT_FIRST_ACCOUNT_NAME); -// assert_eq!(account.mnemonic(), &acc1); -// assert_eq!(account.hd_path(), &hd_path); -// } - -// #[test] -// fn store_a_single_login_with_multi_then_update_pwd_with_wrong_current_pwd_fails() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let acc1 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let new_password = UserPassword::new("new_password".to_string()); -// let wrong_password = UserPassword::new("wrong_password".to_string()); -// let id1 = LoginId::new("first".to_string()); - -// store_login_with_multiple_accounts_at_file(&wallet_file, acc1, hd_path, id1, &password) -// .unwrap(); - -// let err = update_encrypted_logins_at_file(&wallet_file, &wrong_password, &new_password) -// .unwrap_err(); -// assert!(matches!(err, BackendError::StoreCipherError { .. })); -// } - -// #[test] -// fn store_a_single_login_with_multi_then_update_pwd_and_load_with_wrong_pwd_fails() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let acc1 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let new_password = UserPassword::new("new_password".to_string()); -// let id1 = LoginId::new("first".to_string()); - -// store_login_with_multiple_accounts_at_file( -// &wallet_file, -// acc1, -// hd_path, -// id1.clone(), -// &password, -// ) -// .unwrap(); - -// update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); - -// let err = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap_err(); -// assert!(matches!(err, BackendError::StoreCipherError { .. })); -// } - -// #[test] -// fn store_a_second_login_with_a_different_password_fails() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let account2 = Mnemonic::generate(24).unwrap(); -// let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let bad_password = UserPassword::new("bad-password".to_string()); -// let id1 = LoginId::new("first".to_string()); -// let id2 = LoginId::new("second".to_string()); - -// store_login_at_file( -// &wallet_file, -// account1, -// cosmos_hd_path.clone(), -// id1, -// &password, -// ) -// .unwrap(); - -// // Can't store a second login if you use different password -// assert!(matches!( -// store_login_at_file(&wallet_file, account2, cosmos_hd_path, id2, &bad_password), -// Err(BackendError::WalletDifferentPasswordDetected), -// )); -// } - -// #[test] -// fn store_a_second_login_with_a_different_password_fails_with_multi() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let account2 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let bad_password = UserPassword::new("bad-password".to_string()); -// let id1 = LoginId::new("first".to_string()); -// let id2 = LoginId::new("second".to_string()); - -// store_login_with_multiple_accounts_at_file( -// &wallet_file, -// account1, -// hd_path.clone(), -// id1, -// &password, -// ) -// .unwrap(); - -// // Can't store a second login if you use different password -// assert!(matches!( -// store_login_with_multiple_accounts_at_file( -// &wallet_file, -// account2, -// hd_path, -// id2, -// &bad_password -// ), -// Err(BackendError::WalletDifferentPasswordDetected), -// )); -// } - -// #[test] -// fn store_two_mnemonic_accounts_gives_different_salts_and_iv() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let account2 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let different_hd_path: DerivationPath = "m".parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let id1 = LoginId::new("first".to_string()); -// let id2 = LoginId::new("second".to_string()); - -// // Store the first account -// store_login_at_file(&wallet_file, account1, hd_path, id1, &password).unwrap(); - -// let stored_wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); -// let encrypted_blob = &stored_wallet -// .get_encrypted_login_by_index(0) -// .unwrap() -// .account; - -// // keep track of salt and iv for future assertion -// let original_iv = encrypted_blob.iv().to_vec(); -// let original_salt = encrypted_blob.salt().to_vec(); - -// // Add an extra account -// store_login_at_file(&wallet_file, account2, different_hd_path, id2, &password).unwrap(); - -// let loaded_accounts = load_existing_wallet_at_file(&wallet_file).unwrap(); -// assert_eq!(loaded_accounts.len(), 2); -// let encrypted_blob = &loaded_accounts -// .get_encrypted_login_by_index(1) -// .unwrap() -// .account; - -// // fresh IV and salt are used -// assert_ne!(original_iv, encrypted_blob.iv()); -// assert_ne!(original_salt, encrypted_blob.salt()); -// } - -// #[test] -// fn store_two_mnemonic_accounts_using_two_logins() { -// let store_dir = tempdir().unwrap(); -// let wallet = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let account2 = Mnemonic::generate(24).unwrap(); -// let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let different_hd_path: DerivationPath = "m".parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let id1 = LoginId::new("first".to_string()); -// let id2 = LoginId::new("second".to_string()); - -// // Store the first account -// store_login_at_file( -// &wallet, -// account1.clone(), -// cosmos_hd_path.clone(), -// id1.clone(), -// &password, -// ) -// .unwrap(); - -// let login = load_existing_login_at_file(&wallet, &id1, &password).unwrap(); -// let acc = login.as_mnemonic_account().unwrap(); -// assert_eq!(&account1, acc.mnemonic()); -// assert_eq!(&cosmos_hd_path, acc.hd_path()); - -// // Add an extra account -// store_login_at_file( -// &wallet, -// account2.clone(), -// different_hd_path.clone(), -// id2.clone(), -// &password, -// ) -// .unwrap(); - -// // first account should be unchanged -// let loaded_login = load_existing_login_at_file(&wallet, &id1, &password).unwrap(); -// let acc1 = loaded_login.as_mnemonic_account().unwrap(); -// assert_eq!(&account1, acc1.mnemonic()); -// assert_eq!(&cosmos_hd_path, acc1.hd_path()); - -// let loaded_login = load_existing_login_at_file(&wallet, &id2, &password).unwrap(); -// let acc2 = loaded_login.as_mnemonic_account().unwrap(); -// assert_eq!(&account2, acc2.mnemonic()); -// assert_eq!(&different_hd_path, acc2.hd_path()); -// } - -// #[test] -// fn store_two_mnemonic_accounts_using_two_logins_then_update_pwd_and_load() { -// let store_dir = tempdir().unwrap(); -// let wallet = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let account2 = Mnemonic::generate(24).unwrap(); -// let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let different_hd_path: DerivationPath = "m".parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let new_password = UserPassword::new("new_password".to_string()); -// let id1 = LoginId::new("first".to_string()); -// let id2 = LoginId::new("second".to_string()); - -// // Store the first login with an account -// store_login_at_file( -// &wallet, -// account1.clone(), -// cosmos_hd_path.clone(), -// id1.clone(), -// &password, -// ) -// .unwrap(); - -// let login = load_existing_login_at_file(&wallet, &id1, &password).unwrap(); -// let acc = login.as_mnemonic_account().unwrap(); -// assert_eq!(&account1, acc.mnemonic()); -// assert_eq!(&cosmos_hd_path, acc.hd_path()); - -// // Store a second login, also with an account -// store_login_at_file( -// &wallet, -// account2.clone(), -// different_hd_path.clone(), -// id2.clone(), -// &password, -// ) -// .unwrap(); - -// update_encrypted_logins_at_file(&wallet, &password, &new_password).unwrap(); - -// // first account should be unchanged -// let loaded_login = load_existing_login_at_file(&wallet, &id1, &new_password).unwrap(); -// let acc1 = loaded_login.as_mnemonic_account().unwrap(); -// assert_eq!(&account1, acc1.mnemonic()); -// assert_eq!(&cosmos_hd_path, acc1.hd_path()); - -// let loaded_login = load_existing_login_at_file(&wallet, &id2, &new_password).unwrap(); -// let acc2 = loaded_login.as_mnemonic_account().unwrap(); -// assert_eq!(&account2, acc2.mnemonic()); -// assert_eq!(&different_hd_path, acc2.hd_path()); -// } - -// #[test] -// fn store_one_mnemonic_account_and_one_multi_account() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let account2 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let different_hd_path: DerivationPath = "m".parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let id1 = LoginId::new("first".to_string()); -// let id2 = LoginId::new("second".to_string()); - -// // Store the first account -// store_login_at_file( -// &wallet_file, -// account1.clone(), -// hd_path.clone(), -// id1.clone(), -// &password, -// ) -// .unwrap(); - -// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); -// let acc = loaded_login.as_mnemonic_account().unwrap(); -// assert_eq!(&account1, acc.mnemonic()); -// assert_eq!(&hd_path, acc.hd_path()); - -// // Add an extra account -// store_login_with_multiple_accounts_at_file( -// &wallet_file, -// account2.clone(), -// different_hd_path.clone(), -// id2.clone(), -// &password, -// ) -// .unwrap(); - -// // first account should be unchanged -// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); -// let acc1 = loaded_login.as_mnemonic_account().unwrap(); -// assert_eq!(&account1, acc1.mnemonic()); -// assert_eq!(&hd_path, acc1.hd_path()); - -// let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); -// let acc2 = loaded_login.as_multiple_accounts().unwrap(); -// assert_eq!(acc2.len(), 1); -// let account = acc2 -// .get_account(&DEFAULT_FIRST_ACCOUNT_NAME.into()) -// .unwrap(); -// assert_eq!(account.id().as_ref(), DEFAULT_FIRST_ACCOUNT_NAME); -// assert_eq!(account.mnemonic(), &account2); -// assert_eq!(account.hd_path(), &different_hd_path); -// } - -// #[test] -// fn remove_non_existent_id_fails() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let id1 = LoginId::new("first".to_string()); -// let id2 = LoginId::new("second".to_string()); - -// store_login_with_multiple_accounts_at_file(&wallet_file, account1, hd_path, id1, &password) -// .unwrap(); - -// // Fails to delete non-existent id in the wallet -// assert!(matches!( -// remove_login_at_file(&wallet_file, &id2), -// Err(BackendError::WalletNoSuchLoginId), -// )); -// } - -// #[test] -// fn store_and_remove_wallet_login_information() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let account2 = Mnemonic::generate(24).unwrap(); -// let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let different_hd_path: DerivationPath = "m".parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let id1 = LoginId::new("first".to_string()); -// let id2 = LoginId::new("second".to_string()); - -// // Store two accounts with two different passwords -// store_login_at_file( -// &wallet_file, -// account1.clone(), -// cosmos_hd_path.clone(), -// id1.clone(), -// &password, -// ) -// .unwrap(); -// store_login_at_file( -// &wallet_file, -// account2.clone(), -// different_hd_path.clone(), -// id2.clone(), -// &password, -// ) -// .unwrap(); - -// // Load and compare -// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); -// let acc1 = loaded_login.as_mnemonic_account().unwrap(); -// assert_eq!(&account1, acc1.mnemonic()); -// assert_eq!(&cosmos_hd_path, acc1.hd_path()); - -// let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); -// let acc2 = loaded_login.as_mnemonic_account().unwrap(); -// assert_eq!(&account2, acc2.mnemonic()); -// assert_eq!(&different_hd_path, acc2.hd_path()); - -// // Delete the second account -// remove_login_at_file(&wallet_file, &id2).unwrap(); - -// // The first account should be unchanged -// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); -// let acc1 = loaded_login.as_mnemonic_account().unwrap(); -// assert_eq!(&account1, acc1.mnemonic()); -// assert_eq!(&cosmos_hd_path, acc1.hd_path()); - -// // And we can't load the second one anymore -// assert!(matches!( -// load_existing_login_at_file(&wallet_file, &id2, &password), -// Err(BackendError::WalletNoSuchLoginId), -// )); - -// // Delete the first account -// assert!(wallet_file.exists()); -// remove_login_at_file(&wallet_file, &id1).unwrap(); - -// // The file should now be removed -// assert!(!wallet_file.exists()); - -// // And trying to load when the file is gone fails -// assert!(matches!( -// load_existing_login_at_file(&wallet_file, &id1, &password), -// Err(BackendError::WalletFileNotFound), -// )); -// } - -// #[test] -// fn append_account_converts_the_type() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let account2 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let id1 = LoginId::new("first".to_string()); -// let id2 = AccountId::new("second".to_string()); - -// store_login_at_file( -// &wallet_file, -// account1.clone(), -// hd_path.clone(), -// id1.clone(), -// &password, -// ) -// .unwrap(); - -// // Check that it's there as the correct non-multiple type -// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); -// let acc = loaded_login.as_mnemonic_account().unwrap(); -// assert_eq!(acc.mnemonic(), &account1); -// assert_eq!(acc.hd_path(), &hd_path); - -// append_account_to_login_at_file( -// &wallet_file, -// account2.clone(), -// hd_path.clone(), -// id1.clone(), -// id2.clone(), -// &password, -// ) -// .unwrap(); - -// // Check that it is now multiple mnemonic type -// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); -// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); -// let expected = vec![ -// WalletAccount::new(id1.into(), MnemonicAccount::new(account1, hd_path.clone())), -// WalletAccount::new(id2, MnemonicAccount::new(account2, hd_path)), -// ] -// .into(); -// assert_eq!(loaded_accounts, &expected); -// } - -// #[test] -// fn append_accounts_to_existing_login() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let account2 = Mnemonic::generate(24).unwrap(); -// let account3 = Mnemonic::generate(24).unwrap(); -// let account4 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let id1 = LoginId::new("first".to_string()); -// let id2 = LoginId::new("second".to_string()); -// let id3 = AccountId::new("third".to_string()); -// let id4 = AccountId::new("fourth".to_string()); - -// store_login_at_file( -// &wallet_file, -// account1.clone(), -// hd_path.clone(), -// id1.clone(), -// &password, -// ) -// .unwrap(); - -// store_login_at_file( -// &wallet_file, -// account2.clone(), -// hd_path.clone(), -// id2.clone(), -// &password, -// ) -// .unwrap(); - -// // Check that it's there as the correct non-multiple type -// let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); -// let acc2 = loaded_login.as_mnemonic_account().unwrap(); -// assert_eq!(acc2.mnemonic(), &account2); -// assert_eq!(acc2.hd_path(), &hd_path); - -// // Add a third and fourth mnenonic grouped together with the second one -// append_account_to_login_at_file( -// &wallet_file, -// account3.clone(), -// hd_path.clone(), -// id2.clone(), -// id3.clone(), -// &password, -// ) -// .unwrap(); -// append_account_to_login_at_file( -// &wallet_file, -// account4.clone(), -// hd_path.clone(), -// id2.clone(), -// id4.clone(), -// &password, -// ) -// .unwrap(); - -// // Check that we can load all four -// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); -// let acc1 = loaded_login.as_mnemonic_account().unwrap(); -// assert_eq!(acc1.mnemonic(), &account1); -// assert_eq!(acc1.hd_path(), &hd_path); - -// let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); -// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); -// let expected = vec![ -// WalletAccount::new(id2.into(), MnemonicAccount::new(account2, hd_path.clone())), -// WalletAccount::new(id3, MnemonicAccount::new(account3, hd_path.clone())), -// WalletAccount::new(id4, MnemonicAccount::new(account4, hd_path)), -// ] -// .into(); -// assert_eq!(loaded_accounts, &expected); -// } - -// #[test] -// fn append_accounts_to_existing_login_with_multi() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let account2 = Mnemonic::generate(24).unwrap(); -// let account3 = Mnemonic::generate(24).unwrap(); -// let account4 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let id1 = LoginId::new("first".to_string()); -// let id2 = LoginId::new("second".to_string()); -// let id3 = AccountId::new("third".to_string()); -// let id4 = AccountId::new("fourth".to_string()); - -// store_login_with_multiple_accounts_at_file( -// &wallet_file, -// account1.clone(), -// hd_path.clone(), -// id1.clone(), -// &password, -// ) -// .unwrap(); - -// store_login_with_multiple_accounts_at_file( -// &wallet_file, -// account2.clone(), -// hd_path.clone(), -// id2.clone(), -// &password, -// ) -// .unwrap(); - -// // Add a third and fourth mnenonic grouped together with the second one -// append_account_to_login_at_file( -// &wallet_file, -// account3.clone(), -// hd_path.clone(), -// id2.clone(), -// id3.clone(), -// &password, -// ) -// .unwrap(); -// append_account_to_login_at_file( -// &wallet_file, -// account4.clone(), -// hd_path.clone(), -// id2.clone(), -// id4.clone(), -// &password, -// ) -// .unwrap(); - -// // Check that we can load all four -// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); -// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); -// let expected = vec![WalletAccount::new( -// DEFAULT_FIRST_ACCOUNT_NAME.into(), -// MnemonicAccount::new(account1, hd_path.clone()), -// )] -// .into(); -// assert_eq!(loaded_accounts, &expected); - -// let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); -// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); -// let expected = vec![ -// WalletAccount::new( -// DEFAULT_FIRST_ACCOUNT_NAME.into(), -// MnemonicAccount::new(account2, hd_path.clone()), -// ), -// WalletAccount::new(id3, MnemonicAccount::new(account3, hd_path.clone())), -// WalletAccount::new(id4, MnemonicAccount::new(account4, hd_path)), -// ] -// .into(); -// assert_eq!(loaded_accounts, &expected); -// } - -// #[test] -// fn append_account_to_existing_login_with_multi_then_update_pwd_and_load() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let account2 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let new_password = UserPassword::new("new_password".to_string()); -// let login_id = LoginId::new("first".to_string()); -// let appended_account = AccountId::new("second".to_string()); - -// store_login_with_multiple_accounts_at_file( -// &wallet_file, -// account1.clone(), -// hd_path.clone(), -// login_id.clone(), -// &password, -// ) -// .unwrap(); - -// // Append a second mnenonic to the same login -// append_account_to_login_at_file( -// &wallet_file, -// account2.clone(), -// hd_path.clone(), -// login_id.clone(), -// appended_account.clone(), -// &password, -// ) -// .unwrap(); - -// // Update the password -// update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); - -// // Expect that we can load these 2 accounts with the new password -// let loaded_login = -// load_existing_login_at_file(&wallet_file, &login_id, &new_password).unwrap(); -// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); -// let expected = vec![ -// WalletAccount::new( -// DEFAULT_FIRST_ACCOUNT_NAME.into(), -// MnemonicAccount::new(account1, hd_path.clone()), -// ), -// WalletAccount::new(appended_account, MnemonicAccount::new(account2, hd_path)), -// ] -// .into(); -// assert_eq!(loaded_accounts, &expected); - -// // Expect that trying to load these 2 accounts with the old password fails -// let err = load_existing_login_at_file(&wallet_file, &login_id, &password).unwrap_err(); -// assert!(matches!(err, BackendError::StoreCipherError { .. })); -// } - -// #[test] -// fn append_the_same_mnemonic_twice_fails() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let id1 = LoginId::new("first".to_string()); -// let id2 = AccountId::new("second".to_string()); - -// store_login_with_multiple_accounts_at_file( -// &wallet_file, -// account1.clone(), -// hd_path.clone(), -// id1.clone(), -// &password, -// ) -// .unwrap(); - -// assert!(matches!( -// append_account_to_login_at_file(&wallet_file, account1, hd_path, id1, id2, &password), -// Err(BackendError::WalletMnemonicAlreadyExistsInWalletLogin), -// )) -// } - -// #[test] -// fn append_the_same_account_name_twice_fails() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let mnemonic1 = Mnemonic::generate(24).unwrap(); -// let mnemonic2 = Mnemonic::generate(24).unwrap(); -// let mnemonic3 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// // The top-level login id. NOTE: the first account id is always set to default. -// let login_id = LoginId::new("my_login_id".to_string()); - -// // Store the first account under login_id. The first account id is always set to default -// // name. -// store_login_with_multiple_accounts_at_file( -// &wallet_file, -// mnemonic1, -// hd_path.clone(), -// login_id.clone(), -// &password, -// ) -// .unwrap(); - -// // Append another account (account2) to the same login (login_id) -// let account2 = AccountId::new("account_2".to_string()); - -// append_account_to_login_at_file( -// &wallet_file, -// mnemonic2, -// hd_path.clone(), -// login_id.clone(), -// account2.clone(), -// &password, -// ) -// .unwrap(); - -// // Appending the third account, with same account id will fail -// assert!(matches!( -// append_account_to_login_at_file( -// &wallet_file, -// mnemonic3, -// hd_path, -// login_id, -// account2, -// &password, -// ), -// Err(BackendError::WalletAccountIdAlreadyExistsInWalletLogin), -// )) -// } - -// #[test] -// fn delete_the_same_account_twice_for_a_login_fails() { -// let store_dir = tempdir().unwrap(); -// let wallet = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let account2 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let id1 = LoginId::new("first".to_string()); -// let id2 = AccountId::new("second".to_string()); - -// store_login_at_file(&wallet, account1, hd_path.clone(), id1.clone(), &password).unwrap(); - -// append_account_to_login_at_file( -// &wallet, -// account2, -// hd_path, -// id1.clone(), -// id2.clone(), -// &password, -// ) -// .unwrap(); - -// remove_account_from_login_at_file(&wallet, &id1, &id2, &password).unwrap(); - -// assert!(matches!( -// remove_account_from_login_at_file(&wallet, &id1, &id2, &password), -// Err(BackendError::WalletNoSuchAccountIdInWalletLogin), -// )); -// } - -// #[test] -// fn delete_the_same_account_twice_for_a_login_fails_with_multi() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let account2 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let id1 = LoginId::new("first".to_string()); -// let id2 = AccountId::new("second".to_string()); - -// store_login_with_multiple_accounts_at_file( -// &wallet_file, -// account1, -// hd_path.clone(), -// id1.clone(), -// &password, -// ) -// .unwrap(); - -// append_account_to_login_at_file( -// &wallet_file, -// account2, -// hd_path, -// id1.clone(), -// id2.clone(), -// &password, -// ) -// .unwrap(); - -// remove_account_from_login_at_file(&wallet_file, &id1, &id2, &password).unwrap(); - -// assert!(matches!( -// remove_account_from_login_at_file(&wallet_file, &id1, &id2, &password), -// Err(BackendError::WalletNoSuchAccountIdInWalletLogin), -// )); -// } - -// #[test] -// fn delete_appended_account_doesnt_affect_others() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let account2 = Mnemonic::generate(24).unwrap(); -// let account3 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let id1 = LoginId::new("first".to_string()); -// let id2 = LoginId::new("second".to_string()); -// let id3 = AccountId::new("third".to_string()); - -// store_login_at_file( -// &wallet_file, -// account1, -// hd_path.clone(), -// id1.clone(), -// &password, -// ) -// .unwrap(); - -// store_login_at_file( -// &wallet_file, -// account2.clone(), -// hd_path.clone(), -// id2.clone(), -// &password, -// ) -// .unwrap(); - -// append_account_to_login_at_file( -// &wallet_file, -// account3.clone(), -// hd_path.clone(), -// id2.clone(), -// id3.clone(), -// &password, -// ) -// .unwrap(); - -// remove_login_at_file(&wallet_file, &id1).unwrap(); - -// // The second login one is still there -// let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); -// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); -// let expected = vec![ -// WalletAccount::new(id2.into(), MnemonicAccount::new(account2, hd_path.clone())), -// WalletAccount::new(id3, MnemonicAccount::new(account3, hd_path)), -// ] -// .into(); -// assert_eq!(loaded_accounts, &expected); -// } - -// #[test] -// fn remove_all_accounts_for_a_login_removes_the_file_when_empty() { -// let store_dir = tempdir().unwrap(); -// let wallet = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let account2 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let id1 = LoginId::new("first".to_string()); -// let id2 = AccountId::new("second".to_string()); - -// store_login_with_multiple_accounts_at_file( -// &wallet, -// account1, -// hd_path.clone(), -// id1.clone(), -// &password, -// ) -// .unwrap(); - -// append_account_to_login_at_file( -// &wallet, -// account2, -// hd_path, -// id1.clone(), -// id2.clone(), -// &password, -// ) -// .unwrap(); - -// remove_account_from_login_at_file( -// &wallet, -// &id1, -// &DEFAULT_FIRST_ACCOUNT_NAME.into(), -// &password, -// ) -// .unwrap(); -// remove_account_from_login_at_file(&wallet, &id1, &id2, &password).unwrap(); - -// // The file should now be removed -// assert!(!wallet.exists()); - -// // And trying to load when the file is gone fails -// assert!(matches!( -// load_existing_login_at_file(&wallet, &id1, &password), -// Err(BackendError::WalletFileNotFound), -// )); -// } - -// #[test] -// fn remove_all_accounts_for_a_login_removes_that_login() { -// let store_dir = tempdir().unwrap(); -// let wallet = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let account2 = Mnemonic::generate(24).unwrap(); -// let account3 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let id1 = LoginId::new("first".to_string()); -// let id2 = AccountId::new("second".to_string()); -// let id3 = LoginId::new("third".to_string()); - -// store_login_with_multiple_accounts_at_file( -// &wallet, -// account1, -// hd_path.clone(), -// id1.clone(), -// &password, -// ) -// .unwrap(); - -// append_account_to_login_at_file( -// &wallet, -// account2, -// hd_path.clone(), -// id1.clone(), -// id2.clone(), -// &password, -// ) -// .unwrap(); - -// store_login_with_multiple_accounts_at_file( -// &wallet, -// account3.clone(), -// hd_path.clone(), -// id3.clone(), -// &password, -// ) -// .unwrap(); - -// remove_account_from_login_at_file( -// &wallet, -// &id1, -// &DEFAULT_FIRST_ACCOUNT_NAME.into(), -// &password, -// ) -// .unwrap(); -// remove_account_from_login_at_file(&wallet, &id1, &id2, &password).unwrap(); - -// // And trying to load when the file is gone fails -// assert!(matches!( -// load_existing_login_at_file(&wallet, &id1, &password), -// Err(BackendError::WalletNoSuchLoginId), -// )); - -// // The other login is still there -// let loaded_login = load_existing_login_at_file(&wallet, &id3, &password).unwrap(); -// let acc3 = loaded_login.as_multiple_accounts().unwrap(); -// let expected = vec![WalletAccount::new( -// DEFAULT_FIRST_ACCOUNT_NAME.into(), -// MnemonicAccount::new(account3, hd_path), -// )] -// .into(); -// assert_eq!(acc3, &expected); -// } - -// #[test] -// fn append_accounts_and_remove_appended_accounts() { -// let store_dir = tempdir().unwrap(); -// let wallet = store_dir.path().join(WALLET_INFO_FILENAME); -// let acc1 = Mnemonic::generate(24).unwrap(); -// let acc2 = Mnemonic::generate(24).unwrap(); -// let acc3 = Mnemonic::generate(24).unwrap(); -// let acc4 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let id1 = LoginId::new("first".to_string()); -// let id2 = LoginId::new("second".to_string()); -// let id3 = AccountId::new("third".to_string()); -// let id4 = AccountId::new("fourth".to_string()); - -// store_login_at_file( -// &wallet, -// acc1.clone(), -// hd_path.clone(), -// id1.clone(), -// &password, -// ) -// .unwrap(); - -// store_login_at_file( -// &wallet, -// acc2.clone(), -// hd_path.clone(), -// id2.clone(), -// &password, -// ) -// .unwrap(); - -// // Add a third and fourth mnenonic grouped together with the second one -// append_account_to_login_at_file( -// &wallet, -// acc3, -// hd_path.clone(), -// id2.clone(), -// id3.clone(), -// &password, -// ) -// .unwrap(); -// append_account_to_login_at_file( -// &wallet, -// acc4.clone(), -// hd_path.clone(), -// id2.clone(), -// id4.clone(), -// &password, -// ) -// .unwrap(); - -// // Delete the third mnemonic, from the second login entry -// remove_account_from_login_at_file(&wallet, &id2, &id3, &password).unwrap(); - -// // Check that we can still load the other accounts -// let loaded_login = load_existing_login_at_file(&wallet, &id2, &password).unwrap(); -// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); -// let expected = vec![ -// WalletAccount::new( -// id2.clone().into(), -// MnemonicAccount::new(acc2, hd_path.clone()), -// ), -// WalletAccount::new(id4.clone(), MnemonicAccount::new(acc4, hd_path.clone())), -// ] -// .into(); -// assert_eq!(loaded_accounts, &expected); - -// // Delete the second and fourth mnemonic from the second login entry removes the login entry -// remove_account_from_login_at_file(&wallet, &id2, &id2.clone().into(), &password).unwrap(); -// remove_account_from_login_at_file(&wallet, &id2, &id4, &password).unwrap(); -// assert!(matches!( -// load_existing_login_at_file(&wallet, &id2, &password), -// Err(BackendError::WalletNoSuchLoginId), -// )); - -// // The first login is still available -// let loaded_login = load_existing_login_at_file(&wallet, &id1, &password).unwrap(); -// let account = loaded_login.as_mnemonic_account().unwrap(); -// assert_eq!(account.mnemonic(), &acc1); -// assert_eq!(account.hd_path(), &hd_path); -// } - -// #[test] -// fn rename_first_account_in_login() { -// let store_dir = tempdir().unwrap(); -// let wallet = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let login_id = LoginId::new("first".to_string()); - -// store_login_with_multiple_accounts_at_file( -// &wallet, -// account1.clone(), -// hd_path.clone(), -// login_id.clone(), -// &password, -// ) -// .unwrap(); - -// let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); -// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); -// let expected = vec![WalletAccount::new( -// DEFAULT_FIRST_ACCOUNT_NAME.into(), -// MnemonicAccount::new(account1.clone(), hd_path.clone()), -// )] -// .into(); -// assert_eq!(loaded_accounts, &expected); - -// let renamed_account = AccountId::new("new_first".to_string()); - -// rename_account_in_login_at_file( -// &wallet, -// &login_id, -// &DEFAULT_FIRST_ACCOUNT_NAME.into(), -// &renamed_account, -// &password, -// ) -// .unwrap(); - -// let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); -// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); -// let expected = vec![WalletAccount::new( -// renamed_account, -// MnemonicAccount::new(account1, hd_path), -// )] -// .into(); -// assert_eq!(loaded_accounts, &expected); -// } - -// #[test] -// fn rename_one_account_in_login_with_two_accounts() { -// let store_dir = tempdir().unwrap(); -// let wallet = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let account2 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let login_id = LoginId::new("first".to_string()); -// let account_id2 = AccountId::new("second".to_string()); -// let renamed_account_id2 = AccountId::new("new_second".to_string()); - -// store_login_with_multiple_accounts_at_file( -// &wallet, -// account1.clone(), -// hd_path.clone(), -// login_id.clone(), -// &password, -// ) -// .unwrap(); - -// append_account_to_login_at_file( -// &wallet, -// account2.clone(), -// hd_path.clone(), -// login_id.clone(), -// account_id2.clone(), -// &password, -// ) -// .unwrap(); - -// // Load and confirm -// let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); -// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); -// let expected = vec![ -// WalletAccount::new( -// DEFAULT_FIRST_ACCOUNT_NAME.into(), -// MnemonicAccount::new(account1.clone(), hd_path.clone()), -// ), -// WalletAccount::new( -// account_id2.clone(), -// MnemonicAccount::new(account2.clone(), hd_path.clone()), -// ), -// ] -// .into(); -// assert_eq!(loaded_accounts, &expected); - -// // Rename the second account to a new name -// rename_account_in_login_at_file( -// &wallet, -// &login_id, -// &account_id2, -// &renamed_account_id2, -// &password, -// ) -// .unwrap(); - -// // Load and confirm -// let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); -// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); -// let expected = vec![ -// WalletAccount::new( -// DEFAULT_FIRST_ACCOUNT_NAME.into(), -// MnemonicAccount::new(account1, hd_path.clone()), -// ), -// WalletAccount::new(renamed_account_id2, MnemonicAccount::new(account2, hd_path)), -// ] -// .into(); -// assert_eq!(loaded_accounts, &expected); -// } - -// #[test] -// fn rename_account_into_existing_account_id_fails() { -// let store_dir = tempdir().unwrap(); -// let wallet = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let account2 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let login_id = LoginId::new("first".to_string()); -// let account_id2 = AccountId::new("second".to_string()); -// let renamed_account_id2 = DEFAULT_FIRST_ACCOUNT_NAME.into(); - -// store_login_with_multiple_accounts_at_file( -// &wallet, -// account1.clone(), -// hd_path.clone(), -// login_id.clone(), -// &password, -// ) -// .unwrap(); - -// append_account_to_login_at_file( -// &wallet, -// account2.clone(), -// hd_path.clone(), -// login_id.clone(), -// account_id2.clone(), -// &password, -// ) -// .unwrap(); - -// // Load and confirm -// let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); -// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); -// let expected = vec![ -// WalletAccount::new( -// DEFAULT_FIRST_ACCOUNT_NAME.into(), -// MnemonicAccount::new(account1.clone(), hd_path.clone()), -// ), -// WalletAccount::new( -// account_id2.clone(), -// MnemonicAccount::new(account2.clone(), hd_path.clone()), -// ), -// ] -// .into(); -// assert_eq!(loaded_accounts, &expected); - -// // Rename the second account to the name of the first one fails -// assert!(matches!( -// rename_account_in_login_at_file( -// &wallet, -// &login_id, -// &account_id2, -// &renamed_account_id2, -// &password, -// ), -// Err(BackendError::WalletAccountIdAlreadyExistsInWalletLogin) -// )); - -// // Load and confirm nothing was changed -// let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); -// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); -// let expected = vec![ -// WalletAccount::new( -// DEFAULT_FIRST_ACCOUNT_NAME.into(), -// MnemonicAccount::new(account1, hd_path.clone()), -// ), -// WalletAccount::new(account_id2, MnemonicAccount::new(account2, hd_path)), -// ] -// .into(); -// assert_eq!(loaded_accounts, &expected); -// } - -// // Test to that decrypts a stored file from the repo, to make sure we are able to decrypt stored -// // wallets created with older versions. -// #[test] -// fn decrypt_stored_wallet() { -// const SAVED_WALLET: &str = "src/wallet_storage/test-data/saved-wallet.json"; -// let wallet_file = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(SAVED_WALLET); - -// let wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); - -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let bad_password = UserPassword::new("bad-password".to_string()); -// let id1 = LoginId::new("first".to_string()); -// let id2 = LoginId::new("second".to_string()); - -// assert!(!wallet.password_can_decrypt_all(&bad_password)); -// assert!(wallet.password_can_decrypt_all(&password)); - -// let acc1 = wallet.decrypt_login(&id1, &password).unwrap(); -// let acc2 = wallet.decrypt_login(&id2, &password).unwrap(); - -// assert!(matches!(acc1, StoredLogin::Mnemonic(_))); -// assert!(matches!(acc2, StoredLogin::Mnemonic(_))); - -// let expected_acc1 = bip39::Mnemonic::from_str("country mean universe text phone begin deputy reject result good cram illness common cluster proud swamp digital patrol spread bar face december base kick").unwrap(); -// let expected_acc2 = bip39::Mnemonic::from_str("home mansion start quiz dress decide hint second dragon sunny juice always steak real minimum art rival skin draw total pulp foot goddess agent").unwrap(); - -// assert_eq!( -// acc1.as_mnemonic_account().unwrap().mnemonic(), -// &expected_acc1 -// ); -// assert_eq!(acc1.as_mnemonic_account().unwrap().hd_path(), &hd_path,); - -// assert_eq!( -// acc2.as_mnemonic_account().unwrap().mnemonic(), -// &expected_acc2 -// ); -// assert_eq!(acc2.as_mnemonic_account().unwrap().hd_path(), &hd_path,); -// } - -// #[test] -// fn decrypt_stored_wallet_1_0_4() { -// const SAVED_WALLET: &str = "src/wallet_storage/test-data/saved-wallet-1.0.4.json"; -// let wallet_file = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(SAVED_WALLET); - -// let wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); - -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password11!".to_string()); -// let bad_password = UserPassword::new("bad-password".to_string()); -// let login_id = LoginId::new("default".to_string()); - -// assert!(!wallet.password_can_decrypt_all(&bad_password)); -// assert!(wallet.password_can_decrypt_all(&password)); - -// let acc1 = wallet.decrypt_login(&login_id, &password).unwrap(); - -// assert!(matches!(acc1, StoredLogin::Mnemonic(_))); - -// let expected_acc1 = bip39::Mnemonic::from_str("arrow capable abstract industry elevator nominee december piece hotel feed lounge web faint sword veteran bundle hour page actual laptop horror gold test warrior").unwrap(); - -// assert_eq!( -// acc1.as_mnemonic_account().unwrap().mnemonic(), -// &expected_acc1 -// ); -// assert_eq!(acc1.as_mnemonic_account().unwrap().hd_path(), &hd_path,); -// } - -// #[test] -// fn decrypt_stored_wallet_1_0_5_with_multiple_accounts() { -// const SAVED_WALLET: &str = "src/wallet_storage/test-data/saved-wallet-1.0.5.json"; -// let wallet_file = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(SAVED_WALLET); - -// let wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); - -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password11!".to_string()); -// let bad_password = UserPassword::new("bad-password".to_string()); -// let login_id = LoginId::new("default".to_string()); - -// assert!(!wallet.password_can_decrypt_all(&bad_password)); -// assert!(wallet.password_can_decrypt_all(&password)); - -// let login = wallet.decrypt_login(&login_id, &password).unwrap(); - -// assert!(matches!(login, StoredLogin::Multiple(_))); - -// let login = login.as_multiple_accounts().unwrap(); -// assert_eq!(login.len(), 4); - -// let expected_mn1 = bip39::Mnemonic::from_str("arrow capable abstract industry elevator nominee december piece hotel feed lounge web faint sword veteran bundle hour page actual laptop horror gold test warrior").unwrap(); -// let expected_mn2 = bip39::Mnemonic::from_str("border hurt skull lunar goddess second danger game dismiss exhaust oven thumb dog drama onion false orchard spice tent next predict invite cherry green").unwrap(); -// let expected_mn3 = bip39::Mnemonic::from_str("gentle crowd rule snap girl urge flat jump winner cluster night sand museum stock grunt quick tree acquire traffic major awake tag rack peasant").unwrap(); -// let expected_mn4 = bip39::Mnemonic::from_str("debris blue skin annual inhale text border rigid spatial lesson coconut yard horn crystal control survey version vote hawk neck frame arrive oblige width").unwrap(); - -// let expected = vec![ -// WalletAccount::new( -// "default".into(), -// MnemonicAccount::new(expected_mn1, hd_path.clone()), -// ), -// WalletAccount::new( -// "account2".into(), -// MnemonicAccount::new(expected_mn2, hd_path.clone()), -// ), -// WalletAccount::new( -// "foobar".into(), -// MnemonicAccount::new(expected_mn3, hd_path.clone()), -// ), -// WalletAccount::new("42".into(), MnemonicAccount::new(expected_mn4, hd_path)), -// ] -// .into(); - -// assert_eq!(login, &expected); -// } - -// #[test] -// fn append_filename() { -// let wallet_file = PathBuf::from("/tmp/saved-wallet.json"); -// let timestamp = OsString::from("42"); -// #[cfg(target_family = "unix")] -// assert_eq!( -// append_timestamp_to_filename(wallet_file.clone(), timestamp.clone(), None) -// .unwrap() -// .into_os_string() -// .into_string() -// .unwrap(), -// "/tmp/saved-wallet-42.json".to_string(), -// ); -// #[cfg(not(target_family = "unix"))] -// assert_eq!( -// append_timestamp_to_filename(wallet_file.clone(), timestamp.clone(), None) -// .unwrap() -// .into_os_string() -// .into_string() -// .unwrap(), -// r"/tmp\saved-wallet-42.json".to_string(), -// ); - -// #[cfg(target_family = "unix")] -// assert_eq!( -// append_timestamp_to_filename(wallet_file, timestamp, Some(3)) -// .unwrap() -// .into_os_string() -// .into_string() -// .unwrap(), -// "/tmp/saved-wallet-42-3.json".to_string(), -// ); -// #[cfg(not(target_family = "unix"))] -// assert_eq!( -// append_timestamp_to_filename(wallet_file, timestamp, Some(3)) -// .unwrap() -// .into_os_string() -// .into_string() -// .unwrap(), -// r"/tmp\saved-wallet-42-3.json".to_string(), -// ); -// } -// } +#[cfg(test)] +mod tests { + use crate::wallet_storage::account_data::WalletAccount; + + use super::*; + use nym_config::defaults::COSMOS_DERIVATION_PATH; + use std::str::FromStr; + use tempfile::tempdir; + + #[test] + fn trying_to_load_nonexistant_wallet_fails() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let id1 = LoginId::new("first".to_string()); + let password = UserPassword::new("password".to_string()); + + assert!(matches!( + load_existing_wallet_at_file(&wallet_file), + Err(BackendError::WalletFileNotFound), + )); + assert!(matches!( + load_existing_login_at_file(&wallet_file, &id1, &password), + Err(BackendError::WalletFileNotFound), + )); + remove_login_at_file(&wallet_file, &id1).unwrap_err(); + } + + #[test] + fn store_single_login() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let id1 = LoginId::new("first".to_string()); + + store_login_at_file(&wallet_file, account1, hd_path, id1.clone(), &password).unwrap(); + + let stored_wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); + assert_eq!(stored_wallet.len(), 1); + + let login = stored_wallet.get_encrypted_login_by_index(0).unwrap(); + assert_eq!(login.id, id1); + + // some actual ciphertext was saved + assert!(!login.account.ciphertext().is_empty()); + } + + #[test] + fn store_single_login_with_multi() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let id1 = LoginId::new("first".to_string()); + + store_login_with_multiple_accounts_at_file( + &wallet_file, + account1, + cosmos_hd_path, + id1.clone(), + &password, + ) + .unwrap(); + + let stored_wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); + assert_eq!(stored_wallet.len(), 1); + + let login = stored_wallet.get_encrypted_login_by_index(0).unwrap(); + assert_eq!(login.id, id1); + + // some actual ciphertext was saved + assert!(!login.account.ciphertext().is_empty()); + } + + #[test] + fn store_single_login_with_multi_then_update_pwd_and_load() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let new_password = UserPassword::new("new_password".to_string()); + let id1 = LoginId::new("first".to_string()); + + store_login_with_multiple_accounts_at_file( + &wallet_file, + account1, + cosmos_hd_path, + id1.clone(), + &password, + ) + .unwrap(); + + update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); + + let stored_wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); + assert_eq!(stored_wallet.len(), 1); + + let login = stored_wallet.get_encrypted_login_by_index(0).unwrap(); + assert_eq!(login.id, id1); + + // some actual ciphertext was saved + assert!(!login.account.ciphertext().is_empty()); + } + + #[test] + fn store_twice_for_the_same_id_fails() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let id1 = LoginId::new("first".to_string()); + + // Store the first login + store_login_at_file( + &wallet_file, + account1.clone(), + hd_path.clone(), + id1.clone(), + &password, + ) + .unwrap(); + + // and storing the same id again fails + assert!(matches!( + store_login_at_file(&wallet_file, account1, hd_path, id1, &password,), + Err(BackendError::WalletLoginIdAlreadyExists), + )); + } + + #[test] + fn store_twice_for_the_same_id_fails_with_multiple() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let id1 = LoginId::new("first".to_string()); + + // Store the first login + store_login_with_multiple_accounts_at_file( + &wallet_file, + account1.clone(), + hd_path.clone(), + id1.clone(), + &password, + ) + .unwrap(); + + // and storing the same id again fails + assert!(matches!( + store_login_with_multiple_accounts_at_file( + &wallet_file, + account1, + hd_path, + id1, + &password, + ), + Err(BackendError::WalletLoginIdAlreadyExists), + )); + } + + #[test] + fn load_with_wrong_password_fails() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let bad_password = UserPassword::new("bad-password".to_string()); + let id1 = LoginId::new("first".to_string()); + + store_login_at_file(&wallet_file, account1, hd_path, id1.clone(), &password).unwrap(); + + // Trying to load it with wrong password now fails + assert!(matches!( + load_existing_login_at_file(&wallet_file, &id1, &bad_password), + Err(BackendError::StoreCipherError { .. }), + )); + } + + #[test] + fn load_with_wrong_password_fails_with_multi() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let bad_password = UserPassword::new("bad-password".to_string()); + let id1 = LoginId::new("first".to_string()); + + store_login_with_multiple_accounts_at_file( + &wallet_file, + account1, + hd_path, + id1.clone(), + &password, + ) + .unwrap(); + + // Trying to load it with wrong password now fails + assert!(matches!( + load_existing_login_at_file(&wallet_file, &id1, &bad_password), + Err(BackendError::StoreCipherError { .. }), + )); + } + + #[test] + fn load_with_wrong_id_fails() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let id1 = LoginId::new("first".to_string()); + let id2 = LoginId::new("second".to_string()); + + store_login_at_file(&wallet_file, account1, hd_path, id1, &password).unwrap(); + + // Trying to load with the wrong id + assert!(matches!( + load_existing_login_at_file(&wallet_file, &id2, &password), + Err(BackendError::WalletNoSuchLoginId), + )); + } + + #[test] + fn load_with_wrong_id_fails_with_multi() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let id1 = LoginId::new("first".to_string()); + let id2 = LoginId::new("second".to_string()); + + store_login_with_multiple_accounts_at_file(&wallet_file, account1, hd_path, id1, &password) + .unwrap(); + + // Trying to load with the wrong id + assert!(matches!( + load_existing_login_at_file(&wallet_file, &id2, &password), + Err(BackendError::WalletNoSuchLoginId), + )); + } + + #[test] + fn store_and_load_a_single_login() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let id1 = LoginId::new("first".to_string()); + + store_login_at_file( + &wallet_file, + account1.clone(), + hd_path.clone(), + id1.clone(), + &password, + ) + .unwrap(); + + let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); + let acc = loaded_login.as_mnemonic_account().unwrap(); + assert_eq!(&account1, acc.mnemonic()); + assert_eq!(&hd_path, acc.hd_path()); + } + + #[test] + fn store_a_single_login_then_update_pwd_and_load() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let new_password = UserPassword::new("new_password".to_string()); + let id1 = LoginId::new("first".to_string()); + + store_login_at_file( + &wallet_file, + account1.clone(), + hd_path.clone(), + id1.clone(), + &password, + ) + .unwrap(); + + update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); + + let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &new_password).unwrap(); + let acc = loaded_login.as_mnemonic_account().unwrap(); + assert_eq!(&account1, acc.mnemonic()); + assert_eq!(&hd_path, acc.hd_path()); + } + + #[test] + fn store_a_single_login_then_update_pwd_with_identical_pwd_is_noop_but_okay() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let id1 = LoginId::new("first".to_string()); + + store_login_at_file( + &wallet_file, + account1.clone(), + hd_path.clone(), + id1.clone(), + &password, + ) + .unwrap(); + + update_encrypted_logins_at_file(&wallet_file, &password, &password).unwrap(); + + let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); + let acc = loaded_login.as_mnemonic_account().unwrap(); + assert_eq!(&account1, acc.mnemonic()); + assert_eq!(&hd_path, acc.hd_path()); + } + + #[test] + fn store_a_single_login_then_update_pwd_with_wrong_current_pwd_fails() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let new_password = UserPassword::new("new_password".to_string()); + let wrong_password = UserPassword::new("wrong_password".to_string()); + let id1 = LoginId::new("first".to_string()); + + store_login_at_file(&wallet_file, account1, hd_path, id1, &password).unwrap(); + + let err = update_encrypted_logins_at_file(&wallet_file, &wrong_password, &new_password) + .unwrap_err(); + assert!(matches!(err, BackendError::StoreCipherError { .. })); + } + + #[test] + fn store_a_single_login_then_update_pwd_and_load_with_wrong_pwd_fails() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let new_password = UserPassword::new("new_password".to_string()); + let id1 = LoginId::new("first".to_string()); + + store_login_at_file(&wallet_file, account1, hd_path, id1.clone(), &password).unwrap(); + + update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); + + let err = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap_err(); + assert!(matches!(err, BackendError::StoreCipherError { .. })); + } + + #[test] + fn store_and_load_a_single_login_with_multi() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let acc1 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let id1 = LoginId::new("first".to_string()); + + store_login_with_multiple_accounts_at_file( + &wallet_file, + acc1.clone(), + hd_path.clone(), + id1.clone(), + &password, + ) + .unwrap(); + + let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); + let accounts = loaded_login.as_multiple_accounts().unwrap(); + assert_eq!(accounts.len(), 1); + let account = accounts + .get_account(&DEFAULT_FIRST_ACCOUNT_NAME.into()) + .unwrap(); + assert_eq!(account.id().as_ref(), DEFAULT_FIRST_ACCOUNT_NAME); + assert_eq!(account.mnemonic(), &acc1); + assert_eq!(account.hd_path(), &hd_path); + } + + #[test] + fn store_a_single_login_with_multi_then_update_pwd_and_load() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let acc1 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let new_password = UserPassword::new("new_password".to_string()); + let id1 = LoginId::new("first".to_string()); + + store_login_with_multiple_accounts_at_file( + &wallet_file, + acc1.clone(), + hd_path.clone(), + id1.clone(), + &password, + ) + .unwrap(); + + update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); + + let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &new_password).unwrap(); + let accounts = loaded_login.as_multiple_accounts().unwrap(); + assert_eq!(accounts.len(), 1); + let account = accounts + .get_account(&DEFAULT_FIRST_ACCOUNT_NAME.into()) + .unwrap(); + assert_eq!(account.id().as_ref(), DEFAULT_FIRST_ACCOUNT_NAME); + assert_eq!(account.mnemonic(), &acc1); + assert_eq!(account.hd_path(), &hd_path); + } + + #[test] + fn store_a_single_login_with_multi_then_update_pwd_with_wrong_current_pwd_fails() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let acc1 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let new_password = UserPassword::new("new_password".to_string()); + let wrong_password = UserPassword::new("wrong_password".to_string()); + let id1 = LoginId::new("first".to_string()); + + store_login_with_multiple_accounts_at_file(&wallet_file, acc1, hd_path, id1, &password) + .unwrap(); + + let err = update_encrypted_logins_at_file(&wallet_file, &wrong_password, &new_password) + .unwrap_err(); + assert!(matches!(err, BackendError::StoreCipherError { .. })); + } + + #[test] + fn store_a_single_login_with_multi_then_update_pwd_and_load_with_wrong_pwd_fails() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let acc1 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let new_password = UserPassword::new("new_password".to_string()); + let id1 = LoginId::new("first".to_string()); + + store_login_with_multiple_accounts_at_file( + &wallet_file, + acc1, + hd_path, + id1.clone(), + &password, + ) + .unwrap(); + + update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); + + let err = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap_err(); + assert!(matches!(err, BackendError::StoreCipherError { .. })); + } + + #[test] + fn store_a_second_login_with_a_different_password_fails() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let account2 = Mnemonic::generate(24).unwrap(); + let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let bad_password = UserPassword::new("bad-password".to_string()); + let id1 = LoginId::new("first".to_string()); + let id2 = LoginId::new("second".to_string()); + + store_login_at_file( + &wallet_file, + account1, + cosmos_hd_path.clone(), + id1, + &password, + ) + .unwrap(); + + // Can't store a second login if you use different password + assert!(matches!( + store_login_at_file(&wallet_file, account2, cosmos_hd_path, id2, &bad_password), + Err(BackendError::WalletDifferentPasswordDetected), + )); + } + + #[test] + fn store_a_second_login_with_a_different_password_fails_with_multi() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let account2 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let bad_password = UserPassword::new("bad-password".to_string()); + let id1 = LoginId::new("first".to_string()); + let id2 = LoginId::new("second".to_string()); + + store_login_with_multiple_accounts_at_file( + &wallet_file, + account1, + hd_path.clone(), + id1, + &password, + ) + .unwrap(); + + // Can't store a second login if you use different password + assert!(matches!( + store_login_with_multiple_accounts_at_file( + &wallet_file, + account2, + hd_path, + id2, + &bad_password + ), + Err(BackendError::WalletDifferentPasswordDetected), + )); + } + + #[test] + fn store_two_mnemonic_accounts_gives_different_salts_and_iv() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let account2 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let different_hd_path: DerivationPath = "m".parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let id1 = LoginId::new("first".to_string()); + let id2 = LoginId::new("second".to_string()); + + // Store the first account + store_login_at_file(&wallet_file, account1, hd_path, id1, &password).unwrap(); + + let stored_wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); + let encrypted_blob = &stored_wallet + .get_encrypted_login_by_index(0) + .unwrap() + .account; + + // keep track of salt and iv for future assertion + let original_iv = encrypted_blob.iv().to_vec(); + let original_salt = encrypted_blob.salt().to_vec(); + + // Add an extra account + store_login_at_file(&wallet_file, account2, different_hd_path, id2, &password).unwrap(); + + let loaded_accounts = load_existing_wallet_at_file(&wallet_file).unwrap(); + assert_eq!(loaded_accounts.len(), 2); + let encrypted_blob = &loaded_accounts + .get_encrypted_login_by_index(1) + .unwrap() + .account; + + // fresh IV and salt are used + assert_ne!(original_iv, encrypted_blob.iv()); + assert_ne!(original_salt, encrypted_blob.salt()); + } + + #[test] + fn store_two_mnemonic_accounts_using_two_logins() { + let store_dir = tempdir().unwrap(); + let wallet = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let account2 = Mnemonic::generate(24).unwrap(); + let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let different_hd_path: DerivationPath = "m".parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let id1 = LoginId::new("first".to_string()); + let id2 = LoginId::new("second".to_string()); + + // Store the first account + store_login_at_file( + &wallet, + account1.clone(), + cosmos_hd_path.clone(), + id1.clone(), + &password, + ) + .unwrap(); + + let login = load_existing_login_at_file(&wallet, &id1, &password).unwrap(); + let acc = login.as_mnemonic_account().unwrap(); + assert_eq!(&account1, acc.mnemonic()); + assert_eq!(&cosmos_hd_path, acc.hd_path()); + + // Add an extra account + store_login_at_file( + &wallet, + account2.clone(), + different_hd_path.clone(), + id2.clone(), + &password, + ) + .unwrap(); + + // first account should be unchanged + let loaded_login = load_existing_login_at_file(&wallet, &id1, &password).unwrap(); + let acc1 = loaded_login.as_mnemonic_account().unwrap(); + assert_eq!(&account1, acc1.mnemonic()); + assert_eq!(&cosmos_hd_path, acc1.hd_path()); + + let loaded_login = load_existing_login_at_file(&wallet, &id2, &password).unwrap(); + let acc2 = loaded_login.as_mnemonic_account().unwrap(); + assert_eq!(&account2, acc2.mnemonic()); + assert_eq!(&different_hd_path, acc2.hd_path()); + } + + #[test] + fn store_two_mnemonic_accounts_using_two_logins_then_update_pwd_and_load() { + let store_dir = tempdir().unwrap(); + let wallet = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let account2 = Mnemonic::generate(24).unwrap(); + let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let different_hd_path: DerivationPath = "m".parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let new_password = UserPassword::new("new_password".to_string()); + let id1 = LoginId::new("first".to_string()); + let id2 = LoginId::new("second".to_string()); + + // Store the first login with an account + store_login_at_file( + &wallet, + account1.clone(), + cosmos_hd_path.clone(), + id1.clone(), + &password, + ) + .unwrap(); + + let login = load_existing_login_at_file(&wallet, &id1, &password).unwrap(); + let acc = login.as_mnemonic_account().unwrap(); + assert_eq!(&account1, acc.mnemonic()); + assert_eq!(&cosmos_hd_path, acc.hd_path()); + + // Store a second login, also with an account + store_login_at_file( + &wallet, + account2.clone(), + different_hd_path.clone(), + id2.clone(), + &password, + ) + .unwrap(); + + update_encrypted_logins_at_file(&wallet, &password, &new_password).unwrap(); + + // first account should be unchanged + let loaded_login = load_existing_login_at_file(&wallet, &id1, &new_password).unwrap(); + let acc1 = loaded_login.as_mnemonic_account().unwrap(); + assert_eq!(&account1, acc1.mnemonic()); + assert_eq!(&cosmos_hd_path, acc1.hd_path()); + + let loaded_login = load_existing_login_at_file(&wallet, &id2, &new_password).unwrap(); + let acc2 = loaded_login.as_mnemonic_account().unwrap(); + assert_eq!(&account2, acc2.mnemonic()); + assert_eq!(&different_hd_path, acc2.hd_path()); + } + + #[test] + fn store_one_mnemonic_account_and_one_multi_account() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let account2 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let different_hd_path: DerivationPath = "m".parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let id1 = LoginId::new("first".to_string()); + let id2 = LoginId::new("second".to_string()); + + // Store the first account + store_login_at_file( + &wallet_file, + account1.clone(), + hd_path.clone(), + id1.clone(), + &password, + ) + .unwrap(); + + let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); + let acc = loaded_login.as_mnemonic_account().unwrap(); + assert_eq!(&account1, acc.mnemonic()); + assert_eq!(&hd_path, acc.hd_path()); + + // Add an extra account + store_login_with_multiple_accounts_at_file( + &wallet_file, + account2.clone(), + different_hd_path.clone(), + id2.clone(), + &password, + ) + .unwrap(); + + // first account should be unchanged + let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); + let acc1 = loaded_login.as_mnemonic_account().unwrap(); + assert_eq!(&account1, acc1.mnemonic()); + assert_eq!(&hd_path, acc1.hd_path()); + + let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); + let acc2 = loaded_login.as_multiple_accounts().unwrap(); + assert_eq!(acc2.len(), 1); + let account = acc2 + .get_account(&DEFAULT_FIRST_ACCOUNT_NAME.into()) + .unwrap(); + assert_eq!(account.id().as_ref(), DEFAULT_FIRST_ACCOUNT_NAME); + assert_eq!(account.mnemonic(), &account2); + assert_eq!(account.hd_path(), &different_hd_path); + } + + #[test] + fn remove_non_existent_id_fails() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let id1 = LoginId::new("first".to_string()); + let id2 = LoginId::new("second".to_string()); + + store_login_with_multiple_accounts_at_file(&wallet_file, account1, hd_path, id1, &password) + .unwrap(); + + // Fails to delete non-existent id in the wallet + assert!(matches!( + remove_login_at_file(&wallet_file, &id2), + Err(BackendError::WalletNoSuchLoginId), + )); + } + + #[test] + fn store_and_remove_wallet_login_information() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let account2 = Mnemonic::generate(24).unwrap(); + let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let different_hd_path: DerivationPath = "m".parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let id1 = LoginId::new("first".to_string()); + let id2 = LoginId::new("second".to_string()); + + // Store two accounts with two different passwords + store_login_at_file( + &wallet_file, + account1.clone(), + cosmos_hd_path.clone(), + id1.clone(), + &password, + ) + .unwrap(); + store_login_at_file( + &wallet_file, + account2.clone(), + different_hd_path.clone(), + id2.clone(), + &password, + ) + .unwrap(); + + // Load and compare + let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); + let acc1 = loaded_login.as_mnemonic_account().unwrap(); + assert_eq!(&account1, acc1.mnemonic()); + assert_eq!(&cosmos_hd_path, acc1.hd_path()); + + let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); + let acc2 = loaded_login.as_mnemonic_account().unwrap(); + assert_eq!(&account2, acc2.mnemonic()); + assert_eq!(&different_hd_path, acc2.hd_path()); + + // Delete the second account + remove_login_at_file(&wallet_file, &id2).unwrap(); + + // The first account should be unchanged + let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); + let acc1 = loaded_login.as_mnemonic_account().unwrap(); + assert_eq!(&account1, acc1.mnemonic()); + assert_eq!(&cosmos_hd_path, acc1.hd_path()); + + // And we can't load the second one anymore + assert!(matches!( + load_existing_login_at_file(&wallet_file, &id2, &password), + Err(BackendError::WalletNoSuchLoginId), + )); + + // Delete the first account + assert!(wallet_file.exists()); + remove_login_at_file(&wallet_file, &id1).unwrap(); + + // The file should now be removed + assert!(!wallet_file.exists()); + + // And trying to load when the file is gone fails + assert!(matches!( + load_existing_login_at_file(&wallet_file, &id1, &password), + Err(BackendError::WalletFileNotFound), + )); + } + + #[test] + fn append_account_converts_the_type() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let account2 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let id1 = LoginId::new("first".to_string()); + let id2 = AccountId::new("second".to_string()); + + store_login_at_file( + &wallet_file, + account1.clone(), + hd_path.clone(), + id1.clone(), + &password, + ) + .unwrap(); + + // Check that it's there as the correct non-multiple type + let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); + let acc = loaded_login.as_mnemonic_account().unwrap(); + assert_eq!(acc.mnemonic(), &account1); + assert_eq!(acc.hd_path(), &hd_path); + + append_account_to_login_at_file( + &wallet_file, + account2.clone(), + hd_path.clone(), + id1.clone(), + id2.clone(), + &password, + ) + .unwrap(); + + // Check that it is now multiple mnemonic type + let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); + let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); + let expected = vec![ + WalletAccount::new(id1.into(), MnemonicAccount::new(account1, hd_path.clone())), + WalletAccount::new(id2, MnemonicAccount::new(account2, hd_path)), + ] + .into(); + assert_eq!(loaded_accounts, &expected); + } + + #[test] + fn append_accounts_to_existing_login() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let account2 = Mnemonic::generate(24).unwrap(); + let account3 = Mnemonic::generate(24).unwrap(); + let account4 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let id1 = LoginId::new("first".to_string()); + let id2 = LoginId::new("second".to_string()); + let id3 = AccountId::new("third".to_string()); + let id4 = AccountId::new("fourth".to_string()); + + store_login_at_file( + &wallet_file, + account1.clone(), + hd_path.clone(), + id1.clone(), + &password, + ) + .unwrap(); + + store_login_at_file( + &wallet_file, + account2.clone(), + hd_path.clone(), + id2.clone(), + &password, + ) + .unwrap(); + + // Check that it's there as the correct non-multiple type + let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); + let acc2 = loaded_login.as_mnemonic_account().unwrap(); + assert_eq!(acc2.mnemonic(), &account2); + assert_eq!(acc2.hd_path(), &hd_path); + + // Add a third and fourth mnenonic grouped together with the second one + append_account_to_login_at_file( + &wallet_file, + account3.clone(), + hd_path.clone(), + id2.clone(), + id3.clone(), + &password, + ) + .unwrap(); + append_account_to_login_at_file( + &wallet_file, + account4.clone(), + hd_path.clone(), + id2.clone(), + id4.clone(), + &password, + ) + .unwrap(); + + // Check that we can load all four + let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); + let acc1 = loaded_login.as_mnemonic_account().unwrap(); + assert_eq!(acc1.mnemonic(), &account1); + assert_eq!(acc1.hd_path(), &hd_path); + + let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); + let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); + let expected = vec![ + WalletAccount::new(id2.into(), MnemonicAccount::new(account2, hd_path.clone())), + WalletAccount::new(id3, MnemonicAccount::new(account3, hd_path.clone())), + WalletAccount::new(id4, MnemonicAccount::new(account4, hd_path)), + ] + .into(); + assert_eq!(loaded_accounts, &expected); + } + + #[test] + fn append_accounts_to_existing_login_with_multi() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let account2 = Mnemonic::generate(24).unwrap(); + let account3 = Mnemonic::generate(24).unwrap(); + let account4 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let id1 = LoginId::new("first".to_string()); + let id2 = LoginId::new("second".to_string()); + let id3 = AccountId::new("third".to_string()); + let id4 = AccountId::new("fourth".to_string()); + + store_login_with_multiple_accounts_at_file( + &wallet_file, + account1.clone(), + hd_path.clone(), + id1.clone(), + &password, + ) + .unwrap(); + + store_login_with_multiple_accounts_at_file( + &wallet_file, + account2.clone(), + hd_path.clone(), + id2.clone(), + &password, + ) + .unwrap(); + + // Add a third and fourth mnenonic grouped together with the second one + append_account_to_login_at_file( + &wallet_file, + account3.clone(), + hd_path.clone(), + id2.clone(), + id3.clone(), + &password, + ) + .unwrap(); + append_account_to_login_at_file( + &wallet_file, + account4.clone(), + hd_path.clone(), + id2.clone(), + id4.clone(), + &password, + ) + .unwrap(); + + // Check that we can load all four + let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); + let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); + let expected = vec![WalletAccount::new( + DEFAULT_FIRST_ACCOUNT_NAME.into(), + MnemonicAccount::new(account1, hd_path.clone()), + )] + .into(); + assert_eq!(loaded_accounts, &expected); + + let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); + let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); + let expected = vec![ + WalletAccount::new( + DEFAULT_FIRST_ACCOUNT_NAME.into(), + MnemonicAccount::new(account2, hd_path.clone()), + ), + WalletAccount::new(id3, MnemonicAccount::new(account3, hd_path.clone())), + WalletAccount::new(id4, MnemonicAccount::new(account4, hd_path)), + ] + .into(); + assert_eq!(loaded_accounts, &expected); + } + + #[test] + fn append_account_to_existing_login_with_multi_then_update_pwd_and_load() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let account2 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let new_password = UserPassword::new("new_password".to_string()); + let login_id = LoginId::new("first".to_string()); + let appended_account = AccountId::new("second".to_string()); + + store_login_with_multiple_accounts_at_file( + &wallet_file, + account1.clone(), + hd_path.clone(), + login_id.clone(), + &password, + ) + .unwrap(); + + // Append a second mnenonic to the same login + append_account_to_login_at_file( + &wallet_file, + account2.clone(), + hd_path.clone(), + login_id.clone(), + appended_account.clone(), + &password, + ) + .unwrap(); + + // Update the password + update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); + + // Expect that we can load these 2 accounts with the new password + let loaded_login = + load_existing_login_at_file(&wallet_file, &login_id, &new_password).unwrap(); + let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); + let expected = vec![ + WalletAccount::new( + DEFAULT_FIRST_ACCOUNT_NAME.into(), + MnemonicAccount::new(account1, hd_path.clone()), + ), + WalletAccount::new(appended_account, MnemonicAccount::new(account2, hd_path)), + ] + .into(); + assert_eq!(loaded_accounts, &expected); + + // Expect that trying to load these 2 accounts with the old password fails + let err = load_existing_login_at_file(&wallet_file, &login_id, &password).unwrap_err(); + assert!(matches!(err, BackendError::StoreCipherError { .. })); + } + + #[test] + fn append_the_same_mnemonic_twice_fails() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let id1 = LoginId::new("first".to_string()); + let id2 = AccountId::new("second".to_string()); + + store_login_with_multiple_accounts_at_file( + &wallet_file, + account1.clone(), + hd_path.clone(), + id1.clone(), + &password, + ) + .unwrap(); + + assert!(matches!( + append_account_to_login_at_file(&wallet_file, account1, hd_path, id1, id2, &password), + Err(BackendError::WalletMnemonicAlreadyExistsInWalletLogin), + )) + } + + #[test] + fn append_the_same_account_name_twice_fails() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let mnemonic1 = Mnemonic::generate(24).unwrap(); + let mnemonic2 = Mnemonic::generate(24).unwrap(); + let mnemonic3 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + // The top-level login id. NOTE: the first account id is always set to default. + let login_id = LoginId::new("my_login_id".to_string()); + + // Store the first account under login_id. The first account id is always set to default + // name. + store_login_with_multiple_accounts_at_file( + &wallet_file, + mnemonic1, + hd_path.clone(), + login_id.clone(), + &password, + ) + .unwrap(); + + // Append another account (account2) to the same login (login_id) + let account2 = AccountId::new("account_2".to_string()); + + append_account_to_login_at_file( + &wallet_file, + mnemonic2, + hd_path.clone(), + login_id.clone(), + account2.clone(), + &password, + ) + .unwrap(); + + // Appending the third account, with same account id will fail + assert!(matches!( + append_account_to_login_at_file( + &wallet_file, + mnemonic3, + hd_path, + login_id, + account2, + &password, + ), + Err(BackendError::WalletAccountIdAlreadyExistsInWalletLogin), + )) + } + + #[test] + fn delete_the_same_account_twice_for_a_login_fails() { + let store_dir = tempdir().unwrap(); + let wallet = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let account2 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let id1 = LoginId::new("first".to_string()); + let id2 = AccountId::new("second".to_string()); + + store_login_at_file(&wallet, account1, hd_path.clone(), id1.clone(), &password).unwrap(); + + append_account_to_login_at_file( + &wallet, + account2, + hd_path, + id1.clone(), + id2.clone(), + &password, + ) + .unwrap(); + + remove_account_from_login_at_file(&wallet, &id1, &id2, &password).unwrap(); + + assert!(matches!( + remove_account_from_login_at_file(&wallet, &id1, &id2, &password), + Err(BackendError::WalletNoSuchAccountIdInWalletLogin), + )); + } + + #[test] + fn delete_the_same_account_twice_for_a_login_fails_with_multi() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let account2 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let id1 = LoginId::new("first".to_string()); + let id2 = AccountId::new("second".to_string()); + + store_login_with_multiple_accounts_at_file( + &wallet_file, + account1, + hd_path.clone(), + id1.clone(), + &password, + ) + .unwrap(); + + append_account_to_login_at_file( + &wallet_file, + account2, + hd_path, + id1.clone(), + id2.clone(), + &password, + ) + .unwrap(); + + remove_account_from_login_at_file(&wallet_file, &id1, &id2, &password).unwrap(); + + assert!(matches!( + remove_account_from_login_at_file(&wallet_file, &id1, &id2, &password), + Err(BackendError::WalletNoSuchAccountIdInWalletLogin), + )); + } + + #[test] + fn delete_appended_account_doesnt_affect_others() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let account2 = Mnemonic::generate(24).unwrap(); + let account3 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let id1 = LoginId::new("first".to_string()); + let id2 = LoginId::new("second".to_string()); + let id3 = AccountId::new("third".to_string()); + + store_login_at_file( + &wallet_file, + account1, + hd_path.clone(), + id1.clone(), + &password, + ) + .unwrap(); + + store_login_at_file( + &wallet_file, + account2.clone(), + hd_path.clone(), + id2.clone(), + &password, + ) + .unwrap(); + + append_account_to_login_at_file( + &wallet_file, + account3.clone(), + hd_path.clone(), + id2.clone(), + id3.clone(), + &password, + ) + .unwrap(); + + remove_login_at_file(&wallet_file, &id1).unwrap(); + + // The second login one is still there + let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); + let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); + let expected = vec![ + WalletAccount::new(id2.into(), MnemonicAccount::new(account2, hd_path.clone())), + WalletAccount::new(id3, MnemonicAccount::new(account3, hd_path)), + ] + .into(); + assert_eq!(loaded_accounts, &expected); + } + + #[test] + fn remove_all_accounts_for_a_login_removes_the_file_when_empty() { + let store_dir = tempdir().unwrap(); + let wallet = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let account2 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let id1 = LoginId::new("first".to_string()); + let id2 = AccountId::new("second".to_string()); + + store_login_with_multiple_accounts_at_file( + &wallet, + account1, + hd_path.clone(), + id1.clone(), + &password, + ) + .unwrap(); + + append_account_to_login_at_file( + &wallet, + account2, + hd_path, + id1.clone(), + id2.clone(), + &password, + ) + .unwrap(); + + remove_account_from_login_at_file( + &wallet, + &id1, + &DEFAULT_FIRST_ACCOUNT_NAME.into(), + &password, + ) + .unwrap(); + remove_account_from_login_at_file(&wallet, &id1, &id2, &password).unwrap(); + + // The file should now be removed + assert!(!wallet.exists()); + + // And trying to load when the file is gone fails + assert!(matches!( + load_existing_login_at_file(&wallet, &id1, &password), + Err(BackendError::WalletFileNotFound), + )); + } + + #[test] + fn remove_all_accounts_for_a_login_removes_that_login() { + let store_dir = tempdir().unwrap(); + let wallet = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let account2 = Mnemonic::generate(24).unwrap(); + let account3 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let id1 = LoginId::new("first".to_string()); + let id2 = AccountId::new("second".to_string()); + let id3 = LoginId::new("third".to_string()); + + store_login_with_multiple_accounts_at_file( + &wallet, + account1, + hd_path.clone(), + id1.clone(), + &password, + ) + .unwrap(); + + append_account_to_login_at_file( + &wallet, + account2, + hd_path.clone(), + id1.clone(), + id2.clone(), + &password, + ) + .unwrap(); + + store_login_with_multiple_accounts_at_file( + &wallet, + account3.clone(), + hd_path.clone(), + id3.clone(), + &password, + ) + .unwrap(); + + remove_account_from_login_at_file( + &wallet, + &id1, + &DEFAULT_FIRST_ACCOUNT_NAME.into(), + &password, + ) + .unwrap(); + remove_account_from_login_at_file(&wallet, &id1, &id2, &password).unwrap(); + + // And trying to load when the file is gone fails + assert!(matches!( + load_existing_login_at_file(&wallet, &id1, &password), + Err(BackendError::WalletNoSuchLoginId), + )); + + // The other login is still there + let loaded_login = load_existing_login_at_file(&wallet, &id3, &password).unwrap(); + let acc3 = loaded_login.as_multiple_accounts().unwrap(); + let expected = vec![WalletAccount::new( + DEFAULT_FIRST_ACCOUNT_NAME.into(), + MnemonicAccount::new(account3, hd_path), + )] + .into(); + assert_eq!(acc3, &expected); + } + + #[test] + fn append_accounts_and_remove_appended_accounts() { + let store_dir = tempdir().unwrap(); + let wallet = store_dir.path().join(WALLET_INFO_FILENAME); + let acc1 = Mnemonic::generate(24).unwrap(); + let acc2 = Mnemonic::generate(24).unwrap(); + let acc3 = Mnemonic::generate(24).unwrap(); + let acc4 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let id1 = LoginId::new("first".to_string()); + let id2 = LoginId::new("second".to_string()); + let id3 = AccountId::new("third".to_string()); + let id4 = AccountId::new("fourth".to_string()); + + store_login_at_file( + &wallet, + acc1.clone(), + hd_path.clone(), + id1.clone(), + &password, + ) + .unwrap(); + + store_login_at_file( + &wallet, + acc2.clone(), + hd_path.clone(), + id2.clone(), + &password, + ) + .unwrap(); + + // Add a third and fourth mnenonic grouped together with the second one + append_account_to_login_at_file( + &wallet, + acc3, + hd_path.clone(), + id2.clone(), + id3.clone(), + &password, + ) + .unwrap(); + append_account_to_login_at_file( + &wallet, + acc4.clone(), + hd_path.clone(), + id2.clone(), + id4.clone(), + &password, + ) + .unwrap(); + + // Delete the third mnemonic, from the second login entry + remove_account_from_login_at_file(&wallet, &id2, &id3, &password).unwrap(); + + // Check that we can still load the other accounts + let loaded_login = load_existing_login_at_file(&wallet, &id2, &password).unwrap(); + let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); + let expected = vec![ + WalletAccount::new( + id2.clone().into(), + MnemonicAccount::new(acc2, hd_path.clone()), + ), + WalletAccount::new(id4.clone(), MnemonicAccount::new(acc4, hd_path.clone())), + ] + .into(); + assert_eq!(loaded_accounts, &expected); + + // Delete the second and fourth mnemonic from the second login entry removes the login entry + remove_account_from_login_at_file(&wallet, &id2, &id2.clone().into(), &password).unwrap(); + remove_account_from_login_at_file(&wallet, &id2, &id4, &password).unwrap(); + assert!(matches!( + load_existing_login_at_file(&wallet, &id2, &password), + Err(BackendError::WalletNoSuchLoginId), + )); + + // The first login is still available + let loaded_login = load_existing_login_at_file(&wallet, &id1, &password).unwrap(); + let account = loaded_login.as_mnemonic_account().unwrap(); + assert_eq!(account.mnemonic(), &acc1); + assert_eq!(account.hd_path(), &hd_path); + } + + #[test] + fn rename_first_account_in_login() { + let store_dir = tempdir().unwrap(); + let wallet = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let login_id = LoginId::new("first".to_string()); + + store_login_with_multiple_accounts_at_file( + &wallet, + account1.clone(), + hd_path.clone(), + login_id.clone(), + &password, + ) + .unwrap(); + + let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); + let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); + let expected = vec![WalletAccount::new( + DEFAULT_FIRST_ACCOUNT_NAME.into(), + MnemonicAccount::new(account1.clone(), hd_path.clone()), + )] + .into(); + assert_eq!(loaded_accounts, &expected); + + let renamed_account = AccountId::new("new_first".to_string()); + + rename_account_in_login_at_file( + &wallet, + &login_id, + &DEFAULT_FIRST_ACCOUNT_NAME.into(), + &renamed_account, + &password, + ) + .unwrap(); + + let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); + let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); + let expected = vec![WalletAccount::new( + renamed_account, + MnemonicAccount::new(account1, hd_path), + )] + .into(); + assert_eq!(loaded_accounts, &expected); + } + + #[test] + fn rename_one_account_in_login_with_two_accounts() { + let store_dir = tempdir().unwrap(); + let wallet = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let account2 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let login_id = LoginId::new("first".to_string()); + let account_id2 = AccountId::new("second".to_string()); + let renamed_account_id2 = AccountId::new("new_second".to_string()); + + store_login_with_multiple_accounts_at_file( + &wallet, + account1.clone(), + hd_path.clone(), + login_id.clone(), + &password, + ) + .unwrap(); + + append_account_to_login_at_file( + &wallet, + account2.clone(), + hd_path.clone(), + login_id.clone(), + account_id2.clone(), + &password, + ) + .unwrap(); + + // Load and confirm + let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); + let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); + let expected = vec![ + WalletAccount::new( + DEFAULT_FIRST_ACCOUNT_NAME.into(), + MnemonicAccount::new(account1.clone(), hd_path.clone()), + ), + WalletAccount::new( + account_id2.clone(), + MnemonicAccount::new(account2.clone(), hd_path.clone()), + ), + ] + .into(); + assert_eq!(loaded_accounts, &expected); + + // Rename the second account to a new name + rename_account_in_login_at_file( + &wallet, + &login_id, + &account_id2, + &renamed_account_id2, + &password, + ) + .unwrap(); + + // Load and confirm + let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); + let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); + let expected = vec![ + WalletAccount::new( + DEFAULT_FIRST_ACCOUNT_NAME.into(), + MnemonicAccount::new(account1, hd_path.clone()), + ), + WalletAccount::new(renamed_account_id2, MnemonicAccount::new(account2, hd_path)), + ] + .into(); + assert_eq!(loaded_accounts, &expected); + } + + #[test] + fn rename_account_into_existing_account_id_fails() { + let store_dir = tempdir().unwrap(); + let wallet = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let account2 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let login_id = LoginId::new("first".to_string()); + let account_id2 = AccountId::new("second".to_string()); + let renamed_account_id2 = DEFAULT_FIRST_ACCOUNT_NAME.into(); + + store_login_with_multiple_accounts_at_file( + &wallet, + account1.clone(), + hd_path.clone(), + login_id.clone(), + &password, + ) + .unwrap(); + + append_account_to_login_at_file( + &wallet, + account2.clone(), + hd_path.clone(), + login_id.clone(), + account_id2.clone(), + &password, + ) + .unwrap(); + + // Load and confirm + let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); + let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); + let expected = vec![ + WalletAccount::new( + DEFAULT_FIRST_ACCOUNT_NAME.into(), + MnemonicAccount::new(account1.clone(), hd_path.clone()), + ), + WalletAccount::new( + account_id2.clone(), + MnemonicAccount::new(account2.clone(), hd_path.clone()), + ), + ] + .into(); + assert_eq!(loaded_accounts, &expected); + + // Rename the second account to the name of the first one fails + assert!(matches!( + rename_account_in_login_at_file( + &wallet, + &login_id, + &account_id2, + &renamed_account_id2, + &password, + ), + Err(BackendError::WalletAccountIdAlreadyExistsInWalletLogin) + )); + + // Load and confirm nothing was changed + let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); + let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); + let expected = vec![ + WalletAccount::new( + DEFAULT_FIRST_ACCOUNT_NAME.into(), + MnemonicAccount::new(account1, hd_path.clone()), + ), + WalletAccount::new(account_id2, MnemonicAccount::new(account2, hd_path)), + ] + .into(); + assert_eq!(loaded_accounts, &expected); + } + + // Test to that decrypts a stored file from the repo, to make sure we are able to decrypt stored + // wallets created with older versions. + #[test] + fn decrypt_stored_wallet() { + const SAVED_WALLET: &str = "src/wallet_storage/test-data/saved-wallet.json"; + let wallet_file = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(SAVED_WALLET); + + let wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); + + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let bad_password = UserPassword::new("bad-password".to_string()); + let id1 = LoginId::new("first".to_string()); + let id2 = LoginId::new("second".to_string()); + + assert!(!wallet.password_can_decrypt_all(&bad_password)); + assert!(wallet.password_can_decrypt_all(&password)); + + let acc1 = wallet.decrypt_login(&id1, &password).unwrap(); + let acc2 = wallet.decrypt_login(&id2, &password).unwrap(); + + assert!(matches!(acc1, StoredLogin::Mnemonic(_))); + assert!(matches!(acc2, StoredLogin::Mnemonic(_))); + + let expected_acc1 = bip39::Mnemonic::from_str("country mean universe text phone begin deputy reject result good cram illness common cluster proud swamp digital patrol spread bar face december base kick").unwrap(); + let expected_acc2 = bip39::Mnemonic::from_str("home mansion start quiz dress decide hint second dragon sunny juice always steak real minimum art rival skin draw total pulp foot goddess agent").unwrap(); + + assert_eq!( + acc1.as_mnemonic_account().unwrap().mnemonic(), + &expected_acc1 + ); + assert_eq!(acc1.as_mnemonic_account().unwrap().hd_path(), &hd_path,); + + assert_eq!( + acc2.as_mnemonic_account().unwrap().mnemonic(), + &expected_acc2 + ); + assert_eq!(acc2.as_mnemonic_account().unwrap().hd_path(), &hd_path,); + } + + #[test] + fn decrypt_stored_wallet_1_0_4() { + const SAVED_WALLET: &str = "src/wallet_storage/test-data/saved-wallet-1.0.4.json"; + let wallet_file = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(SAVED_WALLET); + + let wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); + + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password11!".to_string()); + let bad_password = UserPassword::new("bad-password".to_string()); + let login_id = LoginId::new("default".to_string()); + + assert!(!wallet.password_can_decrypt_all(&bad_password)); + assert!(wallet.password_can_decrypt_all(&password)); + + let acc1 = wallet.decrypt_login(&login_id, &password).unwrap(); + + assert!(matches!(acc1, StoredLogin::Mnemonic(_))); + + let expected_acc1 = bip39::Mnemonic::from_str("arrow capable abstract industry elevator nominee december piece hotel feed lounge web faint sword veteran bundle hour page actual laptop horror gold test warrior").unwrap(); + + assert_eq!( + acc1.as_mnemonic_account().unwrap().mnemonic(), + &expected_acc1 + ); + assert_eq!(acc1.as_mnemonic_account().unwrap().hd_path(), &hd_path,); + } + + #[test] + fn decrypt_stored_wallet_1_0_5_with_multiple_accounts() { + const SAVED_WALLET: &str = "src/wallet_storage/test-data/saved-wallet-1.0.5.json"; + let wallet_file = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(SAVED_WALLET); + + let wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); + + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password11!".to_string()); + let bad_password = UserPassword::new("bad-password".to_string()); + let login_id = LoginId::new("default".to_string()); + + assert!(!wallet.password_can_decrypt_all(&bad_password)); + assert!(wallet.password_can_decrypt_all(&password)); + + let login = wallet.decrypt_login(&login_id, &password).unwrap(); + + assert!(matches!(login, StoredLogin::Multiple(_))); + + let login = login.as_multiple_accounts().unwrap(); + assert_eq!(login.len(), 4); + + let expected_mn1 = bip39::Mnemonic::from_str("arrow capable abstract industry elevator nominee december piece hotel feed lounge web faint sword veteran bundle hour page actual laptop horror gold test warrior").unwrap(); + let expected_mn2 = bip39::Mnemonic::from_str("border hurt skull lunar goddess second danger game dismiss exhaust oven thumb dog drama onion false orchard spice tent next predict invite cherry green").unwrap(); + let expected_mn3 = bip39::Mnemonic::from_str("gentle crowd rule snap girl urge flat jump winner cluster night sand museum stock grunt quick tree acquire traffic major awake tag rack peasant").unwrap(); + let expected_mn4 = bip39::Mnemonic::from_str("debris blue skin annual inhale text border rigid spatial lesson coconut yard horn crystal control survey version vote hawk neck frame arrive oblige width").unwrap(); + + let expected = vec![ + WalletAccount::new( + "default".into(), + MnemonicAccount::new(expected_mn1, hd_path.clone()), + ), + WalletAccount::new( + "account2".into(), + MnemonicAccount::new(expected_mn2, hd_path.clone()), + ), + WalletAccount::new( + "foobar".into(), + MnemonicAccount::new(expected_mn3, hd_path.clone()), + ), + WalletAccount::new("42".into(), MnemonicAccount::new(expected_mn4, hd_path)), + ] + .into(); + + assert_eq!(login, &expected); + } + + #[test] + fn append_filename() { + let wallet_file = PathBuf::from("/tmp/saved-wallet.json"); + let timestamp = OsString::from("42"); + #[cfg(target_family = "unix")] + assert_eq!( + append_timestamp_to_filename(wallet_file.clone(), timestamp.clone(), None) + .unwrap() + .into_os_string() + .into_string() + .unwrap(), + "/tmp/saved-wallet-42.json".to_string(), + ); + #[cfg(not(target_family = "unix"))] + assert_eq!( + append_timestamp_to_filename(wallet_file.clone(), timestamp.clone(), None) + .unwrap() + .into_os_string() + .into_string() + .unwrap(), + r"/tmp\saved-wallet-42.json".to_string(), + ); + + #[cfg(target_family = "unix")] + assert_eq!( + append_timestamp_to_filename(wallet_file, timestamp, Some(3)) + .unwrap() + .into_os_string() + .into_string() + .unwrap(), + "/tmp/saved-wallet-42-3.json".to_string(), + ); + #[cfg(not(target_family = "unix"))] + assert_eq!( + append_timestamp_to_filename(wallet_file, timestamp, Some(3)) + .unwrap() + .into_os_string() + .into_string() + .unwrap(), + r"/tmp\saved-wallet-42-3.json".to_string(), + ); + } +} \ No newline at end of file From 2ba74ae120ae56dd9e1d9696e257093bf4964f6c Mon Sep 17 00:00:00 2001 From: Tommy Verrall Date: Tue, 1 Apr 2025 17:06:21 +0200 Subject: [PATCH 03/48] migrate to v2 - lots to check and do --- nym-wallet/Cargo.lock | 2748 +++++++++---- nym-wallet/package.json | 10 +- nym-wallet/src-tauri/Cargo.toml | 8 +- .../src-tauri/capabilities/migrated.json | 11 + .../src-tauri/gen/schemas/capabilities.json | 1 + .../src-tauri/gen/schemas/desktop-schema.json | 2279 ++++++++++ .../src-tauri/gen/schemas/macOS-schema.json | 2279 ++++++++++ nym-wallet/src-tauri/src/config/mod.rs | 288 +- nym-wallet/src-tauri/src/log.rs | 4 +- nym-wallet/src-tauri/src/main.rs | 34 +- nym-wallet/src-tauri/src/menu.rs | 31 +- .../src-tauri/src/operations/app/version.rs | 55 +- .../src-tauri/src/operations/app/window.rs | 8 +- .../src-tauri/src/operations/help/log.rs | 12 +- .../src-tauri/src/wallet_storage/mod.rs | 3654 +++++++++-------- nym-wallet/src-tauri/tauri.conf.json | 113 +- nym-wallet/src/components/CopyToClipboard.tsx | 4 +- .../src/components/Settings/AppVersion.tsx | 4 +- .../src/pages/internal-docs/DocEntry.tsx | 2 +- nym-wallet/src/requests/wrapper.ts | 2 +- nym-wallet/src/utils/common.ts | 3 +- yarn.lock | 2041 +-------- 22 files changed, 8942 insertions(+), 4649 deletions(-) create mode 100644 nym-wallet/src-tauri/capabilities/migrated.json create mode 100644 nym-wallet/src-tauri/gen/schemas/capabilities.json create mode 100644 nym-wallet/src-tauri/gen/schemas/desktop-schema.json create mode 100644 nym-wallet/src-tauri/gen/schemas/macOS-schema.json diff --git a/nym-wallet/Cargo.lock b/nym-wallet/Cargo.lock index d5f95eb219..e451c0c1e8 100644 --- a/nym-wallet/Cargo.lock +++ b/nym-wallet/Cargo.lock @@ -100,6 +100,21 @@ version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + [[package]] name = "anstream" version = "0.6.4" @@ -154,6 +169,34 @@ version = "1.0.72" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b13c32d80ecc7ab747b80c3784bce54ee8a7a0cc4fbda9bf4cda2cf6fe90854" +[[package]] +name = "arbitrary" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223" +dependencies = [ + "derive_arbitrary", +] + +[[package]] +name = "arboard" +version = "3.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df099ccb16cd014ff054ac1bf392c67feeef57164b05c42f037cd40f5d4357f4" +dependencies = [ + "clipboard-win", + "core-graphics 0.23.1", + "image", + "log", + "objc2 0.5.2", + "objc2-app-kit 0.2.2", + "objc2-foundation 0.2.2", + "parking_lot", + "windows-sys 0.48.0", + "wl-clipboard-rs", + "x11rb", +] + [[package]] name = "argon2" version = "0.5.1" @@ -313,26 +356,25 @@ dependencies = [ [[package]] name = "atk" -version = "0.15.1" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c3d816ce6f0e2909a96830d6911c2aff044370b1ef92d7f267b43bae5addedd" +checksum = "241b621213072e993be4f6f3a9e4b45f65b7e6faad43001be957184b7bb1824b" dependencies = [ "atk-sys", - "bitflags 1.3.2", "glib", "libc", ] [[package]] name = "atk-sys" -version = "0.15.1" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58aeb089fb698e06db8089971c7ee317ab9644bade33383f63631437b03aafb6" +checksum = "c5e48b684b0ca77d2bbadeef17424c2ea3c897d44d566a1617e7e8f30614d086" dependencies = [ "glib-sys", "gobject-sys", "libc", - "system-deps 6.1.1", + "system-deps", ] [[package]] @@ -341,22 +383,6 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" -[[package]] -name = "attohttpc" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fcf00bc6d5abb29b5f97e3c61a90b6d3caa12f3faf897d4a3e3607c050a35a7" -dependencies = [ - "flate2", - "http 0.2.9", - "log", - "native-tls", - "serde", - "serde_json", - "serde_urlencoded", - "url", -] - [[package]] name = "atty" version = "0.2.14" @@ -479,9 +505,12 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.4.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" +checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" +dependencies = [ + "serde", +] [[package]] name = "blake2" @@ -492,12 +521,6 @@ dependencies = [ "digest 0.10.7", ] -[[package]] -name = "block" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" - [[package]] name = "block-buffer" version = "0.9.0" @@ -516,6 +539,24 @@ dependencies = [ "generic-array", ] +[[package]] +name = "block2" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c132eebf10f5cad5289222520a4a058514204aed6d791f1cf4fe8088b82d15f" +dependencies = [ + "objc2 0.5.2", +] + +[[package]] +name = "block2" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d59b4c170e16f0405a2e95aff44432a0d41aa97675f3d52623effe95792a037" +dependencies = [ + "objc2 0.6.0", +] + [[package]] name = "bls12_381" version = "0.8.0" @@ -546,7 +587,18 @@ checksum = "a1a0b1dbcc8ae29329621f8d4f0d835787c1c38bb1401979b49d13b0b305ff68" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", - "brotli-decompressor", + "brotli-decompressor 2.3.4", +] + +[[package]] +name = "brotli" +version = "7.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc97b8f16f944bba54f0433f07e30be199b6dc2bd25937444bbad560bcea29bd" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", + "brotli-decompressor 4.0.2", ] [[package]] @@ -559,6 +611,16 @@ dependencies = [ "alloc-stdlib", ] +[[package]] +name = "brotli-decompressor" +version = "4.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74fa05ad7d803d413eb8380983b092cbbaf9a85f151b871360e7b00cd7060b37" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", +] + [[package]] name = "bs58" version = "0.5.1" @@ -569,16 +631,6 @@ dependencies = [ "tinyvec", ] -[[package]] -name = "bstr" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6798148dccfbff0fae41c7574d2fa8f1ef3492fba0face179de5d8d447d67b05" -dependencies = [ - "memchr", - "serde", -] - [[package]] name = "bumpalo" version = "3.13.0" @@ -597,6 +649,12 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" +[[package]] +name = "byteorder-lite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" + [[package]] name = "bytes" version = "1.10.1" @@ -608,26 +666,27 @@ dependencies = [ [[package]] name = "cairo-rs" -version = "0.15.12" +version = "0.18.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c76ee391b03d35510d9fa917357c7f1855bd9a6659c95a1b392e33f49b3369bc" +checksum = "8ca26ef0159422fb77631dc9d17b102f253b876fe1586b03b803e63a309b4ee2" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.9.0", "cairo-sys-rs", "glib", "libc", + "once_cell", "thiserror 1.0.64", ] [[package]] name = "cairo-sys-rs" -version = "0.15.1" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c55d429bef56ac9172d25fecb85dc8068307d17acd74b377866b7a1ef25d3c8" +checksum = "685c9fa8e590b8b3d678873528d83411db17242a73fccaed827770ea0fedda51" dependencies = [ "glib-sys", "libc", - "system-deps 6.1.1", + "system-deps", ] [[package]] @@ -663,13 +722,27 @@ dependencies = [ ] [[package]] -name = "cargo_toml" -version = "0.13.3" +name = "cargo_metadata" +version = "0.19.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497049e9477329f8f6a559972ee42e117487d01d1e8c2cc9f836ea6fa23a9e1a" +checksum = "dd5eb614ed4c27c5d706420e4320fbe3216ab31fa1c33cd8246ac36dae4479ba" +dependencies = [ + "camino", + "cargo-platform", + "semver", + "serde", + "serde_json", + "thiserror 2.0.11", +] + +[[package]] +name = "cargo_toml" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02260d489095346e5cafd04dea8e8cb54d1d74fcd759022a9b72986ebe9a1257" dependencies = [ "serde", - "toml 0.5.11", + "toml 0.8.20", ] [[package]] @@ -708,12 +781,14 @@ dependencies = [ ] [[package]] -name = "cfg-expr" -version = "0.9.1" +name = "cfb" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3431df59f28accaf4cb4eed4a9acc66bea3f3c3753aa6cdc2f024174ef232af7" +checksum = "d38f2da7a0a2c4ccf0065be06397cc26a81f4e528be095826eee9d4adbb8c60f" dependencies = [ - "smallvec", + "byteorder", + "fnv", + "uuid 1.10.0", ] [[package]] @@ -732,6 +807,31 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "cfg_aliases" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" + +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + +[[package]] +name = "chrono" +version = "0.4.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a7964611d71df112cb1730f2ee67324fcf4d0fc6606acbbe9bfe06df124637c" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "num-traits", + "serde", + "windows-link", +] + [[package]] name = "cipher" version = "0.4.4" @@ -761,7 +861,7 @@ dependencies = [ "anstream", "anstyle", "clap_lex", - "strsim", + "strsim 0.10.0", ] [[package]] @@ -783,42 +883,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" [[package]] -name = "cocoa" -version = "0.24.1" +name = "clipboard-win" +version = "5.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f425db7937052c684daec3bd6375c8abe2d146dca4b8b143d6db777c39138f3a" +checksum = "15efe7a882b08f34e38556b14f2fb3daa98769d06c7f0c1b076dfd0d983bc892" dependencies = [ - "bitflags 1.3.2", - "block", - "cocoa-foundation", - "core-foundation", - "core-graphics", - "foreign-types", - "libc", - "objc", + "error-code", ] -[[package]] -name = "cocoa-foundation" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "931d3837c286f56e3c58423ce4eba12d08db2374461a785c86f672b08b5650d6" -dependencies = [ - "bitflags 1.3.2", - "block", - "core-foundation", - "core-graphics-types", - "foreign-types", - "libc", - "objc", -] - -[[package]] -name = "color_quant" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" - [[package]] name = "colorchoice" version = "1.0.0" @@ -843,7 +915,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c" dependencies = [ "lazy_static", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -874,6 +946,16 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" +[[package]] +name = "cookie" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747" +dependencies = [ + "time", + "version_check", +] + [[package]] name = "core-foundation" version = "0.9.3" @@ -885,21 +967,44 @@ dependencies = [ ] [[package]] -name = "core-foundation-sys" -version = "0.8.4" +name = "core-foundation" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" +checksum = "b55271e5c8c478ad3f38ad24ef34923091e0548492a266d19b3c0b4d82574c63" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "core-graphics" -version = "0.22.3" +version = "0.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2581bbab3b8ffc6fcbd550bf46c355135d16e9ff2a6ea032ad6b9bf1d7efe4fb" +checksum = "970a29baf4110c26fedbc7f82107d42c23f7e88e404c4577ed73fe99ff85a212" dependencies = [ "bitflags 1.3.2", - "core-foundation", - "core-graphics-types", - "foreign-types", + "core-foundation 0.9.3", + "core-graphics-types 0.1.2", + "foreign-types 0.5.0", + "libc", +] + +[[package]] +name = "core-graphics" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa95a34622365fa5bbf40b20b75dba8dfa8c94c734aea8ac9a5ca38af14316f1" +dependencies = [ + "bitflags 2.9.0", + "core-foundation 0.10.0", + "core-graphics-types 0.2.0", + "foreign-types 0.5.0", "libc", ] @@ -910,7 +1015,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2bb142d41022986c1d8ff29103a1411c8a3dfad3552f87a4f8dc50d61d4f4e33" dependencies = [ "bitflags 1.3.2", - "core-foundation", + "core-foundation 0.9.3", + "libc", +] + +[[package]] +name = "core-graphics-types" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb" +dependencies = [ + "bitflags 2.9.0", + "core-foundation 0.10.0", "libc", ] @@ -1045,9 +1161,9 @@ dependencies = [ [[package]] name = "crc32fast" -version = "1.3.2" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" dependencies = [ "cfg-if", ] @@ -1147,6 +1263,16 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "ctor" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a2785755761f3ddc1492979ce1e48d2c00d09311c39e4466429188f3dd6501" +dependencies = [ + "quote", + "syn 2.0.96", +] + [[package]] name = "ctr" version = "0.9.2" @@ -1298,8 +1424,18 @@ version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c" dependencies = [ - "darling_core", - "darling_macro", + "darling_core 0.13.4", + "darling_macro 0.13.4", +] + +[[package]] +name = "darling" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" +dependencies = [ + "darling_core 0.20.11", + "darling_macro 0.20.11", ] [[package]] @@ -1312,21 +1448,46 @@ dependencies = [ "ident_case", "proc-macro2", "quote", - "strsim", + "strsim 0.10.0", "syn 1.0.109", ] +[[package]] +name = "darling_core" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim 0.11.1", + "syn 2.0.96", +] + [[package]] name = "darling_macro" version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835" dependencies = [ - "darling_core", + "darling_core 0.13.4", "quote", "syn 1.0.109", ] +[[package]] +name = "darling_macro" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" +dependencies = [ + "darling_core 0.20.11", + "quote", + "syn 2.0.96", +] + [[package]] name = "data-encoding" version = "2.7.0" @@ -1364,6 +1525,28 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "derive-new" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d150dea618e920167e5973d70ae6ece4385b7164e0d799fe7c122dd0a5d912ad" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", +] + +[[package]] +name = "derive_arbitrary" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30542c1ad912e0e3d22a1935c290e12e8a29d704a420177a31faad4a601a0800" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", +] + [[package]] name = "derive_more" version = "0.99.17" @@ -1438,13 +1621,12 @@ dependencies = [ ] [[package]] -name = "dirs-next" -version = "2.0.0" +name = "dirs" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" +checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e" dependencies = [ - "cfg-if", - "dirs-sys-next", + "dirs-sys 0.5.0", ] [[package]] @@ -1454,7 +1636,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" dependencies = [ "libc", - "redox_users", + "redox_users 0.4.3", "winapi", ] @@ -1466,19 +1648,20 @@ checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" dependencies = [ "libc", "option-ext", - "redox_users", + "redox_users 0.4.3", "windows-sys 0.48.0", ] [[package]] -name = "dirs-sys-next" -version = "0.1.2" +name = "dirs-sys" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" +checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" dependencies = [ "libc", - "redox_users", - "winapi", + "option-ext", + "redox_users 0.5.0", + "windows-sys 0.59.0", ] [[package]] @@ -1498,12 +1681,50 @@ dependencies = [ "syn 2.0.96", ] +[[package]] +name = "dlopen2" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1297103d2bbaea85724fcee6294c2d50b1081f9ad47d0f6f6f61eda65315a6" +dependencies = [ + "dlopen2_derive", + "libc", + "once_cell", + "winapi", +] + +[[package]] +name = "dlopen2_derive" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2b99bf03862d7f545ebc28ddd33a665b50865f4dfd84031a393823879bd4c54" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", +] + [[package]] name = "dotenvy" version = "0.15.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" +[[package]] +name = "downcast-rs" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" + +[[package]] +name = "dpi" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f25c0e292a7ca6d6498557ff1df68f32c99850012b6ea401cf8daf771f22ff53" +dependencies = [ + "serde", +] + [[package]] name = "dtoa" version = "1.0.9" @@ -1626,6 +1847,20 @@ dependencies = [ "zeroize", ] +[[package]] +name = "embed-resource" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fbc6e0d8e0c03a655b53ca813f0463d2c956bc4db8138dbc89f120b066551e3" +dependencies = [ + "cc", + "memchr", + "rustc_version", + "toml 0.8.20", + "vswhom", + "winreg 0.52.0", +] + [[package]] name = "embed_plist" version = "1.2.2" @@ -1673,26 +1908,31 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] -name = "errno" -version = "0.3.2" +name = "erased-serde" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b30f669a7961ef1631673d2766cc92f52d64f7ef354d4fe0ddfd30ed52f0f4f" +checksum = "e004d887f51fcb9fef17317a2f3525c887d8aa3f4f50fed920816a688284a5b7" dependencies = [ - "errno-dragonfly", - "libc", - "windows-sys 0.48.0", + "serde", + "typeid", ] [[package]] -name = "errno-dragonfly" -version = "0.1.2" +name = "errno" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" dependencies = [ - "cc", "libc", + "windows-sys 0.59.0", ] +[[package]] +name = "error-code" +version = "3.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5d9305ccc6942a704f4335694ecd3de2ea531b114ac2d51f5f843750787a92f" + [[package]] name = "eyre" version = "0.6.12" @@ -1705,15 +1945,15 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.0.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] name = "fdeflate" -version = "0.3.0" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d329bdeac514ee06249dabc27877490f17f5d371ec693360768b838e19f3ae10" +checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c" dependencies = [ "simd-adler32", ] @@ -1756,16 +1996,22 @@ dependencies = [ [[package]] name = "filetime" -version = "0.2.22" +version = "0.2.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4029edd3e734da6fe05b6cd7bd2960760a616bd2ddd0d59a0124746d6272af0" +checksum = "35c0522e981e68cbfa8c3f978441a5f34b30b96e146b33cd3359176b50fe8586" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.3.5", - "windows-sys 0.48.0", + "libredox", + "windows-sys 0.59.0", ] +[[package]] +name = "fixedbitset" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" + [[package]] name = "flate2" version = "1.1.0" @@ -1798,7 +2044,28 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" dependencies = [ - "foreign-types-shared", + "foreign-types-shared 0.1.1", +] + +[[package]] +name = "foreign-types" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" +dependencies = [ + "foreign-types-macros", + "foreign-types-shared 0.3.1", +] + +[[package]] +name = "foreign-types-macros" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", ] [[package]] @@ -1807,6 +2074,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" +[[package]] +name = "foreign-types-shared" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" + [[package]] name = "form_urlencoded" version = "1.2.1" @@ -1926,11 +2199,10 @@ dependencies = [ [[package]] name = "gdk" -version = "0.15.4" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6e05c1f572ab0e1f15be94217f0dc29088c248b14f792a5ff0af0d84bcda9e8" +checksum = "d9f245958c627ac99d8e529166f9823fb3b838d1d41fd2b297af3075093c2691" dependencies = [ - "bitflags 1.3.2", "cairo-rs", "gdk-pixbuf", "gdk-sys", @@ -1942,35 +2214,35 @@ dependencies = [ [[package]] name = "gdk-pixbuf" -version = "0.15.11" +version = "0.18.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad38dd9cc8b099cceecdf41375bb6d481b1b5a7cd5cd603e10a69a9383f8619a" +checksum = "50e1f5f1b0bfb830d6ccc8066d18db35c487b1b2b1e8589b5dfe9f07e8defaec" dependencies = [ - "bitflags 1.3.2", "gdk-pixbuf-sys", "gio", "glib", "libc", + "once_cell", ] [[package]] name = "gdk-pixbuf-sys" -version = "0.15.10" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "140b2f5378256527150350a8346dbdb08fadc13453a7a2d73aecd5fab3c402a7" +checksum = "3f9839ea644ed9c97a34d129ad56d38a25e6756f99f3a88e15cd39c20629caf7" dependencies = [ "gio-sys", "glib-sys", "gobject-sys", "libc", - "system-deps 6.1.1", + "system-deps", ] [[package]] name = "gdk-sys" -version = "0.15.1" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32e7a08c1e8f06f4177fb7e51a777b8c1689f743a7bc11ea91d44d2226073a88" +checksum = "5c2d13f38594ac1e66619e188c6d5a1adb98d11b2fcf7894fc416ad76aa2f3f7" dependencies = [ "cairo-sys-rs", "gdk-pixbuf-sys", @@ -1980,33 +2252,48 @@ dependencies = [ "libc", "pango-sys", "pkg-config", - "system-deps 6.1.1", + "system-deps", ] [[package]] -name = "gdkx11-sys" -version = "0.15.1" +name = "gdkwayland-sys" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4b7f8c7a84b407aa9b143877e267e848ff34106578b64d1e0a24bf550716178" +checksum = "140071d506d223f7572b9f09b5e155afbd77428cd5cc7af8f2694c41d98dfe69" dependencies = [ "gdk-sys", "glib-sys", + "gobject-sys", + "libc", + "pkg-config", + "system-deps", +] + +[[package]] +name = "gdkx11" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3caa00e14351bebbc8183b3c36690327eb77c49abc2268dd4bd36b856db3fbfe" +dependencies = [ + "gdk", + "gdkx11-sys", + "gio", + "glib", "libc", - "system-deps 6.1.1", "x11", ] [[package]] -name = "generator" -version = "0.7.5" +name = "gdkx11-sys" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cc16584ff22b460a382b7feec54b23d2908d858152e5739a120b949293bd74e" +checksum = "6e2e7445fe01ac26f11601db260dd8608fe172514eb63b3b5e261ea6b0f4428d" dependencies = [ - "cc", + "gdk-sys", + "glib-sys", "libc", - "log", - "rustversion", - "windows 0.48.0", + "system-deps", + "x11", ] [[package]] @@ -2020,6 +2307,16 @@ dependencies = [ "zeroize", ] +[[package]] +name = "gethostname" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0176e0459c2e4a1fe232f984bca6890e681076abb9934f6cea7c326f3fc47818" +dependencies = [ + "libc", + "windows-targets 0.48.1", +] + [[package]] name = "getrandom" version = "0.1.16" @@ -2044,6 +2341,18 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "getrandom" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasi 0.14.2+wasi-0.2.4", +] + [[package]] name = "getset" version = "0.1.5" @@ -2074,49 +2383,54 @@ checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" [[package]] name = "gio" -version = "0.15.12" +version = "0.18.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68fdbc90312d462781a395f7a16d96a2b379bb6ef8cd6310a2df272771c4283b" +checksum = "d4fc8f532f87b79cbc51a79748f16a6828fb784be93145a322fa14d06d354c73" dependencies = [ - "bitflags 1.3.2", "futures-channel", "futures-core", "futures-io", + "futures-util", "gio-sys", "glib", "libc", "once_cell", + "pin-project-lite", + "smallvec", "thiserror 1.0.64", ] [[package]] name = "gio-sys" -version = "0.15.10" +version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32157a475271e2c4a023382e9cab31c4584ee30a97da41d3c4e9fdd605abcf8d" +checksum = "37566df850baf5e4cb0dfb78af2e4b9898d817ed9263d1090a2df958c64737d2" dependencies = [ "glib-sys", "gobject-sys", "libc", - "system-deps 6.1.1", + "system-deps", "winapi", ] [[package]] name = "glib" -version = "0.15.12" +version = "0.18.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edb0306fbad0ab5428b0ca674a23893db909a98582969c9b537be4ced78c505d" +checksum = "233daaf6e83ae6a12a52055f568f9d7cf4671dabb78ff9560ab6da230ce00ee5" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.9.0", "futures-channel", "futures-core", "futures-executor", "futures-task", + "futures-util", + "gio-sys", "glib-macros", "glib-sys", "gobject-sys", "libc", + "memchr", "once_cell", "smallvec", "thiserror 1.0.64", @@ -2124,27 +2438,26 @@ dependencies = [ [[package]] name = "glib-macros" -version = "0.15.13" +version = "0.18.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10c6ae9f6fa26f4fb2ac16b528d138d971ead56141de489f8111e259b9df3c4a" +checksum = "0bb0228f477c0900c880fd78c8759b95c7636dbd7842707f49e132378aa2acdc" dependencies = [ - "anyhow", "heck 0.4.1", - "proc-macro-crate", + "proc-macro-crate 2.0.0", "proc-macro-error", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.96", ] [[package]] name = "glib-sys" -version = "0.15.10" +version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef4b192f8e65e9cf76cbf4ea71fa8e3be4a0e18ffe3d68b8da6836974cc5bad4" +checksum = "063ce2eb6a8d0ea93d2bf8ba1957e78dbab6be1c2220dd3daca57d5a9d869898" dependencies = [ "libc", - "system-deps 6.1.1", + "system-deps", ] [[package]] @@ -2153,28 +2466,15 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" -[[package]] -name = "globset" -version = "0.4.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "759c97c1e17c55525b57192c06a267cda0ac5210b222d6b82189a2338fa1c13d" -dependencies = [ - "aho-corasick", - "bstr", - "fnv", - "log", - "regex", -] - [[package]] name = "gobject-sys" -version = "0.15.10" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d57ce44246becd17153bd035ab4d32cfee096a657fc01f2231c9278378d1e0a" +checksum = "0850127b514d1c4a4654ead6dedadb18198999985908e6ffe4436f53c785ce44" dependencies = [ "glib-sys", "libc", - "system-deps 6.1.1", + "system-deps", ] [[package]] @@ -2190,12 +2490,11 @@ dependencies = [ [[package]] name = "gtk" -version = "0.15.5" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92e3004a2d5d6d8b5057d2b57b3712c9529b62e82c77f25c1fecde1fd5c23bd0" +checksum = "fd56fb197bfc42bd5d2751f4f017d44ff59fbb58140c6b49f9b3b2bdab08506a" dependencies = [ "atk", - "bitflags 1.3.2", "cairo-rs", "field-offset", "futures-channel", @@ -2206,16 +2505,15 @@ dependencies = [ "gtk-sys", "gtk3-macros", "libc", - "once_cell", "pango", "pkg-config", ] [[package]] name = "gtk-sys" -version = "0.15.3" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5bc2f0587cba247f60246a0ca11fe25fb733eabc3de12d1965fc07efab87c84" +checksum = "8f29a1c21c59553eb7dd40e918be54dccd60c52b049b75119d5d96ce6b624414" dependencies = [ "atk-sys", "cairo-sys-rs", @@ -2226,21 +2524,20 @@ dependencies = [ "gobject-sys", "libc", "pango-sys", - "system-deps 6.1.1", + "system-deps", ] [[package]] name = "gtk3-macros" -version = "0.15.6" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "684c0456c086e8e7e9af73ec5b84e35938df394712054550e81558d21c44ab0d" +checksum = "52ff3c5b21f14f0736fed6dcfc0bfb4225ebf5725f3c0209edeec181e4d73e9d" dependencies = [ - "anyhow", - "proc-macro-crate", + "proc-macro-crate 1.3.1", "proc-macro-error", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.96", ] [[package]] @@ -2451,7 +2748,21 @@ checksum = "e5c13fb08e5d4dfc151ee5e88bae63f7773d61852f3bdc73c9f4b9e1bde03148" dependencies = [ "log", "mac", - "markup5ever", + "markup5ever 0.10.1", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "html5ever" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bea68cab48b8459f17cf1c944c67ddc572d272d9f2b274140f223ecb1da4a3b7" +dependencies = [ + "log", + "mac", + "markup5ever 0.11.0", "proc-macro2", "quote", "syn 1.0.109", @@ -2513,12 +2824,6 @@ dependencies = [ "pin-project-lite", ] -[[package]] -name = "http-range" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21dec9db110f5f872ed9699c3ecf50cf16f423502706ba5c72462e28d3157573" - [[package]] name = "httparse" version = "1.8.0" @@ -2667,6 +2972,29 @@ dependencies = [ "tracing", ] +[[package]] +name = "iana-time-zone" +version = "0.1.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core 0.52.0", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + [[package]] name = "ico" version = "0.2.0" @@ -2677,6 +3005,16 @@ dependencies = [ "png", ] +[[package]] +name = "ico" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc50b891e4acf8fe0e71ef88ec43ad82ee07b3810ad09de10f1d01f072ed4b98" +dependencies = [ + "byteorder", + "png", +] + [[package]] name = "icu_collections" version = "1.5.0" @@ -2832,34 +3170,17 @@ dependencies = [ "icu_properties", ] -[[package]] -name = "ignore" -version = "0.4.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbe7873dab538a9a44ad79ede1faf5f30d49f9a5c883ddbab48bce81b64b7492" -dependencies = [ - "globset", - "lazy_static", - "log", - "memchr", - "regex", - "same-file", - "thread_local", - "walkdir", - "winapi-util", -] - [[package]] name = "image" -version = "0.24.7" +version = "0.25.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f3dfdbdd72063086ff443e297b61695500514b1e41095b6fb9a5ab48a70a711" +checksum = "db35664ce6b9810857a38a906215e75a9c879f0696556a39f59c62829710251a" dependencies = [ "bytemuck", - "byteorder", - "color_quant", - "num-rational", + "byteorder-lite", "num-traits", + "png", + "tiff", ] [[package]] @@ -2896,7 +3217,16 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "20b2b533137b9cad970793453d4f921c2e91312a6d88b1085c07bc15fc51bb3b" dependencies = [ - "cfb", + "cfb 0.6.1", +] + +[[package]] +name = "infer" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a588916bfdfd92e71cacef98a63d9b1f0d74d6599980d11894290e7ddefffcf7" +dependencies = [ + "cfb 0.7.3", ] [[package]] @@ -2908,15 +3238,6 @@ dependencies = [ "generic-array", ] -[[package]] -name = "instant" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" -dependencies = [ - "cfg-if", -] - [[package]] name = "ipconfig" version = "0.3.2" @@ -2935,6 +3256,15 @@ version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6" +[[package]] +name = "is-docker" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "928bae27f42bc99b60d9ac7334e3a21d10ad8f1835a4e12ec3ec0464765ed1b3" +dependencies = [ + "once_cell", +] + [[package]] name = "is-terminal" version = "0.4.9" @@ -2942,10 +3272,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" dependencies = [ "hermit-abi 0.3.9", - "rustix", + "rustix 0.38.44", "windows-sys 0.48.0", ] +[[package]] +name = "is-wsl" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "173609498df190136aa7dea1a91db051746d339e18476eed5ca40521f02d7aa5" +dependencies = [ + "is-docker", + "once_cell", +] + [[package]] name = "itertools" version = "0.10.5" @@ -2987,9 +3327,9 @@ checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" [[package]] name = "javascriptcore-rs" -version = "0.16.0" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf053e7843f2812ff03ef5afe34bb9c06ffee120385caad4f6b9967fcd37d41c" +checksum = "ca5671e9ffce8ffba57afc24070e906da7fc4b1ba66f2cabebf61bf2ea257fcc" dependencies = [ "bitflags 1.3.2", "glib", @@ -2998,28 +3338,30 @@ dependencies = [ [[package]] name = "javascriptcore-rs-sys" -version = "0.4.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "905fbb87419c5cde6e3269537e4ea7d46431f3008c5d057e915ef3f115e7793c" +checksum = "af1be78d14ffa4b75b66df31840478fef72b51f8c2465d4ca7c194da9f7a5124" dependencies = [ "glib-sys", "gobject-sys", "libc", - "system-deps 5.0.0", + "system-deps", ] [[package]] name = "jni" -version = "0.20.0" +version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "039022cdf4d7b1cf548d31f60ae783138e5fd42013f6271049d7df7afadef96c" +checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" dependencies = [ "cesu8", + "cfg-if", "combine", "jni-sys", "log", "thiserror 1.0.64", "walkdir", + "windows-sys 0.45.0", ] [[package]] @@ -3028,6 +3370,12 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" +[[package]] +name = "jpeg-decoder" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0" + [[package]] name = "js-sys" version = "0.3.64" @@ -3048,6 +3396,28 @@ dependencies = [ "treediff", ] +[[package]] +name = "json-patch" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "863726d7afb6bc2590eeff7135d923545e5e964f004c2ccf8716c25e70a86f08" +dependencies = [ + "jsonptr", + "serde", + "serde_json", + "thiserror 1.0.64", +] + +[[package]] +name = "jsonptr" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dea2b27dd239b2556ed7a25ba842fe47fd602e7fc7433c2a8d6106d4d9edd70" +dependencies = [ + "serde", + "serde_json", +] + [[package]] name = "k256" version = "0.13.4" @@ -3062,6 +3432,17 @@ dependencies = [ "signature", ] +[[package]] +name = "keyboard-types" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b750dcadc39a09dbadd74e118f6dd6598df77fa01df0cfcdc52c28dece74528a" +dependencies = [ + "bitflags 2.9.0", + "serde", + "unicode-segmentation", +] + [[package]] name = "kuchiki" version = "0.8.1" @@ -3069,7 +3450,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ea8e9c6e031377cff82ee3001dc8026cdf431ed4e2e6b51f98ab8c73484a358" dependencies = [ "cssparser", - "html5ever", + "html5ever 0.25.2", + "matches", + "selectors", +] + +[[package]] +name = "kuchikiki" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f29e4755b7b995046f510a7520c42b2fed58b77bd94d5a87a8eb43d2fd126da8" +dependencies = [ + "cssparser", + "html5ever 0.26.0", + "indexmap 1.9.3", "matches", "selectors", ] @@ -3080,12 +3474,57 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +[[package]] +name = "libappindicator" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03589b9607c868cc7ae54c0b2a22c8dc03dd41692d48f2d7df73615c6a95dc0a" +dependencies = [ + "glib", + "gtk", + "gtk-sys", + "libappindicator-sys", + "log", +] + +[[package]] +name = "libappindicator-sys" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e9ec52138abedcc58dc17a7c6c0c00a2bdb4f3427c7f63fa97fd0d859155caf" +dependencies = [ + "gtk-sys", + "libloading", + "once_cell", +] + [[package]] name = "libc" version = "0.2.169" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" +[[package]] +name = "libloading" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" +dependencies = [ + "cfg-if", + "winapi", +] + +[[package]] +name = "libredox" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +dependencies = [ + "bitflags 2.9.0", + "libc", + "redox_syscall 0.5.10", +] + [[package]] name = "line-wrap" version = "0.1.1" @@ -3103,9 +3542,15 @@ checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" [[package]] name = "linux-raw-sys" -version = "0.4.5" +version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57bcfdad1b858c2db7c38303a6d2ad4dfaf5eb53dfeb0910128b2c26d6158503" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" + +[[package]] +name = "linux-raw-sys" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe7db12097d22ec582439daf8618b8fdd1a7bef6270e9af3b1ebcd30893cf413" [[package]] name = "litemap" @@ -3132,21 +3577,6 @@ dependencies = [ "serde", ] -[[package]] -name = "loom" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff50ecb28bb86013e935fb6683ab1f6d3a20016f123c76fd4c27470076ac30f5" -dependencies = [ - "cfg-if", - "generator", - "scoped-tls", - "serde", - "serde_json", - "tracing", - "tracing-subscriber", -] - [[package]] name = "lru-cache" version = "0.1.2" @@ -3162,15 +3592,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" -[[package]] -name = "malloc_buf" -version = "0.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" -dependencies = [ - "libc", -] - [[package]] name = "markup5ever" version = "0.10.1" @@ -3179,7 +3600,21 @@ checksum = "a24f40fb03852d1cdd84330cddcaf98e9ec08a7b7768e952fad3b4cf048ec8fd" dependencies = [ "log", "phf 0.8.0", - "phf_codegen", + "phf_codegen 0.8.0", + "string_cache", + "string_cache_codegen", + "tendril", +] + +[[package]] +name = "markup5ever" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2629bb1404f3d34c2e921f21fd34ba00b206124c81f65c50b43b6aaefeb016" +dependencies = [ + "log", + "phf 0.10.1", + "phf_codegen 0.10.0", "string_cache", "string_cache_codegen", "tendril", @@ -3191,15 +3626,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" -[[package]] -name = "matchers" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" -dependencies = [ - "regex-automata 0.1.10", -] - [[package]] name = "matches" version = "0.1.10" @@ -3228,10 +3654,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] -name = "minisign-verify" +name = "minimal-lexical" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "933dca44d65cdd53b355d0b73d380a2ff5da71f87f036053188bf1eab6a19881" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "minisign-verify" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6367d84fb54d4242af283086402907277715b8fe46976963af5ebf173f8efba3" [[package]] name = "miniz_oxide" @@ -3240,7 +3672,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" dependencies = [ "adler", - "simd-adler32", ] [[package]] @@ -3250,6 +3681,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e3e04debbb59698c15bacbb6d93584a8c0ca9cc3213cb423d31f760d8843ce5" dependencies = [ "adler2", + "simd-adler32", ] [[package]] @@ -3264,6 +3696,27 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "muda" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4de14a9b5d569ca68d7c891d613b390cf5ab4f851c77aaa2f9e435555d3d9492" +dependencies = [ + "crossbeam-channel", + "dpi", + "gtk", + "keyboard-types", + "objc2 0.6.0", + "objc2-app-kit 0.3.0", + "objc2-core-foundation", + "objc2-foundation 0.3.0", + "once_cell", + "png", + "serde", + "thiserror 2.0.11", + "windows-sys 0.59.0", +] + [[package]] name = "native-tls" version = "0.2.11" @@ -3284,14 +3737,16 @@ dependencies = [ [[package]] name = "ndk" -version = "0.6.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2032c77e030ddee34a6787a64166008da93f6a352b629261d0fee232b8742dd4" +checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.9.0", "jni-sys", + "log", "ndk-sys", "num_enum", + "raw-window-handle", "thiserror 1.0.64", ] @@ -3303,9 +3758,9 @@ checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" [[package]] name = "ndk-sys" -version = "0.3.0" +version = "0.6.0+11769913" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e5a6ae77c8ee183dcbbba6150e2e6b9f3f4196a7666c02a715a95692ec1fa97" +checksum = "ee6cda3051665f1fb8d9e08fc35c96d5a244fb1be711a03b71118828afc9a873" dependencies = [ "jni-sys", ] @@ -3316,6 +3771,18 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54" +[[package]] +name = "nix" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4" +dependencies = [ + "bitflags 2.9.0", + "cfg-if", + "cfg_aliases 0.1.1", + "libc", +] + [[package]] name = "nodrop" version = "0.1.14" @@ -3323,13 +3790,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" [[package]] -name = "nu-ansi-term" -version = "0.46.0" +name = "nom" +version = "7.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" dependencies = [ - "overload", - "winapi", + "memchr", + "minimal-lexical", ] [[package]] @@ -3357,17 +3824,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "num-rational" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - [[package]] name = "num-traits" version = "0.2.19" @@ -3379,23 +3835,23 @@ dependencies = [ [[package]] name = "num_enum" -version = "0.5.11" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" +checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179" dependencies = [ "num_enum_derive", ] [[package]] name = "num_enum_derive" -version = "0.5.11" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" +checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" dependencies = [ - "proc-macro-crate", + "proc-macro-crate 2.0.0", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.96", ] [[package]] @@ -3653,7 +4109,7 @@ dependencies = [ name = "nym-network-defaults" version = "0.1.0" dependencies = [ - "cargo_metadata", + "cargo_metadata 0.18.1", "dotenvy", "log", "regex", @@ -3916,8 +4372,12 @@ dependencies = [ "tap", "tauri", "tauri-build", - "tauri-codegen", - "tauri-macros", + "tauri-codegen 1.2.1", + "tauri-macros 1.2.1", + "tauri-plugin-clipboard-manager", + "tauri-plugin-process", + "tauri-plugin-shell", + "tauri-plugin-updater", "tempfile", "thiserror 1.0.64", "time", @@ -3929,42 +4389,267 @@ dependencies = [ ] [[package]] -name = "objc" -version = "0.2.7" +name = "objc-sys" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" +checksum = "cdb91bdd390c7ce1a8607f35f3ca7151b65afc0ff5ff3b34fa350f7d7c7e4310" + +[[package]] +name = "objc2" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46a785d4eeff09c14c487497c162e92766fbb3e4059a71840cecc03d9a50b804" dependencies = [ - "malloc_buf", - "objc_exception", + "objc-sys", + "objc2-encode", ] [[package]] -name = "objc-foundation" +name = "objc2" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3531f65190d9cff863b77a99857e74c314dd16bf56c538c4b57c7cbc3f3a6e59" +dependencies = [ + "objc2-encode", + "objc2-exception-helper", +] + +[[package]] +name = "objc2-app-kit" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff" +dependencies = [ + "bitflags 2.9.0", + "block2 0.5.1", + "libc", + "objc2 0.5.2", + "objc2-core-data 0.2.2", + "objc2-core-image 0.2.2", + "objc2-foundation 0.2.2", + "objc2-quartz-core 0.2.2", +] + +[[package]] +name = "objc2-app-kit" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5906f93257178e2f7ae069efb89fbd6ee94f0592740b5f8a1512ca498814d0fb" +dependencies = [ + "bitflags 2.9.0", + "block2 0.6.0", + "libc", + "objc2 0.6.0", + "objc2-cloud-kit", + "objc2-core-data 0.3.0", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-core-image 0.3.0", + "objc2-foundation 0.3.0", + "objc2-quartz-core 0.3.0", +] + +[[package]] +name = "objc2-cloud-kit" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c1948a9be5f469deadbd6bcb86ad7ff9e47b4f632380139722f7d9840c0d42c" +dependencies = [ + "bitflags 2.9.0", + "objc2 0.6.0", + "objc2-foundation 0.3.0", +] + +[[package]] +name = "objc2-core-data" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef" +dependencies = [ + "bitflags 2.9.0", + "block2 0.5.1", + "objc2 0.5.2", + "objc2-foundation 0.2.2", +] + +[[package]] +name = "objc2-core-data" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f860f8e841f6d32f754836f51e6bc7777cd7e7053cf18528233f6811d3eceb4" +dependencies = [ + "bitflags 2.9.0", + "objc2 0.6.0", + "objc2-foundation 0.3.0", +] + +[[package]] +name = "objc2-core-foundation" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daeaf60f25471d26948a1c2f840e3f7d86f4109e3af4e8e4b5cd70c39690d925" +dependencies = [ + "bitflags 2.9.0", + "objc2 0.6.0", +] + +[[package]] +name = "objc2-core-graphics" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dca602628b65356b6513290a21a6405b4d4027b8b250f0b98dddbb28b7de02" +dependencies = [ + "bitflags 2.9.0", + "objc2 0.6.0", + "objc2-core-foundation", + "objc2-io-surface", +] + +[[package]] +name = "objc2-core-image" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55260963a527c99f1819c4f8e3b47fe04f9650694ef348ffd2227e8196d34c80" +dependencies = [ + "block2 0.5.1", + "objc2 0.5.2", + "objc2-foundation 0.2.2", + "objc2-metal", +] + +[[package]] +name = "objc2-core-image" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ffa6bea72bf42c78b0b34e89c0bafac877d5f80bf91e159a5d96ea7f693ca56" +dependencies = [ + "objc2 0.6.0", + "objc2-foundation 0.3.0", +] + +[[package]] +name = "objc2-encode" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" + +[[package]] +name = "objc2-exception-helper" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1add1b659e36c9607c7aab864a76c7a4c2760cd0cd2e120f3fb8b952c7e22bf9" -dependencies = [ - "block", - "objc", - "objc_id", -] - -[[package]] -name = "objc_exception" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad970fb455818ad6cba4c122ad012fae53ae8b4795f86378bce65e4f6bab2ca4" +checksum = "c7a1c5fbb72d7735b076bb47b578523aedc40f3c439bea6dfd595c089d79d98a" dependencies = [ "cc", ] [[package]] -name = "objc_id" -version = "0.1.1" +name = "objc2-foundation" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c92d4ddb4bd7b50d730c215ff871754d0da6b2178849f8a2a2ab69712d0c073b" +checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" dependencies = [ - "objc", + "bitflags 2.9.0", + "block2 0.5.1", + "libc", + "objc2 0.5.2", +] + +[[package]] +name = "objc2-foundation" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a21c6c9014b82c39515db5b396f91645182611c97d24637cf56ac01e5f8d998" +dependencies = [ + "bitflags 2.9.0", + "block2 0.6.0", + "libc", + "objc2 0.6.0", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-io-surface" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "161a8b87e32610086e1a7a9e9ec39f84459db7b3a0881c1f16ca5a2605581c19" +dependencies = [ + "bitflags 2.9.0", + "objc2 0.6.0", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-metal" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" +dependencies = [ + "bitflags 2.9.0", + "block2 0.5.1", + "objc2 0.5.2", + "objc2-foundation 0.2.2", +] + +[[package]] +name = "objc2-osa-kit" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1ac59da3ceebc4a82179b35dc550431ad9458f9cc326e053f49ba371ce76c5a" +dependencies = [ + "bitflags 2.9.0", + "objc2 0.6.0", + "objc2-app-kit 0.3.0", + "objc2-foundation 0.3.0", +] + +[[package]] +name = "objc2-quartz-core" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a" +dependencies = [ + "bitflags 2.9.0", + "block2 0.5.1", + "objc2 0.5.2", + "objc2-foundation 0.2.2", + "objc2-metal", +] + +[[package]] +name = "objc2-quartz-core" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fb3794501bb1bee12f08dcad8c61f2a5875791ad1c6f47faa71a0f033f20071" +dependencies = [ + "bitflags 2.9.0", + "objc2 0.6.0", + "objc2-foundation 0.3.0", +] + +[[package]] +name = "objc2-ui-kit" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "777a571be14a42a3990d4ebedaeb8b54cd17377ec21b92e8200ac03797b3bee1" +dependencies = [ + "bitflags 2.9.0", + "objc2 0.6.0", + "objc2-core-foundation", + "objc2-foundation 0.3.0", +] + +[[package]] +name = "objc2-web-kit" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b717127e4014b0f9f3e8bba3d3f2acec81f1bde01f656823036e823ed2c94dce" +dependencies = [ + "bitflags 2.9.0", + "block2 0.6.0", + "objc2 0.6.0", + "objc2-app-kit 0.3.0", + "objc2-core-foundation", + "objc2-foundation 0.3.0", ] [[package]] @@ -3990,12 +4675,14 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "open" -version = "3.2.0" +version = "5.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2078c0039e6a54a0c42c28faa984e115fb4c2d5bf2208f77d1961002df8576f8" +checksum = "e2483562e62ea94312f3576a7aca397306df7990b8d89033e18766744377ef95" dependencies = [ + "dunce", + "is-wsl", + "libc", "pathdiff", - "windows-sys 0.42.0", ] [[package]] @@ -4004,9 +4691,9 @@ version = "0.10.70" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61cfb4e166a8bb8c9b55c500bc2308550148ece889be90f609377e58140f42c6" dependencies = [ - "bitflags 2.4.0", + "bitflags 2.9.0", "cfg-if", - "foreign-types", + "foreign-types 0.3.2", "libc", "once_cell", "openssl-macros", @@ -4049,10 +4736,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" [[package]] -name = "overload" -version = "0.1.1" +name = "os_pipe" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" +checksum = "5ffd2b0a5634335b135d5728d84c5e0fd726954b87111f7506a61c502280d982" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + +[[package]] +name = "osakit" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "732c71caeaa72c065bb69d7ea08717bd3f4863a4f451402fc9513e29dbd5261b" +dependencies = [ + "objc2 0.6.0", + "objc2-foundation 0.3.0", + "objc2-osa-kit", + "serde", + "serde_json", + "thiserror 2.0.11", +] [[package]] name = "p256" @@ -4077,11 +4782,11 @@ dependencies = [ [[package]] name = "pango" -version = "0.15.10" +version = "0.18.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22e4045548659aee5313bde6c582b0d83a627b7904dd20dc2d9ef0895d414e4f" +checksum = "7ca27ec1eb0457ab26f3036ea52229edbdb74dee1edd29063f5b9b010e7ebee4" dependencies = [ - "bitflags 1.3.2", + "gio", "glib", "libc", "once_cell", @@ -4090,14 +4795,14 @@ dependencies = [ [[package]] name = "pango-sys" -version = "0.15.10" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2a00081cde4661982ed91d80ef437c20eacaf6aa1a5962c0279ae194662c3aa" +checksum = "436737e391a843e5933d6d9aa102cb126d501e815b83601365a948a518555dc5" dependencies = [ "glib-sys", "gobject-sys", "libc", - "system-deps 6.1.1", + "system-deps", ] [[package]] @@ -4142,9 +4847,9 @@ checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" [[package]] name = "pathdiff" -version = "0.2.1" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" +checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3" [[package]] name = "peg" @@ -4234,6 +4939,16 @@ dependencies = [ "sha2 0.10.8", ] +[[package]] +name = "petgraph" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" +dependencies = [ + "fixedbitset", + "indexmap 2.5.0", +] + [[package]] name = "phf" version = "0.8.0" @@ -4276,6 +4991,16 @@ dependencies = [ "phf_shared 0.8.0", ] +[[package]] +name = "phf_codegen" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb1c3a8bc4dd4e5cfce29b44ffc14bedd2ee294559a294e2a4d4c9e9a6a13cd" +dependencies = [ + "phf_generator 0.10.0", + "phf_shared 0.10.0", +] + [[package]] name = "phf_generator" version = "0.8.0" @@ -4431,22 +5156,22 @@ dependencies = [ "base64 0.21.4", "indexmap 1.9.3", "line-wrap", - "quick-xml", + "quick-xml 0.29.0", "serde", "time", ] [[package]] name = "png" -version = "0.17.9" +version = "0.17.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59871cc5b6cce7eaccca5a802b4173377a1c2ba90654246789a8fa2334426d11" +checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526" dependencies = [ "bitflags 1.3.2", "crc32fast", "fdeflate", "flate2", - "miniz_oxide 0.7.1", + "miniz_oxide 0.8.5", ] [[package]] @@ -4508,6 +5233,15 @@ dependencies = [ "toml_edit 0.19.14", ] +[[package]] +name = "proc-macro-crate" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e8366a6159044a37876a2b9817124296703c586a5c92e2c53751fa06d8d43e8" +dependencies = [ + "toml_edit 0.20.7", +] + [[package]] name = "proc-macro-error" version = "1.0.4" @@ -4613,6 +5347,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "quick-xml" +version = "0.37.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4ce8c88de324ff838700f36fb6ab86c96df0e3c4ab6ef3a9b2044465cce1369" +dependencies = [ + "memchr", +] + [[package]] name = "quote" version = "1.0.35" @@ -4622,6 +5365,12 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "r-efi" +version = "5.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" + [[package]] name = "rand" version = "0.7.3" @@ -4705,9 +5454,9 @@ dependencies = [ [[package]] name = "raw-window-handle" -version = "0.5.2" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9" +checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" [[package]] name = "rayon" @@ -4747,6 +5496,15 @@ dependencies = [ "bitflags 1.3.2", ] +[[package]] +name = "redox_syscall" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b8c0c260b63a8219631167be35e6a988e9554dbd323f8bd08439c8ed1302bd1" +dependencies = [ + "bitflags 2.9.0", +] + [[package]] name = "redox_users" version = "0.4.3" @@ -4758,6 +5516,17 @@ dependencies = [ "thiserror 1.0.64", ] +[[package]] +name = "redox_users" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd6f9d3d47bdd2ad6945c5015a226ec6155d0bcdfd8f7cd29f86b71f8de99d2b" +dependencies = [ + "getrandom 0.2.10", + "libredox", + "thiserror 2.0.11", +] + [[package]] name = "regex" version = "1.11.1" @@ -4766,17 +5535,8 @@ checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.9", - "regex-syntax 0.8.5", -] - -[[package]] -name = "regex-automata" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" -dependencies = [ - "regex-syntax 0.6.29", + "regex-automata", + "regex-syntax", ] [[package]] @@ -4787,15 +5547,9 @@ checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.8.5", + "regex-syntax", ] -[[package]] -name = "regex-syntax" -version = "0.6.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" - [[package]] name = "regex-syntax" version = "0.8.5" @@ -4886,6 +5640,7 @@ dependencies = [ "url", "wasm-bindgen", "wasm-bindgen-futures", + "wasm-streams", "web-sys", "webpki-roots 0.26.1", "winreg 0.52.0", @@ -4911,30 +5666,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "rfd" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0149778bd99b6959285b0933288206090c50e2327f47a9c463bfdbf45c8823ea" -dependencies = [ - "block", - "dispatch", - "glib-sys", - "gobject-sys", - "gtk-sys", - "js-sys", - "lazy_static", - "log", - "objc", - "objc-foundation", - "objc_id", - "raw-window-handle", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "windows 0.37.0", -] - [[package]] name = "ring" version = "0.17.13" @@ -5006,15 +5737,28 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.8" +version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19ed4fa021d81c8392ce04db050a3da9a60299050b7ae1cf482d862b54a7218f" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags 2.4.0", + "bitflags 2.9.0", "errno", "libc", - "linux-raw-sys", - "windows-sys 0.48.0", + "linux-raw-sys 0.4.15", + "windows-sys 0.59.0", +] + +[[package]] +name = "rustix" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a04a32fb43fcdef85b977ce7f77a150805e4b2ea1f2656898d4a547dde78df6" +dependencies = [ + "bitflags 2.9.0", + "errno", + "libc", + "linux-raw-sys 0.9.3", + "windows-sys 0.59.0", ] [[package]] @@ -5148,6 +5892,8 @@ dependencies = [ "schemars_derive", "serde", "serde_json", + "url", + "uuid 1.10.0", ] [[package]] @@ -5162,12 +5908,6 @@ dependencies = [ "syn 2.0.96", ] -[[package]] -name = "scoped-tls" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" - [[package]] name = "scopeguard" version = "1.2.0" @@ -5224,7 +5964,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" dependencies = [ "bitflags 1.3.2", - "core-foundation", + "core-foundation 0.9.3", "core-foundation-sys", "libc", "security-framework-sys", @@ -5253,7 +5993,7 @@ dependencies = [ "log", "matches", "phf 0.8.0", - "phf_codegen", + "phf_codegen 0.8.0", "precomputed-hash", "servo_arc", "smallvec", @@ -5287,6 +6027,17 @@ dependencies = [ "serde", ] +[[package]] +name = "serde-untagged" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "299d9c19d7d466db4ab10addd5703e4c615dec2a5a16dbbafe191045e87ee66e" +dependencies = [ + "erased-serde", + "serde", + "typeid", +] + [[package]] name = "serde_bytes" version = "0.11.17" @@ -5369,7 +6120,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "678b5a069e50bf00ecd22d0cd8ddf7c236f68581b03db652061ed5eb13a312ff" dependencies = [ "serde", - "serde_with_macros", + "serde_with_macros 1.5.2", +] + +[[package]] +name = "serde_with" +version = "3.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6b6f7f2fcb69f747921f79f3926bd1e203fce4fef62c268dd3abfb6d86029aa" +dependencies = [ + "base64 0.22.1", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.5.0", + "serde", + "serde_derive", + "serde_json", + "serde_with_macros 3.12.0", + "time", ] [[package]] @@ -5378,12 +6147,24 @@ version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e182d6ec6f05393cc0e5ed1bf81ad6db3a8feedf8ee515ecdd369809bcce8082" dependencies = [ - "darling", + "darling 0.13.4", "proc-macro2", "quote", "syn 1.0.109", ] +[[package]] +name = "serde_with_macros" +version = "3.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d00caa5193a3c8362ac2b73be6b9e768aa5a4b2f721d8f4b339600c3cb51f8e" +dependencies = [ + "darling 0.20.11", + "proc-macro2", + "quote", + "syn 2.0.96", +] + [[package]] name = "serdect" version = "0.2.0" @@ -5461,12 +6242,13 @@ dependencies = [ ] [[package]] -name = "sharded-slab" -version = "0.1.4" +name = "shared_child" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31" +checksum = "09fa9338aed9a1df411814a5b2252f7cd206c55ae9bf2fa763f8de84603aa60c" dependencies = [ - "lazy_static", + "libc", + "windows-sys 0.59.0", ] [[package]] @@ -5548,31 +6330,51 @@ dependencies = [ ] [[package]] -name = "soup2" -version = "0.2.1" +name = "softbuffer" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2b4d76501d8ba387cf0fefbe055c3e0a59891d09f0f995ae4e4b16f6b60f3c0" +checksum = "18051cdd562e792cad055119e0cdb2cfc137e44e3987532e0f9659a77931bb08" dependencies = [ - "bitflags 1.3.2", - "gio", - "glib", - "libc", - "once_cell", - "soup2-sys", + "bytemuck", + "cfg_aliases 0.2.1", + "core-graphics 0.24.0", + "foreign-types 0.5.0", + "js-sys", + "log", + "objc2 0.5.2", + "objc2-foundation 0.2.2", + "objc2-quartz-core 0.2.2", + "raw-window-handle", + "redox_syscall 0.5.10", + "wasm-bindgen", + "web-sys", + "windows-sys 0.59.0", ] [[package]] -name = "soup2-sys" -version = "0.2.0" +name = "soup3" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "009ef427103fcb17f802871647a7fa6c60cbb654b4c4e4c0ac60a31c5f6dc9cf" +checksum = "471f924a40f31251afc77450e781cb26d55c0b650842efafc9c6cbd2f7cc4f9f" +dependencies = [ + "futures-channel", + "gio", + "glib", + "libc", + "soup3-sys", +] + +[[package]] +name = "soup3-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ebe8950a680a12f24f15ebe1bf70db7af98ad242d9db43596ad3108aab86c27" dependencies = [ - "bitflags 1.3.2", "gio-sys", "glib-sys", "gobject-sys", "libc", - "system-deps 5.0.0", + "system-deps", ] [[package]] @@ -5591,15 +6393,6 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" -[[package]] -name = "state" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbe866e1e51e8260c9eed836a042a5e7f6726bb2b411dffeaa712e19c388f23b" -dependencies = [ - "loom", -] - [[package]] name = "static_assertions" version = "1.1.0" @@ -5638,6 +6431,12 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + [[package]] name = "strum" version = "0.23.0" @@ -5703,6 +6502,17 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "734676eb262c623cec13c3155096e08d1f8f29adce39ba17948b18dad1e54142" +[[package]] +name = "swift-rs" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4057c98e2e852d51fdcfca832aac7b571f6b351ad159f9eda5db1655f8d0c4d7" +dependencies = [ + "base64 0.21.4", + "serde", + "serde_json", +] + [[package]] name = "syn" version = "1.0.109" @@ -5749,7 +6559,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" dependencies = [ "bitflags 1.3.2", - "core-foundation", + "core-foundation 0.9.3", "system-configuration-sys", ] @@ -5763,56 +6573,35 @@ dependencies = [ "libc", ] -[[package]] -name = "system-deps" -version = "5.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18db855554db7bd0e73e06cf7ba3df39f97812cb11d3f75e71c39bf45171797e" -dependencies = [ - "cfg-expr 0.9.1", - "heck 0.3.3", - "pkg-config", - "toml 0.5.11", - "version-compare 0.0.11", -] - [[package]] name = "system-deps" version = "6.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "30c2de8a4d8f4b823d634affc9cd2a74ec98c53a756f317e529a48046cbf71f3" dependencies = [ - "cfg-expr 0.15.4", + "cfg-expr", "heck 0.4.1", "pkg-config", "toml 0.7.6", - "version-compare 0.1.1", + "version-compare", ] [[package]] name = "tao" -version = "0.15.8" +version = "0.32.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac8e6399427c8494f9849b58694754d7cc741293348a6836b6c8d2c5aa82d8e6" +checksum = "63c8b1020610b9138dd7b1e06cf259ae91aa05c30f3bd0d6b42a03997b92dec1" dependencies = [ - "bitflags 1.3.2", - "cairo-rs", - "cc", - "cocoa", - "core-foundation", - "core-graphics", + "bitflags 2.9.0", + "core-foundation 0.10.0", + "core-graphics 0.24.0", "crossbeam-channel", "dispatch", - "gdk", - "gdk-pixbuf", - "gdk-sys", + "dlopen2", + "dpi", + "gdkwayland-sys", "gdkx11-sys", - "gio", - "glib", - "glib-sys", "gtk", - "image", - "instant", "jni", "lazy_static", "libc", @@ -5820,21 +6609,33 @@ dependencies = [ "ndk", "ndk-context", "ndk-sys", - "objc", + "objc2 0.6.0", + "objc2-app-kit 0.3.0", + "objc2-foundation 0.3.0", "once_cell", "parking_lot", - "paste", - "png", "raw-window-handle", "scopeguard", - "serde", + "tao-macros", "unicode-segmentation", - "uuid 1.10.0", - "windows 0.39.0", - "windows-implement", + "url", + "windows 0.60.0", + "windows-core 0.60.1", + "windows-version", "x11-dl", ] +[[package]] +name = "tao-macros" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4e16beb8b2ac17db28eab8bca40e62dbfbb34c0fcdc6d9826b11b7b5d047dfd" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", +] + [[package]] name = "tap" version = "1.0.1" @@ -5843,9 +6644,9 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "tar" -version = "0.4.40" +version = "0.4.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b16afcea1f22891c49a00c751c7b63b2233284064f11a200fc624137c51e2ddb" +checksum = "1d863878d212c87a19c1a610eb53bb01fe12951c0501cf5a0d65f724914a667a" dependencies = [ "filetime", "libc", @@ -5860,71 +6661,74 @@ checksum = "9d0e916b1148c8e263850e1ebcbd046f333e0683c724876bb0da63ea4373dc8a" [[package]] name = "tauri" -version = "1.2.3" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b48820ee3bb6a5031a83b2b6e11f8630bdc5a2f68cb841ab8ebc7a15a916679" +checksum = "511dd38065a5d3b36c33cdba4362b99a40a5103bebcd4aebb930717e7c8ba292" dependencies = [ "anyhow", - "attohttpc", - "base64 0.13.1", - "cocoa", - "dirs-next", + "bytes", + "dirs 6.0.0", + "dunce", "embed_plist", - "encoding_rs", - "flate2", "futures-util", - "glib", + "getrandom 0.2.10", "glob", "gtk", - "heck 0.4.1", - "http 0.2.9", - "ignore", - "minisign-verify", - "objc", - "once_cell", - "open", + "heck 0.5.0", + "http 1.1.0", + "jni", + "libc", + "log", + "mime", + "muda", + "objc2 0.6.0", + "objc2-app-kit 0.3.0", + "objc2-foundation 0.3.0", "percent-encoding", - "rand 0.8.5", + "plist", "raw-window-handle", - "regex", - "rfd", - "semver", + "reqwest 0.12.4", "serde", "serde_json", "serde_repr", "serialize-to-javascript", - "state", - "tar", - "tauri-macros", + "swift-rs", + "tauri-build", + "tauri-macros 2.1.0", "tauri-runtime", "tauri-runtime-wry", - "tauri-utils", - "tempfile", - "thiserror 1.0.64", - "time", + "tauri-utils 2.3.0", + "thiserror 2.0.11", "tokio", + "tray-icon", "url", - "uuid 1.10.0", + "urlpattern", "webkit2gtk", "webview2-com", - "windows 0.39.0", - "zip", + "window-vibrancy", + "windows 0.60.0", ] [[package]] name = "tauri-build" -version = "1.2.1" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8807c85d656b2b93927c19fe5a5f1f1f348f96c2de8b90763b3c2d561511f9b4" +checksum = "7ffa8732a66f90903f5a585215f3cf1e87988d0359bc88c18a502efe7572c1de" dependencies = [ "anyhow", "cargo_toml", - "heck 0.4.1", - "json-patch", + "dirs 6.0.0", + "glob", + "heck 0.5.0", + "json-patch 3.0.1", + "schemars", "semver", + "serde", "serde_json", - "tauri-utils", - "winres", + "tauri-utils 2.3.0", + "tauri-winres", + "toml 0.8.20", + "walkdir", ] [[package]] @@ -5934,25 +6738,51 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14388d484b6b1b5dc0f6a7d6cc6433b3b230bec85eaa576adcdf3f9fafa49251" dependencies = [ "base64 0.13.1", - "brotli", - "ico", - "json-patch", + "brotli 3.3.4", + "ico 0.2.0", + "json-patch 0.2.7", "plist", "png", "proc-macro2", "quote", - "regex", "semver", "serde", "serde_json", "sha2 0.10.8", - "tauri-utils", + "tauri-utils 1.2.1", "thiserror 1.0.64", "time", "uuid 1.10.0", "walkdir", ] +[[package]] +name = "tauri-codegen" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c266a247f14d63f40c6282c2653a8bac5cc3d482ca562a003a88513653ea817a" +dependencies = [ + "base64 0.22.1", + "brotli 7.0.0", + "ico 0.4.0", + "json-patch 3.0.1", + "plist", + "png", + "proc-macro2", + "quote", + "semver", + "serde", + "serde_json", + "sha2 0.10.8", + "syn 2.0.96", + "tauri-utils 2.3.0", + "thiserror 2.0.11", + "time", + "url", + "uuid 1.10.0", + "walkdir", +] + [[package]] name = "tauri-macros" version = "1.2.1" @@ -5963,47 +6793,163 @@ dependencies = [ "proc-macro2", "quote", "syn 1.0.109", - "tauri-codegen", - "tauri-utils", + "tauri-codegen 1.2.1", + "tauri-utils 1.2.1", +] + +[[package]] +name = "tauri-macros" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f47a1cf94b3bd6c4dc37dce1a43fc96120ff29a91757f0ab3cf713c7ad846e7c" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "syn 2.0.96", + "tauri-codegen 2.1.0", + "tauri-utils 2.3.0", +] + +[[package]] +name = "tauri-plugin" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9972871fcbddf16618f70412d965d4d845cd4b76d03fff168709961ef71e5cdf" +dependencies = [ + "anyhow", + "glob", + "plist", + "schemars", + "serde", + "serde_json", + "tauri-utils 2.3.0", + "toml 0.8.20", + "walkdir", +] + +[[package]] +name = "tauri-plugin-clipboard-manager" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ab4cb42fdf745229b768802e9180920a4be63122cf87ed1c879103f7609d98e" +dependencies = [ + "arboard", + "log", + "serde", + "serde_json", + "tauri", + "tauri-plugin", + "thiserror 2.0.11", +] + +[[package]] +name = "tauri-plugin-process" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40cc553ab29581c8c43dfa5fb0c9d5aee8ba962ad3b42908eea26c79610441b7" +dependencies = [ + "tauri", + "tauri-plugin", +] + +[[package]] +name = "tauri-plugin-shell" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb2c50a63e60fb8925956cc5b7569f4b750ac197a4d39f13b8dd46ea8e2bad79" +dependencies = [ + "encoding_rs", + "log", + "open", + "os_pipe", + "regex", + "schemars", + "serde", + "serde_json", + "shared_child", + "tauri", + "tauri-plugin", + "thiserror 2.0.11", + "tokio", +] + +[[package]] +name = "tauri-plugin-updater" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31bfcfb4a8318008d2108ccfba439d8263cf48867baabf372cb0e9f24771896" +dependencies = [ + "base64 0.22.1", + "dirs 6.0.0", + "flate2", + "futures-util", + "http 1.1.0", + "infer 0.19.0", + "log", + "minisign-verify", + "osakit", + "percent-encoding", + "reqwest 0.12.4", + "semver", + "serde", + "serde_json", + "tar", + "tauri", + "tauri-plugin", + "tempfile", + "thiserror 2.0.11", + "time", + "tokio", + "url", + "windows-sys 0.59.0", + "zip", ] [[package]] name = "tauri-runtime" -version = "0.12.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c507d954d08ac8705d235bc70ec6975b9054fb95ff7823af72dbb04186596f3b" +checksum = "9e9c7bce5153f1ca7bc45eba37349b31ba50e975e28edc8b5766c5ec02b0b63a" dependencies = [ + "cookie", + "dpi", "gtk", - "http 0.2.9", - "http-range", - "rand 0.8.5", + "http 1.1.0", + "jni", "raw-window-handle", "serde", "serde_json", - "tauri-utils", - "thiserror 1.0.64", - "uuid 1.10.0", - "webview2-com", - "windows 0.39.0", + "tauri-utils 2.3.0", + "thiserror 2.0.11", + "url", + "windows 0.60.0", ] [[package]] name = "tauri-runtime-wry" -version = "0.12.2" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36b1c5764a41a13176a4599b5b7bd0881bea7d94dfe45e1e755f789b98317e30" +checksum = "087188020fd6facb8578fe9b38e81fa0fe5fb85744c73da51a299f94a530a1e3" dependencies = [ - "cocoa", "gtk", + "http 1.1.0", + "jni", + "log", + "objc2 0.6.0", + "objc2-app-kit 0.3.0", + "objc2-foundation 0.3.0", + "once_cell", "percent-encoding", - "rand 0.8.5", "raw-window-handle", + "softbuffer", + "tao", "tauri-runtime", - "tauri-utils", - "uuid 1.10.0", + "tauri-utils 2.3.0", + "url", "webkit2gtk", "webview2-com", - "windows 0.39.0", + "windows 0.60.0", "wry", ] @@ -6013,13 +6959,12 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5abbc109a6eb45127956ffcc26ef0e875d160150ac16cfa45d26a6b2871686f1" dependencies = [ - "brotli", - "ctor", - "glob", + "brotli 3.3.4", + "ctor 0.1.26", "heck 0.4.1", - "html5ever", - "infer", - "json-patch", + "html5ever 0.25.2", + "infer 0.7.0", + "json-patch 0.2.7", "kuchiki", "memchr", "phf 0.10.1", @@ -6028,24 +6973,71 @@ dependencies = [ "semver", "serde", "serde_json", - "serde_with", + "serde_with 1.14.0", "thiserror 1.0.64", "url", - "walkdir", "windows 0.39.0", ] [[package]] -name = "tempfile" -version = "3.7.1" +name = "tauri-utils" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc02fddf48964c42031a0b3fe0428320ecf3a73c401040fc0096f97794310651" +checksum = "82dcced4014e59af9790cc22f5d271df3be09ecd6728ec68861642553c8d01b7" +dependencies = [ + "anyhow", + "brotli 7.0.0", + "cargo_metadata 0.19.2", + "ctor 0.2.9", + "dunce", + "glob", + "html5ever 0.26.0", + "http 1.1.0", + "infer 0.19.0", + "json-patch 3.0.1", + "kuchikiki", + "log", + "memchr", + "phf 0.11.3", + "proc-macro2", + "quote", + "regex", + "schemars", + "semver", + "serde", + "serde-untagged", + "serde_json", + "serde_with 3.12.0", + "swift-rs", + "thiserror 2.0.11", + "toml 0.8.20", + "url", + "urlpattern", + "uuid 1.10.0", + "walkdir", +] + +[[package]] +name = "tauri-winres" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56eaa45f707bedf34d19312c26d350bc0f3c59a47e58e8adbeecdc850d2c13a0" +dependencies = [ + "embed-resource", + "toml 0.8.20", +] + +[[package]] +name = "tempfile" +version = "3.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7437ac7763b9b123ccf33c338a5cc1bac6f69b45a136c19bdd8a65e3916435bf" dependencies = [ - "cfg-if", "fastrand", - "redox_syscall 0.3.5", - "rustix", - "windows-sys 0.48.0", + "getrandom 0.3.2", + "once_cell", + "rustix 1.0.4", + "windows-sys 0.59.0", ] [[package]] @@ -6207,13 +7199,14 @@ dependencies = [ ] [[package]] -name = "thread_local" -version = "1.1.7" +name = "tiff" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" +checksum = "ba1310fcea54c6a9a4fd1aad794ecc02c31682f6bfbecdf460bf19533eed1e3e" dependencies = [ - "cfg-if", - "once_cell", + "flate2", + "jpeg-decoder", + "weezl", ] [[package]] @@ -6403,6 +7396,17 @@ dependencies = [ "winnow 0.5.10", ] +[[package]] +name = "toml_edit" +version = "0.20.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70f427fce4d84c72b5b732388bf4a9f4531b53f74e2887e3ecb2481f68f66d81" +dependencies = [ + "indexmap 2.5.0", + "toml_datetime", + "winnow 0.5.10", +] + [[package]] name = "toml_edit" version = "0.22.23" @@ -6474,36 +7478,41 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" dependencies = [ "once_cell", - "valuable", ] [[package]] -name = "tracing-log" -version = "0.1.3" +name = "tray-icon" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922" +checksum = "d433764348e7084bad2c5ea22c96c71b61b17afe3a11645710f533bd72b6a2b5" dependencies = [ - "lazy_static", - "log", - "tracing-core", -] - -[[package]] -name = "tracing-subscriber" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30a651bc37f915e81f087d86e62a18eec5f79550c7faff886f7090b4ea757c77" -dependencies = [ - "matchers", - "nu-ansi-term", + "crossbeam-channel", + "dirs 6.0.0", + "libappindicator", + "muda", + "objc2 0.6.0", + "objc2-app-kit 0.3.0", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-foundation 0.3.0", "once_cell", - "regex", - "sharded-slab", - "smallvec", - "thread_local", - "tracing", - "tracing-core", - "tracing-log", + "png", + "serde", + "thiserror 2.0.11", + "windows-sys 0.59.0", +] + +[[package]] +name = "tree_magic_mini" +version = "3.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aac5e8971f245c3389a5a76e648bfc80803ae066a1243a75db0064d7c1129d63" +dependencies = [ + "fnv", + "memchr", + "nom", + "once_cell", + "petgraph", ] [[package]] @@ -6544,6 +7553,12 @@ dependencies = [ "termcolor", ] +[[package]] +name = "typeid" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c" + [[package]] name = "typenum" version = "1.16.0" @@ -6556,6 +7571,47 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" +[[package]] +name = "unic-char-property" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221" +dependencies = [ + "unic-char-range", +] + +[[package]] +name = "unic-char-range" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc" + +[[package]] +name = "unic-common" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc" + +[[package]] +name = "unic-ucd-ident" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e230a37c0381caa9219d67cf063aa3a375ffed5bf541a452db16e744bdab6987" +dependencies = [ + "unic-char-property", + "unic-char-range", + "unic-ucd-version", +] + +[[package]] +name = "unic-ucd-version" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4" +dependencies = [ + "unic-common", +] + [[package]] name = "unicode-bidi" version = "0.3.13" @@ -6579,9 +7635,9 @@ dependencies = [ [[package]] name = "unicode-segmentation" -version = "1.10.1" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" [[package]] name = "unicode-xid" @@ -6617,6 +7673,18 @@ dependencies = [ "serde", ] +[[package]] +name = "urlpattern" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70acd30e3aa1450bc2eece896ce2ad0d178e9c079493819301573dae3c37ba6d" +dependencies = [ + "regex", + "serde", + "unic-ucd-ident", + "url", +] + [[package]] name = "utf-8" version = "0.7.6" @@ -6677,14 +7745,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" dependencies = [ "getrandom 0.2.10", + "serde", ] -[[package]] -name = "valuable" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" - [[package]] name = "vcpkg" version = "0.2.15" @@ -6698,7 +7761,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e27d6bdd219887a9eadd19e1c34f32e47fa332301184935c6d9bca26f3cca525" dependencies = [ "anyhow", - "cargo_metadata", + "cargo_metadata 0.18.1", "cfg-if", "regex", "rustc_version", @@ -6706,12 +7769,6 @@ dependencies = [ "time", ] -[[package]] -name = "version-compare" -version = "0.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c18c859eead79d8b95d09e4678566e8d70105c4e7b251f707a03df32442661b" - [[package]] name = "version-compare" version = "0.1.1" @@ -6724,6 +7781,26 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "vswhom" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be979b7f07507105799e854203b470ff7c78a1639e330a58f183b5fea574608b" +dependencies = [ + "libc", + "vswhom-sys", +] + +[[package]] +name = "vswhom-sys" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb067e4cbd1ff067d1df46c9194b5de0e98efd2810bbc95c5d5e5f25a3231150" +dependencies = [ + "cc", + "libc", +] + [[package]] name = "walkdir" version = "2.3.3" @@ -6755,6 +7832,15 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasi" +version = "0.14.2+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +dependencies = [ + "wit-bindgen-rt", +] + [[package]] name = "wasm-bindgen" version = "0.2.87" @@ -6821,6 +7907,19 @@ version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" +[[package]] +name = "wasm-streams" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e072d4e72f700fb3443d8fe94a39315df013eef1104903cdb0a2abd322bbecd" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + [[package]] name = "wasmtimer" version = "0.4.1" @@ -6835,6 +7934,76 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "wayland-backend" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7208998eaa3870dad37ec8836979581506e0c5c64c20c9e79e9d2a10d6f47bf" +dependencies = [ + "cc", + "downcast-rs", + "rustix 0.38.44", + "smallvec", + "wayland-sys", +] + +[[package]] +name = "wayland-client" +version = "0.31.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2120de3d33638aaef5b9f4472bff75f07c56379cf76ea320bd3a3d65ecaf73f" +dependencies = [ + "bitflags 2.9.0", + "rustix 0.38.44", + "wayland-backend", + "wayland-scanner", +] + +[[package]] +name = "wayland-protocols" +version = "0.31.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f81f365b8b4a97f422ac0e8737c438024b5951734506b0e1d775c73030561f4" +dependencies = [ + "bitflags 2.9.0", + "wayland-backend", + "wayland-client", + "wayland-scanner", +] + +[[package]] +name = "wayland-protocols-wlr" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad1f61b76b6c2d8742e10f9ba5c3737f6530b4c243132c2a2ccc8aa96fe25cd6" +dependencies = [ + "bitflags 2.9.0", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-scanner", +] + +[[package]] +name = "wayland-scanner" +version = "0.31.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "896fdafd5d28145fce7958917d69f2fd44469b1d4e861cb5961bcbeebc6d1484" +dependencies = [ + "proc-macro2", + "quick-xml 0.37.4", + "quote", +] + +[[package]] +name = "wayland-sys" +version = "0.31.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbcebb399c77d5aa9fa5db874806ee7b4eba4e73650948e8f93963f128896615" +dependencies = [ + "pkg-config", +] + [[package]] name = "web-sys" version = "0.3.64" @@ -6847,9 +8016,9 @@ dependencies = [ [[package]] name = "webkit2gtk" -version = "0.18.2" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8f859735e4a452aeb28c6c56a852967a8a76c8eb1cc32dbf931ad28a13d6370" +checksum = "76b1bc1e54c581da1e9f179d0b38512ba358fb1af2d634a1affe42e37172361a" dependencies = [ "bitflags 1.3.2", "cairo-rs", @@ -6865,20 +8034,18 @@ dependencies = [ "javascriptcore-rs", "libc", "once_cell", - "soup2", + "soup3", "webkit2gtk-sys", ] [[package]] name = "webkit2gtk-sys" -version = "0.18.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d76ca6ecc47aeba01ec61e480139dda143796abcae6f83bcddf50d6b5b1dcf3" +checksum = "62daa38afc514d1f8f12b8693d30d5993ff77ced33ce30cd04deebc267a6d57c" dependencies = [ - "atk-sys", "bitflags 1.3.2", "cairo-sys-rs", - "gdk-pixbuf-sys", "gdk-sys", "gio-sys", "glib-sys", @@ -6886,10 +8053,9 @@ dependencies = [ "gtk-sys", "javascriptcore-rs-sys", "libc", - "pango-sys", "pkg-config", - "soup2-sys", - "system-deps 6.1.1", + "soup3-sys", + "system-deps", ] [[package]] @@ -6909,42 +8075,46 @@ dependencies = [ [[package]] name = "webview2-com" -version = "0.19.1" +version = "0.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4a769c9f1a64a8734bde70caafac2b96cada12cd4aefa49196b3a386b8b4178" +checksum = "b0d606f600e5272b514dbb66539dd068211cc20155be8d3958201b4b5bd79ed3" dependencies = [ "webview2-com-macros", "webview2-com-sys", - "windows 0.39.0", - "windows-implement", + "windows 0.60.0", + "windows-core 0.60.1", + "windows-implement 0.59.0", + "windows-interface", ] [[package]] name = "webview2-com-macros" -version = "0.6.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaebe196c01691db62e9e4ca52c5ef1e4fd837dcae27dae3ada599b5a8fd05ac" +checksum = "1d228f15bba3b9d56dde8bddbee66fa24545bd17b48d5128ccf4a8742b18e431" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.96", ] [[package]] name = "webview2-com-sys" -version = "0.19.0" +version = "0.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aac48ef20ddf657755fdcda8dfed2a7b4fc7e4581acce6fe9b88c3d64f29dee7" +checksum = "bfb27fccd3c27f68e9a6af1bcf48c2d82534b8675b83608a4d81446d095a17ac" dependencies = [ - "regex", - "serde", - "serde_json", - "thiserror 1.0.64", - "windows 0.39.0", - "windows-bindgen", - "windows-metadata", + "thiserror 2.0.11", + "windows 0.60.0", + "windows-core 0.60.1", ] +[[package]] +name = "weezl" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082" + [[package]] name = "widestring" version = "1.1.0" @@ -6983,16 +8153,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] -name = "windows" -version = "0.37.0" +name = "window-vibrancy" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57b543186b344cc61c85b5aab0d2e3adf4e0f99bc076eff9aa5927bcc0b8a647" +checksum = "d9bec5a31f3f9362f2258fd0e9c9dd61a9ca432e7306cc78c444258f0dce9a9c" dependencies = [ - "windows_aarch64_msvc 0.37.0", - "windows_i686_gnu 0.37.0", - "windows_i686_msvc 0.37.0", - "windows_x86_64_gnu 0.37.0", - "windows_x86_64_msvc 0.37.0", + "objc2 0.6.0", + "objc2-app-kit 0.3.0", + "objc2-core-foundation", + "objc2-foundation 0.3.0", + "raw-window-handle", + "windows-sys 0.59.0", + "windows-version", ] [[package]] @@ -7001,7 +8173,7 @@ version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1c4bd0a50ac6020f65184721f758dba47bb9fbc2133df715ec74a237b26794a" dependencies = [ - "windows-implement", + "windows-implement 0.39.0", "windows_aarch64_msvc 0.39.0", "windows_i686_gnu 0.39.0", "windows_i686_msvc 0.39.0", @@ -7011,21 +8183,56 @@ dependencies = [ [[package]] name = "windows" -version = "0.48.0" +version = "0.60.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" +checksum = "ddf874e74c7a99773e62b1c671427abf01a425e77c3d3fb9fb1e4883ea934529" dependencies = [ - "windows-targets 0.48.1", + "windows-collections", + "windows-core 0.60.1", + "windows-future", + "windows-link", + "windows-numerics", ] [[package]] -name = "windows-bindgen" -version = "0.39.0" +name = "windows-collections" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68003dbd0e38abc0fb85b939240f4bce37c43a5981d3df37ccbaaa981b47cb41" +checksum = "5467f79cc1ba3f52ebb2ed41dbb459b8e7db636cc3429458d9a852e15bc24dec" dependencies = [ - "windows-metadata", - "windows-tokens", + "windows-core 0.60.1", +] + +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-core" +version = "0.60.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca21a92a9cae9bf4ccae5cf8368dce0837100ddf6e6d57936749e85f152f6247" +dependencies = [ + "windows-implement 0.59.0", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-future" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a787db4595e7eb80239b74ce8babfb1363d8e343ab072f2ffe901400c03349f0" +dependencies = [ + "windows-core 0.60.1", + "windows-link", ] [[package]] @@ -7039,24 +8246,68 @@ dependencies = [ ] [[package]] -name = "windows-metadata" -version = "0.39.0" +name = "windows-implement" +version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ee5e275231f07c6e240d14f34e1b635bf1faa1c76c57cfd59a5cdb9848e4278" +checksum = "83577b051e2f49a058c308f17f273b570a6a758386fc291b5f6a934dd84e48c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", +] + +[[package]] +name = "windows-interface" +version = "0.59.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", +] + +[[package]] +name = "windows-link" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" + +[[package]] +name = "windows-numerics" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "005dea54e2f6499f2cee279b8f703b3cf3b5734a2d8d21867c8f44003182eeed" +dependencies = [ + "windows-core 0.60.1", + "windows-link", +] + +[[package]] +name = "windows-result" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87fa48cc5d406560701792be122a10132491cff9d0aeb23583cc2dcafc847319" +dependencies = [ + "windows-link", +] [[package]] name = "windows-sys" -version = "0.42.0" +version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", + "windows-targets 0.42.2", ] [[package]] @@ -7077,6 +8328,30 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + [[package]] name = "windows-targets" version = "0.48.1" @@ -7114,6 +8389,15 @@ version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f838de2fe15fe6bac988e74b798f26499a8b21a9d97edec321e79b28d1d7f597" +[[package]] +name = "windows-version" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e04a5c6627e310a23ad2358483286c7df260c964eb2d003d8efd6d0f4e79265c" +dependencies = [ + "windows-link", +] + [[package]] name = "windows_aarch64_gnullvm" version = "0.42.2" @@ -7132,12 +8416,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" -[[package]] -name = "windows_aarch64_msvc" -version = "0.37.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2623277cb2d1c216ba3b578c0f3cf9cdebeddb6e66b1b218bb33596ea7769c3a" - [[package]] name = "windows_aarch64_msvc" version = "0.39.0" @@ -7162,12 +8440,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" -[[package]] -name = "windows_i686_gnu" -version = "0.37.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3925fd0b0b804730d44d4b6278c50f9699703ec49bcd628020f46f4ba07d9e1" - [[package]] name = "windows_i686_gnu" version = "0.39.0" @@ -7198,12 +8470,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" -[[package]] -name = "windows_i686_msvc" -version = "0.37.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce907ac74fe331b524c1298683efbf598bb031bc84d5e274db2083696d07c57c" - [[package]] name = "windows_i686_msvc" version = "0.39.0" @@ -7228,12 +8494,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" -[[package]] -name = "windows_x86_64_gnu" -version = "0.37.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2babfba0828f2e6b32457d5341427dcbb577ceef556273229959ac23a10af33d" - [[package]] name = "windows_x86_64_gnu" version = "0.39.0" @@ -7276,12 +8536,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" -[[package]] -name = "windows_x86_64_msvc" -version = "0.37.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4dd6dc7df2d84cf7b33822ed5b86318fb1781948e9663bacd047fc9dd52259d" - [[package]] name = "windows_x86_64_msvc" version = "0.39.0" @@ -7345,12 +8599,32 @@ dependencies = [ ] [[package]] -name = "winres" -version = "0.1.12" +name = "wit-bindgen-rt" +version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b68db261ef59e9e52806f688020631e987592bd83619edccda9c47d42cde4f6c" +checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" dependencies = [ - "toml 0.5.11", + "bitflags 2.9.0", +] + +[[package]] +name = "wl-clipboard-rs" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12b41773911497b18ca8553c3daaf8ec9fe9819caf93d451d3055f69de028adb" +dependencies = [ + "derive-new", + "libc", + "log", + "nix", + "os_pipe", + "tempfile", + "thiserror 1.0.64", + "tree_magic_mini", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-protocols-wlr", ] [[package]] @@ -7367,40 +8641,46 @@ checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" [[package]] name = "wry" -version = "0.23.4" +version = "0.50.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c1ad8e2424f554cc5bdebe8aa374ef5b433feff817aebabca0389961fc7ef98" +checksum = "b19b78efae8b853c6c817e8752fc1dbf9cab8a8ffe9c30f399bd750ccf0f0730" dependencies = [ - "base64 0.13.1", - "block", - "cocoa", - "core-graphics", + "base64 0.22.1", + "block2 0.6.0", + "cookie", "crossbeam-channel", + "dpi", "dunce", - "gdk", - "gio", - "glib", + "gdkx11", "gtk", - "html5ever", - "http 0.2.9", - "kuchiki", + "html5ever 0.26.0", + "http 1.1.0", + "javascriptcore-rs", + "jni", + "kuchikiki", "libc", - "log", - "objc", - "objc_id", + "ndk", + "objc2 0.6.0", + "objc2-app-kit 0.3.0", + "objc2-core-foundation", + "objc2-foundation 0.3.0", + "objc2-ui-kit", + "objc2-web-kit", "once_cell", - "serde", - "serde_json", + "percent-encoding", + "raw-window-handle", "sha2 0.10.8", - "soup2", - "tao", - "thiserror 1.0.64", + "soup3", + "tao-macros", + "thiserror 2.0.11", "url", "webkit2gtk", "webkit2gtk-sys", "webview2-com", - "windows 0.39.0", - "windows-implement", + "windows 0.60.0", + "windows-core 0.60.1", + "windows-version", + "x11-dl", ] [[package]] @@ -7424,6 +8704,23 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "x11rb" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d91ffca73ee7f68ce055750bf9f6eca0780b8c85eff9bc046a3b0da41755e12" +dependencies = [ + "gethostname", + "rustix 0.38.44", + "x11rb-protocol", +] + +[[package]] +name = "x11rb-protocol" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec107c4503ea0b4a98ef47356329af139c0a4f7750e621cf2973cd3385ebcb3d" + [[package]] name = "x25519-dalek" version = "2.0.0" @@ -7438,11 +8735,12 @@ dependencies = [ [[package]] name = "xattr" -version = "1.0.1" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4686009f71ff3e5c4dbcf1a282d0a44db3f021ba69350cd42086b3e5f1c6985" +checksum = "0d65cbf2f12c15564212d48f4e3dfb87923d25d611f2aed18f4cb23f0413d89e" dependencies = [ "libc", + "rustix 1.0.4", ] [[package]] @@ -7555,11 +8853,13 @@ dependencies = [ [[package]] name = "zip" -version = "0.6.6" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "760394e246e4c28189f19d488c058bf16f564016aefac5d32bb1f3b51d5e9261" +checksum = "27c03817464f64e23f6f37574b4fdc8cf65925b5bfd2b0f2aedf959791941f88" dependencies = [ - "byteorder", + "arbitrary", "crc32fast", "crossbeam-utils", + "indexmap 2.5.0", + "memchr", ] diff --git a/nym-wallet/package.json b/nym-wallet/package.json index af6580bc0d..71dcad14cf 100644 --- a/nym-wallet/package.json +++ b/nym-wallet/package.json @@ -21,6 +21,7 @@ "webpack:prod": "yarn webpack --progress --config webpack.prod.js" }, "dependencies": { + "@babel/helper-simple-access": "^7.25.9", "@emotion/react": "^11.7.0", "@emotion/styled": "^11.6.0", "@hookform/resolvers": "^2.8.0", @@ -33,7 +34,10 @@ "@nymproject/react": "^1.0.0", "@nymproject/types": "^1.0.0", "@storybook/react": "^6.5.15", - "@tauri-apps/api": "^1.2.0", + "@tauri-apps/api": "^2.4.0", + "@tauri-apps/cli": "^2.4.0", + "@tauri-apps/plugin-clipboard-manager": "^2.2.2", + "@tauri-apps/plugin-updater": "^2.0.0", "@tauri-apps/tauri-forage": "^1.0.0-beta.2", "big.js": "^6.2.1", "bs58": "^4.0.1", @@ -67,7 +71,7 @@ "@pmmmwh/react-refresh-webpack-plugin": "^0.5.4", "@storybook/react": "^6.5.15", "@svgr/webpack": "^6.1.1", - "@tauri-apps/cli": "^1.0.5", + "@tauri-apps/cli": "^2.4.0", "@testing-library/jest-dom": "^5.14.1", "@testing-library/react": "^12.0.0", "@types/big.js": "^6.1.6", @@ -125,4 +129,4 @@ "webpack-merge": "^5.8.0" }, "private": false -} \ No newline at end of file +} diff --git a/nym-wallet/src-tauri/Cargo.toml b/nym-wallet/src-tauri/Cargo.toml index 19bc4f130e..6dc56f2dbe 100644 --- a/nym-wallet/src-tauri/Cargo.toml +++ b/nym-wallet/src-tauri/Cargo.toml @@ -13,13 +13,17 @@ rust-version = "1.76" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [build-dependencies] -tauri-build = { version = "=1.2.1", features = [] } +tauri-build = { version = "2", features = [] } tauri-codegen = "=1.2.1" tauri-macros = "=1.2.1" [dependencies] async-trait = "0.1.68" +tauri-plugin-updater = "2" +tauri-plugin-clipboard-manager = "2" +tauri-plugin-shell = "2" +tauri-plugin-process = "2" bip39 = { version = "2.0.0", features = ["zeroize", "rand"] } cfg-if = "1.0.0" colored = "2.0" @@ -38,7 +42,7 @@ serde_json = "1.0" serde_repr = "0.1" strum = { version = "0.23", features = ["derive"] } tap = "1" -tauri = { version = "=1.2.3", features = ["clipboard-all", "shell-open", "updater", "window-maximize", "window-print"] } +tauri = { version = "2", features = [] } #tendermint-rpc = "0.23.0" time = { version = "0.3.30", features = ["local-offset"] } thiserror = "1.0" diff --git a/nym-wallet/src-tauri/capabilities/migrated.json b/nym-wallet/src-tauri/capabilities/migrated.json new file mode 100644 index 0000000000..b55bbbbd82 --- /dev/null +++ b/nym-wallet/src-tauri/capabilities/migrated.json @@ -0,0 +1,11 @@ +{ + "identifier": "migrated", + "description": "permissions that were migrated from v1", + "local": true, + "windows": [ + "main" + ], + "permissions": [ + "core:default" + ] +} \ No newline at end of file diff --git a/nym-wallet/src-tauri/gen/schemas/capabilities.json b/nym-wallet/src-tauri/gen/schemas/capabilities.json new file mode 100644 index 0000000000..0ea61d3fbc --- /dev/null +++ b/nym-wallet/src-tauri/gen/schemas/capabilities.json @@ -0,0 +1 @@ +{"migrated":{"identifier":"migrated","description":"permissions that were migrated from v1","local":true,"windows":["main"],"permissions":["core:default"]}} \ No newline at end of file diff --git a/nym-wallet/src-tauri/gen/schemas/desktop-schema.json b/nym-wallet/src-tauri/gen/schemas/desktop-schema.json new file mode 100644 index 0000000000..5ff69983d7 --- /dev/null +++ b/nym-wallet/src-tauri/gen/schemas/desktop-schema.json @@ -0,0 +1,2279 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "CapabilityFile", + "description": "Capability formats accepted in a capability file.", + "anyOf": [ + { + "description": "A single capability.", + "allOf": [ + { + "$ref": "#/definitions/Capability" + } + ] + }, + { + "description": "A list of capabilities.", + "type": "array", + "items": { + "$ref": "#/definitions/Capability" + } + }, + { + "description": "A list of capabilities.", + "type": "object", + "required": [ + "capabilities" + ], + "properties": { + "capabilities": { + "description": "The list of capabilities.", + "type": "array", + "items": { + "$ref": "#/definitions/Capability" + } + } + } + } + ], + "definitions": { + "Capability": { + "description": "A grouping and boundary mechanism developers can use to isolate access to the IPC layer.\n\nIt controls application windows' and webviews' fine grained access to the Tauri core, application, or plugin commands. If a webview or its window is not matching any capability then it has no access to the IPC layer at all.\n\nThis can be done to create groups of windows, based on their required system access, which can reduce impact of frontend vulnerabilities in less privileged windows. Windows can be added to a capability by exact name (e.g. `main-window`) or glob patterns like `*` or `admin-*`. A Window can have none, one, or multiple associated capabilities.\n\n## Example\n\n```json { \"identifier\": \"main-user-files-write\", \"description\": \"This capability allows the `main` window on macOS and Windows access to `filesystem` write related commands and `dialog` commands to enable programatic access to files selected by the user.\", \"windows\": [ \"main\" ], \"permissions\": [ \"core:default\", \"dialog:open\", { \"identifier\": \"fs:allow-write-text-file\", \"allow\": [{ \"path\": \"$HOME/test.txt\" }] }, ], \"platforms\": [\"macOS\",\"windows\"] } ```", + "type": "object", + "required": [ + "identifier", + "permissions" + ], + "properties": { + "identifier": { + "description": "Identifier of the capability.\n\n## Example\n\n`main-user-files-write`", + "type": "string" + }, + "description": { + "description": "Description of what the capability is intended to allow on associated windows.\n\nIt should contain a description of what the grouped permissions should allow.\n\n## Example\n\nThis capability allows the `main` window access to `filesystem` write related commands and `dialog` commands to enable programatic access to files selected by the user.", + "default": "", + "type": "string" + }, + "remote": { + "description": "Configure remote URLs that can use the capability permissions.\n\nThis setting is optional and defaults to not being set, as our default use case is that the content is served from our local application.\n\n:::caution Make sure you understand the security implications of providing remote sources with local system access. :::\n\n## Example\n\n```json { \"urls\": [\"https://*.mydomain.dev\"] } ```", + "anyOf": [ + { + "$ref": "#/definitions/CapabilityRemote" + }, + { + "type": "null" + } + ] + }, + "local": { + "description": "Whether this capability is enabled for local app URLs or not. Defaults to `true`.", + "default": true, + "type": "boolean" + }, + "windows": { + "description": "List of windows that are affected by this capability. Can be a glob pattern.\n\nIf a window label matches any of the patterns in this list, the capability will be enabled on all the webviews of that window, regardless of the value of [`Self::webviews`].\n\nOn multiwebview windows, prefer specifying [`Self::webviews`] and omitting [`Self::windows`] for a fine grained access control.\n\n## Example\n\n`[\"main\"]`", + "type": "array", + "items": { + "type": "string" + } + }, + "webviews": { + "description": "List of webviews that are affected by this capability. Can be a glob pattern.\n\nThe capability will be enabled on all the webviews whose label matches any of the patterns in this list, regardless of whether the webview's window label matches a pattern in [`Self::windows`].\n\n## Example\n\n`[\"sub-webview-one\", \"sub-webview-two\"]`", + "type": "array", + "items": { + "type": "string" + } + }, + "permissions": { + "description": "List of permissions attached to this capability.\n\nMust include the plugin name as prefix in the form of `${plugin-name}:${permission-name}`. For commands directly implemented in the application itself only `${permission-name}` is required.\n\n## Example\n\n```json [ \"core:default\", \"shell:allow-open\", \"dialog:open\", { \"identifier\": \"fs:allow-write-text-file\", \"allow\": [{ \"path\": \"$HOME/test.txt\" }] } ] ```", + "type": "array", + "items": { + "$ref": "#/definitions/PermissionEntry" + }, + "uniqueItems": true + }, + "platforms": { + "description": "Limit which target platforms this capability applies to.\n\nBy default all platforms are targeted.\n\n## Example\n\n`[\"macOS\",\"windows\"]`", + "type": [ + "array", + "null" + ], + "items": { + "$ref": "#/definitions/Target" + } + } + } + }, + "CapabilityRemote": { + "description": "Configuration for remote URLs that are associated with the capability.", + "type": "object", + "required": [ + "urls" + ], + "properties": { + "urls": { + "description": "Remote domains this capability refers to using the [URLPattern standard](https://urlpattern.spec.whatwg.org/).\n\n## Examples\n\n- \"https://*.mydomain.dev\": allows subdomains of mydomain.dev - \"https://mydomain.dev/api/*\": allows any subpath of mydomain.dev/api", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "PermissionEntry": { + "description": "An entry for a permission value in a [`Capability`] can be either a raw permission [`Identifier`] or an object that references a permission and extends its scope.", + "anyOf": [ + { + "description": "Reference a permission or permission set by identifier.", + "allOf": [ + { + "$ref": "#/definitions/Identifier" + } + ] + }, + { + "description": "Reference a permission or permission set by identifier and extends its scope.", + "type": "object", + "allOf": [ + { + "if": { + "properties": { + "identifier": { + "anyOf": [ + { + "description": "This permission set configures which\nshell functionality is exposed by default.\n\n#### Granted Permissions\n\nIt allows to use the `open` functionality without any specific\nscope pre-configured. It will allow opening `http(s)://`,\n`tel:` and `mailto:` links.\n", + "type": "string", + "const": "shell:default" + }, + { + "description": "Enables the execute command without any pre-configured scope.", + "type": "string", + "const": "shell:allow-execute" + }, + { + "description": "Enables the kill command without any pre-configured scope.", + "type": "string", + "const": "shell:allow-kill" + }, + { + "description": "Enables the open command without any pre-configured scope.", + "type": "string", + "const": "shell:allow-open" + }, + { + "description": "Enables the spawn command without any pre-configured scope.", + "type": "string", + "const": "shell:allow-spawn" + }, + { + "description": "Enables the stdin_write command without any pre-configured scope.", + "type": "string", + "const": "shell:allow-stdin-write" + }, + { + "description": "Denies the execute command without any pre-configured scope.", + "type": "string", + "const": "shell:deny-execute" + }, + { + "description": "Denies the kill command without any pre-configured scope.", + "type": "string", + "const": "shell:deny-kill" + }, + { + "description": "Denies the open command without any pre-configured scope.", + "type": "string", + "const": "shell:deny-open" + }, + { + "description": "Denies the spawn command without any pre-configured scope.", + "type": "string", + "const": "shell:deny-spawn" + }, + { + "description": "Denies the stdin_write command without any pre-configured scope.", + "type": "string", + "const": "shell:deny-stdin-write" + } + ] + } + } + }, + "then": { + "properties": { + "allow": { + "items": { + "title": "ShellScopeEntry", + "description": "Shell scope entry.", + "anyOf": [ + { + "type": "object", + "required": [ + "cmd", + "name" + ], + "properties": { + "args": { + "description": "The allowed arguments for the command execution.", + "allOf": [ + { + "$ref": "#/definitions/ShellScopeEntryAllowedArgs" + } + ] + }, + "cmd": { + "description": "The command name. It can start with a variable that resolves to a system base directory. The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`, `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`, `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`, `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.", + "type": "string" + }, + "name": { + "description": "The name for this allowed shell command configuration.\n\nThis name will be used inside of the webview API to call this command along with any specified arguments.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "name", + "sidecar" + ], + "properties": { + "args": { + "description": "The allowed arguments for the command execution.", + "allOf": [ + { + "$ref": "#/definitions/ShellScopeEntryAllowedArgs" + } + ] + }, + "name": { + "description": "The name for this allowed shell command configuration.\n\nThis name will be used inside of the webview API to call this command along with any specified arguments.", + "type": "string" + }, + "sidecar": { + "description": "If this command is a sidecar command.", + "type": "boolean" + } + }, + "additionalProperties": false + } + ] + } + }, + "deny": { + "items": { + "title": "ShellScopeEntry", + "description": "Shell scope entry.", + "anyOf": [ + { + "type": "object", + "required": [ + "cmd", + "name" + ], + "properties": { + "args": { + "description": "The allowed arguments for the command execution.", + "allOf": [ + { + "$ref": "#/definitions/ShellScopeEntryAllowedArgs" + } + ] + }, + "cmd": { + "description": "The command name. It can start with a variable that resolves to a system base directory. The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`, `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`, `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`, `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.", + "type": "string" + }, + "name": { + "description": "The name for this allowed shell command configuration.\n\nThis name will be used inside of the webview API to call this command along with any specified arguments.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "name", + "sidecar" + ], + "properties": { + "args": { + "description": "The allowed arguments for the command execution.", + "allOf": [ + { + "$ref": "#/definitions/ShellScopeEntryAllowedArgs" + } + ] + }, + "name": { + "description": "The name for this allowed shell command configuration.\n\nThis name will be used inside of the webview API to call this command along with any specified arguments.", + "type": "string" + }, + "sidecar": { + "description": "If this command is a sidecar command.", + "type": "boolean" + } + }, + "additionalProperties": false + } + ] + } + } + } + }, + "properties": { + "identifier": { + "description": "Identifier of the permission or permission set.", + "allOf": [ + { + "$ref": "#/definitions/Identifier" + } + ] + } + } + }, + { + "properties": { + "identifier": { + "description": "Identifier of the permission or permission set.", + "allOf": [ + { + "$ref": "#/definitions/Identifier" + } + ] + }, + "allow": { + "description": "Data that defines what is allowed by the scope.", + "type": [ + "array", + "null" + ], + "items": { + "$ref": "#/definitions/Value" + } + }, + "deny": { + "description": "Data that defines what is denied by the scope. This should be prioritized by validation logic.", + "type": [ + "array", + "null" + ], + "items": { + "$ref": "#/definitions/Value" + } + } + } + } + ], + "required": [ + "identifier" + ] + } + ] + }, + "Identifier": { + "description": "Permission identifier", + "oneOf": [ + { + "description": "No features are enabled by default, as we believe\nthe clipboard can be inherently dangerous and it is \napplication specific if read and/or write access is needed.\n\nClipboard interaction needs to be explicitly enabled.\n", + "type": "string", + "const": "clipboard-manager:default" + }, + { + "description": "Enables the clear command without any pre-configured scope.", + "type": "string", + "const": "clipboard-manager:allow-clear" + }, + { + "description": "Enables the read_image command without any pre-configured scope.", + "type": "string", + "const": "clipboard-manager:allow-read-image" + }, + { + "description": "Enables the read_text command without any pre-configured scope.", + "type": "string", + "const": "clipboard-manager:allow-read-text" + }, + { + "description": "Enables the write_html command without any pre-configured scope.", + "type": "string", + "const": "clipboard-manager:allow-write-html" + }, + { + "description": "Enables the write_image command without any pre-configured scope.", + "type": "string", + "const": "clipboard-manager:allow-write-image" + }, + { + "description": "Enables the write_text command without any pre-configured scope.", + "type": "string", + "const": "clipboard-manager:allow-write-text" + }, + { + "description": "Denies the clear command without any pre-configured scope.", + "type": "string", + "const": "clipboard-manager:deny-clear" + }, + { + "description": "Denies the read_image command without any pre-configured scope.", + "type": "string", + "const": "clipboard-manager:deny-read-image" + }, + { + "description": "Denies the read_text command without any pre-configured scope.", + "type": "string", + "const": "clipboard-manager:deny-read-text" + }, + { + "description": "Denies the write_html command without any pre-configured scope.", + "type": "string", + "const": "clipboard-manager:deny-write-html" + }, + { + "description": "Denies the write_image command without any pre-configured scope.", + "type": "string", + "const": "clipboard-manager:deny-write-image" + }, + { + "description": "Denies the write_text command without any pre-configured scope.", + "type": "string", + "const": "clipboard-manager:deny-write-text" + }, + { + "description": "Default core plugins set which includes:\n- 'core:path:default'\n- 'core:event:default'\n- 'core:window:default'\n- 'core:webview:default'\n- 'core:app:default'\n- 'core:image:default'\n- 'core:resources:default'\n- 'core:menu:default'\n- 'core:tray:default'\n", + "type": "string", + "const": "core:default" + }, + { + "description": "Default permissions for the plugin.", + "type": "string", + "const": "core:app:default" + }, + { + "description": "Enables the app_hide command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-app-hide" + }, + { + "description": "Enables the app_show command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-app-show" + }, + { + "description": "Enables the default_window_icon command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-default-window-icon" + }, + { + "description": "Enables the fetch_data_store_identifiers command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-fetch-data-store-identifiers" + }, + { + "description": "Enables the identifier command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-identifier" + }, + { + "description": "Enables the name command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-name" + }, + { + "description": "Enables the remove_data_store command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-remove-data-store" + }, + { + "description": "Enables the set_app_theme command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-set-app-theme" + }, + { + "description": "Enables the tauri_version command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-tauri-version" + }, + { + "description": "Enables the version command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-version" + }, + { + "description": "Denies the app_hide command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-app-hide" + }, + { + "description": "Denies the app_show command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-app-show" + }, + { + "description": "Denies the default_window_icon command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-default-window-icon" + }, + { + "description": "Denies the fetch_data_store_identifiers command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-fetch-data-store-identifiers" + }, + { + "description": "Denies the identifier command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-identifier" + }, + { + "description": "Denies the name command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-name" + }, + { + "description": "Denies the remove_data_store command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-remove-data-store" + }, + { + "description": "Denies the set_app_theme command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-set-app-theme" + }, + { + "description": "Denies the tauri_version command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-tauri-version" + }, + { + "description": "Denies the version command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-version" + }, + { + "description": "Default permissions for the plugin.", + "type": "string", + "const": "core:event:default" + }, + { + "description": "Enables the emit command without any pre-configured scope.", + "type": "string", + "const": "core:event:allow-emit" + }, + { + "description": "Enables the emit_to command without any pre-configured scope.", + "type": "string", + "const": "core:event:allow-emit-to" + }, + { + "description": "Enables the listen command without any pre-configured scope.", + "type": "string", + "const": "core:event:allow-listen" + }, + { + "description": "Enables the unlisten command without any pre-configured scope.", + "type": "string", + "const": "core:event:allow-unlisten" + }, + { + "description": "Denies the emit command without any pre-configured scope.", + "type": "string", + "const": "core:event:deny-emit" + }, + { + "description": "Denies the emit_to command without any pre-configured scope.", + "type": "string", + "const": "core:event:deny-emit-to" + }, + { + "description": "Denies the listen command without any pre-configured scope.", + "type": "string", + "const": "core:event:deny-listen" + }, + { + "description": "Denies the unlisten command without any pre-configured scope.", + "type": "string", + "const": "core:event:deny-unlisten" + }, + { + "description": "Default permissions for the plugin.", + "type": "string", + "const": "core:image:default" + }, + { + "description": "Enables the from_bytes command without any pre-configured scope.", + "type": "string", + "const": "core:image:allow-from-bytes" + }, + { + "description": "Enables the from_path command without any pre-configured scope.", + "type": "string", + "const": "core:image:allow-from-path" + }, + { + "description": "Enables the new command without any pre-configured scope.", + "type": "string", + "const": "core:image:allow-new" + }, + { + "description": "Enables the rgba command without any pre-configured scope.", + "type": "string", + "const": "core:image:allow-rgba" + }, + { + "description": "Enables the size command without any pre-configured scope.", + "type": "string", + "const": "core:image:allow-size" + }, + { + "description": "Denies the from_bytes command without any pre-configured scope.", + "type": "string", + "const": "core:image:deny-from-bytes" + }, + { + "description": "Denies the from_path command without any pre-configured scope.", + "type": "string", + "const": "core:image:deny-from-path" + }, + { + "description": "Denies the new command without any pre-configured scope.", + "type": "string", + "const": "core:image:deny-new" + }, + { + "description": "Denies the rgba command without any pre-configured scope.", + "type": "string", + "const": "core:image:deny-rgba" + }, + { + "description": "Denies the size command without any pre-configured scope.", + "type": "string", + "const": "core:image:deny-size" + }, + { + "description": "Default permissions for the plugin.", + "type": "string", + "const": "core:menu:default" + }, + { + "description": "Enables the append command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-append" + }, + { + "description": "Enables the create_default command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-create-default" + }, + { + "description": "Enables the get command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-get" + }, + { + "description": "Enables the insert command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-insert" + }, + { + "description": "Enables the is_checked command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-is-checked" + }, + { + "description": "Enables the is_enabled command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-is-enabled" + }, + { + "description": "Enables the items command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-items" + }, + { + "description": "Enables the new command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-new" + }, + { + "description": "Enables the popup command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-popup" + }, + { + "description": "Enables the prepend command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-prepend" + }, + { + "description": "Enables the remove command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-remove" + }, + { + "description": "Enables the remove_at command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-remove-at" + }, + { + "description": "Enables the set_accelerator command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-set-accelerator" + }, + { + "description": "Enables the set_as_app_menu command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-set-as-app-menu" + }, + { + "description": "Enables the set_as_help_menu_for_nsapp command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-set-as-help-menu-for-nsapp" + }, + { + "description": "Enables the set_as_window_menu command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-set-as-window-menu" + }, + { + "description": "Enables the set_as_windows_menu_for_nsapp command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-set-as-windows-menu-for-nsapp" + }, + { + "description": "Enables the set_checked command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-set-checked" + }, + { + "description": "Enables the set_enabled command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-set-enabled" + }, + { + "description": "Enables the set_icon command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-set-icon" + }, + { + "description": "Enables the set_text command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-set-text" + }, + { + "description": "Enables the text command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-text" + }, + { + "description": "Denies the append command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-append" + }, + { + "description": "Denies the create_default command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-create-default" + }, + { + "description": "Denies the get command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-get" + }, + { + "description": "Denies the insert command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-insert" + }, + { + "description": "Denies the is_checked command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-is-checked" + }, + { + "description": "Denies the is_enabled command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-is-enabled" + }, + { + "description": "Denies the items command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-items" + }, + { + "description": "Denies the new command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-new" + }, + { + "description": "Denies the popup command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-popup" + }, + { + "description": "Denies the prepend command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-prepend" + }, + { + "description": "Denies the remove command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-remove" + }, + { + "description": "Denies the remove_at command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-remove-at" + }, + { + "description": "Denies the set_accelerator command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-set-accelerator" + }, + { + "description": "Denies the set_as_app_menu command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-set-as-app-menu" + }, + { + "description": "Denies the set_as_help_menu_for_nsapp command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-set-as-help-menu-for-nsapp" + }, + { + "description": "Denies the set_as_window_menu command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-set-as-window-menu" + }, + { + "description": "Denies the set_as_windows_menu_for_nsapp command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-set-as-windows-menu-for-nsapp" + }, + { + "description": "Denies the set_checked command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-set-checked" + }, + { + "description": "Denies the set_enabled command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-set-enabled" + }, + { + "description": "Denies the set_icon command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-set-icon" + }, + { + "description": "Denies the set_text command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-set-text" + }, + { + "description": "Denies the text command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-text" + }, + { + "description": "Default permissions for the plugin.", + "type": "string", + "const": "core:path:default" + }, + { + "description": "Enables the basename command without any pre-configured scope.", + "type": "string", + "const": "core:path:allow-basename" + }, + { + "description": "Enables the dirname command without any pre-configured scope.", + "type": "string", + "const": "core:path:allow-dirname" + }, + { + "description": "Enables the extname command without any pre-configured scope.", + "type": "string", + "const": "core:path:allow-extname" + }, + { + "description": "Enables the is_absolute command without any pre-configured scope.", + "type": "string", + "const": "core:path:allow-is-absolute" + }, + { + "description": "Enables the join command without any pre-configured scope.", + "type": "string", + "const": "core:path:allow-join" + }, + { + "description": "Enables the normalize command without any pre-configured scope.", + "type": "string", + "const": "core:path:allow-normalize" + }, + { + "description": "Enables the resolve command without any pre-configured scope.", + "type": "string", + "const": "core:path:allow-resolve" + }, + { + "description": "Enables the resolve_directory command without any pre-configured scope.", + "type": "string", + "const": "core:path:allow-resolve-directory" + }, + { + "description": "Denies the basename command without any pre-configured scope.", + "type": "string", + "const": "core:path:deny-basename" + }, + { + "description": "Denies the dirname command without any pre-configured scope.", + "type": "string", + "const": "core:path:deny-dirname" + }, + { + "description": "Denies the extname command without any pre-configured scope.", + "type": "string", + "const": "core:path:deny-extname" + }, + { + "description": "Denies the is_absolute command without any pre-configured scope.", + "type": "string", + "const": "core:path:deny-is-absolute" + }, + { + "description": "Denies the join command without any pre-configured scope.", + "type": "string", + "const": "core:path:deny-join" + }, + { + "description": "Denies the normalize command without any pre-configured scope.", + "type": "string", + "const": "core:path:deny-normalize" + }, + { + "description": "Denies the resolve command without any pre-configured scope.", + "type": "string", + "const": "core:path:deny-resolve" + }, + { + "description": "Denies the resolve_directory command without any pre-configured scope.", + "type": "string", + "const": "core:path:deny-resolve-directory" + }, + { + "description": "Default permissions for the plugin.", + "type": "string", + "const": "core:resources:default" + }, + { + "description": "Enables the close command without any pre-configured scope.", + "type": "string", + "const": "core:resources:allow-close" + }, + { + "description": "Denies the close command without any pre-configured scope.", + "type": "string", + "const": "core:resources:deny-close" + }, + { + "description": "Default permissions for the plugin.", + "type": "string", + "const": "core:tray:default" + }, + { + "description": "Enables the get_by_id command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-get-by-id" + }, + { + "description": "Enables the new command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-new" + }, + { + "description": "Enables the remove_by_id command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-remove-by-id" + }, + { + "description": "Enables the set_icon command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-set-icon" + }, + { + "description": "Enables the set_icon_as_template command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-set-icon-as-template" + }, + { + "description": "Enables the set_menu command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-set-menu" + }, + { + "description": "Enables the set_show_menu_on_left_click command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-set-show-menu-on-left-click" + }, + { + "description": "Enables the set_temp_dir_path command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-set-temp-dir-path" + }, + { + "description": "Enables the set_title command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-set-title" + }, + { + "description": "Enables the set_tooltip command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-set-tooltip" + }, + { + "description": "Enables the set_visible command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-set-visible" + }, + { + "description": "Denies the get_by_id command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-get-by-id" + }, + { + "description": "Denies the new command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-new" + }, + { + "description": "Denies the remove_by_id command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-remove-by-id" + }, + { + "description": "Denies the set_icon command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-set-icon" + }, + { + "description": "Denies the set_icon_as_template command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-set-icon-as-template" + }, + { + "description": "Denies the set_menu command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-set-menu" + }, + { + "description": "Denies the set_show_menu_on_left_click command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-set-show-menu-on-left-click" + }, + { + "description": "Denies the set_temp_dir_path command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-set-temp-dir-path" + }, + { + "description": "Denies the set_title command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-set-title" + }, + { + "description": "Denies the set_tooltip command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-set-tooltip" + }, + { + "description": "Denies the set_visible command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-set-visible" + }, + { + "description": "Default permissions for the plugin.", + "type": "string", + "const": "core:webview:default" + }, + { + "description": "Enables the clear_all_browsing_data command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-clear-all-browsing-data" + }, + { + "description": "Enables the create_webview command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-create-webview" + }, + { + "description": "Enables the create_webview_window command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-create-webview-window" + }, + { + "description": "Enables the get_all_webviews command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-get-all-webviews" + }, + { + "description": "Enables the internal_toggle_devtools command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-internal-toggle-devtools" + }, + { + "description": "Enables the print command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-print" + }, + { + "description": "Enables the reparent command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-reparent" + }, + { + "description": "Enables the set_webview_background_color command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-set-webview-background-color" + }, + { + "description": "Enables the set_webview_focus command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-set-webview-focus" + }, + { + "description": "Enables the set_webview_position command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-set-webview-position" + }, + { + "description": "Enables the set_webview_size command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-set-webview-size" + }, + { + "description": "Enables the set_webview_zoom command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-set-webview-zoom" + }, + { + "description": "Enables the webview_close command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-webview-close" + }, + { + "description": "Enables the webview_hide command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-webview-hide" + }, + { + "description": "Enables the webview_position command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-webview-position" + }, + { + "description": "Enables the webview_show command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-webview-show" + }, + { + "description": "Enables the webview_size command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-webview-size" + }, + { + "description": "Denies the clear_all_browsing_data command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-clear-all-browsing-data" + }, + { + "description": "Denies the create_webview command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-create-webview" + }, + { + "description": "Denies the create_webview_window command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-create-webview-window" + }, + { + "description": "Denies the get_all_webviews command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-get-all-webviews" + }, + { + "description": "Denies the internal_toggle_devtools command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-internal-toggle-devtools" + }, + { + "description": "Denies the print command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-print" + }, + { + "description": "Denies the reparent command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-reparent" + }, + { + "description": "Denies the set_webview_background_color command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-set-webview-background-color" + }, + { + "description": "Denies the set_webview_focus command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-set-webview-focus" + }, + { + "description": "Denies the set_webview_position command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-set-webview-position" + }, + { + "description": "Denies the set_webview_size command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-set-webview-size" + }, + { + "description": "Denies the set_webview_zoom command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-set-webview-zoom" + }, + { + "description": "Denies the webview_close command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-webview-close" + }, + { + "description": "Denies the webview_hide command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-webview-hide" + }, + { + "description": "Denies the webview_position command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-webview-position" + }, + { + "description": "Denies the webview_show command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-webview-show" + }, + { + "description": "Denies the webview_size command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-webview-size" + }, + { + "description": "Default permissions for the plugin.", + "type": "string", + "const": "core:window:default" + }, + { + "description": "Enables the available_monitors command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-available-monitors" + }, + { + "description": "Enables the center command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-center" + }, + { + "description": "Enables the close command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-close" + }, + { + "description": "Enables the create command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-create" + }, + { + "description": "Enables the current_monitor command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-current-monitor" + }, + { + "description": "Enables the cursor_position command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-cursor-position" + }, + { + "description": "Enables the destroy command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-destroy" + }, + { + "description": "Enables the get_all_windows command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-get-all-windows" + }, + { + "description": "Enables the hide command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-hide" + }, + { + "description": "Enables the inner_position command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-inner-position" + }, + { + "description": "Enables the inner_size command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-inner-size" + }, + { + "description": "Enables the internal_toggle_maximize command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-internal-toggle-maximize" + }, + { + "description": "Enables the is_always_on_top command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-always-on-top" + }, + { + "description": "Enables the is_closable command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-closable" + }, + { + "description": "Enables the is_decorated command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-decorated" + }, + { + "description": "Enables the is_enabled command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-enabled" + }, + { + "description": "Enables the is_focused command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-focused" + }, + { + "description": "Enables the is_fullscreen command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-fullscreen" + }, + { + "description": "Enables the is_maximizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-maximizable" + }, + { + "description": "Enables the is_maximized command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-maximized" + }, + { + "description": "Enables the is_minimizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-minimizable" + }, + { + "description": "Enables the is_minimized command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-minimized" + }, + { + "description": "Enables the is_resizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-resizable" + }, + { + "description": "Enables the is_visible command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-visible" + }, + { + "description": "Enables the maximize command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-maximize" + }, + { + "description": "Enables the minimize command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-minimize" + }, + { + "description": "Enables the monitor_from_point command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-monitor-from-point" + }, + { + "description": "Enables the outer_position command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-outer-position" + }, + { + "description": "Enables the outer_size command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-outer-size" + }, + { + "description": "Enables the primary_monitor command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-primary-monitor" + }, + { + "description": "Enables the request_user_attention command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-request-user-attention" + }, + { + "description": "Enables the scale_factor command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-scale-factor" + }, + { + "description": "Enables the set_always_on_bottom command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-always-on-bottom" + }, + { + "description": "Enables the set_always_on_top command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-always-on-top" + }, + { + "description": "Enables the set_background_color command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-background-color" + }, + { + "description": "Enables the set_badge_count command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-badge-count" + }, + { + "description": "Enables the set_badge_label command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-badge-label" + }, + { + "description": "Enables the set_closable command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-closable" + }, + { + "description": "Enables the set_content_protected command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-content-protected" + }, + { + "description": "Enables the set_cursor_grab command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-cursor-grab" + }, + { + "description": "Enables the set_cursor_icon command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-cursor-icon" + }, + { + "description": "Enables the set_cursor_position command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-cursor-position" + }, + { + "description": "Enables the set_cursor_visible command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-cursor-visible" + }, + { + "description": "Enables the set_decorations command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-decorations" + }, + { + "description": "Enables the set_effects command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-effects" + }, + { + "description": "Enables the set_enabled command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-enabled" + }, + { + "description": "Enables the set_focus command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-focus" + }, + { + "description": "Enables the set_fullscreen command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-fullscreen" + }, + { + "description": "Enables the set_icon command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-icon" + }, + { + "description": "Enables the set_ignore_cursor_events command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-ignore-cursor-events" + }, + { + "description": "Enables the set_max_size command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-max-size" + }, + { + "description": "Enables the set_maximizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-maximizable" + }, + { + "description": "Enables the set_min_size command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-min-size" + }, + { + "description": "Enables the set_minimizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-minimizable" + }, + { + "description": "Enables the set_overlay_icon command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-overlay-icon" + }, + { + "description": "Enables the set_position command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-position" + }, + { + "description": "Enables the set_progress_bar command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-progress-bar" + }, + { + "description": "Enables the set_resizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-resizable" + }, + { + "description": "Enables the set_shadow command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-shadow" + }, + { + "description": "Enables the set_size command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-size" + }, + { + "description": "Enables the set_size_constraints command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-size-constraints" + }, + { + "description": "Enables the set_skip_taskbar command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-skip-taskbar" + }, + { + "description": "Enables the set_theme command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-theme" + }, + { + "description": "Enables the set_title command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-title" + }, + { + "description": "Enables the set_title_bar_style command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-title-bar-style" + }, + { + "description": "Enables the set_visible_on_all_workspaces command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-visible-on-all-workspaces" + }, + { + "description": "Enables the show command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-show" + }, + { + "description": "Enables the start_dragging command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-start-dragging" + }, + { + "description": "Enables the start_resize_dragging command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-start-resize-dragging" + }, + { + "description": "Enables the theme command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-theme" + }, + { + "description": "Enables the title command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-title" + }, + { + "description": "Enables the toggle_maximize command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-toggle-maximize" + }, + { + "description": "Enables the unmaximize command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-unmaximize" + }, + { + "description": "Enables the unminimize command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-unminimize" + }, + { + "description": "Denies the available_monitors command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-available-monitors" + }, + { + "description": "Denies the center command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-center" + }, + { + "description": "Denies the close command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-close" + }, + { + "description": "Denies the create command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-create" + }, + { + "description": "Denies the current_monitor command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-current-monitor" + }, + { + "description": "Denies the cursor_position command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-cursor-position" + }, + { + "description": "Denies the destroy command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-destroy" + }, + { + "description": "Denies the get_all_windows command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-get-all-windows" + }, + { + "description": "Denies the hide command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-hide" + }, + { + "description": "Denies the inner_position command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-inner-position" + }, + { + "description": "Denies the inner_size command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-inner-size" + }, + { + "description": "Denies the internal_toggle_maximize command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-internal-toggle-maximize" + }, + { + "description": "Denies the is_always_on_top command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-always-on-top" + }, + { + "description": "Denies the is_closable command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-closable" + }, + { + "description": "Denies the is_decorated command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-decorated" + }, + { + "description": "Denies the is_enabled command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-enabled" + }, + { + "description": "Denies the is_focused command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-focused" + }, + { + "description": "Denies the is_fullscreen command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-fullscreen" + }, + { + "description": "Denies the is_maximizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-maximizable" + }, + { + "description": "Denies the is_maximized command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-maximized" + }, + { + "description": "Denies the is_minimizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-minimizable" + }, + { + "description": "Denies the is_minimized command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-minimized" + }, + { + "description": "Denies the is_resizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-resizable" + }, + { + "description": "Denies the is_visible command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-visible" + }, + { + "description": "Denies the maximize command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-maximize" + }, + { + "description": "Denies the minimize command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-minimize" + }, + { + "description": "Denies the monitor_from_point command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-monitor-from-point" + }, + { + "description": "Denies the outer_position command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-outer-position" + }, + { + "description": "Denies the outer_size command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-outer-size" + }, + { + "description": "Denies the primary_monitor command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-primary-monitor" + }, + { + "description": "Denies the request_user_attention command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-request-user-attention" + }, + { + "description": "Denies the scale_factor command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-scale-factor" + }, + { + "description": "Denies the set_always_on_bottom command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-always-on-bottom" + }, + { + "description": "Denies the set_always_on_top command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-always-on-top" + }, + { + "description": "Denies the set_background_color command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-background-color" + }, + { + "description": "Denies the set_badge_count command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-badge-count" + }, + { + "description": "Denies the set_badge_label command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-badge-label" + }, + { + "description": "Denies the set_closable command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-closable" + }, + { + "description": "Denies the set_content_protected command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-content-protected" + }, + { + "description": "Denies the set_cursor_grab command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-cursor-grab" + }, + { + "description": "Denies the set_cursor_icon command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-cursor-icon" + }, + { + "description": "Denies the set_cursor_position command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-cursor-position" + }, + { + "description": "Denies the set_cursor_visible command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-cursor-visible" + }, + { + "description": "Denies the set_decorations command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-decorations" + }, + { + "description": "Denies the set_effects command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-effects" + }, + { + "description": "Denies the set_enabled command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-enabled" + }, + { + "description": "Denies the set_focus command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-focus" + }, + { + "description": "Denies the set_fullscreen command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-fullscreen" + }, + { + "description": "Denies the set_icon command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-icon" + }, + { + "description": "Denies the set_ignore_cursor_events command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-ignore-cursor-events" + }, + { + "description": "Denies the set_max_size command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-max-size" + }, + { + "description": "Denies the set_maximizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-maximizable" + }, + { + "description": "Denies the set_min_size command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-min-size" + }, + { + "description": "Denies the set_minimizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-minimizable" + }, + { + "description": "Denies the set_overlay_icon command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-overlay-icon" + }, + { + "description": "Denies the set_position command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-position" + }, + { + "description": "Denies the set_progress_bar command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-progress-bar" + }, + { + "description": "Denies the set_resizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-resizable" + }, + { + "description": "Denies the set_shadow command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-shadow" + }, + { + "description": "Denies the set_size command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-size" + }, + { + "description": "Denies the set_size_constraints command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-size-constraints" + }, + { + "description": "Denies the set_skip_taskbar command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-skip-taskbar" + }, + { + "description": "Denies the set_theme command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-theme" + }, + { + "description": "Denies the set_title command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-title" + }, + { + "description": "Denies the set_title_bar_style command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-title-bar-style" + }, + { + "description": "Denies the set_visible_on_all_workspaces command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-visible-on-all-workspaces" + }, + { + "description": "Denies the show command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-show" + }, + { + "description": "Denies the start_dragging command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-start-dragging" + }, + { + "description": "Denies the start_resize_dragging command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-start-resize-dragging" + }, + { + "description": "Denies the theme command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-theme" + }, + { + "description": "Denies the title command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-title" + }, + { + "description": "Denies the toggle_maximize command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-toggle-maximize" + }, + { + "description": "Denies the unmaximize command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-unmaximize" + }, + { + "description": "Denies the unminimize command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-unminimize" + }, + { + "description": "This permission set configures which\nprocess feeatures are by default exposed.\n\n#### Granted Permissions\n\nThis enables to quit via `allow-exit` and restart via `allow-restart`\nthe application.\n", + "type": "string", + "const": "process:default" + }, + { + "description": "Enables the exit command without any pre-configured scope.", + "type": "string", + "const": "process:allow-exit" + }, + { + "description": "Enables the restart command without any pre-configured scope.", + "type": "string", + "const": "process:allow-restart" + }, + { + "description": "Denies the exit command without any pre-configured scope.", + "type": "string", + "const": "process:deny-exit" + }, + { + "description": "Denies the restart command without any pre-configured scope.", + "type": "string", + "const": "process:deny-restart" + }, + { + "description": "This permission set configures which\nshell functionality is exposed by default.\n\n#### Granted Permissions\n\nIt allows to use the `open` functionality without any specific\nscope pre-configured. It will allow opening `http(s)://`,\n`tel:` and `mailto:` links.\n", + "type": "string", + "const": "shell:default" + }, + { + "description": "Enables the execute command without any pre-configured scope.", + "type": "string", + "const": "shell:allow-execute" + }, + { + "description": "Enables the kill command without any pre-configured scope.", + "type": "string", + "const": "shell:allow-kill" + }, + { + "description": "Enables the open command without any pre-configured scope.", + "type": "string", + "const": "shell:allow-open" + }, + { + "description": "Enables the spawn command without any pre-configured scope.", + "type": "string", + "const": "shell:allow-spawn" + }, + { + "description": "Enables the stdin_write command without any pre-configured scope.", + "type": "string", + "const": "shell:allow-stdin-write" + }, + { + "description": "Denies the execute command without any pre-configured scope.", + "type": "string", + "const": "shell:deny-execute" + }, + { + "description": "Denies the kill command without any pre-configured scope.", + "type": "string", + "const": "shell:deny-kill" + }, + { + "description": "Denies the open command without any pre-configured scope.", + "type": "string", + "const": "shell:deny-open" + }, + { + "description": "Denies the spawn command without any pre-configured scope.", + "type": "string", + "const": "shell:deny-spawn" + }, + { + "description": "Denies the stdin_write command without any pre-configured scope.", + "type": "string", + "const": "shell:deny-stdin-write" + }, + { + "description": "This permission set configures which kind of\nupdater functions are exposed to the frontend.\n\n#### Granted Permissions\n\nThe full workflow from checking for updates to installing them\nis enabled.\n\n", + "type": "string", + "const": "updater:default" + }, + { + "description": "Enables the check command without any pre-configured scope.", + "type": "string", + "const": "updater:allow-check" + }, + { + "description": "Enables the download command without any pre-configured scope.", + "type": "string", + "const": "updater:allow-download" + }, + { + "description": "Enables the download_and_install command without any pre-configured scope.", + "type": "string", + "const": "updater:allow-download-and-install" + }, + { + "description": "Enables the install command without any pre-configured scope.", + "type": "string", + "const": "updater:allow-install" + }, + { + "description": "Denies the check command without any pre-configured scope.", + "type": "string", + "const": "updater:deny-check" + }, + { + "description": "Denies the download command without any pre-configured scope.", + "type": "string", + "const": "updater:deny-download" + }, + { + "description": "Denies the download_and_install command without any pre-configured scope.", + "type": "string", + "const": "updater:deny-download-and-install" + }, + { + "description": "Denies the install command without any pre-configured scope.", + "type": "string", + "const": "updater:deny-install" + } + ] + }, + "Value": { + "description": "All supported ACL values.", + "anyOf": [ + { + "description": "Represents a null JSON value.", + "type": "null" + }, + { + "description": "Represents a [`bool`].", + "type": "boolean" + }, + { + "description": "Represents a valid ACL [`Number`].", + "allOf": [ + { + "$ref": "#/definitions/Number" + } + ] + }, + { + "description": "Represents a [`String`].", + "type": "string" + }, + { + "description": "Represents a list of other [`Value`]s.", + "type": "array", + "items": { + "$ref": "#/definitions/Value" + } + }, + { + "description": "Represents a map of [`String`] keys to [`Value`]s.", + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/Value" + } + } + ] + }, + "Number": { + "description": "A valid ACL number.", + "anyOf": [ + { + "description": "Represents an [`i64`].", + "type": "integer", + "format": "int64" + }, + { + "description": "Represents a [`f64`].", + "type": "number", + "format": "double" + } + ] + }, + "Target": { + "description": "Platform target.", + "oneOf": [ + { + "description": "MacOS.", + "type": "string", + "enum": [ + "macOS" + ] + }, + { + "description": "Windows.", + "type": "string", + "enum": [ + "windows" + ] + }, + { + "description": "Linux.", + "type": "string", + "enum": [ + "linux" + ] + }, + { + "description": "Android.", + "type": "string", + "enum": [ + "android" + ] + }, + { + "description": "iOS.", + "type": "string", + "enum": [ + "iOS" + ] + } + ] + }, + "ShellScopeEntryAllowedArg": { + "description": "A command argument allowed to be executed by the webview API.", + "anyOf": [ + { + "description": "A non-configurable argument that is passed to the command in the order it was specified.", + "type": "string" + }, + { + "description": "A variable that is set while calling the command from the webview API.", + "type": "object", + "required": [ + "validator" + ], + "properties": { + "raw": { + "description": "Marks the validator as a raw regex, meaning the plugin should not make any modification at runtime.\n\nThis means the regex will not match on the entire string by default, which might be exploited if your regex allow unexpected input to be considered valid. When using this option, make sure your regex is correct.", + "default": false, + "type": "boolean" + }, + "validator": { + "description": "[regex] validator to require passed values to conform to an expected input.\n\nThis will require the argument value passed to this variable to match the `validator` regex before it will be executed.\n\nThe regex string is by default surrounded by `^...$` to match the full string. For example the `https?://\\w+` regex would be registered as `^https?://\\w+$`.\n\n[regex]: ", + "type": "string" + } + }, + "additionalProperties": false + } + ] + }, + "ShellScopeEntryAllowedArgs": { + "description": "A set of command arguments allowed to be executed by the webview API.\n\nA value of `true` will allow any arguments to be passed to the command. `false` will disable all arguments. A list of [`ShellScopeEntryAllowedArg`] will set those arguments as the only valid arguments to be passed to the attached command configuration.", + "anyOf": [ + { + "description": "Use a simple boolean to allow all or disable all arguments to this command configuration.", + "type": "boolean" + }, + { + "description": "A specific set of [`ShellScopeEntryAllowedArg`] that are valid to call for the command configuration.", + "type": "array", + "items": { + "$ref": "#/definitions/ShellScopeEntryAllowedArg" + } + } + ] + } + } +} \ No newline at end of file diff --git a/nym-wallet/src-tauri/gen/schemas/macOS-schema.json b/nym-wallet/src-tauri/gen/schemas/macOS-schema.json new file mode 100644 index 0000000000..5ff69983d7 --- /dev/null +++ b/nym-wallet/src-tauri/gen/schemas/macOS-schema.json @@ -0,0 +1,2279 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "CapabilityFile", + "description": "Capability formats accepted in a capability file.", + "anyOf": [ + { + "description": "A single capability.", + "allOf": [ + { + "$ref": "#/definitions/Capability" + } + ] + }, + { + "description": "A list of capabilities.", + "type": "array", + "items": { + "$ref": "#/definitions/Capability" + } + }, + { + "description": "A list of capabilities.", + "type": "object", + "required": [ + "capabilities" + ], + "properties": { + "capabilities": { + "description": "The list of capabilities.", + "type": "array", + "items": { + "$ref": "#/definitions/Capability" + } + } + } + } + ], + "definitions": { + "Capability": { + "description": "A grouping and boundary mechanism developers can use to isolate access to the IPC layer.\n\nIt controls application windows' and webviews' fine grained access to the Tauri core, application, or plugin commands. If a webview or its window is not matching any capability then it has no access to the IPC layer at all.\n\nThis can be done to create groups of windows, based on their required system access, which can reduce impact of frontend vulnerabilities in less privileged windows. Windows can be added to a capability by exact name (e.g. `main-window`) or glob patterns like `*` or `admin-*`. A Window can have none, one, or multiple associated capabilities.\n\n## Example\n\n```json { \"identifier\": \"main-user-files-write\", \"description\": \"This capability allows the `main` window on macOS and Windows access to `filesystem` write related commands and `dialog` commands to enable programatic access to files selected by the user.\", \"windows\": [ \"main\" ], \"permissions\": [ \"core:default\", \"dialog:open\", { \"identifier\": \"fs:allow-write-text-file\", \"allow\": [{ \"path\": \"$HOME/test.txt\" }] }, ], \"platforms\": [\"macOS\",\"windows\"] } ```", + "type": "object", + "required": [ + "identifier", + "permissions" + ], + "properties": { + "identifier": { + "description": "Identifier of the capability.\n\n## Example\n\n`main-user-files-write`", + "type": "string" + }, + "description": { + "description": "Description of what the capability is intended to allow on associated windows.\n\nIt should contain a description of what the grouped permissions should allow.\n\n## Example\n\nThis capability allows the `main` window access to `filesystem` write related commands and `dialog` commands to enable programatic access to files selected by the user.", + "default": "", + "type": "string" + }, + "remote": { + "description": "Configure remote URLs that can use the capability permissions.\n\nThis setting is optional and defaults to not being set, as our default use case is that the content is served from our local application.\n\n:::caution Make sure you understand the security implications of providing remote sources with local system access. :::\n\n## Example\n\n```json { \"urls\": [\"https://*.mydomain.dev\"] } ```", + "anyOf": [ + { + "$ref": "#/definitions/CapabilityRemote" + }, + { + "type": "null" + } + ] + }, + "local": { + "description": "Whether this capability is enabled for local app URLs or not. Defaults to `true`.", + "default": true, + "type": "boolean" + }, + "windows": { + "description": "List of windows that are affected by this capability. Can be a glob pattern.\n\nIf a window label matches any of the patterns in this list, the capability will be enabled on all the webviews of that window, regardless of the value of [`Self::webviews`].\n\nOn multiwebview windows, prefer specifying [`Self::webviews`] and omitting [`Self::windows`] for a fine grained access control.\n\n## Example\n\n`[\"main\"]`", + "type": "array", + "items": { + "type": "string" + } + }, + "webviews": { + "description": "List of webviews that are affected by this capability. Can be a glob pattern.\n\nThe capability will be enabled on all the webviews whose label matches any of the patterns in this list, regardless of whether the webview's window label matches a pattern in [`Self::windows`].\n\n## Example\n\n`[\"sub-webview-one\", \"sub-webview-two\"]`", + "type": "array", + "items": { + "type": "string" + } + }, + "permissions": { + "description": "List of permissions attached to this capability.\n\nMust include the plugin name as prefix in the form of `${plugin-name}:${permission-name}`. For commands directly implemented in the application itself only `${permission-name}` is required.\n\n## Example\n\n```json [ \"core:default\", \"shell:allow-open\", \"dialog:open\", { \"identifier\": \"fs:allow-write-text-file\", \"allow\": [{ \"path\": \"$HOME/test.txt\" }] } ] ```", + "type": "array", + "items": { + "$ref": "#/definitions/PermissionEntry" + }, + "uniqueItems": true + }, + "platforms": { + "description": "Limit which target platforms this capability applies to.\n\nBy default all platforms are targeted.\n\n## Example\n\n`[\"macOS\",\"windows\"]`", + "type": [ + "array", + "null" + ], + "items": { + "$ref": "#/definitions/Target" + } + } + } + }, + "CapabilityRemote": { + "description": "Configuration for remote URLs that are associated with the capability.", + "type": "object", + "required": [ + "urls" + ], + "properties": { + "urls": { + "description": "Remote domains this capability refers to using the [URLPattern standard](https://urlpattern.spec.whatwg.org/).\n\n## Examples\n\n- \"https://*.mydomain.dev\": allows subdomains of mydomain.dev - \"https://mydomain.dev/api/*\": allows any subpath of mydomain.dev/api", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "PermissionEntry": { + "description": "An entry for a permission value in a [`Capability`] can be either a raw permission [`Identifier`] or an object that references a permission and extends its scope.", + "anyOf": [ + { + "description": "Reference a permission or permission set by identifier.", + "allOf": [ + { + "$ref": "#/definitions/Identifier" + } + ] + }, + { + "description": "Reference a permission or permission set by identifier and extends its scope.", + "type": "object", + "allOf": [ + { + "if": { + "properties": { + "identifier": { + "anyOf": [ + { + "description": "This permission set configures which\nshell functionality is exposed by default.\n\n#### Granted Permissions\n\nIt allows to use the `open` functionality without any specific\nscope pre-configured. It will allow opening `http(s)://`,\n`tel:` and `mailto:` links.\n", + "type": "string", + "const": "shell:default" + }, + { + "description": "Enables the execute command without any pre-configured scope.", + "type": "string", + "const": "shell:allow-execute" + }, + { + "description": "Enables the kill command without any pre-configured scope.", + "type": "string", + "const": "shell:allow-kill" + }, + { + "description": "Enables the open command without any pre-configured scope.", + "type": "string", + "const": "shell:allow-open" + }, + { + "description": "Enables the spawn command without any pre-configured scope.", + "type": "string", + "const": "shell:allow-spawn" + }, + { + "description": "Enables the stdin_write command without any pre-configured scope.", + "type": "string", + "const": "shell:allow-stdin-write" + }, + { + "description": "Denies the execute command without any pre-configured scope.", + "type": "string", + "const": "shell:deny-execute" + }, + { + "description": "Denies the kill command without any pre-configured scope.", + "type": "string", + "const": "shell:deny-kill" + }, + { + "description": "Denies the open command without any pre-configured scope.", + "type": "string", + "const": "shell:deny-open" + }, + { + "description": "Denies the spawn command without any pre-configured scope.", + "type": "string", + "const": "shell:deny-spawn" + }, + { + "description": "Denies the stdin_write command without any pre-configured scope.", + "type": "string", + "const": "shell:deny-stdin-write" + } + ] + } + } + }, + "then": { + "properties": { + "allow": { + "items": { + "title": "ShellScopeEntry", + "description": "Shell scope entry.", + "anyOf": [ + { + "type": "object", + "required": [ + "cmd", + "name" + ], + "properties": { + "args": { + "description": "The allowed arguments for the command execution.", + "allOf": [ + { + "$ref": "#/definitions/ShellScopeEntryAllowedArgs" + } + ] + }, + "cmd": { + "description": "The command name. It can start with a variable that resolves to a system base directory. The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`, `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`, `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`, `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.", + "type": "string" + }, + "name": { + "description": "The name for this allowed shell command configuration.\n\nThis name will be used inside of the webview API to call this command along with any specified arguments.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "name", + "sidecar" + ], + "properties": { + "args": { + "description": "The allowed arguments for the command execution.", + "allOf": [ + { + "$ref": "#/definitions/ShellScopeEntryAllowedArgs" + } + ] + }, + "name": { + "description": "The name for this allowed shell command configuration.\n\nThis name will be used inside of the webview API to call this command along with any specified arguments.", + "type": "string" + }, + "sidecar": { + "description": "If this command is a sidecar command.", + "type": "boolean" + } + }, + "additionalProperties": false + } + ] + } + }, + "deny": { + "items": { + "title": "ShellScopeEntry", + "description": "Shell scope entry.", + "anyOf": [ + { + "type": "object", + "required": [ + "cmd", + "name" + ], + "properties": { + "args": { + "description": "The allowed arguments for the command execution.", + "allOf": [ + { + "$ref": "#/definitions/ShellScopeEntryAllowedArgs" + } + ] + }, + "cmd": { + "description": "The command name. It can start with a variable that resolves to a system base directory. The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`, `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`, `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`, `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.", + "type": "string" + }, + "name": { + "description": "The name for this allowed shell command configuration.\n\nThis name will be used inside of the webview API to call this command along with any specified arguments.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "name", + "sidecar" + ], + "properties": { + "args": { + "description": "The allowed arguments for the command execution.", + "allOf": [ + { + "$ref": "#/definitions/ShellScopeEntryAllowedArgs" + } + ] + }, + "name": { + "description": "The name for this allowed shell command configuration.\n\nThis name will be used inside of the webview API to call this command along with any specified arguments.", + "type": "string" + }, + "sidecar": { + "description": "If this command is a sidecar command.", + "type": "boolean" + } + }, + "additionalProperties": false + } + ] + } + } + } + }, + "properties": { + "identifier": { + "description": "Identifier of the permission or permission set.", + "allOf": [ + { + "$ref": "#/definitions/Identifier" + } + ] + } + } + }, + { + "properties": { + "identifier": { + "description": "Identifier of the permission or permission set.", + "allOf": [ + { + "$ref": "#/definitions/Identifier" + } + ] + }, + "allow": { + "description": "Data that defines what is allowed by the scope.", + "type": [ + "array", + "null" + ], + "items": { + "$ref": "#/definitions/Value" + } + }, + "deny": { + "description": "Data that defines what is denied by the scope. This should be prioritized by validation logic.", + "type": [ + "array", + "null" + ], + "items": { + "$ref": "#/definitions/Value" + } + } + } + } + ], + "required": [ + "identifier" + ] + } + ] + }, + "Identifier": { + "description": "Permission identifier", + "oneOf": [ + { + "description": "No features are enabled by default, as we believe\nthe clipboard can be inherently dangerous and it is \napplication specific if read and/or write access is needed.\n\nClipboard interaction needs to be explicitly enabled.\n", + "type": "string", + "const": "clipboard-manager:default" + }, + { + "description": "Enables the clear command without any pre-configured scope.", + "type": "string", + "const": "clipboard-manager:allow-clear" + }, + { + "description": "Enables the read_image command without any pre-configured scope.", + "type": "string", + "const": "clipboard-manager:allow-read-image" + }, + { + "description": "Enables the read_text command without any pre-configured scope.", + "type": "string", + "const": "clipboard-manager:allow-read-text" + }, + { + "description": "Enables the write_html command without any pre-configured scope.", + "type": "string", + "const": "clipboard-manager:allow-write-html" + }, + { + "description": "Enables the write_image command without any pre-configured scope.", + "type": "string", + "const": "clipboard-manager:allow-write-image" + }, + { + "description": "Enables the write_text command without any pre-configured scope.", + "type": "string", + "const": "clipboard-manager:allow-write-text" + }, + { + "description": "Denies the clear command without any pre-configured scope.", + "type": "string", + "const": "clipboard-manager:deny-clear" + }, + { + "description": "Denies the read_image command without any pre-configured scope.", + "type": "string", + "const": "clipboard-manager:deny-read-image" + }, + { + "description": "Denies the read_text command without any pre-configured scope.", + "type": "string", + "const": "clipboard-manager:deny-read-text" + }, + { + "description": "Denies the write_html command without any pre-configured scope.", + "type": "string", + "const": "clipboard-manager:deny-write-html" + }, + { + "description": "Denies the write_image command without any pre-configured scope.", + "type": "string", + "const": "clipboard-manager:deny-write-image" + }, + { + "description": "Denies the write_text command without any pre-configured scope.", + "type": "string", + "const": "clipboard-manager:deny-write-text" + }, + { + "description": "Default core plugins set which includes:\n- 'core:path:default'\n- 'core:event:default'\n- 'core:window:default'\n- 'core:webview:default'\n- 'core:app:default'\n- 'core:image:default'\n- 'core:resources:default'\n- 'core:menu:default'\n- 'core:tray:default'\n", + "type": "string", + "const": "core:default" + }, + { + "description": "Default permissions for the plugin.", + "type": "string", + "const": "core:app:default" + }, + { + "description": "Enables the app_hide command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-app-hide" + }, + { + "description": "Enables the app_show command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-app-show" + }, + { + "description": "Enables the default_window_icon command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-default-window-icon" + }, + { + "description": "Enables the fetch_data_store_identifiers command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-fetch-data-store-identifiers" + }, + { + "description": "Enables the identifier command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-identifier" + }, + { + "description": "Enables the name command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-name" + }, + { + "description": "Enables the remove_data_store command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-remove-data-store" + }, + { + "description": "Enables the set_app_theme command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-set-app-theme" + }, + { + "description": "Enables the tauri_version command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-tauri-version" + }, + { + "description": "Enables the version command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-version" + }, + { + "description": "Denies the app_hide command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-app-hide" + }, + { + "description": "Denies the app_show command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-app-show" + }, + { + "description": "Denies the default_window_icon command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-default-window-icon" + }, + { + "description": "Denies the fetch_data_store_identifiers command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-fetch-data-store-identifiers" + }, + { + "description": "Denies the identifier command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-identifier" + }, + { + "description": "Denies the name command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-name" + }, + { + "description": "Denies the remove_data_store command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-remove-data-store" + }, + { + "description": "Denies the set_app_theme command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-set-app-theme" + }, + { + "description": "Denies the tauri_version command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-tauri-version" + }, + { + "description": "Denies the version command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-version" + }, + { + "description": "Default permissions for the plugin.", + "type": "string", + "const": "core:event:default" + }, + { + "description": "Enables the emit command without any pre-configured scope.", + "type": "string", + "const": "core:event:allow-emit" + }, + { + "description": "Enables the emit_to command without any pre-configured scope.", + "type": "string", + "const": "core:event:allow-emit-to" + }, + { + "description": "Enables the listen command without any pre-configured scope.", + "type": "string", + "const": "core:event:allow-listen" + }, + { + "description": "Enables the unlisten command without any pre-configured scope.", + "type": "string", + "const": "core:event:allow-unlisten" + }, + { + "description": "Denies the emit command without any pre-configured scope.", + "type": "string", + "const": "core:event:deny-emit" + }, + { + "description": "Denies the emit_to command without any pre-configured scope.", + "type": "string", + "const": "core:event:deny-emit-to" + }, + { + "description": "Denies the listen command without any pre-configured scope.", + "type": "string", + "const": "core:event:deny-listen" + }, + { + "description": "Denies the unlisten command without any pre-configured scope.", + "type": "string", + "const": "core:event:deny-unlisten" + }, + { + "description": "Default permissions for the plugin.", + "type": "string", + "const": "core:image:default" + }, + { + "description": "Enables the from_bytes command without any pre-configured scope.", + "type": "string", + "const": "core:image:allow-from-bytes" + }, + { + "description": "Enables the from_path command without any pre-configured scope.", + "type": "string", + "const": "core:image:allow-from-path" + }, + { + "description": "Enables the new command without any pre-configured scope.", + "type": "string", + "const": "core:image:allow-new" + }, + { + "description": "Enables the rgba command without any pre-configured scope.", + "type": "string", + "const": "core:image:allow-rgba" + }, + { + "description": "Enables the size command without any pre-configured scope.", + "type": "string", + "const": "core:image:allow-size" + }, + { + "description": "Denies the from_bytes command without any pre-configured scope.", + "type": "string", + "const": "core:image:deny-from-bytes" + }, + { + "description": "Denies the from_path command without any pre-configured scope.", + "type": "string", + "const": "core:image:deny-from-path" + }, + { + "description": "Denies the new command without any pre-configured scope.", + "type": "string", + "const": "core:image:deny-new" + }, + { + "description": "Denies the rgba command without any pre-configured scope.", + "type": "string", + "const": "core:image:deny-rgba" + }, + { + "description": "Denies the size command without any pre-configured scope.", + "type": "string", + "const": "core:image:deny-size" + }, + { + "description": "Default permissions for the plugin.", + "type": "string", + "const": "core:menu:default" + }, + { + "description": "Enables the append command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-append" + }, + { + "description": "Enables the create_default command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-create-default" + }, + { + "description": "Enables the get command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-get" + }, + { + "description": "Enables the insert command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-insert" + }, + { + "description": "Enables the is_checked command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-is-checked" + }, + { + "description": "Enables the is_enabled command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-is-enabled" + }, + { + "description": "Enables the items command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-items" + }, + { + "description": "Enables the new command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-new" + }, + { + "description": "Enables the popup command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-popup" + }, + { + "description": "Enables the prepend command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-prepend" + }, + { + "description": "Enables the remove command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-remove" + }, + { + "description": "Enables the remove_at command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-remove-at" + }, + { + "description": "Enables the set_accelerator command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-set-accelerator" + }, + { + "description": "Enables the set_as_app_menu command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-set-as-app-menu" + }, + { + "description": "Enables the set_as_help_menu_for_nsapp command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-set-as-help-menu-for-nsapp" + }, + { + "description": "Enables the set_as_window_menu command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-set-as-window-menu" + }, + { + "description": "Enables the set_as_windows_menu_for_nsapp command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-set-as-windows-menu-for-nsapp" + }, + { + "description": "Enables the set_checked command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-set-checked" + }, + { + "description": "Enables the set_enabled command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-set-enabled" + }, + { + "description": "Enables the set_icon command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-set-icon" + }, + { + "description": "Enables the set_text command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-set-text" + }, + { + "description": "Enables the text command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-text" + }, + { + "description": "Denies the append command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-append" + }, + { + "description": "Denies the create_default command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-create-default" + }, + { + "description": "Denies the get command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-get" + }, + { + "description": "Denies the insert command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-insert" + }, + { + "description": "Denies the is_checked command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-is-checked" + }, + { + "description": "Denies the is_enabled command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-is-enabled" + }, + { + "description": "Denies the items command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-items" + }, + { + "description": "Denies the new command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-new" + }, + { + "description": "Denies the popup command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-popup" + }, + { + "description": "Denies the prepend command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-prepend" + }, + { + "description": "Denies the remove command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-remove" + }, + { + "description": "Denies the remove_at command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-remove-at" + }, + { + "description": "Denies the set_accelerator command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-set-accelerator" + }, + { + "description": "Denies the set_as_app_menu command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-set-as-app-menu" + }, + { + "description": "Denies the set_as_help_menu_for_nsapp command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-set-as-help-menu-for-nsapp" + }, + { + "description": "Denies the set_as_window_menu command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-set-as-window-menu" + }, + { + "description": "Denies the set_as_windows_menu_for_nsapp command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-set-as-windows-menu-for-nsapp" + }, + { + "description": "Denies the set_checked command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-set-checked" + }, + { + "description": "Denies the set_enabled command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-set-enabled" + }, + { + "description": "Denies the set_icon command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-set-icon" + }, + { + "description": "Denies the set_text command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-set-text" + }, + { + "description": "Denies the text command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-text" + }, + { + "description": "Default permissions for the plugin.", + "type": "string", + "const": "core:path:default" + }, + { + "description": "Enables the basename command without any pre-configured scope.", + "type": "string", + "const": "core:path:allow-basename" + }, + { + "description": "Enables the dirname command without any pre-configured scope.", + "type": "string", + "const": "core:path:allow-dirname" + }, + { + "description": "Enables the extname command without any pre-configured scope.", + "type": "string", + "const": "core:path:allow-extname" + }, + { + "description": "Enables the is_absolute command without any pre-configured scope.", + "type": "string", + "const": "core:path:allow-is-absolute" + }, + { + "description": "Enables the join command without any pre-configured scope.", + "type": "string", + "const": "core:path:allow-join" + }, + { + "description": "Enables the normalize command without any pre-configured scope.", + "type": "string", + "const": "core:path:allow-normalize" + }, + { + "description": "Enables the resolve command without any pre-configured scope.", + "type": "string", + "const": "core:path:allow-resolve" + }, + { + "description": "Enables the resolve_directory command without any pre-configured scope.", + "type": "string", + "const": "core:path:allow-resolve-directory" + }, + { + "description": "Denies the basename command without any pre-configured scope.", + "type": "string", + "const": "core:path:deny-basename" + }, + { + "description": "Denies the dirname command without any pre-configured scope.", + "type": "string", + "const": "core:path:deny-dirname" + }, + { + "description": "Denies the extname command without any pre-configured scope.", + "type": "string", + "const": "core:path:deny-extname" + }, + { + "description": "Denies the is_absolute command without any pre-configured scope.", + "type": "string", + "const": "core:path:deny-is-absolute" + }, + { + "description": "Denies the join command without any pre-configured scope.", + "type": "string", + "const": "core:path:deny-join" + }, + { + "description": "Denies the normalize command without any pre-configured scope.", + "type": "string", + "const": "core:path:deny-normalize" + }, + { + "description": "Denies the resolve command without any pre-configured scope.", + "type": "string", + "const": "core:path:deny-resolve" + }, + { + "description": "Denies the resolve_directory command without any pre-configured scope.", + "type": "string", + "const": "core:path:deny-resolve-directory" + }, + { + "description": "Default permissions for the plugin.", + "type": "string", + "const": "core:resources:default" + }, + { + "description": "Enables the close command without any pre-configured scope.", + "type": "string", + "const": "core:resources:allow-close" + }, + { + "description": "Denies the close command without any pre-configured scope.", + "type": "string", + "const": "core:resources:deny-close" + }, + { + "description": "Default permissions for the plugin.", + "type": "string", + "const": "core:tray:default" + }, + { + "description": "Enables the get_by_id command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-get-by-id" + }, + { + "description": "Enables the new command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-new" + }, + { + "description": "Enables the remove_by_id command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-remove-by-id" + }, + { + "description": "Enables the set_icon command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-set-icon" + }, + { + "description": "Enables the set_icon_as_template command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-set-icon-as-template" + }, + { + "description": "Enables the set_menu command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-set-menu" + }, + { + "description": "Enables the set_show_menu_on_left_click command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-set-show-menu-on-left-click" + }, + { + "description": "Enables the set_temp_dir_path command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-set-temp-dir-path" + }, + { + "description": "Enables the set_title command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-set-title" + }, + { + "description": "Enables the set_tooltip command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-set-tooltip" + }, + { + "description": "Enables the set_visible command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-set-visible" + }, + { + "description": "Denies the get_by_id command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-get-by-id" + }, + { + "description": "Denies the new command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-new" + }, + { + "description": "Denies the remove_by_id command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-remove-by-id" + }, + { + "description": "Denies the set_icon command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-set-icon" + }, + { + "description": "Denies the set_icon_as_template command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-set-icon-as-template" + }, + { + "description": "Denies the set_menu command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-set-menu" + }, + { + "description": "Denies the set_show_menu_on_left_click command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-set-show-menu-on-left-click" + }, + { + "description": "Denies the set_temp_dir_path command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-set-temp-dir-path" + }, + { + "description": "Denies the set_title command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-set-title" + }, + { + "description": "Denies the set_tooltip command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-set-tooltip" + }, + { + "description": "Denies the set_visible command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-set-visible" + }, + { + "description": "Default permissions for the plugin.", + "type": "string", + "const": "core:webview:default" + }, + { + "description": "Enables the clear_all_browsing_data command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-clear-all-browsing-data" + }, + { + "description": "Enables the create_webview command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-create-webview" + }, + { + "description": "Enables the create_webview_window command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-create-webview-window" + }, + { + "description": "Enables the get_all_webviews command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-get-all-webviews" + }, + { + "description": "Enables the internal_toggle_devtools command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-internal-toggle-devtools" + }, + { + "description": "Enables the print command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-print" + }, + { + "description": "Enables the reparent command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-reparent" + }, + { + "description": "Enables the set_webview_background_color command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-set-webview-background-color" + }, + { + "description": "Enables the set_webview_focus command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-set-webview-focus" + }, + { + "description": "Enables the set_webview_position command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-set-webview-position" + }, + { + "description": "Enables the set_webview_size command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-set-webview-size" + }, + { + "description": "Enables the set_webview_zoom command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-set-webview-zoom" + }, + { + "description": "Enables the webview_close command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-webview-close" + }, + { + "description": "Enables the webview_hide command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-webview-hide" + }, + { + "description": "Enables the webview_position command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-webview-position" + }, + { + "description": "Enables the webview_show command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-webview-show" + }, + { + "description": "Enables the webview_size command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-webview-size" + }, + { + "description": "Denies the clear_all_browsing_data command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-clear-all-browsing-data" + }, + { + "description": "Denies the create_webview command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-create-webview" + }, + { + "description": "Denies the create_webview_window command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-create-webview-window" + }, + { + "description": "Denies the get_all_webviews command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-get-all-webviews" + }, + { + "description": "Denies the internal_toggle_devtools command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-internal-toggle-devtools" + }, + { + "description": "Denies the print command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-print" + }, + { + "description": "Denies the reparent command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-reparent" + }, + { + "description": "Denies the set_webview_background_color command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-set-webview-background-color" + }, + { + "description": "Denies the set_webview_focus command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-set-webview-focus" + }, + { + "description": "Denies the set_webview_position command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-set-webview-position" + }, + { + "description": "Denies the set_webview_size command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-set-webview-size" + }, + { + "description": "Denies the set_webview_zoom command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-set-webview-zoom" + }, + { + "description": "Denies the webview_close command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-webview-close" + }, + { + "description": "Denies the webview_hide command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-webview-hide" + }, + { + "description": "Denies the webview_position command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-webview-position" + }, + { + "description": "Denies the webview_show command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-webview-show" + }, + { + "description": "Denies the webview_size command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-webview-size" + }, + { + "description": "Default permissions for the plugin.", + "type": "string", + "const": "core:window:default" + }, + { + "description": "Enables the available_monitors command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-available-monitors" + }, + { + "description": "Enables the center command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-center" + }, + { + "description": "Enables the close command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-close" + }, + { + "description": "Enables the create command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-create" + }, + { + "description": "Enables the current_monitor command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-current-monitor" + }, + { + "description": "Enables the cursor_position command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-cursor-position" + }, + { + "description": "Enables the destroy command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-destroy" + }, + { + "description": "Enables the get_all_windows command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-get-all-windows" + }, + { + "description": "Enables the hide command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-hide" + }, + { + "description": "Enables the inner_position command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-inner-position" + }, + { + "description": "Enables the inner_size command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-inner-size" + }, + { + "description": "Enables the internal_toggle_maximize command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-internal-toggle-maximize" + }, + { + "description": "Enables the is_always_on_top command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-always-on-top" + }, + { + "description": "Enables the is_closable command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-closable" + }, + { + "description": "Enables the is_decorated command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-decorated" + }, + { + "description": "Enables the is_enabled command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-enabled" + }, + { + "description": "Enables the is_focused command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-focused" + }, + { + "description": "Enables the is_fullscreen command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-fullscreen" + }, + { + "description": "Enables the is_maximizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-maximizable" + }, + { + "description": "Enables the is_maximized command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-maximized" + }, + { + "description": "Enables the is_minimizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-minimizable" + }, + { + "description": "Enables the is_minimized command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-minimized" + }, + { + "description": "Enables the is_resizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-resizable" + }, + { + "description": "Enables the is_visible command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-visible" + }, + { + "description": "Enables the maximize command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-maximize" + }, + { + "description": "Enables the minimize command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-minimize" + }, + { + "description": "Enables the monitor_from_point command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-monitor-from-point" + }, + { + "description": "Enables the outer_position command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-outer-position" + }, + { + "description": "Enables the outer_size command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-outer-size" + }, + { + "description": "Enables the primary_monitor command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-primary-monitor" + }, + { + "description": "Enables the request_user_attention command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-request-user-attention" + }, + { + "description": "Enables the scale_factor command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-scale-factor" + }, + { + "description": "Enables the set_always_on_bottom command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-always-on-bottom" + }, + { + "description": "Enables the set_always_on_top command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-always-on-top" + }, + { + "description": "Enables the set_background_color command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-background-color" + }, + { + "description": "Enables the set_badge_count command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-badge-count" + }, + { + "description": "Enables the set_badge_label command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-badge-label" + }, + { + "description": "Enables the set_closable command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-closable" + }, + { + "description": "Enables the set_content_protected command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-content-protected" + }, + { + "description": "Enables the set_cursor_grab command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-cursor-grab" + }, + { + "description": "Enables the set_cursor_icon command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-cursor-icon" + }, + { + "description": "Enables the set_cursor_position command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-cursor-position" + }, + { + "description": "Enables the set_cursor_visible command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-cursor-visible" + }, + { + "description": "Enables the set_decorations command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-decorations" + }, + { + "description": "Enables the set_effects command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-effects" + }, + { + "description": "Enables the set_enabled command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-enabled" + }, + { + "description": "Enables the set_focus command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-focus" + }, + { + "description": "Enables the set_fullscreen command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-fullscreen" + }, + { + "description": "Enables the set_icon command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-icon" + }, + { + "description": "Enables the set_ignore_cursor_events command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-ignore-cursor-events" + }, + { + "description": "Enables the set_max_size command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-max-size" + }, + { + "description": "Enables the set_maximizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-maximizable" + }, + { + "description": "Enables the set_min_size command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-min-size" + }, + { + "description": "Enables the set_minimizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-minimizable" + }, + { + "description": "Enables the set_overlay_icon command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-overlay-icon" + }, + { + "description": "Enables the set_position command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-position" + }, + { + "description": "Enables the set_progress_bar command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-progress-bar" + }, + { + "description": "Enables the set_resizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-resizable" + }, + { + "description": "Enables the set_shadow command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-shadow" + }, + { + "description": "Enables the set_size command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-size" + }, + { + "description": "Enables the set_size_constraints command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-size-constraints" + }, + { + "description": "Enables the set_skip_taskbar command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-skip-taskbar" + }, + { + "description": "Enables the set_theme command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-theme" + }, + { + "description": "Enables the set_title command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-title" + }, + { + "description": "Enables the set_title_bar_style command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-title-bar-style" + }, + { + "description": "Enables the set_visible_on_all_workspaces command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-visible-on-all-workspaces" + }, + { + "description": "Enables the show command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-show" + }, + { + "description": "Enables the start_dragging command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-start-dragging" + }, + { + "description": "Enables the start_resize_dragging command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-start-resize-dragging" + }, + { + "description": "Enables the theme command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-theme" + }, + { + "description": "Enables the title command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-title" + }, + { + "description": "Enables the toggle_maximize command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-toggle-maximize" + }, + { + "description": "Enables the unmaximize command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-unmaximize" + }, + { + "description": "Enables the unminimize command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-unminimize" + }, + { + "description": "Denies the available_monitors command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-available-monitors" + }, + { + "description": "Denies the center command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-center" + }, + { + "description": "Denies the close command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-close" + }, + { + "description": "Denies the create command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-create" + }, + { + "description": "Denies the current_monitor command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-current-monitor" + }, + { + "description": "Denies the cursor_position command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-cursor-position" + }, + { + "description": "Denies the destroy command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-destroy" + }, + { + "description": "Denies the get_all_windows command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-get-all-windows" + }, + { + "description": "Denies the hide command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-hide" + }, + { + "description": "Denies the inner_position command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-inner-position" + }, + { + "description": "Denies the inner_size command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-inner-size" + }, + { + "description": "Denies the internal_toggle_maximize command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-internal-toggle-maximize" + }, + { + "description": "Denies the is_always_on_top command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-always-on-top" + }, + { + "description": "Denies the is_closable command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-closable" + }, + { + "description": "Denies the is_decorated command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-decorated" + }, + { + "description": "Denies the is_enabled command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-enabled" + }, + { + "description": "Denies the is_focused command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-focused" + }, + { + "description": "Denies the is_fullscreen command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-fullscreen" + }, + { + "description": "Denies the is_maximizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-maximizable" + }, + { + "description": "Denies the is_maximized command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-maximized" + }, + { + "description": "Denies the is_minimizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-minimizable" + }, + { + "description": "Denies the is_minimized command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-minimized" + }, + { + "description": "Denies the is_resizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-resizable" + }, + { + "description": "Denies the is_visible command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-visible" + }, + { + "description": "Denies the maximize command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-maximize" + }, + { + "description": "Denies the minimize command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-minimize" + }, + { + "description": "Denies the monitor_from_point command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-monitor-from-point" + }, + { + "description": "Denies the outer_position command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-outer-position" + }, + { + "description": "Denies the outer_size command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-outer-size" + }, + { + "description": "Denies the primary_monitor command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-primary-monitor" + }, + { + "description": "Denies the request_user_attention command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-request-user-attention" + }, + { + "description": "Denies the scale_factor command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-scale-factor" + }, + { + "description": "Denies the set_always_on_bottom command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-always-on-bottom" + }, + { + "description": "Denies the set_always_on_top command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-always-on-top" + }, + { + "description": "Denies the set_background_color command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-background-color" + }, + { + "description": "Denies the set_badge_count command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-badge-count" + }, + { + "description": "Denies the set_badge_label command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-badge-label" + }, + { + "description": "Denies the set_closable command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-closable" + }, + { + "description": "Denies the set_content_protected command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-content-protected" + }, + { + "description": "Denies the set_cursor_grab command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-cursor-grab" + }, + { + "description": "Denies the set_cursor_icon command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-cursor-icon" + }, + { + "description": "Denies the set_cursor_position command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-cursor-position" + }, + { + "description": "Denies the set_cursor_visible command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-cursor-visible" + }, + { + "description": "Denies the set_decorations command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-decorations" + }, + { + "description": "Denies the set_effects command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-effects" + }, + { + "description": "Denies the set_enabled command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-enabled" + }, + { + "description": "Denies the set_focus command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-focus" + }, + { + "description": "Denies the set_fullscreen command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-fullscreen" + }, + { + "description": "Denies the set_icon command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-icon" + }, + { + "description": "Denies the set_ignore_cursor_events command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-ignore-cursor-events" + }, + { + "description": "Denies the set_max_size command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-max-size" + }, + { + "description": "Denies the set_maximizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-maximizable" + }, + { + "description": "Denies the set_min_size command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-min-size" + }, + { + "description": "Denies the set_minimizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-minimizable" + }, + { + "description": "Denies the set_overlay_icon command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-overlay-icon" + }, + { + "description": "Denies the set_position command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-position" + }, + { + "description": "Denies the set_progress_bar command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-progress-bar" + }, + { + "description": "Denies the set_resizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-resizable" + }, + { + "description": "Denies the set_shadow command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-shadow" + }, + { + "description": "Denies the set_size command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-size" + }, + { + "description": "Denies the set_size_constraints command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-size-constraints" + }, + { + "description": "Denies the set_skip_taskbar command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-skip-taskbar" + }, + { + "description": "Denies the set_theme command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-theme" + }, + { + "description": "Denies the set_title command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-title" + }, + { + "description": "Denies the set_title_bar_style command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-title-bar-style" + }, + { + "description": "Denies the set_visible_on_all_workspaces command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-visible-on-all-workspaces" + }, + { + "description": "Denies the show command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-show" + }, + { + "description": "Denies the start_dragging command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-start-dragging" + }, + { + "description": "Denies the start_resize_dragging command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-start-resize-dragging" + }, + { + "description": "Denies the theme command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-theme" + }, + { + "description": "Denies the title command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-title" + }, + { + "description": "Denies the toggle_maximize command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-toggle-maximize" + }, + { + "description": "Denies the unmaximize command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-unmaximize" + }, + { + "description": "Denies the unminimize command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-unminimize" + }, + { + "description": "This permission set configures which\nprocess feeatures are by default exposed.\n\n#### Granted Permissions\n\nThis enables to quit via `allow-exit` and restart via `allow-restart`\nthe application.\n", + "type": "string", + "const": "process:default" + }, + { + "description": "Enables the exit command without any pre-configured scope.", + "type": "string", + "const": "process:allow-exit" + }, + { + "description": "Enables the restart command without any pre-configured scope.", + "type": "string", + "const": "process:allow-restart" + }, + { + "description": "Denies the exit command without any pre-configured scope.", + "type": "string", + "const": "process:deny-exit" + }, + { + "description": "Denies the restart command without any pre-configured scope.", + "type": "string", + "const": "process:deny-restart" + }, + { + "description": "This permission set configures which\nshell functionality is exposed by default.\n\n#### Granted Permissions\n\nIt allows to use the `open` functionality without any specific\nscope pre-configured. It will allow opening `http(s)://`,\n`tel:` and `mailto:` links.\n", + "type": "string", + "const": "shell:default" + }, + { + "description": "Enables the execute command without any pre-configured scope.", + "type": "string", + "const": "shell:allow-execute" + }, + { + "description": "Enables the kill command without any pre-configured scope.", + "type": "string", + "const": "shell:allow-kill" + }, + { + "description": "Enables the open command without any pre-configured scope.", + "type": "string", + "const": "shell:allow-open" + }, + { + "description": "Enables the spawn command without any pre-configured scope.", + "type": "string", + "const": "shell:allow-spawn" + }, + { + "description": "Enables the stdin_write command without any pre-configured scope.", + "type": "string", + "const": "shell:allow-stdin-write" + }, + { + "description": "Denies the execute command without any pre-configured scope.", + "type": "string", + "const": "shell:deny-execute" + }, + { + "description": "Denies the kill command without any pre-configured scope.", + "type": "string", + "const": "shell:deny-kill" + }, + { + "description": "Denies the open command without any pre-configured scope.", + "type": "string", + "const": "shell:deny-open" + }, + { + "description": "Denies the spawn command without any pre-configured scope.", + "type": "string", + "const": "shell:deny-spawn" + }, + { + "description": "Denies the stdin_write command without any pre-configured scope.", + "type": "string", + "const": "shell:deny-stdin-write" + }, + { + "description": "This permission set configures which kind of\nupdater functions are exposed to the frontend.\n\n#### Granted Permissions\n\nThe full workflow from checking for updates to installing them\nis enabled.\n\n", + "type": "string", + "const": "updater:default" + }, + { + "description": "Enables the check command without any pre-configured scope.", + "type": "string", + "const": "updater:allow-check" + }, + { + "description": "Enables the download command without any pre-configured scope.", + "type": "string", + "const": "updater:allow-download" + }, + { + "description": "Enables the download_and_install command without any pre-configured scope.", + "type": "string", + "const": "updater:allow-download-and-install" + }, + { + "description": "Enables the install command without any pre-configured scope.", + "type": "string", + "const": "updater:allow-install" + }, + { + "description": "Denies the check command without any pre-configured scope.", + "type": "string", + "const": "updater:deny-check" + }, + { + "description": "Denies the download command without any pre-configured scope.", + "type": "string", + "const": "updater:deny-download" + }, + { + "description": "Denies the download_and_install command without any pre-configured scope.", + "type": "string", + "const": "updater:deny-download-and-install" + }, + { + "description": "Denies the install command without any pre-configured scope.", + "type": "string", + "const": "updater:deny-install" + } + ] + }, + "Value": { + "description": "All supported ACL values.", + "anyOf": [ + { + "description": "Represents a null JSON value.", + "type": "null" + }, + { + "description": "Represents a [`bool`].", + "type": "boolean" + }, + { + "description": "Represents a valid ACL [`Number`].", + "allOf": [ + { + "$ref": "#/definitions/Number" + } + ] + }, + { + "description": "Represents a [`String`].", + "type": "string" + }, + { + "description": "Represents a list of other [`Value`]s.", + "type": "array", + "items": { + "$ref": "#/definitions/Value" + } + }, + { + "description": "Represents a map of [`String`] keys to [`Value`]s.", + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/Value" + } + } + ] + }, + "Number": { + "description": "A valid ACL number.", + "anyOf": [ + { + "description": "Represents an [`i64`].", + "type": "integer", + "format": "int64" + }, + { + "description": "Represents a [`f64`].", + "type": "number", + "format": "double" + } + ] + }, + "Target": { + "description": "Platform target.", + "oneOf": [ + { + "description": "MacOS.", + "type": "string", + "enum": [ + "macOS" + ] + }, + { + "description": "Windows.", + "type": "string", + "enum": [ + "windows" + ] + }, + { + "description": "Linux.", + "type": "string", + "enum": [ + "linux" + ] + }, + { + "description": "Android.", + "type": "string", + "enum": [ + "android" + ] + }, + { + "description": "iOS.", + "type": "string", + "enum": [ + "iOS" + ] + } + ] + }, + "ShellScopeEntryAllowedArg": { + "description": "A command argument allowed to be executed by the webview API.", + "anyOf": [ + { + "description": "A non-configurable argument that is passed to the command in the order it was specified.", + "type": "string" + }, + { + "description": "A variable that is set while calling the command from the webview API.", + "type": "object", + "required": [ + "validator" + ], + "properties": { + "raw": { + "description": "Marks the validator as a raw regex, meaning the plugin should not make any modification at runtime.\n\nThis means the regex will not match on the entire string by default, which might be exploited if your regex allow unexpected input to be considered valid. When using this option, make sure your regex is correct.", + "default": false, + "type": "boolean" + }, + "validator": { + "description": "[regex] validator to require passed values to conform to an expected input.\n\nThis will require the argument value passed to this variable to match the `validator` regex before it will be executed.\n\nThe regex string is by default surrounded by `^...$` to match the full string. For example the `https?://\\w+` regex would be registered as `^https?://\\w+$`.\n\n[regex]: ", + "type": "string" + } + }, + "additionalProperties": false + } + ] + }, + "ShellScopeEntryAllowedArgs": { + "description": "A set of command arguments allowed to be executed by the webview API.\n\nA value of `true` will allow any arguments to be passed to the command. `false` will disable all arguments. A list of [`ShellScopeEntryAllowedArg`] will set those arguments as the only valid arguments to be passed to the attached command configuration.", + "anyOf": [ + { + "description": "Use a simple boolean to allow all or disable all arguments to this command configuration.", + "type": "boolean" + }, + { + "description": "A specific set of [`ShellScopeEntryAllowedArg`] that are valid to call for the command configuration.", + "type": "array", + "items": { + "$ref": "#/definitions/ShellScopeEntryAllowedArg" + } + } + ] + } + } +} \ No newline at end of file diff --git a/nym-wallet/src-tauri/src/config/mod.rs b/nym-wallet/src-tauri/src/config/mod.rs index 1ba8c2166a..87014b1b50 100644 --- a/nym-wallet/src-tauri/src/config/mod.rs +++ b/nym-wallet/src-tauri/src/config/mod.rs @@ -11,6 +11,7 @@ use nym_validator_client::nyxd::AccountId as CosmosAccountId; use serde::de::DeserializeOwned; use serde::{Deserialize, Serialize}; use strum::IntoEnumIterator; +use tauri::Manager; use url::Url; use nym_config::defaults::{DenomDetailsOwned, NymNetworkDetails, ValidatorDetails}; @@ -104,32 +105,35 @@ impl NetworkConfig { } impl Config { - fn root_directory() -> PathBuf { - tauri::api::path::config_dir().expect("Failed to get config directory") + fn root_directory(app_handle: &tauri::AppHandle) -> PathBuf { + app_handle + .path() + .local_data_dir() + .expect("Failed to get config directory") } - fn config_directory() -> PathBuf { - Self::root_directory().join(CONFIG_DIR_NAME) + fn config_directory(app_handle: &tauri::AppHandle) -> PathBuf { + Self::root_directory(app_handle).join(CONFIG_DIR_NAME) } - fn config_file_path(network: Option) -> PathBuf { + fn config_file_path(app_handle: &tauri::AppHandle, network: Option) -> PathBuf { if let Some(network) = network { let network_filename = format!("{}.toml", network.as_key()); - Self::config_directory().join(network_filename) + Self::config_directory(app_handle).join(network_filename) } else { - Self::config_directory().join(CONFIG_FILENAME) + Self::config_directory(app_handle).join(CONFIG_FILENAME) } } - pub fn save_to_files(&self) -> io::Result<()> { + pub fn save_to_files(&self, app_handle: &tauri::AppHandle) -> io::Result<()> { log::trace!("Config::save_to_file"); // Make sure the whole directory structure actually exists - fs::create_dir_all(Self::config_directory())?; + fs::create_dir_all(Self::config_directory(app_handle))?; // Global config if let Some(global) = &self.global { - let location = Self::config_file_path(None); + let location = Self::config_file_path(app_handle, None); match toml::to_string_pretty(&global) .map_err(|toml_err| io::Error::new(io::ErrorKind::Other, toml_err)) @@ -150,7 +154,7 @@ impl Config { } }; - let location = Self::config_file_path(Some(network)); + let location = Self::config_file_path(app_handle, Some(network)); match toml::to_string_pretty(config) .map_err(|toml_err| io::Error::new(io::ErrorKind::Other, toml_err)) .map(|toml| fs::write(location.clone(), toml)) @@ -162,10 +166,10 @@ impl Config { Ok(()) } - pub fn load_from_files() -> Self { + pub fn load_from_files(app_handle: &tauri::AppHandle) -> Self { // Global let global = { - let file = Self::config_file_path(None); + let file = Self::config_file_path(app_handle, None); match load_from_file::(file.clone()) { Ok(global) => { log::debug!("Loaded from file {:#?}", file); @@ -181,7 +185,7 @@ impl Config { // One file per network let mut networks = HashMap::new(); for network in WalletNetwork::iter() { - let file = Self::config_file_path(Some(network)); + let file = Self::config_file_path(app_handle, Some(network)); match load_from_file::(file.clone()) { Ok(config) => { log::trace!("Loaded from file {:#?}", file); @@ -261,6 +265,8 @@ impl Config { } } + //////// + pub fn select_nyxd_url(&mut self, nyxd_url: Url, network: WalletNetwork) { if let Some(net) = self.networks.get_mut(&network.as_key()) { net.selected_nyxd_url = Some(nyxd_url); @@ -531,145 +537,145 @@ impl From for NetworkDetails { } } -#[cfg(test)] -mod tests { - use super::*; +// #[cfg(test)] +// mod tests { +// use super::*; - fn test_config() -> Config { - let netconfig = NetworkConfig { - selected_nyxd_url: None, - selected_api_url: Some("https://my_api_url.com".parse().unwrap()), +// fn test_config() -> Config { +// let netconfig = NetworkConfig { +// selected_nyxd_url: None, +// selected_api_url: Some("https://my_api_url.com".parse().unwrap()), - nyxd_urls: Some(vec![ - ValidatorConfigEntry { - nyxd_url: "https://foo".parse().unwrap(), - nyxd_name: Some("FooName".to_string()), - api_url: None, - }, - ValidatorConfigEntry { - nyxd_url: "https://bar".parse().unwrap(), - nyxd_name: None, - api_url: Some("https://bar/api".parse().unwrap()), - }, - ValidatorConfigEntry { - nyxd_url: "https://baz".parse().unwrap(), - nyxd_name: None, - api_url: Some("https://baz/api".parse().unwrap()), - }, - ]), - ..NetworkConfig::default() - }; +// nyxd_urls: Some(vec![ +// ValidatorConfigEntry { +// nyxd_url: "https://foo".parse().unwrap(), +// nyxd_name: Some("FooName".to_string()), +// api_url: None, +// }, +// ValidatorConfigEntry { +// nyxd_url: "https://bar".parse().unwrap(), +// nyxd_name: None, +// api_url: Some("https://bar/api".parse().unwrap()), +// }, +// ValidatorConfigEntry { +// nyxd_url: "https://baz".parse().unwrap(), +// nyxd_name: None, +// api_url: Some("https://baz/api".parse().unwrap()), +// }, +// ]), +// ..NetworkConfig::default() +// }; - Config { - base: Base::default(), - global: Some(GlobalConfig::default()), - networks: [(WalletNetwork::MAINNET.as_key(), netconfig)] - .into_iter() - .collect(), - } - } +// Config { +// base: Base::default(), +// global: Some(GlobalConfig::default()), +// networks: [(WalletNetwork::MAINNET.as_key(), netconfig)] +// .into_iter() +// .collect(), +// } +// } - #[test] - fn serialize_to_toml() { - let config = test_config(); - let netconfig = &config.networks[&WalletNetwork::MAINNET.as_key()]; - assert_eq!( - toml::to_string_pretty(netconfig).unwrap(), - r#"version = 1 -selected_api_url = 'https://my_api_url.com/' +// #[test] +// fn serialize_to_toml() { +// let config = test_config(); +// let netconfig = &config.networks[&WalletNetwork::MAINNET.as_key()]; +// assert_eq!( +// toml::to_string_pretty(netconfig).unwrap(), +// r#"version = 1 +// selected_api_url = 'https://my_api_url.com/' -[[nyxd_urls]] -nyxd_url = 'https://foo/' -nyxd_name = 'FooName' +// [[nyxd_urls]] +// nyxd_url = 'https://foo/' +// nyxd_name = 'FooName' -[[nyxd_urls]] -nyxd_url = 'https://bar/' -api_url = 'https://bar/api' +// [[nyxd_urls]] +// nyxd_url = 'https://bar/' +// api_url = 'https://bar/api' -[[nyxd_urls]] -nyxd_url = 'https://baz/' -api_url = 'https://baz/api' -"# - ); - } +// [[nyxd_urls]] +// nyxd_url = 'https://baz/' +// api_url = 'https://baz/api' +// "# +// ); +// } - #[test] - fn serialize_to_json() { - let config = test_config(); - let netconfig = &config.networks[&WalletNetwork::MAINNET.as_key()]; - println!("{}", serde_json::to_string_pretty(netconfig).unwrap()); - assert_eq!( - serde_json::to_string_pretty(netconfig).unwrap(), - r#"{ - "version": 1, - "selected_nyxd_url": null, - "default_nyxd_url": null, - "selected_api_url": "https://my_api_url.com/", - "nyxd_urls": [ - { - "nyxd_url": "https://foo/", - "nyxd_name": "FooName", - "api_url": null - }, - { - "nyxd_url": "https://bar/", - "nyxd_name": null, - "api_url": "https://bar/api" - }, - { - "nyxd_url": "https://baz/", - "nyxd_name": null, - "api_url": "https://baz/api" - } - ] -}"# - ); - } +// #[test] +// fn serialize_to_json() { +// let config = test_config(); +// let netconfig = &config.networks[&WalletNetwork::MAINNET.as_key()]; +// println!("{}", serde_json::to_string_pretty(netconfig).unwrap()); +// assert_eq!( +// serde_json::to_string_pretty(netconfig).unwrap(), +// r#"{ +// "version": 1, +// "selected_nyxd_url": null, +// "default_nyxd_url": null, +// "selected_api_url": "https://my_api_url.com/", +// "nyxd_urls": [ +// { +// "nyxd_url": "https://foo/", +// "nyxd_name": "FooName", +// "api_url": null +// }, +// { +// "nyxd_url": "https://bar/", +// "nyxd_name": null, +// "api_url": "https://bar/api" +// }, +// { +// "nyxd_url": "https://baz/", +// "nyxd_name": null, +// "api_url": "https://baz/api" +// } +// ] +// }"# +// ); +// } - #[test] - fn serialize_and_deserialize_to_toml() { - let config = test_config(); - let netconfig = &config.networks[&WalletNetwork::MAINNET.as_key()]; - let config_str = toml::to_string_pretty(netconfig).unwrap(); - let config_from_toml: NetworkConfig = toml::from_str(&config_str).unwrap(); - assert_eq!(netconfig, &config_from_toml); - } +// #[test] +// fn serialize_and_deserialize_to_toml() { +// let config = test_config(); +// let netconfig = &config.networks[&WalletNetwork::MAINNET.as_key()]; +// let config_str = toml::to_string_pretty(netconfig).unwrap(); +// let config_from_toml: NetworkConfig = toml::from_str(&config_str).unwrap(); +// assert_eq!(netconfig, &config_from_toml); +// } - #[test] - fn get_urls_parsed_from_config() { - let config = test_config(); +// #[test] +// fn get_urls_parsed_from_config() { +// let config = test_config(); - let nyxd_url = config - .get_configured_validators(WalletNetwork::MAINNET) - .next() - .map(|v| v.nyxd_url) - .unwrap(); - assert_eq!(nyxd_url.as_ref(), "https://foo/"); +// let nyxd_url = config +// .get_configured_validators(WalletNetwork::MAINNET) +// .next() +// .map(|v| v.nyxd_url) +// .unwrap(); +// assert_eq!(nyxd_url.as_ref(), "https://foo/"); - // The first entry is missing an API URL - let api_url = config - .get_configured_validators(WalletNetwork::MAINNET) - .next() - .and_then(|v| v.api_url); - assert_eq!(api_url, None); - } +// // The first entry is missing an API URL +// let api_url = config +// .get_configured_validators(WalletNetwork::MAINNET) +// .next() +// .and_then(|v| v.api_url); +// assert_eq!(api_url, None); +// } - #[test] - fn get_urls_from_defaults() { - let config = Config::default(); +// #[test] +// fn get_urls_from_defaults() { +// let config = Config::default(); - let nyxd_url = config - .get_base_validators(WalletNetwork::MAINNET) - .next() - .map(|v| v.nyxd_url) - .unwrap(); - assert_eq!(nyxd_url.as_ref(), "https://rpc.nymtech.net/"); +// let nyxd_url = config +// .get_base_validators(WalletNetwork::MAINNET) +// .next() +// .map(|v| v.nyxd_url) +// .unwrap(); +// assert_eq!(nyxd_url.as_ref(), "https://rpc.nymtech.net/"); - let api_url = config - .get_base_validators(WalletNetwork::MAINNET) - .next() - .and_then(|v| v.api_url) - .unwrap(); - assert_eq!(api_url.as_ref(), "https://validator.nymtech.net/api/",); - } -} +// let api_url = config +// .get_base_validators(WalletNetwork::MAINNET) +// .next() +// .and_then(|v| v.api_url) +// .unwrap(); +// assert_eq!(api_url.as_ref(), "https://validator.nymtech.net/api/",); +// } +// } diff --git a/nym-wallet/src-tauri/src/log.rs b/nym-wallet/src-tauri/src/log.rs index 6c9eb072da..d7642291e3 100644 --- a/nym-wallet/src-tauri/src/log.rs +++ b/nym-wallet/src-tauri/src/log.rs @@ -3,7 +3,7 @@ use std::str::FromStr; use fern::colors::{Color, ColoredLevelConfig}; use serde::Serialize; use serde_repr::{Deserialize_repr, Serialize_repr}; -use tauri::Manager; +use tauri::Emitter; use time::{format_description, OffsetDateTime}; fn formatted_time() -> String { @@ -61,7 +61,7 @@ pub fn setup_logging(app_handle: tauri::AppHandle) -> Result<(), log::SetLoggerE message: record.args().to_string(), level: record.level().into(), }; - app_handle.emit_all("log://log", msg).unwrap(); + app_handle.emit("log://log", msg).unwrap(); })); base_config diff --git a/nym-wallet/src-tauri/src/main.rs b/nym-wallet/src-tauri/src/main.rs index 6b3c6a1b27..0f9aa2bcdc 100644 --- a/nym-wallet/src-tauri/src/main.rs +++ b/nym-wallet/src-tauri/src/main.rs @@ -3,11 +3,11 @@ windows_subsystem = "windows" )] -use tauri::{Manager, Menu}; - use nym_mixnet_contract_common::{Gateway, MixNode}; +use tauri::menu::{MenuBuilder, MenuItemBuilder, SubmenuBuilder}; +use tauri::Manager; -use crate::menu::AddDefaultSubmenus; +use crate::menu::SHOW_LOG_WINDOW; use crate::operations::app; use crate::operations::help; use crate::operations::mixnet; @@ -210,13 +210,31 @@ fn main() { app::react::set_react_state, app::react::get_react_state, ]) - .menu(Menu::os_default(&context.package_info().name).add_default_app_submenus()) - .on_menu_event(|event| { - if event.menu_item_id() == menu::SHOW_LOG_WINDOW { - let _r = help::log::help_log_toggle_window(event.window().app_handle()); + .menu(|app| { + // Create a menu builder + let menu_builder = MenuBuilder::new(app); + if ::std::env::var("NYM_WALLET_ENABLE_LOG").is_ok() { + let help_text = MenuItemBuilder::with_id(SHOW_LOG_WINDOW, "Show logs") + .build(app) + .expect("Failed to create menu item"); + + let submenu = SubmenuBuilder::new(app, "Help") + .items(&[&help_text]) + .build() + .expect("Failed to create help submenu"); + + menu_builder.item(&submenu).build() + } else { + // Build a default menu without the submenu + menu_builder.build() } }) - .setup(|app| Ok(log::setup_logging(app.app_handle())?)) + .on_menu_event(|app, event| { + if event.id() == SHOW_LOG_WINDOW { + let _r = help::log::help_log_toggle_window(app.app_handle().clone()); + } + }) + .setup(|app| Ok(log::setup_logging(app.app_handle().clone())?)) .run(context) .expect("error while running tauri application"); } diff --git a/nym-wallet/src-tauri/src/menu.rs b/nym-wallet/src-tauri/src/menu.rs index b38e73a639..618944ca13 100644 --- a/nym-wallet/src-tauri/src/menu.rs +++ b/nym-wallet/src-tauri/src/menu.rs @@ -1,5 +1,5 @@ -use tauri::Menu; -use tauri::{CustomMenuItem, Submenu}; +use tauri::menu::Menu; +use tauri::menu::{MenuBuilder, MenuItemBuilder, SubmenuBuilder}; pub const SHOW_LOG_WINDOW: &str = "show_log_window"; @@ -7,16 +7,29 @@ pub trait AddDefaultSubmenus { fn add_default_app_submenus(self) -> Self; } -impl AddDefaultSubmenus for Menu { +impl AddDefaultSubmenus for Menu { #[allow(dead_code)] fn add_default_app_submenus(self) -> Self { if ::std::env::var("NYM_WALLET_ENABLE_LOG").is_ok() { - let submenu = Submenu::new( - "Help", - Menu::new().add_item(CustomMenuItem::new(SHOW_LOG_WINDOW, "Show logs")), - ); - return self.add_submenu(submenu); + let app_handle = self.app_handle(); + + let help_text = MenuItemBuilder::with_id(SHOW_LOG_WINDOW, "Show logs") + .build(app_handle) + .expect("Failed to create menu item"); + + let submenu = SubmenuBuilder::new(app_handle, "Help") + .items(&[&help_text]) + .build() + .expect("Failed to create help submenu"); + + let menu_builder = MenuBuilder::new(app_handle); + + match menu_builder.item(&submenu).build() { + Ok(new_menu) => new_menu, + Err(_) => self, + } + } else { + self } - self } } diff --git a/nym-wallet/src-tauri/src/operations/app/version.rs b/nym-wallet/src-tauri/src/operations/app/version.rs index d9d475e931..d904449cf0 100644 --- a/nym-wallet/src-tauri/src/operations/app/version.rs +++ b/nym-wallet/src-tauri/src/operations/app/version.rs @@ -3,27 +3,46 @@ use crate::error::BackendError; use nym_wallet_types::app::AppVersion; +use tauri_plugin_updater::UpdaterExt; #[tauri::command] pub async fn check_version(handle: tauri::AppHandle) -> Result { log::info!(">>> Getting app version info"); - let res = tauri::updater::builder(handle) - .check() - .await - .map(|u| AppVersion { - current_version: u.current_version().to_string(), - latest_version: u.latest_version().to_owned(), - is_update_available: u.is_update_available(), + + let updater = handle.updater().map_err(|e| { + log::error!("Failed to get updater: {}", e); + BackendError::CheckAppVersionError + })?; + + // Then check for updates + let update_info = updater.check().await.map_err(|e| { + log::error!("An error occurred while checking for app update {}", e); + BackendError::CheckAppVersionError + })?; + + // Process the result + if let Some(update) = update_info { + log::debug!( + "<<< update available: [true], current version {}, latest version {}", + update.current_version, + update.version + ); + Ok(AppVersion { + current_version: update.current_version.to_string(), + latest_version: update.version, + is_update_available: true, }) - .map_err(|e| { - log::error!("An error ocurred while checking for app update {}", e); - BackendError::CheckAppVersionError - })?; - log::debug!( - "<<< update available: [{}], current version {}, latest version {}", - res.is_update_available, - res.current_version, - res.latest_version - ); - Ok(res) + } else { + // No update available + let current_version = handle.package_info().version.to_string(); + log::debug!( + "<<< update available: [false], current version {}", + current_version + ); + Ok(AppVersion { + current_version: current_version.clone(), + latest_version: current_version, + is_update_available: false, + }) + } } diff --git a/nym-wallet/src-tauri/src/operations/app/window.rs b/nym-wallet/src-tauri/src/operations/app/window.rs index ce30536fe0..d15fde65cf 100644 --- a/nym-wallet/src-tauri/src/operations/app/window.rs +++ b/nym-wallet/src-tauri/src/operations/app/window.rs @@ -26,10 +26,10 @@ async fn create_window( ) -> Result<(), BackendError> { // create the new window first, to stop the app process from exiting log::info!("Creating {} window...", new_window_label); - match tauri::WindowBuilder::new( + match tauri::WebviewWindowBuilder::new( &app_handle, - new_window_label, - tauri::WindowUrl::App(new_window_url.into()), + "main", + tauri::WebviewUrl::App(new_window_url.into()), ) .title("Nym Wallet") .build() @@ -49,7 +49,7 @@ async fn create_window( } // close the old window - match app_handle.windows().get(try_close_window_label) { + match app_handle.get_webview_window(try_close_window_label) { Some(try_close_window) => { if let Err(err) = try_close_window.close() { log::error!("Could not close window: {err}") diff --git a/nym-wallet/src-tauri/src/operations/help/log.rs b/nym-wallet/src-tauri/src/operations/help/log.rs index c4c2357304..42f15fb02f 100644 --- a/nym-wallet/src-tauri/src/operations/help/log.rs +++ b/nym-wallet/src-tauri/src/operations/help/log.rs @@ -3,7 +3,7 @@ use tauri::Manager; #[tauri::command] pub fn help_log_toggle_window(app_handle: tauri::AppHandle) -> Result<(), BackendError> { - if let Some(current_log_window) = app_handle.windows().get("log") { + if let Some(current_log_window) = app_handle.get_webview_window("log") { log::info!("Closing log window..."); if let Err(err) = current_log_window.close() { log::error!("Unable to close log window: {err}"); @@ -12,9 +12,13 @@ pub fn help_log_toggle_window(app_handle: tauri::AppHandle) -> Result<(), Backen } log::info!("Creating log window..."); - match tauri::WindowBuilder::new(&app_handle, "log", tauri::WindowUrl::App("log.html".into())) - .title("Nym Wallet Logs") - .build() + match tauri::WebviewWindowBuilder::new( + &app_handle, + "log", + tauri::WebviewUrl::App("log.html".into()), + ) + .title("Nym Wallet Logs") + .build() { Ok(window) => { if let Err(err) = window.set_focus() { diff --git a/nym-wallet/src-tauri/src/wallet_storage/mod.rs b/nym-wallet/src-tauri/src/wallet_storage/mod.rs index 14d295a8a4..c620351417 100644 --- a/nym-wallet/src-tauri/src/wallet_storage/mod.rs +++ b/nym-wallet/src-tauri/src/wallet_storage/mod.rs @@ -19,6 +19,7 @@ use nym_validator_client::nyxd::bip32::DerivationPath; use std::ffi::OsString; use std::fs::{self, create_dir_all, OpenOptions}; use std::path::{Path, PathBuf}; +use tauri::Manager; use time::OffsetDateTime; #[cfg(test)] @@ -37,14 +38,18 @@ pub(crate) const DEFAULT_LOGIN_ID: &str = "default"; /// this name. pub(crate) const DEFAULT_FIRST_ACCOUNT_NAME: &str = "Account 1"; -fn get_storage_directory() -> Result { - tauri::api::path::local_data_dir() +fn get_storage_directory(app_handle: &tauri::AppHandle) -> Result { + app_handle + .path() + .local_data_dir() .map(|dir| dir.join(STORAGE_DIR_NAME)) .ok_or(BackendError::UnknownStorageDirectory) } -pub(crate) fn wallet_login_filepath() -> Result { - get_storage_directory().map(|dir| dir.join(WALLET_INFO_FILENAME)) +pub(crate) fn wallet_login_filepath( + app_handle: &tauri::AppHandle, +) -> Result { + get_storage_directory(app_handle).map(|dir| dir.join(WALLET_INFO_FILENAME)) } fn write_to_file(filepath: &Path, wallet: &StoredWallet) -> Result<(), BackendError> { @@ -59,8 +64,10 @@ fn write_to_file(filepath: &Path, wallet: &StoredWallet) -> Result<(), BackendEr /// Load stored wallet file #[allow(unused)] -pub(crate) fn load_existing_wallet() -> Result { - let store_dir = get_storage_directory()?; +pub(crate) fn load_existing_wallet( + app_handle: &tauri::AppHandle, +) -> Result { + let store_dir = get_storage_directory(app_handle)?; let filepath = store_dir.join(WALLET_INFO_FILENAME); load_existing_wallet_at_file(&filepath) } @@ -77,10 +84,11 @@ fn load_existing_wallet_at_file(filepath: &Path) -> Result Result { - let store_dir = get_storage_directory()?; + let store_dir = get_storage_directory(app_handle)?; let filepath = store_dir.join(WALLET_INFO_FILENAME); load_existing_login_at_file(&filepath, id, password) } @@ -468,1819 +476,1819 @@ fn rename_account_in_login_at_file( write_to_file(filepath, &stored_wallet) } -#[cfg(test)] -mod tests { - use crate::wallet_storage::account_data::WalletAccount; - - use super::*; - use nym_config::defaults::COSMOS_DERIVATION_PATH; - use std::str::FromStr; - use tempfile::tempdir; - - #[test] - fn trying_to_load_nonexistant_wallet_fails() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let id1 = LoginId::new("first".to_string()); - let password = UserPassword::new("password".to_string()); - - assert!(matches!( - load_existing_wallet_at_file(&wallet_file), - Err(BackendError::WalletFileNotFound), - )); - assert!(matches!( - load_existing_login_at_file(&wallet_file, &id1, &password), - Err(BackendError::WalletFileNotFound), - )); - remove_login_at_file(&wallet_file, &id1).unwrap_err(); - } - - #[test] - fn store_single_login() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - - store_login_at_file(&wallet_file, account1, hd_path, id1.clone(), &password).unwrap(); - - let stored_wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); - assert_eq!(stored_wallet.len(), 1); - - let login = stored_wallet.get_encrypted_login_by_index(0).unwrap(); - assert_eq!(login.id, id1); - - // some actual ciphertext was saved - assert!(!login.account.ciphertext().is_empty()); - } - - #[test] - fn store_single_login_with_multi() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - - store_login_with_multiple_accounts_at_file( - &wallet_file, - account1, - cosmos_hd_path, - id1.clone(), - &password, - ) - .unwrap(); - - let stored_wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); - assert_eq!(stored_wallet.len(), 1); - - let login = stored_wallet.get_encrypted_login_by_index(0).unwrap(); - assert_eq!(login.id, id1); - - // some actual ciphertext was saved - assert!(!login.account.ciphertext().is_empty()); - } - - #[test] - fn store_single_login_with_multi_then_update_pwd_and_load() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let new_password = UserPassword::new("new_password".to_string()); - let id1 = LoginId::new("first".to_string()); - - store_login_with_multiple_accounts_at_file( - &wallet_file, - account1, - cosmos_hd_path, - id1.clone(), - &password, - ) - .unwrap(); - - update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); - - let stored_wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); - assert_eq!(stored_wallet.len(), 1); - - let login = stored_wallet.get_encrypted_login_by_index(0).unwrap(); - assert_eq!(login.id, id1); - - // some actual ciphertext was saved - assert!(!login.account.ciphertext().is_empty()); - } - - #[test] - fn store_twice_for_the_same_id_fails() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - - // Store the first login - store_login_at_file( - &wallet_file, - account1.clone(), - hd_path.clone(), - id1.clone(), - &password, - ) - .unwrap(); - - // and storing the same id again fails - assert!(matches!( - store_login_at_file(&wallet_file, account1, hd_path, id1, &password,), - Err(BackendError::WalletLoginIdAlreadyExists), - )); - } - - #[test] - fn store_twice_for_the_same_id_fails_with_multiple() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - - // Store the first login - store_login_with_multiple_accounts_at_file( - &wallet_file, - account1.clone(), - hd_path.clone(), - id1.clone(), - &password, - ) - .unwrap(); - - // and storing the same id again fails - assert!(matches!( - store_login_with_multiple_accounts_at_file( - &wallet_file, - account1, - hd_path, - id1, - &password, - ), - Err(BackendError::WalletLoginIdAlreadyExists), - )); - } - - #[test] - fn load_with_wrong_password_fails() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let bad_password = UserPassword::new("bad-password".to_string()); - let id1 = LoginId::new("first".to_string()); - - store_login_at_file(&wallet_file, account1, hd_path, id1.clone(), &password).unwrap(); - - // Trying to load it with wrong password now fails - assert!(matches!( - load_existing_login_at_file(&wallet_file, &id1, &bad_password), - Err(BackendError::StoreCipherError { .. }), - )); - } - - #[test] - fn load_with_wrong_password_fails_with_multi() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let bad_password = UserPassword::new("bad-password".to_string()); - let id1 = LoginId::new("first".to_string()); - - store_login_with_multiple_accounts_at_file( - &wallet_file, - account1, - hd_path, - id1.clone(), - &password, - ) - .unwrap(); - - // Trying to load it with wrong password now fails - assert!(matches!( - load_existing_login_at_file(&wallet_file, &id1, &bad_password), - Err(BackendError::StoreCipherError { .. }), - )); - } - - #[test] - fn load_with_wrong_id_fails() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - let id2 = LoginId::new("second".to_string()); - - store_login_at_file(&wallet_file, account1, hd_path, id1, &password).unwrap(); - - // Trying to load with the wrong id - assert!(matches!( - load_existing_login_at_file(&wallet_file, &id2, &password), - Err(BackendError::WalletNoSuchLoginId), - )); - } - - #[test] - fn load_with_wrong_id_fails_with_multi() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - let id2 = LoginId::new("second".to_string()); - - store_login_with_multiple_accounts_at_file(&wallet_file, account1, hd_path, id1, &password) - .unwrap(); - - // Trying to load with the wrong id - assert!(matches!( - load_existing_login_at_file(&wallet_file, &id2, &password), - Err(BackendError::WalletNoSuchLoginId), - )); - } - - #[test] - fn store_and_load_a_single_login() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - - store_login_at_file( - &wallet_file, - account1.clone(), - hd_path.clone(), - id1.clone(), - &password, - ) - .unwrap(); - - let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); - let acc = loaded_login.as_mnemonic_account().unwrap(); - assert_eq!(&account1, acc.mnemonic()); - assert_eq!(&hd_path, acc.hd_path()); - } - - #[test] - fn store_a_single_login_then_update_pwd_and_load() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let new_password = UserPassword::new("new_password".to_string()); - let id1 = LoginId::new("first".to_string()); - - store_login_at_file( - &wallet_file, - account1.clone(), - hd_path.clone(), - id1.clone(), - &password, - ) - .unwrap(); - - update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); - - let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &new_password).unwrap(); - let acc = loaded_login.as_mnemonic_account().unwrap(); - assert_eq!(&account1, acc.mnemonic()); - assert_eq!(&hd_path, acc.hd_path()); - } - - #[test] - fn store_a_single_login_then_update_pwd_with_identical_pwd_is_noop_but_okay() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - - store_login_at_file( - &wallet_file, - account1.clone(), - hd_path.clone(), - id1.clone(), - &password, - ) - .unwrap(); - - update_encrypted_logins_at_file(&wallet_file, &password, &password).unwrap(); - - let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); - let acc = loaded_login.as_mnemonic_account().unwrap(); - assert_eq!(&account1, acc.mnemonic()); - assert_eq!(&hd_path, acc.hd_path()); - } - - #[test] - fn store_a_single_login_then_update_pwd_with_wrong_current_pwd_fails() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let new_password = UserPassword::new("new_password".to_string()); - let wrong_password = UserPassword::new("wrong_password".to_string()); - let id1 = LoginId::new("first".to_string()); - - store_login_at_file(&wallet_file, account1, hd_path, id1, &password).unwrap(); - - let err = update_encrypted_logins_at_file(&wallet_file, &wrong_password, &new_password) - .unwrap_err(); - assert!(matches!(err, BackendError::StoreCipherError { .. })); - } - - #[test] - fn store_a_single_login_then_update_pwd_and_load_with_wrong_pwd_fails() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let new_password = UserPassword::new("new_password".to_string()); - let id1 = LoginId::new("first".to_string()); - - store_login_at_file(&wallet_file, account1, hd_path, id1.clone(), &password).unwrap(); - - update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); - - let err = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap_err(); - assert!(matches!(err, BackendError::StoreCipherError { .. })); - } - - #[test] - fn store_and_load_a_single_login_with_multi() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let acc1 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - - store_login_with_multiple_accounts_at_file( - &wallet_file, - acc1.clone(), - hd_path.clone(), - id1.clone(), - &password, - ) - .unwrap(); - - let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); - let accounts = loaded_login.as_multiple_accounts().unwrap(); - assert_eq!(accounts.len(), 1); - let account = accounts - .get_account(&DEFAULT_FIRST_ACCOUNT_NAME.into()) - .unwrap(); - assert_eq!(account.id().as_ref(), DEFAULT_FIRST_ACCOUNT_NAME); - assert_eq!(account.mnemonic(), &acc1); - assert_eq!(account.hd_path(), &hd_path); - } - - #[test] - fn store_a_single_login_with_multi_then_update_pwd_and_load() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let acc1 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let new_password = UserPassword::new("new_password".to_string()); - let id1 = LoginId::new("first".to_string()); - - store_login_with_multiple_accounts_at_file( - &wallet_file, - acc1.clone(), - hd_path.clone(), - id1.clone(), - &password, - ) - .unwrap(); - - update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); - - let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &new_password).unwrap(); - let accounts = loaded_login.as_multiple_accounts().unwrap(); - assert_eq!(accounts.len(), 1); - let account = accounts - .get_account(&DEFAULT_FIRST_ACCOUNT_NAME.into()) - .unwrap(); - assert_eq!(account.id().as_ref(), DEFAULT_FIRST_ACCOUNT_NAME); - assert_eq!(account.mnemonic(), &acc1); - assert_eq!(account.hd_path(), &hd_path); - } - - #[test] - fn store_a_single_login_with_multi_then_update_pwd_with_wrong_current_pwd_fails() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let acc1 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let new_password = UserPassword::new("new_password".to_string()); - let wrong_password = UserPassword::new("wrong_password".to_string()); - let id1 = LoginId::new("first".to_string()); - - store_login_with_multiple_accounts_at_file(&wallet_file, acc1, hd_path, id1, &password) - .unwrap(); - - let err = update_encrypted_logins_at_file(&wallet_file, &wrong_password, &new_password) - .unwrap_err(); - assert!(matches!(err, BackendError::StoreCipherError { .. })); - } - - #[test] - fn store_a_single_login_with_multi_then_update_pwd_and_load_with_wrong_pwd_fails() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let acc1 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let new_password = UserPassword::new("new_password".to_string()); - let id1 = LoginId::new("first".to_string()); - - store_login_with_multiple_accounts_at_file( - &wallet_file, - acc1, - hd_path, - id1.clone(), - &password, - ) - .unwrap(); - - update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); - - let err = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap_err(); - assert!(matches!(err, BackendError::StoreCipherError { .. })); - } - - #[test] - fn store_a_second_login_with_a_different_password_fails() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let account2 = Mnemonic::generate(24).unwrap(); - let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let bad_password = UserPassword::new("bad-password".to_string()); - let id1 = LoginId::new("first".to_string()); - let id2 = LoginId::new("second".to_string()); - - store_login_at_file( - &wallet_file, - account1, - cosmos_hd_path.clone(), - id1, - &password, - ) - .unwrap(); - - // Can't store a second login if you use different password - assert!(matches!( - store_login_at_file(&wallet_file, account2, cosmos_hd_path, id2, &bad_password), - Err(BackendError::WalletDifferentPasswordDetected), - )); - } - - #[test] - fn store_a_second_login_with_a_different_password_fails_with_multi() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let account2 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let bad_password = UserPassword::new("bad-password".to_string()); - let id1 = LoginId::new("first".to_string()); - let id2 = LoginId::new("second".to_string()); - - store_login_with_multiple_accounts_at_file( - &wallet_file, - account1, - hd_path.clone(), - id1, - &password, - ) - .unwrap(); - - // Can't store a second login if you use different password - assert!(matches!( - store_login_with_multiple_accounts_at_file( - &wallet_file, - account2, - hd_path, - id2, - &bad_password - ), - Err(BackendError::WalletDifferentPasswordDetected), - )); - } - - #[test] - fn store_two_mnemonic_accounts_gives_different_salts_and_iv() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let account2 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let different_hd_path: DerivationPath = "m".parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - let id2 = LoginId::new("second".to_string()); - - // Store the first account - store_login_at_file(&wallet_file, account1, hd_path, id1, &password).unwrap(); - - let stored_wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); - let encrypted_blob = &stored_wallet - .get_encrypted_login_by_index(0) - .unwrap() - .account; - - // keep track of salt and iv for future assertion - let original_iv = encrypted_blob.iv().to_vec(); - let original_salt = encrypted_blob.salt().to_vec(); - - // Add an extra account - store_login_at_file(&wallet_file, account2, different_hd_path, id2, &password).unwrap(); - - let loaded_accounts = load_existing_wallet_at_file(&wallet_file).unwrap(); - assert_eq!(loaded_accounts.len(), 2); - let encrypted_blob = &loaded_accounts - .get_encrypted_login_by_index(1) - .unwrap() - .account; - - // fresh IV and salt are used - assert_ne!(original_iv, encrypted_blob.iv()); - assert_ne!(original_salt, encrypted_blob.salt()); - } - - #[test] - fn store_two_mnemonic_accounts_using_two_logins() { - let store_dir = tempdir().unwrap(); - let wallet = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let account2 = Mnemonic::generate(24).unwrap(); - let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let different_hd_path: DerivationPath = "m".parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - let id2 = LoginId::new("second".to_string()); - - // Store the first account - store_login_at_file( - &wallet, - account1.clone(), - cosmos_hd_path.clone(), - id1.clone(), - &password, - ) - .unwrap(); - - let login = load_existing_login_at_file(&wallet, &id1, &password).unwrap(); - let acc = login.as_mnemonic_account().unwrap(); - assert_eq!(&account1, acc.mnemonic()); - assert_eq!(&cosmos_hd_path, acc.hd_path()); - - // Add an extra account - store_login_at_file( - &wallet, - account2.clone(), - different_hd_path.clone(), - id2.clone(), - &password, - ) - .unwrap(); - - // first account should be unchanged - let loaded_login = load_existing_login_at_file(&wallet, &id1, &password).unwrap(); - let acc1 = loaded_login.as_mnemonic_account().unwrap(); - assert_eq!(&account1, acc1.mnemonic()); - assert_eq!(&cosmos_hd_path, acc1.hd_path()); - - let loaded_login = load_existing_login_at_file(&wallet, &id2, &password).unwrap(); - let acc2 = loaded_login.as_mnemonic_account().unwrap(); - assert_eq!(&account2, acc2.mnemonic()); - assert_eq!(&different_hd_path, acc2.hd_path()); - } - - #[test] - fn store_two_mnemonic_accounts_using_two_logins_then_update_pwd_and_load() { - let store_dir = tempdir().unwrap(); - let wallet = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let account2 = Mnemonic::generate(24).unwrap(); - let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let different_hd_path: DerivationPath = "m".parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let new_password = UserPassword::new("new_password".to_string()); - let id1 = LoginId::new("first".to_string()); - let id2 = LoginId::new("second".to_string()); - - // Store the first login with an account - store_login_at_file( - &wallet, - account1.clone(), - cosmos_hd_path.clone(), - id1.clone(), - &password, - ) - .unwrap(); - - let login = load_existing_login_at_file(&wallet, &id1, &password).unwrap(); - let acc = login.as_mnemonic_account().unwrap(); - assert_eq!(&account1, acc.mnemonic()); - assert_eq!(&cosmos_hd_path, acc.hd_path()); - - // Store a second login, also with an account - store_login_at_file( - &wallet, - account2.clone(), - different_hd_path.clone(), - id2.clone(), - &password, - ) - .unwrap(); - - update_encrypted_logins_at_file(&wallet, &password, &new_password).unwrap(); - - // first account should be unchanged - let loaded_login = load_existing_login_at_file(&wallet, &id1, &new_password).unwrap(); - let acc1 = loaded_login.as_mnemonic_account().unwrap(); - assert_eq!(&account1, acc1.mnemonic()); - assert_eq!(&cosmos_hd_path, acc1.hd_path()); - - let loaded_login = load_existing_login_at_file(&wallet, &id2, &new_password).unwrap(); - let acc2 = loaded_login.as_mnemonic_account().unwrap(); - assert_eq!(&account2, acc2.mnemonic()); - assert_eq!(&different_hd_path, acc2.hd_path()); - } - - #[test] - fn store_one_mnemonic_account_and_one_multi_account() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let account2 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let different_hd_path: DerivationPath = "m".parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - let id2 = LoginId::new("second".to_string()); - - // Store the first account - store_login_at_file( - &wallet_file, - account1.clone(), - hd_path.clone(), - id1.clone(), - &password, - ) - .unwrap(); - - let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); - let acc = loaded_login.as_mnemonic_account().unwrap(); - assert_eq!(&account1, acc.mnemonic()); - assert_eq!(&hd_path, acc.hd_path()); - - // Add an extra account - store_login_with_multiple_accounts_at_file( - &wallet_file, - account2.clone(), - different_hd_path.clone(), - id2.clone(), - &password, - ) - .unwrap(); - - // first account should be unchanged - let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); - let acc1 = loaded_login.as_mnemonic_account().unwrap(); - assert_eq!(&account1, acc1.mnemonic()); - assert_eq!(&hd_path, acc1.hd_path()); - - let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); - let acc2 = loaded_login.as_multiple_accounts().unwrap(); - assert_eq!(acc2.len(), 1); - let account = acc2 - .get_account(&DEFAULT_FIRST_ACCOUNT_NAME.into()) - .unwrap(); - assert_eq!(account.id().as_ref(), DEFAULT_FIRST_ACCOUNT_NAME); - assert_eq!(account.mnemonic(), &account2); - assert_eq!(account.hd_path(), &different_hd_path); - } - - #[test] - fn remove_non_existent_id_fails() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - let id2 = LoginId::new("second".to_string()); - - store_login_with_multiple_accounts_at_file(&wallet_file, account1, hd_path, id1, &password) - .unwrap(); - - // Fails to delete non-existent id in the wallet - assert!(matches!( - remove_login_at_file(&wallet_file, &id2), - Err(BackendError::WalletNoSuchLoginId), - )); - } - - #[test] - fn store_and_remove_wallet_login_information() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let account2 = Mnemonic::generate(24).unwrap(); - let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let different_hd_path: DerivationPath = "m".parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - let id2 = LoginId::new("second".to_string()); - - // Store two accounts with two different passwords - store_login_at_file( - &wallet_file, - account1.clone(), - cosmos_hd_path.clone(), - id1.clone(), - &password, - ) - .unwrap(); - store_login_at_file( - &wallet_file, - account2.clone(), - different_hd_path.clone(), - id2.clone(), - &password, - ) - .unwrap(); - - // Load and compare - let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); - let acc1 = loaded_login.as_mnemonic_account().unwrap(); - assert_eq!(&account1, acc1.mnemonic()); - assert_eq!(&cosmos_hd_path, acc1.hd_path()); - - let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); - let acc2 = loaded_login.as_mnemonic_account().unwrap(); - assert_eq!(&account2, acc2.mnemonic()); - assert_eq!(&different_hd_path, acc2.hd_path()); - - // Delete the second account - remove_login_at_file(&wallet_file, &id2).unwrap(); - - // The first account should be unchanged - let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); - let acc1 = loaded_login.as_mnemonic_account().unwrap(); - assert_eq!(&account1, acc1.mnemonic()); - assert_eq!(&cosmos_hd_path, acc1.hd_path()); - - // And we can't load the second one anymore - assert!(matches!( - load_existing_login_at_file(&wallet_file, &id2, &password), - Err(BackendError::WalletNoSuchLoginId), - )); - - // Delete the first account - assert!(wallet_file.exists()); - remove_login_at_file(&wallet_file, &id1).unwrap(); - - // The file should now be removed - assert!(!wallet_file.exists()); - - // And trying to load when the file is gone fails - assert!(matches!( - load_existing_login_at_file(&wallet_file, &id1, &password), - Err(BackendError::WalletFileNotFound), - )); - } - - #[test] - fn append_account_converts_the_type() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let account2 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - let id2 = AccountId::new("second".to_string()); - - store_login_at_file( - &wallet_file, - account1.clone(), - hd_path.clone(), - id1.clone(), - &password, - ) - .unwrap(); - - // Check that it's there as the correct non-multiple type - let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); - let acc = loaded_login.as_mnemonic_account().unwrap(); - assert_eq!(acc.mnemonic(), &account1); - assert_eq!(acc.hd_path(), &hd_path); - - append_account_to_login_at_file( - &wallet_file, - account2.clone(), - hd_path.clone(), - id1.clone(), - id2.clone(), - &password, - ) - .unwrap(); - - // Check that it is now multiple mnemonic type - let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); - let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); - let expected = vec![ - WalletAccount::new(id1.into(), MnemonicAccount::new(account1, hd_path.clone())), - WalletAccount::new(id2, MnemonicAccount::new(account2, hd_path)), - ] - .into(); - assert_eq!(loaded_accounts, &expected); - } - - #[test] - fn append_accounts_to_existing_login() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let account2 = Mnemonic::generate(24).unwrap(); - let account3 = Mnemonic::generate(24).unwrap(); - let account4 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - let id2 = LoginId::new("second".to_string()); - let id3 = AccountId::new("third".to_string()); - let id4 = AccountId::new("fourth".to_string()); - - store_login_at_file( - &wallet_file, - account1.clone(), - hd_path.clone(), - id1.clone(), - &password, - ) - .unwrap(); - - store_login_at_file( - &wallet_file, - account2.clone(), - hd_path.clone(), - id2.clone(), - &password, - ) - .unwrap(); - - // Check that it's there as the correct non-multiple type - let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); - let acc2 = loaded_login.as_mnemonic_account().unwrap(); - assert_eq!(acc2.mnemonic(), &account2); - assert_eq!(acc2.hd_path(), &hd_path); - - // Add a third and fourth mnenonic grouped together with the second one - append_account_to_login_at_file( - &wallet_file, - account3.clone(), - hd_path.clone(), - id2.clone(), - id3.clone(), - &password, - ) - .unwrap(); - append_account_to_login_at_file( - &wallet_file, - account4.clone(), - hd_path.clone(), - id2.clone(), - id4.clone(), - &password, - ) - .unwrap(); - - // Check that we can load all four - let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); - let acc1 = loaded_login.as_mnemonic_account().unwrap(); - assert_eq!(acc1.mnemonic(), &account1); - assert_eq!(acc1.hd_path(), &hd_path); - - let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); - let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); - let expected = vec![ - WalletAccount::new(id2.into(), MnemonicAccount::new(account2, hd_path.clone())), - WalletAccount::new(id3, MnemonicAccount::new(account3, hd_path.clone())), - WalletAccount::new(id4, MnemonicAccount::new(account4, hd_path)), - ] - .into(); - assert_eq!(loaded_accounts, &expected); - } - - #[test] - fn append_accounts_to_existing_login_with_multi() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let account2 = Mnemonic::generate(24).unwrap(); - let account3 = Mnemonic::generate(24).unwrap(); - let account4 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - let id2 = LoginId::new("second".to_string()); - let id3 = AccountId::new("third".to_string()); - let id4 = AccountId::new("fourth".to_string()); - - store_login_with_multiple_accounts_at_file( - &wallet_file, - account1.clone(), - hd_path.clone(), - id1.clone(), - &password, - ) - .unwrap(); - - store_login_with_multiple_accounts_at_file( - &wallet_file, - account2.clone(), - hd_path.clone(), - id2.clone(), - &password, - ) - .unwrap(); - - // Add a third and fourth mnenonic grouped together with the second one - append_account_to_login_at_file( - &wallet_file, - account3.clone(), - hd_path.clone(), - id2.clone(), - id3.clone(), - &password, - ) - .unwrap(); - append_account_to_login_at_file( - &wallet_file, - account4.clone(), - hd_path.clone(), - id2.clone(), - id4.clone(), - &password, - ) - .unwrap(); - - // Check that we can load all four - let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); - let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); - let expected = vec![WalletAccount::new( - DEFAULT_FIRST_ACCOUNT_NAME.into(), - MnemonicAccount::new(account1, hd_path.clone()), - )] - .into(); - assert_eq!(loaded_accounts, &expected); - - let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); - let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); - let expected = vec![ - WalletAccount::new( - DEFAULT_FIRST_ACCOUNT_NAME.into(), - MnemonicAccount::new(account2, hd_path.clone()), - ), - WalletAccount::new(id3, MnemonicAccount::new(account3, hd_path.clone())), - WalletAccount::new(id4, MnemonicAccount::new(account4, hd_path)), - ] - .into(); - assert_eq!(loaded_accounts, &expected); - } - - #[test] - fn append_account_to_existing_login_with_multi_then_update_pwd_and_load() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let account2 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let new_password = UserPassword::new("new_password".to_string()); - let login_id = LoginId::new("first".to_string()); - let appended_account = AccountId::new("second".to_string()); - - store_login_with_multiple_accounts_at_file( - &wallet_file, - account1.clone(), - hd_path.clone(), - login_id.clone(), - &password, - ) - .unwrap(); - - // Append a second mnenonic to the same login - append_account_to_login_at_file( - &wallet_file, - account2.clone(), - hd_path.clone(), - login_id.clone(), - appended_account.clone(), - &password, - ) - .unwrap(); - - // Update the password - update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); - - // Expect that we can load these 2 accounts with the new password - let loaded_login = - load_existing_login_at_file(&wallet_file, &login_id, &new_password).unwrap(); - let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); - let expected = vec![ - WalletAccount::new( - DEFAULT_FIRST_ACCOUNT_NAME.into(), - MnemonicAccount::new(account1, hd_path.clone()), - ), - WalletAccount::new(appended_account, MnemonicAccount::new(account2, hd_path)), - ] - .into(); - assert_eq!(loaded_accounts, &expected); - - // Expect that trying to load these 2 accounts with the old password fails - let err = load_existing_login_at_file(&wallet_file, &login_id, &password).unwrap_err(); - assert!(matches!(err, BackendError::StoreCipherError { .. })); - } - - #[test] - fn append_the_same_mnemonic_twice_fails() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - let id2 = AccountId::new("second".to_string()); - - store_login_with_multiple_accounts_at_file( - &wallet_file, - account1.clone(), - hd_path.clone(), - id1.clone(), - &password, - ) - .unwrap(); - - assert!(matches!( - append_account_to_login_at_file(&wallet_file, account1, hd_path, id1, id2, &password), - Err(BackendError::WalletMnemonicAlreadyExistsInWalletLogin), - )) - } - - #[test] - fn append_the_same_account_name_twice_fails() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let mnemonic1 = Mnemonic::generate(24).unwrap(); - let mnemonic2 = Mnemonic::generate(24).unwrap(); - let mnemonic3 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - // The top-level login id. NOTE: the first account id is always set to default. - let login_id = LoginId::new("my_login_id".to_string()); - - // Store the first account under login_id. The first account id is always set to default - // name. - store_login_with_multiple_accounts_at_file( - &wallet_file, - mnemonic1, - hd_path.clone(), - login_id.clone(), - &password, - ) - .unwrap(); - - // Append another account (account2) to the same login (login_id) - let account2 = AccountId::new("account_2".to_string()); - - append_account_to_login_at_file( - &wallet_file, - mnemonic2, - hd_path.clone(), - login_id.clone(), - account2.clone(), - &password, - ) - .unwrap(); - - // Appending the third account, with same account id will fail - assert!(matches!( - append_account_to_login_at_file( - &wallet_file, - mnemonic3, - hd_path, - login_id, - account2, - &password, - ), - Err(BackendError::WalletAccountIdAlreadyExistsInWalletLogin), - )) - } - - #[test] - fn delete_the_same_account_twice_for_a_login_fails() { - let store_dir = tempdir().unwrap(); - let wallet = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let account2 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - let id2 = AccountId::new("second".to_string()); - - store_login_at_file(&wallet, account1, hd_path.clone(), id1.clone(), &password).unwrap(); - - append_account_to_login_at_file( - &wallet, - account2, - hd_path, - id1.clone(), - id2.clone(), - &password, - ) - .unwrap(); - - remove_account_from_login_at_file(&wallet, &id1, &id2, &password).unwrap(); - - assert!(matches!( - remove_account_from_login_at_file(&wallet, &id1, &id2, &password), - Err(BackendError::WalletNoSuchAccountIdInWalletLogin), - )); - } - - #[test] - fn delete_the_same_account_twice_for_a_login_fails_with_multi() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let account2 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - let id2 = AccountId::new("second".to_string()); - - store_login_with_multiple_accounts_at_file( - &wallet_file, - account1, - hd_path.clone(), - id1.clone(), - &password, - ) - .unwrap(); - - append_account_to_login_at_file( - &wallet_file, - account2, - hd_path, - id1.clone(), - id2.clone(), - &password, - ) - .unwrap(); - - remove_account_from_login_at_file(&wallet_file, &id1, &id2, &password).unwrap(); - - assert!(matches!( - remove_account_from_login_at_file(&wallet_file, &id1, &id2, &password), - Err(BackendError::WalletNoSuchAccountIdInWalletLogin), - )); - } - - #[test] - fn delete_appended_account_doesnt_affect_others() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let account2 = Mnemonic::generate(24).unwrap(); - let account3 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - let id2 = LoginId::new("second".to_string()); - let id3 = AccountId::new("third".to_string()); - - store_login_at_file( - &wallet_file, - account1, - hd_path.clone(), - id1.clone(), - &password, - ) - .unwrap(); - - store_login_at_file( - &wallet_file, - account2.clone(), - hd_path.clone(), - id2.clone(), - &password, - ) - .unwrap(); - - append_account_to_login_at_file( - &wallet_file, - account3.clone(), - hd_path.clone(), - id2.clone(), - id3.clone(), - &password, - ) - .unwrap(); - - remove_login_at_file(&wallet_file, &id1).unwrap(); - - // The second login one is still there - let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); - let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); - let expected = vec![ - WalletAccount::new(id2.into(), MnemonicAccount::new(account2, hd_path.clone())), - WalletAccount::new(id3, MnemonicAccount::new(account3, hd_path)), - ] - .into(); - assert_eq!(loaded_accounts, &expected); - } - - #[test] - fn remove_all_accounts_for_a_login_removes_the_file_when_empty() { - let store_dir = tempdir().unwrap(); - let wallet = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let account2 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - let id2 = AccountId::new("second".to_string()); - - store_login_with_multiple_accounts_at_file( - &wallet, - account1, - hd_path.clone(), - id1.clone(), - &password, - ) - .unwrap(); - - append_account_to_login_at_file( - &wallet, - account2, - hd_path, - id1.clone(), - id2.clone(), - &password, - ) - .unwrap(); - - remove_account_from_login_at_file( - &wallet, - &id1, - &DEFAULT_FIRST_ACCOUNT_NAME.into(), - &password, - ) - .unwrap(); - remove_account_from_login_at_file(&wallet, &id1, &id2, &password).unwrap(); - - // The file should now be removed - assert!(!wallet.exists()); - - // And trying to load when the file is gone fails - assert!(matches!( - load_existing_login_at_file(&wallet, &id1, &password), - Err(BackendError::WalletFileNotFound), - )); - } - - #[test] - fn remove_all_accounts_for_a_login_removes_that_login() { - let store_dir = tempdir().unwrap(); - let wallet = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let account2 = Mnemonic::generate(24).unwrap(); - let account3 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - let id2 = AccountId::new("second".to_string()); - let id3 = LoginId::new("third".to_string()); - - store_login_with_multiple_accounts_at_file( - &wallet, - account1, - hd_path.clone(), - id1.clone(), - &password, - ) - .unwrap(); - - append_account_to_login_at_file( - &wallet, - account2, - hd_path.clone(), - id1.clone(), - id2.clone(), - &password, - ) - .unwrap(); - - store_login_with_multiple_accounts_at_file( - &wallet, - account3.clone(), - hd_path.clone(), - id3.clone(), - &password, - ) - .unwrap(); - - remove_account_from_login_at_file( - &wallet, - &id1, - &DEFAULT_FIRST_ACCOUNT_NAME.into(), - &password, - ) - .unwrap(); - remove_account_from_login_at_file(&wallet, &id1, &id2, &password).unwrap(); - - // And trying to load when the file is gone fails - assert!(matches!( - load_existing_login_at_file(&wallet, &id1, &password), - Err(BackendError::WalletNoSuchLoginId), - )); - - // The other login is still there - let loaded_login = load_existing_login_at_file(&wallet, &id3, &password).unwrap(); - let acc3 = loaded_login.as_multiple_accounts().unwrap(); - let expected = vec![WalletAccount::new( - DEFAULT_FIRST_ACCOUNT_NAME.into(), - MnemonicAccount::new(account3, hd_path), - )] - .into(); - assert_eq!(acc3, &expected); - } - - #[test] - fn append_accounts_and_remove_appended_accounts() { - let store_dir = tempdir().unwrap(); - let wallet = store_dir.path().join(WALLET_INFO_FILENAME); - let acc1 = Mnemonic::generate(24).unwrap(); - let acc2 = Mnemonic::generate(24).unwrap(); - let acc3 = Mnemonic::generate(24).unwrap(); - let acc4 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - let id2 = LoginId::new("second".to_string()); - let id3 = AccountId::new("third".to_string()); - let id4 = AccountId::new("fourth".to_string()); - - store_login_at_file( - &wallet, - acc1.clone(), - hd_path.clone(), - id1.clone(), - &password, - ) - .unwrap(); - - store_login_at_file( - &wallet, - acc2.clone(), - hd_path.clone(), - id2.clone(), - &password, - ) - .unwrap(); - - // Add a third and fourth mnenonic grouped together with the second one - append_account_to_login_at_file( - &wallet, - acc3, - hd_path.clone(), - id2.clone(), - id3.clone(), - &password, - ) - .unwrap(); - append_account_to_login_at_file( - &wallet, - acc4.clone(), - hd_path.clone(), - id2.clone(), - id4.clone(), - &password, - ) - .unwrap(); - - // Delete the third mnemonic, from the second login entry - remove_account_from_login_at_file(&wallet, &id2, &id3, &password).unwrap(); - - // Check that we can still load the other accounts - let loaded_login = load_existing_login_at_file(&wallet, &id2, &password).unwrap(); - let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); - let expected = vec![ - WalletAccount::new( - id2.clone().into(), - MnemonicAccount::new(acc2, hd_path.clone()), - ), - WalletAccount::new(id4.clone(), MnemonicAccount::new(acc4, hd_path.clone())), - ] - .into(); - assert_eq!(loaded_accounts, &expected); - - // Delete the second and fourth mnemonic from the second login entry removes the login entry - remove_account_from_login_at_file(&wallet, &id2, &id2.clone().into(), &password).unwrap(); - remove_account_from_login_at_file(&wallet, &id2, &id4, &password).unwrap(); - assert!(matches!( - load_existing_login_at_file(&wallet, &id2, &password), - Err(BackendError::WalletNoSuchLoginId), - )); - - // The first login is still available - let loaded_login = load_existing_login_at_file(&wallet, &id1, &password).unwrap(); - let account = loaded_login.as_mnemonic_account().unwrap(); - assert_eq!(account.mnemonic(), &acc1); - assert_eq!(account.hd_path(), &hd_path); - } - - #[test] - fn rename_first_account_in_login() { - let store_dir = tempdir().unwrap(); - let wallet = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let login_id = LoginId::new("first".to_string()); - - store_login_with_multiple_accounts_at_file( - &wallet, - account1.clone(), - hd_path.clone(), - login_id.clone(), - &password, - ) - .unwrap(); - - let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); - let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); - let expected = vec![WalletAccount::new( - DEFAULT_FIRST_ACCOUNT_NAME.into(), - MnemonicAccount::new(account1.clone(), hd_path.clone()), - )] - .into(); - assert_eq!(loaded_accounts, &expected); - - let renamed_account = AccountId::new("new_first".to_string()); - - rename_account_in_login_at_file( - &wallet, - &login_id, - &DEFAULT_FIRST_ACCOUNT_NAME.into(), - &renamed_account, - &password, - ) - .unwrap(); - - let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); - let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); - let expected = vec![WalletAccount::new( - renamed_account, - MnemonicAccount::new(account1, hd_path), - )] - .into(); - assert_eq!(loaded_accounts, &expected); - } - - #[test] - fn rename_one_account_in_login_with_two_accounts() { - let store_dir = tempdir().unwrap(); - let wallet = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let account2 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let login_id = LoginId::new("first".to_string()); - let account_id2 = AccountId::new("second".to_string()); - let renamed_account_id2 = AccountId::new("new_second".to_string()); - - store_login_with_multiple_accounts_at_file( - &wallet, - account1.clone(), - hd_path.clone(), - login_id.clone(), - &password, - ) - .unwrap(); - - append_account_to_login_at_file( - &wallet, - account2.clone(), - hd_path.clone(), - login_id.clone(), - account_id2.clone(), - &password, - ) - .unwrap(); - - // Load and confirm - let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); - let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); - let expected = vec![ - WalletAccount::new( - DEFAULT_FIRST_ACCOUNT_NAME.into(), - MnemonicAccount::new(account1.clone(), hd_path.clone()), - ), - WalletAccount::new( - account_id2.clone(), - MnemonicAccount::new(account2.clone(), hd_path.clone()), - ), - ] - .into(); - assert_eq!(loaded_accounts, &expected); - - // Rename the second account to a new name - rename_account_in_login_at_file( - &wallet, - &login_id, - &account_id2, - &renamed_account_id2, - &password, - ) - .unwrap(); - - // Load and confirm - let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); - let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); - let expected = vec![ - WalletAccount::new( - DEFAULT_FIRST_ACCOUNT_NAME.into(), - MnemonicAccount::new(account1, hd_path.clone()), - ), - WalletAccount::new(renamed_account_id2, MnemonicAccount::new(account2, hd_path)), - ] - .into(); - assert_eq!(loaded_accounts, &expected); - } - - #[test] - fn rename_account_into_existing_account_id_fails() { - let store_dir = tempdir().unwrap(); - let wallet = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let account2 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let login_id = LoginId::new("first".to_string()); - let account_id2 = AccountId::new("second".to_string()); - let renamed_account_id2 = DEFAULT_FIRST_ACCOUNT_NAME.into(); - - store_login_with_multiple_accounts_at_file( - &wallet, - account1.clone(), - hd_path.clone(), - login_id.clone(), - &password, - ) - .unwrap(); - - append_account_to_login_at_file( - &wallet, - account2.clone(), - hd_path.clone(), - login_id.clone(), - account_id2.clone(), - &password, - ) - .unwrap(); - - // Load and confirm - let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); - let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); - let expected = vec![ - WalletAccount::new( - DEFAULT_FIRST_ACCOUNT_NAME.into(), - MnemonicAccount::new(account1.clone(), hd_path.clone()), - ), - WalletAccount::new( - account_id2.clone(), - MnemonicAccount::new(account2.clone(), hd_path.clone()), - ), - ] - .into(); - assert_eq!(loaded_accounts, &expected); - - // Rename the second account to the name of the first one fails - assert!(matches!( - rename_account_in_login_at_file( - &wallet, - &login_id, - &account_id2, - &renamed_account_id2, - &password, - ), - Err(BackendError::WalletAccountIdAlreadyExistsInWalletLogin) - )); - - // Load and confirm nothing was changed - let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); - let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); - let expected = vec![ - WalletAccount::new( - DEFAULT_FIRST_ACCOUNT_NAME.into(), - MnemonicAccount::new(account1, hd_path.clone()), - ), - WalletAccount::new(account_id2, MnemonicAccount::new(account2, hd_path)), - ] - .into(); - assert_eq!(loaded_accounts, &expected); - } - - // Test to that decrypts a stored file from the repo, to make sure we are able to decrypt stored - // wallets created with older versions. - #[test] - fn decrypt_stored_wallet() { - const SAVED_WALLET: &str = "src/wallet_storage/test-data/saved-wallet.json"; - let wallet_file = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(SAVED_WALLET); - - let wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); - - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let bad_password = UserPassword::new("bad-password".to_string()); - let id1 = LoginId::new("first".to_string()); - let id2 = LoginId::new("second".to_string()); - - assert!(!wallet.password_can_decrypt_all(&bad_password)); - assert!(wallet.password_can_decrypt_all(&password)); - - let acc1 = wallet.decrypt_login(&id1, &password).unwrap(); - let acc2 = wallet.decrypt_login(&id2, &password).unwrap(); - - assert!(matches!(acc1, StoredLogin::Mnemonic(_))); - assert!(matches!(acc2, StoredLogin::Mnemonic(_))); - - let expected_acc1 = bip39::Mnemonic::from_str("country mean universe text phone begin deputy reject result good cram illness common cluster proud swamp digital patrol spread bar face december base kick").unwrap(); - let expected_acc2 = bip39::Mnemonic::from_str("home mansion start quiz dress decide hint second dragon sunny juice always steak real minimum art rival skin draw total pulp foot goddess agent").unwrap(); - - assert_eq!( - acc1.as_mnemonic_account().unwrap().mnemonic(), - &expected_acc1 - ); - assert_eq!(acc1.as_mnemonic_account().unwrap().hd_path(), &hd_path,); - - assert_eq!( - acc2.as_mnemonic_account().unwrap().mnemonic(), - &expected_acc2 - ); - assert_eq!(acc2.as_mnemonic_account().unwrap().hd_path(), &hd_path,); - } - - #[test] - fn decrypt_stored_wallet_1_0_4() { - const SAVED_WALLET: &str = "src/wallet_storage/test-data/saved-wallet-1.0.4.json"; - let wallet_file = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(SAVED_WALLET); - - let wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); - - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password11!".to_string()); - let bad_password = UserPassword::new("bad-password".to_string()); - let login_id = LoginId::new("default".to_string()); - - assert!(!wallet.password_can_decrypt_all(&bad_password)); - assert!(wallet.password_can_decrypt_all(&password)); - - let acc1 = wallet.decrypt_login(&login_id, &password).unwrap(); - - assert!(matches!(acc1, StoredLogin::Mnemonic(_))); - - let expected_acc1 = bip39::Mnemonic::from_str("arrow capable abstract industry elevator nominee december piece hotel feed lounge web faint sword veteran bundle hour page actual laptop horror gold test warrior").unwrap(); - - assert_eq!( - acc1.as_mnemonic_account().unwrap().mnemonic(), - &expected_acc1 - ); - assert_eq!(acc1.as_mnemonic_account().unwrap().hd_path(), &hd_path,); - } - - #[test] - fn decrypt_stored_wallet_1_0_5_with_multiple_accounts() { - const SAVED_WALLET: &str = "src/wallet_storage/test-data/saved-wallet-1.0.5.json"; - let wallet_file = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(SAVED_WALLET); - - let wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); - - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password11!".to_string()); - let bad_password = UserPassword::new("bad-password".to_string()); - let login_id = LoginId::new("default".to_string()); - - assert!(!wallet.password_can_decrypt_all(&bad_password)); - assert!(wallet.password_can_decrypt_all(&password)); - - let login = wallet.decrypt_login(&login_id, &password).unwrap(); - - assert!(matches!(login, StoredLogin::Multiple(_))); - - let login = login.as_multiple_accounts().unwrap(); - assert_eq!(login.len(), 4); - - let expected_mn1 = bip39::Mnemonic::from_str("arrow capable abstract industry elevator nominee december piece hotel feed lounge web faint sword veteran bundle hour page actual laptop horror gold test warrior").unwrap(); - let expected_mn2 = bip39::Mnemonic::from_str("border hurt skull lunar goddess second danger game dismiss exhaust oven thumb dog drama onion false orchard spice tent next predict invite cherry green").unwrap(); - let expected_mn3 = bip39::Mnemonic::from_str("gentle crowd rule snap girl urge flat jump winner cluster night sand museum stock grunt quick tree acquire traffic major awake tag rack peasant").unwrap(); - let expected_mn4 = bip39::Mnemonic::from_str("debris blue skin annual inhale text border rigid spatial lesson coconut yard horn crystal control survey version vote hawk neck frame arrive oblige width").unwrap(); - - let expected = vec![ - WalletAccount::new( - "default".into(), - MnemonicAccount::new(expected_mn1, hd_path.clone()), - ), - WalletAccount::new( - "account2".into(), - MnemonicAccount::new(expected_mn2, hd_path.clone()), - ), - WalletAccount::new( - "foobar".into(), - MnemonicAccount::new(expected_mn3, hd_path.clone()), - ), - WalletAccount::new("42".into(), MnemonicAccount::new(expected_mn4, hd_path)), - ] - .into(); - - assert_eq!(login, &expected); - } - - #[test] - fn append_filename() { - let wallet_file = PathBuf::from("/tmp/saved-wallet.json"); - let timestamp = OsString::from("42"); - #[cfg(target_family = "unix")] - assert_eq!( - append_timestamp_to_filename(wallet_file.clone(), timestamp.clone(), None) - .unwrap() - .into_os_string() - .into_string() - .unwrap(), - "/tmp/saved-wallet-42.json".to_string(), - ); - #[cfg(not(target_family = "unix"))] - assert_eq!( - append_timestamp_to_filename(wallet_file.clone(), timestamp.clone(), None) - .unwrap() - .into_os_string() - .into_string() - .unwrap(), - r"/tmp\saved-wallet-42.json".to_string(), - ); - - #[cfg(target_family = "unix")] - assert_eq!( - append_timestamp_to_filename(wallet_file, timestamp, Some(3)) - .unwrap() - .into_os_string() - .into_string() - .unwrap(), - "/tmp/saved-wallet-42-3.json".to_string(), - ); - #[cfg(not(target_family = "unix"))] - assert_eq!( - append_timestamp_to_filename(wallet_file, timestamp, Some(3)) - .unwrap() - .into_os_string() - .into_string() - .unwrap(), - r"/tmp\saved-wallet-42-3.json".to_string(), - ); - } -} +// #[cfg(test)] +// mod tests { +// use crate::wallet_storage::account_data::WalletAccount; + +// use super::*; +// use nym_config::defaults::COSMOS_DERIVATION_PATH; +// use std::str::FromStr; +// use tempfile::tempdir; + +// #[test] +// fn trying_to_load_nonexistant_wallet_fails() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let id1 = LoginId::new("first".to_string()); +// let password = UserPassword::new("password".to_string()); + +// assert!(matches!( +// load_existing_wallet_at_file(&wallet_file), +// Err(BackendError::WalletFileNotFound), +// )); +// assert!(matches!( +// load_existing_login_at_file(&wallet_file, &id1, &password), +// Err(BackendError::WalletFileNotFound), +// )); +// remove_login_at_file(&wallet_file, &id1).unwrap_err(); +// } + +// #[test] +// fn store_single_login() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); + +// store_login_at_file(&wallet_file, account1, hd_path, id1.clone(), &password).unwrap(); + +// let stored_wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); +// assert_eq!(stored_wallet.len(), 1); + +// let login = stored_wallet.get_encrypted_login_by_index(0).unwrap(); +// assert_eq!(login.id, id1); + +// // some actual ciphertext was saved +// assert!(!login.account.ciphertext().is_empty()); +// } + +// #[test] +// fn store_single_login_with_multi() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); + +// store_login_with_multiple_accounts_at_file( +// &wallet_file, +// account1, +// cosmos_hd_path, +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// let stored_wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); +// assert_eq!(stored_wallet.len(), 1); + +// let login = stored_wallet.get_encrypted_login_by_index(0).unwrap(); +// assert_eq!(login.id, id1); + +// // some actual ciphertext was saved +// assert!(!login.account.ciphertext().is_empty()); +// } + +// #[test] +// fn store_single_login_with_multi_then_update_pwd_and_load() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let new_password = UserPassword::new("new_password".to_string()); +// let id1 = LoginId::new("first".to_string()); + +// store_login_with_multiple_accounts_at_file( +// &wallet_file, +// account1, +// cosmos_hd_path, +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); + +// let stored_wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); +// assert_eq!(stored_wallet.len(), 1); + +// let login = stored_wallet.get_encrypted_login_by_index(0).unwrap(); +// assert_eq!(login.id, id1); + +// // some actual ciphertext was saved +// assert!(!login.account.ciphertext().is_empty()); +// } + +// #[test] +// fn store_twice_for_the_same_id_fails() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); + +// // Store the first login +// store_login_at_file( +// &wallet_file, +// account1.clone(), +// hd_path.clone(), +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// // and storing the same id again fails +// assert!(matches!( +// store_login_at_file(&wallet_file, account1, hd_path, id1, &password,), +// Err(BackendError::WalletLoginIdAlreadyExists), +// )); +// } + +// #[test] +// fn store_twice_for_the_same_id_fails_with_multiple() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); + +// // Store the first login +// store_login_with_multiple_accounts_at_file( +// &wallet_file, +// account1.clone(), +// hd_path.clone(), +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// // and storing the same id again fails +// assert!(matches!( +// store_login_with_multiple_accounts_at_file( +// &wallet_file, +// account1, +// hd_path, +// id1, +// &password, +// ), +// Err(BackendError::WalletLoginIdAlreadyExists), +// )); +// } + +// #[test] +// fn load_with_wrong_password_fails() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let bad_password = UserPassword::new("bad-password".to_string()); +// let id1 = LoginId::new("first".to_string()); + +// store_login_at_file(&wallet_file, account1, hd_path, id1.clone(), &password).unwrap(); + +// // Trying to load it with wrong password now fails +// assert!(matches!( +// load_existing_login_at_file(&wallet_file, &id1, &bad_password), +// Err(BackendError::StoreCipherError { .. }), +// )); +// } + +// #[test] +// fn load_with_wrong_password_fails_with_multi() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let bad_password = UserPassword::new("bad-password".to_string()); +// let id1 = LoginId::new("first".to_string()); + +// store_login_with_multiple_accounts_at_file( +// &wallet_file, +// account1, +// hd_path, +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// // Trying to load it with wrong password now fails +// assert!(matches!( +// load_existing_login_at_file(&wallet_file, &id1, &bad_password), +// Err(BackendError::StoreCipherError { .. }), +// )); +// } + +// #[test] +// fn load_with_wrong_id_fails() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); +// let id2 = LoginId::new("second".to_string()); + +// store_login_at_file(&wallet_file, account1, hd_path, id1, &password).unwrap(); + +// // Trying to load with the wrong id +// assert!(matches!( +// load_existing_login_at_file(&wallet_file, &id2, &password), +// Err(BackendError::WalletNoSuchLoginId), +// )); +// } + +// #[test] +// fn load_with_wrong_id_fails_with_multi() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); +// let id2 = LoginId::new("second".to_string()); + +// store_login_with_multiple_accounts_at_file(&wallet_file, account1, hd_path, id1, &password) +// .unwrap(); + +// // Trying to load with the wrong id +// assert!(matches!( +// load_existing_login_at_file(&wallet_file, &id2, &password), +// Err(BackendError::WalletNoSuchLoginId), +// )); +// } + +// #[test] +// fn store_and_load_a_single_login() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); + +// store_login_at_file( +// &wallet_file, +// account1.clone(), +// hd_path.clone(), +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); +// let acc = loaded_login.as_mnemonic_account().unwrap(); +// assert_eq!(&account1, acc.mnemonic()); +// assert_eq!(&hd_path, acc.hd_path()); +// } + +// #[test] +// fn store_a_single_login_then_update_pwd_and_load() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let new_password = UserPassword::new("new_password".to_string()); +// let id1 = LoginId::new("first".to_string()); + +// store_login_at_file( +// &wallet_file, +// account1.clone(), +// hd_path.clone(), +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); + +// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &new_password).unwrap(); +// let acc = loaded_login.as_mnemonic_account().unwrap(); +// assert_eq!(&account1, acc.mnemonic()); +// assert_eq!(&hd_path, acc.hd_path()); +// } + +// #[test] +// fn store_a_single_login_then_update_pwd_with_identical_pwd_is_noop_but_okay() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); + +// store_login_at_file( +// &wallet_file, +// account1.clone(), +// hd_path.clone(), +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// update_encrypted_logins_at_file(&wallet_file, &password, &password).unwrap(); + +// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); +// let acc = loaded_login.as_mnemonic_account().unwrap(); +// assert_eq!(&account1, acc.mnemonic()); +// assert_eq!(&hd_path, acc.hd_path()); +// } + +// #[test] +// fn store_a_single_login_then_update_pwd_with_wrong_current_pwd_fails() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let new_password = UserPassword::new("new_password".to_string()); +// let wrong_password = UserPassword::new("wrong_password".to_string()); +// let id1 = LoginId::new("first".to_string()); + +// store_login_at_file(&wallet_file, account1, hd_path, id1, &password).unwrap(); + +// let err = update_encrypted_logins_at_file(&wallet_file, &wrong_password, &new_password) +// .unwrap_err(); +// assert!(matches!(err, BackendError::StoreCipherError { .. })); +// } + +// #[test] +// fn store_a_single_login_then_update_pwd_and_load_with_wrong_pwd_fails() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let new_password = UserPassword::new("new_password".to_string()); +// let id1 = LoginId::new("first".to_string()); + +// store_login_at_file(&wallet_file, account1, hd_path, id1.clone(), &password).unwrap(); + +// update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); + +// let err = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap_err(); +// assert!(matches!(err, BackendError::StoreCipherError { .. })); +// } + +// #[test] +// fn store_and_load_a_single_login_with_multi() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let acc1 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); + +// store_login_with_multiple_accounts_at_file( +// &wallet_file, +// acc1.clone(), +// hd_path.clone(), +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); +// let accounts = loaded_login.as_multiple_accounts().unwrap(); +// assert_eq!(accounts.len(), 1); +// let account = accounts +// .get_account(&DEFAULT_FIRST_ACCOUNT_NAME.into()) +// .unwrap(); +// assert_eq!(account.id().as_ref(), DEFAULT_FIRST_ACCOUNT_NAME); +// assert_eq!(account.mnemonic(), &acc1); +// assert_eq!(account.hd_path(), &hd_path); +// } + +// #[test] +// fn store_a_single_login_with_multi_then_update_pwd_and_load() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let acc1 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let new_password = UserPassword::new("new_password".to_string()); +// let id1 = LoginId::new("first".to_string()); + +// store_login_with_multiple_accounts_at_file( +// &wallet_file, +// acc1.clone(), +// hd_path.clone(), +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); + +// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &new_password).unwrap(); +// let accounts = loaded_login.as_multiple_accounts().unwrap(); +// assert_eq!(accounts.len(), 1); +// let account = accounts +// .get_account(&DEFAULT_FIRST_ACCOUNT_NAME.into()) +// .unwrap(); +// assert_eq!(account.id().as_ref(), DEFAULT_FIRST_ACCOUNT_NAME); +// assert_eq!(account.mnemonic(), &acc1); +// assert_eq!(account.hd_path(), &hd_path); +// } + +// #[test] +// fn store_a_single_login_with_multi_then_update_pwd_with_wrong_current_pwd_fails() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let acc1 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let new_password = UserPassword::new("new_password".to_string()); +// let wrong_password = UserPassword::new("wrong_password".to_string()); +// let id1 = LoginId::new("first".to_string()); + +// store_login_with_multiple_accounts_at_file(&wallet_file, acc1, hd_path, id1, &password) +// .unwrap(); + +// let err = update_encrypted_logins_at_file(&wallet_file, &wrong_password, &new_password) +// .unwrap_err(); +// assert!(matches!(err, BackendError::StoreCipherError { .. })); +// } + +// #[test] +// fn store_a_single_login_with_multi_then_update_pwd_and_load_with_wrong_pwd_fails() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let acc1 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let new_password = UserPassword::new("new_password".to_string()); +// let id1 = LoginId::new("first".to_string()); + +// store_login_with_multiple_accounts_at_file( +// &wallet_file, +// acc1, +// hd_path, +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); + +// let err = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap_err(); +// assert!(matches!(err, BackendError::StoreCipherError { .. })); +// } + +// #[test] +// fn store_a_second_login_with_a_different_password_fails() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let account2 = Mnemonic::generate(24).unwrap(); +// let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let bad_password = UserPassword::new("bad-password".to_string()); +// let id1 = LoginId::new("first".to_string()); +// let id2 = LoginId::new("second".to_string()); + +// store_login_at_file( +// &wallet_file, +// account1, +// cosmos_hd_path.clone(), +// id1, +// &password, +// ) +// .unwrap(); + +// // Can't store a second login if you use different password +// assert!(matches!( +// store_login_at_file(&wallet_file, account2, cosmos_hd_path, id2, &bad_password), +// Err(BackendError::WalletDifferentPasswordDetected), +// )); +// } + +// #[test] +// fn store_a_second_login_with_a_different_password_fails_with_multi() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let account2 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let bad_password = UserPassword::new("bad-password".to_string()); +// let id1 = LoginId::new("first".to_string()); +// let id2 = LoginId::new("second".to_string()); + +// store_login_with_multiple_accounts_at_file( +// &wallet_file, +// account1, +// hd_path.clone(), +// id1, +// &password, +// ) +// .unwrap(); + +// // Can't store a second login if you use different password +// assert!(matches!( +// store_login_with_multiple_accounts_at_file( +// &wallet_file, +// account2, +// hd_path, +// id2, +// &bad_password +// ), +// Err(BackendError::WalletDifferentPasswordDetected), +// )); +// } + +// #[test] +// fn store_two_mnemonic_accounts_gives_different_salts_and_iv() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let account2 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let different_hd_path: DerivationPath = "m".parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); +// let id2 = LoginId::new("second".to_string()); + +// // Store the first account +// store_login_at_file(&wallet_file, account1, hd_path, id1, &password).unwrap(); + +// let stored_wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); +// let encrypted_blob = &stored_wallet +// .get_encrypted_login_by_index(0) +// .unwrap() +// .account; + +// // keep track of salt and iv for future assertion +// let original_iv = encrypted_blob.iv().to_vec(); +// let original_salt = encrypted_blob.salt().to_vec(); + +// // Add an extra account +// store_login_at_file(&wallet_file, account2, different_hd_path, id2, &password).unwrap(); + +// let loaded_accounts = load_existing_wallet_at_file(&wallet_file).unwrap(); +// assert_eq!(loaded_accounts.len(), 2); +// let encrypted_blob = &loaded_accounts +// .get_encrypted_login_by_index(1) +// .unwrap() +// .account; + +// // fresh IV and salt are used +// assert_ne!(original_iv, encrypted_blob.iv()); +// assert_ne!(original_salt, encrypted_blob.salt()); +// } + +// #[test] +// fn store_two_mnemonic_accounts_using_two_logins() { +// let store_dir = tempdir().unwrap(); +// let wallet = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let account2 = Mnemonic::generate(24).unwrap(); +// let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let different_hd_path: DerivationPath = "m".parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); +// let id2 = LoginId::new("second".to_string()); + +// // Store the first account +// store_login_at_file( +// &wallet, +// account1.clone(), +// cosmos_hd_path.clone(), +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// let login = load_existing_login_at_file(&wallet, &id1, &password).unwrap(); +// let acc = login.as_mnemonic_account().unwrap(); +// assert_eq!(&account1, acc.mnemonic()); +// assert_eq!(&cosmos_hd_path, acc.hd_path()); + +// // Add an extra account +// store_login_at_file( +// &wallet, +// account2.clone(), +// different_hd_path.clone(), +// id2.clone(), +// &password, +// ) +// .unwrap(); + +// // first account should be unchanged +// let loaded_login = load_existing_login_at_file(&wallet, &id1, &password).unwrap(); +// let acc1 = loaded_login.as_mnemonic_account().unwrap(); +// assert_eq!(&account1, acc1.mnemonic()); +// assert_eq!(&cosmos_hd_path, acc1.hd_path()); + +// let loaded_login = load_existing_login_at_file(&wallet, &id2, &password).unwrap(); +// let acc2 = loaded_login.as_mnemonic_account().unwrap(); +// assert_eq!(&account2, acc2.mnemonic()); +// assert_eq!(&different_hd_path, acc2.hd_path()); +// } + +// #[test] +// fn store_two_mnemonic_accounts_using_two_logins_then_update_pwd_and_load() { +// let store_dir = tempdir().unwrap(); +// let wallet = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let account2 = Mnemonic::generate(24).unwrap(); +// let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let different_hd_path: DerivationPath = "m".parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let new_password = UserPassword::new("new_password".to_string()); +// let id1 = LoginId::new("first".to_string()); +// let id2 = LoginId::new("second".to_string()); + +// // Store the first login with an account +// store_login_at_file( +// &wallet, +// account1.clone(), +// cosmos_hd_path.clone(), +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// let login = load_existing_login_at_file(&wallet, &id1, &password).unwrap(); +// let acc = login.as_mnemonic_account().unwrap(); +// assert_eq!(&account1, acc.mnemonic()); +// assert_eq!(&cosmos_hd_path, acc.hd_path()); + +// // Store a second login, also with an account +// store_login_at_file( +// &wallet, +// account2.clone(), +// different_hd_path.clone(), +// id2.clone(), +// &password, +// ) +// .unwrap(); + +// update_encrypted_logins_at_file(&wallet, &password, &new_password).unwrap(); + +// // first account should be unchanged +// let loaded_login = load_existing_login_at_file(&wallet, &id1, &new_password).unwrap(); +// let acc1 = loaded_login.as_mnemonic_account().unwrap(); +// assert_eq!(&account1, acc1.mnemonic()); +// assert_eq!(&cosmos_hd_path, acc1.hd_path()); + +// let loaded_login = load_existing_login_at_file(&wallet, &id2, &new_password).unwrap(); +// let acc2 = loaded_login.as_mnemonic_account().unwrap(); +// assert_eq!(&account2, acc2.mnemonic()); +// assert_eq!(&different_hd_path, acc2.hd_path()); +// } + +// #[test] +// fn store_one_mnemonic_account_and_one_multi_account() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let account2 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let different_hd_path: DerivationPath = "m".parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); +// let id2 = LoginId::new("second".to_string()); + +// // Store the first account +// store_login_at_file( +// &wallet_file, +// account1.clone(), +// hd_path.clone(), +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); +// let acc = loaded_login.as_mnemonic_account().unwrap(); +// assert_eq!(&account1, acc.mnemonic()); +// assert_eq!(&hd_path, acc.hd_path()); + +// // Add an extra account +// store_login_with_multiple_accounts_at_file( +// &wallet_file, +// account2.clone(), +// different_hd_path.clone(), +// id2.clone(), +// &password, +// ) +// .unwrap(); + +// // first account should be unchanged +// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); +// let acc1 = loaded_login.as_mnemonic_account().unwrap(); +// assert_eq!(&account1, acc1.mnemonic()); +// assert_eq!(&hd_path, acc1.hd_path()); + +// let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); +// let acc2 = loaded_login.as_multiple_accounts().unwrap(); +// assert_eq!(acc2.len(), 1); +// let account = acc2 +// .get_account(&DEFAULT_FIRST_ACCOUNT_NAME.into()) +// .unwrap(); +// assert_eq!(account.id().as_ref(), DEFAULT_FIRST_ACCOUNT_NAME); +// assert_eq!(account.mnemonic(), &account2); +// assert_eq!(account.hd_path(), &different_hd_path); +// } + +// #[test] +// fn remove_non_existent_id_fails() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); +// let id2 = LoginId::new("second".to_string()); + +// store_login_with_multiple_accounts_at_file(&wallet_file, account1, hd_path, id1, &password) +// .unwrap(); + +// // Fails to delete non-existent id in the wallet +// assert!(matches!( +// remove_login_at_file(&wallet_file, &id2), +// Err(BackendError::WalletNoSuchLoginId), +// )); +// } + +// #[test] +// fn store_and_remove_wallet_login_information() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let account2 = Mnemonic::generate(24).unwrap(); +// let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let different_hd_path: DerivationPath = "m".parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); +// let id2 = LoginId::new("second".to_string()); + +// // Store two accounts with two different passwords +// store_login_at_file( +// &wallet_file, +// account1.clone(), +// cosmos_hd_path.clone(), +// id1.clone(), +// &password, +// ) +// .unwrap(); +// store_login_at_file( +// &wallet_file, +// account2.clone(), +// different_hd_path.clone(), +// id2.clone(), +// &password, +// ) +// .unwrap(); + +// // Load and compare +// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); +// let acc1 = loaded_login.as_mnemonic_account().unwrap(); +// assert_eq!(&account1, acc1.mnemonic()); +// assert_eq!(&cosmos_hd_path, acc1.hd_path()); + +// let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); +// let acc2 = loaded_login.as_mnemonic_account().unwrap(); +// assert_eq!(&account2, acc2.mnemonic()); +// assert_eq!(&different_hd_path, acc2.hd_path()); + +// // Delete the second account +// remove_login_at_file(&wallet_file, &id2).unwrap(); + +// // The first account should be unchanged +// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); +// let acc1 = loaded_login.as_mnemonic_account().unwrap(); +// assert_eq!(&account1, acc1.mnemonic()); +// assert_eq!(&cosmos_hd_path, acc1.hd_path()); + +// // And we can't load the second one anymore +// assert!(matches!( +// load_existing_login_at_file(&wallet_file, &id2, &password), +// Err(BackendError::WalletNoSuchLoginId), +// )); + +// // Delete the first account +// assert!(wallet_file.exists()); +// remove_login_at_file(&wallet_file, &id1).unwrap(); + +// // The file should now be removed +// assert!(!wallet_file.exists()); + +// // And trying to load when the file is gone fails +// assert!(matches!( +// load_existing_login_at_file(&wallet_file, &id1, &password), +// Err(BackendError::WalletFileNotFound), +// )); +// } + +// #[test] +// fn append_account_converts_the_type() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let account2 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); +// let id2 = AccountId::new("second".to_string()); + +// store_login_at_file( +// &wallet_file, +// account1.clone(), +// hd_path.clone(), +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// // Check that it's there as the correct non-multiple type +// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); +// let acc = loaded_login.as_mnemonic_account().unwrap(); +// assert_eq!(acc.mnemonic(), &account1); +// assert_eq!(acc.hd_path(), &hd_path); + +// append_account_to_login_at_file( +// &wallet_file, +// account2.clone(), +// hd_path.clone(), +// id1.clone(), +// id2.clone(), +// &password, +// ) +// .unwrap(); + +// // Check that it is now multiple mnemonic type +// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); +// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); +// let expected = vec![ +// WalletAccount::new(id1.into(), MnemonicAccount::new(account1, hd_path.clone())), +// WalletAccount::new(id2, MnemonicAccount::new(account2, hd_path)), +// ] +// .into(); +// assert_eq!(loaded_accounts, &expected); +// } + +// #[test] +// fn append_accounts_to_existing_login() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let account2 = Mnemonic::generate(24).unwrap(); +// let account3 = Mnemonic::generate(24).unwrap(); +// let account4 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); +// let id2 = LoginId::new("second".to_string()); +// let id3 = AccountId::new("third".to_string()); +// let id4 = AccountId::new("fourth".to_string()); + +// store_login_at_file( +// &wallet_file, +// account1.clone(), +// hd_path.clone(), +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// store_login_at_file( +// &wallet_file, +// account2.clone(), +// hd_path.clone(), +// id2.clone(), +// &password, +// ) +// .unwrap(); + +// // Check that it's there as the correct non-multiple type +// let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); +// let acc2 = loaded_login.as_mnemonic_account().unwrap(); +// assert_eq!(acc2.mnemonic(), &account2); +// assert_eq!(acc2.hd_path(), &hd_path); + +// // Add a third and fourth mnenonic grouped together with the second one +// append_account_to_login_at_file( +// &wallet_file, +// account3.clone(), +// hd_path.clone(), +// id2.clone(), +// id3.clone(), +// &password, +// ) +// .unwrap(); +// append_account_to_login_at_file( +// &wallet_file, +// account4.clone(), +// hd_path.clone(), +// id2.clone(), +// id4.clone(), +// &password, +// ) +// .unwrap(); + +// // Check that we can load all four +// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); +// let acc1 = loaded_login.as_mnemonic_account().unwrap(); +// assert_eq!(acc1.mnemonic(), &account1); +// assert_eq!(acc1.hd_path(), &hd_path); + +// let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); +// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); +// let expected = vec![ +// WalletAccount::new(id2.into(), MnemonicAccount::new(account2, hd_path.clone())), +// WalletAccount::new(id3, MnemonicAccount::new(account3, hd_path.clone())), +// WalletAccount::new(id4, MnemonicAccount::new(account4, hd_path)), +// ] +// .into(); +// assert_eq!(loaded_accounts, &expected); +// } + +// #[test] +// fn append_accounts_to_existing_login_with_multi() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let account2 = Mnemonic::generate(24).unwrap(); +// let account3 = Mnemonic::generate(24).unwrap(); +// let account4 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); +// let id2 = LoginId::new("second".to_string()); +// let id3 = AccountId::new("third".to_string()); +// let id4 = AccountId::new("fourth".to_string()); + +// store_login_with_multiple_accounts_at_file( +// &wallet_file, +// account1.clone(), +// hd_path.clone(), +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// store_login_with_multiple_accounts_at_file( +// &wallet_file, +// account2.clone(), +// hd_path.clone(), +// id2.clone(), +// &password, +// ) +// .unwrap(); + +// // Add a third and fourth mnenonic grouped together with the second one +// append_account_to_login_at_file( +// &wallet_file, +// account3.clone(), +// hd_path.clone(), +// id2.clone(), +// id3.clone(), +// &password, +// ) +// .unwrap(); +// append_account_to_login_at_file( +// &wallet_file, +// account4.clone(), +// hd_path.clone(), +// id2.clone(), +// id4.clone(), +// &password, +// ) +// .unwrap(); + +// // Check that we can load all four +// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); +// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); +// let expected = vec![WalletAccount::new( +// DEFAULT_FIRST_ACCOUNT_NAME.into(), +// MnemonicAccount::new(account1, hd_path.clone()), +// )] +// .into(); +// assert_eq!(loaded_accounts, &expected); + +// let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); +// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); +// let expected = vec![ +// WalletAccount::new( +// DEFAULT_FIRST_ACCOUNT_NAME.into(), +// MnemonicAccount::new(account2, hd_path.clone()), +// ), +// WalletAccount::new(id3, MnemonicAccount::new(account3, hd_path.clone())), +// WalletAccount::new(id4, MnemonicAccount::new(account4, hd_path)), +// ] +// .into(); +// assert_eq!(loaded_accounts, &expected); +// } + +// #[test] +// fn append_account_to_existing_login_with_multi_then_update_pwd_and_load() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let account2 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let new_password = UserPassword::new("new_password".to_string()); +// let login_id = LoginId::new("first".to_string()); +// let appended_account = AccountId::new("second".to_string()); + +// store_login_with_multiple_accounts_at_file( +// &wallet_file, +// account1.clone(), +// hd_path.clone(), +// login_id.clone(), +// &password, +// ) +// .unwrap(); + +// // Append a second mnenonic to the same login +// append_account_to_login_at_file( +// &wallet_file, +// account2.clone(), +// hd_path.clone(), +// login_id.clone(), +// appended_account.clone(), +// &password, +// ) +// .unwrap(); + +// // Update the password +// update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); + +// // Expect that we can load these 2 accounts with the new password +// let loaded_login = +// load_existing_login_at_file(&wallet_file, &login_id, &new_password).unwrap(); +// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); +// let expected = vec![ +// WalletAccount::new( +// DEFAULT_FIRST_ACCOUNT_NAME.into(), +// MnemonicAccount::new(account1, hd_path.clone()), +// ), +// WalletAccount::new(appended_account, MnemonicAccount::new(account2, hd_path)), +// ] +// .into(); +// assert_eq!(loaded_accounts, &expected); + +// // Expect that trying to load these 2 accounts with the old password fails +// let err = load_existing_login_at_file(&wallet_file, &login_id, &password).unwrap_err(); +// assert!(matches!(err, BackendError::StoreCipherError { .. })); +// } + +// #[test] +// fn append_the_same_mnemonic_twice_fails() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); +// let id2 = AccountId::new("second".to_string()); + +// store_login_with_multiple_accounts_at_file( +// &wallet_file, +// account1.clone(), +// hd_path.clone(), +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// assert!(matches!( +// append_account_to_login_at_file(&wallet_file, account1, hd_path, id1, id2, &password), +// Err(BackendError::WalletMnemonicAlreadyExistsInWalletLogin), +// )) +// } + +// #[test] +// fn append_the_same_account_name_twice_fails() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let mnemonic1 = Mnemonic::generate(24).unwrap(); +// let mnemonic2 = Mnemonic::generate(24).unwrap(); +// let mnemonic3 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// // The top-level login id. NOTE: the first account id is always set to default. +// let login_id = LoginId::new("my_login_id".to_string()); + +// // Store the first account under login_id. The first account id is always set to default +// // name. +// store_login_with_multiple_accounts_at_file( +// &wallet_file, +// mnemonic1, +// hd_path.clone(), +// login_id.clone(), +// &password, +// ) +// .unwrap(); + +// // Append another account (account2) to the same login (login_id) +// let account2 = AccountId::new("account_2".to_string()); + +// append_account_to_login_at_file( +// &wallet_file, +// mnemonic2, +// hd_path.clone(), +// login_id.clone(), +// account2.clone(), +// &password, +// ) +// .unwrap(); + +// // Appending the third account, with same account id will fail +// assert!(matches!( +// append_account_to_login_at_file( +// &wallet_file, +// mnemonic3, +// hd_path, +// login_id, +// account2, +// &password, +// ), +// Err(BackendError::WalletAccountIdAlreadyExistsInWalletLogin), +// )) +// } + +// #[test] +// fn delete_the_same_account_twice_for_a_login_fails() { +// let store_dir = tempdir().unwrap(); +// let wallet = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let account2 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); +// let id2 = AccountId::new("second".to_string()); + +// store_login_at_file(&wallet, account1, hd_path.clone(), id1.clone(), &password).unwrap(); + +// append_account_to_login_at_file( +// &wallet, +// account2, +// hd_path, +// id1.clone(), +// id2.clone(), +// &password, +// ) +// .unwrap(); + +// remove_account_from_login_at_file(&wallet, &id1, &id2, &password).unwrap(); + +// assert!(matches!( +// remove_account_from_login_at_file(&wallet, &id1, &id2, &password), +// Err(BackendError::WalletNoSuchAccountIdInWalletLogin), +// )); +// } + +// #[test] +// fn delete_the_same_account_twice_for_a_login_fails_with_multi() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let account2 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); +// let id2 = AccountId::new("second".to_string()); + +// store_login_with_multiple_accounts_at_file( +// &wallet_file, +// account1, +// hd_path.clone(), +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// append_account_to_login_at_file( +// &wallet_file, +// account2, +// hd_path, +// id1.clone(), +// id2.clone(), +// &password, +// ) +// .unwrap(); + +// remove_account_from_login_at_file(&wallet_file, &id1, &id2, &password).unwrap(); + +// assert!(matches!( +// remove_account_from_login_at_file(&wallet_file, &id1, &id2, &password), +// Err(BackendError::WalletNoSuchAccountIdInWalletLogin), +// )); +// } + +// #[test] +// fn delete_appended_account_doesnt_affect_others() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let account2 = Mnemonic::generate(24).unwrap(); +// let account3 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); +// let id2 = LoginId::new("second".to_string()); +// let id3 = AccountId::new("third".to_string()); + +// store_login_at_file( +// &wallet_file, +// account1, +// hd_path.clone(), +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// store_login_at_file( +// &wallet_file, +// account2.clone(), +// hd_path.clone(), +// id2.clone(), +// &password, +// ) +// .unwrap(); + +// append_account_to_login_at_file( +// &wallet_file, +// account3.clone(), +// hd_path.clone(), +// id2.clone(), +// id3.clone(), +// &password, +// ) +// .unwrap(); + +// remove_login_at_file(&wallet_file, &id1).unwrap(); + +// // The second login one is still there +// let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); +// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); +// let expected = vec![ +// WalletAccount::new(id2.into(), MnemonicAccount::new(account2, hd_path.clone())), +// WalletAccount::new(id3, MnemonicAccount::new(account3, hd_path)), +// ] +// .into(); +// assert_eq!(loaded_accounts, &expected); +// } + +// #[test] +// fn remove_all_accounts_for_a_login_removes_the_file_when_empty() { +// let store_dir = tempdir().unwrap(); +// let wallet = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let account2 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); +// let id2 = AccountId::new("second".to_string()); + +// store_login_with_multiple_accounts_at_file( +// &wallet, +// account1, +// hd_path.clone(), +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// append_account_to_login_at_file( +// &wallet, +// account2, +// hd_path, +// id1.clone(), +// id2.clone(), +// &password, +// ) +// .unwrap(); + +// remove_account_from_login_at_file( +// &wallet, +// &id1, +// &DEFAULT_FIRST_ACCOUNT_NAME.into(), +// &password, +// ) +// .unwrap(); +// remove_account_from_login_at_file(&wallet, &id1, &id2, &password).unwrap(); + +// // The file should now be removed +// assert!(!wallet.exists()); + +// // And trying to load when the file is gone fails +// assert!(matches!( +// load_existing_login_at_file(&wallet, &id1, &password), +// Err(BackendError::WalletFileNotFound), +// )); +// } + +// #[test] +// fn remove_all_accounts_for_a_login_removes_that_login() { +// let store_dir = tempdir().unwrap(); +// let wallet = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let account2 = Mnemonic::generate(24).unwrap(); +// let account3 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); +// let id2 = AccountId::new("second".to_string()); +// let id3 = LoginId::new("third".to_string()); + +// store_login_with_multiple_accounts_at_file( +// &wallet, +// account1, +// hd_path.clone(), +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// append_account_to_login_at_file( +// &wallet, +// account2, +// hd_path.clone(), +// id1.clone(), +// id2.clone(), +// &password, +// ) +// .unwrap(); + +// store_login_with_multiple_accounts_at_file( +// &wallet, +// account3.clone(), +// hd_path.clone(), +// id3.clone(), +// &password, +// ) +// .unwrap(); + +// remove_account_from_login_at_file( +// &wallet, +// &id1, +// &DEFAULT_FIRST_ACCOUNT_NAME.into(), +// &password, +// ) +// .unwrap(); +// remove_account_from_login_at_file(&wallet, &id1, &id2, &password).unwrap(); + +// // And trying to load when the file is gone fails +// assert!(matches!( +// load_existing_login_at_file(&wallet, &id1, &password), +// Err(BackendError::WalletNoSuchLoginId), +// )); + +// // The other login is still there +// let loaded_login = load_existing_login_at_file(&wallet, &id3, &password).unwrap(); +// let acc3 = loaded_login.as_multiple_accounts().unwrap(); +// let expected = vec![WalletAccount::new( +// DEFAULT_FIRST_ACCOUNT_NAME.into(), +// MnemonicAccount::new(account3, hd_path), +// )] +// .into(); +// assert_eq!(acc3, &expected); +// } + +// #[test] +// fn append_accounts_and_remove_appended_accounts() { +// let store_dir = tempdir().unwrap(); +// let wallet = store_dir.path().join(WALLET_INFO_FILENAME); +// let acc1 = Mnemonic::generate(24).unwrap(); +// let acc2 = Mnemonic::generate(24).unwrap(); +// let acc3 = Mnemonic::generate(24).unwrap(); +// let acc4 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); +// let id2 = LoginId::new("second".to_string()); +// let id3 = AccountId::new("third".to_string()); +// let id4 = AccountId::new("fourth".to_string()); + +// store_login_at_file( +// &wallet, +// acc1.clone(), +// hd_path.clone(), +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// store_login_at_file( +// &wallet, +// acc2.clone(), +// hd_path.clone(), +// id2.clone(), +// &password, +// ) +// .unwrap(); + +// // Add a third and fourth mnenonic grouped together with the second one +// append_account_to_login_at_file( +// &wallet, +// acc3, +// hd_path.clone(), +// id2.clone(), +// id3.clone(), +// &password, +// ) +// .unwrap(); +// append_account_to_login_at_file( +// &wallet, +// acc4.clone(), +// hd_path.clone(), +// id2.clone(), +// id4.clone(), +// &password, +// ) +// .unwrap(); + +// // Delete the third mnemonic, from the second login entry +// remove_account_from_login_at_file(&wallet, &id2, &id3, &password).unwrap(); + +// // Check that we can still load the other accounts +// let loaded_login = load_existing_login_at_file(&wallet, &id2, &password).unwrap(); +// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); +// let expected = vec![ +// WalletAccount::new( +// id2.clone().into(), +// MnemonicAccount::new(acc2, hd_path.clone()), +// ), +// WalletAccount::new(id4.clone(), MnemonicAccount::new(acc4, hd_path.clone())), +// ] +// .into(); +// assert_eq!(loaded_accounts, &expected); + +// // Delete the second and fourth mnemonic from the second login entry removes the login entry +// remove_account_from_login_at_file(&wallet, &id2, &id2.clone().into(), &password).unwrap(); +// remove_account_from_login_at_file(&wallet, &id2, &id4, &password).unwrap(); +// assert!(matches!( +// load_existing_login_at_file(&wallet, &id2, &password), +// Err(BackendError::WalletNoSuchLoginId), +// )); + +// // The first login is still available +// let loaded_login = load_existing_login_at_file(&wallet, &id1, &password).unwrap(); +// let account = loaded_login.as_mnemonic_account().unwrap(); +// assert_eq!(account.mnemonic(), &acc1); +// assert_eq!(account.hd_path(), &hd_path); +// } + +// #[test] +// fn rename_first_account_in_login() { +// let store_dir = tempdir().unwrap(); +// let wallet = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let login_id = LoginId::new("first".to_string()); + +// store_login_with_multiple_accounts_at_file( +// &wallet, +// account1.clone(), +// hd_path.clone(), +// login_id.clone(), +// &password, +// ) +// .unwrap(); + +// let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); +// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); +// let expected = vec![WalletAccount::new( +// DEFAULT_FIRST_ACCOUNT_NAME.into(), +// MnemonicAccount::new(account1.clone(), hd_path.clone()), +// )] +// .into(); +// assert_eq!(loaded_accounts, &expected); + +// let renamed_account = AccountId::new("new_first".to_string()); + +// rename_account_in_login_at_file( +// &wallet, +// &login_id, +// &DEFAULT_FIRST_ACCOUNT_NAME.into(), +// &renamed_account, +// &password, +// ) +// .unwrap(); + +// let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); +// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); +// let expected = vec![WalletAccount::new( +// renamed_account, +// MnemonicAccount::new(account1, hd_path), +// )] +// .into(); +// assert_eq!(loaded_accounts, &expected); +// } + +// #[test] +// fn rename_one_account_in_login_with_two_accounts() { +// let store_dir = tempdir().unwrap(); +// let wallet = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let account2 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let login_id = LoginId::new("first".to_string()); +// let account_id2 = AccountId::new("second".to_string()); +// let renamed_account_id2 = AccountId::new("new_second".to_string()); + +// store_login_with_multiple_accounts_at_file( +// &wallet, +// account1.clone(), +// hd_path.clone(), +// login_id.clone(), +// &password, +// ) +// .unwrap(); + +// append_account_to_login_at_file( +// &wallet, +// account2.clone(), +// hd_path.clone(), +// login_id.clone(), +// account_id2.clone(), +// &password, +// ) +// .unwrap(); + +// // Load and confirm +// let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); +// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); +// let expected = vec![ +// WalletAccount::new( +// DEFAULT_FIRST_ACCOUNT_NAME.into(), +// MnemonicAccount::new(account1.clone(), hd_path.clone()), +// ), +// WalletAccount::new( +// account_id2.clone(), +// MnemonicAccount::new(account2.clone(), hd_path.clone()), +// ), +// ] +// .into(); +// assert_eq!(loaded_accounts, &expected); + +// // Rename the second account to a new name +// rename_account_in_login_at_file( +// &wallet, +// &login_id, +// &account_id2, +// &renamed_account_id2, +// &password, +// ) +// .unwrap(); + +// // Load and confirm +// let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); +// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); +// let expected = vec![ +// WalletAccount::new( +// DEFAULT_FIRST_ACCOUNT_NAME.into(), +// MnemonicAccount::new(account1, hd_path.clone()), +// ), +// WalletAccount::new(renamed_account_id2, MnemonicAccount::new(account2, hd_path)), +// ] +// .into(); +// assert_eq!(loaded_accounts, &expected); +// } + +// #[test] +// fn rename_account_into_existing_account_id_fails() { +// let store_dir = tempdir().unwrap(); +// let wallet = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let account2 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let login_id = LoginId::new("first".to_string()); +// let account_id2 = AccountId::new("second".to_string()); +// let renamed_account_id2 = DEFAULT_FIRST_ACCOUNT_NAME.into(); + +// store_login_with_multiple_accounts_at_file( +// &wallet, +// account1.clone(), +// hd_path.clone(), +// login_id.clone(), +// &password, +// ) +// .unwrap(); + +// append_account_to_login_at_file( +// &wallet, +// account2.clone(), +// hd_path.clone(), +// login_id.clone(), +// account_id2.clone(), +// &password, +// ) +// .unwrap(); + +// // Load and confirm +// let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); +// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); +// let expected = vec![ +// WalletAccount::new( +// DEFAULT_FIRST_ACCOUNT_NAME.into(), +// MnemonicAccount::new(account1.clone(), hd_path.clone()), +// ), +// WalletAccount::new( +// account_id2.clone(), +// MnemonicAccount::new(account2.clone(), hd_path.clone()), +// ), +// ] +// .into(); +// assert_eq!(loaded_accounts, &expected); + +// // Rename the second account to the name of the first one fails +// assert!(matches!( +// rename_account_in_login_at_file( +// &wallet, +// &login_id, +// &account_id2, +// &renamed_account_id2, +// &password, +// ), +// Err(BackendError::WalletAccountIdAlreadyExistsInWalletLogin) +// )); + +// // Load and confirm nothing was changed +// let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); +// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); +// let expected = vec![ +// WalletAccount::new( +// DEFAULT_FIRST_ACCOUNT_NAME.into(), +// MnemonicAccount::new(account1, hd_path.clone()), +// ), +// WalletAccount::new(account_id2, MnemonicAccount::new(account2, hd_path)), +// ] +// .into(); +// assert_eq!(loaded_accounts, &expected); +// } + +// // Test to that decrypts a stored file from the repo, to make sure we are able to decrypt stored +// // wallets created with older versions. +// #[test] +// fn decrypt_stored_wallet() { +// const SAVED_WALLET: &str = "src/wallet_storage/test-data/saved-wallet.json"; +// let wallet_file = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(SAVED_WALLET); + +// let wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); + +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let bad_password = UserPassword::new("bad-password".to_string()); +// let id1 = LoginId::new("first".to_string()); +// let id2 = LoginId::new("second".to_string()); + +// assert!(!wallet.password_can_decrypt_all(&bad_password)); +// assert!(wallet.password_can_decrypt_all(&password)); + +// let acc1 = wallet.decrypt_login(&id1, &password).unwrap(); +// let acc2 = wallet.decrypt_login(&id2, &password).unwrap(); + +// assert!(matches!(acc1, StoredLogin::Mnemonic(_))); +// assert!(matches!(acc2, StoredLogin::Mnemonic(_))); + +// let expected_acc1 = bip39::Mnemonic::from_str("country mean universe text phone begin deputy reject result good cram illness common cluster proud swamp digital patrol spread bar face december base kick").unwrap(); +// let expected_acc2 = bip39::Mnemonic::from_str("home mansion start quiz dress decide hint second dragon sunny juice always steak real minimum art rival skin draw total pulp foot goddess agent").unwrap(); + +// assert_eq!( +// acc1.as_mnemonic_account().unwrap().mnemonic(), +// &expected_acc1 +// ); +// assert_eq!(acc1.as_mnemonic_account().unwrap().hd_path(), &hd_path,); + +// assert_eq!( +// acc2.as_mnemonic_account().unwrap().mnemonic(), +// &expected_acc2 +// ); +// assert_eq!(acc2.as_mnemonic_account().unwrap().hd_path(), &hd_path,); +// } + +// #[test] +// fn decrypt_stored_wallet_1_0_4() { +// const SAVED_WALLET: &str = "src/wallet_storage/test-data/saved-wallet-1.0.4.json"; +// let wallet_file = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(SAVED_WALLET); + +// let wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); + +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password11!".to_string()); +// let bad_password = UserPassword::new("bad-password".to_string()); +// let login_id = LoginId::new("default".to_string()); + +// assert!(!wallet.password_can_decrypt_all(&bad_password)); +// assert!(wallet.password_can_decrypt_all(&password)); + +// let acc1 = wallet.decrypt_login(&login_id, &password).unwrap(); + +// assert!(matches!(acc1, StoredLogin::Mnemonic(_))); + +// let expected_acc1 = bip39::Mnemonic::from_str("arrow capable abstract industry elevator nominee december piece hotel feed lounge web faint sword veteran bundle hour page actual laptop horror gold test warrior").unwrap(); + +// assert_eq!( +// acc1.as_mnemonic_account().unwrap().mnemonic(), +// &expected_acc1 +// ); +// assert_eq!(acc1.as_mnemonic_account().unwrap().hd_path(), &hd_path,); +// } + +// #[test] +// fn decrypt_stored_wallet_1_0_5_with_multiple_accounts() { +// const SAVED_WALLET: &str = "src/wallet_storage/test-data/saved-wallet-1.0.5.json"; +// let wallet_file = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(SAVED_WALLET); + +// let wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); + +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password11!".to_string()); +// let bad_password = UserPassword::new("bad-password".to_string()); +// let login_id = LoginId::new("default".to_string()); + +// assert!(!wallet.password_can_decrypt_all(&bad_password)); +// assert!(wallet.password_can_decrypt_all(&password)); + +// let login = wallet.decrypt_login(&login_id, &password).unwrap(); + +// assert!(matches!(login, StoredLogin::Multiple(_))); + +// let login = login.as_multiple_accounts().unwrap(); +// assert_eq!(login.len(), 4); + +// let expected_mn1 = bip39::Mnemonic::from_str("arrow capable abstract industry elevator nominee december piece hotel feed lounge web faint sword veteran bundle hour page actual laptop horror gold test warrior").unwrap(); +// let expected_mn2 = bip39::Mnemonic::from_str("border hurt skull lunar goddess second danger game dismiss exhaust oven thumb dog drama onion false orchard spice tent next predict invite cherry green").unwrap(); +// let expected_mn3 = bip39::Mnemonic::from_str("gentle crowd rule snap girl urge flat jump winner cluster night sand museum stock grunt quick tree acquire traffic major awake tag rack peasant").unwrap(); +// let expected_mn4 = bip39::Mnemonic::from_str("debris blue skin annual inhale text border rigid spatial lesson coconut yard horn crystal control survey version vote hawk neck frame arrive oblige width").unwrap(); + +// let expected = vec![ +// WalletAccount::new( +// "default".into(), +// MnemonicAccount::new(expected_mn1, hd_path.clone()), +// ), +// WalletAccount::new( +// "account2".into(), +// MnemonicAccount::new(expected_mn2, hd_path.clone()), +// ), +// WalletAccount::new( +// "foobar".into(), +// MnemonicAccount::new(expected_mn3, hd_path.clone()), +// ), +// WalletAccount::new("42".into(), MnemonicAccount::new(expected_mn4, hd_path)), +// ] +// .into(); + +// assert_eq!(login, &expected); +// } + +// #[test] +// fn append_filename() { +// let wallet_file = PathBuf::from("/tmp/saved-wallet.json"); +// let timestamp = OsString::from("42"); +// #[cfg(target_family = "unix")] +// assert_eq!( +// append_timestamp_to_filename(wallet_file.clone(), timestamp.clone(), None) +// .unwrap() +// .into_os_string() +// .into_string() +// .unwrap(), +// "/tmp/saved-wallet-42.json".to_string(), +// ); +// #[cfg(not(target_family = "unix"))] +// assert_eq!( +// append_timestamp_to_filename(wallet_file.clone(), timestamp.clone(), None) +// .unwrap() +// .into_os_string() +// .into_string() +// .unwrap(), +// r"/tmp\saved-wallet-42.json".to_string(), +// ); + +// #[cfg(target_family = "unix")] +// assert_eq!( +// append_timestamp_to_filename(wallet_file, timestamp, Some(3)) +// .unwrap() +// .into_os_string() +// .into_string() +// .unwrap(), +// "/tmp/saved-wallet-42-3.json".to_string(), +// ); +// #[cfg(not(target_family = "unix"))] +// assert_eq!( +// append_timestamp_to_filename(wallet_file, timestamp, Some(3)) +// .unwrap() +// .into_os_string() +// .into_string() +// .unwrap(), +// r"/tmp\saved-wallet-42-3.json".to_string(), +// ); +// } +// } diff --git a/nym-wallet/src-tauri/tauri.conf.json b/nym-wallet/src-tauri/tauri.conf.json index ba7704c0fd..5843d192dd 100644 --- a/nym-wallet/src-tauri/tauri.conf.json +++ b/nym-wallet/src-tauri/tauri.conf.json @@ -1,78 +1,69 @@ { - "package": { - "productName": "nym-wallet", - "version": "1.2.16" - }, - "build": { - "distDir": "../dist", - "devPath": "http://localhost:9000", - "beforeDevCommand": "", - "beforeBuildCommand": "" - }, - "tauri": { - "bundle": { - "active": true, - "targets": "all", - "identifier": "net.nymtech.wallet", - "icon": [ - "icons/32x32.png", - "icons/128x128.png", - "icons/128x128@2x.png", - "icons/icon.icns", - "icons/icon.ico" - ], - "resources": [], - "externalBin": [], - "copyright": "Copyright © 2021-2023 Nym Technologies SA", - "category": "Business", - "shortDescription": "Nym desktop wallet allows you to manage your NYM tokens", - "longDescription": "", + "bundle": { + "active": true, + "targets": "all", + "windows": { + "certificateThumbprint": "6DB77B1F529A0804FE0E6843A3EB8A8CECFFD408", + "digestAlgorithm": "sha256", + "timestampUrl": "http://timestamp.comodoca.com" + }, + "icon": [ + "icons/32x32.png", + "icons/128x128.png", + "icons/128x128@2x.png", + "icons/icon.icns", + "icons/icon.ico" + ], + "resources": [], + "externalBin": [], + "copyright": "Copyright © 2021-2025 Nym Technologies SA", + "category": "Business", + "shortDescription": "Nym desktop wallet allows you to manage your NYM tokens", + "longDescription": "", + "macOS": { + "frameworks": [], + "minimumSystemVersion": "", + "exceptionDomain": "", + "signingIdentity": "Developer ID Application: Nym Technologies SA (VW5DZLFHM5)", + "entitlements": null + }, + "linux": { "deb": { "depends": [] - }, - "macOS": { - "frameworks": [], - "minimumSystemVersion": "", - "exceptionDomain": "", - "signingIdentity": "Developer ID Application: Nym Technologies SA (VW5DZLFHM5)", - "entitlements": null - }, - "windows": { - "certificateThumbprint": "6DB77B1F529A0804FE0E6843A3EB8A8CECFFD408", - "digestAlgorithm": "sha256", - "timestampUrl": "http://timestamp.comodoca.com" } }, + "createUpdaterArtifacts": "v1Compatible" + }, + "build": { + "beforeBuildCommand": "", + "frontendDist": "../dist", + "beforeDevCommand": "", + "devUrl": "http://localhost:9000" + }, + "productName": "nym-wallet", + "mainBinaryName": "nym-wallet", + "version": "1.2.15", + "identifier": "net.nymtech.wallet", + "plugins": { "updater": { - "active": true, + "pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IENCNzQ2M0E5N0VFODE2NApSV1JrZ2U2WE9rYTNETTg1OTBKdE5uWUEra0hML2syOVUvQ2lxZmFZRzZ1T3NWbGM0eVRzUTVhVwo=", "endpoints": [ "https://nymtech.net/.wellknown/wallet/updater.json" - ], - "dialog": true, - "pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IENCNzQ2M0E5N0VFODE2NApSV1JrZ2U2WE9rYTNETTg1OTBKdE5uWUEra0hML2syOVUvQ2lxZmFZRzZ1T3NWbGM0eVRzUTVhVwo=" - }, - "allowlist": { - "window": { - "maximize": true, - "print": true - }, - "clipboard": { - "all": true - }, - "shell": { - "open": true - } + ] + } + }, + "app": { + "security": { + "csp": "default-src blob: data: filesystem: ws: wss: http: https: tauri: 'unsafe-eval' 'unsafe-inline' 'self' img-src: 'self'; connect-src ipc: http://ipc.localhost" }, "windows": [ { "title": "Nym Wallet", "width": 1268, "height": 768, - "resizable": true + "resizable": true, + "useHttpsScheme": true } - ], - "security": { - "csp": "default-src blob: data: filesystem: ws: wss: http: https: tauri: 'unsafe-eval' 'unsafe-inline' 'self' img-src: 'self'" - } + ] } } \ No newline at end of file diff --git a/nym-wallet/src/components/CopyToClipboard.tsx b/nym-wallet/src/components/CopyToClipboard.tsx index d6de5cab9b..a58f8ff859 100644 --- a/nym-wallet/src/components/CopyToClipboard.tsx +++ b/nym-wallet/src/components/CopyToClipboard.tsx @@ -1,15 +1,15 @@ import React, { useEffect, useState } from 'react'; import { Button, IconButton, Tooltip } from '@mui/material'; import { Check, ContentCopy } from '@mui/icons-material'; -import { clipboard } from '@tauri-apps/api'; import { Console } from '../utils/console'; +import { writeText, readText } from '@tauri-apps/plugin-clipboard-manager'; export const CopyToClipboard = ({ text = '', iconButton }: { text?: string; iconButton?: boolean }) => { const [copied, setCopied] = useState(false); const handleCopy = async (_text: string) => { try { - await clipboard.writeText(_text); + await writeText(_text); setCopied(true); } catch (e) { Console.error(`failed to copy: ${e}`); diff --git a/nym-wallet/src/components/Settings/AppVersion.tsx b/nym-wallet/src/components/Settings/AppVersion.tsx index e968a0696c..28e4dcc824 100644 --- a/nym-wallet/src/components/Settings/AppVersion.tsx +++ b/nym-wallet/src/components/Settings/AppVersion.tsx @@ -1,6 +1,6 @@ import React, { useContext, useEffect, useState } from 'react'; import { Button, Stack, Typography } from '@mui/material'; -import { checkUpdate } from '@tauri-apps/api/updater'; +import { check } from '@tauri-apps/plugin-updater'; import { AppContext } from '../../context'; import { checkVersion } from '../../requests'; import { Console } from '../../utils/console'; @@ -28,7 +28,7 @@ const AppVersion = () => { try { // despite the name, this will spawn an external native window with // an embedded "download and update the Wallet" flow - checkUpdate(); + check(); } catch (e) { Console.error(e); } diff --git a/nym-wallet/src/pages/internal-docs/DocEntry.tsx b/nym-wallet/src/pages/internal-docs/DocEntry.tsx index 22374cc92c..8f6a3231c3 100644 --- a/nym-wallet/src/pages/internal-docs/DocEntry.tsx +++ b/nym-wallet/src/pages/internal-docs/DocEntry.tsx @@ -1,7 +1,7 @@ /* eslint-disable react/destructuring-assignment */ import React from 'react'; import { Button, Card, CardContent, TextField } from '@mui/material'; -import { invoke } from '@tauri-apps/api'; +import { invoke } from '@tauri-apps/api/core'; interface DocEntryProps { function: FunctionDef; diff --git a/nym-wallet/src/requests/wrapper.ts b/nym-wallet/src/requests/wrapper.ts index 5c9cb97f3a..6041bed1e4 100644 --- a/nym-wallet/src/requests/wrapper.ts +++ b/nym-wallet/src/requests/wrapper.ts @@ -1,4 +1,4 @@ -import { invoke } from '@tauri-apps/api'; +import { invoke } from '@tauri-apps/api/core'; import { config } from '../config'; import { Console } from '../utils/console'; diff --git a/nym-wallet/src/utils/common.ts b/nym-wallet/src/utils/common.ts index ed5d7453cd..f2da75f177 100644 --- a/nym-wallet/src/utils/common.ts +++ b/nym-wallet/src/utils/common.ts @@ -1,4 +1,4 @@ -import { appWindow } from '@tauri-apps/api/window'; +import { getCurrentWebviewWindow } from '@tauri-apps/api/webviewWindow'; import bs58 from 'bs58'; import Big from 'big.js'; import { valid } from 'semver'; @@ -14,6 +14,7 @@ import { userBalance, } from '../requests'; import { Console } from './console'; +const appWindow = getCurrentWebviewWindow() export const validateKey = (key: string, bytesLength: number): boolean => { // it must be a valid base58 key diff --git a/yarn.lock b/yarn.lock index ac67ada74f..843efd355b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -15,17 +15,6 @@ "@jridgewell/gen-mapping" "^0.3.5" "@jridgewell/trace-mapping" "^0.3.24" -"@asamuzakjp/css-color@^3.1.1": - version "3.1.1" - resolved "https://registry.yarnpkg.com/@asamuzakjp/css-color/-/css-color-3.1.1.tgz#41a612834dafd9353b89855b37baa8a03fb67bf2" - integrity sha512-hpRD68SV2OMcZCsrbdkccTw5FXjNDLo5OuqSHyHZfwweGsDWZwDJ2+gONyNAbazZclobMirACLw0lk8WVxIqxA== - dependencies: - "@csstools/css-calc" "^2.1.2" - "@csstools/css-color-parser" "^3.0.8" - "@csstools/css-parser-algorithms" "^3.0.4" - "@csstools/css-tokenizer" "^3.0.3" - lru-cache "^10.4.3" - "@babel/code-frame@7.12.11": version "7.12.11" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.12.11.tgz#f4ad435aa263db935b8f10f2c552d23fb716a63f" @@ -226,6 +215,14 @@ "@babel/helper-optimise-call-expression" "^7.25.9" "@babel/traverse" "^7.26.5" +"@babel/helper-simple-access@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.25.9.tgz#6d51783299884a2c74618d6ef0f86820ec2e7739" + integrity sha512-c6WHXuiaRsJTyHYLJV75t9IqsmTbItYfdj99PnzYGQZkYKvan5/2jKJ7gu31J3/BJ/A18grImSPModuyG/Eo0Q== + dependencies: + "@babel/traverse" "^7.25.9" + "@babel/types" "^7.25.9" + "@babel/helper-skip-transparent-expression-wrappers@^7.20.0", "@babel/helper-skip-transparent-expression-wrappers@^7.25.9": version "7.25.9" resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.25.9.tgz#0b2e1b62d560d6b1954893fd2b705dc17c91f0c9" @@ -1148,7 +1145,7 @@ dependencies: regenerator-runtime "^0.13.2" -"@babel/runtime@^7.0.0", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.15.4", "@babel/runtime@^7.17.8", "@babel/runtime@^7.18.3", "@babel/runtime@^7.18.9", "@babel/runtime@^7.21.0", "@babel/runtime@^7.23.2", "@babel/runtime@^7.23.9", "@babel/runtime@^7.24.0", "@babel/runtime@^7.25.0", "@babel/runtime@^7.25.7", "@babel/runtime@^7.26.0", "@babel/runtime@^7.26.10", "@babel/runtime@^7.3.1", "@babel/runtime@^7.5.0", "@babel/runtime@^7.5.5", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.3", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2": +"@babel/runtime@^7.0.0", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.15.4", "@babel/runtime@^7.17.8", "@babel/runtime@^7.18.3", "@babel/runtime@^7.18.9", "@babel/runtime@^7.21.0", "@babel/runtime@^7.23.9", "@babel/runtime@^7.24.0", "@babel/runtime@^7.25.7", "@babel/runtime@^7.26.0", "@babel/runtime@^7.3.1", "@babel/runtime@^7.5.0", "@babel/runtime@^7.5.5", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.3", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2": version "7.26.10" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.26.10.tgz#a07b4d8fa27af131a633d7b3524db803eb4764c2" integrity sha512-2WJMeRQPHKSPemqk/awGrAiuFfzBmOIPXKizAsVhWH9YJqLZ0H+HS4c8loHGgW6utJ3E/ejXQUsiGaQy2NZ9Fw== @@ -1202,60 +1199,6 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== -"@biomejs/biome@1.9.4": - version "1.9.4" - resolved "https://registry.yarnpkg.com/@biomejs/biome/-/biome-1.9.4.tgz#89766281cbc3a0aae865a7ff13d6aaffea2842bf" - integrity sha512-1rkd7G70+o9KkTn5KLmDYXihGoTaIGO9PIIN2ZB7UJxFrWw04CZHPYiMRjYsaDvVV7hP1dYNRLxSANLaBFGpog== - optionalDependencies: - "@biomejs/cli-darwin-arm64" "1.9.4" - "@biomejs/cli-darwin-x64" "1.9.4" - "@biomejs/cli-linux-arm64" "1.9.4" - "@biomejs/cli-linux-arm64-musl" "1.9.4" - "@biomejs/cli-linux-x64" "1.9.4" - "@biomejs/cli-linux-x64-musl" "1.9.4" - "@biomejs/cli-win32-arm64" "1.9.4" - "@biomejs/cli-win32-x64" "1.9.4" - -"@biomejs/cli-darwin-arm64@1.9.4": - version "1.9.4" - resolved "https://registry.yarnpkg.com/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-1.9.4.tgz#dfa376d23a54a2d8f17133c92f23c1bf2e62509f" - integrity sha512-bFBsPWrNvkdKrNCYeAp+xo2HecOGPAy9WyNyB/jKnnedgzl4W4Hb9ZMzYNbf8dMCGmUdSavlYHiR01QaYR58cw== - -"@biomejs/cli-darwin-x64@1.9.4": - version "1.9.4" - resolved "https://registry.yarnpkg.com/@biomejs/cli-darwin-x64/-/cli-darwin-x64-1.9.4.tgz#eafc2ce3849d385fc02238aad1ca4a73395a64d9" - integrity sha512-ngYBh/+bEedqkSevPVhLP4QfVPCpb+4BBe2p7Xs32dBgs7rh9nY2AIYUL6BgLw1JVXV8GlpKmb/hNiuIxfPfZg== - -"@biomejs/cli-linux-arm64-musl@1.9.4": - version "1.9.4" - resolved "https://registry.yarnpkg.com/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-1.9.4.tgz#d780c3e01758fc90f3268357e3f19163d1f84fca" - integrity sha512-v665Ct9WCRjGa8+kTr0CzApU0+XXtRgwmzIf1SeKSGAv+2scAlW6JR5PMFo6FzqqZ64Po79cKODKf3/AAmECqA== - -"@biomejs/cli-linux-arm64@1.9.4": - version "1.9.4" - resolved "https://registry.yarnpkg.com/@biomejs/cli-linux-arm64/-/cli-linux-arm64-1.9.4.tgz#8ed1dd0e89419a4b66a47f95aefb8c46ae6041c9" - integrity sha512-fJIW0+LYujdjUgJJuwesP4EjIBl/N/TcOX3IvIHJQNsAqvV2CHIogsmA94BPG6jZATS4Hi+xv4SkBBQSt1N4/g== - -"@biomejs/cli-linux-x64-musl@1.9.4": - version "1.9.4" - resolved "https://registry.yarnpkg.com/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-1.9.4.tgz#f36982b966bd671a36671e1de4417963d7db15fb" - integrity sha512-gEhi/jSBhZ2m6wjV530Yy8+fNqG8PAinM3oV7CyO+6c3CEh16Eizm21uHVsyVBEB6RIM8JHIl6AGYCv6Q6Q9Tg== - -"@biomejs/cli-linux-x64@1.9.4": - version "1.9.4" - resolved "https://registry.yarnpkg.com/@biomejs/cli-linux-x64/-/cli-linux-x64-1.9.4.tgz#a0a7f56680c76b8034ddc149dbf398bdd3a462e8" - integrity sha512-lRCJv/Vi3Vlwmbd6K+oQ0KhLHMAysN8lXoCI7XeHlxaajk06u7G+UsFSO01NAs5iYuWKmVZjmiOzJ0OJmGsMwg== - -"@biomejs/cli-win32-arm64@1.9.4": - version "1.9.4" - resolved "https://registry.yarnpkg.com/@biomejs/cli-win32-arm64/-/cli-win32-arm64-1.9.4.tgz#e2ef4e0084e76b7e26f0fc887c5ef1265ea56200" - integrity sha512-tlbhLk+WXZmgwoIKwHIHEBZUwxml7bRJgk0X2sPyNR3S93cdRq6XulAZRQJ17FYGGzWne0fgrXBKpl7l4M87Hg== - -"@biomejs/cli-win32-x64@1.9.4": - version "1.9.4" - resolved "https://registry.yarnpkg.com/@biomejs/cli-win32-x64/-/cli-win32-x64-1.9.4.tgz#4c7afa90e3970213599b4095e62f87e5972b2340" - integrity sha512-8Y5wMhVIPaWe6jw2H+KlEm4wP/f7EW3810ZLmDlrEEy5KvBsb9ECEfu/kMWD484ijfQ8+nIi0giMgu9g1UAuuA== - "@chain-registry/client@^1.49.11": version "1.53.94" resolved "https://registry.yarnpkg.com/@chain-registry/client/-/client-1.53.94.tgz#cbee6721abb8a7cdbf54383c3c7746c0b2e32a46" @@ -1288,7 +1231,7 @@ resolved "https://registry.yarnpkg.com/@chain-registry/types/-/types-0.46.15.tgz#f4c0219fb7060d97cb224b55f349adb1d436aac9" integrity sha512-gzf+pkAbEZ7fKuTuwmrEAEcx1K/BNrKuCnB9s+WwSo9Ad/3s+GV5LOXcOOxjjHh9Mrs9kvnxKvzKjOwWu8gDJw== -"@chain-registry/types@^0.50.36", "@chain-registry/types@^0.50.94": +"@chain-registry/types@^0.50.94": version "0.50.94" resolved "https://registry.yarnpkg.com/@chain-registry/types/-/types-0.50.94.tgz#9c9cdeb45b122dae116f19ba04418ad6a183e154" integrity sha512-D9PvO09UTjdKD+MmHFNSP//vfWXVEt1ILXOBGvZfi9kBy1a5xM4tCLuH/DH6kX5hjZHGxFmcBUwvYwh1kUJc7w== @@ -1767,7 +1710,7 @@ nock "13.5.4" uuid "^9.0.1" -"@cosmos-kit/keplr-extension@^2.14.0", "@cosmos-kit/keplr-extension@^2.7.9": +"@cosmos-kit/keplr-extension@^2.7.9": version "2.14.6" resolved "https://registry.yarnpkg.com/@cosmos-kit/keplr-extension/-/keplr-extension-2.14.6.tgz#4956500d1b6fe7a0ca39b7e77a2df5232b21ca6f" integrity sha512-oebXKrcwTcKCAjU05Jx4bS2gkBPF7oXqhvHReaCgxyMx+D6xHOORI7a70BxJIUbZZjTslAfgDN8k2/Xcj5/PIQ== @@ -1786,7 +1729,7 @@ "@cosmos-kit/core" "^2.15.5" "@dao-dao/cosmiframe" "^1.0.0-rc.1" -"@cosmos-kit/react@^2.10.11", "@cosmos-kit/react@^2.20.1": +"@cosmos-kit/react@^2.10.11": version "2.21.7" resolved "https://registry.yarnpkg.com/@cosmos-kit/react/-/react-2.21.7.tgz#009cb60f6604bc3894d4946c9ce038a2039c6166" integrity sha512-7LByV1LpwtXR2ohW4OOLd7lHS6jVs2AWvgMx8I8Y+B7y7k6YU1MU63dnMqKyV2JqfSv9GpeCa+Xhadd35kcMrw== @@ -1803,34 +1746,6 @@ dependencies: "@jridgewell/trace-mapping" "0.3.9" -"@csstools/color-helpers@^5.0.2": - version "5.0.2" - resolved "https://registry.yarnpkg.com/@csstools/color-helpers/-/color-helpers-5.0.2.tgz#82592c9a7c2b83c293d9161894e2a6471feb97b8" - integrity sha512-JqWH1vsgdGcw2RR6VliXXdA0/59LttzlU8UlRT/iUUsEeWfYq8I+K0yhihEUTTHLRm1EXvpsCx3083EU15ecsA== - -"@csstools/css-calc@^2.1.2": - version "2.1.2" - resolved "https://registry.yarnpkg.com/@csstools/css-calc/-/css-calc-2.1.2.tgz#bffd55f002dab119b76d4023f95cd943e6c8c11e" - integrity sha512-TklMyb3uBB28b5uQdxjReG4L80NxAqgrECqLZFQbyLekwwlcDDS8r3f07DKqeo8C4926Br0gf/ZDe17Zv4wIuw== - -"@csstools/css-color-parser@^3.0.8": - version "3.0.8" - resolved "https://registry.yarnpkg.com/@csstools/css-color-parser/-/css-color-parser-3.0.8.tgz#5fe9322920851450bf5e065c2b0e731b9e165394" - integrity sha512-pdwotQjCCnRPuNi06jFuP68cykU1f3ZWExLe/8MQ1LOs8Xq+fTkYgd+2V8mWUWMrOn9iS2HftPVaMZDaXzGbhQ== - dependencies: - "@csstools/color-helpers" "^5.0.2" - "@csstools/css-calc" "^2.1.2" - -"@csstools/css-parser-algorithms@^3.0.4": - version "3.0.4" - resolved "https://registry.yarnpkg.com/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.4.tgz#74426e93bd1c4dcab3e441f5cc7ba4fb35d94356" - integrity sha512-Up7rBoV77rv29d3uKHUIVubz1BTcgyUK72IvCQAbfbMv584xHcGKCKbWh7i8hPrRJ7qU4Y8IO3IY9m+iTB7P3A== - -"@csstools/css-tokenizer@^3.0.3": - version "3.0.3" - resolved "https://registry.yarnpkg.com/@csstools/css-tokenizer/-/css-tokenizer-3.0.3.tgz#a5502c8539265fecbd873c1e395a890339f119c2" - integrity sha512-UJnjoFsmxfKUdNYdWgOB0mWUypuLvAfQPH1+pyvRJs6euowbFkFC6P13w1l8mJyi3vxYMxc9kld5jZEGRQs6bw== - "@dao-dao/cosmiframe@^1.0.0-rc.1": version "1.0.0-rc.1" resolved "https://registry.yarnpkg.com/@dao-dao/cosmiframe/-/cosmiframe-1.0.0-rc.1.tgz#51d4d1801605b8abfd01ae0425c797b6b876991e" @@ -1956,7 +1871,7 @@ resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.9.0.tgz#745969d649977776b43fc7648c556aaa462b4102" integrity sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ== -"@emotion/react@^11.13.5", "@emotion/react@^11.4.1", "@emotion/react@^11.7.0": +"@emotion/react@^11.4.1", "@emotion/react@^11.7.0": version "11.14.0" resolved "https://registry.yarnpkg.com/@emotion/react/-/react-11.14.0.tgz#cfaae35ebc67dd9ef4ea2e9acc6cd29e157dd05d" integrity sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA== @@ -1986,7 +1901,7 @@ resolved "https://registry.yarnpkg.com/@emotion/sheet/-/sheet-1.4.0.tgz#c9299c34d248bc26e82563735f78953d2efca83c" integrity sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg== -"@emotion/styled@^11.13.5", "@emotion/styled@^11.3.0", "@emotion/styled@^11.6.0": +"@emotion/styled@^11.3.0", "@emotion/styled@^11.6.0": version "11.14.0" resolved "https://registry.yarnpkg.com/@emotion/styled/-/styled-11.14.0.tgz#f47ca7219b1a295186d7661583376fcea95f0ff3" integrity sha512-XxfOnXFffatap2IyCeJyNov3kiDQWoR08gPUQxvbL7fxKryGBKUZUkG6Hz48DZwVrJSVh9sJboyV1Ds4OW6SgA== @@ -2018,14 +1933,14 @@ resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz#5e13fac887f08c44f76b0ccaf3370eb00fec9bb6" integrity sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg== -"@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0": +"@eslint-community/eslint-utils@^4.2.0": version "4.5.1" resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.5.1.tgz#b0fc7e06d0c94f801537fd4237edc2706d3b8e4c" integrity sha512-soEIOALTfTK6EjmKMMoLugwaP0rzkad90iIWd1hMO9ARkSAyjfMfkRRhLvD5qH7vvM0Cg72pieUfR6yh6XxC4w== dependencies: eslint-visitor-keys "^3.4.3" -"@eslint-community/regexpp@^4.10.0", "@eslint-community/regexpp@^4.4.0", "@eslint-community/regexpp@^4.6.1": +"@eslint-community/regexpp@^4.4.0", "@eslint-community/regexpp@^4.6.1": version "4.12.1" resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.12.1.tgz#cfc6cffe39df390a3841cde2abccf92eaa7ae0e0" integrity sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ== @@ -2381,7 +2296,7 @@ resolved "https://registry.yarnpkg.com/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.5.tgz#56f00962ff0c4e0eb93d34a047d29fa995e3e342" integrity sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg== -"@interchain-ui/react@^1.14.2", "@interchain-ui/react@^1.26.1": +"@interchain-ui/react@^1.14.2": version "1.26.2" resolved "https://registry.yarnpkg.com/@interchain-ui/react/-/react-1.26.2.tgz#bb38916362cb48c220c40dabf94d5cb60214aeab" integrity sha512-AstIeKU+CvZwXR1LOeeiepCIK5QWpScTZKPpR6JCSfJCYuXox5qmim/dv57t3mlJReLiPX1R+Cy+0zGrxhX14Q== @@ -3017,12 +2932,7 @@ resolved "https://registry.yarnpkg.com/@mui/core-downloads-tracker/-/core-downloads-tracker-5.16.14.tgz#e6536f1b6caa873f7915fbf9703fdc840a5a98d9" integrity sha512-sbjXW+BBSvmzn61XyTMun899E7nGPTXwqD9drm1jBUAvWEhJpPFIRxwQQiATWZnd9rvdxtnhhdsDxEGWI0jxqA== -"@mui/core-downloads-tracker@^6.4.8": - version "6.4.8" - resolved "https://registry.yarnpkg.com/@mui/core-downloads-tracker/-/core-downloads-tracker-6.4.8.tgz#ffabbdce0e7b341e9c2906bb06a00b34c9d15e05" - integrity sha512-vjP4+A1ybyCRhDZC7r5EPWu/gLseFZxaGyPdDl94vzVvk6Yj6gahdaqcjbhkaCrJjdZj90m3VioltWPAnWF/zw== - -"@mui/icons-material@^5.0.0", "@mui/icons-material@^5.16.11", "@mui/icons-material@^5.2.0": +"@mui/icons-material@^5.0.0", "@mui/icons-material@^5.2.0": version "5.16.14" resolved "https://registry.yarnpkg.com/@mui/icons-material/-/icons-material-5.16.14.tgz#4fdecb05c15b1696f6f668fc29d549875544c892" integrity sha512-heL4S+EawrP61xMXBm59QH6HODsu0gxtZi5JtnXF2r+rghzyU/3Uftlt1ij8rmJh+cFdKTQug1L9KkZB5JgpMQ== @@ -3042,13 +2952,6 @@ clsx "^2.1.0" prop-types "^15.8.1" -"@mui/material-nextjs@^6.1.9": - version "6.4.3" - resolved "https://registry.yarnpkg.com/@mui/material-nextjs/-/material-nextjs-6.4.3.tgz#516eec4b6ca7d79f196f5c8b1b7f91e69e5c95ab" - integrity sha512-4ZRLrcD1HeWpvY8c7MrKYKuaUSobtvqcLYeEfGh/x5ezzPgKizhl7C0jpVVEgf6g+C9OgOGbhLTVfks7Y2IBAQ== - dependencies: - "@babel/runtime" "^7.26.0" - "@mui/material@^5.0.1", "@mui/material@^5.2.2": version "5.16.14" resolved "https://registry.yarnpkg.com/@mui/material/-/material-5.16.14.tgz#da8a75822f039d8c1b0ab7fb4146d767c2f9248a" @@ -3067,24 +2970,6 @@ react-is "^19.0.0" react-transition-group "^4.4.5" -"@mui/material@^6.1.10": - version "6.4.8" - resolved "https://registry.yarnpkg.com/@mui/material/-/material-6.4.8.tgz#3da961a35b778246832db22fab98c49adbccda77" - integrity sha512-5S9UTjKZZBd9GfbcYh/nYfD9cv6OXmj5Y7NgKYfk7JcSoshp8/pW5zP4wecRiroBSZX8wcrywSgogpVNO+5W0Q== - dependencies: - "@babel/runtime" "^7.26.0" - "@mui/core-downloads-tracker" "^6.4.8" - "@mui/system" "^6.4.8" - "@mui/types" "~7.2.24" - "@mui/utils" "^6.4.8" - "@popperjs/core" "^2.11.8" - "@types/react-transition-group" "^4.4.12" - clsx "^2.1.1" - csstype "^3.1.3" - prop-types "^15.8.1" - react-is "^19.0.0" - react-transition-group "^4.4.5" - "@mui/private-theming@^5.16.14": version "5.16.14" resolved "https://registry.yarnpkg.com/@mui/private-theming/-/private-theming-5.16.14.tgz#7ad2b8a8fe0417f9fdfd05011806b9cb33c1a20a" @@ -3094,15 +2979,6 @@ "@mui/utils" "^5.16.14" prop-types "^15.8.1" -"@mui/private-theming@^6.4.8": - version "6.4.8" - resolved "https://registry.yarnpkg.com/@mui/private-theming/-/private-theming-6.4.8.tgz#210d1a1ab4501f6cf31f1abdf1b0a37fbdfc2991" - integrity sha512-sWwQoNSn6elsPTAtSqCf+w5aaGoh7AASURNmpy+QTTD/zwJ0Jgwt0ZaaP6mXq2IcgHxYnYloM/+vJgHPMkRKTQ== - dependencies: - "@babel/runtime" "^7.26.0" - "@mui/utils" "^6.4.8" - prop-types "^15.8.1" - "@mui/styled-engine@^5.16.14": version "5.16.14" resolved "https://registry.yarnpkg.com/@mui/styled-engine/-/styled-engine-5.16.14.tgz#f90fef5b4f8ebf11d48e1b1df8854a45bb31a9f5" @@ -3113,18 +2989,6 @@ csstype "^3.1.3" prop-types "^15.8.1" -"@mui/styled-engine@^6.4.8": - version "6.4.8" - resolved "https://registry.yarnpkg.com/@mui/styled-engine/-/styled-engine-6.4.8.tgz#a454115ab349cf729510c107f2564fd1e23c385a" - integrity sha512-oyjx1b1FvUCI85ZMO4trrjNxGm90eLN3Ohy0AP/SqK5gWvRQg1677UjNf7t6iETOKAleHctJjuq0B3aXO2gtmw== - dependencies: - "@babel/runtime" "^7.26.0" - "@emotion/cache" "^11.13.5" - "@emotion/serialize" "^1.3.3" - "@emotion/sheet" "^1.4.0" - csstype "^3.1.3" - prop-types "^15.8.1" - "@mui/styles@^5.0.1", "@mui/styles@^5.2.2": version "5.16.14" resolved "https://registry.yarnpkg.com/@mui/styles/-/styles-5.16.14.tgz#a9482d0287ae83636c146817ba99f7dde6fcdaca" @@ -3162,20 +3026,6 @@ csstype "^3.1.3" prop-types "^15.8.1" -"@mui/system@^6.4.8": - version "6.4.8" - resolved "https://registry.yarnpkg.com/@mui/system/-/system-6.4.8.tgz#e4ecb65677c2268959effad1f4f885d9b836c09b" - integrity sha512-gV7iBHoqlsIenU2BP0wq14BefRoZcASZ/4LeyuQglayBl+DfLX5rEd3EYR3J409V2EZpR0NOM1LATAGlNk2cyA== - dependencies: - "@babel/runtime" "^7.26.0" - "@mui/private-theming" "^6.4.8" - "@mui/styled-engine" "^6.4.8" - "@mui/types" "~7.2.24" - "@mui/utils" "^6.4.8" - clsx "^2.1.1" - csstype "^3.1.3" - prop-types "^15.8.1" - "@mui/types@^7.2.14", "@mui/types@^7.2.15", "@mui/types@~7.2.24": version "7.2.24" resolved "https://registry.yarnpkg.com/@mui/types/-/types-7.2.24.tgz#5eff63129d9c29d80bbf2d2e561bd0690314dec2" @@ -3277,19 +3127,6 @@ prop-types "^15.8.1" react-transition-group "^4.4.5" -"@mui/x-date-pickers@^7.23.2": - version "7.28.0" - resolved "https://registry.yarnpkg.com/@mui/x-date-pickers/-/x-date-pickers-7.28.0.tgz#1daa089722b7b3b7458ad9af1ef39ae5ec9a9918" - integrity sha512-m1bfkZLOw3cMogeh6q92SjykVmLzfptnz3ZTgAlFKV7UBnVFuGUITvmwbgTZ1Mz3FmLVnGUQYUpZWw0ZnoghNA== - dependencies: - "@babel/runtime" "^7.25.7" - "@mui/utils" "^5.16.6 || ^6.0.0 || ^7.0.0 || ^7.0.0-beta" - "@mui/x-internals" "7.28.0" - "@types/react-transition-group" "^4.4.11" - clsx "^2.1.1" - prop-types "^15.8.1" - react-transition-group "^4.4.5" - "@mui/x-internals@7.28.0": version "7.28.0" resolved "https://registry.yarnpkg.com/@mui/x-internals/-/x-internals-7.28.0.tgz#b0a04f4c0f53f2f91d13a46f357f731b77c832c5" @@ -3320,15 +3157,10 @@ "@emnapi/runtime" "^1.3.1" "@tybys/wasm-util" "^0.9.0" -"@next/env@14.1.4": - version "14.1.4" - resolved "https://registry.yarnpkg.com/@next/env/-/env-14.1.4.tgz#432e80651733fbd67230bf262aee28be65252674" - integrity sha512-e7X7bbn3Z6DWnDi75UWn+REgAbLEqxI8Tq2pkFOFAMpWAWApz/YCUhtWMWn410h8Q2fYiYL7Yg5OlxMOCfFjJQ== - -"@next/env@15.2.2": - version "15.2.2" - resolved "https://registry.yarnpkg.com/@next/env/-/env-15.2.2.tgz#6345352365a811c523cecf284874ff489b675e59" - integrity sha512-yWgopCfA9XDR8ZH3taB5nRKtKJ1Q5fYsTOuYkzIIoS8TJ0UAUKAGF73JnGszbjk2ufAQDj6mDdgsJAFx5CLtYQ== +"@next/env@14.2.21": + version "14.2.21" + resolved "https://registry.yarnpkg.com/@next/env/-/env-14.2.21.tgz#09ff0813d29c596397e141205d4f5fd5c236bdd0" + integrity sha512-lXcwcJd5oR01tggjWJ6SrNNYFGuOOMB9c251wUNkjCpkoXOPkDeF/15c3mnVlBqrW4JJXb2kVxDFhC4GduJt2A== "@next/eslint-plugin-next@14.1.4": version "14.1.4" @@ -3337,213 +3169,50 @@ dependencies: glob "10.3.10" -"@next/eslint-plugin-next@15.0.3": - version "15.0.3" - resolved "https://registry.yarnpkg.com/@next/eslint-plugin-next/-/eslint-plugin-next-15.0.3.tgz#ce953098036d462f6901e423cc6445fc165b78c4" - integrity sha512-3Ln/nHq2V+v8uIaxCR6YfYo7ceRgZNXfTd3yW1ukTaFbO+/I8jNakrjYWODvG9BuR2v5kgVtH/C8r0i11quOgw== - dependencies: - fast-glob "3.3.1" +"@next/swc-darwin-arm64@14.2.21": + version "14.2.21" + resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.21.tgz#32a31992aace1440981df9cf7cb3af7845d94fec" + integrity sha512-HwEjcKsXtvszXz5q5Z7wCtrHeTTDSTgAbocz45PHMUjU3fBYInfvhR+ZhavDRUYLonm53aHZbB09QtJVJj8T7g== -"@next/swc-darwin-arm64@14.1.4": - version "14.1.4" - resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.1.4.tgz#a3bca0dc4393ac4cf3169bbf24df63441de66bb7" - integrity sha512-ubmUkbmW65nIAOmoxT1IROZdmmJMmdYvXIe8211send9ZYJu+SqxSnJM4TrPj9wmL6g9Atvj0S/2cFmMSS99jg== +"@next/swc-darwin-x64@14.2.21": + version "14.2.21" + resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.21.tgz#5ab4b3f6685b6b52f810d0f5cf6e471480ddffdb" + integrity sha512-TSAA2ROgNzm4FhKbTbyJOBrsREOMVdDIltZ6aZiKvCi/v0UwFmwigBGeqXDA97TFMpR3LNNpw52CbVelkoQBxA== -"@next/swc-darwin-arm64@15.2.2": - version "15.2.2" - resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.2.2.tgz#d3131279d4815ed7604eba7bf0063e0b8bfd2135" - integrity sha512-HNBRnz+bkZ+KfyOExpUxTMR0Ow8nkkcE6IlsdEa9W/rI7gefud19+Sn1xYKwB9pdCdxIP1lPru/ZfjfA+iT8pw== +"@next/swc-linux-arm64-gnu@14.2.21": + version "14.2.21" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.21.tgz#8a0e1fa887aef19ca218af2af515d0a5ee67ba3f" + integrity sha512-0Dqjn0pEUz3JG+AImpnMMW/m8hRtl1GQCNbO66V1yp6RswSTiKmnHf3pTX6xMdJYSemf3O4Q9ykiL0jymu0TuA== -"@next/swc-darwin-x64@14.1.4": - version "14.1.4" - resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-14.1.4.tgz#ba3683d4e2d30099f3f2864dd7349a4d9f440140" - integrity sha512-b0Xo1ELj3u7IkZWAKcJPJEhBop117U78l70nfoQGo4xUSvv0PJSTaV4U9xQBLvZlnjsYkc8RwQN1HoH/oQmLlQ== +"@next/swc-linux-arm64-musl@14.2.21": + version "14.2.21" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.21.tgz#ddad844406b42fa8965fe11250abc85c1fe0fd05" + integrity sha512-Ggfw5qnMXldscVntwnjfaQs5GbBbjioV4B4loP+bjqNEb42fzZlAaK+ldL0jm2CTJga9LynBMhekNfV8W4+HBw== -"@next/swc-darwin-x64@15.2.2": - version "15.2.2" - resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-15.2.2.tgz#8562f6b51177aa30f3a6e95a8b3e0e176a6cb04f" - integrity sha512-mJOUwp7al63tDpLpEFpKwwg5jwvtL1lhRW2fI1Aog0nYCPAhxbJsaZKdoVyPZCy8MYf/iQVNDuk/+i29iLCzIA== +"@next/swc-linux-x64-gnu@14.2.21": + version "14.2.21" + resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.21.tgz#db55fd666f9ba27718f65caa54b622a912cdd16b" + integrity sha512-uokj0lubN1WoSa5KKdThVPRffGyiWlm/vCc/cMkWOQHw69Qt0X1o3b2PyLLx8ANqlefILZh1EdfLRz9gVpG6tg== -"@next/swc-linux-arm64-gnu@14.1.4": - version "14.1.4" - resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.1.4.tgz#3519969293f16379954b7e196deb0c1eecbb2f8b" - integrity sha512-457G0hcLrdYA/u1O2XkRMsDKId5VKe3uKPvrKVOyuARa6nXrdhJOOYU9hkKKyQTMru1B8qEP78IAhf/1XnVqKA== +"@next/swc-linux-x64-musl@14.2.21": + version "14.2.21" + resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.21.tgz#dddb850353624efcd58c4c4e30ad8a1aab379642" + integrity sha512-iAEBPzWNbciah4+0yI4s7Pce6BIoxTQ0AGCkxn/UBuzJFkYyJt71MadYQkjPqCQCJAFQ26sYh7MOKdU+VQFgPg== -"@next/swc-linux-arm64-gnu@15.2.2": - version "15.2.2" - resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.2.2.tgz#c2466cffc541f39ae4d435087c81f5899d401903" - integrity sha512-5ZZ0Zwy3SgMr7MfWtRE7cQWVssfOvxYfD9O7XHM7KM4nrf5EOeqwq67ZXDgo86LVmffgsu5tPO57EeFKRnrfSQ== +"@next/swc-win32-arm64-msvc@14.2.21": + version "14.2.21" + resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.21.tgz#290012ee57b196d3d2d04853e6bf0179cae9fbaf" + integrity sha512-plykgB3vL2hB4Z32W3ktsfqyuyGAPxqwiyrAi2Mr8LlEUhNn9VgkiAl5hODSBpzIfWweX3er1f5uNpGDygfQVQ== -"@next/swc-linux-arm64-musl@14.1.4": - version "14.1.4" - resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.1.4.tgz#4bb3196bd402b3f84cf5373ff1021f547264d62f" - integrity sha512-l/kMG+z6MB+fKA9KdtyprkTQ1ihlJcBh66cf0HvqGP+rXBbOXX0dpJatjZbHeunvEHoBBS69GYQG5ry78JMy3g== +"@next/swc-win32-ia32-msvc@14.2.21": + version "14.2.21" + resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.21.tgz#c959135a78cab18cca588d11d1e33bcf199590d4" + integrity sha512-w5bacz4Vxqrh06BjWgua3Yf7EMDb8iMcVhNrNx8KnJXt8t+Uu0Zg4JHLDL/T7DkTCEEfKXO/Er1fcfWxn2xfPA== -"@next/swc-linux-arm64-musl@15.2.2": - version "15.2.2" - resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.2.2.tgz#24ed87fe47325597e986bd01c427efae53d6b677" - integrity sha512-cgKWBuFMLlJ4TWcFHl1KOaVVUAF8vy4qEvX5KsNd0Yj5mhu989QFCq1WjuaEbv/tO1ZpsQI6h/0YR8bLwEi+nA== - -"@next/swc-linux-x64-gnu@14.1.4": - version "14.1.4" - resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.1.4.tgz#1b3372c98c83dcdab946cdb4ee06e068b8139ba3" - integrity sha512-BapIFZ3ZRnvQ1uWbmqEGJuPT9cgLwvKtxhK/L2t4QYO7l+/DxXuIGjvp1x8rvfa/x1FFSsipERZK70pewbtJtw== - -"@next/swc-linux-x64-gnu@15.2.2": - version "15.2.2" - resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.2.2.tgz#0240f44530ca39c1fc899fabe8f33aa35d7e77da" - integrity sha512-c3kWSOSsVL8rcNBBfOq1+/j2PKs2nsMwJUV4icUxRgGBwUOfppeh7YhN5s79enBQFU+8xRgVatFkhHU1QW7yUA== - -"@next/swc-linux-x64-musl@14.1.4": - version "14.1.4" - resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.1.4.tgz#8459088bdc872648ff78f121db596f2533df5808" - integrity sha512-mqVxTwk4XuBl49qn2A5UmzFImoL1iLm0KQQwtdRJRKl21ylQwwGCxJtIYo2rbfkZHoSKlh/YgztY0qH3wG1xIg== - -"@next/swc-linux-x64-musl@15.2.2": - version "15.2.2" - resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.2.2.tgz#cca6ce8e9ec89afaa618673f0f8658682c30ff56" - integrity sha512-PXTW9PLTxdNlVYgPJ0equojcq1kNu5NtwcNjRjHAB+/sdoKZ+X8FBu70fdJFadkxFIGekQTyRvPMFF+SOJaQjw== - -"@next/swc-win32-arm64-msvc@14.1.4": - version "14.1.4" - resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.1.4.tgz#84280a08c00cc3be24ddd3a12f4617b108e6dea6" - integrity sha512-xzxF4ErcumXjO2Pvg/wVGrtr9QQJLk3IyQX1ddAC/fi6/5jZCZ9xpuL9Tzc4KPWMFq8GGWFVDMshZOdHGdkvag== - -"@next/swc-win32-arm64-msvc@15.2.2": - version "15.2.2" - resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.2.2.tgz#08f621602ed53fa21ddf6f656160b9186860be15" - integrity sha512-nG644Es5llSGEcTaXhnGWR/aThM/hIaz0jx4MDg4gWC8GfTCp8eDBWZ77CVuv2ha/uL9Ce+nPTfYkSLG67/sHg== - -"@next/swc-win32-ia32-msvc@14.1.4": - version "14.1.4" - resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.1.4.tgz#23ff7f4bd0a27177428669ef6fa5c3923c738031" - integrity sha512-WZiz8OdbkpRw6/IU/lredZWKKZopUMhcI2F+XiMAcPja0uZYdMTZQRoQ0WZcvinn9xZAidimE7tN9W5v9Yyfyw== - -"@next/swc-win32-x64-msvc@14.1.4": - version "14.1.4" - resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.1.4.tgz#bccf5beccfde66d6c66fa4e2509118c796385eda" - integrity sha512-4Rto21sPfw555sZ/XNLqfxDUNeLhNYGO2dlPqsnuCg8N8a2a9u1ltqBOPQ4vj1Gf7eJC0W2hHG2eYUHuiXgY2w== - -"@next/swc-win32-x64-msvc@15.2.2": - version "15.2.2" - resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.2.2.tgz#2072d69374f8c944134a5c5a80ce03ff84254cfa" - integrity sha512-52nWy65S/R6/kejz3jpvHAjZDPKIbEQu4x9jDBzmB9jJfuOy5rspjKu4u77+fI4M/WzLXrrQd57hlFGzz1ubcQ== - -"@nivo/annotations@0.88.0": - version "0.88.0" - resolved "https://registry.yarnpkg.com/@nivo/annotations/-/annotations-0.88.0.tgz#96830d735331dea2b60e66ce3971ef43b45cca8c" - integrity sha512-NXE+1oIUn+EGWMQpnpeRMLgi2wyuzhGDoJQY4OUHissCUiNotid2oNQ/PXJwN0toiu+/j9SyhzI32xr70OPi7Q== - dependencies: - "@nivo/colors" "0.88.0" - "@nivo/core" "0.88.0" - "@react-spring/web" "9.4.5 || ^9.7.2" - lodash "^4.17.21" - -"@nivo/axes@0.88.0": - version "0.88.0" - resolved "https://registry.yarnpkg.com/@nivo/axes/-/axes-0.88.0.tgz#b773efb217fc0faedbb5f8bc458f4b78820e2761" - integrity sha512-jF7aIxzTNayV5cI1J/b9Q1FfpMBxTXGk3OwSigXMSfYWlliskDn2u0qGRLiYhuXFdQAWIp4oXsO1GcAQ0eRVdg== - dependencies: - "@nivo/core" "0.88.0" - "@nivo/scales" "0.88.0" - "@react-spring/web" "9.4.5 || ^9.7.2" - "@types/d3-format" "^1.4.1" - "@types/d3-time-format" "^2.3.1" - d3-format "^1.4.4" - d3-time-format "^3.0.0" - -"@nivo/colors@0.88.0": - version "0.88.0" - resolved "https://registry.yarnpkg.com/@nivo/colors/-/colors-0.88.0.tgz#2790ac0607381800270f2902e4d957e65e2cad70" - integrity sha512-IZ+leYIqAlo7dyLHmsQwujanfRgXyoQ5H7PU3RWLEn1PP0zxDKLgEjFEDADpDauuslh2Tx0L81GNkWR6QSP0Mw== - dependencies: - "@nivo/core" "0.88.0" - "@types/d3-color" "^3.0.0" - "@types/d3-scale" "^4.0.8" - "@types/d3-scale-chromatic" "^3.0.0" - "@types/prop-types" "^15.7.2" - d3-color "^3.1.0" - d3-scale "^4.0.2" - d3-scale-chromatic "^3.0.0" - lodash "^4.17.21" - prop-types "^15.7.2" - -"@nivo/core@0.88.0": - version "0.88.0" - resolved "https://registry.yarnpkg.com/@nivo/core/-/core-0.88.0.tgz#ec79c7d63311473f15a463fd78274d9971a52848" - integrity sha512-XjUkA5MmwjLP38bdrJwn36Gj7T5SYMKD55LYQp/1nIJPdxqJ38dUfE4XyBDfIEgfP6yrHOihw3C63cUdnUBoiw== - dependencies: - "@nivo/tooltip" "0.88.0" - "@react-spring/web" "9.4.5 || ^9.7.2" - "@types/d3-shape" "^3.1.6" - d3-color "^3.1.0" - d3-format "^1.4.4" - d3-interpolate "^3.0.1" - d3-scale "^4.0.2" - d3-scale-chromatic "^3.0.0" - d3-shape "^3.2.0" - d3-time-format "^3.0.0" - lodash "^4.17.21" - prop-types "^15.7.2" - -"@nivo/legends@0.88.0": - version "0.88.0" - resolved "https://registry.yarnpkg.com/@nivo/legends/-/legends-0.88.0.tgz#a6007492048358a14bd061472a117ed34e7b0c81" - integrity sha512-d4DF9pHbD8LmGJlp/Gp1cF4e8y2wfQTcw3jVhbZj9zkb7ZWB7JfeF60VHRfbXNux9bjQ9U78/SssQqueVDPEmg== - dependencies: - "@nivo/colors" "0.88.0" - "@nivo/core" "0.88.0" - "@types/d3-scale" "^4.0.8" - d3-scale "^4.0.2" - -"@nivo/line@^0.88.0": - version "0.88.0" - resolved "https://registry.yarnpkg.com/@nivo/line/-/line-0.88.0.tgz#145b194f2c1bff2dd6071ab46fe114f9c4237811" - integrity sha512-hFTyZ3BdAZvq2HwdwMj2SJGUeodjEW+7DLtFMIIoVIxmjZlAs3z533HcJ9cJd3it928fDm8SF/rgHs0TztYf9Q== - dependencies: - "@nivo/annotations" "0.88.0" - "@nivo/axes" "0.88.0" - "@nivo/colors" "0.88.0" - "@nivo/core" "0.88.0" - "@nivo/legends" "0.88.0" - "@nivo/scales" "0.88.0" - "@nivo/tooltip" "0.88.0" - "@nivo/voronoi" "0.88.0" - "@react-spring/web" "9.4.5 || ^9.7.2" - d3-shape "^3.2.0" - -"@nivo/scales@0.88.0": - version "0.88.0" - resolved "https://registry.yarnpkg.com/@nivo/scales/-/scales-0.88.0.tgz#c69110b5ab504debb80a5a01a7824780af241ffb" - integrity sha512-HbpxkQp6tHCltZ1yDGeqdLcaJl5ze54NPjurfGtx/Uq+H5IQoBd4Tln49bUar5CsFAMsXw8yF1HQvASr7I1SIA== - dependencies: - "@types/d3-scale" "^4.0.8" - "@types/d3-time" "^1.1.1" - "@types/d3-time-format" "^3.0.0" - d3-scale "^4.0.2" - d3-time "^1.0.11" - d3-time-format "^3.0.0" - lodash "^4.17.21" - -"@nivo/tooltip@0.88.0": - version "0.88.0" - resolved "https://registry.yarnpkg.com/@nivo/tooltip/-/tooltip-0.88.0.tgz#b98348c0d617fea09c1bbddccc443c12ca697620" - integrity sha512-iEjVfQA8gumAzg/yUinjTwswygCkE5Iwuo8opwnrbpNIqMrleBV+EAKIgB0PrzepIoW8CFG/SJhoiRfbU8jhOw== - dependencies: - "@nivo/core" "0.88.0" - "@react-spring/web" "9.4.5 || ^9.7.2" - -"@nivo/voronoi@0.88.0": - version "0.88.0" - resolved "https://registry.yarnpkg.com/@nivo/voronoi/-/voronoi-0.88.0.tgz#029f984ccb6e72b415f398e1bd74c572960197ae" - integrity sha512-MyiNLvODthFoMjQ7Wjp693nogbTmVEx8Yn/7QkJhyPQbFyyA37TF/D1a/ox4h2OslXtP6K9QFN+42gB/zu7ixw== - dependencies: - "@nivo/core" "0.88.0" - "@nivo/tooltip" "0.88.0" - "@types/d3-delaunay" "^6.0.4" - "@types/d3-scale" "^4.0.8" - d3-delaunay "^6.0.4" - d3-scale "^4.0.2" +"@next/swc-win32-x64-msvc@14.2.21": + version "14.2.21" + resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.21.tgz#21ff892286555b90538a7d1b505ea21a005d6ead" + integrity sha512-sT6+llIkzpsexGYZq8cjjthRyRGe5cJVhqh12FmlbxHqna6zsDDK8UNaV7g41T6atFHCJUPeLb3uyAwrBwy0NA== "@noble/hashes@^1", "@noble/hashes@^1.0.0", "@noble/hashes@^1.2.0": version "1.7.1" @@ -3748,11 +3417,6 @@ resolved "https://registry.yarnpkg.com/@nymproject/contract-clients/-/contract-clients-1.2.4-rc.1.tgz#10b385f63d8b33beea6bf47d948fc7cc0c6c2357" integrity sha512-WMiEIHNxc9oTZ8GxAE8278ifpNJ2AIZHEw4JifN1WRhn2XViYaDDJaPCA3o0p+WGRySmqdurHmployr3iUEz1Q== -"@nymproject/contract-clients@^1.4.1": - version "1.4.1" - resolved "https://registry.yarnpkg.com/@nymproject/contract-clients/-/contract-clients-1.4.1.tgz#ae2644387b518eb13e8825fa8021d4c81ffe7852" - integrity sha512-HuJZ4Hv+Rl6ZZEtCHKgurNLJapM+QQRJlGkevFH2a4UdqUqF9omUkUi3AVes4679dPoSFgvA7plyVSDBdbgV6w== - "@nymproject/node-tester@^1.0.0", "@nymproject/node-tester@^1.2.3": version "1.2.3" resolved "https://registry.yarnpkg.com/@nymproject/node-tester/-/node-tester-1.2.3.tgz#79fbde8b69e2d1180eed897557c4610a1aa1038f" @@ -4649,7 +4313,7 @@ resolved "https://registry.yarnpkg.com/@react-spring/types/-/types-9.7.5.tgz#e5dd180f3ed985b44fd2cd2f32aa9203752ef3e8" integrity sha512-HVj7LrZ4ReHWBimBvu2SKND3cDVUPWKLqRTmWe/fNY6o1owGOX0cAHbdPDTMelgBlVbrTKrre6lFkhqGZErK/g== -"@react-spring/web@9.4.5 || ^9.7.2", "@react-spring/web@^9.7.5": +"@react-spring/web@^9.7.5": version "9.7.5" resolved "https://registry.yarnpkg.com/@react-spring/web/-/web-9.7.5.tgz#7d7782560b3a6fb9066b52824690da738605de80" integrity sha512-lmvqGwpe+CSttsWNZVr+Dg62adtKhauGwLyGE/RRyZ8AAMLgb9x3NDMA5RMElXo+IMyTkPp7nxTB8ZQlmhb6JQ== @@ -5196,7 +4860,7 @@ resolved "https://registry.yarnpkg.com/@rtsao/scc/-/scc-1.1.0.tgz#927dd2fae9bc3361403ac2c7a00c32ddce9ad7e8" integrity sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g== -"@rushstack/eslint-patch@^1.10.3", "@rushstack/eslint-patch@^1.3.3": +"@rushstack/eslint-patch@^1.3.3": version "1.11.0" resolved "https://registry.yarnpkg.com/@rushstack/eslint-patch/-/eslint-patch-1.11.0.tgz#75dce8e972f90bba488e2b0cc677fb233aa357ab" integrity sha512-zxnHvoMQVqewTJr/W4pKjF0bMGiKJv1WX7bSrkl46Hg0QjESbzBROWK0Wg4RphzSOS5Jiy7eFimmM3UgMrMZbQ== @@ -6345,25 +6009,26 @@ "@svgr/plugin-jsx" "^6.5.1" "@svgr/plugin-svgo" "^6.5.1" -"@swc/counter@0.1.3": +"@swc/counter@^0.1.3": version "0.1.3" resolved "https://registry.yarnpkg.com/@swc/counter/-/counter-0.1.3.tgz#cc7463bd02949611c6329596fccd2b0ec782b0e9" integrity sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ== -"@swc/helpers@0.5.15", "@swc/helpers@^0.5.0": +"@swc/helpers@0.5.5": + version "0.5.5" + resolved "https://registry.yarnpkg.com/@swc/helpers/-/helpers-0.5.5.tgz#12689df71bfc9b21c4f4ca00ae55f2f16c8b77c0" + integrity sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A== + dependencies: + "@swc/counter" "^0.1.3" + tslib "^2.4.0" + +"@swc/helpers@^0.5.0": version "0.5.15" resolved "https://registry.yarnpkg.com/@swc/helpers/-/helpers-0.5.15.tgz#79efab344c5819ecf83a43f3f9f811fc84b516d7" integrity sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g== dependencies: tslib "^2.8.0" -"@swc/helpers@0.5.2": - version "0.5.2" - resolved "https://registry.yarnpkg.com/@swc/helpers/-/helpers-0.5.2.tgz#85ea0c76450b61ad7d10a37050289eded783c27d" - integrity sha512-E4KcWTpoLHqwPHLxidpOqQbcrZVgi0rsmmZXUle1jXmJfuIf/UWpczUJ7MZZ5tlxytgJXyp0w4PGkkeLiuIdZw== - dependencies: - tslib "^2.4.0" - "@tanstack/match-sorter-utils@8.19.4": version "8.19.4" resolved "https://registry.yarnpkg.com/@tanstack/match-sorter-utils/-/match-sorter-utils-8.19.4.tgz#dacf772b5d94f4684f10dbeb2518cf72dccab8a5" @@ -6371,35 +6036,6 @@ dependencies: remove-accents "0.5.0" -"@tanstack/query-core@5.68.0": - version "5.68.0" - resolved "https://registry.yarnpkg.com/@tanstack/query-core/-/query-core-5.68.0.tgz#3765573de58741c68fb80b128d3e3ffb4d80cb68" - integrity sha512-r8rFYYo8/sY/LNaOqX84h12w7EQev4abFXDWy4UoDVUJzJ5d9Fbmb8ayTi7ScG+V0ap44SF3vNs/45mkzDGyGw== - -"@tanstack/query-devtools@5.67.2": - version "5.67.2" - resolved "https://registry.yarnpkg.com/@tanstack/query-devtools/-/query-devtools-5.67.2.tgz#890ae9913bd21d3969c7fd85c68b1bd1500cfc57" - integrity sha512-O4QXFFd7xqp6EX7sdvc9tsVO8nm4lpWBqwpgjpVLW5g7IeOY6VnS/xvs/YzbRhBVkKTMaJMOUGU7NhSX+YGoNg== - -"@tanstack/react-query-devtools@^5.64.2": - version "5.68.0" - resolved "https://registry.yarnpkg.com/@tanstack/react-query-devtools/-/react-query-devtools-5.68.0.tgz#83a76ae279796e0c2105cd760ef27e93bee79155" - integrity sha512-h9ArHkfa7SD5eGnJ9h+9M5uYWBdeVeY+WalrtGLCAtJJvHx6/RrtbbzxeoEQbPyx3f0kPcwJ58DGQ+7CBXelpg== - dependencies: - "@tanstack/query-devtools" "5.67.2" - -"@tanstack/react-query-next-experimental@^5.66.0": - version "5.68.0" - resolved "https://registry.yarnpkg.com/@tanstack/react-query-next-experimental/-/react-query-next-experimental-5.68.0.tgz#72ceeb510b0a50058d8612c5ba892b8aff1978be" - integrity sha512-nkmxjo7CbURBjDMX9MwNEj5Us8iG6ClK2fHtLUlnZJlkrIjDPPfCjp6Eh4otZjNXLPSXb92AWJWmPZSavCbM5A== - -"@tanstack/react-query@^5.64.2": - version "5.68.0" - resolved "https://registry.yarnpkg.com/@tanstack/react-query/-/react-query-5.68.0.tgz#f7b242eb800ce7c36b4edbe26c9ba8478eb18826" - integrity sha512-mMOdGDKlwTP/WV72QqSNf4PAMeoBp/DqBHQ222wBfb51Looi8QUqnCnb9O98ZgvNISmy6fzxRGBJdZ+9IBvX2Q== - dependencies: - "@tanstack/query-core" "5.68.0" - "@tanstack/react-table@8.20.5": version "8.20.5" resolved "https://registry.yarnpkg.com/@tanstack/react-table/-/react-table-8.20.5.tgz#19987d101e1ea25ef5406dce4352cab3932449d8" @@ -6407,20 +6043,6 @@ dependencies: "@tanstack/table-core" "8.20.5" -"@tanstack/react-table@8.20.6": - version "8.20.6" - resolved "https://registry.yarnpkg.com/@tanstack/react-table/-/react-table-8.20.6.tgz#a1f3103327aa59aa621931f4087a7604a21054d0" - integrity sha512-w0jluT718MrOKthRcr2xsjqzx+oEM7B7s/XXyfs19ll++hlId3fjTm+B2zrR3ijpANpkzBAr15j1XGVOMxpggQ== - dependencies: - "@tanstack/table-core" "8.20.5" - -"@tanstack/react-table@^8.20.6": - version "8.21.2" - resolved "https://registry.yarnpkg.com/@tanstack/react-table/-/react-table-8.21.2.tgz#6a7fce828b64547e33f4606ada8114db496007cc" - integrity sha512-11tNlEDTdIhMJba2RBH+ecJ9l1zgS2kjmexDPAraulc8jeNA4xocSNeyzextT0XJyASil4XsCYlJmf5jEWAtYg== - dependencies: - "@tanstack/table-core" "8.21.2" - "@tanstack/react-virtual@3.10.6": version "3.10.6" resolved "https://registry.yarnpkg.com/@tanstack/react-virtual/-/react-virtual-3.10.6.tgz#f90f97d50a8d83dcd3c3a2d425aadbb55d4837db" @@ -6428,13 +6050,6 @@ dependencies: "@tanstack/virtual-core" "3.10.6" -"@tanstack/react-virtual@3.11.2": - version "3.11.2" - resolved "https://registry.yarnpkg.com/@tanstack/react-virtual/-/react-virtual-3.11.2.tgz#d6b9bd999c181f0a2edce270c87a2febead04322" - integrity sha512-OuFzMXPF4+xZgx8UzJha0AieuMihhhaWG0tCqpp6tDzlFwOmNBPYMuLOtMJ1Tr4pXLHmgjcWhG6RlknY2oNTdQ== - dependencies: - "@tanstack/virtual-core" "3.11.2" - "@tanstack/react-virtual@^3.10.5": version "3.13.4" resolved "https://registry.yarnpkg.com/@tanstack/react-virtual/-/react-virtual-3.13.4.tgz#1188d4e452c8c7569cf42d0f605018ed67941e5c" @@ -6447,98 +6062,111 @@ resolved "https://registry.yarnpkg.com/@tanstack/table-core/-/table-core-8.20.5.tgz#3974f0b090bed11243d4107283824167a395cf1d" integrity sha512-P9dF7XbibHph2PFRz8gfBKEXEY/HJPOhym8CHmjF8y3q5mWpKx9xtZapXQUWCgkqvsK0R46Azuz+VaxD4Xl+Tg== -"@tanstack/table-core@8.21.2": - version "8.21.2" - resolved "https://registry.yarnpkg.com/@tanstack/table-core/-/table-core-8.21.2.tgz#dd57595a1773652bb6fb437e90a5f5386a49fd7e" - integrity sha512-uvXk/U4cBiFMxt+p9/G7yUWI/UbHYbyghLCjlpWZ3mLeIZiUBSKcUnw9UnKkdRz7Z/N4UBuFLWQdJCjUe7HjvA== - "@tanstack/virtual-core@3.10.6": version "3.10.6" resolved "https://registry.yarnpkg.com/@tanstack/virtual-core/-/virtual-core-3.10.6.tgz#babe3989b2344a5f12fc64129f9bbed5d3402999" integrity sha512-1giLc4dzgEKLMx5pgKjL6HlG5fjZMgCjzlKAlpr7yoUtetVPELgER1NtephAI910nMwfPTHNyWKSFmJdHkz2Cw== -"@tanstack/virtual-core@3.11.2": - version "3.11.2" - resolved "https://registry.yarnpkg.com/@tanstack/virtual-core/-/virtual-core-3.11.2.tgz#00409e743ac4eea9afe5b7708594d5fcebb00212" - integrity sha512-vTtpNt7mKCiZ1pwU9hfKPhpdVO2sVzFQsxoVBGtOSHxlrRRzYr8iQ2TlwbAcRYCcEiZ9ECAM8kBzH0v2+VzfKw== - "@tanstack/virtual-core@3.13.4": version "3.13.4" resolved "https://registry.yarnpkg.com/@tanstack/virtual-core/-/virtual-core-3.13.4.tgz#31ac7710d102ed59463f922ab088dde5a9e62010" integrity sha512-fNGO9fjjSLns87tlcto106enQQLycCKR4DPNpgq3djP5IdcPFdPAmaKjsgzIeRhH7hWrELgW12hYnRthS5kLUw== -"@tauri-apps/api@^1.2.0", "@tauri-apps/api@^1.5.1": +"@tauri-apps/api@^1.5.1": version "1.6.0" resolved "https://registry.yarnpkg.com/@tauri-apps/api/-/api-1.6.0.tgz#745b7e4e26782c3b2ad9510d558fa5bb2cf29186" integrity sha512-rqI++FWClU5I2UBp4HXFvl+sBWkdigBkxnpJDQUWttNyG7IZP4FwQGhTNL5EOw0vI8i6eSAJ5frLqO7n7jbJdg== -"@tauri-apps/cli-darwin-arm64@1.6.3": - version "1.6.3" - resolved "https://registry.yarnpkg.com/@tauri-apps/cli-darwin-arm64/-/cli-darwin-arm64-1.6.3.tgz#a204b9c686c88d774b7a67e0344cf660a0704558" - integrity sha512-fQN6IYSL8bG4NvkdKE4sAGF4dF/QqqQq4hOAU+t8ksOzHJr0hUlJYfncFeJYutr/MMkdF7hYKadSb0j5EE9r0A== +"@tauri-apps/api@^2.0.0", "@tauri-apps/api@^2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@tauri-apps/api/-/api-2.4.0.tgz#1b93976a129383ad914e979402431a63045c6329" + integrity sha512-F1zXTsmwcCp+ocg6fbzD/YL0OHeSG1eynCag1UNlX2tD5+dlXy7eRbTu9cAcscPjcR7Nix7by2wiv/+VfWUieg== -"@tauri-apps/cli-darwin-x64@1.6.3": - version "1.6.3" - resolved "https://registry.yarnpkg.com/@tauri-apps/cli-darwin-x64/-/cli-darwin-x64-1.6.3.tgz#2486f54c0b3beddf9f007b76d0b31d4da7b80e5d" - integrity sha512-1yTXZzLajKAYINJOJhZfmMhCzweHSgKQ3bEgJSn6t+1vFkOgY8Yx4oFgWcybrrWI5J1ZLZAl47+LPOY81dLcyA== +"@tauri-apps/cli-darwin-arm64@2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@tauri-apps/cli-darwin-arm64/-/cli-darwin-arm64-2.4.0.tgz#f727421a1a41538ba046faf98fb24e7703418e0b" + integrity sha512-MVzYrahJBgDyzUJ2gNEU8H+0oCVEucN115+CVorFnidHcJ6DtDRMCaKLkpjOZNfJyag1WQ25fu7imvZSe0mz/g== -"@tauri-apps/cli-linux-arm-gnueabihf@1.6.3": - version "1.6.3" - resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-arm-gnueabihf/-/cli-linux-arm-gnueabihf-1.6.3.tgz#53066f4e8292f33c1967ab5732ec9c53a0fe8531" - integrity sha512-CjTEr9r9xgjcvos09AQw8QMRPuH152B1jvlZt4PfAsyJNPFigzuwed5/SF7XAd8bFikA7zArP4UT12RdBxrx7w== +"@tauri-apps/cli-darwin-x64@2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@tauri-apps/cli-darwin-x64/-/cli-darwin-x64-2.4.0.tgz#0fd659ed5a34dc7077bcea66c4cc00808bc2f58d" + integrity sha512-/4IdbWv6IWSuBn0WVe5JkiSIP1gZhXCZRcumSsYq3ZmOlq4BqXeXT36Oig5mlDnS/2/UpNS94kd8gOA1DNdIeQ== -"@tauri-apps/cli-linux-arm64-gnu@1.6.3": - version "1.6.3" - resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-arm64-gnu/-/cli-linux-arm64-gnu-1.6.3.tgz#e204925e9f229d36cf8af17df59545ca88becfeb" - integrity sha512-G9EUUS4M8M/Jz1UKZqvJmQQCKOzgTb8/0jZKvfBuGfh5AjFBu8LHvlFpwkKVm1l4951Xg4ulUp6P9Q7WRJ9XSA== +"@tauri-apps/cli-linux-arm-gnueabihf@2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-arm-gnueabihf/-/cli-linux-arm-gnueabihf-2.4.0.tgz#b7fa0bf87184caa751d1f92db5d320586f918291" + integrity sha512-rOjlk3Vd6R847LXds4pOAFKUL5NVdSWlaiQvr4H9WDUwIWWoxnj7SQfpryTtElDb2wV7a0BNaOCXCpyFEAXjkw== -"@tauri-apps/cli-linux-arm64-musl@1.6.3": - version "1.6.3" - resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-arm64-musl/-/cli-linux-arm64-musl-1.6.3.tgz#a3753b2fa8d3c68bd8ebdd4af08d4b9a83bbf127" - integrity sha512-MuBTHJyNpZRbPVG8IZBN8+Zs7aKqwD22tkWVBcL1yOGL4zNNTJlkfL+zs5qxRnHlUsn6YAlbW/5HKocfpxVwBw== +"@tauri-apps/cli-linux-arm64-gnu@2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-arm64-gnu/-/cli-linux-arm64-gnu-2.4.0.tgz#12cea428829a635de446c6695da4b2ce7e5f5ea9" + integrity sha512-X/uCwao6R/weWT2y4f3JKJMeUsujo9H4nBMAv9RZhRsz93n9Amw9ETavAOP11pyhl57YkasvKTCRQN6FwsaoXg== -"@tauri-apps/cli-linux-x64-gnu@1.6.3": - version "1.6.3" - resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-x64-gnu/-/cli-linux-x64-gnu-1.6.3.tgz#9a22f12f310ed2af9ff46cc6251203f6d2b81aee" - integrity sha512-Uvi7M+NK3tAjCZEY1WGel+dFlzJmqcvu3KND+nqa22762NFmOuBIZ4KJR/IQHfpEYqKFNUhJfCGnpUDfiC3Oxg== +"@tauri-apps/cli-linux-arm64-musl@2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.4.0.tgz#4c9e3826c5254b2c70192f8560c5064c65e741c2" + integrity sha512-GhvQtrTjadW3eLSmfrSfybSqgJMZzUXC+0WqDzFovLug3a1a1go0m9QK9YGvYLkyEpTY5zRxLtwv+tbZXN4tZw== -"@tauri-apps/cli-linux-x64-musl@1.6.3": - version "1.6.3" - resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-x64-musl/-/cli-linux-x64-musl-1.6.3.tgz#c5b022f26c869b4877898589baf8dabf87a79096" - integrity sha512-rc6B342C0ra8VezB/OJom9j/N+9oW4VRA4qMxS2f4bHY2B/z3J9NPOe6GOILeg4v/CV62ojkLsC3/K/CeF3fqQ== +"@tauri-apps/cli-linux-riscv64-gnu@2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-riscv64-gnu/-/cli-linux-riscv64-gnu-2.4.0.tgz#39a8fed2f60cd34d55695a1583e9a8dfa33f98f0" + integrity sha512-NgeNihQ9uHS/ibMWLge5VA/BgsS/g8VPSVtCp8DSPyub3bBuCy79A8V+bzNKlMOiDVrqK4vQ//FS9kSxoJOtXw== -"@tauri-apps/cli-win32-arm64-msvc@1.6.3": - version "1.6.3" - resolved "https://registry.yarnpkg.com/@tauri-apps/cli-win32-arm64-msvc/-/cli-win32-arm64-msvc-1.6.3.tgz#5a871f96a7d58da5adacae07c848e5a0f3e82286" - integrity sha512-cSH2qOBYuYC4UVIFtrc1YsGfc5tfYrotoHrpTvRjUGu0VywvmyNk82+ZsHEnWZ2UHmu3l3lXIGRqSWveLln0xg== +"@tauri-apps/cli-linux-x64-gnu@2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-x64-gnu/-/cli-linux-x64-gnu-2.4.0.tgz#e4fc5751fe578cd14cff172791d2839bd2b5f970" + integrity sha512-ebRmV2HLIVms1KlNNueQCp3OrXBv6cimU3mYEh5NbZ8dH88f2sF46dFCyPq8Qr/Zti4qPEaAArVG7RYFjfECPw== -"@tauri-apps/cli-win32-ia32-msvc@1.6.3": - version "1.6.3" - resolved "https://registry.yarnpkg.com/@tauri-apps/cli-win32-ia32-msvc/-/cli-win32-ia32-msvc-1.6.3.tgz#cbec8e197a1cf0a63d329661ee45dcd7a31d05eb" - integrity sha512-T8V6SJQqE4PSWmYBl0ChQVmS6AR2hXFHURH2DwAhgSGSQ6uBXgwlYFcfIeQpBQA727K2Eq8X2hGfvmoySyHMRw== +"@tauri-apps/cli-linux-x64-musl@2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-x64-musl/-/cli-linux-x64-musl-2.4.0.tgz#b136eb34dbfeddf078d3a2023086da9a0b39499e" + integrity sha512-FOp2cBFyq5LnUr3he95Z99PQm3nCSJv2GZNeH7UqmUpeHwdcYuhBERU7C+8VDJJPR98Q5fkcoV00Pc4nw0v5KQ== -"@tauri-apps/cli-win32-x64-msvc@1.6.3": - version "1.6.3" - resolved "https://registry.yarnpkg.com/@tauri-apps/cli-win32-x64-msvc/-/cli-win32-x64-msvc-1.6.3.tgz#a78f5ccabbaca1d7ac3ce2acab2b1bd0276b53c4" - integrity sha512-HUkWZ+lYHI/Gjkh2QjHD/OBDpqLVmvjZGpLK9losur1Eg974Jip6k+vsoTUxQBCBDfj30eDBct9E1FvXOspWeg== +"@tauri-apps/cli-win32-arm64-msvc@2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@tauri-apps/cli-win32-arm64-msvc/-/cli-win32-arm64-msvc-2.4.0.tgz#18bba98ba67690a1fb76c540a9df01c7f220d68a" + integrity sha512-SVf1wDagYsaFM+mpUYKmjNveKodcUSGPEM27WmMW4Enh6aXGzTJi4IYOE3GEFOJF1BpRNscslwE1Rd064kfpcg== -"@tauri-apps/cli@^1.0.5": - version "1.6.3" - resolved "https://registry.yarnpkg.com/@tauri-apps/cli/-/cli-1.6.3.tgz#75e23dea0b67774fc6f150d637a0dbecf1f9592c" - integrity sha512-q46umd6QLRKDd4Gg6WyZBGa2fWvk0pbeUA5vFomm4uOs1/17LIciHv2iQ4UD+2Yv5H7AO8YiE1t50V0POiEGEw== - dependencies: - semver ">=7.5.2" +"@tauri-apps/cli-win32-ia32-msvc@2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@tauri-apps/cli-win32-ia32-msvc/-/cli-win32-ia32-msvc-2.4.0.tgz#cd5b3a1d3fda1dae254922cece455e394afaf43d" + integrity sha512-j+fOFVeSSejk9hrUePY7bJuaYr+80xr+ftjXzxCj0CS0d2oSbq+lT8/zS514WemJk9e9yxUus+2ke/Ng17wkkQ== + +"@tauri-apps/cli-win32-x64-msvc@2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@tauri-apps/cli-win32-x64-msvc/-/cli-win32-x64-msvc-2.4.0.tgz#d509a54c6cdf1436978dcf853f8ec3d903573799" + integrity sha512-nv84b3a8eI5Y7ksTLBKjjvtr9NOlAGGGo7OJbjKT+xZLdiPOZ0nJ2cT+4IdfnNAZ33pKJugAfuj1fBvQKeTy0w== + +"@tauri-apps/cli@^2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@tauri-apps/cli/-/cli-2.4.0.tgz#d1174ee62f5d982a2c6e65101c53e6763e40b2d6" + integrity sha512-Esg7s20tuSULd2YF3lmtMa1vF7yr5eh/TlBHXjEyrC+XSD9aBxHVoXb6oz7oKybDY9Jf9OiBa0bf2PbybcmOLA== optionalDependencies: - "@tauri-apps/cli-darwin-arm64" "1.6.3" - "@tauri-apps/cli-darwin-x64" "1.6.3" - "@tauri-apps/cli-linux-arm-gnueabihf" "1.6.3" - "@tauri-apps/cli-linux-arm64-gnu" "1.6.3" - "@tauri-apps/cli-linux-arm64-musl" "1.6.3" - "@tauri-apps/cli-linux-x64-gnu" "1.6.3" - "@tauri-apps/cli-linux-x64-musl" "1.6.3" - "@tauri-apps/cli-win32-arm64-msvc" "1.6.3" - "@tauri-apps/cli-win32-ia32-msvc" "1.6.3" - "@tauri-apps/cli-win32-x64-msvc" "1.6.3" + "@tauri-apps/cli-darwin-arm64" "2.4.0" + "@tauri-apps/cli-darwin-x64" "2.4.0" + "@tauri-apps/cli-linux-arm-gnueabihf" "2.4.0" + "@tauri-apps/cli-linux-arm64-gnu" "2.4.0" + "@tauri-apps/cli-linux-arm64-musl" "2.4.0" + "@tauri-apps/cli-linux-riscv64-gnu" "2.4.0" + "@tauri-apps/cli-linux-x64-gnu" "2.4.0" + "@tauri-apps/cli-linux-x64-musl" "2.4.0" + "@tauri-apps/cli-win32-arm64-msvc" "2.4.0" + "@tauri-apps/cli-win32-ia32-msvc" "2.4.0" + "@tauri-apps/cli-win32-x64-msvc" "2.4.0" + +"@tauri-apps/plugin-clipboard-manager@^2.2.2": + version "2.2.2" + resolved "https://registry.yarnpkg.com/@tauri-apps/plugin-clipboard-manager/-/plugin-clipboard-manager-2.2.2.tgz#19559f2b80dd6c692b4e7b884515b5b693ca9791" + integrity sha512-bZvDLMqfcNmsw7Ag8I49jlaCjdpDvvlJHnpp6P+Gg/3xtpSERdwlDxm7cKGbs2mj46dsw4AuG3RoAgcpwgioUA== + dependencies: + "@tauri-apps/api" "^2.0.0" + +"@tauri-apps/plugin-updater@^2.6.1": + version "2.6.1" + resolved "https://registry.yarnpkg.com/@tauri-apps/plugin-updater/-/plugin-updater-2.6.1.tgz#ce6665c1ab77fff96092283702dbe2777c3b295d" + integrity sha512-iiOevw4kc12Ok99J9KthXwUqwPv1sYjG+tNEDZqPmwvOmIq7s58nKMRz6NJPKXT4U16NzMPffFcP/LUOsz6c4A== + dependencies: + "@tauri-apps/api" "^2.0.0" "@tauri-apps/tauri-forage@^1.0.0-beta.2": version "1.0.0-beta.2" @@ -6736,7 +6364,7 @@ resolved "https://registry.yarnpkg.com/@types/d3-array/-/d3-array-3.2.1.tgz#1f6658e3d2006c4fceac53fde464166859f8b8c5" integrity sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg== -"@types/d3-color@*", "@types/d3-color@^3.0.0", "@types/d3-color@^3.1.3": +"@types/d3-color@*", "@types/d3-color@^3.1.3": version "3.1.3" resolved "https://registry.yarnpkg.com/@types/d3-color/-/d3-color-3.1.3.tgz#368c961a18de721da8200e80bf3943fb53136af2" integrity sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A== @@ -6768,11 +6396,6 @@ dependencies: "@types/d3-dsv" "*" -"@types/d3-format@^1.4.1": - version "1.4.5" - resolved "https://registry.yarnpkg.com/@types/d3-format/-/d3-format-1.4.5.tgz#6392303c2ca3c287c3a1a2046455cd0a0bd50bbe" - integrity sha512-mLxrC1MSWupOSncXN/HOlWUAAIffAEBaI4+PKy2uMPsKe4FNZlk7qrbTjmzJXITQQqBHivaks4Td18azgqnotA== - "@types/d3-geo@^1": version "1.12.7" resolved "https://registry.yarnpkg.com/@types/d3-geo/-/d3-geo-1.12.7.tgz#595680bf9b9525031cef956ed80baf38b8f3b4be" @@ -6806,11 +6429,6 @@ resolved "https://registry.yarnpkg.com/@types/d3-path/-/d3-path-3.1.1.tgz#f632b380c3aca1dba8e34aa049bcd6a4af23df8a" integrity sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg== -"@types/d3-scale-chromatic@^3.0.0": - version "3.1.0" - resolved "https://registry.yarnpkg.com/@types/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz#dc6d4f9a98376f18ea50bad6c39537f1b5463c39" - integrity sha512-iWMJgwkK7yTRmWqRB5plb1kadXyQ5Sj8V/zYlFGMUBbIPKQScw+Dku9cAAMgJG+z5GYDoMjWGLVOvjghDEFnKQ== - "@types/d3-scale@^4.0.1", "@types/d3-scale@^4.0.2", "@types/d3-scale@^4.0.8": version "4.0.9" resolved "https://registry.yarnpkg.com/@types/d3-scale/-/d3-scale-4.0.9.tgz#57a2f707242e6fe1de81ad7bfcccaaf606179afb" @@ -6830,26 +6448,11 @@ dependencies: "@types/d3-path" "*" -"@types/d3-time-format@^2.3.1": - version "2.3.4" - resolved "https://registry.yarnpkg.com/@types/d3-time-format/-/d3-time-format-2.3.4.tgz#544af5184df8b3fc4d9b42b14058789acee2905e" - integrity sha512-xdDXbpVO74EvadI3UDxjxTdR6QIxm1FKzEA/+F8tL4GWWUg/hgvBqf6chql64U5A9ZUGWo7pEu4eNlyLwbKdhg== - -"@types/d3-time-format@^3.0.0": - version "3.0.4" - resolved "https://registry.yarnpkg.com/@types/d3-time-format/-/d3-time-format-3.0.4.tgz#f972bdd7be1048184577cf235a44721a78c6bb4b" - integrity sha512-or9DiDnYI1h38J9hxKEsw513+KVuFbEVhl7qdxcaudoiqWWepapUen+2vAriFGexr6W5+P4l9+HJrB39GG+oRg== - "@types/d3-time@*", "@types/d3-time@^3.0.0", "@types/d3-time@^3.0.3": version "3.0.4" resolved "https://registry.yarnpkg.com/@types/d3-time/-/d3-time-3.0.4.tgz#8472feecd639691450dd8000eb33edd444e1323f" integrity sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g== -"@types/d3-time@^1.1.1": - version "1.1.4" - resolved "https://registry.yarnpkg.com/@types/d3-time/-/d3-time-1.1.4.tgz#20da4b75c537a940e7319b75717c67a2e499515a" - integrity sha512-JIvy2HjRInE+TXOmIGN5LCmeO0hkFZx5f9FZ7kiN+D+YTcc8pptsiLiuHsvwxwC7VVKmJ2ExHUgNlAiV7vQM9g== - "@types/d3-timer@^3.0.0": version "3.0.2" resolved "https://registry.yarnpkg.com/@types/d3-timer/-/d3-timer-3.0.2.tgz#70bbda77dc23aa727413e22e214afa3f0e852f70" @@ -6863,13 +6466,6 @@ "@types/d3-interpolate" "^1" "@types/d3-selection" "^1" -"@types/debug@^4.0.0": - version "4.1.12" - resolved "https://registry.yarnpkg.com/@types/debug/-/debug-4.1.12.tgz#a155f21690871953410df4b6b6f53187f0500917" - integrity sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ== - dependencies: - "@types/ms" "*" - "@types/eslint-scope@^3.7.7": version "3.7.7" resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.7.tgz#3108bd5f18b0cdb277c867b3dd449c9ed7079ac5" @@ -6886,13 +6482,6 @@ "@types/estree" "*" "@types/json-schema" "*" -"@types/estree-jsx@^1.0.0": - version "1.0.5" - resolved "https://registry.yarnpkg.com/@types/estree-jsx/-/estree-jsx-1.0.5.tgz#858a88ea20f34fe65111f005a689fa1ebf70dc18" - integrity sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg== - dependencies: - "@types/estree" "*" - "@types/estree@*", "@types/estree@^1.0.0", "@types/estree@^1.0.6": version "1.0.6" resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.6.tgz#628effeeae2064a1b4e79f78e81d87b7e5fc7b50" @@ -6990,13 +6579,6 @@ dependencies: "@types/unist" "^2" -"@types/hast@^3.0.0": - version "3.0.4" - resolved "https://registry.yarnpkg.com/@types/hast/-/hast-3.0.4.tgz#1d6b39993b82cea6ad783945b0508c25903e15aa" - integrity sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ== - dependencies: - "@types/unist" "*" - "@types/html-minifier-terser@^5.0.0": version "5.1.2" resolved "https://registry.yarnpkg.com/@types/html-minifier-terser/-/html-minifier-terser-5.1.2.tgz#693b316ad323ea97eed6b38ed1a3cc02b1672b57" @@ -7086,13 +6668,6 @@ dependencies: "@types/unist" "^2" -"@types/mdast@^4.0.0": - version "4.0.4" - resolved "https://registry.yarnpkg.com/@types/mdast/-/mdast-4.0.4.tgz#7ccf72edd2f1aa7dd3437e180c64373585804dd6" - integrity sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA== - dependencies: - "@types/unist" "*" - "@types/mime@^1": version "1.3.5" resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.5.tgz#1ef302e01cf7d2b5a0fa526790c9123bf1d06690" @@ -7113,11 +6688,6 @@ resolved "https://registry.yarnpkg.com/@types/minimist/-/minimist-1.2.5.tgz#ec10755e871497bcd83efe927e43ec46e8c0747e" integrity sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag== -"@types/ms@*": - version "2.1.0" - resolved "https://registry.yarnpkg.com/@types/ms/-/ms-2.1.0.tgz#052aa67a48eccc4309d7f0191b7e41434b90bb78" - integrity sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA== - "@types/node-fetch@^2.5.7": version "2.6.12" resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.6.12.tgz#8ab5c3ef8330f13100a7479e2cd56d3386830a03" @@ -7194,7 +6764,7 @@ resolved "https://registry.yarnpkg.com/@types/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz#ee1bd8c9f7a01b3445786aad0ef23aba5f511a44" integrity sha512-nj39q0wAIdhwn7DGUyT9irmsKK1tV0bd5WFEhgpqNTMFZ8cE+jieuTphCW0tfdm47S2zVT5mr09B28b1chmQMA== -"@types/prop-types@*", "@types/prop-types@^15.7.12", "@types/prop-types@^15.7.14", "@types/prop-types@^15.7.2": +"@types/prop-types@*", "@types/prop-types@^15.7.12", "@types/prop-types@^15.7.14": version "15.7.14" resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.14.tgz#1433419d73b2a7ebfc6918dcefd2ec0d5cd698f2" integrity sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ== @@ -7206,7 +6776,7 @@ dependencies: "@types/react" "*" -"@types/qs@*", "@types/qs@^6.9.18", "@types/qs@^6.9.5": +"@types/qs@*", "@types/qs@^6.9.5": version "6.9.18" resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.18.tgz#877292caa91f7c1b213032b34626505b746624c2" integrity sha512-kK7dgTYDyGqS+e2Q4aK9X3D7q234CIZ1Bv0q/7Z5IwRDoADNU81xXJK/YVyLbLTZCoIwUoDoffFeF+p/eIklAA== @@ -7243,7 +6813,7 @@ dependencies: react-tooltip "*" -"@types/react-transition-group@^4.4.10", "@types/react-transition-group@^4.4.11", "@types/react-transition-group@^4.4.12": +"@types/react-transition-group@^4.4.10", "@types/react-transition-group@^4.4.11": version "4.4.12" resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-4.4.12.tgz#b5d76568485b02a307238270bfe96cb51ee2a044" integrity sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w== @@ -7346,11 +6916,6 @@ dependencies: "@types/geojson" "*" -"@types/trusted-types@^2.0.7": - version "2.0.7" - resolved "https://registry.yarnpkg.com/@types/trusted-types/-/trusted-types-2.0.7.tgz#baccb07a970b91707df3a3e8ba6896c57ead2d11" - integrity sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw== - "@types/uglify-js@*": version "3.17.5" resolved "https://registry.yarnpkg.com/@types/uglify-js/-/uglify-js-3.17.5.tgz#905ce03a3cbbf2e31cbefcbc68d15497ee2e17df" @@ -7358,11 +6923,6 @@ dependencies: source-map "^0.6.1" -"@types/unist@*", "@types/unist@^3.0.0": - version "3.0.3" - resolved "https://registry.yarnpkg.com/@types/unist/-/unist-3.0.3.tgz#acaab0f919ce69cce629c2d4ed2eb4adc1b6c20c" - integrity sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q== - "@types/unist@^2", "@types/unist@^2.0.0", "@types/unist@^2.0.2", "@types/unist@^2.0.3": version "2.0.11" resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.11.tgz#11af57b127e32487774841f7a4e54eab166d03c4" @@ -7453,21 +7013,6 @@ semver "^7.3.7" tsutils "^3.21.0" -"@typescript-eslint/eslint-plugin@^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0": - version "8.26.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.26.1.tgz#3e48eb847924161843b092c87a9b65176b53782f" - integrity sha512-2X3mwqsj9Bd3Ciz508ZUtoQQYpOhU/kWoUqIf49H8Z0+Vbh6UF/y0OEYp0Q0axOGzaBGs7QxRwq0knSQ8khQNA== - dependencies: - "@eslint-community/regexpp" "^4.10.0" - "@typescript-eslint/scope-manager" "8.26.1" - "@typescript-eslint/type-utils" "8.26.1" - "@typescript-eslint/utils" "8.26.1" - "@typescript-eslint/visitor-keys" "8.26.1" - graphemer "^1.4.0" - ignore "^5.3.1" - natural-compare "^1.4.0" - ts-api-utils "^2.0.1" - "@typescript-eslint/experimental-utils@^5.3.0": version "5.62.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-5.62.0.tgz#14559bf73383a308026b427a4a6129bae2146741" @@ -7496,17 +7041,6 @@ "@typescript-eslint/visitor-keys" "6.21.0" debug "^4.3.4" -"@typescript-eslint/parser@^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0": - version "8.26.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.26.1.tgz#0e2f915a497519fc43f52cf2ecbfa607ff56f72e" - integrity sha512-w6HZUV4NWxqd8BdeFf81t07d7/YV9s7TCWrQQbG5uhuvGUAW+fq1usZ1Hmz9UPNLniFnD8GLSsDpjP0hm1S4lQ== - dependencies: - "@typescript-eslint/scope-manager" "8.26.1" - "@typescript-eslint/types" "8.26.1" - "@typescript-eslint/typescript-estree" "8.26.1" - "@typescript-eslint/visitor-keys" "8.26.1" - debug "^4.3.4" - "@typescript-eslint/scope-manager@5.62.0": version "5.62.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz#d9457ccc6a0b8d6b37d0eb252a23022478c5460c" @@ -7523,14 +7057,6 @@ "@typescript-eslint/types" "6.21.0" "@typescript-eslint/visitor-keys" "6.21.0" -"@typescript-eslint/scope-manager@8.26.1": - version "8.26.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.26.1.tgz#5e6ad0ac258ccf79462e91c3f43a3f1f7f31a6cc" - integrity sha512-6EIvbE5cNER8sqBu6V7+KeMZIC1664d2Yjt+B9EWUXrsyWpxx4lEZrmvxgSKRC6gX+efDL/UY9OpPZ267io3mg== - dependencies: - "@typescript-eslint/types" "8.26.1" - "@typescript-eslint/visitor-keys" "8.26.1" - "@typescript-eslint/type-utils@5.62.0": version "5.62.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.62.0.tgz#286f0389c41681376cdad96b309cedd17d70346a" @@ -7541,16 +7067,6 @@ debug "^4.3.4" tsutils "^3.21.0" -"@typescript-eslint/type-utils@8.26.1": - version "8.26.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.26.1.tgz#462f0bae09de72ac6e8e1af2ebe588c23224d7f8" - integrity sha512-Kcj/TagJLwoY/5w9JGEFV0dclQdyqw9+VMndxOJKtoFSjfZhLXhYjzsQEeyza03rwHx2vFEGvrJWJBXKleRvZg== - dependencies: - "@typescript-eslint/typescript-estree" "8.26.1" - "@typescript-eslint/utils" "8.26.1" - debug "^4.3.4" - ts-api-utils "^2.0.1" - "@typescript-eslint/types@5.62.0": version "5.62.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.62.0.tgz#258607e60effa309f067608931c3df6fed41fd2f" @@ -7561,11 +7077,6 @@ resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-6.21.0.tgz#205724c5123a8fef7ecd195075fa6e85bac3436d" integrity sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg== -"@typescript-eslint/types@8.26.1": - version "8.26.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.26.1.tgz#d5978721670cff263348d5062773389231a64132" - integrity sha512-n4THUQW27VmQMx+3P+B0Yptl7ydfceUj4ON/AQILAASwgYdZ/2dhfymRMh5egRUrvK5lSmaOm77Ry+lmXPOgBQ== - "@typescript-eslint/typescript-estree@5.62.0": version "5.62.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz#7d17794b77fabcac615d6a48fb143330d962eb9b" @@ -7593,20 +7104,6 @@ semver "^7.5.4" ts-api-utils "^1.0.1" -"@typescript-eslint/typescript-estree@8.26.1": - version "8.26.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.26.1.tgz#eb0e4ce31753683d83be53441a409fd5f0b34afd" - integrity sha512-yUwPpUHDgdrv1QJ7YQal3cMVBGWfnuCdKbXw1yyjArax3353rEJP1ZA+4F8nOlQ3RfS2hUN/wze3nlY+ZOhvoA== - dependencies: - "@typescript-eslint/types" "8.26.1" - "@typescript-eslint/visitor-keys" "8.26.1" - debug "^4.3.4" - fast-glob "^3.3.2" - is-glob "^4.0.3" - minimatch "^9.0.4" - semver "^7.6.0" - ts-api-utils "^2.0.1" - "@typescript-eslint/utils@5.62.0", "@typescript-eslint/utils@^5.10.0": version "5.62.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.62.0.tgz#141e809c71636e4a75daa39faed2fb5f4b10df86" @@ -7621,16 +7118,6 @@ eslint-scope "^5.1.1" semver "^7.3.7" -"@typescript-eslint/utils@8.26.1": - version "8.26.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.26.1.tgz#54cc58469955f25577f659753b71a0e117a0539f" - integrity sha512-V4Urxa/XtSUroUrnI7q6yUTD3hDtfJ2jzVfeT3VK0ciizfK2q/zGC0iDh1lFMUZR8cImRrep6/q0xd/1ZGPQpg== - dependencies: - "@eslint-community/eslint-utils" "^4.4.0" - "@typescript-eslint/scope-manager" "8.26.1" - "@typescript-eslint/types" "8.26.1" - "@typescript-eslint/typescript-estree" "8.26.1" - "@typescript-eslint/visitor-keys@5.62.0": version "5.62.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz#2174011917ce582875954ffe2f6912d5931e353e" @@ -7647,20 +7134,7 @@ "@typescript-eslint/types" "6.21.0" eslint-visitor-keys "^3.4.1" -"@typescript-eslint/visitor-keys@8.26.1": - version "8.26.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.26.1.tgz#c5267fcc82795cf10280363023837deacad2647c" - integrity sha512-AjOC3zfnxd6S4Eiy3jwktJPclqhFHNyd8L6Gycf9WUPoKZpgM5PjkxY1X7uSy61xVpiJDhhk7XT2NVsN3ALTWg== - dependencies: - "@typescript-eslint/types" "8.26.1" - eslint-visitor-keys "^4.2.0" - -"@uidotdev/usehooks@^2.4.1": - version "2.4.1" - resolved "https://registry.yarnpkg.com/@uidotdev/usehooks/-/usehooks-2.4.1.tgz#4b733eaeae09a7be143c6c9ca158b56cc1ea75bf" - integrity sha512-1I+RwWyS+kdv3Mv0Vmc+p0dPYH0DTRAo04HLyXReYBL9AeseDWUJyi4THuksBJcu9F0Pih69Ak150VDnqbVnXg== - -"@ungap/structured-clone@^1.0.0", "@ungap/structured-clone@^1.2.0": +"@ungap/structured-clone@^1.2.0": version "1.3.0" resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.3.0.tgz#d06bbb384ebcf6c505fde1c3d0ed4ddffe0aaff8" integrity sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g== @@ -8242,11 +7716,6 @@ agent-base@6, agent-base@^6.0.2: dependencies: debug "4" -agent-base@^7.1.0, agent-base@^7.1.2: - version "7.1.3" - resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.3.tgz#29435eb821bc4194633a5b89e5bc4703bafc25a1" - integrity sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw== - agentkeepalive@^4.2.1: version "4.6.0" resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.6.0.tgz#35f73e94b3f40bf65f105219c623ad19c136ea6a" @@ -8966,11 +8435,6 @@ bail@^1.0.0: resolved "https://registry.yarnpkg.com/bail/-/bail-1.0.5.tgz#b6fa133404a392cbc1f8c4bf63f5953351e7a776" integrity sha512-xFbRxM1tahm08yHBP16MMjVUAvDaBMD38zsM9EMAUN61omwLmKlOpB/Zku5QkjZ8TZ4vn53pj+t518cH0S03RQ== -bail@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/bail/-/bail-2.0.2.tgz#d26f5cd8fe5d6f832a31517b9f7c356040ba6d5d" - integrity sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw== - balanced-match@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" @@ -9641,12 +9105,7 @@ ccount@^1.0.0: resolved "https://registry.yarnpkg.com/ccount/-/ccount-1.1.0.tgz#246687debb6014735131be8abab2d93898f8d043" integrity sha512-vlNK021QdI7PNeiUh/lKkC/mNHHfV0m/Ad5JoI0TYtlBnJAslM/JIkm/tGC88bkLIwO6OQ5uV6ztS6kVAtCDlg== -ccount@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/ccount/-/ccount-2.0.1.tgz#17a3bf82302e0870d6da43a01311a8bc02a3ecf5" - integrity sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg== - -chain-registry@^1.29.1, chain-registry@^1.69.64: +chain-registry@^1.29.1: version "1.69.155" resolved "https://registry.yarnpkg.com/chain-registry/-/chain-registry-1.69.155.tgz#72077fba2a0e39cf241854aca6745f585261d032" integrity sha512-d6bQWd7xSmp31pNMSam1bApqO7oCjLeoodipRLQNJL1C9svNquAv+MseNI9O5rf+cpb9kIpvrqa6NaAOt0i8WQ== @@ -9691,41 +9150,21 @@ char-regex@^1.0.2: resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf" integrity sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw== -character-entities-html4@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/character-entities-html4/-/character-entities-html4-2.1.0.tgz#1f1adb940c971a4b22ba39ddca6b618dc6e56b2b" - integrity sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA== - character-entities-legacy@^1.0.0: version "1.1.4" resolved "https://registry.yarnpkg.com/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz#94bc1845dce70a5bb9d2ecc748725661293d8fc1" integrity sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA== -character-entities-legacy@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz#76bc83a90738901d7bc223a9e93759fdd560125b" - integrity sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ== - character-entities@^1.0.0: version "1.2.4" resolved "https://registry.yarnpkg.com/character-entities/-/character-entities-1.2.4.tgz#e12c3939b7eaf4e5b15e7ad4c5e28e1d48c5b16b" integrity sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw== -character-entities@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/character-entities/-/character-entities-2.0.2.tgz#2d09c2e72cd9523076ccb21157dff66ad43fcc22" - integrity sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ== - character-reference-invalid@^1.0.0: version "1.1.4" resolved "https://registry.yarnpkg.com/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz#083329cda0eae272ab3dbbf37e9a382c13af1560" integrity sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg== -character-reference-invalid@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz#85c66b041e43b47210faf401278abf808ac45cb9" - integrity sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw== - chardet@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" @@ -9825,11 +9264,6 @@ classnames@^2.3.0: resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.5.1.tgz#ba774c614be0f016da105c858e7159eae8e7687b" integrity sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow== -cldr-compact-number@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/cldr-compact-number/-/cldr-compact-number-0.4.0.tgz#d74bb8cedab92ca63032423a1bd26aaaf76b9b2c" - integrity sha512-f/Wz46qoQrfhF2Mq4TcsSbIXMaB70LhioR4fJAbYi5nTnczh1LnSsWuBOXuyKTwFsyrMCRnPFr0OHjl0XB6y9A== - clean-css@^4.2.3: version "4.2.4" resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-4.2.4.tgz#733bf46eba4e607c6891ea57c24a989356831178" @@ -10062,11 +9496,6 @@ comma-separated-tokens@^1.0.0: resolved "https://registry.yarnpkg.com/comma-separated-tokens/-/comma-separated-tokens-1.0.8.tgz#632b80b6117867a158f1080ad498b2fbe7e3f5ea" integrity sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw== -comma-separated-tokens@^2.0.0: - version "2.0.3" - resolved "https://registry.yarnpkg.com/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz#4e89c9458acb61bc8fef19f4529973b2392839ee" - integrity sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg== - commander@2, commander@^2.19.0, commander@^2.20.0: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" @@ -10589,17 +10018,6 @@ css-select@^4.1.3: domutils "^2.8.0" nth-check "^2.0.1" -css-select@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/css-select/-/css-select-5.1.0.tgz#b8ebd6554c3637ccc76688804ad3f6a6fdaea8a6" - integrity sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg== - dependencies: - boolbase "^1.0.0" - css-what "^6.1.0" - domhandler "^5.0.2" - domutils "^3.0.1" - nth-check "^2.0.1" - css-tree@^1.1.2, css-tree@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.1.3.tgz#eb4870fb6fd7707327ec95c2ff2ab09b5e8db91d" @@ -10608,22 +10026,6 @@ css-tree@^1.1.2, css-tree@^1.1.3: mdn-data "2.0.14" source-map "^0.6.1" -css-tree@^2.3.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-2.3.1.tgz#10264ce1e5442e8572fc82fbe490644ff54b5c20" - integrity sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw== - dependencies: - mdn-data "2.0.30" - source-map-js "^1.0.1" - -css-tree@~2.2.0: - version "2.2.1" - resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-2.2.1.tgz#36115d382d60afd271e377f9c5f67d02bd48c032" - integrity sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA== - dependencies: - mdn-data "2.0.28" - source-map-js "^1.0.1" - css-vendor@^2.0.8: version "2.0.8" resolved "https://registry.yarnpkg.com/css-vendor/-/css-vendor-2.0.8.tgz#e47f91d3bd3117d49180a3c935e62e3d9f7f449d" @@ -10703,13 +10105,6 @@ csso@^4.2.0: dependencies: css-tree "^1.1.2" -csso@^5.0.5: - version "5.0.5" - resolved "https://registry.yarnpkg.com/csso/-/csso-5.0.5.tgz#f9b7fe6cc6ac0b7d90781bb16d5e9874303e2ca6" - integrity sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ== - dependencies: - css-tree "~2.2.0" - cssom@^0.4.4: version "0.4.4" resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.4.4.tgz#5a66cf93d2d0b661d80bf6a44fb65f5c2e4e0a10" @@ -10727,14 +10122,6 @@ cssstyle@^2.3.0: dependencies: cssom "~0.3.6" -cssstyle@^4.2.1: - version "4.3.0" - resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-4.3.0.tgz#83db22d1aec8eb7e5ecd812b4d14a17fb3dd243d" - integrity sha512-6r0NiY0xizYqfBvWp1G7WXJ06/bZyrk7Dc6PHql82C/pKGUTKu4yAX4Y8JPamb1ob9nBKuxWzCGTRuGwU3yxJQ== - dependencies: - "@asamuzakjp/css-color" "^3.1.1" - rrweb-cssom "^0.8.0" - csstype@^3.0.2, csstype@^3.0.7, csstype@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.3.tgz#d80ff294d114fb0e6ac500fbf85b60137d7eff81" @@ -10752,13 +10139,6 @@ cyclist@^1.0.1: resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-1.0.2.tgz#673b5f233bf34d8e602b949429f8171d9121bea3" integrity sha512-0sVXIohTfLqVIW3kb/0n6IiWF3Ifj5nm2XaSrLq2DI6fKIGa2fYAZdk917rUneaeLVpYfFcyXE2ft0fe3remsA== -d3-array@2, d3-array@^2.5.0: - version "2.12.1" - resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-2.12.1.tgz#e20b41aafcdffdf5d50928004ececf815a465e81" - integrity sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ== - dependencies: - internmap "^1.0.0" - "d3-array@2 - 3", "d3-array@2.10.0 - 3", d3-array@^3.1.6: version "3.2.4" resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-3.2.4.tgz#15fec33b237f97ac5d7c986dc77da273a8ed0bb5" @@ -10766,6 +10146,13 @@ d3-array@2, d3-array@^2.5.0: dependencies: internmap "1 - 2" +d3-array@^2.5.0: + version "2.12.1" + resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-2.12.1.tgz#e20b41aafcdffdf5d50928004ececf815a465e81" + integrity sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ== + dependencies: + internmap "^1.0.0" + "d3-color@1 - 2": version "2.0.0" resolved "https://registry.yarnpkg.com/d3-color/-/d3-color-2.0.0.tgz#8d625cab42ed9b8f601a1760a389f7ea9189d62e" @@ -10811,11 +10198,6 @@ d3-ease@^3.0.1: resolved "https://registry.yarnpkg.com/d3-format/-/d3-format-3.1.0.tgz#9260e23a28ea5cb109e93b21a06e24e2ebd55641" integrity sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA== -d3-format@^1.4.4: - version "1.4.5" - resolved "https://registry.yarnpkg.com/d3-format/-/d3-format-1.4.5.tgz#374f2ba1320e3717eb74a9356c67daee17a7edb4" - integrity sha512-J0piedu6Z8iB6TbIGfZgDzfXxUFN3qQRMofy2oPdXzQibYGqPB/9iMcxr/TGalU+2RsyDO+U4f33id8tbnSRMQ== - d3-geo@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/d3-geo/-/d3-geo-2.0.2.tgz#c065c1b71fe8c5f1be657e5f43d9bdd010383c40" @@ -10830,7 +10212,7 @@ d3-geo@^2.0.1: dependencies: d3-color "1 - 2" -"d3-interpolate@1 - 3", "d3-interpolate@1.2.0 - 3", d3-interpolate@^3.0.1: +"d3-interpolate@1.2.0 - 3", d3-interpolate@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/d3-interpolate/-/d3-interpolate-3.0.1.tgz#3c47aa5b32c5b3dfb56ef3fd4342078a632b400d" integrity sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g== @@ -10842,14 +10224,6 @@ d3-path@^3.1.0: resolved "https://registry.yarnpkg.com/d3-path/-/d3-path-3.1.0.tgz#22df939032fb5a71ae8b1800d61ddb7851c42526" integrity sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ== -d3-scale-chromatic@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz#34c39da298b23c20e02f1a4b239bd0f22e7f1314" - integrity sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ== - dependencies: - d3-color "1 - 3" - d3-interpolate "1 - 3" - d3-scale@^4.0.0, d3-scale@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/d3-scale/-/d3-scale-4.0.2.tgz#82b38e8e8ff7080764f8dcec77bd4be393689396" @@ -10880,20 +10254,6 @@ d3-shape@^3.1.0, d3-shape@^3.2.0: dependencies: d3-time "1 - 3" -d3-time-format@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/d3-time-format/-/d3-time-format-3.0.0.tgz#df8056c83659e01f20ac5da5fdeae7c08d5f1bb6" - integrity sha512-UXJh6EKsHBTjopVqZBhFysQcoXSv/5yLONZvkQ5Kk3qbwiUYkdX17Xa1PT6U1ZWXGGfB1ey5L8dKMlFq2DO0Ag== - dependencies: - d3-time "1 - 2" - -"d3-time@1 - 2": - version "2.1.1" - resolved "https://registry.yarnpkg.com/d3-time/-/d3-time-2.1.1.tgz#e9d8a8a88691f4548e68ca085e5ff956724a6682" - integrity sha512-/eIQe/eR4kCQwq7yxi7z4c6qEXf2IYGcjoWB5OOQy4Tq9Uv39/947qlDcN2TLkiTzQWzvnsuYPB9TrWaNfipKQ== - dependencies: - d3-array "2" - "d3-time@1 - 3", "d3-time@2.1.1 - 3", d3-time@^3.0.0, d3-time@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/d3-time/-/d3-time-3.1.0.tgz#9310db56e992e3c0175e1ef385e545e48a9bb5c7" @@ -10901,11 +10261,6 @@ d3-time-format@^3.0.0: dependencies: d3-array "2 - 3" -d3-time@^1.0.11: - version "1.1.0" - resolved "https://registry.yarnpkg.com/d3-time/-/d3-time-1.1.0.tgz#b1e19d307dae9c900b7e5b25ffc5dcc249a8a0f1" - integrity sha512-Xh0isrZ5rPYYdqhAVk8VLnMEidhz5aP7htAADH6MfzgmmicPkTo8LhkLxci61/lCB7n7UmE3bN0leRt+qvkLxA== - "d3-timer@1 - 2": version "2.0.0" resolved "https://registry.yarnpkg.com/d3-timer/-/d3-timer-2.0.0.tgz#055edb1d170cfe31ab2da8968deee940b56623e6" @@ -10957,14 +10312,6 @@ data-urls@^2.0.0: whatwg-mimetype "^2.3.0" whatwg-url "^8.0.0" -data-urls@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-5.0.0.tgz#2f76906bce1824429ffecb6920f45a0b30f00dde" - integrity sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg== - dependencies: - whatwg-mimetype "^4.0.0" - whatwg-url "^14.0.0" - data-view-buffer@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/data-view-buffer/-/data-view-buffer-1.0.2.tgz#211a03ba95ecaf7798a8c7198d79536211f88570" @@ -10999,11 +10346,6 @@ date-fns@^2.24.0, date-fns@^2.28.0: dependencies: "@babel/runtime" "^7.21.0" -date-fns@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-4.1.0.tgz#64b3d83fff5aa80438f5b1a633c2e83b8a1c2d14" - integrity sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg== - dateformat@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae" @@ -11016,7 +10358,7 @@ debug@2.6.9, debug@^2.2.0, debug@^2.3.3: dependencies: ms "2.0.0" -debug@4, debug@^4.0.0, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4, debug@^4.3.5, debug@^4.4.0: +debug@4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4, debug@^4.3.5, debug@^4.4.0: version "4.4.0" resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.0.tgz#2b3f2aea2ffeb776477460267377dc8710faba8a" integrity sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA== @@ -11053,18 +10395,11 @@ decimal.js-light@^2.4.1: resolved "https://registry.yarnpkg.com/decimal.js-light/-/decimal.js-light-2.5.1.tgz#134fd32508f19e208f4fb2f8dac0d2626a867934" integrity sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg== -decimal.js@10, decimal.js@^10.2.1, decimal.js@^10.4.3: +decimal.js@10, decimal.js@^10.2.1: version "10.5.0" resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.5.0.tgz#0f371c7cf6c4898ce0afb09836db73cd82010f22" integrity sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw== -decode-named-character-reference@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/decode-named-character-reference/-/decode-named-character-reference-1.1.0.tgz#5d6ce68792808901210dac42a8e9853511e2b8bf" - integrity sha512-Wy+JTSbFThEOXQIR2L6mxJvEs+veIzpmqD7ynWxMXGpnk3smkHQOp6forLdHsKpAMW9iJpaBBIxz285t1n1C3w== - dependencies: - character-entities "^2.0.0" - decode-uri-component@^0.2.0: version "0.2.2" resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.2.tgz#e69dbe25d37941171dd540e024c444cd5188e1e9" @@ -11242,11 +10577,6 @@ deprecation@^2.0.0: resolved "https://registry.yarnpkg.com/deprecation/-/deprecation-2.3.1.tgz#6368cbdb40abf3373b525ac87e4a260c3a700919" integrity sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ== -dequal@^2.0.0: - version "2.0.3" - resolved "https://registry.yarnpkg.com/dequal/-/dequal-2.0.3.tgz#2644214f1997d39ed0ee0ece72335490a7ac67be" - integrity sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA== - des.js@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.1.0.tgz#1d37f5766f3bbff4ee9638e871a8768c173b81da" @@ -11307,13 +10637,6 @@ detect-port@^1.3.0: address "^1.0.1" debug "4" -devlop@^1.0.0, devlop@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/devlop/-/devlop-1.1.0.tgz#4db7c2ca4dc6e0e834c30be70c94bbc976dc7018" - integrity sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA== - dependencies: - dequal "^2.0.0" - diacritics@1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/diacritics/-/diacritics-1.3.0.tgz#3efa87323ebb863e6696cebb0082d48ff3d6f7a1" @@ -11422,15 +10745,6 @@ dom-serializer@^1.0.1: domhandler "^4.2.0" entities "^2.0.0" -dom-serializer@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-2.0.0.tgz#e41b802e1eedf9f6cae183ce5e622d789d7d8e53" - integrity sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg== - dependencies: - domelementtype "^2.3.0" - domhandler "^5.0.2" - entities "^4.2.0" - dom-walk@^0.1.0: version "0.1.2" resolved "https://registry.yarnpkg.com/dom-walk/-/dom-walk-0.1.2.tgz#0c548bef048f4d1f2a97249002236060daa3fd84" @@ -11441,7 +10755,7 @@ domain-browser@^1.1.1: resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.2.0.tgz#3d31f50191a6749dd1375a7f522e823d42e54eda" integrity sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA== -domelementtype@^2.0.1, domelementtype@^2.2.0, domelementtype@^2.3.0: +domelementtype@^2.0.1, domelementtype@^2.2.0: version "2.3.0" resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.3.0.tgz#5c45e8e869952626331d7aab326d01daf65d589d" integrity sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw== @@ -11460,20 +10774,6 @@ domhandler@^4.0.0, domhandler@^4.2.0, domhandler@^4.3.1: dependencies: domelementtype "^2.2.0" -domhandler@^5.0.2, domhandler@^5.0.3: - version "5.0.3" - resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-5.0.3.tgz#cc385f7f751f1d1fc650c21374804254538c7d31" - integrity sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w== - dependencies: - domelementtype "^2.3.0" - -dompurify@^3.2.4: - version "3.2.4" - resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-3.2.4.tgz#af5a5a11407524431456cf18836c55d13441cd8e" - integrity sha512-ysFSFEDVduQpyhzAob/kkuJjf5zWkZD8/A9ywSp1byueyuCfHamrCBa14/Oc2iiB0e51B+NpxSl5gmzn+Ms/mg== - optionalDependencies: - "@types/trusted-types" "^2.0.7" - domutils@^2.5.2, domutils@^2.8.0: version "2.8.0" resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.8.0.tgz#4437def5db6e2d1f5d6ee859bd95ca7d02048135" @@ -11483,15 +10783,6 @@ domutils@^2.5.2, domutils@^2.8.0: domelementtype "^2.2.0" domhandler "^4.2.0" -domutils@^3.0.1: - version "3.2.2" - resolved "https://registry.yarnpkg.com/domutils/-/domutils-3.2.2.tgz#edbfe2b668b0c1d97c24baf0f1062b132221bc78" - integrity sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw== - dependencies: - dom-serializer "^2.0.0" - domelementtype "^2.3.0" - domhandler "^5.0.3" - dot-case@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/dot-case/-/dot-case-3.0.4.tgz#9b2b670d00a431667a8a75ba29cd1b98809ce751" @@ -11705,7 +10996,7 @@ entities@^2.0.0: resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55" integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A== -entities@^4.2.0, entities@^4.4.0, entities@^4.5.0: +entities@^4.4.0: version "4.5.0" resolved "https://registry.yarnpkg.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48" integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw== @@ -11985,22 +11276,6 @@ eslint-config-next@14.1.4: eslint-plugin-react "^7.33.2" eslint-plugin-react-hooks "^4.5.0 || 5.0.0-canary-7118f5dd7-20230705" -eslint-config-next@15.0.3: - version "15.0.3" - resolved "https://registry.yarnpkg.com/eslint-config-next/-/eslint-config-next-15.0.3.tgz#b483585260d5e55050d4ab87e053c88089ae12ee" - integrity sha512-IGP2DdQQrgjcr4mwFPve4DrCqo7CVVez1WoYY47XwKSrYO4hC0Dlb+iJA60i0YfICOzgNADIb8r28BpQ5Zs0wg== - dependencies: - "@next/eslint-plugin-next" "15.0.3" - "@rushstack/eslint-patch" "^1.10.3" - "@typescript-eslint/eslint-plugin" "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0" - "@typescript-eslint/parser" "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0" - eslint-import-resolver-node "^0.3.6" - eslint-import-resolver-typescript "^3.5.2" - eslint-plugin-import "^2.31.0" - eslint-plugin-jsx-a11y "^6.10.0" - eslint-plugin-react "^7.35.0" - eslint-plugin-react-hooks "^5.0.0" - eslint-config-prettier@^8.3.0, eslint-config-prettier@^8.5.0: version "8.10.0" resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.10.0.tgz#3a06a662130807e2502fc3ff8b4143d8a0658e11" @@ -12043,7 +11318,7 @@ eslint-module-utils@^2.12.0: dependencies: debug "^3.2.7" -eslint-plugin-import@^2.25.4, eslint-plugin-import@^2.28.1, eslint-plugin-import@^2.31.0: +eslint-plugin-import@^2.25.4, eslint-plugin-import@^2.28.1: version "2.31.0" resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.31.0.tgz#310ce7e720ca1d9c0bb3f69adfd1c6bdd7d9e0e7" integrity sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A== @@ -12075,7 +11350,7 @@ eslint-plugin-jest@^26.1.1: dependencies: "@typescript-eslint/utils" "^5.10.0" -eslint-plugin-jsx-a11y@^6.10.0, eslint-plugin-jsx-a11y@^6.5.1, eslint-plugin-jsx-a11y@^6.7.1: +eslint-plugin-jsx-a11y@^6.5.1, eslint-plugin-jsx-a11y@^6.7.1: version "6.10.2" resolved "https://registry.yarnpkg.com/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.10.2.tgz#d2812bb23bf1ab4665f1718ea442e8372e638483" integrity sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q== @@ -12122,12 +11397,7 @@ eslint-plugin-react-hooks@^4.3.0: resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.0.0-canary-7118f5dd7-20230705.tgz#4d55c50e186f1a2b0636433d2b0b2f592ddbccfd" integrity sha512-AZYbMo/NW9chdL7vk6HQzQhT+PvTAEVqWk9ziruUoW2kAOcN5qNyelv70e0F1VNQAbvutOC9oc+xfWycI9FxDw== -eslint-plugin-react-hooks@^5.0.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.2.0.tgz#1be0080901e6ac31ce7971beed3d3ec0a423d9e3" - integrity sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg== - -eslint-plugin-react@^7.29.2, eslint-plugin-react@^7.33.2, eslint-plugin-react@^7.35.0: +eslint-plugin-react@^7.29.2, eslint-plugin-react@^7.33.2: version "7.37.4" resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.37.4.tgz#1b6c80b6175b6ae4b26055ae4d55d04c414c7181" integrity sha512-BGP0jRmfYyvOyvMoRX/uoUeW+GqNj9y16bPQzqAHf3AYII/tDs+jMN0dBVkl88/OZwNGwrVFxE7riHsXVfy/LQ== @@ -12213,11 +11483,6 @@ eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4 resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== -eslint-visitor-keys@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz#687bacb2af884fcdda8a6e7d65c606f46a14cd45" - integrity sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw== - eslint@^7.18.0: version "7.32.0" resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.32.0.tgz#c6d328a14be3fb08c8d1d21e12c02fdb7a2a812d" @@ -12364,11 +11629,6 @@ estree-to-babel@^3.1.0: "@babel/types" "^7.2.0" c8 "^7.6.0" -estree-util-is-identifier-name@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz#0b5ef4c4ff13508b34dcd01ecfa945f61fce5dbd" - integrity sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg== - estree-walker@^0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-0.6.1.tgz#53049143f40c6eb918b23671d1fe3219f3a1b362" @@ -12605,17 +11865,6 @@ fast-equals@^5.0.1: resolved "https://registry.yarnpkg.com/fast-equals/-/fast-equals-5.2.2.tgz#885d7bfb079fac0ce0e8450374bce29e9b742484" integrity sha512-V7/RktU11J3I36Nwq2JnZEM7tNm17eBJz+u25qdxBZeCKiX6BkVSZQjwWIr+IobgnZy+ag73tTZgZi7tr0LrBw== -fast-glob@3.3.1: - version "3.3.1" - resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.1.tgz#784b4e897340f3dbbef17413b3f11acf03c874c4" - integrity sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg== - dependencies: - "@nodelib/fs.stat" "^2.0.2" - "@nodelib/fs.walk" "^1.2.3" - glob-parent "^5.1.2" - merge2 "^1.3.0" - micromatch "^4.0.4" - fast-glob@^2.2.6: version "2.2.7" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-2.2.7.tgz#6953857c3afa475fff92ee6015d52da70a4cd39d" @@ -12628,7 +11877,7 @@ fast-glob@^2.2.6: merge2 "^1.2.3" micromatch "^3.1.10" -fast-glob@^3.2.9, fast-glob@^3.3.2: +fast-glob@^3.2.9: version "3.3.3" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.3.tgz#d06d585ce8dba90a16b0505c543c3ccfb3aeb818" integrity sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg== @@ -12986,7 +12235,7 @@ form-data@^3.0.0: es-set-tostringtag "^2.1.0" mime-types "^2.1.35" -form-data@^4.0.0, form-data@^4.0.1: +form-data@^4.0.0: version "4.0.2" resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.2.tgz#35cabbdd30c3ce73deb2c42d3c8d3ed9ca51794c" integrity sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w== @@ -13692,27 +12941,6 @@ hast-util-raw@6.0.1: xtend "^4.0.0" zwitch "^1.0.0" -hast-util-to-jsx-runtime@^2.0.0: - version "2.3.6" - resolved "https://registry.yarnpkg.com/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.6.tgz#ff31897aae59f62232e21594eac7ef6b63333e98" - integrity sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg== - dependencies: - "@types/estree" "^1.0.0" - "@types/hast" "^3.0.0" - "@types/unist" "^3.0.0" - comma-separated-tokens "^2.0.0" - devlop "^1.0.0" - estree-util-is-identifier-name "^3.0.0" - hast-util-whitespace "^3.0.0" - mdast-util-mdx-expression "^2.0.0" - mdast-util-mdx-jsx "^3.0.0" - mdast-util-mdxjs-esm "^2.0.0" - property-information "^7.0.0" - space-separated-tokens "^2.0.0" - style-to-js "^1.0.0" - unist-util-position "^5.0.0" - vfile-message "^4.0.0" - hast-util-to-parse5@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/hast-util-to-parse5/-/hast-util-to-parse5-6.0.0.tgz#1ec44650b631d72952066cea9b1445df699f8479" @@ -13724,13 +12952,6 @@ hast-util-to-parse5@^6.0.0: xtend "^4.0.0" zwitch "^1.0.0" -hast-util-whitespace@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz#7778ed9d3c92dd9e8c5c8f648a49c21fc51cb621" - integrity sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw== - dependencies: - "@types/hast" "^3.0.0" - hastscript@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/hastscript/-/hastscript-6.0.0.tgz#e8768d7eac56c3fdeac8a92830d58e811e5bf640" @@ -13757,11 +12978,6 @@ highlight-words@1.2.2: resolved "https://registry.yarnpkg.com/highlight-words/-/highlight-words-1.2.2.tgz#9875b75d11814d7356b24f23feeb7d77761fa867" integrity sha512-Mf4xfPXYm8Ay1wTibCrHpNWeR2nUMynMVFkXCi4mbl+TEgmNOe+I4hV7W3OCZcSvzGL6kupaqpfHOemliMTGxQ== -highlight-words@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/highlight-words/-/highlight-words-2.0.0.tgz#06853d68f1f7c8e59d6ef2dd072fe2f64fc93936" - integrity sha512-If5n+IhSBRXTScE7wl16VPmd+44Vy7kof24EdqhjsZsDuHikpv1OCagVcJFpB4fS4UPUniedlWqrjIO8vWOsIQ== - hmac-drbg@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" @@ -13821,13 +13037,6 @@ html-encoding-sniffer@^2.0.1: dependencies: whatwg-encoding "^1.0.5" -html-encoding-sniffer@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz#696df529a7cfd82446369dc5193e590a3735b448" - integrity sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ== - dependencies: - whatwg-encoding "^3.1.1" - html-entities@^2.1.0, html-entities@^2.3.2: version "2.5.2" resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-2.5.2.tgz#201a3cf95d3a15be7099521620d19dfb4f65359f" @@ -13864,23 +13073,11 @@ html-minifier-terser@^6.0.2: relateurl "^0.2.7" terser "^5.10.0" -html-parse-stringify@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz#dfc1017347ce9f77c8141a507f233040c59c55d2" - integrity sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg== - dependencies: - void-elements "3.1.0" - html-tags@^3.1.0: version "3.3.1" resolved "https://registry.yarnpkg.com/html-tags/-/html-tags-3.3.1.tgz#a04026a18c882e4bba8a01a3d39cfe465d40b5ce" integrity sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ== -html-url-attributes@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/html-url-attributes/-/html-url-attributes-3.0.1.tgz#83b052cd5e437071b756cd74ae70f708870c2d87" - integrity sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ== - html-void-elements@^1.0.0: version "1.0.5" resolved "https://registry.yarnpkg.com/html-void-elements/-/html-void-elements-1.0.5.tgz#ce9159494e86d95e45795b166c2021c2cfca4483" @@ -13976,14 +13173,6 @@ http-proxy-agent@^5.0.0: agent-base "6" debug "4" -http-proxy-agent@^7.0.2: - version "7.0.2" - resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz#9a8b1f246866c028509486585f62b8f2c18c270e" - integrity sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig== - dependencies: - agent-base "^7.1.0" - debug "^4.3.4" - http-proxy-middleware@^2.0.3, http-proxy-middleware@^2.0.6: version "2.0.7" resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-2.0.7.tgz#915f236d92ae98ef48278a95dedf17e991936ec6" @@ -14017,14 +13206,6 @@ https-proxy-agent@^5.0.0: agent-base "6" debug "4" -https-proxy-agent@^7.0.6: - version "7.0.6" - resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz#da8dfeac7da130b05c2ba4b59c9b6cd66611a6b9" - integrity sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw== - dependencies: - agent-base "^7.1.2" - debug "4" - human-signals@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" @@ -14049,20 +13230,6 @@ i18n-iso-countries@^6.8.0: dependencies: diacritics "1.3.0" -i18next-resources-to-backend@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/i18next-resources-to-backend/-/i18next-resources-to-backend-1.2.1.tgz#fded121e63e3139ce839c9901b9449dbbea7351d" - integrity sha512-okHbVA+HZ7n1/76MsfhPqDou0fptl2dAlhRDu2ideXloRRduzHsqDOznJBef+R3DFZnbvWoBW+KxJ7fnFjd6Yw== - dependencies: - "@babel/runtime" "^7.23.2" - -i18next@^24.2.2: - version "24.2.3" - resolved "https://registry.yarnpkg.com/i18next/-/i18next-24.2.3.tgz#3a05f72615cbd7c00d7e348667e2aabef1df753b" - integrity sha512-lfbf80OzkocvX7nmZtu7nSTNbrTYR52sLWxPtlXX1zAhVw8WEnFk4puUkCR4B1dNQwbSpEHHHemcZu//7EcB7A== - dependencies: - "@babel/runtime" "^7.26.10" - iconv-lite@0.4.24, iconv-lite@^0.4.24: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" @@ -14070,7 +13237,7 @@ iconv-lite@0.4.24, iconv-lite@^0.4.24: dependencies: safer-buffer ">= 2.1.2 < 3" -iconv-lite@0.6.3, iconv-lite@^0.6.2: +iconv-lite@^0.6.2: version "0.6.3" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== @@ -14128,7 +13295,7 @@ ignore@^4.0.3, ignore@^4.0.6: resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== -ignore@^5.0.4, ignore@^5.2.0, ignore@^5.3.1: +ignore@^5.0.4, ignore@^5.2.0: version "5.3.2" resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5" integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g== @@ -14230,11 +13397,6 @@ inline-style-parser@0.1.1: resolved "https://registry.yarnpkg.com/inline-style-parser/-/inline-style-parser-0.1.1.tgz#ec8a3b429274e9c0a1f1c4ffa9453a7fef72cea1" integrity sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q== -inline-style-parser@0.2.4: - version "0.2.4" - resolved "https://registry.yarnpkg.com/inline-style-parser/-/inline-style-parser-0.2.4.tgz#f4af5fe72e612839fcd453d989a586566d695f22" - integrity sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q== - inquirer@^8.2.4: version "8.2.6" resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-8.2.6.tgz#733b74888195d8d400a67ac332011b5fae5ea562" @@ -14335,11 +13497,6 @@ is-alphabetical@1.0.4, is-alphabetical@^1.0.0: resolved "https://registry.yarnpkg.com/is-alphabetical/-/is-alphabetical-1.0.4.tgz#9e7d6b94916be22153745d184c298cbf986a686d" integrity sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg== -is-alphabetical@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/is-alphabetical/-/is-alphabetical-2.0.1.tgz#01072053ea7c1036df3c7d19a6daaec7f19e789b" - integrity sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ== - is-alphanumerical@^1.0.0: version "1.0.4" resolved "https://registry.yarnpkg.com/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz#7eb9a2431f855f6b1ef1a78e326df515696c4dbf" @@ -14348,14 +13505,6 @@ is-alphanumerical@^1.0.0: is-alphabetical "^1.0.0" is-decimal "^1.0.0" -is-alphanumerical@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz#7c03fbe96e3e931113e57f964b0a368cc2dfd875" - integrity sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw== - dependencies: - is-alphabetical "^2.0.0" - is-decimal "^2.0.0" - is-arguments@^1.1.1: version "1.2.0" resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.2.0.tgz#ad58c6aecf563b78ef2bf04df540da8f5d7d8e1b" @@ -14495,11 +13644,6 @@ is-decimal@^1.0.0: resolved "https://registry.yarnpkg.com/is-decimal/-/is-decimal-1.0.4.tgz#65a3a5958a1c5b63a706e1b333d7cd9f630d3fa5" integrity sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw== -is-decimal@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/is-decimal/-/is-decimal-2.0.1.tgz#9469d2dc190d0214fd87d78b78caecc0cc14eef7" - integrity sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A== - is-descriptor@^0.1.0: version "0.1.7" resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.7.tgz#2727eb61fd789dcd5bdf0ed4569f551d2fe3be33" @@ -14602,11 +13746,6 @@ is-hexadecimal@^1.0.0: resolved "https://registry.yarnpkg.com/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz#cc35c97588da4bd49a8eedd6bc4082d44dcb23a7" integrity sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw== -is-hexadecimal@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz#86b5bf668fca307498d319dfc03289d781a90027" - integrity sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg== - is-in-browser@^1.0.2, is-in-browser@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/is-in-browser/-/is-in-browser-1.1.3.tgz#56ff4db683a078c6082eb95dad7dc62e1d04f835" @@ -14701,11 +13840,6 @@ is-plain-obj@^3.0.0: resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-3.0.0.tgz#af6f2ea14ac5a646183a5bbdb5baabbc156ad9d7" integrity sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA== -is-plain-obj@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-4.1.0.tgz#d65025edec3657ce032fd7db63c97883eaed71f0" - integrity sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg== - is-plain-object@5.0.0, is-plain-object@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344" @@ -14904,14 +14038,6 @@ isobject@^4.0.0: resolved "https://registry.yarnpkg.com/isobject/-/isobject-4.0.0.tgz#3f1c9155e73b192022a80819bacd0343711697b0" integrity sha512-S/2fF5wH8SJA/kmwr6HYhK/RI/OkhD84k8ntalo0iJjZikgq1XFvR5M8NPT1x5F7fBwCG3qHfnzeP/Vh/ZxCUA== -isomorphic-dompurify@^2.21.0: - version "2.22.0" - resolved "https://registry.yarnpkg.com/isomorphic-dompurify/-/isomorphic-dompurify-2.22.0.tgz#675b751bb80dc1e96a5c6760bbc6396b03f4786d" - integrity sha512-A2xsDNST1yB94rErEnwqlzSvGllCJ4e8lDMe1OWBH2hvpfc/2qzgMEiDshTO1HwO+PIDTiYeOc7ZDB7Ds49BOg== - dependencies: - dompurify "^3.2.4" - jsdom "^26.0.0" - isomorphic-unfetch@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/isomorphic-unfetch/-/isomorphic-unfetch-3.1.0.tgz#87341d5f4f7b63843d468438128cb087b7c3e98f" @@ -15663,33 +14789,6 @@ jsdom@^16.6.0: ws "^7.4.6" xml-name-validator "^3.0.0" -jsdom@^26.0.0: - version "26.0.0" - resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-26.0.0.tgz#446dd1ad8cfc50df7e714e58f1f972c1763b354c" - integrity sha512-BZYDGVAIriBWTpIxYzrXjv3E/4u8+/pSG5bQdIYCbNCGOvsPkDQfTVLAIXAf9ETdCpduCVTkDe2NNZ8NIwUVzw== - dependencies: - cssstyle "^4.2.1" - data-urls "^5.0.0" - decimal.js "^10.4.3" - form-data "^4.0.1" - html-encoding-sniffer "^4.0.0" - http-proxy-agent "^7.0.2" - https-proxy-agent "^7.0.6" - is-potential-custom-element-name "^1.0.1" - nwsapi "^2.2.16" - parse5 "^7.2.1" - rrweb-cssom "^0.8.0" - saxes "^6.0.0" - symbol-tree "^3.2.4" - tough-cookie "^5.0.0" - w3c-xmlserializer "^5.0.0" - webidl-conversions "^7.0.0" - whatwg-encoding "^3.1.1" - whatwg-mimetype "^4.0.0" - whatwg-url "^14.1.0" - ws "^8.18.0" - xml-name-validator "^5.0.0" - jsesc@^3.0.2: version "3.1.0" resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-3.1.0.tgz#74d335a234f67ed19907fdadfac7ccf9d409825d" @@ -15933,72 +15032,6 @@ lazy-universal-dotenv@^3.0.1: dotenv "^8.0.0" dotenv-expand "^5.1.0" -lefthook-darwin-arm64@1.11.3: - version "1.11.3" - resolved "https://registry.yarnpkg.com/lefthook-darwin-arm64/-/lefthook-darwin-arm64-1.11.3.tgz#adb4c44d4f82a07f97e09deca36fb706d51514d5" - integrity sha512-IYzAOf8Qwqk7q+LoRyy7kSk9vzpUZ5wb/vLzEAH/F86Vay9AUaWe1f2pzeLwFg18qEc1QNklT69h9p/uLQMojA== - -lefthook-darwin-x64@1.11.3: - version "1.11.3" - resolved "https://registry.yarnpkg.com/lefthook-darwin-x64/-/lefthook-darwin-x64-1.11.3.tgz#b77c231371f6f60ddaf097d500a4aae5c374eb80" - integrity sha512-z/Wp7UMjE1Vyl+x9sjN3NvN6qKdwgHl+cDf98MKKDg/WyPE5XnzqLm9rLLJgImjyClfH7ptTfZxEyhTG3M3XvQ== - -lefthook-freebsd-arm64@1.11.3: - version "1.11.3" - resolved "https://registry.yarnpkg.com/lefthook-freebsd-arm64/-/lefthook-freebsd-arm64-1.11.3.tgz#27665ae8ab91c40a137d2f5a68ba84594988c120" - integrity sha512-QevwQ7lrv5wBCkk7LLTzT5KR3Bk/5nttSxT1UH2o0EsgirS/c2K5xSgQmV6m3CiZYuCe2Pja4BSIwN3zt17SMw== - -lefthook-freebsd-x64@1.11.3: - version "1.11.3" - resolved "https://registry.yarnpkg.com/lefthook-freebsd-x64/-/lefthook-freebsd-x64-1.11.3.tgz#30f5f398af759212dd68b8b5307229d04ce5c26c" - integrity sha512-PYbcyNgdJJ4J2pEO9Ss4oYo5yq4vmQGTKm3RTYbRx4viSWR65hvKCP0C4LnIqspMvmR05SJi2bqe7UBP2t60EA== - -lefthook-linux-arm64@1.11.3: - version "1.11.3" - resolved "https://registry.yarnpkg.com/lefthook-linux-arm64/-/lefthook-linux-arm64-1.11.3.tgz#ac338808e1c560935704473707e7540171170618" - integrity sha512-0pBMBAoafOAEg345eOPozsmRjWR0zCr6k+m5ZxwRBZbZx1bQFDqBakQ3TpFCphhcykmgFyaa1KeZJZUOrEsezA== - -lefthook-linux-x64@1.11.3: - version "1.11.3" - resolved "https://registry.yarnpkg.com/lefthook-linux-x64/-/lefthook-linux-x64-1.11.3.tgz#eea29dc603f34822c57f47bd9777bfa0e1ffa223" - integrity sha512-eiezheZ/bisBCMB2Ur0mctug/RDFyu39B5wzoE8y4z0W1yw6jHGrWMJ4Y8+5qKZ7fmdZg+7YPuMHZ2eFxOnhQA== - -lefthook-openbsd-arm64@1.11.3: - version "1.11.3" - resolved "https://registry.yarnpkg.com/lefthook-openbsd-arm64/-/lefthook-openbsd-arm64-1.11.3.tgz#b6eae9109a1a7520392317d635810e43cabe1b89" - integrity sha512-DRLTzXdtCj/TizpLcGSqXcnrqvgxeXgn/6nqzclIGqNdKCsNXDzpI0D3sP13Vwwmyoqv2etoTak2IHqZiXZDqg== - -lefthook-openbsd-x64@1.11.3: - version "1.11.3" - resolved "https://registry.yarnpkg.com/lefthook-openbsd-x64/-/lefthook-openbsd-x64-1.11.3.tgz#4b681975fe17ad9615b92310ca094c4d292aa5ec" - integrity sha512-l7om+ZjWpYrVZyDuElwnucZhEqa7YfwlRaKBenkBxEh2zMje8O6Zodeuma1KmyDbSFvnvEjARo/Ejiot4gLXEw== - -lefthook-windows-arm64@1.11.3: - version "1.11.3" - resolved "https://registry.yarnpkg.com/lefthook-windows-arm64/-/lefthook-windows-arm64-1.11.3.tgz#0cf029605d0c2983bf287269b8347aed460561c5" - integrity sha512-X0iTrql2gfPAkU2dzRwuHWgW5RcqCPbzJtKQ41X6Y/F7iQacRknmuYUGyC81funSvzGAsvlusMVLUvaFjIKnbA== - -lefthook-windows-x64@1.11.3: - version "1.11.3" - resolved "https://registry.yarnpkg.com/lefthook-windows-x64/-/lefthook-windows-x64-1.11.3.tgz#6baac87f6fb36885dea0c360452049dd8d6dad44" - integrity sha512-F+ORMn6YJXoS0EXU5LtN1FgV4QX9rC9LucZEkRmK6sKmS7hcb9IHpyb7siRGytArYzJvXVjPbxPBNSBdN4egZQ== - -lefthook@^1.8.5: - version "1.11.3" - resolved "https://registry.yarnpkg.com/lefthook/-/lefthook-1.11.3.tgz#d4c23eade3f9d6c32900c356555ca6380c601513" - integrity sha512-HJp37y62j3j8qzAOODWuUJl4ysLwsDvCTBV6odr3jIRHR/a5e+tI14VQGIBcpK9ysqC3pGWyW5Rp9Jv1YDubyw== - optionalDependencies: - lefthook-darwin-arm64 "1.11.3" - lefthook-darwin-x64 "1.11.3" - lefthook-freebsd-arm64 "1.11.3" - lefthook-freebsd-x64 "1.11.3" - lefthook-linux-arm64 "1.11.3" - lefthook-linux-x64 "1.11.3" - lefthook-openbsd-arm64 "1.11.3" - lefthook-openbsd-x64 "1.11.3" - lefthook-windows-arm64 "1.11.3" - lefthook-windows-x64 "1.11.3" - lerna@^7.3.0: version "7.4.2" resolved "https://registry.yarnpkg.com/lerna/-/lerna-7.4.2.tgz#03497125d7b7c8d463eebfe17a701b16bde2ad09" @@ -16325,11 +15358,6 @@ long@^4.0.0: resolved "https://registry.yarnpkg.com/long/-/long-4.0.0.tgz#9a7b71cfb7d361a194ea555241c92f7468d5bf28" integrity sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA== -longest-streak@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/longest-streak/-/longest-streak-3.1.0.tgz#62fa67cd958742a1574af9f39866364102d90cd4" - integrity sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g== - loose-envify@^1.1.0, loose-envify@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" @@ -16548,16 +15576,6 @@ material-react-table@^2.12.1: "@tanstack/react-virtual" "3.10.6" highlight-words "1.2.2" -material-react-table@^3.0.3: - version "3.2.1" - resolved "https://registry.yarnpkg.com/material-react-table/-/material-react-table-3.2.1.tgz#56f595755cab3b669b399999fed9eb305fbb6dd7" - integrity sha512-sQtTf7bETpkPN2Hm5BVtz89wrfXCVQguz6XlwMChSnfKFO5QCKAJJC5aSIKnUc3S0AvTz/k/ILi00FnnY1Gixw== - dependencies: - "@tanstack/match-sorter-utils" "8.19.4" - "@tanstack/react-table" "8.20.6" - "@tanstack/react-virtual" "3.11.2" - highlight-words "2.0.0" - math-intrinsics@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz#a0dd74be81e2aa5c2f27e65ce283605ee4e2b7f9" @@ -16586,74 +15604,6 @@ mdast-util-definitions@^4.0.0: dependencies: unist-util-visit "^2.0.0" -mdast-util-from-markdown@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.2.tgz#4850390ca7cf17413a9b9a0fbefcd1bc0eb4160a" - integrity sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA== - dependencies: - "@types/mdast" "^4.0.0" - "@types/unist" "^3.0.0" - decode-named-character-reference "^1.0.0" - devlop "^1.0.0" - mdast-util-to-string "^4.0.0" - micromark "^4.0.0" - micromark-util-decode-numeric-character-reference "^2.0.0" - micromark-util-decode-string "^2.0.0" - micromark-util-normalize-identifier "^2.0.0" - micromark-util-symbol "^2.0.0" - micromark-util-types "^2.0.0" - unist-util-stringify-position "^4.0.0" - -mdast-util-mdx-expression@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.1.tgz#43f0abac9adc756e2086f63822a38c8d3c3a5096" - integrity sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ== - dependencies: - "@types/estree-jsx" "^1.0.0" - "@types/hast" "^3.0.0" - "@types/mdast" "^4.0.0" - devlop "^1.0.0" - mdast-util-from-markdown "^2.0.0" - mdast-util-to-markdown "^2.0.0" - -mdast-util-mdx-jsx@^3.0.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.2.0.tgz#fd04c67a2a7499efb905a8a5c578dddc9fdada0d" - integrity sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q== - dependencies: - "@types/estree-jsx" "^1.0.0" - "@types/hast" "^3.0.0" - "@types/mdast" "^4.0.0" - "@types/unist" "^3.0.0" - ccount "^2.0.0" - devlop "^1.1.0" - mdast-util-from-markdown "^2.0.0" - mdast-util-to-markdown "^2.0.0" - parse-entities "^4.0.0" - stringify-entities "^4.0.0" - unist-util-stringify-position "^4.0.0" - vfile-message "^4.0.0" - -mdast-util-mdxjs-esm@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-2.0.1.tgz#019cfbe757ad62dd557db35a695e7314bcc9fa97" - integrity sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg== - dependencies: - "@types/estree-jsx" "^1.0.0" - "@types/hast" "^3.0.0" - "@types/mdast" "^4.0.0" - devlop "^1.0.0" - mdast-util-from-markdown "^2.0.0" - mdast-util-to-markdown "^2.0.0" - -mdast-util-phrasing@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz#7cc0a8dec30eaf04b7b1a9661a92adb3382aa6e3" - integrity sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w== - dependencies: - "@types/mdast" "^4.0.0" - unist-util-is "^6.0.0" - mdast-util-to-hast@10.0.1: version "10.0.1" resolved "https://registry.yarnpkg.com/mdast-util-to-hast/-/mdast-util-to-hast-10.0.1.tgz#0cfc82089494c52d46eb0e3edb7a4eb2aea021eb" @@ -16668,63 +15618,16 @@ mdast-util-to-hast@10.0.1: unist-util-position "^3.0.0" unist-util-visit "^2.0.0" -mdast-util-to-hast@^13.0.0: - version "13.2.0" - resolved "https://registry.yarnpkg.com/mdast-util-to-hast/-/mdast-util-to-hast-13.2.0.tgz#5ca58e5b921cc0a3ded1bc02eed79a4fe4fe41f4" - integrity sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA== - dependencies: - "@types/hast" "^3.0.0" - "@types/mdast" "^4.0.0" - "@ungap/structured-clone" "^1.0.0" - devlop "^1.0.0" - micromark-util-sanitize-uri "^2.0.0" - trim-lines "^3.0.0" - unist-util-position "^5.0.0" - unist-util-visit "^5.0.0" - vfile "^6.0.0" - -mdast-util-to-markdown@^2.0.0: - version "2.1.2" - resolved "https://registry.yarnpkg.com/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.2.tgz#f910ffe60897f04bb4b7e7ee434486f76288361b" - integrity sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA== - dependencies: - "@types/mdast" "^4.0.0" - "@types/unist" "^3.0.0" - longest-streak "^3.0.0" - mdast-util-phrasing "^4.0.0" - mdast-util-to-string "^4.0.0" - micromark-util-classify-character "^2.0.0" - micromark-util-decode-string "^2.0.0" - unist-util-visit "^5.0.0" - zwitch "^2.0.0" - mdast-util-to-string@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/mdast-util-to-string/-/mdast-util-to-string-1.1.0.tgz#27055500103f51637bd07d01da01eb1967a43527" integrity sha512-jVU0Nr2B9X3MU4tSK7JP1CMkSvOj7X5l/GboG1tKRw52lLF1x2Ju92Ms9tNetCcbfX3hzlM73zYo2NKkWSfF/A== -mdast-util-to-string@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz#7a5121475556a04e7eddeb67b264aae79d312814" - integrity sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg== - dependencies: - "@types/mdast" "^4.0.0" - mdn-data@2.0.14: version "2.0.14" resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.14.tgz#7113fc4281917d63ce29b43446f701e68c25ba50" integrity sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow== -mdn-data@2.0.28: - version "2.0.28" - resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.28.tgz#5ec48e7bef120654539069e1ae4ddc81ca490eba" - integrity sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g== - -mdn-data@2.0.30: - version "2.0.30" - resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.30.tgz#ce4df6f80af6cfbe218ecd5c552ba13c4dfa08cc" - integrity sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA== - mdurl@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e" @@ -16843,200 +15746,6 @@ microevent.ts@~0.1.1: resolved "https://registry.yarnpkg.com/microevent.ts/-/microevent.ts-0.1.1.tgz#70b09b83f43df5172d0205a63025bce0f7357fa0" integrity sha512-jo1OfR4TaEwd5HOrt5+tAZ9mqT4jmpNAusXtyfNzqVm9uiSYFZlKM1wYL4oU7azZW/PxQW53wM0S6OR1JHNa2g== -micromark-core-commonmark@^2.0.0: - version "2.0.3" - resolved "https://registry.yarnpkg.com/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz#c691630e485021a68cf28dbc2b2ca27ebf678cd4" - integrity sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg== - dependencies: - decode-named-character-reference "^1.0.0" - devlop "^1.0.0" - micromark-factory-destination "^2.0.0" - micromark-factory-label "^2.0.0" - micromark-factory-space "^2.0.0" - micromark-factory-title "^2.0.0" - micromark-factory-whitespace "^2.0.0" - micromark-util-character "^2.0.0" - micromark-util-chunked "^2.0.0" - micromark-util-classify-character "^2.0.0" - micromark-util-html-tag-name "^2.0.0" - micromark-util-normalize-identifier "^2.0.0" - micromark-util-resolve-all "^2.0.0" - micromark-util-subtokenize "^2.0.0" - micromark-util-symbol "^2.0.0" - micromark-util-types "^2.0.0" - -micromark-factory-destination@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz#8fef8e0f7081f0474fbdd92deb50c990a0264639" - integrity sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA== - dependencies: - micromark-util-character "^2.0.0" - micromark-util-symbol "^2.0.0" - micromark-util-types "^2.0.0" - -micromark-factory-label@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz#5267efa97f1e5254efc7f20b459a38cb21058ba1" - integrity sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg== - dependencies: - devlop "^1.0.0" - micromark-util-character "^2.0.0" - micromark-util-symbol "^2.0.0" - micromark-util-types "^2.0.0" - -micromark-factory-space@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz#36d0212e962b2b3121f8525fc7a3c7c029f334fc" - integrity sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg== - dependencies: - micromark-util-character "^2.0.0" - micromark-util-types "^2.0.0" - -micromark-factory-title@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz#237e4aa5d58a95863f01032d9ee9b090f1de6e94" - integrity sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw== - dependencies: - micromark-factory-space "^2.0.0" - micromark-util-character "^2.0.0" - micromark-util-symbol "^2.0.0" - micromark-util-types "^2.0.0" - -micromark-factory-whitespace@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz#06b26b2983c4d27bfcc657b33e25134d4868b0b1" - integrity sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ== - dependencies: - micromark-factory-space "^2.0.0" - micromark-util-character "^2.0.0" - micromark-util-symbol "^2.0.0" - micromark-util-types "^2.0.0" - -micromark-util-character@^2.0.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/micromark-util-character/-/micromark-util-character-2.1.1.tgz#2f987831a40d4c510ac261e89852c4e9703ccda6" - integrity sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q== - dependencies: - micromark-util-symbol "^2.0.0" - micromark-util-types "^2.0.0" - -micromark-util-chunked@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz#47fbcd93471a3fccab86cff03847fc3552db1051" - integrity sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA== - dependencies: - micromark-util-symbol "^2.0.0" - -micromark-util-classify-character@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz#d399faf9c45ca14c8b4be98b1ea481bced87b629" - integrity sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q== - dependencies: - micromark-util-character "^2.0.0" - micromark-util-symbol "^2.0.0" - micromark-util-types "^2.0.0" - -micromark-util-combine-extensions@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz#2a0f490ab08bff5cc2fd5eec6dd0ca04f89b30a9" - integrity sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg== - dependencies: - micromark-util-chunked "^2.0.0" - micromark-util-types "^2.0.0" - -micromark-util-decode-numeric-character-reference@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz#fcf15b660979388e6f118cdb6bf7d79d73d26fe5" - integrity sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw== - dependencies: - micromark-util-symbol "^2.0.0" - -micromark-util-decode-string@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz#6cb99582e5d271e84efca8e61a807994d7161eb2" - integrity sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ== - dependencies: - decode-named-character-reference "^1.0.0" - micromark-util-character "^2.0.0" - micromark-util-decode-numeric-character-reference "^2.0.0" - micromark-util-symbol "^2.0.0" - -micromark-util-encode@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz#0d51d1c095551cfaac368326963cf55f15f540b8" - integrity sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw== - -micromark-util-html-tag-name@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz#e40403096481986b41c106627f98f72d4d10b825" - integrity sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA== - -micromark-util-normalize-identifier@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz#c30d77b2e832acf6526f8bf1aa47bc9c9438c16d" - integrity sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q== - dependencies: - micromark-util-symbol "^2.0.0" - -micromark-util-resolve-all@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz#e1a2d62cdd237230a2ae11839027b19381e31e8b" - integrity sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg== - dependencies: - micromark-util-types "^2.0.0" - -micromark-util-sanitize-uri@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz#ab89789b818a58752b73d6b55238621b7faa8fd7" - integrity sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ== - dependencies: - micromark-util-character "^2.0.0" - micromark-util-encode "^2.0.0" - micromark-util-symbol "^2.0.0" - -micromark-util-subtokenize@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz#d8ade5ba0f3197a1cf6a2999fbbfe6357a1a19ee" - integrity sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA== - dependencies: - devlop "^1.0.0" - micromark-util-chunked "^2.0.0" - micromark-util-symbol "^2.0.0" - micromark-util-types "^2.0.0" - -micromark-util-symbol@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz#e5da494e8eb2b071a0d08fb34f6cefec6c0a19b8" - integrity sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q== - -micromark-util-types@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/micromark-util-types/-/micromark-util-types-2.0.2.tgz#f00225f5f5a0ebc3254f96c36b6605c4b393908e" - integrity sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA== - -micromark@^4.0.0: - version "4.0.2" - resolved "https://registry.yarnpkg.com/micromark/-/micromark-4.0.2.tgz#91395a3e1884a198e62116e33c9c568e39936fdb" - integrity sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA== - dependencies: - "@types/debug" "^4.0.0" - debug "^4.0.0" - decode-named-character-reference "^1.0.0" - devlop "^1.0.0" - micromark-core-commonmark "^2.0.0" - micromark-factory-space "^2.0.0" - micromark-util-character "^2.0.0" - micromark-util-chunked "^2.0.0" - micromark-util-combine-extensions "^2.0.0" - micromark-util-decode-numeric-character-reference "^2.0.0" - micromark-util-encode "^2.0.0" - micromark-util-normalize-identifier "^2.0.0" - micromark-util-resolve-all "^2.0.0" - micromark-util-sanitize-uri "^2.0.0" - micromark-util-subtokenize "^2.0.0" - micromark-util-symbol "^2.0.0" - micromark-util-types "^2.0.0" - micromatch@^3.1.10, micromatch@^3.1.4: version "3.1.10" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" @@ -17485,51 +16194,28 @@ nested-error-stacks@^2.0.0, nested-error-stacks@^2.1.0: resolved "https://registry.yarnpkg.com/nested-error-stacks/-/nested-error-stacks-2.1.1.tgz#26c8a3cee6cc05fbcf1e333cd2fc3e003326c0b5" integrity sha512-9iN1ka/9zmX1ZvLV9ewJYEk9h7RyRRtqdK0woXcqohu8EWIerfPUjYJPg0ULy0UqP7cslmdGc8xKDJcojlKiaw== -next@14.1.4: - version "14.1.4" - resolved "https://registry.yarnpkg.com/next/-/next-14.1.4.tgz#203310f7310578563fd5c961f0db4729ce7a502d" - integrity sha512-1WTaXeSrUwlz/XcnhGTY7+8eiaFvdet5z9u3V2jb+Ek1vFo0VhHKSAIJvDWfQpttWjnyw14kBeq28TPq7bTeEQ== +next@14.2.21: + version "14.2.21" + resolved "https://registry.yarnpkg.com/next/-/next-14.2.21.tgz#f6da9e2abba1a0e4ca7a5273825daf06632554ba" + integrity sha512-rZmLwucLHr3/zfDMYbJXbw0ZeoBpirxkXuvsJbk7UPorvPYZhP7vq7aHbKnU7dQNCYIimRrbB2pp3xmf+wsYUg== dependencies: - "@next/env" "14.1.4" - "@swc/helpers" "0.5.2" + "@next/env" "14.2.21" + "@swc/helpers" "0.5.5" busboy "1.6.0" caniuse-lite "^1.0.30001579" graceful-fs "^4.2.11" postcss "8.4.31" styled-jsx "5.1.1" optionalDependencies: - "@next/swc-darwin-arm64" "14.1.4" - "@next/swc-darwin-x64" "14.1.4" - "@next/swc-linux-arm64-gnu" "14.1.4" - "@next/swc-linux-arm64-musl" "14.1.4" - "@next/swc-linux-x64-gnu" "14.1.4" - "@next/swc-linux-x64-musl" "14.1.4" - "@next/swc-win32-arm64-msvc" "14.1.4" - "@next/swc-win32-ia32-msvc" "14.1.4" - "@next/swc-win32-x64-msvc" "14.1.4" - -next@^15.2.0: - version "15.2.2" - resolved "https://registry.yarnpkg.com/next/-/next-15.2.2.tgz#e3941a0e0e76cfe1880b57452807489e0546e3a2" - integrity sha512-dgp8Kcx5XZRjMw2KNwBtUzhngRaURPioxoNIVl5BOyJbhi9CUgEtKDO7fx5wh8Z8vOVX1nYZ9meawJoRrlASYA== - dependencies: - "@next/env" "15.2.2" - "@swc/counter" "0.1.3" - "@swc/helpers" "0.5.15" - busboy "1.6.0" - caniuse-lite "^1.0.30001579" - postcss "8.4.31" - styled-jsx "5.1.6" - optionalDependencies: - "@next/swc-darwin-arm64" "15.2.2" - "@next/swc-darwin-x64" "15.2.2" - "@next/swc-linux-arm64-gnu" "15.2.2" - "@next/swc-linux-arm64-musl" "15.2.2" - "@next/swc-linux-x64-gnu" "15.2.2" - "@next/swc-linux-x64-musl" "15.2.2" - "@next/swc-win32-arm64-msvc" "15.2.2" - "@next/swc-win32-x64-msvc" "15.2.2" - sharp "^0.33.5" + "@next/swc-darwin-arm64" "14.2.21" + "@next/swc-darwin-x64" "14.2.21" + "@next/swc-linux-arm64-gnu" "14.2.21" + "@next/swc-linux-arm64-musl" "14.2.21" + "@next/swc-linux-x64-gnu" "14.2.21" + "@next/swc-linux-x64-musl" "14.2.21" + "@next/swc-win32-arm64-msvc" "14.2.21" + "@next/swc-win32-ia32-msvc" "14.2.21" + "@next/swc-win32-x64-msvc" "14.2.21" nice-try@^1.0.4: version "1.0.5" @@ -17899,7 +16585,7 @@ num2fraction@^1.2.2: resolved "https://registry.yarnpkg.com/num2fraction/-/num2fraction-1.2.2.tgz#6f682b6a027a4e9ddfa4564cd2589d1d4e669ede" integrity sha512-Y1wZESM7VUThYY+4W+X4ySH2maqcA+p7UR+w8VWNWVAd6lwuXXWz/w/Cz43J/dI2I+PS6wD5N+bJUF+gjWvIqg== -nwsapi@^2.2.0, nwsapi@^2.2.16: +nwsapi@^2.2.0: version "2.2.18" resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.18.tgz#3c4d7927e1ef4d042d319438ecfda6cd81b7ee41" integrity sha512-p1TRH/edngVEHVbwqWnxUViEmq5znDvyB+Sik5cmuLpGOIfDf/39zLiq3swPF8Vakqn+gvNiOQAZu8djYlQILA== @@ -18134,18 +16820,6 @@ open@^8.0.9, open@^8.4.0: is-docker "^2.1.1" is-wsl "^2.2.0" -openapi-fetch@^0.13.4: - version "0.13.5" - resolved "https://registry.yarnpkg.com/openapi-fetch/-/openapi-fetch-0.13.5.tgz#805606860d85b8ba8c2e7cb36ea30b473d8065d9" - integrity sha512-AQK8T9GSKFREFlN1DBXTYsLjs7YV2tZcJ7zUWxbjMoQmj8dDSFRrzhLCbHPZWA1TMV3vACqfCxLEZcwf2wxV6Q== - dependencies: - openapi-typescript-helpers "^0.0.15" - -openapi-typescript-helpers@^0.0.15: - version "0.0.15" - resolved "https://registry.yarnpkg.com/openapi-typescript-helpers/-/openapi-typescript-helpers-0.0.15.tgz#96ffa762a5e01ef66a661b163d5f1109ed1967ed" - integrity sha512-opyTPaunsklCBpTK8JGef6mfPhLSnyy5a0IN9vKtx3+4aExf+KxEqYwIy3hqkedXIB97u357uLMJsOnm3GVjsw== - optionator@^0.9.1, optionator@^0.9.3: version "0.9.4" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.4.tgz#7ea1c1a5d91d764fb282139c88fe11e182a3a734" @@ -18445,19 +17119,6 @@ parse-entities@^2.0.0: is-decimal "^1.0.0" is-hexadecimal "^1.0.0" -parse-entities@^4.0.0: - version "4.0.2" - resolved "https://registry.yarnpkg.com/parse-entities/-/parse-entities-4.0.2.tgz#61d46f5ed28e4ee62e9ddc43d6b010188443f159" - integrity sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw== - dependencies: - "@types/unist" "^2.0.0" - character-entities-legacy "^3.0.0" - character-reference-invalid "^2.0.0" - decode-named-character-reference "^1.0.0" - is-alphanumerical "^2.0.0" - is-decimal "^2.0.0" - is-hexadecimal "^2.0.0" - parse-json@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9" @@ -18502,13 +17163,6 @@ parse5@6.0.1, parse5@^6.0.0, parse5@^6.0.1: resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw== -parse5@^7.2.1: - version "7.2.1" - resolved "https://registry.yarnpkg.com/parse5/-/parse5-7.2.1.tgz#8928f55915e6125f430cc44309765bf17556a33a" - integrity sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ== - dependencies: - entities "^4.5.0" - parseurl@~1.3.2, parseurl@~1.3.3: version "1.3.3" resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" @@ -19262,11 +17916,6 @@ property-information@^5.0.0, property-information@^5.3.0: dependencies: xtend "^4.0.0" -property-information@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/property-information/-/property-information-7.0.0.tgz#3508a6d6b0b8eb3ca6eb2c6623b164d2ed2ab112" - integrity sha512-7D/qOz/+Y4X/rzSB6jKxKUsQnphO046ei8qxG59mtM3RG3DHgTK81HrxrmoDVINJb8NKT5ZsRbwHvQ6B68Iyhg== - protobufjs@^6.11.2, protobufjs@^6.8.8, protobufjs@~6.11.2, protobufjs@~6.11.3: version "6.11.4" resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-6.11.4.tgz#29a412c38bf70d89e537b6d02d904a6f448173aa" @@ -19401,11 +18050,6 @@ qrcode.react@^1.0.1: prop-types "^15.6.0" qr.js "0.0.0" -qrcode.react@^4.1.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/qrcode.react/-/qrcode.react-4.2.0.tgz#1bce8363f348197d145c0da640929a24c83cbca3" - integrity sha512-QpgqWi8rD9DsS9EP3z7BT+5lY5SFhsqGjpgW5DY/i3mK4M9DTBNz3ErMi8BWYEfI3L0d8GIbGmcdFAS1uIRGjA== - qs@6.13.0: version "6.13.0" resolved "https://registry.yarnpkg.com/qs/-/qs-6.13.0.tgz#6ca3bd58439f7e245655798997787b0d88a51906" @@ -19413,7 +18057,7 @@ qs@6.13.0: dependencies: side-channel "^1.0.6" -qs@^6.10.0, qs@^6.12.3, qs@^6.14.0: +qs@^6.10.0, qs@^6.12.3: version "6.14.0" resolved "https://registry.yarnpkg.com/qs/-/qs-6.14.0.tgz#c63fa40680d2c5c941412a0e899c89af60c0a930" integrity sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w== @@ -19577,7 +18221,7 @@ react-docgen@^5.0.0: node-dir "^0.1.10" strip-indent "^3.0.0" -react-dom@^18, react-dom@^18.0.0, react-dom@^18.2.0: +react-dom@^18, react-dom@^18.2.0: version "18.3.1" resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.3.1.tgz#c2265d79511b57d479b3dd3fdfa51536494c5cb4" integrity sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw== @@ -19620,14 +18264,6 @@ react-hook-form@^7.14.2: resolved "https://registry.yarnpkg.com/react-hook-form/-/react-hook-form-7.54.2.tgz#8c26ed54c71628dff57ccd3c074b1dd377cfb211" integrity sha512-eHpAUgUjWbZocoQYUHposymRb4ZP6d0uwUnooL2uOybA9/3tPUvoAKqEWK1WaSiTxxOfTpffNZP7QwlnM3/gEg== -react-i18next@^15.4.0: - version "15.4.1" - resolved "https://registry.yarnpkg.com/react-i18next/-/react-i18next-15.4.1.tgz#33f3e89c2f6c68e2bfcbf9aa59986ad42fe78758" - integrity sha512-ahGab+IaSgZmNPYXdV1n+OYky95TGpFwnKRflX/16dY04DsYYKHtVLjeny7sBSCREEcoMbAgSkFiGLF5g5Oofw== - dependencies: - "@babel/runtime" "^7.25.0" - html-parse-stringify "^3.0.1" - react-identicons@^1.2.5: version "1.2.5" resolved "https://registry.yarnpkg.com/react-identicons/-/react-identicons-1.2.5.tgz#3502249e49d88f4e3500092694410a984bb102fa" @@ -19667,33 +18303,11 @@ react-load-script@^0.0.6: resolved "https://registry.yarnpkg.com/react-load-script/-/react-load-script-0.0.6.tgz#db6851236aaa25bb622677a2eb51dad4f8d2c258" integrity sha512-aRGxDGP9VoLxcsaYvKWIW+LRrMOzz2eEcubTS4NvQPPugjk2VvMhow0wWTkSl7RxookomD1MwcP4l5UStg5ShQ== -react-markdown@^9.0.3: - version "9.1.0" - resolved "https://registry.yarnpkg.com/react-markdown/-/react-markdown-9.1.0.tgz#606bd74c6af131ba382a7c1282ff506708ed2e26" - integrity sha512-xaijuJB0kzGiUdG7nc2MOMDUDBWPyGAjZtUrow9XxUeua8IqeP+VlIfAZ3bphpcLTnSZXz6z9jcVC/TCwbfgdw== - dependencies: - "@types/hast" "^3.0.0" - "@types/mdast" "^4.0.0" - devlop "^1.0.0" - hast-util-to-jsx-runtime "^2.0.0" - html-url-attributes "^3.0.0" - mdast-util-to-hast "^13.0.0" - remark-parse "^11.0.0" - remark-rehype "^11.0.0" - unified "^11.0.0" - unist-util-visit "^5.0.0" - vfile "^6.0.0" - react-merge-refs@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/react-merge-refs/-/react-merge-refs-1.1.0.tgz#73d88b892c6c68cbb7a66e0800faa374f4c38b06" integrity sha512-alTKsjEL0dKH/ru1Iyn7vliS2QRcBp9zZPGoWxUOvRGWPUYgjo+V01is7p04It6KhgrzhJGnIj9GgX8W4bZoCQ== -react-random-avatars@^1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/react-random-avatars/-/react-random-avatars-1.3.1.tgz#cfea11dd80262939656e4134438fa76eb2d95371" - integrity sha512-HyMGoWniK2D5CRthoA1he4c6LVFIX/yBjp4qarEXN/DsgJ+1R6M5gJwYhTBoATO+3itrGCAGnKZNqimG1sDjAA== - react-refresh-typescript@^2.0.2: version "2.0.10" resolved "https://registry.yarnpkg.com/react-refresh-typescript/-/react-refresh-typescript-2.0.10.tgz#8dfe40d2a51866926d97fb7150d139e70d649eb2" @@ -19801,16 +18415,7 @@ react-transition-group@^4.4.5: loose-envify "^1.4.0" prop-types "^15.6.2" -react-world-flags@^1.6.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/react-world-flags/-/react-world-flags-1.6.0.tgz#7b075aa9be8a1b3789451a9df2cf491294e6fbae" - integrity sha512-eutSeAy5YKoVh14js/JUCSlA6EBk1n4k+bDaV+NkNB50VhnG+f4QDTpYycnTUTsZ5cqw/saPmk0Z4Fa0VVZ1Iw== - dependencies: - svg-country-flags "^1.2.10" - svgo "^3.0.2" - world-countries "^5.0.0" - -react@^18, react@^18.0.0, react@^18.2.0: +react@^18, react@^18.2.0: version "18.3.1" resolved "https://registry.yarnpkg.com/react/-/react-18.3.1.tgz#49ab892009c53933625bd16b2533fc754cab2891" integrity sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ== @@ -20153,27 +18758,6 @@ remark-parse@8.0.3: vfile-location "^3.0.0" xtend "^4.0.1" -remark-parse@^11.0.0: - version "11.0.0" - resolved "https://registry.yarnpkg.com/remark-parse/-/remark-parse-11.0.0.tgz#aa60743fcb37ebf6b069204eb4da304e40db45a1" - integrity sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA== - dependencies: - "@types/mdast" "^4.0.0" - mdast-util-from-markdown "^2.0.0" - micromark-util-types "^2.0.0" - unified "^11.0.0" - -remark-rehype@^11.0.0: - version "11.1.1" - resolved "https://registry.yarnpkg.com/remark-rehype/-/remark-rehype-11.1.1.tgz#f864dd2947889a11997c0a2667cd6b38f685bca7" - integrity sha512-g/osARvjkBXb6Wo0XvAeXQohVta8i84ACbenPpoSsxTOQH/Ae0/RGP4WZgnMH5pMLpsj4FG7OHmcIcXxpza8eQ== - dependencies: - "@types/hast" "^3.0.0" - "@types/mdast" "^4.0.0" - mdast-util-to-hast "^13.0.0" - unified "^11.0.0" - vfile "^6.0.0" - remark-slug@^6.0.0: version "6.1.0" resolved "https://registry.yarnpkg.com/remark-slug/-/remark-slug-6.1.0.tgz#0503268d5f0c4ecb1f33315c00465ccdd97923ce" @@ -20420,11 +19004,6 @@ rollup@^3.17.2, rollup@^3.2.1: optionalDependencies: fsevents "~2.3.2" -rrweb-cssom@^0.8.0: - version "0.8.0" - resolved "https://registry.yarnpkg.com/rrweb-cssom/-/rrweb-cssom-0.8.0.tgz#3021d1b4352fbf3b614aaeed0bc0d5739abe0bc2" - integrity sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw== - rspack-resolver@^1.1.0: version "1.1.2" resolved "https://registry.yarnpkg.com/rspack-resolver/-/rspack-resolver-1.1.2.tgz#33b91858f7ae962d086ab00cbf66b8a4b8e9b570" @@ -20560,13 +19139,6 @@ saxes@^5.0.1: dependencies: xmlchars "^2.2.0" -saxes@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/saxes/-/saxes-6.0.0.tgz#fe5b4a4768df4f14a201b1ba6a65c1f3d9988cc5" - integrity sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA== - dependencies: - xmlchars "^2.2.0" - scheduler@^0.23.2: version "0.23.2" resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.2.tgz#414ba64a3b282892e944cf2108ecc078d115cdc3" @@ -20645,7 +19217,7 @@ semver@7.5.3: dependencies: lru-cache "^6.0.0" -semver@7.x, semver@>=7.5.2, semver@^7.0.0, semver@^7.1.1, semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8, semver@^7.5.0, semver@^7.5.3, semver@^7.5.4, semver@^7.6.0, semver@^7.6.3: +semver@7.x, semver@^7.0.0, semver@^7.1.1, semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8, semver@^7.5.0, semver@^7.5.3, semver@^7.5.4, semver@^7.6.3: version "7.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.1.tgz#abd5098d82b18c6c81f6074ff2647fd3e7220c9f" integrity sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA== @@ -20810,7 +19382,7 @@ shallow-clone@^3.0.0: dependencies: kind-of "^6.0.2" -sharp@^0.33.1, sharp@^0.33.5: +sharp@^0.33.1: version "0.33.5" resolved "https://registry.yarnpkg.com/sharp/-/sharp-0.33.5.tgz#13e0e4130cc309d6a9497596715240b2ec0c594e" integrity sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw== @@ -21061,7 +19633,7 @@ source-list-map@^2.0.0: resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34" integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw== -source-map-js@^1.0.1, source-map-js@^1.0.2, source-map-js@^1.2.1: +source-map-js@^1.0.2, source-map-js@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46" integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA== @@ -21115,11 +19687,6 @@ space-separated-tokens@^1.0.0: resolved "https://registry.yarnpkg.com/space-separated-tokens/-/space-separated-tokens-1.1.5.tgz#85f32c3d10d9682007e917414ddc5c26d1aa6899" integrity sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA== -space-separated-tokens@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz#1ecd9d2350a3844572c3f4a312bceb018348859f" - integrity sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q== - spdx-correct@^3.0.0: version "3.2.0" resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.2.0.tgz#4f5ab0668f0059e34f9c00dce331784a12de4e9c" @@ -21478,14 +20045,6 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -stringify-entities@^4.0.0: - version "4.0.4" - resolved "https://registry.yarnpkg.com/stringify-entities/-/stringify-entities-4.0.4.tgz#b3b79ef5f277cc4ac73caeb0236c5ba939b3a4f3" - integrity sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg== - dependencies: - character-entities-html4 "^2.0.0" - character-entities-legacy "^3.0.0" - "strip-ansi-cjs@npm:strip-ansi@^6.0.1": version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" @@ -21590,13 +20149,6 @@ style-loader@^3.3.1: resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-3.3.4.tgz#f30f786c36db03a45cbd55b6a70d930c479090e7" integrity sha512-0WqXzrsMTyb8yjZJHDqwmnwRJvhALK9LfRtRc6B4UTWe8AijYLZYZ9thuJTZc2VfQWINADW/j+LiJnfy2RoC1w== -style-to-js@^1.0.0: - version "1.1.16" - resolved "https://registry.yarnpkg.com/style-to-js/-/style-to-js-1.1.16.tgz#e6bd6cd29e250bcf8fa5e6591d07ced7575dbe7a" - integrity sha512-/Q6ld50hKYPH3d/r6nr117TZkHR0w0kGGIVfpG9N6D8NymRPM9RqCUv4pRpJ62E5DqOYx2AFpbZMyCPnjQCnOw== - dependencies: - style-to-object "1.0.8" - style-to-object@0.3.0, style-to-object@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/style-to-object/-/style-to-object-0.3.0.tgz#b1b790d205991cc783801967214979ee19a76e46" @@ -21604,13 +20156,6 @@ style-to-object@0.3.0, style-to-object@^0.3.0: dependencies: inline-style-parser "0.1.1" -style-to-object@1.0.8: - version "1.0.8" - resolved "https://registry.yarnpkg.com/style-to-object/-/style-to-object-1.0.8.tgz#67a29bca47eaa587db18118d68f9d95955e81292" - integrity sha512-xT47I/Eo0rwJmaXC4oilDGDWLohVhR6o/xAQcPQN8q6QBuZVL8qMYL85kLmST5cPjAorwvqIA4qXTRQoYHaL6g== - dependencies: - inline-style-parser "0.2.4" - styled-jsx@5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/styled-jsx/-/styled-jsx-5.1.1.tgz#839a1c3aaacc4e735fed0781b8619ea5d0009d1f" @@ -21618,13 +20163,6 @@ styled-jsx@5.1.1: dependencies: client-only "0.0.1" -styled-jsx@5.1.6: - version "5.1.6" - resolved "https://registry.yarnpkg.com/styled-jsx/-/styled-jsx-5.1.6.tgz#83b90c077e6c6a80f7f5e8781d0f311b2fe41499" - integrity sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA== - dependencies: - client-only "0.0.1" - stylehacks@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/stylehacks/-/stylehacks-5.1.1.tgz#7934a34eb59d7152149fa69d6e9e56f2fc34bcc9" @@ -21672,11 +20210,6 @@ supports-preserve-symlinks-flag@^1.0.0: resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== -svg-country-flags@^1.2.10: - version "1.2.10" - resolved "https://registry.yarnpkg.com/svg-country-flags/-/svg-country-flags-1.2.10.tgz#b1b94e9f08f065d7b3d0ff9ab359898edd4dd3b0" - integrity sha512-xrqwo0TYf/h2cfPvGpjdSuSguUbri4vNNizBnwzoZnX0xGo3O5nGJMlbYEp7NOYcnPGBm6LE2axqDWSB847bLw== - svg-parser@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/svg-parser/-/svg-parser-2.0.4.tgz#fdc2e29e13951736140b76cb122c8ee6630eb6b5" @@ -21695,19 +20228,6 @@ svgo@^2.7.0, svgo@^2.8.0: picocolors "^1.0.0" stable "^0.1.8" -svgo@^3.0.2: - version "3.3.2" - resolved "https://registry.yarnpkg.com/svgo/-/svgo-3.3.2.tgz#ad58002652dffbb5986fc9716afe52d869ecbda8" - integrity sha512-OoohrmuUlBs8B8o6MB2Aevn+pRIH9zDALSR+6hhqVfa6fRwG/Qw9VUMSMW9VNg2CFc/MTIfabtdOVl9ODIJjpw== - dependencies: - "@trysound/sax" "0.2.0" - commander "^7.2.0" - css-select "^5.1.0" - css-tree "^2.3.1" - css-what "^6.1.0" - csso "^5.0.5" - picocolors "^1.0.0" - symbol-observable@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-2.0.3.tgz#5b521d3d07a43c351055fa43b8355b62d33fd16a" @@ -21985,18 +20505,6 @@ tinyrainbow@^1.2.0: resolved "https://registry.yarnpkg.com/tinyrainbow/-/tinyrainbow-1.2.0.tgz#5c57d2fc0fb3d1afd78465c33ca885d04f02abb5" integrity sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ== -tldts-core@^6.1.84: - version "6.1.84" - resolved "https://registry.yarnpkg.com/tldts-core/-/tldts-core-6.1.84.tgz#f8ac2af9969bf9c2f7a99fa05d9c667b5e5b768c" - integrity sha512-NaQa1W76W2aCGjXybvnMYzGSM4x8fvG2AN/pla7qxcg0ZHbooOPhA8kctmOZUDfZyhDL27OGNbwAeig8P4p1vg== - -tldts@^6.1.32: - version "6.1.84" - resolved "https://registry.yarnpkg.com/tldts/-/tldts-6.1.84.tgz#fb58b1ceb70972a1ecd683606cea3d06c78f7238" - integrity sha512-aRGIbCIF3teodtUFAYSdQONVmDRy21REM3o6JnqWn5ZkQBJJ4gHxhw6OfwQ+WkSAi3ASamrS4N4nyazWx6uTYg== - dependencies: - tldts-core "^6.1.84" - tmp@^0.0.33: version "0.0.33" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" @@ -22088,13 +20596,6 @@ tough-cookie@^4.0.0: universalify "^0.2.0" url-parse "^1.5.3" -tough-cookie@^5.0.0: - version "5.1.2" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-5.1.2.tgz#66d774b4a1d9e12dc75089725af3ac75ec31bed7" - integrity sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A== - dependencies: - tldts "^6.1.32" - tr46@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/tr46/-/tr46-2.1.0.tgz#fa87aa81ca5d5941da8cbf1f9b749dc969a4e240" @@ -22102,23 +20603,11 @@ tr46@^2.1.0: dependencies: punycode "^2.1.1" -tr46@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/tr46/-/tr46-5.1.0.tgz#4a077922360ae807e172075ce5beb79b36e4a101" - integrity sha512-IUWnUK7ADYR5Sl1fZlO1INDUhVhatWl7BtJWsIhwJ0UAK7ilzzIa8uIqOO/aYVWHZPJkKbEL+362wrzoeRF7bw== - dependencies: - punycode "^2.3.1" - tr46@~0.0.3: version "0.0.3" resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== -trim-lines@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/trim-lines/-/trim-lines-3.0.1.tgz#d802e332a07df861c48802c04321017b1bd87338" - integrity sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg== - trim-newlines@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613" @@ -22144,21 +20633,11 @@ trough@^1.0.0: resolved "https://registry.yarnpkg.com/trough/-/trough-1.0.5.tgz#b8b639cefad7d0bb2abd37d433ff8293efa5f406" integrity sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA== -trough@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/trough/-/trough-2.2.0.tgz#94a60bd6bd375c152c1df911a4b11d5b0256f50f" - integrity sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw== - ts-api-utils@^1.0.1: version "1.4.3" resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.4.3.tgz#bfc2215fe6528fecab2b0fba570a2e8a4263b064" integrity sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw== -ts-api-utils@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-2.0.1.tgz#660729385b625b939aaa58054f45c058f33f10cd" - integrity sha512-dnlgjFSVetynI8nzgJ+qF62efpglpWRk8isUEWZGWlJYySCTD6aKvbUDu+zbPeDakk3bg5H4XpitHukgfL1m9w== - ts-dedent@^2.0.0, ts-dedent@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/ts-dedent/-/ts-dedent-2.2.0.tgz#39e4bd297cd036292ae2394eb3412be63f563bb5" @@ -22538,19 +21017,6 @@ unified@9.2.0: trough "^1.0.0" vfile "^4.0.0" -unified@^11.0.0: - version "11.0.5" - resolved "https://registry.yarnpkg.com/unified/-/unified-11.0.5.tgz#f66677610a5c0a9ee90cab2b8d4d66037026d9e1" - integrity sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA== - dependencies: - "@types/unist" "^3.0.0" - bail "^2.0.0" - devlop "^1.0.0" - extend "^3.0.0" - is-plain-obj "^4.0.0" - trough "^2.0.0" - vfile "^6.0.0" - union-value@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847" @@ -22618,25 +21084,11 @@ unist-util-is@^4.0.0: resolved "https://registry.yarnpkg.com/unist-util-is/-/unist-util-is-4.1.0.tgz#976e5f462a7a5de73d94b706bac1b90671b57797" integrity sha512-ZOQSsnce92GrxSqlnEEseX0gi7GH9zTJZ0p9dtu87WRb/37mMPO2Ilx1s/t9vBHrFhbgweUwb+t7cIn5dxPhZg== -unist-util-is@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/unist-util-is/-/unist-util-is-6.0.0.tgz#b775956486aff107a9ded971d996c173374be424" - integrity sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw== - dependencies: - "@types/unist" "^3.0.0" - unist-util-position@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/unist-util-position/-/unist-util-position-3.1.0.tgz#1c42ee6301f8d52f47d14f62bbdb796571fa2d47" integrity sha512-w+PkwCbYSFw8vpgWD0v7zRCl1FpY3fjDSQ3/N/wNd9Ffa4gPi8+4keqt99N3XW6F99t/mUzp2xAhNmfKWp95QA== -unist-util-position@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/unist-util-position/-/unist-util-position-5.0.0.tgz#678f20ab5ca1207a97d7ea8a388373c9cf896be4" - integrity sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA== - dependencies: - "@types/unist" "^3.0.0" - unist-util-remove-position@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/unist-util-remove-position/-/unist-util-remove-position-2.0.1.tgz#5d19ca79fdba712301999b2b73553ca8f3b352cc" @@ -22658,13 +21110,6 @@ unist-util-stringify-position@^2.0.0: dependencies: "@types/unist" "^2.0.2" -unist-util-stringify-position@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz#449c6e21a880e0855bf5aabadeb3a740314abac2" - integrity sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ== - dependencies: - "@types/unist" "^3.0.0" - unist-util-visit-parents@^3.0.0: version "3.1.1" resolved "https://registry.yarnpkg.com/unist-util-visit-parents/-/unist-util-visit-parents-3.1.1.tgz#65a6ce698f78a6b0f56aa0e88f13801886cdaef6" @@ -22673,14 +21118,6 @@ unist-util-visit-parents@^3.0.0: "@types/unist" "^2.0.0" unist-util-is "^4.0.0" -unist-util-visit-parents@^6.0.0: - version "6.0.1" - resolved "https://registry.yarnpkg.com/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz#4d5f85755c3b8f0dc69e21eca5d6d82d22162815" - integrity sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw== - dependencies: - "@types/unist" "^3.0.0" - unist-util-is "^6.0.0" - unist-util-visit@2.0.3, unist-util-visit@^2.0.0: version "2.0.3" resolved "https://registry.yarnpkg.com/unist-util-visit/-/unist-util-visit-2.0.3.tgz#c3703893146df47203bb8a9795af47d7b971208c" @@ -22690,15 +21127,6 @@ unist-util-visit@2.0.3, unist-util-visit@^2.0.0: unist-util-is "^4.0.0" unist-util-visit-parents "^3.0.0" -unist-util-visit@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/unist-util-visit/-/unist-util-visit-5.0.0.tgz#a7de1f31f72ffd3519ea71814cccf5fd6a9217d6" - integrity sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg== - dependencies: - "@types/unist" "^3.0.0" - unist-util-is "^6.0.0" - unist-util-visit-parents "^6.0.0" - universal-user-agent@^6.0.0: version "6.0.1" resolved "https://registry.yarnpkg.com/universal-user-agent/-/universal-user-agent-6.0.1.tgz#15f20f55da3c930c57bddbf1734c6654d5fd35aa" @@ -22965,14 +21393,6 @@ vfile-message@^2.0.0: "@types/unist" "^2.0.0" unist-util-stringify-position "^2.0.0" -vfile-message@^4.0.0: - version "4.0.2" - resolved "https://registry.yarnpkg.com/vfile-message/-/vfile-message-4.0.2.tgz#c883c9f677c72c166362fd635f21fc165a7d1181" - integrity sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw== - dependencies: - "@types/unist" "^3.0.0" - unist-util-stringify-position "^4.0.0" - vfile@^4.0.0: version "4.2.1" resolved "https://registry.yarnpkg.com/vfile/-/vfile-4.2.1.tgz#03f1dce28fc625c625bc6514350fbdb00fa9e624" @@ -22983,14 +21403,6 @@ vfile@^4.0.0: unist-util-stringify-position "^2.0.0" vfile-message "^2.0.0" -vfile@^6.0.0: - version "6.0.3" - resolved "https://registry.yarnpkg.com/vfile/-/vfile-6.0.3.tgz#3652ab1c496531852bf55a6bac57af981ebc38ab" - integrity sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q== - dependencies: - "@types/unist" "^3.0.0" - vfile-message "^4.0.0" - victory-vendor@^36.6.8: version "36.9.2" resolved "https://registry.yarnpkg.com/victory-vendor/-/victory-vendor-36.9.2.tgz#668b02a448fa4ea0f788dbf4228b7e64669ff801" @@ -23016,11 +21428,6 @@ vm-browserify@^1.0.1: resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0" integrity sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ== -void-elements@3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-3.1.0.tgz#614f7fbf8d801f0bb5f0661f5b2f5785750e4f09" - integrity sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w== - vscode-oniguruma@^1.6.1: version "1.7.0" resolved "https://registry.yarnpkg.com/vscode-oniguruma/-/vscode-oniguruma-1.7.0.tgz#439bfad8fe71abd7798338d1cd3dc53a8beea94b" @@ -23045,13 +21452,6 @@ w3c-xmlserializer@^2.0.0: dependencies: xml-name-validator "^3.0.0" -w3c-xmlserializer@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz#f925ba26855158594d907313cedd1476c5967f6c" - integrity sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA== - dependencies: - xml-name-validator "^5.0.0" - walker@^1.0.7, walker@~1.0.5: version "1.0.8" resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.8.tgz#bd498db477afe573dc04185f011d3ab8a8d7653f" @@ -23119,11 +21519,6 @@ webidl-conversions@^6.1.0: resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-6.1.0.tgz#9111b4d7ea80acd40f5270d666621afa78b69514" integrity sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w== -webidl-conversions@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-7.0.0.tgz#256b4e1882be7debbf01d05f0aa2039778ea080a" - integrity sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g== - webpack-cli@^4.8.0: version "4.10.0" resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-4.10.0.tgz#37c1d69c8d85214c5a65e589378f53aec64dab31" @@ -23354,31 +21749,11 @@ whatwg-encoding@^1.0.5: dependencies: iconv-lite "0.4.24" -whatwg-encoding@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz#d0f4ef769905d426e1688f3e34381a99b60b76e5" - integrity sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ== - dependencies: - iconv-lite "0.6.3" - whatwg-mimetype@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf" integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g== -whatwg-mimetype@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz#bc1bf94a985dc50388d54a9258ac405c3ca2fc0a" - integrity sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg== - -whatwg-url@^14.0.0, whatwg-url@^14.1.0: - version "14.2.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-14.2.0.tgz#4ee02d5d725155dae004f6ae95c73e7ef5d95663" - integrity sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw== - dependencies: - tr46 "^5.1.0" - webidl-conversions "^7.0.0" - whatwg-url@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" @@ -23525,11 +21900,6 @@ workerpool@^6.5.1: resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.5.1.tgz#060f73b39d0caf97c6db64da004cd01b4c099544" integrity sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA== -world-countries@^5.0.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/world-countries/-/world-countries-5.1.0.tgz#ea6b6156d9ef80c9ae6ff1e238d11b7ebd90351f" - integrity sha512-CXR6EBvTbArDlDDIWU3gfKb7Qk0ck2WNZ234b/A0vuecPzIfzzxH+O6Ejnvg1sT8XuiZjVlzOH0h08ZtaO7g0w== - "wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" @@ -23624,7 +21994,7 @@ ws@^7, ws@^7.4.6: resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.10.tgz#58b5c20dc281633f6c19113f39b349bd8bd558d9" integrity sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ== -ws@^8.13.0, ws@^8.18.0, ws@^8.2.3: +ws@^8.13.0, ws@^8.2.3: version "8.18.1" resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.1.tgz#ea131d3784e1dfdff91adb0a4a116b127515e3cb" integrity sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w== @@ -23641,11 +22011,6 @@ xml-name-validator@^3.0.0: resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a" integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw== -xml-name-validator@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-5.0.0.tgz#82be9b957f7afdacf961e5980f1bf227c0bf7673" - integrity sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg== - xml2js@^0.6.1: version "0.6.2" resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.6.2.tgz#dd0b630083aa09c161e25a4d0901e2b2a929b499" @@ -23781,11 +22146,6 @@ yup@^0.32.9: property-expr "^2.0.4" toposort "^2.0.2" -zod@^3.24.1: - version "3.24.2" - resolved "https://registry.yarnpkg.com/zod/-/zod-3.24.2.tgz#8efa74126287c675e92f46871cfc8d15c34372b3" - integrity sha512-lY7CDW43ECgW9u1TcT3IoXHflywfVqDYze4waEz812jR/bZ8FHDsl7pFQoSZTz5N+2NqRXs8GBwnAwo3ZNxqhQ== - zustand@^4.5.5: version "4.5.6" resolved "https://registry.yarnpkg.com/zustand/-/zustand-4.5.6.tgz#6857d52af44874a79fb3408c9473f78367255c96" @@ -23798,11 +22158,6 @@ zwitch@^1.0.0: resolved "https://registry.yarnpkg.com/zwitch/-/zwitch-1.0.5.tgz#d11d7381ffed16b742f6af7b3f223d5cd9fe9920" integrity sha512-V50KMwwzqJV0NpZIZFwfOD5/lyny3WlSzRiXgA0G7VUnRlqttta1L6UQIHzd6EuBY/cHGfwTIck7w1yH6Q5zUw== -zwitch@^2.0.0: - version "2.0.4" - resolved "https://registry.yarnpkg.com/zwitch/-/zwitch-2.0.4.tgz#c827d4b0acb76fc3e685a4c6ec2902d51070e9d7" - integrity sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A== - zxcvbn@^4.4.2: version "4.4.2" resolved "https://registry.yarnpkg.com/zxcvbn/-/zxcvbn-4.4.2.tgz#28ec17cf09743edcab056ddd8b1b06262cc73c30" From 400aa6ba6d638d0a7e4a062936e883a0a03a31b8 Mon Sep 17 00:00:00 2001 From: Tommy Verrall Date: Wed, 2 Apr 2025 08:53:40 +0200 Subject: [PATCH 04/48] add the base points back in - now i've reverted back to the original two here, as the compiler is failing around `tauri::api::path` however, looking into the new design for the path resolver in tower this tasks, requires pratically changing the whole wallet_strorage and config set up - it seems pretty straight forward https://v2.tauri.app/start/migrate/from-tauri-1/#migrate-path-to-tauri-manager - however, I would need a second set of eyes on this --- nym-wallet/src-tauri/src/config/mod.rs | 288 +- .../src-tauri/src/wallet_storage/mod.rs | 3654 ++++++++--------- 2 files changed, 1964 insertions(+), 1978 deletions(-) diff --git a/nym-wallet/src-tauri/src/config/mod.rs b/nym-wallet/src-tauri/src/config/mod.rs index 87014b1b50..1f79075157 100644 --- a/nym-wallet/src-tauri/src/config/mod.rs +++ b/nym-wallet/src-tauri/src/config/mod.rs @@ -11,7 +11,6 @@ use nym_validator_client::nyxd::AccountId as CosmosAccountId; use serde::de::DeserializeOwned; use serde::{Deserialize, Serialize}; use strum::IntoEnumIterator; -use tauri::Manager; use url::Url; use nym_config::defaults::{DenomDetailsOwned, NymNetworkDetails, ValidatorDetails}; @@ -105,35 +104,32 @@ impl NetworkConfig { } impl Config { - fn root_directory(app_handle: &tauri::AppHandle) -> PathBuf { - app_handle - .path() - .local_data_dir() - .expect("Failed to get config directory") + fn root_directory() -> PathBuf { + tauri::api::path::config_dir().expect("Failed to get config directory") } - fn config_directory(app_handle: &tauri::AppHandle) -> PathBuf { - Self::root_directory(app_handle).join(CONFIG_DIR_NAME) + fn config_directory() -> PathBuf { + Self::root_directory().join(CONFIG_DIR_NAME) } - fn config_file_path(app_handle: &tauri::AppHandle, network: Option) -> PathBuf { + fn config_file_path(network: Option) -> PathBuf { if let Some(network) = network { let network_filename = format!("{}.toml", network.as_key()); - Self::config_directory(app_handle).join(network_filename) + Self::config_directory().join(network_filename) } else { - Self::config_directory(app_handle).join(CONFIG_FILENAME) + Self::config_directory().join(CONFIG_FILENAME) } } - pub fn save_to_files(&self, app_handle: &tauri::AppHandle) -> io::Result<()> { + pub fn save_to_files(&self) -> io::Result<()> { log::trace!("Config::save_to_file"); // Make sure the whole directory structure actually exists - fs::create_dir_all(Self::config_directory(app_handle))?; + fs::create_dir_all(Self::config_directory())?; // Global config if let Some(global) = &self.global { - let location = Self::config_file_path(app_handle, None); + let location = Self::config_file_path(None); match toml::to_string_pretty(&global) .map_err(|toml_err| io::Error::new(io::ErrorKind::Other, toml_err)) @@ -154,7 +150,7 @@ impl Config { } }; - let location = Self::config_file_path(app_handle, Some(network)); + let location = Self::config_file_path(Some(network)); match toml::to_string_pretty(config) .map_err(|toml_err| io::Error::new(io::ErrorKind::Other, toml_err)) .map(|toml| fs::write(location.clone(), toml)) @@ -166,10 +162,10 @@ impl Config { Ok(()) } - pub fn load_from_files(app_handle: &tauri::AppHandle) -> Self { + pub fn load_from_files() -> Self { // Global let global = { - let file = Self::config_file_path(app_handle, None); + let file = Self::config_file_path(None); match load_from_file::(file.clone()) { Ok(global) => { log::debug!("Loaded from file {:#?}", file); @@ -185,7 +181,7 @@ impl Config { // One file per network let mut networks = HashMap::new(); for network in WalletNetwork::iter() { - let file = Self::config_file_path(app_handle, Some(network)); + let file = Self::config_file_path(Some(network)); match load_from_file::(file.clone()) { Ok(config) => { log::trace!("Loaded from file {:#?}", file); @@ -265,8 +261,6 @@ impl Config { } } - //////// - pub fn select_nyxd_url(&mut self, nyxd_url: Url, network: WalletNetwork) { if let Some(net) = self.networks.get_mut(&network.as_key()) { net.selected_nyxd_url = Some(nyxd_url); @@ -537,145 +531,145 @@ impl From for NetworkDetails { } } -// #[cfg(test)] -// mod tests { -// use super::*; +#[cfg(test)] +mod tests { + use super::*; -// fn test_config() -> Config { -// let netconfig = NetworkConfig { -// selected_nyxd_url: None, -// selected_api_url: Some("https://my_api_url.com".parse().unwrap()), + fn test_config() -> Config { + let netconfig = NetworkConfig { + selected_nyxd_url: None, + selected_api_url: Some("https://my_api_url.com".parse().unwrap()), -// nyxd_urls: Some(vec![ -// ValidatorConfigEntry { -// nyxd_url: "https://foo".parse().unwrap(), -// nyxd_name: Some("FooName".to_string()), -// api_url: None, -// }, -// ValidatorConfigEntry { -// nyxd_url: "https://bar".parse().unwrap(), -// nyxd_name: None, -// api_url: Some("https://bar/api".parse().unwrap()), -// }, -// ValidatorConfigEntry { -// nyxd_url: "https://baz".parse().unwrap(), -// nyxd_name: None, -// api_url: Some("https://baz/api".parse().unwrap()), -// }, -// ]), -// ..NetworkConfig::default() -// }; + nyxd_urls: Some(vec![ + ValidatorConfigEntry { + nyxd_url: "https://foo".parse().unwrap(), + nyxd_name: Some("FooName".to_string()), + api_url: None, + }, + ValidatorConfigEntry { + nyxd_url: "https://bar".parse().unwrap(), + nyxd_name: None, + api_url: Some("https://bar/api".parse().unwrap()), + }, + ValidatorConfigEntry { + nyxd_url: "https://baz".parse().unwrap(), + nyxd_name: None, + api_url: Some("https://baz/api".parse().unwrap()), + }, + ]), + ..NetworkConfig::default() + }; -// Config { -// base: Base::default(), -// global: Some(GlobalConfig::default()), -// networks: [(WalletNetwork::MAINNET.as_key(), netconfig)] -// .into_iter() -// .collect(), -// } -// } + Config { + base: Base::default(), + global: Some(GlobalConfig::default()), + networks: [(WalletNetwork::MAINNET.as_key(), netconfig)] + .into_iter() + .collect(), + } + } -// #[test] -// fn serialize_to_toml() { -// let config = test_config(); -// let netconfig = &config.networks[&WalletNetwork::MAINNET.as_key()]; -// assert_eq!( -// toml::to_string_pretty(netconfig).unwrap(), -// r#"version = 1 -// selected_api_url = 'https://my_api_url.com/' + #[test] + fn serialize_to_toml() { + let config = test_config(); + let netconfig = &config.networks[&WalletNetwork::MAINNET.as_key()]; + assert_eq!( + toml::to_string_pretty(netconfig).unwrap(), + r#"version = 1 +selected_api_url = 'https://my_api_url.com/' -// [[nyxd_urls]] -// nyxd_url = 'https://foo/' -// nyxd_name = 'FooName' +[[nyxd_urls]] +nyxd_url = 'https://foo/' +nyxd_name = 'FooName' -// [[nyxd_urls]] -// nyxd_url = 'https://bar/' -// api_url = 'https://bar/api' +[[nyxd_urls]] +nyxd_url = 'https://bar/' +api_url = 'https://bar/api' -// [[nyxd_urls]] -// nyxd_url = 'https://baz/' -// api_url = 'https://baz/api' -// "# -// ); -// } +[[nyxd_urls]] +nyxd_url = 'https://baz/' +api_url = 'https://baz/api' +"# + ); + } -// #[test] -// fn serialize_to_json() { -// let config = test_config(); -// let netconfig = &config.networks[&WalletNetwork::MAINNET.as_key()]; -// println!("{}", serde_json::to_string_pretty(netconfig).unwrap()); -// assert_eq!( -// serde_json::to_string_pretty(netconfig).unwrap(), -// r#"{ -// "version": 1, -// "selected_nyxd_url": null, -// "default_nyxd_url": null, -// "selected_api_url": "https://my_api_url.com/", -// "nyxd_urls": [ -// { -// "nyxd_url": "https://foo/", -// "nyxd_name": "FooName", -// "api_url": null -// }, -// { -// "nyxd_url": "https://bar/", -// "nyxd_name": null, -// "api_url": "https://bar/api" -// }, -// { -// "nyxd_url": "https://baz/", -// "nyxd_name": null, -// "api_url": "https://baz/api" -// } -// ] -// }"# -// ); -// } + #[test] + fn serialize_to_json() { + let config = test_config(); + let netconfig = &config.networks[&WalletNetwork::MAINNET.as_key()]; + println!("{}", serde_json::to_string_pretty(netconfig).unwrap()); + assert_eq!( + serde_json::to_string_pretty(netconfig).unwrap(), + r#"{ + "version": 1, + "selected_nyxd_url": null, + "default_nyxd_url": null, + "selected_api_url": "https://my_api_url.com/", + "nyxd_urls": [ + { + "nyxd_url": "https://foo/", + "nyxd_name": "FooName", + "api_url": null + }, + { + "nyxd_url": "https://bar/", + "nyxd_name": null, + "api_url": "https://bar/api" + }, + { + "nyxd_url": "https://baz/", + "nyxd_name": null, + "api_url": "https://baz/api" + } + ] +}"# + ); + } -// #[test] -// fn serialize_and_deserialize_to_toml() { -// let config = test_config(); -// let netconfig = &config.networks[&WalletNetwork::MAINNET.as_key()]; -// let config_str = toml::to_string_pretty(netconfig).unwrap(); -// let config_from_toml: NetworkConfig = toml::from_str(&config_str).unwrap(); -// assert_eq!(netconfig, &config_from_toml); -// } + #[test] + fn serialize_and_deserialize_to_toml() { + let config = test_config(); + let netconfig = &config.networks[&WalletNetwork::MAINNET.as_key()]; + let config_str = toml::to_string_pretty(netconfig).unwrap(); + let config_from_toml: NetworkConfig = toml::from_str(&config_str).unwrap(); + assert_eq!(netconfig, &config_from_toml); + } -// #[test] -// fn get_urls_parsed_from_config() { -// let config = test_config(); + #[test] + fn get_urls_parsed_from_config() { + let config = test_config(); -// let nyxd_url = config -// .get_configured_validators(WalletNetwork::MAINNET) -// .next() -// .map(|v| v.nyxd_url) -// .unwrap(); -// assert_eq!(nyxd_url.as_ref(), "https://foo/"); + let nyxd_url = config + .get_configured_validators(WalletNetwork::MAINNET) + .next() + .map(|v| v.nyxd_url) + .unwrap(); + assert_eq!(nyxd_url.as_ref(), "https://foo/"); -// // The first entry is missing an API URL -// let api_url = config -// .get_configured_validators(WalletNetwork::MAINNET) -// .next() -// .and_then(|v| v.api_url); -// assert_eq!(api_url, None); -// } + // The first entry is missing an API URL + let api_url = config + .get_configured_validators(WalletNetwork::MAINNET) + .next() + .and_then(|v| v.api_url); + assert_eq!(api_url, None); + } -// #[test] -// fn get_urls_from_defaults() { -// let config = Config::default(); + #[test] + fn get_urls_from_defaults() { + let config = Config::default(); -// let nyxd_url = config -// .get_base_validators(WalletNetwork::MAINNET) -// .next() -// .map(|v| v.nyxd_url) -// .unwrap(); -// assert_eq!(nyxd_url.as_ref(), "https://rpc.nymtech.net/"); + let nyxd_url = config + .get_base_validators(WalletNetwork::MAINNET) + .next() + .map(|v| v.nyxd_url) + .unwrap(); + assert_eq!(nyxd_url.as_ref(), "https://rpc.nymtech.net/"); -// let api_url = config -// .get_base_validators(WalletNetwork::MAINNET) -// .next() -// .and_then(|v| v.api_url) -// .unwrap(); -// assert_eq!(api_url.as_ref(), "https://validator.nymtech.net/api/",); -// } -// } + let api_url = config + .get_base_validators(WalletNetwork::MAINNET) + .next() + .and_then(|v| v.api_url) + .unwrap(); + assert_eq!(api_url.as_ref(), "https://validator.nymtech.net/api/",); + } +} \ No newline at end of file diff --git a/nym-wallet/src-tauri/src/wallet_storage/mod.rs b/nym-wallet/src-tauri/src/wallet_storage/mod.rs index c620351417..115823da4a 100644 --- a/nym-wallet/src-tauri/src/wallet_storage/mod.rs +++ b/nym-wallet/src-tauri/src/wallet_storage/mod.rs @@ -19,7 +19,6 @@ use nym_validator_client::nyxd::bip32::DerivationPath; use std::ffi::OsString; use std::fs::{self, create_dir_all, OpenOptions}; use std::path::{Path, PathBuf}; -use tauri::Manager; use time::OffsetDateTime; #[cfg(test)] @@ -38,18 +37,14 @@ pub(crate) const DEFAULT_LOGIN_ID: &str = "default"; /// this name. pub(crate) const DEFAULT_FIRST_ACCOUNT_NAME: &str = "Account 1"; -fn get_storage_directory(app_handle: &tauri::AppHandle) -> Result { - app_handle - .path() - .local_data_dir() +fn get_storage_directory() -> Result { + tauri::api::path::local_data_dir() .map(|dir| dir.join(STORAGE_DIR_NAME)) .ok_or(BackendError::UnknownStorageDirectory) } -pub(crate) fn wallet_login_filepath( - app_handle: &tauri::AppHandle, -) -> Result { - get_storage_directory(app_handle).map(|dir| dir.join(WALLET_INFO_FILENAME)) +pub(crate) fn wallet_login_filepath() -> Result { + get_storage_directory().map(|dir| dir.join(WALLET_INFO_FILENAME)) } fn write_to_file(filepath: &Path, wallet: &StoredWallet) -> Result<(), BackendError> { @@ -64,10 +59,8 @@ fn write_to_file(filepath: &Path, wallet: &StoredWallet) -> Result<(), BackendEr /// Load stored wallet file #[allow(unused)] -pub(crate) fn load_existing_wallet( - app_handle: &tauri::AppHandle, -) -> Result { - let store_dir = get_storage_directory(app_handle)?; +pub(crate) fn load_existing_wallet() -> Result { + let store_dir = get_storage_directory()?; let filepath = store_dir.join(WALLET_INFO_FILENAME); load_existing_wallet_at_file(&filepath) } @@ -84,11 +77,10 @@ fn load_existing_wallet_at_file(filepath: &Path) -> Result Result { - let store_dir = get_storage_directory(app_handle)?; + let store_dir = get_storage_directory()?; let filepath = store_dir.join(WALLET_INFO_FILENAME); load_existing_login_at_file(&filepath, id, password) } @@ -476,1819 +468,1819 @@ fn rename_account_in_login_at_file( write_to_file(filepath, &stored_wallet) } -// #[cfg(test)] -// mod tests { -// use crate::wallet_storage::account_data::WalletAccount; - -// use super::*; -// use nym_config::defaults::COSMOS_DERIVATION_PATH; -// use std::str::FromStr; -// use tempfile::tempdir; - -// #[test] -// fn trying_to_load_nonexistant_wallet_fails() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let id1 = LoginId::new("first".to_string()); -// let password = UserPassword::new("password".to_string()); - -// assert!(matches!( -// load_existing_wallet_at_file(&wallet_file), -// Err(BackendError::WalletFileNotFound), -// )); -// assert!(matches!( -// load_existing_login_at_file(&wallet_file, &id1, &password), -// Err(BackendError::WalletFileNotFound), -// )); -// remove_login_at_file(&wallet_file, &id1).unwrap_err(); -// } - -// #[test] -// fn store_single_login() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let id1 = LoginId::new("first".to_string()); - -// store_login_at_file(&wallet_file, account1, hd_path, id1.clone(), &password).unwrap(); - -// let stored_wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); -// assert_eq!(stored_wallet.len(), 1); - -// let login = stored_wallet.get_encrypted_login_by_index(0).unwrap(); -// assert_eq!(login.id, id1); - -// // some actual ciphertext was saved -// assert!(!login.account.ciphertext().is_empty()); -// } - -// #[test] -// fn store_single_login_with_multi() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let id1 = LoginId::new("first".to_string()); - -// store_login_with_multiple_accounts_at_file( -// &wallet_file, -// account1, -// cosmos_hd_path, -// id1.clone(), -// &password, -// ) -// .unwrap(); - -// let stored_wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); -// assert_eq!(stored_wallet.len(), 1); - -// let login = stored_wallet.get_encrypted_login_by_index(0).unwrap(); -// assert_eq!(login.id, id1); - -// // some actual ciphertext was saved -// assert!(!login.account.ciphertext().is_empty()); -// } - -// #[test] -// fn store_single_login_with_multi_then_update_pwd_and_load() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let new_password = UserPassword::new("new_password".to_string()); -// let id1 = LoginId::new("first".to_string()); - -// store_login_with_multiple_accounts_at_file( -// &wallet_file, -// account1, -// cosmos_hd_path, -// id1.clone(), -// &password, -// ) -// .unwrap(); - -// update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); - -// let stored_wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); -// assert_eq!(stored_wallet.len(), 1); - -// let login = stored_wallet.get_encrypted_login_by_index(0).unwrap(); -// assert_eq!(login.id, id1); - -// // some actual ciphertext was saved -// assert!(!login.account.ciphertext().is_empty()); -// } - -// #[test] -// fn store_twice_for_the_same_id_fails() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let id1 = LoginId::new("first".to_string()); - -// // Store the first login -// store_login_at_file( -// &wallet_file, -// account1.clone(), -// hd_path.clone(), -// id1.clone(), -// &password, -// ) -// .unwrap(); - -// // and storing the same id again fails -// assert!(matches!( -// store_login_at_file(&wallet_file, account1, hd_path, id1, &password,), -// Err(BackendError::WalletLoginIdAlreadyExists), -// )); -// } - -// #[test] -// fn store_twice_for_the_same_id_fails_with_multiple() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let id1 = LoginId::new("first".to_string()); - -// // Store the first login -// store_login_with_multiple_accounts_at_file( -// &wallet_file, -// account1.clone(), -// hd_path.clone(), -// id1.clone(), -// &password, -// ) -// .unwrap(); - -// // and storing the same id again fails -// assert!(matches!( -// store_login_with_multiple_accounts_at_file( -// &wallet_file, -// account1, -// hd_path, -// id1, -// &password, -// ), -// Err(BackendError::WalletLoginIdAlreadyExists), -// )); -// } - -// #[test] -// fn load_with_wrong_password_fails() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let bad_password = UserPassword::new("bad-password".to_string()); -// let id1 = LoginId::new("first".to_string()); - -// store_login_at_file(&wallet_file, account1, hd_path, id1.clone(), &password).unwrap(); - -// // Trying to load it with wrong password now fails -// assert!(matches!( -// load_existing_login_at_file(&wallet_file, &id1, &bad_password), -// Err(BackendError::StoreCipherError { .. }), -// )); -// } - -// #[test] -// fn load_with_wrong_password_fails_with_multi() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let bad_password = UserPassword::new("bad-password".to_string()); -// let id1 = LoginId::new("first".to_string()); - -// store_login_with_multiple_accounts_at_file( -// &wallet_file, -// account1, -// hd_path, -// id1.clone(), -// &password, -// ) -// .unwrap(); - -// // Trying to load it with wrong password now fails -// assert!(matches!( -// load_existing_login_at_file(&wallet_file, &id1, &bad_password), -// Err(BackendError::StoreCipherError { .. }), -// )); -// } - -// #[test] -// fn load_with_wrong_id_fails() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let id1 = LoginId::new("first".to_string()); -// let id2 = LoginId::new("second".to_string()); - -// store_login_at_file(&wallet_file, account1, hd_path, id1, &password).unwrap(); - -// // Trying to load with the wrong id -// assert!(matches!( -// load_existing_login_at_file(&wallet_file, &id2, &password), -// Err(BackendError::WalletNoSuchLoginId), -// )); -// } - -// #[test] -// fn load_with_wrong_id_fails_with_multi() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let id1 = LoginId::new("first".to_string()); -// let id2 = LoginId::new("second".to_string()); - -// store_login_with_multiple_accounts_at_file(&wallet_file, account1, hd_path, id1, &password) -// .unwrap(); - -// // Trying to load with the wrong id -// assert!(matches!( -// load_existing_login_at_file(&wallet_file, &id2, &password), -// Err(BackendError::WalletNoSuchLoginId), -// )); -// } - -// #[test] -// fn store_and_load_a_single_login() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let id1 = LoginId::new("first".to_string()); - -// store_login_at_file( -// &wallet_file, -// account1.clone(), -// hd_path.clone(), -// id1.clone(), -// &password, -// ) -// .unwrap(); - -// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); -// let acc = loaded_login.as_mnemonic_account().unwrap(); -// assert_eq!(&account1, acc.mnemonic()); -// assert_eq!(&hd_path, acc.hd_path()); -// } - -// #[test] -// fn store_a_single_login_then_update_pwd_and_load() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let new_password = UserPassword::new("new_password".to_string()); -// let id1 = LoginId::new("first".to_string()); - -// store_login_at_file( -// &wallet_file, -// account1.clone(), -// hd_path.clone(), -// id1.clone(), -// &password, -// ) -// .unwrap(); - -// update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); - -// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &new_password).unwrap(); -// let acc = loaded_login.as_mnemonic_account().unwrap(); -// assert_eq!(&account1, acc.mnemonic()); -// assert_eq!(&hd_path, acc.hd_path()); -// } - -// #[test] -// fn store_a_single_login_then_update_pwd_with_identical_pwd_is_noop_but_okay() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let id1 = LoginId::new("first".to_string()); - -// store_login_at_file( -// &wallet_file, -// account1.clone(), -// hd_path.clone(), -// id1.clone(), -// &password, -// ) -// .unwrap(); - -// update_encrypted_logins_at_file(&wallet_file, &password, &password).unwrap(); - -// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); -// let acc = loaded_login.as_mnemonic_account().unwrap(); -// assert_eq!(&account1, acc.mnemonic()); -// assert_eq!(&hd_path, acc.hd_path()); -// } - -// #[test] -// fn store_a_single_login_then_update_pwd_with_wrong_current_pwd_fails() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let new_password = UserPassword::new("new_password".to_string()); -// let wrong_password = UserPassword::new("wrong_password".to_string()); -// let id1 = LoginId::new("first".to_string()); - -// store_login_at_file(&wallet_file, account1, hd_path, id1, &password).unwrap(); - -// let err = update_encrypted_logins_at_file(&wallet_file, &wrong_password, &new_password) -// .unwrap_err(); -// assert!(matches!(err, BackendError::StoreCipherError { .. })); -// } - -// #[test] -// fn store_a_single_login_then_update_pwd_and_load_with_wrong_pwd_fails() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let new_password = UserPassword::new("new_password".to_string()); -// let id1 = LoginId::new("first".to_string()); - -// store_login_at_file(&wallet_file, account1, hd_path, id1.clone(), &password).unwrap(); - -// update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); - -// let err = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap_err(); -// assert!(matches!(err, BackendError::StoreCipherError { .. })); -// } - -// #[test] -// fn store_and_load_a_single_login_with_multi() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let acc1 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let id1 = LoginId::new("first".to_string()); - -// store_login_with_multiple_accounts_at_file( -// &wallet_file, -// acc1.clone(), -// hd_path.clone(), -// id1.clone(), -// &password, -// ) -// .unwrap(); - -// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); -// let accounts = loaded_login.as_multiple_accounts().unwrap(); -// assert_eq!(accounts.len(), 1); -// let account = accounts -// .get_account(&DEFAULT_FIRST_ACCOUNT_NAME.into()) -// .unwrap(); -// assert_eq!(account.id().as_ref(), DEFAULT_FIRST_ACCOUNT_NAME); -// assert_eq!(account.mnemonic(), &acc1); -// assert_eq!(account.hd_path(), &hd_path); -// } - -// #[test] -// fn store_a_single_login_with_multi_then_update_pwd_and_load() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let acc1 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let new_password = UserPassword::new("new_password".to_string()); -// let id1 = LoginId::new("first".to_string()); - -// store_login_with_multiple_accounts_at_file( -// &wallet_file, -// acc1.clone(), -// hd_path.clone(), -// id1.clone(), -// &password, -// ) -// .unwrap(); - -// update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); - -// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &new_password).unwrap(); -// let accounts = loaded_login.as_multiple_accounts().unwrap(); -// assert_eq!(accounts.len(), 1); -// let account = accounts -// .get_account(&DEFAULT_FIRST_ACCOUNT_NAME.into()) -// .unwrap(); -// assert_eq!(account.id().as_ref(), DEFAULT_FIRST_ACCOUNT_NAME); -// assert_eq!(account.mnemonic(), &acc1); -// assert_eq!(account.hd_path(), &hd_path); -// } - -// #[test] -// fn store_a_single_login_with_multi_then_update_pwd_with_wrong_current_pwd_fails() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let acc1 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let new_password = UserPassword::new("new_password".to_string()); -// let wrong_password = UserPassword::new("wrong_password".to_string()); -// let id1 = LoginId::new("first".to_string()); - -// store_login_with_multiple_accounts_at_file(&wallet_file, acc1, hd_path, id1, &password) -// .unwrap(); - -// let err = update_encrypted_logins_at_file(&wallet_file, &wrong_password, &new_password) -// .unwrap_err(); -// assert!(matches!(err, BackendError::StoreCipherError { .. })); -// } - -// #[test] -// fn store_a_single_login_with_multi_then_update_pwd_and_load_with_wrong_pwd_fails() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let acc1 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let new_password = UserPassword::new("new_password".to_string()); -// let id1 = LoginId::new("first".to_string()); - -// store_login_with_multiple_accounts_at_file( -// &wallet_file, -// acc1, -// hd_path, -// id1.clone(), -// &password, -// ) -// .unwrap(); - -// update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); - -// let err = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap_err(); -// assert!(matches!(err, BackendError::StoreCipherError { .. })); -// } - -// #[test] -// fn store_a_second_login_with_a_different_password_fails() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let account2 = Mnemonic::generate(24).unwrap(); -// let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let bad_password = UserPassword::new("bad-password".to_string()); -// let id1 = LoginId::new("first".to_string()); -// let id2 = LoginId::new("second".to_string()); - -// store_login_at_file( -// &wallet_file, -// account1, -// cosmos_hd_path.clone(), -// id1, -// &password, -// ) -// .unwrap(); - -// // Can't store a second login if you use different password -// assert!(matches!( -// store_login_at_file(&wallet_file, account2, cosmos_hd_path, id2, &bad_password), -// Err(BackendError::WalletDifferentPasswordDetected), -// )); -// } - -// #[test] -// fn store_a_second_login_with_a_different_password_fails_with_multi() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let account2 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let bad_password = UserPassword::new("bad-password".to_string()); -// let id1 = LoginId::new("first".to_string()); -// let id2 = LoginId::new("second".to_string()); - -// store_login_with_multiple_accounts_at_file( -// &wallet_file, -// account1, -// hd_path.clone(), -// id1, -// &password, -// ) -// .unwrap(); - -// // Can't store a second login if you use different password -// assert!(matches!( -// store_login_with_multiple_accounts_at_file( -// &wallet_file, -// account2, -// hd_path, -// id2, -// &bad_password -// ), -// Err(BackendError::WalletDifferentPasswordDetected), -// )); -// } - -// #[test] -// fn store_two_mnemonic_accounts_gives_different_salts_and_iv() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let account2 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let different_hd_path: DerivationPath = "m".parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let id1 = LoginId::new("first".to_string()); -// let id2 = LoginId::new("second".to_string()); - -// // Store the first account -// store_login_at_file(&wallet_file, account1, hd_path, id1, &password).unwrap(); - -// let stored_wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); -// let encrypted_blob = &stored_wallet -// .get_encrypted_login_by_index(0) -// .unwrap() -// .account; - -// // keep track of salt and iv for future assertion -// let original_iv = encrypted_blob.iv().to_vec(); -// let original_salt = encrypted_blob.salt().to_vec(); - -// // Add an extra account -// store_login_at_file(&wallet_file, account2, different_hd_path, id2, &password).unwrap(); - -// let loaded_accounts = load_existing_wallet_at_file(&wallet_file).unwrap(); -// assert_eq!(loaded_accounts.len(), 2); -// let encrypted_blob = &loaded_accounts -// .get_encrypted_login_by_index(1) -// .unwrap() -// .account; - -// // fresh IV and salt are used -// assert_ne!(original_iv, encrypted_blob.iv()); -// assert_ne!(original_salt, encrypted_blob.salt()); -// } - -// #[test] -// fn store_two_mnemonic_accounts_using_two_logins() { -// let store_dir = tempdir().unwrap(); -// let wallet = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let account2 = Mnemonic::generate(24).unwrap(); -// let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let different_hd_path: DerivationPath = "m".parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let id1 = LoginId::new("first".to_string()); -// let id2 = LoginId::new("second".to_string()); - -// // Store the first account -// store_login_at_file( -// &wallet, -// account1.clone(), -// cosmos_hd_path.clone(), -// id1.clone(), -// &password, -// ) -// .unwrap(); - -// let login = load_existing_login_at_file(&wallet, &id1, &password).unwrap(); -// let acc = login.as_mnemonic_account().unwrap(); -// assert_eq!(&account1, acc.mnemonic()); -// assert_eq!(&cosmos_hd_path, acc.hd_path()); - -// // Add an extra account -// store_login_at_file( -// &wallet, -// account2.clone(), -// different_hd_path.clone(), -// id2.clone(), -// &password, -// ) -// .unwrap(); - -// // first account should be unchanged -// let loaded_login = load_existing_login_at_file(&wallet, &id1, &password).unwrap(); -// let acc1 = loaded_login.as_mnemonic_account().unwrap(); -// assert_eq!(&account1, acc1.mnemonic()); -// assert_eq!(&cosmos_hd_path, acc1.hd_path()); - -// let loaded_login = load_existing_login_at_file(&wallet, &id2, &password).unwrap(); -// let acc2 = loaded_login.as_mnemonic_account().unwrap(); -// assert_eq!(&account2, acc2.mnemonic()); -// assert_eq!(&different_hd_path, acc2.hd_path()); -// } - -// #[test] -// fn store_two_mnemonic_accounts_using_two_logins_then_update_pwd_and_load() { -// let store_dir = tempdir().unwrap(); -// let wallet = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let account2 = Mnemonic::generate(24).unwrap(); -// let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let different_hd_path: DerivationPath = "m".parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let new_password = UserPassword::new("new_password".to_string()); -// let id1 = LoginId::new("first".to_string()); -// let id2 = LoginId::new("second".to_string()); - -// // Store the first login with an account -// store_login_at_file( -// &wallet, -// account1.clone(), -// cosmos_hd_path.clone(), -// id1.clone(), -// &password, -// ) -// .unwrap(); - -// let login = load_existing_login_at_file(&wallet, &id1, &password).unwrap(); -// let acc = login.as_mnemonic_account().unwrap(); -// assert_eq!(&account1, acc.mnemonic()); -// assert_eq!(&cosmos_hd_path, acc.hd_path()); - -// // Store a second login, also with an account -// store_login_at_file( -// &wallet, -// account2.clone(), -// different_hd_path.clone(), -// id2.clone(), -// &password, -// ) -// .unwrap(); - -// update_encrypted_logins_at_file(&wallet, &password, &new_password).unwrap(); - -// // first account should be unchanged -// let loaded_login = load_existing_login_at_file(&wallet, &id1, &new_password).unwrap(); -// let acc1 = loaded_login.as_mnemonic_account().unwrap(); -// assert_eq!(&account1, acc1.mnemonic()); -// assert_eq!(&cosmos_hd_path, acc1.hd_path()); - -// let loaded_login = load_existing_login_at_file(&wallet, &id2, &new_password).unwrap(); -// let acc2 = loaded_login.as_mnemonic_account().unwrap(); -// assert_eq!(&account2, acc2.mnemonic()); -// assert_eq!(&different_hd_path, acc2.hd_path()); -// } - -// #[test] -// fn store_one_mnemonic_account_and_one_multi_account() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let account2 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let different_hd_path: DerivationPath = "m".parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let id1 = LoginId::new("first".to_string()); -// let id2 = LoginId::new("second".to_string()); - -// // Store the first account -// store_login_at_file( -// &wallet_file, -// account1.clone(), -// hd_path.clone(), -// id1.clone(), -// &password, -// ) -// .unwrap(); - -// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); -// let acc = loaded_login.as_mnemonic_account().unwrap(); -// assert_eq!(&account1, acc.mnemonic()); -// assert_eq!(&hd_path, acc.hd_path()); - -// // Add an extra account -// store_login_with_multiple_accounts_at_file( -// &wallet_file, -// account2.clone(), -// different_hd_path.clone(), -// id2.clone(), -// &password, -// ) -// .unwrap(); - -// // first account should be unchanged -// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); -// let acc1 = loaded_login.as_mnemonic_account().unwrap(); -// assert_eq!(&account1, acc1.mnemonic()); -// assert_eq!(&hd_path, acc1.hd_path()); - -// let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); -// let acc2 = loaded_login.as_multiple_accounts().unwrap(); -// assert_eq!(acc2.len(), 1); -// let account = acc2 -// .get_account(&DEFAULT_FIRST_ACCOUNT_NAME.into()) -// .unwrap(); -// assert_eq!(account.id().as_ref(), DEFAULT_FIRST_ACCOUNT_NAME); -// assert_eq!(account.mnemonic(), &account2); -// assert_eq!(account.hd_path(), &different_hd_path); -// } - -// #[test] -// fn remove_non_existent_id_fails() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let id1 = LoginId::new("first".to_string()); -// let id2 = LoginId::new("second".to_string()); - -// store_login_with_multiple_accounts_at_file(&wallet_file, account1, hd_path, id1, &password) -// .unwrap(); - -// // Fails to delete non-existent id in the wallet -// assert!(matches!( -// remove_login_at_file(&wallet_file, &id2), -// Err(BackendError::WalletNoSuchLoginId), -// )); -// } - -// #[test] -// fn store_and_remove_wallet_login_information() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let account2 = Mnemonic::generate(24).unwrap(); -// let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let different_hd_path: DerivationPath = "m".parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let id1 = LoginId::new("first".to_string()); -// let id2 = LoginId::new("second".to_string()); - -// // Store two accounts with two different passwords -// store_login_at_file( -// &wallet_file, -// account1.clone(), -// cosmos_hd_path.clone(), -// id1.clone(), -// &password, -// ) -// .unwrap(); -// store_login_at_file( -// &wallet_file, -// account2.clone(), -// different_hd_path.clone(), -// id2.clone(), -// &password, -// ) -// .unwrap(); - -// // Load and compare -// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); -// let acc1 = loaded_login.as_mnemonic_account().unwrap(); -// assert_eq!(&account1, acc1.mnemonic()); -// assert_eq!(&cosmos_hd_path, acc1.hd_path()); - -// let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); -// let acc2 = loaded_login.as_mnemonic_account().unwrap(); -// assert_eq!(&account2, acc2.mnemonic()); -// assert_eq!(&different_hd_path, acc2.hd_path()); - -// // Delete the second account -// remove_login_at_file(&wallet_file, &id2).unwrap(); - -// // The first account should be unchanged -// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); -// let acc1 = loaded_login.as_mnemonic_account().unwrap(); -// assert_eq!(&account1, acc1.mnemonic()); -// assert_eq!(&cosmos_hd_path, acc1.hd_path()); - -// // And we can't load the second one anymore -// assert!(matches!( -// load_existing_login_at_file(&wallet_file, &id2, &password), -// Err(BackendError::WalletNoSuchLoginId), -// )); - -// // Delete the first account -// assert!(wallet_file.exists()); -// remove_login_at_file(&wallet_file, &id1).unwrap(); - -// // The file should now be removed -// assert!(!wallet_file.exists()); - -// // And trying to load when the file is gone fails -// assert!(matches!( -// load_existing_login_at_file(&wallet_file, &id1, &password), -// Err(BackendError::WalletFileNotFound), -// )); -// } - -// #[test] -// fn append_account_converts_the_type() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let account2 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let id1 = LoginId::new("first".to_string()); -// let id2 = AccountId::new("second".to_string()); - -// store_login_at_file( -// &wallet_file, -// account1.clone(), -// hd_path.clone(), -// id1.clone(), -// &password, -// ) -// .unwrap(); - -// // Check that it's there as the correct non-multiple type -// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); -// let acc = loaded_login.as_mnemonic_account().unwrap(); -// assert_eq!(acc.mnemonic(), &account1); -// assert_eq!(acc.hd_path(), &hd_path); - -// append_account_to_login_at_file( -// &wallet_file, -// account2.clone(), -// hd_path.clone(), -// id1.clone(), -// id2.clone(), -// &password, -// ) -// .unwrap(); - -// // Check that it is now multiple mnemonic type -// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); -// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); -// let expected = vec![ -// WalletAccount::new(id1.into(), MnemonicAccount::new(account1, hd_path.clone())), -// WalletAccount::new(id2, MnemonicAccount::new(account2, hd_path)), -// ] -// .into(); -// assert_eq!(loaded_accounts, &expected); -// } - -// #[test] -// fn append_accounts_to_existing_login() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let account2 = Mnemonic::generate(24).unwrap(); -// let account3 = Mnemonic::generate(24).unwrap(); -// let account4 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let id1 = LoginId::new("first".to_string()); -// let id2 = LoginId::new("second".to_string()); -// let id3 = AccountId::new("third".to_string()); -// let id4 = AccountId::new("fourth".to_string()); - -// store_login_at_file( -// &wallet_file, -// account1.clone(), -// hd_path.clone(), -// id1.clone(), -// &password, -// ) -// .unwrap(); - -// store_login_at_file( -// &wallet_file, -// account2.clone(), -// hd_path.clone(), -// id2.clone(), -// &password, -// ) -// .unwrap(); - -// // Check that it's there as the correct non-multiple type -// let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); -// let acc2 = loaded_login.as_mnemonic_account().unwrap(); -// assert_eq!(acc2.mnemonic(), &account2); -// assert_eq!(acc2.hd_path(), &hd_path); - -// // Add a third and fourth mnenonic grouped together with the second one -// append_account_to_login_at_file( -// &wallet_file, -// account3.clone(), -// hd_path.clone(), -// id2.clone(), -// id3.clone(), -// &password, -// ) -// .unwrap(); -// append_account_to_login_at_file( -// &wallet_file, -// account4.clone(), -// hd_path.clone(), -// id2.clone(), -// id4.clone(), -// &password, -// ) -// .unwrap(); - -// // Check that we can load all four -// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); -// let acc1 = loaded_login.as_mnemonic_account().unwrap(); -// assert_eq!(acc1.mnemonic(), &account1); -// assert_eq!(acc1.hd_path(), &hd_path); - -// let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); -// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); -// let expected = vec![ -// WalletAccount::new(id2.into(), MnemonicAccount::new(account2, hd_path.clone())), -// WalletAccount::new(id3, MnemonicAccount::new(account3, hd_path.clone())), -// WalletAccount::new(id4, MnemonicAccount::new(account4, hd_path)), -// ] -// .into(); -// assert_eq!(loaded_accounts, &expected); -// } - -// #[test] -// fn append_accounts_to_existing_login_with_multi() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let account2 = Mnemonic::generate(24).unwrap(); -// let account3 = Mnemonic::generate(24).unwrap(); -// let account4 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let id1 = LoginId::new("first".to_string()); -// let id2 = LoginId::new("second".to_string()); -// let id3 = AccountId::new("third".to_string()); -// let id4 = AccountId::new("fourth".to_string()); - -// store_login_with_multiple_accounts_at_file( -// &wallet_file, -// account1.clone(), -// hd_path.clone(), -// id1.clone(), -// &password, -// ) -// .unwrap(); - -// store_login_with_multiple_accounts_at_file( -// &wallet_file, -// account2.clone(), -// hd_path.clone(), -// id2.clone(), -// &password, -// ) -// .unwrap(); - -// // Add a third and fourth mnenonic grouped together with the second one -// append_account_to_login_at_file( -// &wallet_file, -// account3.clone(), -// hd_path.clone(), -// id2.clone(), -// id3.clone(), -// &password, -// ) -// .unwrap(); -// append_account_to_login_at_file( -// &wallet_file, -// account4.clone(), -// hd_path.clone(), -// id2.clone(), -// id4.clone(), -// &password, -// ) -// .unwrap(); - -// // Check that we can load all four -// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); -// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); -// let expected = vec![WalletAccount::new( -// DEFAULT_FIRST_ACCOUNT_NAME.into(), -// MnemonicAccount::new(account1, hd_path.clone()), -// )] -// .into(); -// assert_eq!(loaded_accounts, &expected); - -// let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); -// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); -// let expected = vec![ -// WalletAccount::new( -// DEFAULT_FIRST_ACCOUNT_NAME.into(), -// MnemonicAccount::new(account2, hd_path.clone()), -// ), -// WalletAccount::new(id3, MnemonicAccount::new(account3, hd_path.clone())), -// WalletAccount::new(id4, MnemonicAccount::new(account4, hd_path)), -// ] -// .into(); -// assert_eq!(loaded_accounts, &expected); -// } - -// #[test] -// fn append_account_to_existing_login_with_multi_then_update_pwd_and_load() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let account2 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let new_password = UserPassword::new("new_password".to_string()); -// let login_id = LoginId::new("first".to_string()); -// let appended_account = AccountId::new("second".to_string()); - -// store_login_with_multiple_accounts_at_file( -// &wallet_file, -// account1.clone(), -// hd_path.clone(), -// login_id.clone(), -// &password, -// ) -// .unwrap(); - -// // Append a second mnenonic to the same login -// append_account_to_login_at_file( -// &wallet_file, -// account2.clone(), -// hd_path.clone(), -// login_id.clone(), -// appended_account.clone(), -// &password, -// ) -// .unwrap(); - -// // Update the password -// update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); - -// // Expect that we can load these 2 accounts with the new password -// let loaded_login = -// load_existing_login_at_file(&wallet_file, &login_id, &new_password).unwrap(); -// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); -// let expected = vec![ -// WalletAccount::new( -// DEFAULT_FIRST_ACCOUNT_NAME.into(), -// MnemonicAccount::new(account1, hd_path.clone()), -// ), -// WalletAccount::new(appended_account, MnemonicAccount::new(account2, hd_path)), -// ] -// .into(); -// assert_eq!(loaded_accounts, &expected); - -// // Expect that trying to load these 2 accounts with the old password fails -// let err = load_existing_login_at_file(&wallet_file, &login_id, &password).unwrap_err(); -// assert!(matches!(err, BackendError::StoreCipherError { .. })); -// } - -// #[test] -// fn append_the_same_mnemonic_twice_fails() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let id1 = LoginId::new("first".to_string()); -// let id2 = AccountId::new("second".to_string()); - -// store_login_with_multiple_accounts_at_file( -// &wallet_file, -// account1.clone(), -// hd_path.clone(), -// id1.clone(), -// &password, -// ) -// .unwrap(); - -// assert!(matches!( -// append_account_to_login_at_file(&wallet_file, account1, hd_path, id1, id2, &password), -// Err(BackendError::WalletMnemonicAlreadyExistsInWalletLogin), -// )) -// } - -// #[test] -// fn append_the_same_account_name_twice_fails() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let mnemonic1 = Mnemonic::generate(24).unwrap(); -// let mnemonic2 = Mnemonic::generate(24).unwrap(); -// let mnemonic3 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// // The top-level login id. NOTE: the first account id is always set to default. -// let login_id = LoginId::new("my_login_id".to_string()); - -// // Store the first account under login_id. The first account id is always set to default -// // name. -// store_login_with_multiple_accounts_at_file( -// &wallet_file, -// mnemonic1, -// hd_path.clone(), -// login_id.clone(), -// &password, -// ) -// .unwrap(); - -// // Append another account (account2) to the same login (login_id) -// let account2 = AccountId::new("account_2".to_string()); - -// append_account_to_login_at_file( -// &wallet_file, -// mnemonic2, -// hd_path.clone(), -// login_id.clone(), -// account2.clone(), -// &password, -// ) -// .unwrap(); - -// // Appending the third account, with same account id will fail -// assert!(matches!( -// append_account_to_login_at_file( -// &wallet_file, -// mnemonic3, -// hd_path, -// login_id, -// account2, -// &password, -// ), -// Err(BackendError::WalletAccountIdAlreadyExistsInWalletLogin), -// )) -// } - -// #[test] -// fn delete_the_same_account_twice_for_a_login_fails() { -// let store_dir = tempdir().unwrap(); -// let wallet = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let account2 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let id1 = LoginId::new("first".to_string()); -// let id2 = AccountId::new("second".to_string()); - -// store_login_at_file(&wallet, account1, hd_path.clone(), id1.clone(), &password).unwrap(); - -// append_account_to_login_at_file( -// &wallet, -// account2, -// hd_path, -// id1.clone(), -// id2.clone(), -// &password, -// ) -// .unwrap(); - -// remove_account_from_login_at_file(&wallet, &id1, &id2, &password).unwrap(); - -// assert!(matches!( -// remove_account_from_login_at_file(&wallet, &id1, &id2, &password), -// Err(BackendError::WalletNoSuchAccountIdInWalletLogin), -// )); -// } - -// #[test] -// fn delete_the_same_account_twice_for_a_login_fails_with_multi() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let account2 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let id1 = LoginId::new("first".to_string()); -// let id2 = AccountId::new("second".to_string()); - -// store_login_with_multiple_accounts_at_file( -// &wallet_file, -// account1, -// hd_path.clone(), -// id1.clone(), -// &password, -// ) -// .unwrap(); - -// append_account_to_login_at_file( -// &wallet_file, -// account2, -// hd_path, -// id1.clone(), -// id2.clone(), -// &password, -// ) -// .unwrap(); - -// remove_account_from_login_at_file(&wallet_file, &id1, &id2, &password).unwrap(); - -// assert!(matches!( -// remove_account_from_login_at_file(&wallet_file, &id1, &id2, &password), -// Err(BackendError::WalletNoSuchAccountIdInWalletLogin), -// )); -// } - -// #[test] -// fn delete_appended_account_doesnt_affect_others() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let account2 = Mnemonic::generate(24).unwrap(); -// let account3 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let id1 = LoginId::new("first".to_string()); -// let id2 = LoginId::new("second".to_string()); -// let id3 = AccountId::new("third".to_string()); - -// store_login_at_file( -// &wallet_file, -// account1, -// hd_path.clone(), -// id1.clone(), -// &password, -// ) -// .unwrap(); - -// store_login_at_file( -// &wallet_file, -// account2.clone(), -// hd_path.clone(), -// id2.clone(), -// &password, -// ) -// .unwrap(); - -// append_account_to_login_at_file( -// &wallet_file, -// account3.clone(), -// hd_path.clone(), -// id2.clone(), -// id3.clone(), -// &password, -// ) -// .unwrap(); - -// remove_login_at_file(&wallet_file, &id1).unwrap(); - -// // The second login one is still there -// let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); -// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); -// let expected = vec![ -// WalletAccount::new(id2.into(), MnemonicAccount::new(account2, hd_path.clone())), -// WalletAccount::new(id3, MnemonicAccount::new(account3, hd_path)), -// ] -// .into(); -// assert_eq!(loaded_accounts, &expected); -// } - -// #[test] -// fn remove_all_accounts_for_a_login_removes_the_file_when_empty() { -// let store_dir = tempdir().unwrap(); -// let wallet = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let account2 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let id1 = LoginId::new("first".to_string()); -// let id2 = AccountId::new("second".to_string()); - -// store_login_with_multiple_accounts_at_file( -// &wallet, -// account1, -// hd_path.clone(), -// id1.clone(), -// &password, -// ) -// .unwrap(); - -// append_account_to_login_at_file( -// &wallet, -// account2, -// hd_path, -// id1.clone(), -// id2.clone(), -// &password, -// ) -// .unwrap(); - -// remove_account_from_login_at_file( -// &wallet, -// &id1, -// &DEFAULT_FIRST_ACCOUNT_NAME.into(), -// &password, -// ) -// .unwrap(); -// remove_account_from_login_at_file(&wallet, &id1, &id2, &password).unwrap(); - -// // The file should now be removed -// assert!(!wallet.exists()); - -// // And trying to load when the file is gone fails -// assert!(matches!( -// load_existing_login_at_file(&wallet, &id1, &password), -// Err(BackendError::WalletFileNotFound), -// )); -// } - -// #[test] -// fn remove_all_accounts_for_a_login_removes_that_login() { -// let store_dir = tempdir().unwrap(); -// let wallet = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let account2 = Mnemonic::generate(24).unwrap(); -// let account3 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let id1 = LoginId::new("first".to_string()); -// let id2 = AccountId::new("second".to_string()); -// let id3 = LoginId::new("third".to_string()); - -// store_login_with_multiple_accounts_at_file( -// &wallet, -// account1, -// hd_path.clone(), -// id1.clone(), -// &password, -// ) -// .unwrap(); - -// append_account_to_login_at_file( -// &wallet, -// account2, -// hd_path.clone(), -// id1.clone(), -// id2.clone(), -// &password, -// ) -// .unwrap(); - -// store_login_with_multiple_accounts_at_file( -// &wallet, -// account3.clone(), -// hd_path.clone(), -// id3.clone(), -// &password, -// ) -// .unwrap(); - -// remove_account_from_login_at_file( -// &wallet, -// &id1, -// &DEFAULT_FIRST_ACCOUNT_NAME.into(), -// &password, -// ) -// .unwrap(); -// remove_account_from_login_at_file(&wallet, &id1, &id2, &password).unwrap(); - -// // And trying to load when the file is gone fails -// assert!(matches!( -// load_existing_login_at_file(&wallet, &id1, &password), -// Err(BackendError::WalletNoSuchLoginId), -// )); - -// // The other login is still there -// let loaded_login = load_existing_login_at_file(&wallet, &id3, &password).unwrap(); -// let acc3 = loaded_login.as_multiple_accounts().unwrap(); -// let expected = vec![WalletAccount::new( -// DEFAULT_FIRST_ACCOUNT_NAME.into(), -// MnemonicAccount::new(account3, hd_path), -// )] -// .into(); -// assert_eq!(acc3, &expected); -// } - -// #[test] -// fn append_accounts_and_remove_appended_accounts() { -// let store_dir = tempdir().unwrap(); -// let wallet = store_dir.path().join(WALLET_INFO_FILENAME); -// let acc1 = Mnemonic::generate(24).unwrap(); -// let acc2 = Mnemonic::generate(24).unwrap(); -// let acc3 = Mnemonic::generate(24).unwrap(); -// let acc4 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let id1 = LoginId::new("first".to_string()); -// let id2 = LoginId::new("second".to_string()); -// let id3 = AccountId::new("third".to_string()); -// let id4 = AccountId::new("fourth".to_string()); - -// store_login_at_file( -// &wallet, -// acc1.clone(), -// hd_path.clone(), -// id1.clone(), -// &password, -// ) -// .unwrap(); - -// store_login_at_file( -// &wallet, -// acc2.clone(), -// hd_path.clone(), -// id2.clone(), -// &password, -// ) -// .unwrap(); - -// // Add a third and fourth mnenonic grouped together with the second one -// append_account_to_login_at_file( -// &wallet, -// acc3, -// hd_path.clone(), -// id2.clone(), -// id3.clone(), -// &password, -// ) -// .unwrap(); -// append_account_to_login_at_file( -// &wallet, -// acc4.clone(), -// hd_path.clone(), -// id2.clone(), -// id4.clone(), -// &password, -// ) -// .unwrap(); - -// // Delete the third mnemonic, from the second login entry -// remove_account_from_login_at_file(&wallet, &id2, &id3, &password).unwrap(); - -// // Check that we can still load the other accounts -// let loaded_login = load_existing_login_at_file(&wallet, &id2, &password).unwrap(); -// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); -// let expected = vec![ -// WalletAccount::new( -// id2.clone().into(), -// MnemonicAccount::new(acc2, hd_path.clone()), -// ), -// WalletAccount::new(id4.clone(), MnemonicAccount::new(acc4, hd_path.clone())), -// ] -// .into(); -// assert_eq!(loaded_accounts, &expected); - -// // Delete the second and fourth mnemonic from the second login entry removes the login entry -// remove_account_from_login_at_file(&wallet, &id2, &id2.clone().into(), &password).unwrap(); -// remove_account_from_login_at_file(&wallet, &id2, &id4, &password).unwrap(); -// assert!(matches!( -// load_existing_login_at_file(&wallet, &id2, &password), -// Err(BackendError::WalletNoSuchLoginId), -// )); - -// // The first login is still available -// let loaded_login = load_existing_login_at_file(&wallet, &id1, &password).unwrap(); -// let account = loaded_login.as_mnemonic_account().unwrap(); -// assert_eq!(account.mnemonic(), &acc1); -// assert_eq!(account.hd_path(), &hd_path); -// } - -// #[test] -// fn rename_first_account_in_login() { -// let store_dir = tempdir().unwrap(); -// let wallet = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let login_id = LoginId::new("first".to_string()); - -// store_login_with_multiple_accounts_at_file( -// &wallet, -// account1.clone(), -// hd_path.clone(), -// login_id.clone(), -// &password, -// ) -// .unwrap(); - -// let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); -// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); -// let expected = vec![WalletAccount::new( -// DEFAULT_FIRST_ACCOUNT_NAME.into(), -// MnemonicAccount::new(account1.clone(), hd_path.clone()), -// )] -// .into(); -// assert_eq!(loaded_accounts, &expected); - -// let renamed_account = AccountId::new("new_first".to_string()); - -// rename_account_in_login_at_file( -// &wallet, -// &login_id, -// &DEFAULT_FIRST_ACCOUNT_NAME.into(), -// &renamed_account, -// &password, -// ) -// .unwrap(); - -// let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); -// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); -// let expected = vec![WalletAccount::new( -// renamed_account, -// MnemonicAccount::new(account1, hd_path), -// )] -// .into(); -// assert_eq!(loaded_accounts, &expected); -// } - -// #[test] -// fn rename_one_account_in_login_with_two_accounts() { -// let store_dir = tempdir().unwrap(); -// let wallet = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let account2 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let login_id = LoginId::new("first".to_string()); -// let account_id2 = AccountId::new("second".to_string()); -// let renamed_account_id2 = AccountId::new("new_second".to_string()); - -// store_login_with_multiple_accounts_at_file( -// &wallet, -// account1.clone(), -// hd_path.clone(), -// login_id.clone(), -// &password, -// ) -// .unwrap(); - -// append_account_to_login_at_file( -// &wallet, -// account2.clone(), -// hd_path.clone(), -// login_id.clone(), -// account_id2.clone(), -// &password, -// ) -// .unwrap(); - -// // Load and confirm -// let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); -// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); -// let expected = vec![ -// WalletAccount::new( -// DEFAULT_FIRST_ACCOUNT_NAME.into(), -// MnemonicAccount::new(account1.clone(), hd_path.clone()), -// ), -// WalletAccount::new( -// account_id2.clone(), -// MnemonicAccount::new(account2.clone(), hd_path.clone()), -// ), -// ] -// .into(); -// assert_eq!(loaded_accounts, &expected); - -// // Rename the second account to a new name -// rename_account_in_login_at_file( -// &wallet, -// &login_id, -// &account_id2, -// &renamed_account_id2, -// &password, -// ) -// .unwrap(); - -// // Load and confirm -// let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); -// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); -// let expected = vec![ -// WalletAccount::new( -// DEFAULT_FIRST_ACCOUNT_NAME.into(), -// MnemonicAccount::new(account1, hd_path.clone()), -// ), -// WalletAccount::new(renamed_account_id2, MnemonicAccount::new(account2, hd_path)), -// ] -// .into(); -// assert_eq!(loaded_accounts, &expected); -// } - -// #[test] -// fn rename_account_into_existing_account_id_fails() { -// let store_dir = tempdir().unwrap(); -// let wallet = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let account2 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let login_id = LoginId::new("first".to_string()); -// let account_id2 = AccountId::new("second".to_string()); -// let renamed_account_id2 = DEFAULT_FIRST_ACCOUNT_NAME.into(); - -// store_login_with_multiple_accounts_at_file( -// &wallet, -// account1.clone(), -// hd_path.clone(), -// login_id.clone(), -// &password, -// ) -// .unwrap(); - -// append_account_to_login_at_file( -// &wallet, -// account2.clone(), -// hd_path.clone(), -// login_id.clone(), -// account_id2.clone(), -// &password, -// ) -// .unwrap(); - -// // Load and confirm -// let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); -// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); -// let expected = vec![ -// WalletAccount::new( -// DEFAULT_FIRST_ACCOUNT_NAME.into(), -// MnemonicAccount::new(account1.clone(), hd_path.clone()), -// ), -// WalletAccount::new( -// account_id2.clone(), -// MnemonicAccount::new(account2.clone(), hd_path.clone()), -// ), -// ] -// .into(); -// assert_eq!(loaded_accounts, &expected); - -// // Rename the second account to the name of the first one fails -// assert!(matches!( -// rename_account_in_login_at_file( -// &wallet, -// &login_id, -// &account_id2, -// &renamed_account_id2, -// &password, -// ), -// Err(BackendError::WalletAccountIdAlreadyExistsInWalletLogin) -// )); - -// // Load and confirm nothing was changed -// let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); -// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); -// let expected = vec![ -// WalletAccount::new( -// DEFAULT_FIRST_ACCOUNT_NAME.into(), -// MnemonicAccount::new(account1, hd_path.clone()), -// ), -// WalletAccount::new(account_id2, MnemonicAccount::new(account2, hd_path)), -// ] -// .into(); -// assert_eq!(loaded_accounts, &expected); -// } - -// // Test to that decrypts a stored file from the repo, to make sure we are able to decrypt stored -// // wallets created with older versions. -// #[test] -// fn decrypt_stored_wallet() { -// const SAVED_WALLET: &str = "src/wallet_storage/test-data/saved-wallet.json"; -// let wallet_file = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(SAVED_WALLET); - -// let wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); - -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let bad_password = UserPassword::new("bad-password".to_string()); -// let id1 = LoginId::new("first".to_string()); -// let id2 = LoginId::new("second".to_string()); - -// assert!(!wallet.password_can_decrypt_all(&bad_password)); -// assert!(wallet.password_can_decrypt_all(&password)); - -// let acc1 = wallet.decrypt_login(&id1, &password).unwrap(); -// let acc2 = wallet.decrypt_login(&id2, &password).unwrap(); - -// assert!(matches!(acc1, StoredLogin::Mnemonic(_))); -// assert!(matches!(acc2, StoredLogin::Mnemonic(_))); - -// let expected_acc1 = bip39::Mnemonic::from_str("country mean universe text phone begin deputy reject result good cram illness common cluster proud swamp digital patrol spread bar face december base kick").unwrap(); -// let expected_acc2 = bip39::Mnemonic::from_str("home mansion start quiz dress decide hint second dragon sunny juice always steak real minimum art rival skin draw total pulp foot goddess agent").unwrap(); - -// assert_eq!( -// acc1.as_mnemonic_account().unwrap().mnemonic(), -// &expected_acc1 -// ); -// assert_eq!(acc1.as_mnemonic_account().unwrap().hd_path(), &hd_path,); - -// assert_eq!( -// acc2.as_mnemonic_account().unwrap().mnemonic(), -// &expected_acc2 -// ); -// assert_eq!(acc2.as_mnemonic_account().unwrap().hd_path(), &hd_path,); -// } - -// #[test] -// fn decrypt_stored_wallet_1_0_4() { -// const SAVED_WALLET: &str = "src/wallet_storage/test-data/saved-wallet-1.0.4.json"; -// let wallet_file = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(SAVED_WALLET); - -// let wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); - -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password11!".to_string()); -// let bad_password = UserPassword::new("bad-password".to_string()); -// let login_id = LoginId::new("default".to_string()); - -// assert!(!wallet.password_can_decrypt_all(&bad_password)); -// assert!(wallet.password_can_decrypt_all(&password)); - -// let acc1 = wallet.decrypt_login(&login_id, &password).unwrap(); - -// assert!(matches!(acc1, StoredLogin::Mnemonic(_))); - -// let expected_acc1 = bip39::Mnemonic::from_str("arrow capable abstract industry elevator nominee december piece hotel feed lounge web faint sword veteran bundle hour page actual laptop horror gold test warrior").unwrap(); - -// assert_eq!( -// acc1.as_mnemonic_account().unwrap().mnemonic(), -// &expected_acc1 -// ); -// assert_eq!(acc1.as_mnemonic_account().unwrap().hd_path(), &hd_path,); -// } - -// #[test] -// fn decrypt_stored_wallet_1_0_5_with_multiple_accounts() { -// const SAVED_WALLET: &str = "src/wallet_storage/test-data/saved-wallet-1.0.5.json"; -// let wallet_file = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(SAVED_WALLET); - -// let wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); - -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password11!".to_string()); -// let bad_password = UserPassword::new("bad-password".to_string()); -// let login_id = LoginId::new("default".to_string()); - -// assert!(!wallet.password_can_decrypt_all(&bad_password)); -// assert!(wallet.password_can_decrypt_all(&password)); - -// let login = wallet.decrypt_login(&login_id, &password).unwrap(); - -// assert!(matches!(login, StoredLogin::Multiple(_))); - -// let login = login.as_multiple_accounts().unwrap(); -// assert_eq!(login.len(), 4); - -// let expected_mn1 = bip39::Mnemonic::from_str("arrow capable abstract industry elevator nominee december piece hotel feed lounge web faint sword veteran bundle hour page actual laptop horror gold test warrior").unwrap(); -// let expected_mn2 = bip39::Mnemonic::from_str("border hurt skull lunar goddess second danger game dismiss exhaust oven thumb dog drama onion false orchard spice tent next predict invite cherry green").unwrap(); -// let expected_mn3 = bip39::Mnemonic::from_str("gentle crowd rule snap girl urge flat jump winner cluster night sand museum stock grunt quick tree acquire traffic major awake tag rack peasant").unwrap(); -// let expected_mn4 = bip39::Mnemonic::from_str("debris blue skin annual inhale text border rigid spatial lesson coconut yard horn crystal control survey version vote hawk neck frame arrive oblige width").unwrap(); - -// let expected = vec![ -// WalletAccount::new( -// "default".into(), -// MnemonicAccount::new(expected_mn1, hd_path.clone()), -// ), -// WalletAccount::new( -// "account2".into(), -// MnemonicAccount::new(expected_mn2, hd_path.clone()), -// ), -// WalletAccount::new( -// "foobar".into(), -// MnemonicAccount::new(expected_mn3, hd_path.clone()), -// ), -// WalletAccount::new("42".into(), MnemonicAccount::new(expected_mn4, hd_path)), -// ] -// .into(); - -// assert_eq!(login, &expected); -// } - -// #[test] -// fn append_filename() { -// let wallet_file = PathBuf::from("/tmp/saved-wallet.json"); -// let timestamp = OsString::from("42"); -// #[cfg(target_family = "unix")] -// assert_eq!( -// append_timestamp_to_filename(wallet_file.clone(), timestamp.clone(), None) -// .unwrap() -// .into_os_string() -// .into_string() -// .unwrap(), -// "/tmp/saved-wallet-42.json".to_string(), -// ); -// #[cfg(not(target_family = "unix"))] -// assert_eq!( -// append_timestamp_to_filename(wallet_file.clone(), timestamp.clone(), None) -// .unwrap() -// .into_os_string() -// .into_string() -// .unwrap(), -// r"/tmp\saved-wallet-42.json".to_string(), -// ); - -// #[cfg(target_family = "unix")] -// assert_eq!( -// append_timestamp_to_filename(wallet_file, timestamp, Some(3)) -// .unwrap() -// .into_os_string() -// .into_string() -// .unwrap(), -// "/tmp/saved-wallet-42-3.json".to_string(), -// ); -// #[cfg(not(target_family = "unix"))] -// assert_eq!( -// append_timestamp_to_filename(wallet_file, timestamp, Some(3)) -// .unwrap() -// .into_os_string() -// .into_string() -// .unwrap(), -// r"/tmp\saved-wallet-42-3.json".to_string(), -// ); -// } -// } +#[cfg(test)] +mod tests { + use crate::wallet_storage::account_data::WalletAccount; + + use super::*; + use nym_config::defaults::COSMOS_DERIVATION_PATH; + use std::str::FromStr; + use tempfile::tempdir; + + #[test] + fn trying_to_load_nonexistant_wallet_fails() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let id1 = LoginId::new("first".to_string()); + let password = UserPassword::new("password".to_string()); + + assert!(matches!( + load_existing_wallet_at_file(&wallet_file), + Err(BackendError::WalletFileNotFound), + )); + assert!(matches!( + load_existing_login_at_file(&wallet_file, &id1, &password), + Err(BackendError::WalletFileNotFound), + )); + remove_login_at_file(&wallet_file, &id1).unwrap_err(); + } + + #[test] + fn store_single_login() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let id1 = LoginId::new("first".to_string()); + + store_login_at_file(&wallet_file, account1, hd_path, id1.clone(), &password).unwrap(); + + let stored_wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); + assert_eq!(stored_wallet.len(), 1); + + let login = stored_wallet.get_encrypted_login_by_index(0).unwrap(); + assert_eq!(login.id, id1); + + // some actual ciphertext was saved + assert!(!login.account.ciphertext().is_empty()); + } + + #[test] + fn store_single_login_with_multi() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let id1 = LoginId::new("first".to_string()); + + store_login_with_multiple_accounts_at_file( + &wallet_file, + account1, + cosmos_hd_path, + id1.clone(), + &password, + ) + .unwrap(); + + let stored_wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); + assert_eq!(stored_wallet.len(), 1); + + let login = stored_wallet.get_encrypted_login_by_index(0).unwrap(); + assert_eq!(login.id, id1); + + // some actual ciphertext was saved + assert!(!login.account.ciphertext().is_empty()); + } + + #[test] + fn store_single_login_with_multi_then_update_pwd_and_load() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let new_password = UserPassword::new("new_password".to_string()); + let id1 = LoginId::new("first".to_string()); + + store_login_with_multiple_accounts_at_file( + &wallet_file, + account1, + cosmos_hd_path, + id1.clone(), + &password, + ) + .unwrap(); + + update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); + + let stored_wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); + assert_eq!(stored_wallet.len(), 1); + + let login = stored_wallet.get_encrypted_login_by_index(0).unwrap(); + assert_eq!(login.id, id1); + + // some actual ciphertext was saved + assert!(!login.account.ciphertext().is_empty()); + } + + #[test] + fn store_twice_for_the_same_id_fails() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let id1 = LoginId::new("first".to_string()); + + // Store the first login + store_login_at_file( + &wallet_file, + account1.clone(), + hd_path.clone(), + id1.clone(), + &password, + ) + .unwrap(); + + // and storing the same id again fails + assert!(matches!( + store_login_at_file(&wallet_file, account1, hd_path, id1, &password,), + Err(BackendError::WalletLoginIdAlreadyExists), + )); + } + + #[test] + fn store_twice_for_the_same_id_fails_with_multiple() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let id1 = LoginId::new("first".to_string()); + + // Store the first login + store_login_with_multiple_accounts_at_file( + &wallet_file, + account1.clone(), + hd_path.clone(), + id1.clone(), + &password, + ) + .unwrap(); + + // and storing the same id again fails + assert!(matches!( + store_login_with_multiple_accounts_at_file( + &wallet_file, + account1, + hd_path, + id1, + &password, + ), + Err(BackendError::WalletLoginIdAlreadyExists), + )); + } + + #[test] + fn load_with_wrong_password_fails() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let bad_password = UserPassword::new("bad-password".to_string()); + let id1 = LoginId::new("first".to_string()); + + store_login_at_file(&wallet_file, account1, hd_path, id1.clone(), &password).unwrap(); + + // Trying to load it with wrong password now fails + assert!(matches!( + load_existing_login_at_file(&wallet_file, &id1, &bad_password), + Err(BackendError::StoreCipherError { .. }), + )); + } + + #[test] + fn load_with_wrong_password_fails_with_multi() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let bad_password = UserPassword::new("bad-password".to_string()); + let id1 = LoginId::new("first".to_string()); + + store_login_with_multiple_accounts_at_file( + &wallet_file, + account1, + hd_path, + id1.clone(), + &password, + ) + .unwrap(); + + // Trying to load it with wrong password now fails + assert!(matches!( + load_existing_login_at_file(&wallet_file, &id1, &bad_password), + Err(BackendError::StoreCipherError { .. }), + )); + } + + #[test] + fn load_with_wrong_id_fails() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let id1 = LoginId::new("first".to_string()); + let id2 = LoginId::new("second".to_string()); + + store_login_at_file(&wallet_file, account1, hd_path, id1, &password).unwrap(); + + // Trying to load with the wrong id + assert!(matches!( + load_existing_login_at_file(&wallet_file, &id2, &password), + Err(BackendError::WalletNoSuchLoginId), + )); + } + + #[test] + fn load_with_wrong_id_fails_with_multi() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let id1 = LoginId::new("first".to_string()); + let id2 = LoginId::new("second".to_string()); + + store_login_with_multiple_accounts_at_file(&wallet_file, account1, hd_path, id1, &password) + .unwrap(); + + // Trying to load with the wrong id + assert!(matches!( + load_existing_login_at_file(&wallet_file, &id2, &password), + Err(BackendError::WalletNoSuchLoginId), + )); + } + + #[test] + fn store_and_load_a_single_login() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let id1 = LoginId::new("first".to_string()); + + store_login_at_file( + &wallet_file, + account1.clone(), + hd_path.clone(), + id1.clone(), + &password, + ) + .unwrap(); + + let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); + let acc = loaded_login.as_mnemonic_account().unwrap(); + assert_eq!(&account1, acc.mnemonic()); + assert_eq!(&hd_path, acc.hd_path()); + } + + #[test] + fn store_a_single_login_then_update_pwd_and_load() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let new_password = UserPassword::new("new_password".to_string()); + let id1 = LoginId::new("first".to_string()); + + store_login_at_file( + &wallet_file, + account1.clone(), + hd_path.clone(), + id1.clone(), + &password, + ) + .unwrap(); + + update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); + + let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &new_password).unwrap(); + let acc = loaded_login.as_mnemonic_account().unwrap(); + assert_eq!(&account1, acc.mnemonic()); + assert_eq!(&hd_path, acc.hd_path()); + } + + #[test] + fn store_a_single_login_then_update_pwd_with_identical_pwd_is_noop_but_okay() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let id1 = LoginId::new("first".to_string()); + + store_login_at_file( + &wallet_file, + account1.clone(), + hd_path.clone(), + id1.clone(), + &password, + ) + .unwrap(); + + update_encrypted_logins_at_file(&wallet_file, &password, &password).unwrap(); + + let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); + let acc = loaded_login.as_mnemonic_account().unwrap(); + assert_eq!(&account1, acc.mnemonic()); + assert_eq!(&hd_path, acc.hd_path()); + } + + #[test] + fn store_a_single_login_then_update_pwd_with_wrong_current_pwd_fails() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let new_password = UserPassword::new("new_password".to_string()); + let wrong_password = UserPassword::new("wrong_password".to_string()); + let id1 = LoginId::new("first".to_string()); + + store_login_at_file(&wallet_file, account1, hd_path, id1, &password).unwrap(); + + let err = update_encrypted_logins_at_file(&wallet_file, &wrong_password, &new_password) + .unwrap_err(); + assert!(matches!(err, BackendError::StoreCipherError { .. })); + } + + #[test] + fn store_a_single_login_then_update_pwd_and_load_with_wrong_pwd_fails() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let new_password = UserPassword::new("new_password".to_string()); + let id1 = LoginId::new("first".to_string()); + + store_login_at_file(&wallet_file, account1, hd_path, id1.clone(), &password).unwrap(); + + update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); + + let err = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap_err(); + assert!(matches!(err, BackendError::StoreCipherError { .. })); + } + + #[test] + fn store_and_load_a_single_login_with_multi() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let acc1 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let id1 = LoginId::new("first".to_string()); + + store_login_with_multiple_accounts_at_file( + &wallet_file, + acc1.clone(), + hd_path.clone(), + id1.clone(), + &password, + ) + .unwrap(); + + let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); + let accounts = loaded_login.as_multiple_accounts().unwrap(); + assert_eq!(accounts.len(), 1); + let account = accounts + .get_account(&DEFAULT_FIRST_ACCOUNT_NAME.into()) + .unwrap(); + assert_eq!(account.id().as_ref(), DEFAULT_FIRST_ACCOUNT_NAME); + assert_eq!(account.mnemonic(), &acc1); + assert_eq!(account.hd_path(), &hd_path); + } + + #[test] + fn store_a_single_login_with_multi_then_update_pwd_and_load() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let acc1 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let new_password = UserPassword::new("new_password".to_string()); + let id1 = LoginId::new("first".to_string()); + + store_login_with_multiple_accounts_at_file( + &wallet_file, + acc1.clone(), + hd_path.clone(), + id1.clone(), + &password, + ) + .unwrap(); + + update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); + + let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &new_password).unwrap(); + let accounts = loaded_login.as_multiple_accounts().unwrap(); + assert_eq!(accounts.len(), 1); + let account = accounts + .get_account(&DEFAULT_FIRST_ACCOUNT_NAME.into()) + .unwrap(); + assert_eq!(account.id().as_ref(), DEFAULT_FIRST_ACCOUNT_NAME); + assert_eq!(account.mnemonic(), &acc1); + assert_eq!(account.hd_path(), &hd_path); + } + + #[test] + fn store_a_single_login_with_multi_then_update_pwd_with_wrong_current_pwd_fails() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let acc1 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let new_password = UserPassword::new("new_password".to_string()); + let wrong_password = UserPassword::new("wrong_password".to_string()); + let id1 = LoginId::new("first".to_string()); + + store_login_with_multiple_accounts_at_file(&wallet_file, acc1, hd_path, id1, &password) + .unwrap(); + + let err = update_encrypted_logins_at_file(&wallet_file, &wrong_password, &new_password) + .unwrap_err(); + assert!(matches!(err, BackendError::StoreCipherError { .. })); + } + + #[test] + fn store_a_single_login_with_multi_then_update_pwd_and_load_with_wrong_pwd_fails() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let acc1 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let new_password = UserPassword::new("new_password".to_string()); + let id1 = LoginId::new("first".to_string()); + + store_login_with_multiple_accounts_at_file( + &wallet_file, + acc1, + hd_path, + id1.clone(), + &password, + ) + .unwrap(); + + update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); + + let err = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap_err(); + assert!(matches!(err, BackendError::StoreCipherError { .. })); + } + + #[test] + fn store_a_second_login_with_a_different_password_fails() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let account2 = Mnemonic::generate(24).unwrap(); + let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let bad_password = UserPassword::new("bad-password".to_string()); + let id1 = LoginId::new("first".to_string()); + let id2 = LoginId::new("second".to_string()); + + store_login_at_file( + &wallet_file, + account1, + cosmos_hd_path.clone(), + id1, + &password, + ) + .unwrap(); + + // Can't store a second login if you use different password + assert!(matches!( + store_login_at_file(&wallet_file, account2, cosmos_hd_path, id2, &bad_password), + Err(BackendError::WalletDifferentPasswordDetected), + )); + } + + #[test] + fn store_a_second_login_with_a_different_password_fails_with_multi() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let account2 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let bad_password = UserPassword::new("bad-password".to_string()); + let id1 = LoginId::new("first".to_string()); + let id2 = LoginId::new("second".to_string()); + + store_login_with_multiple_accounts_at_file( + &wallet_file, + account1, + hd_path.clone(), + id1, + &password, + ) + .unwrap(); + + // Can't store a second login if you use different password + assert!(matches!( + store_login_with_multiple_accounts_at_file( + &wallet_file, + account2, + hd_path, + id2, + &bad_password + ), + Err(BackendError::WalletDifferentPasswordDetected), + )); + } + + #[test] + fn store_two_mnemonic_accounts_gives_different_salts_and_iv() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let account2 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let different_hd_path: DerivationPath = "m".parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let id1 = LoginId::new("first".to_string()); + let id2 = LoginId::new("second".to_string()); + + // Store the first account + store_login_at_file(&wallet_file, account1, hd_path, id1, &password).unwrap(); + + let stored_wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); + let encrypted_blob = &stored_wallet + .get_encrypted_login_by_index(0) + .unwrap() + .account; + + // keep track of salt and iv for future assertion + let original_iv = encrypted_blob.iv().to_vec(); + let original_salt = encrypted_blob.salt().to_vec(); + + // Add an extra account + store_login_at_file(&wallet_file, account2, different_hd_path, id2, &password).unwrap(); + + let loaded_accounts = load_existing_wallet_at_file(&wallet_file).unwrap(); + assert_eq!(loaded_accounts.len(), 2); + let encrypted_blob = &loaded_accounts + .get_encrypted_login_by_index(1) + .unwrap() + .account; + + // fresh IV and salt are used + assert_ne!(original_iv, encrypted_blob.iv()); + assert_ne!(original_salt, encrypted_blob.salt()); + } + + #[test] + fn store_two_mnemonic_accounts_using_two_logins() { + let store_dir = tempdir().unwrap(); + let wallet = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let account2 = Mnemonic::generate(24).unwrap(); + let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let different_hd_path: DerivationPath = "m".parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let id1 = LoginId::new("first".to_string()); + let id2 = LoginId::new("second".to_string()); + + // Store the first account + store_login_at_file( + &wallet, + account1.clone(), + cosmos_hd_path.clone(), + id1.clone(), + &password, + ) + .unwrap(); + + let login = load_existing_login_at_file(&wallet, &id1, &password).unwrap(); + let acc = login.as_mnemonic_account().unwrap(); + assert_eq!(&account1, acc.mnemonic()); + assert_eq!(&cosmos_hd_path, acc.hd_path()); + + // Add an extra account + store_login_at_file( + &wallet, + account2.clone(), + different_hd_path.clone(), + id2.clone(), + &password, + ) + .unwrap(); + + // first account should be unchanged + let loaded_login = load_existing_login_at_file(&wallet, &id1, &password).unwrap(); + let acc1 = loaded_login.as_mnemonic_account().unwrap(); + assert_eq!(&account1, acc1.mnemonic()); + assert_eq!(&cosmos_hd_path, acc1.hd_path()); + + let loaded_login = load_existing_login_at_file(&wallet, &id2, &password).unwrap(); + let acc2 = loaded_login.as_mnemonic_account().unwrap(); + assert_eq!(&account2, acc2.mnemonic()); + assert_eq!(&different_hd_path, acc2.hd_path()); + } + + #[test] + fn store_two_mnemonic_accounts_using_two_logins_then_update_pwd_and_load() { + let store_dir = tempdir().unwrap(); + let wallet = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let account2 = Mnemonic::generate(24).unwrap(); + let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let different_hd_path: DerivationPath = "m".parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let new_password = UserPassword::new("new_password".to_string()); + let id1 = LoginId::new("first".to_string()); + let id2 = LoginId::new("second".to_string()); + + // Store the first login with an account + store_login_at_file( + &wallet, + account1.clone(), + cosmos_hd_path.clone(), + id1.clone(), + &password, + ) + .unwrap(); + + let login = load_existing_login_at_file(&wallet, &id1, &password).unwrap(); + let acc = login.as_mnemonic_account().unwrap(); + assert_eq!(&account1, acc.mnemonic()); + assert_eq!(&cosmos_hd_path, acc.hd_path()); + + // Store a second login, also with an account + store_login_at_file( + &wallet, + account2.clone(), + different_hd_path.clone(), + id2.clone(), + &password, + ) + .unwrap(); + + update_encrypted_logins_at_file(&wallet, &password, &new_password).unwrap(); + + // first account should be unchanged + let loaded_login = load_existing_login_at_file(&wallet, &id1, &new_password).unwrap(); + let acc1 = loaded_login.as_mnemonic_account().unwrap(); + assert_eq!(&account1, acc1.mnemonic()); + assert_eq!(&cosmos_hd_path, acc1.hd_path()); + + let loaded_login = load_existing_login_at_file(&wallet, &id2, &new_password).unwrap(); + let acc2 = loaded_login.as_mnemonic_account().unwrap(); + assert_eq!(&account2, acc2.mnemonic()); + assert_eq!(&different_hd_path, acc2.hd_path()); + } + + #[test] + fn store_one_mnemonic_account_and_one_multi_account() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let account2 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let different_hd_path: DerivationPath = "m".parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let id1 = LoginId::new("first".to_string()); + let id2 = LoginId::new("second".to_string()); + + // Store the first account + store_login_at_file( + &wallet_file, + account1.clone(), + hd_path.clone(), + id1.clone(), + &password, + ) + .unwrap(); + + let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); + let acc = loaded_login.as_mnemonic_account().unwrap(); + assert_eq!(&account1, acc.mnemonic()); + assert_eq!(&hd_path, acc.hd_path()); + + // Add an extra account + store_login_with_multiple_accounts_at_file( + &wallet_file, + account2.clone(), + different_hd_path.clone(), + id2.clone(), + &password, + ) + .unwrap(); + + // first account should be unchanged + let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); + let acc1 = loaded_login.as_mnemonic_account().unwrap(); + assert_eq!(&account1, acc1.mnemonic()); + assert_eq!(&hd_path, acc1.hd_path()); + + let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); + let acc2 = loaded_login.as_multiple_accounts().unwrap(); + assert_eq!(acc2.len(), 1); + let account = acc2 + .get_account(&DEFAULT_FIRST_ACCOUNT_NAME.into()) + .unwrap(); + assert_eq!(account.id().as_ref(), DEFAULT_FIRST_ACCOUNT_NAME); + assert_eq!(account.mnemonic(), &account2); + assert_eq!(account.hd_path(), &different_hd_path); + } + + #[test] + fn remove_non_existent_id_fails() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let id1 = LoginId::new("first".to_string()); + let id2 = LoginId::new("second".to_string()); + + store_login_with_multiple_accounts_at_file(&wallet_file, account1, hd_path, id1, &password) + .unwrap(); + + // Fails to delete non-existent id in the wallet + assert!(matches!( + remove_login_at_file(&wallet_file, &id2), + Err(BackendError::WalletNoSuchLoginId), + )); + } + + #[test] + fn store_and_remove_wallet_login_information() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let account2 = Mnemonic::generate(24).unwrap(); + let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let different_hd_path: DerivationPath = "m".parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let id1 = LoginId::new("first".to_string()); + let id2 = LoginId::new("second".to_string()); + + // Store two accounts with two different passwords + store_login_at_file( + &wallet_file, + account1.clone(), + cosmos_hd_path.clone(), + id1.clone(), + &password, + ) + .unwrap(); + store_login_at_file( + &wallet_file, + account2.clone(), + different_hd_path.clone(), + id2.clone(), + &password, + ) + .unwrap(); + + // Load and compare + let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); + let acc1 = loaded_login.as_mnemonic_account().unwrap(); + assert_eq!(&account1, acc1.mnemonic()); + assert_eq!(&cosmos_hd_path, acc1.hd_path()); + + let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); + let acc2 = loaded_login.as_mnemonic_account().unwrap(); + assert_eq!(&account2, acc2.mnemonic()); + assert_eq!(&different_hd_path, acc2.hd_path()); + + // Delete the second account + remove_login_at_file(&wallet_file, &id2).unwrap(); + + // The first account should be unchanged + let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); + let acc1 = loaded_login.as_mnemonic_account().unwrap(); + assert_eq!(&account1, acc1.mnemonic()); + assert_eq!(&cosmos_hd_path, acc1.hd_path()); + + // And we can't load the second one anymore + assert!(matches!( + load_existing_login_at_file(&wallet_file, &id2, &password), + Err(BackendError::WalletNoSuchLoginId), + )); + + // Delete the first account + assert!(wallet_file.exists()); + remove_login_at_file(&wallet_file, &id1).unwrap(); + + // The file should now be removed + assert!(!wallet_file.exists()); + + // And trying to load when the file is gone fails + assert!(matches!( + load_existing_login_at_file(&wallet_file, &id1, &password), + Err(BackendError::WalletFileNotFound), + )); + } + + #[test] + fn append_account_converts_the_type() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let account2 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let id1 = LoginId::new("first".to_string()); + let id2 = AccountId::new("second".to_string()); + + store_login_at_file( + &wallet_file, + account1.clone(), + hd_path.clone(), + id1.clone(), + &password, + ) + .unwrap(); + + // Check that it's there as the correct non-multiple type + let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); + let acc = loaded_login.as_mnemonic_account().unwrap(); + assert_eq!(acc.mnemonic(), &account1); + assert_eq!(acc.hd_path(), &hd_path); + + append_account_to_login_at_file( + &wallet_file, + account2.clone(), + hd_path.clone(), + id1.clone(), + id2.clone(), + &password, + ) + .unwrap(); + + // Check that it is now multiple mnemonic type + let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); + let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); + let expected = vec![ + WalletAccount::new(id1.into(), MnemonicAccount::new(account1, hd_path.clone())), + WalletAccount::new(id2, MnemonicAccount::new(account2, hd_path)), + ] + .into(); + assert_eq!(loaded_accounts, &expected); + } + + #[test] + fn append_accounts_to_existing_login() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let account2 = Mnemonic::generate(24).unwrap(); + let account3 = Mnemonic::generate(24).unwrap(); + let account4 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let id1 = LoginId::new("first".to_string()); + let id2 = LoginId::new("second".to_string()); + let id3 = AccountId::new("third".to_string()); + let id4 = AccountId::new("fourth".to_string()); + + store_login_at_file( + &wallet_file, + account1.clone(), + hd_path.clone(), + id1.clone(), + &password, + ) + .unwrap(); + + store_login_at_file( + &wallet_file, + account2.clone(), + hd_path.clone(), + id2.clone(), + &password, + ) + .unwrap(); + + // Check that it's there as the correct non-multiple type + let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); + let acc2 = loaded_login.as_mnemonic_account().unwrap(); + assert_eq!(acc2.mnemonic(), &account2); + assert_eq!(acc2.hd_path(), &hd_path); + + // Add a third and fourth mnenonic grouped together with the second one + append_account_to_login_at_file( + &wallet_file, + account3.clone(), + hd_path.clone(), + id2.clone(), + id3.clone(), + &password, + ) + .unwrap(); + append_account_to_login_at_file( + &wallet_file, + account4.clone(), + hd_path.clone(), + id2.clone(), + id4.clone(), + &password, + ) + .unwrap(); + + // Check that we can load all four + let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); + let acc1 = loaded_login.as_mnemonic_account().unwrap(); + assert_eq!(acc1.mnemonic(), &account1); + assert_eq!(acc1.hd_path(), &hd_path); + + let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); + let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); + let expected = vec![ + WalletAccount::new(id2.into(), MnemonicAccount::new(account2, hd_path.clone())), + WalletAccount::new(id3, MnemonicAccount::new(account3, hd_path.clone())), + WalletAccount::new(id4, MnemonicAccount::new(account4, hd_path)), + ] + .into(); + assert_eq!(loaded_accounts, &expected); + } + + #[test] + fn append_accounts_to_existing_login_with_multi() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let account2 = Mnemonic::generate(24).unwrap(); + let account3 = Mnemonic::generate(24).unwrap(); + let account4 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let id1 = LoginId::new("first".to_string()); + let id2 = LoginId::new("second".to_string()); + let id3 = AccountId::new("third".to_string()); + let id4 = AccountId::new("fourth".to_string()); + + store_login_with_multiple_accounts_at_file( + &wallet_file, + account1.clone(), + hd_path.clone(), + id1.clone(), + &password, + ) + .unwrap(); + + store_login_with_multiple_accounts_at_file( + &wallet_file, + account2.clone(), + hd_path.clone(), + id2.clone(), + &password, + ) + .unwrap(); + + // Add a third and fourth mnenonic grouped together with the second one + append_account_to_login_at_file( + &wallet_file, + account3.clone(), + hd_path.clone(), + id2.clone(), + id3.clone(), + &password, + ) + .unwrap(); + append_account_to_login_at_file( + &wallet_file, + account4.clone(), + hd_path.clone(), + id2.clone(), + id4.clone(), + &password, + ) + .unwrap(); + + // Check that we can load all four + let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); + let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); + let expected = vec![WalletAccount::new( + DEFAULT_FIRST_ACCOUNT_NAME.into(), + MnemonicAccount::new(account1, hd_path.clone()), + )] + .into(); + assert_eq!(loaded_accounts, &expected); + + let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); + let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); + let expected = vec![ + WalletAccount::new( + DEFAULT_FIRST_ACCOUNT_NAME.into(), + MnemonicAccount::new(account2, hd_path.clone()), + ), + WalletAccount::new(id3, MnemonicAccount::new(account3, hd_path.clone())), + WalletAccount::new(id4, MnemonicAccount::new(account4, hd_path)), + ] + .into(); + assert_eq!(loaded_accounts, &expected); + } + + #[test] + fn append_account_to_existing_login_with_multi_then_update_pwd_and_load() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let account2 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let new_password = UserPassword::new("new_password".to_string()); + let login_id = LoginId::new("first".to_string()); + let appended_account = AccountId::new("second".to_string()); + + store_login_with_multiple_accounts_at_file( + &wallet_file, + account1.clone(), + hd_path.clone(), + login_id.clone(), + &password, + ) + .unwrap(); + + // Append a second mnenonic to the same login + append_account_to_login_at_file( + &wallet_file, + account2.clone(), + hd_path.clone(), + login_id.clone(), + appended_account.clone(), + &password, + ) + .unwrap(); + + // Update the password + update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); + + // Expect that we can load these 2 accounts with the new password + let loaded_login = + load_existing_login_at_file(&wallet_file, &login_id, &new_password).unwrap(); + let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); + let expected = vec![ + WalletAccount::new( + DEFAULT_FIRST_ACCOUNT_NAME.into(), + MnemonicAccount::new(account1, hd_path.clone()), + ), + WalletAccount::new(appended_account, MnemonicAccount::new(account2, hd_path)), + ] + .into(); + assert_eq!(loaded_accounts, &expected); + + // Expect that trying to load these 2 accounts with the old password fails + let err = load_existing_login_at_file(&wallet_file, &login_id, &password).unwrap_err(); + assert!(matches!(err, BackendError::StoreCipherError { .. })); + } + + #[test] + fn append_the_same_mnemonic_twice_fails() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let id1 = LoginId::new("first".to_string()); + let id2 = AccountId::new("second".to_string()); + + store_login_with_multiple_accounts_at_file( + &wallet_file, + account1.clone(), + hd_path.clone(), + id1.clone(), + &password, + ) + .unwrap(); + + assert!(matches!( + append_account_to_login_at_file(&wallet_file, account1, hd_path, id1, id2, &password), + Err(BackendError::WalletMnemonicAlreadyExistsInWalletLogin), + )) + } + + #[test] + fn append_the_same_account_name_twice_fails() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let mnemonic1 = Mnemonic::generate(24).unwrap(); + let mnemonic2 = Mnemonic::generate(24).unwrap(); + let mnemonic3 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + // The top-level login id. NOTE: the first account id is always set to default. + let login_id = LoginId::new("my_login_id".to_string()); + + // Store the first account under login_id. The first account id is always set to default + // name. + store_login_with_multiple_accounts_at_file( + &wallet_file, + mnemonic1, + hd_path.clone(), + login_id.clone(), + &password, + ) + .unwrap(); + + // Append another account (account2) to the same login (login_id) + let account2 = AccountId::new("account_2".to_string()); + + append_account_to_login_at_file( + &wallet_file, + mnemonic2, + hd_path.clone(), + login_id.clone(), + account2.clone(), + &password, + ) + .unwrap(); + + // Appending the third account, with same account id will fail + assert!(matches!( + append_account_to_login_at_file( + &wallet_file, + mnemonic3, + hd_path, + login_id, + account2, + &password, + ), + Err(BackendError::WalletAccountIdAlreadyExistsInWalletLogin), + )) + } + + #[test] + fn delete_the_same_account_twice_for_a_login_fails() { + let store_dir = tempdir().unwrap(); + let wallet = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let account2 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let id1 = LoginId::new("first".to_string()); + let id2 = AccountId::new("second".to_string()); + + store_login_at_file(&wallet, account1, hd_path.clone(), id1.clone(), &password).unwrap(); + + append_account_to_login_at_file( + &wallet, + account2, + hd_path, + id1.clone(), + id2.clone(), + &password, + ) + .unwrap(); + + remove_account_from_login_at_file(&wallet, &id1, &id2, &password).unwrap(); + + assert!(matches!( + remove_account_from_login_at_file(&wallet, &id1, &id2, &password), + Err(BackendError::WalletNoSuchAccountIdInWalletLogin), + )); + } + + #[test] + fn delete_the_same_account_twice_for_a_login_fails_with_multi() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let account2 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let id1 = LoginId::new("first".to_string()); + let id2 = AccountId::new("second".to_string()); + + store_login_with_multiple_accounts_at_file( + &wallet_file, + account1, + hd_path.clone(), + id1.clone(), + &password, + ) + .unwrap(); + + append_account_to_login_at_file( + &wallet_file, + account2, + hd_path, + id1.clone(), + id2.clone(), + &password, + ) + .unwrap(); + + remove_account_from_login_at_file(&wallet_file, &id1, &id2, &password).unwrap(); + + assert!(matches!( + remove_account_from_login_at_file(&wallet_file, &id1, &id2, &password), + Err(BackendError::WalletNoSuchAccountIdInWalletLogin), + )); + } + + #[test] + fn delete_appended_account_doesnt_affect_others() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let account2 = Mnemonic::generate(24).unwrap(); + let account3 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let id1 = LoginId::new("first".to_string()); + let id2 = LoginId::new("second".to_string()); + let id3 = AccountId::new("third".to_string()); + + store_login_at_file( + &wallet_file, + account1, + hd_path.clone(), + id1.clone(), + &password, + ) + .unwrap(); + + store_login_at_file( + &wallet_file, + account2.clone(), + hd_path.clone(), + id2.clone(), + &password, + ) + .unwrap(); + + append_account_to_login_at_file( + &wallet_file, + account3.clone(), + hd_path.clone(), + id2.clone(), + id3.clone(), + &password, + ) + .unwrap(); + + remove_login_at_file(&wallet_file, &id1).unwrap(); + + // The second login one is still there + let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); + let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); + let expected = vec![ + WalletAccount::new(id2.into(), MnemonicAccount::new(account2, hd_path.clone())), + WalletAccount::new(id3, MnemonicAccount::new(account3, hd_path)), + ] + .into(); + assert_eq!(loaded_accounts, &expected); + } + + #[test] + fn remove_all_accounts_for_a_login_removes_the_file_when_empty() { + let store_dir = tempdir().unwrap(); + let wallet = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let account2 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let id1 = LoginId::new("first".to_string()); + let id2 = AccountId::new("second".to_string()); + + store_login_with_multiple_accounts_at_file( + &wallet, + account1, + hd_path.clone(), + id1.clone(), + &password, + ) + .unwrap(); + + append_account_to_login_at_file( + &wallet, + account2, + hd_path, + id1.clone(), + id2.clone(), + &password, + ) + .unwrap(); + + remove_account_from_login_at_file( + &wallet, + &id1, + &DEFAULT_FIRST_ACCOUNT_NAME.into(), + &password, + ) + .unwrap(); + remove_account_from_login_at_file(&wallet, &id1, &id2, &password).unwrap(); + + // The file should now be removed + assert!(!wallet.exists()); + + // And trying to load when the file is gone fails + assert!(matches!( + load_existing_login_at_file(&wallet, &id1, &password), + Err(BackendError::WalletFileNotFound), + )); + } + + #[test] + fn remove_all_accounts_for_a_login_removes_that_login() { + let store_dir = tempdir().unwrap(); + let wallet = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let account2 = Mnemonic::generate(24).unwrap(); + let account3 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let id1 = LoginId::new("first".to_string()); + let id2 = AccountId::new("second".to_string()); + let id3 = LoginId::new("third".to_string()); + + store_login_with_multiple_accounts_at_file( + &wallet, + account1, + hd_path.clone(), + id1.clone(), + &password, + ) + .unwrap(); + + append_account_to_login_at_file( + &wallet, + account2, + hd_path.clone(), + id1.clone(), + id2.clone(), + &password, + ) + .unwrap(); + + store_login_with_multiple_accounts_at_file( + &wallet, + account3.clone(), + hd_path.clone(), + id3.clone(), + &password, + ) + .unwrap(); + + remove_account_from_login_at_file( + &wallet, + &id1, + &DEFAULT_FIRST_ACCOUNT_NAME.into(), + &password, + ) + .unwrap(); + remove_account_from_login_at_file(&wallet, &id1, &id2, &password).unwrap(); + + // And trying to load when the file is gone fails + assert!(matches!( + load_existing_login_at_file(&wallet, &id1, &password), + Err(BackendError::WalletNoSuchLoginId), + )); + + // The other login is still there + let loaded_login = load_existing_login_at_file(&wallet, &id3, &password).unwrap(); + let acc3 = loaded_login.as_multiple_accounts().unwrap(); + let expected = vec![WalletAccount::new( + DEFAULT_FIRST_ACCOUNT_NAME.into(), + MnemonicAccount::new(account3, hd_path), + )] + .into(); + assert_eq!(acc3, &expected); + } + + #[test] + fn append_accounts_and_remove_appended_accounts() { + let store_dir = tempdir().unwrap(); + let wallet = store_dir.path().join(WALLET_INFO_FILENAME); + let acc1 = Mnemonic::generate(24).unwrap(); + let acc2 = Mnemonic::generate(24).unwrap(); + let acc3 = Mnemonic::generate(24).unwrap(); + let acc4 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let id1 = LoginId::new("first".to_string()); + let id2 = LoginId::new("second".to_string()); + let id3 = AccountId::new("third".to_string()); + let id4 = AccountId::new("fourth".to_string()); + + store_login_at_file( + &wallet, + acc1.clone(), + hd_path.clone(), + id1.clone(), + &password, + ) + .unwrap(); + + store_login_at_file( + &wallet, + acc2.clone(), + hd_path.clone(), + id2.clone(), + &password, + ) + .unwrap(); + + // Add a third and fourth mnenonic grouped together with the second one + append_account_to_login_at_file( + &wallet, + acc3, + hd_path.clone(), + id2.clone(), + id3.clone(), + &password, + ) + .unwrap(); + append_account_to_login_at_file( + &wallet, + acc4.clone(), + hd_path.clone(), + id2.clone(), + id4.clone(), + &password, + ) + .unwrap(); + + // Delete the third mnemonic, from the second login entry + remove_account_from_login_at_file(&wallet, &id2, &id3, &password).unwrap(); + + // Check that we can still load the other accounts + let loaded_login = load_existing_login_at_file(&wallet, &id2, &password).unwrap(); + let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); + let expected = vec![ + WalletAccount::new( + id2.clone().into(), + MnemonicAccount::new(acc2, hd_path.clone()), + ), + WalletAccount::new(id4.clone(), MnemonicAccount::new(acc4, hd_path.clone())), + ] + .into(); + assert_eq!(loaded_accounts, &expected); + + // Delete the second and fourth mnemonic from the second login entry removes the login entry + remove_account_from_login_at_file(&wallet, &id2, &id2.clone().into(), &password).unwrap(); + remove_account_from_login_at_file(&wallet, &id2, &id4, &password).unwrap(); + assert!(matches!( + load_existing_login_at_file(&wallet, &id2, &password), + Err(BackendError::WalletNoSuchLoginId), + )); + + // The first login is still available + let loaded_login = load_existing_login_at_file(&wallet, &id1, &password).unwrap(); + let account = loaded_login.as_mnemonic_account().unwrap(); + assert_eq!(account.mnemonic(), &acc1); + assert_eq!(account.hd_path(), &hd_path); + } + + #[test] + fn rename_first_account_in_login() { + let store_dir = tempdir().unwrap(); + let wallet = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let login_id = LoginId::new("first".to_string()); + + store_login_with_multiple_accounts_at_file( + &wallet, + account1.clone(), + hd_path.clone(), + login_id.clone(), + &password, + ) + .unwrap(); + + let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); + let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); + let expected = vec![WalletAccount::new( + DEFAULT_FIRST_ACCOUNT_NAME.into(), + MnemonicAccount::new(account1.clone(), hd_path.clone()), + )] + .into(); + assert_eq!(loaded_accounts, &expected); + + let renamed_account = AccountId::new("new_first".to_string()); + + rename_account_in_login_at_file( + &wallet, + &login_id, + &DEFAULT_FIRST_ACCOUNT_NAME.into(), + &renamed_account, + &password, + ) + .unwrap(); + + let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); + let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); + let expected = vec![WalletAccount::new( + renamed_account, + MnemonicAccount::new(account1, hd_path), + )] + .into(); + assert_eq!(loaded_accounts, &expected); + } + + #[test] + fn rename_one_account_in_login_with_two_accounts() { + let store_dir = tempdir().unwrap(); + let wallet = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let account2 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let login_id = LoginId::new("first".to_string()); + let account_id2 = AccountId::new("second".to_string()); + let renamed_account_id2 = AccountId::new("new_second".to_string()); + + store_login_with_multiple_accounts_at_file( + &wallet, + account1.clone(), + hd_path.clone(), + login_id.clone(), + &password, + ) + .unwrap(); + + append_account_to_login_at_file( + &wallet, + account2.clone(), + hd_path.clone(), + login_id.clone(), + account_id2.clone(), + &password, + ) + .unwrap(); + + // Load and confirm + let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); + let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); + let expected = vec![ + WalletAccount::new( + DEFAULT_FIRST_ACCOUNT_NAME.into(), + MnemonicAccount::new(account1.clone(), hd_path.clone()), + ), + WalletAccount::new( + account_id2.clone(), + MnemonicAccount::new(account2.clone(), hd_path.clone()), + ), + ] + .into(); + assert_eq!(loaded_accounts, &expected); + + // Rename the second account to a new name + rename_account_in_login_at_file( + &wallet, + &login_id, + &account_id2, + &renamed_account_id2, + &password, + ) + .unwrap(); + + // Load and confirm + let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); + let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); + let expected = vec![ + WalletAccount::new( + DEFAULT_FIRST_ACCOUNT_NAME.into(), + MnemonicAccount::new(account1, hd_path.clone()), + ), + WalletAccount::new(renamed_account_id2, MnemonicAccount::new(account2, hd_path)), + ] + .into(); + assert_eq!(loaded_accounts, &expected); + } + + #[test] + fn rename_account_into_existing_account_id_fails() { + let store_dir = tempdir().unwrap(); + let wallet = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let account2 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let login_id = LoginId::new("first".to_string()); + let account_id2 = AccountId::new("second".to_string()); + let renamed_account_id2 = DEFAULT_FIRST_ACCOUNT_NAME.into(); + + store_login_with_multiple_accounts_at_file( + &wallet, + account1.clone(), + hd_path.clone(), + login_id.clone(), + &password, + ) + .unwrap(); + + append_account_to_login_at_file( + &wallet, + account2.clone(), + hd_path.clone(), + login_id.clone(), + account_id2.clone(), + &password, + ) + .unwrap(); + + // Load and confirm + let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); + let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); + let expected = vec![ + WalletAccount::new( + DEFAULT_FIRST_ACCOUNT_NAME.into(), + MnemonicAccount::new(account1.clone(), hd_path.clone()), + ), + WalletAccount::new( + account_id2.clone(), + MnemonicAccount::new(account2.clone(), hd_path.clone()), + ), + ] + .into(); + assert_eq!(loaded_accounts, &expected); + + // Rename the second account to the name of the first one fails + assert!(matches!( + rename_account_in_login_at_file( + &wallet, + &login_id, + &account_id2, + &renamed_account_id2, + &password, + ), + Err(BackendError::WalletAccountIdAlreadyExistsInWalletLogin) + )); + + // Load and confirm nothing was changed + let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); + let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); + let expected = vec![ + WalletAccount::new( + DEFAULT_FIRST_ACCOUNT_NAME.into(), + MnemonicAccount::new(account1, hd_path.clone()), + ), + WalletAccount::new(account_id2, MnemonicAccount::new(account2, hd_path)), + ] + .into(); + assert_eq!(loaded_accounts, &expected); + } + + // Test to that decrypts a stored file from the repo, to make sure we are able to decrypt stored + // wallets created with older versions. + #[test] + fn decrypt_stored_wallet() { + const SAVED_WALLET: &str = "src/wallet_storage/test-data/saved-wallet.json"; + let wallet_file = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(SAVED_WALLET); + + let wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); + + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let bad_password = UserPassword::new("bad-password".to_string()); + let id1 = LoginId::new("first".to_string()); + let id2 = LoginId::new("second".to_string()); + + assert!(!wallet.password_can_decrypt_all(&bad_password)); + assert!(wallet.password_can_decrypt_all(&password)); + + let acc1 = wallet.decrypt_login(&id1, &password).unwrap(); + let acc2 = wallet.decrypt_login(&id2, &password).unwrap(); + + assert!(matches!(acc1, StoredLogin::Mnemonic(_))); + assert!(matches!(acc2, StoredLogin::Mnemonic(_))); + + let expected_acc1 = bip39::Mnemonic::from_str("country mean universe text phone begin deputy reject result good cram illness common cluster proud swamp digital patrol spread bar face december base kick").unwrap(); + let expected_acc2 = bip39::Mnemonic::from_str("home mansion start quiz dress decide hint second dragon sunny juice always steak real minimum art rival skin draw total pulp foot goddess agent").unwrap(); + + assert_eq!( + acc1.as_mnemonic_account().unwrap().mnemonic(), + &expected_acc1 + ); + assert_eq!(acc1.as_mnemonic_account().unwrap().hd_path(), &hd_path,); + + assert_eq!( + acc2.as_mnemonic_account().unwrap().mnemonic(), + &expected_acc2 + ); + assert_eq!(acc2.as_mnemonic_account().unwrap().hd_path(), &hd_path,); + } + + #[test] + fn decrypt_stored_wallet_1_0_4() { + const SAVED_WALLET: &str = "src/wallet_storage/test-data/saved-wallet-1.0.4.json"; + let wallet_file = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(SAVED_WALLET); + + let wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); + + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password11!".to_string()); + let bad_password = UserPassword::new("bad-password".to_string()); + let login_id = LoginId::new("default".to_string()); + + assert!(!wallet.password_can_decrypt_all(&bad_password)); + assert!(wallet.password_can_decrypt_all(&password)); + + let acc1 = wallet.decrypt_login(&login_id, &password).unwrap(); + + assert!(matches!(acc1, StoredLogin::Mnemonic(_))); + + let expected_acc1 = bip39::Mnemonic::from_str("arrow capable abstract industry elevator nominee december piece hotel feed lounge web faint sword veteran bundle hour page actual laptop horror gold test warrior").unwrap(); + + assert_eq!( + acc1.as_mnemonic_account().unwrap().mnemonic(), + &expected_acc1 + ); + assert_eq!(acc1.as_mnemonic_account().unwrap().hd_path(), &hd_path,); + } + + #[test] + fn decrypt_stored_wallet_1_0_5_with_multiple_accounts() { + const SAVED_WALLET: &str = "src/wallet_storage/test-data/saved-wallet-1.0.5.json"; + let wallet_file = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(SAVED_WALLET); + + let wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); + + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password11!".to_string()); + let bad_password = UserPassword::new("bad-password".to_string()); + let login_id = LoginId::new("default".to_string()); + + assert!(!wallet.password_can_decrypt_all(&bad_password)); + assert!(wallet.password_can_decrypt_all(&password)); + + let login = wallet.decrypt_login(&login_id, &password).unwrap(); + + assert!(matches!(login, StoredLogin::Multiple(_))); + + let login = login.as_multiple_accounts().unwrap(); + assert_eq!(login.len(), 4); + + let expected_mn1 = bip39::Mnemonic::from_str("arrow capable abstract industry elevator nominee december piece hotel feed lounge web faint sword veteran bundle hour page actual laptop horror gold test warrior").unwrap(); + let expected_mn2 = bip39::Mnemonic::from_str("border hurt skull lunar goddess second danger game dismiss exhaust oven thumb dog drama onion false orchard spice tent next predict invite cherry green").unwrap(); + let expected_mn3 = bip39::Mnemonic::from_str("gentle crowd rule snap girl urge flat jump winner cluster night sand museum stock grunt quick tree acquire traffic major awake tag rack peasant").unwrap(); + let expected_mn4 = bip39::Mnemonic::from_str("debris blue skin annual inhale text border rigid spatial lesson coconut yard horn crystal control survey version vote hawk neck frame arrive oblige width").unwrap(); + + let expected = vec![ + WalletAccount::new( + "default".into(), + MnemonicAccount::new(expected_mn1, hd_path.clone()), + ), + WalletAccount::new( + "account2".into(), + MnemonicAccount::new(expected_mn2, hd_path.clone()), + ), + WalletAccount::new( + "foobar".into(), + MnemonicAccount::new(expected_mn3, hd_path.clone()), + ), + WalletAccount::new("42".into(), MnemonicAccount::new(expected_mn4, hd_path)), + ] + .into(); + + assert_eq!(login, &expected); + } + + #[test] + fn append_filename() { + let wallet_file = PathBuf::from("/tmp/saved-wallet.json"); + let timestamp = OsString::from("42"); + #[cfg(target_family = "unix")] + assert_eq!( + append_timestamp_to_filename(wallet_file.clone(), timestamp.clone(), None) + .unwrap() + .into_os_string() + .into_string() + .unwrap(), + "/tmp/saved-wallet-42.json".to_string(), + ); + #[cfg(not(target_family = "unix"))] + assert_eq!( + append_timestamp_to_filename(wallet_file.clone(), timestamp.clone(), None) + .unwrap() + .into_os_string() + .into_string() + .unwrap(), + r"/tmp\saved-wallet-42.json".to_string(), + ); + + #[cfg(target_family = "unix")] + assert_eq!( + append_timestamp_to_filename(wallet_file, timestamp, Some(3)) + .unwrap() + .into_os_string() + .into_string() + .unwrap(), + "/tmp/saved-wallet-42-3.json".to_string(), + ); + #[cfg(not(target_family = "unix"))] + assert_eq!( + append_timestamp_to_filename(wallet_file, timestamp, Some(3)) + .unwrap() + .into_os_string() + .into_string() + .unwrap(), + r"/tmp\saved-wallet-42-3.json".to_string(), + ); + } +} \ No newline at end of file From 4e9f1bc0eddf0bebfe9b8fb38c363ff77d3243c9 Mon Sep 17 00:00:00 2001 From: Tommy Verrall Date: Tue, 1 Apr 2025 17:06:21 +0200 Subject: [PATCH 05/48] migrate to v2 - lots to check and do --- nym-wallet/src-tauri/src/config/mod.rs | 272 +- .../src-tauri/src/wallet_storage/mod.rs | 3612 +++++++++-------- 2 files changed, 1949 insertions(+), 1935 deletions(-) diff --git a/nym-wallet/src-tauri/src/config/mod.rs b/nym-wallet/src-tauri/src/config/mod.rs index 1f79075157..5b7dec4099 100644 --- a/nym-wallet/src-tauri/src/config/mod.rs +++ b/nym-wallet/src-tauri/src/config/mod.rs @@ -11,6 +11,7 @@ use nym_validator_client::nyxd::AccountId as CosmosAccountId; use serde::de::DeserializeOwned; use serde::{Deserialize, Serialize}; use strum::IntoEnumIterator; +use tauri::Manager; use url::Url; use nym_config::defaults::{DenomDetailsOwned, NymNetworkDetails, ValidatorDetails}; @@ -104,32 +105,35 @@ impl NetworkConfig { } impl Config { - fn root_directory() -> PathBuf { - tauri::api::path::config_dir().expect("Failed to get config directory") + fn root_directory(app_handle: &tauri::AppHandle) -> PathBuf { + app_handle + .path() + .local_data_dir() + .expect("Failed to get config directory") } - fn config_directory() -> PathBuf { - Self::root_directory().join(CONFIG_DIR_NAME) + fn config_directory(app_handle: &tauri::AppHandle) -> PathBuf { + Self::root_directory(app_handle).join(CONFIG_DIR_NAME) } - fn config_file_path(network: Option) -> PathBuf { + fn config_file_path(app_handle: &tauri::AppHandle, network: Option) -> PathBuf { if let Some(network) = network { let network_filename = format!("{}.toml", network.as_key()); - Self::config_directory().join(network_filename) + Self::config_directory(app_handle).join(network_filename) } else { - Self::config_directory().join(CONFIG_FILENAME) + Self::config_directory(app_handle).join(CONFIG_FILENAME) } } - pub fn save_to_files(&self) -> io::Result<()> { + pub fn save_to_files(&self, app_handle: &tauri::AppHandle) -> io::Result<()> { log::trace!("Config::save_to_file"); // Make sure the whole directory structure actually exists - fs::create_dir_all(Self::config_directory())?; + fs::create_dir_all(Self::config_directory(app_handle))?; // Global config if let Some(global) = &self.global { - let location = Self::config_file_path(None); + let location = Self::config_file_path(app_handle, None); match toml::to_string_pretty(&global) .map_err(|toml_err| io::Error::new(io::ErrorKind::Other, toml_err)) @@ -150,7 +154,7 @@ impl Config { } }; - let location = Self::config_file_path(Some(network)); + let location = Self::config_file_path(app_handle, Some(network)); match toml::to_string_pretty(config) .map_err(|toml_err| io::Error::new(io::ErrorKind::Other, toml_err)) .map(|toml| fs::write(location.clone(), toml)) @@ -162,10 +166,10 @@ impl Config { Ok(()) } - pub fn load_from_files() -> Self { + pub fn load_from_files(app_handle: &tauri::AppHandle) -> Self { // Global let global = { - let file = Self::config_file_path(None); + let file = Self::config_file_path(app_handle, None); match load_from_file::(file.clone()) { Ok(global) => { log::debug!("Loaded from file {:#?}", file); @@ -181,7 +185,7 @@ impl Config { // One file per network let mut networks = HashMap::new(); for network in WalletNetwork::iter() { - let file = Self::config_file_path(Some(network)); + let file = Self::config_file_path(app_handle, Some(network)); match load_from_file::(file.clone()) { Ok(config) => { log::trace!("Loaded from file {:#?}", file); @@ -261,6 +265,8 @@ impl Config { } } + //////// + pub fn select_nyxd_url(&mut self, nyxd_url: Url, network: WalletNetwork) { if let Some(net) = self.networks.get_mut(&network.as_key()) { net.selected_nyxd_url = Some(nyxd_url); @@ -531,139 +537,139 @@ impl From for NetworkDetails { } } -#[cfg(test)] -mod tests { - use super::*; +// #[cfg(test)] +// mod tests { +// use super::*; - fn test_config() -> Config { - let netconfig = NetworkConfig { - selected_nyxd_url: None, - selected_api_url: Some("https://my_api_url.com".parse().unwrap()), +// fn test_config() -> Config { +// let netconfig = NetworkConfig { +// selected_nyxd_url: None, +// selected_api_url: Some("https://my_api_url.com".parse().unwrap()), - nyxd_urls: Some(vec![ - ValidatorConfigEntry { - nyxd_url: "https://foo".parse().unwrap(), - nyxd_name: Some("FooName".to_string()), - api_url: None, - }, - ValidatorConfigEntry { - nyxd_url: "https://bar".parse().unwrap(), - nyxd_name: None, - api_url: Some("https://bar/api".parse().unwrap()), - }, - ValidatorConfigEntry { - nyxd_url: "https://baz".parse().unwrap(), - nyxd_name: None, - api_url: Some("https://baz/api".parse().unwrap()), - }, - ]), - ..NetworkConfig::default() - }; +// nyxd_urls: Some(vec![ +// ValidatorConfigEntry { +// nyxd_url: "https://foo".parse().unwrap(), +// nyxd_name: Some("FooName".to_string()), +// api_url: None, +// }, +// ValidatorConfigEntry { +// nyxd_url: "https://bar".parse().unwrap(), +// nyxd_name: None, +// api_url: Some("https://bar/api".parse().unwrap()), +// }, +// ValidatorConfigEntry { +// nyxd_url: "https://baz".parse().unwrap(), +// nyxd_name: None, +// api_url: Some("https://baz/api".parse().unwrap()), +// }, +// ]), +// ..NetworkConfig::default() +// }; - Config { - base: Base::default(), - global: Some(GlobalConfig::default()), - networks: [(WalletNetwork::MAINNET.as_key(), netconfig)] - .into_iter() - .collect(), - } - } +// Config { +// base: Base::default(), +// global: Some(GlobalConfig::default()), +// networks: [(WalletNetwork::MAINNET.as_key(), netconfig)] +// .into_iter() +// .collect(), +// } +// } - #[test] - fn serialize_to_toml() { - let config = test_config(); - let netconfig = &config.networks[&WalletNetwork::MAINNET.as_key()]; - assert_eq!( - toml::to_string_pretty(netconfig).unwrap(), - r#"version = 1 -selected_api_url = 'https://my_api_url.com/' +// #[test] +// fn serialize_to_toml() { +// let config = test_config(); +// let netconfig = &config.networks[&WalletNetwork::MAINNET.as_key()]; +// assert_eq!( +// toml::to_string_pretty(netconfig).unwrap(), +// r#"version = 1 +// selected_api_url = 'https://my_api_url.com/' -[[nyxd_urls]] -nyxd_url = 'https://foo/' -nyxd_name = 'FooName' +// [[nyxd_urls]] +// nyxd_url = 'https://foo/' +// nyxd_name = 'FooName' -[[nyxd_urls]] -nyxd_url = 'https://bar/' -api_url = 'https://bar/api' +// [[nyxd_urls]] +// nyxd_url = 'https://bar/' +// api_url = 'https://bar/api' -[[nyxd_urls]] -nyxd_url = 'https://baz/' -api_url = 'https://baz/api' -"# - ); - } +// [[nyxd_urls]] +// nyxd_url = 'https://baz/' +// api_url = 'https://baz/api' +// "# +// ); +// } - #[test] - fn serialize_to_json() { - let config = test_config(); - let netconfig = &config.networks[&WalletNetwork::MAINNET.as_key()]; - println!("{}", serde_json::to_string_pretty(netconfig).unwrap()); - assert_eq!( - serde_json::to_string_pretty(netconfig).unwrap(), - r#"{ - "version": 1, - "selected_nyxd_url": null, - "default_nyxd_url": null, - "selected_api_url": "https://my_api_url.com/", - "nyxd_urls": [ - { - "nyxd_url": "https://foo/", - "nyxd_name": "FooName", - "api_url": null - }, - { - "nyxd_url": "https://bar/", - "nyxd_name": null, - "api_url": "https://bar/api" - }, - { - "nyxd_url": "https://baz/", - "nyxd_name": null, - "api_url": "https://baz/api" - } - ] -}"# - ); - } +// #[test] +// fn serialize_to_json() { +// let config = test_config(); +// let netconfig = &config.networks[&WalletNetwork::MAINNET.as_key()]; +// println!("{}", serde_json::to_string_pretty(netconfig).unwrap()); +// assert_eq!( +// serde_json::to_string_pretty(netconfig).unwrap(), +// r#"{ +// "version": 1, +// "selected_nyxd_url": null, +// "default_nyxd_url": null, +// "selected_api_url": "https://my_api_url.com/", +// "nyxd_urls": [ +// { +// "nyxd_url": "https://foo/", +// "nyxd_name": "FooName", +// "api_url": null +// }, +// { +// "nyxd_url": "https://bar/", +// "nyxd_name": null, +// "api_url": "https://bar/api" +// }, +// { +// "nyxd_url": "https://baz/", +// "nyxd_name": null, +// "api_url": "https://baz/api" +// } +// ] +// }"# +// ); +// } - #[test] - fn serialize_and_deserialize_to_toml() { - let config = test_config(); - let netconfig = &config.networks[&WalletNetwork::MAINNET.as_key()]; - let config_str = toml::to_string_pretty(netconfig).unwrap(); - let config_from_toml: NetworkConfig = toml::from_str(&config_str).unwrap(); - assert_eq!(netconfig, &config_from_toml); - } +// #[test] +// fn serialize_and_deserialize_to_toml() { +// let config = test_config(); +// let netconfig = &config.networks[&WalletNetwork::MAINNET.as_key()]; +// let config_str = toml::to_string_pretty(netconfig).unwrap(); +// let config_from_toml: NetworkConfig = toml::from_str(&config_str).unwrap(); +// assert_eq!(netconfig, &config_from_toml); +// } - #[test] - fn get_urls_parsed_from_config() { - let config = test_config(); +// #[test] +// fn get_urls_parsed_from_config() { +// let config = test_config(); - let nyxd_url = config - .get_configured_validators(WalletNetwork::MAINNET) - .next() - .map(|v| v.nyxd_url) - .unwrap(); - assert_eq!(nyxd_url.as_ref(), "https://foo/"); +// let nyxd_url = config +// .get_configured_validators(WalletNetwork::MAINNET) +// .next() +// .map(|v| v.nyxd_url) +// .unwrap(); +// assert_eq!(nyxd_url.as_ref(), "https://foo/"); - // The first entry is missing an API URL - let api_url = config - .get_configured_validators(WalletNetwork::MAINNET) - .next() - .and_then(|v| v.api_url); - assert_eq!(api_url, None); - } +// // The first entry is missing an API URL +// let api_url = config +// .get_configured_validators(WalletNetwork::MAINNET) +// .next() +// .and_then(|v| v.api_url); +// assert_eq!(api_url, None); +// } - #[test] - fn get_urls_from_defaults() { - let config = Config::default(); +// #[test] +// fn get_urls_from_defaults() { +// let config = Config::default(); - let nyxd_url = config - .get_base_validators(WalletNetwork::MAINNET) - .next() - .map(|v| v.nyxd_url) - .unwrap(); - assert_eq!(nyxd_url.as_ref(), "https://rpc.nymtech.net/"); +// let nyxd_url = config +// .get_base_validators(WalletNetwork::MAINNET) +// .next() +// .map(|v| v.nyxd_url) +// .unwrap(); +// assert_eq!(nyxd_url.as_ref(), "https://rpc.nymtech.net/"); let api_url = config .get_base_validators(WalletNetwork::MAINNET) diff --git a/nym-wallet/src-tauri/src/wallet_storage/mod.rs b/nym-wallet/src-tauri/src/wallet_storage/mod.rs index 115823da4a..faa026452d 100644 --- a/nym-wallet/src-tauri/src/wallet_storage/mod.rs +++ b/nym-wallet/src-tauri/src/wallet_storage/mod.rs @@ -19,6 +19,7 @@ use nym_validator_client::nyxd::bip32::DerivationPath; use std::ffi::OsString; use std::fs::{self, create_dir_all, OpenOptions}; use std::path::{Path, PathBuf}; +use tauri::Manager; use time::OffsetDateTime; #[cfg(test)] @@ -37,14 +38,18 @@ pub(crate) const DEFAULT_LOGIN_ID: &str = "default"; /// this name. pub(crate) const DEFAULT_FIRST_ACCOUNT_NAME: &str = "Account 1"; -fn get_storage_directory() -> Result { - tauri::api::path::local_data_dir() +fn get_storage_directory(app_handle: &tauri::AppHandle) -> Result { + app_handle + .path() + .local_data_dir() .map(|dir| dir.join(STORAGE_DIR_NAME)) .ok_or(BackendError::UnknownStorageDirectory) } -pub(crate) fn wallet_login_filepath() -> Result { - get_storage_directory().map(|dir| dir.join(WALLET_INFO_FILENAME)) +pub(crate) fn wallet_login_filepath( + app_handle: &tauri::AppHandle, +) -> Result { + get_storage_directory(app_handle).map(|dir| dir.join(WALLET_INFO_FILENAME)) } fn write_to_file(filepath: &Path, wallet: &StoredWallet) -> Result<(), BackendError> { @@ -59,8 +64,10 @@ fn write_to_file(filepath: &Path, wallet: &StoredWallet) -> Result<(), BackendEr /// Load stored wallet file #[allow(unused)] -pub(crate) fn load_existing_wallet() -> Result { - let store_dir = get_storage_directory()?; +pub(crate) fn load_existing_wallet( + app_handle: &tauri::AppHandle, +) -> Result { + let store_dir = get_storage_directory(app_handle)?; let filepath = store_dir.join(WALLET_INFO_FILENAME); load_existing_wallet_at_file(&filepath) } @@ -77,10 +84,11 @@ fn load_existing_wallet_at_file(filepath: &Path) -> Result Result { - let store_dir = get_storage_directory()?; + let store_dir = get_storage_directory(app_handle)?; let filepath = store_dir.join(WALLET_INFO_FILENAME); load_existing_login_at_file(&filepath, id, password) } @@ -468,1801 +476,1801 @@ fn rename_account_in_login_at_file( write_to_file(filepath, &stored_wallet) } -#[cfg(test)] -mod tests { - use crate::wallet_storage::account_data::WalletAccount; - - use super::*; - use nym_config::defaults::COSMOS_DERIVATION_PATH; - use std::str::FromStr; - use tempfile::tempdir; - - #[test] - fn trying_to_load_nonexistant_wallet_fails() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let id1 = LoginId::new("first".to_string()); - let password = UserPassword::new("password".to_string()); - - assert!(matches!( - load_existing_wallet_at_file(&wallet_file), - Err(BackendError::WalletFileNotFound), - )); - assert!(matches!( - load_existing_login_at_file(&wallet_file, &id1, &password), - Err(BackendError::WalletFileNotFound), - )); - remove_login_at_file(&wallet_file, &id1).unwrap_err(); - } - - #[test] - fn store_single_login() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - - store_login_at_file(&wallet_file, account1, hd_path, id1.clone(), &password).unwrap(); - - let stored_wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); - assert_eq!(stored_wallet.len(), 1); - - let login = stored_wallet.get_encrypted_login_by_index(0).unwrap(); - assert_eq!(login.id, id1); - - // some actual ciphertext was saved - assert!(!login.account.ciphertext().is_empty()); - } - - #[test] - fn store_single_login_with_multi() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - - store_login_with_multiple_accounts_at_file( - &wallet_file, - account1, - cosmos_hd_path, - id1.clone(), - &password, - ) - .unwrap(); - - let stored_wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); - assert_eq!(stored_wallet.len(), 1); - - let login = stored_wallet.get_encrypted_login_by_index(0).unwrap(); - assert_eq!(login.id, id1); - - // some actual ciphertext was saved - assert!(!login.account.ciphertext().is_empty()); - } - - #[test] - fn store_single_login_with_multi_then_update_pwd_and_load() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let new_password = UserPassword::new("new_password".to_string()); - let id1 = LoginId::new("first".to_string()); - - store_login_with_multiple_accounts_at_file( - &wallet_file, - account1, - cosmos_hd_path, - id1.clone(), - &password, - ) - .unwrap(); - - update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); - - let stored_wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); - assert_eq!(stored_wallet.len(), 1); - - let login = stored_wallet.get_encrypted_login_by_index(0).unwrap(); - assert_eq!(login.id, id1); - - // some actual ciphertext was saved - assert!(!login.account.ciphertext().is_empty()); - } - - #[test] - fn store_twice_for_the_same_id_fails() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - - // Store the first login - store_login_at_file( - &wallet_file, - account1.clone(), - hd_path.clone(), - id1.clone(), - &password, - ) - .unwrap(); - - // and storing the same id again fails - assert!(matches!( - store_login_at_file(&wallet_file, account1, hd_path, id1, &password,), - Err(BackendError::WalletLoginIdAlreadyExists), - )); - } - - #[test] - fn store_twice_for_the_same_id_fails_with_multiple() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - - // Store the first login - store_login_with_multiple_accounts_at_file( - &wallet_file, - account1.clone(), - hd_path.clone(), - id1.clone(), - &password, - ) - .unwrap(); - - // and storing the same id again fails - assert!(matches!( - store_login_with_multiple_accounts_at_file( - &wallet_file, - account1, - hd_path, - id1, - &password, - ), - Err(BackendError::WalletLoginIdAlreadyExists), - )); - } - - #[test] - fn load_with_wrong_password_fails() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let bad_password = UserPassword::new("bad-password".to_string()); - let id1 = LoginId::new("first".to_string()); - - store_login_at_file(&wallet_file, account1, hd_path, id1.clone(), &password).unwrap(); - - // Trying to load it with wrong password now fails - assert!(matches!( - load_existing_login_at_file(&wallet_file, &id1, &bad_password), - Err(BackendError::StoreCipherError { .. }), - )); - } - - #[test] - fn load_with_wrong_password_fails_with_multi() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let bad_password = UserPassword::new("bad-password".to_string()); - let id1 = LoginId::new("first".to_string()); - - store_login_with_multiple_accounts_at_file( - &wallet_file, - account1, - hd_path, - id1.clone(), - &password, - ) - .unwrap(); - - // Trying to load it with wrong password now fails - assert!(matches!( - load_existing_login_at_file(&wallet_file, &id1, &bad_password), - Err(BackendError::StoreCipherError { .. }), - )); - } - - #[test] - fn load_with_wrong_id_fails() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - let id2 = LoginId::new("second".to_string()); - - store_login_at_file(&wallet_file, account1, hd_path, id1, &password).unwrap(); - - // Trying to load with the wrong id - assert!(matches!( - load_existing_login_at_file(&wallet_file, &id2, &password), - Err(BackendError::WalletNoSuchLoginId), - )); - } - - #[test] - fn load_with_wrong_id_fails_with_multi() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - let id2 = LoginId::new("second".to_string()); - - store_login_with_multiple_accounts_at_file(&wallet_file, account1, hd_path, id1, &password) - .unwrap(); - - // Trying to load with the wrong id - assert!(matches!( - load_existing_login_at_file(&wallet_file, &id2, &password), - Err(BackendError::WalletNoSuchLoginId), - )); - } - - #[test] - fn store_and_load_a_single_login() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - - store_login_at_file( - &wallet_file, - account1.clone(), - hd_path.clone(), - id1.clone(), - &password, - ) - .unwrap(); - - let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); - let acc = loaded_login.as_mnemonic_account().unwrap(); - assert_eq!(&account1, acc.mnemonic()); - assert_eq!(&hd_path, acc.hd_path()); - } - - #[test] - fn store_a_single_login_then_update_pwd_and_load() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let new_password = UserPassword::new("new_password".to_string()); - let id1 = LoginId::new("first".to_string()); - - store_login_at_file( - &wallet_file, - account1.clone(), - hd_path.clone(), - id1.clone(), - &password, - ) - .unwrap(); - - update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); - - let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &new_password).unwrap(); - let acc = loaded_login.as_mnemonic_account().unwrap(); - assert_eq!(&account1, acc.mnemonic()); - assert_eq!(&hd_path, acc.hd_path()); - } - - #[test] - fn store_a_single_login_then_update_pwd_with_identical_pwd_is_noop_but_okay() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - - store_login_at_file( - &wallet_file, - account1.clone(), - hd_path.clone(), - id1.clone(), - &password, - ) - .unwrap(); - - update_encrypted_logins_at_file(&wallet_file, &password, &password).unwrap(); - - let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); - let acc = loaded_login.as_mnemonic_account().unwrap(); - assert_eq!(&account1, acc.mnemonic()); - assert_eq!(&hd_path, acc.hd_path()); - } - - #[test] - fn store_a_single_login_then_update_pwd_with_wrong_current_pwd_fails() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let new_password = UserPassword::new("new_password".to_string()); - let wrong_password = UserPassword::new("wrong_password".to_string()); - let id1 = LoginId::new("first".to_string()); - - store_login_at_file(&wallet_file, account1, hd_path, id1, &password).unwrap(); - - let err = update_encrypted_logins_at_file(&wallet_file, &wrong_password, &new_password) - .unwrap_err(); - assert!(matches!(err, BackendError::StoreCipherError { .. })); - } - - #[test] - fn store_a_single_login_then_update_pwd_and_load_with_wrong_pwd_fails() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let new_password = UserPassword::new("new_password".to_string()); - let id1 = LoginId::new("first".to_string()); - - store_login_at_file(&wallet_file, account1, hd_path, id1.clone(), &password).unwrap(); - - update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); - - let err = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap_err(); - assert!(matches!(err, BackendError::StoreCipherError { .. })); - } - - #[test] - fn store_and_load_a_single_login_with_multi() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let acc1 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - - store_login_with_multiple_accounts_at_file( - &wallet_file, - acc1.clone(), - hd_path.clone(), - id1.clone(), - &password, - ) - .unwrap(); - - let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); - let accounts = loaded_login.as_multiple_accounts().unwrap(); - assert_eq!(accounts.len(), 1); - let account = accounts - .get_account(&DEFAULT_FIRST_ACCOUNT_NAME.into()) - .unwrap(); - assert_eq!(account.id().as_ref(), DEFAULT_FIRST_ACCOUNT_NAME); - assert_eq!(account.mnemonic(), &acc1); - assert_eq!(account.hd_path(), &hd_path); - } - - #[test] - fn store_a_single_login_with_multi_then_update_pwd_and_load() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let acc1 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let new_password = UserPassword::new("new_password".to_string()); - let id1 = LoginId::new("first".to_string()); - - store_login_with_multiple_accounts_at_file( - &wallet_file, - acc1.clone(), - hd_path.clone(), - id1.clone(), - &password, - ) - .unwrap(); - - update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); - - let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &new_password).unwrap(); - let accounts = loaded_login.as_multiple_accounts().unwrap(); - assert_eq!(accounts.len(), 1); - let account = accounts - .get_account(&DEFAULT_FIRST_ACCOUNT_NAME.into()) - .unwrap(); - assert_eq!(account.id().as_ref(), DEFAULT_FIRST_ACCOUNT_NAME); - assert_eq!(account.mnemonic(), &acc1); - assert_eq!(account.hd_path(), &hd_path); - } - - #[test] - fn store_a_single_login_with_multi_then_update_pwd_with_wrong_current_pwd_fails() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let acc1 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let new_password = UserPassword::new("new_password".to_string()); - let wrong_password = UserPassword::new("wrong_password".to_string()); - let id1 = LoginId::new("first".to_string()); - - store_login_with_multiple_accounts_at_file(&wallet_file, acc1, hd_path, id1, &password) - .unwrap(); - - let err = update_encrypted_logins_at_file(&wallet_file, &wrong_password, &new_password) - .unwrap_err(); - assert!(matches!(err, BackendError::StoreCipherError { .. })); - } - - #[test] - fn store_a_single_login_with_multi_then_update_pwd_and_load_with_wrong_pwd_fails() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let acc1 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let new_password = UserPassword::new("new_password".to_string()); - let id1 = LoginId::new("first".to_string()); - - store_login_with_multiple_accounts_at_file( - &wallet_file, - acc1, - hd_path, - id1.clone(), - &password, - ) - .unwrap(); - - update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); - - let err = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap_err(); - assert!(matches!(err, BackendError::StoreCipherError { .. })); - } - - #[test] - fn store_a_second_login_with_a_different_password_fails() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let account2 = Mnemonic::generate(24).unwrap(); - let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let bad_password = UserPassword::new("bad-password".to_string()); - let id1 = LoginId::new("first".to_string()); - let id2 = LoginId::new("second".to_string()); - - store_login_at_file( - &wallet_file, - account1, - cosmos_hd_path.clone(), - id1, - &password, - ) - .unwrap(); - - // Can't store a second login if you use different password - assert!(matches!( - store_login_at_file(&wallet_file, account2, cosmos_hd_path, id2, &bad_password), - Err(BackendError::WalletDifferentPasswordDetected), - )); - } - - #[test] - fn store_a_second_login_with_a_different_password_fails_with_multi() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let account2 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let bad_password = UserPassword::new("bad-password".to_string()); - let id1 = LoginId::new("first".to_string()); - let id2 = LoginId::new("second".to_string()); - - store_login_with_multiple_accounts_at_file( - &wallet_file, - account1, - hd_path.clone(), - id1, - &password, - ) - .unwrap(); - - // Can't store a second login if you use different password - assert!(matches!( - store_login_with_multiple_accounts_at_file( - &wallet_file, - account2, - hd_path, - id2, - &bad_password - ), - Err(BackendError::WalletDifferentPasswordDetected), - )); - } - - #[test] - fn store_two_mnemonic_accounts_gives_different_salts_and_iv() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let account2 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let different_hd_path: DerivationPath = "m".parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - let id2 = LoginId::new("second".to_string()); - - // Store the first account - store_login_at_file(&wallet_file, account1, hd_path, id1, &password).unwrap(); - - let stored_wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); - let encrypted_blob = &stored_wallet - .get_encrypted_login_by_index(0) - .unwrap() - .account; - - // keep track of salt and iv for future assertion - let original_iv = encrypted_blob.iv().to_vec(); - let original_salt = encrypted_blob.salt().to_vec(); - - // Add an extra account - store_login_at_file(&wallet_file, account2, different_hd_path, id2, &password).unwrap(); - - let loaded_accounts = load_existing_wallet_at_file(&wallet_file).unwrap(); - assert_eq!(loaded_accounts.len(), 2); - let encrypted_blob = &loaded_accounts - .get_encrypted_login_by_index(1) - .unwrap() - .account; - - // fresh IV and salt are used - assert_ne!(original_iv, encrypted_blob.iv()); - assert_ne!(original_salt, encrypted_blob.salt()); - } - - #[test] - fn store_two_mnemonic_accounts_using_two_logins() { - let store_dir = tempdir().unwrap(); - let wallet = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let account2 = Mnemonic::generate(24).unwrap(); - let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let different_hd_path: DerivationPath = "m".parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - let id2 = LoginId::new("second".to_string()); - - // Store the first account - store_login_at_file( - &wallet, - account1.clone(), - cosmos_hd_path.clone(), - id1.clone(), - &password, - ) - .unwrap(); - - let login = load_existing_login_at_file(&wallet, &id1, &password).unwrap(); - let acc = login.as_mnemonic_account().unwrap(); - assert_eq!(&account1, acc.mnemonic()); - assert_eq!(&cosmos_hd_path, acc.hd_path()); - - // Add an extra account - store_login_at_file( - &wallet, - account2.clone(), - different_hd_path.clone(), - id2.clone(), - &password, - ) - .unwrap(); - - // first account should be unchanged - let loaded_login = load_existing_login_at_file(&wallet, &id1, &password).unwrap(); - let acc1 = loaded_login.as_mnemonic_account().unwrap(); - assert_eq!(&account1, acc1.mnemonic()); - assert_eq!(&cosmos_hd_path, acc1.hd_path()); - - let loaded_login = load_existing_login_at_file(&wallet, &id2, &password).unwrap(); - let acc2 = loaded_login.as_mnemonic_account().unwrap(); - assert_eq!(&account2, acc2.mnemonic()); - assert_eq!(&different_hd_path, acc2.hd_path()); - } - - #[test] - fn store_two_mnemonic_accounts_using_two_logins_then_update_pwd_and_load() { - let store_dir = tempdir().unwrap(); - let wallet = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let account2 = Mnemonic::generate(24).unwrap(); - let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let different_hd_path: DerivationPath = "m".parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let new_password = UserPassword::new("new_password".to_string()); - let id1 = LoginId::new("first".to_string()); - let id2 = LoginId::new("second".to_string()); - - // Store the first login with an account - store_login_at_file( - &wallet, - account1.clone(), - cosmos_hd_path.clone(), - id1.clone(), - &password, - ) - .unwrap(); - - let login = load_existing_login_at_file(&wallet, &id1, &password).unwrap(); - let acc = login.as_mnemonic_account().unwrap(); - assert_eq!(&account1, acc.mnemonic()); - assert_eq!(&cosmos_hd_path, acc.hd_path()); - - // Store a second login, also with an account - store_login_at_file( - &wallet, - account2.clone(), - different_hd_path.clone(), - id2.clone(), - &password, - ) - .unwrap(); - - update_encrypted_logins_at_file(&wallet, &password, &new_password).unwrap(); - - // first account should be unchanged - let loaded_login = load_existing_login_at_file(&wallet, &id1, &new_password).unwrap(); - let acc1 = loaded_login.as_mnemonic_account().unwrap(); - assert_eq!(&account1, acc1.mnemonic()); - assert_eq!(&cosmos_hd_path, acc1.hd_path()); - - let loaded_login = load_existing_login_at_file(&wallet, &id2, &new_password).unwrap(); - let acc2 = loaded_login.as_mnemonic_account().unwrap(); - assert_eq!(&account2, acc2.mnemonic()); - assert_eq!(&different_hd_path, acc2.hd_path()); - } - - #[test] - fn store_one_mnemonic_account_and_one_multi_account() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let account2 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let different_hd_path: DerivationPath = "m".parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - let id2 = LoginId::new("second".to_string()); - - // Store the first account - store_login_at_file( - &wallet_file, - account1.clone(), - hd_path.clone(), - id1.clone(), - &password, - ) - .unwrap(); - - let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); - let acc = loaded_login.as_mnemonic_account().unwrap(); - assert_eq!(&account1, acc.mnemonic()); - assert_eq!(&hd_path, acc.hd_path()); - - // Add an extra account - store_login_with_multiple_accounts_at_file( - &wallet_file, - account2.clone(), - different_hd_path.clone(), - id2.clone(), - &password, - ) - .unwrap(); - - // first account should be unchanged - let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); - let acc1 = loaded_login.as_mnemonic_account().unwrap(); - assert_eq!(&account1, acc1.mnemonic()); - assert_eq!(&hd_path, acc1.hd_path()); - - let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); - let acc2 = loaded_login.as_multiple_accounts().unwrap(); - assert_eq!(acc2.len(), 1); - let account = acc2 - .get_account(&DEFAULT_FIRST_ACCOUNT_NAME.into()) - .unwrap(); - assert_eq!(account.id().as_ref(), DEFAULT_FIRST_ACCOUNT_NAME); - assert_eq!(account.mnemonic(), &account2); - assert_eq!(account.hd_path(), &different_hd_path); - } - - #[test] - fn remove_non_existent_id_fails() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - let id2 = LoginId::new("second".to_string()); - - store_login_with_multiple_accounts_at_file(&wallet_file, account1, hd_path, id1, &password) - .unwrap(); - - // Fails to delete non-existent id in the wallet - assert!(matches!( - remove_login_at_file(&wallet_file, &id2), - Err(BackendError::WalletNoSuchLoginId), - )); - } - - #[test] - fn store_and_remove_wallet_login_information() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let account2 = Mnemonic::generate(24).unwrap(); - let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let different_hd_path: DerivationPath = "m".parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - let id2 = LoginId::new("second".to_string()); - - // Store two accounts with two different passwords - store_login_at_file( - &wallet_file, - account1.clone(), - cosmos_hd_path.clone(), - id1.clone(), - &password, - ) - .unwrap(); - store_login_at_file( - &wallet_file, - account2.clone(), - different_hd_path.clone(), - id2.clone(), - &password, - ) - .unwrap(); - - // Load and compare - let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); - let acc1 = loaded_login.as_mnemonic_account().unwrap(); - assert_eq!(&account1, acc1.mnemonic()); - assert_eq!(&cosmos_hd_path, acc1.hd_path()); - - let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); - let acc2 = loaded_login.as_mnemonic_account().unwrap(); - assert_eq!(&account2, acc2.mnemonic()); - assert_eq!(&different_hd_path, acc2.hd_path()); - - // Delete the second account - remove_login_at_file(&wallet_file, &id2).unwrap(); - - // The first account should be unchanged - let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); - let acc1 = loaded_login.as_mnemonic_account().unwrap(); - assert_eq!(&account1, acc1.mnemonic()); - assert_eq!(&cosmos_hd_path, acc1.hd_path()); - - // And we can't load the second one anymore - assert!(matches!( - load_existing_login_at_file(&wallet_file, &id2, &password), - Err(BackendError::WalletNoSuchLoginId), - )); - - // Delete the first account - assert!(wallet_file.exists()); - remove_login_at_file(&wallet_file, &id1).unwrap(); - - // The file should now be removed - assert!(!wallet_file.exists()); - - // And trying to load when the file is gone fails - assert!(matches!( - load_existing_login_at_file(&wallet_file, &id1, &password), - Err(BackendError::WalletFileNotFound), - )); - } - - #[test] - fn append_account_converts_the_type() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let account2 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - let id2 = AccountId::new("second".to_string()); - - store_login_at_file( - &wallet_file, - account1.clone(), - hd_path.clone(), - id1.clone(), - &password, - ) - .unwrap(); - - // Check that it's there as the correct non-multiple type - let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); - let acc = loaded_login.as_mnemonic_account().unwrap(); - assert_eq!(acc.mnemonic(), &account1); - assert_eq!(acc.hd_path(), &hd_path); - - append_account_to_login_at_file( - &wallet_file, - account2.clone(), - hd_path.clone(), - id1.clone(), - id2.clone(), - &password, - ) - .unwrap(); - - // Check that it is now multiple mnemonic type - let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); - let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); - let expected = vec![ - WalletAccount::new(id1.into(), MnemonicAccount::new(account1, hd_path.clone())), - WalletAccount::new(id2, MnemonicAccount::new(account2, hd_path)), - ] - .into(); - assert_eq!(loaded_accounts, &expected); - } - - #[test] - fn append_accounts_to_existing_login() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let account2 = Mnemonic::generate(24).unwrap(); - let account3 = Mnemonic::generate(24).unwrap(); - let account4 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - let id2 = LoginId::new("second".to_string()); - let id3 = AccountId::new("third".to_string()); - let id4 = AccountId::new("fourth".to_string()); - - store_login_at_file( - &wallet_file, - account1.clone(), - hd_path.clone(), - id1.clone(), - &password, - ) - .unwrap(); - - store_login_at_file( - &wallet_file, - account2.clone(), - hd_path.clone(), - id2.clone(), - &password, - ) - .unwrap(); - - // Check that it's there as the correct non-multiple type - let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); - let acc2 = loaded_login.as_mnemonic_account().unwrap(); - assert_eq!(acc2.mnemonic(), &account2); - assert_eq!(acc2.hd_path(), &hd_path); - - // Add a third and fourth mnenonic grouped together with the second one - append_account_to_login_at_file( - &wallet_file, - account3.clone(), - hd_path.clone(), - id2.clone(), - id3.clone(), - &password, - ) - .unwrap(); - append_account_to_login_at_file( - &wallet_file, - account4.clone(), - hd_path.clone(), - id2.clone(), - id4.clone(), - &password, - ) - .unwrap(); - - // Check that we can load all four - let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); - let acc1 = loaded_login.as_mnemonic_account().unwrap(); - assert_eq!(acc1.mnemonic(), &account1); - assert_eq!(acc1.hd_path(), &hd_path); - - let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); - let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); - let expected = vec![ - WalletAccount::new(id2.into(), MnemonicAccount::new(account2, hd_path.clone())), - WalletAccount::new(id3, MnemonicAccount::new(account3, hd_path.clone())), - WalletAccount::new(id4, MnemonicAccount::new(account4, hd_path)), - ] - .into(); - assert_eq!(loaded_accounts, &expected); - } - - #[test] - fn append_accounts_to_existing_login_with_multi() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let account2 = Mnemonic::generate(24).unwrap(); - let account3 = Mnemonic::generate(24).unwrap(); - let account4 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - let id2 = LoginId::new("second".to_string()); - let id3 = AccountId::new("third".to_string()); - let id4 = AccountId::new("fourth".to_string()); - - store_login_with_multiple_accounts_at_file( - &wallet_file, - account1.clone(), - hd_path.clone(), - id1.clone(), - &password, - ) - .unwrap(); - - store_login_with_multiple_accounts_at_file( - &wallet_file, - account2.clone(), - hd_path.clone(), - id2.clone(), - &password, - ) - .unwrap(); - - // Add a third and fourth mnenonic grouped together with the second one - append_account_to_login_at_file( - &wallet_file, - account3.clone(), - hd_path.clone(), - id2.clone(), - id3.clone(), - &password, - ) - .unwrap(); - append_account_to_login_at_file( - &wallet_file, - account4.clone(), - hd_path.clone(), - id2.clone(), - id4.clone(), - &password, - ) - .unwrap(); - - // Check that we can load all four - let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); - let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); - let expected = vec![WalletAccount::new( - DEFAULT_FIRST_ACCOUNT_NAME.into(), - MnemonicAccount::new(account1, hd_path.clone()), - )] - .into(); - assert_eq!(loaded_accounts, &expected); - - let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); - let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); - let expected = vec![ - WalletAccount::new( - DEFAULT_FIRST_ACCOUNT_NAME.into(), - MnemonicAccount::new(account2, hd_path.clone()), - ), - WalletAccount::new(id3, MnemonicAccount::new(account3, hd_path.clone())), - WalletAccount::new(id4, MnemonicAccount::new(account4, hd_path)), - ] - .into(); - assert_eq!(loaded_accounts, &expected); - } - - #[test] - fn append_account_to_existing_login_with_multi_then_update_pwd_and_load() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let account2 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let new_password = UserPassword::new("new_password".to_string()); - let login_id = LoginId::new("first".to_string()); - let appended_account = AccountId::new("second".to_string()); - - store_login_with_multiple_accounts_at_file( - &wallet_file, - account1.clone(), - hd_path.clone(), - login_id.clone(), - &password, - ) - .unwrap(); - - // Append a second mnenonic to the same login - append_account_to_login_at_file( - &wallet_file, - account2.clone(), - hd_path.clone(), - login_id.clone(), - appended_account.clone(), - &password, - ) - .unwrap(); - - // Update the password - update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); - - // Expect that we can load these 2 accounts with the new password - let loaded_login = - load_existing_login_at_file(&wallet_file, &login_id, &new_password).unwrap(); - let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); - let expected = vec![ - WalletAccount::new( - DEFAULT_FIRST_ACCOUNT_NAME.into(), - MnemonicAccount::new(account1, hd_path.clone()), - ), - WalletAccount::new(appended_account, MnemonicAccount::new(account2, hd_path)), - ] - .into(); - assert_eq!(loaded_accounts, &expected); - - // Expect that trying to load these 2 accounts with the old password fails - let err = load_existing_login_at_file(&wallet_file, &login_id, &password).unwrap_err(); - assert!(matches!(err, BackendError::StoreCipherError { .. })); - } - - #[test] - fn append_the_same_mnemonic_twice_fails() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - let id2 = AccountId::new("second".to_string()); - - store_login_with_multiple_accounts_at_file( - &wallet_file, - account1.clone(), - hd_path.clone(), - id1.clone(), - &password, - ) - .unwrap(); - - assert!(matches!( - append_account_to_login_at_file(&wallet_file, account1, hd_path, id1, id2, &password), - Err(BackendError::WalletMnemonicAlreadyExistsInWalletLogin), - )) - } - - #[test] - fn append_the_same_account_name_twice_fails() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let mnemonic1 = Mnemonic::generate(24).unwrap(); - let mnemonic2 = Mnemonic::generate(24).unwrap(); - let mnemonic3 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - // The top-level login id. NOTE: the first account id is always set to default. - let login_id = LoginId::new("my_login_id".to_string()); - - // Store the first account under login_id. The first account id is always set to default - // name. - store_login_with_multiple_accounts_at_file( - &wallet_file, - mnemonic1, - hd_path.clone(), - login_id.clone(), - &password, - ) - .unwrap(); - - // Append another account (account2) to the same login (login_id) - let account2 = AccountId::new("account_2".to_string()); - - append_account_to_login_at_file( - &wallet_file, - mnemonic2, - hd_path.clone(), - login_id.clone(), - account2.clone(), - &password, - ) - .unwrap(); - - // Appending the third account, with same account id will fail - assert!(matches!( - append_account_to_login_at_file( - &wallet_file, - mnemonic3, - hd_path, - login_id, - account2, - &password, - ), - Err(BackendError::WalletAccountIdAlreadyExistsInWalletLogin), - )) - } - - #[test] - fn delete_the_same_account_twice_for_a_login_fails() { - let store_dir = tempdir().unwrap(); - let wallet = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let account2 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - let id2 = AccountId::new("second".to_string()); - - store_login_at_file(&wallet, account1, hd_path.clone(), id1.clone(), &password).unwrap(); - - append_account_to_login_at_file( - &wallet, - account2, - hd_path, - id1.clone(), - id2.clone(), - &password, - ) - .unwrap(); - - remove_account_from_login_at_file(&wallet, &id1, &id2, &password).unwrap(); - - assert!(matches!( - remove_account_from_login_at_file(&wallet, &id1, &id2, &password), - Err(BackendError::WalletNoSuchAccountIdInWalletLogin), - )); - } - - #[test] - fn delete_the_same_account_twice_for_a_login_fails_with_multi() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let account2 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - let id2 = AccountId::new("second".to_string()); - - store_login_with_multiple_accounts_at_file( - &wallet_file, - account1, - hd_path.clone(), - id1.clone(), - &password, - ) - .unwrap(); - - append_account_to_login_at_file( - &wallet_file, - account2, - hd_path, - id1.clone(), - id2.clone(), - &password, - ) - .unwrap(); - - remove_account_from_login_at_file(&wallet_file, &id1, &id2, &password).unwrap(); - - assert!(matches!( - remove_account_from_login_at_file(&wallet_file, &id1, &id2, &password), - Err(BackendError::WalletNoSuchAccountIdInWalletLogin), - )); - } - - #[test] - fn delete_appended_account_doesnt_affect_others() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let account2 = Mnemonic::generate(24).unwrap(); - let account3 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - let id2 = LoginId::new("second".to_string()); - let id3 = AccountId::new("third".to_string()); - - store_login_at_file( - &wallet_file, - account1, - hd_path.clone(), - id1.clone(), - &password, - ) - .unwrap(); - - store_login_at_file( - &wallet_file, - account2.clone(), - hd_path.clone(), - id2.clone(), - &password, - ) - .unwrap(); - - append_account_to_login_at_file( - &wallet_file, - account3.clone(), - hd_path.clone(), - id2.clone(), - id3.clone(), - &password, - ) - .unwrap(); - - remove_login_at_file(&wallet_file, &id1).unwrap(); - - // The second login one is still there - let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); - let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); - let expected = vec![ - WalletAccount::new(id2.into(), MnemonicAccount::new(account2, hd_path.clone())), - WalletAccount::new(id3, MnemonicAccount::new(account3, hd_path)), - ] - .into(); - assert_eq!(loaded_accounts, &expected); - } - - #[test] - fn remove_all_accounts_for_a_login_removes_the_file_when_empty() { - let store_dir = tempdir().unwrap(); - let wallet = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let account2 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - let id2 = AccountId::new("second".to_string()); - - store_login_with_multiple_accounts_at_file( - &wallet, - account1, - hd_path.clone(), - id1.clone(), - &password, - ) - .unwrap(); - - append_account_to_login_at_file( - &wallet, - account2, - hd_path, - id1.clone(), - id2.clone(), - &password, - ) - .unwrap(); - - remove_account_from_login_at_file( - &wallet, - &id1, - &DEFAULT_FIRST_ACCOUNT_NAME.into(), - &password, - ) - .unwrap(); - remove_account_from_login_at_file(&wallet, &id1, &id2, &password).unwrap(); - - // The file should now be removed - assert!(!wallet.exists()); - - // And trying to load when the file is gone fails - assert!(matches!( - load_existing_login_at_file(&wallet, &id1, &password), - Err(BackendError::WalletFileNotFound), - )); - } - - #[test] - fn remove_all_accounts_for_a_login_removes_that_login() { - let store_dir = tempdir().unwrap(); - let wallet = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let account2 = Mnemonic::generate(24).unwrap(); - let account3 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - let id2 = AccountId::new("second".to_string()); - let id3 = LoginId::new("third".to_string()); - - store_login_with_multiple_accounts_at_file( - &wallet, - account1, - hd_path.clone(), - id1.clone(), - &password, - ) - .unwrap(); - - append_account_to_login_at_file( - &wallet, - account2, - hd_path.clone(), - id1.clone(), - id2.clone(), - &password, - ) - .unwrap(); - - store_login_with_multiple_accounts_at_file( - &wallet, - account3.clone(), - hd_path.clone(), - id3.clone(), - &password, - ) - .unwrap(); - - remove_account_from_login_at_file( - &wallet, - &id1, - &DEFAULT_FIRST_ACCOUNT_NAME.into(), - &password, - ) - .unwrap(); - remove_account_from_login_at_file(&wallet, &id1, &id2, &password).unwrap(); - - // And trying to load when the file is gone fails - assert!(matches!( - load_existing_login_at_file(&wallet, &id1, &password), - Err(BackendError::WalletNoSuchLoginId), - )); - - // The other login is still there - let loaded_login = load_existing_login_at_file(&wallet, &id3, &password).unwrap(); - let acc3 = loaded_login.as_multiple_accounts().unwrap(); - let expected = vec![WalletAccount::new( - DEFAULT_FIRST_ACCOUNT_NAME.into(), - MnemonicAccount::new(account3, hd_path), - )] - .into(); - assert_eq!(acc3, &expected); - } - - #[test] - fn append_accounts_and_remove_appended_accounts() { - let store_dir = tempdir().unwrap(); - let wallet = store_dir.path().join(WALLET_INFO_FILENAME); - let acc1 = Mnemonic::generate(24).unwrap(); - let acc2 = Mnemonic::generate(24).unwrap(); - let acc3 = Mnemonic::generate(24).unwrap(); - let acc4 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - let id2 = LoginId::new("second".to_string()); - let id3 = AccountId::new("third".to_string()); - let id4 = AccountId::new("fourth".to_string()); - - store_login_at_file( - &wallet, - acc1.clone(), - hd_path.clone(), - id1.clone(), - &password, - ) - .unwrap(); - - store_login_at_file( - &wallet, - acc2.clone(), - hd_path.clone(), - id2.clone(), - &password, - ) - .unwrap(); - - // Add a third and fourth mnenonic grouped together with the second one - append_account_to_login_at_file( - &wallet, - acc3, - hd_path.clone(), - id2.clone(), - id3.clone(), - &password, - ) - .unwrap(); - append_account_to_login_at_file( - &wallet, - acc4.clone(), - hd_path.clone(), - id2.clone(), - id4.clone(), - &password, - ) - .unwrap(); - - // Delete the third mnemonic, from the second login entry - remove_account_from_login_at_file(&wallet, &id2, &id3, &password).unwrap(); - - // Check that we can still load the other accounts - let loaded_login = load_existing_login_at_file(&wallet, &id2, &password).unwrap(); - let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); - let expected = vec![ - WalletAccount::new( - id2.clone().into(), - MnemonicAccount::new(acc2, hd_path.clone()), - ), - WalletAccount::new(id4.clone(), MnemonicAccount::new(acc4, hd_path.clone())), - ] - .into(); - assert_eq!(loaded_accounts, &expected); - - // Delete the second and fourth mnemonic from the second login entry removes the login entry - remove_account_from_login_at_file(&wallet, &id2, &id2.clone().into(), &password).unwrap(); - remove_account_from_login_at_file(&wallet, &id2, &id4, &password).unwrap(); - assert!(matches!( - load_existing_login_at_file(&wallet, &id2, &password), - Err(BackendError::WalletNoSuchLoginId), - )); - - // The first login is still available - let loaded_login = load_existing_login_at_file(&wallet, &id1, &password).unwrap(); - let account = loaded_login.as_mnemonic_account().unwrap(); - assert_eq!(account.mnemonic(), &acc1); - assert_eq!(account.hd_path(), &hd_path); - } - - #[test] - fn rename_first_account_in_login() { - let store_dir = tempdir().unwrap(); - let wallet = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let login_id = LoginId::new("first".to_string()); - - store_login_with_multiple_accounts_at_file( - &wallet, - account1.clone(), - hd_path.clone(), - login_id.clone(), - &password, - ) - .unwrap(); - - let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); - let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); - let expected = vec![WalletAccount::new( - DEFAULT_FIRST_ACCOUNT_NAME.into(), - MnemonicAccount::new(account1.clone(), hd_path.clone()), - )] - .into(); - assert_eq!(loaded_accounts, &expected); - - let renamed_account = AccountId::new("new_first".to_string()); - - rename_account_in_login_at_file( - &wallet, - &login_id, - &DEFAULT_FIRST_ACCOUNT_NAME.into(), - &renamed_account, - &password, - ) - .unwrap(); - - let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); - let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); - let expected = vec![WalletAccount::new( - renamed_account, - MnemonicAccount::new(account1, hd_path), - )] - .into(); - assert_eq!(loaded_accounts, &expected); - } - - #[test] - fn rename_one_account_in_login_with_two_accounts() { - let store_dir = tempdir().unwrap(); - let wallet = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let account2 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let login_id = LoginId::new("first".to_string()); - let account_id2 = AccountId::new("second".to_string()); - let renamed_account_id2 = AccountId::new("new_second".to_string()); - - store_login_with_multiple_accounts_at_file( - &wallet, - account1.clone(), - hd_path.clone(), - login_id.clone(), - &password, - ) - .unwrap(); - - append_account_to_login_at_file( - &wallet, - account2.clone(), - hd_path.clone(), - login_id.clone(), - account_id2.clone(), - &password, - ) - .unwrap(); - - // Load and confirm - let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); - let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); - let expected = vec![ - WalletAccount::new( - DEFAULT_FIRST_ACCOUNT_NAME.into(), - MnemonicAccount::new(account1.clone(), hd_path.clone()), - ), - WalletAccount::new( - account_id2.clone(), - MnemonicAccount::new(account2.clone(), hd_path.clone()), - ), - ] - .into(); - assert_eq!(loaded_accounts, &expected); - - // Rename the second account to a new name - rename_account_in_login_at_file( - &wallet, - &login_id, - &account_id2, - &renamed_account_id2, - &password, - ) - .unwrap(); - - // Load and confirm - let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); - let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); - let expected = vec![ - WalletAccount::new( - DEFAULT_FIRST_ACCOUNT_NAME.into(), - MnemonicAccount::new(account1, hd_path.clone()), - ), - WalletAccount::new(renamed_account_id2, MnemonicAccount::new(account2, hd_path)), - ] - .into(); - assert_eq!(loaded_accounts, &expected); - } - - #[test] - fn rename_account_into_existing_account_id_fails() { - let store_dir = tempdir().unwrap(); - let wallet = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let account2 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let login_id = LoginId::new("first".to_string()); - let account_id2 = AccountId::new("second".to_string()); - let renamed_account_id2 = DEFAULT_FIRST_ACCOUNT_NAME.into(); - - store_login_with_multiple_accounts_at_file( - &wallet, - account1.clone(), - hd_path.clone(), - login_id.clone(), - &password, - ) - .unwrap(); - - append_account_to_login_at_file( - &wallet, - account2.clone(), - hd_path.clone(), - login_id.clone(), - account_id2.clone(), - &password, - ) - .unwrap(); - - // Load and confirm - let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); - let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); - let expected = vec![ - WalletAccount::new( - DEFAULT_FIRST_ACCOUNT_NAME.into(), - MnemonicAccount::new(account1.clone(), hd_path.clone()), - ), - WalletAccount::new( - account_id2.clone(), - MnemonicAccount::new(account2.clone(), hd_path.clone()), - ), - ] - .into(); - assert_eq!(loaded_accounts, &expected); - - // Rename the second account to the name of the first one fails - assert!(matches!( - rename_account_in_login_at_file( - &wallet, - &login_id, - &account_id2, - &renamed_account_id2, - &password, - ), - Err(BackendError::WalletAccountIdAlreadyExistsInWalletLogin) - )); - - // Load and confirm nothing was changed - let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); - let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); - let expected = vec![ - WalletAccount::new( - DEFAULT_FIRST_ACCOUNT_NAME.into(), - MnemonicAccount::new(account1, hd_path.clone()), - ), - WalletAccount::new(account_id2, MnemonicAccount::new(account2, hd_path)), - ] - .into(); - assert_eq!(loaded_accounts, &expected); - } - - // Test to that decrypts a stored file from the repo, to make sure we are able to decrypt stored - // wallets created with older versions. - #[test] - fn decrypt_stored_wallet() { - const SAVED_WALLET: &str = "src/wallet_storage/test-data/saved-wallet.json"; - let wallet_file = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(SAVED_WALLET); - - let wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); - - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let bad_password = UserPassword::new("bad-password".to_string()); - let id1 = LoginId::new("first".to_string()); - let id2 = LoginId::new("second".to_string()); - - assert!(!wallet.password_can_decrypt_all(&bad_password)); - assert!(wallet.password_can_decrypt_all(&password)); - - let acc1 = wallet.decrypt_login(&id1, &password).unwrap(); - let acc2 = wallet.decrypt_login(&id2, &password).unwrap(); - - assert!(matches!(acc1, StoredLogin::Mnemonic(_))); - assert!(matches!(acc2, StoredLogin::Mnemonic(_))); - - let expected_acc1 = bip39::Mnemonic::from_str("country mean universe text phone begin deputy reject result good cram illness common cluster proud swamp digital patrol spread bar face december base kick").unwrap(); - let expected_acc2 = bip39::Mnemonic::from_str("home mansion start quiz dress decide hint second dragon sunny juice always steak real minimum art rival skin draw total pulp foot goddess agent").unwrap(); - - assert_eq!( - acc1.as_mnemonic_account().unwrap().mnemonic(), - &expected_acc1 - ); - assert_eq!(acc1.as_mnemonic_account().unwrap().hd_path(), &hd_path,); - - assert_eq!( - acc2.as_mnemonic_account().unwrap().mnemonic(), - &expected_acc2 - ); - assert_eq!(acc2.as_mnemonic_account().unwrap().hd_path(), &hd_path,); - } - - #[test] - fn decrypt_stored_wallet_1_0_4() { - const SAVED_WALLET: &str = "src/wallet_storage/test-data/saved-wallet-1.0.4.json"; - let wallet_file = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(SAVED_WALLET); - - let wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); - - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password11!".to_string()); - let bad_password = UserPassword::new("bad-password".to_string()); - let login_id = LoginId::new("default".to_string()); - - assert!(!wallet.password_can_decrypt_all(&bad_password)); - assert!(wallet.password_can_decrypt_all(&password)); - - let acc1 = wallet.decrypt_login(&login_id, &password).unwrap(); - - assert!(matches!(acc1, StoredLogin::Mnemonic(_))); - - let expected_acc1 = bip39::Mnemonic::from_str("arrow capable abstract industry elevator nominee december piece hotel feed lounge web faint sword veteran bundle hour page actual laptop horror gold test warrior").unwrap(); - - assert_eq!( - acc1.as_mnemonic_account().unwrap().mnemonic(), - &expected_acc1 - ); - assert_eq!(acc1.as_mnemonic_account().unwrap().hd_path(), &hd_path,); - } - - #[test] - fn decrypt_stored_wallet_1_0_5_with_multiple_accounts() { - const SAVED_WALLET: &str = "src/wallet_storage/test-data/saved-wallet-1.0.5.json"; - let wallet_file = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(SAVED_WALLET); - - let wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); - - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password11!".to_string()); - let bad_password = UserPassword::new("bad-password".to_string()); - let login_id = LoginId::new("default".to_string()); - - assert!(!wallet.password_can_decrypt_all(&bad_password)); - assert!(wallet.password_can_decrypt_all(&password)); - - let login = wallet.decrypt_login(&login_id, &password).unwrap(); - - assert!(matches!(login, StoredLogin::Multiple(_))); - - let login = login.as_multiple_accounts().unwrap(); - assert_eq!(login.len(), 4); - - let expected_mn1 = bip39::Mnemonic::from_str("arrow capable abstract industry elevator nominee december piece hotel feed lounge web faint sword veteran bundle hour page actual laptop horror gold test warrior").unwrap(); - let expected_mn2 = bip39::Mnemonic::from_str("border hurt skull lunar goddess second danger game dismiss exhaust oven thumb dog drama onion false orchard spice tent next predict invite cherry green").unwrap(); - let expected_mn3 = bip39::Mnemonic::from_str("gentle crowd rule snap girl urge flat jump winner cluster night sand museum stock grunt quick tree acquire traffic major awake tag rack peasant").unwrap(); - let expected_mn4 = bip39::Mnemonic::from_str("debris blue skin annual inhale text border rigid spatial lesson coconut yard horn crystal control survey version vote hawk neck frame arrive oblige width").unwrap(); - - let expected = vec![ - WalletAccount::new( - "default".into(), - MnemonicAccount::new(expected_mn1, hd_path.clone()), - ), - WalletAccount::new( - "account2".into(), - MnemonicAccount::new(expected_mn2, hd_path.clone()), - ), - WalletAccount::new( - "foobar".into(), - MnemonicAccount::new(expected_mn3, hd_path.clone()), - ), - WalletAccount::new("42".into(), MnemonicAccount::new(expected_mn4, hd_path)), - ] - .into(); - - assert_eq!(login, &expected); - } - - #[test] - fn append_filename() { - let wallet_file = PathBuf::from("/tmp/saved-wallet.json"); - let timestamp = OsString::from("42"); - #[cfg(target_family = "unix")] - assert_eq!( - append_timestamp_to_filename(wallet_file.clone(), timestamp.clone(), None) - .unwrap() - .into_os_string() - .into_string() - .unwrap(), - "/tmp/saved-wallet-42.json".to_string(), - ); - #[cfg(not(target_family = "unix"))] - assert_eq!( - append_timestamp_to_filename(wallet_file.clone(), timestamp.clone(), None) - .unwrap() - .into_os_string() - .into_string() - .unwrap(), - r"/tmp\saved-wallet-42.json".to_string(), - ); +// #[cfg(test)] +// mod tests { +// use crate::wallet_storage::account_data::WalletAccount; + +// use super::*; +// use nym_config::defaults::COSMOS_DERIVATION_PATH; +// use std::str::FromStr; +// use tempfile::tempdir; + +// #[test] +// fn trying_to_load_nonexistant_wallet_fails() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let id1 = LoginId::new("first".to_string()); +// let password = UserPassword::new("password".to_string()); + +// assert!(matches!( +// load_existing_wallet_at_file(&wallet_file), +// Err(BackendError::WalletFileNotFound), +// )); +// assert!(matches!( +// load_existing_login_at_file(&wallet_file, &id1, &password), +// Err(BackendError::WalletFileNotFound), +// )); +// remove_login_at_file(&wallet_file, &id1).unwrap_err(); +// } + +// #[test] +// fn store_single_login() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); + +// store_login_at_file(&wallet_file, account1, hd_path, id1.clone(), &password).unwrap(); + +// let stored_wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); +// assert_eq!(stored_wallet.len(), 1); + +// let login = stored_wallet.get_encrypted_login_by_index(0).unwrap(); +// assert_eq!(login.id, id1); + +// // some actual ciphertext was saved +// assert!(!login.account.ciphertext().is_empty()); +// } + +// #[test] +// fn store_single_login_with_multi() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); + +// store_login_with_multiple_accounts_at_file( +// &wallet_file, +// account1, +// cosmos_hd_path, +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// let stored_wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); +// assert_eq!(stored_wallet.len(), 1); + +// let login = stored_wallet.get_encrypted_login_by_index(0).unwrap(); +// assert_eq!(login.id, id1); + +// // some actual ciphertext was saved +// assert!(!login.account.ciphertext().is_empty()); +// } + +// #[test] +// fn store_single_login_with_multi_then_update_pwd_and_load() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let new_password = UserPassword::new("new_password".to_string()); +// let id1 = LoginId::new("first".to_string()); + +// store_login_with_multiple_accounts_at_file( +// &wallet_file, +// account1, +// cosmos_hd_path, +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); + +// let stored_wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); +// assert_eq!(stored_wallet.len(), 1); + +// let login = stored_wallet.get_encrypted_login_by_index(0).unwrap(); +// assert_eq!(login.id, id1); + +// // some actual ciphertext was saved +// assert!(!login.account.ciphertext().is_empty()); +// } + +// #[test] +// fn store_twice_for_the_same_id_fails() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); + +// // Store the first login +// store_login_at_file( +// &wallet_file, +// account1.clone(), +// hd_path.clone(), +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// // and storing the same id again fails +// assert!(matches!( +// store_login_at_file(&wallet_file, account1, hd_path, id1, &password,), +// Err(BackendError::WalletLoginIdAlreadyExists), +// )); +// } + +// #[test] +// fn store_twice_for_the_same_id_fails_with_multiple() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); + +// // Store the first login +// store_login_with_multiple_accounts_at_file( +// &wallet_file, +// account1.clone(), +// hd_path.clone(), +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// // and storing the same id again fails +// assert!(matches!( +// store_login_with_multiple_accounts_at_file( +// &wallet_file, +// account1, +// hd_path, +// id1, +// &password, +// ), +// Err(BackendError::WalletLoginIdAlreadyExists), +// )); +// } + +// #[test] +// fn load_with_wrong_password_fails() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let bad_password = UserPassword::new("bad-password".to_string()); +// let id1 = LoginId::new("first".to_string()); + +// store_login_at_file(&wallet_file, account1, hd_path, id1.clone(), &password).unwrap(); + +// // Trying to load it with wrong password now fails +// assert!(matches!( +// load_existing_login_at_file(&wallet_file, &id1, &bad_password), +// Err(BackendError::StoreCipherError { .. }), +// )); +// } + +// #[test] +// fn load_with_wrong_password_fails_with_multi() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let bad_password = UserPassword::new("bad-password".to_string()); +// let id1 = LoginId::new("first".to_string()); + +// store_login_with_multiple_accounts_at_file( +// &wallet_file, +// account1, +// hd_path, +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// // Trying to load it with wrong password now fails +// assert!(matches!( +// load_existing_login_at_file(&wallet_file, &id1, &bad_password), +// Err(BackendError::StoreCipherError { .. }), +// )); +// } + +// #[test] +// fn load_with_wrong_id_fails() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); +// let id2 = LoginId::new("second".to_string()); + +// store_login_at_file(&wallet_file, account1, hd_path, id1, &password).unwrap(); + +// // Trying to load with the wrong id +// assert!(matches!( +// load_existing_login_at_file(&wallet_file, &id2, &password), +// Err(BackendError::WalletNoSuchLoginId), +// )); +// } + +// #[test] +// fn load_with_wrong_id_fails_with_multi() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); +// let id2 = LoginId::new("second".to_string()); + +// store_login_with_multiple_accounts_at_file(&wallet_file, account1, hd_path, id1, &password) +// .unwrap(); + +// // Trying to load with the wrong id +// assert!(matches!( +// load_existing_login_at_file(&wallet_file, &id2, &password), +// Err(BackendError::WalletNoSuchLoginId), +// )); +// } + +// #[test] +// fn store_and_load_a_single_login() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); + +// store_login_at_file( +// &wallet_file, +// account1.clone(), +// hd_path.clone(), +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); +// let acc = loaded_login.as_mnemonic_account().unwrap(); +// assert_eq!(&account1, acc.mnemonic()); +// assert_eq!(&hd_path, acc.hd_path()); +// } + +// #[test] +// fn store_a_single_login_then_update_pwd_and_load() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let new_password = UserPassword::new("new_password".to_string()); +// let id1 = LoginId::new("first".to_string()); + +// store_login_at_file( +// &wallet_file, +// account1.clone(), +// hd_path.clone(), +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); + +// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &new_password).unwrap(); +// let acc = loaded_login.as_mnemonic_account().unwrap(); +// assert_eq!(&account1, acc.mnemonic()); +// assert_eq!(&hd_path, acc.hd_path()); +// } + +// #[test] +// fn store_a_single_login_then_update_pwd_with_identical_pwd_is_noop_but_okay() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); + +// store_login_at_file( +// &wallet_file, +// account1.clone(), +// hd_path.clone(), +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// update_encrypted_logins_at_file(&wallet_file, &password, &password).unwrap(); + +// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); +// let acc = loaded_login.as_mnemonic_account().unwrap(); +// assert_eq!(&account1, acc.mnemonic()); +// assert_eq!(&hd_path, acc.hd_path()); +// } + +// #[test] +// fn store_a_single_login_then_update_pwd_with_wrong_current_pwd_fails() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let new_password = UserPassword::new("new_password".to_string()); +// let wrong_password = UserPassword::new("wrong_password".to_string()); +// let id1 = LoginId::new("first".to_string()); + +// store_login_at_file(&wallet_file, account1, hd_path, id1, &password).unwrap(); + +// let err = update_encrypted_logins_at_file(&wallet_file, &wrong_password, &new_password) +// .unwrap_err(); +// assert!(matches!(err, BackendError::StoreCipherError { .. })); +// } + +// #[test] +// fn store_a_single_login_then_update_pwd_and_load_with_wrong_pwd_fails() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let new_password = UserPassword::new("new_password".to_string()); +// let id1 = LoginId::new("first".to_string()); + +// store_login_at_file(&wallet_file, account1, hd_path, id1.clone(), &password).unwrap(); + +// update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); + +// let err = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap_err(); +// assert!(matches!(err, BackendError::StoreCipherError { .. })); +// } + +// #[test] +// fn store_and_load_a_single_login_with_multi() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let acc1 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); + +// store_login_with_multiple_accounts_at_file( +// &wallet_file, +// acc1.clone(), +// hd_path.clone(), +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); +// let accounts = loaded_login.as_multiple_accounts().unwrap(); +// assert_eq!(accounts.len(), 1); +// let account = accounts +// .get_account(&DEFAULT_FIRST_ACCOUNT_NAME.into()) +// .unwrap(); +// assert_eq!(account.id().as_ref(), DEFAULT_FIRST_ACCOUNT_NAME); +// assert_eq!(account.mnemonic(), &acc1); +// assert_eq!(account.hd_path(), &hd_path); +// } + +// #[test] +// fn store_a_single_login_with_multi_then_update_pwd_and_load() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let acc1 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let new_password = UserPassword::new("new_password".to_string()); +// let id1 = LoginId::new("first".to_string()); + +// store_login_with_multiple_accounts_at_file( +// &wallet_file, +// acc1.clone(), +// hd_path.clone(), +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); + +// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &new_password).unwrap(); +// let accounts = loaded_login.as_multiple_accounts().unwrap(); +// assert_eq!(accounts.len(), 1); +// let account = accounts +// .get_account(&DEFAULT_FIRST_ACCOUNT_NAME.into()) +// .unwrap(); +// assert_eq!(account.id().as_ref(), DEFAULT_FIRST_ACCOUNT_NAME); +// assert_eq!(account.mnemonic(), &acc1); +// assert_eq!(account.hd_path(), &hd_path); +// } + +// #[test] +// fn store_a_single_login_with_multi_then_update_pwd_with_wrong_current_pwd_fails() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let acc1 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let new_password = UserPassword::new("new_password".to_string()); +// let wrong_password = UserPassword::new("wrong_password".to_string()); +// let id1 = LoginId::new("first".to_string()); + +// store_login_with_multiple_accounts_at_file(&wallet_file, acc1, hd_path, id1, &password) +// .unwrap(); + +// let err = update_encrypted_logins_at_file(&wallet_file, &wrong_password, &new_password) +// .unwrap_err(); +// assert!(matches!(err, BackendError::StoreCipherError { .. })); +// } + +// #[test] +// fn store_a_single_login_with_multi_then_update_pwd_and_load_with_wrong_pwd_fails() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let acc1 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let new_password = UserPassword::new("new_password".to_string()); +// let id1 = LoginId::new("first".to_string()); + +// store_login_with_multiple_accounts_at_file( +// &wallet_file, +// acc1, +// hd_path, +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); + +// let err = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap_err(); +// assert!(matches!(err, BackendError::StoreCipherError { .. })); +// } + +// #[test] +// fn store_a_second_login_with_a_different_password_fails() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let account2 = Mnemonic::generate(24).unwrap(); +// let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let bad_password = UserPassword::new("bad-password".to_string()); +// let id1 = LoginId::new("first".to_string()); +// let id2 = LoginId::new("second".to_string()); + +// store_login_at_file( +// &wallet_file, +// account1, +// cosmos_hd_path.clone(), +// id1, +// &password, +// ) +// .unwrap(); + +// // Can't store a second login if you use different password +// assert!(matches!( +// store_login_at_file(&wallet_file, account2, cosmos_hd_path, id2, &bad_password), +// Err(BackendError::WalletDifferentPasswordDetected), +// )); +// } + +// #[test] +// fn store_a_second_login_with_a_different_password_fails_with_multi() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let account2 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let bad_password = UserPassword::new("bad-password".to_string()); +// let id1 = LoginId::new("first".to_string()); +// let id2 = LoginId::new("second".to_string()); + +// store_login_with_multiple_accounts_at_file( +// &wallet_file, +// account1, +// hd_path.clone(), +// id1, +// &password, +// ) +// .unwrap(); + +// // Can't store a second login if you use different password +// assert!(matches!( +// store_login_with_multiple_accounts_at_file( +// &wallet_file, +// account2, +// hd_path, +// id2, +// &bad_password +// ), +// Err(BackendError::WalletDifferentPasswordDetected), +// )); +// } + +// #[test] +// fn store_two_mnemonic_accounts_gives_different_salts_and_iv() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let account2 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let different_hd_path: DerivationPath = "m".parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); +// let id2 = LoginId::new("second".to_string()); + +// // Store the first account +// store_login_at_file(&wallet_file, account1, hd_path, id1, &password).unwrap(); + +// let stored_wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); +// let encrypted_blob = &stored_wallet +// .get_encrypted_login_by_index(0) +// .unwrap() +// .account; + +// // keep track of salt and iv for future assertion +// let original_iv = encrypted_blob.iv().to_vec(); +// let original_salt = encrypted_blob.salt().to_vec(); + +// // Add an extra account +// store_login_at_file(&wallet_file, account2, different_hd_path, id2, &password).unwrap(); + +// let loaded_accounts = load_existing_wallet_at_file(&wallet_file).unwrap(); +// assert_eq!(loaded_accounts.len(), 2); +// let encrypted_blob = &loaded_accounts +// .get_encrypted_login_by_index(1) +// .unwrap() +// .account; + +// // fresh IV and salt are used +// assert_ne!(original_iv, encrypted_blob.iv()); +// assert_ne!(original_salt, encrypted_blob.salt()); +// } + +// #[test] +// fn store_two_mnemonic_accounts_using_two_logins() { +// let store_dir = tempdir().unwrap(); +// let wallet = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let account2 = Mnemonic::generate(24).unwrap(); +// let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let different_hd_path: DerivationPath = "m".parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); +// let id2 = LoginId::new("second".to_string()); + +// // Store the first account +// store_login_at_file( +// &wallet, +// account1.clone(), +// cosmos_hd_path.clone(), +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// let login = load_existing_login_at_file(&wallet, &id1, &password).unwrap(); +// let acc = login.as_mnemonic_account().unwrap(); +// assert_eq!(&account1, acc.mnemonic()); +// assert_eq!(&cosmos_hd_path, acc.hd_path()); + +// // Add an extra account +// store_login_at_file( +// &wallet, +// account2.clone(), +// different_hd_path.clone(), +// id2.clone(), +// &password, +// ) +// .unwrap(); + +// // first account should be unchanged +// let loaded_login = load_existing_login_at_file(&wallet, &id1, &password).unwrap(); +// let acc1 = loaded_login.as_mnemonic_account().unwrap(); +// assert_eq!(&account1, acc1.mnemonic()); +// assert_eq!(&cosmos_hd_path, acc1.hd_path()); + +// let loaded_login = load_existing_login_at_file(&wallet, &id2, &password).unwrap(); +// let acc2 = loaded_login.as_mnemonic_account().unwrap(); +// assert_eq!(&account2, acc2.mnemonic()); +// assert_eq!(&different_hd_path, acc2.hd_path()); +// } + +// #[test] +// fn store_two_mnemonic_accounts_using_two_logins_then_update_pwd_and_load() { +// let store_dir = tempdir().unwrap(); +// let wallet = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let account2 = Mnemonic::generate(24).unwrap(); +// let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let different_hd_path: DerivationPath = "m".parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let new_password = UserPassword::new("new_password".to_string()); +// let id1 = LoginId::new("first".to_string()); +// let id2 = LoginId::new("second".to_string()); + +// // Store the first login with an account +// store_login_at_file( +// &wallet, +// account1.clone(), +// cosmos_hd_path.clone(), +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// let login = load_existing_login_at_file(&wallet, &id1, &password).unwrap(); +// let acc = login.as_mnemonic_account().unwrap(); +// assert_eq!(&account1, acc.mnemonic()); +// assert_eq!(&cosmos_hd_path, acc.hd_path()); + +// // Store a second login, also with an account +// store_login_at_file( +// &wallet, +// account2.clone(), +// different_hd_path.clone(), +// id2.clone(), +// &password, +// ) +// .unwrap(); + +// update_encrypted_logins_at_file(&wallet, &password, &new_password).unwrap(); + +// // first account should be unchanged +// let loaded_login = load_existing_login_at_file(&wallet, &id1, &new_password).unwrap(); +// let acc1 = loaded_login.as_mnemonic_account().unwrap(); +// assert_eq!(&account1, acc1.mnemonic()); +// assert_eq!(&cosmos_hd_path, acc1.hd_path()); + +// let loaded_login = load_existing_login_at_file(&wallet, &id2, &new_password).unwrap(); +// let acc2 = loaded_login.as_mnemonic_account().unwrap(); +// assert_eq!(&account2, acc2.mnemonic()); +// assert_eq!(&different_hd_path, acc2.hd_path()); +// } + +// #[test] +// fn store_one_mnemonic_account_and_one_multi_account() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let account2 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let different_hd_path: DerivationPath = "m".parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); +// let id2 = LoginId::new("second".to_string()); + +// // Store the first account +// store_login_at_file( +// &wallet_file, +// account1.clone(), +// hd_path.clone(), +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); +// let acc = loaded_login.as_mnemonic_account().unwrap(); +// assert_eq!(&account1, acc.mnemonic()); +// assert_eq!(&hd_path, acc.hd_path()); + +// // Add an extra account +// store_login_with_multiple_accounts_at_file( +// &wallet_file, +// account2.clone(), +// different_hd_path.clone(), +// id2.clone(), +// &password, +// ) +// .unwrap(); + +// // first account should be unchanged +// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); +// let acc1 = loaded_login.as_mnemonic_account().unwrap(); +// assert_eq!(&account1, acc1.mnemonic()); +// assert_eq!(&hd_path, acc1.hd_path()); + +// let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); +// let acc2 = loaded_login.as_multiple_accounts().unwrap(); +// assert_eq!(acc2.len(), 1); +// let account = acc2 +// .get_account(&DEFAULT_FIRST_ACCOUNT_NAME.into()) +// .unwrap(); +// assert_eq!(account.id().as_ref(), DEFAULT_FIRST_ACCOUNT_NAME); +// assert_eq!(account.mnemonic(), &account2); +// assert_eq!(account.hd_path(), &different_hd_path); +// } + +// #[test] +// fn remove_non_existent_id_fails() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); +// let id2 = LoginId::new("second".to_string()); + +// store_login_with_multiple_accounts_at_file(&wallet_file, account1, hd_path, id1, &password) +// .unwrap(); + +// // Fails to delete non-existent id in the wallet +// assert!(matches!( +// remove_login_at_file(&wallet_file, &id2), +// Err(BackendError::WalletNoSuchLoginId), +// )); +// } + +// #[test] +// fn store_and_remove_wallet_login_information() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let account2 = Mnemonic::generate(24).unwrap(); +// let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let different_hd_path: DerivationPath = "m".parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); +// let id2 = LoginId::new("second".to_string()); + +// // Store two accounts with two different passwords +// store_login_at_file( +// &wallet_file, +// account1.clone(), +// cosmos_hd_path.clone(), +// id1.clone(), +// &password, +// ) +// .unwrap(); +// store_login_at_file( +// &wallet_file, +// account2.clone(), +// different_hd_path.clone(), +// id2.clone(), +// &password, +// ) +// .unwrap(); + +// // Load and compare +// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); +// let acc1 = loaded_login.as_mnemonic_account().unwrap(); +// assert_eq!(&account1, acc1.mnemonic()); +// assert_eq!(&cosmos_hd_path, acc1.hd_path()); + +// let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); +// let acc2 = loaded_login.as_mnemonic_account().unwrap(); +// assert_eq!(&account2, acc2.mnemonic()); +// assert_eq!(&different_hd_path, acc2.hd_path()); + +// // Delete the second account +// remove_login_at_file(&wallet_file, &id2).unwrap(); + +// // The first account should be unchanged +// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); +// let acc1 = loaded_login.as_mnemonic_account().unwrap(); +// assert_eq!(&account1, acc1.mnemonic()); +// assert_eq!(&cosmos_hd_path, acc1.hd_path()); + +// // And we can't load the second one anymore +// assert!(matches!( +// load_existing_login_at_file(&wallet_file, &id2, &password), +// Err(BackendError::WalletNoSuchLoginId), +// )); + +// // Delete the first account +// assert!(wallet_file.exists()); +// remove_login_at_file(&wallet_file, &id1).unwrap(); + +// // The file should now be removed +// assert!(!wallet_file.exists()); + +// // And trying to load when the file is gone fails +// assert!(matches!( +// load_existing_login_at_file(&wallet_file, &id1, &password), +// Err(BackendError::WalletFileNotFound), +// )); +// } + +// #[test] +// fn append_account_converts_the_type() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let account2 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); +// let id2 = AccountId::new("second".to_string()); + +// store_login_at_file( +// &wallet_file, +// account1.clone(), +// hd_path.clone(), +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// // Check that it's there as the correct non-multiple type +// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); +// let acc = loaded_login.as_mnemonic_account().unwrap(); +// assert_eq!(acc.mnemonic(), &account1); +// assert_eq!(acc.hd_path(), &hd_path); + +// append_account_to_login_at_file( +// &wallet_file, +// account2.clone(), +// hd_path.clone(), +// id1.clone(), +// id2.clone(), +// &password, +// ) +// .unwrap(); + +// // Check that it is now multiple mnemonic type +// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); +// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); +// let expected = vec![ +// WalletAccount::new(id1.into(), MnemonicAccount::new(account1, hd_path.clone())), +// WalletAccount::new(id2, MnemonicAccount::new(account2, hd_path)), +// ] +// .into(); +// assert_eq!(loaded_accounts, &expected); +// } + +// #[test] +// fn append_accounts_to_existing_login() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let account2 = Mnemonic::generate(24).unwrap(); +// let account3 = Mnemonic::generate(24).unwrap(); +// let account4 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); +// let id2 = LoginId::new("second".to_string()); +// let id3 = AccountId::new("third".to_string()); +// let id4 = AccountId::new("fourth".to_string()); + +// store_login_at_file( +// &wallet_file, +// account1.clone(), +// hd_path.clone(), +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// store_login_at_file( +// &wallet_file, +// account2.clone(), +// hd_path.clone(), +// id2.clone(), +// &password, +// ) +// .unwrap(); + +// // Check that it's there as the correct non-multiple type +// let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); +// let acc2 = loaded_login.as_mnemonic_account().unwrap(); +// assert_eq!(acc2.mnemonic(), &account2); +// assert_eq!(acc2.hd_path(), &hd_path); + +// // Add a third and fourth mnenonic grouped together with the second one +// append_account_to_login_at_file( +// &wallet_file, +// account3.clone(), +// hd_path.clone(), +// id2.clone(), +// id3.clone(), +// &password, +// ) +// .unwrap(); +// append_account_to_login_at_file( +// &wallet_file, +// account4.clone(), +// hd_path.clone(), +// id2.clone(), +// id4.clone(), +// &password, +// ) +// .unwrap(); + +// // Check that we can load all four +// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); +// let acc1 = loaded_login.as_mnemonic_account().unwrap(); +// assert_eq!(acc1.mnemonic(), &account1); +// assert_eq!(acc1.hd_path(), &hd_path); + +// let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); +// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); +// let expected = vec![ +// WalletAccount::new(id2.into(), MnemonicAccount::new(account2, hd_path.clone())), +// WalletAccount::new(id3, MnemonicAccount::new(account3, hd_path.clone())), +// WalletAccount::new(id4, MnemonicAccount::new(account4, hd_path)), +// ] +// .into(); +// assert_eq!(loaded_accounts, &expected); +// } + +// #[test] +// fn append_accounts_to_existing_login_with_multi() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let account2 = Mnemonic::generate(24).unwrap(); +// let account3 = Mnemonic::generate(24).unwrap(); +// let account4 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); +// let id2 = LoginId::new("second".to_string()); +// let id3 = AccountId::new("third".to_string()); +// let id4 = AccountId::new("fourth".to_string()); + +// store_login_with_multiple_accounts_at_file( +// &wallet_file, +// account1.clone(), +// hd_path.clone(), +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// store_login_with_multiple_accounts_at_file( +// &wallet_file, +// account2.clone(), +// hd_path.clone(), +// id2.clone(), +// &password, +// ) +// .unwrap(); + +// // Add a third and fourth mnenonic grouped together with the second one +// append_account_to_login_at_file( +// &wallet_file, +// account3.clone(), +// hd_path.clone(), +// id2.clone(), +// id3.clone(), +// &password, +// ) +// .unwrap(); +// append_account_to_login_at_file( +// &wallet_file, +// account4.clone(), +// hd_path.clone(), +// id2.clone(), +// id4.clone(), +// &password, +// ) +// .unwrap(); + +// // Check that we can load all four +// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); +// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); +// let expected = vec![WalletAccount::new( +// DEFAULT_FIRST_ACCOUNT_NAME.into(), +// MnemonicAccount::new(account1, hd_path.clone()), +// )] +// .into(); +// assert_eq!(loaded_accounts, &expected); + +// let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); +// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); +// let expected = vec![ +// WalletAccount::new( +// DEFAULT_FIRST_ACCOUNT_NAME.into(), +// MnemonicAccount::new(account2, hd_path.clone()), +// ), +// WalletAccount::new(id3, MnemonicAccount::new(account3, hd_path.clone())), +// WalletAccount::new(id4, MnemonicAccount::new(account4, hd_path)), +// ] +// .into(); +// assert_eq!(loaded_accounts, &expected); +// } + +// #[test] +// fn append_account_to_existing_login_with_multi_then_update_pwd_and_load() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let account2 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let new_password = UserPassword::new("new_password".to_string()); +// let login_id = LoginId::new("first".to_string()); +// let appended_account = AccountId::new("second".to_string()); + +// store_login_with_multiple_accounts_at_file( +// &wallet_file, +// account1.clone(), +// hd_path.clone(), +// login_id.clone(), +// &password, +// ) +// .unwrap(); + +// // Append a second mnenonic to the same login +// append_account_to_login_at_file( +// &wallet_file, +// account2.clone(), +// hd_path.clone(), +// login_id.clone(), +// appended_account.clone(), +// &password, +// ) +// .unwrap(); + +// // Update the password +// update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); + +// // Expect that we can load these 2 accounts with the new password +// let loaded_login = +// load_existing_login_at_file(&wallet_file, &login_id, &new_password).unwrap(); +// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); +// let expected = vec![ +// WalletAccount::new( +// DEFAULT_FIRST_ACCOUNT_NAME.into(), +// MnemonicAccount::new(account1, hd_path.clone()), +// ), +// WalletAccount::new(appended_account, MnemonicAccount::new(account2, hd_path)), +// ] +// .into(); +// assert_eq!(loaded_accounts, &expected); + +// // Expect that trying to load these 2 accounts with the old password fails +// let err = load_existing_login_at_file(&wallet_file, &login_id, &password).unwrap_err(); +// assert!(matches!(err, BackendError::StoreCipherError { .. })); +// } + +// #[test] +// fn append_the_same_mnemonic_twice_fails() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); +// let id2 = AccountId::new("second".to_string()); + +// store_login_with_multiple_accounts_at_file( +// &wallet_file, +// account1.clone(), +// hd_path.clone(), +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// assert!(matches!( +// append_account_to_login_at_file(&wallet_file, account1, hd_path, id1, id2, &password), +// Err(BackendError::WalletMnemonicAlreadyExistsInWalletLogin), +// )) +// } + +// #[test] +// fn append_the_same_account_name_twice_fails() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let mnemonic1 = Mnemonic::generate(24).unwrap(); +// let mnemonic2 = Mnemonic::generate(24).unwrap(); +// let mnemonic3 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// // The top-level login id. NOTE: the first account id is always set to default. +// let login_id = LoginId::new("my_login_id".to_string()); + +// // Store the first account under login_id. The first account id is always set to default +// // name. +// store_login_with_multiple_accounts_at_file( +// &wallet_file, +// mnemonic1, +// hd_path.clone(), +// login_id.clone(), +// &password, +// ) +// .unwrap(); + +// // Append another account (account2) to the same login (login_id) +// let account2 = AccountId::new("account_2".to_string()); + +// append_account_to_login_at_file( +// &wallet_file, +// mnemonic2, +// hd_path.clone(), +// login_id.clone(), +// account2.clone(), +// &password, +// ) +// .unwrap(); + +// // Appending the third account, with same account id will fail +// assert!(matches!( +// append_account_to_login_at_file( +// &wallet_file, +// mnemonic3, +// hd_path, +// login_id, +// account2, +// &password, +// ), +// Err(BackendError::WalletAccountIdAlreadyExistsInWalletLogin), +// )) +// } + +// #[test] +// fn delete_the_same_account_twice_for_a_login_fails() { +// let store_dir = tempdir().unwrap(); +// let wallet = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let account2 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); +// let id2 = AccountId::new("second".to_string()); + +// store_login_at_file(&wallet, account1, hd_path.clone(), id1.clone(), &password).unwrap(); + +// append_account_to_login_at_file( +// &wallet, +// account2, +// hd_path, +// id1.clone(), +// id2.clone(), +// &password, +// ) +// .unwrap(); + +// remove_account_from_login_at_file(&wallet, &id1, &id2, &password).unwrap(); + +// assert!(matches!( +// remove_account_from_login_at_file(&wallet, &id1, &id2, &password), +// Err(BackendError::WalletNoSuchAccountIdInWalletLogin), +// )); +// } + +// #[test] +// fn delete_the_same_account_twice_for_a_login_fails_with_multi() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let account2 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); +// let id2 = AccountId::new("second".to_string()); + +// store_login_with_multiple_accounts_at_file( +// &wallet_file, +// account1, +// hd_path.clone(), +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// append_account_to_login_at_file( +// &wallet_file, +// account2, +// hd_path, +// id1.clone(), +// id2.clone(), +// &password, +// ) +// .unwrap(); + +// remove_account_from_login_at_file(&wallet_file, &id1, &id2, &password).unwrap(); + +// assert!(matches!( +// remove_account_from_login_at_file(&wallet_file, &id1, &id2, &password), +// Err(BackendError::WalletNoSuchAccountIdInWalletLogin), +// )); +// } + +// #[test] +// fn delete_appended_account_doesnt_affect_others() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let account2 = Mnemonic::generate(24).unwrap(); +// let account3 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); +// let id2 = LoginId::new("second".to_string()); +// let id3 = AccountId::new("third".to_string()); + +// store_login_at_file( +// &wallet_file, +// account1, +// hd_path.clone(), +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// store_login_at_file( +// &wallet_file, +// account2.clone(), +// hd_path.clone(), +// id2.clone(), +// &password, +// ) +// .unwrap(); + +// append_account_to_login_at_file( +// &wallet_file, +// account3.clone(), +// hd_path.clone(), +// id2.clone(), +// id3.clone(), +// &password, +// ) +// .unwrap(); + +// remove_login_at_file(&wallet_file, &id1).unwrap(); + +// // The second login one is still there +// let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); +// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); +// let expected = vec![ +// WalletAccount::new(id2.into(), MnemonicAccount::new(account2, hd_path.clone())), +// WalletAccount::new(id3, MnemonicAccount::new(account3, hd_path)), +// ] +// .into(); +// assert_eq!(loaded_accounts, &expected); +// } + +// #[test] +// fn remove_all_accounts_for_a_login_removes_the_file_when_empty() { +// let store_dir = tempdir().unwrap(); +// let wallet = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let account2 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); +// let id2 = AccountId::new("second".to_string()); + +// store_login_with_multiple_accounts_at_file( +// &wallet, +// account1, +// hd_path.clone(), +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// append_account_to_login_at_file( +// &wallet, +// account2, +// hd_path, +// id1.clone(), +// id2.clone(), +// &password, +// ) +// .unwrap(); + +// remove_account_from_login_at_file( +// &wallet, +// &id1, +// &DEFAULT_FIRST_ACCOUNT_NAME.into(), +// &password, +// ) +// .unwrap(); +// remove_account_from_login_at_file(&wallet, &id1, &id2, &password).unwrap(); + +// // The file should now be removed +// assert!(!wallet.exists()); + +// // And trying to load when the file is gone fails +// assert!(matches!( +// load_existing_login_at_file(&wallet, &id1, &password), +// Err(BackendError::WalletFileNotFound), +// )); +// } + +// #[test] +// fn remove_all_accounts_for_a_login_removes_that_login() { +// let store_dir = tempdir().unwrap(); +// let wallet = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let account2 = Mnemonic::generate(24).unwrap(); +// let account3 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); +// let id2 = AccountId::new("second".to_string()); +// let id3 = LoginId::new("third".to_string()); + +// store_login_with_multiple_accounts_at_file( +// &wallet, +// account1, +// hd_path.clone(), +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// append_account_to_login_at_file( +// &wallet, +// account2, +// hd_path.clone(), +// id1.clone(), +// id2.clone(), +// &password, +// ) +// .unwrap(); + +// store_login_with_multiple_accounts_at_file( +// &wallet, +// account3.clone(), +// hd_path.clone(), +// id3.clone(), +// &password, +// ) +// .unwrap(); + +// remove_account_from_login_at_file( +// &wallet, +// &id1, +// &DEFAULT_FIRST_ACCOUNT_NAME.into(), +// &password, +// ) +// .unwrap(); +// remove_account_from_login_at_file(&wallet, &id1, &id2, &password).unwrap(); + +// // And trying to load when the file is gone fails +// assert!(matches!( +// load_existing_login_at_file(&wallet, &id1, &password), +// Err(BackendError::WalletNoSuchLoginId), +// )); + +// // The other login is still there +// let loaded_login = load_existing_login_at_file(&wallet, &id3, &password).unwrap(); +// let acc3 = loaded_login.as_multiple_accounts().unwrap(); +// let expected = vec![WalletAccount::new( +// DEFAULT_FIRST_ACCOUNT_NAME.into(), +// MnemonicAccount::new(account3, hd_path), +// )] +// .into(); +// assert_eq!(acc3, &expected); +// } + +// #[test] +// fn append_accounts_and_remove_appended_accounts() { +// let store_dir = tempdir().unwrap(); +// let wallet = store_dir.path().join(WALLET_INFO_FILENAME); +// let acc1 = Mnemonic::generate(24).unwrap(); +// let acc2 = Mnemonic::generate(24).unwrap(); +// let acc3 = Mnemonic::generate(24).unwrap(); +// let acc4 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); +// let id2 = LoginId::new("second".to_string()); +// let id3 = AccountId::new("third".to_string()); +// let id4 = AccountId::new("fourth".to_string()); + +// store_login_at_file( +// &wallet, +// acc1.clone(), +// hd_path.clone(), +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// store_login_at_file( +// &wallet, +// acc2.clone(), +// hd_path.clone(), +// id2.clone(), +// &password, +// ) +// .unwrap(); + +// // Add a third and fourth mnenonic grouped together with the second one +// append_account_to_login_at_file( +// &wallet, +// acc3, +// hd_path.clone(), +// id2.clone(), +// id3.clone(), +// &password, +// ) +// .unwrap(); +// append_account_to_login_at_file( +// &wallet, +// acc4.clone(), +// hd_path.clone(), +// id2.clone(), +// id4.clone(), +// &password, +// ) +// .unwrap(); + +// // Delete the third mnemonic, from the second login entry +// remove_account_from_login_at_file(&wallet, &id2, &id3, &password).unwrap(); + +// // Check that we can still load the other accounts +// let loaded_login = load_existing_login_at_file(&wallet, &id2, &password).unwrap(); +// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); +// let expected = vec![ +// WalletAccount::new( +// id2.clone().into(), +// MnemonicAccount::new(acc2, hd_path.clone()), +// ), +// WalletAccount::new(id4.clone(), MnemonicAccount::new(acc4, hd_path.clone())), +// ] +// .into(); +// assert_eq!(loaded_accounts, &expected); + +// // Delete the second and fourth mnemonic from the second login entry removes the login entry +// remove_account_from_login_at_file(&wallet, &id2, &id2.clone().into(), &password).unwrap(); +// remove_account_from_login_at_file(&wallet, &id2, &id4, &password).unwrap(); +// assert!(matches!( +// load_existing_login_at_file(&wallet, &id2, &password), +// Err(BackendError::WalletNoSuchLoginId), +// )); + +// // The first login is still available +// let loaded_login = load_existing_login_at_file(&wallet, &id1, &password).unwrap(); +// let account = loaded_login.as_mnemonic_account().unwrap(); +// assert_eq!(account.mnemonic(), &acc1); +// assert_eq!(account.hd_path(), &hd_path); +// } + +// #[test] +// fn rename_first_account_in_login() { +// let store_dir = tempdir().unwrap(); +// let wallet = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let login_id = LoginId::new("first".to_string()); + +// store_login_with_multiple_accounts_at_file( +// &wallet, +// account1.clone(), +// hd_path.clone(), +// login_id.clone(), +// &password, +// ) +// .unwrap(); + +// let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); +// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); +// let expected = vec![WalletAccount::new( +// DEFAULT_FIRST_ACCOUNT_NAME.into(), +// MnemonicAccount::new(account1.clone(), hd_path.clone()), +// )] +// .into(); +// assert_eq!(loaded_accounts, &expected); + +// let renamed_account = AccountId::new("new_first".to_string()); + +// rename_account_in_login_at_file( +// &wallet, +// &login_id, +// &DEFAULT_FIRST_ACCOUNT_NAME.into(), +// &renamed_account, +// &password, +// ) +// .unwrap(); + +// let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); +// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); +// let expected = vec![WalletAccount::new( +// renamed_account, +// MnemonicAccount::new(account1, hd_path), +// )] +// .into(); +// assert_eq!(loaded_accounts, &expected); +// } + +// #[test] +// fn rename_one_account_in_login_with_two_accounts() { +// let store_dir = tempdir().unwrap(); +// let wallet = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let account2 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let login_id = LoginId::new("first".to_string()); +// let account_id2 = AccountId::new("second".to_string()); +// let renamed_account_id2 = AccountId::new("new_second".to_string()); + +// store_login_with_multiple_accounts_at_file( +// &wallet, +// account1.clone(), +// hd_path.clone(), +// login_id.clone(), +// &password, +// ) +// .unwrap(); + +// append_account_to_login_at_file( +// &wallet, +// account2.clone(), +// hd_path.clone(), +// login_id.clone(), +// account_id2.clone(), +// &password, +// ) +// .unwrap(); + +// // Load and confirm +// let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); +// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); +// let expected = vec![ +// WalletAccount::new( +// DEFAULT_FIRST_ACCOUNT_NAME.into(), +// MnemonicAccount::new(account1.clone(), hd_path.clone()), +// ), +// WalletAccount::new( +// account_id2.clone(), +// MnemonicAccount::new(account2.clone(), hd_path.clone()), +// ), +// ] +// .into(); +// assert_eq!(loaded_accounts, &expected); + +// // Rename the second account to a new name +// rename_account_in_login_at_file( +// &wallet, +// &login_id, +// &account_id2, +// &renamed_account_id2, +// &password, +// ) +// .unwrap(); + +// // Load and confirm +// let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); +// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); +// let expected = vec![ +// WalletAccount::new( +// DEFAULT_FIRST_ACCOUNT_NAME.into(), +// MnemonicAccount::new(account1, hd_path.clone()), +// ), +// WalletAccount::new(renamed_account_id2, MnemonicAccount::new(account2, hd_path)), +// ] +// .into(); +// assert_eq!(loaded_accounts, &expected); +// } + +// #[test] +// fn rename_account_into_existing_account_id_fails() { +// let store_dir = tempdir().unwrap(); +// let wallet = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let account2 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let login_id = LoginId::new("first".to_string()); +// let account_id2 = AccountId::new("second".to_string()); +// let renamed_account_id2 = DEFAULT_FIRST_ACCOUNT_NAME.into(); + +// store_login_with_multiple_accounts_at_file( +// &wallet, +// account1.clone(), +// hd_path.clone(), +// login_id.clone(), +// &password, +// ) +// .unwrap(); + +// append_account_to_login_at_file( +// &wallet, +// account2.clone(), +// hd_path.clone(), +// login_id.clone(), +// account_id2.clone(), +// &password, +// ) +// .unwrap(); + +// // Load and confirm +// let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); +// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); +// let expected = vec![ +// WalletAccount::new( +// DEFAULT_FIRST_ACCOUNT_NAME.into(), +// MnemonicAccount::new(account1.clone(), hd_path.clone()), +// ), +// WalletAccount::new( +// account_id2.clone(), +// MnemonicAccount::new(account2.clone(), hd_path.clone()), +// ), +// ] +// .into(); +// assert_eq!(loaded_accounts, &expected); + +// // Rename the second account to the name of the first one fails +// assert!(matches!( +// rename_account_in_login_at_file( +// &wallet, +// &login_id, +// &account_id2, +// &renamed_account_id2, +// &password, +// ), +// Err(BackendError::WalletAccountIdAlreadyExistsInWalletLogin) +// )); + +// // Load and confirm nothing was changed +// let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); +// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); +// let expected = vec![ +// WalletAccount::new( +// DEFAULT_FIRST_ACCOUNT_NAME.into(), +// MnemonicAccount::new(account1, hd_path.clone()), +// ), +// WalletAccount::new(account_id2, MnemonicAccount::new(account2, hd_path)), +// ] +// .into(); +// assert_eq!(loaded_accounts, &expected); +// } + +// // Test to that decrypts a stored file from the repo, to make sure we are able to decrypt stored +// // wallets created with older versions. +// #[test] +// fn decrypt_stored_wallet() { +// const SAVED_WALLET: &str = "src/wallet_storage/test-data/saved-wallet.json"; +// let wallet_file = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(SAVED_WALLET); + +// let wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); + +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let bad_password = UserPassword::new("bad-password".to_string()); +// let id1 = LoginId::new("first".to_string()); +// let id2 = LoginId::new("second".to_string()); + +// assert!(!wallet.password_can_decrypt_all(&bad_password)); +// assert!(wallet.password_can_decrypt_all(&password)); + +// let acc1 = wallet.decrypt_login(&id1, &password).unwrap(); +// let acc2 = wallet.decrypt_login(&id2, &password).unwrap(); + +// assert!(matches!(acc1, StoredLogin::Mnemonic(_))); +// assert!(matches!(acc2, StoredLogin::Mnemonic(_))); + +// let expected_acc1 = bip39::Mnemonic::from_str("country mean universe text phone begin deputy reject result good cram illness common cluster proud swamp digital patrol spread bar face december base kick").unwrap(); +// let expected_acc2 = bip39::Mnemonic::from_str("home mansion start quiz dress decide hint second dragon sunny juice always steak real minimum art rival skin draw total pulp foot goddess agent").unwrap(); + +// assert_eq!( +// acc1.as_mnemonic_account().unwrap().mnemonic(), +// &expected_acc1 +// ); +// assert_eq!(acc1.as_mnemonic_account().unwrap().hd_path(), &hd_path,); + +// assert_eq!( +// acc2.as_mnemonic_account().unwrap().mnemonic(), +// &expected_acc2 +// ); +// assert_eq!(acc2.as_mnemonic_account().unwrap().hd_path(), &hd_path,); +// } + +// #[test] +// fn decrypt_stored_wallet_1_0_4() { +// const SAVED_WALLET: &str = "src/wallet_storage/test-data/saved-wallet-1.0.4.json"; +// let wallet_file = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(SAVED_WALLET); + +// let wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); + +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password11!".to_string()); +// let bad_password = UserPassword::new("bad-password".to_string()); +// let login_id = LoginId::new("default".to_string()); + +// assert!(!wallet.password_can_decrypt_all(&bad_password)); +// assert!(wallet.password_can_decrypt_all(&password)); + +// let acc1 = wallet.decrypt_login(&login_id, &password).unwrap(); + +// assert!(matches!(acc1, StoredLogin::Mnemonic(_))); + +// let expected_acc1 = bip39::Mnemonic::from_str("arrow capable abstract industry elevator nominee december piece hotel feed lounge web faint sword veteran bundle hour page actual laptop horror gold test warrior").unwrap(); + +// assert_eq!( +// acc1.as_mnemonic_account().unwrap().mnemonic(), +// &expected_acc1 +// ); +// assert_eq!(acc1.as_mnemonic_account().unwrap().hd_path(), &hd_path,); +// } + +// #[test] +// fn decrypt_stored_wallet_1_0_5_with_multiple_accounts() { +// const SAVED_WALLET: &str = "src/wallet_storage/test-data/saved-wallet-1.0.5.json"; +// let wallet_file = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(SAVED_WALLET); + +// let wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); + +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password11!".to_string()); +// let bad_password = UserPassword::new("bad-password".to_string()); +// let login_id = LoginId::new("default".to_string()); + +// assert!(!wallet.password_can_decrypt_all(&bad_password)); +// assert!(wallet.password_can_decrypt_all(&password)); + +// let login = wallet.decrypt_login(&login_id, &password).unwrap(); + +// assert!(matches!(login, StoredLogin::Multiple(_))); + +// let login = login.as_multiple_accounts().unwrap(); +// assert_eq!(login.len(), 4); + +// let expected_mn1 = bip39::Mnemonic::from_str("arrow capable abstract industry elevator nominee december piece hotel feed lounge web faint sword veteran bundle hour page actual laptop horror gold test warrior").unwrap(); +// let expected_mn2 = bip39::Mnemonic::from_str("border hurt skull lunar goddess second danger game dismiss exhaust oven thumb dog drama onion false orchard spice tent next predict invite cherry green").unwrap(); +// let expected_mn3 = bip39::Mnemonic::from_str("gentle crowd rule snap girl urge flat jump winner cluster night sand museum stock grunt quick tree acquire traffic major awake tag rack peasant").unwrap(); +// let expected_mn4 = bip39::Mnemonic::from_str("debris blue skin annual inhale text border rigid spatial lesson coconut yard horn crystal control survey version vote hawk neck frame arrive oblige width").unwrap(); + +// let expected = vec![ +// WalletAccount::new( +// "default".into(), +// MnemonicAccount::new(expected_mn1, hd_path.clone()), +// ), +// WalletAccount::new( +// "account2".into(), +// MnemonicAccount::new(expected_mn2, hd_path.clone()), +// ), +// WalletAccount::new( +// "foobar".into(), +// MnemonicAccount::new(expected_mn3, hd_path.clone()), +// ), +// WalletAccount::new("42".into(), MnemonicAccount::new(expected_mn4, hd_path)), +// ] +// .into(); + +// assert_eq!(login, &expected); +// } + +// #[test] +// fn append_filename() { +// let wallet_file = PathBuf::from("/tmp/saved-wallet.json"); +// let timestamp = OsString::from("42"); +// #[cfg(target_family = "unix")] +// assert_eq!( +// append_timestamp_to_filename(wallet_file.clone(), timestamp.clone(), None) +// .unwrap() +// .into_os_string() +// .into_string() +// .unwrap(), +// "/tmp/saved-wallet-42.json".to_string(), +// ); +// #[cfg(not(target_family = "unix"))] +// assert_eq!( +// append_timestamp_to_filename(wallet_file.clone(), timestamp.clone(), None) +// .unwrap() +// .into_os_string() +// .into_string() +// .unwrap(), +// r"/tmp\saved-wallet-42.json".to_string(), +// ); #[cfg(target_family = "unix")] assert_eq!( From 2760a17323b73fe3eb1c76689dec1efcc5312742 Mon Sep 17 00:00:00 2001 From: Tommy Verrall Date: Wed, 2 Apr 2025 08:53:40 +0200 Subject: [PATCH 06/48] add the base points back in - now i've reverted back to the original two here, as the compiler is failing around `tauri::api::path` however, looking into the new design for the path resolver in tower this tasks, requires pratically changing the whole wallet_strorage and config set up - it seems pretty straight forward https://v2.tauri.app/start/migrate/from-tauri-1/#migrate-path-to-tauri-manager - however, I would need a second set of eyes on this --- nym-wallet/src-tauri/src/config/mod.rs | 272 +- .../src-tauri/src/wallet_storage/mod.rs | 3612 ++++++++--------- 2 files changed, 1935 insertions(+), 1949 deletions(-) diff --git a/nym-wallet/src-tauri/src/config/mod.rs b/nym-wallet/src-tauri/src/config/mod.rs index 5b7dec4099..1f79075157 100644 --- a/nym-wallet/src-tauri/src/config/mod.rs +++ b/nym-wallet/src-tauri/src/config/mod.rs @@ -11,7 +11,6 @@ use nym_validator_client::nyxd::AccountId as CosmosAccountId; use serde::de::DeserializeOwned; use serde::{Deserialize, Serialize}; use strum::IntoEnumIterator; -use tauri::Manager; use url::Url; use nym_config::defaults::{DenomDetailsOwned, NymNetworkDetails, ValidatorDetails}; @@ -105,35 +104,32 @@ impl NetworkConfig { } impl Config { - fn root_directory(app_handle: &tauri::AppHandle) -> PathBuf { - app_handle - .path() - .local_data_dir() - .expect("Failed to get config directory") + fn root_directory() -> PathBuf { + tauri::api::path::config_dir().expect("Failed to get config directory") } - fn config_directory(app_handle: &tauri::AppHandle) -> PathBuf { - Self::root_directory(app_handle).join(CONFIG_DIR_NAME) + fn config_directory() -> PathBuf { + Self::root_directory().join(CONFIG_DIR_NAME) } - fn config_file_path(app_handle: &tauri::AppHandle, network: Option) -> PathBuf { + fn config_file_path(network: Option) -> PathBuf { if let Some(network) = network { let network_filename = format!("{}.toml", network.as_key()); - Self::config_directory(app_handle).join(network_filename) + Self::config_directory().join(network_filename) } else { - Self::config_directory(app_handle).join(CONFIG_FILENAME) + Self::config_directory().join(CONFIG_FILENAME) } } - pub fn save_to_files(&self, app_handle: &tauri::AppHandle) -> io::Result<()> { + pub fn save_to_files(&self) -> io::Result<()> { log::trace!("Config::save_to_file"); // Make sure the whole directory structure actually exists - fs::create_dir_all(Self::config_directory(app_handle))?; + fs::create_dir_all(Self::config_directory())?; // Global config if let Some(global) = &self.global { - let location = Self::config_file_path(app_handle, None); + let location = Self::config_file_path(None); match toml::to_string_pretty(&global) .map_err(|toml_err| io::Error::new(io::ErrorKind::Other, toml_err)) @@ -154,7 +150,7 @@ impl Config { } }; - let location = Self::config_file_path(app_handle, Some(network)); + let location = Self::config_file_path(Some(network)); match toml::to_string_pretty(config) .map_err(|toml_err| io::Error::new(io::ErrorKind::Other, toml_err)) .map(|toml| fs::write(location.clone(), toml)) @@ -166,10 +162,10 @@ impl Config { Ok(()) } - pub fn load_from_files(app_handle: &tauri::AppHandle) -> Self { + pub fn load_from_files() -> Self { // Global let global = { - let file = Self::config_file_path(app_handle, None); + let file = Self::config_file_path(None); match load_from_file::(file.clone()) { Ok(global) => { log::debug!("Loaded from file {:#?}", file); @@ -185,7 +181,7 @@ impl Config { // One file per network let mut networks = HashMap::new(); for network in WalletNetwork::iter() { - let file = Self::config_file_path(app_handle, Some(network)); + let file = Self::config_file_path(Some(network)); match load_from_file::(file.clone()) { Ok(config) => { log::trace!("Loaded from file {:#?}", file); @@ -265,8 +261,6 @@ impl Config { } } - //////// - pub fn select_nyxd_url(&mut self, nyxd_url: Url, network: WalletNetwork) { if let Some(net) = self.networks.get_mut(&network.as_key()) { net.selected_nyxd_url = Some(nyxd_url); @@ -537,139 +531,139 @@ impl From for NetworkDetails { } } -// #[cfg(test)] -// mod tests { -// use super::*; +#[cfg(test)] +mod tests { + use super::*; -// fn test_config() -> Config { -// let netconfig = NetworkConfig { -// selected_nyxd_url: None, -// selected_api_url: Some("https://my_api_url.com".parse().unwrap()), + fn test_config() -> Config { + let netconfig = NetworkConfig { + selected_nyxd_url: None, + selected_api_url: Some("https://my_api_url.com".parse().unwrap()), -// nyxd_urls: Some(vec![ -// ValidatorConfigEntry { -// nyxd_url: "https://foo".parse().unwrap(), -// nyxd_name: Some("FooName".to_string()), -// api_url: None, -// }, -// ValidatorConfigEntry { -// nyxd_url: "https://bar".parse().unwrap(), -// nyxd_name: None, -// api_url: Some("https://bar/api".parse().unwrap()), -// }, -// ValidatorConfigEntry { -// nyxd_url: "https://baz".parse().unwrap(), -// nyxd_name: None, -// api_url: Some("https://baz/api".parse().unwrap()), -// }, -// ]), -// ..NetworkConfig::default() -// }; + nyxd_urls: Some(vec![ + ValidatorConfigEntry { + nyxd_url: "https://foo".parse().unwrap(), + nyxd_name: Some("FooName".to_string()), + api_url: None, + }, + ValidatorConfigEntry { + nyxd_url: "https://bar".parse().unwrap(), + nyxd_name: None, + api_url: Some("https://bar/api".parse().unwrap()), + }, + ValidatorConfigEntry { + nyxd_url: "https://baz".parse().unwrap(), + nyxd_name: None, + api_url: Some("https://baz/api".parse().unwrap()), + }, + ]), + ..NetworkConfig::default() + }; -// Config { -// base: Base::default(), -// global: Some(GlobalConfig::default()), -// networks: [(WalletNetwork::MAINNET.as_key(), netconfig)] -// .into_iter() -// .collect(), -// } -// } + Config { + base: Base::default(), + global: Some(GlobalConfig::default()), + networks: [(WalletNetwork::MAINNET.as_key(), netconfig)] + .into_iter() + .collect(), + } + } -// #[test] -// fn serialize_to_toml() { -// let config = test_config(); -// let netconfig = &config.networks[&WalletNetwork::MAINNET.as_key()]; -// assert_eq!( -// toml::to_string_pretty(netconfig).unwrap(), -// r#"version = 1 -// selected_api_url = 'https://my_api_url.com/' + #[test] + fn serialize_to_toml() { + let config = test_config(); + let netconfig = &config.networks[&WalletNetwork::MAINNET.as_key()]; + assert_eq!( + toml::to_string_pretty(netconfig).unwrap(), + r#"version = 1 +selected_api_url = 'https://my_api_url.com/' -// [[nyxd_urls]] -// nyxd_url = 'https://foo/' -// nyxd_name = 'FooName' +[[nyxd_urls]] +nyxd_url = 'https://foo/' +nyxd_name = 'FooName' -// [[nyxd_urls]] -// nyxd_url = 'https://bar/' -// api_url = 'https://bar/api' +[[nyxd_urls]] +nyxd_url = 'https://bar/' +api_url = 'https://bar/api' -// [[nyxd_urls]] -// nyxd_url = 'https://baz/' -// api_url = 'https://baz/api' -// "# -// ); -// } +[[nyxd_urls]] +nyxd_url = 'https://baz/' +api_url = 'https://baz/api' +"# + ); + } -// #[test] -// fn serialize_to_json() { -// let config = test_config(); -// let netconfig = &config.networks[&WalletNetwork::MAINNET.as_key()]; -// println!("{}", serde_json::to_string_pretty(netconfig).unwrap()); -// assert_eq!( -// serde_json::to_string_pretty(netconfig).unwrap(), -// r#"{ -// "version": 1, -// "selected_nyxd_url": null, -// "default_nyxd_url": null, -// "selected_api_url": "https://my_api_url.com/", -// "nyxd_urls": [ -// { -// "nyxd_url": "https://foo/", -// "nyxd_name": "FooName", -// "api_url": null -// }, -// { -// "nyxd_url": "https://bar/", -// "nyxd_name": null, -// "api_url": "https://bar/api" -// }, -// { -// "nyxd_url": "https://baz/", -// "nyxd_name": null, -// "api_url": "https://baz/api" -// } -// ] -// }"# -// ); -// } + #[test] + fn serialize_to_json() { + let config = test_config(); + let netconfig = &config.networks[&WalletNetwork::MAINNET.as_key()]; + println!("{}", serde_json::to_string_pretty(netconfig).unwrap()); + assert_eq!( + serde_json::to_string_pretty(netconfig).unwrap(), + r#"{ + "version": 1, + "selected_nyxd_url": null, + "default_nyxd_url": null, + "selected_api_url": "https://my_api_url.com/", + "nyxd_urls": [ + { + "nyxd_url": "https://foo/", + "nyxd_name": "FooName", + "api_url": null + }, + { + "nyxd_url": "https://bar/", + "nyxd_name": null, + "api_url": "https://bar/api" + }, + { + "nyxd_url": "https://baz/", + "nyxd_name": null, + "api_url": "https://baz/api" + } + ] +}"# + ); + } -// #[test] -// fn serialize_and_deserialize_to_toml() { -// let config = test_config(); -// let netconfig = &config.networks[&WalletNetwork::MAINNET.as_key()]; -// let config_str = toml::to_string_pretty(netconfig).unwrap(); -// let config_from_toml: NetworkConfig = toml::from_str(&config_str).unwrap(); -// assert_eq!(netconfig, &config_from_toml); -// } + #[test] + fn serialize_and_deserialize_to_toml() { + let config = test_config(); + let netconfig = &config.networks[&WalletNetwork::MAINNET.as_key()]; + let config_str = toml::to_string_pretty(netconfig).unwrap(); + let config_from_toml: NetworkConfig = toml::from_str(&config_str).unwrap(); + assert_eq!(netconfig, &config_from_toml); + } -// #[test] -// fn get_urls_parsed_from_config() { -// let config = test_config(); + #[test] + fn get_urls_parsed_from_config() { + let config = test_config(); -// let nyxd_url = config -// .get_configured_validators(WalletNetwork::MAINNET) -// .next() -// .map(|v| v.nyxd_url) -// .unwrap(); -// assert_eq!(nyxd_url.as_ref(), "https://foo/"); + let nyxd_url = config + .get_configured_validators(WalletNetwork::MAINNET) + .next() + .map(|v| v.nyxd_url) + .unwrap(); + assert_eq!(nyxd_url.as_ref(), "https://foo/"); -// // The first entry is missing an API URL -// let api_url = config -// .get_configured_validators(WalletNetwork::MAINNET) -// .next() -// .and_then(|v| v.api_url); -// assert_eq!(api_url, None); -// } + // The first entry is missing an API URL + let api_url = config + .get_configured_validators(WalletNetwork::MAINNET) + .next() + .and_then(|v| v.api_url); + assert_eq!(api_url, None); + } -// #[test] -// fn get_urls_from_defaults() { -// let config = Config::default(); + #[test] + fn get_urls_from_defaults() { + let config = Config::default(); -// let nyxd_url = config -// .get_base_validators(WalletNetwork::MAINNET) -// .next() -// .map(|v| v.nyxd_url) -// .unwrap(); -// assert_eq!(nyxd_url.as_ref(), "https://rpc.nymtech.net/"); + let nyxd_url = config + .get_base_validators(WalletNetwork::MAINNET) + .next() + .map(|v| v.nyxd_url) + .unwrap(); + assert_eq!(nyxd_url.as_ref(), "https://rpc.nymtech.net/"); let api_url = config .get_base_validators(WalletNetwork::MAINNET) diff --git a/nym-wallet/src-tauri/src/wallet_storage/mod.rs b/nym-wallet/src-tauri/src/wallet_storage/mod.rs index faa026452d..115823da4a 100644 --- a/nym-wallet/src-tauri/src/wallet_storage/mod.rs +++ b/nym-wallet/src-tauri/src/wallet_storage/mod.rs @@ -19,7 +19,6 @@ use nym_validator_client::nyxd::bip32::DerivationPath; use std::ffi::OsString; use std::fs::{self, create_dir_all, OpenOptions}; use std::path::{Path, PathBuf}; -use tauri::Manager; use time::OffsetDateTime; #[cfg(test)] @@ -38,18 +37,14 @@ pub(crate) const DEFAULT_LOGIN_ID: &str = "default"; /// this name. pub(crate) const DEFAULT_FIRST_ACCOUNT_NAME: &str = "Account 1"; -fn get_storage_directory(app_handle: &tauri::AppHandle) -> Result { - app_handle - .path() - .local_data_dir() +fn get_storage_directory() -> Result { + tauri::api::path::local_data_dir() .map(|dir| dir.join(STORAGE_DIR_NAME)) .ok_or(BackendError::UnknownStorageDirectory) } -pub(crate) fn wallet_login_filepath( - app_handle: &tauri::AppHandle, -) -> Result { - get_storage_directory(app_handle).map(|dir| dir.join(WALLET_INFO_FILENAME)) +pub(crate) fn wallet_login_filepath() -> Result { + get_storage_directory().map(|dir| dir.join(WALLET_INFO_FILENAME)) } fn write_to_file(filepath: &Path, wallet: &StoredWallet) -> Result<(), BackendError> { @@ -64,10 +59,8 @@ fn write_to_file(filepath: &Path, wallet: &StoredWallet) -> Result<(), BackendEr /// Load stored wallet file #[allow(unused)] -pub(crate) fn load_existing_wallet( - app_handle: &tauri::AppHandle, -) -> Result { - let store_dir = get_storage_directory(app_handle)?; +pub(crate) fn load_existing_wallet() -> Result { + let store_dir = get_storage_directory()?; let filepath = store_dir.join(WALLET_INFO_FILENAME); load_existing_wallet_at_file(&filepath) } @@ -84,11 +77,10 @@ fn load_existing_wallet_at_file(filepath: &Path) -> Result Result { - let store_dir = get_storage_directory(app_handle)?; + let store_dir = get_storage_directory()?; let filepath = store_dir.join(WALLET_INFO_FILENAME); load_existing_login_at_file(&filepath, id, password) } @@ -476,1801 +468,1801 @@ fn rename_account_in_login_at_file( write_to_file(filepath, &stored_wallet) } -// #[cfg(test)] -// mod tests { -// use crate::wallet_storage::account_data::WalletAccount; - -// use super::*; -// use nym_config::defaults::COSMOS_DERIVATION_PATH; -// use std::str::FromStr; -// use tempfile::tempdir; - -// #[test] -// fn trying_to_load_nonexistant_wallet_fails() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let id1 = LoginId::new("first".to_string()); -// let password = UserPassword::new("password".to_string()); - -// assert!(matches!( -// load_existing_wallet_at_file(&wallet_file), -// Err(BackendError::WalletFileNotFound), -// )); -// assert!(matches!( -// load_existing_login_at_file(&wallet_file, &id1, &password), -// Err(BackendError::WalletFileNotFound), -// )); -// remove_login_at_file(&wallet_file, &id1).unwrap_err(); -// } - -// #[test] -// fn store_single_login() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let id1 = LoginId::new("first".to_string()); - -// store_login_at_file(&wallet_file, account1, hd_path, id1.clone(), &password).unwrap(); - -// let stored_wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); -// assert_eq!(stored_wallet.len(), 1); - -// let login = stored_wallet.get_encrypted_login_by_index(0).unwrap(); -// assert_eq!(login.id, id1); - -// // some actual ciphertext was saved -// assert!(!login.account.ciphertext().is_empty()); -// } - -// #[test] -// fn store_single_login_with_multi() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let id1 = LoginId::new("first".to_string()); - -// store_login_with_multiple_accounts_at_file( -// &wallet_file, -// account1, -// cosmos_hd_path, -// id1.clone(), -// &password, -// ) -// .unwrap(); - -// let stored_wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); -// assert_eq!(stored_wallet.len(), 1); - -// let login = stored_wallet.get_encrypted_login_by_index(0).unwrap(); -// assert_eq!(login.id, id1); - -// // some actual ciphertext was saved -// assert!(!login.account.ciphertext().is_empty()); -// } - -// #[test] -// fn store_single_login_with_multi_then_update_pwd_and_load() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let new_password = UserPassword::new("new_password".to_string()); -// let id1 = LoginId::new("first".to_string()); - -// store_login_with_multiple_accounts_at_file( -// &wallet_file, -// account1, -// cosmos_hd_path, -// id1.clone(), -// &password, -// ) -// .unwrap(); - -// update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); - -// let stored_wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); -// assert_eq!(stored_wallet.len(), 1); - -// let login = stored_wallet.get_encrypted_login_by_index(0).unwrap(); -// assert_eq!(login.id, id1); - -// // some actual ciphertext was saved -// assert!(!login.account.ciphertext().is_empty()); -// } - -// #[test] -// fn store_twice_for_the_same_id_fails() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let id1 = LoginId::new("first".to_string()); - -// // Store the first login -// store_login_at_file( -// &wallet_file, -// account1.clone(), -// hd_path.clone(), -// id1.clone(), -// &password, -// ) -// .unwrap(); - -// // and storing the same id again fails -// assert!(matches!( -// store_login_at_file(&wallet_file, account1, hd_path, id1, &password,), -// Err(BackendError::WalletLoginIdAlreadyExists), -// )); -// } - -// #[test] -// fn store_twice_for_the_same_id_fails_with_multiple() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let id1 = LoginId::new("first".to_string()); - -// // Store the first login -// store_login_with_multiple_accounts_at_file( -// &wallet_file, -// account1.clone(), -// hd_path.clone(), -// id1.clone(), -// &password, -// ) -// .unwrap(); - -// // and storing the same id again fails -// assert!(matches!( -// store_login_with_multiple_accounts_at_file( -// &wallet_file, -// account1, -// hd_path, -// id1, -// &password, -// ), -// Err(BackendError::WalletLoginIdAlreadyExists), -// )); -// } - -// #[test] -// fn load_with_wrong_password_fails() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let bad_password = UserPassword::new("bad-password".to_string()); -// let id1 = LoginId::new("first".to_string()); - -// store_login_at_file(&wallet_file, account1, hd_path, id1.clone(), &password).unwrap(); - -// // Trying to load it with wrong password now fails -// assert!(matches!( -// load_existing_login_at_file(&wallet_file, &id1, &bad_password), -// Err(BackendError::StoreCipherError { .. }), -// )); -// } - -// #[test] -// fn load_with_wrong_password_fails_with_multi() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let bad_password = UserPassword::new("bad-password".to_string()); -// let id1 = LoginId::new("first".to_string()); - -// store_login_with_multiple_accounts_at_file( -// &wallet_file, -// account1, -// hd_path, -// id1.clone(), -// &password, -// ) -// .unwrap(); - -// // Trying to load it with wrong password now fails -// assert!(matches!( -// load_existing_login_at_file(&wallet_file, &id1, &bad_password), -// Err(BackendError::StoreCipherError { .. }), -// )); -// } - -// #[test] -// fn load_with_wrong_id_fails() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let id1 = LoginId::new("first".to_string()); -// let id2 = LoginId::new("second".to_string()); - -// store_login_at_file(&wallet_file, account1, hd_path, id1, &password).unwrap(); - -// // Trying to load with the wrong id -// assert!(matches!( -// load_existing_login_at_file(&wallet_file, &id2, &password), -// Err(BackendError::WalletNoSuchLoginId), -// )); -// } - -// #[test] -// fn load_with_wrong_id_fails_with_multi() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let id1 = LoginId::new("first".to_string()); -// let id2 = LoginId::new("second".to_string()); - -// store_login_with_multiple_accounts_at_file(&wallet_file, account1, hd_path, id1, &password) -// .unwrap(); - -// // Trying to load with the wrong id -// assert!(matches!( -// load_existing_login_at_file(&wallet_file, &id2, &password), -// Err(BackendError::WalletNoSuchLoginId), -// )); -// } - -// #[test] -// fn store_and_load_a_single_login() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let id1 = LoginId::new("first".to_string()); - -// store_login_at_file( -// &wallet_file, -// account1.clone(), -// hd_path.clone(), -// id1.clone(), -// &password, -// ) -// .unwrap(); - -// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); -// let acc = loaded_login.as_mnemonic_account().unwrap(); -// assert_eq!(&account1, acc.mnemonic()); -// assert_eq!(&hd_path, acc.hd_path()); -// } - -// #[test] -// fn store_a_single_login_then_update_pwd_and_load() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let new_password = UserPassword::new("new_password".to_string()); -// let id1 = LoginId::new("first".to_string()); - -// store_login_at_file( -// &wallet_file, -// account1.clone(), -// hd_path.clone(), -// id1.clone(), -// &password, -// ) -// .unwrap(); - -// update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); - -// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &new_password).unwrap(); -// let acc = loaded_login.as_mnemonic_account().unwrap(); -// assert_eq!(&account1, acc.mnemonic()); -// assert_eq!(&hd_path, acc.hd_path()); -// } - -// #[test] -// fn store_a_single_login_then_update_pwd_with_identical_pwd_is_noop_but_okay() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let id1 = LoginId::new("first".to_string()); - -// store_login_at_file( -// &wallet_file, -// account1.clone(), -// hd_path.clone(), -// id1.clone(), -// &password, -// ) -// .unwrap(); - -// update_encrypted_logins_at_file(&wallet_file, &password, &password).unwrap(); - -// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); -// let acc = loaded_login.as_mnemonic_account().unwrap(); -// assert_eq!(&account1, acc.mnemonic()); -// assert_eq!(&hd_path, acc.hd_path()); -// } - -// #[test] -// fn store_a_single_login_then_update_pwd_with_wrong_current_pwd_fails() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let new_password = UserPassword::new("new_password".to_string()); -// let wrong_password = UserPassword::new("wrong_password".to_string()); -// let id1 = LoginId::new("first".to_string()); - -// store_login_at_file(&wallet_file, account1, hd_path, id1, &password).unwrap(); - -// let err = update_encrypted_logins_at_file(&wallet_file, &wrong_password, &new_password) -// .unwrap_err(); -// assert!(matches!(err, BackendError::StoreCipherError { .. })); -// } - -// #[test] -// fn store_a_single_login_then_update_pwd_and_load_with_wrong_pwd_fails() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let new_password = UserPassword::new("new_password".to_string()); -// let id1 = LoginId::new("first".to_string()); - -// store_login_at_file(&wallet_file, account1, hd_path, id1.clone(), &password).unwrap(); - -// update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); - -// let err = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap_err(); -// assert!(matches!(err, BackendError::StoreCipherError { .. })); -// } - -// #[test] -// fn store_and_load_a_single_login_with_multi() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let acc1 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let id1 = LoginId::new("first".to_string()); - -// store_login_with_multiple_accounts_at_file( -// &wallet_file, -// acc1.clone(), -// hd_path.clone(), -// id1.clone(), -// &password, -// ) -// .unwrap(); - -// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); -// let accounts = loaded_login.as_multiple_accounts().unwrap(); -// assert_eq!(accounts.len(), 1); -// let account = accounts -// .get_account(&DEFAULT_FIRST_ACCOUNT_NAME.into()) -// .unwrap(); -// assert_eq!(account.id().as_ref(), DEFAULT_FIRST_ACCOUNT_NAME); -// assert_eq!(account.mnemonic(), &acc1); -// assert_eq!(account.hd_path(), &hd_path); -// } - -// #[test] -// fn store_a_single_login_with_multi_then_update_pwd_and_load() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let acc1 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let new_password = UserPassword::new("new_password".to_string()); -// let id1 = LoginId::new("first".to_string()); - -// store_login_with_multiple_accounts_at_file( -// &wallet_file, -// acc1.clone(), -// hd_path.clone(), -// id1.clone(), -// &password, -// ) -// .unwrap(); - -// update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); - -// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &new_password).unwrap(); -// let accounts = loaded_login.as_multiple_accounts().unwrap(); -// assert_eq!(accounts.len(), 1); -// let account = accounts -// .get_account(&DEFAULT_FIRST_ACCOUNT_NAME.into()) -// .unwrap(); -// assert_eq!(account.id().as_ref(), DEFAULT_FIRST_ACCOUNT_NAME); -// assert_eq!(account.mnemonic(), &acc1); -// assert_eq!(account.hd_path(), &hd_path); -// } - -// #[test] -// fn store_a_single_login_with_multi_then_update_pwd_with_wrong_current_pwd_fails() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let acc1 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let new_password = UserPassword::new("new_password".to_string()); -// let wrong_password = UserPassword::new("wrong_password".to_string()); -// let id1 = LoginId::new("first".to_string()); - -// store_login_with_multiple_accounts_at_file(&wallet_file, acc1, hd_path, id1, &password) -// .unwrap(); - -// let err = update_encrypted_logins_at_file(&wallet_file, &wrong_password, &new_password) -// .unwrap_err(); -// assert!(matches!(err, BackendError::StoreCipherError { .. })); -// } - -// #[test] -// fn store_a_single_login_with_multi_then_update_pwd_and_load_with_wrong_pwd_fails() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let acc1 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let new_password = UserPassword::new("new_password".to_string()); -// let id1 = LoginId::new("first".to_string()); - -// store_login_with_multiple_accounts_at_file( -// &wallet_file, -// acc1, -// hd_path, -// id1.clone(), -// &password, -// ) -// .unwrap(); - -// update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); - -// let err = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap_err(); -// assert!(matches!(err, BackendError::StoreCipherError { .. })); -// } - -// #[test] -// fn store_a_second_login_with_a_different_password_fails() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let account2 = Mnemonic::generate(24).unwrap(); -// let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let bad_password = UserPassword::new("bad-password".to_string()); -// let id1 = LoginId::new("first".to_string()); -// let id2 = LoginId::new("second".to_string()); - -// store_login_at_file( -// &wallet_file, -// account1, -// cosmos_hd_path.clone(), -// id1, -// &password, -// ) -// .unwrap(); - -// // Can't store a second login if you use different password -// assert!(matches!( -// store_login_at_file(&wallet_file, account2, cosmos_hd_path, id2, &bad_password), -// Err(BackendError::WalletDifferentPasswordDetected), -// )); -// } - -// #[test] -// fn store_a_second_login_with_a_different_password_fails_with_multi() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let account2 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let bad_password = UserPassword::new("bad-password".to_string()); -// let id1 = LoginId::new("first".to_string()); -// let id2 = LoginId::new("second".to_string()); - -// store_login_with_multiple_accounts_at_file( -// &wallet_file, -// account1, -// hd_path.clone(), -// id1, -// &password, -// ) -// .unwrap(); - -// // Can't store a second login if you use different password -// assert!(matches!( -// store_login_with_multiple_accounts_at_file( -// &wallet_file, -// account2, -// hd_path, -// id2, -// &bad_password -// ), -// Err(BackendError::WalletDifferentPasswordDetected), -// )); -// } - -// #[test] -// fn store_two_mnemonic_accounts_gives_different_salts_and_iv() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let account2 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let different_hd_path: DerivationPath = "m".parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let id1 = LoginId::new("first".to_string()); -// let id2 = LoginId::new("second".to_string()); - -// // Store the first account -// store_login_at_file(&wallet_file, account1, hd_path, id1, &password).unwrap(); - -// let stored_wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); -// let encrypted_blob = &stored_wallet -// .get_encrypted_login_by_index(0) -// .unwrap() -// .account; - -// // keep track of salt and iv for future assertion -// let original_iv = encrypted_blob.iv().to_vec(); -// let original_salt = encrypted_blob.salt().to_vec(); - -// // Add an extra account -// store_login_at_file(&wallet_file, account2, different_hd_path, id2, &password).unwrap(); - -// let loaded_accounts = load_existing_wallet_at_file(&wallet_file).unwrap(); -// assert_eq!(loaded_accounts.len(), 2); -// let encrypted_blob = &loaded_accounts -// .get_encrypted_login_by_index(1) -// .unwrap() -// .account; - -// // fresh IV and salt are used -// assert_ne!(original_iv, encrypted_blob.iv()); -// assert_ne!(original_salt, encrypted_blob.salt()); -// } - -// #[test] -// fn store_two_mnemonic_accounts_using_two_logins() { -// let store_dir = tempdir().unwrap(); -// let wallet = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let account2 = Mnemonic::generate(24).unwrap(); -// let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let different_hd_path: DerivationPath = "m".parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let id1 = LoginId::new("first".to_string()); -// let id2 = LoginId::new("second".to_string()); - -// // Store the first account -// store_login_at_file( -// &wallet, -// account1.clone(), -// cosmos_hd_path.clone(), -// id1.clone(), -// &password, -// ) -// .unwrap(); - -// let login = load_existing_login_at_file(&wallet, &id1, &password).unwrap(); -// let acc = login.as_mnemonic_account().unwrap(); -// assert_eq!(&account1, acc.mnemonic()); -// assert_eq!(&cosmos_hd_path, acc.hd_path()); - -// // Add an extra account -// store_login_at_file( -// &wallet, -// account2.clone(), -// different_hd_path.clone(), -// id2.clone(), -// &password, -// ) -// .unwrap(); - -// // first account should be unchanged -// let loaded_login = load_existing_login_at_file(&wallet, &id1, &password).unwrap(); -// let acc1 = loaded_login.as_mnemonic_account().unwrap(); -// assert_eq!(&account1, acc1.mnemonic()); -// assert_eq!(&cosmos_hd_path, acc1.hd_path()); - -// let loaded_login = load_existing_login_at_file(&wallet, &id2, &password).unwrap(); -// let acc2 = loaded_login.as_mnemonic_account().unwrap(); -// assert_eq!(&account2, acc2.mnemonic()); -// assert_eq!(&different_hd_path, acc2.hd_path()); -// } - -// #[test] -// fn store_two_mnemonic_accounts_using_two_logins_then_update_pwd_and_load() { -// let store_dir = tempdir().unwrap(); -// let wallet = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let account2 = Mnemonic::generate(24).unwrap(); -// let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let different_hd_path: DerivationPath = "m".parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let new_password = UserPassword::new("new_password".to_string()); -// let id1 = LoginId::new("first".to_string()); -// let id2 = LoginId::new("second".to_string()); - -// // Store the first login with an account -// store_login_at_file( -// &wallet, -// account1.clone(), -// cosmos_hd_path.clone(), -// id1.clone(), -// &password, -// ) -// .unwrap(); - -// let login = load_existing_login_at_file(&wallet, &id1, &password).unwrap(); -// let acc = login.as_mnemonic_account().unwrap(); -// assert_eq!(&account1, acc.mnemonic()); -// assert_eq!(&cosmos_hd_path, acc.hd_path()); - -// // Store a second login, also with an account -// store_login_at_file( -// &wallet, -// account2.clone(), -// different_hd_path.clone(), -// id2.clone(), -// &password, -// ) -// .unwrap(); - -// update_encrypted_logins_at_file(&wallet, &password, &new_password).unwrap(); - -// // first account should be unchanged -// let loaded_login = load_existing_login_at_file(&wallet, &id1, &new_password).unwrap(); -// let acc1 = loaded_login.as_mnemonic_account().unwrap(); -// assert_eq!(&account1, acc1.mnemonic()); -// assert_eq!(&cosmos_hd_path, acc1.hd_path()); - -// let loaded_login = load_existing_login_at_file(&wallet, &id2, &new_password).unwrap(); -// let acc2 = loaded_login.as_mnemonic_account().unwrap(); -// assert_eq!(&account2, acc2.mnemonic()); -// assert_eq!(&different_hd_path, acc2.hd_path()); -// } - -// #[test] -// fn store_one_mnemonic_account_and_one_multi_account() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let account2 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let different_hd_path: DerivationPath = "m".parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let id1 = LoginId::new("first".to_string()); -// let id2 = LoginId::new("second".to_string()); - -// // Store the first account -// store_login_at_file( -// &wallet_file, -// account1.clone(), -// hd_path.clone(), -// id1.clone(), -// &password, -// ) -// .unwrap(); - -// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); -// let acc = loaded_login.as_mnemonic_account().unwrap(); -// assert_eq!(&account1, acc.mnemonic()); -// assert_eq!(&hd_path, acc.hd_path()); - -// // Add an extra account -// store_login_with_multiple_accounts_at_file( -// &wallet_file, -// account2.clone(), -// different_hd_path.clone(), -// id2.clone(), -// &password, -// ) -// .unwrap(); - -// // first account should be unchanged -// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); -// let acc1 = loaded_login.as_mnemonic_account().unwrap(); -// assert_eq!(&account1, acc1.mnemonic()); -// assert_eq!(&hd_path, acc1.hd_path()); - -// let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); -// let acc2 = loaded_login.as_multiple_accounts().unwrap(); -// assert_eq!(acc2.len(), 1); -// let account = acc2 -// .get_account(&DEFAULT_FIRST_ACCOUNT_NAME.into()) -// .unwrap(); -// assert_eq!(account.id().as_ref(), DEFAULT_FIRST_ACCOUNT_NAME); -// assert_eq!(account.mnemonic(), &account2); -// assert_eq!(account.hd_path(), &different_hd_path); -// } - -// #[test] -// fn remove_non_existent_id_fails() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let id1 = LoginId::new("first".to_string()); -// let id2 = LoginId::new("second".to_string()); - -// store_login_with_multiple_accounts_at_file(&wallet_file, account1, hd_path, id1, &password) -// .unwrap(); - -// // Fails to delete non-existent id in the wallet -// assert!(matches!( -// remove_login_at_file(&wallet_file, &id2), -// Err(BackendError::WalletNoSuchLoginId), -// )); -// } - -// #[test] -// fn store_and_remove_wallet_login_information() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let account2 = Mnemonic::generate(24).unwrap(); -// let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let different_hd_path: DerivationPath = "m".parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let id1 = LoginId::new("first".to_string()); -// let id2 = LoginId::new("second".to_string()); - -// // Store two accounts with two different passwords -// store_login_at_file( -// &wallet_file, -// account1.clone(), -// cosmos_hd_path.clone(), -// id1.clone(), -// &password, -// ) -// .unwrap(); -// store_login_at_file( -// &wallet_file, -// account2.clone(), -// different_hd_path.clone(), -// id2.clone(), -// &password, -// ) -// .unwrap(); - -// // Load and compare -// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); -// let acc1 = loaded_login.as_mnemonic_account().unwrap(); -// assert_eq!(&account1, acc1.mnemonic()); -// assert_eq!(&cosmos_hd_path, acc1.hd_path()); - -// let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); -// let acc2 = loaded_login.as_mnemonic_account().unwrap(); -// assert_eq!(&account2, acc2.mnemonic()); -// assert_eq!(&different_hd_path, acc2.hd_path()); - -// // Delete the second account -// remove_login_at_file(&wallet_file, &id2).unwrap(); - -// // The first account should be unchanged -// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); -// let acc1 = loaded_login.as_mnemonic_account().unwrap(); -// assert_eq!(&account1, acc1.mnemonic()); -// assert_eq!(&cosmos_hd_path, acc1.hd_path()); - -// // And we can't load the second one anymore -// assert!(matches!( -// load_existing_login_at_file(&wallet_file, &id2, &password), -// Err(BackendError::WalletNoSuchLoginId), -// )); - -// // Delete the first account -// assert!(wallet_file.exists()); -// remove_login_at_file(&wallet_file, &id1).unwrap(); - -// // The file should now be removed -// assert!(!wallet_file.exists()); - -// // And trying to load when the file is gone fails -// assert!(matches!( -// load_existing_login_at_file(&wallet_file, &id1, &password), -// Err(BackendError::WalletFileNotFound), -// )); -// } - -// #[test] -// fn append_account_converts_the_type() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let account2 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let id1 = LoginId::new("first".to_string()); -// let id2 = AccountId::new("second".to_string()); - -// store_login_at_file( -// &wallet_file, -// account1.clone(), -// hd_path.clone(), -// id1.clone(), -// &password, -// ) -// .unwrap(); - -// // Check that it's there as the correct non-multiple type -// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); -// let acc = loaded_login.as_mnemonic_account().unwrap(); -// assert_eq!(acc.mnemonic(), &account1); -// assert_eq!(acc.hd_path(), &hd_path); - -// append_account_to_login_at_file( -// &wallet_file, -// account2.clone(), -// hd_path.clone(), -// id1.clone(), -// id2.clone(), -// &password, -// ) -// .unwrap(); - -// // Check that it is now multiple mnemonic type -// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); -// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); -// let expected = vec![ -// WalletAccount::new(id1.into(), MnemonicAccount::new(account1, hd_path.clone())), -// WalletAccount::new(id2, MnemonicAccount::new(account2, hd_path)), -// ] -// .into(); -// assert_eq!(loaded_accounts, &expected); -// } - -// #[test] -// fn append_accounts_to_existing_login() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let account2 = Mnemonic::generate(24).unwrap(); -// let account3 = Mnemonic::generate(24).unwrap(); -// let account4 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let id1 = LoginId::new("first".to_string()); -// let id2 = LoginId::new("second".to_string()); -// let id3 = AccountId::new("third".to_string()); -// let id4 = AccountId::new("fourth".to_string()); - -// store_login_at_file( -// &wallet_file, -// account1.clone(), -// hd_path.clone(), -// id1.clone(), -// &password, -// ) -// .unwrap(); - -// store_login_at_file( -// &wallet_file, -// account2.clone(), -// hd_path.clone(), -// id2.clone(), -// &password, -// ) -// .unwrap(); - -// // Check that it's there as the correct non-multiple type -// let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); -// let acc2 = loaded_login.as_mnemonic_account().unwrap(); -// assert_eq!(acc2.mnemonic(), &account2); -// assert_eq!(acc2.hd_path(), &hd_path); - -// // Add a third and fourth mnenonic grouped together with the second one -// append_account_to_login_at_file( -// &wallet_file, -// account3.clone(), -// hd_path.clone(), -// id2.clone(), -// id3.clone(), -// &password, -// ) -// .unwrap(); -// append_account_to_login_at_file( -// &wallet_file, -// account4.clone(), -// hd_path.clone(), -// id2.clone(), -// id4.clone(), -// &password, -// ) -// .unwrap(); - -// // Check that we can load all four -// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); -// let acc1 = loaded_login.as_mnemonic_account().unwrap(); -// assert_eq!(acc1.mnemonic(), &account1); -// assert_eq!(acc1.hd_path(), &hd_path); - -// let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); -// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); -// let expected = vec![ -// WalletAccount::new(id2.into(), MnemonicAccount::new(account2, hd_path.clone())), -// WalletAccount::new(id3, MnemonicAccount::new(account3, hd_path.clone())), -// WalletAccount::new(id4, MnemonicAccount::new(account4, hd_path)), -// ] -// .into(); -// assert_eq!(loaded_accounts, &expected); -// } - -// #[test] -// fn append_accounts_to_existing_login_with_multi() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let account2 = Mnemonic::generate(24).unwrap(); -// let account3 = Mnemonic::generate(24).unwrap(); -// let account4 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let id1 = LoginId::new("first".to_string()); -// let id2 = LoginId::new("second".to_string()); -// let id3 = AccountId::new("third".to_string()); -// let id4 = AccountId::new("fourth".to_string()); - -// store_login_with_multiple_accounts_at_file( -// &wallet_file, -// account1.clone(), -// hd_path.clone(), -// id1.clone(), -// &password, -// ) -// .unwrap(); - -// store_login_with_multiple_accounts_at_file( -// &wallet_file, -// account2.clone(), -// hd_path.clone(), -// id2.clone(), -// &password, -// ) -// .unwrap(); - -// // Add a third and fourth mnenonic grouped together with the second one -// append_account_to_login_at_file( -// &wallet_file, -// account3.clone(), -// hd_path.clone(), -// id2.clone(), -// id3.clone(), -// &password, -// ) -// .unwrap(); -// append_account_to_login_at_file( -// &wallet_file, -// account4.clone(), -// hd_path.clone(), -// id2.clone(), -// id4.clone(), -// &password, -// ) -// .unwrap(); - -// // Check that we can load all four -// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); -// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); -// let expected = vec![WalletAccount::new( -// DEFAULT_FIRST_ACCOUNT_NAME.into(), -// MnemonicAccount::new(account1, hd_path.clone()), -// )] -// .into(); -// assert_eq!(loaded_accounts, &expected); - -// let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); -// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); -// let expected = vec![ -// WalletAccount::new( -// DEFAULT_FIRST_ACCOUNT_NAME.into(), -// MnemonicAccount::new(account2, hd_path.clone()), -// ), -// WalletAccount::new(id3, MnemonicAccount::new(account3, hd_path.clone())), -// WalletAccount::new(id4, MnemonicAccount::new(account4, hd_path)), -// ] -// .into(); -// assert_eq!(loaded_accounts, &expected); -// } - -// #[test] -// fn append_account_to_existing_login_with_multi_then_update_pwd_and_load() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let account2 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let new_password = UserPassword::new("new_password".to_string()); -// let login_id = LoginId::new("first".to_string()); -// let appended_account = AccountId::new("second".to_string()); - -// store_login_with_multiple_accounts_at_file( -// &wallet_file, -// account1.clone(), -// hd_path.clone(), -// login_id.clone(), -// &password, -// ) -// .unwrap(); - -// // Append a second mnenonic to the same login -// append_account_to_login_at_file( -// &wallet_file, -// account2.clone(), -// hd_path.clone(), -// login_id.clone(), -// appended_account.clone(), -// &password, -// ) -// .unwrap(); - -// // Update the password -// update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); - -// // Expect that we can load these 2 accounts with the new password -// let loaded_login = -// load_existing_login_at_file(&wallet_file, &login_id, &new_password).unwrap(); -// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); -// let expected = vec![ -// WalletAccount::new( -// DEFAULT_FIRST_ACCOUNT_NAME.into(), -// MnemonicAccount::new(account1, hd_path.clone()), -// ), -// WalletAccount::new(appended_account, MnemonicAccount::new(account2, hd_path)), -// ] -// .into(); -// assert_eq!(loaded_accounts, &expected); - -// // Expect that trying to load these 2 accounts with the old password fails -// let err = load_existing_login_at_file(&wallet_file, &login_id, &password).unwrap_err(); -// assert!(matches!(err, BackendError::StoreCipherError { .. })); -// } - -// #[test] -// fn append_the_same_mnemonic_twice_fails() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let id1 = LoginId::new("first".to_string()); -// let id2 = AccountId::new("second".to_string()); - -// store_login_with_multiple_accounts_at_file( -// &wallet_file, -// account1.clone(), -// hd_path.clone(), -// id1.clone(), -// &password, -// ) -// .unwrap(); - -// assert!(matches!( -// append_account_to_login_at_file(&wallet_file, account1, hd_path, id1, id2, &password), -// Err(BackendError::WalletMnemonicAlreadyExistsInWalletLogin), -// )) -// } - -// #[test] -// fn append_the_same_account_name_twice_fails() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let mnemonic1 = Mnemonic::generate(24).unwrap(); -// let mnemonic2 = Mnemonic::generate(24).unwrap(); -// let mnemonic3 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// // The top-level login id. NOTE: the first account id is always set to default. -// let login_id = LoginId::new("my_login_id".to_string()); - -// // Store the first account under login_id. The first account id is always set to default -// // name. -// store_login_with_multiple_accounts_at_file( -// &wallet_file, -// mnemonic1, -// hd_path.clone(), -// login_id.clone(), -// &password, -// ) -// .unwrap(); - -// // Append another account (account2) to the same login (login_id) -// let account2 = AccountId::new("account_2".to_string()); - -// append_account_to_login_at_file( -// &wallet_file, -// mnemonic2, -// hd_path.clone(), -// login_id.clone(), -// account2.clone(), -// &password, -// ) -// .unwrap(); - -// // Appending the third account, with same account id will fail -// assert!(matches!( -// append_account_to_login_at_file( -// &wallet_file, -// mnemonic3, -// hd_path, -// login_id, -// account2, -// &password, -// ), -// Err(BackendError::WalletAccountIdAlreadyExistsInWalletLogin), -// )) -// } - -// #[test] -// fn delete_the_same_account_twice_for_a_login_fails() { -// let store_dir = tempdir().unwrap(); -// let wallet = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let account2 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let id1 = LoginId::new("first".to_string()); -// let id2 = AccountId::new("second".to_string()); - -// store_login_at_file(&wallet, account1, hd_path.clone(), id1.clone(), &password).unwrap(); - -// append_account_to_login_at_file( -// &wallet, -// account2, -// hd_path, -// id1.clone(), -// id2.clone(), -// &password, -// ) -// .unwrap(); - -// remove_account_from_login_at_file(&wallet, &id1, &id2, &password).unwrap(); - -// assert!(matches!( -// remove_account_from_login_at_file(&wallet, &id1, &id2, &password), -// Err(BackendError::WalletNoSuchAccountIdInWalletLogin), -// )); -// } - -// #[test] -// fn delete_the_same_account_twice_for_a_login_fails_with_multi() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let account2 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let id1 = LoginId::new("first".to_string()); -// let id2 = AccountId::new("second".to_string()); - -// store_login_with_multiple_accounts_at_file( -// &wallet_file, -// account1, -// hd_path.clone(), -// id1.clone(), -// &password, -// ) -// .unwrap(); - -// append_account_to_login_at_file( -// &wallet_file, -// account2, -// hd_path, -// id1.clone(), -// id2.clone(), -// &password, -// ) -// .unwrap(); - -// remove_account_from_login_at_file(&wallet_file, &id1, &id2, &password).unwrap(); - -// assert!(matches!( -// remove_account_from_login_at_file(&wallet_file, &id1, &id2, &password), -// Err(BackendError::WalletNoSuchAccountIdInWalletLogin), -// )); -// } - -// #[test] -// fn delete_appended_account_doesnt_affect_others() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let account2 = Mnemonic::generate(24).unwrap(); -// let account3 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let id1 = LoginId::new("first".to_string()); -// let id2 = LoginId::new("second".to_string()); -// let id3 = AccountId::new("third".to_string()); - -// store_login_at_file( -// &wallet_file, -// account1, -// hd_path.clone(), -// id1.clone(), -// &password, -// ) -// .unwrap(); - -// store_login_at_file( -// &wallet_file, -// account2.clone(), -// hd_path.clone(), -// id2.clone(), -// &password, -// ) -// .unwrap(); - -// append_account_to_login_at_file( -// &wallet_file, -// account3.clone(), -// hd_path.clone(), -// id2.clone(), -// id3.clone(), -// &password, -// ) -// .unwrap(); - -// remove_login_at_file(&wallet_file, &id1).unwrap(); - -// // The second login one is still there -// let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); -// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); -// let expected = vec![ -// WalletAccount::new(id2.into(), MnemonicAccount::new(account2, hd_path.clone())), -// WalletAccount::new(id3, MnemonicAccount::new(account3, hd_path)), -// ] -// .into(); -// assert_eq!(loaded_accounts, &expected); -// } - -// #[test] -// fn remove_all_accounts_for_a_login_removes_the_file_when_empty() { -// let store_dir = tempdir().unwrap(); -// let wallet = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let account2 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let id1 = LoginId::new("first".to_string()); -// let id2 = AccountId::new("second".to_string()); - -// store_login_with_multiple_accounts_at_file( -// &wallet, -// account1, -// hd_path.clone(), -// id1.clone(), -// &password, -// ) -// .unwrap(); - -// append_account_to_login_at_file( -// &wallet, -// account2, -// hd_path, -// id1.clone(), -// id2.clone(), -// &password, -// ) -// .unwrap(); - -// remove_account_from_login_at_file( -// &wallet, -// &id1, -// &DEFAULT_FIRST_ACCOUNT_NAME.into(), -// &password, -// ) -// .unwrap(); -// remove_account_from_login_at_file(&wallet, &id1, &id2, &password).unwrap(); - -// // The file should now be removed -// assert!(!wallet.exists()); - -// // And trying to load when the file is gone fails -// assert!(matches!( -// load_existing_login_at_file(&wallet, &id1, &password), -// Err(BackendError::WalletFileNotFound), -// )); -// } - -// #[test] -// fn remove_all_accounts_for_a_login_removes_that_login() { -// let store_dir = tempdir().unwrap(); -// let wallet = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let account2 = Mnemonic::generate(24).unwrap(); -// let account3 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let id1 = LoginId::new("first".to_string()); -// let id2 = AccountId::new("second".to_string()); -// let id3 = LoginId::new("third".to_string()); - -// store_login_with_multiple_accounts_at_file( -// &wallet, -// account1, -// hd_path.clone(), -// id1.clone(), -// &password, -// ) -// .unwrap(); - -// append_account_to_login_at_file( -// &wallet, -// account2, -// hd_path.clone(), -// id1.clone(), -// id2.clone(), -// &password, -// ) -// .unwrap(); - -// store_login_with_multiple_accounts_at_file( -// &wallet, -// account3.clone(), -// hd_path.clone(), -// id3.clone(), -// &password, -// ) -// .unwrap(); - -// remove_account_from_login_at_file( -// &wallet, -// &id1, -// &DEFAULT_FIRST_ACCOUNT_NAME.into(), -// &password, -// ) -// .unwrap(); -// remove_account_from_login_at_file(&wallet, &id1, &id2, &password).unwrap(); - -// // And trying to load when the file is gone fails -// assert!(matches!( -// load_existing_login_at_file(&wallet, &id1, &password), -// Err(BackendError::WalletNoSuchLoginId), -// )); - -// // The other login is still there -// let loaded_login = load_existing_login_at_file(&wallet, &id3, &password).unwrap(); -// let acc3 = loaded_login.as_multiple_accounts().unwrap(); -// let expected = vec![WalletAccount::new( -// DEFAULT_FIRST_ACCOUNT_NAME.into(), -// MnemonicAccount::new(account3, hd_path), -// )] -// .into(); -// assert_eq!(acc3, &expected); -// } - -// #[test] -// fn append_accounts_and_remove_appended_accounts() { -// let store_dir = tempdir().unwrap(); -// let wallet = store_dir.path().join(WALLET_INFO_FILENAME); -// let acc1 = Mnemonic::generate(24).unwrap(); -// let acc2 = Mnemonic::generate(24).unwrap(); -// let acc3 = Mnemonic::generate(24).unwrap(); -// let acc4 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let id1 = LoginId::new("first".to_string()); -// let id2 = LoginId::new("second".to_string()); -// let id3 = AccountId::new("third".to_string()); -// let id4 = AccountId::new("fourth".to_string()); - -// store_login_at_file( -// &wallet, -// acc1.clone(), -// hd_path.clone(), -// id1.clone(), -// &password, -// ) -// .unwrap(); - -// store_login_at_file( -// &wallet, -// acc2.clone(), -// hd_path.clone(), -// id2.clone(), -// &password, -// ) -// .unwrap(); - -// // Add a third and fourth mnenonic grouped together with the second one -// append_account_to_login_at_file( -// &wallet, -// acc3, -// hd_path.clone(), -// id2.clone(), -// id3.clone(), -// &password, -// ) -// .unwrap(); -// append_account_to_login_at_file( -// &wallet, -// acc4.clone(), -// hd_path.clone(), -// id2.clone(), -// id4.clone(), -// &password, -// ) -// .unwrap(); - -// // Delete the third mnemonic, from the second login entry -// remove_account_from_login_at_file(&wallet, &id2, &id3, &password).unwrap(); - -// // Check that we can still load the other accounts -// let loaded_login = load_existing_login_at_file(&wallet, &id2, &password).unwrap(); -// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); -// let expected = vec![ -// WalletAccount::new( -// id2.clone().into(), -// MnemonicAccount::new(acc2, hd_path.clone()), -// ), -// WalletAccount::new(id4.clone(), MnemonicAccount::new(acc4, hd_path.clone())), -// ] -// .into(); -// assert_eq!(loaded_accounts, &expected); - -// // Delete the second and fourth mnemonic from the second login entry removes the login entry -// remove_account_from_login_at_file(&wallet, &id2, &id2.clone().into(), &password).unwrap(); -// remove_account_from_login_at_file(&wallet, &id2, &id4, &password).unwrap(); -// assert!(matches!( -// load_existing_login_at_file(&wallet, &id2, &password), -// Err(BackendError::WalletNoSuchLoginId), -// )); - -// // The first login is still available -// let loaded_login = load_existing_login_at_file(&wallet, &id1, &password).unwrap(); -// let account = loaded_login.as_mnemonic_account().unwrap(); -// assert_eq!(account.mnemonic(), &acc1); -// assert_eq!(account.hd_path(), &hd_path); -// } - -// #[test] -// fn rename_first_account_in_login() { -// let store_dir = tempdir().unwrap(); -// let wallet = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let login_id = LoginId::new("first".to_string()); - -// store_login_with_multiple_accounts_at_file( -// &wallet, -// account1.clone(), -// hd_path.clone(), -// login_id.clone(), -// &password, -// ) -// .unwrap(); - -// let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); -// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); -// let expected = vec![WalletAccount::new( -// DEFAULT_FIRST_ACCOUNT_NAME.into(), -// MnemonicAccount::new(account1.clone(), hd_path.clone()), -// )] -// .into(); -// assert_eq!(loaded_accounts, &expected); - -// let renamed_account = AccountId::new("new_first".to_string()); - -// rename_account_in_login_at_file( -// &wallet, -// &login_id, -// &DEFAULT_FIRST_ACCOUNT_NAME.into(), -// &renamed_account, -// &password, -// ) -// .unwrap(); - -// let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); -// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); -// let expected = vec![WalletAccount::new( -// renamed_account, -// MnemonicAccount::new(account1, hd_path), -// )] -// .into(); -// assert_eq!(loaded_accounts, &expected); -// } - -// #[test] -// fn rename_one_account_in_login_with_two_accounts() { -// let store_dir = tempdir().unwrap(); -// let wallet = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let account2 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let login_id = LoginId::new("first".to_string()); -// let account_id2 = AccountId::new("second".to_string()); -// let renamed_account_id2 = AccountId::new("new_second".to_string()); - -// store_login_with_multiple_accounts_at_file( -// &wallet, -// account1.clone(), -// hd_path.clone(), -// login_id.clone(), -// &password, -// ) -// .unwrap(); - -// append_account_to_login_at_file( -// &wallet, -// account2.clone(), -// hd_path.clone(), -// login_id.clone(), -// account_id2.clone(), -// &password, -// ) -// .unwrap(); - -// // Load and confirm -// let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); -// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); -// let expected = vec![ -// WalletAccount::new( -// DEFAULT_FIRST_ACCOUNT_NAME.into(), -// MnemonicAccount::new(account1.clone(), hd_path.clone()), -// ), -// WalletAccount::new( -// account_id2.clone(), -// MnemonicAccount::new(account2.clone(), hd_path.clone()), -// ), -// ] -// .into(); -// assert_eq!(loaded_accounts, &expected); - -// // Rename the second account to a new name -// rename_account_in_login_at_file( -// &wallet, -// &login_id, -// &account_id2, -// &renamed_account_id2, -// &password, -// ) -// .unwrap(); - -// // Load and confirm -// let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); -// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); -// let expected = vec![ -// WalletAccount::new( -// DEFAULT_FIRST_ACCOUNT_NAME.into(), -// MnemonicAccount::new(account1, hd_path.clone()), -// ), -// WalletAccount::new(renamed_account_id2, MnemonicAccount::new(account2, hd_path)), -// ] -// .into(); -// assert_eq!(loaded_accounts, &expected); -// } - -// #[test] -// fn rename_account_into_existing_account_id_fails() { -// let store_dir = tempdir().unwrap(); -// let wallet = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let account2 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let login_id = LoginId::new("first".to_string()); -// let account_id2 = AccountId::new("second".to_string()); -// let renamed_account_id2 = DEFAULT_FIRST_ACCOUNT_NAME.into(); - -// store_login_with_multiple_accounts_at_file( -// &wallet, -// account1.clone(), -// hd_path.clone(), -// login_id.clone(), -// &password, -// ) -// .unwrap(); - -// append_account_to_login_at_file( -// &wallet, -// account2.clone(), -// hd_path.clone(), -// login_id.clone(), -// account_id2.clone(), -// &password, -// ) -// .unwrap(); - -// // Load and confirm -// let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); -// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); -// let expected = vec![ -// WalletAccount::new( -// DEFAULT_FIRST_ACCOUNT_NAME.into(), -// MnemonicAccount::new(account1.clone(), hd_path.clone()), -// ), -// WalletAccount::new( -// account_id2.clone(), -// MnemonicAccount::new(account2.clone(), hd_path.clone()), -// ), -// ] -// .into(); -// assert_eq!(loaded_accounts, &expected); - -// // Rename the second account to the name of the first one fails -// assert!(matches!( -// rename_account_in_login_at_file( -// &wallet, -// &login_id, -// &account_id2, -// &renamed_account_id2, -// &password, -// ), -// Err(BackendError::WalletAccountIdAlreadyExistsInWalletLogin) -// )); - -// // Load and confirm nothing was changed -// let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); -// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); -// let expected = vec![ -// WalletAccount::new( -// DEFAULT_FIRST_ACCOUNT_NAME.into(), -// MnemonicAccount::new(account1, hd_path.clone()), -// ), -// WalletAccount::new(account_id2, MnemonicAccount::new(account2, hd_path)), -// ] -// .into(); -// assert_eq!(loaded_accounts, &expected); -// } - -// // Test to that decrypts a stored file from the repo, to make sure we are able to decrypt stored -// // wallets created with older versions. -// #[test] -// fn decrypt_stored_wallet() { -// const SAVED_WALLET: &str = "src/wallet_storage/test-data/saved-wallet.json"; -// let wallet_file = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(SAVED_WALLET); - -// let wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); - -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let bad_password = UserPassword::new("bad-password".to_string()); -// let id1 = LoginId::new("first".to_string()); -// let id2 = LoginId::new("second".to_string()); - -// assert!(!wallet.password_can_decrypt_all(&bad_password)); -// assert!(wallet.password_can_decrypt_all(&password)); - -// let acc1 = wallet.decrypt_login(&id1, &password).unwrap(); -// let acc2 = wallet.decrypt_login(&id2, &password).unwrap(); - -// assert!(matches!(acc1, StoredLogin::Mnemonic(_))); -// assert!(matches!(acc2, StoredLogin::Mnemonic(_))); - -// let expected_acc1 = bip39::Mnemonic::from_str("country mean universe text phone begin deputy reject result good cram illness common cluster proud swamp digital patrol spread bar face december base kick").unwrap(); -// let expected_acc2 = bip39::Mnemonic::from_str("home mansion start quiz dress decide hint second dragon sunny juice always steak real minimum art rival skin draw total pulp foot goddess agent").unwrap(); - -// assert_eq!( -// acc1.as_mnemonic_account().unwrap().mnemonic(), -// &expected_acc1 -// ); -// assert_eq!(acc1.as_mnemonic_account().unwrap().hd_path(), &hd_path,); - -// assert_eq!( -// acc2.as_mnemonic_account().unwrap().mnemonic(), -// &expected_acc2 -// ); -// assert_eq!(acc2.as_mnemonic_account().unwrap().hd_path(), &hd_path,); -// } - -// #[test] -// fn decrypt_stored_wallet_1_0_4() { -// const SAVED_WALLET: &str = "src/wallet_storage/test-data/saved-wallet-1.0.4.json"; -// let wallet_file = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(SAVED_WALLET); - -// let wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); - -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password11!".to_string()); -// let bad_password = UserPassword::new("bad-password".to_string()); -// let login_id = LoginId::new("default".to_string()); - -// assert!(!wallet.password_can_decrypt_all(&bad_password)); -// assert!(wallet.password_can_decrypt_all(&password)); - -// let acc1 = wallet.decrypt_login(&login_id, &password).unwrap(); - -// assert!(matches!(acc1, StoredLogin::Mnemonic(_))); - -// let expected_acc1 = bip39::Mnemonic::from_str("arrow capable abstract industry elevator nominee december piece hotel feed lounge web faint sword veteran bundle hour page actual laptop horror gold test warrior").unwrap(); - -// assert_eq!( -// acc1.as_mnemonic_account().unwrap().mnemonic(), -// &expected_acc1 -// ); -// assert_eq!(acc1.as_mnemonic_account().unwrap().hd_path(), &hd_path,); -// } - -// #[test] -// fn decrypt_stored_wallet_1_0_5_with_multiple_accounts() { -// const SAVED_WALLET: &str = "src/wallet_storage/test-data/saved-wallet-1.0.5.json"; -// let wallet_file = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(SAVED_WALLET); - -// let wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); - -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password11!".to_string()); -// let bad_password = UserPassword::new("bad-password".to_string()); -// let login_id = LoginId::new("default".to_string()); - -// assert!(!wallet.password_can_decrypt_all(&bad_password)); -// assert!(wallet.password_can_decrypt_all(&password)); - -// let login = wallet.decrypt_login(&login_id, &password).unwrap(); - -// assert!(matches!(login, StoredLogin::Multiple(_))); - -// let login = login.as_multiple_accounts().unwrap(); -// assert_eq!(login.len(), 4); - -// let expected_mn1 = bip39::Mnemonic::from_str("arrow capable abstract industry elevator nominee december piece hotel feed lounge web faint sword veteran bundle hour page actual laptop horror gold test warrior").unwrap(); -// let expected_mn2 = bip39::Mnemonic::from_str("border hurt skull lunar goddess second danger game dismiss exhaust oven thumb dog drama onion false orchard spice tent next predict invite cherry green").unwrap(); -// let expected_mn3 = bip39::Mnemonic::from_str("gentle crowd rule snap girl urge flat jump winner cluster night sand museum stock grunt quick tree acquire traffic major awake tag rack peasant").unwrap(); -// let expected_mn4 = bip39::Mnemonic::from_str("debris blue skin annual inhale text border rigid spatial lesson coconut yard horn crystal control survey version vote hawk neck frame arrive oblige width").unwrap(); - -// let expected = vec![ -// WalletAccount::new( -// "default".into(), -// MnemonicAccount::new(expected_mn1, hd_path.clone()), -// ), -// WalletAccount::new( -// "account2".into(), -// MnemonicAccount::new(expected_mn2, hd_path.clone()), -// ), -// WalletAccount::new( -// "foobar".into(), -// MnemonicAccount::new(expected_mn3, hd_path.clone()), -// ), -// WalletAccount::new("42".into(), MnemonicAccount::new(expected_mn4, hd_path)), -// ] -// .into(); - -// assert_eq!(login, &expected); -// } - -// #[test] -// fn append_filename() { -// let wallet_file = PathBuf::from("/tmp/saved-wallet.json"); -// let timestamp = OsString::from("42"); -// #[cfg(target_family = "unix")] -// assert_eq!( -// append_timestamp_to_filename(wallet_file.clone(), timestamp.clone(), None) -// .unwrap() -// .into_os_string() -// .into_string() -// .unwrap(), -// "/tmp/saved-wallet-42.json".to_string(), -// ); -// #[cfg(not(target_family = "unix"))] -// assert_eq!( -// append_timestamp_to_filename(wallet_file.clone(), timestamp.clone(), None) -// .unwrap() -// .into_os_string() -// .into_string() -// .unwrap(), -// r"/tmp\saved-wallet-42.json".to_string(), -// ); +#[cfg(test)] +mod tests { + use crate::wallet_storage::account_data::WalletAccount; + + use super::*; + use nym_config::defaults::COSMOS_DERIVATION_PATH; + use std::str::FromStr; + use tempfile::tempdir; + + #[test] + fn trying_to_load_nonexistant_wallet_fails() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let id1 = LoginId::new("first".to_string()); + let password = UserPassword::new("password".to_string()); + + assert!(matches!( + load_existing_wallet_at_file(&wallet_file), + Err(BackendError::WalletFileNotFound), + )); + assert!(matches!( + load_existing_login_at_file(&wallet_file, &id1, &password), + Err(BackendError::WalletFileNotFound), + )); + remove_login_at_file(&wallet_file, &id1).unwrap_err(); + } + + #[test] + fn store_single_login() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let id1 = LoginId::new("first".to_string()); + + store_login_at_file(&wallet_file, account1, hd_path, id1.clone(), &password).unwrap(); + + let stored_wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); + assert_eq!(stored_wallet.len(), 1); + + let login = stored_wallet.get_encrypted_login_by_index(0).unwrap(); + assert_eq!(login.id, id1); + + // some actual ciphertext was saved + assert!(!login.account.ciphertext().is_empty()); + } + + #[test] + fn store_single_login_with_multi() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let id1 = LoginId::new("first".to_string()); + + store_login_with_multiple_accounts_at_file( + &wallet_file, + account1, + cosmos_hd_path, + id1.clone(), + &password, + ) + .unwrap(); + + let stored_wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); + assert_eq!(stored_wallet.len(), 1); + + let login = stored_wallet.get_encrypted_login_by_index(0).unwrap(); + assert_eq!(login.id, id1); + + // some actual ciphertext was saved + assert!(!login.account.ciphertext().is_empty()); + } + + #[test] + fn store_single_login_with_multi_then_update_pwd_and_load() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let new_password = UserPassword::new("new_password".to_string()); + let id1 = LoginId::new("first".to_string()); + + store_login_with_multiple_accounts_at_file( + &wallet_file, + account1, + cosmos_hd_path, + id1.clone(), + &password, + ) + .unwrap(); + + update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); + + let stored_wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); + assert_eq!(stored_wallet.len(), 1); + + let login = stored_wallet.get_encrypted_login_by_index(0).unwrap(); + assert_eq!(login.id, id1); + + // some actual ciphertext was saved + assert!(!login.account.ciphertext().is_empty()); + } + + #[test] + fn store_twice_for_the_same_id_fails() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let id1 = LoginId::new("first".to_string()); + + // Store the first login + store_login_at_file( + &wallet_file, + account1.clone(), + hd_path.clone(), + id1.clone(), + &password, + ) + .unwrap(); + + // and storing the same id again fails + assert!(matches!( + store_login_at_file(&wallet_file, account1, hd_path, id1, &password,), + Err(BackendError::WalletLoginIdAlreadyExists), + )); + } + + #[test] + fn store_twice_for_the_same_id_fails_with_multiple() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let id1 = LoginId::new("first".to_string()); + + // Store the first login + store_login_with_multiple_accounts_at_file( + &wallet_file, + account1.clone(), + hd_path.clone(), + id1.clone(), + &password, + ) + .unwrap(); + + // and storing the same id again fails + assert!(matches!( + store_login_with_multiple_accounts_at_file( + &wallet_file, + account1, + hd_path, + id1, + &password, + ), + Err(BackendError::WalletLoginIdAlreadyExists), + )); + } + + #[test] + fn load_with_wrong_password_fails() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let bad_password = UserPassword::new("bad-password".to_string()); + let id1 = LoginId::new("first".to_string()); + + store_login_at_file(&wallet_file, account1, hd_path, id1.clone(), &password).unwrap(); + + // Trying to load it with wrong password now fails + assert!(matches!( + load_existing_login_at_file(&wallet_file, &id1, &bad_password), + Err(BackendError::StoreCipherError { .. }), + )); + } + + #[test] + fn load_with_wrong_password_fails_with_multi() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let bad_password = UserPassword::new("bad-password".to_string()); + let id1 = LoginId::new("first".to_string()); + + store_login_with_multiple_accounts_at_file( + &wallet_file, + account1, + hd_path, + id1.clone(), + &password, + ) + .unwrap(); + + // Trying to load it with wrong password now fails + assert!(matches!( + load_existing_login_at_file(&wallet_file, &id1, &bad_password), + Err(BackendError::StoreCipherError { .. }), + )); + } + + #[test] + fn load_with_wrong_id_fails() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let id1 = LoginId::new("first".to_string()); + let id2 = LoginId::new("second".to_string()); + + store_login_at_file(&wallet_file, account1, hd_path, id1, &password).unwrap(); + + // Trying to load with the wrong id + assert!(matches!( + load_existing_login_at_file(&wallet_file, &id2, &password), + Err(BackendError::WalletNoSuchLoginId), + )); + } + + #[test] + fn load_with_wrong_id_fails_with_multi() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let id1 = LoginId::new("first".to_string()); + let id2 = LoginId::new("second".to_string()); + + store_login_with_multiple_accounts_at_file(&wallet_file, account1, hd_path, id1, &password) + .unwrap(); + + // Trying to load with the wrong id + assert!(matches!( + load_existing_login_at_file(&wallet_file, &id2, &password), + Err(BackendError::WalletNoSuchLoginId), + )); + } + + #[test] + fn store_and_load_a_single_login() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let id1 = LoginId::new("first".to_string()); + + store_login_at_file( + &wallet_file, + account1.clone(), + hd_path.clone(), + id1.clone(), + &password, + ) + .unwrap(); + + let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); + let acc = loaded_login.as_mnemonic_account().unwrap(); + assert_eq!(&account1, acc.mnemonic()); + assert_eq!(&hd_path, acc.hd_path()); + } + + #[test] + fn store_a_single_login_then_update_pwd_and_load() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let new_password = UserPassword::new("new_password".to_string()); + let id1 = LoginId::new("first".to_string()); + + store_login_at_file( + &wallet_file, + account1.clone(), + hd_path.clone(), + id1.clone(), + &password, + ) + .unwrap(); + + update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); + + let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &new_password).unwrap(); + let acc = loaded_login.as_mnemonic_account().unwrap(); + assert_eq!(&account1, acc.mnemonic()); + assert_eq!(&hd_path, acc.hd_path()); + } + + #[test] + fn store_a_single_login_then_update_pwd_with_identical_pwd_is_noop_but_okay() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let id1 = LoginId::new("first".to_string()); + + store_login_at_file( + &wallet_file, + account1.clone(), + hd_path.clone(), + id1.clone(), + &password, + ) + .unwrap(); + + update_encrypted_logins_at_file(&wallet_file, &password, &password).unwrap(); + + let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); + let acc = loaded_login.as_mnemonic_account().unwrap(); + assert_eq!(&account1, acc.mnemonic()); + assert_eq!(&hd_path, acc.hd_path()); + } + + #[test] + fn store_a_single_login_then_update_pwd_with_wrong_current_pwd_fails() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let new_password = UserPassword::new("new_password".to_string()); + let wrong_password = UserPassword::new("wrong_password".to_string()); + let id1 = LoginId::new("first".to_string()); + + store_login_at_file(&wallet_file, account1, hd_path, id1, &password).unwrap(); + + let err = update_encrypted_logins_at_file(&wallet_file, &wrong_password, &new_password) + .unwrap_err(); + assert!(matches!(err, BackendError::StoreCipherError { .. })); + } + + #[test] + fn store_a_single_login_then_update_pwd_and_load_with_wrong_pwd_fails() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let new_password = UserPassword::new("new_password".to_string()); + let id1 = LoginId::new("first".to_string()); + + store_login_at_file(&wallet_file, account1, hd_path, id1.clone(), &password).unwrap(); + + update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); + + let err = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap_err(); + assert!(matches!(err, BackendError::StoreCipherError { .. })); + } + + #[test] + fn store_and_load_a_single_login_with_multi() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let acc1 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let id1 = LoginId::new("first".to_string()); + + store_login_with_multiple_accounts_at_file( + &wallet_file, + acc1.clone(), + hd_path.clone(), + id1.clone(), + &password, + ) + .unwrap(); + + let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); + let accounts = loaded_login.as_multiple_accounts().unwrap(); + assert_eq!(accounts.len(), 1); + let account = accounts + .get_account(&DEFAULT_FIRST_ACCOUNT_NAME.into()) + .unwrap(); + assert_eq!(account.id().as_ref(), DEFAULT_FIRST_ACCOUNT_NAME); + assert_eq!(account.mnemonic(), &acc1); + assert_eq!(account.hd_path(), &hd_path); + } + + #[test] + fn store_a_single_login_with_multi_then_update_pwd_and_load() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let acc1 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let new_password = UserPassword::new("new_password".to_string()); + let id1 = LoginId::new("first".to_string()); + + store_login_with_multiple_accounts_at_file( + &wallet_file, + acc1.clone(), + hd_path.clone(), + id1.clone(), + &password, + ) + .unwrap(); + + update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); + + let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &new_password).unwrap(); + let accounts = loaded_login.as_multiple_accounts().unwrap(); + assert_eq!(accounts.len(), 1); + let account = accounts + .get_account(&DEFAULT_FIRST_ACCOUNT_NAME.into()) + .unwrap(); + assert_eq!(account.id().as_ref(), DEFAULT_FIRST_ACCOUNT_NAME); + assert_eq!(account.mnemonic(), &acc1); + assert_eq!(account.hd_path(), &hd_path); + } + + #[test] + fn store_a_single_login_with_multi_then_update_pwd_with_wrong_current_pwd_fails() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let acc1 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let new_password = UserPassword::new("new_password".to_string()); + let wrong_password = UserPassword::new("wrong_password".to_string()); + let id1 = LoginId::new("first".to_string()); + + store_login_with_multiple_accounts_at_file(&wallet_file, acc1, hd_path, id1, &password) + .unwrap(); + + let err = update_encrypted_logins_at_file(&wallet_file, &wrong_password, &new_password) + .unwrap_err(); + assert!(matches!(err, BackendError::StoreCipherError { .. })); + } + + #[test] + fn store_a_single_login_with_multi_then_update_pwd_and_load_with_wrong_pwd_fails() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let acc1 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let new_password = UserPassword::new("new_password".to_string()); + let id1 = LoginId::new("first".to_string()); + + store_login_with_multiple_accounts_at_file( + &wallet_file, + acc1, + hd_path, + id1.clone(), + &password, + ) + .unwrap(); + + update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); + + let err = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap_err(); + assert!(matches!(err, BackendError::StoreCipherError { .. })); + } + + #[test] + fn store_a_second_login_with_a_different_password_fails() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let account2 = Mnemonic::generate(24).unwrap(); + let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let bad_password = UserPassword::new("bad-password".to_string()); + let id1 = LoginId::new("first".to_string()); + let id2 = LoginId::new("second".to_string()); + + store_login_at_file( + &wallet_file, + account1, + cosmos_hd_path.clone(), + id1, + &password, + ) + .unwrap(); + + // Can't store a second login if you use different password + assert!(matches!( + store_login_at_file(&wallet_file, account2, cosmos_hd_path, id2, &bad_password), + Err(BackendError::WalletDifferentPasswordDetected), + )); + } + + #[test] + fn store_a_second_login_with_a_different_password_fails_with_multi() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let account2 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let bad_password = UserPassword::new("bad-password".to_string()); + let id1 = LoginId::new("first".to_string()); + let id2 = LoginId::new("second".to_string()); + + store_login_with_multiple_accounts_at_file( + &wallet_file, + account1, + hd_path.clone(), + id1, + &password, + ) + .unwrap(); + + // Can't store a second login if you use different password + assert!(matches!( + store_login_with_multiple_accounts_at_file( + &wallet_file, + account2, + hd_path, + id2, + &bad_password + ), + Err(BackendError::WalletDifferentPasswordDetected), + )); + } + + #[test] + fn store_two_mnemonic_accounts_gives_different_salts_and_iv() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let account2 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let different_hd_path: DerivationPath = "m".parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let id1 = LoginId::new("first".to_string()); + let id2 = LoginId::new("second".to_string()); + + // Store the first account + store_login_at_file(&wallet_file, account1, hd_path, id1, &password).unwrap(); + + let stored_wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); + let encrypted_blob = &stored_wallet + .get_encrypted_login_by_index(0) + .unwrap() + .account; + + // keep track of salt and iv for future assertion + let original_iv = encrypted_blob.iv().to_vec(); + let original_salt = encrypted_blob.salt().to_vec(); + + // Add an extra account + store_login_at_file(&wallet_file, account2, different_hd_path, id2, &password).unwrap(); + + let loaded_accounts = load_existing_wallet_at_file(&wallet_file).unwrap(); + assert_eq!(loaded_accounts.len(), 2); + let encrypted_blob = &loaded_accounts + .get_encrypted_login_by_index(1) + .unwrap() + .account; + + // fresh IV and salt are used + assert_ne!(original_iv, encrypted_blob.iv()); + assert_ne!(original_salt, encrypted_blob.salt()); + } + + #[test] + fn store_two_mnemonic_accounts_using_two_logins() { + let store_dir = tempdir().unwrap(); + let wallet = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let account2 = Mnemonic::generate(24).unwrap(); + let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let different_hd_path: DerivationPath = "m".parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let id1 = LoginId::new("first".to_string()); + let id2 = LoginId::new("second".to_string()); + + // Store the first account + store_login_at_file( + &wallet, + account1.clone(), + cosmos_hd_path.clone(), + id1.clone(), + &password, + ) + .unwrap(); + + let login = load_existing_login_at_file(&wallet, &id1, &password).unwrap(); + let acc = login.as_mnemonic_account().unwrap(); + assert_eq!(&account1, acc.mnemonic()); + assert_eq!(&cosmos_hd_path, acc.hd_path()); + + // Add an extra account + store_login_at_file( + &wallet, + account2.clone(), + different_hd_path.clone(), + id2.clone(), + &password, + ) + .unwrap(); + + // first account should be unchanged + let loaded_login = load_existing_login_at_file(&wallet, &id1, &password).unwrap(); + let acc1 = loaded_login.as_mnemonic_account().unwrap(); + assert_eq!(&account1, acc1.mnemonic()); + assert_eq!(&cosmos_hd_path, acc1.hd_path()); + + let loaded_login = load_existing_login_at_file(&wallet, &id2, &password).unwrap(); + let acc2 = loaded_login.as_mnemonic_account().unwrap(); + assert_eq!(&account2, acc2.mnemonic()); + assert_eq!(&different_hd_path, acc2.hd_path()); + } + + #[test] + fn store_two_mnemonic_accounts_using_two_logins_then_update_pwd_and_load() { + let store_dir = tempdir().unwrap(); + let wallet = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let account2 = Mnemonic::generate(24).unwrap(); + let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let different_hd_path: DerivationPath = "m".parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let new_password = UserPassword::new("new_password".to_string()); + let id1 = LoginId::new("first".to_string()); + let id2 = LoginId::new("second".to_string()); + + // Store the first login with an account + store_login_at_file( + &wallet, + account1.clone(), + cosmos_hd_path.clone(), + id1.clone(), + &password, + ) + .unwrap(); + + let login = load_existing_login_at_file(&wallet, &id1, &password).unwrap(); + let acc = login.as_mnemonic_account().unwrap(); + assert_eq!(&account1, acc.mnemonic()); + assert_eq!(&cosmos_hd_path, acc.hd_path()); + + // Store a second login, also with an account + store_login_at_file( + &wallet, + account2.clone(), + different_hd_path.clone(), + id2.clone(), + &password, + ) + .unwrap(); + + update_encrypted_logins_at_file(&wallet, &password, &new_password).unwrap(); + + // first account should be unchanged + let loaded_login = load_existing_login_at_file(&wallet, &id1, &new_password).unwrap(); + let acc1 = loaded_login.as_mnemonic_account().unwrap(); + assert_eq!(&account1, acc1.mnemonic()); + assert_eq!(&cosmos_hd_path, acc1.hd_path()); + + let loaded_login = load_existing_login_at_file(&wallet, &id2, &new_password).unwrap(); + let acc2 = loaded_login.as_mnemonic_account().unwrap(); + assert_eq!(&account2, acc2.mnemonic()); + assert_eq!(&different_hd_path, acc2.hd_path()); + } + + #[test] + fn store_one_mnemonic_account_and_one_multi_account() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let account2 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let different_hd_path: DerivationPath = "m".parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let id1 = LoginId::new("first".to_string()); + let id2 = LoginId::new("second".to_string()); + + // Store the first account + store_login_at_file( + &wallet_file, + account1.clone(), + hd_path.clone(), + id1.clone(), + &password, + ) + .unwrap(); + + let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); + let acc = loaded_login.as_mnemonic_account().unwrap(); + assert_eq!(&account1, acc.mnemonic()); + assert_eq!(&hd_path, acc.hd_path()); + + // Add an extra account + store_login_with_multiple_accounts_at_file( + &wallet_file, + account2.clone(), + different_hd_path.clone(), + id2.clone(), + &password, + ) + .unwrap(); + + // first account should be unchanged + let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); + let acc1 = loaded_login.as_mnemonic_account().unwrap(); + assert_eq!(&account1, acc1.mnemonic()); + assert_eq!(&hd_path, acc1.hd_path()); + + let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); + let acc2 = loaded_login.as_multiple_accounts().unwrap(); + assert_eq!(acc2.len(), 1); + let account = acc2 + .get_account(&DEFAULT_FIRST_ACCOUNT_NAME.into()) + .unwrap(); + assert_eq!(account.id().as_ref(), DEFAULT_FIRST_ACCOUNT_NAME); + assert_eq!(account.mnemonic(), &account2); + assert_eq!(account.hd_path(), &different_hd_path); + } + + #[test] + fn remove_non_existent_id_fails() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let id1 = LoginId::new("first".to_string()); + let id2 = LoginId::new("second".to_string()); + + store_login_with_multiple_accounts_at_file(&wallet_file, account1, hd_path, id1, &password) + .unwrap(); + + // Fails to delete non-existent id in the wallet + assert!(matches!( + remove_login_at_file(&wallet_file, &id2), + Err(BackendError::WalletNoSuchLoginId), + )); + } + + #[test] + fn store_and_remove_wallet_login_information() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let account2 = Mnemonic::generate(24).unwrap(); + let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let different_hd_path: DerivationPath = "m".parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let id1 = LoginId::new("first".to_string()); + let id2 = LoginId::new("second".to_string()); + + // Store two accounts with two different passwords + store_login_at_file( + &wallet_file, + account1.clone(), + cosmos_hd_path.clone(), + id1.clone(), + &password, + ) + .unwrap(); + store_login_at_file( + &wallet_file, + account2.clone(), + different_hd_path.clone(), + id2.clone(), + &password, + ) + .unwrap(); + + // Load and compare + let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); + let acc1 = loaded_login.as_mnemonic_account().unwrap(); + assert_eq!(&account1, acc1.mnemonic()); + assert_eq!(&cosmos_hd_path, acc1.hd_path()); + + let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); + let acc2 = loaded_login.as_mnemonic_account().unwrap(); + assert_eq!(&account2, acc2.mnemonic()); + assert_eq!(&different_hd_path, acc2.hd_path()); + + // Delete the second account + remove_login_at_file(&wallet_file, &id2).unwrap(); + + // The first account should be unchanged + let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); + let acc1 = loaded_login.as_mnemonic_account().unwrap(); + assert_eq!(&account1, acc1.mnemonic()); + assert_eq!(&cosmos_hd_path, acc1.hd_path()); + + // And we can't load the second one anymore + assert!(matches!( + load_existing_login_at_file(&wallet_file, &id2, &password), + Err(BackendError::WalletNoSuchLoginId), + )); + + // Delete the first account + assert!(wallet_file.exists()); + remove_login_at_file(&wallet_file, &id1).unwrap(); + + // The file should now be removed + assert!(!wallet_file.exists()); + + // And trying to load when the file is gone fails + assert!(matches!( + load_existing_login_at_file(&wallet_file, &id1, &password), + Err(BackendError::WalletFileNotFound), + )); + } + + #[test] + fn append_account_converts_the_type() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let account2 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let id1 = LoginId::new("first".to_string()); + let id2 = AccountId::new("second".to_string()); + + store_login_at_file( + &wallet_file, + account1.clone(), + hd_path.clone(), + id1.clone(), + &password, + ) + .unwrap(); + + // Check that it's there as the correct non-multiple type + let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); + let acc = loaded_login.as_mnemonic_account().unwrap(); + assert_eq!(acc.mnemonic(), &account1); + assert_eq!(acc.hd_path(), &hd_path); + + append_account_to_login_at_file( + &wallet_file, + account2.clone(), + hd_path.clone(), + id1.clone(), + id2.clone(), + &password, + ) + .unwrap(); + + // Check that it is now multiple mnemonic type + let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); + let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); + let expected = vec![ + WalletAccount::new(id1.into(), MnemonicAccount::new(account1, hd_path.clone())), + WalletAccount::new(id2, MnemonicAccount::new(account2, hd_path)), + ] + .into(); + assert_eq!(loaded_accounts, &expected); + } + + #[test] + fn append_accounts_to_existing_login() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let account2 = Mnemonic::generate(24).unwrap(); + let account3 = Mnemonic::generate(24).unwrap(); + let account4 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let id1 = LoginId::new("first".to_string()); + let id2 = LoginId::new("second".to_string()); + let id3 = AccountId::new("third".to_string()); + let id4 = AccountId::new("fourth".to_string()); + + store_login_at_file( + &wallet_file, + account1.clone(), + hd_path.clone(), + id1.clone(), + &password, + ) + .unwrap(); + + store_login_at_file( + &wallet_file, + account2.clone(), + hd_path.clone(), + id2.clone(), + &password, + ) + .unwrap(); + + // Check that it's there as the correct non-multiple type + let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); + let acc2 = loaded_login.as_mnemonic_account().unwrap(); + assert_eq!(acc2.mnemonic(), &account2); + assert_eq!(acc2.hd_path(), &hd_path); + + // Add a third and fourth mnenonic grouped together with the second one + append_account_to_login_at_file( + &wallet_file, + account3.clone(), + hd_path.clone(), + id2.clone(), + id3.clone(), + &password, + ) + .unwrap(); + append_account_to_login_at_file( + &wallet_file, + account4.clone(), + hd_path.clone(), + id2.clone(), + id4.clone(), + &password, + ) + .unwrap(); + + // Check that we can load all four + let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); + let acc1 = loaded_login.as_mnemonic_account().unwrap(); + assert_eq!(acc1.mnemonic(), &account1); + assert_eq!(acc1.hd_path(), &hd_path); + + let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); + let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); + let expected = vec![ + WalletAccount::new(id2.into(), MnemonicAccount::new(account2, hd_path.clone())), + WalletAccount::new(id3, MnemonicAccount::new(account3, hd_path.clone())), + WalletAccount::new(id4, MnemonicAccount::new(account4, hd_path)), + ] + .into(); + assert_eq!(loaded_accounts, &expected); + } + + #[test] + fn append_accounts_to_existing_login_with_multi() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let account2 = Mnemonic::generate(24).unwrap(); + let account3 = Mnemonic::generate(24).unwrap(); + let account4 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let id1 = LoginId::new("first".to_string()); + let id2 = LoginId::new("second".to_string()); + let id3 = AccountId::new("third".to_string()); + let id4 = AccountId::new("fourth".to_string()); + + store_login_with_multiple_accounts_at_file( + &wallet_file, + account1.clone(), + hd_path.clone(), + id1.clone(), + &password, + ) + .unwrap(); + + store_login_with_multiple_accounts_at_file( + &wallet_file, + account2.clone(), + hd_path.clone(), + id2.clone(), + &password, + ) + .unwrap(); + + // Add a third and fourth mnenonic grouped together with the second one + append_account_to_login_at_file( + &wallet_file, + account3.clone(), + hd_path.clone(), + id2.clone(), + id3.clone(), + &password, + ) + .unwrap(); + append_account_to_login_at_file( + &wallet_file, + account4.clone(), + hd_path.clone(), + id2.clone(), + id4.clone(), + &password, + ) + .unwrap(); + + // Check that we can load all four + let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); + let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); + let expected = vec![WalletAccount::new( + DEFAULT_FIRST_ACCOUNT_NAME.into(), + MnemonicAccount::new(account1, hd_path.clone()), + )] + .into(); + assert_eq!(loaded_accounts, &expected); + + let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); + let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); + let expected = vec![ + WalletAccount::new( + DEFAULT_FIRST_ACCOUNT_NAME.into(), + MnemonicAccount::new(account2, hd_path.clone()), + ), + WalletAccount::new(id3, MnemonicAccount::new(account3, hd_path.clone())), + WalletAccount::new(id4, MnemonicAccount::new(account4, hd_path)), + ] + .into(); + assert_eq!(loaded_accounts, &expected); + } + + #[test] + fn append_account_to_existing_login_with_multi_then_update_pwd_and_load() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let account2 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let new_password = UserPassword::new("new_password".to_string()); + let login_id = LoginId::new("first".to_string()); + let appended_account = AccountId::new("second".to_string()); + + store_login_with_multiple_accounts_at_file( + &wallet_file, + account1.clone(), + hd_path.clone(), + login_id.clone(), + &password, + ) + .unwrap(); + + // Append a second mnenonic to the same login + append_account_to_login_at_file( + &wallet_file, + account2.clone(), + hd_path.clone(), + login_id.clone(), + appended_account.clone(), + &password, + ) + .unwrap(); + + // Update the password + update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); + + // Expect that we can load these 2 accounts with the new password + let loaded_login = + load_existing_login_at_file(&wallet_file, &login_id, &new_password).unwrap(); + let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); + let expected = vec![ + WalletAccount::new( + DEFAULT_FIRST_ACCOUNT_NAME.into(), + MnemonicAccount::new(account1, hd_path.clone()), + ), + WalletAccount::new(appended_account, MnemonicAccount::new(account2, hd_path)), + ] + .into(); + assert_eq!(loaded_accounts, &expected); + + // Expect that trying to load these 2 accounts with the old password fails + let err = load_existing_login_at_file(&wallet_file, &login_id, &password).unwrap_err(); + assert!(matches!(err, BackendError::StoreCipherError { .. })); + } + + #[test] + fn append_the_same_mnemonic_twice_fails() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let id1 = LoginId::new("first".to_string()); + let id2 = AccountId::new("second".to_string()); + + store_login_with_multiple_accounts_at_file( + &wallet_file, + account1.clone(), + hd_path.clone(), + id1.clone(), + &password, + ) + .unwrap(); + + assert!(matches!( + append_account_to_login_at_file(&wallet_file, account1, hd_path, id1, id2, &password), + Err(BackendError::WalletMnemonicAlreadyExistsInWalletLogin), + )) + } + + #[test] + fn append_the_same_account_name_twice_fails() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let mnemonic1 = Mnemonic::generate(24).unwrap(); + let mnemonic2 = Mnemonic::generate(24).unwrap(); + let mnemonic3 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + // The top-level login id. NOTE: the first account id is always set to default. + let login_id = LoginId::new("my_login_id".to_string()); + + // Store the first account under login_id. The first account id is always set to default + // name. + store_login_with_multiple_accounts_at_file( + &wallet_file, + mnemonic1, + hd_path.clone(), + login_id.clone(), + &password, + ) + .unwrap(); + + // Append another account (account2) to the same login (login_id) + let account2 = AccountId::new("account_2".to_string()); + + append_account_to_login_at_file( + &wallet_file, + mnemonic2, + hd_path.clone(), + login_id.clone(), + account2.clone(), + &password, + ) + .unwrap(); + + // Appending the third account, with same account id will fail + assert!(matches!( + append_account_to_login_at_file( + &wallet_file, + mnemonic3, + hd_path, + login_id, + account2, + &password, + ), + Err(BackendError::WalletAccountIdAlreadyExistsInWalletLogin), + )) + } + + #[test] + fn delete_the_same_account_twice_for_a_login_fails() { + let store_dir = tempdir().unwrap(); + let wallet = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let account2 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let id1 = LoginId::new("first".to_string()); + let id2 = AccountId::new("second".to_string()); + + store_login_at_file(&wallet, account1, hd_path.clone(), id1.clone(), &password).unwrap(); + + append_account_to_login_at_file( + &wallet, + account2, + hd_path, + id1.clone(), + id2.clone(), + &password, + ) + .unwrap(); + + remove_account_from_login_at_file(&wallet, &id1, &id2, &password).unwrap(); + + assert!(matches!( + remove_account_from_login_at_file(&wallet, &id1, &id2, &password), + Err(BackendError::WalletNoSuchAccountIdInWalletLogin), + )); + } + + #[test] + fn delete_the_same_account_twice_for_a_login_fails_with_multi() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let account2 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let id1 = LoginId::new("first".to_string()); + let id2 = AccountId::new("second".to_string()); + + store_login_with_multiple_accounts_at_file( + &wallet_file, + account1, + hd_path.clone(), + id1.clone(), + &password, + ) + .unwrap(); + + append_account_to_login_at_file( + &wallet_file, + account2, + hd_path, + id1.clone(), + id2.clone(), + &password, + ) + .unwrap(); + + remove_account_from_login_at_file(&wallet_file, &id1, &id2, &password).unwrap(); + + assert!(matches!( + remove_account_from_login_at_file(&wallet_file, &id1, &id2, &password), + Err(BackendError::WalletNoSuchAccountIdInWalletLogin), + )); + } + + #[test] + fn delete_appended_account_doesnt_affect_others() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let account2 = Mnemonic::generate(24).unwrap(); + let account3 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let id1 = LoginId::new("first".to_string()); + let id2 = LoginId::new("second".to_string()); + let id3 = AccountId::new("third".to_string()); + + store_login_at_file( + &wallet_file, + account1, + hd_path.clone(), + id1.clone(), + &password, + ) + .unwrap(); + + store_login_at_file( + &wallet_file, + account2.clone(), + hd_path.clone(), + id2.clone(), + &password, + ) + .unwrap(); + + append_account_to_login_at_file( + &wallet_file, + account3.clone(), + hd_path.clone(), + id2.clone(), + id3.clone(), + &password, + ) + .unwrap(); + + remove_login_at_file(&wallet_file, &id1).unwrap(); + + // The second login one is still there + let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); + let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); + let expected = vec![ + WalletAccount::new(id2.into(), MnemonicAccount::new(account2, hd_path.clone())), + WalletAccount::new(id3, MnemonicAccount::new(account3, hd_path)), + ] + .into(); + assert_eq!(loaded_accounts, &expected); + } + + #[test] + fn remove_all_accounts_for_a_login_removes_the_file_when_empty() { + let store_dir = tempdir().unwrap(); + let wallet = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let account2 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let id1 = LoginId::new("first".to_string()); + let id2 = AccountId::new("second".to_string()); + + store_login_with_multiple_accounts_at_file( + &wallet, + account1, + hd_path.clone(), + id1.clone(), + &password, + ) + .unwrap(); + + append_account_to_login_at_file( + &wallet, + account2, + hd_path, + id1.clone(), + id2.clone(), + &password, + ) + .unwrap(); + + remove_account_from_login_at_file( + &wallet, + &id1, + &DEFAULT_FIRST_ACCOUNT_NAME.into(), + &password, + ) + .unwrap(); + remove_account_from_login_at_file(&wallet, &id1, &id2, &password).unwrap(); + + // The file should now be removed + assert!(!wallet.exists()); + + // And trying to load when the file is gone fails + assert!(matches!( + load_existing_login_at_file(&wallet, &id1, &password), + Err(BackendError::WalletFileNotFound), + )); + } + + #[test] + fn remove_all_accounts_for_a_login_removes_that_login() { + let store_dir = tempdir().unwrap(); + let wallet = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let account2 = Mnemonic::generate(24).unwrap(); + let account3 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let id1 = LoginId::new("first".to_string()); + let id2 = AccountId::new("second".to_string()); + let id3 = LoginId::new("third".to_string()); + + store_login_with_multiple_accounts_at_file( + &wallet, + account1, + hd_path.clone(), + id1.clone(), + &password, + ) + .unwrap(); + + append_account_to_login_at_file( + &wallet, + account2, + hd_path.clone(), + id1.clone(), + id2.clone(), + &password, + ) + .unwrap(); + + store_login_with_multiple_accounts_at_file( + &wallet, + account3.clone(), + hd_path.clone(), + id3.clone(), + &password, + ) + .unwrap(); + + remove_account_from_login_at_file( + &wallet, + &id1, + &DEFAULT_FIRST_ACCOUNT_NAME.into(), + &password, + ) + .unwrap(); + remove_account_from_login_at_file(&wallet, &id1, &id2, &password).unwrap(); + + // And trying to load when the file is gone fails + assert!(matches!( + load_existing_login_at_file(&wallet, &id1, &password), + Err(BackendError::WalletNoSuchLoginId), + )); + + // The other login is still there + let loaded_login = load_existing_login_at_file(&wallet, &id3, &password).unwrap(); + let acc3 = loaded_login.as_multiple_accounts().unwrap(); + let expected = vec![WalletAccount::new( + DEFAULT_FIRST_ACCOUNT_NAME.into(), + MnemonicAccount::new(account3, hd_path), + )] + .into(); + assert_eq!(acc3, &expected); + } + + #[test] + fn append_accounts_and_remove_appended_accounts() { + let store_dir = tempdir().unwrap(); + let wallet = store_dir.path().join(WALLET_INFO_FILENAME); + let acc1 = Mnemonic::generate(24).unwrap(); + let acc2 = Mnemonic::generate(24).unwrap(); + let acc3 = Mnemonic::generate(24).unwrap(); + let acc4 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let id1 = LoginId::new("first".to_string()); + let id2 = LoginId::new("second".to_string()); + let id3 = AccountId::new("third".to_string()); + let id4 = AccountId::new("fourth".to_string()); + + store_login_at_file( + &wallet, + acc1.clone(), + hd_path.clone(), + id1.clone(), + &password, + ) + .unwrap(); + + store_login_at_file( + &wallet, + acc2.clone(), + hd_path.clone(), + id2.clone(), + &password, + ) + .unwrap(); + + // Add a third and fourth mnenonic grouped together with the second one + append_account_to_login_at_file( + &wallet, + acc3, + hd_path.clone(), + id2.clone(), + id3.clone(), + &password, + ) + .unwrap(); + append_account_to_login_at_file( + &wallet, + acc4.clone(), + hd_path.clone(), + id2.clone(), + id4.clone(), + &password, + ) + .unwrap(); + + // Delete the third mnemonic, from the second login entry + remove_account_from_login_at_file(&wallet, &id2, &id3, &password).unwrap(); + + // Check that we can still load the other accounts + let loaded_login = load_existing_login_at_file(&wallet, &id2, &password).unwrap(); + let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); + let expected = vec![ + WalletAccount::new( + id2.clone().into(), + MnemonicAccount::new(acc2, hd_path.clone()), + ), + WalletAccount::new(id4.clone(), MnemonicAccount::new(acc4, hd_path.clone())), + ] + .into(); + assert_eq!(loaded_accounts, &expected); + + // Delete the second and fourth mnemonic from the second login entry removes the login entry + remove_account_from_login_at_file(&wallet, &id2, &id2.clone().into(), &password).unwrap(); + remove_account_from_login_at_file(&wallet, &id2, &id4, &password).unwrap(); + assert!(matches!( + load_existing_login_at_file(&wallet, &id2, &password), + Err(BackendError::WalletNoSuchLoginId), + )); + + // The first login is still available + let loaded_login = load_existing_login_at_file(&wallet, &id1, &password).unwrap(); + let account = loaded_login.as_mnemonic_account().unwrap(); + assert_eq!(account.mnemonic(), &acc1); + assert_eq!(account.hd_path(), &hd_path); + } + + #[test] + fn rename_first_account_in_login() { + let store_dir = tempdir().unwrap(); + let wallet = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let login_id = LoginId::new("first".to_string()); + + store_login_with_multiple_accounts_at_file( + &wallet, + account1.clone(), + hd_path.clone(), + login_id.clone(), + &password, + ) + .unwrap(); + + let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); + let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); + let expected = vec![WalletAccount::new( + DEFAULT_FIRST_ACCOUNT_NAME.into(), + MnemonicAccount::new(account1.clone(), hd_path.clone()), + )] + .into(); + assert_eq!(loaded_accounts, &expected); + + let renamed_account = AccountId::new("new_first".to_string()); + + rename_account_in_login_at_file( + &wallet, + &login_id, + &DEFAULT_FIRST_ACCOUNT_NAME.into(), + &renamed_account, + &password, + ) + .unwrap(); + + let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); + let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); + let expected = vec![WalletAccount::new( + renamed_account, + MnemonicAccount::new(account1, hd_path), + )] + .into(); + assert_eq!(loaded_accounts, &expected); + } + + #[test] + fn rename_one_account_in_login_with_two_accounts() { + let store_dir = tempdir().unwrap(); + let wallet = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let account2 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let login_id = LoginId::new("first".to_string()); + let account_id2 = AccountId::new("second".to_string()); + let renamed_account_id2 = AccountId::new("new_second".to_string()); + + store_login_with_multiple_accounts_at_file( + &wallet, + account1.clone(), + hd_path.clone(), + login_id.clone(), + &password, + ) + .unwrap(); + + append_account_to_login_at_file( + &wallet, + account2.clone(), + hd_path.clone(), + login_id.clone(), + account_id2.clone(), + &password, + ) + .unwrap(); + + // Load and confirm + let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); + let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); + let expected = vec![ + WalletAccount::new( + DEFAULT_FIRST_ACCOUNT_NAME.into(), + MnemonicAccount::new(account1.clone(), hd_path.clone()), + ), + WalletAccount::new( + account_id2.clone(), + MnemonicAccount::new(account2.clone(), hd_path.clone()), + ), + ] + .into(); + assert_eq!(loaded_accounts, &expected); + + // Rename the second account to a new name + rename_account_in_login_at_file( + &wallet, + &login_id, + &account_id2, + &renamed_account_id2, + &password, + ) + .unwrap(); + + // Load and confirm + let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); + let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); + let expected = vec![ + WalletAccount::new( + DEFAULT_FIRST_ACCOUNT_NAME.into(), + MnemonicAccount::new(account1, hd_path.clone()), + ), + WalletAccount::new(renamed_account_id2, MnemonicAccount::new(account2, hd_path)), + ] + .into(); + assert_eq!(loaded_accounts, &expected); + } + + #[test] + fn rename_account_into_existing_account_id_fails() { + let store_dir = tempdir().unwrap(); + let wallet = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let account2 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let login_id = LoginId::new("first".to_string()); + let account_id2 = AccountId::new("second".to_string()); + let renamed_account_id2 = DEFAULT_FIRST_ACCOUNT_NAME.into(); + + store_login_with_multiple_accounts_at_file( + &wallet, + account1.clone(), + hd_path.clone(), + login_id.clone(), + &password, + ) + .unwrap(); + + append_account_to_login_at_file( + &wallet, + account2.clone(), + hd_path.clone(), + login_id.clone(), + account_id2.clone(), + &password, + ) + .unwrap(); + + // Load and confirm + let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); + let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); + let expected = vec![ + WalletAccount::new( + DEFAULT_FIRST_ACCOUNT_NAME.into(), + MnemonicAccount::new(account1.clone(), hd_path.clone()), + ), + WalletAccount::new( + account_id2.clone(), + MnemonicAccount::new(account2.clone(), hd_path.clone()), + ), + ] + .into(); + assert_eq!(loaded_accounts, &expected); + + // Rename the second account to the name of the first one fails + assert!(matches!( + rename_account_in_login_at_file( + &wallet, + &login_id, + &account_id2, + &renamed_account_id2, + &password, + ), + Err(BackendError::WalletAccountIdAlreadyExistsInWalletLogin) + )); + + // Load and confirm nothing was changed + let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); + let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); + let expected = vec![ + WalletAccount::new( + DEFAULT_FIRST_ACCOUNT_NAME.into(), + MnemonicAccount::new(account1, hd_path.clone()), + ), + WalletAccount::new(account_id2, MnemonicAccount::new(account2, hd_path)), + ] + .into(); + assert_eq!(loaded_accounts, &expected); + } + + // Test to that decrypts a stored file from the repo, to make sure we are able to decrypt stored + // wallets created with older versions. + #[test] + fn decrypt_stored_wallet() { + const SAVED_WALLET: &str = "src/wallet_storage/test-data/saved-wallet.json"; + let wallet_file = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(SAVED_WALLET); + + let wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); + + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let bad_password = UserPassword::new("bad-password".to_string()); + let id1 = LoginId::new("first".to_string()); + let id2 = LoginId::new("second".to_string()); + + assert!(!wallet.password_can_decrypt_all(&bad_password)); + assert!(wallet.password_can_decrypt_all(&password)); + + let acc1 = wallet.decrypt_login(&id1, &password).unwrap(); + let acc2 = wallet.decrypt_login(&id2, &password).unwrap(); + + assert!(matches!(acc1, StoredLogin::Mnemonic(_))); + assert!(matches!(acc2, StoredLogin::Mnemonic(_))); + + let expected_acc1 = bip39::Mnemonic::from_str("country mean universe text phone begin deputy reject result good cram illness common cluster proud swamp digital patrol spread bar face december base kick").unwrap(); + let expected_acc2 = bip39::Mnemonic::from_str("home mansion start quiz dress decide hint second dragon sunny juice always steak real minimum art rival skin draw total pulp foot goddess agent").unwrap(); + + assert_eq!( + acc1.as_mnemonic_account().unwrap().mnemonic(), + &expected_acc1 + ); + assert_eq!(acc1.as_mnemonic_account().unwrap().hd_path(), &hd_path,); + + assert_eq!( + acc2.as_mnemonic_account().unwrap().mnemonic(), + &expected_acc2 + ); + assert_eq!(acc2.as_mnemonic_account().unwrap().hd_path(), &hd_path,); + } + + #[test] + fn decrypt_stored_wallet_1_0_4() { + const SAVED_WALLET: &str = "src/wallet_storage/test-data/saved-wallet-1.0.4.json"; + let wallet_file = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(SAVED_WALLET); + + let wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); + + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password11!".to_string()); + let bad_password = UserPassword::new("bad-password".to_string()); + let login_id = LoginId::new("default".to_string()); + + assert!(!wallet.password_can_decrypt_all(&bad_password)); + assert!(wallet.password_can_decrypt_all(&password)); + + let acc1 = wallet.decrypt_login(&login_id, &password).unwrap(); + + assert!(matches!(acc1, StoredLogin::Mnemonic(_))); + + let expected_acc1 = bip39::Mnemonic::from_str("arrow capable abstract industry elevator nominee december piece hotel feed lounge web faint sword veteran bundle hour page actual laptop horror gold test warrior").unwrap(); + + assert_eq!( + acc1.as_mnemonic_account().unwrap().mnemonic(), + &expected_acc1 + ); + assert_eq!(acc1.as_mnemonic_account().unwrap().hd_path(), &hd_path,); + } + + #[test] + fn decrypt_stored_wallet_1_0_5_with_multiple_accounts() { + const SAVED_WALLET: &str = "src/wallet_storage/test-data/saved-wallet-1.0.5.json"; + let wallet_file = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(SAVED_WALLET); + + let wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); + + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password11!".to_string()); + let bad_password = UserPassword::new("bad-password".to_string()); + let login_id = LoginId::new("default".to_string()); + + assert!(!wallet.password_can_decrypt_all(&bad_password)); + assert!(wallet.password_can_decrypt_all(&password)); + + let login = wallet.decrypt_login(&login_id, &password).unwrap(); + + assert!(matches!(login, StoredLogin::Multiple(_))); + + let login = login.as_multiple_accounts().unwrap(); + assert_eq!(login.len(), 4); + + let expected_mn1 = bip39::Mnemonic::from_str("arrow capable abstract industry elevator nominee december piece hotel feed lounge web faint sword veteran bundle hour page actual laptop horror gold test warrior").unwrap(); + let expected_mn2 = bip39::Mnemonic::from_str("border hurt skull lunar goddess second danger game dismiss exhaust oven thumb dog drama onion false orchard spice tent next predict invite cherry green").unwrap(); + let expected_mn3 = bip39::Mnemonic::from_str("gentle crowd rule snap girl urge flat jump winner cluster night sand museum stock grunt quick tree acquire traffic major awake tag rack peasant").unwrap(); + let expected_mn4 = bip39::Mnemonic::from_str("debris blue skin annual inhale text border rigid spatial lesson coconut yard horn crystal control survey version vote hawk neck frame arrive oblige width").unwrap(); + + let expected = vec![ + WalletAccount::new( + "default".into(), + MnemonicAccount::new(expected_mn1, hd_path.clone()), + ), + WalletAccount::new( + "account2".into(), + MnemonicAccount::new(expected_mn2, hd_path.clone()), + ), + WalletAccount::new( + "foobar".into(), + MnemonicAccount::new(expected_mn3, hd_path.clone()), + ), + WalletAccount::new("42".into(), MnemonicAccount::new(expected_mn4, hd_path)), + ] + .into(); + + assert_eq!(login, &expected); + } + + #[test] + fn append_filename() { + let wallet_file = PathBuf::from("/tmp/saved-wallet.json"); + let timestamp = OsString::from("42"); + #[cfg(target_family = "unix")] + assert_eq!( + append_timestamp_to_filename(wallet_file.clone(), timestamp.clone(), None) + .unwrap() + .into_os_string() + .into_string() + .unwrap(), + "/tmp/saved-wallet-42.json".to_string(), + ); + #[cfg(not(target_family = "unix"))] + assert_eq!( + append_timestamp_to_filename(wallet_file.clone(), timestamp.clone(), None) + .unwrap() + .into_os_string() + .into_string() + .unwrap(), + r"/tmp\saved-wallet-42.json".to_string(), + ); #[cfg(target_family = "unix")] assert_eq!( From 6ca2a3c539f79c0feee410aed12ca09e3cd49a7f Mon Sep 17 00:00:00 2001 From: Tommy Verrall Date: Tue, 1 Apr 2025 17:06:21 +0200 Subject: [PATCH 07/48] migrate to v2 - lots to check and do --- nym-wallet/src-tauri/src/config/mod.rs | 272 +- .../src-tauri/src/wallet_storage/mod.rs | 3612 +++++++++-------- 2 files changed, 1949 insertions(+), 1935 deletions(-) diff --git a/nym-wallet/src-tauri/src/config/mod.rs b/nym-wallet/src-tauri/src/config/mod.rs index 1f79075157..5b7dec4099 100644 --- a/nym-wallet/src-tauri/src/config/mod.rs +++ b/nym-wallet/src-tauri/src/config/mod.rs @@ -11,6 +11,7 @@ use nym_validator_client::nyxd::AccountId as CosmosAccountId; use serde::de::DeserializeOwned; use serde::{Deserialize, Serialize}; use strum::IntoEnumIterator; +use tauri::Manager; use url::Url; use nym_config::defaults::{DenomDetailsOwned, NymNetworkDetails, ValidatorDetails}; @@ -104,32 +105,35 @@ impl NetworkConfig { } impl Config { - fn root_directory() -> PathBuf { - tauri::api::path::config_dir().expect("Failed to get config directory") + fn root_directory(app_handle: &tauri::AppHandle) -> PathBuf { + app_handle + .path() + .local_data_dir() + .expect("Failed to get config directory") } - fn config_directory() -> PathBuf { - Self::root_directory().join(CONFIG_DIR_NAME) + fn config_directory(app_handle: &tauri::AppHandle) -> PathBuf { + Self::root_directory(app_handle).join(CONFIG_DIR_NAME) } - fn config_file_path(network: Option) -> PathBuf { + fn config_file_path(app_handle: &tauri::AppHandle, network: Option) -> PathBuf { if let Some(network) = network { let network_filename = format!("{}.toml", network.as_key()); - Self::config_directory().join(network_filename) + Self::config_directory(app_handle).join(network_filename) } else { - Self::config_directory().join(CONFIG_FILENAME) + Self::config_directory(app_handle).join(CONFIG_FILENAME) } } - pub fn save_to_files(&self) -> io::Result<()> { + pub fn save_to_files(&self, app_handle: &tauri::AppHandle) -> io::Result<()> { log::trace!("Config::save_to_file"); // Make sure the whole directory structure actually exists - fs::create_dir_all(Self::config_directory())?; + fs::create_dir_all(Self::config_directory(app_handle))?; // Global config if let Some(global) = &self.global { - let location = Self::config_file_path(None); + let location = Self::config_file_path(app_handle, None); match toml::to_string_pretty(&global) .map_err(|toml_err| io::Error::new(io::ErrorKind::Other, toml_err)) @@ -150,7 +154,7 @@ impl Config { } }; - let location = Self::config_file_path(Some(network)); + let location = Self::config_file_path(app_handle, Some(network)); match toml::to_string_pretty(config) .map_err(|toml_err| io::Error::new(io::ErrorKind::Other, toml_err)) .map(|toml| fs::write(location.clone(), toml)) @@ -162,10 +166,10 @@ impl Config { Ok(()) } - pub fn load_from_files() -> Self { + pub fn load_from_files(app_handle: &tauri::AppHandle) -> Self { // Global let global = { - let file = Self::config_file_path(None); + let file = Self::config_file_path(app_handle, None); match load_from_file::(file.clone()) { Ok(global) => { log::debug!("Loaded from file {:#?}", file); @@ -181,7 +185,7 @@ impl Config { // One file per network let mut networks = HashMap::new(); for network in WalletNetwork::iter() { - let file = Self::config_file_path(Some(network)); + let file = Self::config_file_path(app_handle, Some(network)); match load_from_file::(file.clone()) { Ok(config) => { log::trace!("Loaded from file {:#?}", file); @@ -261,6 +265,8 @@ impl Config { } } + //////// + pub fn select_nyxd_url(&mut self, nyxd_url: Url, network: WalletNetwork) { if let Some(net) = self.networks.get_mut(&network.as_key()) { net.selected_nyxd_url = Some(nyxd_url); @@ -531,139 +537,139 @@ impl From for NetworkDetails { } } -#[cfg(test)] -mod tests { - use super::*; +// #[cfg(test)] +// mod tests { +// use super::*; - fn test_config() -> Config { - let netconfig = NetworkConfig { - selected_nyxd_url: None, - selected_api_url: Some("https://my_api_url.com".parse().unwrap()), +// fn test_config() -> Config { +// let netconfig = NetworkConfig { +// selected_nyxd_url: None, +// selected_api_url: Some("https://my_api_url.com".parse().unwrap()), - nyxd_urls: Some(vec![ - ValidatorConfigEntry { - nyxd_url: "https://foo".parse().unwrap(), - nyxd_name: Some("FooName".to_string()), - api_url: None, - }, - ValidatorConfigEntry { - nyxd_url: "https://bar".parse().unwrap(), - nyxd_name: None, - api_url: Some("https://bar/api".parse().unwrap()), - }, - ValidatorConfigEntry { - nyxd_url: "https://baz".parse().unwrap(), - nyxd_name: None, - api_url: Some("https://baz/api".parse().unwrap()), - }, - ]), - ..NetworkConfig::default() - }; +// nyxd_urls: Some(vec![ +// ValidatorConfigEntry { +// nyxd_url: "https://foo".parse().unwrap(), +// nyxd_name: Some("FooName".to_string()), +// api_url: None, +// }, +// ValidatorConfigEntry { +// nyxd_url: "https://bar".parse().unwrap(), +// nyxd_name: None, +// api_url: Some("https://bar/api".parse().unwrap()), +// }, +// ValidatorConfigEntry { +// nyxd_url: "https://baz".parse().unwrap(), +// nyxd_name: None, +// api_url: Some("https://baz/api".parse().unwrap()), +// }, +// ]), +// ..NetworkConfig::default() +// }; - Config { - base: Base::default(), - global: Some(GlobalConfig::default()), - networks: [(WalletNetwork::MAINNET.as_key(), netconfig)] - .into_iter() - .collect(), - } - } +// Config { +// base: Base::default(), +// global: Some(GlobalConfig::default()), +// networks: [(WalletNetwork::MAINNET.as_key(), netconfig)] +// .into_iter() +// .collect(), +// } +// } - #[test] - fn serialize_to_toml() { - let config = test_config(); - let netconfig = &config.networks[&WalletNetwork::MAINNET.as_key()]; - assert_eq!( - toml::to_string_pretty(netconfig).unwrap(), - r#"version = 1 -selected_api_url = 'https://my_api_url.com/' +// #[test] +// fn serialize_to_toml() { +// let config = test_config(); +// let netconfig = &config.networks[&WalletNetwork::MAINNET.as_key()]; +// assert_eq!( +// toml::to_string_pretty(netconfig).unwrap(), +// r#"version = 1 +// selected_api_url = 'https://my_api_url.com/' -[[nyxd_urls]] -nyxd_url = 'https://foo/' -nyxd_name = 'FooName' +// [[nyxd_urls]] +// nyxd_url = 'https://foo/' +// nyxd_name = 'FooName' -[[nyxd_urls]] -nyxd_url = 'https://bar/' -api_url = 'https://bar/api' +// [[nyxd_urls]] +// nyxd_url = 'https://bar/' +// api_url = 'https://bar/api' -[[nyxd_urls]] -nyxd_url = 'https://baz/' -api_url = 'https://baz/api' -"# - ); - } +// [[nyxd_urls]] +// nyxd_url = 'https://baz/' +// api_url = 'https://baz/api' +// "# +// ); +// } - #[test] - fn serialize_to_json() { - let config = test_config(); - let netconfig = &config.networks[&WalletNetwork::MAINNET.as_key()]; - println!("{}", serde_json::to_string_pretty(netconfig).unwrap()); - assert_eq!( - serde_json::to_string_pretty(netconfig).unwrap(), - r#"{ - "version": 1, - "selected_nyxd_url": null, - "default_nyxd_url": null, - "selected_api_url": "https://my_api_url.com/", - "nyxd_urls": [ - { - "nyxd_url": "https://foo/", - "nyxd_name": "FooName", - "api_url": null - }, - { - "nyxd_url": "https://bar/", - "nyxd_name": null, - "api_url": "https://bar/api" - }, - { - "nyxd_url": "https://baz/", - "nyxd_name": null, - "api_url": "https://baz/api" - } - ] -}"# - ); - } +// #[test] +// fn serialize_to_json() { +// let config = test_config(); +// let netconfig = &config.networks[&WalletNetwork::MAINNET.as_key()]; +// println!("{}", serde_json::to_string_pretty(netconfig).unwrap()); +// assert_eq!( +// serde_json::to_string_pretty(netconfig).unwrap(), +// r#"{ +// "version": 1, +// "selected_nyxd_url": null, +// "default_nyxd_url": null, +// "selected_api_url": "https://my_api_url.com/", +// "nyxd_urls": [ +// { +// "nyxd_url": "https://foo/", +// "nyxd_name": "FooName", +// "api_url": null +// }, +// { +// "nyxd_url": "https://bar/", +// "nyxd_name": null, +// "api_url": "https://bar/api" +// }, +// { +// "nyxd_url": "https://baz/", +// "nyxd_name": null, +// "api_url": "https://baz/api" +// } +// ] +// }"# +// ); +// } - #[test] - fn serialize_and_deserialize_to_toml() { - let config = test_config(); - let netconfig = &config.networks[&WalletNetwork::MAINNET.as_key()]; - let config_str = toml::to_string_pretty(netconfig).unwrap(); - let config_from_toml: NetworkConfig = toml::from_str(&config_str).unwrap(); - assert_eq!(netconfig, &config_from_toml); - } +// #[test] +// fn serialize_and_deserialize_to_toml() { +// let config = test_config(); +// let netconfig = &config.networks[&WalletNetwork::MAINNET.as_key()]; +// let config_str = toml::to_string_pretty(netconfig).unwrap(); +// let config_from_toml: NetworkConfig = toml::from_str(&config_str).unwrap(); +// assert_eq!(netconfig, &config_from_toml); +// } - #[test] - fn get_urls_parsed_from_config() { - let config = test_config(); +// #[test] +// fn get_urls_parsed_from_config() { +// let config = test_config(); - let nyxd_url = config - .get_configured_validators(WalletNetwork::MAINNET) - .next() - .map(|v| v.nyxd_url) - .unwrap(); - assert_eq!(nyxd_url.as_ref(), "https://foo/"); +// let nyxd_url = config +// .get_configured_validators(WalletNetwork::MAINNET) +// .next() +// .map(|v| v.nyxd_url) +// .unwrap(); +// assert_eq!(nyxd_url.as_ref(), "https://foo/"); - // The first entry is missing an API URL - let api_url = config - .get_configured_validators(WalletNetwork::MAINNET) - .next() - .and_then(|v| v.api_url); - assert_eq!(api_url, None); - } +// // The first entry is missing an API URL +// let api_url = config +// .get_configured_validators(WalletNetwork::MAINNET) +// .next() +// .and_then(|v| v.api_url); +// assert_eq!(api_url, None); +// } - #[test] - fn get_urls_from_defaults() { - let config = Config::default(); +// #[test] +// fn get_urls_from_defaults() { +// let config = Config::default(); - let nyxd_url = config - .get_base_validators(WalletNetwork::MAINNET) - .next() - .map(|v| v.nyxd_url) - .unwrap(); - assert_eq!(nyxd_url.as_ref(), "https://rpc.nymtech.net/"); +// let nyxd_url = config +// .get_base_validators(WalletNetwork::MAINNET) +// .next() +// .map(|v| v.nyxd_url) +// .unwrap(); +// assert_eq!(nyxd_url.as_ref(), "https://rpc.nymtech.net/"); let api_url = config .get_base_validators(WalletNetwork::MAINNET) diff --git a/nym-wallet/src-tauri/src/wallet_storage/mod.rs b/nym-wallet/src-tauri/src/wallet_storage/mod.rs index 115823da4a..faa026452d 100644 --- a/nym-wallet/src-tauri/src/wallet_storage/mod.rs +++ b/nym-wallet/src-tauri/src/wallet_storage/mod.rs @@ -19,6 +19,7 @@ use nym_validator_client::nyxd::bip32::DerivationPath; use std::ffi::OsString; use std::fs::{self, create_dir_all, OpenOptions}; use std::path::{Path, PathBuf}; +use tauri::Manager; use time::OffsetDateTime; #[cfg(test)] @@ -37,14 +38,18 @@ pub(crate) const DEFAULT_LOGIN_ID: &str = "default"; /// this name. pub(crate) const DEFAULT_FIRST_ACCOUNT_NAME: &str = "Account 1"; -fn get_storage_directory() -> Result { - tauri::api::path::local_data_dir() +fn get_storage_directory(app_handle: &tauri::AppHandle) -> Result { + app_handle + .path() + .local_data_dir() .map(|dir| dir.join(STORAGE_DIR_NAME)) .ok_or(BackendError::UnknownStorageDirectory) } -pub(crate) fn wallet_login_filepath() -> Result { - get_storage_directory().map(|dir| dir.join(WALLET_INFO_FILENAME)) +pub(crate) fn wallet_login_filepath( + app_handle: &tauri::AppHandle, +) -> Result { + get_storage_directory(app_handle).map(|dir| dir.join(WALLET_INFO_FILENAME)) } fn write_to_file(filepath: &Path, wallet: &StoredWallet) -> Result<(), BackendError> { @@ -59,8 +64,10 @@ fn write_to_file(filepath: &Path, wallet: &StoredWallet) -> Result<(), BackendEr /// Load stored wallet file #[allow(unused)] -pub(crate) fn load_existing_wallet() -> Result { - let store_dir = get_storage_directory()?; +pub(crate) fn load_existing_wallet( + app_handle: &tauri::AppHandle, +) -> Result { + let store_dir = get_storage_directory(app_handle)?; let filepath = store_dir.join(WALLET_INFO_FILENAME); load_existing_wallet_at_file(&filepath) } @@ -77,10 +84,11 @@ fn load_existing_wallet_at_file(filepath: &Path) -> Result Result { - let store_dir = get_storage_directory()?; + let store_dir = get_storage_directory(app_handle)?; let filepath = store_dir.join(WALLET_INFO_FILENAME); load_existing_login_at_file(&filepath, id, password) } @@ -468,1801 +476,1801 @@ fn rename_account_in_login_at_file( write_to_file(filepath, &stored_wallet) } -#[cfg(test)] -mod tests { - use crate::wallet_storage::account_data::WalletAccount; - - use super::*; - use nym_config::defaults::COSMOS_DERIVATION_PATH; - use std::str::FromStr; - use tempfile::tempdir; - - #[test] - fn trying_to_load_nonexistant_wallet_fails() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let id1 = LoginId::new("first".to_string()); - let password = UserPassword::new("password".to_string()); - - assert!(matches!( - load_existing_wallet_at_file(&wallet_file), - Err(BackendError::WalletFileNotFound), - )); - assert!(matches!( - load_existing_login_at_file(&wallet_file, &id1, &password), - Err(BackendError::WalletFileNotFound), - )); - remove_login_at_file(&wallet_file, &id1).unwrap_err(); - } - - #[test] - fn store_single_login() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - - store_login_at_file(&wallet_file, account1, hd_path, id1.clone(), &password).unwrap(); - - let stored_wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); - assert_eq!(stored_wallet.len(), 1); - - let login = stored_wallet.get_encrypted_login_by_index(0).unwrap(); - assert_eq!(login.id, id1); - - // some actual ciphertext was saved - assert!(!login.account.ciphertext().is_empty()); - } - - #[test] - fn store_single_login_with_multi() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - - store_login_with_multiple_accounts_at_file( - &wallet_file, - account1, - cosmos_hd_path, - id1.clone(), - &password, - ) - .unwrap(); - - let stored_wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); - assert_eq!(stored_wallet.len(), 1); - - let login = stored_wallet.get_encrypted_login_by_index(0).unwrap(); - assert_eq!(login.id, id1); - - // some actual ciphertext was saved - assert!(!login.account.ciphertext().is_empty()); - } - - #[test] - fn store_single_login_with_multi_then_update_pwd_and_load() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let new_password = UserPassword::new("new_password".to_string()); - let id1 = LoginId::new("first".to_string()); - - store_login_with_multiple_accounts_at_file( - &wallet_file, - account1, - cosmos_hd_path, - id1.clone(), - &password, - ) - .unwrap(); - - update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); - - let stored_wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); - assert_eq!(stored_wallet.len(), 1); - - let login = stored_wallet.get_encrypted_login_by_index(0).unwrap(); - assert_eq!(login.id, id1); - - // some actual ciphertext was saved - assert!(!login.account.ciphertext().is_empty()); - } - - #[test] - fn store_twice_for_the_same_id_fails() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - - // Store the first login - store_login_at_file( - &wallet_file, - account1.clone(), - hd_path.clone(), - id1.clone(), - &password, - ) - .unwrap(); - - // and storing the same id again fails - assert!(matches!( - store_login_at_file(&wallet_file, account1, hd_path, id1, &password,), - Err(BackendError::WalletLoginIdAlreadyExists), - )); - } - - #[test] - fn store_twice_for_the_same_id_fails_with_multiple() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - - // Store the first login - store_login_with_multiple_accounts_at_file( - &wallet_file, - account1.clone(), - hd_path.clone(), - id1.clone(), - &password, - ) - .unwrap(); - - // and storing the same id again fails - assert!(matches!( - store_login_with_multiple_accounts_at_file( - &wallet_file, - account1, - hd_path, - id1, - &password, - ), - Err(BackendError::WalletLoginIdAlreadyExists), - )); - } - - #[test] - fn load_with_wrong_password_fails() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let bad_password = UserPassword::new("bad-password".to_string()); - let id1 = LoginId::new("first".to_string()); - - store_login_at_file(&wallet_file, account1, hd_path, id1.clone(), &password).unwrap(); - - // Trying to load it with wrong password now fails - assert!(matches!( - load_existing_login_at_file(&wallet_file, &id1, &bad_password), - Err(BackendError::StoreCipherError { .. }), - )); - } - - #[test] - fn load_with_wrong_password_fails_with_multi() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let bad_password = UserPassword::new("bad-password".to_string()); - let id1 = LoginId::new("first".to_string()); - - store_login_with_multiple_accounts_at_file( - &wallet_file, - account1, - hd_path, - id1.clone(), - &password, - ) - .unwrap(); - - // Trying to load it with wrong password now fails - assert!(matches!( - load_existing_login_at_file(&wallet_file, &id1, &bad_password), - Err(BackendError::StoreCipherError { .. }), - )); - } - - #[test] - fn load_with_wrong_id_fails() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - let id2 = LoginId::new("second".to_string()); - - store_login_at_file(&wallet_file, account1, hd_path, id1, &password).unwrap(); - - // Trying to load with the wrong id - assert!(matches!( - load_existing_login_at_file(&wallet_file, &id2, &password), - Err(BackendError::WalletNoSuchLoginId), - )); - } - - #[test] - fn load_with_wrong_id_fails_with_multi() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - let id2 = LoginId::new("second".to_string()); - - store_login_with_multiple_accounts_at_file(&wallet_file, account1, hd_path, id1, &password) - .unwrap(); - - // Trying to load with the wrong id - assert!(matches!( - load_existing_login_at_file(&wallet_file, &id2, &password), - Err(BackendError::WalletNoSuchLoginId), - )); - } - - #[test] - fn store_and_load_a_single_login() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - - store_login_at_file( - &wallet_file, - account1.clone(), - hd_path.clone(), - id1.clone(), - &password, - ) - .unwrap(); - - let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); - let acc = loaded_login.as_mnemonic_account().unwrap(); - assert_eq!(&account1, acc.mnemonic()); - assert_eq!(&hd_path, acc.hd_path()); - } - - #[test] - fn store_a_single_login_then_update_pwd_and_load() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let new_password = UserPassword::new("new_password".to_string()); - let id1 = LoginId::new("first".to_string()); - - store_login_at_file( - &wallet_file, - account1.clone(), - hd_path.clone(), - id1.clone(), - &password, - ) - .unwrap(); - - update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); - - let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &new_password).unwrap(); - let acc = loaded_login.as_mnemonic_account().unwrap(); - assert_eq!(&account1, acc.mnemonic()); - assert_eq!(&hd_path, acc.hd_path()); - } - - #[test] - fn store_a_single_login_then_update_pwd_with_identical_pwd_is_noop_but_okay() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - - store_login_at_file( - &wallet_file, - account1.clone(), - hd_path.clone(), - id1.clone(), - &password, - ) - .unwrap(); - - update_encrypted_logins_at_file(&wallet_file, &password, &password).unwrap(); - - let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); - let acc = loaded_login.as_mnemonic_account().unwrap(); - assert_eq!(&account1, acc.mnemonic()); - assert_eq!(&hd_path, acc.hd_path()); - } - - #[test] - fn store_a_single_login_then_update_pwd_with_wrong_current_pwd_fails() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let new_password = UserPassword::new("new_password".to_string()); - let wrong_password = UserPassword::new("wrong_password".to_string()); - let id1 = LoginId::new("first".to_string()); - - store_login_at_file(&wallet_file, account1, hd_path, id1, &password).unwrap(); - - let err = update_encrypted_logins_at_file(&wallet_file, &wrong_password, &new_password) - .unwrap_err(); - assert!(matches!(err, BackendError::StoreCipherError { .. })); - } - - #[test] - fn store_a_single_login_then_update_pwd_and_load_with_wrong_pwd_fails() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let new_password = UserPassword::new("new_password".to_string()); - let id1 = LoginId::new("first".to_string()); - - store_login_at_file(&wallet_file, account1, hd_path, id1.clone(), &password).unwrap(); - - update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); - - let err = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap_err(); - assert!(matches!(err, BackendError::StoreCipherError { .. })); - } - - #[test] - fn store_and_load_a_single_login_with_multi() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let acc1 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - - store_login_with_multiple_accounts_at_file( - &wallet_file, - acc1.clone(), - hd_path.clone(), - id1.clone(), - &password, - ) - .unwrap(); - - let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); - let accounts = loaded_login.as_multiple_accounts().unwrap(); - assert_eq!(accounts.len(), 1); - let account = accounts - .get_account(&DEFAULT_FIRST_ACCOUNT_NAME.into()) - .unwrap(); - assert_eq!(account.id().as_ref(), DEFAULT_FIRST_ACCOUNT_NAME); - assert_eq!(account.mnemonic(), &acc1); - assert_eq!(account.hd_path(), &hd_path); - } - - #[test] - fn store_a_single_login_with_multi_then_update_pwd_and_load() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let acc1 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let new_password = UserPassword::new("new_password".to_string()); - let id1 = LoginId::new("first".to_string()); - - store_login_with_multiple_accounts_at_file( - &wallet_file, - acc1.clone(), - hd_path.clone(), - id1.clone(), - &password, - ) - .unwrap(); - - update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); - - let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &new_password).unwrap(); - let accounts = loaded_login.as_multiple_accounts().unwrap(); - assert_eq!(accounts.len(), 1); - let account = accounts - .get_account(&DEFAULT_FIRST_ACCOUNT_NAME.into()) - .unwrap(); - assert_eq!(account.id().as_ref(), DEFAULT_FIRST_ACCOUNT_NAME); - assert_eq!(account.mnemonic(), &acc1); - assert_eq!(account.hd_path(), &hd_path); - } - - #[test] - fn store_a_single_login_with_multi_then_update_pwd_with_wrong_current_pwd_fails() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let acc1 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let new_password = UserPassword::new("new_password".to_string()); - let wrong_password = UserPassword::new("wrong_password".to_string()); - let id1 = LoginId::new("first".to_string()); - - store_login_with_multiple_accounts_at_file(&wallet_file, acc1, hd_path, id1, &password) - .unwrap(); - - let err = update_encrypted_logins_at_file(&wallet_file, &wrong_password, &new_password) - .unwrap_err(); - assert!(matches!(err, BackendError::StoreCipherError { .. })); - } - - #[test] - fn store_a_single_login_with_multi_then_update_pwd_and_load_with_wrong_pwd_fails() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let acc1 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let new_password = UserPassword::new("new_password".to_string()); - let id1 = LoginId::new("first".to_string()); - - store_login_with_multiple_accounts_at_file( - &wallet_file, - acc1, - hd_path, - id1.clone(), - &password, - ) - .unwrap(); - - update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); - - let err = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap_err(); - assert!(matches!(err, BackendError::StoreCipherError { .. })); - } - - #[test] - fn store_a_second_login_with_a_different_password_fails() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let account2 = Mnemonic::generate(24).unwrap(); - let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let bad_password = UserPassword::new("bad-password".to_string()); - let id1 = LoginId::new("first".to_string()); - let id2 = LoginId::new("second".to_string()); - - store_login_at_file( - &wallet_file, - account1, - cosmos_hd_path.clone(), - id1, - &password, - ) - .unwrap(); - - // Can't store a second login if you use different password - assert!(matches!( - store_login_at_file(&wallet_file, account2, cosmos_hd_path, id2, &bad_password), - Err(BackendError::WalletDifferentPasswordDetected), - )); - } - - #[test] - fn store_a_second_login_with_a_different_password_fails_with_multi() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let account2 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let bad_password = UserPassword::new("bad-password".to_string()); - let id1 = LoginId::new("first".to_string()); - let id2 = LoginId::new("second".to_string()); - - store_login_with_multiple_accounts_at_file( - &wallet_file, - account1, - hd_path.clone(), - id1, - &password, - ) - .unwrap(); - - // Can't store a second login if you use different password - assert!(matches!( - store_login_with_multiple_accounts_at_file( - &wallet_file, - account2, - hd_path, - id2, - &bad_password - ), - Err(BackendError::WalletDifferentPasswordDetected), - )); - } - - #[test] - fn store_two_mnemonic_accounts_gives_different_salts_and_iv() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let account2 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let different_hd_path: DerivationPath = "m".parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - let id2 = LoginId::new("second".to_string()); - - // Store the first account - store_login_at_file(&wallet_file, account1, hd_path, id1, &password).unwrap(); - - let stored_wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); - let encrypted_blob = &stored_wallet - .get_encrypted_login_by_index(0) - .unwrap() - .account; - - // keep track of salt and iv for future assertion - let original_iv = encrypted_blob.iv().to_vec(); - let original_salt = encrypted_blob.salt().to_vec(); - - // Add an extra account - store_login_at_file(&wallet_file, account2, different_hd_path, id2, &password).unwrap(); - - let loaded_accounts = load_existing_wallet_at_file(&wallet_file).unwrap(); - assert_eq!(loaded_accounts.len(), 2); - let encrypted_blob = &loaded_accounts - .get_encrypted_login_by_index(1) - .unwrap() - .account; - - // fresh IV and salt are used - assert_ne!(original_iv, encrypted_blob.iv()); - assert_ne!(original_salt, encrypted_blob.salt()); - } - - #[test] - fn store_two_mnemonic_accounts_using_two_logins() { - let store_dir = tempdir().unwrap(); - let wallet = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let account2 = Mnemonic::generate(24).unwrap(); - let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let different_hd_path: DerivationPath = "m".parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - let id2 = LoginId::new("second".to_string()); - - // Store the first account - store_login_at_file( - &wallet, - account1.clone(), - cosmos_hd_path.clone(), - id1.clone(), - &password, - ) - .unwrap(); - - let login = load_existing_login_at_file(&wallet, &id1, &password).unwrap(); - let acc = login.as_mnemonic_account().unwrap(); - assert_eq!(&account1, acc.mnemonic()); - assert_eq!(&cosmos_hd_path, acc.hd_path()); - - // Add an extra account - store_login_at_file( - &wallet, - account2.clone(), - different_hd_path.clone(), - id2.clone(), - &password, - ) - .unwrap(); - - // first account should be unchanged - let loaded_login = load_existing_login_at_file(&wallet, &id1, &password).unwrap(); - let acc1 = loaded_login.as_mnemonic_account().unwrap(); - assert_eq!(&account1, acc1.mnemonic()); - assert_eq!(&cosmos_hd_path, acc1.hd_path()); - - let loaded_login = load_existing_login_at_file(&wallet, &id2, &password).unwrap(); - let acc2 = loaded_login.as_mnemonic_account().unwrap(); - assert_eq!(&account2, acc2.mnemonic()); - assert_eq!(&different_hd_path, acc2.hd_path()); - } - - #[test] - fn store_two_mnemonic_accounts_using_two_logins_then_update_pwd_and_load() { - let store_dir = tempdir().unwrap(); - let wallet = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let account2 = Mnemonic::generate(24).unwrap(); - let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let different_hd_path: DerivationPath = "m".parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let new_password = UserPassword::new("new_password".to_string()); - let id1 = LoginId::new("first".to_string()); - let id2 = LoginId::new("second".to_string()); - - // Store the first login with an account - store_login_at_file( - &wallet, - account1.clone(), - cosmos_hd_path.clone(), - id1.clone(), - &password, - ) - .unwrap(); - - let login = load_existing_login_at_file(&wallet, &id1, &password).unwrap(); - let acc = login.as_mnemonic_account().unwrap(); - assert_eq!(&account1, acc.mnemonic()); - assert_eq!(&cosmos_hd_path, acc.hd_path()); - - // Store a second login, also with an account - store_login_at_file( - &wallet, - account2.clone(), - different_hd_path.clone(), - id2.clone(), - &password, - ) - .unwrap(); - - update_encrypted_logins_at_file(&wallet, &password, &new_password).unwrap(); - - // first account should be unchanged - let loaded_login = load_existing_login_at_file(&wallet, &id1, &new_password).unwrap(); - let acc1 = loaded_login.as_mnemonic_account().unwrap(); - assert_eq!(&account1, acc1.mnemonic()); - assert_eq!(&cosmos_hd_path, acc1.hd_path()); - - let loaded_login = load_existing_login_at_file(&wallet, &id2, &new_password).unwrap(); - let acc2 = loaded_login.as_mnemonic_account().unwrap(); - assert_eq!(&account2, acc2.mnemonic()); - assert_eq!(&different_hd_path, acc2.hd_path()); - } - - #[test] - fn store_one_mnemonic_account_and_one_multi_account() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let account2 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let different_hd_path: DerivationPath = "m".parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - let id2 = LoginId::new("second".to_string()); - - // Store the first account - store_login_at_file( - &wallet_file, - account1.clone(), - hd_path.clone(), - id1.clone(), - &password, - ) - .unwrap(); - - let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); - let acc = loaded_login.as_mnemonic_account().unwrap(); - assert_eq!(&account1, acc.mnemonic()); - assert_eq!(&hd_path, acc.hd_path()); - - // Add an extra account - store_login_with_multiple_accounts_at_file( - &wallet_file, - account2.clone(), - different_hd_path.clone(), - id2.clone(), - &password, - ) - .unwrap(); - - // first account should be unchanged - let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); - let acc1 = loaded_login.as_mnemonic_account().unwrap(); - assert_eq!(&account1, acc1.mnemonic()); - assert_eq!(&hd_path, acc1.hd_path()); - - let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); - let acc2 = loaded_login.as_multiple_accounts().unwrap(); - assert_eq!(acc2.len(), 1); - let account = acc2 - .get_account(&DEFAULT_FIRST_ACCOUNT_NAME.into()) - .unwrap(); - assert_eq!(account.id().as_ref(), DEFAULT_FIRST_ACCOUNT_NAME); - assert_eq!(account.mnemonic(), &account2); - assert_eq!(account.hd_path(), &different_hd_path); - } - - #[test] - fn remove_non_existent_id_fails() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - let id2 = LoginId::new("second".to_string()); - - store_login_with_multiple_accounts_at_file(&wallet_file, account1, hd_path, id1, &password) - .unwrap(); - - // Fails to delete non-existent id in the wallet - assert!(matches!( - remove_login_at_file(&wallet_file, &id2), - Err(BackendError::WalletNoSuchLoginId), - )); - } - - #[test] - fn store_and_remove_wallet_login_information() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let account2 = Mnemonic::generate(24).unwrap(); - let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let different_hd_path: DerivationPath = "m".parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - let id2 = LoginId::new("second".to_string()); - - // Store two accounts with two different passwords - store_login_at_file( - &wallet_file, - account1.clone(), - cosmos_hd_path.clone(), - id1.clone(), - &password, - ) - .unwrap(); - store_login_at_file( - &wallet_file, - account2.clone(), - different_hd_path.clone(), - id2.clone(), - &password, - ) - .unwrap(); - - // Load and compare - let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); - let acc1 = loaded_login.as_mnemonic_account().unwrap(); - assert_eq!(&account1, acc1.mnemonic()); - assert_eq!(&cosmos_hd_path, acc1.hd_path()); - - let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); - let acc2 = loaded_login.as_mnemonic_account().unwrap(); - assert_eq!(&account2, acc2.mnemonic()); - assert_eq!(&different_hd_path, acc2.hd_path()); - - // Delete the second account - remove_login_at_file(&wallet_file, &id2).unwrap(); - - // The first account should be unchanged - let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); - let acc1 = loaded_login.as_mnemonic_account().unwrap(); - assert_eq!(&account1, acc1.mnemonic()); - assert_eq!(&cosmos_hd_path, acc1.hd_path()); - - // And we can't load the second one anymore - assert!(matches!( - load_existing_login_at_file(&wallet_file, &id2, &password), - Err(BackendError::WalletNoSuchLoginId), - )); - - // Delete the first account - assert!(wallet_file.exists()); - remove_login_at_file(&wallet_file, &id1).unwrap(); - - // The file should now be removed - assert!(!wallet_file.exists()); - - // And trying to load when the file is gone fails - assert!(matches!( - load_existing_login_at_file(&wallet_file, &id1, &password), - Err(BackendError::WalletFileNotFound), - )); - } - - #[test] - fn append_account_converts_the_type() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let account2 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - let id2 = AccountId::new("second".to_string()); - - store_login_at_file( - &wallet_file, - account1.clone(), - hd_path.clone(), - id1.clone(), - &password, - ) - .unwrap(); - - // Check that it's there as the correct non-multiple type - let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); - let acc = loaded_login.as_mnemonic_account().unwrap(); - assert_eq!(acc.mnemonic(), &account1); - assert_eq!(acc.hd_path(), &hd_path); - - append_account_to_login_at_file( - &wallet_file, - account2.clone(), - hd_path.clone(), - id1.clone(), - id2.clone(), - &password, - ) - .unwrap(); - - // Check that it is now multiple mnemonic type - let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); - let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); - let expected = vec![ - WalletAccount::new(id1.into(), MnemonicAccount::new(account1, hd_path.clone())), - WalletAccount::new(id2, MnemonicAccount::new(account2, hd_path)), - ] - .into(); - assert_eq!(loaded_accounts, &expected); - } - - #[test] - fn append_accounts_to_existing_login() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let account2 = Mnemonic::generate(24).unwrap(); - let account3 = Mnemonic::generate(24).unwrap(); - let account4 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - let id2 = LoginId::new("second".to_string()); - let id3 = AccountId::new("third".to_string()); - let id4 = AccountId::new("fourth".to_string()); - - store_login_at_file( - &wallet_file, - account1.clone(), - hd_path.clone(), - id1.clone(), - &password, - ) - .unwrap(); - - store_login_at_file( - &wallet_file, - account2.clone(), - hd_path.clone(), - id2.clone(), - &password, - ) - .unwrap(); - - // Check that it's there as the correct non-multiple type - let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); - let acc2 = loaded_login.as_mnemonic_account().unwrap(); - assert_eq!(acc2.mnemonic(), &account2); - assert_eq!(acc2.hd_path(), &hd_path); - - // Add a third and fourth mnenonic grouped together with the second one - append_account_to_login_at_file( - &wallet_file, - account3.clone(), - hd_path.clone(), - id2.clone(), - id3.clone(), - &password, - ) - .unwrap(); - append_account_to_login_at_file( - &wallet_file, - account4.clone(), - hd_path.clone(), - id2.clone(), - id4.clone(), - &password, - ) - .unwrap(); - - // Check that we can load all four - let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); - let acc1 = loaded_login.as_mnemonic_account().unwrap(); - assert_eq!(acc1.mnemonic(), &account1); - assert_eq!(acc1.hd_path(), &hd_path); - - let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); - let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); - let expected = vec![ - WalletAccount::new(id2.into(), MnemonicAccount::new(account2, hd_path.clone())), - WalletAccount::new(id3, MnemonicAccount::new(account3, hd_path.clone())), - WalletAccount::new(id4, MnemonicAccount::new(account4, hd_path)), - ] - .into(); - assert_eq!(loaded_accounts, &expected); - } - - #[test] - fn append_accounts_to_existing_login_with_multi() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let account2 = Mnemonic::generate(24).unwrap(); - let account3 = Mnemonic::generate(24).unwrap(); - let account4 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - let id2 = LoginId::new("second".to_string()); - let id3 = AccountId::new("third".to_string()); - let id4 = AccountId::new("fourth".to_string()); - - store_login_with_multiple_accounts_at_file( - &wallet_file, - account1.clone(), - hd_path.clone(), - id1.clone(), - &password, - ) - .unwrap(); - - store_login_with_multiple_accounts_at_file( - &wallet_file, - account2.clone(), - hd_path.clone(), - id2.clone(), - &password, - ) - .unwrap(); - - // Add a third and fourth mnenonic grouped together with the second one - append_account_to_login_at_file( - &wallet_file, - account3.clone(), - hd_path.clone(), - id2.clone(), - id3.clone(), - &password, - ) - .unwrap(); - append_account_to_login_at_file( - &wallet_file, - account4.clone(), - hd_path.clone(), - id2.clone(), - id4.clone(), - &password, - ) - .unwrap(); - - // Check that we can load all four - let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); - let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); - let expected = vec![WalletAccount::new( - DEFAULT_FIRST_ACCOUNT_NAME.into(), - MnemonicAccount::new(account1, hd_path.clone()), - )] - .into(); - assert_eq!(loaded_accounts, &expected); - - let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); - let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); - let expected = vec![ - WalletAccount::new( - DEFAULT_FIRST_ACCOUNT_NAME.into(), - MnemonicAccount::new(account2, hd_path.clone()), - ), - WalletAccount::new(id3, MnemonicAccount::new(account3, hd_path.clone())), - WalletAccount::new(id4, MnemonicAccount::new(account4, hd_path)), - ] - .into(); - assert_eq!(loaded_accounts, &expected); - } - - #[test] - fn append_account_to_existing_login_with_multi_then_update_pwd_and_load() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let account2 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let new_password = UserPassword::new("new_password".to_string()); - let login_id = LoginId::new("first".to_string()); - let appended_account = AccountId::new("second".to_string()); - - store_login_with_multiple_accounts_at_file( - &wallet_file, - account1.clone(), - hd_path.clone(), - login_id.clone(), - &password, - ) - .unwrap(); - - // Append a second mnenonic to the same login - append_account_to_login_at_file( - &wallet_file, - account2.clone(), - hd_path.clone(), - login_id.clone(), - appended_account.clone(), - &password, - ) - .unwrap(); - - // Update the password - update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); - - // Expect that we can load these 2 accounts with the new password - let loaded_login = - load_existing_login_at_file(&wallet_file, &login_id, &new_password).unwrap(); - let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); - let expected = vec![ - WalletAccount::new( - DEFAULT_FIRST_ACCOUNT_NAME.into(), - MnemonicAccount::new(account1, hd_path.clone()), - ), - WalletAccount::new(appended_account, MnemonicAccount::new(account2, hd_path)), - ] - .into(); - assert_eq!(loaded_accounts, &expected); - - // Expect that trying to load these 2 accounts with the old password fails - let err = load_existing_login_at_file(&wallet_file, &login_id, &password).unwrap_err(); - assert!(matches!(err, BackendError::StoreCipherError { .. })); - } - - #[test] - fn append_the_same_mnemonic_twice_fails() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - let id2 = AccountId::new("second".to_string()); - - store_login_with_multiple_accounts_at_file( - &wallet_file, - account1.clone(), - hd_path.clone(), - id1.clone(), - &password, - ) - .unwrap(); - - assert!(matches!( - append_account_to_login_at_file(&wallet_file, account1, hd_path, id1, id2, &password), - Err(BackendError::WalletMnemonicAlreadyExistsInWalletLogin), - )) - } - - #[test] - fn append_the_same_account_name_twice_fails() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let mnemonic1 = Mnemonic::generate(24).unwrap(); - let mnemonic2 = Mnemonic::generate(24).unwrap(); - let mnemonic3 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - // The top-level login id. NOTE: the first account id is always set to default. - let login_id = LoginId::new("my_login_id".to_string()); - - // Store the first account under login_id. The first account id is always set to default - // name. - store_login_with_multiple_accounts_at_file( - &wallet_file, - mnemonic1, - hd_path.clone(), - login_id.clone(), - &password, - ) - .unwrap(); - - // Append another account (account2) to the same login (login_id) - let account2 = AccountId::new("account_2".to_string()); - - append_account_to_login_at_file( - &wallet_file, - mnemonic2, - hd_path.clone(), - login_id.clone(), - account2.clone(), - &password, - ) - .unwrap(); - - // Appending the third account, with same account id will fail - assert!(matches!( - append_account_to_login_at_file( - &wallet_file, - mnemonic3, - hd_path, - login_id, - account2, - &password, - ), - Err(BackendError::WalletAccountIdAlreadyExistsInWalletLogin), - )) - } - - #[test] - fn delete_the_same_account_twice_for_a_login_fails() { - let store_dir = tempdir().unwrap(); - let wallet = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let account2 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - let id2 = AccountId::new("second".to_string()); - - store_login_at_file(&wallet, account1, hd_path.clone(), id1.clone(), &password).unwrap(); - - append_account_to_login_at_file( - &wallet, - account2, - hd_path, - id1.clone(), - id2.clone(), - &password, - ) - .unwrap(); - - remove_account_from_login_at_file(&wallet, &id1, &id2, &password).unwrap(); - - assert!(matches!( - remove_account_from_login_at_file(&wallet, &id1, &id2, &password), - Err(BackendError::WalletNoSuchAccountIdInWalletLogin), - )); - } - - #[test] - fn delete_the_same_account_twice_for_a_login_fails_with_multi() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let account2 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - let id2 = AccountId::new("second".to_string()); - - store_login_with_multiple_accounts_at_file( - &wallet_file, - account1, - hd_path.clone(), - id1.clone(), - &password, - ) - .unwrap(); - - append_account_to_login_at_file( - &wallet_file, - account2, - hd_path, - id1.clone(), - id2.clone(), - &password, - ) - .unwrap(); - - remove_account_from_login_at_file(&wallet_file, &id1, &id2, &password).unwrap(); - - assert!(matches!( - remove_account_from_login_at_file(&wallet_file, &id1, &id2, &password), - Err(BackendError::WalletNoSuchAccountIdInWalletLogin), - )); - } - - #[test] - fn delete_appended_account_doesnt_affect_others() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let account2 = Mnemonic::generate(24).unwrap(); - let account3 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - let id2 = LoginId::new("second".to_string()); - let id3 = AccountId::new("third".to_string()); - - store_login_at_file( - &wallet_file, - account1, - hd_path.clone(), - id1.clone(), - &password, - ) - .unwrap(); - - store_login_at_file( - &wallet_file, - account2.clone(), - hd_path.clone(), - id2.clone(), - &password, - ) - .unwrap(); - - append_account_to_login_at_file( - &wallet_file, - account3.clone(), - hd_path.clone(), - id2.clone(), - id3.clone(), - &password, - ) - .unwrap(); - - remove_login_at_file(&wallet_file, &id1).unwrap(); - - // The second login one is still there - let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); - let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); - let expected = vec![ - WalletAccount::new(id2.into(), MnemonicAccount::new(account2, hd_path.clone())), - WalletAccount::new(id3, MnemonicAccount::new(account3, hd_path)), - ] - .into(); - assert_eq!(loaded_accounts, &expected); - } - - #[test] - fn remove_all_accounts_for_a_login_removes_the_file_when_empty() { - let store_dir = tempdir().unwrap(); - let wallet = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let account2 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - let id2 = AccountId::new("second".to_string()); - - store_login_with_multiple_accounts_at_file( - &wallet, - account1, - hd_path.clone(), - id1.clone(), - &password, - ) - .unwrap(); - - append_account_to_login_at_file( - &wallet, - account2, - hd_path, - id1.clone(), - id2.clone(), - &password, - ) - .unwrap(); - - remove_account_from_login_at_file( - &wallet, - &id1, - &DEFAULT_FIRST_ACCOUNT_NAME.into(), - &password, - ) - .unwrap(); - remove_account_from_login_at_file(&wallet, &id1, &id2, &password).unwrap(); - - // The file should now be removed - assert!(!wallet.exists()); - - // And trying to load when the file is gone fails - assert!(matches!( - load_existing_login_at_file(&wallet, &id1, &password), - Err(BackendError::WalletFileNotFound), - )); - } - - #[test] - fn remove_all_accounts_for_a_login_removes_that_login() { - let store_dir = tempdir().unwrap(); - let wallet = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let account2 = Mnemonic::generate(24).unwrap(); - let account3 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - let id2 = AccountId::new("second".to_string()); - let id3 = LoginId::new("third".to_string()); - - store_login_with_multiple_accounts_at_file( - &wallet, - account1, - hd_path.clone(), - id1.clone(), - &password, - ) - .unwrap(); - - append_account_to_login_at_file( - &wallet, - account2, - hd_path.clone(), - id1.clone(), - id2.clone(), - &password, - ) - .unwrap(); - - store_login_with_multiple_accounts_at_file( - &wallet, - account3.clone(), - hd_path.clone(), - id3.clone(), - &password, - ) - .unwrap(); - - remove_account_from_login_at_file( - &wallet, - &id1, - &DEFAULT_FIRST_ACCOUNT_NAME.into(), - &password, - ) - .unwrap(); - remove_account_from_login_at_file(&wallet, &id1, &id2, &password).unwrap(); - - // And trying to load when the file is gone fails - assert!(matches!( - load_existing_login_at_file(&wallet, &id1, &password), - Err(BackendError::WalletNoSuchLoginId), - )); - - // The other login is still there - let loaded_login = load_existing_login_at_file(&wallet, &id3, &password).unwrap(); - let acc3 = loaded_login.as_multiple_accounts().unwrap(); - let expected = vec![WalletAccount::new( - DEFAULT_FIRST_ACCOUNT_NAME.into(), - MnemonicAccount::new(account3, hd_path), - )] - .into(); - assert_eq!(acc3, &expected); - } - - #[test] - fn append_accounts_and_remove_appended_accounts() { - let store_dir = tempdir().unwrap(); - let wallet = store_dir.path().join(WALLET_INFO_FILENAME); - let acc1 = Mnemonic::generate(24).unwrap(); - let acc2 = Mnemonic::generate(24).unwrap(); - let acc3 = Mnemonic::generate(24).unwrap(); - let acc4 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - let id2 = LoginId::new("second".to_string()); - let id3 = AccountId::new("third".to_string()); - let id4 = AccountId::new("fourth".to_string()); - - store_login_at_file( - &wallet, - acc1.clone(), - hd_path.clone(), - id1.clone(), - &password, - ) - .unwrap(); - - store_login_at_file( - &wallet, - acc2.clone(), - hd_path.clone(), - id2.clone(), - &password, - ) - .unwrap(); - - // Add a third and fourth mnenonic grouped together with the second one - append_account_to_login_at_file( - &wallet, - acc3, - hd_path.clone(), - id2.clone(), - id3.clone(), - &password, - ) - .unwrap(); - append_account_to_login_at_file( - &wallet, - acc4.clone(), - hd_path.clone(), - id2.clone(), - id4.clone(), - &password, - ) - .unwrap(); - - // Delete the third mnemonic, from the second login entry - remove_account_from_login_at_file(&wallet, &id2, &id3, &password).unwrap(); - - // Check that we can still load the other accounts - let loaded_login = load_existing_login_at_file(&wallet, &id2, &password).unwrap(); - let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); - let expected = vec![ - WalletAccount::new( - id2.clone().into(), - MnemonicAccount::new(acc2, hd_path.clone()), - ), - WalletAccount::new(id4.clone(), MnemonicAccount::new(acc4, hd_path.clone())), - ] - .into(); - assert_eq!(loaded_accounts, &expected); - - // Delete the second and fourth mnemonic from the second login entry removes the login entry - remove_account_from_login_at_file(&wallet, &id2, &id2.clone().into(), &password).unwrap(); - remove_account_from_login_at_file(&wallet, &id2, &id4, &password).unwrap(); - assert!(matches!( - load_existing_login_at_file(&wallet, &id2, &password), - Err(BackendError::WalletNoSuchLoginId), - )); - - // The first login is still available - let loaded_login = load_existing_login_at_file(&wallet, &id1, &password).unwrap(); - let account = loaded_login.as_mnemonic_account().unwrap(); - assert_eq!(account.mnemonic(), &acc1); - assert_eq!(account.hd_path(), &hd_path); - } - - #[test] - fn rename_first_account_in_login() { - let store_dir = tempdir().unwrap(); - let wallet = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let login_id = LoginId::new("first".to_string()); - - store_login_with_multiple_accounts_at_file( - &wallet, - account1.clone(), - hd_path.clone(), - login_id.clone(), - &password, - ) - .unwrap(); - - let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); - let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); - let expected = vec![WalletAccount::new( - DEFAULT_FIRST_ACCOUNT_NAME.into(), - MnemonicAccount::new(account1.clone(), hd_path.clone()), - )] - .into(); - assert_eq!(loaded_accounts, &expected); - - let renamed_account = AccountId::new("new_first".to_string()); - - rename_account_in_login_at_file( - &wallet, - &login_id, - &DEFAULT_FIRST_ACCOUNT_NAME.into(), - &renamed_account, - &password, - ) - .unwrap(); - - let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); - let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); - let expected = vec![WalletAccount::new( - renamed_account, - MnemonicAccount::new(account1, hd_path), - )] - .into(); - assert_eq!(loaded_accounts, &expected); - } - - #[test] - fn rename_one_account_in_login_with_two_accounts() { - let store_dir = tempdir().unwrap(); - let wallet = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let account2 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let login_id = LoginId::new("first".to_string()); - let account_id2 = AccountId::new("second".to_string()); - let renamed_account_id2 = AccountId::new("new_second".to_string()); - - store_login_with_multiple_accounts_at_file( - &wallet, - account1.clone(), - hd_path.clone(), - login_id.clone(), - &password, - ) - .unwrap(); - - append_account_to_login_at_file( - &wallet, - account2.clone(), - hd_path.clone(), - login_id.clone(), - account_id2.clone(), - &password, - ) - .unwrap(); - - // Load and confirm - let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); - let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); - let expected = vec![ - WalletAccount::new( - DEFAULT_FIRST_ACCOUNT_NAME.into(), - MnemonicAccount::new(account1.clone(), hd_path.clone()), - ), - WalletAccount::new( - account_id2.clone(), - MnemonicAccount::new(account2.clone(), hd_path.clone()), - ), - ] - .into(); - assert_eq!(loaded_accounts, &expected); - - // Rename the second account to a new name - rename_account_in_login_at_file( - &wallet, - &login_id, - &account_id2, - &renamed_account_id2, - &password, - ) - .unwrap(); - - // Load and confirm - let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); - let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); - let expected = vec![ - WalletAccount::new( - DEFAULT_FIRST_ACCOUNT_NAME.into(), - MnemonicAccount::new(account1, hd_path.clone()), - ), - WalletAccount::new(renamed_account_id2, MnemonicAccount::new(account2, hd_path)), - ] - .into(); - assert_eq!(loaded_accounts, &expected); - } - - #[test] - fn rename_account_into_existing_account_id_fails() { - let store_dir = tempdir().unwrap(); - let wallet = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let account2 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let login_id = LoginId::new("first".to_string()); - let account_id2 = AccountId::new("second".to_string()); - let renamed_account_id2 = DEFAULT_FIRST_ACCOUNT_NAME.into(); - - store_login_with_multiple_accounts_at_file( - &wallet, - account1.clone(), - hd_path.clone(), - login_id.clone(), - &password, - ) - .unwrap(); - - append_account_to_login_at_file( - &wallet, - account2.clone(), - hd_path.clone(), - login_id.clone(), - account_id2.clone(), - &password, - ) - .unwrap(); - - // Load and confirm - let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); - let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); - let expected = vec![ - WalletAccount::new( - DEFAULT_FIRST_ACCOUNT_NAME.into(), - MnemonicAccount::new(account1.clone(), hd_path.clone()), - ), - WalletAccount::new( - account_id2.clone(), - MnemonicAccount::new(account2.clone(), hd_path.clone()), - ), - ] - .into(); - assert_eq!(loaded_accounts, &expected); - - // Rename the second account to the name of the first one fails - assert!(matches!( - rename_account_in_login_at_file( - &wallet, - &login_id, - &account_id2, - &renamed_account_id2, - &password, - ), - Err(BackendError::WalletAccountIdAlreadyExistsInWalletLogin) - )); - - // Load and confirm nothing was changed - let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); - let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); - let expected = vec![ - WalletAccount::new( - DEFAULT_FIRST_ACCOUNT_NAME.into(), - MnemonicAccount::new(account1, hd_path.clone()), - ), - WalletAccount::new(account_id2, MnemonicAccount::new(account2, hd_path)), - ] - .into(); - assert_eq!(loaded_accounts, &expected); - } - - // Test to that decrypts a stored file from the repo, to make sure we are able to decrypt stored - // wallets created with older versions. - #[test] - fn decrypt_stored_wallet() { - const SAVED_WALLET: &str = "src/wallet_storage/test-data/saved-wallet.json"; - let wallet_file = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(SAVED_WALLET); - - let wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); - - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let bad_password = UserPassword::new("bad-password".to_string()); - let id1 = LoginId::new("first".to_string()); - let id2 = LoginId::new("second".to_string()); - - assert!(!wallet.password_can_decrypt_all(&bad_password)); - assert!(wallet.password_can_decrypt_all(&password)); - - let acc1 = wallet.decrypt_login(&id1, &password).unwrap(); - let acc2 = wallet.decrypt_login(&id2, &password).unwrap(); - - assert!(matches!(acc1, StoredLogin::Mnemonic(_))); - assert!(matches!(acc2, StoredLogin::Mnemonic(_))); - - let expected_acc1 = bip39::Mnemonic::from_str("country mean universe text phone begin deputy reject result good cram illness common cluster proud swamp digital patrol spread bar face december base kick").unwrap(); - let expected_acc2 = bip39::Mnemonic::from_str("home mansion start quiz dress decide hint second dragon sunny juice always steak real minimum art rival skin draw total pulp foot goddess agent").unwrap(); - - assert_eq!( - acc1.as_mnemonic_account().unwrap().mnemonic(), - &expected_acc1 - ); - assert_eq!(acc1.as_mnemonic_account().unwrap().hd_path(), &hd_path,); - - assert_eq!( - acc2.as_mnemonic_account().unwrap().mnemonic(), - &expected_acc2 - ); - assert_eq!(acc2.as_mnemonic_account().unwrap().hd_path(), &hd_path,); - } - - #[test] - fn decrypt_stored_wallet_1_0_4() { - const SAVED_WALLET: &str = "src/wallet_storage/test-data/saved-wallet-1.0.4.json"; - let wallet_file = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(SAVED_WALLET); - - let wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); - - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password11!".to_string()); - let bad_password = UserPassword::new("bad-password".to_string()); - let login_id = LoginId::new("default".to_string()); - - assert!(!wallet.password_can_decrypt_all(&bad_password)); - assert!(wallet.password_can_decrypt_all(&password)); - - let acc1 = wallet.decrypt_login(&login_id, &password).unwrap(); - - assert!(matches!(acc1, StoredLogin::Mnemonic(_))); - - let expected_acc1 = bip39::Mnemonic::from_str("arrow capable abstract industry elevator nominee december piece hotel feed lounge web faint sword veteran bundle hour page actual laptop horror gold test warrior").unwrap(); - - assert_eq!( - acc1.as_mnemonic_account().unwrap().mnemonic(), - &expected_acc1 - ); - assert_eq!(acc1.as_mnemonic_account().unwrap().hd_path(), &hd_path,); - } - - #[test] - fn decrypt_stored_wallet_1_0_5_with_multiple_accounts() { - const SAVED_WALLET: &str = "src/wallet_storage/test-data/saved-wallet-1.0.5.json"; - let wallet_file = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(SAVED_WALLET); - - let wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); - - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password11!".to_string()); - let bad_password = UserPassword::new("bad-password".to_string()); - let login_id = LoginId::new("default".to_string()); - - assert!(!wallet.password_can_decrypt_all(&bad_password)); - assert!(wallet.password_can_decrypt_all(&password)); - - let login = wallet.decrypt_login(&login_id, &password).unwrap(); - - assert!(matches!(login, StoredLogin::Multiple(_))); - - let login = login.as_multiple_accounts().unwrap(); - assert_eq!(login.len(), 4); - - let expected_mn1 = bip39::Mnemonic::from_str("arrow capable abstract industry elevator nominee december piece hotel feed lounge web faint sword veteran bundle hour page actual laptop horror gold test warrior").unwrap(); - let expected_mn2 = bip39::Mnemonic::from_str("border hurt skull lunar goddess second danger game dismiss exhaust oven thumb dog drama onion false orchard spice tent next predict invite cherry green").unwrap(); - let expected_mn3 = bip39::Mnemonic::from_str("gentle crowd rule snap girl urge flat jump winner cluster night sand museum stock grunt quick tree acquire traffic major awake tag rack peasant").unwrap(); - let expected_mn4 = bip39::Mnemonic::from_str("debris blue skin annual inhale text border rigid spatial lesson coconut yard horn crystal control survey version vote hawk neck frame arrive oblige width").unwrap(); - - let expected = vec![ - WalletAccount::new( - "default".into(), - MnemonicAccount::new(expected_mn1, hd_path.clone()), - ), - WalletAccount::new( - "account2".into(), - MnemonicAccount::new(expected_mn2, hd_path.clone()), - ), - WalletAccount::new( - "foobar".into(), - MnemonicAccount::new(expected_mn3, hd_path.clone()), - ), - WalletAccount::new("42".into(), MnemonicAccount::new(expected_mn4, hd_path)), - ] - .into(); - - assert_eq!(login, &expected); - } - - #[test] - fn append_filename() { - let wallet_file = PathBuf::from("/tmp/saved-wallet.json"); - let timestamp = OsString::from("42"); - #[cfg(target_family = "unix")] - assert_eq!( - append_timestamp_to_filename(wallet_file.clone(), timestamp.clone(), None) - .unwrap() - .into_os_string() - .into_string() - .unwrap(), - "/tmp/saved-wallet-42.json".to_string(), - ); - #[cfg(not(target_family = "unix"))] - assert_eq!( - append_timestamp_to_filename(wallet_file.clone(), timestamp.clone(), None) - .unwrap() - .into_os_string() - .into_string() - .unwrap(), - r"/tmp\saved-wallet-42.json".to_string(), - ); +// #[cfg(test)] +// mod tests { +// use crate::wallet_storage::account_data::WalletAccount; + +// use super::*; +// use nym_config::defaults::COSMOS_DERIVATION_PATH; +// use std::str::FromStr; +// use tempfile::tempdir; + +// #[test] +// fn trying_to_load_nonexistant_wallet_fails() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let id1 = LoginId::new("first".to_string()); +// let password = UserPassword::new("password".to_string()); + +// assert!(matches!( +// load_existing_wallet_at_file(&wallet_file), +// Err(BackendError::WalletFileNotFound), +// )); +// assert!(matches!( +// load_existing_login_at_file(&wallet_file, &id1, &password), +// Err(BackendError::WalletFileNotFound), +// )); +// remove_login_at_file(&wallet_file, &id1).unwrap_err(); +// } + +// #[test] +// fn store_single_login() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); + +// store_login_at_file(&wallet_file, account1, hd_path, id1.clone(), &password).unwrap(); + +// let stored_wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); +// assert_eq!(stored_wallet.len(), 1); + +// let login = stored_wallet.get_encrypted_login_by_index(0).unwrap(); +// assert_eq!(login.id, id1); + +// // some actual ciphertext was saved +// assert!(!login.account.ciphertext().is_empty()); +// } + +// #[test] +// fn store_single_login_with_multi() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); + +// store_login_with_multiple_accounts_at_file( +// &wallet_file, +// account1, +// cosmos_hd_path, +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// let stored_wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); +// assert_eq!(stored_wallet.len(), 1); + +// let login = stored_wallet.get_encrypted_login_by_index(0).unwrap(); +// assert_eq!(login.id, id1); + +// // some actual ciphertext was saved +// assert!(!login.account.ciphertext().is_empty()); +// } + +// #[test] +// fn store_single_login_with_multi_then_update_pwd_and_load() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let new_password = UserPassword::new("new_password".to_string()); +// let id1 = LoginId::new("first".to_string()); + +// store_login_with_multiple_accounts_at_file( +// &wallet_file, +// account1, +// cosmos_hd_path, +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); + +// let stored_wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); +// assert_eq!(stored_wallet.len(), 1); + +// let login = stored_wallet.get_encrypted_login_by_index(0).unwrap(); +// assert_eq!(login.id, id1); + +// // some actual ciphertext was saved +// assert!(!login.account.ciphertext().is_empty()); +// } + +// #[test] +// fn store_twice_for_the_same_id_fails() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); + +// // Store the first login +// store_login_at_file( +// &wallet_file, +// account1.clone(), +// hd_path.clone(), +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// // and storing the same id again fails +// assert!(matches!( +// store_login_at_file(&wallet_file, account1, hd_path, id1, &password,), +// Err(BackendError::WalletLoginIdAlreadyExists), +// )); +// } + +// #[test] +// fn store_twice_for_the_same_id_fails_with_multiple() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); + +// // Store the first login +// store_login_with_multiple_accounts_at_file( +// &wallet_file, +// account1.clone(), +// hd_path.clone(), +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// // and storing the same id again fails +// assert!(matches!( +// store_login_with_multiple_accounts_at_file( +// &wallet_file, +// account1, +// hd_path, +// id1, +// &password, +// ), +// Err(BackendError::WalletLoginIdAlreadyExists), +// )); +// } + +// #[test] +// fn load_with_wrong_password_fails() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let bad_password = UserPassword::new("bad-password".to_string()); +// let id1 = LoginId::new("first".to_string()); + +// store_login_at_file(&wallet_file, account1, hd_path, id1.clone(), &password).unwrap(); + +// // Trying to load it with wrong password now fails +// assert!(matches!( +// load_existing_login_at_file(&wallet_file, &id1, &bad_password), +// Err(BackendError::StoreCipherError { .. }), +// )); +// } + +// #[test] +// fn load_with_wrong_password_fails_with_multi() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let bad_password = UserPassword::new("bad-password".to_string()); +// let id1 = LoginId::new("first".to_string()); + +// store_login_with_multiple_accounts_at_file( +// &wallet_file, +// account1, +// hd_path, +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// // Trying to load it with wrong password now fails +// assert!(matches!( +// load_existing_login_at_file(&wallet_file, &id1, &bad_password), +// Err(BackendError::StoreCipherError { .. }), +// )); +// } + +// #[test] +// fn load_with_wrong_id_fails() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); +// let id2 = LoginId::new("second".to_string()); + +// store_login_at_file(&wallet_file, account1, hd_path, id1, &password).unwrap(); + +// // Trying to load with the wrong id +// assert!(matches!( +// load_existing_login_at_file(&wallet_file, &id2, &password), +// Err(BackendError::WalletNoSuchLoginId), +// )); +// } + +// #[test] +// fn load_with_wrong_id_fails_with_multi() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); +// let id2 = LoginId::new("second".to_string()); + +// store_login_with_multiple_accounts_at_file(&wallet_file, account1, hd_path, id1, &password) +// .unwrap(); + +// // Trying to load with the wrong id +// assert!(matches!( +// load_existing_login_at_file(&wallet_file, &id2, &password), +// Err(BackendError::WalletNoSuchLoginId), +// )); +// } + +// #[test] +// fn store_and_load_a_single_login() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); + +// store_login_at_file( +// &wallet_file, +// account1.clone(), +// hd_path.clone(), +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); +// let acc = loaded_login.as_mnemonic_account().unwrap(); +// assert_eq!(&account1, acc.mnemonic()); +// assert_eq!(&hd_path, acc.hd_path()); +// } + +// #[test] +// fn store_a_single_login_then_update_pwd_and_load() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let new_password = UserPassword::new("new_password".to_string()); +// let id1 = LoginId::new("first".to_string()); + +// store_login_at_file( +// &wallet_file, +// account1.clone(), +// hd_path.clone(), +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); + +// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &new_password).unwrap(); +// let acc = loaded_login.as_mnemonic_account().unwrap(); +// assert_eq!(&account1, acc.mnemonic()); +// assert_eq!(&hd_path, acc.hd_path()); +// } + +// #[test] +// fn store_a_single_login_then_update_pwd_with_identical_pwd_is_noop_but_okay() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); + +// store_login_at_file( +// &wallet_file, +// account1.clone(), +// hd_path.clone(), +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// update_encrypted_logins_at_file(&wallet_file, &password, &password).unwrap(); + +// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); +// let acc = loaded_login.as_mnemonic_account().unwrap(); +// assert_eq!(&account1, acc.mnemonic()); +// assert_eq!(&hd_path, acc.hd_path()); +// } + +// #[test] +// fn store_a_single_login_then_update_pwd_with_wrong_current_pwd_fails() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let new_password = UserPassword::new("new_password".to_string()); +// let wrong_password = UserPassword::new("wrong_password".to_string()); +// let id1 = LoginId::new("first".to_string()); + +// store_login_at_file(&wallet_file, account1, hd_path, id1, &password).unwrap(); + +// let err = update_encrypted_logins_at_file(&wallet_file, &wrong_password, &new_password) +// .unwrap_err(); +// assert!(matches!(err, BackendError::StoreCipherError { .. })); +// } + +// #[test] +// fn store_a_single_login_then_update_pwd_and_load_with_wrong_pwd_fails() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let new_password = UserPassword::new("new_password".to_string()); +// let id1 = LoginId::new("first".to_string()); + +// store_login_at_file(&wallet_file, account1, hd_path, id1.clone(), &password).unwrap(); + +// update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); + +// let err = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap_err(); +// assert!(matches!(err, BackendError::StoreCipherError { .. })); +// } + +// #[test] +// fn store_and_load_a_single_login_with_multi() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let acc1 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); + +// store_login_with_multiple_accounts_at_file( +// &wallet_file, +// acc1.clone(), +// hd_path.clone(), +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); +// let accounts = loaded_login.as_multiple_accounts().unwrap(); +// assert_eq!(accounts.len(), 1); +// let account = accounts +// .get_account(&DEFAULT_FIRST_ACCOUNT_NAME.into()) +// .unwrap(); +// assert_eq!(account.id().as_ref(), DEFAULT_FIRST_ACCOUNT_NAME); +// assert_eq!(account.mnemonic(), &acc1); +// assert_eq!(account.hd_path(), &hd_path); +// } + +// #[test] +// fn store_a_single_login_with_multi_then_update_pwd_and_load() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let acc1 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let new_password = UserPassword::new("new_password".to_string()); +// let id1 = LoginId::new("first".to_string()); + +// store_login_with_multiple_accounts_at_file( +// &wallet_file, +// acc1.clone(), +// hd_path.clone(), +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); + +// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &new_password).unwrap(); +// let accounts = loaded_login.as_multiple_accounts().unwrap(); +// assert_eq!(accounts.len(), 1); +// let account = accounts +// .get_account(&DEFAULT_FIRST_ACCOUNT_NAME.into()) +// .unwrap(); +// assert_eq!(account.id().as_ref(), DEFAULT_FIRST_ACCOUNT_NAME); +// assert_eq!(account.mnemonic(), &acc1); +// assert_eq!(account.hd_path(), &hd_path); +// } + +// #[test] +// fn store_a_single_login_with_multi_then_update_pwd_with_wrong_current_pwd_fails() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let acc1 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let new_password = UserPassword::new("new_password".to_string()); +// let wrong_password = UserPassword::new("wrong_password".to_string()); +// let id1 = LoginId::new("first".to_string()); + +// store_login_with_multiple_accounts_at_file(&wallet_file, acc1, hd_path, id1, &password) +// .unwrap(); + +// let err = update_encrypted_logins_at_file(&wallet_file, &wrong_password, &new_password) +// .unwrap_err(); +// assert!(matches!(err, BackendError::StoreCipherError { .. })); +// } + +// #[test] +// fn store_a_single_login_with_multi_then_update_pwd_and_load_with_wrong_pwd_fails() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let acc1 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let new_password = UserPassword::new("new_password".to_string()); +// let id1 = LoginId::new("first".to_string()); + +// store_login_with_multiple_accounts_at_file( +// &wallet_file, +// acc1, +// hd_path, +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); + +// let err = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap_err(); +// assert!(matches!(err, BackendError::StoreCipherError { .. })); +// } + +// #[test] +// fn store_a_second_login_with_a_different_password_fails() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let account2 = Mnemonic::generate(24).unwrap(); +// let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let bad_password = UserPassword::new("bad-password".to_string()); +// let id1 = LoginId::new("first".to_string()); +// let id2 = LoginId::new("second".to_string()); + +// store_login_at_file( +// &wallet_file, +// account1, +// cosmos_hd_path.clone(), +// id1, +// &password, +// ) +// .unwrap(); + +// // Can't store a second login if you use different password +// assert!(matches!( +// store_login_at_file(&wallet_file, account2, cosmos_hd_path, id2, &bad_password), +// Err(BackendError::WalletDifferentPasswordDetected), +// )); +// } + +// #[test] +// fn store_a_second_login_with_a_different_password_fails_with_multi() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let account2 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let bad_password = UserPassword::new("bad-password".to_string()); +// let id1 = LoginId::new("first".to_string()); +// let id2 = LoginId::new("second".to_string()); + +// store_login_with_multiple_accounts_at_file( +// &wallet_file, +// account1, +// hd_path.clone(), +// id1, +// &password, +// ) +// .unwrap(); + +// // Can't store a second login if you use different password +// assert!(matches!( +// store_login_with_multiple_accounts_at_file( +// &wallet_file, +// account2, +// hd_path, +// id2, +// &bad_password +// ), +// Err(BackendError::WalletDifferentPasswordDetected), +// )); +// } + +// #[test] +// fn store_two_mnemonic_accounts_gives_different_salts_and_iv() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let account2 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let different_hd_path: DerivationPath = "m".parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); +// let id2 = LoginId::new("second".to_string()); + +// // Store the first account +// store_login_at_file(&wallet_file, account1, hd_path, id1, &password).unwrap(); + +// let stored_wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); +// let encrypted_blob = &stored_wallet +// .get_encrypted_login_by_index(0) +// .unwrap() +// .account; + +// // keep track of salt and iv for future assertion +// let original_iv = encrypted_blob.iv().to_vec(); +// let original_salt = encrypted_blob.salt().to_vec(); + +// // Add an extra account +// store_login_at_file(&wallet_file, account2, different_hd_path, id2, &password).unwrap(); + +// let loaded_accounts = load_existing_wallet_at_file(&wallet_file).unwrap(); +// assert_eq!(loaded_accounts.len(), 2); +// let encrypted_blob = &loaded_accounts +// .get_encrypted_login_by_index(1) +// .unwrap() +// .account; + +// // fresh IV and salt are used +// assert_ne!(original_iv, encrypted_blob.iv()); +// assert_ne!(original_salt, encrypted_blob.salt()); +// } + +// #[test] +// fn store_two_mnemonic_accounts_using_two_logins() { +// let store_dir = tempdir().unwrap(); +// let wallet = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let account2 = Mnemonic::generate(24).unwrap(); +// let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let different_hd_path: DerivationPath = "m".parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); +// let id2 = LoginId::new("second".to_string()); + +// // Store the first account +// store_login_at_file( +// &wallet, +// account1.clone(), +// cosmos_hd_path.clone(), +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// let login = load_existing_login_at_file(&wallet, &id1, &password).unwrap(); +// let acc = login.as_mnemonic_account().unwrap(); +// assert_eq!(&account1, acc.mnemonic()); +// assert_eq!(&cosmos_hd_path, acc.hd_path()); + +// // Add an extra account +// store_login_at_file( +// &wallet, +// account2.clone(), +// different_hd_path.clone(), +// id2.clone(), +// &password, +// ) +// .unwrap(); + +// // first account should be unchanged +// let loaded_login = load_existing_login_at_file(&wallet, &id1, &password).unwrap(); +// let acc1 = loaded_login.as_mnemonic_account().unwrap(); +// assert_eq!(&account1, acc1.mnemonic()); +// assert_eq!(&cosmos_hd_path, acc1.hd_path()); + +// let loaded_login = load_existing_login_at_file(&wallet, &id2, &password).unwrap(); +// let acc2 = loaded_login.as_mnemonic_account().unwrap(); +// assert_eq!(&account2, acc2.mnemonic()); +// assert_eq!(&different_hd_path, acc2.hd_path()); +// } + +// #[test] +// fn store_two_mnemonic_accounts_using_two_logins_then_update_pwd_and_load() { +// let store_dir = tempdir().unwrap(); +// let wallet = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let account2 = Mnemonic::generate(24).unwrap(); +// let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let different_hd_path: DerivationPath = "m".parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let new_password = UserPassword::new("new_password".to_string()); +// let id1 = LoginId::new("first".to_string()); +// let id2 = LoginId::new("second".to_string()); + +// // Store the first login with an account +// store_login_at_file( +// &wallet, +// account1.clone(), +// cosmos_hd_path.clone(), +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// let login = load_existing_login_at_file(&wallet, &id1, &password).unwrap(); +// let acc = login.as_mnemonic_account().unwrap(); +// assert_eq!(&account1, acc.mnemonic()); +// assert_eq!(&cosmos_hd_path, acc.hd_path()); + +// // Store a second login, also with an account +// store_login_at_file( +// &wallet, +// account2.clone(), +// different_hd_path.clone(), +// id2.clone(), +// &password, +// ) +// .unwrap(); + +// update_encrypted_logins_at_file(&wallet, &password, &new_password).unwrap(); + +// // first account should be unchanged +// let loaded_login = load_existing_login_at_file(&wallet, &id1, &new_password).unwrap(); +// let acc1 = loaded_login.as_mnemonic_account().unwrap(); +// assert_eq!(&account1, acc1.mnemonic()); +// assert_eq!(&cosmos_hd_path, acc1.hd_path()); + +// let loaded_login = load_existing_login_at_file(&wallet, &id2, &new_password).unwrap(); +// let acc2 = loaded_login.as_mnemonic_account().unwrap(); +// assert_eq!(&account2, acc2.mnemonic()); +// assert_eq!(&different_hd_path, acc2.hd_path()); +// } + +// #[test] +// fn store_one_mnemonic_account_and_one_multi_account() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let account2 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let different_hd_path: DerivationPath = "m".parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); +// let id2 = LoginId::new("second".to_string()); + +// // Store the first account +// store_login_at_file( +// &wallet_file, +// account1.clone(), +// hd_path.clone(), +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); +// let acc = loaded_login.as_mnemonic_account().unwrap(); +// assert_eq!(&account1, acc.mnemonic()); +// assert_eq!(&hd_path, acc.hd_path()); + +// // Add an extra account +// store_login_with_multiple_accounts_at_file( +// &wallet_file, +// account2.clone(), +// different_hd_path.clone(), +// id2.clone(), +// &password, +// ) +// .unwrap(); + +// // first account should be unchanged +// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); +// let acc1 = loaded_login.as_mnemonic_account().unwrap(); +// assert_eq!(&account1, acc1.mnemonic()); +// assert_eq!(&hd_path, acc1.hd_path()); + +// let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); +// let acc2 = loaded_login.as_multiple_accounts().unwrap(); +// assert_eq!(acc2.len(), 1); +// let account = acc2 +// .get_account(&DEFAULT_FIRST_ACCOUNT_NAME.into()) +// .unwrap(); +// assert_eq!(account.id().as_ref(), DEFAULT_FIRST_ACCOUNT_NAME); +// assert_eq!(account.mnemonic(), &account2); +// assert_eq!(account.hd_path(), &different_hd_path); +// } + +// #[test] +// fn remove_non_existent_id_fails() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); +// let id2 = LoginId::new("second".to_string()); + +// store_login_with_multiple_accounts_at_file(&wallet_file, account1, hd_path, id1, &password) +// .unwrap(); + +// // Fails to delete non-existent id in the wallet +// assert!(matches!( +// remove_login_at_file(&wallet_file, &id2), +// Err(BackendError::WalletNoSuchLoginId), +// )); +// } + +// #[test] +// fn store_and_remove_wallet_login_information() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let account2 = Mnemonic::generate(24).unwrap(); +// let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let different_hd_path: DerivationPath = "m".parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); +// let id2 = LoginId::new("second".to_string()); + +// // Store two accounts with two different passwords +// store_login_at_file( +// &wallet_file, +// account1.clone(), +// cosmos_hd_path.clone(), +// id1.clone(), +// &password, +// ) +// .unwrap(); +// store_login_at_file( +// &wallet_file, +// account2.clone(), +// different_hd_path.clone(), +// id2.clone(), +// &password, +// ) +// .unwrap(); + +// // Load and compare +// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); +// let acc1 = loaded_login.as_mnemonic_account().unwrap(); +// assert_eq!(&account1, acc1.mnemonic()); +// assert_eq!(&cosmos_hd_path, acc1.hd_path()); + +// let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); +// let acc2 = loaded_login.as_mnemonic_account().unwrap(); +// assert_eq!(&account2, acc2.mnemonic()); +// assert_eq!(&different_hd_path, acc2.hd_path()); + +// // Delete the second account +// remove_login_at_file(&wallet_file, &id2).unwrap(); + +// // The first account should be unchanged +// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); +// let acc1 = loaded_login.as_mnemonic_account().unwrap(); +// assert_eq!(&account1, acc1.mnemonic()); +// assert_eq!(&cosmos_hd_path, acc1.hd_path()); + +// // And we can't load the second one anymore +// assert!(matches!( +// load_existing_login_at_file(&wallet_file, &id2, &password), +// Err(BackendError::WalletNoSuchLoginId), +// )); + +// // Delete the first account +// assert!(wallet_file.exists()); +// remove_login_at_file(&wallet_file, &id1).unwrap(); + +// // The file should now be removed +// assert!(!wallet_file.exists()); + +// // And trying to load when the file is gone fails +// assert!(matches!( +// load_existing_login_at_file(&wallet_file, &id1, &password), +// Err(BackendError::WalletFileNotFound), +// )); +// } + +// #[test] +// fn append_account_converts_the_type() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let account2 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); +// let id2 = AccountId::new("second".to_string()); + +// store_login_at_file( +// &wallet_file, +// account1.clone(), +// hd_path.clone(), +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// // Check that it's there as the correct non-multiple type +// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); +// let acc = loaded_login.as_mnemonic_account().unwrap(); +// assert_eq!(acc.mnemonic(), &account1); +// assert_eq!(acc.hd_path(), &hd_path); + +// append_account_to_login_at_file( +// &wallet_file, +// account2.clone(), +// hd_path.clone(), +// id1.clone(), +// id2.clone(), +// &password, +// ) +// .unwrap(); + +// // Check that it is now multiple mnemonic type +// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); +// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); +// let expected = vec![ +// WalletAccount::new(id1.into(), MnemonicAccount::new(account1, hd_path.clone())), +// WalletAccount::new(id2, MnemonicAccount::new(account2, hd_path)), +// ] +// .into(); +// assert_eq!(loaded_accounts, &expected); +// } + +// #[test] +// fn append_accounts_to_existing_login() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let account2 = Mnemonic::generate(24).unwrap(); +// let account3 = Mnemonic::generate(24).unwrap(); +// let account4 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); +// let id2 = LoginId::new("second".to_string()); +// let id3 = AccountId::new("third".to_string()); +// let id4 = AccountId::new("fourth".to_string()); + +// store_login_at_file( +// &wallet_file, +// account1.clone(), +// hd_path.clone(), +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// store_login_at_file( +// &wallet_file, +// account2.clone(), +// hd_path.clone(), +// id2.clone(), +// &password, +// ) +// .unwrap(); + +// // Check that it's there as the correct non-multiple type +// let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); +// let acc2 = loaded_login.as_mnemonic_account().unwrap(); +// assert_eq!(acc2.mnemonic(), &account2); +// assert_eq!(acc2.hd_path(), &hd_path); + +// // Add a third and fourth mnenonic grouped together with the second one +// append_account_to_login_at_file( +// &wallet_file, +// account3.clone(), +// hd_path.clone(), +// id2.clone(), +// id3.clone(), +// &password, +// ) +// .unwrap(); +// append_account_to_login_at_file( +// &wallet_file, +// account4.clone(), +// hd_path.clone(), +// id2.clone(), +// id4.clone(), +// &password, +// ) +// .unwrap(); + +// // Check that we can load all four +// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); +// let acc1 = loaded_login.as_mnemonic_account().unwrap(); +// assert_eq!(acc1.mnemonic(), &account1); +// assert_eq!(acc1.hd_path(), &hd_path); + +// let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); +// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); +// let expected = vec![ +// WalletAccount::new(id2.into(), MnemonicAccount::new(account2, hd_path.clone())), +// WalletAccount::new(id3, MnemonicAccount::new(account3, hd_path.clone())), +// WalletAccount::new(id4, MnemonicAccount::new(account4, hd_path)), +// ] +// .into(); +// assert_eq!(loaded_accounts, &expected); +// } + +// #[test] +// fn append_accounts_to_existing_login_with_multi() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let account2 = Mnemonic::generate(24).unwrap(); +// let account3 = Mnemonic::generate(24).unwrap(); +// let account4 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); +// let id2 = LoginId::new("second".to_string()); +// let id3 = AccountId::new("third".to_string()); +// let id4 = AccountId::new("fourth".to_string()); + +// store_login_with_multiple_accounts_at_file( +// &wallet_file, +// account1.clone(), +// hd_path.clone(), +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// store_login_with_multiple_accounts_at_file( +// &wallet_file, +// account2.clone(), +// hd_path.clone(), +// id2.clone(), +// &password, +// ) +// .unwrap(); + +// // Add a third and fourth mnenonic grouped together with the second one +// append_account_to_login_at_file( +// &wallet_file, +// account3.clone(), +// hd_path.clone(), +// id2.clone(), +// id3.clone(), +// &password, +// ) +// .unwrap(); +// append_account_to_login_at_file( +// &wallet_file, +// account4.clone(), +// hd_path.clone(), +// id2.clone(), +// id4.clone(), +// &password, +// ) +// .unwrap(); + +// // Check that we can load all four +// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); +// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); +// let expected = vec![WalletAccount::new( +// DEFAULT_FIRST_ACCOUNT_NAME.into(), +// MnemonicAccount::new(account1, hd_path.clone()), +// )] +// .into(); +// assert_eq!(loaded_accounts, &expected); + +// let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); +// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); +// let expected = vec![ +// WalletAccount::new( +// DEFAULT_FIRST_ACCOUNT_NAME.into(), +// MnemonicAccount::new(account2, hd_path.clone()), +// ), +// WalletAccount::new(id3, MnemonicAccount::new(account3, hd_path.clone())), +// WalletAccount::new(id4, MnemonicAccount::new(account4, hd_path)), +// ] +// .into(); +// assert_eq!(loaded_accounts, &expected); +// } + +// #[test] +// fn append_account_to_existing_login_with_multi_then_update_pwd_and_load() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let account2 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let new_password = UserPassword::new("new_password".to_string()); +// let login_id = LoginId::new("first".to_string()); +// let appended_account = AccountId::new("second".to_string()); + +// store_login_with_multiple_accounts_at_file( +// &wallet_file, +// account1.clone(), +// hd_path.clone(), +// login_id.clone(), +// &password, +// ) +// .unwrap(); + +// // Append a second mnenonic to the same login +// append_account_to_login_at_file( +// &wallet_file, +// account2.clone(), +// hd_path.clone(), +// login_id.clone(), +// appended_account.clone(), +// &password, +// ) +// .unwrap(); + +// // Update the password +// update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); + +// // Expect that we can load these 2 accounts with the new password +// let loaded_login = +// load_existing_login_at_file(&wallet_file, &login_id, &new_password).unwrap(); +// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); +// let expected = vec![ +// WalletAccount::new( +// DEFAULT_FIRST_ACCOUNT_NAME.into(), +// MnemonicAccount::new(account1, hd_path.clone()), +// ), +// WalletAccount::new(appended_account, MnemonicAccount::new(account2, hd_path)), +// ] +// .into(); +// assert_eq!(loaded_accounts, &expected); + +// // Expect that trying to load these 2 accounts with the old password fails +// let err = load_existing_login_at_file(&wallet_file, &login_id, &password).unwrap_err(); +// assert!(matches!(err, BackendError::StoreCipherError { .. })); +// } + +// #[test] +// fn append_the_same_mnemonic_twice_fails() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); +// let id2 = AccountId::new("second".to_string()); + +// store_login_with_multiple_accounts_at_file( +// &wallet_file, +// account1.clone(), +// hd_path.clone(), +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// assert!(matches!( +// append_account_to_login_at_file(&wallet_file, account1, hd_path, id1, id2, &password), +// Err(BackendError::WalletMnemonicAlreadyExistsInWalletLogin), +// )) +// } + +// #[test] +// fn append_the_same_account_name_twice_fails() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let mnemonic1 = Mnemonic::generate(24).unwrap(); +// let mnemonic2 = Mnemonic::generate(24).unwrap(); +// let mnemonic3 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// // The top-level login id. NOTE: the first account id is always set to default. +// let login_id = LoginId::new("my_login_id".to_string()); + +// // Store the first account under login_id. The first account id is always set to default +// // name. +// store_login_with_multiple_accounts_at_file( +// &wallet_file, +// mnemonic1, +// hd_path.clone(), +// login_id.clone(), +// &password, +// ) +// .unwrap(); + +// // Append another account (account2) to the same login (login_id) +// let account2 = AccountId::new("account_2".to_string()); + +// append_account_to_login_at_file( +// &wallet_file, +// mnemonic2, +// hd_path.clone(), +// login_id.clone(), +// account2.clone(), +// &password, +// ) +// .unwrap(); + +// // Appending the third account, with same account id will fail +// assert!(matches!( +// append_account_to_login_at_file( +// &wallet_file, +// mnemonic3, +// hd_path, +// login_id, +// account2, +// &password, +// ), +// Err(BackendError::WalletAccountIdAlreadyExistsInWalletLogin), +// )) +// } + +// #[test] +// fn delete_the_same_account_twice_for_a_login_fails() { +// let store_dir = tempdir().unwrap(); +// let wallet = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let account2 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); +// let id2 = AccountId::new("second".to_string()); + +// store_login_at_file(&wallet, account1, hd_path.clone(), id1.clone(), &password).unwrap(); + +// append_account_to_login_at_file( +// &wallet, +// account2, +// hd_path, +// id1.clone(), +// id2.clone(), +// &password, +// ) +// .unwrap(); + +// remove_account_from_login_at_file(&wallet, &id1, &id2, &password).unwrap(); + +// assert!(matches!( +// remove_account_from_login_at_file(&wallet, &id1, &id2, &password), +// Err(BackendError::WalletNoSuchAccountIdInWalletLogin), +// )); +// } + +// #[test] +// fn delete_the_same_account_twice_for_a_login_fails_with_multi() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let account2 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); +// let id2 = AccountId::new("second".to_string()); + +// store_login_with_multiple_accounts_at_file( +// &wallet_file, +// account1, +// hd_path.clone(), +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// append_account_to_login_at_file( +// &wallet_file, +// account2, +// hd_path, +// id1.clone(), +// id2.clone(), +// &password, +// ) +// .unwrap(); + +// remove_account_from_login_at_file(&wallet_file, &id1, &id2, &password).unwrap(); + +// assert!(matches!( +// remove_account_from_login_at_file(&wallet_file, &id1, &id2, &password), +// Err(BackendError::WalletNoSuchAccountIdInWalletLogin), +// )); +// } + +// #[test] +// fn delete_appended_account_doesnt_affect_others() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let account2 = Mnemonic::generate(24).unwrap(); +// let account3 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); +// let id2 = LoginId::new("second".to_string()); +// let id3 = AccountId::new("third".to_string()); + +// store_login_at_file( +// &wallet_file, +// account1, +// hd_path.clone(), +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// store_login_at_file( +// &wallet_file, +// account2.clone(), +// hd_path.clone(), +// id2.clone(), +// &password, +// ) +// .unwrap(); + +// append_account_to_login_at_file( +// &wallet_file, +// account3.clone(), +// hd_path.clone(), +// id2.clone(), +// id3.clone(), +// &password, +// ) +// .unwrap(); + +// remove_login_at_file(&wallet_file, &id1).unwrap(); + +// // The second login one is still there +// let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); +// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); +// let expected = vec![ +// WalletAccount::new(id2.into(), MnemonicAccount::new(account2, hd_path.clone())), +// WalletAccount::new(id3, MnemonicAccount::new(account3, hd_path)), +// ] +// .into(); +// assert_eq!(loaded_accounts, &expected); +// } + +// #[test] +// fn remove_all_accounts_for_a_login_removes_the_file_when_empty() { +// let store_dir = tempdir().unwrap(); +// let wallet = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let account2 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); +// let id2 = AccountId::new("second".to_string()); + +// store_login_with_multiple_accounts_at_file( +// &wallet, +// account1, +// hd_path.clone(), +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// append_account_to_login_at_file( +// &wallet, +// account2, +// hd_path, +// id1.clone(), +// id2.clone(), +// &password, +// ) +// .unwrap(); + +// remove_account_from_login_at_file( +// &wallet, +// &id1, +// &DEFAULT_FIRST_ACCOUNT_NAME.into(), +// &password, +// ) +// .unwrap(); +// remove_account_from_login_at_file(&wallet, &id1, &id2, &password).unwrap(); + +// // The file should now be removed +// assert!(!wallet.exists()); + +// // And trying to load when the file is gone fails +// assert!(matches!( +// load_existing_login_at_file(&wallet, &id1, &password), +// Err(BackendError::WalletFileNotFound), +// )); +// } + +// #[test] +// fn remove_all_accounts_for_a_login_removes_that_login() { +// let store_dir = tempdir().unwrap(); +// let wallet = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let account2 = Mnemonic::generate(24).unwrap(); +// let account3 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); +// let id2 = AccountId::new("second".to_string()); +// let id3 = LoginId::new("third".to_string()); + +// store_login_with_multiple_accounts_at_file( +// &wallet, +// account1, +// hd_path.clone(), +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// append_account_to_login_at_file( +// &wallet, +// account2, +// hd_path.clone(), +// id1.clone(), +// id2.clone(), +// &password, +// ) +// .unwrap(); + +// store_login_with_multiple_accounts_at_file( +// &wallet, +// account3.clone(), +// hd_path.clone(), +// id3.clone(), +// &password, +// ) +// .unwrap(); + +// remove_account_from_login_at_file( +// &wallet, +// &id1, +// &DEFAULT_FIRST_ACCOUNT_NAME.into(), +// &password, +// ) +// .unwrap(); +// remove_account_from_login_at_file(&wallet, &id1, &id2, &password).unwrap(); + +// // And trying to load when the file is gone fails +// assert!(matches!( +// load_existing_login_at_file(&wallet, &id1, &password), +// Err(BackendError::WalletNoSuchLoginId), +// )); + +// // The other login is still there +// let loaded_login = load_existing_login_at_file(&wallet, &id3, &password).unwrap(); +// let acc3 = loaded_login.as_multiple_accounts().unwrap(); +// let expected = vec![WalletAccount::new( +// DEFAULT_FIRST_ACCOUNT_NAME.into(), +// MnemonicAccount::new(account3, hd_path), +// )] +// .into(); +// assert_eq!(acc3, &expected); +// } + +// #[test] +// fn append_accounts_and_remove_appended_accounts() { +// let store_dir = tempdir().unwrap(); +// let wallet = store_dir.path().join(WALLET_INFO_FILENAME); +// let acc1 = Mnemonic::generate(24).unwrap(); +// let acc2 = Mnemonic::generate(24).unwrap(); +// let acc3 = Mnemonic::generate(24).unwrap(); +// let acc4 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); +// let id2 = LoginId::new("second".to_string()); +// let id3 = AccountId::new("third".to_string()); +// let id4 = AccountId::new("fourth".to_string()); + +// store_login_at_file( +// &wallet, +// acc1.clone(), +// hd_path.clone(), +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// store_login_at_file( +// &wallet, +// acc2.clone(), +// hd_path.clone(), +// id2.clone(), +// &password, +// ) +// .unwrap(); + +// // Add a third and fourth mnenonic grouped together with the second one +// append_account_to_login_at_file( +// &wallet, +// acc3, +// hd_path.clone(), +// id2.clone(), +// id3.clone(), +// &password, +// ) +// .unwrap(); +// append_account_to_login_at_file( +// &wallet, +// acc4.clone(), +// hd_path.clone(), +// id2.clone(), +// id4.clone(), +// &password, +// ) +// .unwrap(); + +// // Delete the third mnemonic, from the second login entry +// remove_account_from_login_at_file(&wallet, &id2, &id3, &password).unwrap(); + +// // Check that we can still load the other accounts +// let loaded_login = load_existing_login_at_file(&wallet, &id2, &password).unwrap(); +// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); +// let expected = vec![ +// WalletAccount::new( +// id2.clone().into(), +// MnemonicAccount::new(acc2, hd_path.clone()), +// ), +// WalletAccount::new(id4.clone(), MnemonicAccount::new(acc4, hd_path.clone())), +// ] +// .into(); +// assert_eq!(loaded_accounts, &expected); + +// // Delete the second and fourth mnemonic from the second login entry removes the login entry +// remove_account_from_login_at_file(&wallet, &id2, &id2.clone().into(), &password).unwrap(); +// remove_account_from_login_at_file(&wallet, &id2, &id4, &password).unwrap(); +// assert!(matches!( +// load_existing_login_at_file(&wallet, &id2, &password), +// Err(BackendError::WalletNoSuchLoginId), +// )); + +// // The first login is still available +// let loaded_login = load_existing_login_at_file(&wallet, &id1, &password).unwrap(); +// let account = loaded_login.as_mnemonic_account().unwrap(); +// assert_eq!(account.mnemonic(), &acc1); +// assert_eq!(account.hd_path(), &hd_path); +// } + +// #[test] +// fn rename_first_account_in_login() { +// let store_dir = tempdir().unwrap(); +// let wallet = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let login_id = LoginId::new("first".to_string()); + +// store_login_with_multiple_accounts_at_file( +// &wallet, +// account1.clone(), +// hd_path.clone(), +// login_id.clone(), +// &password, +// ) +// .unwrap(); + +// let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); +// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); +// let expected = vec![WalletAccount::new( +// DEFAULT_FIRST_ACCOUNT_NAME.into(), +// MnemonicAccount::new(account1.clone(), hd_path.clone()), +// )] +// .into(); +// assert_eq!(loaded_accounts, &expected); + +// let renamed_account = AccountId::new("new_first".to_string()); + +// rename_account_in_login_at_file( +// &wallet, +// &login_id, +// &DEFAULT_FIRST_ACCOUNT_NAME.into(), +// &renamed_account, +// &password, +// ) +// .unwrap(); + +// let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); +// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); +// let expected = vec![WalletAccount::new( +// renamed_account, +// MnemonicAccount::new(account1, hd_path), +// )] +// .into(); +// assert_eq!(loaded_accounts, &expected); +// } + +// #[test] +// fn rename_one_account_in_login_with_two_accounts() { +// let store_dir = tempdir().unwrap(); +// let wallet = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let account2 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let login_id = LoginId::new("first".to_string()); +// let account_id2 = AccountId::new("second".to_string()); +// let renamed_account_id2 = AccountId::new("new_second".to_string()); + +// store_login_with_multiple_accounts_at_file( +// &wallet, +// account1.clone(), +// hd_path.clone(), +// login_id.clone(), +// &password, +// ) +// .unwrap(); + +// append_account_to_login_at_file( +// &wallet, +// account2.clone(), +// hd_path.clone(), +// login_id.clone(), +// account_id2.clone(), +// &password, +// ) +// .unwrap(); + +// // Load and confirm +// let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); +// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); +// let expected = vec![ +// WalletAccount::new( +// DEFAULT_FIRST_ACCOUNT_NAME.into(), +// MnemonicAccount::new(account1.clone(), hd_path.clone()), +// ), +// WalletAccount::new( +// account_id2.clone(), +// MnemonicAccount::new(account2.clone(), hd_path.clone()), +// ), +// ] +// .into(); +// assert_eq!(loaded_accounts, &expected); + +// // Rename the second account to a new name +// rename_account_in_login_at_file( +// &wallet, +// &login_id, +// &account_id2, +// &renamed_account_id2, +// &password, +// ) +// .unwrap(); + +// // Load and confirm +// let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); +// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); +// let expected = vec![ +// WalletAccount::new( +// DEFAULT_FIRST_ACCOUNT_NAME.into(), +// MnemonicAccount::new(account1, hd_path.clone()), +// ), +// WalletAccount::new(renamed_account_id2, MnemonicAccount::new(account2, hd_path)), +// ] +// .into(); +// assert_eq!(loaded_accounts, &expected); +// } + +// #[test] +// fn rename_account_into_existing_account_id_fails() { +// let store_dir = tempdir().unwrap(); +// let wallet = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let account2 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let login_id = LoginId::new("first".to_string()); +// let account_id2 = AccountId::new("second".to_string()); +// let renamed_account_id2 = DEFAULT_FIRST_ACCOUNT_NAME.into(); + +// store_login_with_multiple_accounts_at_file( +// &wallet, +// account1.clone(), +// hd_path.clone(), +// login_id.clone(), +// &password, +// ) +// .unwrap(); + +// append_account_to_login_at_file( +// &wallet, +// account2.clone(), +// hd_path.clone(), +// login_id.clone(), +// account_id2.clone(), +// &password, +// ) +// .unwrap(); + +// // Load and confirm +// let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); +// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); +// let expected = vec![ +// WalletAccount::new( +// DEFAULT_FIRST_ACCOUNT_NAME.into(), +// MnemonicAccount::new(account1.clone(), hd_path.clone()), +// ), +// WalletAccount::new( +// account_id2.clone(), +// MnemonicAccount::new(account2.clone(), hd_path.clone()), +// ), +// ] +// .into(); +// assert_eq!(loaded_accounts, &expected); + +// // Rename the second account to the name of the first one fails +// assert!(matches!( +// rename_account_in_login_at_file( +// &wallet, +// &login_id, +// &account_id2, +// &renamed_account_id2, +// &password, +// ), +// Err(BackendError::WalletAccountIdAlreadyExistsInWalletLogin) +// )); + +// // Load and confirm nothing was changed +// let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); +// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); +// let expected = vec![ +// WalletAccount::new( +// DEFAULT_FIRST_ACCOUNT_NAME.into(), +// MnemonicAccount::new(account1, hd_path.clone()), +// ), +// WalletAccount::new(account_id2, MnemonicAccount::new(account2, hd_path)), +// ] +// .into(); +// assert_eq!(loaded_accounts, &expected); +// } + +// // Test to that decrypts a stored file from the repo, to make sure we are able to decrypt stored +// // wallets created with older versions. +// #[test] +// fn decrypt_stored_wallet() { +// const SAVED_WALLET: &str = "src/wallet_storage/test-data/saved-wallet.json"; +// let wallet_file = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(SAVED_WALLET); + +// let wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); + +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let bad_password = UserPassword::new("bad-password".to_string()); +// let id1 = LoginId::new("first".to_string()); +// let id2 = LoginId::new("second".to_string()); + +// assert!(!wallet.password_can_decrypt_all(&bad_password)); +// assert!(wallet.password_can_decrypt_all(&password)); + +// let acc1 = wallet.decrypt_login(&id1, &password).unwrap(); +// let acc2 = wallet.decrypt_login(&id2, &password).unwrap(); + +// assert!(matches!(acc1, StoredLogin::Mnemonic(_))); +// assert!(matches!(acc2, StoredLogin::Mnemonic(_))); + +// let expected_acc1 = bip39::Mnemonic::from_str("country mean universe text phone begin deputy reject result good cram illness common cluster proud swamp digital patrol spread bar face december base kick").unwrap(); +// let expected_acc2 = bip39::Mnemonic::from_str("home mansion start quiz dress decide hint second dragon sunny juice always steak real minimum art rival skin draw total pulp foot goddess agent").unwrap(); + +// assert_eq!( +// acc1.as_mnemonic_account().unwrap().mnemonic(), +// &expected_acc1 +// ); +// assert_eq!(acc1.as_mnemonic_account().unwrap().hd_path(), &hd_path,); + +// assert_eq!( +// acc2.as_mnemonic_account().unwrap().mnemonic(), +// &expected_acc2 +// ); +// assert_eq!(acc2.as_mnemonic_account().unwrap().hd_path(), &hd_path,); +// } + +// #[test] +// fn decrypt_stored_wallet_1_0_4() { +// const SAVED_WALLET: &str = "src/wallet_storage/test-data/saved-wallet-1.0.4.json"; +// let wallet_file = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(SAVED_WALLET); + +// let wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); + +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password11!".to_string()); +// let bad_password = UserPassword::new("bad-password".to_string()); +// let login_id = LoginId::new("default".to_string()); + +// assert!(!wallet.password_can_decrypt_all(&bad_password)); +// assert!(wallet.password_can_decrypt_all(&password)); + +// let acc1 = wallet.decrypt_login(&login_id, &password).unwrap(); + +// assert!(matches!(acc1, StoredLogin::Mnemonic(_))); + +// let expected_acc1 = bip39::Mnemonic::from_str("arrow capable abstract industry elevator nominee december piece hotel feed lounge web faint sword veteran bundle hour page actual laptop horror gold test warrior").unwrap(); + +// assert_eq!( +// acc1.as_mnemonic_account().unwrap().mnemonic(), +// &expected_acc1 +// ); +// assert_eq!(acc1.as_mnemonic_account().unwrap().hd_path(), &hd_path,); +// } + +// #[test] +// fn decrypt_stored_wallet_1_0_5_with_multiple_accounts() { +// const SAVED_WALLET: &str = "src/wallet_storage/test-data/saved-wallet-1.0.5.json"; +// let wallet_file = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(SAVED_WALLET); + +// let wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); + +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password11!".to_string()); +// let bad_password = UserPassword::new("bad-password".to_string()); +// let login_id = LoginId::new("default".to_string()); + +// assert!(!wallet.password_can_decrypt_all(&bad_password)); +// assert!(wallet.password_can_decrypt_all(&password)); + +// let login = wallet.decrypt_login(&login_id, &password).unwrap(); + +// assert!(matches!(login, StoredLogin::Multiple(_))); + +// let login = login.as_multiple_accounts().unwrap(); +// assert_eq!(login.len(), 4); + +// let expected_mn1 = bip39::Mnemonic::from_str("arrow capable abstract industry elevator nominee december piece hotel feed lounge web faint sword veteran bundle hour page actual laptop horror gold test warrior").unwrap(); +// let expected_mn2 = bip39::Mnemonic::from_str("border hurt skull lunar goddess second danger game dismiss exhaust oven thumb dog drama onion false orchard spice tent next predict invite cherry green").unwrap(); +// let expected_mn3 = bip39::Mnemonic::from_str("gentle crowd rule snap girl urge flat jump winner cluster night sand museum stock grunt quick tree acquire traffic major awake tag rack peasant").unwrap(); +// let expected_mn4 = bip39::Mnemonic::from_str("debris blue skin annual inhale text border rigid spatial lesson coconut yard horn crystal control survey version vote hawk neck frame arrive oblige width").unwrap(); + +// let expected = vec![ +// WalletAccount::new( +// "default".into(), +// MnemonicAccount::new(expected_mn1, hd_path.clone()), +// ), +// WalletAccount::new( +// "account2".into(), +// MnemonicAccount::new(expected_mn2, hd_path.clone()), +// ), +// WalletAccount::new( +// "foobar".into(), +// MnemonicAccount::new(expected_mn3, hd_path.clone()), +// ), +// WalletAccount::new("42".into(), MnemonicAccount::new(expected_mn4, hd_path)), +// ] +// .into(); + +// assert_eq!(login, &expected); +// } + +// #[test] +// fn append_filename() { +// let wallet_file = PathBuf::from("/tmp/saved-wallet.json"); +// let timestamp = OsString::from("42"); +// #[cfg(target_family = "unix")] +// assert_eq!( +// append_timestamp_to_filename(wallet_file.clone(), timestamp.clone(), None) +// .unwrap() +// .into_os_string() +// .into_string() +// .unwrap(), +// "/tmp/saved-wallet-42.json".to_string(), +// ); +// #[cfg(not(target_family = "unix"))] +// assert_eq!( +// append_timestamp_to_filename(wallet_file.clone(), timestamp.clone(), None) +// .unwrap() +// .into_os_string() +// .into_string() +// .unwrap(), +// r"/tmp\saved-wallet-42.json".to_string(), +// ); #[cfg(target_family = "unix")] assert_eq!( From b4865520a444f3065915c21f636d73e8286137fc Mon Sep 17 00:00:00 2001 From: Tommy Verrall Date: Wed, 2 Apr 2025 15:36:49 +0200 Subject: [PATCH 08/48] Revert "add the base points back in" This reverts commit 400aa6ba6d638d0a7e4a062936e883a0a03a31b8. --- nym-wallet/src-tauri/src/config/mod.rs | 16 ++++---- .../src-tauri/src/wallet_storage/mod.rs | 40 +++++++++---------- 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/nym-wallet/src-tauri/src/config/mod.rs b/nym-wallet/src-tauri/src/config/mod.rs index 5b7dec4099..87014b1b50 100644 --- a/nym-wallet/src-tauri/src/config/mod.rs +++ b/nym-wallet/src-tauri/src/config/mod.rs @@ -671,11 +671,11 @@ impl From for NetworkDetails { // .unwrap(); // assert_eq!(nyxd_url.as_ref(), "https://rpc.nymtech.net/"); - let api_url = config - .get_base_validators(WalletNetwork::MAINNET) - .next() - .and_then(|v| v.api_url) - .unwrap(); - assert_eq!(api_url.as_ref(), "https://validator.nymtech.net/api/",); - } -} \ No newline at end of file +// let api_url = config +// .get_base_validators(WalletNetwork::MAINNET) +// .next() +// .and_then(|v| v.api_url) +// .unwrap(); +// assert_eq!(api_url.as_ref(), "https://validator.nymtech.net/api/",); +// } +// } diff --git a/nym-wallet/src-tauri/src/wallet_storage/mod.rs b/nym-wallet/src-tauri/src/wallet_storage/mod.rs index faa026452d..c620351417 100644 --- a/nym-wallet/src-tauri/src/wallet_storage/mod.rs +++ b/nym-wallet/src-tauri/src/wallet_storage/mod.rs @@ -2272,23 +2272,23 @@ fn rename_account_in_login_at_file( // r"/tmp\saved-wallet-42.json".to_string(), // ); - #[cfg(target_family = "unix")] - assert_eq!( - append_timestamp_to_filename(wallet_file, timestamp, Some(3)) - .unwrap() - .into_os_string() - .into_string() - .unwrap(), - "/tmp/saved-wallet-42-3.json".to_string(), - ); - #[cfg(not(target_family = "unix"))] - assert_eq!( - append_timestamp_to_filename(wallet_file, timestamp, Some(3)) - .unwrap() - .into_os_string() - .into_string() - .unwrap(), - r"/tmp\saved-wallet-42-3.json".to_string(), - ); - } -} \ No newline at end of file +// #[cfg(target_family = "unix")] +// assert_eq!( +// append_timestamp_to_filename(wallet_file, timestamp, Some(3)) +// .unwrap() +// .into_os_string() +// .into_string() +// .unwrap(), +// "/tmp/saved-wallet-42-3.json".to_string(), +// ); +// #[cfg(not(target_family = "unix"))] +// assert_eq!( +// append_timestamp_to_filename(wallet_file, timestamp, Some(3)) +// .unwrap() +// .into_os_string() +// .into_string() +// .unwrap(), +// r"/tmp\saved-wallet-42-3.json".to_string(), +// ); +// } +// } From 7b6077ba6415b2d267eb2a1ad642205d585ee6a6 Mon Sep 17 00:00:00 2001 From: Tommy Verrall Date: Fri, 4 Apr 2025 13:56:20 +0200 Subject: [PATCH 09/48] update to log in - next up fix hyperlinks --- nym-wallet/Cargo.lock | 2344 ++++++----- nym-wallet/src-tauri/Cargo.toml | 1 + .../src-tauri/gen/schemas/acl-manifests.json | 1 + .../src-tauri/gen/schemas/desktop-schema.json | 1117 +++-- .../src-tauri/gen/schemas/macOS-schema.json | 1117 +++-- nym-wallet/src-tauri/src/config/mod.rs | 298 +- .../src-tauri/src/operations/app/window.rs | 6 +- .../src-tauri/src/wallet_storage/mod.rs | 3666 +++++++++-------- yarn.lock | 1923 ++++++++- 9 files changed, 6505 insertions(+), 3968 deletions(-) create mode 100644 nym-wallet/src-tauri/gen/schemas/acl-manifests.json diff --git a/nym-wallet/Cargo.lock b/nym-wallet/Cargo.lock index e451c0c1e8..bdc7304084 100644 --- a/nym-wallet/Cargo.lock +++ b/nym-wallet/Cargo.lock @@ -4,19 +4,13 @@ version = 3 [[package]] name = "addr2line" -version = "0.20.0" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4fa78e18c64fce05e902adecd7a5eed15a5e0a3439f7b0e169f0252214865e3" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" dependencies = [ "gimli", ] -[[package]] -name = "adler" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" - [[package]] name = "adler2" version = "2.0.0" @@ -46,9 +40,9 @@ dependencies = [ [[package]] name = "aes-gcm" -version = "0.10.2" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "209b47e8954a928e1d72e86eca7000ebb6655fe1436d33eefc2201cad027e237" +checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" dependencies = [ "aead", "aes", @@ -67,14 +61,14 @@ dependencies = [ "cfg-if", "once_cell", "version_check", - "zerocopy", + "zerocopy 0.7.35", ] [[package]] name = "aho-corasick" -version = "1.0.3" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b8f9420f797f2d9e935edf629310eb938a0d839f984e25327f3c7eed22300c" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] @@ -117,57 +111,59 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.4" +version = "0.6.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44" +checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", "colorchoice", + "is_terminal_polyfill", "utf8parse", ] [[package]] name = "anstyle" -version = "1.0.1" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a30da5c5f2d5e72842e00bcb57657162cdabef0931f40e2deb9b4140440cecd" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" [[package]] name = "anstyle-parse" -version = "0.2.1" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "938874ff5980b03a87c5524b3ae5b59cf99b1d6bc836848df7bc5ada9643c333" +checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.0.0" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" +checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.59.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.1" +version = "3.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628" +checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e" dependencies = [ "anstyle", - "windows-sys 0.48.0", + "once_cell", + "windows-sys 0.59.0", ] [[package]] name = "anyhow" -version = "1.0.72" +version = "1.0.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b13c32d80ecc7ab747b80c3784bce54ee8a7a0cc4fbda9bf4cda2cf6fe90854" +checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f" [[package]] name = "arbitrary" @@ -180,28 +176,30 @@ dependencies = [ [[package]] name = "arboard" -version = "3.4.1" +version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df099ccb16cd014ff054ac1bf392c67feeef57164b05c42f037cd40f5d4357f4" +checksum = "c1df21f715862ede32a0c525ce2ca4d52626bb0007f8c18b87a384503ac33e70" dependencies = [ "clipboard-win", - "core-graphics 0.23.1", "image", "log", - "objc2 0.5.2", - "objc2-app-kit 0.2.2", - "objc2-foundation 0.2.2", + "objc2 0.6.0", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-foundation 0.3.0", "parking_lot", - "windows-sys 0.48.0", + "percent-encoding", + "windows-sys 0.59.0", "wl-clipboard-rs", "x11rb", ] [[package]] name = "argon2" -version = "0.5.1" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2e554a8638bdc1e4eae9984845306cc95f8a9208ba8d49c3859fd958b46774d" +checksum = "3c3610892ee6e0cbce8ae2700349fcf8f98adb0dbfbee85aec3c9179d29cc072" dependencies = [ "base64ct", "blake2", @@ -332,9 +330,9 @@ dependencies = [ [[package]] name = "async-compression" -version = "0.4.18" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df895a515f70646414f4b45c0b79082783b80552b373a68283012928df56f522" +checksum = "59a194f9d963d8099596278594b3107448656ba73831c9d8c783e613ce86da64" dependencies = [ "flate2", "futures-core", @@ -351,7 +349,7 @@ checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.100", ] [[package]] @@ -396,23 +394,23 @@ dependencies = [ [[package]] name = "autocfg" -version = "1.1.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "backtrace" -version = "0.3.68" +version = "0.3.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4319208da049c43661739c5fade2ba182f09d1dc2299b32298d3a31692b17e12" +checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" dependencies = [ "addr2line", - "cc", "cfg-if", "libc", - "miniz_oxide 0.7.1", + "miniz_oxide", "object", "rustc-demangle", + "windows-targets 0.52.6", ] [[package]] @@ -429,9 +427,9 @@ checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" [[package]] name = "base64" -version = "0.21.4" +version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ba43ea6f343b788c8764558649e08df62f86c6ef251fdaeb1ffd010a9ae50a2" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" [[package]] name = "base64" @@ -441,9 +439,9 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "base64ct" -version = "1.6.0" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" +checksum = "89e25b6adfb930f02d1981565a6e5d9c547ac15a96606256d3b59040e5cd4ca3" [[package]] name = "bech32" @@ -479,9 +477,9 @@ dependencies = [ [[package]] name = "bip39" -version = "2.0.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93f2635620bf0b9d4576eb7bb9a38a55df78bd1205d26fa994b25911a69f212f" +checksum = "33415e24172c1b7d6066f6d999545375ab8e1d95421d6784bdfff9496f292387" dependencies = [ "bitcoin_hashes", "rand 0.8.5", @@ -492,10 +490,20 @@ dependencies = [ ] [[package]] -name = "bitcoin_hashes" -version = "0.11.0" +name = "bitcoin-internals" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90064b8dee6815a6470d60bad07bbbaee885c0e12d04177138fa3291a01b7bc4" +checksum = "9425c3bf7089c983facbae04de54513cce73b41c7f9ff8c845b54e7bc64ebbfb" + +[[package]] +name = "bitcoin_hashes" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1930a4dabfebb8d7d9992db18ebe3ae2876f0a305fab206fd168df931ede293b" +dependencies = [ + "bitcoin-internals", + "hex-conservative", +] [[package]] name = "bitflags" @@ -581,13 +589,13 @@ checksum = "3e31ea183f6ee62ac8b8a8cf7feddd766317adfb13ff469de57ce033efd6a790" [[package]] name = "brotli" -version = "3.3.4" +version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1a0b1dbcc8ae29329621f8d4f0d835787c1c38bb1401979b49d13b0b305ff68" +checksum = "d640d25bc63c50fb1f0b545ffd80207d2e10a4c965530809b40ba3386825c391" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", - "brotli-decompressor 2.3.4", + "brotli-decompressor 2.5.1", ] [[package]] @@ -603,9 +611,9 @@ dependencies = [ [[package]] name = "brotli-decompressor" -version = "2.3.4" +version = "2.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b6561fd3f895a11e8f72af2cb7d22e08366bebc2b6b57f7744c4bda27034744" +checksum = "4e2e4afe60d7dd600fdd3de8d0f08c2b7ec039712e3b6137ff98b7004e82de4f" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", @@ -633,15 +641,15 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.13.0" +version = "3.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" +checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" [[package]] name = "bytemuck" -version = "1.13.1" +version = "1.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17febce684fd15d89027105661fec94afb475cb995fbc59d2865198446ba2eea" +checksum = "b6b1fc10dbac614ebc03540c9dbd60e83887fda27794998c6528f1782047d540" [[package]] name = "byteorder" @@ -675,7 +683,7 @@ dependencies = [ "glib", "libc", "once_cell", - "thiserror 1.0.64", + "thiserror 1.0.69", ] [[package]] @@ -691,18 +699,18 @@ dependencies = [ [[package]] name = "camino" -version = "1.1.7" +version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0ec6b951b160caa93cc0c7b209e5a3bff7aae9062213451ac99493cd844c239" +checksum = "8b96ec4966b5813e2c0507c1f86115c8c5abaadc3980879c3424042a02fd1ad3" dependencies = [ "serde", ] [[package]] name = "cargo-platform" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24b1f0365a6c6bb4020cd05806fd0d33c44d38046b8bd7f0e40814b9763cabfc" +checksum = "e35af189006b9c0f00a064685c727031e3ed2d8020f7ba284d78cc2671bd36ea" dependencies = [ "serde", ] @@ -718,7 +726,7 @@ dependencies = [ "semver", "serde", "serde_json", - "thiserror 1.0.64", + "thiserror 1.0.69", ] [[package]] @@ -732,7 +740,7 @@ dependencies = [ "semver", "serde", "serde_json", - "thiserror 2.0.11", + "thiserror 2.0.12", ] [[package]] @@ -747,9 +755,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.16" +version = "1.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be714c154be609ec7f5dad223a33bf1482fff90472de28f7362806e6d4832b8c" +checksum = "1fcb57c740ae1daf453ae85f16e37396f672b039e00d9d866e07ddb24e328e3a" dependencies = [ "shlex", ] @@ -770,16 +778,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" -[[package]] -name = "cfb" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74f89d248799e3f15f91b70917f65381062a01bb8e222700ea0e5a7ff9785f9c" -dependencies = [ - "byteorder", - "uuid 0.8.2", -] - [[package]] name = "cfb" version = "0.7.3" @@ -788,14 +786,14 @@ checksum = "d38f2da7a0a2c4ccf0065be06397cc26a81f4e528be095826eee9d4adbb8c60f" dependencies = [ "byteorder", "fnv", - "uuid 1.10.0", + "uuid", ] [[package]] name = "cfg-expr" -version = "0.15.4" +version = "0.15.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b40ccee03b5175c18cde8f37e7d2a33bcef6f8ec8f7cc0d81090d1bb380949c9" +checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02" dependencies = [ "smallvec", "target-lexicon", @@ -807,12 +805,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" -[[package]] -name = "cfg_aliases" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" - [[package]] name = "cfg_aliases" version = "0.2.1" @@ -844,9 +836,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.4.7" +version = "4.5.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac495e00dcec98c83465d5ad66c5c4fabd652fd6686e7c6269b117e729a6f17b" +checksum = "d8aa86934b44c19c50f87cc2790e19f54f7a67aedb64101c2e1a2e5ecfb73944" dependencies = [ "clap_builder", "clap_derive", @@ -854,33 +846,33 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.4.7" +version = "4.5.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c77ed9a32a62e6ca27175d00d29d05ca32e396ea1eb5fb01d8256b669cec7663" +checksum = "2414dbb2dd0695280da6ea9261e327479e9d37b0630f6b53ba2a11c60c679fd9" dependencies = [ "anstream", "anstyle", "clap_lex", - "strsim 0.10.0", + "strsim", ] [[package]] name = "clap_derive" -version = "4.4.7" +version = "4.5.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" +checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7" dependencies = [ - "heck 0.4.1", + "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.100", ] [[package]] name = "clap_lex" -version = "0.6.0" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" +checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" [[package]] name = "clipboard-win" @@ -893,9 +885,9 @@ dependencies = [ [[package]] name = "colorchoice" -version = "1.0.0" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" [[package]] name = "colored" @@ -920,9 +912,9 @@ dependencies = [ [[package]] name = "combine" -version = "4.6.6" +version = "4.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35ed6e9d84f0b51a7f52daf1c7d71dd136fd7a3f41a8462b8cdb8c78d920fad4" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" dependencies = [ "bytes", "memchr", @@ -930,15 +922,15 @@ dependencies = [ [[package]] name = "const-oid" -version = "0.9.5" +version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28c122c3980598d243d63d9a704629a2d748d101f278052ff068be5a4423ab6f" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" [[package]] name = "const-str" -version = "0.5.6" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aca749d3d3f5b87a0d6100509879f9cf486ab510803a4a4e1001da1ff61c2bd6" +checksum = "3618cccc083bb987a415d85c02ca6c9994ea5b44731ec28b9ecf09658655fba9" [[package]] name = "convert_case" @@ -958,9 +950,9 @@ dependencies = [ [[package]] name = "core-foundation" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" dependencies = [ "core-foundation-sys", "libc", @@ -982,19 +974,6 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" -[[package]] -name = "core-graphics" -version = "0.23.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "970a29baf4110c26fedbc7f82107d42c23f7e88e404c4577ed73fe99ff85a212" -dependencies = [ - "bitflags 1.3.2", - "core-foundation 0.9.3", - "core-graphics-types 0.1.2", - "foreign-types 0.5.0", - "libc", -] - [[package]] name = "core-graphics" version = "0.24.0" @@ -1003,22 +982,11 @@ checksum = "fa95a34622365fa5bbf40b20b75dba8dfa8c94c734aea8ac9a5ca38af14316f1" dependencies = [ "bitflags 2.9.0", "core-foundation 0.10.0", - "core-graphics-types 0.2.0", + "core-graphics-types", "foreign-types 0.5.0", "libc", ] -[[package]] -name = "core-graphics-types" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bb142d41022986c1d8ff29103a1411c8a3dfad3552f87a4f8dc50d61d4f4e33" -dependencies = [ - "bitflags 1.3.2", - "core-foundation 0.9.3", - "libc", -] - [[package]] name = "core-graphics-types" version = "0.2.0" @@ -1058,7 +1026,7 @@ dependencies = [ "subtle-encoding", "tendermint", "tendermint-rpc", - "thiserror 1.0.64", + "thiserror 1.0.69", ] [[package]] @@ -1088,7 +1056,7 @@ dependencies = [ "rand_core 0.6.4", "rayon", "sha2 0.10.8", - "thiserror 1.0.64", + "thiserror 1.0.69", ] [[package]] @@ -1099,7 +1067,7 @@ checksum = "a782b93fae93e57ca8ad3e9e994e784583f5933aeaaa5c80a545c4b437be2047" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.100", ] [[package]] @@ -1112,7 +1080,7 @@ dependencies = [ "schemars", "serde", "serde_json", - "thiserror 1.0.64", + "thiserror 1.0.69", ] [[package]] @@ -1123,7 +1091,7 @@ checksum = "e01c9214319017f6ebd8e299036e1f717fa9bb6724e758f7d6fb2477599d1a29" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.100", ] [[package]] @@ -1147,14 +1115,14 @@ dependencies = [ "serde-json-wasm", "sha2 0.10.8", "static_assertions", - "thiserror 1.0.64", + "thiserror 1.0.69", ] [[package]] name = "cpufeatures" -version = "0.2.9" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" dependencies = [ "libc", ] @@ -1170,11 +1138,10 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.8" +version = "0.5.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" +checksum = "06ba6d68e24814cb8de6bb986db8222d3a027d15872cabc0d18817bc3c0e4471" dependencies = [ - "cfg-if", "crossbeam-utils", ] @@ -1205,9 +1172,9 @@ checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] name = "crypto-bigint" -version = "0.5.2" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf4c2f4e1afd912bc40bfd6fed5d9dc1f288e0ba01bfcc835cc5bc3eb13efe15" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" dependencies = [ "generic-array", "rand_core 0.6.4", @@ -1250,17 +1217,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331" dependencies = [ "quote", - "syn 2.0.96", -] - -[[package]] -name = "ctor" -version = "0.1.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d2301688392eb071b0bf1a37be05c469d3cc4dbbd95df672fe28ab021e6a096" -dependencies = [ - "quote", - "syn 1.0.109", + "syn 2.0.100", ] [[package]] @@ -1270,7 +1227,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a2785755761f3ddc1492979ce1e48d2c00d09311c39e4466429188f3dd6501" dependencies = [ "quote", - "syn 2.0.96", + "syn 2.0.100", ] [[package]] @@ -1301,13 +1258,13 @@ dependencies = [ [[package]] name = "curve25519-dalek-derive" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83fdaf97f4804dcebfa5862639bc9ce4121e82140bec2a987ac5140294865b5b" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.100", ] [[package]] @@ -1335,7 +1292,7 @@ dependencies = [ "cw-utils", "schemars", "serde", - "thiserror 1.0.64", + "thiserror 1.0.69", ] [[package]] @@ -1359,7 +1316,7 @@ dependencies = [ "cosmwasm-std", "schemars", "serde", - "thiserror 1.0.64", + "thiserror 1.0.69", ] [[package]] @@ -1374,7 +1331,7 @@ dependencies = [ "schemars", "semver", "serde", - "thiserror 1.0.64", + "thiserror 1.0.69", ] [[package]] @@ -1402,7 +1359,7 @@ dependencies = [ "cw20", "schemars", "serde", - "thiserror 1.0.64", + "thiserror 1.0.69", ] [[package]] @@ -1418,38 +1375,14 @@ dependencies = [ "serde", ] -[[package]] -name = "darling" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c" -dependencies = [ - "darling_core 0.13.4", - "darling_macro 0.13.4", -] - [[package]] name = "darling" version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" dependencies = [ - "darling_core 0.20.11", - "darling_macro 0.20.11", -] - -[[package]] -name = "darling_core" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610" -dependencies = [ - "fnv", - "ident_case", - "proc-macro2", - "quote", - "strsim 0.10.0", - "syn 1.0.109", + "darling_core", + "darling_macro", ] [[package]] @@ -1462,19 +1395,8 @@ dependencies = [ "ident_case", "proc-macro2", "quote", - "strsim 0.11.1", - "syn 2.0.96", -] - -[[package]] -name = "darling_macro" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835" -dependencies = [ - "darling_core 0.13.4", - "quote", - "syn 1.0.109", + "strsim", + "syn 2.0.100", ] [[package]] @@ -1483,22 +1405,22 @@ version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ - "darling_core 0.20.11", + "darling_core", "quote", - "syn 2.0.96", + "syn 2.0.100", ] [[package]] name = "data-encoding" -version = "2.7.0" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e60eed09d8c01d3cee5b7d30acb059b76614c918fa0f992e0dd6eeb10daad6f" +checksum = "575f75dfd25738df5b91b8e43e14d44bda14637a58fae779fd2b064f8bf3e010" [[package]] name = "der" -version = "0.7.8" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c" +checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" dependencies = [ "const-oid", "zeroize", @@ -1506,9 +1428,9 @@ dependencies = [ [[package]] name = "deranged" -version = "0.3.9" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f32d04922c60427da6f9fef14d042d9edddef64cb9d4ce0d64d0685fbeb1fd3" +checksum = "28cfac68e08048ae1883171632c2aef3ebc555621ae56fbccce1cbf22dd7f058" dependencies = [ "powerfmt", "serde", @@ -1525,17 +1447,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "derive-new" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d150dea618e920167e5973d70ae6ece4385b7164e0d799fe7c122dd0a5d912ad" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.96", -] - [[package]] name = "derive_arbitrary" version = "1.4.1" @@ -1544,20 +1455,20 @@ checksum = "30542c1ad912e0e3d22a1935c290e12e8a29d704a420177a31faad4a601a0800" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.100", ] [[package]] name = "derive_more" -version = "0.99.17" +version = "0.99.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +checksum = "3da29a38df43d6f156149c9b43ded5e018ddff2a855cf2cfd62e8cd7d079c69f" dependencies = [ "convert_case", "proc-macro2", "quote", "rustc_version", - "syn 1.0.109", + "syn 2.0.100", ] [[package]] @@ -1577,7 +1488,7 @@ checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.100", "unicode-xid", ] @@ -1636,7 +1547,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" dependencies = [ "libc", - "redox_users 0.4.3", + "redox_users 0.4.6", "winapi", ] @@ -1648,7 +1559,7 @@ checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" dependencies = [ "libc", "option-ext", - "redox_users 0.4.3", + "redox_users 0.4.6", "windows-sys 0.48.0", ] @@ -1678,7 +1589,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.100", ] [[package]] @@ -1701,7 +1612,7 @@ checksum = "f2b99bf03862d7f545ebc28ddd33a665b50865f4dfd84031a393823879bd4c54" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.100", ] [[package]] @@ -1727,36 +1638,36 @@ dependencies = [ [[package]] name = "dtoa" -version = "1.0.9" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcbb2bf8e87535c23f7a8a321e364ce21462d0ff10cb6407820e8e96dfff6653" +checksum = "d6add3b8cff394282be81f3fc1a0605db594ed69890078ca6e2cab1c408bcf04" [[package]] name = "dtoa-short" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbaceec3c6e4211c79e7b1800fb9680527106beb2f9c51904a3210c03a448c74" +checksum = "cd1511a7b6a56299bd043a9c167a6d2bfb37bf84a6dfceaba651168adfb43c87" dependencies = [ "dtoa", ] [[package]] name = "dunce" -version = "1.0.4" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" [[package]] name = "dyn-clone" -version = "1.0.12" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "304e6508efa593091e97a9abbc10f90aa7ca635b6d2784feff3c89d41dd12272" +checksum = "1c7a8fb8a9fbf66c1f703fe16184d10ca0ee9d23be5b4436400408ba54a95005" [[package]] name = "ecdsa" -version = "0.16.8" +version = "0.16.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4b1e0c257a9e9f25f90ff76d7a68360ed497ee519c8e428d1825ef0000799d4" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" dependencies = [ "der", "digest 0.10.7", @@ -1769,9 +1680,9 @@ dependencies = [ [[package]] name = "ed25519" -version = "2.2.2" +version = "2.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60f6d271ca33075c88028be6f04d502853d63a5ece419d269c15315d4fc1cf1d" +checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" dependencies = [ "pkcs8", "serde", @@ -1823,9 +1734,9 @@ dependencies = [ [[package]] name = "either" -version = "1.9.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" [[package]] name = "elliptic-curve" @@ -1885,7 +1796,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.100", ] [[package]] @@ -1903,9 +1814,9 @@ dependencies = [ [[package]] name = "equivalent" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "erased-serde" @@ -1980,9 +1891,9 @@ dependencies = [ [[package]] name = "fiat-crypto" -version = "0.2.1" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0870c84016d4b481be5c9f323c24f65e31e901ae618f0e80f4308fb00de1d2d" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" [[package]] name = "field-offset" @@ -2014,12 +1925,12 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" [[package]] name = "flate2" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11faaf5a5236997af9848be0bef4db95824b1d534ebc64d0f0c6cf3e67bd38dc" +checksum = "7ced92e76e966ca2fd84c8f7aa01a4aea65b0eb6648d72f7c8f3e2764a67fece" dependencies = [ "crc32fast", - "miniz_oxide 0.8.5", + "miniz_oxide", ] [[package]] @@ -2032,6 +1943,15 @@ dependencies = [ "paste", ] +[[package]] +name = "fluent-uri" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17c704e9dbe1ddd863da1e6ff3567795087b1eb201ce80d8fa81162e1516500d" +dependencies = [ + "bitflags 1.3.2", +] + [[package]] name = "fnv" version = "1.0.7" @@ -2065,7 +1985,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.100", ] [[package]] @@ -2155,7 +2075,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.100", ] [[package]] @@ -2314,7 +2234,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0176e0459c2e4a1fe232f984bca6890e681076abb9934f6cea7c326f3fc47818" dependencies = [ "libc", - "windows-targets 0.48.1", + "windows-targets 0.48.5", ] [[package]] @@ -2330,9 +2250,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.10" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "js-sys", @@ -2348,9 +2268,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0" dependencies = [ "cfg-if", + "js-sys", "libc", "r-efi", "wasi 0.14.2+wasi-0.2.4", + "wasm-bindgen", ] [[package]] @@ -2362,14 +2284,14 @@ dependencies = [ "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.100", ] [[package]] name = "ghash" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d930750de5717d2dd0b8c0d42c076c0e884c81a73e6cab859bbd2339c71e3e40" +checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1" dependencies = [ "opaque-debug", "polyval", @@ -2377,9 +2299,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.27.3" +version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" [[package]] name = "gio" @@ -2397,7 +2319,7 @@ dependencies = [ "once_cell", "pin-project-lite", "smallvec", - "thiserror 1.0.64", + "thiserror 1.0.69", ] [[package]] @@ -2433,7 +2355,7 @@ dependencies = [ "memchr", "once_cell", "smallvec", - "thiserror 1.0.64", + "thiserror 1.0.69", ] [[package]] @@ -2447,7 +2369,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.100", ] [[package]] @@ -2462,9 +2384,9 @@ dependencies = [ [[package]] name = "glob" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" +checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" [[package]] name = "gobject-sys" @@ -2537,22 +2459,22 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.100", ] [[package]] name = "h2" -version = "0.3.20" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97ec8491ebaf99c8eaa73058b045fe58073cd6be7f596ac993ced0b0a0c01049" +checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" dependencies = [ "bytes", "fnv", "futures-core", "futures-sink", "futures-util", - "http 0.2.9", - "indexmap 1.9.3", + "http 0.2.12", + "indexmap 2.8.0", "slab", "tokio", "tokio-util", @@ -2561,17 +2483,17 @@ dependencies = [ [[package]] name = "h2" -version = "0.4.5" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa82e28a107a8cc405f0839610bdc9b15f1e25ec7d696aa5cf173edbcb1486ab" +checksum = "5017294ff4bb30944501348f6f8e42e6ad28f42c8bbef7a74029aff064a4e3c2" dependencies = [ "atomic-waker", "bytes", "fnv", "futures-core", "futures-sink", - "http 1.1.0", - "indexmap 2.5.0", + "http 1.3.1", + "indexmap 2.8.0", "slab", "tokio", "tokio-util", @@ -2617,6 +2539,12 @@ dependencies = [ "allocator-api2", ] +[[package]] +name = "hashbrown" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" + [[package]] name = "heck" version = "0.3.3" @@ -2649,9 +2577,9 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.3.9" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" +checksum = "fbd780fe5cc30f81464441920d82ac8740e2e46b29a6fad543ddd075229ce37e" [[package]] name = "hex" @@ -2659,6 +2587,12 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "hex-conservative" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "212ab92002354b4819390025006c897e8140934349e8635c9b077f47b4dcbd20" + [[package]] name = "hex-literal" version = "0.3.4" @@ -2667,9 +2601,9 @@ checksum = "7ebdb29d2ea9ed0083cd8cece49bbd968021bd99b0849edb4a9a7ee0fdf6a4e0" [[package]] name = "hickory-proto" -version = "0.24.3" +version = "0.24.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ad3d6d98c648ed628df039541a5577bee1a7c83e9e16fe3dbedeea4cdfeb971" +checksum = "92652067c9ce6f66ce53cc38d1169daa36e6e7eb7dd3b63b5103bd9d97117248" dependencies = [ "async-trait", "bytes", @@ -2679,15 +2613,15 @@ dependencies = [ "futures-channel", "futures-io", "futures-util", - "h2 0.3.20", - "http 0.2.9", - "idna 1.0.3", + "h2 0.3.26", + "http 0.2.12", + "idna", "ipnet", "once_cell", "rand 0.8.5", - "rustls 0.21.9", + "rustls 0.21.12", "rustls-pemfile 1.0.4", - "thiserror 1.0.64", + "thiserror 1.0.69", "tinyvec", "tokio", "tokio-rustls 0.24.1", @@ -2711,9 +2645,9 @@ dependencies = [ "parking_lot", "rand 0.8.5", "resolv-conf", - "rustls 0.21.9", + "rustls 0.21.12", "smallvec", - "thiserror 1.0.64", + "thiserror 1.0.69", "tokio", "tokio-rustls 0.24.1", "tracing", @@ -2731,27 +2665,13 @@ dependencies = [ [[package]] name = "hostname" -version = "0.3.1" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" +checksum = "f9c7c7c8ac16c798734b8a24560c1362120597c40d5e1459f09498f8f6c8f2ba" dependencies = [ + "cfg-if", "libc", - "match_cfg", - "winapi", -] - -[[package]] -name = "html5ever" -version = "0.25.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5c13fb08e5d4dfc151ee5e88bae63f7773d61852f3bdc73c9f4b9e1bde03148" -dependencies = [ - "log", - "mac", - "markup5ever 0.10.1", - "proc-macro2", - "quote", - "syn 1.0.109", + "windows 0.52.0", ] [[package]] @@ -2762,7 +2682,7 @@ checksum = "bea68cab48b8459f17cf1c944c67ddc572d272d9f2b274140f223ecb1da4a3b7" dependencies = [ "log", "mac", - "markup5ever 0.11.0", + "markup5ever", "proc-macro2", "quote", "syn 1.0.109", @@ -2770,65 +2690,65 @@ dependencies = [ [[package]] name = "http" -version = "0.2.9" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" dependencies = [ "bytes", "fnv", - "itoa 1.0.9", + "itoa 1.0.15", ] [[package]] name = "http" -version = "1.1.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" +checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" dependencies = [ "bytes", "fnv", - "itoa 1.0.9", + "itoa 1.0.15", ] [[package]] name = "http-body" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" dependencies = [ "bytes", - "http 0.2.9", + "http 0.2.12", "pin-project-lite", ] [[package]] name = "http-body" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", - "http 1.1.0", + "http 1.3.1", ] [[package]] name = "http-body-util" -version = "0.1.1" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0475f8b2ac86659c21b64320d5d653f9efe42acd2a4e560073ec61a155a34f1d" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" dependencies = [ "bytes", "futures-core", - "http 1.1.0", - "http-body 1.0.0", + "http 1.3.1", + "http-body 1.0.1", "pin-project-lite", ] [[package]] name = "httparse" -version = "1.8.0" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" [[package]] name = "httpdate" @@ -2863,22 +2783,22 @@ dependencies = [ [[package]] name = "hyper" -version = "0.14.27" +version = "0.14.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" +checksum = "41dfc780fdec9373c01bae43289ea34c972e40ee3c9f6b3c8801a35f35586ce7" dependencies = [ "bytes", "futures-channel", "futures-core", "futures-util", - "h2 0.3.20", - "http 0.2.9", - "http-body 0.4.5", + "h2 0.3.26", + "http 0.2.12", + "http-body 0.4.6", "httparse", "httpdate", - "itoa 1.0.9", + "itoa 1.0.15", "pin-project-lite", - "socket2 0.4.9", + "socket2", "tokio", "tower-service", "tracing", @@ -2887,18 +2807,18 @@ dependencies = [ [[package]] name = "hyper" -version = "1.3.1" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe575dd17d0862a9a33781c8c4696a55c320909004a67a00fb286ba8b1bc496d" +checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80" dependencies = [ "bytes", "futures-channel", "futures-util", - "h2 0.4.5", - "http 1.1.0", - "http-body 1.0.0", + "h2 0.4.8", + "http 1.3.1", + "http-body 1.0.1", "httparse", - "itoa 1.0.9", + "itoa 1.0.15", "pin-project-lite", "smallvec", "tokio", @@ -2912,28 +2832,29 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" dependencies = [ "futures-util", - "http 0.2.9", - "hyper 0.14.27", - "rustls 0.21.9", + "http 0.2.12", + "hyper 0.14.32", + "rustls 0.21.12", "tokio", "tokio-rustls 0.24.1", ] [[package]] name = "hyper-rustls" -version = "0.26.0" +version = "0.27.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0bea761b46ae2b24eb4aef630d8d1c398157b6fc29e6350ecf090a0b70c952c" +checksum = "2d191583f3da1305256f22463b9bb0471acad48a4e534a5218b9963e9c1f59b2" dependencies = [ "futures-util", - "http 1.1.0", - "hyper 1.3.1", + "http 1.3.1", + "hyper 1.6.0", "hyper-util", - "rustls 0.22.4", + "rustls 0.23.25", "rustls-pki-types", "tokio", - "tokio-rustls 0.25.0", + "tokio-rustls 0.26.2", "tower-service", + "webpki-roots 0.26.8", ] [[package]] @@ -2944,7 +2865,7 @@ checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" dependencies = [ "bytes", "http-body-util", - "hyper 1.3.1", + "hyper 1.6.0", "hyper-util", "native-tls", "tokio", @@ -2954,36 +2875,37 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.3" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca38ef113da30126bbff9cd1705f9273e15d45498615d138b0c20279ac7a76aa" +checksum = "497bbc33a26fdd4af9ed9c70d63f61cf56a938375fbb32df34db9b1cd6d643f2" dependencies = [ "bytes", "futures-channel", "futures-util", - "http 1.1.0", - "http-body 1.0.0", - "hyper 1.3.1", + "http 1.3.1", + "http-body 1.0.1", + "hyper 1.6.0", + "libc", "pin-project-lite", - "socket2 0.5.5", + "socket2", "tokio", - "tower", "tower-service", "tracing", ] [[package]] name = "iana-time-zone" -version = "0.1.60" +version = "0.1.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" +checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", + "log", "wasm-bindgen", - "windows-core 0.52.0", + "windows-core 0.61.0", ] [[package]] @@ -3056,9 +2978,9 @@ dependencies = [ [[package]] name = "icu_locid_transform_data" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" +checksum = "7515e6d781098bf9f7205ab3fc7e9709d34554ae0b21ddbcb5febfa4bc7df11d" [[package]] name = "icu_normalizer" @@ -3080,9 +3002,9 @@ dependencies = [ [[package]] name = "icu_normalizer_data" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" +checksum = "c5e8338228bdc8ab83303f16b797e177953730f601a96c25d10cb3ab0daa0cb7" [[package]] name = "icu_properties" @@ -3101,9 +3023,9 @@ dependencies = [ [[package]] name = "icu_properties_data" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" +checksum = "85fb8799753b75aee8d2a21d7c14d9f38921b54b3dbda10f5a3c7a7b82dba5e2" [[package]] name = "icu_provider" @@ -3130,7 +3052,7 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.100", ] [[package]] @@ -3139,16 +3061,6 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" -[[package]] -name = "idna" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" -dependencies = [ - "unicode-bidi", - "unicode-normalization", -] - [[package]] name = "idna" version = "1.0.3" @@ -3202,22 +3114,22 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.5.0" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" +checksum = "3954d50fe15b02142bf25d3b8bdadb634ec3948f103d04ffe3031bc8fe9d7058" dependencies = [ "equivalent", - "hashbrown 0.14.5", + "hashbrown 0.15.2", "serde", ] [[package]] name = "infer" -version = "0.7.0" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20b2b533137b9cad970793453d4f921c2e91312a6d88b1085c07bc15fc51bb3b" +checksum = "f551f8c3a39f68f986517db0d1759de85881894fdc7db798bd2a9df9cb04b7fc" dependencies = [ - "cfb 0.6.1", + "cfb", ] [[package]] @@ -3226,14 +3138,14 @@ version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a588916bfdfd92e71cacef98a63d9b1f0d74d6599980d11894290e7ddefffcf7" dependencies = [ - "cfb 0.7.3", + "cfb", ] [[package]] name = "inout" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" dependencies = [ "generic-array", ] @@ -3244,7 +3156,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b58db92f96b720de98181bbbe63c831e87005ab460c1bf306eb2622b4707997f" dependencies = [ - "socket2 0.5.5", + "socket2", "widestring", "windows-sys 0.48.0", "winreg 0.50.0", @@ -3252,9 +3164,9 @@ dependencies = [ [[package]] name = "ipnet" -version = "2.8.0" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6" +checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" [[package]] name = "is-docker" @@ -3267,13 +3179,13 @@ dependencies = [ [[package]] name = "is-terminal" -version = "0.4.9" +version = "0.4.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" +checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9" dependencies = [ - "hermit-abi 0.3.9", - "rustix 0.38.44", - "windows-sys 0.48.0", + "hermit-abi 0.5.0", + "libc", + "windows-sys 0.59.0", ] [[package]] @@ -3286,6 +3198,12 @@ dependencies = [ "once_cell", ] +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + [[package]] name = "itertools" version = "0.10.5" @@ -3295,15 +3213,6 @@ dependencies = [ "either", ] -[[package]] -name = "itertools" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" -dependencies = [ - "either", -] - [[package]] name = "itertools" version = "0.14.0" @@ -3321,9 +3230,9 @@ checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" [[package]] name = "itoa" -version = "1.0.9" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "javascriptcore-rs" @@ -3359,7 +3268,7 @@ dependencies = [ "combine", "jni-sys", "log", - "thiserror 1.0.64", + "thiserror 1.0.69", "walkdir", "windows-sys 0.45.0", ] @@ -3378,10 +3287,11 @@ checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0" [[package]] name = "js-sys" -version = "0.3.64" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" dependencies = [ + "once_cell", "wasm-bindgen", ] @@ -3396,16 +3306,39 @@ dependencies = [ "treediff", ] +[[package]] +name = "json-patch" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b1fb8864823fad91877e6caea0baca82e49e8db50f8e5c9f9a453e27d3330fc" +dependencies = [ + "jsonptr 0.4.7", + "serde", + "serde_json", + "thiserror 1.0.69", +] + [[package]] name = "json-patch" version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "863726d7afb6bc2590eeff7135d923545e5e964f004c2ccf8716c25e70a86f08" dependencies = [ - "jsonptr", + "jsonptr 0.6.3", + "serde", + "serde_json", + "thiserror 1.0.69", +] + +[[package]] +name = "jsonptr" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c6e529149475ca0b2820835d3dce8fcc41c6b943ca608d32f35b449255e4627" +dependencies = [ + "fluent-uri", "serde", "serde_json", - "thiserror 1.0.64", ] [[package]] @@ -3443,18 +3376,6 @@ dependencies = [ "unicode-segmentation", ] -[[package]] -name = "kuchiki" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ea8e9c6e031377cff82ee3001dc8026cdf431ed4e2e6b51f98ab8c73484a358" -dependencies = [ - "cssparser", - "html5ever 0.25.2", - "matches", - "selectors", -] - [[package]] name = "kuchikiki" version = "0.8.2" @@ -3462,7 +3383,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f29e4755b7b995046f510a7520c42b2fed58b77bd94d5a87a8eb43d2fd126da8" dependencies = [ "cssparser", - "html5ever 0.26.0", + "html5ever", "indexmap 1.9.3", "matches", "selectors", @@ -3470,9 +3391,9 @@ dependencies = [ [[package]] name = "lazy_static" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libappindicator" @@ -3500,9 +3421,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.169" +version = "0.2.171" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" +checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" [[package]] name = "libloading" @@ -3522,16 +3443,7 @@ checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ "bitflags 2.9.0", "libc", - "redox_syscall 0.5.10", -] - -[[package]] -name = "line-wrap" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f30344350a2a51da54c1d53be93fade8a237e545dbcc4bdbe635413f2117cab9" -dependencies = [ - "safemem", + "redox_syscall", ] [[package]] @@ -3554,15 +3466,15 @@ checksum = "fe7db12097d22ec582439daf8618b8fdd1a7bef6270e9af3b1ebcd30893cf413" [[package]] name = "litemap" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" +checksum = "23fb14cb19457329c82206317a5663005a4d404783dc74f4252769b0d5f42856" [[package]] name = "lock_api" -version = "0.4.10" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" dependencies = [ "autocfg", "scopeguard", @@ -3570,9 +3482,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.21" +version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" dependencies = [ "serde", ] @@ -3592,20 +3504,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" -[[package]] -name = "markup5ever" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a24f40fb03852d1cdd84330cddcaf98e9ec08a7b7768e952fad3b4cf048ec8fd" -dependencies = [ - "log", - "phf 0.8.0", - "phf_codegen 0.8.0", - "string_cache", - "string_cache_codegen", - "tendril", -] - [[package]] name = "markup5ever" version = "0.11.0" @@ -3620,12 +3518,6 @@ dependencies = [ "tendril", ] -[[package]] -name = "match_cfg" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" - [[package]] name = "matches" version = "0.1.10" @@ -3640,9 +3532,9 @@ checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "memoffset" -version = "0.9.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" dependencies = [ "autocfg", ] @@ -3667,18 +3559,9 @@ checksum = "6367d84fb54d4242af283086402907277715b8fe46976963af5ebf173f8efba3" [[package]] name = "miniz_oxide" -version = "0.7.1" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" -dependencies = [ - "adler", -] - -[[package]] -name = "miniz_oxide" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e3e04debbb59698c15bacbb6d93584a8c0ca9cc3213cb423d31f760d8843ce5" +checksum = "ff70ce3e48ae43fa075863cef62e8b43b71a4f2382229920e0df362592919430" dependencies = [ "adler2", "simd-adler32", @@ -3686,11 +3569,10 @@ dependencies = [ [[package]] name = "mio" -version = "1.0.1" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4569e456d394deccd22ce1c1913e6ea0e54519f577285001215d33557431afe4" +checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" dependencies = [ - "hermit-abi 0.3.9", "libc", "wasi 0.11.0+wasi-snapshot-preview1", "windows-sys 0.52.0", @@ -3707,23 +3589,22 @@ dependencies = [ "gtk", "keyboard-types", "objc2 0.6.0", - "objc2-app-kit 0.3.0", + "objc2-app-kit", "objc2-core-foundation", "objc2-foundation 0.3.0", "once_cell", "png", "serde", - "thiserror 2.0.11", + "thiserror 2.0.12", "windows-sys 0.59.0", ] [[package]] name = "native-tls" -version = "0.2.11" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" +checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e" dependencies = [ - "lazy_static", "libc", "log", "openssl", @@ -3747,7 +3628,7 @@ dependencies = [ "ndk-sys", "num_enum", "raw-window-handle", - "thiserror 1.0.64", + "thiserror 1.0.69", ] [[package]] @@ -3767,21 +3648,9 @@ dependencies = [ [[package]] name = "new_debug_unreachable" -version = "1.0.4" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54" - -[[package]] -name = "nix" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4" -dependencies = [ - "bitflags 2.9.0", - "cfg-if", - "cfg_aliases 0.1.1", - "libc", -] +checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" [[package]] name = "nodrop" @@ -3848,17 +3717,17 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" dependencies = [ - "proc-macro-crate 2.0.0", + "proc-macro-crate 3.3.0", "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.100", ] [[package]] name = "num_threads" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44" +checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" dependencies = [ "libc", ] @@ -3891,7 +3760,7 @@ dependencies = [ "sha2 0.10.8", "tendermint", "tendermint-rpc", - "thiserror 2.0.11", + "thiserror 2.0.12", "time", "utoipa", ] @@ -3940,7 +3809,7 @@ dependencies = [ "serde", "sha2 0.10.8", "subtle", - "thiserror 2.0.11", + "thiserror 2.0.12", "zeroize", ] @@ -3953,7 +3822,7 @@ dependencies = [ "log", "nym-network-defaults", "serde", - "thiserror 2.0.11", + "thiserror 2.0.12", "toml 0.8.20", "url", ] @@ -3968,7 +3837,7 @@ dependencies = [ "cw-storage-plus", "schemars", "serde", - "thiserror 2.0.11", + "thiserror 2.0.12", "vergen", ] @@ -3983,7 +3852,7 @@ dependencies = [ "rand 0.8.5", "serde", "strum 0.26.3", - "thiserror 2.0.11", + "thiserror 2.0.12", "time", "utoipa", ] @@ -4000,7 +3869,7 @@ dependencies = [ "serde", "serde_bytes", "subtle-encoding", - "thiserror 2.0.11", + "thiserror 2.0.12", "x25519-dalek", "zeroize", ] @@ -4015,7 +3884,7 @@ dependencies = [ "cw-controllers", "cw-utils", "nym-multisig-contract-common", - "thiserror 2.0.11", + "thiserror 2.0.12", ] [[package]] @@ -4031,7 +3900,7 @@ version = "0.1.0" dependencies = [ "serde", "serde_json", - "thiserror 2.0.11", + "thiserror 2.0.12", "tracing", "utoipa", ] @@ -4055,14 +3924,14 @@ dependencies = [ "bytes", "encoding_rs", "hickory-resolver", - "http 1.1.0", + "http 1.3.1", "mime", "nym-bin-common", "once_cell", - "reqwest 0.12.4", + "reqwest 0.12.15", "serde", "serde_json", - "thiserror 2.0.11", + "thiserror 2.0.12", "tracing", "url", "wasmtimer", @@ -4084,7 +3953,7 @@ dependencies = [ "serde", "serde-json-wasm", "serde_repr", - "thiserror 2.0.11", + "thiserror 2.0.12", "time", "ts-rs", "utoipa", @@ -4102,7 +3971,7 @@ dependencies = [ "cw4", "schemars", "serde", - "thiserror 2.0.11", + "thiserror 2.0.12", ] [[package]] @@ -4136,7 +4005,7 @@ dependencies = [ "serde", "serde_json", "strum 0.26.3", - "thiserror 2.0.11", + "thiserror 2.0.12", "time", "utoipa", ] @@ -4164,7 +4033,7 @@ dependencies = [ name = "nym-sphinx-types" version = "0.2.0" dependencies = [ - "thiserror 2.0.11", + "thiserror 2.0.12", ] [[package]] @@ -4174,11 +4043,11 @@ dependencies = [ "aes-gcm", "argon2", "generic-array", - "getrandom 0.2.10", + "getrandom 0.2.15", "rand 0.8.5", "serde", "serde_json", - "thiserror 2.0.11", + "thiserror 2.0.12", "zeroize", ] @@ -4212,13 +4081,13 @@ dependencies = [ "nym-mixnet-contract-common", "nym-validator-client", "nym-vesting-contract-common", - "reqwest 0.12.4", + "reqwest 0.12.15", "schemars", "serde", "serde_json", "sha2 0.10.8", "strum 0.26.3", - "thiserror 2.0.11", + "thiserror 2.0.12", "ts-rs", "url", "utoipa", @@ -4259,12 +4128,12 @@ dependencies = [ "nym-serde-helpers", "nym-vesting-contract-common", "prost", - "reqwest 0.12.4", + "reqwest 0.12.15", "serde", "serde_json", "sha2 0.10.8", "tendermint-rpc", - "thiserror 2.0.11", + "thiserror 2.0.12", "time", "tokio", "tracing", @@ -4282,7 +4151,7 @@ dependencies = [ "nym-contracts-common", "nym-mixnet-contract-common", "serde", - "thiserror 2.0.11", + "thiserror 2.0.12", ] [[package]] @@ -4328,13 +4197,13 @@ dependencies = [ "nym-config", "nym-network-defaults", "serde", - "thiserror 2.0.11", + "thiserror 2.0.12", "x25519-dalek", ] [[package]] name = "nym_wallet" -version = "1.2.15" +version = "1.2.16" dependencies = [ "async-trait", "base64 0.13.1", @@ -4362,9 +4231,10 @@ dependencies = [ "nym-vesting-contract-common", "nym-wallet-types", "once_cell", + "open 3.2.0", "pretty_env_logger", "rand_chacha 0.3.1", - "reqwest 0.12.4", + "reqwest 0.12.15", "serde", "serde_json", "serde_repr", @@ -4379,7 +4249,7 @@ dependencies = [ "tauri-plugin-shell", "tauri-plugin-updater", "tempfile", - "thiserror 1.0.64", + "thiserror 1.0.69", "time", "tokio", "toml 0.5.11", @@ -4414,22 +4284,6 @@ dependencies = [ "objc2-exception-helper", ] -[[package]] -name = "objc2-app-kit" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff" -dependencies = [ - "bitflags 2.9.0", - "block2 0.5.1", - "libc", - "objc2 0.5.2", - "objc2-core-data 0.2.2", - "objc2-core-image 0.2.2", - "objc2-foundation 0.2.2", - "objc2-quartz-core 0.2.2", -] - [[package]] name = "objc2-app-kit" version = "0.3.0" @@ -4441,10 +4295,10 @@ dependencies = [ "libc", "objc2 0.6.0", "objc2-cloud-kit", - "objc2-core-data 0.3.0", + "objc2-core-data", "objc2-core-foundation", "objc2-core-graphics", - "objc2-core-image 0.3.0", + "objc2-core-image", "objc2-foundation 0.3.0", "objc2-quartz-core 0.3.0", ] @@ -4460,18 +4314,6 @@ dependencies = [ "objc2-foundation 0.3.0", ] -[[package]] -name = "objc2-core-data" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef" -dependencies = [ - "bitflags 2.9.0", - "block2 0.5.1", - "objc2 0.5.2", - "objc2-foundation 0.2.2", -] - [[package]] name = "objc2-core-data" version = "0.3.0" @@ -4505,18 +4347,6 @@ dependencies = [ "objc2-io-surface", ] -[[package]] -name = "objc2-core-image" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55260963a527c99f1819c4f8e3b47fe04f9650694ef348ffd2227e8196d34c80" -dependencies = [ - "block2 0.5.1", - "objc2 0.5.2", - "objc2-foundation 0.2.2", - "objc2-metal", -] - [[package]] name = "objc2-core-image" version = "0.3.0" @@ -4598,7 +4428,7 @@ checksum = "a1ac59da3ceebc4a82179b35dc550431ad9458f9cc326e053f49ba371ce76c5a" dependencies = [ "bitflags 2.9.0", "objc2 0.6.0", - "objc2-app-kit 0.3.0", + "objc2-app-kit", "objc2-foundation 0.3.0", ] @@ -4647,31 +4477,41 @@ dependencies = [ "bitflags 2.9.0", "block2 0.6.0", "objc2 0.6.0", - "objc2-app-kit 0.3.0", + "objc2-app-kit", "objc2-core-foundation", "objc2-foundation 0.3.0", ] [[package]] name = "object" -version = "0.31.1" +version = "0.36.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bda667d9f2b5051b8833f59f3bf748b28ef54f850f4fcb389a252aa383866d1" +checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" dependencies = [ "memchr", ] [[package]] name = "once_cell" -version = "1.21.1" +version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d75b0bedcc4fe52caa0e03d9f1151a323e4aa5e2d78ba3580400cd3c9e2bc4bc" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] name = "opaque-debug" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" + +[[package]] +name = "open" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2078c0039e6a54a0c42c28faa984e115fb4c2d5bf2208f77d1961002df8576f8" +dependencies = [ + "pathdiff", + "windows-sys 0.42.0", +] [[package]] name = "open" @@ -4687,9 +4527,9 @@ dependencies = [ [[package]] name = "openssl" -version = "0.10.70" +version = "0.10.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61cfb4e166a8bb8c9b55c500bc2308550148ece889be90f609377e58140f42c6" +checksum = "5e14130c6a98cd258fdcb0fb6d744152343ff729cbfcb28c656a9d12b999fbcd" dependencies = [ "bitflags 2.9.0", "cfg-if", @@ -4708,20 +4548,20 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.100", ] [[package]] name = "openssl-probe" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" +checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" [[package]] name = "openssl-sys" -version = "0.9.105" +version = "0.9.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b22d5b84be05a8d6947c7cb71f7c849aa0f112acd4bf51c2a7c1c988ac0a9dc" +checksum = "8bb61ea9811cc39e3c2069f40b8b8e2e70d8569b361f879786cc7ed48b777cdd" dependencies = [ "cc", "libc", @@ -4756,7 +4596,7 @@ dependencies = [ "objc2-osa-kit", "serde", "serde_json", - "thiserror 2.0.11", + "thiserror 2.0.12", ] [[package]] @@ -4807,9 +4647,9 @@ dependencies = [ [[package]] name = "parking_lot" -version = "0.12.1" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" dependencies = [ "lock_api", "parking_lot_core", @@ -4817,15 +4657,15 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.8" +version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.3.5", + "redox_syscall", "smallvec", - "windows-targets 0.48.1", + "windows-targets 0.52.6", ] [[package]] @@ -4841,9 +4681,9 @@ dependencies = [ [[package]] name = "paste" -version = "1.0.14" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" [[package]] name = "pathdiff" @@ -4853,9 +4693,9 @@ checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3" [[package]] name = "peg" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "295283b02df346d1ef66052a757869b2876ac29a6bb0ac3f5f7cd44aebe40e8f" +checksum = "9928cfca101b36ec5163e70049ee5368a8a1c3c6efc9ca9c5f9cc2f816152477" dependencies = [ "peg-macros", "peg-runtime", @@ -4863,9 +4703,9 @@ dependencies = [ [[package]] name = "peg-macros" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdad6a1d9cf116a059582ce415d5f5566aabcd4008646779dab7fdc2a9a9d426" +checksum = "6298ab04c202fa5b5d52ba03269fb7b74550b150323038878fe6c372d8280f71" dependencies = [ "peg-runtime", "proc-macro2", @@ -4874,9 +4714,9 @@ dependencies = [ [[package]] name = "peg-runtime" -version = "0.8.3" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3aeb8f54c078314c2065ee649a7241f46b9d8e418e1a9581ba0546657d7aa3a" +checksum = "132dca9b868d927b35b5dd728167b2dee150eb1ad686008fc71ccb298b776fca" [[package]] name = "pem" @@ -4897,19 +4737,20 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pest" -version = "2.7.2" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1acb4a4365a13f749a93f1a094a7805e5cfa0955373a9de860d962eaa3a5fe5a" +checksum = "198db74531d58c70a361c42201efde7e2591e976d518caf7662a47dc5720e7b6" dependencies = [ - "thiserror 1.0.64", + "memchr", + "thiserror 2.0.12", "ucd-trie", ] [[package]] name = "pest_derive" -version = "2.7.2" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "666d00490d4ac815001da55838c500eafb0320019bbaa44444137c48b443a853" +checksum = "d725d9cfd79e87dccc9341a2ef39d1b6f6353d68c4b33c177febbe1a402c97c5" dependencies = [ "pest", "pest_generator", @@ -4917,22 +4758,22 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.7.2" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68ca01446f50dbda87c1786af8770d535423fa8a53aec03b8f4e3d7eb10e0929" +checksum = "db7d01726be8ab66ab32f9df467ae8b1148906685bbe75c82d1e65d7f5b3f841" dependencies = [ "pest", "pest_meta", "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.100", ] [[package]] name = "pest_meta" -version = "2.7.2" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56af0a30af74d0445c0bf6d9d051c979b516a1a5af790d251daee76005420a48" +checksum = "7f9f832470494906d1fca5329f8ab5791cc60beb230c74815dff541cbd2b5ca0" dependencies = [ "once_cell", "pest", @@ -4946,7 +4787,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" dependencies = [ "fixedbitset", - "indexmap 2.5.0", + "indexmap 2.8.0", ] [[package]] @@ -4966,9 +4807,7 @@ version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259" dependencies = [ - "phf_macros 0.10.0", "phf_shared 0.10.0", - "proc-macro-hack", ] [[package]] @@ -5045,20 +4884,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "phf_macros" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58fdf3184dd560f160dd73922bea2d5cd6e8f064bf4b13110abd81b03697b4e0" -dependencies = [ - "phf_generator 0.10.0", - "phf_shared 0.10.0", - "proc-macro-hack", - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "phf_macros" version = "0.11.3" @@ -5069,7 +4894,7 @@ dependencies = [ "phf_shared 0.11.3", "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.100", ] [[package]] @@ -5078,7 +4903,7 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c00cf8b9eafe68dde5e9eaa2cef8ee84a9336a47d566ec55ca16589633b65af7" dependencies = [ - "siphasher 0.3.10", + "siphasher 0.3.11", ] [[package]] @@ -5087,7 +4912,7 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" dependencies = [ - "siphasher 0.3.10", + "siphasher 0.3.11", ] [[package]] @@ -5101,29 +4926,29 @@ dependencies = [ [[package]] name = "pin-project" -version = "1.1.3" +version = "1.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422" +checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.3" +version = "1.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" +checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.100", ] [[package]] name = "pin-project-lite" -version = "0.2.12" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12cc1b0bf1727a77a54b6654e7b5f1af8604923edc8b81885f8ec92f9e3f0a05" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" [[package]] name = "pin-utils" @@ -5143,20 +4968,19 @@ dependencies = [ [[package]] name = "pkg-config" -version = "0.3.27" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" [[package]] name = "plist" -version = "1.5.0" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdc0001cfea3db57a2e24bc0d818e9e20e554b5f97fabb9bc231dc240269ae06" +checksum = "eac26e981c03a6e53e0aee43c113e3202f5581d5360dae7bd2c70e800dd0451d" dependencies = [ - "base64 0.21.4", - "indexmap 1.9.3", - "line-wrap", - "quick-xml 0.29.0", + "base64 0.22.1", + "indexmap 2.8.0", + "quick-xml 0.32.0", "serde", "time", ] @@ -5171,14 +4995,14 @@ dependencies = [ "crc32fast", "fdeflate", "flate2", - "miniz_oxide 0.8.5", + "miniz_oxide", ] [[package]] name = "polyval" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d52cff9d1d4dee5fe6d03729099f4a310a41179e0a10dbf542039873f2e826fb" +checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" dependencies = [ "cfg-if", "cpufeatures", @@ -5194,9 +5018,12 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "ppv-lite86" -version = "0.2.17" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy 0.8.24", +] [[package]] name = "precomputed-hash" @@ -5230,7 +5057,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" dependencies = [ "once_cell", - "toml_edit 0.19.14", + "toml_edit 0.19.15", ] [[package]] @@ -5242,6 +5069,15 @@ dependencies = [ "toml_edit 0.20.7", ] +[[package]] +name = "proc-macro-crate" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35" +dependencies = [ + "toml_edit 0.22.24", +] + [[package]] name = "proc-macro-error" version = "1.0.4" @@ -5285,7 +5121,7 @@ dependencies = [ "proc-macro-error-attr2", "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.100", ] [[package]] @@ -5296,18 +5132,18 @@ checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" [[package]] name = "proc-macro2" -version = "1.0.93" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" +checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" dependencies = [ "unicode-ident", ] [[package]] name = "prost" -version = "0.13.4" +version = "0.13.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c0fef6c4230e4ccf618a35c59d7ede15dea37de8427500f50aff708806e42ec" +checksum = "2796faa41db3ec313a31f7624d9286acf277b52de526150b7e69f3debf891ee5" dependencies = [ "bytes", "prost-derive", @@ -5315,15 +5151,15 @@ dependencies = [ [[package]] name = "prost-derive" -version = "0.13.4" +version = "0.13.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "157c5a9d7ea5c2ed2d9fb8f495b64759f7816c7eaea54ba3978f0d63000162e3" +checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d" dependencies = [ "anyhow", - "itertools 0.13.0", + "itertools 0.14.0", "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.100", ] [[package]] @@ -5340,9 +5176,9 @@ checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" [[package]] name = "quick-xml" -version = "0.29.0" +version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81b9228215d82c7b61490fec1de287136b5de6f5700f6e58ea9ad61a7964ca51" +checksum = "1d3a6e5838b60e0e8fa7a43f22ade549a37d61f8bdbe636d0d7816191de969c2" dependencies = [ "memchr", ] @@ -5357,10 +5193,64 @@ dependencies = [ ] [[package]] -name = "quote" -version = "1.0.35" +name = "quinn" +version = "0.11.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +checksum = "c3bd15a6f2967aef83887dcb9fec0014580467e33720d073560cf015a5683012" +dependencies = [ + "bytes", + "cfg_aliases", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash", + "rustls 0.23.25", + "socket2", + "thiserror 2.0.12", + "tokio", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-proto" +version = "0.11.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b820744eb4dc9b57a3398183639c511b5a26d2ed702cedd3febaa1393caa22cc" +dependencies = [ + "bytes", + "getrandom 0.3.2", + "rand 0.9.0", + "ring", + "rustc-hash", + "rustls 0.23.25", + "rustls-pki-types", + "slab", + "thiserror 2.0.12", + "tinyvec", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-udp" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "541d0f57c6ec747a90738a52741d3221f7960e8ac2f0ff4b1a63680e033b4ab5" +dependencies = [ + "cfg_aliases", + "libc", + "once_cell", + "socket2", + "tracing", + "windows-sys 0.59.0", +] + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" dependencies = [ "proc-macro2", ] @@ -5396,6 +5286,17 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "rand" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.3", + "zerocopy 0.8.24", +] + [[package]] name = "rand_chacha" version = "0.2.2" @@ -5416,6 +5317,16 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.3", +] + [[package]] name = "rand_core" version = "0.5.1" @@ -5431,7 +5342,16 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.10", + "getrandom 0.2.15", +] + +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +dependencies = [ + "getrandom 0.3.2", ] [[package]] @@ -5478,24 +5398,6 @@ dependencies = [ "crossbeam-utils", ] -[[package]] -name = "redox_syscall" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" -dependencies = [ - "bitflags 1.3.2", -] - -[[package]] -name = "redox_syscall" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" -dependencies = [ - "bitflags 1.3.2", -] - [[package]] name = "redox_syscall" version = "0.5.10" @@ -5507,13 +5409,13 @@ dependencies = [ [[package]] name = "redox_users" -version = "0.4.3" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" +checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" dependencies = [ - "getrandom 0.2.10", - "redox_syscall 0.2.16", - "thiserror 1.0.64", + "getrandom 0.2.15", + "libredox", + "thiserror 1.0.69", ] [[package]] @@ -5522,9 +5424,9 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd6f9d3d47bdd2ad6945c5015a226ec6155d0bcdfd8f7cd29f86b71f8de99d2b" dependencies = [ - "getrandom 0.2.10", + "getrandom 0.2.15", "libredox", - "thiserror 2.0.11", + "thiserror 2.0.12", ] [[package]] @@ -5558,19 +5460,19 @@ checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "reqwest" -version = "0.11.22" +version = "0.11.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "046cd98826c46c2ac8ddecae268eb5c2e58628688a5fc7a2643704a73faba95b" +checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" dependencies = [ - "base64 0.21.4", + "base64 0.21.7", "bytes", "encoding_rs", "futures-core", "futures-util", - "h2 0.3.20", - "http 0.2.9", - "http-body 0.4.5", - "hyper 0.14.27", + "h2 0.3.26", + "http 0.2.12", + "http-body 0.4.6", + "hyper 0.14.32", "hyper-rustls 0.24.2", "ipnet", "js-sys", @@ -5579,13 +5481,14 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", - "rustls 0.21.9", + "rustls 0.21.12", "rustls-native-certs", "rustls-pemfile 1.0.4", "serde", "serde_json", "serde_urlencoded", - "system-configuration", + "sync_wrapper 0.1.2", + "system-configuration 0.5.1", "tokio", "tokio-rustls 0.24.1", "tower-service", @@ -5598,9 +5501,9 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.12.4" +version = "0.12.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "566cafdd92868e0939d3fb961bd0dc25fcfaaed179291093b3d43e6b3150ea10" +checksum = "d19c46a6fdd48bc4dab94b6103fccc55d34c67cc0ad04653aad4ea2a07cd7bbb" dependencies = [ "async-compression", "base64 0.22.1", @@ -5608,12 +5511,12 @@ dependencies = [ "encoding_rs", "futures-core", "futures-util", - "h2 0.4.5", - "http 1.1.0", - "http-body 1.0.0", + "h2 0.4.8", + "http 1.3.1", + "http-body 1.0.1", "http-body-util", - "hyper 1.3.1", - "hyper-rustls 0.26.0", + "hyper 1.6.0", + "hyper-rustls 0.27.5", "hyper-tls", "hyper-util", "ipnet", @@ -5624,36 +5527,37 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", - "rustls 0.22.4", - "rustls-pemfile 2.1.2", + "quinn", + "rustls 0.23.25", + "rustls-pemfile 2.2.0", "rustls-pki-types", "serde", "serde_json", "serde_urlencoded", - "sync_wrapper", - "system-configuration", + "sync_wrapper 1.0.2", + "system-configuration 0.6.1", "tokio", "tokio-native-tls", - "tokio-rustls 0.25.0", + "tokio-rustls 0.26.2", "tokio-util", + "tower", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", "wasm-streams", "web-sys", - "webpki-roots 0.26.1", - "winreg 0.52.0", + "webpki-roots 0.26.8", + "windows-registry", ] [[package]] name = "resolv-conf" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52e44394d2086d010551b14b53b1f24e31647570cd1deb0379e2c21b329aba00" +checksum = "48375394603e3dd4b2d64371f7148fd8c7baa2680e28741f2cb8d23b59e3d4c4" dependencies = [ "hostname", - "quick-error 1.2.3", ] [[package]] @@ -5668,13 +5572,13 @@ dependencies = [ [[package]] name = "ring" -version = "0.17.13" +version = "0.17.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ac5d832aa16abd7d1def883a8545280c20a60f523a370aa3a9617c2b8550ee" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" dependencies = [ "cc", "cfg-if", - "getrandom 0.2.10", + "getrandom 0.2.15", "libc", "untrusted", "windows-sys 0.52.0", @@ -5722,15 +5626,21 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + +[[package]] +name = "rustc-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" [[package]] name = "rustc_version" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" dependencies = [ "semver", ] @@ -5750,9 +5660,9 @@ dependencies = [ [[package]] name = "rustix" -version = "1.0.4" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a04a32fb43fcdef85b977ce7f77a150805e4b2ea1f2656898d4a547dde78df6" +checksum = "d97817398dd4bb2e6da002002db259209759911da105da92bec29ccb12cf58bf" dependencies = [ "bitflags 2.9.0", "errno", @@ -5763,9 +5673,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.21.9" +version = "0.21.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "629648aced5775d558af50b2b4c7b02983a04b312126d45eeead26e7caa498b9" +checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" dependencies = [ "log", "ring", @@ -5775,14 +5685,14 @@ dependencies = [ [[package]] name = "rustls" -version = "0.22.4" +version = "0.23.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf4ef73721ac7bcd79b2b315da7779d8fc09718c6b3d2d1b2d94850eb8c18432" +checksum = "822ee9188ac4ec04a2f0531e55d035fb2de73f18b41a63c70c2712503b6fb13c" dependencies = [ - "log", + "once_cell", "ring", "rustls-pki-types", - "rustls-webpki 0.102.4", + "rustls-webpki 0.103.1", "subtle", "zeroize", ] @@ -5805,24 +5715,26 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" dependencies = [ - "base64 0.21.4", + "base64 0.21.7", ] [[package]] name = "rustls-pemfile" -version = "2.1.2" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29993a25686778eb88d4189742cd713c9bce943bc54251a33509dc63cbacf73d" +checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" dependencies = [ - "base64 0.22.1", "rustls-pki-types", ] [[package]] name = "rustls-pki-types" -version = "1.7.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d" +checksum = "917ce264624a4b4db1c364dcc35bfca9ded014d0a958cd47ad3e960e988ea51c" +dependencies = [ + "web-time", +] [[package]] name = "rustls-webpki" @@ -5836,9 +5748,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.102.4" +version = "0.103.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff448f7e92e913c4b7d4c6d8e4540a1724b319b4152b8aef6d4cf8339712b33e" +checksum = "fef8b8769aaccf73098557a87cd1816b4f9c7c16811c9c77142aa695c16f2c03" dependencies = [ "ring", "rustls-pki-types", @@ -5847,21 +5759,15 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.14" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" +checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" [[package]] name = "ryu" -version = "1.0.15" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" - -[[package]] -name = "safemem" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" [[package]] name = "same-file" @@ -5874,11 +5780,11 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.22" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" +checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.59.0", ] [[package]] @@ -5893,7 +5799,7 @@ dependencies = [ "serde", "serde_json", "url", - "uuid 1.10.0", + "uuid", ] [[package]] @@ -5905,7 +5811,7 @@ dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 2.0.96", + "syn 2.0.100", ] [[package]] @@ -5959,12 +5865,12 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.9.2" +version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 1.3.2", - "core-foundation 0.9.3", + "bitflags 2.9.0", + "core-foundation 0.9.4", "core-foundation-sys", "libc", "security-framework-sys", @@ -5972,9 +5878,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.9.1" +version = "2.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" +checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" dependencies = [ "core-foundation-sys", "libc", @@ -5988,7 +5894,7 @@ checksum = "df320f1889ac4ba6bc0cdc9c9af7af4bd64bb927bccdf32d81140dc1f9be12fe" dependencies = [ "bitflags 1.3.2", "cssparser", - "derive_more 0.99.17", + "derive_more 0.99.19", "fxhash", "log", "matches", @@ -6055,7 +5961,7 @@ checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.100", ] [[package]] @@ -6066,7 +5972,7 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.100", ] [[package]] @@ -6075,7 +5981,7 @@ version = "1.0.140" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" dependencies = [ - "itoa 1.0.9", + "itoa 1.0.15", "memchr", "ryu", "serde", @@ -6083,13 +5989,13 @@ dependencies = [ [[package]] name = "serde_repr" -version = "0.1.16" +version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8725e1dfadb3a50f7e5ce0b1a540466f6ed3fe7a0fca2ac2b8b831d31316bd00" +checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.100", ] [[package]] @@ -6108,21 +6014,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" dependencies = [ "form_urlencoded", - "itoa 1.0.9", + "itoa 1.0.15", "ryu", "serde", ] -[[package]] -name = "serde_with" -version = "1.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "678b5a069e50bf00ecd22d0cd8ddf7c236f68581b03db652061ed5eb13a312ff" -dependencies = [ - "serde", - "serde_with_macros 1.5.2", -] - [[package]] name = "serde_with" version = "3.12.0" @@ -6133,36 +6029,24 @@ dependencies = [ "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.5.0", + "indexmap 2.8.0", "serde", "serde_derive", "serde_json", - "serde_with_macros 3.12.0", + "serde_with_macros", "time", ] -[[package]] -name = "serde_with_macros" -version = "1.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e182d6ec6f05393cc0e5ed1bf81ad6db3a8feedf8ee515ecdd369809bcce8082" -dependencies = [ - "darling 0.13.4", - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "serde_with_macros" version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d00caa5193a3c8362ac2b73be6b9e768aa5a4b2f721d8f4b339600c3cb51f8e" dependencies = [ - "darling 0.20.11", + "darling", "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.100", ] [[package]] @@ -6259,18 +6143,18 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "signal-hook-registry" -version = "1.4.1" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" dependencies = [ "libc", ] [[package]] name = "signature" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e1788eed21689f9cf370582dfc467ef36ed9c707f073528ddafa8d83e3b8500" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" dependencies = [ "digest 0.10.7", "rand_core 0.6.4", @@ -6284,9 +6168,9 @@ checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" [[package]] name = "siphasher" -version = "0.3.10" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de" +checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" [[package]] name = "siphasher" @@ -6296,37 +6180,27 @@ checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" [[package]] name = "slab" -version = "0.4.8" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" dependencies = [ "autocfg", ] [[package]] name = "smallvec" -version = "1.13.2" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" +checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd" [[package]] name = "socket2" -version = "0.4.9" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" +checksum = "4f5fd57c80058a56cf5c777ab8a126398ece8e442983605d280a44ce79d0edef" dependencies = [ "libc", - "winapi", -] - -[[package]] -name = "socket2" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" -dependencies = [ - "libc", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -6336,8 +6210,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "18051cdd562e792cad055119e0cdb2cfc137e44e3987532e0f9659a77931bb08" dependencies = [ "bytemuck", - "cfg_aliases 0.2.1", - "core-graphics 0.24.0", + "cfg_aliases", + "core-graphics", "foreign-types 0.5.0", "js-sys", "log", @@ -6345,7 +6219,7 @@ dependencies = [ "objc2-foundation 0.2.2", "objc2-quartz-core 0.2.2", "raw-window-handle", - "redox_syscall 0.5.10", + "redox_syscall", "wasm-bindgen", "web-sys", "windows-sys 0.59.0", @@ -6379,9 +6253,9 @@ dependencies = [ [[package]] name = "spki" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d1e996ef02c474957d681f1b05213dfb0abab947b446a62d37770b23500184a" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" dependencies = [ "base64ct", "der", @@ -6401,36 +6275,29 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "string_cache" -version = "0.8.7" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f91138e76242f575eb1d3b38b4f1362f10d3a43f47d182a5b359af488a02293b" +checksum = "bf776ba3fa74f83bf4b63c3dcbbf82173db2632ed8452cb2d891d33f459de70f" dependencies = [ "new_debug_unreachable", - "once_cell", "parking_lot", - "phf_shared 0.10.0", + "phf_shared 0.11.3", "precomputed-hash", "serde", ] [[package]] name = "string_cache_codegen" -version = "0.5.2" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bb30289b722be4ff74a408c3cc27edeaad656e06cb1fe8fa9231fa59c728988" +checksum = "c711928715f1fe0fe509c53b43e993a9a557babc2d0a3567d0a3006f1ac931a0" dependencies = [ - "phf_generator 0.10.0", - "phf_shared 0.10.0", + "phf_generator 0.11.3", + "phf_shared 0.11.3", "proc-macro2", "quote", ] -[[package]] -name = "strsim" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" - [[package]] name = "strsim" version = "0.11.1" @@ -6478,14 +6345,14 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.96", + "syn 2.0.100", ] [[package]] name = "subtle" -version = "2.5.0" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "subtle-encoding" @@ -6508,7 +6375,7 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4057c98e2e852d51fdcfca832aac7b571f6b351ad159f9eda5db1655f8d0c4d7" dependencies = [ - "base64 0.21.4", + "base64 0.21.7", "serde", "serde_json", ] @@ -6526,9 +6393,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.96" +version = "2.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80" +checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" dependencies = [ "proc-macro2", "quote", @@ -6541,6 +6408,15 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +dependencies = [ + "futures-core", +] + [[package]] name = "synstructure" version = "0.13.1" @@ -6549,7 +6425,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.100", ] [[package]] @@ -6559,8 +6435,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" dependencies = [ "bitflags 1.3.2", - "core-foundation 0.9.3", - "system-configuration-sys", + "core-foundation 0.9.4", + "system-configuration-sys 0.5.0", +] + +[[package]] +name = "system-configuration" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" +dependencies = [ + "bitflags 2.9.0", + "core-foundation 0.9.4", + "system-configuration-sys 0.6.0", ] [[package]] @@ -6574,15 +6461,25 @@ dependencies = [ ] [[package]] -name = "system-deps" -version = "6.1.1" +name = "system-configuration-sys" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30c2de8a4d8f4b823d634affc9cd2a74ec98c53a756f317e529a48046cbf71f3" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "system-deps" +version = "6.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349" dependencies = [ "cfg-expr", - "heck 0.4.1", + "heck 0.5.0", "pkg-config", - "toml 0.7.6", + "toml 0.8.20", "version-compare", ] @@ -6594,7 +6491,7 @@ checksum = "63c8b1020610b9138dd7b1e06cf259ae91aa05c30f3bd0d6b42a03997b92dec1" dependencies = [ "bitflags 2.9.0", "core-foundation 0.10.0", - "core-graphics 0.24.0", + "core-graphics", "crossbeam-channel", "dispatch", "dlopen2", @@ -6610,7 +6507,7 @@ dependencies = [ "ndk-context", "ndk-sys", "objc2 0.6.0", - "objc2-app-kit 0.3.0", + "objc2-app-kit", "objc2-foundation 0.3.0", "once_cell", "parking_lot", @@ -6633,7 +6530,7 @@ checksum = "f4e16beb8b2ac17db28eab8bca40e62dbfbb34c0fcdc6d9826b11b7b5d047dfd" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.100", ] [[package]] @@ -6655,15 +6552,15 @@ dependencies = [ [[package]] name = "target-lexicon" -version = "0.12.11" +version = "0.12.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d0e916b1148c8e263850e1ebcbd046f333e0683c724876bb0da63ea4373dc8a" +checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" [[package]] name = "tauri" -version = "2.4.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "511dd38065a5d3b36c33cdba4362b99a40a5103bebcd4aebb930717e7c8ba292" +checksum = "4d08db1ff9e011e04014e737ec022610d756c0eae0b3b3a9037bccaf3003173a" dependencies = [ "anyhow", "bytes", @@ -6671,34 +6568,34 @@ dependencies = [ "dunce", "embed_plist", "futures-util", - "getrandom 0.2.10", + "getrandom 0.2.15", "glob", "gtk", "heck 0.5.0", - "http 1.1.0", + "http 1.3.1", "jni", "libc", "log", "mime", "muda", "objc2 0.6.0", - "objc2-app-kit 0.3.0", + "objc2-app-kit", "objc2-foundation 0.3.0", "percent-encoding", "plist", "raw-window-handle", - "reqwest 0.12.4", + "reqwest 0.12.15", "serde", "serde_json", "serde_repr", "serialize-to-javascript", "swift-rs", "tauri-build", - "tauri-macros 2.1.0", + "tauri-macros 2.1.1", "tauri-runtime", "tauri-runtime-wry", - "tauri-utils 2.3.0", - "thiserror 2.0.11", + "tauri-utils 2.3.1", + "thiserror 2.0.12", "tokio", "tray-icon", "url", @@ -6711,9 +6608,9 @@ dependencies = [ [[package]] name = "tauri-build" -version = "2.1.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ffa8732a66f90903f5a585215f3cf1e87988d0359bc88c18a502efe7572c1de" +checksum = "0fd20e4661c2cce65343319e6e8da256958f5af958cafc47c0d0af66a55dcd17" dependencies = [ "anyhow", "cargo_toml", @@ -6725,7 +6622,7 @@ dependencies = [ "semver", "serde", "serde_json", - "tauri-utils 2.3.0", + "tauri-utils 2.3.1", "tauri-winres", "toml 0.8.20", "walkdir", @@ -6738,7 +6635,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14388d484b6b1b5dc0f6a7d6cc6433b3b230bec85eaa576adcdf3f9fafa49251" dependencies = [ "base64 0.13.1", - "brotli 3.3.4", + "brotli 3.5.0", "ico 0.2.0", "json-patch 0.2.7", "plist", @@ -6749,18 +6646,18 @@ dependencies = [ "serde", "serde_json", "sha2 0.10.8", - "tauri-utils 1.2.1", - "thiserror 1.0.64", + "tauri-utils 1.6.2", + "thiserror 1.0.69", "time", - "uuid 1.10.0", + "uuid", "walkdir", ] [[package]] name = "tauri-codegen" -version = "2.1.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c266a247f14d63f40c6282c2653a8bac5cc3d482ca562a003a88513653ea817a" +checksum = "458258b19032450ccf975840116ecf013e539eadbb74420bd890e8c56ab2b1a4" dependencies = [ "base64 0.22.1", "brotli 7.0.0", @@ -6774,12 +6671,12 @@ dependencies = [ "serde", "serde_json", "sha2 0.10.8", - "syn 2.0.96", - "tauri-utils 2.3.0", - "thiserror 2.0.11", + "syn 2.0.100", + "tauri-utils 2.3.1", + "thiserror 2.0.12", "time", "url", - "uuid 1.10.0", + "uuid", "walkdir", ] @@ -6794,28 +6691,28 @@ dependencies = [ "quote", "syn 1.0.109", "tauri-codegen 1.2.1", - "tauri-utils 1.2.1", + "tauri-utils 1.6.2", ] [[package]] name = "tauri-macros" -version = "2.1.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f47a1cf94b3bd6c4dc37dce1a43fc96120ff29a91757f0ab3cf713c7ad846e7c" +checksum = "d402813d3b9c773a0fa58697c457c771f10e735498fdcb7b343264d18e5a601f" dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.96", - "tauri-codegen 2.1.0", - "tauri-utils 2.3.0", + "syn 2.0.100", + "tauri-codegen 2.1.1", + "tauri-utils 2.3.1", ] [[package]] name = "tauri-plugin" -version = "2.1.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9972871fcbddf16618f70412d965d4d845cd4b76d03fff168709961ef71e5cdf" +checksum = "a4190775d6ff73fe66d9af44c012739a2659720efd9c0e1e56a918678038699d" dependencies = [ "anyhow", "glob", @@ -6823,7 +6720,7 @@ dependencies = [ "schemars", "serde", "serde_json", - "tauri-utils 2.3.0", + "tauri-utils 2.3.1", "toml 0.8.20", "walkdir", ] @@ -6840,14 +6737,14 @@ dependencies = [ "serde_json", "tauri", "tauri-plugin", - "thiserror 2.0.11", + "thiserror 2.0.12", ] [[package]] name = "tauri-plugin-process" -version = "2.2.0" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40cc553ab29581c8c43dfa5fb0c9d5aee8ba962ad3b42908eea26c79610441b7" +checksum = "57da5888533e802b6206b9685091f8714aa1f5266dc80051a82388449558b773" dependencies = [ "tauri", "tauri-plugin", @@ -6855,13 +6752,13 @@ dependencies = [ [[package]] name = "tauri-plugin-shell" -version = "2.2.0" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb2c50a63e60fb8925956cc5b7569f4b750ac197a4d39f13b8dd46ea8e2bad79" +checksum = "69d5eb3368b959937ad2aeaf6ef9a8f5d11e01ffe03629d3530707bbcb27ff5d" dependencies = [ "encoding_rs", "log", - "open", + "open 5.3.2", "os_pipe", "regex", "schemars", @@ -6870,27 +6767,27 @@ dependencies = [ "shared_child", "tauri", "tauri-plugin", - "thiserror 2.0.11", + "thiserror 2.0.12", "tokio", ] [[package]] name = "tauri-plugin-updater" -version = "2.6.1" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a31bfcfb4a8318008d2108ccfba439d8263cf48867baabf372cb0e9f24771896" +checksum = "d82da763248e635d60ee4aed56c862290e523acc838d83097171f9a544708387" dependencies = [ "base64 0.22.1", "dirs 6.0.0", "flate2", "futures-util", - "http 1.1.0", + "http 1.3.1", "infer 0.19.0", "log", "minisign-verify", "osakit", "percent-encoding", - "reqwest 0.12.4", + "reqwest 0.12.15", "semver", "serde", "serde_json", @@ -6898,7 +6795,7 @@ dependencies = [ "tauri", "tauri-plugin", "tempfile", - "thiserror 2.0.11", + "thiserror 2.0.12", "time", "tokio", "url", @@ -6908,36 +6805,36 @@ dependencies = [ [[package]] name = "tauri-runtime" -version = "2.5.0" +version = "2.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e9c7bce5153f1ca7bc45eba37349b31ba50e975e28edc8b5766c5ec02b0b63a" +checksum = "00ada7ac2f9276f09b8c3afffd3215fd5d9bff23c22df8a7c70e7ef67cacd532" dependencies = [ "cookie", "dpi", "gtk", - "http 1.1.0", + "http 1.3.1", "jni", "raw-window-handle", "serde", "serde_json", - "tauri-utils 2.3.0", - "thiserror 2.0.11", + "tauri-utils 2.3.1", + "thiserror 2.0.12", "url", "windows 0.60.0", ] [[package]] name = "tauri-runtime-wry" -version = "2.5.0" +version = "2.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "087188020fd6facb8578fe9b38e81fa0fe5fb85744c73da51a299f94a530a1e3" +checksum = "cf2e5842c57e154af43a20a49c7efee0ce2578c20b4c2bdf266852b422d2e421" dependencies = [ "gtk", - "http 1.1.0", + "http 1.3.1", "jni", "log", "objc2 0.6.0", - "objc2-app-kit 0.3.0", + "objc2-app-kit", "objc2-foundation 0.3.0", "once_cell", "percent-encoding", @@ -6945,7 +6842,7 @@ dependencies = [ "softbuffer", "tao", "tauri-runtime", - "tauri-utils 2.3.0", + "tauri-utils 2.3.1", "url", "webkit2gtk", "webview2-com", @@ -6955,44 +6852,46 @@ dependencies = [ [[package]] name = "tauri-utils" -version = "1.2.1" +version = "1.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5abbc109a6eb45127956ffcc26ef0e875d160150ac16cfa45d26a6b2871686f1" +checksum = "c357952645e679de02cd35007190fcbce869b93ffc61b029f33fe02648453774" dependencies = [ - "brotli 3.3.4", - "ctor 0.1.26", - "heck 0.4.1", - "html5ever 0.25.2", - "infer 0.7.0", - "json-patch 0.2.7", - "kuchiki", + "brotli 7.0.0", + "ctor", + "dunce", + "heck 0.5.0", + "html5ever", + "infer 0.13.0", + "json-patch 2.0.0", + "kuchikiki", + "log", "memchr", - "phf 0.10.1", + "phf 0.11.3", "proc-macro2", "quote", "semver", "serde", "serde_json", - "serde_with 1.14.0", - "thiserror 1.0.64", + "serde_with", + "thiserror 1.0.69", "url", - "windows 0.39.0", + "windows-version", ] [[package]] name = "tauri-utils" -version = "2.3.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82dcced4014e59af9790cc22f5d271df3be09ecd6728ec68861642553c8d01b7" +checksum = "1f037e66c7638cc0a2213f61566932b9a06882b8346486579c90e4b019bac447" dependencies = [ "anyhow", "brotli 7.0.0", "cargo_metadata 0.19.2", - "ctor 0.2.9", + "ctor", "dunce", "glob", - "html5ever 0.26.0", - "http 1.1.0", + "html5ever", + "http 1.3.1", "infer 0.19.0", "json-patch 3.0.1", "kuchikiki", @@ -7007,13 +6906,13 @@ dependencies = [ "serde", "serde-untagged", "serde_json", - "serde_with 3.12.0", + "serde_with", "swift-rs", - "thiserror 2.0.11", + "thiserror 2.0.12", "toml 0.8.20", "url", "urlpattern", - "uuid 1.10.0", + "uuid", "walkdir", ] @@ -7036,15 +6935,15 @@ dependencies = [ "fastrand", "getrandom 0.3.2", "once_cell", - "rustix 1.0.4", + "rustix 1.0.5", "windows-sys 0.59.0", ] [[package]] name = "tendermint" -version = "0.40.1" +version = "0.40.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9703e34d940c2a293804752555107f8dbe2b84ec4c6dd5203831235868105d2" +checksum = "7bed57bd840305efef8e83b752dd63dde3bb0fc1c35b6490f0a4de3a0ffd8dd9" dependencies = [ "bytes", "digest 0.10.7", @@ -7072,9 +6971,9 @@ dependencies = [ [[package]] name = "tendermint-config" -version = "0.40.1" +version = "0.40.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89cc3ea9a39b7ee34eefcff771cc067ecaa0c988c1c5ac08defd878471a06f76" +checksum = "38aeb44e9e275b3afe31a4f3302dfc5aa1e0aa488a22abb7577634b22b144a22" dependencies = [ "flex-error", "serde", @@ -7086,9 +6985,9 @@ dependencies = [ [[package]] name = "tendermint-proto" -version = "0.40.1" +version = "0.40.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ae9e1705aa0fa5ecb2c6aa7fb78c2313c4a31158ea5f02048bf318f849352eb" +checksum = "9a218320a6e92ac731ac094169cb742e3203a3ab24d30417b72cfdaab5d50c67" dependencies = [ "bytes", "flex-error", @@ -7101,19 +7000,19 @@ dependencies = [ [[package]] name = "tendermint-rpc" -version = "0.40.1" +version = "0.40.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "835a52aa504c63ec05519e31348d3f4ba2fe79493c588e2cad5323d5e81b161a" +checksum = "93ffcf862b519894f8afa79e5be73e376c3367cbd2bbe0eaf9c653ab01cd8b20" dependencies = [ "async-trait", "bytes", "flex-error", "futures", - "getrandom 0.2.10", + "getrandom 0.2.15", "peg", "pin-project", "rand 0.8.5", - "reqwest 0.11.22", + "reqwest 0.11.27", "semver", "serde", "serde_bytes", @@ -7123,12 +7022,12 @@ dependencies = [ "tendermint", "tendermint-config", "tendermint-proto", - "thiserror 1.0.64", + "thiserror 1.0.69", "time", "tokio", "tracing", "url", - "uuid 1.10.0", + "uuid", "walkdir", ] @@ -7145,9 +7044,9 @@ dependencies = [ [[package]] name = "termcolor" -version = "1.2.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" dependencies = [ "winapi-util", ] @@ -7160,42 +7059,42 @@ checksum = "8eaa81235c7058867fa8c0e7314f33dcce9c215f535d1913822a2b3f5e289f3c" [[package]] name = "thiserror" -version = "1.0.64" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ - "thiserror-impl 1.0.64", + "thiserror-impl 1.0.69", ] [[package]] name = "thiserror" -version = "2.0.11" +version = "2.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc" +checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" dependencies = [ - "thiserror-impl 2.0.11", + "thiserror-impl 2.0.12", ] [[package]] name = "thiserror-impl" -version = "1.0.64" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.100", ] [[package]] name = "thiserror-impl" -version = "2.0.11" +version = "2.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2" +checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.100", ] [[package]] @@ -7211,12 +7110,12 @@ dependencies = [ [[package]] name = "time" -version = "0.3.39" +version = "0.3.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dad298b01a40a23aac4580b67e3dbedb7cc8402f3592d7f49469de2ea4aecdd8" +checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" dependencies = [ "deranged", - "itoa 1.0.9", + "itoa 1.0.15", "libc", "num-conv", "num_threads", @@ -7228,15 +7127,15 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "765c97a5b985b7c11d7bc27fa927dc4fe6af3a6dfb021d28deb60d3bf51e76ef" +checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" [[package]] name = "time-macros" -version = "0.2.20" +version = "0.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8093bc3e81c3bc5f7879de09619d06c9a5a5e45ca44dfeeb7225bae38005c5c" +checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" dependencies = [ "num-conv", "time-core", @@ -7254,9 +7153,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.6.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +checksum = "09b3661f17e86524eccd4371ab0429194e0d7c008abb45f7a7495b1719463c71" dependencies = [ "tinyvec_macros", ] @@ -7269,9 +7168,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.44.0" +version = "1.44.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9975ea0f48b5aa3972bf2d888c238182458437cc2a19374b81b25cdf1023fb3a" +checksum = "f382da615b842244d4b8738c82ed1275e6c5dd90c459a30941cd07080b06c91a" dependencies = [ "backtrace", "bytes", @@ -7280,7 +7179,7 @@ dependencies = [ "parking_lot", "pin-project-lite", "signal-hook-registry", - "socket2 0.5.5", + "socket2", "tokio-macros", "windows-sys 0.52.0", ] @@ -7293,7 +7192,7 @@ checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.100", ] [[package]] @@ -7312,33 +7211,31 @@ version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" dependencies = [ - "rustls 0.21.9", + "rustls 0.21.12", "tokio", ] [[package]] name = "tokio-rustls" -version = "0.25.0" +version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "775e0c0f0adb3a2f22a00c4745d728b479985fc15ee7ca6a2608388c5569860f" +checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b" dependencies = [ - "rustls 0.22.4", - "rustls-pki-types", + "rustls 0.23.25", "tokio", ] [[package]] name = "tokio-util" -version = "0.7.8" +version = "0.7.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d" +checksum = "6b9590b93e6fcc1739458317cccd391ad3955e2bde8913edf6f95f9e65a8f034" dependencies = [ "bytes", "futures-core", "futures-sink", "pin-project-lite", "tokio", - "tracing", ] [[package]] @@ -7350,18 +7247,6 @@ dependencies = [ "serde", ] -[[package]] -name = "toml" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c17e963a819c331dcacd7ab957d80bc2b9a9c1e71c804826d2f283dd65306542" -dependencies = [ - "serde", - "serde_spanned", - "toml_datetime", - "toml_edit 0.19.14", -] - [[package]] name = "toml" version = "0.8.20" @@ -7371,7 +7256,7 @@ dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit 0.22.23", + "toml_edit 0.22.24", ] [[package]] @@ -7385,15 +7270,13 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.19.14" +version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8123f27e969974a3dfba720fdb560be359f57b44302d280ba72e76a74480e8a" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "indexmap 2.5.0", - "serde", - "serde_spanned", + "indexmap 2.8.0", "toml_datetime", - "winnow 0.5.10", + "winnow 0.5.40", ] [[package]] @@ -7402,51 +7285,50 @@ version = "0.20.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70f427fce4d84c72b5b732388bf4a9f4531b53f74e2887e3ecb2481f68f66d81" dependencies = [ - "indexmap 2.5.0", + "indexmap 2.8.0", "toml_datetime", - "winnow 0.5.10", + "winnow 0.5.40", ] [[package]] name = "toml_edit" -version = "0.22.23" +version = "0.22.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02a8b472d1a3d7c18e2d61a489aee3453fd9031c33e4f55bd533f4a7adca1bee" +checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474" dependencies = [ - "indexmap 2.5.0", + "indexmap 2.8.0", "serde", "serde_spanned", "toml_datetime", - "winnow 0.7.2", + "winnow 0.7.4", ] [[package]] name = "tower" -version = "0.4.13" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" dependencies = [ "futures-core", "futures-util", - "pin-project", "pin-project-lite", + "sync_wrapper 1.0.2", "tokio", "tower-layer", "tower-service", - "tracing", ] [[package]] name = "tower-layer" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" [[package]] name = "tower-service" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" @@ -7454,7 +7336,6 @@ version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" dependencies = [ - "log", "pin-project-lite", "tracing-attributes", "tracing-core", @@ -7468,7 +7349,7 @@ checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.100", ] [[package]] @@ -7491,14 +7372,14 @@ dependencies = [ "libappindicator", "muda", "objc2 0.6.0", - "objc2-app-kit 0.3.0", + "objc2-app-kit", "objc2-core-foundation", "objc2-core-graphics", "objc2-foundation 0.3.0", "once_cell", "png", "serde", - "thiserror 2.0.11", + "thiserror 2.0.12", "windows-sys 0.59.0", ] @@ -7526,9 +7407,9 @@ dependencies = [ [[package]] name = "try-lock" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "ts-rs" @@ -7537,7 +7418,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e640d9b0964e9d39df633548591090ab92f7a4567bc31d3891af23471a3365c6" dependencies = [ "lazy_static", - "thiserror 2.0.11", + "thiserror 2.0.12", "ts-rs-macros", ] @@ -7549,7 +7430,7 @@ checksum = "0e9d8656589772eeec2cf7a8264d9cda40fb28b9bc53118ceb9e8c07f8f38730" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.100", "termcolor", ] @@ -7561,15 +7442,15 @@ checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c" [[package]] name = "typenum" -version = "1.16.0" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" +checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" [[package]] name = "ucd-trie" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" +checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" [[package]] name = "unic-char-property" @@ -7612,17 +7493,11 @@ dependencies = [ "unic-common", ] -[[package]] -name = "unicode-bidi" -version = "0.3.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" - [[package]] name = "unicode-ident" -version = "1.0.11" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" [[package]] name = "unicode-normalization" @@ -7663,12 +7538,12 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.5.2" +version = "2.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" dependencies = [ "form_urlencoded", - "idna 0.5.0", + "idna", "percent-encoding", "serde", ] @@ -7705,9 +7580,9 @@ checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" [[package]] name = "utf8parse" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "utoipa" @@ -7715,7 +7590,7 @@ version = "5.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "435c6f69ef38c9017b4b4eea965dfb91e71e53d869e896db40d1cf2441dd75c0" dependencies = [ - "indexmap 2.5.0", + "indexmap 2.8.0", "serde", "serde_json", "utoipa-gen", @@ -7729,22 +7604,16 @@ checksum = "a77d306bc75294fd52f3e99b13ece67c02c1a2789190a6f31d32f736624326f7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.100", ] [[package]] name = "uuid" -version = "0.8.2" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" - -[[package]] -name = "uuid" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" +checksum = "458f7a779bf54acc9f347480ac654f68407d3aab21269a6e3c9f922acd9e2da9" dependencies = [ - "getrandom 0.2.10", + "getrandom 0.3.2", "serde", ] @@ -7771,15 +7640,15 @@ dependencies = [ [[package]] name = "version-compare" -version = "0.1.1" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "579a42fc0b8e0c63b76519a339be31bed574929511fa53c1a3acae26eb258f29" +checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b" [[package]] name = "version_check" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "vswhom" @@ -7803,9 +7672,9 @@ dependencies = [ [[package]] name = "walkdir" -version = "2.3.3" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" dependencies = [ "same-file", "winapi-util", @@ -7843,46 +7712,48 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.87" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" dependencies = [ "cfg-if", + "once_cell", + "rustversion", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.87" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" dependencies = [ "bumpalo", "log", - "once_cell", "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.100", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.37" +version = "0.4.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03" +checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" dependencies = [ "cfg-if", "js-sys", + "once_cell", "wasm-bindgen", "web-sys", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.87" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -7890,28 +7761,31 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.87" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.100", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.87" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] [[package]] name = "wasm-streams" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e072d4e72f700fb3443d8fe94a39315df013eef1104903cdb0a2abd322bbecd" +checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65" dependencies = [ "futures-util", "js-sys", @@ -7961,9 +7835,9 @@ dependencies = [ [[package]] name = "wayland-protocols" -version = "0.31.2" +version = "0.32.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f81f365b8b4a97f422ac0e8737c438024b5951734506b0e1d775c73030561f4" +checksum = "0781cf46869b37e36928f7b432273c0995aa8aed9552c556fb18754420541efc" dependencies = [ "bitflags 2.9.0", "wayland-backend", @@ -7973,9 +7847,9 @@ dependencies = [ [[package]] name = "wayland-protocols-wlr" -version = "0.2.0" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad1f61b76b6c2d8742e10f9ba5c3737f6530b4c243132c2a2ccc8aa96fe25cd6" +checksum = "248a02e6f595aad796561fa82d25601bd2c8c3b145b1c7453fc8f94c1a58f8b2" dependencies = [ "bitflags 2.9.0", "wayland-backend", @@ -8006,9 +7880,19 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.64" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" +checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" dependencies = [ "js-sys", "wasm-bindgen", @@ -8066,9 +7950,9 @@ checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" [[package]] name = "webpki-roots" -version = "0.26.1" +version = "0.26.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3de34ae270483955a94f4b21bdaaeb83d508bb84a01435f393818edb0012009" +checksum = "2210b291f7ea53617fbafcc4939f10914214ec15aace5ba62293a668f322c5c9" dependencies = [ "rustls-pki-types", ] @@ -8095,7 +7979,7 @@ checksum = "1d228f15bba3b9d56dde8bddbee66fa24545bd17b48d5128ccf4a8742b18e431" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.100", ] [[package]] @@ -8104,7 +7988,7 @@ version = "0.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfb27fccd3c27f68e9a6af1bcf48c2d82534b8675b83608a4d81446d095a17ac" dependencies = [ - "thiserror 2.0.11", + "thiserror 2.0.12", "windows 0.60.0", "windows-core 0.60.1", ] @@ -8117,9 +8001,9 @@ checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082" [[package]] name = "widestring" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7219d36b6eac893fa81e84ebe06485e7dcbb616177469b142df14f1f4deb1311" +checksum = "dd7cf3379ca1aac9eea11fba24fd7e315d621f8dfe35c8d7d2be8b793726e07d" [[package]] name = "winapi" @@ -8139,11 +8023,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.5" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "winapi", + "windows-sys 0.59.0", ] [[package]] @@ -8159,7 +8043,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9bec5a31f3f9362f2258fd0e9c9dd61a9ca432e7306cc78c444258f0dce9a9c" dependencies = [ "objc2 0.6.0", - "objc2-app-kit 0.3.0", + "objc2-app-kit", "objc2-core-foundation", "objc2-foundation 0.3.0", "raw-window-handle", @@ -8169,16 +8053,12 @@ dependencies = [ [[package]] name = "windows" -version = "0.39.0" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1c4bd0a50ac6020f65184721f758dba47bb9fbc2133df715ec74a237b26794a" +checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" dependencies = [ - "windows-implement 0.39.0", - "windows_aarch64_msvc 0.39.0", - "windows_i686_gnu 0.39.0", - "windows_i686_msvc 0.39.0", - "windows_x86_64_gnu 0.39.0", - "windows_x86_64_msvc 0.39.0", + "windows-core 0.52.0", + "windows-targets 0.52.6", ] [[package]] @@ -8222,7 +8102,20 @@ dependencies = [ "windows-interface", "windows-link", "windows-result", - "windows-strings", + "windows-strings 0.3.1", +] + +[[package]] +name = "windows-core" +version = "0.61.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980" +dependencies = [ + "windows-implement 0.60.0", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings 0.4.0", ] [[package]] @@ -8235,16 +8128,6 @@ dependencies = [ "windows-link", ] -[[package]] -name = "windows-implement" -version = "0.39.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba01f98f509cb5dc05f4e5fc95e535f78260f15fea8fe1a8abdd08f774f1cee7" -dependencies = [ - "syn 1.0.109", - "windows-tokens", -] - [[package]] name = "windows-implement" version = "0.59.0" @@ -8253,7 +8136,18 @@ checksum = "83577b051e2f49a058c308f17f273b570a6a758386fc291b5f6a934dd84e48c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.100", +] + +[[package]] +name = "windows-implement" +version = "0.60.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.100", ] [[package]] @@ -8264,7 +8158,7 @@ checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.100", ] [[package]] @@ -8283,6 +8177,17 @@ dependencies = [ "windows-link", ] +[[package]] +name = "windows-registry" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4286ad90ddb45071efd1a66dfa43eb02dd0dfbae1545ad6cc3c51cf34d7e8ba3" +dependencies = [ + "windows-result", + "windows-strings 0.3.1", + "windows-targets 0.53.0", +] + [[package]] name = "windows-result" version = "0.3.2" @@ -8301,6 +8206,30 @@ dependencies = [ "windows-link", ] +[[package]] +name = "windows-strings" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2ba9642430ee452d5a7aa78d72907ebe8cfda358e8cb7918a2050581322f97" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-sys" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + [[package]] name = "windows-sys" version = "0.45.0" @@ -8316,7 +8245,7 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets 0.48.1", + "windows-targets 0.48.5", ] [[package]] @@ -8354,17 +8283,17 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.48.1" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ - "windows_aarch64_gnullvm 0.48.0", - "windows_aarch64_msvc 0.48.0", - "windows_i686_gnu 0.48.0", - "windows_i686_msvc 0.48.0", - "windows_x86_64_gnu 0.48.0", - "windows_x86_64_gnullvm 0.48.0", - "windows_x86_64_msvc 0.48.0", + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", ] [[package]] @@ -8376,7 +8305,7 @@ dependencies = [ "windows_aarch64_gnullvm 0.52.6", "windows_aarch64_msvc 0.52.6", "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm", + "windows_i686_gnullvm 0.52.6", "windows_i686_msvc 0.52.6", "windows_x86_64_gnu 0.52.6", "windows_x86_64_gnullvm 0.52.6", @@ -8384,10 +8313,20 @@ dependencies = [ ] [[package]] -name = "windows-tokens" -version = "0.39.0" +name = "windows-targets" +version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f838de2fe15fe6bac988e74b798f26499a8b21a9d97edec321e79b28d1d7f597" +checksum = "b1e4c7e8ceaaf9cb7d7507c974735728ab453b67ef8f18febdd7c11fe59dca8b" +dependencies = [ + "windows_aarch64_gnullvm 0.53.0", + "windows_aarch64_msvc 0.53.0", + "windows_i686_gnu 0.53.0", + "windows_i686_gnullvm 0.53.0", + "windows_i686_msvc 0.53.0", + "windows_x86_64_gnu 0.53.0", + "windows_x86_64_gnullvm 0.53.0", + "windows_x86_64_msvc 0.53.0", +] [[package]] name = "windows-version" @@ -8406,9 +8345,9 @@ checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" @@ -8417,10 +8356,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] -name = "windows_aarch64_msvc" -version = "0.39.0" +name = "windows_aarch64_gnullvm" +version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec7711666096bd4096ffa835238905bb33fb87267910e154b18b44eaabb340f2" +checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" [[package]] name = "windows_aarch64_msvc" @@ -8430,9 +8369,9 @@ checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" [[package]] name = "windows_aarch64_msvc" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" @@ -8441,10 +8380,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] -name = "windows_i686_gnu" -version = "0.39.0" +name = "windows_aarch64_msvc" +version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "763fc57100a5f7042e3057e7e8d9bdd7860d330070251a73d003563a3bb49e1b" +checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" [[package]] name = "windows_i686_gnu" @@ -8454,9 +8393,9 @@ checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" [[package]] name = "windows_i686_gnu" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" @@ -8464,6 +8403,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" +[[package]] +name = "windows_i686_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" + [[package]] name = "windows_i686_gnullvm" version = "0.52.6" @@ -8471,10 +8416,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] -name = "windows_i686_msvc" -version = "0.39.0" +name = "windows_i686_gnullvm" +version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bc7cbfe58828921e10a9f446fcaaf649204dcfe6c1ddd712c5eebae6bda1106" +checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" [[package]] name = "windows_i686_msvc" @@ -8484,9 +8429,9 @@ checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" [[package]] name = "windows_i686_msvc" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" @@ -8495,10 +8440,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] -name = "windows_x86_64_gnu" -version = "0.39.0" +name = "windows_i686_msvc" +version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6868c165637d653ae1e8dc4d82c25d4f97dd6605eaa8d784b5c6e0ab2a252b65" +checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" [[package]] name = "windows_x86_64_gnu" @@ -8508,9 +8453,9 @@ checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" [[package]] name = "windows_x86_64_gnu" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" @@ -8518,6 +8463,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" + [[package]] name = "windows_x86_64_gnullvm" version = "0.42.2" @@ -8526,9 +8477,9 @@ checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" [[package]] name = "windows_x86_64_gnullvm" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" @@ -8537,10 +8488,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] -name = "windows_x86_64_msvc" -version = "0.39.0" +name = "windows_x86_64_gnullvm" +version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e4d40883ae9cae962787ca76ba76390ffa29214667a111db9e0a1ad8377e809" +checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" [[package]] name = "windows_x86_64_msvc" @@ -8550,9 +8501,9 @@ checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" [[package]] name = "windows_x86_64_msvc" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" @@ -8561,19 +8512,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] -name = "winnow" -version = "0.5.10" +name = "windows_x86_64_msvc" +version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5504cc7644f4b593cbc05c4a55bf9bd4e94b867c3c0bd440934174d50482427d" +checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" + +[[package]] +name = "winnow" +version = "0.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" dependencies = [ "memchr", ] [[package]] name = "winnow" -version = "0.7.2" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59690dea168f2198d1a3b0cac23b8063efcd11012f10ae4698f284808c8ef603" +checksum = "0e97b544156e9bebe1a0ffbc03484fc1ffe3100cbce3ffb17eac35f7cdd7ab36" dependencies = [ "memchr", ] @@ -8609,17 +8566,16 @@ dependencies = [ [[package]] name = "wl-clipboard-rs" -version = "0.8.1" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12b41773911497b18ca8553c3daaf8ec9fe9819caf93d451d3055f69de028adb" +checksum = "8e5ff8d0e60065f549fafd9d6cb626203ea64a798186c80d8e7df4f8af56baeb" dependencies = [ - "derive-new", "libc", "log", - "nix", "os_pipe", + "rustix 0.38.44", "tempfile", - "thiserror 1.0.64", + "thiserror 2.0.12", "tree_magic_mini", "wayland-backend", "wayland-client", @@ -8653,15 +8609,15 @@ dependencies = [ "dunce", "gdkx11", "gtk", - "html5ever 0.26.0", - "http 1.1.0", + "html5ever", + "http 1.3.1", "javascriptcore-rs", "jni", "kuchikiki", "libc", "ndk", "objc2 0.6.0", - "objc2-app-kit 0.3.0", + "objc2-app-kit", "objc2-core-foundation", "objc2-foundation 0.3.0", "objc2-ui-kit", @@ -8672,7 +8628,7 @@ dependencies = [ "sha2 0.10.8", "soup3", "tao-macros", - "thiserror 2.0.11", + "thiserror 2.0.12", "url", "webkit2gtk", "webkit2gtk-sys", @@ -8723,9 +8679,9 @@ checksum = "ec107c4503ea0b4a98ef47356329af139c0a4f7750e621cf2973cd3385ebcb3d" [[package]] name = "x25519-dalek" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb66477291e7e8d2b0ff1bcb900bf29489a9692816d79874bea351e7a8b6de96" +checksum = "c7e468321c81fb07fa7f4c636c3972b9100f0346e5b6a9f2bd0603a52f7ed277" dependencies = [ "curve25519-dalek", "rand_core 0.6.4", @@ -8740,7 +8696,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d65cbf2f12c15564212d48f4e3dfb87923d25d611f2aed18f4cb23f0413d89e" dependencies = [ "libc", - "rustix 1.0.4", + "rustix 1.0.5", ] [[package]] @@ -8763,7 +8719,7 @@ checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.100", "synstructure", ] @@ -8773,7 +8729,16 @@ version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ - "zerocopy-derive", + "zerocopy-derive 0.7.35", +] + +[[package]] +name = "zerocopy" +version = "0.8.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2586fea28e186957ef732a5f8b3be2da217d65c5969d4b1e17f973ebbe876879" +dependencies = [ + "zerocopy-derive 0.8.24", ] [[package]] @@ -8784,27 +8749,38 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.100", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a996a8f63c5c4448cd959ac1bab0aaa3306ccfd060472f85943ee0750f0169be" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.100", ] [[package]] name = "zerofrom" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" dependencies = [ "zerofrom-derive", ] [[package]] name = "zerofrom-derive" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.100", "synstructure", ] @@ -8826,7 +8802,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.100", ] [[package]] @@ -8848,18 +8824,18 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.100", ] [[package]] name = "zip" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27c03817464f64e23f6f37574b4fdc8cf65925b5bfd2b0f2aedf959791941f88" +checksum = "febbe83a485467affa75a75d28dc7494acd2f819e549536c47d46b3089b56164" dependencies = [ "arbitrary", "crc32fast", "crossbeam-utils", - "indexmap 2.5.0", + "indexmap 2.8.0", "memchr", ] diff --git a/nym-wallet/src-tauri/Cargo.toml b/nym-wallet/src-tauri/Cargo.toml index 6dc56f2dbe..d8101b2515 100644 --- a/nym-wallet/src-tauri/Cargo.toml +++ b/nym-wallet/src-tauri/Cargo.toml @@ -35,6 +35,7 @@ futures = "0.3.15" itertools = "0.10" log = { version = "0.4", features = ["serde"] } once_cell = "1.7.2" +open = "3.2" pretty_env_logger = "0.4" reqwest = { version = "0.12.4", features = ["json"] } serde = { version = "1.0", features = ["derive"] } diff --git a/nym-wallet/src-tauri/gen/schemas/acl-manifests.json b/nym-wallet/src-tauri/gen/schemas/acl-manifests.json new file mode 100644 index 0000000000..16217add43 --- /dev/null +++ b/nym-wallet/src-tauri/gen/schemas/acl-manifests.json @@ -0,0 +1 @@ +{"clipboard-manager":{"default_permission":{"identifier":"default","description":"No features are enabled by default, as we believe\nthe clipboard can be inherently dangerous and it is \napplication specific if read and/or write access is needed.\n\nClipboard interaction needs to be explicitly enabled.\n","permissions":[]},"permissions":{"allow-clear":{"identifier":"allow-clear","description":"Enables the clear command without any pre-configured scope.","commands":{"allow":["clear"],"deny":[]}},"allow-read-image":{"identifier":"allow-read-image","description":"Enables the read_image command without any pre-configured scope.","commands":{"allow":["read_image"],"deny":[]}},"allow-read-text":{"identifier":"allow-read-text","description":"Enables the read_text command without any pre-configured scope.","commands":{"allow":["read_text"],"deny":[]}},"allow-write-html":{"identifier":"allow-write-html","description":"Enables the write_html command without any pre-configured scope.","commands":{"allow":["write_html"],"deny":[]}},"allow-write-image":{"identifier":"allow-write-image","description":"Enables the write_image command without any pre-configured scope.","commands":{"allow":["write_image"],"deny":[]}},"allow-write-text":{"identifier":"allow-write-text","description":"Enables the write_text command without any pre-configured scope.","commands":{"allow":["write_text"],"deny":[]}},"deny-clear":{"identifier":"deny-clear","description":"Denies the clear command without any pre-configured scope.","commands":{"allow":[],"deny":["clear"]}},"deny-read-image":{"identifier":"deny-read-image","description":"Denies the read_image command without any pre-configured scope.","commands":{"allow":[],"deny":["read_image"]}},"deny-read-text":{"identifier":"deny-read-text","description":"Denies the read_text command without any pre-configured scope.","commands":{"allow":[],"deny":["read_text"]}},"deny-write-html":{"identifier":"deny-write-html","description":"Denies the write_html command without any pre-configured scope.","commands":{"allow":[],"deny":["write_html"]}},"deny-write-image":{"identifier":"deny-write-image","description":"Denies the write_image command without any pre-configured scope.","commands":{"allow":[],"deny":["write_image"]}},"deny-write-text":{"identifier":"deny-write-text","description":"Denies the write_text command without any pre-configured scope.","commands":{"allow":[],"deny":["write_text"]}}},"permission_sets":{},"global_scope_schema":null},"core":{"default_permission":{"identifier":"default","description":"Default core plugins set.","permissions":["core:path:default","core:event:default","core:window:default","core:webview:default","core:app:default","core:image:default","core:resources:default","core:menu:default","core:tray:default"]},"permissions":{},"permission_sets":{},"global_scope_schema":null},"core:app":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin.","permissions":["allow-version","allow-name","allow-tauri-version","allow-identifier"]},"permissions":{"allow-app-hide":{"identifier":"allow-app-hide","description":"Enables the app_hide command without any pre-configured scope.","commands":{"allow":["app_hide"],"deny":[]}},"allow-app-show":{"identifier":"allow-app-show","description":"Enables the app_show command without any pre-configured scope.","commands":{"allow":["app_show"],"deny":[]}},"allow-default-window-icon":{"identifier":"allow-default-window-icon","description":"Enables the default_window_icon command without any pre-configured scope.","commands":{"allow":["default_window_icon"],"deny":[]}},"allow-fetch-data-store-identifiers":{"identifier":"allow-fetch-data-store-identifiers","description":"Enables the fetch_data_store_identifiers command without any pre-configured scope.","commands":{"allow":["fetch_data_store_identifiers"],"deny":[]}},"allow-identifier":{"identifier":"allow-identifier","description":"Enables the identifier command without any pre-configured scope.","commands":{"allow":["identifier"],"deny":[]}},"allow-name":{"identifier":"allow-name","description":"Enables the name command without any pre-configured scope.","commands":{"allow":["name"],"deny":[]}},"allow-remove-data-store":{"identifier":"allow-remove-data-store","description":"Enables the remove_data_store command without any pre-configured scope.","commands":{"allow":["remove_data_store"],"deny":[]}},"allow-set-app-theme":{"identifier":"allow-set-app-theme","description":"Enables the set_app_theme command without any pre-configured scope.","commands":{"allow":["set_app_theme"],"deny":[]}},"allow-tauri-version":{"identifier":"allow-tauri-version","description":"Enables the tauri_version command without any pre-configured scope.","commands":{"allow":["tauri_version"],"deny":[]}},"allow-version":{"identifier":"allow-version","description":"Enables the version command without any pre-configured scope.","commands":{"allow":["version"],"deny":[]}},"deny-app-hide":{"identifier":"deny-app-hide","description":"Denies the app_hide command without any pre-configured scope.","commands":{"allow":[],"deny":["app_hide"]}},"deny-app-show":{"identifier":"deny-app-show","description":"Denies the app_show command without any pre-configured scope.","commands":{"allow":[],"deny":["app_show"]}},"deny-default-window-icon":{"identifier":"deny-default-window-icon","description":"Denies the default_window_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["default_window_icon"]}},"deny-fetch-data-store-identifiers":{"identifier":"deny-fetch-data-store-identifiers","description":"Denies the fetch_data_store_identifiers command without any pre-configured scope.","commands":{"allow":[],"deny":["fetch_data_store_identifiers"]}},"deny-identifier":{"identifier":"deny-identifier","description":"Denies the identifier command without any pre-configured scope.","commands":{"allow":[],"deny":["identifier"]}},"deny-name":{"identifier":"deny-name","description":"Denies the name command without any pre-configured scope.","commands":{"allow":[],"deny":["name"]}},"deny-remove-data-store":{"identifier":"deny-remove-data-store","description":"Denies the remove_data_store command without any pre-configured scope.","commands":{"allow":[],"deny":["remove_data_store"]}},"deny-set-app-theme":{"identifier":"deny-set-app-theme","description":"Denies the set_app_theme command without any pre-configured scope.","commands":{"allow":[],"deny":["set_app_theme"]}},"deny-tauri-version":{"identifier":"deny-tauri-version","description":"Denies the tauri_version command without any pre-configured scope.","commands":{"allow":[],"deny":["tauri_version"]}},"deny-version":{"identifier":"deny-version","description":"Denies the version command without any pre-configured scope.","commands":{"allow":[],"deny":["version"]}}},"permission_sets":{},"global_scope_schema":null},"core:event":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin, which enables all commands.","permissions":["allow-listen","allow-unlisten","allow-emit","allow-emit-to"]},"permissions":{"allow-emit":{"identifier":"allow-emit","description":"Enables the emit command without any pre-configured scope.","commands":{"allow":["emit"],"deny":[]}},"allow-emit-to":{"identifier":"allow-emit-to","description":"Enables the emit_to command without any pre-configured scope.","commands":{"allow":["emit_to"],"deny":[]}},"allow-listen":{"identifier":"allow-listen","description":"Enables the listen command without any pre-configured scope.","commands":{"allow":["listen"],"deny":[]}},"allow-unlisten":{"identifier":"allow-unlisten","description":"Enables the unlisten command without any pre-configured scope.","commands":{"allow":["unlisten"],"deny":[]}},"deny-emit":{"identifier":"deny-emit","description":"Denies the emit command without any pre-configured scope.","commands":{"allow":[],"deny":["emit"]}},"deny-emit-to":{"identifier":"deny-emit-to","description":"Denies the emit_to command without any pre-configured scope.","commands":{"allow":[],"deny":["emit_to"]}},"deny-listen":{"identifier":"deny-listen","description":"Denies the listen command without any pre-configured scope.","commands":{"allow":[],"deny":["listen"]}},"deny-unlisten":{"identifier":"deny-unlisten","description":"Denies the unlisten command without any pre-configured scope.","commands":{"allow":[],"deny":["unlisten"]}}},"permission_sets":{},"global_scope_schema":null},"core:image":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin, which enables all commands.","permissions":["allow-new","allow-from-bytes","allow-from-path","allow-rgba","allow-size"]},"permissions":{"allow-from-bytes":{"identifier":"allow-from-bytes","description":"Enables the from_bytes command without any pre-configured scope.","commands":{"allow":["from_bytes"],"deny":[]}},"allow-from-path":{"identifier":"allow-from-path","description":"Enables the from_path command without any pre-configured scope.","commands":{"allow":["from_path"],"deny":[]}},"allow-new":{"identifier":"allow-new","description":"Enables the new command without any pre-configured scope.","commands":{"allow":["new"],"deny":[]}},"allow-rgba":{"identifier":"allow-rgba","description":"Enables the rgba command without any pre-configured scope.","commands":{"allow":["rgba"],"deny":[]}},"allow-size":{"identifier":"allow-size","description":"Enables the size command without any pre-configured scope.","commands":{"allow":["size"],"deny":[]}},"deny-from-bytes":{"identifier":"deny-from-bytes","description":"Denies the from_bytes command without any pre-configured scope.","commands":{"allow":[],"deny":["from_bytes"]}},"deny-from-path":{"identifier":"deny-from-path","description":"Denies the from_path command without any pre-configured scope.","commands":{"allow":[],"deny":["from_path"]}},"deny-new":{"identifier":"deny-new","description":"Denies the new command without any pre-configured scope.","commands":{"allow":[],"deny":["new"]}},"deny-rgba":{"identifier":"deny-rgba","description":"Denies the rgba command without any pre-configured scope.","commands":{"allow":[],"deny":["rgba"]}},"deny-size":{"identifier":"deny-size","description":"Denies the size command without any pre-configured scope.","commands":{"allow":[],"deny":["size"]}}},"permission_sets":{},"global_scope_schema":null},"core:menu":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin, which enables all commands.","permissions":["allow-new","allow-append","allow-prepend","allow-insert","allow-remove","allow-remove-at","allow-items","allow-get","allow-popup","allow-create-default","allow-set-as-app-menu","allow-set-as-window-menu","allow-text","allow-set-text","allow-is-enabled","allow-set-enabled","allow-set-accelerator","allow-set-as-windows-menu-for-nsapp","allow-set-as-help-menu-for-nsapp","allow-is-checked","allow-set-checked","allow-set-icon"]},"permissions":{"allow-append":{"identifier":"allow-append","description":"Enables the append command without any pre-configured scope.","commands":{"allow":["append"],"deny":[]}},"allow-create-default":{"identifier":"allow-create-default","description":"Enables the create_default command without any pre-configured scope.","commands":{"allow":["create_default"],"deny":[]}},"allow-get":{"identifier":"allow-get","description":"Enables the get command without any pre-configured scope.","commands":{"allow":["get"],"deny":[]}},"allow-insert":{"identifier":"allow-insert","description":"Enables the insert command without any pre-configured scope.","commands":{"allow":["insert"],"deny":[]}},"allow-is-checked":{"identifier":"allow-is-checked","description":"Enables the is_checked command without any pre-configured scope.","commands":{"allow":["is_checked"],"deny":[]}},"allow-is-enabled":{"identifier":"allow-is-enabled","description":"Enables the is_enabled command without any pre-configured scope.","commands":{"allow":["is_enabled"],"deny":[]}},"allow-items":{"identifier":"allow-items","description":"Enables the items command without any pre-configured scope.","commands":{"allow":["items"],"deny":[]}},"allow-new":{"identifier":"allow-new","description":"Enables the new command without any pre-configured scope.","commands":{"allow":["new"],"deny":[]}},"allow-popup":{"identifier":"allow-popup","description":"Enables the popup command without any pre-configured scope.","commands":{"allow":["popup"],"deny":[]}},"allow-prepend":{"identifier":"allow-prepend","description":"Enables the prepend command without any pre-configured scope.","commands":{"allow":["prepend"],"deny":[]}},"allow-remove":{"identifier":"allow-remove","description":"Enables the remove command without any pre-configured scope.","commands":{"allow":["remove"],"deny":[]}},"allow-remove-at":{"identifier":"allow-remove-at","description":"Enables the remove_at command without any pre-configured scope.","commands":{"allow":["remove_at"],"deny":[]}},"allow-set-accelerator":{"identifier":"allow-set-accelerator","description":"Enables the set_accelerator command without any pre-configured scope.","commands":{"allow":["set_accelerator"],"deny":[]}},"allow-set-as-app-menu":{"identifier":"allow-set-as-app-menu","description":"Enables the set_as_app_menu command without any pre-configured scope.","commands":{"allow":["set_as_app_menu"],"deny":[]}},"allow-set-as-help-menu-for-nsapp":{"identifier":"allow-set-as-help-menu-for-nsapp","description":"Enables the set_as_help_menu_for_nsapp command without any pre-configured scope.","commands":{"allow":["set_as_help_menu_for_nsapp"],"deny":[]}},"allow-set-as-window-menu":{"identifier":"allow-set-as-window-menu","description":"Enables the set_as_window_menu command without any pre-configured scope.","commands":{"allow":["set_as_window_menu"],"deny":[]}},"allow-set-as-windows-menu-for-nsapp":{"identifier":"allow-set-as-windows-menu-for-nsapp","description":"Enables the set_as_windows_menu_for_nsapp command without any pre-configured scope.","commands":{"allow":["set_as_windows_menu_for_nsapp"],"deny":[]}},"allow-set-checked":{"identifier":"allow-set-checked","description":"Enables the set_checked command without any pre-configured scope.","commands":{"allow":["set_checked"],"deny":[]}},"allow-set-enabled":{"identifier":"allow-set-enabled","description":"Enables the set_enabled command without any pre-configured scope.","commands":{"allow":["set_enabled"],"deny":[]}},"allow-set-icon":{"identifier":"allow-set-icon","description":"Enables the set_icon command without any pre-configured scope.","commands":{"allow":["set_icon"],"deny":[]}},"allow-set-text":{"identifier":"allow-set-text","description":"Enables the set_text command without any pre-configured scope.","commands":{"allow":["set_text"],"deny":[]}},"allow-text":{"identifier":"allow-text","description":"Enables the text command without any pre-configured scope.","commands":{"allow":["text"],"deny":[]}},"deny-append":{"identifier":"deny-append","description":"Denies the append command without any pre-configured scope.","commands":{"allow":[],"deny":["append"]}},"deny-create-default":{"identifier":"deny-create-default","description":"Denies the create_default command without any pre-configured scope.","commands":{"allow":[],"deny":["create_default"]}},"deny-get":{"identifier":"deny-get","description":"Denies the get command without any pre-configured scope.","commands":{"allow":[],"deny":["get"]}},"deny-insert":{"identifier":"deny-insert","description":"Denies the insert command without any pre-configured scope.","commands":{"allow":[],"deny":["insert"]}},"deny-is-checked":{"identifier":"deny-is-checked","description":"Denies the is_checked command without any pre-configured scope.","commands":{"allow":[],"deny":["is_checked"]}},"deny-is-enabled":{"identifier":"deny-is-enabled","description":"Denies the is_enabled command without any pre-configured scope.","commands":{"allow":[],"deny":["is_enabled"]}},"deny-items":{"identifier":"deny-items","description":"Denies the items command without any pre-configured scope.","commands":{"allow":[],"deny":["items"]}},"deny-new":{"identifier":"deny-new","description":"Denies the new command without any pre-configured scope.","commands":{"allow":[],"deny":["new"]}},"deny-popup":{"identifier":"deny-popup","description":"Denies the popup command without any pre-configured scope.","commands":{"allow":[],"deny":["popup"]}},"deny-prepend":{"identifier":"deny-prepend","description":"Denies the prepend command without any pre-configured scope.","commands":{"allow":[],"deny":["prepend"]}},"deny-remove":{"identifier":"deny-remove","description":"Denies the remove command without any pre-configured scope.","commands":{"allow":[],"deny":["remove"]}},"deny-remove-at":{"identifier":"deny-remove-at","description":"Denies the remove_at command without any pre-configured scope.","commands":{"allow":[],"deny":["remove_at"]}},"deny-set-accelerator":{"identifier":"deny-set-accelerator","description":"Denies the set_accelerator command without any pre-configured scope.","commands":{"allow":[],"deny":["set_accelerator"]}},"deny-set-as-app-menu":{"identifier":"deny-set-as-app-menu","description":"Denies the set_as_app_menu command without any pre-configured scope.","commands":{"allow":[],"deny":["set_as_app_menu"]}},"deny-set-as-help-menu-for-nsapp":{"identifier":"deny-set-as-help-menu-for-nsapp","description":"Denies the set_as_help_menu_for_nsapp command without any pre-configured scope.","commands":{"allow":[],"deny":["set_as_help_menu_for_nsapp"]}},"deny-set-as-window-menu":{"identifier":"deny-set-as-window-menu","description":"Denies the set_as_window_menu command without any pre-configured scope.","commands":{"allow":[],"deny":["set_as_window_menu"]}},"deny-set-as-windows-menu-for-nsapp":{"identifier":"deny-set-as-windows-menu-for-nsapp","description":"Denies the set_as_windows_menu_for_nsapp command without any pre-configured scope.","commands":{"allow":[],"deny":["set_as_windows_menu_for_nsapp"]}},"deny-set-checked":{"identifier":"deny-set-checked","description":"Denies the set_checked command without any pre-configured scope.","commands":{"allow":[],"deny":["set_checked"]}},"deny-set-enabled":{"identifier":"deny-set-enabled","description":"Denies the set_enabled command without any pre-configured scope.","commands":{"allow":[],"deny":["set_enabled"]}},"deny-set-icon":{"identifier":"deny-set-icon","description":"Denies the set_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["set_icon"]}},"deny-set-text":{"identifier":"deny-set-text","description":"Denies the set_text command without any pre-configured scope.","commands":{"allow":[],"deny":["set_text"]}},"deny-text":{"identifier":"deny-text","description":"Denies the text command without any pre-configured scope.","commands":{"allow":[],"deny":["text"]}}},"permission_sets":{},"global_scope_schema":null},"core:path":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin, which enables all commands.","permissions":["allow-resolve-directory","allow-resolve","allow-normalize","allow-join","allow-dirname","allow-extname","allow-basename","allow-is-absolute"]},"permissions":{"allow-basename":{"identifier":"allow-basename","description":"Enables the basename command without any pre-configured scope.","commands":{"allow":["basename"],"deny":[]}},"allow-dirname":{"identifier":"allow-dirname","description":"Enables the dirname command without any pre-configured scope.","commands":{"allow":["dirname"],"deny":[]}},"allow-extname":{"identifier":"allow-extname","description":"Enables the extname command without any pre-configured scope.","commands":{"allow":["extname"],"deny":[]}},"allow-is-absolute":{"identifier":"allow-is-absolute","description":"Enables the is_absolute command without any pre-configured scope.","commands":{"allow":["is_absolute"],"deny":[]}},"allow-join":{"identifier":"allow-join","description":"Enables the join command without any pre-configured scope.","commands":{"allow":["join"],"deny":[]}},"allow-normalize":{"identifier":"allow-normalize","description":"Enables the normalize command without any pre-configured scope.","commands":{"allow":["normalize"],"deny":[]}},"allow-resolve":{"identifier":"allow-resolve","description":"Enables the resolve command without any pre-configured scope.","commands":{"allow":["resolve"],"deny":[]}},"allow-resolve-directory":{"identifier":"allow-resolve-directory","description":"Enables the resolve_directory command without any pre-configured scope.","commands":{"allow":["resolve_directory"],"deny":[]}},"deny-basename":{"identifier":"deny-basename","description":"Denies the basename command without any pre-configured scope.","commands":{"allow":[],"deny":["basename"]}},"deny-dirname":{"identifier":"deny-dirname","description":"Denies the dirname command without any pre-configured scope.","commands":{"allow":[],"deny":["dirname"]}},"deny-extname":{"identifier":"deny-extname","description":"Denies the extname command without any pre-configured scope.","commands":{"allow":[],"deny":["extname"]}},"deny-is-absolute":{"identifier":"deny-is-absolute","description":"Denies the is_absolute command without any pre-configured scope.","commands":{"allow":[],"deny":["is_absolute"]}},"deny-join":{"identifier":"deny-join","description":"Denies the join command without any pre-configured scope.","commands":{"allow":[],"deny":["join"]}},"deny-normalize":{"identifier":"deny-normalize","description":"Denies the normalize command without any pre-configured scope.","commands":{"allow":[],"deny":["normalize"]}},"deny-resolve":{"identifier":"deny-resolve","description":"Denies the resolve command without any pre-configured scope.","commands":{"allow":[],"deny":["resolve"]}},"deny-resolve-directory":{"identifier":"deny-resolve-directory","description":"Denies the resolve_directory command without any pre-configured scope.","commands":{"allow":[],"deny":["resolve_directory"]}}},"permission_sets":{},"global_scope_schema":null},"core:resources":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin, which enables all commands.","permissions":["allow-close"]},"permissions":{"allow-close":{"identifier":"allow-close","description":"Enables the close command without any pre-configured scope.","commands":{"allow":["close"],"deny":[]}},"deny-close":{"identifier":"deny-close","description":"Denies the close command without any pre-configured scope.","commands":{"allow":[],"deny":["close"]}}},"permission_sets":{},"global_scope_schema":null},"core:tray":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin, which enables all commands.","permissions":["allow-new","allow-get-by-id","allow-remove-by-id","allow-set-icon","allow-set-menu","allow-set-tooltip","allow-set-title","allow-set-visible","allow-set-temp-dir-path","allow-set-icon-as-template","allow-set-show-menu-on-left-click"]},"permissions":{"allow-get-by-id":{"identifier":"allow-get-by-id","description":"Enables the get_by_id command without any pre-configured scope.","commands":{"allow":["get_by_id"],"deny":[]}},"allow-new":{"identifier":"allow-new","description":"Enables the new command without any pre-configured scope.","commands":{"allow":["new"],"deny":[]}},"allow-remove-by-id":{"identifier":"allow-remove-by-id","description":"Enables the remove_by_id command without any pre-configured scope.","commands":{"allow":["remove_by_id"],"deny":[]}},"allow-set-icon":{"identifier":"allow-set-icon","description":"Enables the set_icon command without any pre-configured scope.","commands":{"allow":["set_icon"],"deny":[]}},"allow-set-icon-as-template":{"identifier":"allow-set-icon-as-template","description":"Enables the set_icon_as_template command without any pre-configured scope.","commands":{"allow":["set_icon_as_template"],"deny":[]}},"allow-set-menu":{"identifier":"allow-set-menu","description":"Enables the set_menu command without any pre-configured scope.","commands":{"allow":["set_menu"],"deny":[]}},"allow-set-show-menu-on-left-click":{"identifier":"allow-set-show-menu-on-left-click","description":"Enables the set_show_menu_on_left_click command without any pre-configured scope.","commands":{"allow":["set_show_menu_on_left_click"],"deny":[]}},"allow-set-temp-dir-path":{"identifier":"allow-set-temp-dir-path","description":"Enables the set_temp_dir_path command without any pre-configured scope.","commands":{"allow":["set_temp_dir_path"],"deny":[]}},"allow-set-title":{"identifier":"allow-set-title","description":"Enables the set_title command without any pre-configured scope.","commands":{"allow":["set_title"],"deny":[]}},"allow-set-tooltip":{"identifier":"allow-set-tooltip","description":"Enables the set_tooltip command without any pre-configured scope.","commands":{"allow":["set_tooltip"],"deny":[]}},"allow-set-visible":{"identifier":"allow-set-visible","description":"Enables the set_visible command without any pre-configured scope.","commands":{"allow":["set_visible"],"deny":[]}},"deny-get-by-id":{"identifier":"deny-get-by-id","description":"Denies the get_by_id command without any pre-configured scope.","commands":{"allow":[],"deny":["get_by_id"]}},"deny-new":{"identifier":"deny-new","description":"Denies the new command without any pre-configured scope.","commands":{"allow":[],"deny":["new"]}},"deny-remove-by-id":{"identifier":"deny-remove-by-id","description":"Denies the remove_by_id command without any pre-configured scope.","commands":{"allow":[],"deny":["remove_by_id"]}},"deny-set-icon":{"identifier":"deny-set-icon","description":"Denies the set_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["set_icon"]}},"deny-set-icon-as-template":{"identifier":"deny-set-icon-as-template","description":"Denies the set_icon_as_template command without any pre-configured scope.","commands":{"allow":[],"deny":["set_icon_as_template"]}},"deny-set-menu":{"identifier":"deny-set-menu","description":"Denies the set_menu command without any pre-configured scope.","commands":{"allow":[],"deny":["set_menu"]}},"deny-set-show-menu-on-left-click":{"identifier":"deny-set-show-menu-on-left-click","description":"Denies the set_show_menu_on_left_click command without any pre-configured scope.","commands":{"allow":[],"deny":["set_show_menu_on_left_click"]}},"deny-set-temp-dir-path":{"identifier":"deny-set-temp-dir-path","description":"Denies the set_temp_dir_path command without any pre-configured scope.","commands":{"allow":[],"deny":["set_temp_dir_path"]}},"deny-set-title":{"identifier":"deny-set-title","description":"Denies the set_title command without any pre-configured scope.","commands":{"allow":[],"deny":["set_title"]}},"deny-set-tooltip":{"identifier":"deny-set-tooltip","description":"Denies the set_tooltip command without any pre-configured scope.","commands":{"allow":[],"deny":["set_tooltip"]}},"deny-set-visible":{"identifier":"deny-set-visible","description":"Denies the set_visible command without any pre-configured scope.","commands":{"allow":[],"deny":["set_visible"]}}},"permission_sets":{},"global_scope_schema":null},"core:webview":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin.","permissions":["allow-get-all-webviews","allow-webview-position","allow-webview-size","allow-internal-toggle-devtools"]},"permissions":{"allow-clear-all-browsing-data":{"identifier":"allow-clear-all-browsing-data","description":"Enables the clear_all_browsing_data command without any pre-configured scope.","commands":{"allow":["clear_all_browsing_data"],"deny":[]}},"allow-create-webview":{"identifier":"allow-create-webview","description":"Enables the create_webview command without any pre-configured scope.","commands":{"allow":["create_webview"],"deny":[]}},"allow-create-webview-window":{"identifier":"allow-create-webview-window","description":"Enables the create_webview_window command without any pre-configured scope.","commands":{"allow":["create_webview_window"],"deny":[]}},"allow-get-all-webviews":{"identifier":"allow-get-all-webviews","description":"Enables the get_all_webviews command without any pre-configured scope.","commands":{"allow":["get_all_webviews"],"deny":[]}},"allow-internal-toggle-devtools":{"identifier":"allow-internal-toggle-devtools","description":"Enables the internal_toggle_devtools command without any pre-configured scope.","commands":{"allow":["internal_toggle_devtools"],"deny":[]}},"allow-print":{"identifier":"allow-print","description":"Enables the print command without any pre-configured scope.","commands":{"allow":["print"],"deny":[]}},"allow-reparent":{"identifier":"allow-reparent","description":"Enables the reparent command without any pre-configured scope.","commands":{"allow":["reparent"],"deny":[]}},"allow-set-webview-background-color":{"identifier":"allow-set-webview-background-color","description":"Enables the set_webview_background_color command without any pre-configured scope.","commands":{"allow":["set_webview_background_color"],"deny":[]}},"allow-set-webview-focus":{"identifier":"allow-set-webview-focus","description":"Enables the set_webview_focus command without any pre-configured scope.","commands":{"allow":["set_webview_focus"],"deny":[]}},"allow-set-webview-position":{"identifier":"allow-set-webview-position","description":"Enables the set_webview_position command without any pre-configured scope.","commands":{"allow":["set_webview_position"],"deny":[]}},"allow-set-webview-size":{"identifier":"allow-set-webview-size","description":"Enables the set_webview_size command without any pre-configured scope.","commands":{"allow":["set_webview_size"],"deny":[]}},"allow-set-webview-zoom":{"identifier":"allow-set-webview-zoom","description":"Enables the set_webview_zoom command without any pre-configured scope.","commands":{"allow":["set_webview_zoom"],"deny":[]}},"allow-webview-close":{"identifier":"allow-webview-close","description":"Enables the webview_close command without any pre-configured scope.","commands":{"allow":["webview_close"],"deny":[]}},"allow-webview-hide":{"identifier":"allow-webview-hide","description":"Enables the webview_hide command without any pre-configured scope.","commands":{"allow":["webview_hide"],"deny":[]}},"allow-webview-position":{"identifier":"allow-webview-position","description":"Enables the webview_position command without any pre-configured scope.","commands":{"allow":["webview_position"],"deny":[]}},"allow-webview-show":{"identifier":"allow-webview-show","description":"Enables the webview_show command without any pre-configured scope.","commands":{"allow":["webview_show"],"deny":[]}},"allow-webview-size":{"identifier":"allow-webview-size","description":"Enables the webview_size command without any pre-configured scope.","commands":{"allow":["webview_size"],"deny":[]}},"deny-clear-all-browsing-data":{"identifier":"deny-clear-all-browsing-data","description":"Denies the clear_all_browsing_data command without any pre-configured scope.","commands":{"allow":[],"deny":["clear_all_browsing_data"]}},"deny-create-webview":{"identifier":"deny-create-webview","description":"Denies the create_webview command without any pre-configured scope.","commands":{"allow":[],"deny":["create_webview"]}},"deny-create-webview-window":{"identifier":"deny-create-webview-window","description":"Denies the create_webview_window command without any pre-configured scope.","commands":{"allow":[],"deny":["create_webview_window"]}},"deny-get-all-webviews":{"identifier":"deny-get-all-webviews","description":"Denies the get_all_webviews command without any pre-configured scope.","commands":{"allow":[],"deny":["get_all_webviews"]}},"deny-internal-toggle-devtools":{"identifier":"deny-internal-toggle-devtools","description":"Denies the internal_toggle_devtools command without any pre-configured scope.","commands":{"allow":[],"deny":["internal_toggle_devtools"]}},"deny-print":{"identifier":"deny-print","description":"Denies the print command without any pre-configured scope.","commands":{"allow":[],"deny":["print"]}},"deny-reparent":{"identifier":"deny-reparent","description":"Denies the reparent command without any pre-configured scope.","commands":{"allow":[],"deny":["reparent"]}},"deny-set-webview-background-color":{"identifier":"deny-set-webview-background-color","description":"Denies the set_webview_background_color command without any pre-configured scope.","commands":{"allow":[],"deny":["set_webview_background_color"]}},"deny-set-webview-focus":{"identifier":"deny-set-webview-focus","description":"Denies the set_webview_focus command without any pre-configured scope.","commands":{"allow":[],"deny":["set_webview_focus"]}},"deny-set-webview-position":{"identifier":"deny-set-webview-position","description":"Denies the set_webview_position command without any pre-configured scope.","commands":{"allow":[],"deny":["set_webview_position"]}},"deny-set-webview-size":{"identifier":"deny-set-webview-size","description":"Denies the set_webview_size command without any pre-configured scope.","commands":{"allow":[],"deny":["set_webview_size"]}},"deny-set-webview-zoom":{"identifier":"deny-set-webview-zoom","description":"Denies the set_webview_zoom command without any pre-configured scope.","commands":{"allow":[],"deny":["set_webview_zoom"]}},"deny-webview-close":{"identifier":"deny-webview-close","description":"Denies the webview_close command without any pre-configured scope.","commands":{"allow":[],"deny":["webview_close"]}},"deny-webview-hide":{"identifier":"deny-webview-hide","description":"Denies the webview_hide command without any pre-configured scope.","commands":{"allow":[],"deny":["webview_hide"]}},"deny-webview-position":{"identifier":"deny-webview-position","description":"Denies the webview_position command without any pre-configured scope.","commands":{"allow":[],"deny":["webview_position"]}},"deny-webview-show":{"identifier":"deny-webview-show","description":"Denies the webview_show command without any pre-configured scope.","commands":{"allow":[],"deny":["webview_show"]}},"deny-webview-size":{"identifier":"deny-webview-size","description":"Denies the webview_size command without any pre-configured scope.","commands":{"allow":[],"deny":["webview_size"]}}},"permission_sets":{},"global_scope_schema":null},"core:window":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin.","permissions":["allow-get-all-windows","allow-scale-factor","allow-inner-position","allow-outer-position","allow-inner-size","allow-outer-size","allow-is-fullscreen","allow-is-minimized","allow-is-maximized","allow-is-focused","allow-is-decorated","allow-is-resizable","allow-is-maximizable","allow-is-minimizable","allow-is-closable","allow-is-visible","allow-is-enabled","allow-title","allow-current-monitor","allow-primary-monitor","allow-monitor-from-point","allow-available-monitors","allow-cursor-position","allow-theme","allow-is-always-on-top","allow-internal-toggle-maximize"]},"permissions":{"allow-available-monitors":{"identifier":"allow-available-monitors","description":"Enables the available_monitors command without any pre-configured scope.","commands":{"allow":["available_monitors"],"deny":[]}},"allow-center":{"identifier":"allow-center","description":"Enables the center command without any pre-configured scope.","commands":{"allow":["center"],"deny":[]}},"allow-close":{"identifier":"allow-close","description":"Enables the close command without any pre-configured scope.","commands":{"allow":["close"],"deny":[]}},"allow-create":{"identifier":"allow-create","description":"Enables the create command without any pre-configured scope.","commands":{"allow":["create"],"deny":[]}},"allow-current-monitor":{"identifier":"allow-current-monitor","description":"Enables the current_monitor command without any pre-configured scope.","commands":{"allow":["current_monitor"],"deny":[]}},"allow-cursor-position":{"identifier":"allow-cursor-position","description":"Enables the cursor_position command without any pre-configured scope.","commands":{"allow":["cursor_position"],"deny":[]}},"allow-destroy":{"identifier":"allow-destroy","description":"Enables the destroy command without any pre-configured scope.","commands":{"allow":["destroy"],"deny":[]}},"allow-get-all-windows":{"identifier":"allow-get-all-windows","description":"Enables the get_all_windows command without any pre-configured scope.","commands":{"allow":["get_all_windows"],"deny":[]}},"allow-hide":{"identifier":"allow-hide","description":"Enables the hide command without any pre-configured scope.","commands":{"allow":["hide"],"deny":[]}},"allow-inner-position":{"identifier":"allow-inner-position","description":"Enables the inner_position command without any pre-configured scope.","commands":{"allow":["inner_position"],"deny":[]}},"allow-inner-size":{"identifier":"allow-inner-size","description":"Enables the inner_size command without any pre-configured scope.","commands":{"allow":["inner_size"],"deny":[]}},"allow-internal-toggle-maximize":{"identifier":"allow-internal-toggle-maximize","description":"Enables the internal_toggle_maximize command without any pre-configured scope.","commands":{"allow":["internal_toggle_maximize"],"deny":[]}},"allow-is-always-on-top":{"identifier":"allow-is-always-on-top","description":"Enables the is_always_on_top command without any pre-configured scope.","commands":{"allow":["is_always_on_top"],"deny":[]}},"allow-is-closable":{"identifier":"allow-is-closable","description":"Enables the is_closable command without any pre-configured scope.","commands":{"allow":["is_closable"],"deny":[]}},"allow-is-decorated":{"identifier":"allow-is-decorated","description":"Enables the is_decorated command without any pre-configured scope.","commands":{"allow":["is_decorated"],"deny":[]}},"allow-is-enabled":{"identifier":"allow-is-enabled","description":"Enables the is_enabled command without any pre-configured scope.","commands":{"allow":["is_enabled"],"deny":[]}},"allow-is-focused":{"identifier":"allow-is-focused","description":"Enables the is_focused command without any pre-configured scope.","commands":{"allow":["is_focused"],"deny":[]}},"allow-is-fullscreen":{"identifier":"allow-is-fullscreen","description":"Enables the is_fullscreen command without any pre-configured scope.","commands":{"allow":["is_fullscreen"],"deny":[]}},"allow-is-maximizable":{"identifier":"allow-is-maximizable","description":"Enables the is_maximizable command without any pre-configured scope.","commands":{"allow":["is_maximizable"],"deny":[]}},"allow-is-maximized":{"identifier":"allow-is-maximized","description":"Enables the is_maximized command without any pre-configured scope.","commands":{"allow":["is_maximized"],"deny":[]}},"allow-is-minimizable":{"identifier":"allow-is-minimizable","description":"Enables the is_minimizable command without any pre-configured scope.","commands":{"allow":["is_minimizable"],"deny":[]}},"allow-is-minimized":{"identifier":"allow-is-minimized","description":"Enables the is_minimized command without any pre-configured scope.","commands":{"allow":["is_minimized"],"deny":[]}},"allow-is-resizable":{"identifier":"allow-is-resizable","description":"Enables the is_resizable command without any pre-configured scope.","commands":{"allow":["is_resizable"],"deny":[]}},"allow-is-visible":{"identifier":"allow-is-visible","description":"Enables the is_visible command without any pre-configured scope.","commands":{"allow":["is_visible"],"deny":[]}},"allow-maximize":{"identifier":"allow-maximize","description":"Enables the maximize command without any pre-configured scope.","commands":{"allow":["maximize"],"deny":[]}},"allow-minimize":{"identifier":"allow-minimize","description":"Enables the minimize command without any pre-configured scope.","commands":{"allow":["minimize"],"deny":[]}},"allow-monitor-from-point":{"identifier":"allow-monitor-from-point","description":"Enables the monitor_from_point command without any pre-configured scope.","commands":{"allow":["monitor_from_point"],"deny":[]}},"allow-outer-position":{"identifier":"allow-outer-position","description":"Enables the outer_position command without any pre-configured scope.","commands":{"allow":["outer_position"],"deny":[]}},"allow-outer-size":{"identifier":"allow-outer-size","description":"Enables the outer_size command without any pre-configured scope.","commands":{"allow":["outer_size"],"deny":[]}},"allow-primary-monitor":{"identifier":"allow-primary-monitor","description":"Enables the primary_monitor command without any pre-configured scope.","commands":{"allow":["primary_monitor"],"deny":[]}},"allow-request-user-attention":{"identifier":"allow-request-user-attention","description":"Enables the request_user_attention command without any pre-configured scope.","commands":{"allow":["request_user_attention"],"deny":[]}},"allow-scale-factor":{"identifier":"allow-scale-factor","description":"Enables the scale_factor command without any pre-configured scope.","commands":{"allow":["scale_factor"],"deny":[]}},"allow-set-always-on-bottom":{"identifier":"allow-set-always-on-bottom","description":"Enables the set_always_on_bottom command without any pre-configured scope.","commands":{"allow":["set_always_on_bottom"],"deny":[]}},"allow-set-always-on-top":{"identifier":"allow-set-always-on-top","description":"Enables the set_always_on_top command without any pre-configured scope.","commands":{"allow":["set_always_on_top"],"deny":[]}},"allow-set-background-color":{"identifier":"allow-set-background-color","description":"Enables the set_background_color command without any pre-configured scope.","commands":{"allow":["set_background_color"],"deny":[]}},"allow-set-badge-count":{"identifier":"allow-set-badge-count","description":"Enables the set_badge_count command without any pre-configured scope.","commands":{"allow":["set_badge_count"],"deny":[]}},"allow-set-badge-label":{"identifier":"allow-set-badge-label","description":"Enables the set_badge_label command without any pre-configured scope.","commands":{"allow":["set_badge_label"],"deny":[]}},"allow-set-closable":{"identifier":"allow-set-closable","description":"Enables the set_closable command without any pre-configured scope.","commands":{"allow":["set_closable"],"deny":[]}},"allow-set-content-protected":{"identifier":"allow-set-content-protected","description":"Enables the set_content_protected command without any pre-configured scope.","commands":{"allow":["set_content_protected"],"deny":[]}},"allow-set-cursor-grab":{"identifier":"allow-set-cursor-grab","description":"Enables the set_cursor_grab command without any pre-configured scope.","commands":{"allow":["set_cursor_grab"],"deny":[]}},"allow-set-cursor-icon":{"identifier":"allow-set-cursor-icon","description":"Enables the set_cursor_icon command without any pre-configured scope.","commands":{"allow":["set_cursor_icon"],"deny":[]}},"allow-set-cursor-position":{"identifier":"allow-set-cursor-position","description":"Enables the set_cursor_position command without any pre-configured scope.","commands":{"allow":["set_cursor_position"],"deny":[]}},"allow-set-cursor-visible":{"identifier":"allow-set-cursor-visible","description":"Enables the set_cursor_visible command without any pre-configured scope.","commands":{"allow":["set_cursor_visible"],"deny":[]}},"allow-set-decorations":{"identifier":"allow-set-decorations","description":"Enables the set_decorations command without any pre-configured scope.","commands":{"allow":["set_decorations"],"deny":[]}},"allow-set-effects":{"identifier":"allow-set-effects","description":"Enables the set_effects command without any pre-configured scope.","commands":{"allow":["set_effects"],"deny":[]}},"allow-set-enabled":{"identifier":"allow-set-enabled","description":"Enables the set_enabled command without any pre-configured scope.","commands":{"allow":["set_enabled"],"deny":[]}},"allow-set-focus":{"identifier":"allow-set-focus","description":"Enables the set_focus command without any pre-configured scope.","commands":{"allow":["set_focus"],"deny":[]}},"allow-set-fullscreen":{"identifier":"allow-set-fullscreen","description":"Enables the set_fullscreen command without any pre-configured scope.","commands":{"allow":["set_fullscreen"],"deny":[]}},"allow-set-icon":{"identifier":"allow-set-icon","description":"Enables the set_icon command without any pre-configured scope.","commands":{"allow":["set_icon"],"deny":[]}},"allow-set-ignore-cursor-events":{"identifier":"allow-set-ignore-cursor-events","description":"Enables the set_ignore_cursor_events command without any pre-configured scope.","commands":{"allow":["set_ignore_cursor_events"],"deny":[]}},"allow-set-max-size":{"identifier":"allow-set-max-size","description":"Enables the set_max_size command without any pre-configured scope.","commands":{"allow":["set_max_size"],"deny":[]}},"allow-set-maximizable":{"identifier":"allow-set-maximizable","description":"Enables the set_maximizable command without any pre-configured scope.","commands":{"allow":["set_maximizable"],"deny":[]}},"allow-set-min-size":{"identifier":"allow-set-min-size","description":"Enables the set_min_size command without any pre-configured scope.","commands":{"allow":["set_min_size"],"deny":[]}},"allow-set-minimizable":{"identifier":"allow-set-minimizable","description":"Enables the set_minimizable command without any pre-configured scope.","commands":{"allow":["set_minimizable"],"deny":[]}},"allow-set-overlay-icon":{"identifier":"allow-set-overlay-icon","description":"Enables the set_overlay_icon command without any pre-configured scope.","commands":{"allow":["set_overlay_icon"],"deny":[]}},"allow-set-position":{"identifier":"allow-set-position","description":"Enables the set_position command without any pre-configured scope.","commands":{"allow":["set_position"],"deny":[]}},"allow-set-progress-bar":{"identifier":"allow-set-progress-bar","description":"Enables the set_progress_bar command without any pre-configured scope.","commands":{"allow":["set_progress_bar"],"deny":[]}},"allow-set-resizable":{"identifier":"allow-set-resizable","description":"Enables the set_resizable command without any pre-configured scope.","commands":{"allow":["set_resizable"],"deny":[]}},"allow-set-shadow":{"identifier":"allow-set-shadow","description":"Enables the set_shadow command without any pre-configured scope.","commands":{"allow":["set_shadow"],"deny":[]}},"allow-set-size":{"identifier":"allow-set-size","description":"Enables the set_size command without any pre-configured scope.","commands":{"allow":["set_size"],"deny":[]}},"allow-set-size-constraints":{"identifier":"allow-set-size-constraints","description":"Enables the set_size_constraints command without any pre-configured scope.","commands":{"allow":["set_size_constraints"],"deny":[]}},"allow-set-skip-taskbar":{"identifier":"allow-set-skip-taskbar","description":"Enables the set_skip_taskbar command without any pre-configured scope.","commands":{"allow":["set_skip_taskbar"],"deny":[]}},"allow-set-theme":{"identifier":"allow-set-theme","description":"Enables the set_theme command without any pre-configured scope.","commands":{"allow":["set_theme"],"deny":[]}},"allow-set-title":{"identifier":"allow-set-title","description":"Enables the set_title command without any pre-configured scope.","commands":{"allow":["set_title"],"deny":[]}},"allow-set-title-bar-style":{"identifier":"allow-set-title-bar-style","description":"Enables the set_title_bar_style command without any pre-configured scope.","commands":{"allow":["set_title_bar_style"],"deny":[]}},"allow-set-visible-on-all-workspaces":{"identifier":"allow-set-visible-on-all-workspaces","description":"Enables the set_visible_on_all_workspaces command without any pre-configured scope.","commands":{"allow":["set_visible_on_all_workspaces"],"deny":[]}},"allow-show":{"identifier":"allow-show","description":"Enables the show command without any pre-configured scope.","commands":{"allow":["show"],"deny":[]}},"allow-start-dragging":{"identifier":"allow-start-dragging","description":"Enables the start_dragging command without any pre-configured scope.","commands":{"allow":["start_dragging"],"deny":[]}},"allow-start-resize-dragging":{"identifier":"allow-start-resize-dragging","description":"Enables the start_resize_dragging command without any pre-configured scope.","commands":{"allow":["start_resize_dragging"],"deny":[]}},"allow-theme":{"identifier":"allow-theme","description":"Enables the theme command without any pre-configured scope.","commands":{"allow":["theme"],"deny":[]}},"allow-title":{"identifier":"allow-title","description":"Enables the title command without any pre-configured scope.","commands":{"allow":["title"],"deny":[]}},"allow-toggle-maximize":{"identifier":"allow-toggle-maximize","description":"Enables the toggle_maximize command without any pre-configured scope.","commands":{"allow":["toggle_maximize"],"deny":[]}},"allow-unmaximize":{"identifier":"allow-unmaximize","description":"Enables the unmaximize command without any pre-configured scope.","commands":{"allow":["unmaximize"],"deny":[]}},"allow-unminimize":{"identifier":"allow-unminimize","description":"Enables the unminimize command without any pre-configured scope.","commands":{"allow":["unminimize"],"deny":[]}},"deny-available-monitors":{"identifier":"deny-available-monitors","description":"Denies the available_monitors command without any pre-configured scope.","commands":{"allow":[],"deny":["available_monitors"]}},"deny-center":{"identifier":"deny-center","description":"Denies the center command without any pre-configured scope.","commands":{"allow":[],"deny":["center"]}},"deny-close":{"identifier":"deny-close","description":"Denies the close command without any pre-configured scope.","commands":{"allow":[],"deny":["close"]}},"deny-create":{"identifier":"deny-create","description":"Denies the create command without any pre-configured scope.","commands":{"allow":[],"deny":["create"]}},"deny-current-monitor":{"identifier":"deny-current-monitor","description":"Denies the current_monitor command without any pre-configured scope.","commands":{"allow":[],"deny":["current_monitor"]}},"deny-cursor-position":{"identifier":"deny-cursor-position","description":"Denies the cursor_position command without any pre-configured scope.","commands":{"allow":[],"deny":["cursor_position"]}},"deny-destroy":{"identifier":"deny-destroy","description":"Denies the destroy command without any pre-configured scope.","commands":{"allow":[],"deny":["destroy"]}},"deny-get-all-windows":{"identifier":"deny-get-all-windows","description":"Denies the get_all_windows command without any pre-configured scope.","commands":{"allow":[],"deny":["get_all_windows"]}},"deny-hide":{"identifier":"deny-hide","description":"Denies the hide command without any pre-configured scope.","commands":{"allow":[],"deny":["hide"]}},"deny-inner-position":{"identifier":"deny-inner-position","description":"Denies the inner_position command without any pre-configured scope.","commands":{"allow":[],"deny":["inner_position"]}},"deny-inner-size":{"identifier":"deny-inner-size","description":"Denies the inner_size command without any pre-configured scope.","commands":{"allow":[],"deny":["inner_size"]}},"deny-internal-toggle-maximize":{"identifier":"deny-internal-toggle-maximize","description":"Denies the internal_toggle_maximize command without any pre-configured scope.","commands":{"allow":[],"deny":["internal_toggle_maximize"]}},"deny-is-always-on-top":{"identifier":"deny-is-always-on-top","description":"Denies the is_always_on_top command without any pre-configured scope.","commands":{"allow":[],"deny":["is_always_on_top"]}},"deny-is-closable":{"identifier":"deny-is-closable","description":"Denies the is_closable command without any pre-configured scope.","commands":{"allow":[],"deny":["is_closable"]}},"deny-is-decorated":{"identifier":"deny-is-decorated","description":"Denies the is_decorated command without any pre-configured scope.","commands":{"allow":[],"deny":["is_decorated"]}},"deny-is-enabled":{"identifier":"deny-is-enabled","description":"Denies the is_enabled command without any pre-configured scope.","commands":{"allow":[],"deny":["is_enabled"]}},"deny-is-focused":{"identifier":"deny-is-focused","description":"Denies the is_focused command without any pre-configured scope.","commands":{"allow":[],"deny":["is_focused"]}},"deny-is-fullscreen":{"identifier":"deny-is-fullscreen","description":"Denies the is_fullscreen command without any pre-configured scope.","commands":{"allow":[],"deny":["is_fullscreen"]}},"deny-is-maximizable":{"identifier":"deny-is-maximizable","description":"Denies the is_maximizable command without any pre-configured scope.","commands":{"allow":[],"deny":["is_maximizable"]}},"deny-is-maximized":{"identifier":"deny-is-maximized","description":"Denies the is_maximized command without any pre-configured scope.","commands":{"allow":[],"deny":["is_maximized"]}},"deny-is-minimizable":{"identifier":"deny-is-minimizable","description":"Denies the is_minimizable command without any pre-configured scope.","commands":{"allow":[],"deny":["is_minimizable"]}},"deny-is-minimized":{"identifier":"deny-is-minimized","description":"Denies the is_minimized command without any pre-configured scope.","commands":{"allow":[],"deny":["is_minimized"]}},"deny-is-resizable":{"identifier":"deny-is-resizable","description":"Denies the is_resizable command without any pre-configured scope.","commands":{"allow":[],"deny":["is_resizable"]}},"deny-is-visible":{"identifier":"deny-is-visible","description":"Denies the is_visible command without any pre-configured scope.","commands":{"allow":[],"deny":["is_visible"]}},"deny-maximize":{"identifier":"deny-maximize","description":"Denies the maximize command without any pre-configured scope.","commands":{"allow":[],"deny":["maximize"]}},"deny-minimize":{"identifier":"deny-minimize","description":"Denies the minimize command without any pre-configured scope.","commands":{"allow":[],"deny":["minimize"]}},"deny-monitor-from-point":{"identifier":"deny-monitor-from-point","description":"Denies the monitor_from_point command without any pre-configured scope.","commands":{"allow":[],"deny":["monitor_from_point"]}},"deny-outer-position":{"identifier":"deny-outer-position","description":"Denies the outer_position command without any pre-configured scope.","commands":{"allow":[],"deny":["outer_position"]}},"deny-outer-size":{"identifier":"deny-outer-size","description":"Denies the outer_size command without any pre-configured scope.","commands":{"allow":[],"deny":["outer_size"]}},"deny-primary-monitor":{"identifier":"deny-primary-monitor","description":"Denies the primary_monitor command without any pre-configured scope.","commands":{"allow":[],"deny":["primary_monitor"]}},"deny-request-user-attention":{"identifier":"deny-request-user-attention","description":"Denies the request_user_attention command without any pre-configured scope.","commands":{"allow":[],"deny":["request_user_attention"]}},"deny-scale-factor":{"identifier":"deny-scale-factor","description":"Denies the scale_factor command without any pre-configured scope.","commands":{"allow":[],"deny":["scale_factor"]}},"deny-set-always-on-bottom":{"identifier":"deny-set-always-on-bottom","description":"Denies the set_always_on_bottom command without any pre-configured scope.","commands":{"allow":[],"deny":["set_always_on_bottom"]}},"deny-set-always-on-top":{"identifier":"deny-set-always-on-top","description":"Denies the set_always_on_top command without any pre-configured scope.","commands":{"allow":[],"deny":["set_always_on_top"]}},"deny-set-background-color":{"identifier":"deny-set-background-color","description":"Denies the set_background_color command without any pre-configured scope.","commands":{"allow":[],"deny":["set_background_color"]}},"deny-set-badge-count":{"identifier":"deny-set-badge-count","description":"Denies the set_badge_count command without any pre-configured scope.","commands":{"allow":[],"deny":["set_badge_count"]}},"deny-set-badge-label":{"identifier":"deny-set-badge-label","description":"Denies the set_badge_label command without any pre-configured scope.","commands":{"allow":[],"deny":["set_badge_label"]}},"deny-set-closable":{"identifier":"deny-set-closable","description":"Denies the set_closable command without any pre-configured scope.","commands":{"allow":[],"deny":["set_closable"]}},"deny-set-content-protected":{"identifier":"deny-set-content-protected","description":"Denies the set_content_protected command without any pre-configured scope.","commands":{"allow":[],"deny":["set_content_protected"]}},"deny-set-cursor-grab":{"identifier":"deny-set-cursor-grab","description":"Denies the set_cursor_grab command without any pre-configured scope.","commands":{"allow":[],"deny":["set_cursor_grab"]}},"deny-set-cursor-icon":{"identifier":"deny-set-cursor-icon","description":"Denies the set_cursor_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["set_cursor_icon"]}},"deny-set-cursor-position":{"identifier":"deny-set-cursor-position","description":"Denies the set_cursor_position command without any pre-configured scope.","commands":{"allow":[],"deny":["set_cursor_position"]}},"deny-set-cursor-visible":{"identifier":"deny-set-cursor-visible","description":"Denies the set_cursor_visible command without any pre-configured scope.","commands":{"allow":[],"deny":["set_cursor_visible"]}},"deny-set-decorations":{"identifier":"deny-set-decorations","description":"Denies the set_decorations command without any pre-configured scope.","commands":{"allow":[],"deny":["set_decorations"]}},"deny-set-effects":{"identifier":"deny-set-effects","description":"Denies the set_effects command without any pre-configured scope.","commands":{"allow":[],"deny":["set_effects"]}},"deny-set-enabled":{"identifier":"deny-set-enabled","description":"Denies the set_enabled command without any pre-configured scope.","commands":{"allow":[],"deny":["set_enabled"]}},"deny-set-focus":{"identifier":"deny-set-focus","description":"Denies the set_focus command without any pre-configured scope.","commands":{"allow":[],"deny":["set_focus"]}},"deny-set-fullscreen":{"identifier":"deny-set-fullscreen","description":"Denies the set_fullscreen command without any pre-configured scope.","commands":{"allow":[],"deny":["set_fullscreen"]}},"deny-set-icon":{"identifier":"deny-set-icon","description":"Denies the set_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["set_icon"]}},"deny-set-ignore-cursor-events":{"identifier":"deny-set-ignore-cursor-events","description":"Denies the set_ignore_cursor_events command without any pre-configured scope.","commands":{"allow":[],"deny":["set_ignore_cursor_events"]}},"deny-set-max-size":{"identifier":"deny-set-max-size","description":"Denies the set_max_size command without any pre-configured scope.","commands":{"allow":[],"deny":["set_max_size"]}},"deny-set-maximizable":{"identifier":"deny-set-maximizable","description":"Denies the set_maximizable command without any pre-configured scope.","commands":{"allow":[],"deny":["set_maximizable"]}},"deny-set-min-size":{"identifier":"deny-set-min-size","description":"Denies the set_min_size command without any pre-configured scope.","commands":{"allow":[],"deny":["set_min_size"]}},"deny-set-minimizable":{"identifier":"deny-set-minimizable","description":"Denies the set_minimizable command without any pre-configured scope.","commands":{"allow":[],"deny":["set_minimizable"]}},"deny-set-overlay-icon":{"identifier":"deny-set-overlay-icon","description":"Denies the set_overlay_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["set_overlay_icon"]}},"deny-set-position":{"identifier":"deny-set-position","description":"Denies the set_position command without any pre-configured scope.","commands":{"allow":[],"deny":["set_position"]}},"deny-set-progress-bar":{"identifier":"deny-set-progress-bar","description":"Denies the set_progress_bar command without any pre-configured scope.","commands":{"allow":[],"deny":["set_progress_bar"]}},"deny-set-resizable":{"identifier":"deny-set-resizable","description":"Denies the set_resizable command without any pre-configured scope.","commands":{"allow":[],"deny":["set_resizable"]}},"deny-set-shadow":{"identifier":"deny-set-shadow","description":"Denies the set_shadow command without any pre-configured scope.","commands":{"allow":[],"deny":["set_shadow"]}},"deny-set-size":{"identifier":"deny-set-size","description":"Denies the set_size command without any pre-configured scope.","commands":{"allow":[],"deny":["set_size"]}},"deny-set-size-constraints":{"identifier":"deny-set-size-constraints","description":"Denies the set_size_constraints command without any pre-configured scope.","commands":{"allow":[],"deny":["set_size_constraints"]}},"deny-set-skip-taskbar":{"identifier":"deny-set-skip-taskbar","description":"Denies the set_skip_taskbar command without any pre-configured scope.","commands":{"allow":[],"deny":["set_skip_taskbar"]}},"deny-set-theme":{"identifier":"deny-set-theme","description":"Denies the set_theme command without any pre-configured scope.","commands":{"allow":[],"deny":["set_theme"]}},"deny-set-title":{"identifier":"deny-set-title","description":"Denies the set_title command without any pre-configured scope.","commands":{"allow":[],"deny":["set_title"]}},"deny-set-title-bar-style":{"identifier":"deny-set-title-bar-style","description":"Denies the set_title_bar_style command without any pre-configured scope.","commands":{"allow":[],"deny":["set_title_bar_style"]}},"deny-set-visible-on-all-workspaces":{"identifier":"deny-set-visible-on-all-workspaces","description":"Denies the set_visible_on_all_workspaces command without any pre-configured scope.","commands":{"allow":[],"deny":["set_visible_on_all_workspaces"]}},"deny-show":{"identifier":"deny-show","description":"Denies the show command without any pre-configured scope.","commands":{"allow":[],"deny":["show"]}},"deny-start-dragging":{"identifier":"deny-start-dragging","description":"Denies the start_dragging command without any pre-configured scope.","commands":{"allow":[],"deny":["start_dragging"]}},"deny-start-resize-dragging":{"identifier":"deny-start-resize-dragging","description":"Denies the start_resize_dragging command without any pre-configured scope.","commands":{"allow":[],"deny":["start_resize_dragging"]}},"deny-theme":{"identifier":"deny-theme","description":"Denies the theme command without any pre-configured scope.","commands":{"allow":[],"deny":["theme"]}},"deny-title":{"identifier":"deny-title","description":"Denies the title command without any pre-configured scope.","commands":{"allow":[],"deny":["title"]}},"deny-toggle-maximize":{"identifier":"deny-toggle-maximize","description":"Denies the toggle_maximize command without any pre-configured scope.","commands":{"allow":[],"deny":["toggle_maximize"]}},"deny-unmaximize":{"identifier":"deny-unmaximize","description":"Denies the unmaximize command without any pre-configured scope.","commands":{"allow":[],"deny":["unmaximize"]}},"deny-unminimize":{"identifier":"deny-unminimize","description":"Denies the unminimize command without any pre-configured scope.","commands":{"allow":[],"deny":["unminimize"]}}},"permission_sets":{},"global_scope_schema":null},"process":{"default_permission":{"identifier":"default","description":"This permission set configures which\nprocess features are by default exposed.\n\n#### Granted Permissions\n\nThis enables to quit via `allow-exit` and restart via `allow-restart`\nthe application.\n","permissions":["allow-exit","allow-restart"]},"permissions":{"allow-exit":{"identifier":"allow-exit","description":"Enables the exit command without any pre-configured scope.","commands":{"allow":["exit"],"deny":[]}},"allow-restart":{"identifier":"allow-restart","description":"Enables the restart command without any pre-configured scope.","commands":{"allow":["restart"],"deny":[]}},"deny-exit":{"identifier":"deny-exit","description":"Denies the exit command without any pre-configured scope.","commands":{"allow":[],"deny":["exit"]}},"deny-restart":{"identifier":"deny-restart","description":"Denies the restart command without any pre-configured scope.","commands":{"allow":[],"deny":["restart"]}}},"permission_sets":{},"global_scope_schema":null},"shell":{"default_permission":{"identifier":"default","description":"This permission set configures which\nshell functionality is exposed by default.\n\n#### Granted Permissions\n\nIt allows to use the `open` functionality with a reasonable\nscope pre-configured. It will allow opening `http(s)://`,\n`tel:` and `mailto:` links.\n","permissions":["allow-open"]},"permissions":{"allow-execute":{"identifier":"allow-execute","description":"Enables the execute command without any pre-configured scope.","commands":{"allow":["execute"],"deny":[]}},"allow-kill":{"identifier":"allow-kill","description":"Enables the kill command without any pre-configured scope.","commands":{"allow":["kill"],"deny":[]}},"allow-open":{"identifier":"allow-open","description":"Enables the open command without any pre-configured scope.","commands":{"allow":["open"],"deny":[]}},"allow-spawn":{"identifier":"allow-spawn","description":"Enables the spawn command without any pre-configured scope.","commands":{"allow":["spawn"],"deny":[]}},"allow-stdin-write":{"identifier":"allow-stdin-write","description":"Enables the stdin_write command without any pre-configured scope.","commands":{"allow":["stdin_write"],"deny":[]}},"deny-execute":{"identifier":"deny-execute","description":"Denies the execute command without any pre-configured scope.","commands":{"allow":[],"deny":["execute"]}},"deny-kill":{"identifier":"deny-kill","description":"Denies the kill command without any pre-configured scope.","commands":{"allow":[],"deny":["kill"]}},"deny-open":{"identifier":"deny-open","description":"Denies the open command without any pre-configured scope.","commands":{"allow":[],"deny":["open"]}},"deny-spawn":{"identifier":"deny-spawn","description":"Denies the spawn command without any pre-configured scope.","commands":{"allow":[],"deny":["spawn"]}},"deny-stdin-write":{"identifier":"deny-stdin-write","description":"Denies the stdin_write command without any pre-configured scope.","commands":{"allow":[],"deny":["stdin_write"]}}},"permission_sets":{},"global_scope_schema":{"$schema":"http://json-schema.org/draft-07/schema#","anyOf":[{"additionalProperties":false,"properties":{"args":{"allOf":[{"$ref":"#/definitions/ShellScopeEntryAllowedArgs"}],"description":"The allowed arguments for the command execution."},"cmd":{"description":"The command name. It can start with a variable that resolves to a system base directory. The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`, `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`, `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`, `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.","type":"string"},"name":{"description":"The name for this allowed shell command configuration.\n\nThis name will be used inside of the webview API to call this command along with any specified arguments.","type":"string"}},"required":["cmd","name"],"type":"object"},{"additionalProperties":false,"properties":{"args":{"allOf":[{"$ref":"#/definitions/ShellScopeEntryAllowedArgs"}],"description":"The allowed arguments for the command execution."},"name":{"description":"The name for this allowed shell command configuration.\n\nThis name will be used inside of the webview API to call this command along with any specified arguments.","type":"string"},"sidecar":{"description":"If this command is a sidecar command.","type":"boolean"}},"required":["name","sidecar"],"type":"object"}],"definitions":{"ShellScopeEntryAllowedArg":{"anyOf":[{"description":"A non-configurable argument that is passed to the command in the order it was specified.","type":"string"},{"additionalProperties":false,"description":"A variable that is set while calling the command from the webview API.","properties":{"raw":{"default":false,"description":"Marks the validator as a raw regex, meaning the plugin should not make any modification at runtime.\n\nThis means the regex will not match on the entire string by default, which might be exploited if your regex allow unexpected input to be considered valid. When using this option, make sure your regex is correct.","type":"boolean"},"validator":{"description":"[regex] validator to require passed values to conform to an expected input.\n\nThis will require the argument value passed to this variable to match the `validator` regex before it will be executed.\n\nThe regex string is by default surrounded by `^...$` to match the full string. For example the `https?://\\w+` regex would be registered as `^https?://\\w+$`.\n\n[regex]: ","type":"string"}},"required":["validator"],"type":"object"}],"description":"A command argument allowed to be executed by the webview API."},"ShellScopeEntryAllowedArgs":{"anyOf":[{"description":"Use a simple boolean to allow all or disable all arguments to this command configuration.","type":"boolean"},{"description":"A specific set of [`ShellScopeEntryAllowedArg`] that are valid to call for the command configuration.","items":{"$ref":"#/definitions/ShellScopeEntryAllowedArg"},"type":"array"}],"description":"A set of command arguments allowed to be executed by the webview API.\n\nA value of `true` will allow any arguments to be passed to the command. `false` will disable all arguments. A list of [`ShellScopeEntryAllowedArg`] will set those arguments as the only valid arguments to be passed to the attached command configuration."}},"description":"Shell scope entry.","title":"ShellScopeEntry"}},"updater":{"default_permission":{"identifier":"default","description":"This permission set configures which kind of\nupdater functions are exposed to the frontend.\n\n#### Granted Permissions\n\nThe full workflow from checking for updates to installing them\nis enabled.\n\n","permissions":["allow-check","allow-download","allow-install","allow-download-and-install"]},"permissions":{"allow-check":{"identifier":"allow-check","description":"Enables the check command without any pre-configured scope.","commands":{"allow":["check"],"deny":[]}},"allow-download":{"identifier":"allow-download","description":"Enables the download command without any pre-configured scope.","commands":{"allow":["download"],"deny":[]}},"allow-download-and-install":{"identifier":"allow-download-and-install","description":"Enables the download_and_install command without any pre-configured scope.","commands":{"allow":["download_and_install"],"deny":[]}},"allow-install":{"identifier":"allow-install","description":"Enables the install command without any pre-configured scope.","commands":{"allow":["install"],"deny":[]}},"deny-check":{"identifier":"deny-check","description":"Denies the check command without any pre-configured scope.","commands":{"allow":[],"deny":["check"]}},"deny-download":{"identifier":"deny-download","description":"Denies the download command without any pre-configured scope.","commands":{"allow":[],"deny":["download"]}},"deny-download-and-install":{"identifier":"deny-download-and-install","description":"Denies the download_and_install command without any pre-configured scope.","commands":{"allow":[],"deny":["download_and_install"]}},"deny-install":{"identifier":"deny-install","description":"Denies the install command without any pre-configured scope.","commands":{"allow":[],"deny":["install"]}}},"permission_sets":{},"global_scope_schema":null}} \ No newline at end of file diff --git a/nym-wallet/src-tauri/gen/schemas/desktop-schema.json b/nym-wallet/src-tauri/gen/schemas/desktop-schema.json index 5ff69983d7..fa33888ae0 100644 --- a/nym-wallet/src-tauri/gen/schemas/desktop-schema.json +++ b/nym-wallet/src-tauri/gen/schemas/desktop-schema.json @@ -140,59 +140,70 @@ "identifier": { "anyOf": [ { - "description": "This permission set configures which\nshell functionality is exposed by default.\n\n#### Granted Permissions\n\nIt allows to use the `open` functionality without any specific\nscope pre-configured. It will allow opening `http(s)://`,\n`tel:` and `mailto:` links.\n", + "description": "This permission set configures which\nshell functionality is exposed by default.\n\n#### Granted Permissions\n\nIt allows to use the `open` functionality with a reasonable\nscope pre-configured. It will allow opening `http(s)://`,\n`tel:` and `mailto:` links.\n\n#### This default permission set includes:\n\n- `allow-open`", "type": "string", - "const": "shell:default" + "const": "shell:default", + "markdownDescription": "This permission set configures which\nshell functionality is exposed by default.\n\n#### Granted Permissions\n\nIt allows to use the `open` functionality with a reasonable\nscope pre-configured. It will allow opening `http(s)://`,\n`tel:` and `mailto:` links.\n\n#### This default permission set includes:\n\n- `allow-open`" }, { "description": "Enables the execute command without any pre-configured scope.", "type": "string", - "const": "shell:allow-execute" + "const": "shell:allow-execute", + "markdownDescription": "Enables the execute command without any pre-configured scope." }, { "description": "Enables the kill command without any pre-configured scope.", "type": "string", - "const": "shell:allow-kill" + "const": "shell:allow-kill", + "markdownDescription": "Enables the kill command without any pre-configured scope." }, { "description": "Enables the open command without any pre-configured scope.", "type": "string", - "const": "shell:allow-open" + "const": "shell:allow-open", + "markdownDescription": "Enables the open command without any pre-configured scope." }, { "description": "Enables the spawn command without any pre-configured scope.", "type": "string", - "const": "shell:allow-spawn" + "const": "shell:allow-spawn", + "markdownDescription": "Enables the spawn command without any pre-configured scope." }, { "description": "Enables the stdin_write command without any pre-configured scope.", "type": "string", - "const": "shell:allow-stdin-write" + "const": "shell:allow-stdin-write", + "markdownDescription": "Enables the stdin_write command without any pre-configured scope." }, { "description": "Denies the execute command without any pre-configured scope.", "type": "string", - "const": "shell:deny-execute" + "const": "shell:deny-execute", + "markdownDescription": "Denies the execute command without any pre-configured scope." }, { "description": "Denies the kill command without any pre-configured scope.", "type": "string", - "const": "shell:deny-kill" + "const": "shell:deny-kill", + "markdownDescription": "Denies the kill command without any pre-configured scope." }, { "description": "Denies the open command without any pre-configured scope.", "type": "string", - "const": "shell:deny-open" + "const": "shell:deny-open", + "markdownDescription": "Denies the open command without any pre-configured scope." }, { "description": "Denies the spawn command without any pre-configured scope.", "type": "string", - "const": "shell:deny-spawn" + "const": "shell:deny-spawn", + "markdownDescription": "Denies the spawn command without any pre-configured scope." }, { "description": "Denies the stdin_write command without any pre-configured scope.", "type": "string", - "const": "shell:deny-stdin-write" + "const": "shell:deny-stdin-write", + "markdownDescription": "Denies the stdin_write command without any pre-configured scope." } ] } @@ -378,1762 +389,2114 @@ { "description": "No features are enabled by default, as we believe\nthe clipboard can be inherently dangerous and it is \napplication specific if read and/or write access is needed.\n\nClipboard interaction needs to be explicitly enabled.\n", "type": "string", - "const": "clipboard-manager:default" + "const": "clipboard-manager:default", + "markdownDescription": "No features are enabled by default, as we believe\nthe clipboard can be inherently dangerous and it is \napplication specific if read and/or write access is needed.\n\nClipboard interaction needs to be explicitly enabled.\n" }, { "description": "Enables the clear command without any pre-configured scope.", "type": "string", - "const": "clipboard-manager:allow-clear" + "const": "clipboard-manager:allow-clear", + "markdownDescription": "Enables the clear command without any pre-configured scope." }, { "description": "Enables the read_image command without any pre-configured scope.", "type": "string", - "const": "clipboard-manager:allow-read-image" + "const": "clipboard-manager:allow-read-image", + "markdownDescription": "Enables the read_image command without any pre-configured scope." }, { "description": "Enables the read_text command without any pre-configured scope.", "type": "string", - "const": "clipboard-manager:allow-read-text" + "const": "clipboard-manager:allow-read-text", + "markdownDescription": "Enables the read_text command without any pre-configured scope." }, { "description": "Enables the write_html command without any pre-configured scope.", "type": "string", - "const": "clipboard-manager:allow-write-html" + "const": "clipboard-manager:allow-write-html", + "markdownDescription": "Enables the write_html command without any pre-configured scope." }, { "description": "Enables the write_image command without any pre-configured scope.", "type": "string", - "const": "clipboard-manager:allow-write-image" + "const": "clipboard-manager:allow-write-image", + "markdownDescription": "Enables the write_image command without any pre-configured scope." }, { "description": "Enables the write_text command without any pre-configured scope.", "type": "string", - "const": "clipboard-manager:allow-write-text" + "const": "clipboard-manager:allow-write-text", + "markdownDescription": "Enables the write_text command without any pre-configured scope." }, { "description": "Denies the clear command without any pre-configured scope.", "type": "string", - "const": "clipboard-manager:deny-clear" + "const": "clipboard-manager:deny-clear", + "markdownDescription": "Denies the clear command without any pre-configured scope." }, { "description": "Denies the read_image command without any pre-configured scope.", "type": "string", - "const": "clipboard-manager:deny-read-image" + "const": "clipboard-manager:deny-read-image", + "markdownDescription": "Denies the read_image command without any pre-configured scope." }, { "description": "Denies the read_text command without any pre-configured scope.", "type": "string", - "const": "clipboard-manager:deny-read-text" + "const": "clipboard-manager:deny-read-text", + "markdownDescription": "Denies the read_text command without any pre-configured scope." }, { "description": "Denies the write_html command without any pre-configured scope.", "type": "string", - "const": "clipboard-manager:deny-write-html" + "const": "clipboard-manager:deny-write-html", + "markdownDescription": "Denies the write_html command without any pre-configured scope." }, { "description": "Denies the write_image command without any pre-configured scope.", "type": "string", - "const": "clipboard-manager:deny-write-image" + "const": "clipboard-manager:deny-write-image", + "markdownDescription": "Denies the write_image command without any pre-configured scope." }, { "description": "Denies the write_text command without any pre-configured scope.", "type": "string", - "const": "clipboard-manager:deny-write-text" + "const": "clipboard-manager:deny-write-text", + "markdownDescription": "Denies the write_text command without any pre-configured scope." }, { - "description": "Default core plugins set which includes:\n- 'core:path:default'\n- 'core:event:default'\n- 'core:window:default'\n- 'core:webview:default'\n- 'core:app:default'\n- 'core:image:default'\n- 'core:resources:default'\n- 'core:menu:default'\n- 'core:tray:default'\n", + "description": "Default core plugins set.\n#### This default permission set includes:\n\n- `core:path:default`\n- `core:event:default`\n- `core:window:default`\n- `core:webview:default`\n- `core:app:default`\n- `core:image:default`\n- `core:resources:default`\n- `core:menu:default`\n- `core:tray:default`", "type": "string", - "const": "core:default" + "const": "core:default", + "markdownDescription": "Default core plugins set.\n#### This default permission set includes:\n\n- `core:path:default`\n- `core:event:default`\n- `core:window:default`\n- `core:webview:default`\n- `core:app:default`\n- `core:image:default`\n- `core:resources:default`\n- `core:menu:default`\n- `core:tray:default`" }, { - "description": "Default permissions for the plugin.", + "description": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-version`\n- `allow-name`\n- `allow-tauri-version`\n- `allow-identifier`", "type": "string", - "const": "core:app:default" + "const": "core:app:default", + "markdownDescription": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-version`\n- `allow-name`\n- `allow-tauri-version`\n- `allow-identifier`" }, { "description": "Enables the app_hide command without any pre-configured scope.", "type": "string", - "const": "core:app:allow-app-hide" + "const": "core:app:allow-app-hide", + "markdownDescription": "Enables the app_hide command without any pre-configured scope." }, { "description": "Enables the app_show command without any pre-configured scope.", "type": "string", - "const": "core:app:allow-app-show" + "const": "core:app:allow-app-show", + "markdownDescription": "Enables the app_show command without any pre-configured scope." }, { "description": "Enables the default_window_icon command without any pre-configured scope.", "type": "string", - "const": "core:app:allow-default-window-icon" + "const": "core:app:allow-default-window-icon", + "markdownDescription": "Enables the default_window_icon command without any pre-configured scope." }, { "description": "Enables the fetch_data_store_identifiers command without any pre-configured scope.", "type": "string", - "const": "core:app:allow-fetch-data-store-identifiers" + "const": "core:app:allow-fetch-data-store-identifiers", + "markdownDescription": "Enables the fetch_data_store_identifiers command without any pre-configured scope." }, { "description": "Enables the identifier command without any pre-configured scope.", "type": "string", - "const": "core:app:allow-identifier" + "const": "core:app:allow-identifier", + "markdownDescription": "Enables the identifier command without any pre-configured scope." }, { "description": "Enables the name command without any pre-configured scope.", "type": "string", - "const": "core:app:allow-name" + "const": "core:app:allow-name", + "markdownDescription": "Enables the name command without any pre-configured scope." }, { "description": "Enables the remove_data_store command without any pre-configured scope.", "type": "string", - "const": "core:app:allow-remove-data-store" + "const": "core:app:allow-remove-data-store", + "markdownDescription": "Enables the remove_data_store command without any pre-configured scope." }, { "description": "Enables the set_app_theme command without any pre-configured scope.", "type": "string", - "const": "core:app:allow-set-app-theme" + "const": "core:app:allow-set-app-theme", + "markdownDescription": "Enables the set_app_theme command without any pre-configured scope." }, { "description": "Enables the tauri_version command without any pre-configured scope.", "type": "string", - "const": "core:app:allow-tauri-version" + "const": "core:app:allow-tauri-version", + "markdownDescription": "Enables the tauri_version command without any pre-configured scope." }, { "description": "Enables the version command without any pre-configured scope.", "type": "string", - "const": "core:app:allow-version" + "const": "core:app:allow-version", + "markdownDescription": "Enables the version command without any pre-configured scope." }, { "description": "Denies the app_hide command without any pre-configured scope.", "type": "string", - "const": "core:app:deny-app-hide" + "const": "core:app:deny-app-hide", + "markdownDescription": "Denies the app_hide command without any pre-configured scope." }, { "description": "Denies the app_show command without any pre-configured scope.", "type": "string", - "const": "core:app:deny-app-show" + "const": "core:app:deny-app-show", + "markdownDescription": "Denies the app_show command without any pre-configured scope." }, { "description": "Denies the default_window_icon command without any pre-configured scope.", "type": "string", - "const": "core:app:deny-default-window-icon" + "const": "core:app:deny-default-window-icon", + "markdownDescription": "Denies the default_window_icon command without any pre-configured scope." }, { "description": "Denies the fetch_data_store_identifiers command without any pre-configured scope.", "type": "string", - "const": "core:app:deny-fetch-data-store-identifiers" + "const": "core:app:deny-fetch-data-store-identifiers", + "markdownDescription": "Denies the fetch_data_store_identifiers command without any pre-configured scope." }, { "description": "Denies the identifier command without any pre-configured scope.", "type": "string", - "const": "core:app:deny-identifier" + "const": "core:app:deny-identifier", + "markdownDescription": "Denies the identifier command without any pre-configured scope." }, { "description": "Denies the name command without any pre-configured scope.", "type": "string", - "const": "core:app:deny-name" + "const": "core:app:deny-name", + "markdownDescription": "Denies the name command without any pre-configured scope." }, { "description": "Denies the remove_data_store command without any pre-configured scope.", "type": "string", - "const": "core:app:deny-remove-data-store" + "const": "core:app:deny-remove-data-store", + "markdownDescription": "Denies the remove_data_store command without any pre-configured scope." }, { "description": "Denies the set_app_theme command without any pre-configured scope.", "type": "string", - "const": "core:app:deny-set-app-theme" + "const": "core:app:deny-set-app-theme", + "markdownDescription": "Denies the set_app_theme command without any pre-configured scope." }, { "description": "Denies the tauri_version command without any pre-configured scope.", "type": "string", - "const": "core:app:deny-tauri-version" + "const": "core:app:deny-tauri-version", + "markdownDescription": "Denies the tauri_version command without any pre-configured scope." }, { "description": "Denies the version command without any pre-configured scope.", "type": "string", - "const": "core:app:deny-version" + "const": "core:app:deny-version", + "markdownDescription": "Denies the version command without any pre-configured scope." }, { - "description": "Default permissions for the plugin.", + "description": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-listen`\n- `allow-unlisten`\n- `allow-emit`\n- `allow-emit-to`", "type": "string", - "const": "core:event:default" + "const": "core:event:default", + "markdownDescription": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-listen`\n- `allow-unlisten`\n- `allow-emit`\n- `allow-emit-to`" }, { "description": "Enables the emit command without any pre-configured scope.", "type": "string", - "const": "core:event:allow-emit" + "const": "core:event:allow-emit", + "markdownDescription": "Enables the emit command without any pre-configured scope." }, { "description": "Enables the emit_to command without any pre-configured scope.", "type": "string", - "const": "core:event:allow-emit-to" + "const": "core:event:allow-emit-to", + "markdownDescription": "Enables the emit_to command without any pre-configured scope." }, { "description": "Enables the listen command without any pre-configured scope.", "type": "string", - "const": "core:event:allow-listen" + "const": "core:event:allow-listen", + "markdownDescription": "Enables the listen command without any pre-configured scope." }, { "description": "Enables the unlisten command without any pre-configured scope.", "type": "string", - "const": "core:event:allow-unlisten" + "const": "core:event:allow-unlisten", + "markdownDescription": "Enables the unlisten command without any pre-configured scope." }, { "description": "Denies the emit command without any pre-configured scope.", "type": "string", - "const": "core:event:deny-emit" + "const": "core:event:deny-emit", + "markdownDescription": "Denies the emit command without any pre-configured scope." }, { "description": "Denies the emit_to command without any pre-configured scope.", "type": "string", - "const": "core:event:deny-emit-to" + "const": "core:event:deny-emit-to", + "markdownDescription": "Denies the emit_to command without any pre-configured scope." }, { "description": "Denies the listen command without any pre-configured scope.", "type": "string", - "const": "core:event:deny-listen" + "const": "core:event:deny-listen", + "markdownDescription": "Denies the listen command without any pre-configured scope." }, { "description": "Denies the unlisten command without any pre-configured scope.", "type": "string", - "const": "core:event:deny-unlisten" + "const": "core:event:deny-unlisten", + "markdownDescription": "Denies the unlisten command without any pre-configured scope." }, { - "description": "Default permissions for the plugin.", + "description": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-new`\n- `allow-from-bytes`\n- `allow-from-path`\n- `allow-rgba`\n- `allow-size`", "type": "string", - "const": "core:image:default" + "const": "core:image:default", + "markdownDescription": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-new`\n- `allow-from-bytes`\n- `allow-from-path`\n- `allow-rgba`\n- `allow-size`" }, { "description": "Enables the from_bytes command without any pre-configured scope.", "type": "string", - "const": "core:image:allow-from-bytes" + "const": "core:image:allow-from-bytes", + "markdownDescription": "Enables the from_bytes command without any pre-configured scope." }, { "description": "Enables the from_path command without any pre-configured scope.", "type": "string", - "const": "core:image:allow-from-path" + "const": "core:image:allow-from-path", + "markdownDescription": "Enables the from_path command without any pre-configured scope." }, { "description": "Enables the new command without any pre-configured scope.", "type": "string", - "const": "core:image:allow-new" + "const": "core:image:allow-new", + "markdownDescription": "Enables the new command without any pre-configured scope." }, { "description": "Enables the rgba command without any pre-configured scope.", "type": "string", - "const": "core:image:allow-rgba" + "const": "core:image:allow-rgba", + "markdownDescription": "Enables the rgba command without any pre-configured scope." }, { "description": "Enables the size command without any pre-configured scope.", "type": "string", - "const": "core:image:allow-size" + "const": "core:image:allow-size", + "markdownDescription": "Enables the size command without any pre-configured scope." }, { "description": "Denies the from_bytes command without any pre-configured scope.", "type": "string", - "const": "core:image:deny-from-bytes" + "const": "core:image:deny-from-bytes", + "markdownDescription": "Denies the from_bytes command without any pre-configured scope." }, { "description": "Denies the from_path command without any pre-configured scope.", "type": "string", - "const": "core:image:deny-from-path" + "const": "core:image:deny-from-path", + "markdownDescription": "Denies the from_path command without any pre-configured scope." }, { "description": "Denies the new command without any pre-configured scope.", "type": "string", - "const": "core:image:deny-new" + "const": "core:image:deny-new", + "markdownDescription": "Denies the new command without any pre-configured scope." }, { "description": "Denies the rgba command without any pre-configured scope.", "type": "string", - "const": "core:image:deny-rgba" + "const": "core:image:deny-rgba", + "markdownDescription": "Denies the rgba command without any pre-configured scope." }, { "description": "Denies the size command without any pre-configured scope.", "type": "string", - "const": "core:image:deny-size" + "const": "core:image:deny-size", + "markdownDescription": "Denies the size command without any pre-configured scope." }, { - "description": "Default permissions for the plugin.", + "description": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-new`\n- `allow-append`\n- `allow-prepend`\n- `allow-insert`\n- `allow-remove`\n- `allow-remove-at`\n- `allow-items`\n- `allow-get`\n- `allow-popup`\n- `allow-create-default`\n- `allow-set-as-app-menu`\n- `allow-set-as-window-menu`\n- `allow-text`\n- `allow-set-text`\n- `allow-is-enabled`\n- `allow-set-enabled`\n- `allow-set-accelerator`\n- `allow-set-as-windows-menu-for-nsapp`\n- `allow-set-as-help-menu-for-nsapp`\n- `allow-is-checked`\n- `allow-set-checked`\n- `allow-set-icon`", "type": "string", - "const": "core:menu:default" + "const": "core:menu:default", + "markdownDescription": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-new`\n- `allow-append`\n- `allow-prepend`\n- `allow-insert`\n- `allow-remove`\n- `allow-remove-at`\n- `allow-items`\n- `allow-get`\n- `allow-popup`\n- `allow-create-default`\n- `allow-set-as-app-menu`\n- `allow-set-as-window-menu`\n- `allow-text`\n- `allow-set-text`\n- `allow-is-enabled`\n- `allow-set-enabled`\n- `allow-set-accelerator`\n- `allow-set-as-windows-menu-for-nsapp`\n- `allow-set-as-help-menu-for-nsapp`\n- `allow-is-checked`\n- `allow-set-checked`\n- `allow-set-icon`" }, { "description": "Enables the append command without any pre-configured scope.", "type": "string", - "const": "core:menu:allow-append" + "const": "core:menu:allow-append", + "markdownDescription": "Enables the append command without any pre-configured scope." }, { "description": "Enables the create_default command without any pre-configured scope.", "type": "string", - "const": "core:menu:allow-create-default" + "const": "core:menu:allow-create-default", + "markdownDescription": "Enables the create_default command without any pre-configured scope." }, { "description": "Enables the get command without any pre-configured scope.", "type": "string", - "const": "core:menu:allow-get" + "const": "core:menu:allow-get", + "markdownDescription": "Enables the get command without any pre-configured scope." }, { "description": "Enables the insert command without any pre-configured scope.", "type": "string", - "const": "core:menu:allow-insert" + "const": "core:menu:allow-insert", + "markdownDescription": "Enables the insert command without any pre-configured scope." }, { "description": "Enables the is_checked command without any pre-configured scope.", "type": "string", - "const": "core:menu:allow-is-checked" + "const": "core:menu:allow-is-checked", + "markdownDescription": "Enables the is_checked command without any pre-configured scope." }, { "description": "Enables the is_enabled command without any pre-configured scope.", "type": "string", - "const": "core:menu:allow-is-enabled" + "const": "core:menu:allow-is-enabled", + "markdownDescription": "Enables the is_enabled command without any pre-configured scope." }, { "description": "Enables the items command without any pre-configured scope.", "type": "string", - "const": "core:menu:allow-items" + "const": "core:menu:allow-items", + "markdownDescription": "Enables the items command without any pre-configured scope." }, { "description": "Enables the new command without any pre-configured scope.", "type": "string", - "const": "core:menu:allow-new" + "const": "core:menu:allow-new", + "markdownDescription": "Enables the new command without any pre-configured scope." }, { "description": "Enables the popup command without any pre-configured scope.", "type": "string", - "const": "core:menu:allow-popup" + "const": "core:menu:allow-popup", + "markdownDescription": "Enables the popup command without any pre-configured scope." }, { "description": "Enables the prepend command without any pre-configured scope.", "type": "string", - "const": "core:menu:allow-prepend" + "const": "core:menu:allow-prepend", + "markdownDescription": "Enables the prepend command without any pre-configured scope." }, { "description": "Enables the remove command without any pre-configured scope.", "type": "string", - "const": "core:menu:allow-remove" + "const": "core:menu:allow-remove", + "markdownDescription": "Enables the remove command without any pre-configured scope." }, { "description": "Enables the remove_at command without any pre-configured scope.", "type": "string", - "const": "core:menu:allow-remove-at" + "const": "core:menu:allow-remove-at", + "markdownDescription": "Enables the remove_at command without any pre-configured scope." }, { "description": "Enables the set_accelerator command without any pre-configured scope.", "type": "string", - "const": "core:menu:allow-set-accelerator" + "const": "core:menu:allow-set-accelerator", + "markdownDescription": "Enables the set_accelerator command without any pre-configured scope." }, { "description": "Enables the set_as_app_menu command without any pre-configured scope.", "type": "string", - "const": "core:menu:allow-set-as-app-menu" + "const": "core:menu:allow-set-as-app-menu", + "markdownDescription": "Enables the set_as_app_menu command without any pre-configured scope." }, { "description": "Enables the set_as_help_menu_for_nsapp command without any pre-configured scope.", "type": "string", - "const": "core:menu:allow-set-as-help-menu-for-nsapp" + "const": "core:menu:allow-set-as-help-menu-for-nsapp", + "markdownDescription": "Enables the set_as_help_menu_for_nsapp command without any pre-configured scope." }, { "description": "Enables the set_as_window_menu command without any pre-configured scope.", "type": "string", - "const": "core:menu:allow-set-as-window-menu" + "const": "core:menu:allow-set-as-window-menu", + "markdownDescription": "Enables the set_as_window_menu command without any pre-configured scope." }, { "description": "Enables the set_as_windows_menu_for_nsapp command without any pre-configured scope.", "type": "string", - "const": "core:menu:allow-set-as-windows-menu-for-nsapp" + "const": "core:menu:allow-set-as-windows-menu-for-nsapp", + "markdownDescription": "Enables the set_as_windows_menu_for_nsapp command without any pre-configured scope." }, { "description": "Enables the set_checked command without any pre-configured scope.", "type": "string", - "const": "core:menu:allow-set-checked" + "const": "core:menu:allow-set-checked", + "markdownDescription": "Enables the set_checked command without any pre-configured scope." }, { "description": "Enables the set_enabled command without any pre-configured scope.", "type": "string", - "const": "core:menu:allow-set-enabled" + "const": "core:menu:allow-set-enabled", + "markdownDescription": "Enables the set_enabled command without any pre-configured scope." }, { "description": "Enables the set_icon command without any pre-configured scope.", "type": "string", - "const": "core:menu:allow-set-icon" + "const": "core:menu:allow-set-icon", + "markdownDescription": "Enables the set_icon command without any pre-configured scope." }, { "description": "Enables the set_text command without any pre-configured scope.", "type": "string", - "const": "core:menu:allow-set-text" + "const": "core:menu:allow-set-text", + "markdownDescription": "Enables the set_text command without any pre-configured scope." }, { "description": "Enables the text command without any pre-configured scope.", "type": "string", - "const": "core:menu:allow-text" + "const": "core:menu:allow-text", + "markdownDescription": "Enables the text command without any pre-configured scope." }, { "description": "Denies the append command without any pre-configured scope.", "type": "string", - "const": "core:menu:deny-append" + "const": "core:menu:deny-append", + "markdownDescription": "Denies the append command without any pre-configured scope." }, { "description": "Denies the create_default command without any pre-configured scope.", "type": "string", - "const": "core:menu:deny-create-default" + "const": "core:menu:deny-create-default", + "markdownDescription": "Denies the create_default command without any pre-configured scope." }, { "description": "Denies the get command without any pre-configured scope.", "type": "string", - "const": "core:menu:deny-get" + "const": "core:menu:deny-get", + "markdownDescription": "Denies the get command without any pre-configured scope." }, { "description": "Denies the insert command without any pre-configured scope.", "type": "string", - "const": "core:menu:deny-insert" + "const": "core:menu:deny-insert", + "markdownDescription": "Denies the insert command without any pre-configured scope." }, { "description": "Denies the is_checked command without any pre-configured scope.", "type": "string", - "const": "core:menu:deny-is-checked" + "const": "core:menu:deny-is-checked", + "markdownDescription": "Denies the is_checked command without any pre-configured scope." }, { "description": "Denies the is_enabled command without any pre-configured scope.", "type": "string", - "const": "core:menu:deny-is-enabled" + "const": "core:menu:deny-is-enabled", + "markdownDescription": "Denies the is_enabled command without any pre-configured scope." }, { "description": "Denies the items command without any pre-configured scope.", "type": "string", - "const": "core:menu:deny-items" + "const": "core:menu:deny-items", + "markdownDescription": "Denies the items command without any pre-configured scope." }, { "description": "Denies the new command without any pre-configured scope.", "type": "string", - "const": "core:menu:deny-new" + "const": "core:menu:deny-new", + "markdownDescription": "Denies the new command without any pre-configured scope." }, { "description": "Denies the popup command without any pre-configured scope.", "type": "string", - "const": "core:menu:deny-popup" + "const": "core:menu:deny-popup", + "markdownDescription": "Denies the popup command without any pre-configured scope." }, { "description": "Denies the prepend command without any pre-configured scope.", "type": "string", - "const": "core:menu:deny-prepend" + "const": "core:menu:deny-prepend", + "markdownDescription": "Denies the prepend command without any pre-configured scope." }, { "description": "Denies the remove command without any pre-configured scope.", "type": "string", - "const": "core:menu:deny-remove" + "const": "core:menu:deny-remove", + "markdownDescription": "Denies the remove command without any pre-configured scope." }, { "description": "Denies the remove_at command without any pre-configured scope.", "type": "string", - "const": "core:menu:deny-remove-at" + "const": "core:menu:deny-remove-at", + "markdownDescription": "Denies the remove_at command without any pre-configured scope." }, { "description": "Denies the set_accelerator command without any pre-configured scope.", "type": "string", - "const": "core:menu:deny-set-accelerator" + "const": "core:menu:deny-set-accelerator", + "markdownDescription": "Denies the set_accelerator command without any pre-configured scope." }, { "description": "Denies the set_as_app_menu command without any pre-configured scope.", "type": "string", - "const": "core:menu:deny-set-as-app-menu" + "const": "core:menu:deny-set-as-app-menu", + "markdownDescription": "Denies the set_as_app_menu command without any pre-configured scope." }, { "description": "Denies the set_as_help_menu_for_nsapp command without any pre-configured scope.", "type": "string", - "const": "core:menu:deny-set-as-help-menu-for-nsapp" + "const": "core:menu:deny-set-as-help-menu-for-nsapp", + "markdownDescription": "Denies the set_as_help_menu_for_nsapp command without any pre-configured scope." }, { "description": "Denies the set_as_window_menu command without any pre-configured scope.", "type": "string", - "const": "core:menu:deny-set-as-window-menu" + "const": "core:menu:deny-set-as-window-menu", + "markdownDescription": "Denies the set_as_window_menu command without any pre-configured scope." }, { "description": "Denies the set_as_windows_menu_for_nsapp command without any pre-configured scope.", "type": "string", - "const": "core:menu:deny-set-as-windows-menu-for-nsapp" + "const": "core:menu:deny-set-as-windows-menu-for-nsapp", + "markdownDescription": "Denies the set_as_windows_menu_for_nsapp command without any pre-configured scope." }, { "description": "Denies the set_checked command without any pre-configured scope.", "type": "string", - "const": "core:menu:deny-set-checked" + "const": "core:menu:deny-set-checked", + "markdownDescription": "Denies the set_checked command without any pre-configured scope." }, { "description": "Denies the set_enabled command without any pre-configured scope.", "type": "string", - "const": "core:menu:deny-set-enabled" + "const": "core:menu:deny-set-enabled", + "markdownDescription": "Denies the set_enabled command without any pre-configured scope." }, { "description": "Denies the set_icon command without any pre-configured scope.", "type": "string", - "const": "core:menu:deny-set-icon" + "const": "core:menu:deny-set-icon", + "markdownDescription": "Denies the set_icon command without any pre-configured scope." }, { "description": "Denies the set_text command without any pre-configured scope.", "type": "string", - "const": "core:menu:deny-set-text" + "const": "core:menu:deny-set-text", + "markdownDescription": "Denies the set_text command without any pre-configured scope." }, { "description": "Denies the text command without any pre-configured scope.", "type": "string", - "const": "core:menu:deny-text" + "const": "core:menu:deny-text", + "markdownDescription": "Denies the text command without any pre-configured scope." }, { - "description": "Default permissions for the plugin.", + "description": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-resolve-directory`\n- `allow-resolve`\n- `allow-normalize`\n- `allow-join`\n- `allow-dirname`\n- `allow-extname`\n- `allow-basename`\n- `allow-is-absolute`", "type": "string", - "const": "core:path:default" + "const": "core:path:default", + "markdownDescription": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-resolve-directory`\n- `allow-resolve`\n- `allow-normalize`\n- `allow-join`\n- `allow-dirname`\n- `allow-extname`\n- `allow-basename`\n- `allow-is-absolute`" }, { "description": "Enables the basename command without any pre-configured scope.", "type": "string", - "const": "core:path:allow-basename" + "const": "core:path:allow-basename", + "markdownDescription": "Enables the basename command without any pre-configured scope." }, { "description": "Enables the dirname command without any pre-configured scope.", "type": "string", - "const": "core:path:allow-dirname" + "const": "core:path:allow-dirname", + "markdownDescription": "Enables the dirname command without any pre-configured scope." }, { "description": "Enables the extname command without any pre-configured scope.", "type": "string", - "const": "core:path:allow-extname" + "const": "core:path:allow-extname", + "markdownDescription": "Enables the extname command without any pre-configured scope." }, { "description": "Enables the is_absolute command without any pre-configured scope.", "type": "string", - "const": "core:path:allow-is-absolute" + "const": "core:path:allow-is-absolute", + "markdownDescription": "Enables the is_absolute command without any pre-configured scope." }, { "description": "Enables the join command without any pre-configured scope.", "type": "string", - "const": "core:path:allow-join" + "const": "core:path:allow-join", + "markdownDescription": "Enables the join command without any pre-configured scope." }, { "description": "Enables the normalize command without any pre-configured scope.", "type": "string", - "const": "core:path:allow-normalize" + "const": "core:path:allow-normalize", + "markdownDescription": "Enables the normalize command without any pre-configured scope." }, { "description": "Enables the resolve command without any pre-configured scope.", "type": "string", - "const": "core:path:allow-resolve" + "const": "core:path:allow-resolve", + "markdownDescription": "Enables the resolve command without any pre-configured scope." }, { "description": "Enables the resolve_directory command without any pre-configured scope.", "type": "string", - "const": "core:path:allow-resolve-directory" + "const": "core:path:allow-resolve-directory", + "markdownDescription": "Enables the resolve_directory command without any pre-configured scope." }, { "description": "Denies the basename command without any pre-configured scope.", "type": "string", - "const": "core:path:deny-basename" + "const": "core:path:deny-basename", + "markdownDescription": "Denies the basename command without any pre-configured scope." }, { "description": "Denies the dirname command without any pre-configured scope.", "type": "string", - "const": "core:path:deny-dirname" + "const": "core:path:deny-dirname", + "markdownDescription": "Denies the dirname command without any pre-configured scope." }, { "description": "Denies the extname command without any pre-configured scope.", "type": "string", - "const": "core:path:deny-extname" + "const": "core:path:deny-extname", + "markdownDescription": "Denies the extname command without any pre-configured scope." }, { "description": "Denies the is_absolute command without any pre-configured scope.", "type": "string", - "const": "core:path:deny-is-absolute" + "const": "core:path:deny-is-absolute", + "markdownDescription": "Denies the is_absolute command without any pre-configured scope." }, { "description": "Denies the join command without any pre-configured scope.", "type": "string", - "const": "core:path:deny-join" + "const": "core:path:deny-join", + "markdownDescription": "Denies the join command without any pre-configured scope." }, { "description": "Denies the normalize command without any pre-configured scope.", "type": "string", - "const": "core:path:deny-normalize" + "const": "core:path:deny-normalize", + "markdownDescription": "Denies the normalize command without any pre-configured scope." }, { "description": "Denies the resolve command without any pre-configured scope.", "type": "string", - "const": "core:path:deny-resolve" + "const": "core:path:deny-resolve", + "markdownDescription": "Denies the resolve command without any pre-configured scope." }, { "description": "Denies the resolve_directory command without any pre-configured scope.", "type": "string", - "const": "core:path:deny-resolve-directory" + "const": "core:path:deny-resolve-directory", + "markdownDescription": "Denies the resolve_directory command without any pre-configured scope." }, { - "description": "Default permissions for the plugin.", + "description": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-close`", "type": "string", - "const": "core:resources:default" + "const": "core:resources:default", + "markdownDescription": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-close`" }, { "description": "Enables the close command without any pre-configured scope.", "type": "string", - "const": "core:resources:allow-close" + "const": "core:resources:allow-close", + "markdownDescription": "Enables the close command without any pre-configured scope." }, { "description": "Denies the close command without any pre-configured scope.", "type": "string", - "const": "core:resources:deny-close" + "const": "core:resources:deny-close", + "markdownDescription": "Denies the close command without any pre-configured scope." }, { - "description": "Default permissions for the plugin.", + "description": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-new`\n- `allow-get-by-id`\n- `allow-remove-by-id`\n- `allow-set-icon`\n- `allow-set-menu`\n- `allow-set-tooltip`\n- `allow-set-title`\n- `allow-set-visible`\n- `allow-set-temp-dir-path`\n- `allow-set-icon-as-template`\n- `allow-set-show-menu-on-left-click`", "type": "string", - "const": "core:tray:default" + "const": "core:tray:default", + "markdownDescription": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-new`\n- `allow-get-by-id`\n- `allow-remove-by-id`\n- `allow-set-icon`\n- `allow-set-menu`\n- `allow-set-tooltip`\n- `allow-set-title`\n- `allow-set-visible`\n- `allow-set-temp-dir-path`\n- `allow-set-icon-as-template`\n- `allow-set-show-menu-on-left-click`" }, { "description": "Enables the get_by_id command without any pre-configured scope.", "type": "string", - "const": "core:tray:allow-get-by-id" + "const": "core:tray:allow-get-by-id", + "markdownDescription": "Enables the get_by_id command without any pre-configured scope." }, { "description": "Enables the new command without any pre-configured scope.", "type": "string", - "const": "core:tray:allow-new" + "const": "core:tray:allow-new", + "markdownDescription": "Enables the new command without any pre-configured scope." }, { "description": "Enables the remove_by_id command without any pre-configured scope.", "type": "string", - "const": "core:tray:allow-remove-by-id" + "const": "core:tray:allow-remove-by-id", + "markdownDescription": "Enables the remove_by_id command without any pre-configured scope." }, { "description": "Enables the set_icon command without any pre-configured scope.", "type": "string", - "const": "core:tray:allow-set-icon" + "const": "core:tray:allow-set-icon", + "markdownDescription": "Enables the set_icon command without any pre-configured scope." }, { "description": "Enables the set_icon_as_template command without any pre-configured scope.", "type": "string", - "const": "core:tray:allow-set-icon-as-template" + "const": "core:tray:allow-set-icon-as-template", + "markdownDescription": "Enables the set_icon_as_template command without any pre-configured scope." }, { "description": "Enables the set_menu command without any pre-configured scope.", "type": "string", - "const": "core:tray:allow-set-menu" + "const": "core:tray:allow-set-menu", + "markdownDescription": "Enables the set_menu command without any pre-configured scope." }, { "description": "Enables the set_show_menu_on_left_click command without any pre-configured scope.", "type": "string", - "const": "core:tray:allow-set-show-menu-on-left-click" + "const": "core:tray:allow-set-show-menu-on-left-click", + "markdownDescription": "Enables the set_show_menu_on_left_click command without any pre-configured scope." }, { "description": "Enables the set_temp_dir_path command without any pre-configured scope.", "type": "string", - "const": "core:tray:allow-set-temp-dir-path" + "const": "core:tray:allow-set-temp-dir-path", + "markdownDescription": "Enables the set_temp_dir_path command without any pre-configured scope." }, { "description": "Enables the set_title command without any pre-configured scope.", "type": "string", - "const": "core:tray:allow-set-title" + "const": "core:tray:allow-set-title", + "markdownDescription": "Enables the set_title command without any pre-configured scope." }, { "description": "Enables the set_tooltip command without any pre-configured scope.", "type": "string", - "const": "core:tray:allow-set-tooltip" + "const": "core:tray:allow-set-tooltip", + "markdownDescription": "Enables the set_tooltip command without any pre-configured scope." }, { "description": "Enables the set_visible command without any pre-configured scope.", "type": "string", - "const": "core:tray:allow-set-visible" + "const": "core:tray:allow-set-visible", + "markdownDescription": "Enables the set_visible command without any pre-configured scope." }, { "description": "Denies the get_by_id command without any pre-configured scope.", "type": "string", - "const": "core:tray:deny-get-by-id" + "const": "core:tray:deny-get-by-id", + "markdownDescription": "Denies the get_by_id command without any pre-configured scope." }, { "description": "Denies the new command without any pre-configured scope.", "type": "string", - "const": "core:tray:deny-new" + "const": "core:tray:deny-new", + "markdownDescription": "Denies the new command without any pre-configured scope." }, { "description": "Denies the remove_by_id command without any pre-configured scope.", "type": "string", - "const": "core:tray:deny-remove-by-id" + "const": "core:tray:deny-remove-by-id", + "markdownDescription": "Denies the remove_by_id command without any pre-configured scope." }, { "description": "Denies the set_icon command without any pre-configured scope.", "type": "string", - "const": "core:tray:deny-set-icon" + "const": "core:tray:deny-set-icon", + "markdownDescription": "Denies the set_icon command without any pre-configured scope." }, { "description": "Denies the set_icon_as_template command without any pre-configured scope.", "type": "string", - "const": "core:tray:deny-set-icon-as-template" + "const": "core:tray:deny-set-icon-as-template", + "markdownDescription": "Denies the set_icon_as_template command without any pre-configured scope." }, { "description": "Denies the set_menu command without any pre-configured scope.", "type": "string", - "const": "core:tray:deny-set-menu" + "const": "core:tray:deny-set-menu", + "markdownDescription": "Denies the set_menu command without any pre-configured scope." }, { "description": "Denies the set_show_menu_on_left_click command without any pre-configured scope.", "type": "string", - "const": "core:tray:deny-set-show-menu-on-left-click" + "const": "core:tray:deny-set-show-menu-on-left-click", + "markdownDescription": "Denies the set_show_menu_on_left_click command without any pre-configured scope." }, { "description": "Denies the set_temp_dir_path command without any pre-configured scope.", "type": "string", - "const": "core:tray:deny-set-temp-dir-path" + "const": "core:tray:deny-set-temp-dir-path", + "markdownDescription": "Denies the set_temp_dir_path command without any pre-configured scope." }, { "description": "Denies the set_title command without any pre-configured scope.", "type": "string", - "const": "core:tray:deny-set-title" + "const": "core:tray:deny-set-title", + "markdownDescription": "Denies the set_title command without any pre-configured scope." }, { "description": "Denies the set_tooltip command without any pre-configured scope.", "type": "string", - "const": "core:tray:deny-set-tooltip" + "const": "core:tray:deny-set-tooltip", + "markdownDescription": "Denies the set_tooltip command without any pre-configured scope." }, { "description": "Denies the set_visible command without any pre-configured scope.", "type": "string", - "const": "core:tray:deny-set-visible" + "const": "core:tray:deny-set-visible", + "markdownDescription": "Denies the set_visible command without any pre-configured scope." }, { - "description": "Default permissions for the plugin.", + "description": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-get-all-webviews`\n- `allow-webview-position`\n- `allow-webview-size`\n- `allow-internal-toggle-devtools`", "type": "string", - "const": "core:webview:default" + "const": "core:webview:default", + "markdownDescription": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-get-all-webviews`\n- `allow-webview-position`\n- `allow-webview-size`\n- `allow-internal-toggle-devtools`" }, { "description": "Enables the clear_all_browsing_data command without any pre-configured scope.", "type": "string", - "const": "core:webview:allow-clear-all-browsing-data" + "const": "core:webview:allow-clear-all-browsing-data", + "markdownDescription": "Enables the clear_all_browsing_data command without any pre-configured scope." }, { "description": "Enables the create_webview command without any pre-configured scope.", "type": "string", - "const": "core:webview:allow-create-webview" + "const": "core:webview:allow-create-webview", + "markdownDescription": "Enables the create_webview command without any pre-configured scope." }, { "description": "Enables the create_webview_window command without any pre-configured scope.", "type": "string", - "const": "core:webview:allow-create-webview-window" + "const": "core:webview:allow-create-webview-window", + "markdownDescription": "Enables the create_webview_window command without any pre-configured scope." }, { "description": "Enables the get_all_webviews command without any pre-configured scope.", "type": "string", - "const": "core:webview:allow-get-all-webviews" + "const": "core:webview:allow-get-all-webviews", + "markdownDescription": "Enables the get_all_webviews command without any pre-configured scope." }, { "description": "Enables the internal_toggle_devtools command without any pre-configured scope.", "type": "string", - "const": "core:webview:allow-internal-toggle-devtools" + "const": "core:webview:allow-internal-toggle-devtools", + "markdownDescription": "Enables the internal_toggle_devtools command without any pre-configured scope." }, { "description": "Enables the print command without any pre-configured scope.", "type": "string", - "const": "core:webview:allow-print" + "const": "core:webview:allow-print", + "markdownDescription": "Enables the print command without any pre-configured scope." }, { "description": "Enables the reparent command without any pre-configured scope.", "type": "string", - "const": "core:webview:allow-reparent" + "const": "core:webview:allow-reparent", + "markdownDescription": "Enables the reparent command without any pre-configured scope." }, { "description": "Enables the set_webview_background_color command without any pre-configured scope.", "type": "string", - "const": "core:webview:allow-set-webview-background-color" + "const": "core:webview:allow-set-webview-background-color", + "markdownDescription": "Enables the set_webview_background_color command without any pre-configured scope." }, { "description": "Enables the set_webview_focus command without any pre-configured scope.", "type": "string", - "const": "core:webview:allow-set-webview-focus" + "const": "core:webview:allow-set-webview-focus", + "markdownDescription": "Enables the set_webview_focus command without any pre-configured scope." }, { "description": "Enables the set_webview_position command without any pre-configured scope.", "type": "string", - "const": "core:webview:allow-set-webview-position" + "const": "core:webview:allow-set-webview-position", + "markdownDescription": "Enables the set_webview_position command without any pre-configured scope." }, { "description": "Enables the set_webview_size command without any pre-configured scope.", "type": "string", - "const": "core:webview:allow-set-webview-size" + "const": "core:webview:allow-set-webview-size", + "markdownDescription": "Enables the set_webview_size command without any pre-configured scope." }, { "description": "Enables the set_webview_zoom command without any pre-configured scope.", "type": "string", - "const": "core:webview:allow-set-webview-zoom" + "const": "core:webview:allow-set-webview-zoom", + "markdownDescription": "Enables the set_webview_zoom command without any pre-configured scope." }, { "description": "Enables the webview_close command without any pre-configured scope.", "type": "string", - "const": "core:webview:allow-webview-close" + "const": "core:webview:allow-webview-close", + "markdownDescription": "Enables the webview_close command without any pre-configured scope." }, { "description": "Enables the webview_hide command without any pre-configured scope.", "type": "string", - "const": "core:webview:allow-webview-hide" + "const": "core:webview:allow-webview-hide", + "markdownDescription": "Enables the webview_hide command without any pre-configured scope." }, { "description": "Enables the webview_position command without any pre-configured scope.", "type": "string", - "const": "core:webview:allow-webview-position" + "const": "core:webview:allow-webview-position", + "markdownDescription": "Enables the webview_position command without any pre-configured scope." }, { "description": "Enables the webview_show command without any pre-configured scope.", "type": "string", - "const": "core:webview:allow-webview-show" + "const": "core:webview:allow-webview-show", + "markdownDescription": "Enables the webview_show command without any pre-configured scope." }, { "description": "Enables the webview_size command without any pre-configured scope.", "type": "string", - "const": "core:webview:allow-webview-size" + "const": "core:webview:allow-webview-size", + "markdownDescription": "Enables the webview_size command without any pre-configured scope." }, { "description": "Denies the clear_all_browsing_data command without any pre-configured scope.", "type": "string", - "const": "core:webview:deny-clear-all-browsing-data" + "const": "core:webview:deny-clear-all-browsing-data", + "markdownDescription": "Denies the clear_all_browsing_data command without any pre-configured scope." }, { "description": "Denies the create_webview command without any pre-configured scope.", "type": "string", - "const": "core:webview:deny-create-webview" + "const": "core:webview:deny-create-webview", + "markdownDescription": "Denies the create_webview command without any pre-configured scope." }, { "description": "Denies the create_webview_window command without any pre-configured scope.", "type": "string", - "const": "core:webview:deny-create-webview-window" + "const": "core:webview:deny-create-webview-window", + "markdownDescription": "Denies the create_webview_window command without any pre-configured scope." }, { "description": "Denies the get_all_webviews command without any pre-configured scope.", "type": "string", - "const": "core:webview:deny-get-all-webviews" + "const": "core:webview:deny-get-all-webviews", + "markdownDescription": "Denies the get_all_webviews command without any pre-configured scope." }, { "description": "Denies the internal_toggle_devtools command without any pre-configured scope.", "type": "string", - "const": "core:webview:deny-internal-toggle-devtools" + "const": "core:webview:deny-internal-toggle-devtools", + "markdownDescription": "Denies the internal_toggle_devtools command without any pre-configured scope." }, { "description": "Denies the print command without any pre-configured scope.", "type": "string", - "const": "core:webview:deny-print" + "const": "core:webview:deny-print", + "markdownDescription": "Denies the print command without any pre-configured scope." }, { "description": "Denies the reparent command without any pre-configured scope.", "type": "string", - "const": "core:webview:deny-reparent" + "const": "core:webview:deny-reparent", + "markdownDescription": "Denies the reparent command without any pre-configured scope." }, { "description": "Denies the set_webview_background_color command without any pre-configured scope.", "type": "string", - "const": "core:webview:deny-set-webview-background-color" + "const": "core:webview:deny-set-webview-background-color", + "markdownDescription": "Denies the set_webview_background_color command without any pre-configured scope." }, { "description": "Denies the set_webview_focus command without any pre-configured scope.", "type": "string", - "const": "core:webview:deny-set-webview-focus" + "const": "core:webview:deny-set-webview-focus", + "markdownDescription": "Denies the set_webview_focus command without any pre-configured scope." }, { "description": "Denies the set_webview_position command without any pre-configured scope.", "type": "string", - "const": "core:webview:deny-set-webview-position" + "const": "core:webview:deny-set-webview-position", + "markdownDescription": "Denies the set_webview_position command without any pre-configured scope." }, { "description": "Denies the set_webview_size command without any pre-configured scope.", "type": "string", - "const": "core:webview:deny-set-webview-size" + "const": "core:webview:deny-set-webview-size", + "markdownDescription": "Denies the set_webview_size command without any pre-configured scope." }, { "description": "Denies the set_webview_zoom command without any pre-configured scope.", "type": "string", - "const": "core:webview:deny-set-webview-zoom" + "const": "core:webview:deny-set-webview-zoom", + "markdownDescription": "Denies the set_webview_zoom command without any pre-configured scope." }, { "description": "Denies the webview_close command without any pre-configured scope.", "type": "string", - "const": "core:webview:deny-webview-close" + "const": "core:webview:deny-webview-close", + "markdownDescription": "Denies the webview_close command without any pre-configured scope." }, { "description": "Denies the webview_hide command without any pre-configured scope.", "type": "string", - "const": "core:webview:deny-webview-hide" + "const": "core:webview:deny-webview-hide", + "markdownDescription": "Denies the webview_hide command without any pre-configured scope." }, { "description": "Denies the webview_position command without any pre-configured scope.", "type": "string", - "const": "core:webview:deny-webview-position" + "const": "core:webview:deny-webview-position", + "markdownDescription": "Denies the webview_position command without any pre-configured scope." }, { "description": "Denies the webview_show command without any pre-configured scope.", "type": "string", - "const": "core:webview:deny-webview-show" + "const": "core:webview:deny-webview-show", + "markdownDescription": "Denies the webview_show command without any pre-configured scope." }, { "description": "Denies the webview_size command without any pre-configured scope.", "type": "string", - "const": "core:webview:deny-webview-size" + "const": "core:webview:deny-webview-size", + "markdownDescription": "Denies the webview_size command without any pre-configured scope." }, { - "description": "Default permissions for the plugin.", + "description": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-get-all-windows`\n- `allow-scale-factor`\n- `allow-inner-position`\n- `allow-outer-position`\n- `allow-inner-size`\n- `allow-outer-size`\n- `allow-is-fullscreen`\n- `allow-is-minimized`\n- `allow-is-maximized`\n- `allow-is-focused`\n- `allow-is-decorated`\n- `allow-is-resizable`\n- `allow-is-maximizable`\n- `allow-is-minimizable`\n- `allow-is-closable`\n- `allow-is-visible`\n- `allow-is-enabled`\n- `allow-title`\n- `allow-current-monitor`\n- `allow-primary-monitor`\n- `allow-monitor-from-point`\n- `allow-available-monitors`\n- `allow-cursor-position`\n- `allow-theme`\n- `allow-is-always-on-top`\n- `allow-internal-toggle-maximize`", "type": "string", - "const": "core:window:default" + "const": "core:window:default", + "markdownDescription": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-get-all-windows`\n- `allow-scale-factor`\n- `allow-inner-position`\n- `allow-outer-position`\n- `allow-inner-size`\n- `allow-outer-size`\n- `allow-is-fullscreen`\n- `allow-is-minimized`\n- `allow-is-maximized`\n- `allow-is-focused`\n- `allow-is-decorated`\n- `allow-is-resizable`\n- `allow-is-maximizable`\n- `allow-is-minimizable`\n- `allow-is-closable`\n- `allow-is-visible`\n- `allow-is-enabled`\n- `allow-title`\n- `allow-current-monitor`\n- `allow-primary-monitor`\n- `allow-monitor-from-point`\n- `allow-available-monitors`\n- `allow-cursor-position`\n- `allow-theme`\n- `allow-is-always-on-top`\n- `allow-internal-toggle-maximize`" }, { "description": "Enables the available_monitors command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-available-monitors" + "const": "core:window:allow-available-monitors", + "markdownDescription": "Enables the available_monitors command without any pre-configured scope." }, { "description": "Enables the center command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-center" + "const": "core:window:allow-center", + "markdownDescription": "Enables the center command without any pre-configured scope." }, { "description": "Enables the close command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-close" + "const": "core:window:allow-close", + "markdownDescription": "Enables the close command without any pre-configured scope." }, { "description": "Enables the create command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-create" + "const": "core:window:allow-create", + "markdownDescription": "Enables the create command without any pre-configured scope." }, { "description": "Enables the current_monitor command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-current-monitor" + "const": "core:window:allow-current-monitor", + "markdownDescription": "Enables the current_monitor command without any pre-configured scope." }, { "description": "Enables the cursor_position command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-cursor-position" + "const": "core:window:allow-cursor-position", + "markdownDescription": "Enables the cursor_position command without any pre-configured scope." }, { "description": "Enables the destroy command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-destroy" + "const": "core:window:allow-destroy", + "markdownDescription": "Enables the destroy command without any pre-configured scope." }, { "description": "Enables the get_all_windows command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-get-all-windows" + "const": "core:window:allow-get-all-windows", + "markdownDescription": "Enables the get_all_windows command without any pre-configured scope." }, { "description": "Enables the hide command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-hide" + "const": "core:window:allow-hide", + "markdownDescription": "Enables the hide command without any pre-configured scope." }, { "description": "Enables the inner_position command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-inner-position" + "const": "core:window:allow-inner-position", + "markdownDescription": "Enables the inner_position command without any pre-configured scope." }, { "description": "Enables the inner_size command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-inner-size" + "const": "core:window:allow-inner-size", + "markdownDescription": "Enables the inner_size command without any pre-configured scope." }, { "description": "Enables the internal_toggle_maximize command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-internal-toggle-maximize" + "const": "core:window:allow-internal-toggle-maximize", + "markdownDescription": "Enables the internal_toggle_maximize command without any pre-configured scope." }, { "description": "Enables the is_always_on_top command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-is-always-on-top" + "const": "core:window:allow-is-always-on-top", + "markdownDescription": "Enables the is_always_on_top command without any pre-configured scope." }, { "description": "Enables the is_closable command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-is-closable" + "const": "core:window:allow-is-closable", + "markdownDescription": "Enables the is_closable command without any pre-configured scope." }, { "description": "Enables the is_decorated command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-is-decorated" + "const": "core:window:allow-is-decorated", + "markdownDescription": "Enables the is_decorated command without any pre-configured scope." }, { "description": "Enables the is_enabled command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-is-enabled" + "const": "core:window:allow-is-enabled", + "markdownDescription": "Enables the is_enabled command without any pre-configured scope." }, { "description": "Enables the is_focused command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-is-focused" + "const": "core:window:allow-is-focused", + "markdownDescription": "Enables the is_focused command without any pre-configured scope." }, { "description": "Enables the is_fullscreen command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-is-fullscreen" + "const": "core:window:allow-is-fullscreen", + "markdownDescription": "Enables the is_fullscreen command without any pre-configured scope." }, { "description": "Enables the is_maximizable command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-is-maximizable" + "const": "core:window:allow-is-maximizable", + "markdownDescription": "Enables the is_maximizable command without any pre-configured scope." }, { "description": "Enables the is_maximized command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-is-maximized" + "const": "core:window:allow-is-maximized", + "markdownDescription": "Enables the is_maximized command without any pre-configured scope." }, { "description": "Enables the is_minimizable command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-is-minimizable" + "const": "core:window:allow-is-minimizable", + "markdownDescription": "Enables the is_minimizable command without any pre-configured scope." }, { "description": "Enables the is_minimized command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-is-minimized" + "const": "core:window:allow-is-minimized", + "markdownDescription": "Enables the is_minimized command without any pre-configured scope." }, { "description": "Enables the is_resizable command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-is-resizable" + "const": "core:window:allow-is-resizable", + "markdownDescription": "Enables the is_resizable command without any pre-configured scope." }, { "description": "Enables the is_visible command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-is-visible" + "const": "core:window:allow-is-visible", + "markdownDescription": "Enables the is_visible command without any pre-configured scope." }, { "description": "Enables the maximize command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-maximize" + "const": "core:window:allow-maximize", + "markdownDescription": "Enables the maximize command without any pre-configured scope." }, { "description": "Enables the minimize command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-minimize" + "const": "core:window:allow-minimize", + "markdownDescription": "Enables the minimize command without any pre-configured scope." }, { "description": "Enables the monitor_from_point command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-monitor-from-point" + "const": "core:window:allow-monitor-from-point", + "markdownDescription": "Enables the monitor_from_point command without any pre-configured scope." }, { "description": "Enables the outer_position command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-outer-position" + "const": "core:window:allow-outer-position", + "markdownDescription": "Enables the outer_position command without any pre-configured scope." }, { "description": "Enables the outer_size command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-outer-size" + "const": "core:window:allow-outer-size", + "markdownDescription": "Enables the outer_size command without any pre-configured scope." }, { "description": "Enables the primary_monitor command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-primary-monitor" + "const": "core:window:allow-primary-monitor", + "markdownDescription": "Enables the primary_monitor command without any pre-configured scope." }, { "description": "Enables the request_user_attention command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-request-user-attention" + "const": "core:window:allow-request-user-attention", + "markdownDescription": "Enables the request_user_attention command without any pre-configured scope." }, { "description": "Enables the scale_factor command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-scale-factor" + "const": "core:window:allow-scale-factor", + "markdownDescription": "Enables the scale_factor command without any pre-configured scope." }, { "description": "Enables the set_always_on_bottom command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-set-always-on-bottom" + "const": "core:window:allow-set-always-on-bottom", + "markdownDescription": "Enables the set_always_on_bottom command without any pre-configured scope." }, { "description": "Enables the set_always_on_top command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-set-always-on-top" + "const": "core:window:allow-set-always-on-top", + "markdownDescription": "Enables the set_always_on_top command without any pre-configured scope." }, { "description": "Enables the set_background_color command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-set-background-color" + "const": "core:window:allow-set-background-color", + "markdownDescription": "Enables the set_background_color command without any pre-configured scope." }, { "description": "Enables the set_badge_count command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-set-badge-count" + "const": "core:window:allow-set-badge-count", + "markdownDescription": "Enables the set_badge_count command without any pre-configured scope." }, { "description": "Enables the set_badge_label command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-set-badge-label" + "const": "core:window:allow-set-badge-label", + "markdownDescription": "Enables the set_badge_label command without any pre-configured scope." }, { "description": "Enables the set_closable command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-set-closable" + "const": "core:window:allow-set-closable", + "markdownDescription": "Enables the set_closable command without any pre-configured scope." }, { "description": "Enables the set_content_protected command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-set-content-protected" + "const": "core:window:allow-set-content-protected", + "markdownDescription": "Enables the set_content_protected command without any pre-configured scope." }, { "description": "Enables the set_cursor_grab command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-set-cursor-grab" + "const": "core:window:allow-set-cursor-grab", + "markdownDescription": "Enables the set_cursor_grab command without any pre-configured scope." }, { "description": "Enables the set_cursor_icon command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-set-cursor-icon" + "const": "core:window:allow-set-cursor-icon", + "markdownDescription": "Enables the set_cursor_icon command without any pre-configured scope." }, { "description": "Enables the set_cursor_position command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-set-cursor-position" + "const": "core:window:allow-set-cursor-position", + "markdownDescription": "Enables the set_cursor_position command without any pre-configured scope." }, { "description": "Enables the set_cursor_visible command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-set-cursor-visible" + "const": "core:window:allow-set-cursor-visible", + "markdownDescription": "Enables the set_cursor_visible command without any pre-configured scope." }, { "description": "Enables the set_decorations command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-set-decorations" + "const": "core:window:allow-set-decorations", + "markdownDescription": "Enables the set_decorations command without any pre-configured scope." }, { "description": "Enables the set_effects command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-set-effects" + "const": "core:window:allow-set-effects", + "markdownDescription": "Enables the set_effects command without any pre-configured scope." }, { "description": "Enables the set_enabled command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-set-enabled" + "const": "core:window:allow-set-enabled", + "markdownDescription": "Enables the set_enabled command without any pre-configured scope." }, { "description": "Enables the set_focus command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-set-focus" + "const": "core:window:allow-set-focus", + "markdownDescription": "Enables the set_focus command without any pre-configured scope." }, { "description": "Enables the set_fullscreen command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-set-fullscreen" + "const": "core:window:allow-set-fullscreen", + "markdownDescription": "Enables the set_fullscreen command without any pre-configured scope." }, { "description": "Enables the set_icon command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-set-icon" + "const": "core:window:allow-set-icon", + "markdownDescription": "Enables the set_icon command without any pre-configured scope." }, { "description": "Enables the set_ignore_cursor_events command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-set-ignore-cursor-events" + "const": "core:window:allow-set-ignore-cursor-events", + "markdownDescription": "Enables the set_ignore_cursor_events command without any pre-configured scope." }, { "description": "Enables the set_max_size command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-set-max-size" + "const": "core:window:allow-set-max-size", + "markdownDescription": "Enables the set_max_size command without any pre-configured scope." }, { "description": "Enables the set_maximizable command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-set-maximizable" + "const": "core:window:allow-set-maximizable", + "markdownDescription": "Enables the set_maximizable command without any pre-configured scope." }, { "description": "Enables the set_min_size command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-set-min-size" + "const": "core:window:allow-set-min-size", + "markdownDescription": "Enables the set_min_size command without any pre-configured scope." }, { "description": "Enables the set_minimizable command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-set-minimizable" + "const": "core:window:allow-set-minimizable", + "markdownDescription": "Enables the set_minimizable command without any pre-configured scope." }, { "description": "Enables the set_overlay_icon command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-set-overlay-icon" + "const": "core:window:allow-set-overlay-icon", + "markdownDescription": "Enables the set_overlay_icon command without any pre-configured scope." }, { "description": "Enables the set_position command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-set-position" + "const": "core:window:allow-set-position", + "markdownDescription": "Enables the set_position command without any pre-configured scope." }, { "description": "Enables the set_progress_bar command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-set-progress-bar" + "const": "core:window:allow-set-progress-bar", + "markdownDescription": "Enables the set_progress_bar command without any pre-configured scope." }, { "description": "Enables the set_resizable command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-set-resizable" + "const": "core:window:allow-set-resizable", + "markdownDescription": "Enables the set_resizable command without any pre-configured scope." }, { "description": "Enables the set_shadow command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-set-shadow" + "const": "core:window:allow-set-shadow", + "markdownDescription": "Enables the set_shadow command without any pre-configured scope." }, { "description": "Enables the set_size command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-set-size" + "const": "core:window:allow-set-size", + "markdownDescription": "Enables the set_size command without any pre-configured scope." }, { "description": "Enables the set_size_constraints command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-set-size-constraints" + "const": "core:window:allow-set-size-constraints", + "markdownDescription": "Enables the set_size_constraints command without any pre-configured scope." }, { "description": "Enables the set_skip_taskbar command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-set-skip-taskbar" + "const": "core:window:allow-set-skip-taskbar", + "markdownDescription": "Enables the set_skip_taskbar command without any pre-configured scope." }, { "description": "Enables the set_theme command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-set-theme" + "const": "core:window:allow-set-theme", + "markdownDescription": "Enables the set_theme command without any pre-configured scope." }, { "description": "Enables the set_title command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-set-title" + "const": "core:window:allow-set-title", + "markdownDescription": "Enables the set_title command without any pre-configured scope." }, { "description": "Enables the set_title_bar_style command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-set-title-bar-style" + "const": "core:window:allow-set-title-bar-style", + "markdownDescription": "Enables the set_title_bar_style command without any pre-configured scope." }, { "description": "Enables the set_visible_on_all_workspaces command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-set-visible-on-all-workspaces" + "const": "core:window:allow-set-visible-on-all-workspaces", + "markdownDescription": "Enables the set_visible_on_all_workspaces command without any pre-configured scope." }, { "description": "Enables the show command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-show" + "const": "core:window:allow-show", + "markdownDescription": "Enables the show command without any pre-configured scope." }, { "description": "Enables the start_dragging command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-start-dragging" + "const": "core:window:allow-start-dragging", + "markdownDescription": "Enables the start_dragging command without any pre-configured scope." }, { "description": "Enables the start_resize_dragging command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-start-resize-dragging" + "const": "core:window:allow-start-resize-dragging", + "markdownDescription": "Enables the start_resize_dragging command without any pre-configured scope." }, { "description": "Enables the theme command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-theme" + "const": "core:window:allow-theme", + "markdownDescription": "Enables the theme command without any pre-configured scope." }, { "description": "Enables the title command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-title" + "const": "core:window:allow-title", + "markdownDescription": "Enables the title command without any pre-configured scope." }, { "description": "Enables the toggle_maximize command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-toggle-maximize" + "const": "core:window:allow-toggle-maximize", + "markdownDescription": "Enables the toggle_maximize command without any pre-configured scope." }, { "description": "Enables the unmaximize command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-unmaximize" + "const": "core:window:allow-unmaximize", + "markdownDescription": "Enables the unmaximize command without any pre-configured scope." }, { "description": "Enables the unminimize command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-unminimize" + "const": "core:window:allow-unminimize", + "markdownDescription": "Enables the unminimize command without any pre-configured scope." }, { "description": "Denies the available_monitors command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-available-monitors" + "const": "core:window:deny-available-monitors", + "markdownDescription": "Denies the available_monitors command without any pre-configured scope." }, { "description": "Denies the center command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-center" + "const": "core:window:deny-center", + "markdownDescription": "Denies the center command without any pre-configured scope." }, { "description": "Denies the close command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-close" + "const": "core:window:deny-close", + "markdownDescription": "Denies the close command without any pre-configured scope." }, { "description": "Denies the create command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-create" + "const": "core:window:deny-create", + "markdownDescription": "Denies the create command without any pre-configured scope." }, { "description": "Denies the current_monitor command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-current-monitor" + "const": "core:window:deny-current-monitor", + "markdownDescription": "Denies the current_monitor command without any pre-configured scope." }, { "description": "Denies the cursor_position command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-cursor-position" + "const": "core:window:deny-cursor-position", + "markdownDescription": "Denies the cursor_position command without any pre-configured scope." }, { "description": "Denies the destroy command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-destroy" + "const": "core:window:deny-destroy", + "markdownDescription": "Denies the destroy command without any pre-configured scope." }, { "description": "Denies the get_all_windows command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-get-all-windows" + "const": "core:window:deny-get-all-windows", + "markdownDescription": "Denies the get_all_windows command without any pre-configured scope." }, { "description": "Denies the hide command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-hide" + "const": "core:window:deny-hide", + "markdownDescription": "Denies the hide command without any pre-configured scope." }, { "description": "Denies the inner_position command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-inner-position" + "const": "core:window:deny-inner-position", + "markdownDescription": "Denies the inner_position command without any pre-configured scope." }, { "description": "Denies the inner_size command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-inner-size" + "const": "core:window:deny-inner-size", + "markdownDescription": "Denies the inner_size command without any pre-configured scope." }, { "description": "Denies the internal_toggle_maximize command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-internal-toggle-maximize" + "const": "core:window:deny-internal-toggle-maximize", + "markdownDescription": "Denies the internal_toggle_maximize command without any pre-configured scope." }, { "description": "Denies the is_always_on_top command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-is-always-on-top" + "const": "core:window:deny-is-always-on-top", + "markdownDescription": "Denies the is_always_on_top command without any pre-configured scope." }, { "description": "Denies the is_closable command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-is-closable" + "const": "core:window:deny-is-closable", + "markdownDescription": "Denies the is_closable command without any pre-configured scope." }, { "description": "Denies the is_decorated command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-is-decorated" + "const": "core:window:deny-is-decorated", + "markdownDescription": "Denies the is_decorated command without any pre-configured scope." }, { "description": "Denies the is_enabled command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-is-enabled" + "const": "core:window:deny-is-enabled", + "markdownDescription": "Denies the is_enabled command without any pre-configured scope." }, { "description": "Denies the is_focused command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-is-focused" + "const": "core:window:deny-is-focused", + "markdownDescription": "Denies the is_focused command without any pre-configured scope." }, { "description": "Denies the is_fullscreen command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-is-fullscreen" + "const": "core:window:deny-is-fullscreen", + "markdownDescription": "Denies the is_fullscreen command without any pre-configured scope." }, { "description": "Denies the is_maximizable command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-is-maximizable" + "const": "core:window:deny-is-maximizable", + "markdownDescription": "Denies the is_maximizable command without any pre-configured scope." }, { "description": "Denies the is_maximized command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-is-maximized" + "const": "core:window:deny-is-maximized", + "markdownDescription": "Denies the is_maximized command without any pre-configured scope." }, { "description": "Denies the is_minimizable command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-is-minimizable" + "const": "core:window:deny-is-minimizable", + "markdownDescription": "Denies the is_minimizable command without any pre-configured scope." }, { "description": "Denies the is_minimized command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-is-minimized" + "const": "core:window:deny-is-minimized", + "markdownDescription": "Denies the is_minimized command without any pre-configured scope." }, { "description": "Denies the is_resizable command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-is-resizable" + "const": "core:window:deny-is-resizable", + "markdownDescription": "Denies the is_resizable command without any pre-configured scope." }, { "description": "Denies the is_visible command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-is-visible" + "const": "core:window:deny-is-visible", + "markdownDescription": "Denies the is_visible command without any pre-configured scope." }, { "description": "Denies the maximize command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-maximize" + "const": "core:window:deny-maximize", + "markdownDescription": "Denies the maximize command without any pre-configured scope." }, { "description": "Denies the minimize command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-minimize" + "const": "core:window:deny-minimize", + "markdownDescription": "Denies the minimize command without any pre-configured scope." }, { "description": "Denies the monitor_from_point command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-monitor-from-point" + "const": "core:window:deny-monitor-from-point", + "markdownDescription": "Denies the monitor_from_point command without any pre-configured scope." }, { "description": "Denies the outer_position command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-outer-position" + "const": "core:window:deny-outer-position", + "markdownDescription": "Denies the outer_position command without any pre-configured scope." }, { "description": "Denies the outer_size command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-outer-size" + "const": "core:window:deny-outer-size", + "markdownDescription": "Denies the outer_size command without any pre-configured scope." }, { "description": "Denies the primary_monitor command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-primary-monitor" + "const": "core:window:deny-primary-monitor", + "markdownDescription": "Denies the primary_monitor command without any pre-configured scope." }, { "description": "Denies the request_user_attention command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-request-user-attention" + "const": "core:window:deny-request-user-attention", + "markdownDescription": "Denies the request_user_attention command without any pre-configured scope." }, { "description": "Denies the scale_factor command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-scale-factor" + "const": "core:window:deny-scale-factor", + "markdownDescription": "Denies the scale_factor command without any pre-configured scope." }, { "description": "Denies the set_always_on_bottom command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-set-always-on-bottom" + "const": "core:window:deny-set-always-on-bottom", + "markdownDescription": "Denies the set_always_on_bottom command without any pre-configured scope." }, { "description": "Denies the set_always_on_top command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-set-always-on-top" + "const": "core:window:deny-set-always-on-top", + "markdownDescription": "Denies the set_always_on_top command without any pre-configured scope." }, { "description": "Denies the set_background_color command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-set-background-color" + "const": "core:window:deny-set-background-color", + "markdownDescription": "Denies the set_background_color command without any pre-configured scope." }, { "description": "Denies the set_badge_count command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-set-badge-count" + "const": "core:window:deny-set-badge-count", + "markdownDescription": "Denies the set_badge_count command without any pre-configured scope." }, { "description": "Denies the set_badge_label command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-set-badge-label" + "const": "core:window:deny-set-badge-label", + "markdownDescription": "Denies the set_badge_label command without any pre-configured scope." }, { "description": "Denies the set_closable command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-set-closable" + "const": "core:window:deny-set-closable", + "markdownDescription": "Denies the set_closable command without any pre-configured scope." }, { "description": "Denies the set_content_protected command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-set-content-protected" + "const": "core:window:deny-set-content-protected", + "markdownDescription": "Denies the set_content_protected command without any pre-configured scope." }, { "description": "Denies the set_cursor_grab command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-set-cursor-grab" + "const": "core:window:deny-set-cursor-grab", + "markdownDescription": "Denies the set_cursor_grab command without any pre-configured scope." }, { "description": "Denies the set_cursor_icon command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-set-cursor-icon" + "const": "core:window:deny-set-cursor-icon", + "markdownDescription": "Denies the set_cursor_icon command without any pre-configured scope." }, { "description": "Denies the set_cursor_position command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-set-cursor-position" + "const": "core:window:deny-set-cursor-position", + "markdownDescription": "Denies the set_cursor_position command without any pre-configured scope." }, { "description": "Denies the set_cursor_visible command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-set-cursor-visible" + "const": "core:window:deny-set-cursor-visible", + "markdownDescription": "Denies the set_cursor_visible command without any pre-configured scope." }, { "description": "Denies the set_decorations command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-set-decorations" + "const": "core:window:deny-set-decorations", + "markdownDescription": "Denies the set_decorations command without any pre-configured scope." }, { "description": "Denies the set_effects command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-set-effects" + "const": "core:window:deny-set-effects", + "markdownDescription": "Denies the set_effects command without any pre-configured scope." }, { "description": "Denies the set_enabled command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-set-enabled" + "const": "core:window:deny-set-enabled", + "markdownDescription": "Denies the set_enabled command without any pre-configured scope." }, { "description": "Denies the set_focus command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-set-focus" + "const": "core:window:deny-set-focus", + "markdownDescription": "Denies the set_focus command without any pre-configured scope." }, { "description": "Denies the set_fullscreen command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-set-fullscreen" + "const": "core:window:deny-set-fullscreen", + "markdownDescription": "Denies the set_fullscreen command without any pre-configured scope." }, { "description": "Denies the set_icon command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-set-icon" + "const": "core:window:deny-set-icon", + "markdownDescription": "Denies the set_icon command without any pre-configured scope." }, { "description": "Denies the set_ignore_cursor_events command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-set-ignore-cursor-events" + "const": "core:window:deny-set-ignore-cursor-events", + "markdownDescription": "Denies the set_ignore_cursor_events command without any pre-configured scope." }, { "description": "Denies the set_max_size command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-set-max-size" + "const": "core:window:deny-set-max-size", + "markdownDescription": "Denies the set_max_size command without any pre-configured scope." }, { "description": "Denies the set_maximizable command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-set-maximizable" + "const": "core:window:deny-set-maximizable", + "markdownDescription": "Denies the set_maximizable command without any pre-configured scope." }, { "description": "Denies the set_min_size command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-set-min-size" + "const": "core:window:deny-set-min-size", + "markdownDescription": "Denies the set_min_size command without any pre-configured scope." }, { "description": "Denies the set_minimizable command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-set-minimizable" + "const": "core:window:deny-set-minimizable", + "markdownDescription": "Denies the set_minimizable command without any pre-configured scope." }, { "description": "Denies the set_overlay_icon command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-set-overlay-icon" + "const": "core:window:deny-set-overlay-icon", + "markdownDescription": "Denies the set_overlay_icon command without any pre-configured scope." }, { "description": "Denies the set_position command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-set-position" + "const": "core:window:deny-set-position", + "markdownDescription": "Denies the set_position command without any pre-configured scope." }, { "description": "Denies the set_progress_bar command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-set-progress-bar" + "const": "core:window:deny-set-progress-bar", + "markdownDescription": "Denies the set_progress_bar command without any pre-configured scope." }, { "description": "Denies the set_resizable command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-set-resizable" + "const": "core:window:deny-set-resizable", + "markdownDescription": "Denies the set_resizable command without any pre-configured scope." }, { "description": "Denies the set_shadow command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-set-shadow" + "const": "core:window:deny-set-shadow", + "markdownDescription": "Denies the set_shadow command without any pre-configured scope." }, { "description": "Denies the set_size command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-set-size" + "const": "core:window:deny-set-size", + "markdownDescription": "Denies the set_size command without any pre-configured scope." }, { "description": "Denies the set_size_constraints command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-set-size-constraints" + "const": "core:window:deny-set-size-constraints", + "markdownDescription": "Denies the set_size_constraints command without any pre-configured scope." }, { "description": "Denies the set_skip_taskbar command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-set-skip-taskbar" + "const": "core:window:deny-set-skip-taskbar", + "markdownDescription": "Denies the set_skip_taskbar command without any pre-configured scope." }, { "description": "Denies the set_theme command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-set-theme" + "const": "core:window:deny-set-theme", + "markdownDescription": "Denies the set_theme command without any pre-configured scope." }, { "description": "Denies the set_title command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-set-title" + "const": "core:window:deny-set-title", + "markdownDescription": "Denies the set_title command without any pre-configured scope." }, { "description": "Denies the set_title_bar_style command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-set-title-bar-style" + "const": "core:window:deny-set-title-bar-style", + "markdownDescription": "Denies the set_title_bar_style command without any pre-configured scope." }, { "description": "Denies the set_visible_on_all_workspaces command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-set-visible-on-all-workspaces" + "const": "core:window:deny-set-visible-on-all-workspaces", + "markdownDescription": "Denies the set_visible_on_all_workspaces command without any pre-configured scope." }, { "description": "Denies the show command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-show" + "const": "core:window:deny-show", + "markdownDescription": "Denies the show command without any pre-configured scope." }, { "description": "Denies the start_dragging command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-start-dragging" + "const": "core:window:deny-start-dragging", + "markdownDescription": "Denies the start_dragging command without any pre-configured scope." }, { "description": "Denies the start_resize_dragging command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-start-resize-dragging" + "const": "core:window:deny-start-resize-dragging", + "markdownDescription": "Denies the start_resize_dragging command without any pre-configured scope." }, { "description": "Denies the theme command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-theme" + "const": "core:window:deny-theme", + "markdownDescription": "Denies the theme command without any pre-configured scope." }, { "description": "Denies the title command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-title" + "const": "core:window:deny-title", + "markdownDescription": "Denies the title command without any pre-configured scope." }, { "description": "Denies the toggle_maximize command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-toggle-maximize" + "const": "core:window:deny-toggle-maximize", + "markdownDescription": "Denies the toggle_maximize command without any pre-configured scope." }, { "description": "Denies the unmaximize command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-unmaximize" + "const": "core:window:deny-unmaximize", + "markdownDescription": "Denies the unmaximize command without any pre-configured scope." }, { "description": "Denies the unminimize command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-unminimize" + "const": "core:window:deny-unminimize", + "markdownDescription": "Denies the unminimize command without any pre-configured scope." }, { - "description": "This permission set configures which\nprocess feeatures are by default exposed.\n\n#### Granted Permissions\n\nThis enables to quit via `allow-exit` and restart via `allow-restart`\nthe application.\n", + "description": "This permission set configures which\nprocess features are by default exposed.\n\n#### Granted Permissions\n\nThis enables to quit via `allow-exit` and restart via `allow-restart`\nthe application.\n\n#### This default permission set includes:\n\n- `allow-exit`\n- `allow-restart`", "type": "string", - "const": "process:default" + "const": "process:default", + "markdownDescription": "This permission set configures which\nprocess features are by default exposed.\n\n#### Granted Permissions\n\nThis enables to quit via `allow-exit` and restart via `allow-restart`\nthe application.\n\n#### This default permission set includes:\n\n- `allow-exit`\n- `allow-restart`" }, { "description": "Enables the exit command without any pre-configured scope.", "type": "string", - "const": "process:allow-exit" + "const": "process:allow-exit", + "markdownDescription": "Enables the exit command without any pre-configured scope." }, { "description": "Enables the restart command without any pre-configured scope.", "type": "string", - "const": "process:allow-restart" + "const": "process:allow-restart", + "markdownDescription": "Enables the restart command without any pre-configured scope." }, { "description": "Denies the exit command without any pre-configured scope.", "type": "string", - "const": "process:deny-exit" + "const": "process:deny-exit", + "markdownDescription": "Denies the exit command without any pre-configured scope." }, { "description": "Denies the restart command without any pre-configured scope.", "type": "string", - "const": "process:deny-restart" + "const": "process:deny-restart", + "markdownDescription": "Denies the restart command without any pre-configured scope." }, { - "description": "This permission set configures which\nshell functionality is exposed by default.\n\n#### Granted Permissions\n\nIt allows to use the `open` functionality without any specific\nscope pre-configured. It will allow opening `http(s)://`,\n`tel:` and `mailto:` links.\n", + "description": "This permission set configures which\nshell functionality is exposed by default.\n\n#### Granted Permissions\n\nIt allows to use the `open` functionality with a reasonable\nscope pre-configured. It will allow opening `http(s)://`,\n`tel:` and `mailto:` links.\n\n#### This default permission set includes:\n\n- `allow-open`", "type": "string", - "const": "shell:default" + "const": "shell:default", + "markdownDescription": "This permission set configures which\nshell functionality is exposed by default.\n\n#### Granted Permissions\n\nIt allows to use the `open` functionality with a reasonable\nscope pre-configured. It will allow opening `http(s)://`,\n`tel:` and `mailto:` links.\n\n#### This default permission set includes:\n\n- `allow-open`" }, { "description": "Enables the execute command without any pre-configured scope.", "type": "string", - "const": "shell:allow-execute" + "const": "shell:allow-execute", + "markdownDescription": "Enables the execute command without any pre-configured scope." }, { "description": "Enables the kill command without any pre-configured scope.", "type": "string", - "const": "shell:allow-kill" + "const": "shell:allow-kill", + "markdownDescription": "Enables the kill command without any pre-configured scope." }, { "description": "Enables the open command without any pre-configured scope.", "type": "string", - "const": "shell:allow-open" + "const": "shell:allow-open", + "markdownDescription": "Enables the open command without any pre-configured scope." }, { "description": "Enables the spawn command without any pre-configured scope.", "type": "string", - "const": "shell:allow-spawn" + "const": "shell:allow-spawn", + "markdownDescription": "Enables the spawn command without any pre-configured scope." }, { "description": "Enables the stdin_write command without any pre-configured scope.", "type": "string", - "const": "shell:allow-stdin-write" + "const": "shell:allow-stdin-write", + "markdownDescription": "Enables the stdin_write command without any pre-configured scope." }, { "description": "Denies the execute command without any pre-configured scope.", "type": "string", - "const": "shell:deny-execute" + "const": "shell:deny-execute", + "markdownDescription": "Denies the execute command without any pre-configured scope." }, { "description": "Denies the kill command without any pre-configured scope.", "type": "string", - "const": "shell:deny-kill" + "const": "shell:deny-kill", + "markdownDescription": "Denies the kill command without any pre-configured scope." }, { "description": "Denies the open command without any pre-configured scope.", "type": "string", - "const": "shell:deny-open" + "const": "shell:deny-open", + "markdownDescription": "Denies the open command without any pre-configured scope." }, { "description": "Denies the spawn command without any pre-configured scope.", "type": "string", - "const": "shell:deny-spawn" + "const": "shell:deny-spawn", + "markdownDescription": "Denies the spawn command without any pre-configured scope." }, { "description": "Denies the stdin_write command without any pre-configured scope.", "type": "string", - "const": "shell:deny-stdin-write" + "const": "shell:deny-stdin-write", + "markdownDescription": "Denies the stdin_write command without any pre-configured scope." }, { - "description": "This permission set configures which kind of\nupdater functions are exposed to the frontend.\n\n#### Granted Permissions\n\nThe full workflow from checking for updates to installing them\nis enabled.\n\n", + "description": "This permission set configures which kind of\nupdater functions are exposed to the frontend.\n\n#### Granted Permissions\n\nThe full workflow from checking for updates to installing them\nis enabled.\n\n\n#### This default permission set includes:\n\n- `allow-check`\n- `allow-download`\n- `allow-install`\n- `allow-download-and-install`", "type": "string", - "const": "updater:default" + "const": "updater:default", + "markdownDescription": "This permission set configures which kind of\nupdater functions are exposed to the frontend.\n\n#### Granted Permissions\n\nThe full workflow from checking for updates to installing them\nis enabled.\n\n\n#### This default permission set includes:\n\n- `allow-check`\n- `allow-download`\n- `allow-install`\n- `allow-download-and-install`" }, { "description": "Enables the check command without any pre-configured scope.", "type": "string", - "const": "updater:allow-check" + "const": "updater:allow-check", + "markdownDescription": "Enables the check command without any pre-configured scope." }, { "description": "Enables the download command without any pre-configured scope.", "type": "string", - "const": "updater:allow-download" + "const": "updater:allow-download", + "markdownDescription": "Enables the download command without any pre-configured scope." }, { "description": "Enables the download_and_install command without any pre-configured scope.", "type": "string", - "const": "updater:allow-download-and-install" + "const": "updater:allow-download-and-install", + "markdownDescription": "Enables the download_and_install command without any pre-configured scope." }, { "description": "Enables the install command without any pre-configured scope.", "type": "string", - "const": "updater:allow-install" + "const": "updater:allow-install", + "markdownDescription": "Enables the install command without any pre-configured scope." }, { "description": "Denies the check command without any pre-configured scope.", "type": "string", - "const": "updater:deny-check" + "const": "updater:deny-check", + "markdownDescription": "Denies the check command without any pre-configured scope." }, { "description": "Denies the download command without any pre-configured scope.", "type": "string", - "const": "updater:deny-download" + "const": "updater:deny-download", + "markdownDescription": "Denies the download command without any pre-configured scope." }, { "description": "Denies the download_and_install command without any pre-configured scope.", "type": "string", - "const": "updater:deny-download-and-install" + "const": "updater:deny-download-and-install", + "markdownDescription": "Denies the download_and_install command without any pre-configured scope." }, { "description": "Denies the install command without any pre-configured scope.", "type": "string", - "const": "updater:deny-install" + "const": "updater:deny-install", + "markdownDescription": "Denies the install command without any pre-configured scope." } ] }, diff --git a/nym-wallet/src-tauri/gen/schemas/macOS-schema.json b/nym-wallet/src-tauri/gen/schemas/macOS-schema.json index 5ff69983d7..fa33888ae0 100644 --- a/nym-wallet/src-tauri/gen/schemas/macOS-schema.json +++ b/nym-wallet/src-tauri/gen/schemas/macOS-schema.json @@ -140,59 +140,70 @@ "identifier": { "anyOf": [ { - "description": "This permission set configures which\nshell functionality is exposed by default.\n\n#### Granted Permissions\n\nIt allows to use the `open` functionality without any specific\nscope pre-configured. It will allow opening `http(s)://`,\n`tel:` and `mailto:` links.\n", + "description": "This permission set configures which\nshell functionality is exposed by default.\n\n#### Granted Permissions\n\nIt allows to use the `open` functionality with a reasonable\nscope pre-configured. It will allow opening `http(s)://`,\n`tel:` and `mailto:` links.\n\n#### This default permission set includes:\n\n- `allow-open`", "type": "string", - "const": "shell:default" + "const": "shell:default", + "markdownDescription": "This permission set configures which\nshell functionality is exposed by default.\n\n#### Granted Permissions\n\nIt allows to use the `open` functionality with a reasonable\nscope pre-configured. It will allow opening `http(s)://`,\n`tel:` and `mailto:` links.\n\n#### This default permission set includes:\n\n- `allow-open`" }, { "description": "Enables the execute command without any pre-configured scope.", "type": "string", - "const": "shell:allow-execute" + "const": "shell:allow-execute", + "markdownDescription": "Enables the execute command without any pre-configured scope." }, { "description": "Enables the kill command without any pre-configured scope.", "type": "string", - "const": "shell:allow-kill" + "const": "shell:allow-kill", + "markdownDescription": "Enables the kill command without any pre-configured scope." }, { "description": "Enables the open command without any pre-configured scope.", "type": "string", - "const": "shell:allow-open" + "const": "shell:allow-open", + "markdownDescription": "Enables the open command without any pre-configured scope." }, { "description": "Enables the spawn command without any pre-configured scope.", "type": "string", - "const": "shell:allow-spawn" + "const": "shell:allow-spawn", + "markdownDescription": "Enables the spawn command without any pre-configured scope." }, { "description": "Enables the stdin_write command without any pre-configured scope.", "type": "string", - "const": "shell:allow-stdin-write" + "const": "shell:allow-stdin-write", + "markdownDescription": "Enables the stdin_write command without any pre-configured scope." }, { "description": "Denies the execute command without any pre-configured scope.", "type": "string", - "const": "shell:deny-execute" + "const": "shell:deny-execute", + "markdownDescription": "Denies the execute command without any pre-configured scope." }, { "description": "Denies the kill command without any pre-configured scope.", "type": "string", - "const": "shell:deny-kill" + "const": "shell:deny-kill", + "markdownDescription": "Denies the kill command without any pre-configured scope." }, { "description": "Denies the open command without any pre-configured scope.", "type": "string", - "const": "shell:deny-open" + "const": "shell:deny-open", + "markdownDescription": "Denies the open command without any pre-configured scope." }, { "description": "Denies the spawn command without any pre-configured scope.", "type": "string", - "const": "shell:deny-spawn" + "const": "shell:deny-spawn", + "markdownDescription": "Denies the spawn command without any pre-configured scope." }, { "description": "Denies the stdin_write command without any pre-configured scope.", "type": "string", - "const": "shell:deny-stdin-write" + "const": "shell:deny-stdin-write", + "markdownDescription": "Denies the stdin_write command without any pre-configured scope." } ] } @@ -378,1762 +389,2114 @@ { "description": "No features are enabled by default, as we believe\nthe clipboard can be inherently dangerous and it is \napplication specific if read and/or write access is needed.\n\nClipboard interaction needs to be explicitly enabled.\n", "type": "string", - "const": "clipboard-manager:default" + "const": "clipboard-manager:default", + "markdownDescription": "No features are enabled by default, as we believe\nthe clipboard can be inherently dangerous and it is \napplication specific if read and/or write access is needed.\n\nClipboard interaction needs to be explicitly enabled.\n" }, { "description": "Enables the clear command without any pre-configured scope.", "type": "string", - "const": "clipboard-manager:allow-clear" + "const": "clipboard-manager:allow-clear", + "markdownDescription": "Enables the clear command without any pre-configured scope." }, { "description": "Enables the read_image command without any pre-configured scope.", "type": "string", - "const": "clipboard-manager:allow-read-image" + "const": "clipboard-manager:allow-read-image", + "markdownDescription": "Enables the read_image command without any pre-configured scope." }, { "description": "Enables the read_text command without any pre-configured scope.", "type": "string", - "const": "clipboard-manager:allow-read-text" + "const": "clipboard-manager:allow-read-text", + "markdownDescription": "Enables the read_text command without any pre-configured scope." }, { "description": "Enables the write_html command without any pre-configured scope.", "type": "string", - "const": "clipboard-manager:allow-write-html" + "const": "clipboard-manager:allow-write-html", + "markdownDescription": "Enables the write_html command without any pre-configured scope." }, { "description": "Enables the write_image command without any pre-configured scope.", "type": "string", - "const": "clipboard-manager:allow-write-image" + "const": "clipboard-manager:allow-write-image", + "markdownDescription": "Enables the write_image command without any pre-configured scope." }, { "description": "Enables the write_text command without any pre-configured scope.", "type": "string", - "const": "clipboard-manager:allow-write-text" + "const": "clipboard-manager:allow-write-text", + "markdownDescription": "Enables the write_text command without any pre-configured scope." }, { "description": "Denies the clear command without any pre-configured scope.", "type": "string", - "const": "clipboard-manager:deny-clear" + "const": "clipboard-manager:deny-clear", + "markdownDescription": "Denies the clear command without any pre-configured scope." }, { "description": "Denies the read_image command without any pre-configured scope.", "type": "string", - "const": "clipboard-manager:deny-read-image" + "const": "clipboard-manager:deny-read-image", + "markdownDescription": "Denies the read_image command without any pre-configured scope." }, { "description": "Denies the read_text command without any pre-configured scope.", "type": "string", - "const": "clipboard-manager:deny-read-text" + "const": "clipboard-manager:deny-read-text", + "markdownDescription": "Denies the read_text command without any pre-configured scope." }, { "description": "Denies the write_html command without any pre-configured scope.", "type": "string", - "const": "clipboard-manager:deny-write-html" + "const": "clipboard-manager:deny-write-html", + "markdownDescription": "Denies the write_html command without any pre-configured scope." }, { "description": "Denies the write_image command without any pre-configured scope.", "type": "string", - "const": "clipboard-manager:deny-write-image" + "const": "clipboard-manager:deny-write-image", + "markdownDescription": "Denies the write_image command without any pre-configured scope." }, { "description": "Denies the write_text command without any pre-configured scope.", "type": "string", - "const": "clipboard-manager:deny-write-text" + "const": "clipboard-manager:deny-write-text", + "markdownDescription": "Denies the write_text command without any pre-configured scope." }, { - "description": "Default core plugins set which includes:\n- 'core:path:default'\n- 'core:event:default'\n- 'core:window:default'\n- 'core:webview:default'\n- 'core:app:default'\n- 'core:image:default'\n- 'core:resources:default'\n- 'core:menu:default'\n- 'core:tray:default'\n", + "description": "Default core plugins set.\n#### This default permission set includes:\n\n- `core:path:default`\n- `core:event:default`\n- `core:window:default`\n- `core:webview:default`\n- `core:app:default`\n- `core:image:default`\n- `core:resources:default`\n- `core:menu:default`\n- `core:tray:default`", "type": "string", - "const": "core:default" + "const": "core:default", + "markdownDescription": "Default core plugins set.\n#### This default permission set includes:\n\n- `core:path:default`\n- `core:event:default`\n- `core:window:default`\n- `core:webview:default`\n- `core:app:default`\n- `core:image:default`\n- `core:resources:default`\n- `core:menu:default`\n- `core:tray:default`" }, { - "description": "Default permissions for the plugin.", + "description": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-version`\n- `allow-name`\n- `allow-tauri-version`\n- `allow-identifier`", "type": "string", - "const": "core:app:default" + "const": "core:app:default", + "markdownDescription": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-version`\n- `allow-name`\n- `allow-tauri-version`\n- `allow-identifier`" }, { "description": "Enables the app_hide command without any pre-configured scope.", "type": "string", - "const": "core:app:allow-app-hide" + "const": "core:app:allow-app-hide", + "markdownDescription": "Enables the app_hide command without any pre-configured scope." }, { "description": "Enables the app_show command without any pre-configured scope.", "type": "string", - "const": "core:app:allow-app-show" + "const": "core:app:allow-app-show", + "markdownDescription": "Enables the app_show command without any pre-configured scope." }, { "description": "Enables the default_window_icon command without any pre-configured scope.", "type": "string", - "const": "core:app:allow-default-window-icon" + "const": "core:app:allow-default-window-icon", + "markdownDescription": "Enables the default_window_icon command without any pre-configured scope." }, { "description": "Enables the fetch_data_store_identifiers command without any pre-configured scope.", "type": "string", - "const": "core:app:allow-fetch-data-store-identifiers" + "const": "core:app:allow-fetch-data-store-identifiers", + "markdownDescription": "Enables the fetch_data_store_identifiers command without any pre-configured scope." }, { "description": "Enables the identifier command without any pre-configured scope.", "type": "string", - "const": "core:app:allow-identifier" + "const": "core:app:allow-identifier", + "markdownDescription": "Enables the identifier command without any pre-configured scope." }, { "description": "Enables the name command without any pre-configured scope.", "type": "string", - "const": "core:app:allow-name" + "const": "core:app:allow-name", + "markdownDescription": "Enables the name command without any pre-configured scope." }, { "description": "Enables the remove_data_store command without any pre-configured scope.", "type": "string", - "const": "core:app:allow-remove-data-store" + "const": "core:app:allow-remove-data-store", + "markdownDescription": "Enables the remove_data_store command without any pre-configured scope." }, { "description": "Enables the set_app_theme command without any pre-configured scope.", "type": "string", - "const": "core:app:allow-set-app-theme" + "const": "core:app:allow-set-app-theme", + "markdownDescription": "Enables the set_app_theme command without any pre-configured scope." }, { "description": "Enables the tauri_version command without any pre-configured scope.", "type": "string", - "const": "core:app:allow-tauri-version" + "const": "core:app:allow-tauri-version", + "markdownDescription": "Enables the tauri_version command without any pre-configured scope." }, { "description": "Enables the version command without any pre-configured scope.", "type": "string", - "const": "core:app:allow-version" + "const": "core:app:allow-version", + "markdownDescription": "Enables the version command without any pre-configured scope." }, { "description": "Denies the app_hide command without any pre-configured scope.", "type": "string", - "const": "core:app:deny-app-hide" + "const": "core:app:deny-app-hide", + "markdownDescription": "Denies the app_hide command without any pre-configured scope." }, { "description": "Denies the app_show command without any pre-configured scope.", "type": "string", - "const": "core:app:deny-app-show" + "const": "core:app:deny-app-show", + "markdownDescription": "Denies the app_show command without any pre-configured scope." }, { "description": "Denies the default_window_icon command without any pre-configured scope.", "type": "string", - "const": "core:app:deny-default-window-icon" + "const": "core:app:deny-default-window-icon", + "markdownDescription": "Denies the default_window_icon command without any pre-configured scope." }, { "description": "Denies the fetch_data_store_identifiers command without any pre-configured scope.", "type": "string", - "const": "core:app:deny-fetch-data-store-identifiers" + "const": "core:app:deny-fetch-data-store-identifiers", + "markdownDescription": "Denies the fetch_data_store_identifiers command without any pre-configured scope." }, { "description": "Denies the identifier command without any pre-configured scope.", "type": "string", - "const": "core:app:deny-identifier" + "const": "core:app:deny-identifier", + "markdownDescription": "Denies the identifier command without any pre-configured scope." }, { "description": "Denies the name command without any pre-configured scope.", "type": "string", - "const": "core:app:deny-name" + "const": "core:app:deny-name", + "markdownDescription": "Denies the name command without any pre-configured scope." }, { "description": "Denies the remove_data_store command without any pre-configured scope.", "type": "string", - "const": "core:app:deny-remove-data-store" + "const": "core:app:deny-remove-data-store", + "markdownDescription": "Denies the remove_data_store command without any pre-configured scope." }, { "description": "Denies the set_app_theme command without any pre-configured scope.", "type": "string", - "const": "core:app:deny-set-app-theme" + "const": "core:app:deny-set-app-theme", + "markdownDescription": "Denies the set_app_theme command without any pre-configured scope." }, { "description": "Denies the tauri_version command without any pre-configured scope.", "type": "string", - "const": "core:app:deny-tauri-version" + "const": "core:app:deny-tauri-version", + "markdownDescription": "Denies the tauri_version command without any pre-configured scope." }, { "description": "Denies the version command without any pre-configured scope.", "type": "string", - "const": "core:app:deny-version" + "const": "core:app:deny-version", + "markdownDescription": "Denies the version command without any pre-configured scope." }, { - "description": "Default permissions for the plugin.", + "description": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-listen`\n- `allow-unlisten`\n- `allow-emit`\n- `allow-emit-to`", "type": "string", - "const": "core:event:default" + "const": "core:event:default", + "markdownDescription": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-listen`\n- `allow-unlisten`\n- `allow-emit`\n- `allow-emit-to`" }, { "description": "Enables the emit command without any pre-configured scope.", "type": "string", - "const": "core:event:allow-emit" + "const": "core:event:allow-emit", + "markdownDescription": "Enables the emit command without any pre-configured scope." }, { "description": "Enables the emit_to command without any pre-configured scope.", "type": "string", - "const": "core:event:allow-emit-to" + "const": "core:event:allow-emit-to", + "markdownDescription": "Enables the emit_to command without any pre-configured scope." }, { "description": "Enables the listen command without any pre-configured scope.", "type": "string", - "const": "core:event:allow-listen" + "const": "core:event:allow-listen", + "markdownDescription": "Enables the listen command without any pre-configured scope." }, { "description": "Enables the unlisten command without any pre-configured scope.", "type": "string", - "const": "core:event:allow-unlisten" + "const": "core:event:allow-unlisten", + "markdownDescription": "Enables the unlisten command without any pre-configured scope." }, { "description": "Denies the emit command without any pre-configured scope.", "type": "string", - "const": "core:event:deny-emit" + "const": "core:event:deny-emit", + "markdownDescription": "Denies the emit command without any pre-configured scope." }, { "description": "Denies the emit_to command without any pre-configured scope.", "type": "string", - "const": "core:event:deny-emit-to" + "const": "core:event:deny-emit-to", + "markdownDescription": "Denies the emit_to command without any pre-configured scope." }, { "description": "Denies the listen command without any pre-configured scope.", "type": "string", - "const": "core:event:deny-listen" + "const": "core:event:deny-listen", + "markdownDescription": "Denies the listen command without any pre-configured scope." }, { "description": "Denies the unlisten command without any pre-configured scope.", "type": "string", - "const": "core:event:deny-unlisten" + "const": "core:event:deny-unlisten", + "markdownDescription": "Denies the unlisten command without any pre-configured scope." }, { - "description": "Default permissions for the plugin.", + "description": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-new`\n- `allow-from-bytes`\n- `allow-from-path`\n- `allow-rgba`\n- `allow-size`", "type": "string", - "const": "core:image:default" + "const": "core:image:default", + "markdownDescription": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-new`\n- `allow-from-bytes`\n- `allow-from-path`\n- `allow-rgba`\n- `allow-size`" }, { "description": "Enables the from_bytes command without any pre-configured scope.", "type": "string", - "const": "core:image:allow-from-bytes" + "const": "core:image:allow-from-bytes", + "markdownDescription": "Enables the from_bytes command without any pre-configured scope." }, { "description": "Enables the from_path command without any pre-configured scope.", "type": "string", - "const": "core:image:allow-from-path" + "const": "core:image:allow-from-path", + "markdownDescription": "Enables the from_path command without any pre-configured scope." }, { "description": "Enables the new command without any pre-configured scope.", "type": "string", - "const": "core:image:allow-new" + "const": "core:image:allow-new", + "markdownDescription": "Enables the new command without any pre-configured scope." }, { "description": "Enables the rgba command without any pre-configured scope.", "type": "string", - "const": "core:image:allow-rgba" + "const": "core:image:allow-rgba", + "markdownDescription": "Enables the rgba command without any pre-configured scope." }, { "description": "Enables the size command without any pre-configured scope.", "type": "string", - "const": "core:image:allow-size" + "const": "core:image:allow-size", + "markdownDescription": "Enables the size command without any pre-configured scope." }, { "description": "Denies the from_bytes command without any pre-configured scope.", "type": "string", - "const": "core:image:deny-from-bytes" + "const": "core:image:deny-from-bytes", + "markdownDescription": "Denies the from_bytes command without any pre-configured scope." }, { "description": "Denies the from_path command without any pre-configured scope.", "type": "string", - "const": "core:image:deny-from-path" + "const": "core:image:deny-from-path", + "markdownDescription": "Denies the from_path command without any pre-configured scope." }, { "description": "Denies the new command without any pre-configured scope.", "type": "string", - "const": "core:image:deny-new" + "const": "core:image:deny-new", + "markdownDescription": "Denies the new command without any pre-configured scope." }, { "description": "Denies the rgba command without any pre-configured scope.", "type": "string", - "const": "core:image:deny-rgba" + "const": "core:image:deny-rgba", + "markdownDescription": "Denies the rgba command without any pre-configured scope." }, { "description": "Denies the size command without any pre-configured scope.", "type": "string", - "const": "core:image:deny-size" + "const": "core:image:deny-size", + "markdownDescription": "Denies the size command without any pre-configured scope." }, { - "description": "Default permissions for the plugin.", + "description": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-new`\n- `allow-append`\n- `allow-prepend`\n- `allow-insert`\n- `allow-remove`\n- `allow-remove-at`\n- `allow-items`\n- `allow-get`\n- `allow-popup`\n- `allow-create-default`\n- `allow-set-as-app-menu`\n- `allow-set-as-window-menu`\n- `allow-text`\n- `allow-set-text`\n- `allow-is-enabled`\n- `allow-set-enabled`\n- `allow-set-accelerator`\n- `allow-set-as-windows-menu-for-nsapp`\n- `allow-set-as-help-menu-for-nsapp`\n- `allow-is-checked`\n- `allow-set-checked`\n- `allow-set-icon`", "type": "string", - "const": "core:menu:default" + "const": "core:menu:default", + "markdownDescription": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-new`\n- `allow-append`\n- `allow-prepend`\n- `allow-insert`\n- `allow-remove`\n- `allow-remove-at`\n- `allow-items`\n- `allow-get`\n- `allow-popup`\n- `allow-create-default`\n- `allow-set-as-app-menu`\n- `allow-set-as-window-menu`\n- `allow-text`\n- `allow-set-text`\n- `allow-is-enabled`\n- `allow-set-enabled`\n- `allow-set-accelerator`\n- `allow-set-as-windows-menu-for-nsapp`\n- `allow-set-as-help-menu-for-nsapp`\n- `allow-is-checked`\n- `allow-set-checked`\n- `allow-set-icon`" }, { "description": "Enables the append command without any pre-configured scope.", "type": "string", - "const": "core:menu:allow-append" + "const": "core:menu:allow-append", + "markdownDescription": "Enables the append command without any pre-configured scope." }, { "description": "Enables the create_default command without any pre-configured scope.", "type": "string", - "const": "core:menu:allow-create-default" + "const": "core:menu:allow-create-default", + "markdownDescription": "Enables the create_default command without any pre-configured scope." }, { "description": "Enables the get command without any pre-configured scope.", "type": "string", - "const": "core:menu:allow-get" + "const": "core:menu:allow-get", + "markdownDescription": "Enables the get command without any pre-configured scope." }, { "description": "Enables the insert command without any pre-configured scope.", "type": "string", - "const": "core:menu:allow-insert" + "const": "core:menu:allow-insert", + "markdownDescription": "Enables the insert command without any pre-configured scope." }, { "description": "Enables the is_checked command without any pre-configured scope.", "type": "string", - "const": "core:menu:allow-is-checked" + "const": "core:menu:allow-is-checked", + "markdownDescription": "Enables the is_checked command without any pre-configured scope." }, { "description": "Enables the is_enabled command without any pre-configured scope.", "type": "string", - "const": "core:menu:allow-is-enabled" + "const": "core:menu:allow-is-enabled", + "markdownDescription": "Enables the is_enabled command without any pre-configured scope." }, { "description": "Enables the items command without any pre-configured scope.", "type": "string", - "const": "core:menu:allow-items" + "const": "core:menu:allow-items", + "markdownDescription": "Enables the items command without any pre-configured scope." }, { "description": "Enables the new command without any pre-configured scope.", "type": "string", - "const": "core:menu:allow-new" + "const": "core:menu:allow-new", + "markdownDescription": "Enables the new command without any pre-configured scope." }, { "description": "Enables the popup command without any pre-configured scope.", "type": "string", - "const": "core:menu:allow-popup" + "const": "core:menu:allow-popup", + "markdownDescription": "Enables the popup command without any pre-configured scope." }, { "description": "Enables the prepend command without any pre-configured scope.", "type": "string", - "const": "core:menu:allow-prepend" + "const": "core:menu:allow-prepend", + "markdownDescription": "Enables the prepend command without any pre-configured scope." }, { "description": "Enables the remove command without any pre-configured scope.", "type": "string", - "const": "core:menu:allow-remove" + "const": "core:menu:allow-remove", + "markdownDescription": "Enables the remove command without any pre-configured scope." }, { "description": "Enables the remove_at command without any pre-configured scope.", "type": "string", - "const": "core:menu:allow-remove-at" + "const": "core:menu:allow-remove-at", + "markdownDescription": "Enables the remove_at command without any pre-configured scope." }, { "description": "Enables the set_accelerator command without any pre-configured scope.", "type": "string", - "const": "core:menu:allow-set-accelerator" + "const": "core:menu:allow-set-accelerator", + "markdownDescription": "Enables the set_accelerator command without any pre-configured scope." }, { "description": "Enables the set_as_app_menu command without any pre-configured scope.", "type": "string", - "const": "core:menu:allow-set-as-app-menu" + "const": "core:menu:allow-set-as-app-menu", + "markdownDescription": "Enables the set_as_app_menu command without any pre-configured scope." }, { "description": "Enables the set_as_help_menu_for_nsapp command without any pre-configured scope.", "type": "string", - "const": "core:menu:allow-set-as-help-menu-for-nsapp" + "const": "core:menu:allow-set-as-help-menu-for-nsapp", + "markdownDescription": "Enables the set_as_help_menu_for_nsapp command without any pre-configured scope." }, { "description": "Enables the set_as_window_menu command without any pre-configured scope.", "type": "string", - "const": "core:menu:allow-set-as-window-menu" + "const": "core:menu:allow-set-as-window-menu", + "markdownDescription": "Enables the set_as_window_menu command without any pre-configured scope." }, { "description": "Enables the set_as_windows_menu_for_nsapp command without any pre-configured scope.", "type": "string", - "const": "core:menu:allow-set-as-windows-menu-for-nsapp" + "const": "core:menu:allow-set-as-windows-menu-for-nsapp", + "markdownDescription": "Enables the set_as_windows_menu_for_nsapp command without any pre-configured scope." }, { "description": "Enables the set_checked command without any pre-configured scope.", "type": "string", - "const": "core:menu:allow-set-checked" + "const": "core:menu:allow-set-checked", + "markdownDescription": "Enables the set_checked command without any pre-configured scope." }, { "description": "Enables the set_enabled command without any pre-configured scope.", "type": "string", - "const": "core:menu:allow-set-enabled" + "const": "core:menu:allow-set-enabled", + "markdownDescription": "Enables the set_enabled command without any pre-configured scope." }, { "description": "Enables the set_icon command without any pre-configured scope.", "type": "string", - "const": "core:menu:allow-set-icon" + "const": "core:menu:allow-set-icon", + "markdownDescription": "Enables the set_icon command without any pre-configured scope." }, { "description": "Enables the set_text command without any pre-configured scope.", "type": "string", - "const": "core:menu:allow-set-text" + "const": "core:menu:allow-set-text", + "markdownDescription": "Enables the set_text command without any pre-configured scope." }, { "description": "Enables the text command without any pre-configured scope.", "type": "string", - "const": "core:menu:allow-text" + "const": "core:menu:allow-text", + "markdownDescription": "Enables the text command without any pre-configured scope." }, { "description": "Denies the append command without any pre-configured scope.", "type": "string", - "const": "core:menu:deny-append" + "const": "core:menu:deny-append", + "markdownDescription": "Denies the append command without any pre-configured scope." }, { "description": "Denies the create_default command without any pre-configured scope.", "type": "string", - "const": "core:menu:deny-create-default" + "const": "core:menu:deny-create-default", + "markdownDescription": "Denies the create_default command without any pre-configured scope." }, { "description": "Denies the get command without any pre-configured scope.", "type": "string", - "const": "core:menu:deny-get" + "const": "core:menu:deny-get", + "markdownDescription": "Denies the get command without any pre-configured scope." }, { "description": "Denies the insert command without any pre-configured scope.", "type": "string", - "const": "core:menu:deny-insert" + "const": "core:menu:deny-insert", + "markdownDescription": "Denies the insert command without any pre-configured scope." }, { "description": "Denies the is_checked command without any pre-configured scope.", "type": "string", - "const": "core:menu:deny-is-checked" + "const": "core:menu:deny-is-checked", + "markdownDescription": "Denies the is_checked command without any pre-configured scope." }, { "description": "Denies the is_enabled command without any pre-configured scope.", "type": "string", - "const": "core:menu:deny-is-enabled" + "const": "core:menu:deny-is-enabled", + "markdownDescription": "Denies the is_enabled command without any pre-configured scope." }, { "description": "Denies the items command without any pre-configured scope.", "type": "string", - "const": "core:menu:deny-items" + "const": "core:menu:deny-items", + "markdownDescription": "Denies the items command without any pre-configured scope." }, { "description": "Denies the new command without any pre-configured scope.", "type": "string", - "const": "core:menu:deny-new" + "const": "core:menu:deny-new", + "markdownDescription": "Denies the new command without any pre-configured scope." }, { "description": "Denies the popup command without any pre-configured scope.", "type": "string", - "const": "core:menu:deny-popup" + "const": "core:menu:deny-popup", + "markdownDescription": "Denies the popup command without any pre-configured scope." }, { "description": "Denies the prepend command without any pre-configured scope.", "type": "string", - "const": "core:menu:deny-prepend" + "const": "core:menu:deny-prepend", + "markdownDescription": "Denies the prepend command without any pre-configured scope." }, { "description": "Denies the remove command without any pre-configured scope.", "type": "string", - "const": "core:menu:deny-remove" + "const": "core:menu:deny-remove", + "markdownDescription": "Denies the remove command without any pre-configured scope." }, { "description": "Denies the remove_at command without any pre-configured scope.", "type": "string", - "const": "core:menu:deny-remove-at" + "const": "core:menu:deny-remove-at", + "markdownDescription": "Denies the remove_at command without any pre-configured scope." }, { "description": "Denies the set_accelerator command without any pre-configured scope.", "type": "string", - "const": "core:menu:deny-set-accelerator" + "const": "core:menu:deny-set-accelerator", + "markdownDescription": "Denies the set_accelerator command without any pre-configured scope." }, { "description": "Denies the set_as_app_menu command without any pre-configured scope.", "type": "string", - "const": "core:menu:deny-set-as-app-menu" + "const": "core:menu:deny-set-as-app-menu", + "markdownDescription": "Denies the set_as_app_menu command without any pre-configured scope." }, { "description": "Denies the set_as_help_menu_for_nsapp command without any pre-configured scope.", "type": "string", - "const": "core:menu:deny-set-as-help-menu-for-nsapp" + "const": "core:menu:deny-set-as-help-menu-for-nsapp", + "markdownDescription": "Denies the set_as_help_menu_for_nsapp command without any pre-configured scope." }, { "description": "Denies the set_as_window_menu command without any pre-configured scope.", "type": "string", - "const": "core:menu:deny-set-as-window-menu" + "const": "core:menu:deny-set-as-window-menu", + "markdownDescription": "Denies the set_as_window_menu command without any pre-configured scope." }, { "description": "Denies the set_as_windows_menu_for_nsapp command without any pre-configured scope.", "type": "string", - "const": "core:menu:deny-set-as-windows-menu-for-nsapp" + "const": "core:menu:deny-set-as-windows-menu-for-nsapp", + "markdownDescription": "Denies the set_as_windows_menu_for_nsapp command without any pre-configured scope." }, { "description": "Denies the set_checked command without any pre-configured scope.", "type": "string", - "const": "core:menu:deny-set-checked" + "const": "core:menu:deny-set-checked", + "markdownDescription": "Denies the set_checked command without any pre-configured scope." }, { "description": "Denies the set_enabled command without any pre-configured scope.", "type": "string", - "const": "core:menu:deny-set-enabled" + "const": "core:menu:deny-set-enabled", + "markdownDescription": "Denies the set_enabled command without any pre-configured scope." }, { "description": "Denies the set_icon command without any pre-configured scope.", "type": "string", - "const": "core:menu:deny-set-icon" + "const": "core:menu:deny-set-icon", + "markdownDescription": "Denies the set_icon command without any pre-configured scope." }, { "description": "Denies the set_text command without any pre-configured scope.", "type": "string", - "const": "core:menu:deny-set-text" + "const": "core:menu:deny-set-text", + "markdownDescription": "Denies the set_text command without any pre-configured scope." }, { "description": "Denies the text command without any pre-configured scope.", "type": "string", - "const": "core:menu:deny-text" + "const": "core:menu:deny-text", + "markdownDescription": "Denies the text command without any pre-configured scope." }, { - "description": "Default permissions for the plugin.", + "description": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-resolve-directory`\n- `allow-resolve`\n- `allow-normalize`\n- `allow-join`\n- `allow-dirname`\n- `allow-extname`\n- `allow-basename`\n- `allow-is-absolute`", "type": "string", - "const": "core:path:default" + "const": "core:path:default", + "markdownDescription": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-resolve-directory`\n- `allow-resolve`\n- `allow-normalize`\n- `allow-join`\n- `allow-dirname`\n- `allow-extname`\n- `allow-basename`\n- `allow-is-absolute`" }, { "description": "Enables the basename command without any pre-configured scope.", "type": "string", - "const": "core:path:allow-basename" + "const": "core:path:allow-basename", + "markdownDescription": "Enables the basename command without any pre-configured scope." }, { "description": "Enables the dirname command without any pre-configured scope.", "type": "string", - "const": "core:path:allow-dirname" + "const": "core:path:allow-dirname", + "markdownDescription": "Enables the dirname command without any pre-configured scope." }, { "description": "Enables the extname command without any pre-configured scope.", "type": "string", - "const": "core:path:allow-extname" + "const": "core:path:allow-extname", + "markdownDescription": "Enables the extname command without any pre-configured scope." }, { "description": "Enables the is_absolute command without any pre-configured scope.", "type": "string", - "const": "core:path:allow-is-absolute" + "const": "core:path:allow-is-absolute", + "markdownDescription": "Enables the is_absolute command without any pre-configured scope." }, { "description": "Enables the join command without any pre-configured scope.", "type": "string", - "const": "core:path:allow-join" + "const": "core:path:allow-join", + "markdownDescription": "Enables the join command without any pre-configured scope." }, { "description": "Enables the normalize command without any pre-configured scope.", "type": "string", - "const": "core:path:allow-normalize" + "const": "core:path:allow-normalize", + "markdownDescription": "Enables the normalize command without any pre-configured scope." }, { "description": "Enables the resolve command without any pre-configured scope.", "type": "string", - "const": "core:path:allow-resolve" + "const": "core:path:allow-resolve", + "markdownDescription": "Enables the resolve command without any pre-configured scope." }, { "description": "Enables the resolve_directory command without any pre-configured scope.", "type": "string", - "const": "core:path:allow-resolve-directory" + "const": "core:path:allow-resolve-directory", + "markdownDescription": "Enables the resolve_directory command without any pre-configured scope." }, { "description": "Denies the basename command without any pre-configured scope.", "type": "string", - "const": "core:path:deny-basename" + "const": "core:path:deny-basename", + "markdownDescription": "Denies the basename command without any pre-configured scope." }, { "description": "Denies the dirname command without any pre-configured scope.", "type": "string", - "const": "core:path:deny-dirname" + "const": "core:path:deny-dirname", + "markdownDescription": "Denies the dirname command without any pre-configured scope." }, { "description": "Denies the extname command without any pre-configured scope.", "type": "string", - "const": "core:path:deny-extname" + "const": "core:path:deny-extname", + "markdownDescription": "Denies the extname command without any pre-configured scope." }, { "description": "Denies the is_absolute command without any pre-configured scope.", "type": "string", - "const": "core:path:deny-is-absolute" + "const": "core:path:deny-is-absolute", + "markdownDescription": "Denies the is_absolute command without any pre-configured scope." }, { "description": "Denies the join command without any pre-configured scope.", "type": "string", - "const": "core:path:deny-join" + "const": "core:path:deny-join", + "markdownDescription": "Denies the join command without any pre-configured scope." }, { "description": "Denies the normalize command without any pre-configured scope.", "type": "string", - "const": "core:path:deny-normalize" + "const": "core:path:deny-normalize", + "markdownDescription": "Denies the normalize command without any pre-configured scope." }, { "description": "Denies the resolve command without any pre-configured scope.", "type": "string", - "const": "core:path:deny-resolve" + "const": "core:path:deny-resolve", + "markdownDescription": "Denies the resolve command without any pre-configured scope." }, { "description": "Denies the resolve_directory command without any pre-configured scope.", "type": "string", - "const": "core:path:deny-resolve-directory" + "const": "core:path:deny-resolve-directory", + "markdownDescription": "Denies the resolve_directory command without any pre-configured scope." }, { - "description": "Default permissions for the plugin.", + "description": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-close`", "type": "string", - "const": "core:resources:default" + "const": "core:resources:default", + "markdownDescription": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-close`" }, { "description": "Enables the close command without any pre-configured scope.", "type": "string", - "const": "core:resources:allow-close" + "const": "core:resources:allow-close", + "markdownDescription": "Enables the close command without any pre-configured scope." }, { "description": "Denies the close command without any pre-configured scope.", "type": "string", - "const": "core:resources:deny-close" + "const": "core:resources:deny-close", + "markdownDescription": "Denies the close command without any pre-configured scope." }, { - "description": "Default permissions for the plugin.", + "description": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-new`\n- `allow-get-by-id`\n- `allow-remove-by-id`\n- `allow-set-icon`\n- `allow-set-menu`\n- `allow-set-tooltip`\n- `allow-set-title`\n- `allow-set-visible`\n- `allow-set-temp-dir-path`\n- `allow-set-icon-as-template`\n- `allow-set-show-menu-on-left-click`", "type": "string", - "const": "core:tray:default" + "const": "core:tray:default", + "markdownDescription": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-new`\n- `allow-get-by-id`\n- `allow-remove-by-id`\n- `allow-set-icon`\n- `allow-set-menu`\n- `allow-set-tooltip`\n- `allow-set-title`\n- `allow-set-visible`\n- `allow-set-temp-dir-path`\n- `allow-set-icon-as-template`\n- `allow-set-show-menu-on-left-click`" }, { "description": "Enables the get_by_id command without any pre-configured scope.", "type": "string", - "const": "core:tray:allow-get-by-id" + "const": "core:tray:allow-get-by-id", + "markdownDescription": "Enables the get_by_id command without any pre-configured scope." }, { "description": "Enables the new command without any pre-configured scope.", "type": "string", - "const": "core:tray:allow-new" + "const": "core:tray:allow-new", + "markdownDescription": "Enables the new command without any pre-configured scope." }, { "description": "Enables the remove_by_id command without any pre-configured scope.", "type": "string", - "const": "core:tray:allow-remove-by-id" + "const": "core:tray:allow-remove-by-id", + "markdownDescription": "Enables the remove_by_id command without any pre-configured scope." }, { "description": "Enables the set_icon command without any pre-configured scope.", "type": "string", - "const": "core:tray:allow-set-icon" + "const": "core:tray:allow-set-icon", + "markdownDescription": "Enables the set_icon command without any pre-configured scope." }, { "description": "Enables the set_icon_as_template command without any pre-configured scope.", "type": "string", - "const": "core:tray:allow-set-icon-as-template" + "const": "core:tray:allow-set-icon-as-template", + "markdownDescription": "Enables the set_icon_as_template command without any pre-configured scope." }, { "description": "Enables the set_menu command without any pre-configured scope.", "type": "string", - "const": "core:tray:allow-set-menu" + "const": "core:tray:allow-set-menu", + "markdownDescription": "Enables the set_menu command without any pre-configured scope." }, { "description": "Enables the set_show_menu_on_left_click command without any pre-configured scope.", "type": "string", - "const": "core:tray:allow-set-show-menu-on-left-click" + "const": "core:tray:allow-set-show-menu-on-left-click", + "markdownDescription": "Enables the set_show_menu_on_left_click command without any pre-configured scope." }, { "description": "Enables the set_temp_dir_path command without any pre-configured scope.", "type": "string", - "const": "core:tray:allow-set-temp-dir-path" + "const": "core:tray:allow-set-temp-dir-path", + "markdownDescription": "Enables the set_temp_dir_path command without any pre-configured scope." }, { "description": "Enables the set_title command without any pre-configured scope.", "type": "string", - "const": "core:tray:allow-set-title" + "const": "core:tray:allow-set-title", + "markdownDescription": "Enables the set_title command without any pre-configured scope." }, { "description": "Enables the set_tooltip command without any pre-configured scope.", "type": "string", - "const": "core:tray:allow-set-tooltip" + "const": "core:tray:allow-set-tooltip", + "markdownDescription": "Enables the set_tooltip command without any pre-configured scope." }, { "description": "Enables the set_visible command without any pre-configured scope.", "type": "string", - "const": "core:tray:allow-set-visible" + "const": "core:tray:allow-set-visible", + "markdownDescription": "Enables the set_visible command without any pre-configured scope." }, { "description": "Denies the get_by_id command without any pre-configured scope.", "type": "string", - "const": "core:tray:deny-get-by-id" + "const": "core:tray:deny-get-by-id", + "markdownDescription": "Denies the get_by_id command without any pre-configured scope." }, { "description": "Denies the new command without any pre-configured scope.", "type": "string", - "const": "core:tray:deny-new" + "const": "core:tray:deny-new", + "markdownDescription": "Denies the new command without any pre-configured scope." }, { "description": "Denies the remove_by_id command without any pre-configured scope.", "type": "string", - "const": "core:tray:deny-remove-by-id" + "const": "core:tray:deny-remove-by-id", + "markdownDescription": "Denies the remove_by_id command without any pre-configured scope." }, { "description": "Denies the set_icon command without any pre-configured scope.", "type": "string", - "const": "core:tray:deny-set-icon" + "const": "core:tray:deny-set-icon", + "markdownDescription": "Denies the set_icon command without any pre-configured scope." }, { "description": "Denies the set_icon_as_template command without any pre-configured scope.", "type": "string", - "const": "core:tray:deny-set-icon-as-template" + "const": "core:tray:deny-set-icon-as-template", + "markdownDescription": "Denies the set_icon_as_template command without any pre-configured scope." }, { "description": "Denies the set_menu command without any pre-configured scope.", "type": "string", - "const": "core:tray:deny-set-menu" + "const": "core:tray:deny-set-menu", + "markdownDescription": "Denies the set_menu command without any pre-configured scope." }, { "description": "Denies the set_show_menu_on_left_click command without any pre-configured scope.", "type": "string", - "const": "core:tray:deny-set-show-menu-on-left-click" + "const": "core:tray:deny-set-show-menu-on-left-click", + "markdownDescription": "Denies the set_show_menu_on_left_click command without any pre-configured scope." }, { "description": "Denies the set_temp_dir_path command without any pre-configured scope.", "type": "string", - "const": "core:tray:deny-set-temp-dir-path" + "const": "core:tray:deny-set-temp-dir-path", + "markdownDescription": "Denies the set_temp_dir_path command without any pre-configured scope." }, { "description": "Denies the set_title command without any pre-configured scope.", "type": "string", - "const": "core:tray:deny-set-title" + "const": "core:tray:deny-set-title", + "markdownDescription": "Denies the set_title command without any pre-configured scope." }, { "description": "Denies the set_tooltip command without any pre-configured scope.", "type": "string", - "const": "core:tray:deny-set-tooltip" + "const": "core:tray:deny-set-tooltip", + "markdownDescription": "Denies the set_tooltip command without any pre-configured scope." }, { "description": "Denies the set_visible command without any pre-configured scope.", "type": "string", - "const": "core:tray:deny-set-visible" + "const": "core:tray:deny-set-visible", + "markdownDescription": "Denies the set_visible command without any pre-configured scope." }, { - "description": "Default permissions for the plugin.", + "description": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-get-all-webviews`\n- `allow-webview-position`\n- `allow-webview-size`\n- `allow-internal-toggle-devtools`", "type": "string", - "const": "core:webview:default" + "const": "core:webview:default", + "markdownDescription": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-get-all-webviews`\n- `allow-webview-position`\n- `allow-webview-size`\n- `allow-internal-toggle-devtools`" }, { "description": "Enables the clear_all_browsing_data command without any pre-configured scope.", "type": "string", - "const": "core:webview:allow-clear-all-browsing-data" + "const": "core:webview:allow-clear-all-browsing-data", + "markdownDescription": "Enables the clear_all_browsing_data command without any pre-configured scope." }, { "description": "Enables the create_webview command without any pre-configured scope.", "type": "string", - "const": "core:webview:allow-create-webview" + "const": "core:webview:allow-create-webview", + "markdownDescription": "Enables the create_webview command without any pre-configured scope." }, { "description": "Enables the create_webview_window command without any pre-configured scope.", "type": "string", - "const": "core:webview:allow-create-webview-window" + "const": "core:webview:allow-create-webview-window", + "markdownDescription": "Enables the create_webview_window command without any pre-configured scope." }, { "description": "Enables the get_all_webviews command without any pre-configured scope.", "type": "string", - "const": "core:webview:allow-get-all-webviews" + "const": "core:webview:allow-get-all-webviews", + "markdownDescription": "Enables the get_all_webviews command without any pre-configured scope." }, { "description": "Enables the internal_toggle_devtools command without any pre-configured scope.", "type": "string", - "const": "core:webview:allow-internal-toggle-devtools" + "const": "core:webview:allow-internal-toggle-devtools", + "markdownDescription": "Enables the internal_toggle_devtools command without any pre-configured scope." }, { "description": "Enables the print command without any pre-configured scope.", "type": "string", - "const": "core:webview:allow-print" + "const": "core:webview:allow-print", + "markdownDescription": "Enables the print command without any pre-configured scope." }, { "description": "Enables the reparent command without any pre-configured scope.", "type": "string", - "const": "core:webview:allow-reparent" + "const": "core:webview:allow-reparent", + "markdownDescription": "Enables the reparent command without any pre-configured scope." }, { "description": "Enables the set_webview_background_color command without any pre-configured scope.", "type": "string", - "const": "core:webview:allow-set-webview-background-color" + "const": "core:webview:allow-set-webview-background-color", + "markdownDescription": "Enables the set_webview_background_color command without any pre-configured scope." }, { "description": "Enables the set_webview_focus command without any pre-configured scope.", "type": "string", - "const": "core:webview:allow-set-webview-focus" + "const": "core:webview:allow-set-webview-focus", + "markdownDescription": "Enables the set_webview_focus command without any pre-configured scope." }, { "description": "Enables the set_webview_position command without any pre-configured scope.", "type": "string", - "const": "core:webview:allow-set-webview-position" + "const": "core:webview:allow-set-webview-position", + "markdownDescription": "Enables the set_webview_position command without any pre-configured scope." }, { "description": "Enables the set_webview_size command without any pre-configured scope.", "type": "string", - "const": "core:webview:allow-set-webview-size" + "const": "core:webview:allow-set-webview-size", + "markdownDescription": "Enables the set_webview_size command without any pre-configured scope." }, { "description": "Enables the set_webview_zoom command without any pre-configured scope.", "type": "string", - "const": "core:webview:allow-set-webview-zoom" + "const": "core:webview:allow-set-webview-zoom", + "markdownDescription": "Enables the set_webview_zoom command without any pre-configured scope." }, { "description": "Enables the webview_close command without any pre-configured scope.", "type": "string", - "const": "core:webview:allow-webview-close" + "const": "core:webview:allow-webview-close", + "markdownDescription": "Enables the webview_close command without any pre-configured scope." }, { "description": "Enables the webview_hide command without any pre-configured scope.", "type": "string", - "const": "core:webview:allow-webview-hide" + "const": "core:webview:allow-webview-hide", + "markdownDescription": "Enables the webview_hide command without any pre-configured scope." }, { "description": "Enables the webview_position command without any pre-configured scope.", "type": "string", - "const": "core:webview:allow-webview-position" + "const": "core:webview:allow-webview-position", + "markdownDescription": "Enables the webview_position command without any pre-configured scope." }, { "description": "Enables the webview_show command without any pre-configured scope.", "type": "string", - "const": "core:webview:allow-webview-show" + "const": "core:webview:allow-webview-show", + "markdownDescription": "Enables the webview_show command without any pre-configured scope." }, { "description": "Enables the webview_size command without any pre-configured scope.", "type": "string", - "const": "core:webview:allow-webview-size" + "const": "core:webview:allow-webview-size", + "markdownDescription": "Enables the webview_size command without any pre-configured scope." }, { "description": "Denies the clear_all_browsing_data command without any pre-configured scope.", "type": "string", - "const": "core:webview:deny-clear-all-browsing-data" + "const": "core:webview:deny-clear-all-browsing-data", + "markdownDescription": "Denies the clear_all_browsing_data command without any pre-configured scope." }, { "description": "Denies the create_webview command without any pre-configured scope.", "type": "string", - "const": "core:webview:deny-create-webview" + "const": "core:webview:deny-create-webview", + "markdownDescription": "Denies the create_webview command without any pre-configured scope." }, { "description": "Denies the create_webview_window command without any pre-configured scope.", "type": "string", - "const": "core:webview:deny-create-webview-window" + "const": "core:webview:deny-create-webview-window", + "markdownDescription": "Denies the create_webview_window command without any pre-configured scope." }, { "description": "Denies the get_all_webviews command without any pre-configured scope.", "type": "string", - "const": "core:webview:deny-get-all-webviews" + "const": "core:webview:deny-get-all-webviews", + "markdownDescription": "Denies the get_all_webviews command without any pre-configured scope." }, { "description": "Denies the internal_toggle_devtools command without any pre-configured scope.", "type": "string", - "const": "core:webview:deny-internal-toggle-devtools" + "const": "core:webview:deny-internal-toggle-devtools", + "markdownDescription": "Denies the internal_toggle_devtools command without any pre-configured scope." }, { "description": "Denies the print command without any pre-configured scope.", "type": "string", - "const": "core:webview:deny-print" + "const": "core:webview:deny-print", + "markdownDescription": "Denies the print command without any pre-configured scope." }, { "description": "Denies the reparent command without any pre-configured scope.", "type": "string", - "const": "core:webview:deny-reparent" + "const": "core:webview:deny-reparent", + "markdownDescription": "Denies the reparent command without any pre-configured scope." }, { "description": "Denies the set_webview_background_color command without any pre-configured scope.", "type": "string", - "const": "core:webview:deny-set-webview-background-color" + "const": "core:webview:deny-set-webview-background-color", + "markdownDescription": "Denies the set_webview_background_color command without any pre-configured scope." }, { "description": "Denies the set_webview_focus command without any pre-configured scope.", "type": "string", - "const": "core:webview:deny-set-webview-focus" + "const": "core:webview:deny-set-webview-focus", + "markdownDescription": "Denies the set_webview_focus command without any pre-configured scope." }, { "description": "Denies the set_webview_position command without any pre-configured scope.", "type": "string", - "const": "core:webview:deny-set-webview-position" + "const": "core:webview:deny-set-webview-position", + "markdownDescription": "Denies the set_webview_position command without any pre-configured scope." }, { "description": "Denies the set_webview_size command without any pre-configured scope.", "type": "string", - "const": "core:webview:deny-set-webview-size" + "const": "core:webview:deny-set-webview-size", + "markdownDescription": "Denies the set_webview_size command without any pre-configured scope." }, { "description": "Denies the set_webview_zoom command without any pre-configured scope.", "type": "string", - "const": "core:webview:deny-set-webview-zoom" + "const": "core:webview:deny-set-webview-zoom", + "markdownDescription": "Denies the set_webview_zoom command without any pre-configured scope." }, { "description": "Denies the webview_close command without any pre-configured scope.", "type": "string", - "const": "core:webview:deny-webview-close" + "const": "core:webview:deny-webview-close", + "markdownDescription": "Denies the webview_close command without any pre-configured scope." }, { "description": "Denies the webview_hide command without any pre-configured scope.", "type": "string", - "const": "core:webview:deny-webview-hide" + "const": "core:webview:deny-webview-hide", + "markdownDescription": "Denies the webview_hide command without any pre-configured scope." }, { "description": "Denies the webview_position command without any pre-configured scope.", "type": "string", - "const": "core:webview:deny-webview-position" + "const": "core:webview:deny-webview-position", + "markdownDescription": "Denies the webview_position command without any pre-configured scope." }, { "description": "Denies the webview_show command without any pre-configured scope.", "type": "string", - "const": "core:webview:deny-webview-show" + "const": "core:webview:deny-webview-show", + "markdownDescription": "Denies the webview_show command without any pre-configured scope." }, { "description": "Denies the webview_size command without any pre-configured scope.", "type": "string", - "const": "core:webview:deny-webview-size" + "const": "core:webview:deny-webview-size", + "markdownDescription": "Denies the webview_size command without any pre-configured scope." }, { - "description": "Default permissions for the plugin.", + "description": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-get-all-windows`\n- `allow-scale-factor`\n- `allow-inner-position`\n- `allow-outer-position`\n- `allow-inner-size`\n- `allow-outer-size`\n- `allow-is-fullscreen`\n- `allow-is-minimized`\n- `allow-is-maximized`\n- `allow-is-focused`\n- `allow-is-decorated`\n- `allow-is-resizable`\n- `allow-is-maximizable`\n- `allow-is-minimizable`\n- `allow-is-closable`\n- `allow-is-visible`\n- `allow-is-enabled`\n- `allow-title`\n- `allow-current-monitor`\n- `allow-primary-monitor`\n- `allow-monitor-from-point`\n- `allow-available-monitors`\n- `allow-cursor-position`\n- `allow-theme`\n- `allow-is-always-on-top`\n- `allow-internal-toggle-maximize`", "type": "string", - "const": "core:window:default" + "const": "core:window:default", + "markdownDescription": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-get-all-windows`\n- `allow-scale-factor`\n- `allow-inner-position`\n- `allow-outer-position`\n- `allow-inner-size`\n- `allow-outer-size`\n- `allow-is-fullscreen`\n- `allow-is-minimized`\n- `allow-is-maximized`\n- `allow-is-focused`\n- `allow-is-decorated`\n- `allow-is-resizable`\n- `allow-is-maximizable`\n- `allow-is-minimizable`\n- `allow-is-closable`\n- `allow-is-visible`\n- `allow-is-enabled`\n- `allow-title`\n- `allow-current-monitor`\n- `allow-primary-monitor`\n- `allow-monitor-from-point`\n- `allow-available-monitors`\n- `allow-cursor-position`\n- `allow-theme`\n- `allow-is-always-on-top`\n- `allow-internal-toggle-maximize`" }, { "description": "Enables the available_monitors command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-available-monitors" + "const": "core:window:allow-available-monitors", + "markdownDescription": "Enables the available_monitors command without any pre-configured scope." }, { "description": "Enables the center command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-center" + "const": "core:window:allow-center", + "markdownDescription": "Enables the center command without any pre-configured scope." }, { "description": "Enables the close command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-close" + "const": "core:window:allow-close", + "markdownDescription": "Enables the close command without any pre-configured scope." }, { "description": "Enables the create command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-create" + "const": "core:window:allow-create", + "markdownDescription": "Enables the create command without any pre-configured scope." }, { "description": "Enables the current_monitor command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-current-monitor" + "const": "core:window:allow-current-monitor", + "markdownDescription": "Enables the current_monitor command without any pre-configured scope." }, { "description": "Enables the cursor_position command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-cursor-position" + "const": "core:window:allow-cursor-position", + "markdownDescription": "Enables the cursor_position command without any pre-configured scope." }, { "description": "Enables the destroy command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-destroy" + "const": "core:window:allow-destroy", + "markdownDescription": "Enables the destroy command without any pre-configured scope." }, { "description": "Enables the get_all_windows command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-get-all-windows" + "const": "core:window:allow-get-all-windows", + "markdownDescription": "Enables the get_all_windows command without any pre-configured scope." }, { "description": "Enables the hide command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-hide" + "const": "core:window:allow-hide", + "markdownDescription": "Enables the hide command without any pre-configured scope." }, { "description": "Enables the inner_position command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-inner-position" + "const": "core:window:allow-inner-position", + "markdownDescription": "Enables the inner_position command without any pre-configured scope." }, { "description": "Enables the inner_size command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-inner-size" + "const": "core:window:allow-inner-size", + "markdownDescription": "Enables the inner_size command without any pre-configured scope." }, { "description": "Enables the internal_toggle_maximize command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-internal-toggle-maximize" + "const": "core:window:allow-internal-toggle-maximize", + "markdownDescription": "Enables the internal_toggle_maximize command without any pre-configured scope." }, { "description": "Enables the is_always_on_top command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-is-always-on-top" + "const": "core:window:allow-is-always-on-top", + "markdownDescription": "Enables the is_always_on_top command without any pre-configured scope." }, { "description": "Enables the is_closable command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-is-closable" + "const": "core:window:allow-is-closable", + "markdownDescription": "Enables the is_closable command without any pre-configured scope." }, { "description": "Enables the is_decorated command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-is-decorated" + "const": "core:window:allow-is-decorated", + "markdownDescription": "Enables the is_decorated command without any pre-configured scope." }, { "description": "Enables the is_enabled command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-is-enabled" + "const": "core:window:allow-is-enabled", + "markdownDescription": "Enables the is_enabled command without any pre-configured scope." }, { "description": "Enables the is_focused command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-is-focused" + "const": "core:window:allow-is-focused", + "markdownDescription": "Enables the is_focused command without any pre-configured scope." }, { "description": "Enables the is_fullscreen command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-is-fullscreen" + "const": "core:window:allow-is-fullscreen", + "markdownDescription": "Enables the is_fullscreen command without any pre-configured scope." }, { "description": "Enables the is_maximizable command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-is-maximizable" + "const": "core:window:allow-is-maximizable", + "markdownDescription": "Enables the is_maximizable command without any pre-configured scope." }, { "description": "Enables the is_maximized command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-is-maximized" + "const": "core:window:allow-is-maximized", + "markdownDescription": "Enables the is_maximized command without any pre-configured scope." }, { "description": "Enables the is_minimizable command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-is-minimizable" + "const": "core:window:allow-is-minimizable", + "markdownDescription": "Enables the is_minimizable command without any pre-configured scope." }, { "description": "Enables the is_minimized command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-is-minimized" + "const": "core:window:allow-is-minimized", + "markdownDescription": "Enables the is_minimized command without any pre-configured scope." }, { "description": "Enables the is_resizable command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-is-resizable" + "const": "core:window:allow-is-resizable", + "markdownDescription": "Enables the is_resizable command without any pre-configured scope." }, { "description": "Enables the is_visible command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-is-visible" + "const": "core:window:allow-is-visible", + "markdownDescription": "Enables the is_visible command without any pre-configured scope." }, { "description": "Enables the maximize command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-maximize" + "const": "core:window:allow-maximize", + "markdownDescription": "Enables the maximize command without any pre-configured scope." }, { "description": "Enables the minimize command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-minimize" + "const": "core:window:allow-minimize", + "markdownDescription": "Enables the minimize command without any pre-configured scope." }, { "description": "Enables the monitor_from_point command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-monitor-from-point" + "const": "core:window:allow-monitor-from-point", + "markdownDescription": "Enables the monitor_from_point command without any pre-configured scope." }, { "description": "Enables the outer_position command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-outer-position" + "const": "core:window:allow-outer-position", + "markdownDescription": "Enables the outer_position command without any pre-configured scope." }, { "description": "Enables the outer_size command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-outer-size" + "const": "core:window:allow-outer-size", + "markdownDescription": "Enables the outer_size command without any pre-configured scope." }, { "description": "Enables the primary_monitor command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-primary-monitor" + "const": "core:window:allow-primary-monitor", + "markdownDescription": "Enables the primary_monitor command without any pre-configured scope." }, { "description": "Enables the request_user_attention command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-request-user-attention" + "const": "core:window:allow-request-user-attention", + "markdownDescription": "Enables the request_user_attention command without any pre-configured scope." }, { "description": "Enables the scale_factor command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-scale-factor" + "const": "core:window:allow-scale-factor", + "markdownDescription": "Enables the scale_factor command without any pre-configured scope." }, { "description": "Enables the set_always_on_bottom command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-set-always-on-bottom" + "const": "core:window:allow-set-always-on-bottom", + "markdownDescription": "Enables the set_always_on_bottom command without any pre-configured scope." }, { "description": "Enables the set_always_on_top command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-set-always-on-top" + "const": "core:window:allow-set-always-on-top", + "markdownDescription": "Enables the set_always_on_top command without any pre-configured scope." }, { "description": "Enables the set_background_color command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-set-background-color" + "const": "core:window:allow-set-background-color", + "markdownDescription": "Enables the set_background_color command without any pre-configured scope." }, { "description": "Enables the set_badge_count command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-set-badge-count" + "const": "core:window:allow-set-badge-count", + "markdownDescription": "Enables the set_badge_count command without any pre-configured scope." }, { "description": "Enables the set_badge_label command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-set-badge-label" + "const": "core:window:allow-set-badge-label", + "markdownDescription": "Enables the set_badge_label command without any pre-configured scope." }, { "description": "Enables the set_closable command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-set-closable" + "const": "core:window:allow-set-closable", + "markdownDescription": "Enables the set_closable command without any pre-configured scope." }, { "description": "Enables the set_content_protected command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-set-content-protected" + "const": "core:window:allow-set-content-protected", + "markdownDescription": "Enables the set_content_protected command without any pre-configured scope." }, { "description": "Enables the set_cursor_grab command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-set-cursor-grab" + "const": "core:window:allow-set-cursor-grab", + "markdownDescription": "Enables the set_cursor_grab command without any pre-configured scope." }, { "description": "Enables the set_cursor_icon command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-set-cursor-icon" + "const": "core:window:allow-set-cursor-icon", + "markdownDescription": "Enables the set_cursor_icon command without any pre-configured scope." }, { "description": "Enables the set_cursor_position command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-set-cursor-position" + "const": "core:window:allow-set-cursor-position", + "markdownDescription": "Enables the set_cursor_position command without any pre-configured scope." }, { "description": "Enables the set_cursor_visible command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-set-cursor-visible" + "const": "core:window:allow-set-cursor-visible", + "markdownDescription": "Enables the set_cursor_visible command without any pre-configured scope." }, { "description": "Enables the set_decorations command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-set-decorations" + "const": "core:window:allow-set-decorations", + "markdownDescription": "Enables the set_decorations command without any pre-configured scope." }, { "description": "Enables the set_effects command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-set-effects" + "const": "core:window:allow-set-effects", + "markdownDescription": "Enables the set_effects command without any pre-configured scope." }, { "description": "Enables the set_enabled command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-set-enabled" + "const": "core:window:allow-set-enabled", + "markdownDescription": "Enables the set_enabled command without any pre-configured scope." }, { "description": "Enables the set_focus command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-set-focus" + "const": "core:window:allow-set-focus", + "markdownDescription": "Enables the set_focus command without any pre-configured scope." }, { "description": "Enables the set_fullscreen command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-set-fullscreen" + "const": "core:window:allow-set-fullscreen", + "markdownDescription": "Enables the set_fullscreen command without any pre-configured scope." }, { "description": "Enables the set_icon command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-set-icon" + "const": "core:window:allow-set-icon", + "markdownDescription": "Enables the set_icon command without any pre-configured scope." }, { "description": "Enables the set_ignore_cursor_events command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-set-ignore-cursor-events" + "const": "core:window:allow-set-ignore-cursor-events", + "markdownDescription": "Enables the set_ignore_cursor_events command without any pre-configured scope." }, { "description": "Enables the set_max_size command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-set-max-size" + "const": "core:window:allow-set-max-size", + "markdownDescription": "Enables the set_max_size command without any pre-configured scope." }, { "description": "Enables the set_maximizable command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-set-maximizable" + "const": "core:window:allow-set-maximizable", + "markdownDescription": "Enables the set_maximizable command without any pre-configured scope." }, { "description": "Enables the set_min_size command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-set-min-size" + "const": "core:window:allow-set-min-size", + "markdownDescription": "Enables the set_min_size command without any pre-configured scope." }, { "description": "Enables the set_minimizable command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-set-minimizable" + "const": "core:window:allow-set-minimizable", + "markdownDescription": "Enables the set_minimizable command without any pre-configured scope." }, { "description": "Enables the set_overlay_icon command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-set-overlay-icon" + "const": "core:window:allow-set-overlay-icon", + "markdownDescription": "Enables the set_overlay_icon command without any pre-configured scope." }, { "description": "Enables the set_position command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-set-position" + "const": "core:window:allow-set-position", + "markdownDescription": "Enables the set_position command without any pre-configured scope." }, { "description": "Enables the set_progress_bar command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-set-progress-bar" + "const": "core:window:allow-set-progress-bar", + "markdownDescription": "Enables the set_progress_bar command without any pre-configured scope." }, { "description": "Enables the set_resizable command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-set-resizable" + "const": "core:window:allow-set-resizable", + "markdownDescription": "Enables the set_resizable command without any pre-configured scope." }, { "description": "Enables the set_shadow command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-set-shadow" + "const": "core:window:allow-set-shadow", + "markdownDescription": "Enables the set_shadow command without any pre-configured scope." }, { "description": "Enables the set_size command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-set-size" + "const": "core:window:allow-set-size", + "markdownDescription": "Enables the set_size command without any pre-configured scope." }, { "description": "Enables the set_size_constraints command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-set-size-constraints" + "const": "core:window:allow-set-size-constraints", + "markdownDescription": "Enables the set_size_constraints command without any pre-configured scope." }, { "description": "Enables the set_skip_taskbar command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-set-skip-taskbar" + "const": "core:window:allow-set-skip-taskbar", + "markdownDescription": "Enables the set_skip_taskbar command without any pre-configured scope." }, { "description": "Enables the set_theme command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-set-theme" + "const": "core:window:allow-set-theme", + "markdownDescription": "Enables the set_theme command without any pre-configured scope." }, { "description": "Enables the set_title command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-set-title" + "const": "core:window:allow-set-title", + "markdownDescription": "Enables the set_title command without any pre-configured scope." }, { "description": "Enables the set_title_bar_style command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-set-title-bar-style" + "const": "core:window:allow-set-title-bar-style", + "markdownDescription": "Enables the set_title_bar_style command without any pre-configured scope." }, { "description": "Enables the set_visible_on_all_workspaces command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-set-visible-on-all-workspaces" + "const": "core:window:allow-set-visible-on-all-workspaces", + "markdownDescription": "Enables the set_visible_on_all_workspaces command without any pre-configured scope." }, { "description": "Enables the show command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-show" + "const": "core:window:allow-show", + "markdownDescription": "Enables the show command without any pre-configured scope." }, { "description": "Enables the start_dragging command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-start-dragging" + "const": "core:window:allow-start-dragging", + "markdownDescription": "Enables the start_dragging command without any pre-configured scope." }, { "description": "Enables the start_resize_dragging command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-start-resize-dragging" + "const": "core:window:allow-start-resize-dragging", + "markdownDescription": "Enables the start_resize_dragging command without any pre-configured scope." }, { "description": "Enables the theme command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-theme" + "const": "core:window:allow-theme", + "markdownDescription": "Enables the theme command without any pre-configured scope." }, { "description": "Enables the title command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-title" + "const": "core:window:allow-title", + "markdownDescription": "Enables the title command without any pre-configured scope." }, { "description": "Enables the toggle_maximize command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-toggle-maximize" + "const": "core:window:allow-toggle-maximize", + "markdownDescription": "Enables the toggle_maximize command without any pre-configured scope." }, { "description": "Enables the unmaximize command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-unmaximize" + "const": "core:window:allow-unmaximize", + "markdownDescription": "Enables the unmaximize command without any pre-configured scope." }, { "description": "Enables the unminimize command without any pre-configured scope.", "type": "string", - "const": "core:window:allow-unminimize" + "const": "core:window:allow-unminimize", + "markdownDescription": "Enables the unminimize command without any pre-configured scope." }, { "description": "Denies the available_monitors command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-available-monitors" + "const": "core:window:deny-available-monitors", + "markdownDescription": "Denies the available_monitors command without any pre-configured scope." }, { "description": "Denies the center command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-center" + "const": "core:window:deny-center", + "markdownDescription": "Denies the center command without any pre-configured scope." }, { "description": "Denies the close command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-close" + "const": "core:window:deny-close", + "markdownDescription": "Denies the close command without any pre-configured scope." }, { "description": "Denies the create command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-create" + "const": "core:window:deny-create", + "markdownDescription": "Denies the create command without any pre-configured scope." }, { "description": "Denies the current_monitor command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-current-monitor" + "const": "core:window:deny-current-monitor", + "markdownDescription": "Denies the current_monitor command without any pre-configured scope." }, { "description": "Denies the cursor_position command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-cursor-position" + "const": "core:window:deny-cursor-position", + "markdownDescription": "Denies the cursor_position command without any pre-configured scope." }, { "description": "Denies the destroy command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-destroy" + "const": "core:window:deny-destroy", + "markdownDescription": "Denies the destroy command without any pre-configured scope." }, { "description": "Denies the get_all_windows command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-get-all-windows" + "const": "core:window:deny-get-all-windows", + "markdownDescription": "Denies the get_all_windows command without any pre-configured scope." }, { "description": "Denies the hide command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-hide" + "const": "core:window:deny-hide", + "markdownDescription": "Denies the hide command without any pre-configured scope." }, { "description": "Denies the inner_position command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-inner-position" + "const": "core:window:deny-inner-position", + "markdownDescription": "Denies the inner_position command without any pre-configured scope." }, { "description": "Denies the inner_size command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-inner-size" + "const": "core:window:deny-inner-size", + "markdownDescription": "Denies the inner_size command without any pre-configured scope." }, { "description": "Denies the internal_toggle_maximize command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-internal-toggle-maximize" + "const": "core:window:deny-internal-toggle-maximize", + "markdownDescription": "Denies the internal_toggle_maximize command without any pre-configured scope." }, { "description": "Denies the is_always_on_top command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-is-always-on-top" + "const": "core:window:deny-is-always-on-top", + "markdownDescription": "Denies the is_always_on_top command without any pre-configured scope." }, { "description": "Denies the is_closable command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-is-closable" + "const": "core:window:deny-is-closable", + "markdownDescription": "Denies the is_closable command without any pre-configured scope." }, { "description": "Denies the is_decorated command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-is-decorated" + "const": "core:window:deny-is-decorated", + "markdownDescription": "Denies the is_decorated command without any pre-configured scope." }, { "description": "Denies the is_enabled command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-is-enabled" + "const": "core:window:deny-is-enabled", + "markdownDescription": "Denies the is_enabled command without any pre-configured scope." }, { "description": "Denies the is_focused command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-is-focused" + "const": "core:window:deny-is-focused", + "markdownDescription": "Denies the is_focused command without any pre-configured scope." }, { "description": "Denies the is_fullscreen command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-is-fullscreen" + "const": "core:window:deny-is-fullscreen", + "markdownDescription": "Denies the is_fullscreen command without any pre-configured scope." }, { "description": "Denies the is_maximizable command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-is-maximizable" + "const": "core:window:deny-is-maximizable", + "markdownDescription": "Denies the is_maximizable command without any pre-configured scope." }, { "description": "Denies the is_maximized command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-is-maximized" + "const": "core:window:deny-is-maximized", + "markdownDescription": "Denies the is_maximized command without any pre-configured scope." }, { "description": "Denies the is_minimizable command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-is-minimizable" + "const": "core:window:deny-is-minimizable", + "markdownDescription": "Denies the is_minimizable command without any pre-configured scope." }, { "description": "Denies the is_minimized command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-is-minimized" + "const": "core:window:deny-is-minimized", + "markdownDescription": "Denies the is_minimized command without any pre-configured scope." }, { "description": "Denies the is_resizable command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-is-resizable" + "const": "core:window:deny-is-resizable", + "markdownDescription": "Denies the is_resizable command without any pre-configured scope." }, { "description": "Denies the is_visible command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-is-visible" + "const": "core:window:deny-is-visible", + "markdownDescription": "Denies the is_visible command without any pre-configured scope." }, { "description": "Denies the maximize command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-maximize" + "const": "core:window:deny-maximize", + "markdownDescription": "Denies the maximize command without any pre-configured scope." }, { "description": "Denies the minimize command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-minimize" + "const": "core:window:deny-minimize", + "markdownDescription": "Denies the minimize command without any pre-configured scope." }, { "description": "Denies the monitor_from_point command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-monitor-from-point" + "const": "core:window:deny-monitor-from-point", + "markdownDescription": "Denies the monitor_from_point command without any pre-configured scope." }, { "description": "Denies the outer_position command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-outer-position" + "const": "core:window:deny-outer-position", + "markdownDescription": "Denies the outer_position command without any pre-configured scope." }, { "description": "Denies the outer_size command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-outer-size" + "const": "core:window:deny-outer-size", + "markdownDescription": "Denies the outer_size command without any pre-configured scope." }, { "description": "Denies the primary_monitor command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-primary-monitor" + "const": "core:window:deny-primary-monitor", + "markdownDescription": "Denies the primary_monitor command without any pre-configured scope." }, { "description": "Denies the request_user_attention command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-request-user-attention" + "const": "core:window:deny-request-user-attention", + "markdownDescription": "Denies the request_user_attention command without any pre-configured scope." }, { "description": "Denies the scale_factor command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-scale-factor" + "const": "core:window:deny-scale-factor", + "markdownDescription": "Denies the scale_factor command without any pre-configured scope." }, { "description": "Denies the set_always_on_bottom command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-set-always-on-bottom" + "const": "core:window:deny-set-always-on-bottom", + "markdownDescription": "Denies the set_always_on_bottom command without any pre-configured scope." }, { "description": "Denies the set_always_on_top command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-set-always-on-top" + "const": "core:window:deny-set-always-on-top", + "markdownDescription": "Denies the set_always_on_top command without any pre-configured scope." }, { "description": "Denies the set_background_color command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-set-background-color" + "const": "core:window:deny-set-background-color", + "markdownDescription": "Denies the set_background_color command without any pre-configured scope." }, { "description": "Denies the set_badge_count command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-set-badge-count" + "const": "core:window:deny-set-badge-count", + "markdownDescription": "Denies the set_badge_count command without any pre-configured scope." }, { "description": "Denies the set_badge_label command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-set-badge-label" + "const": "core:window:deny-set-badge-label", + "markdownDescription": "Denies the set_badge_label command without any pre-configured scope." }, { "description": "Denies the set_closable command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-set-closable" + "const": "core:window:deny-set-closable", + "markdownDescription": "Denies the set_closable command without any pre-configured scope." }, { "description": "Denies the set_content_protected command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-set-content-protected" + "const": "core:window:deny-set-content-protected", + "markdownDescription": "Denies the set_content_protected command without any pre-configured scope." }, { "description": "Denies the set_cursor_grab command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-set-cursor-grab" + "const": "core:window:deny-set-cursor-grab", + "markdownDescription": "Denies the set_cursor_grab command without any pre-configured scope." }, { "description": "Denies the set_cursor_icon command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-set-cursor-icon" + "const": "core:window:deny-set-cursor-icon", + "markdownDescription": "Denies the set_cursor_icon command without any pre-configured scope." }, { "description": "Denies the set_cursor_position command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-set-cursor-position" + "const": "core:window:deny-set-cursor-position", + "markdownDescription": "Denies the set_cursor_position command without any pre-configured scope." }, { "description": "Denies the set_cursor_visible command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-set-cursor-visible" + "const": "core:window:deny-set-cursor-visible", + "markdownDescription": "Denies the set_cursor_visible command without any pre-configured scope." }, { "description": "Denies the set_decorations command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-set-decorations" + "const": "core:window:deny-set-decorations", + "markdownDescription": "Denies the set_decorations command without any pre-configured scope." }, { "description": "Denies the set_effects command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-set-effects" + "const": "core:window:deny-set-effects", + "markdownDescription": "Denies the set_effects command without any pre-configured scope." }, { "description": "Denies the set_enabled command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-set-enabled" + "const": "core:window:deny-set-enabled", + "markdownDescription": "Denies the set_enabled command without any pre-configured scope." }, { "description": "Denies the set_focus command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-set-focus" + "const": "core:window:deny-set-focus", + "markdownDescription": "Denies the set_focus command without any pre-configured scope." }, { "description": "Denies the set_fullscreen command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-set-fullscreen" + "const": "core:window:deny-set-fullscreen", + "markdownDescription": "Denies the set_fullscreen command without any pre-configured scope." }, { "description": "Denies the set_icon command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-set-icon" + "const": "core:window:deny-set-icon", + "markdownDescription": "Denies the set_icon command without any pre-configured scope." }, { "description": "Denies the set_ignore_cursor_events command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-set-ignore-cursor-events" + "const": "core:window:deny-set-ignore-cursor-events", + "markdownDescription": "Denies the set_ignore_cursor_events command without any pre-configured scope." }, { "description": "Denies the set_max_size command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-set-max-size" + "const": "core:window:deny-set-max-size", + "markdownDescription": "Denies the set_max_size command without any pre-configured scope." }, { "description": "Denies the set_maximizable command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-set-maximizable" + "const": "core:window:deny-set-maximizable", + "markdownDescription": "Denies the set_maximizable command without any pre-configured scope." }, { "description": "Denies the set_min_size command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-set-min-size" + "const": "core:window:deny-set-min-size", + "markdownDescription": "Denies the set_min_size command without any pre-configured scope." }, { "description": "Denies the set_minimizable command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-set-minimizable" + "const": "core:window:deny-set-minimizable", + "markdownDescription": "Denies the set_minimizable command without any pre-configured scope." }, { "description": "Denies the set_overlay_icon command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-set-overlay-icon" + "const": "core:window:deny-set-overlay-icon", + "markdownDescription": "Denies the set_overlay_icon command without any pre-configured scope." }, { "description": "Denies the set_position command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-set-position" + "const": "core:window:deny-set-position", + "markdownDescription": "Denies the set_position command without any pre-configured scope." }, { "description": "Denies the set_progress_bar command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-set-progress-bar" + "const": "core:window:deny-set-progress-bar", + "markdownDescription": "Denies the set_progress_bar command without any pre-configured scope." }, { "description": "Denies the set_resizable command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-set-resizable" + "const": "core:window:deny-set-resizable", + "markdownDescription": "Denies the set_resizable command without any pre-configured scope." }, { "description": "Denies the set_shadow command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-set-shadow" + "const": "core:window:deny-set-shadow", + "markdownDescription": "Denies the set_shadow command without any pre-configured scope." }, { "description": "Denies the set_size command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-set-size" + "const": "core:window:deny-set-size", + "markdownDescription": "Denies the set_size command without any pre-configured scope." }, { "description": "Denies the set_size_constraints command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-set-size-constraints" + "const": "core:window:deny-set-size-constraints", + "markdownDescription": "Denies the set_size_constraints command without any pre-configured scope." }, { "description": "Denies the set_skip_taskbar command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-set-skip-taskbar" + "const": "core:window:deny-set-skip-taskbar", + "markdownDescription": "Denies the set_skip_taskbar command without any pre-configured scope." }, { "description": "Denies the set_theme command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-set-theme" + "const": "core:window:deny-set-theme", + "markdownDescription": "Denies the set_theme command without any pre-configured scope." }, { "description": "Denies the set_title command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-set-title" + "const": "core:window:deny-set-title", + "markdownDescription": "Denies the set_title command without any pre-configured scope." }, { "description": "Denies the set_title_bar_style command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-set-title-bar-style" + "const": "core:window:deny-set-title-bar-style", + "markdownDescription": "Denies the set_title_bar_style command without any pre-configured scope." }, { "description": "Denies the set_visible_on_all_workspaces command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-set-visible-on-all-workspaces" + "const": "core:window:deny-set-visible-on-all-workspaces", + "markdownDescription": "Denies the set_visible_on_all_workspaces command without any pre-configured scope." }, { "description": "Denies the show command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-show" + "const": "core:window:deny-show", + "markdownDescription": "Denies the show command without any pre-configured scope." }, { "description": "Denies the start_dragging command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-start-dragging" + "const": "core:window:deny-start-dragging", + "markdownDescription": "Denies the start_dragging command without any pre-configured scope." }, { "description": "Denies the start_resize_dragging command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-start-resize-dragging" + "const": "core:window:deny-start-resize-dragging", + "markdownDescription": "Denies the start_resize_dragging command without any pre-configured scope." }, { "description": "Denies the theme command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-theme" + "const": "core:window:deny-theme", + "markdownDescription": "Denies the theme command without any pre-configured scope." }, { "description": "Denies the title command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-title" + "const": "core:window:deny-title", + "markdownDescription": "Denies the title command without any pre-configured scope." }, { "description": "Denies the toggle_maximize command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-toggle-maximize" + "const": "core:window:deny-toggle-maximize", + "markdownDescription": "Denies the toggle_maximize command without any pre-configured scope." }, { "description": "Denies the unmaximize command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-unmaximize" + "const": "core:window:deny-unmaximize", + "markdownDescription": "Denies the unmaximize command without any pre-configured scope." }, { "description": "Denies the unminimize command without any pre-configured scope.", "type": "string", - "const": "core:window:deny-unminimize" + "const": "core:window:deny-unminimize", + "markdownDescription": "Denies the unminimize command without any pre-configured scope." }, { - "description": "This permission set configures which\nprocess feeatures are by default exposed.\n\n#### Granted Permissions\n\nThis enables to quit via `allow-exit` and restart via `allow-restart`\nthe application.\n", + "description": "This permission set configures which\nprocess features are by default exposed.\n\n#### Granted Permissions\n\nThis enables to quit via `allow-exit` and restart via `allow-restart`\nthe application.\n\n#### This default permission set includes:\n\n- `allow-exit`\n- `allow-restart`", "type": "string", - "const": "process:default" + "const": "process:default", + "markdownDescription": "This permission set configures which\nprocess features are by default exposed.\n\n#### Granted Permissions\n\nThis enables to quit via `allow-exit` and restart via `allow-restart`\nthe application.\n\n#### This default permission set includes:\n\n- `allow-exit`\n- `allow-restart`" }, { "description": "Enables the exit command without any pre-configured scope.", "type": "string", - "const": "process:allow-exit" + "const": "process:allow-exit", + "markdownDescription": "Enables the exit command without any pre-configured scope." }, { "description": "Enables the restart command without any pre-configured scope.", "type": "string", - "const": "process:allow-restart" + "const": "process:allow-restart", + "markdownDescription": "Enables the restart command without any pre-configured scope." }, { "description": "Denies the exit command without any pre-configured scope.", "type": "string", - "const": "process:deny-exit" + "const": "process:deny-exit", + "markdownDescription": "Denies the exit command without any pre-configured scope." }, { "description": "Denies the restart command without any pre-configured scope.", "type": "string", - "const": "process:deny-restart" + "const": "process:deny-restart", + "markdownDescription": "Denies the restart command without any pre-configured scope." }, { - "description": "This permission set configures which\nshell functionality is exposed by default.\n\n#### Granted Permissions\n\nIt allows to use the `open` functionality without any specific\nscope pre-configured. It will allow opening `http(s)://`,\n`tel:` and `mailto:` links.\n", + "description": "This permission set configures which\nshell functionality is exposed by default.\n\n#### Granted Permissions\n\nIt allows to use the `open` functionality with a reasonable\nscope pre-configured. It will allow opening `http(s)://`,\n`tel:` and `mailto:` links.\n\n#### This default permission set includes:\n\n- `allow-open`", "type": "string", - "const": "shell:default" + "const": "shell:default", + "markdownDescription": "This permission set configures which\nshell functionality is exposed by default.\n\n#### Granted Permissions\n\nIt allows to use the `open` functionality with a reasonable\nscope pre-configured. It will allow opening `http(s)://`,\n`tel:` and `mailto:` links.\n\n#### This default permission set includes:\n\n- `allow-open`" }, { "description": "Enables the execute command without any pre-configured scope.", "type": "string", - "const": "shell:allow-execute" + "const": "shell:allow-execute", + "markdownDescription": "Enables the execute command without any pre-configured scope." }, { "description": "Enables the kill command without any pre-configured scope.", "type": "string", - "const": "shell:allow-kill" + "const": "shell:allow-kill", + "markdownDescription": "Enables the kill command without any pre-configured scope." }, { "description": "Enables the open command without any pre-configured scope.", "type": "string", - "const": "shell:allow-open" + "const": "shell:allow-open", + "markdownDescription": "Enables the open command without any pre-configured scope." }, { "description": "Enables the spawn command without any pre-configured scope.", "type": "string", - "const": "shell:allow-spawn" + "const": "shell:allow-spawn", + "markdownDescription": "Enables the spawn command without any pre-configured scope." }, { "description": "Enables the stdin_write command without any pre-configured scope.", "type": "string", - "const": "shell:allow-stdin-write" + "const": "shell:allow-stdin-write", + "markdownDescription": "Enables the stdin_write command without any pre-configured scope." }, { "description": "Denies the execute command without any pre-configured scope.", "type": "string", - "const": "shell:deny-execute" + "const": "shell:deny-execute", + "markdownDescription": "Denies the execute command without any pre-configured scope." }, { "description": "Denies the kill command without any pre-configured scope.", "type": "string", - "const": "shell:deny-kill" + "const": "shell:deny-kill", + "markdownDescription": "Denies the kill command without any pre-configured scope." }, { "description": "Denies the open command without any pre-configured scope.", "type": "string", - "const": "shell:deny-open" + "const": "shell:deny-open", + "markdownDescription": "Denies the open command without any pre-configured scope." }, { "description": "Denies the spawn command without any pre-configured scope.", "type": "string", - "const": "shell:deny-spawn" + "const": "shell:deny-spawn", + "markdownDescription": "Denies the spawn command without any pre-configured scope." }, { "description": "Denies the stdin_write command without any pre-configured scope.", "type": "string", - "const": "shell:deny-stdin-write" + "const": "shell:deny-stdin-write", + "markdownDescription": "Denies the stdin_write command without any pre-configured scope." }, { - "description": "This permission set configures which kind of\nupdater functions are exposed to the frontend.\n\n#### Granted Permissions\n\nThe full workflow from checking for updates to installing them\nis enabled.\n\n", + "description": "This permission set configures which kind of\nupdater functions are exposed to the frontend.\n\n#### Granted Permissions\n\nThe full workflow from checking for updates to installing them\nis enabled.\n\n\n#### This default permission set includes:\n\n- `allow-check`\n- `allow-download`\n- `allow-install`\n- `allow-download-and-install`", "type": "string", - "const": "updater:default" + "const": "updater:default", + "markdownDescription": "This permission set configures which kind of\nupdater functions are exposed to the frontend.\n\n#### Granted Permissions\n\nThe full workflow from checking for updates to installing them\nis enabled.\n\n\n#### This default permission set includes:\n\n- `allow-check`\n- `allow-download`\n- `allow-install`\n- `allow-download-and-install`" }, { "description": "Enables the check command without any pre-configured scope.", "type": "string", - "const": "updater:allow-check" + "const": "updater:allow-check", + "markdownDescription": "Enables the check command without any pre-configured scope." }, { "description": "Enables the download command without any pre-configured scope.", "type": "string", - "const": "updater:allow-download" + "const": "updater:allow-download", + "markdownDescription": "Enables the download command without any pre-configured scope." }, { "description": "Enables the download_and_install command without any pre-configured scope.", "type": "string", - "const": "updater:allow-download-and-install" + "const": "updater:allow-download-and-install", + "markdownDescription": "Enables the download_and_install command without any pre-configured scope." }, { "description": "Enables the install command without any pre-configured scope.", "type": "string", - "const": "updater:allow-install" + "const": "updater:allow-install", + "markdownDescription": "Enables the install command without any pre-configured scope." }, { "description": "Denies the check command without any pre-configured scope.", "type": "string", - "const": "updater:deny-check" + "const": "updater:deny-check", + "markdownDescription": "Denies the check command without any pre-configured scope." }, { "description": "Denies the download command without any pre-configured scope.", "type": "string", - "const": "updater:deny-download" + "const": "updater:deny-download", + "markdownDescription": "Denies the download command without any pre-configured scope." }, { "description": "Denies the download_and_install command without any pre-configured scope.", "type": "string", - "const": "updater:deny-download-and-install" + "const": "updater:deny-download-and-install", + "markdownDescription": "Denies the download_and_install command without any pre-configured scope." }, { "description": "Denies the install command without any pre-configured scope.", "type": "string", - "const": "updater:deny-install" + "const": "updater:deny-install", + "markdownDescription": "Denies the install command without any pre-configured scope." } ] }, diff --git a/nym-wallet/src-tauri/src/config/mod.rs b/nym-wallet/src-tauri/src/config/mod.rs index 87014b1b50..d708540d67 100644 --- a/nym-wallet/src-tauri/src/config/mod.rs +++ b/nym-wallet/src-tauri/src/config/mod.rs @@ -11,7 +11,6 @@ use nym_validator_client::nyxd::AccountId as CosmosAccountId; use serde::de::DeserializeOwned; use serde::{Deserialize, Serialize}; use strum::IntoEnumIterator; -use tauri::Manager; use url::Url; use nym_config::defaults::{DenomDetailsOwned, NymNetworkDetails, ValidatorDetails}; @@ -105,35 +104,42 @@ impl NetworkConfig { } impl Config { - fn root_directory(app_handle: &tauri::AppHandle) -> PathBuf { - app_handle - .path() - .local_data_dir() - .expect("Failed to get config directory") + fn root_directory() -> PathBuf { + // tauri v1 (via `tauri::api::path::config_dir()`) was internally calling `dirs_next::config_dir()` + // which ultimately was getting resolved to + // - **Linux:** Resolves to `$XDG_CONFIG_HOME` or `$HOME/.config`. + // - **macOS:** Resolves to `$HOME/Library/Application Support`. + // - **Windows:** Resolves to `{FOLDERID_RoamingAppData}`. + // + // tauri v2 calls `dirs::config_dir().ok_or(Error::UnknownPath)` which ultimately does the same thing, + // however, it changed its API so that it's called on a `PathResolver`. + // but, to instantiate one here would be a hassle as we don't need those specific functionalities, + // so let's just recreate tauri's behaviour + dirs::config_dir().expect("Failed to get config directory") } - fn config_directory(app_handle: &tauri::AppHandle) -> PathBuf { - Self::root_directory(app_handle).join(CONFIG_DIR_NAME) + fn config_directory() -> PathBuf { + Self::root_directory().join(CONFIG_DIR_NAME) } - fn config_file_path(app_handle: &tauri::AppHandle, network: Option) -> PathBuf { + fn config_file_path(network: Option) -> PathBuf { if let Some(network) = network { let network_filename = format!("{}.toml", network.as_key()); - Self::config_directory(app_handle).join(network_filename) + Self::config_directory().join(network_filename) } else { - Self::config_directory(app_handle).join(CONFIG_FILENAME) + Self::config_directory().join(CONFIG_FILENAME) } } - pub fn save_to_files(&self, app_handle: &tauri::AppHandle) -> io::Result<()> { + pub fn save_to_files(&self) -> io::Result<()> { log::trace!("Config::save_to_file"); // Make sure the whole directory structure actually exists - fs::create_dir_all(Self::config_directory(app_handle))?; + fs::create_dir_all(Self::config_directory())?; // Global config if let Some(global) = &self.global { - let location = Self::config_file_path(app_handle, None); + let location = Self::config_file_path(None); match toml::to_string_pretty(&global) .map_err(|toml_err| io::Error::new(io::ErrorKind::Other, toml_err)) @@ -154,7 +160,7 @@ impl Config { } }; - let location = Self::config_file_path(app_handle, Some(network)); + let location = Self::config_file_path(Some(network)); match toml::to_string_pretty(config) .map_err(|toml_err| io::Error::new(io::ErrorKind::Other, toml_err)) .map(|toml| fs::write(location.clone(), toml)) @@ -166,10 +172,10 @@ impl Config { Ok(()) } - pub fn load_from_files(app_handle: &tauri::AppHandle) -> Self { + pub fn load_from_files() -> Self { // Global let global = { - let file = Self::config_file_path(app_handle, None); + let file = Self::config_file_path(None); match load_from_file::(file.clone()) { Ok(global) => { log::debug!("Loaded from file {:#?}", file); @@ -185,7 +191,7 @@ impl Config { // One file per network let mut networks = HashMap::new(); for network in WalletNetwork::iter() { - let file = Self::config_file_path(app_handle, Some(network)); + let file = Self::config_file_path(Some(network)); match load_from_file::(file.clone()) { Ok(config) => { log::trace!("Loaded from file {:#?}", file); @@ -265,8 +271,6 @@ impl Config { } } - //////// - pub fn select_nyxd_url(&mut self, nyxd_url: Url, network: WalletNetwork) { if let Some(net) = self.networks.get_mut(&network.as_key()) { net.selected_nyxd_url = Some(nyxd_url); @@ -537,145 +541,145 @@ impl From for NetworkDetails { } } -// #[cfg(test)] -// mod tests { -// use super::*; +#[cfg(test)] +mod tests { + use super::*; -// fn test_config() -> Config { -// let netconfig = NetworkConfig { -// selected_nyxd_url: None, -// selected_api_url: Some("https://my_api_url.com".parse().unwrap()), + fn test_config() -> Config { + let netconfig = NetworkConfig { + selected_nyxd_url: None, + selected_api_url: Some("https://my_api_url.com".parse().unwrap()), -// nyxd_urls: Some(vec![ -// ValidatorConfigEntry { -// nyxd_url: "https://foo".parse().unwrap(), -// nyxd_name: Some("FooName".to_string()), -// api_url: None, -// }, -// ValidatorConfigEntry { -// nyxd_url: "https://bar".parse().unwrap(), -// nyxd_name: None, -// api_url: Some("https://bar/api".parse().unwrap()), -// }, -// ValidatorConfigEntry { -// nyxd_url: "https://baz".parse().unwrap(), -// nyxd_name: None, -// api_url: Some("https://baz/api".parse().unwrap()), -// }, -// ]), -// ..NetworkConfig::default() -// }; + nyxd_urls: Some(vec![ + ValidatorConfigEntry { + nyxd_url: "https://foo".parse().unwrap(), + nyxd_name: Some("FooName".to_string()), + api_url: None, + }, + ValidatorConfigEntry { + nyxd_url: "https://bar".parse().unwrap(), + nyxd_name: None, + api_url: Some("https://bar/api".parse().unwrap()), + }, + ValidatorConfigEntry { + nyxd_url: "https://baz".parse().unwrap(), + nyxd_name: None, + api_url: Some("https://baz/api".parse().unwrap()), + }, + ]), + ..NetworkConfig::default() + }; -// Config { -// base: Base::default(), -// global: Some(GlobalConfig::default()), -// networks: [(WalletNetwork::MAINNET.as_key(), netconfig)] -// .into_iter() -// .collect(), -// } -// } + Config { + base: Base::default(), + global: Some(GlobalConfig::default()), + networks: [(WalletNetwork::MAINNET.as_key(), netconfig)] + .into_iter() + .collect(), + } + } -// #[test] -// fn serialize_to_toml() { -// let config = test_config(); -// let netconfig = &config.networks[&WalletNetwork::MAINNET.as_key()]; -// assert_eq!( -// toml::to_string_pretty(netconfig).unwrap(), -// r#"version = 1 -// selected_api_url = 'https://my_api_url.com/' + #[test] + fn serialize_to_toml() { + let config = test_config(); + let netconfig = &config.networks[&WalletNetwork::MAINNET.as_key()]; + assert_eq!( + toml::to_string_pretty(netconfig).unwrap(), + r#"version = 1 +selected_api_url = 'https://my_api_url.com/' -// [[nyxd_urls]] -// nyxd_url = 'https://foo/' -// nyxd_name = 'FooName' +[[nyxd_urls]] +nyxd_url = 'https://foo/' +nyxd_name = 'FooName' -// [[nyxd_urls]] -// nyxd_url = 'https://bar/' -// api_url = 'https://bar/api' +[[nyxd_urls]] +nyxd_url = 'https://bar/' +api_url = 'https://bar/api' -// [[nyxd_urls]] -// nyxd_url = 'https://baz/' -// api_url = 'https://baz/api' -// "# -// ); -// } +[[nyxd_urls]] +nyxd_url = 'https://baz/' +api_url = 'https://baz/api' +"# + ); + } -// #[test] -// fn serialize_to_json() { -// let config = test_config(); -// let netconfig = &config.networks[&WalletNetwork::MAINNET.as_key()]; -// println!("{}", serde_json::to_string_pretty(netconfig).unwrap()); -// assert_eq!( -// serde_json::to_string_pretty(netconfig).unwrap(), -// r#"{ -// "version": 1, -// "selected_nyxd_url": null, -// "default_nyxd_url": null, -// "selected_api_url": "https://my_api_url.com/", -// "nyxd_urls": [ -// { -// "nyxd_url": "https://foo/", -// "nyxd_name": "FooName", -// "api_url": null -// }, -// { -// "nyxd_url": "https://bar/", -// "nyxd_name": null, -// "api_url": "https://bar/api" -// }, -// { -// "nyxd_url": "https://baz/", -// "nyxd_name": null, -// "api_url": "https://baz/api" -// } -// ] -// }"# -// ); -// } + #[test] + fn serialize_to_json() { + let config = test_config(); + let netconfig = &config.networks[&WalletNetwork::MAINNET.as_key()]; + println!("{}", serde_json::to_string_pretty(netconfig).unwrap()); + assert_eq!( + serde_json::to_string_pretty(netconfig).unwrap(), + r#"{ + "version": 1, + "selected_nyxd_url": null, + "default_nyxd_url": null, + "selected_api_url": "https://my_api_url.com/", + "nyxd_urls": [ + { + "nyxd_url": "https://foo/", + "nyxd_name": "FooName", + "api_url": null + }, + { + "nyxd_url": "https://bar/", + "nyxd_name": null, + "api_url": "https://bar/api" + }, + { + "nyxd_url": "https://baz/", + "nyxd_name": null, + "api_url": "https://baz/api" + } + ] +}"# + ); + } -// #[test] -// fn serialize_and_deserialize_to_toml() { -// let config = test_config(); -// let netconfig = &config.networks[&WalletNetwork::MAINNET.as_key()]; -// let config_str = toml::to_string_pretty(netconfig).unwrap(); -// let config_from_toml: NetworkConfig = toml::from_str(&config_str).unwrap(); -// assert_eq!(netconfig, &config_from_toml); -// } + #[test] + fn serialize_and_deserialize_to_toml() { + let config = test_config(); + let netconfig = &config.networks[&WalletNetwork::MAINNET.as_key()]; + let config_str = toml::to_string_pretty(netconfig).unwrap(); + let config_from_toml: NetworkConfig = toml::from_str(&config_str).unwrap(); + assert_eq!(netconfig, &config_from_toml); + } -// #[test] -// fn get_urls_parsed_from_config() { -// let config = test_config(); + #[test] + fn get_urls_parsed_from_config() { + let config = test_config(); -// let nyxd_url = config -// .get_configured_validators(WalletNetwork::MAINNET) -// .next() -// .map(|v| v.nyxd_url) -// .unwrap(); -// assert_eq!(nyxd_url.as_ref(), "https://foo/"); + let nyxd_url = config + .get_configured_validators(WalletNetwork::MAINNET) + .next() + .map(|v| v.nyxd_url) + .unwrap(); + assert_eq!(nyxd_url.as_ref(), "https://foo/"); -// // The first entry is missing an API URL -// let api_url = config -// .get_configured_validators(WalletNetwork::MAINNET) -// .next() -// .and_then(|v| v.api_url); -// assert_eq!(api_url, None); -// } + // The first entry is missing an API URL + let api_url = config + .get_configured_validators(WalletNetwork::MAINNET) + .next() + .and_then(|v| v.api_url); + assert_eq!(api_url, None); + } -// #[test] -// fn get_urls_from_defaults() { -// let config = Config::default(); + #[test] + fn get_urls_from_defaults() { + let config = Config::default(); -// let nyxd_url = config -// .get_base_validators(WalletNetwork::MAINNET) -// .next() -// .map(|v| v.nyxd_url) -// .unwrap(); -// assert_eq!(nyxd_url.as_ref(), "https://rpc.nymtech.net/"); + let nyxd_url = config + .get_base_validators(WalletNetwork::MAINNET) + .next() + .map(|v| v.nyxd_url) + .unwrap(); + assert_eq!(nyxd_url.as_ref(), "https://rpc.nymtech.net/"); -// let api_url = config -// .get_base_validators(WalletNetwork::MAINNET) -// .next() -// .and_then(|v| v.api_url) -// .unwrap(); -// assert_eq!(api_url.as_ref(), "https://validator.nymtech.net/api/",); -// } -// } + let api_url = config + .get_base_validators(WalletNetwork::MAINNET) + .next() + .and_then(|v| v.api_url) + .unwrap(); + assert_eq!(api_url.as_ref(), "https://validator.nymtech.net/api/",); + } +} diff --git a/nym-wallet/src-tauri/src/operations/app/window.rs b/nym-wallet/src-tauri/src/operations/app/window.rs index d15fde65cf..212af30fb1 100644 --- a/nym-wallet/src-tauri/src/operations/app/window.rs +++ b/nym-wallet/src-tauri/src/operations/app/window.rs @@ -28,7 +28,7 @@ async fn create_window( log::info!("Creating {} window...", new_window_label); match tauri::WebviewWindowBuilder::new( &app_handle, - "main", + new_window_label, tauri::WebviewUrl::App(new_window_url.into()), ) .title("Nym Wallet") @@ -36,14 +36,14 @@ async fn create_window( { Ok(window) => { if let Err(err) = window.set_focus() { - log::error!("Unable to focus log window: {err}"); + log::error!("Unable to focus window: {err}"); } if let Err(err) = window.maximize() { log::error!("Could not maximize window: {err}"); } } Err(err) => { - log::error!("Unable to create log window: {err}"); + log::error!("Unable to create window: {err}"); return Err(BackendError::NewWindowError); } } diff --git a/nym-wallet/src-tauri/src/wallet_storage/mod.rs b/nym-wallet/src-tauri/src/wallet_storage/mod.rs index c620351417..973172f4fc 100644 --- a/nym-wallet/src-tauri/src/wallet_storage/mod.rs +++ b/nym-wallet/src-tauri/src/wallet_storage/mod.rs @@ -1,6 +1,8 @@ // Copyright 2022 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 +#![allow(clippy::doc_overindented_list_items)] + /// The wallet storage file contains a set of logins, each with an associated login ID and an /// encrypted field. Once decrypted, each login contains either an account, or a set of accounts. /// One difference is that the latter have an associated account ID. @@ -19,7 +21,6 @@ use nym_validator_client::nyxd::bip32::DerivationPath; use std::ffi::OsString; use std::fs::{self, create_dir_all, OpenOptions}; use std::path::{Path, PathBuf}; -use tauri::Manager; use time::OffsetDateTime; #[cfg(test)] @@ -38,18 +39,24 @@ pub(crate) const DEFAULT_LOGIN_ID: &str = "default"; /// this name. pub(crate) const DEFAULT_FIRST_ACCOUNT_NAME: &str = "Account 1"; -fn get_storage_directory(app_handle: &tauri::AppHandle) -> Result { - app_handle - .path() - .local_data_dir() +fn get_storage_directory() -> Result { + // tauri v1 (via `tauri::api::path::local_data_dir()`) was internally calling `dirs_next::data_local_dir()` + // which ultimately was getting resolved to + // - **Linux:** Resolves to `$XDG_DATA_HOME` or `$HOME/.local/share`. + // - **macOS:** Resolves to `$HOME/Library/Application Support`. + // - **Windows:** Resolves to `{FOLDERID_LocalAppData}`. + // + // tauri v2 calls `dirs::data_local_dir().ok_or(Error::UnknownPath)` which ultimately does the same thing, + // however, it changed its API so that it's called on a `PathResolver`. + // but, to instantiate one here would be a hassle as we don't need those specific functionalities, + // so let's just recreate tauri's behaviour + dirs::data_local_dir() .map(|dir| dir.join(STORAGE_DIR_NAME)) .ok_or(BackendError::UnknownStorageDirectory) } -pub(crate) fn wallet_login_filepath( - app_handle: &tauri::AppHandle, -) -> Result { - get_storage_directory(app_handle).map(|dir| dir.join(WALLET_INFO_FILENAME)) +pub(crate) fn wallet_login_filepath() -> Result { + get_storage_directory().map(|dir| dir.join(WALLET_INFO_FILENAME)) } fn write_to_file(filepath: &Path, wallet: &StoredWallet) -> Result<(), BackendError> { @@ -64,10 +71,8 @@ fn write_to_file(filepath: &Path, wallet: &StoredWallet) -> Result<(), BackendEr /// Load stored wallet file #[allow(unused)] -pub(crate) fn load_existing_wallet( - app_handle: &tauri::AppHandle, -) -> Result { - let store_dir = get_storage_directory(app_handle)?; +pub(crate) fn load_existing_wallet() -> Result { + let store_dir = get_storage_directory()?; let filepath = store_dir.join(WALLET_INFO_FILENAME); load_existing_wallet_at_file(&filepath) } @@ -84,11 +89,10 @@ fn load_existing_wallet_at_file(filepath: &Path) -> Result Result { - let store_dir = get_storage_directory(app_handle)?; + let store_dir = get_storage_directory()?; let filepath = store_dir.join(WALLET_INFO_FILENAME); load_existing_login_at_file(&filepath, id, password) } @@ -476,1819 +480,1819 @@ fn rename_account_in_login_at_file( write_to_file(filepath, &stored_wallet) } -// #[cfg(test)] -// mod tests { -// use crate::wallet_storage::account_data::WalletAccount; - -// use super::*; -// use nym_config::defaults::COSMOS_DERIVATION_PATH; -// use std::str::FromStr; -// use tempfile::tempdir; - -// #[test] -// fn trying_to_load_nonexistant_wallet_fails() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let id1 = LoginId::new("first".to_string()); -// let password = UserPassword::new("password".to_string()); - -// assert!(matches!( -// load_existing_wallet_at_file(&wallet_file), -// Err(BackendError::WalletFileNotFound), -// )); -// assert!(matches!( -// load_existing_login_at_file(&wallet_file, &id1, &password), -// Err(BackendError::WalletFileNotFound), -// )); -// remove_login_at_file(&wallet_file, &id1).unwrap_err(); -// } - -// #[test] -// fn store_single_login() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let id1 = LoginId::new("first".to_string()); - -// store_login_at_file(&wallet_file, account1, hd_path, id1.clone(), &password).unwrap(); - -// let stored_wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); -// assert_eq!(stored_wallet.len(), 1); - -// let login = stored_wallet.get_encrypted_login_by_index(0).unwrap(); -// assert_eq!(login.id, id1); - -// // some actual ciphertext was saved -// assert!(!login.account.ciphertext().is_empty()); -// } - -// #[test] -// fn store_single_login_with_multi() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let id1 = LoginId::new("first".to_string()); - -// store_login_with_multiple_accounts_at_file( -// &wallet_file, -// account1, -// cosmos_hd_path, -// id1.clone(), -// &password, -// ) -// .unwrap(); - -// let stored_wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); -// assert_eq!(stored_wallet.len(), 1); - -// let login = stored_wallet.get_encrypted_login_by_index(0).unwrap(); -// assert_eq!(login.id, id1); - -// // some actual ciphertext was saved -// assert!(!login.account.ciphertext().is_empty()); -// } - -// #[test] -// fn store_single_login_with_multi_then_update_pwd_and_load() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let new_password = UserPassword::new("new_password".to_string()); -// let id1 = LoginId::new("first".to_string()); - -// store_login_with_multiple_accounts_at_file( -// &wallet_file, -// account1, -// cosmos_hd_path, -// id1.clone(), -// &password, -// ) -// .unwrap(); - -// update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); - -// let stored_wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); -// assert_eq!(stored_wallet.len(), 1); - -// let login = stored_wallet.get_encrypted_login_by_index(0).unwrap(); -// assert_eq!(login.id, id1); - -// // some actual ciphertext was saved -// assert!(!login.account.ciphertext().is_empty()); -// } - -// #[test] -// fn store_twice_for_the_same_id_fails() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let id1 = LoginId::new("first".to_string()); - -// // Store the first login -// store_login_at_file( -// &wallet_file, -// account1.clone(), -// hd_path.clone(), -// id1.clone(), -// &password, -// ) -// .unwrap(); - -// // and storing the same id again fails -// assert!(matches!( -// store_login_at_file(&wallet_file, account1, hd_path, id1, &password,), -// Err(BackendError::WalletLoginIdAlreadyExists), -// )); -// } - -// #[test] -// fn store_twice_for_the_same_id_fails_with_multiple() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let id1 = LoginId::new("first".to_string()); - -// // Store the first login -// store_login_with_multiple_accounts_at_file( -// &wallet_file, -// account1.clone(), -// hd_path.clone(), -// id1.clone(), -// &password, -// ) -// .unwrap(); - -// // and storing the same id again fails -// assert!(matches!( -// store_login_with_multiple_accounts_at_file( -// &wallet_file, -// account1, -// hd_path, -// id1, -// &password, -// ), -// Err(BackendError::WalletLoginIdAlreadyExists), -// )); -// } - -// #[test] -// fn load_with_wrong_password_fails() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let bad_password = UserPassword::new("bad-password".to_string()); -// let id1 = LoginId::new("first".to_string()); - -// store_login_at_file(&wallet_file, account1, hd_path, id1.clone(), &password).unwrap(); - -// // Trying to load it with wrong password now fails -// assert!(matches!( -// load_existing_login_at_file(&wallet_file, &id1, &bad_password), -// Err(BackendError::StoreCipherError { .. }), -// )); -// } - -// #[test] -// fn load_with_wrong_password_fails_with_multi() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let bad_password = UserPassword::new("bad-password".to_string()); -// let id1 = LoginId::new("first".to_string()); - -// store_login_with_multiple_accounts_at_file( -// &wallet_file, -// account1, -// hd_path, -// id1.clone(), -// &password, -// ) -// .unwrap(); - -// // Trying to load it with wrong password now fails -// assert!(matches!( -// load_existing_login_at_file(&wallet_file, &id1, &bad_password), -// Err(BackendError::StoreCipherError { .. }), -// )); -// } - -// #[test] -// fn load_with_wrong_id_fails() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let id1 = LoginId::new("first".to_string()); -// let id2 = LoginId::new("second".to_string()); - -// store_login_at_file(&wallet_file, account1, hd_path, id1, &password).unwrap(); - -// // Trying to load with the wrong id -// assert!(matches!( -// load_existing_login_at_file(&wallet_file, &id2, &password), -// Err(BackendError::WalletNoSuchLoginId), -// )); -// } - -// #[test] -// fn load_with_wrong_id_fails_with_multi() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let id1 = LoginId::new("first".to_string()); -// let id2 = LoginId::new("second".to_string()); - -// store_login_with_multiple_accounts_at_file(&wallet_file, account1, hd_path, id1, &password) -// .unwrap(); - -// // Trying to load with the wrong id -// assert!(matches!( -// load_existing_login_at_file(&wallet_file, &id2, &password), -// Err(BackendError::WalletNoSuchLoginId), -// )); -// } - -// #[test] -// fn store_and_load_a_single_login() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let id1 = LoginId::new("first".to_string()); - -// store_login_at_file( -// &wallet_file, -// account1.clone(), -// hd_path.clone(), -// id1.clone(), -// &password, -// ) -// .unwrap(); - -// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); -// let acc = loaded_login.as_mnemonic_account().unwrap(); -// assert_eq!(&account1, acc.mnemonic()); -// assert_eq!(&hd_path, acc.hd_path()); -// } - -// #[test] -// fn store_a_single_login_then_update_pwd_and_load() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let new_password = UserPassword::new("new_password".to_string()); -// let id1 = LoginId::new("first".to_string()); - -// store_login_at_file( -// &wallet_file, -// account1.clone(), -// hd_path.clone(), -// id1.clone(), -// &password, -// ) -// .unwrap(); - -// update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); - -// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &new_password).unwrap(); -// let acc = loaded_login.as_mnemonic_account().unwrap(); -// assert_eq!(&account1, acc.mnemonic()); -// assert_eq!(&hd_path, acc.hd_path()); -// } - -// #[test] -// fn store_a_single_login_then_update_pwd_with_identical_pwd_is_noop_but_okay() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let id1 = LoginId::new("first".to_string()); - -// store_login_at_file( -// &wallet_file, -// account1.clone(), -// hd_path.clone(), -// id1.clone(), -// &password, -// ) -// .unwrap(); - -// update_encrypted_logins_at_file(&wallet_file, &password, &password).unwrap(); - -// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); -// let acc = loaded_login.as_mnemonic_account().unwrap(); -// assert_eq!(&account1, acc.mnemonic()); -// assert_eq!(&hd_path, acc.hd_path()); -// } - -// #[test] -// fn store_a_single_login_then_update_pwd_with_wrong_current_pwd_fails() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let new_password = UserPassword::new("new_password".to_string()); -// let wrong_password = UserPassword::new("wrong_password".to_string()); -// let id1 = LoginId::new("first".to_string()); - -// store_login_at_file(&wallet_file, account1, hd_path, id1, &password).unwrap(); - -// let err = update_encrypted_logins_at_file(&wallet_file, &wrong_password, &new_password) -// .unwrap_err(); -// assert!(matches!(err, BackendError::StoreCipherError { .. })); -// } - -// #[test] -// fn store_a_single_login_then_update_pwd_and_load_with_wrong_pwd_fails() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let new_password = UserPassword::new("new_password".to_string()); -// let id1 = LoginId::new("first".to_string()); - -// store_login_at_file(&wallet_file, account1, hd_path, id1.clone(), &password).unwrap(); - -// update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); - -// let err = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap_err(); -// assert!(matches!(err, BackendError::StoreCipherError { .. })); -// } - -// #[test] -// fn store_and_load_a_single_login_with_multi() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let acc1 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let id1 = LoginId::new("first".to_string()); - -// store_login_with_multiple_accounts_at_file( -// &wallet_file, -// acc1.clone(), -// hd_path.clone(), -// id1.clone(), -// &password, -// ) -// .unwrap(); - -// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); -// let accounts = loaded_login.as_multiple_accounts().unwrap(); -// assert_eq!(accounts.len(), 1); -// let account = accounts -// .get_account(&DEFAULT_FIRST_ACCOUNT_NAME.into()) -// .unwrap(); -// assert_eq!(account.id().as_ref(), DEFAULT_FIRST_ACCOUNT_NAME); -// assert_eq!(account.mnemonic(), &acc1); -// assert_eq!(account.hd_path(), &hd_path); -// } - -// #[test] -// fn store_a_single_login_with_multi_then_update_pwd_and_load() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let acc1 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let new_password = UserPassword::new("new_password".to_string()); -// let id1 = LoginId::new("first".to_string()); - -// store_login_with_multiple_accounts_at_file( -// &wallet_file, -// acc1.clone(), -// hd_path.clone(), -// id1.clone(), -// &password, -// ) -// .unwrap(); - -// update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); - -// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &new_password).unwrap(); -// let accounts = loaded_login.as_multiple_accounts().unwrap(); -// assert_eq!(accounts.len(), 1); -// let account = accounts -// .get_account(&DEFAULT_FIRST_ACCOUNT_NAME.into()) -// .unwrap(); -// assert_eq!(account.id().as_ref(), DEFAULT_FIRST_ACCOUNT_NAME); -// assert_eq!(account.mnemonic(), &acc1); -// assert_eq!(account.hd_path(), &hd_path); -// } - -// #[test] -// fn store_a_single_login_with_multi_then_update_pwd_with_wrong_current_pwd_fails() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let acc1 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let new_password = UserPassword::new("new_password".to_string()); -// let wrong_password = UserPassword::new("wrong_password".to_string()); -// let id1 = LoginId::new("first".to_string()); - -// store_login_with_multiple_accounts_at_file(&wallet_file, acc1, hd_path, id1, &password) -// .unwrap(); - -// let err = update_encrypted_logins_at_file(&wallet_file, &wrong_password, &new_password) -// .unwrap_err(); -// assert!(matches!(err, BackendError::StoreCipherError { .. })); -// } - -// #[test] -// fn store_a_single_login_with_multi_then_update_pwd_and_load_with_wrong_pwd_fails() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let acc1 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let new_password = UserPassword::new("new_password".to_string()); -// let id1 = LoginId::new("first".to_string()); - -// store_login_with_multiple_accounts_at_file( -// &wallet_file, -// acc1, -// hd_path, -// id1.clone(), -// &password, -// ) -// .unwrap(); - -// update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); - -// let err = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap_err(); -// assert!(matches!(err, BackendError::StoreCipherError { .. })); -// } - -// #[test] -// fn store_a_second_login_with_a_different_password_fails() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let account2 = Mnemonic::generate(24).unwrap(); -// let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let bad_password = UserPassword::new("bad-password".to_string()); -// let id1 = LoginId::new("first".to_string()); -// let id2 = LoginId::new("second".to_string()); - -// store_login_at_file( -// &wallet_file, -// account1, -// cosmos_hd_path.clone(), -// id1, -// &password, -// ) -// .unwrap(); - -// // Can't store a second login if you use different password -// assert!(matches!( -// store_login_at_file(&wallet_file, account2, cosmos_hd_path, id2, &bad_password), -// Err(BackendError::WalletDifferentPasswordDetected), -// )); -// } - -// #[test] -// fn store_a_second_login_with_a_different_password_fails_with_multi() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let account2 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let bad_password = UserPassword::new("bad-password".to_string()); -// let id1 = LoginId::new("first".to_string()); -// let id2 = LoginId::new("second".to_string()); - -// store_login_with_multiple_accounts_at_file( -// &wallet_file, -// account1, -// hd_path.clone(), -// id1, -// &password, -// ) -// .unwrap(); - -// // Can't store a second login if you use different password -// assert!(matches!( -// store_login_with_multiple_accounts_at_file( -// &wallet_file, -// account2, -// hd_path, -// id2, -// &bad_password -// ), -// Err(BackendError::WalletDifferentPasswordDetected), -// )); -// } - -// #[test] -// fn store_two_mnemonic_accounts_gives_different_salts_and_iv() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let account2 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let different_hd_path: DerivationPath = "m".parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let id1 = LoginId::new("first".to_string()); -// let id2 = LoginId::new("second".to_string()); - -// // Store the first account -// store_login_at_file(&wallet_file, account1, hd_path, id1, &password).unwrap(); - -// let stored_wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); -// let encrypted_blob = &stored_wallet -// .get_encrypted_login_by_index(0) -// .unwrap() -// .account; - -// // keep track of salt and iv for future assertion -// let original_iv = encrypted_blob.iv().to_vec(); -// let original_salt = encrypted_blob.salt().to_vec(); - -// // Add an extra account -// store_login_at_file(&wallet_file, account2, different_hd_path, id2, &password).unwrap(); - -// let loaded_accounts = load_existing_wallet_at_file(&wallet_file).unwrap(); -// assert_eq!(loaded_accounts.len(), 2); -// let encrypted_blob = &loaded_accounts -// .get_encrypted_login_by_index(1) -// .unwrap() -// .account; - -// // fresh IV and salt are used -// assert_ne!(original_iv, encrypted_blob.iv()); -// assert_ne!(original_salt, encrypted_blob.salt()); -// } - -// #[test] -// fn store_two_mnemonic_accounts_using_two_logins() { -// let store_dir = tempdir().unwrap(); -// let wallet = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let account2 = Mnemonic::generate(24).unwrap(); -// let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let different_hd_path: DerivationPath = "m".parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let id1 = LoginId::new("first".to_string()); -// let id2 = LoginId::new("second".to_string()); - -// // Store the first account -// store_login_at_file( -// &wallet, -// account1.clone(), -// cosmos_hd_path.clone(), -// id1.clone(), -// &password, -// ) -// .unwrap(); - -// let login = load_existing_login_at_file(&wallet, &id1, &password).unwrap(); -// let acc = login.as_mnemonic_account().unwrap(); -// assert_eq!(&account1, acc.mnemonic()); -// assert_eq!(&cosmos_hd_path, acc.hd_path()); - -// // Add an extra account -// store_login_at_file( -// &wallet, -// account2.clone(), -// different_hd_path.clone(), -// id2.clone(), -// &password, -// ) -// .unwrap(); - -// // first account should be unchanged -// let loaded_login = load_existing_login_at_file(&wallet, &id1, &password).unwrap(); -// let acc1 = loaded_login.as_mnemonic_account().unwrap(); -// assert_eq!(&account1, acc1.mnemonic()); -// assert_eq!(&cosmos_hd_path, acc1.hd_path()); - -// let loaded_login = load_existing_login_at_file(&wallet, &id2, &password).unwrap(); -// let acc2 = loaded_login.as_mnemonic_account().unwrap(); -// assert_eq!(&account2, acc2.mnemonic()); -// assert_eq!(&different_hd_path, acc2.hd_path()); -// } - -// #[test] -// fn store_two_mnemonic_accounts_using_two_logins_then_update_pwd_and_load() { -// let store_dir = tempdir().unwrap(); -// let wallet = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let account2 = Mnemonic::generate(24).unwrap(); -// let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let different_hd_path: DerivationPath = "m".parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let new_password = UserPassword::new("new_password".to_string()); -// let id1 = LoginId::new("first".to_string()); -// let id2 = LoginId::new("second".to_string()); - -// // Store the first login with an account -// store_login_at_file( -// &wallet, -// account1.clone(), -// cosmos_hd_path.clone(), -// id1.clone(), -// &password, -// ) -// .unwrap(); - -// let login = load_existing_login_at_file(&wallet, &id1, &password).unwrap(); -// let acc = login.as_mnemonic_account().unwrap(); -// assert_eq!(&account1, acc.mnemonic()); -// assert_eq!(&cosmos_hd_path, acc.hd_path()); - -// // Store a second login, also with an account -// store_login_at_file( -// &wallet, -// account2.clone(), -// different_hd_path.clone(), -// id2.clone(), -// &password, -// ) -// .unwrap(); - -// update_encrypted_logins_at_file(&wallet, &password, &new_password).unwrap(); - -// // first account should be unchanged -// let loaded_login = load_existing_login_at_file(&wallet, &id1, &new_password).unwrap(); -// let acc1 = loaded_login.as_mnemonic_account().unwrap(); -// assert_eq!(&account1, acc1.mnemonic()); -// assert_eq!(&cosmos_hd_path, acc1.hd_path()); - -// let loaded_login = load_existing_login_at_file(&wallet, &id2, &new_password).unwrap(); -// let acc2 = loaded_login.as_mnemonic_account().unwrap(); -// assert_eq!(&account2, acc2.mnemonic()); -// assert_eq!(&different_hd_path, acc2.hd_path()); -// } - -// #[test] -// fn store_one_mnemonic_account_and_one_multi_account() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let account2 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let different_hd_path: DerivationPath = "m".parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let id1 = LoginId::new("first".to_string()); -// let id2 = LoginId::new("second".to_string()); - -// // Store the first account -// store_login_at_file( -// &wallet_file, -// account1.clone(), -// hd_path.clone(), -// id1.clone(), -// &password, -// ) -// .unwrap(); - -// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); -// let acc = loaded_login.as_mnemonic_account().unwrap(); -// assert_eq!(&account1, acc.mnemonic()); -// assert_eq!(&hd_path, acc.hd_path()); - -// // Add an extra account -// store_login_with_multiple_accounts_at_file( -// &wallet_file, -// account2.clone(), -// different_hd_path.clone(), -// id2.clone(), -// &password, -// ) -// .unwrap(); - -// // first account should be unchanged -// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); -// let acc1 = loaded_login.as_mnemonic_account().unwrap(); -// assert_eq!(&account1, acc1.mnemonic()); -// assert_eq!(&hd_path, acc1.hd_path()); - -// let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); -// let acc2 = loaded_login.as_multiple_accounts().unwrap(); -// assert_eq!(acc2.len(), 1); -// let account = acc2 -// .get_account(&DEFAULT_FIRST_ACCOUNT_NAME.into()) -// .unwrap(); -// assert_eq!(account.id().as_ref(), DEFAULT_FIRST_ACCOUNT_NAME); -// assert_eq!(account.mnemonic(), &account2); -// assert_eq!(account.hd_path(), &different_hd_path); -// } - -// #[test] -// fn remove_non_existent_id_fails() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let id1 = LoginId::new("first".to_string()); -// let id2 = LoginId::new("second".to_string()); - -// store_login_with_multiple_accounts_at_file(&wallet_file, account1, hd_path, id1, &password) -// .unwrap(); - -// // Fails to delete non-existent id in the wallet -// assert!(matches!( -// remove_login_at_file(&wallet_file, &id2), -// Err(BackendError::WalletNoSuchLoginId), -// )); -// } - -// #[test] -// fn store_and_remove_wallet_login_information() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let account2 = Mnemonic::generate(24).unwrap(); -// let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let different_hd_path: DerivationPath = "m".parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let id1 = LoginId::new("first".to_string()); -// let id2 = LoginId::new("second".to_string()); - -// // Store two accounts with two different passwords -// store_login_at_file( -// &wallet_file, -// account1.clone(), -// cosmos_hd_path.clone(), -// id1.clone(), -// &password, -// ) -// .unwrap(); -// store_login_at_file( -// &wallet_file, -// account2.clone(), -// different_hd_path.clone(), -// id2.clone(), -// &password, -// ) -// .unwrap(); - -// // Load and compare -// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); -// let acc1 = loaded_login.as_mnemonic_account().unwrap(); -// assert_eq!(&account1, acc1.mnemonic()); -// assert_eq!(&cosmos_hd_path, acc1.hd_path()); - -// let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); -// let acc2 = loaded_login.as_mnemonic_account().unwrap(); -// assert_eq!(&account2, acc2.mnemonic()); -// assert_eq!(&different_hd_path, acc2.hd_path()); - -// // Delete the second account -// remove_login_at_file(&wallet_file, &id2).unwrap(); - -// // The first account should be unchanged -// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); -// let acc1 = loaded_login.as_mnemonic_account().unwrap(); -// assert_eq!(&account1, acc1.mnemonic()); -// assert_eq!(&cosmos_hd_path, acc1.hd_path()); - -// // And we can't load the second one anymore -// assert!(matches!( -// load_existing_login_at_file(&wallet_file, &id2, &password), -// Err(BackendError::WalletNoSuchLoginId), -// )); - -// // Delete the first account -// assert!(wallet_file.exists()); -// remove_login_at_file(&wallet_file, &id1).unwrap(); - -// // The file should now be removed -// assert!(!wallet_file.exists()); - -// // And trying to load when the file is gone fails -// assert!(matches!( -// load_existing_login_at_file(&wallet_file, &id1, &password), -// Err(BackendError::WalletFileNotFound), -// )); -// } - -// #[test] -// fn append_account_converts_the_type() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let account2 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let id1 = LoginId::new("first".to_string()); -// let id2 = AccountId::new("second".to_string()); - -// store_login_at_file( -// &wallet_file, -// account1.clone(), -// hd_path.clone(), -// id1.clone(), -// &password, -// ) -// .unwrap(); - -// // Check that it's there as the correct non-multiple type -// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); -// let acc = loaded_login.as_mnemonic_account().unwrap(); -// assert_eq!(acc.mnemonic(), &account1); -// assert_eq!(acc.hd_path(), &hd_path); - -// append_account_to_login_at_file( -// &wallet_file, -// account2.clone(), -// hd_path.clone(), -// id1.clone(), -// id2.clone(), -// &password, -// ) -// .unwrap(); - -// // Check that it is now multiple mnemonic type -// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); -// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); -// let expected = vec![ -// WalletAccount::new(id1.into(), MnemonicAccount::new(account1, hd_path.clone())), -// WalletAccount::new(id2, MnemonicAccount::new(account2, hd_path)), -// ] -// .into(); -// assert_eq!(loaded_accounts, &expected); -// } - -// #[test] -// fn append_accounts_to_existing_login() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let account2 = Mnemonic::generate(24).unwrap(); -// let account3 = Mnemonic::generate(24).unwrap(); -// let account4 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let id1 = LoginId::new("first".to_string()); -// let id2 = LoginId::new("second".to_string()); -// let id3 = AccountId::new("third".to_string()); -// let id4 = AccountId::new("fourth".to_string()); - -// store_login_at_file( -// &wallet_file, -// account1.clone(), -// hd_path.clone(), -// id1.clone(), -// &password, -// ) -// .unwrap(); - -// store_login_at_file( -// &wallet_file, -// account2.clone(), -// hd_path.clone(), -// id2.clone(), -// &password, -// ) -// .unwrap(); - -// // Check that it's there as the correct non-multiple type -// let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); -// let acc2 = loaded_login.as_mnemonic_account().unwrap(); -// assert_eq!(acc2.mnemonic(), &account2); -// assert_eq!(acc2.hd_path(), &hd_path); - -// // Add a third and fourth mnenonic grouped together with the second one -// append_account_to_login_at_file( -// &wallet_file, -// account3.clone(), -// hd_path.clone(), -// id2.clone(), -// id3.clone(), -// &password, -// ) -// .unwrap(); -// append_account_to_login_at_file( -// &wallet_file, -// account4.clone(), -// hd_path.clone(), -// id2.clone(), -// id4.clone(), -// &password, -// ) -// .unwrap(); - -// // Check that we can load all four -// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); -// let acc1 = loaded_login.as_mnemonic_account().unwrap(); -// assert_eq!(acc1.mnemonic(), &account1); -// assert_eq!(acc1.hd_path(), &hd_path); - -// let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); -// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); -// let expected = vec![ -// WalletAccount::new(id2.into(), MnemonicAccount::new(account2, hd_path.clone())), -// WalletAccount::new(id3, MnemonicAccount::new(account3, hd_path.clone())), -// WalletAccount::new(id4, MnemonicAccount::new(account4, hd_path)), -// ] -// .into(); -// assert_eq!(loaded_accounts, &expected); -// } - -// #[test] -// fn append_accounts_to_existing_login_with_multi() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let account2 = Mnemonic::generate(24).unwrap(); -// let account3 = Mnemonic::generate(24).unwrap(); -// let account4 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let id1 = LoginId::new("first".to_string()); -// let id2 = LoginId::new("second".to_string()); -// let id3 = AccountId::new("third".to_string()); -// let id4 = AccountId::new("fourth".to_string()); - -// store_login_with_multiple_accounts_at_file( -// &wallet_file, -// account1.clone(), -// hd_path.clone(), -// id1.clone(), -// &password, -// ) -// .unwrap(); - -// store_login_with_multiple_accounts_at_file( -// &wallet_file, -// account2.clone(), -// hd_path.clone(), -// id2.clone(), -// &password, -// ) -// .unwrap(); - -// // Add a third and fourth mnenonic grouped together with the second one -// append_account_to_login_at_file( -// &wallet_file, -// account3.clone(), -// hd_path.clone(), -// id2.clone(), -// id3.clone(), -// &password, -// ) -// .unwrap(); -// append_account_to_login_at_file( -// &wallet_file, -// account4.clone(), -// hd_path.clone(), -// id2.clone(), -// id4.clone(), -// &password, -// ) -// .unwrap(); - -// // Check that we can load all four -// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); -// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); -// let expected = vec![WalletAccount::new( -// DEFAULT_FIRST_ACCOUNT_NAME.into(), -// MnemonicAccount::new(account1, hd_path.clone()), -// )] -// .into(); -// assert_eq!(loaded_accounts, &expected); - -// let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); -// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); -// let expected = vec![ -// WalletAccount::new( -// DEFAULT_FIRST_ACCOUNT_NAME.into(), -// MnemonicAccount::new(account2, hd_path.clone()), -// ), -// WalletAccount::new(id3, MnemonicAccount::new(account3, hd_path.clone())), -// WalletAccount::new(id4, MnemonicAccount::new(account4, hd_path)), -// ] -// .into(); -// assert_eq!(loaded_accounts, &expected); -// } - -// #[test] -// fn append_account_to_existing_login_with_multi_then_update_pwd_and_load() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let account2 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let new_password = UserPassword::new("new_password".to_string()); -// let login_id = LoginId::new("first".to_string()); -// let appended_account = AccountId::new("second".to_string()); - -// store_login_with_multiple_accounts_at_file( -// &wallet_file, -// account1.clone(), -// hd_path.clone(), -// login_id.clone(), -// &password, -// ) -// .unwrap(); - -// // Append a second mnenonic to the same login -// append_account_to_login_at_file( -// &wallet_file, -// account2.clone(), -// hd_path.clone(), -// login_id.clone(), -// appended_account.clone(), -// &password, -// ) -// .unwrap(); - -// // Update the password -// update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); - -// // Expect that we can load these 2 accounts with the new password -// let loaded_login = -// load_existing_login_at_file(&wallet_file, &login_id, &new_password).unwrap(); -// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); -// let expected = vec![ -// WalletAccount::new( -// DEFAULT_FIRST_ACCOUNT_NAME.into(), -// MnemonicAccount::new(account1, hd_path.clone()), -// ), -// WalletAccount::new(appended_account, MnemonicAccount::new(account2, hd_path)), -// ] -// .into(); -// assert_eq!(loaded_accounts, &expected); - -// // Expect that trying to load these 2 accounts with the old password fails -// let err = load_existing_login_at_file(&wallet_file, &login_id, &password).unwrap_err(); -// assert!(matches!(err, BackendError::StoreCipherError { .. })); -// } - -// #[test] -// fn append_the_same_mnemonic_twice_fails() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let id1 = LoginId::new("first".to_string()); -// let id2 = AccountId::new("second".to_string()); - -// store_login_with_multiple_accounts_at_file( -// &wallet_file, -// account1.clone(), -// hd_path.clone(), -// id1.clone(), -// &password, -// ) -// .unwrap(); - -// assert!(matches!( -// append_account_to_login_at_file(&wallet_file, account1, hd_path, id1, id2, &password), -// Err(BackendError::WalletMnemonicAlreadyExistsInWalletLogin), -// )) -// } - -// #[test] -// fn append_the_same_account_name_twice_fails() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let mnemonic1 = Mnemonic::generate(24).unwrap(); -// let mnemonic2 = Mnemonic::generate(24).unwrap(); -// let mnemonic3 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// // The top-level login id. NOTE: the first account id is always set to default. -// let login_id = LoginId::new("my_login_id".to_string()); - -// // Store the first account under login_id. The first account id is always set to default -// // name. -// store_login_with_multiple_accounts_at_file( -// &wallet_file, -// mnemonic1, -// hd_path.clone(), -// login_id.clone(), -// &password, -// ) -// .unwrap(); - -// // Append another account (account2) to the same login (login_id) -// let account2 = AccountId::new("account_2".to_string()); - -// append_account_to_login_at_file( -// &wallet_file, -// mnemonic2, -// hd_path.clone(), -// login_id.clone(), -// account2.clone(), -// &password, -// ) -// .unwrap(); - -// // Appending the third account, with same account id will fail -// assert!(matches!( -// append_account_to_login_at_file( -// &wallet_file, -// mnemonic3, -// hd_path, -// login_id, -// account2, -// &password, -// ), -// Err(BackendError::WalletAccountIdAlreadyExistsInWalletLogin), -// )) -// } - -// #[test] -// fn delete_the_same_account_twice_for_a_login_fails() { -// let store_dir = tempdir().unwrap(); -// let wallet = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let account2 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let id1 = LoginId::new("first".to_string()); -// let id2 = AccountId::new("second".to_string()); - -// store_login_at_file(&wallet, account1, hd_path.clone(), id1.clone(), &password).unwrap(); - -// append_account_to_login_at_file( -// &wallet, -// account2, -// hd_path, -// id1.clone(), -// id2.clone(), -// &password, -// ) -// .unwrap(); - -// remove_account_from_login_at_file(&wallet, &id1, &id2, &password).unwrap(); - -// assert!(matches!( -// remove_account_from_login_at_file(&wallet, &id1, &id2, &password), -// Err(BackendError::WalletNoSuchAccountIdInWalletLogin), -// )); -// } - -// #[test] -// fn delete_the_same_account_twice_for_a_login_fails_with_multi() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let account2 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let id1 = LoginId::new("first".to_string()); -// let id2 = AccountId::new("second".to_string()); - -// store_login_with_multiple_accounts_at_file( -// &wallet_file, -// account1, -// hd_path.clone(), -// id1.clone(), -// &password, -// ) -// .unwrap(); - -// append_account_to_login_at_file( -// &wallet_file, -// account2, -// hd_path, -// id1.clone(), -// id2.clone(), -// &password, -// ) -// .unwrap(); - -// remove_account_from_login_at_file(&wallet_file, &id1, &id2, &password).unwrap(); - -// assert!(matches!( -// remove_account_from_login_at_file(&wallet_file, &id1, &id2, &password), -// Err(BackendError::WalletNoSuchAccountIdInWalletLogin), -// )); -// } - -// #[test] -// fn delete_appended_account_doesnt_affect_others() { -// let store_dir = tempdir().unwrap(); -// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let account2 = Mnemonic::generate(24).unwrap(); -// let account3 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let id1 = LoginId::new("first".to_string()); -// let id2 = LoginId::new("second".to_string()); -// let id3 = AccountId::new("third".to_string()); - -// store_login_at_file( -// &wallet_file, -// account1, -// hd_path.clone(), -// id1.clone(), -// &password, -// ) -// .unwrap(); - -// store_login_at_file( -// &wallet_file, -// account2.clone(), -// hd_path.clone(), -// id2.clone(), -// &password, -// ) -// .unwrap(); - -// append_account_to_login_at_file( -// &wallet_file, -// account3.clone(), -// hd_path.clone(), -// id2.clone(), -// id3.clone(), -// &password, -// ) -// .unwrap(); - -// remove_login_at_file(&wallet_file, &id1).unwrap(); - -// // The second login one is still there -// let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); -// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); -// let expected = vec![ -// WalletAccount::new(id2.into(), MnemonicAccount::new(account2, hd_path.clone())), -// WalletAccount::new(id3, MnemonicAccount::new(account3, hd_path)), -// ] -// .into(); -// assert_eq!(loaded_accounts, &expected); -// } - -// #[test] -// fn remove_all_accounts_for_a_login_removes_the_file_when_empty() { -// let store_dir = tempdir().unwrap(); -// let wallet = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let account2 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let id1 = LoginId::new("first".to_string()); -// let id2 = AccountId::new("second".to_string()); - -// store_login_with_multiple_accounts_at_file( -// &wallet, -// account1, -// hd_path.clone(), -// id1.clone(), -// &password, -// ) -// .unwrap(); - -// append_account_to_login_at_file( -// &wallet, -// account2, -// hd_path, -// id1.clone(), -// id2.clone(), -// &password, -// ) -// .unwrap(); - -// remove_account_from_login_at_file( -// &wallet, -// &id1, -// &DEFAULT_FIRST_ACCOUNT_NAME.into(), -// &password, -// ) -// .unwrap(); -// remove_account_from_login_at_file(&wallet, &id1, &id2, &password).unwrap(); - -// // The file should now be removed -// assert!(!wallet.exists()); - -// // And trying to load when the file is gone fails -// assert!(matches!( -// load_existing_login_at_file(&wallet, &id1, &password), -// Err(BackendError::WalletFileNotFound), -// )); -// } - -// #[test] -// fn remove_all_accounts_for_a_login_removes_that_login() { -// let store_dir = tempdir().unwrap(); -// let wallet = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let account2 = Mnemonic::generate(24).unwrap(); -// let account3 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let id1 = LoginId::new("first".to_string()); -// let id2 = AccountId::new("second".to_string()); -// let id3 = LoginId::new("third".to_string()); - -// store_login_with_multiple_accounts_at_file( -// &wallet, -// account1, -// hd_path.clone(), -// id1.clone(), -// &password, -// ) -// .unwrap(); - -// append_account_to_login_at_file( -// &wallet, -// account2, -// hd_path.clone(), -// id1.clone(), -// id2.clone(), -// &password, -// ) -// .unwrap(); - -// store_login_with_multiple_accounts_at_file( -// &wallet, -// account3.clone(), -// hd_path.clone(), -// id3.clone(), -// &password, -// ) -// .unwrap(); - -// remove_account_from_login_at_file( -// &wallet, -// &id1, -// &DEFAULT_FIRST_ACCOUNT_NAME.into(), -// &password, -// ) -// .unwrap(); -// remove_account_from_login_at_file(&wallet, &id1, &id2, &password).unwrap(); - -// // And trying to load when the file is gone fails -// assert!(matches!( -// load_existing_login_at_file(&wallet, &id1, &password), -// Err(BackendError::WalletNoSuchLoginId), -// )); - -// // The other login is still there -// let loaded_login = load_existing_login_at_file(&wallet, &id3, &password).unwrap(); -// let acc3 = loaded_login.as_multiple_accounts().unwrap(); -// let expected = vec![WalletAccount::new( -// DEFAULT_FIRST_ACCOUNT_NAME.into(), -// MnemonicAccount::new(account3, hd_path), -// )] -// .into(); -// assert_eq!(acc3, &expected); -// } - -// #[test] -// fn append_accounts_and_remove_appended_accounts() { -// let store_dir = tempdir().unwrap(); -// let wallet = store_dir.path().join(WALLET_INFO_FILENAME); -// let acc1 = Mnemonic::generate(24).unwrap(); -// let acc2 = Mnemonic::generate(24).unwrap(); -// let acc3 = Mnemonic::generate(24).unwrap(); -// let acc4 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let id1 = LoginId::new("first".to_string()); -// let id2 = LoginId::new("second".to_string()); -// let id3 = AccountId::new("third".to_string()); -// let id4 = AccountId::new("fourth".to_string()); - -// store_login_at_file( -// &wallet, -// acc1.clone(), -// hd_path.clone(), -// id1.clone(), -// &password, -// ) -// .unwrap(); - -// store_login_at_file( -// &wallet, -// acc2.clone(), -// hd_path.clone(), -// id2.clone(), -// &password, -// ) -// .unwrap(); - -// // Add a third and fourth mnenonic grouped together with the second one -// append_account_to_login_at_file( -// &wallet, -// acc3, -// hd_path.clone(), -// id2.clone(), -// id3.clone(), -// &password, -// ) -// .unwrap(); -// append_account_to_login_at_file( -// &wallet, -// acc4.clone(), -// hd_path.clone(), -// id2.clone(), -// id4.clone(), -// &password, -// ) -// .unwrap(); - -// // Delete the third mnemonic, from the second login entry -// remove_account_from_login_at_file(&wallet, &id2, &id3, &password).unwrap(); - -// // Check that we can still load the other accounts -// let loaded_login = load_existing_login_at_file(&wallet, &id2, &password).unwrap(); -// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); -// let expected = vec![ -// WalletAccount::new( -// id2.clone().into(), -// MnemonicAccount::new(acc2, hd_path.clone()), -// ), -// WalletAccount::new(id4.clone(), MnemonicAccount::new(acc4, hd_path.clone())), -// ] -// .into(); -// assert_eq!(loaded_accounts, &expected); - -// // Delete the second and fourth mnemonic from the second login entry removes the login entry -// remove_account_from_login_at_file(&wallet, &id2, &id2.clone().into(), &password).unwrap(); -// remove_account_from_login_at_file(&wallet, &id2, &id4, &password).unwrap(); -// assert!(matches!( -// load_existing_login_at_file(&wallet, &id2, &password), -// Err(BackendError::WalletNoSuchLoginId), -// )); - -// // The first login is still available -// let loaded_login = load_existing_login_at_file(&wallet, &id1, &password).unwrap(); -// let account = loaded_login.as_mnemonic_account().unwrap(); -// assert_eq!(account.mnemonic(), &acc1); -// assert_eq!(account.hd_path(), &hd_path); -// } - -// #[test] -// fn rename_first_account_in_login() { -// let store_dir = tempdir().unwrap(); -// let wallet = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let login_id = LoginId::new("first".to_string()); - -// store_login_with_multiple_accounts_at_file( -// &wallet, -// account1.clone(), -// hd_path.clone(), -// login_id.clone(), -// &password, -// ) -// .unwrap(); - -// let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); -// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); -// let expected = vec![WalletAccount::new( -// DEFAULT_FIRST_ACCOUNT_NAME.into(), -// MnemonicAccount::new(account1.clone(), hd_path.clone()), -// )] -// .into(); -// assert_eq!(loaded_accounts, &expected); - -// let renamed_account = AccountId::new("new_first".to_string()); - -// rename_account_in_login_at_file( -// &wallet, -// &login_id, -// &DEFAULT_FIRST_ACCOUNT_NAME.into(), -// &renamed_account, -// &password, -// ) -// .unwrap(); - -// let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); -// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); -// let expected = vec![WalletAccount::new( -// renamed_account, -// MnemonicAccount::new(account1, hd_path), -// )] -// .into(); -// assert_eq!(loaded_accounts, &expected); -// } - -// #[test] -// fn rename_one_account_in_login_with_two_accounts() { -// let store_dir = tempdir().unwrap(); -// let wallet = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let account2 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let login_id = LoginId::new("first".to_string()); -// let account_id2 = AccountId::new("second".to_string()); -// let renamed_account_id2 = AccountId::new("new_second".to_string()); - -// store_login_with_multiple_accounts_at_file( -// &wallet, -// account1.clone(), -// hd_path.clone(), -// login_id.clone(), -// &password, -// ) -// .unwrap(); - -// append_account_to_login_at_file( -// &wallet, -// account2.clone(), -// hd_path.clone(), -// login_id.clone(), -// account_id2.clone(), -// &password, -// ) -// .unwrap(); - -// // Load and confirm -// let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); -// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); -// let expected = vec![ -// WalletAccount::new( -// DEFAULT_FIRST_ACCOUNT_NAME.into(), -// MnemonicAccount::new(account1.clone(), hd_path.clone()), -// ), -// WalletAccount::new( -// account_id2.clone(), -// MnemonicAccount::new(account2.clone(), hd_path.clone()), -// ), -// ] -// .into(); -// assert_eq!(loaded_accounts, &expected); - -// // Rename the second account to a new name -// rename_account_in_login_at_file( -// &wallet, -// &login_id, -// &account_id2, -// &renamed_account_id2, -// &password, -// ) -// .unwrap(); - -// // Load and confirm -// let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); -// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); -// let expected = vec![ -// WalletAccount::new( -// DEFAULT_FIRST_ACCOUNT_NAME.into(), -// MnemonicAccount::new(account1, hd_path.clone()), -// ), -// WalletAccount::new(renamed_account_id2, MnemonicAccount::new(account2, hd_path)), -// ] -// .into(); -// assert_eq!(loaded_accounts, &expected); -// } - -// #[test] -// fn rename_account_into_existing_account_id_fails() { -// let store_dir = tempdir().unwrap(); -// let wallet = store_dir.path().join(WALLET_INFO_FILENAME); -// let account1 = Mnemonic::generate(24).unwrap(); -// let account2 = Mnemonic::generate(24).unwrap(); -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let login_id = LoginId::new("first".to_string()); -// let account_id2 = AccountId::new("second".to_string()); -// let renamed_account_id2 = DEFAULT_FIRST_ACCOUNT_NAME.into(); - -// store_login_with_multiple_accounts_at_file( -// &wallet, -// account1.clone(), -// hd_path.clone(), -// login_id.clone(), -// &password, -// ) -// .unwrap(); - -// append_account_to_login_at_file( -// &wallet, -// account2.clone(), -// hd_path.clone(), -// login_id.clone(), -// account_id2.clone(), -// &password, -// ) -// .unwrap(); - -// // Load and confirm -// let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); -// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); -// let expected = vec![ -// WalletAccount::new( -// DEFAULT_FIRST_ACCOUNT_NAME.into(), -// MnemonicAccount::new(account1.clone(), hd_path.clone()), -// ), -// WalletAccount::new( -// account_id2.clone(), -// MnemonicAccount::new(account2.clone(), hd_path.clone()), -// ), -// ] -// .into(); -// assert_eq!(loaded_accounts, &expected); - -// // Rename the second account to the name of the first one fails -// assert!(matches!( -// rename_account_in_login_at_file( -// &wallet, -// &login_id, -// &account_id2, -// &renamed_account_id2, -// &password, -// ), -// Err(BackendError::WalletAccountIdAlreadyExistsInWalletLogin) -// )); - -// // Load and confirm nothing was changed -// let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); -// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); -// let expected = vec![ -// WalletAccount::new( -// DEFAULT_FIRST_ACCOUNT_NAME.into(), -// MnemonicAccount::new(account1, hd_path.clone()), -// ), -// WalletAccount::new(account_id2, MnemonicAccount::new(account2, hd_path)), -// ] -// .into(); -// assert_eq!(loaded_accounts, &expected); -// } - -// // Test to that decrypts a stored file from the repo, to make sure we are able to decrypt stored -// // wallets created with older versions. -// #[test] -// fn decrypt_stored_wallet() { -// const SAVED_WALLET: &str = "src/wallet_storage/test-data/saved-wallet.json"; -// let wallet_file = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(SAVED_WALLET); - -// let wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); - -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password".to_string()); -// let bad_password = UserPassword::new("bad-password".to_string()); -// let id1 = LoginId::new("first".to_string()); -// let id2 = LoginId::new("second".to_string()); - -// assert!(!wallet.password_can_decrypt_all(&bad_password)); -// assert!(wallet.password_can_decrypt_all(&password)); - -// let acc1 = wallet.decrypt_login(&id1, &password).unwrap(); -// let acc2 = wallet.decrypt_login(&id2, &password).unwrap(); - -// assert!(matches!(acc1, StoredLogin::Mnemonic(_))); -// assert!(matches!(acc2, StoredLogin::Mnemonic(_))); - -// let expected_acc1 = bip39::Mnemonic::from_str("country mean universe text phone begin deputy reject result good cram illness common cluster proud swamp digital patrol spread bar face december base kick").unwrap(); -// let expected_acc2 = bip39::Mnemonic::from_str("home mansion start quiz dress decide hint second dragon sunny juice always steak real minimum art rival skin draw total pulp foot goddess agent").unwrap(); - -// assert_eq!( -// acc1.as_mnemonic_account().unwrap().mnemonic(), -// &expected_acc1 -// ); -// assert_eq!(acc1.as_mnemonic_account().unwrap().hd_path(), &hd_path,); - -// assert_eq!( -// acc2.as_mnemonic_account().unwrap().mnemonic(), -// &expected_acc2 -// ); -// assert_eq!(acc2.as_mnemonic_account().unwrap().hd_path(), &hd_path,); -// } - -// #[test] -// fn decrypt_stored_wallet_1_0_4() { -// const SAVED_WALLET: &str = "src/wallet_storage/test-data/saved-wallet-1.0.4.json"; -// let wallet_file = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(SAVED_WALLET); - -// let wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); - -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password11!".to_string()); -// let bad_password = UserPassword::new("bad-password".to_string()); -// let login_id = LoginId::new("default".to_string()); - -// assert!(!wallet.password_can_decrypt_all(&bad_password)); -// assert!(wallet.password_can_decrypt_all(&password)); - -// let acc1 = wallet.decrypt_login(&login_id, &password).unwrap(); - -// assert!(matches!(acc1, StoredLogin::Mnemonic(_))); - -// let expected_acc1 = bip39::Mnemonic::from_str("arrow capable abstract industry elevator nominee december piece hotel feed lounge web faint sword veteran bundle hour page actual laptop horror gold test warrior").unwrap(); - -// assert_eq!( -// acc1.as_mnemonic_account().unwrap().mnemonic(), -// &expected_acc1 -// ); -// assert_eq!(acc1.as_mnemonic_account().unwrap().hd_path(), &hd_path,); -// } - -// #[test] -// fn decrypt_stored_wallet_1_0_5_with_multiple_accounts() { -// const SAVED_WALLET: &str = "src/wallet_storage/test-data/saved-wallet-1.0.5.json"; -// let wallet_file = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(SAVED_WALLET); - -// let wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); - -// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); -// let password = UserPassword::new("password11!".to_string()); -// let bad_password = UserPassword::new("bad-password".to_string()); -// let login_id = LoginId::new("default".to_string()); - -// assert!(!wallet.password_can_decrypt_all(&bad_password)); -// assert!(wallet.password_can_decrypt_all(&password)); - -// let login = wallet.decrypt_login(&login_id, &password).unwrap(); - -// assert!(matches!(login, StoredLogin::Multiple(_))); - -// let login = login.as_multiple_accounts().unwrap(); -// assert_eq!(login.len(), 4); - -// let expected_mn1 = bip39::Mnemonic::from_str("arrow capable abstract industry elevator nominee december piece hotel feed lounge web faint sword veteran bundle hour page actual laptop horror gold test warrior").unwrap(); -// let expected_mn2 = bip39::Mnemonic::from_str("border hurt skull lunar goddess second danger game dismiss exhaust oven thumb dog drama onion false orchard spice tent next predict invite cherry green").unwrap(); -// let expected_mn3 = bip39::Mnemonic::from_str("gentle crowd rule snap girl urge flat jump winner cluster night sand museum stock grunt quick tree acquire traffic major awake tag rack peasant").unwrap(); -// let expected_mn4 = bip39::Mnemonic::from_str("debris blue skin annual inhale text border rigid spatial lesson coconut yard horn crystal control survey version vote hawk neck frame arrive oblige width").unwrap(); - -// let expected = vec![ -// WalletAccount::new( -// "default".into(), -// MnemonicAccount::new(expected_mn1, hd_path.clone()), -// ), -// WalletAccount::new( -// "account2".into(), -// MnemonicAccount::new(expected_mn2, hd_path.clone()), -// ), -// WalletAccount::new( -// "foobar".into(), -// MnemonicAccount::new(expected_mn3, hd_path.clone()), -// ), -// WalletAccount::new("42".into(), MnemonicAccount::new(expected_mn4, hd_path)), -// ] -// .into(); - -// assert_eq!(login, &expected); -// } - -// #[test] -// fn append_filename() { -// let wallet_file = PathBuf::from("/tmp/saved-wallet.json"); -// let timestamp = OsString::from("42"); -// #[cfg(target_family = "unix")] -// assert_eq!( -// append_timestamp_to_filename(wallet_file.clone(), timestamp.clone(), None) -// .unwrap() -// .into_os_string() -// .into_string() -// .unwrap(), -// "/tmp/saved-wallet-42.json".to_string(), -// ); -// #[cfg(not(target_family = "unix"))] -// assert_eq!( -// append_timestamp_to_filename(wallet_file.clone(), timestamp.clone(), None) -// .unwrap() -// .into_os_string() -// .into_string() -// .unwrap(), -// r"/tmp\saved-wallet-42.json".to_string(), -// ); - -// #[cfg(target_family = "unix")] -// assert_eq!( -// append_timestamp_to_filename(wallet_file, timestamp, Some(3)) -// .unwrap() -// .into_os_string() -// .into_string() -// .unwrap(), -// "/tmp/saved-wallet-42-3.json".to_string(), -// ); -// #[cfg(not(target_family = "unix"))] -// assert_eq!( -// append_timestamp_to_filename(wallet_file, timestamp, Some(3)) -// .unwrap() -// .into_os_string() -// .into_string() -// .unwrap(), -// r"/tmp\saved-wallet-42-3.json".to_string(), -// ); -// } -// } +#[cfg(test)] +mod tests { + use crate::wallet_storage::account_data::WalletAccount; + + use super::*; + use nym_config::defaults::COSMOS_DERIVATION_PATH; + use std::str::FromStr; + use tempfile::tempdir; + + #[test] + fn trying_to_load_nonexistant_wallet_fails() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let id1 = LoginId::new("first".to_string()); + let password = UserPassword::new("password".to_string()); + + assert!(matches!( + load_existing_wallet_at_file(&wallet_file), + Err(BackendError::WalletFileNotFound), + )); + assert!(matches!( + load_existing_login_at_file(&wallet_file, &id1, &password), + Err(BackendError::WalletFileNotFound), + )); + remove_login_at_file(&wallet_file, &id1).unwrap_err(); + } + + #[test] + fn store_single_login() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let id1 = LoginId::new("first".to_string()); + + store_login_at_file(&wallet_file, account1, hd_path, id1.clone(), &password).unwrap(); + + let stored_wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); + assert_eq!(stored_wallet.len(), 1); + + let login = stored_wallet.get_encrypted_login_by_index(0).unwrap(); + assert_eq!(login.id, id1); + + // some actual ciphertext was saved + assert!(!login.account.ciphertext().is_empty()); + } + + #[test] + fn store_single_login_with_multi() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let id1 = LoginId::new("first".to_string()); + + store_login_with_multiple_accounts_at_file( + &wallet_file, + account1, + cosmos_hd_path, + id1.clone(), + &password, + ) + .unwrap(); + + let stored_wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); + assert_eq!(stored_wallet.len(), 1); + + let login = stored_wallet.get_encrypted_login_by_index(0).unwrap(); + assert_eq!(login.id, id1); + + // some actual ciphertext was saved + assert!(!login.account.ciphertext().is_empty()); + } + + #[test] + fn store_single_login_with_multi_then_update_pwd_and_load() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let new_password = UserPassword::new("new_password".to_string()); + let id1 = LoginId::new("first".to_string()); + + store_login_with_multiple_accounts_at_file( + &wallet_file, + account1, + cosmos_hd_path, + id1.clone(), + &password, + ) + .unwrap(); + + update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); + + let stored_wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); + assert_eq!(stored_wallet.len(), 1); + + let login = stored_wallet.get_encrypted_login_by_index(0).unwrap(); + assert_eq!(login.id, id1); + + // some actual ciphertext was saved + assert!(!login.account.ciphertext().is_empty()); + } + + #[test] + fn store_twice_for_the_same_id_fails() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let id1 = LoginId::new("first".to_string()); + + // Store the first login + store_login_at_file( + &wallet_file, + account1.clone(), + hd_path.clone(), + id1.clone(), + &password, + ) + .unwrap(); + + // and storing the same id again fails + assert!(matches!( + store_login_at_file(&wallet_file, account1, hd_path, id1, &password,), + Err(BackendError::WalletLoginIdAlreadyExists), + )); + } + + #[test] + fn store_twice_for_the_same_id_fails_with_multiple() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let id1 = LoginId::new("first".to_string()); + + // Store the first login + store_login_with_multiple_accounts_at_file( + &wallet_file, + account1.clone(), + hd_path.clone(), + id1.clone(), + &password, + ) + .unwrap(); + + // and storing the same id again fails + assert!(matches!( + store_login_with_multiple_accounts_at_file( + &wallet_file, + account1, + hd_path, + id1, + &password, + ), + Err(BackendError::WalletLoginIdAlreadyExists), + )); + } + + #[test] + fn load_with_wrong_password_fails() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let bad_password = UserPassword::new("bad-password".to_string()); + let id1 = LoginId::new("first".to_string()); + + store_login_at_file(&wallet_file, account1, hd_path, id1.clone(), &password).unwrap(); + + // Trying to load it with wrong password now fails + assert!(matches!( + load_existing_login_at_file(&wallet_file, &id1, &bad_password), + Err(BackendError::StoreCipherError { .. }), + )); + } + + #[test] + fn load_with_wrong_password_fails_with_multi() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let bad_password = UserPassword::new("bad-password".to_string()); + let id1 = LoginId::new("first".to_string()); + + store_login_with_multiple_accounts_at_file( + &wallet_file, + account1, + hd_path, + id1.clone(), + &password, + ) + .unwrap(); + + // Trying to load it with wrong password now fails + assert!(matches!( + load_existing_login_at_file(&wallet_file, &id1, &bad_password), + Err(BackendError::StoreCipherError { .. }), + )); + } + + #[test] + fn load_with_wrong_id_fails() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let id1 = LoginId::new("first".to_string()); + let id2 = LoginId::new("second".to_string()); + + store_login_at_file(&wallet_file, account1, hd_path, id1, &password).unwrap(); + + // Trying to load with the wrong id + assert!(matches!( + load_existing_login_at_file(&wallet_file, &id2, &password), + Err(BackendError::WalletNoSuchLoginId), + )); + } + + #[test] + fn load_with_wrong_id_fails_with_multi() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let id1 = LoginId::new("first".to_string()); + let id2 = LoginId::new("second".to_string()); + + store_login_with_multiple_accounts_at_file(&wallet_file, account1, hd_path, id1, &password) + .unwrap(); + + // Trying to load with the wrong id + assert!(matches!( + load_existing_login_at_file(&wallet_file, &id2, &password), + Err(BackendError::WalletNoSuchLoginId), + )); + } + + #[test] + fn store_and_load_a_single_login() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let id1 = LoginId::new("first".to_string()); + + store_login_at_file( + &wallet_file, + account1.clone(), + hd_path.clone(), + id1.clone(), + &password, + ) + .unwrap(); + + let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); + let acc = loaded_login.as_mnemonic_account().unwrap(); + assert_eq!(&account1, acc.mnemonic()); + assert_eq!(&hd_path, acc.hd_path()); + } + + #[test] + fn store_a_single_login_then_update_pwd_and_load() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let new_password = UserPassword::new("new_password".to_string()); + let id1 = LoginId::new("first".to_string()); + + store_login_at_file( + &wallet_file, + account1.clone(), + hd_path.clone(), + id1.clone(), + &password, + ) + .unwrap(); + + update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); + + let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &new_password).unwrap(); + let acc = loaded_login.as_mnemonic_account().unwrap(); + assert_eq!(&account1, acc.mnemonic()); + assert_eq!(&hd_path, acc.hd_path()); + } + + #[test] + fn store_a_single_login_then_update_pwd_with_identical_pwd_is_noop_but_okay() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let id1 = LoginId::new("first".to_string()); + + store_login_at_file( + &wallet_file, + account1.clone(), + hd_path.clone(), + id1.clone(), + &password, + ) + .unwrap(); + + update_encrypted_logins_at_file(&wallet_file, &password, &password).unwrap(); + + let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); + let acc = loaded_login.as_mnemonic_account().unwrap(); + assert_eq!(&account1, acc.mnemonic()); + assert_eq!(&hd_path, acc.hd_path()); + } + + #[test] + fn store_a_single_login_then_update_pwd_with_wrong_current_pwd_fails() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let new_password = UserPassword::new("new_password".to_string()); + let wrong_password = UserPassword::new("wrong_password".to_string()); + let id1 = LoginId::new("first".to_string()); + + store_login_at_file(&wallet_file, account1, hd_path, id1, &password).unwrap(); + + let err = update_encrypted_logins_at_file(&wallet_file, &wrong_password, &new_password) + .unwrap_err(); + assert!(matches!(err, BackendError::StoreCipherError { .. })); + } + + #[test] + fn store_a_single_login_then_update_pwd_and_load_with_wrong_pwd_fails() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let new_password = UserPassword::new("new_password".to_string()); + let id1 = LoginId::new("first".to_string()); + + store_login_at_file(&wallet_file, account1, hd_path, id1.clone(), &password).unwrap(); + + update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); + + let err = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap_err(); + assert!(matches!(err, BackendError::StoreCipherError { .. })); + } + + #[test] + fn store_and_load_a_single_login_with_multi() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let acc1 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let id1 = LoginId::new("first".to_string()); + + store_login_with_multiple_accounts_at_file( + &wallet_file, + acc1.clone(), + hd_path.clone(), + id1.clone(), + &password, + ) + .unwrap(); + + let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); + let accounts = loaded_login.as_multiple_accounts().unwrap(); + assert_eq!(accounts.len(), 1); + let account = accounts + .get_account(&DEFAULT_FIRST_ACCOUNT_NAME.into()) + .unwrap(); + assert_eq!(account.id().as_ref(), DEFAULT_FIRST_ACCOUNT_NAME); + assert_eq!(account.mnemonic(), &acc1); + assert_eq!(account.hd_path(), &hd_path); + } + + #[test] + fn store_a_single_login_with_multi_then_update_pwd_and_load() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let acc1 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let new_password = UserPassword::new("new_password".to_string()); + let id1 = LoginId::new("first".to_string()); + + store_login_with_multiple_accounts_at_file( + &wallet_file, + acc1.clone(), + hd_path.clone(), + id1.clone(), + &password, + ) + .unwrap(); + + update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); + + let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &new_password).unwrap(); + let accounts = loaded_login.as_multiple_accounts().unwrap(); + assert_eq!(accounts.len(), 1); + let account = accounts + .get_account(&DEFAULT_FIRST_ACCOUNT_NAME.into()) + .unwrap(); + assert_eq!(account.id().as_ref(), DEFAULT_FIRST_ACCOUNT_NAME); + assert_eq!(account.mnemonic(), &acc1); + assert_eq!(account.hd_path(), &hd_path); + } + + #[test] + fn store_a_single_login_with_multi_then_update_pwd_with_wrong_current_pwd_fails() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let acc1 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let new_password = UserPassword::new("new_password".to_string()); + let wrong_password = UserPassword::new("wrong_password".to_string()); + let id1 = LoginId::new("first".to_string()); + + store_login_with_multiple_accounts_at_file(&wallet_file, acc1, hd_path, id1, &password) + .unwrap(); + + let err = update_encrypted_logins_at_file(&wallet_file, &wrong_password, &new_password) + .unwrap_err(); + assert!(matches!(err, BackendError::StoreCipherError { .. })); + } + + #[test] + fn store_a_single_login_with_multi_then_update_pwd_and_load_with_wrong_pwd_fails() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let acc1 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let new_password = UserPassword::new("new_password".to_string()); + let id1 = LoginId::new("first".to_string()); + + store_login_with_multiple_accounts_at_file( + &wallet_file, + acc1, + hd_path, + id1.clone(), + &password, + ) + .unwrap(); + + update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); + + let err = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap_err(); + assert!(matches!(err, BackendError::StoreCipherError { .. })); + } + + #[test] + fn store_a_second_login_with_a_different_password_fails() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let account2 = Mnemonic::generate(24).unwrap(); + let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let bad_password = UserPassword::new("bad-password".to_string()); + let id1 = LoginId::new("first".to_string()); + let id2 = LoginId::new("second".to_string()); + + store_login_at_file( + &wallet_file, + account1, + cosmos_hd_path.clone(), + id1, + &password, + ) + .unwrap(); + + // Can't store a second login if you use different password + assert!(matches!( + store_login_at_file(&wallet_file, account2, cosmos_hd_path, id2, &bad_password), + Err(BackendError::WalletDifferentPasswordDetected), + )); + } + + #[test] + fn store_a_second_login_with_a_different_password_fails_with_multi() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let account2 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let bad_password = UserPassword::new("bad-password".to_string()); + let id1 = LoginId::new("first".to_string()); + let id2 = LoginId::new("second".to_string()); + + store_login_with_multiple_accounts_at_file( + &wallet_file, + account1, + hd_path.clone(), + id1, + &password, + ) + .unwrap(); + + // Can't store a second login if you use different password + assert!(matches!( + store_login_with_multiple_accounts_at_file( + &wallet_file, + account2, + hd_path, + id2, + &bad_password + ), + Err(BackendError::WalletDifferentPasswordDetected), + )); + } + + #[test] + fn store_two_mnemonic_accounts_gives_different_salts_and_iv() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let account2 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let different_hd_path: DerivationPath = "m".parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let id1 = LoginId::new("first".to_string()); + let id2 = LoginId::new("second".to_string()); + + // Store the first account + store_login_at_file(&wallet_file, account1, hd_path, id1, &password).unwrap(); + + let stored_wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); + let encrypted_blob = &stored_wallet + .get_encrypted_login_by_index(0) + .unwrap() + .account; + + // keep track of salt and iv for future assertion + let original_iv = encrypted_blob.iv().to_vec(); + let original_salt = encrypted_blob.salt().to_vec(); + + // Add an extra account + store_login_at_file(&wallet_file, account2, different_hd_path, id2, &password).unwrap(); + + let loaded_accounts = load_existing_wallet_at_file(&wallet_file).unwrap(); + assert_eq!(loaded_accounts.len(), 2); + let encrypted_blob = &loaded_accounts + .get_encrypted_login_by_index(1) + .unwrap() + .account; + + // fresh IV and salt are used + assert_ne!(original_iv, encrypted_blob.iv()); + assert_ne!(original_salt, encrypted_blob.salt()); + } + + #[test] + fn store_two_mnemonic_accounts_using_two_logins() { + let store_dir = tempdir().unwrap(); + let wallet = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let account2 = Mnemonic::generate(24).unwrap(); + let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let different_hd_path: DerivationPath = "m".parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let id1 = LoginId::new("first".to_string()); + let id2 = LoginId::new("second".to_string()); + + // Store the first account + store_login_at_file( + &wallet, + account1.clone(), + cosmos_hd_path.clone(), + id1.clone(), + &password, + ) + .unwrap(); + + let login = load_existing_login_at_file(&wallet, &id1, &password).unwrap(); + let acc = login.as_mnemonic_account().unwrap(); + assert_eq!(&account1, acc.mnemonic()); + assert_eq!(&cosmos_hd_path, acc.hd_path()); + + // Add an extra account + store_login_at_file( + &wallet, + account2.clone(), + different_hd_path.clone(), + id2.clone(), + &password, + ) + .unwrap(); + + // first account should be unchanged + let loaded_login = load_existing_login_at_file(&wallet, &id1, &password).unwrap(); + let acc1 = loaded_login.as_mnemonic_account().unwrap(); + assert_eq!(&account1, acc1.mnemonic()); + assert_eq!(&cosmos_hd_path, acc1.hd_path()); + + let loaded_login = load_existing_login_at_file(&wallet, &id2, &password).unwrap(); + let acc2 = loaded_login.as_mnemonic_account().unwrap(); + assert_eq!(&account2, acc2.mnemonic()); + assert_eq!(&different_hd_path, acc2.hd_path()); + } + + #[test] + fn store_two_mnemonic_accounts_using_two_logins_then_update_pwd_and_load() { + let store_dir = tempdir().unwrap(); + let wallet = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let account2 = Mnemonic::generate(24).unwrap(); + let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let different_hd_path: DerivationPath = "m".parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let new_password = UserPassword::new("new_password".to_string()); + let id1 = LoginId::new("first".to_string()); + let id2 = LoginId::new("second".to_string()); + + // Store the first login with an account + store_login_at_file( + &wallet, + account1.clone(), + cosmos_hd_path.clone(), + id1.clone(), + &password, + ) + .unwrap(); + + let login = load_existing_login_at_file(&wallet, &id1, &password).unwrap(); + let acc = login.as_mnemonic_account().unwrap(); + assert_eq!(&account1, acc.mnemonic()); + assert_eq!(&cosmos_hd_path, acc.hd_path()); + + // Store a second login, also with an account + store_login_at_file( + &wallet, + account2.clone(), + different_hd_path.clone(), + id2.clone(), + &password, + ) + .unwrap(); + + update_encrypted_logins_at_file(&wallet, &password, &new_password).unwrap(); + + // first account should be unchanged + let loaded_login = load_existing_login_at_file(&wallet, &id1, &new_password).unwrap(); + let acc1 = loaded_login.as_mnemonic_account().unwrap(); + assert_eq!(&account1, acc1.mnemonic()); + assert_eq!(&cosmos_hd_path, acc1.hd_path()); + + let loaded_login = load_existing_login_at_file(&wallet, &id2, &new_password).unwrap(); + let acc2 = loaded_login.as_mnemonic_account().unwrap(); + assert_eq!(&account2, acc2.mnemonic()); + assert_eq!(&different_hd_path, acc2.hd_path()); + } + + #[test] + fn store_one_mnemonic_account_and_one_multi_account() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let account2 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let different_hd_path: DerivationPath = "m".parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let id1 = LoginId::new("first".to_string()); + let id2 = LoginId::new("second".to_string()); + + // Store the first account + store_login_at_file( + &wallet_file, + account1.clone(), + hd_path.clone(), + id1.clone(), + &password, + ) + .unwrap(); + + let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); + let acc = loaded_login.as_mnemonic_account().unwrap(); + assert_eq!(&account1, acc.mnemonic()); + assert_eq!(&hd_path, acc.hd_path()); + + // Add an extra account + store_login_with_multiple_accounts_at_file( + &wallet_file, + account2.clone(), + different_hd_path.clone(), + id2.clone(), + &password, + ) + .unwrap(); + + // first account should be unchanged + let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); + let acc1 = loaded_login.as_mnemonic_account().unwrap(); + assert_eq!(&account1, acc1.mnemonic()); + assert_eq!(&hd_path, acc1.hd_path()); + + let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); + let acc2 = loaded_login.as_multiple_accounts().unwrap(); + assert_eq!(acc2.len(), 1); + let account = acc2 + .get_account(&DEFAULT_FIRST_ACCOUNT_NAME.into()) + .unwrap(); + assert_eq!(account.id().as_ref(), DEFAULT_FIRST_ACCOUNT_NAME); + assert_eq!(account.mnemonic(), &account2); + assert_eq!(account.hd_path(), &different_hd_path); + } + + #[test] + fn remove_non_existent_id_fails() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let id1 = LoginId::new("first".to_string()); + let id2 = LoginId::new("second".to_string()); + + store_login_with_multiple_accounts_at_file(&wallet_file, account1, hd_path, id1, &password) + .unwrap(); + + // Fails to delete non-existent id in the wallet + assert!(matches!( + remove_login_at_file(&wallet_file, &id2), + Err(BackendError::WalletNoSuchLoginId), + )); + } + + #[test] + fn store_and_remove_wallet_login_information() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let account2 = Mnemonic::generate(24).unwrap(); + let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let different_hd_path: DerivationPath = "m".parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let id1 = LoginId::new("first".to_string()); + let id2 = LoginId::new("second".to_string()); + + // Store two accounts with two different passwords + store_login_at_file( + &wallet_file, + account1.clone(), + cosmos_hd_path.clone(), + id1.clone(), + &password, + ) + .unwrap(); + store_login_at_file( + &wallet_file, + account2.clone(), + different_hd_path.clone(), + id2.clone(), + &password, + ) + .unwrap(); + + // Load and compare + let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); + let acc1 = loaded_login.as_mnemonic_account().unwrap(); + assert_eq!(&account1, acc1.mnemonic()); + assert_eq!(&cosmos_hd_path, acc1.hd_path()); + + let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); + let acc2 = loaded_login.as_mnemonic_account().unwrap(); + assert_eq!(&account2, acc2.mnemonic()); + assert_eq!(&different_hd_path, acc2.hd_path()); + + // Delete the second account + remove_login_at_file(&wallet_file, &id2).unwrap(); + + // The first account should be unchanged + let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); + let acc1 = loaded_login.as_mnemonic_account().unwrap(); + assert_eq!(&account1, acc1.mnemonic()); + assert_eq!(&cosmos_hd_path, acc1.hd_path()); + + // And we can't load the second one anymore + assert!(matches!( + load_existing_login_at_file(&wallet_file, &id2, &password), + Err(BackendError::WalletNoSuchLoginId), + )); + + // Delete the first account + assert!(wallet_file.exists()); + remove_login_at_file(&wallet_file, &id1).unwrap(); + + // The file should now be removed + assert!(!wallet_file.exists()); + + // And trying to load when the file is gone fails + assert!(matches!( + load_existing_login_at_file(&wallet_file, &id1, &password), + Err(BackendError::WalletFileNotFound), + )); + } + + #[test] + fn append_account_converts_the_type() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let account2 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let id1 = LoginId::new("first".to_string()); + let id2 = AccountId::new("second".to_string()); + + store_login_at_file( + &wallet_file, + account1.clone(), + hd_path.clone(), + id1.clone(), + &password, + ) + .unwrap(); + + // Check that it's there as the correct non-multiple type + let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); + let acc = loaded_login.as_mnemonic_account().unwrap(); + assert_eq!(acc.mnemonic(), &account1); + assert_eq!(acc.hd_path(), &hd_path); + + append_account_to_login_at_file( + &wallet_file, + account2.clone(), + hd_path.clone(), + id1.clone(), + id2.clone(), + &password, + ) + .unwrap(); + + // Check that it is now multiple mnemonic type + let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); + let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); + let expected = vec![ + WalletAccount::new(id1.into(), MnemonicAccount::new(account1, hd_path.clone())), + WalletAccount::new(id2, MnemonicAccount::new(account2, hd_path)), + ] + .into(); + assert_eq!(loaded_accounts, &expected); + } + + #[test] + fn append_accounts_to_existing_login() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let account2 = Mnemonic::generate(24).unwrap(); + let account3 = Mnemonic::generate(24).unwrap(); + let account4 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let id1 = LoginId::new("first".to_string()); + let id2 = LoginId::new("second".to_string()); + let id3 = AccountId::new("third".to_string()); + let id4 = AccountId::new("fourth".to_string()); + + store_login_at_file( + &wallet_file, + account1.clone(), + hd_path.clone(), + id1.clone(), + &password, + ) + .unwrap(); + + store_login_at_file( + &wallet_file, + account2.clone(), + hd_path.clone(), + id2.clone(), + &password, + ) + .unwrap(); + + // Check that it's there as the correct non-multiple type + let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); + let acc2 = loaded_login.as_mnemonic_account().unwrap(); + assert_eq!(acc2.mnemonic(), &account2); + assert_eq!(acc2.hd_path(), &hd_path); + + // Add a third and fourth mnenonic grouped together with the second one + append_account_to_login_at_file( + &wallet_file, + account3.clone(), + hd_path.clone(), + id2.clone(), + id3.clone(), + &password, + ) + .unwrap(); + append_account_to_login_at_file( + &wallet_file, + account4.clone(), + hd_path.clone(), + id2.clone(), + id4.clone(), + &password, + ) + .unwrap(); + + // Check that we can load all four + let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); + let acc1 = loaded_login.as_mnemonic_account().unwrap(); + assert_eq!(acc1.mnemonic(), &account1); + assert_eq!(acc1.hd_path(), &hd_path); + + let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); + let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); + let expected = vec![ + WalletAccount::new(id2.into(), MnemonicAccount::new(account2, hd_path.clone())), + WalletAccount::new(id3, MnemonicAccount::new(account3, hd_path.clone())), + WalletAccount::new(id4, MnemonicAccount::new(account4, hd_path)), + ] + .into(); + assert_eq!(loaded_accounts, &expected); + } + + #[test] + fn append_accounts_to_existing_login_with_multi() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let account2 = Mnemonic::generate(24).unwrap(); + let account3 = Mnemonic::generate(24).unwrap(); + let account4 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let id1 = LoginId::new("first".to_string()); + let id2 = LoginId::new("second".to_string()); + let id3 = AccountId::new("third".to_string()); + let id4 = AccountId::new("fourth".to_string()); + + store_login_with_multiple_accounts_at_file( + &wallet_file, + account1.clone(), + hd_path.clone(), + id1.clone(), + &password, + ) + .unwrap(); + + store_login_with_multiple_accounts_at_file( + &wallet_file, + account2.clone(), + hd_path.clone(), + id2.clone(), + &password, + ) + .unwrap(); + + // Add a third and fourth mnenonic grouped together with the second one + append_account_to_login_at_file( + &wallet_file, + account3.clone(), + hd_path.clone(), + id2.clone(), + id3.clone(), + &password, + ) + .unwrap(); + append_account_to_login_at_file( + &wallet_file, + account4.clone(), + hd_path.clone(), + id2.clone(), + id4.clone(), + &password, + ) + .unwrap(); + + // Check that we can load all four + let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); + let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); + let expected = vec![WalletAccount::new( + DEFAULT_FIRST_ACCOUNT_NAME.into(), + MnemonicAccount::new(account1, hd_path.clone()), + )] + .into(); + assert_eq!(loaded_accounts, &expected); + + let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); + let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); + let expected = vec![ + WalletAccount::new( + DEFAULT_FIRST_ACCOUNT_NAME.into(), + MnemonicAccount::new(account2, hd_path.clone()), + ), + WalletAccount::new(id3, MnemonicAccount::new(account3, hd_path.clone())), + WalletAccount::new(id4, MnemonicAccount::new(account4, hd_path)), + ] + .into(); + assert_eq!(loaded_accounts, &expected); + } + + #[test] + fn append_account_to_existing_login_with_multi_then_update_pwd_and_load() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let account2 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let new_password = UserPassword::new("new_password".to_string()); + let login_id = LoginId::new("first".to_string()); + let appended_account = AccountId::new("second".to_string()); + + store_login_with_multiple_accounts_at_file( + &wallet_file, + account1.clone(), + hd_path.clone(), + login_id.clone(), + &password, + ) + .unwrap(); + + // Append a second mnenonic to the same login + append_account_to_login_at_file( + &wallet_file, + account2.clone(), + hd_path.clone(), + login_id.clone(), + appended_account.clone(), + &password, + ) + .unwrap(); + + // Update the password + update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); + + // Expect that we can load these 2 accounts with the new password + let loaded_login = + load_existing_login_at_file(&wallet_file, &login_id, &new_password).unwrap(); + let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); + let expected = vec![ + WalletAccount::new( + DEFAULT_FIRST_ACCOUNT_NAME.into(), + MnemonicAccount::new(account1, hd_path.clone()), + ), + WalletAccount::new(appended_account, MnemonicAccount::new(account2, hd_path)), + ] + .into(); + assert_eq!(loaded_accounts, &expected); + + // Expect that trying to load these 2 accounts with the old password fails + let err = load_existing_login_at_file(&wallet_file, &login_id, &password).unwrap_err(); + assert!(matches!(err, BackendError::StoreCipherError { .. })); + } + + #[test] + fn append_the_same_mnemonic_twice_fails() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let id1 = LoginId::new("first".to_string()); + let id2 = AccountId::new("second".to_string()); + + store_login_with_multiple_accounts_at_file( + &wallet_file, + account1.clone(), + hd_path.clone(), + id1.clone(), + &password, + ) + .unwrap(); + + assert!(matches!( + append_account_to_login_at_file(&wallet_file, account1, hd_path, id1, id2, &password), + Err(BackendError::WalletMnemonicAlreadyExistsInWalletLogin), + )) + } + + #[test] + fn append_the_same_account_name_twice_fails() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let mnemonic1 = Mnemonic::generate(24).unwrap(); + let mnemonic2 = Mnemonic::generate(24).unwrap(); + let mnemonic3 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + // The top-level login id. NOTE: the first account id is always set to default. + let login_id = LoginId::new("my_login_id".to_string()); + + // Store the first account under login_id. The first account id is always set to default + // name. + store_login_with_multiple_accounts_at_file( + &wallet_file, + mnemonic1, + hd_path.clone(), + login_id.clone(), + &password, + ) + .unwrap(); + + // Append another account (account2) to the same login (login_id) + let account2 = AccountId::new("account_2".to_string()); + + append_account_to_login_at_file( + &wallet_file, + mnemonic2, + hd_path.clone(), + login_id.clone(), + account2.clone(), + &password, + ) + .unwrap(); + + // Appending the third account, with same account id will fail + assert!(matches!( + append_account_to_login_at_file( + &wallet_file, + mnemonic3, + hd_path, + login_id, + account2, + &password, + ), + Err(BackendError::WalletAccountIdAlreadyExistsInWalletLogin), + )) + } + + #[test] + fn delete_the_same_account_twice_for_a_login_fails() { + let store_dir = tempdir().unwrap(); + let wallet = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let account2 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let id1 = LoginId::new("first".to_string()); + let id2 = AccountId::new("second".to_string()); + + store_login_at_file(&wallet, account1, hd_path.clone(), id1.clone(), &password).unwrap(); + + append_account_to_login_at_file( + &wallet, + account2, + hd_path, + id1.clone(), + id2.clone(), + &password, + ) + .unwrap(); + + remove_account_from_login_at_file(&wallet, &id1, &id2, &password).unwrap(); + + assert!(matches!( + remove_account_from_login_at_file(&wallet, &id1, &id2, &password), + Err(BackendError::WalletNoSuchAccountIdInWalletLogin), + )); + } + + #[test] + fn delete_the_same_account_twice_for_a_login_fails_with_multi() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let account2 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let id1 = LoginId::new("first".to_string()); + let id2 = AccountId::new("second".to_string()); + + store_login_with_multiple_accounts_at_file( + &wallet_file, + account1, + hd_path.clone(), + id1.clone(), + &password, + ) + .unwrap(); + + append_account_to_login_at_file( + &wallet_file, + account2, + hd_path, + id1.clone(), + id2.clone(), + &password, + ) + .unwrap(); + + remove_account_from_login_at_file(&wallet_file, &id1, &id2, &password).unwrap(); + + assert!(matches!( + remove_account_from_login_at_file(&wallet_file, &id1, &id2, &password), + Err(BackendError::WalletNoSuchAccountIdInWalletLogin), + )); + } + + #[test] + fn delete_appended_account_doesnt_affect_others() { + let store_dir = tempdir().unwrap(); + let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let account2 = Mnemonic::generate(24).unwrap(); + let account3 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let id1 = LoginId::new("first".to_string()); + let id2 = LoginId::new("second".to_string()); + let id3 = AccountId::new("third".to_string()); + + store_login_at_file( + &wallet_file, + account1, + hd_path.clone(), + id1.clone(), + &password, + ) + .unwrap(); + + store_login_at_file( + &wallet_file, + account2.clone(), + hd_path.clone(), + id2.clone(), + &password, + ) + .unwrap(); + + append_account_to_login_at_file( + &wallet_file, + account3.clone(), + hd_path.clone(), + id2.clone(), + id3.clone(), + &password, + ) + .unwrap(); + + remove_login_at_file(&wallet_file, &id1).unwrap(); + + // The second login one is still there + let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); + let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); + let expected = vec![ + WalletAccount::new(id2.into(), MnemonicAccount::new(account2, hd_path.clone())), + WalletAccount::new(id3, MnemonicAccount::new(account3, hd_path)), + ] + .into(); + assert_eq!(loaded_accounts, &expected); + } + + #[test] + fn remove_all_accounts_for_a_login_removes_the_file_when_empty() { + let store_dir = tempdir().unwrap(); + let wallet = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let account2 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let id1 = LoginId::new("first".to_string()); + let id2 = AccountId::new("second".to_string()); + + store_login_with_multiple_accounts_at_file( + &wallet, + account1, + hd_path.clone(), + id1.clone(), + &password, + ) + .unwrap(); + + append_account_to_login_at_file( + &wallet, + account2, + hd_path, + id1.clone(), + id2.clone(), + &password, + ) + .unwrap(); + + remove_account_from_login_at_file( + &wallet, + &id1, + &DEFAULT_FIRST_ACCOUNT_NAME.into(), + &password, + ) + .unwrap(); + remove_account_from_login_at_file(&wallet, &id1, &id2, &password).unwrap(); + + // The file should now be removed + assert!(!wallet.exists()); + + // And trying to load when the file is gone fails + assert!(matches!( + load_existing_login_at_file(&wallet, &id1, &password), + Err(BackendError::WalletFileNotFound), + )); + } + + #[test] + fn remove_all_accounts_for_a_login_removes_that_login() { + let store_dir = tempdir().unwrap(); + let wallet = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let account2 = Mnemonic::generate(24).unwrap(); + let account3 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let id1 = LoginId::new("first".to_string()); + let id2 = AccountId::new("second".to_string()); + let id3 = LoginId::new("third".to_string()); + + store_login_with_multiple_accounts_at_file( + &wallet, + account1, + hd_path.clone(), + id1.clone(), + &password, + ) + .unwrap(); + + append_account_to_login_at_file( + &wallet, + account2, + hd_path.clone(), + id1.clone(), + id2.clone(), + &password, + ) + .unwrap(); + + store_login_with_multiple_accounts_at_file( + &wallet, + account3.clone(), + hd_path.clone(), + id3.clone(), + &password, + ) + .unwrap(); + + remove_account_from_login_at_file( + &wallet, + &id1, + &DEFAULT_FIRST_ACCOUNT_NAME.into(), + &password, + ) + .unwrap(); + remove_account_from_login_at_file(&wallet, &id1, &id2, &password).unwrap(); + + // And trying to load when the file is gone fails + assert!(matches!( + load_existing_login_at_file(&wallet, &id1, &password), + Err(BackendError::WalletNoSuchLoginId), + )); + + // The other login is still there + let loaded_login = load_existing_login_at_file(&wallet, &id3, &password).unwrap(); + let acc3 = loaded_login.as_multiple_accounts().unwrap(); + let expected = vec![WalletAccount::new( + DEFAULT_FIRST_ACCOUNT_NAME.into(), + MnemonicAccount::new(account3, hd_path), + )] + .into(); + assert_eq!(acc3, &expected); + } + + #[test] + fn append_accounts_and_remove_appended_accounts() { + let store_dir = tempdir().unwrap(); + let wallet = store_dir.path().join(WALLET_INFO_FILENAME); + let acc1 = Mnemonic::generate(24).unwrap(); + let acc2 = Mnemonic::generate(24).unwrap(); + let acc3 = Mnemonic::generate(24).unwrap(); + let acc4 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let id1 = LoginId::new("first".to_string()); + let id2 = LoginId::new("second".to_string()); + let id3 = AccountId::new("third".to_string()); + let id4 = AccountId::new("fourth".to_string()); + + store_login_at_file( + &wallet, + acc1.clone(), + hd_path.clone(), + id1.clone(), + &password, + ) + .unwrap(); + + store_login_at_file( + &wallet, + acc2.clone(), + hd_path.clone(), + id2.clone(), + &password, + ) + .unwrap(); + + // Add a third and fourth mnenonic grouped together with the second one + append_account_to_login_at_file( + &wallet, + acc3, + hd_path.clone(), + id2.clone(), + id3.clone(), + &password, + ) + .unwrap(); + append_account_to_login_at_file( + &wallet, + acc4.clone(), + hd_path.clone(), + id2.clone(), + id4.clone(), + &password, + ) + .unwrap(); + + // Delete the third mnemonic, from the second login entry + remove_account_from_login_at_file(&wallet, &id2, &id3, &password).unwrap(); + + // Check that we can still load the other accounts + let loaded_login = load_existing_login_at_file(&wallet, &id2, &password).unwrap(); + let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); + let expected = vec![ + WalletAccount::new( + id2.clone().into(), + MnemonicAccount::new(acc2, hd_path.clone()), + ), + WalletAccount::new(id4.clone(), MnemonicAccount::new(acc4, hd_path.clone())), + ] + .into(); + assert_eq!(loaded_accounts, &expected); + + // Delete the second and fourth mnemonic from the second login entry removes the login entry + remove_account_from_login_at_file(&wallet, &id2, &id2.clone().into(), &password).unwrap(); + remove_account_from_login_at_file(&wallet, &id2, &id4, &password).unwrap(); + assert!(matches!( + load_existing_login_at_file(&wallet, &id2, &password), + Err(BackendError::WalletNoSuchLoginId), + )); + + // The first login is still available + let loaded_login = load_existing_login_at_file(&wallet, &id1, &password).unwrap(); + let account = loaded_login.as_mnemonic_account().unwrap(); + assert_eq!(account.mnemonic(), &acc1); + assert_eq!(account.hd_path(), &hd_path); + } + + #[test] + fn rename_first_account_in_login() { + let store_dir = tempdir().unwrap(); + let wallet = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let login_id = LoginId::new("first".to_string()); + + store_login_with_multiple_accounts_at_file( + &wallet, + account1.clone(), + hd_path.clone(), + login_id.clone(), + &password, + ) + .unwrap(); + + let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); + let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); + let expected = vec![WalletAccount::new( + DEFAULT_FIRST_ACCOUNT_NAME.into(), + MnemonicAccount::new(account1.clone(), hd_path.clone()), + )] + .into(); + assert_eq!(loaded_accounts, &expected); + + let renamed_account = AccountId::new("new_first".to_string()); + + rename_account_in_login_at_file( + &wallet, + &login_id, + &DEFAULT_FIRST_ACCOUNT_NAME.into(), + &renamed_account, + &password, + ) + .unwrap(); + + let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); + let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); + let expected = vec![WalletAccount::new( + renamed_account, + MnemonicAccount::new(account1, hd_path), + )] + .into(); + assert_eq!(loaded_accounts, &expected); + } + + #[test] + fn rename_one_account_in_login_with_two_accounts() { + let store_dir = tempdir().unwrap(); + let wallet = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let account2 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let login_id = LoginId::new("first".to_string()); + let account_id2 = AccountId::new("second".to_string()); + let renamed_account_id2 = AccountId::new("new_second".to_string()); + + store_login_with_multiple_accounts_at_file( + &wallet, + account1.clone(), + hd_path.clone(), + login_id.clone(), + &password, + ) + .unwrap(); + + append_account_to_login_at_file( + &wallet, + account2.clone(), + hd_path.clone(), + login_id.clone(), + account_id2.clone(), + &password, + ) + .unwrap(); + + // Load and confirm + let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); + let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); + let expected = vec![ + WalletAccount::new( + DEFAULT_FIRST_ACCOUNT_NAME.into(), + MnemonicAccount::new(account1.clone(), hd_path.clone()), + ), + WalletAccount::new( + account_id2.clone(), + MnemonicAccount::new(account2.clone(), hd_path.clone()), + ), + ] + .into(); + assert_eq!(loaded_accounts, &expected); + + // Rename the second account to a new name + rename_account_in_login_at_file( + &wallet, + &login_id, + &account_id2, + &renamed_account_id2, + &password, + ) + .unwrap(); + + // Load and confirm + let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); + let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); + let expected = vec![ + WalletAccount::new( + DEFAULT_FIRST_ACCOUNT_NAME.into(), + MnemonicAccount::new(account1, hd_path.clone()), + ), + WalletAccount::new(renamed_account_id2, MnemonicAccount::new(account2, hd_path)), + ] + .into(); + assert_eq!(loaded_accounts, &expected); + } + + #[test] + fn rename_account_into_existing_account_id_fails() { + let store_dir = tempdir().unwrap(); + let wallet = store_dir.path().join(WALLET_INFO_FILENAME); + let account1 = Mnemonic::generate(24).unwrap(); + let account2 = Mnemonic::generate(24).unwrap(); + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let login_id = LoginId::new("first".to_string()); + let account_id2 = AccountId::new("second".to_string()); + let renamed_account_id2 = DEFAULT_FIRST_ACCOUNT_NAME.into(); + + store_login_with_multiple_accounts_at_file( + &wallet, + account1.clone(), + hd_path.clone(), + login_id.clone(), + &password, + ) + .unwrap(); + + append_account_to_login_at_file( + &wallet, + account2.clone(), + hd_path.clone(), + login_id.clone(), + account_id2.clone(), + &password, + ) + .unwrap(); + + // Load and confirm + let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); + let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); + let expected = vec![ + WalletAccount::new( + DEFAULT_FIRST_ACCOUNT_NAME.into(), + MnemonicAccount::new(account1.clone(), hd_path.clone()), + ), + WalletAccount::new( + account_id2.clone(), + MnemonicAccount::new(account2.clone(), hd_path.clone()), + ), + ] + .into(); + assert_eq!(loaded_accounts, &expected); + + // Rename the second account to the name of the first one fails + assert!(matches!( + rename_account_in_login_at_file( + &wallet, + &login_id, + &account_id2, + &renamed_account_id2, + &password, + ), + Err(BackendError::WalletAccountIdAlreadyExistsInWalletLogin) + )); + + // Load and confirm nothing was changed + let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); + let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); + let expected = vec![ + WalletAccount::new( + DEFAULT_FIRST_ACCOUNT_NAME.into(), + MnemonicAccount::new(account1, hd_path.clone()), + ), + WalletAccount::new(account_id2, MnemonicAccount::new(account2, hd_path)), + ] + .into(); + assert_eq!(loaded_accounts, &expected); + } + + // Test to that decrypts a stored file from the repo, to make sure we are able to decrypt stored + // wallets created with older versions. + #[test] + fn decrypt_stored_wallet() { + const SAVED_WALLET: &str = "src/wallet_storage/test-data/saved-wallet.json"; + let wallet_file = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(SAVED_WALLET); + + let wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); + + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let bad_password = UserPassword::new("bad-password".to_string()); + let id1 = LoginId::new("first".to_string()); + let id2 = LoginId::new("second".to_string()); + + assert!(!wallet.password_can_decrypt_all(&bad_password)); + assert!(wallet.password_can_decrypt_all(&password)); + + let acc1 = wallet.decrypt_login(&id1, &password).unwrap(); + let acc2 = wallet.decrypt_login(&id2, &password).unwrap(); + + assert!(matches!(acc1, StoredLogin::Mnemonic(_))); + assert!(matches!(acc2, StoredLogin::Mnemonic(_))); + + let expected_acc1 = bip39::Mnemonic::from_str("country mean universe text phone begin deputy reject result good cram illness common cluster proud swamp digital patrol spread bar face december base kick").unwrap(); + let expected_acc2 = bip39::Mnemonic::from_str("home mansion start quiz dress decide hint second dragon sunny juice always steak real minimum art rival skin draw total pulp foot goddess agent").unwrap(); + + assert_eq!( + acc1.as_mnemonic_account().unwrap().mnemonic(), + &expected_acc1 + ); + assert_eq!(acc1.as_mnemonic_account().unwrap().hd_path(), &hd_path,); + + assert_eq!( + acc2.as_mnemonic_account().unwrap().mnemonic(), + &expected_acc2 + ); + assert_eq!(acc2.as_mnemonic_account().unwrap().hd_path(), &hd_path,); + } + + #[test] + fn decrypt_stored_wallet_1_0_4() { + const SAVED_WALLET: &str = "src/wallet_storage/test-data/saved-wallet-1.0.4.json"; + let wallet_file = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(SAVED_WALLET); + + let wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); + + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password11!".to_string()); + let bad_password = UserPassword::new("bad-password".to_string()); + let login_id = LoginId::new("default".to_string()); + + assert!(!wallet.password_can_decrypt_all(&bad_password)); + assert!(wallet.password_can_decrypt_all(&password)); + + let acc1 = wallet.decrypt_login(&login_id, &password).unwrap(); + + assert!(matches!(acc1, StoredLogin::Mnemonic(_))); + + let expected_acc1 = bip39::Mnemonic::from_str("arrow capable abstract industry elevator nominee december piece hotel feed lounge web faint sword veteran bundle hour page actual laptop horror gold test warrior").unwrap(); + + assert_eq!( + acc1.as_mnemonic_account().unwrap().mnemonic(), + &expected_acc1 + ); + assert_eq!(acc1.as_mnemonic_account().unwrap().hd_path(), &hd_path,); + } + + #[test] + fn decrypt_stored_wallet_1_0_5_with_multiple_accounts() { + const SAVED_WALLET: &str = "src/wallet_storage/test-data/saved-wallet-1.0.5.json"; + let wallet_file = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(SAVED_WALLET); + + let wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); + + let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password11!".to_string()); + let bad_password = UserPassword::new("bad-password".to_string()); + let login_id = LoginId::new("default".to_string()); + + assert!(!wallet.password_can_decrypt_all(&bad_password)); + assert!(wallet.password_can_decrypt_all(&password)); + + let login = wallet.decrypt_login(&login_id, &password).unwrap(); + + assert!(matches!(login, StoredLogin::Multiple(_))); + + let login = login.as_multiple_accounts().unwrap(); + assert_eq!(login.len(), 4); + + let expected_mn1 = bip39::Mnemonic::from_str("arrow capable abstract industry elevator nominee december piece hotel feed lounge web faint sword veteran bundle hour page actual laptop horror gold test warrior").unwrap(); + let expected_mn2 = bip39::Mnemonic::from_str("border hurt skull lunar goddess second danger game dismiss exhaust oven thumb dog drama onion false orchard spice tent next predict invite cherry green").unwrap(); + let expected_mn3 = bip39::Mnemonic::from_str("gentle crowd rule snap girl urge flat jump winner cluster night sand museum stock grunt quick tree acquire traffic major awake tag rack peasant").unwrap(); + let expected_mn4 = bip39::Mnemonic::from_str("debris blue skin annual inhale text border rigid spatial lesson coconut yard horn crystal control survey version vote hawk neck frame arrive oblige width").unwrap(); + + let expected = vec![ + WalletAccount::new( + "default".into(), + MnemonicAccount::new(expected_mn1, hd_path.clone()), + ), + WalletAccount::new( + "account2".into(), + MnemonicAccount::new(expected_mn2, hd_path.clone()), + ), + WalletAccount::new( + "foobar".into(), + MnemonicAccount::new(expected_mn3, hd_path.clone()), + ), + WalletAccount::new("42".into(), MnemonicAccount::new(expected_mn4, hd_path)), + ] + .into(); + + assert_eq!(login, &expected); + } + + #[test] + fn append_filename() { + let wallet_file = PathBuf::from("/tmp/saved-wallet.json"); + let timestamp = OsString::from("42"); + #[cfg(target_family = "unix")] + assert_eq!( + append_timestamp_to_filename(wallet_file.clone(), timestamp.clone(), None) + .unwrap() + .into_os_string() + .into_string() + .unwrap(), + "/tmp/saved-wallet-42.json".to_string(), + ); + #[cfg(not(target_family = "unix"))] + assert_eq!( + append_timestamp_to_filename(wallet_file.clone(), timestamp.clone(), None) + .unwrap() + .into_os_string() + .into_string() + .unwrap(), + r"/tmp\saved-wallet-42.json".to_string(), + ); + + #[cfg(target_family = "unix")] + assert_eq!( + append_timestamp_to_filename(wallet_file, timestamp, Some(3)) + .unwrap() + .into_os_string() + .into_string() + .unwrap(), + "/tmp/saved-wallet-42-3.json".to_string(), + ); + #[cfg(not(target_family = "unix"))] + assert_eq!( + append_timestamp_to_filename(wallet_file, timestamp, Some(3)) + .unwrap() + .into_os_string() + .into_string() + .unwrap(), + r"/tmp\saved-wallet-42-3.json".to_string(), + ); + } +} diff --git a/yarn.lock b/yarn.lock index 843efd355b..ff82392398 100644 --- a/yarn.lock +++ b/yarn.lock @@ -15,6 +15,17 @@ "@jridgewell/gen-mapping" "^0.3.5" "@jridgewell/trace-mapping" "^0.3.24" +"@asamuzakjp/css-color@^3.1.1": + version "3.1.1" + resolved "https://registry.yarnpkg.com/@asamuzakjp/css-color/-/css-color-3.1.1.tgz#41a612834dafd9353b89855b37baa8a03fb67bf2" + integrity sha512-hpRD68SV2OMcZCsrbdkccTw5FXjNDLo5OuqSHyHZfwweGsDWZwDJ2+gONyNAbazZclobMirACLw0lk8WVxIqxA== + dependencies: + "@csstools/css-calc" "^2.1.2" + "@csstools/css-color-parser" "^3.0.8" + "@csstools/css-parser-algorithms" "^3.0.4" + "@csstools/css-tokenizer" "^3.0.3" + lru-cache "^10.4.3" + "@babel/code-frame@7.12.11": version "7.12.11" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.12.11.tgz#f4ad435aa263db935b8f10f2c552d23fb716a63f" @@ -1152,6 +1163,13 @@ dependencies: regenerator-runtime "^0.14.0" +"@babel/runtime@^7.23.2", "@babel/runtime@^7.25.0", "@babel/runtime@^7.26.10": + version "7.27.0" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.27.0.tgz#fbee7cf97c709518ecc1f590984481d5460d4762" + integrity sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw== + dependencies: + regenerator-runtime "^0.14.0" + "@babel/runtime@~7.5.4": version "7.5.5" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.5.5.tgz#74fba56d35efbeca444091c7850ccd494fd2f132" @@ -1199,6 +1217,60 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== +"@biomejs/biome@1.9.4": + version "1.9.4" + resolved "https://registry.yarnpkg.com/@biomejs/biome/-/biome-1.9.4.tgz#89766281cbc3a0aae865a7ff13d6aaffea2842bf" + integrity sha512-1rkd7G70+o9KkTn5KLmDYXihGoTaIGO9PIIN2ZB7UJxFrWw04CZHPYiMRjYsaDvVV7hP1dYNRLxSANLaBFGpog== + optionalDependencies: + "@biomejs/cli-darwin-arm64" "1.9.4" + "@biomejs/cli-darwin-x64" "1.9.4" + "@biomejs/cli-linux-arm64" "1.9.4" + "@biomejs/cli-linux-arm64-musl" "1.9.4" + "@biomejs/cli-linux-x64" "1.9.4" + "@biomejs/cli-linux-x64-musl" "1.9.4" + "@biomejs/cli-win32-arm64" "1.9.4" + "@biomejs/cli-win32-x64" "1.9.4" + +"@biomejs/cli-darwin-arm64@1.9.4": + version "1.9.4" + resolved "https://registry.yarnpkg.com/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-1.9.4.tgz#dfa376d23a54a2d8f17133c92f23c1bf2e62509f" + integrity sha512-bFBsPWrNvkdKrNCYeAp+xo2HecOGPAy9WyNyB/jKnnedgzl4W4Hb9ZMzYNbf8dMCGmUdSavlYHiR01QaYR58cw== + +"@biomejs/cli-darwin-x64@1.9.4": + version "1.9.4" + resolved "https://registry.yarnpkg.com/@biomejs/cli-darwin-x64/-/cli-darwin-x64-1.9.4.tgz#eafc2ce3849d385fc02238aad1ca4a73395a64d9" + integrity sha512-ngYBh/+bEedqkSevPVhLP4QfVPCpb+4BBe2p7Xs32dBgs7rh9nY2AIYUL6BgLw1JVXV8GlpKmb/hNiuIxfPfZg== + +"@biomejs/cli-linux-arm64-musl@1.9.4": + version "1.9.4" + resolved "https://registry.yarnpkg.com/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-1.9.4.tgz#d780c3e01758fc90f3268357e3f19163d1f84fca" + integrity sha512-v665Ct9WCRjGa8+kTr0CzApU0+XXtRgwmzIf1SeKSGAv+2scAlW6JR5PMFo6FzqqZ64Po79cKODKf3/AAmECqA== + +"@biomejs/cli-linux-arm64@1.9.4": + version "1.9.4" + resolved "https://registry.yarnpkg.com/@biomejs/cli-linux-arm64/-/cli-linux-arm64-1.9.4.tgz#8ed1dd0e89419a4b66a47f95aefb8c46ae6041c9" + integrity sha512-fJIW0+LYujdjUgJJuwesP4EjIBl/N/TcOX3IvIHJQNsAqvV2CHIogsmA94BPG6jZATS4Hi+xv4SkBBQSt1N4/g== + +"@biomejs/cli-linux-x64-musl@1.9.4": + version "1.9.4" + resolved "https://registry.yarnpkg.com/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-1.9.4.tgz#f36982b966bd671a36671e1de4417963d7db15fb" + integrity sha512-gEhi/jSBhZ2m6wjV530Yy8+fNqG8PAinM3oV7CyO+6c3CEh16Eizm21uHVsyVBEB6RIM8JHIl6AGYCv6Q6Q9Tg== + +"@biomejs/cli-linux-x64@1.9.4": + version "1.9.4" + resolved "https://registry.yarnpkg.com/@biomejs/cli-linux-x64/-/cli-linux-x64-1.9.4.tgz#a0a7f56680c76b8034ddc149dbf398bdd3a462e8" + integrity sha512-lRCJv/Vi3Vlwmbd6K+oQ0KhLHMAysN8lXoCI7XeHlxaajk06u7G+UsFSO01NAs5iYuWKmVZjmiOzJ0OJmGsMwg== + +"@biomejs/cli-win32-arm64@1.9.4": + version "1.9.4" + resolved "https://registry.yarnpkg.com/@biomejs/cli-win32-arm64/-/cli-win32-arm64-1.9.4.tgz#e2ef4e0084e76b7e26f0fc887c5ef1265ea56200" + integrity sha512-tlbhLk+WXZmgwoIKwHIHEBZUwxml7bRJgk0X2sPyNR3S93cdRq6XulAZRQJ17FYGGzWne0fgrXBKpl7l4M87Hg== + +"@biomejs/cli-win32-x64@1.9.4": + version "1.9.4" + resolved "https://registry.yarnpkg.com/@biomejs/cli-win32-x64/-/cli-win32-x64-1.9.4.tgz#4c7afa90e3970213599b4095e62f87e5972b2340" + integrity sha512-8Y5wMhVIPaWe6jw2H+KlEm4wP/f7EW3810ZLmDlrEEy5KvBsb9ECEfu/kMWD484ijfQ8+nIi0giMgu9g1UAuuA== + "@chain-registry/client@^1.49.11": version "1.53.94" resolved "https://registry.yarnpkg.com/@chain-registry/client/-/client-1.53.94.tgz#cbee6721abb8a7cdbf54383c3c7746c0b2e32a46" @@ -1231,6 +1303,11 @@ resolved "https://registry.yarnpkg.com/@chain-registry/types/-/types-0.46.15.tgz#f4c0219fb7060d97cb224b55f349adb1d436aac9" integrity sha512-gzf+pkAbEZ7fKuTuwmrEAEcx1K/BNrKuCnB9s+WwSo9Ad/3s+GV5LOXcOOxjjHh9Mrs9kvnxKvzKjOwWu8gDJw== +"@chain-registry/types@^0.50.107", "@chain-registry/types@^0.50.36": + version "0.50.107" + resolved "https://registry.yarnpkg.com/@chain-registry/types/-/types-0.50.107.tgz#460bac6486ac609702accd3f2ad619b523c5b9fa" + integrity sha512-9J88lyxWAgWgjAcV8LhYwhCGXnZmSnxH0WWbHY5JHJ05BDWuWRcWV4NE+9p085tfDKeOo56ua+gTG1hfeMUXsA== + "@chain-registry/types@^0.50.94": version "0.50.94" resolved "https://registry.yarnpkg.com/@chain-registry/types/-/types-0.50.94.tgz#9c9cdeb45b122dae116f19ba04418ad6a183e154" @@ -1710,6 +1787,36 @@ nock "13.5.4" uuid "^9.0.1" +"@cosmos-kit/core@^2.16.0": + version "2.16.0" + resolved "https://registry.yarnpkg.com/@cosmos-kit/core/-/core-2.16.0.tgz#19a393cad477183014e919690ce409ce32c7decf" + integrity sha512-bhN3Fs4TcAoasdYMVaYHbpJWo8ZUhmCRc49t1Nc5UJHHu9VGs9NSnSk2NoprLA+kciIt+Y+RUXZLnt588nkG3Q== + dependencies: + "@chain-registry/client" "^1.49.11" + "@chain-registry/keplr" "^1.69.13" + "@chain-registry/types" "^0.46.11" + "@cosmjs/amino" "^0.32.3" + "@cosmjs/cosmwasm-stargate" "^0.32.3" + "@cosmjs/proto-signing" "^0.32.3" + "@cosmjs/stargate" "^0.32.3" + "@dao-dao/cosmiframe" "^1.0.0-rc.1" + "@walletconnect/types" "2.11.0" + bowser "2.11.0" + cosmjs-types "^0.9.0" + events "3.3.0" + nock "13.5.4" + uuid "^9.0.1" + +"@cosmos-kit/keplr-extension@^2.14.0": + version "2.15.0" + resolved "https://registry.yarnpkg.com/@cosmos-kit/keplr-extension/-/keplr-extension-2.15.0.tgz#a674d8a657a182ef1227498db89c93d52a9c13dc" + integrity sha512-jaB2mEfy4bamnZL+RYonfrsAecoVNrk1e+PsTxPNNJh8EFAohRTY6OG4Q6ZZGy8zIQuUNTGdFTIt2T03o7o0ig== + dependencies: + "@chain-registry/keplr" "^1.69.13" + "@cosmos-kit/core" "^2.16.0" + "@keplr-wallet/provider-extension" "^0.12.95" + "@keplr-wallet/types" "^0.12.95" + "@cosmos-kit/keplr-extension@^2.7.9": version "2.14.6" resolved "https://registry.yarnpkg.com/@cosmos-kit/keplr-extension/-/keplr-extension-2.14.6.tgz#4956500d1b6fe7a0ca39b7e77a2df5232b21ca6f" @@ -1729,6 +1836,15 @@ "@cosmos-kit/core" "^2.15.5" "@dao-dao/cosmiframe" "^1.0.0-rc.1" +"@cosmos-kit/react-lite@^2.16.0": + version "2.16.0" + resolved "https://registry.yarnpkg.com/@cosmos-kit/react-lite/-/react-lite-2.16.0.tgz#f7eb23930965a242209e6480c89689f3bd11eed5" + integrity sha512-vXPHtGZwGZn5DwBllPNnnSlwuFRliLuqqAHgGzAkMR9FpyjTQzUrkJRsl5Ur8KTvJiO6CqR2//fU2b1uZirw5A== + dependencies: + "@chain-registry/types" "^0.46.11" + "@cosmos-kit/core" "^2.16.0" + "@dao-dao/cosmiframe" "^1.0.0-rc.1" + "@cosmos-kit/react@^2.10.11": version "2.21.7" resolved "https://registry.yarnpkg.com/@cosmos-kit/react/-/react-2.21.7.tgz#009cb60f6604bc3894d4946c9ce038a2039c6166" @@ -1739,6 +1855,16 @@ "@cosmos-kit/react-lite" "^2.15.8" "@react-icons/all-files" "^4.1.0" +"@cosmos-kit/react@^2.20.1": + version "2.22.0" + resolved "https://registry.yarnpkg.com/@cosmos-kit/react/-/react-2.22.0.tgz#831980d3322e121c64f88c80b9e4593c0c5a9d93" + integrity sha512-boTQT4hMUYlcfCk9BqlbGA52T+OXU9djFlIYFyp6vryOWLpy53Jx0sTwsvViOzrMtcpSJGBnw1Zil1cneexu+A== + dependencies: + "@chain-registry/types" "^0.46.11" + "@cosmos-kit/core" "^2.16.0" + "@cosmos-kit/react-lite" "^2.16.0" + "@react-icons/all-files" "^4.1.0" + "@cspotcode/source-map-support@^0.8.0": version "0.8.1" resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1" @@ -1746,6 +1872,34 @@ dependencies: "@jridgewell/trace-mapping" "0.3.9" +"@csstools/color-helpers@^5.0.2": + version "5.0.2" + resolved "https://registry.yarnpkg.com/@csstools/color-helpers/-/color-helpers-5.0.2.tgz#82592c9a7c2b83c293d9161894e2a6471feb97b8" + integrity sha512-JqWH1vsgdGcw2RR6VliXXdA0/59LttzlU8UlRT/iUUsEeWfYq8I+K0yhihEUTTHLRm1EXvpsCx3083EU15ecsA== + +"@csstools/css-calc@^2.1.2": + version "2.1.2" + resolved "https://registry.yarnpkg.com/@csstools/css-calc/-/css-calc-2.1.2.tgz#bffd55f002dab119b76d4023f95cd943e6c8c11e" + integrity sha512-TklMyb3uBB28b5uQdxjReG4L80NxAqgrECqLZFQbyLekwwlcDDS8r3f07DKqeo8C4926Br0gf/ZDe17Zv4wIuw== + +"@csstools/css-color-parser@^3.0.8": + version "3.0.8" + resolved "https://registry.yarnpkg.com/@csstools/css-color-parser/-/css-color-parser-3.0.8.tgz#5fe9322920851450bf5e065c2b0e731b9e165394" + integrity sha512-pdwotQjCCnRPuNi06jFuP68cykU1f3ZWExLe/8MQ1LOs8Xq+fTkYgd+2V8mWUWMrOn9iS2HftPVaMZDaXzGbhQ== + dependencies: + "@csstools/color-helpers" "^5.0.2" + "@csstools/css-calc" "^2.1.2" + +"@csstools/css-parser-algorithms@^3.0.4": + version "3.0.4" + resolved "https://registry.yarnpkg.com/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.4.tgz#74426e93bd1c4dcab3e441f5cc7ba4fb35d94356" + integrity sha512-Up7rBoV77rv29d3uKHUIVubz1BTcgyUK72IvCQAbfbMv584xHcGKCKbWh7i8hPrRJ7qU4Y8IO3IY9m+iTB7P3A== + +"@csstools/css-tokenizer@^3.0.3": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@csstools/css-tokenizer/-/css-tokenizer-3.0.3.tgz#a5502c8539265fecbd873c1e395a890339f119c2" + integrity sha512-UJnjoFsmxfKUdNYdWgOB0mWUypuLvAfQPH1+pyvRJs6euowbFkFC6P13w1l8mJyi3vxYMxc9kld5jZEGRQs6bw== + "@dao-dao/cosmiframe@^1.0.0-rc.1": version "1.0.0-rc.1" resolved "https://registry.yarnpkg.com/@dao-dao/cosmiframe/-/cosmiframe-1.0.0-rc.1.tgz#51d4d1801605b8abfd01ae0425c797b6b876991e" @@ -1871,7 +2025,7 @@ resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.9.0.tgz#745969d649977776b43fc7648c556aaa462b4102" integrity sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ== -"@emotion/react@^11.4.1", "@emotion/react@^11.7.0": +"@emotion/react@^11.13.5", "@emotion/react@^11.4.1", "@emotion/react@^11.7.0": version "11.14.0" resolved "https://registry.yarnpkg.com/@emotion/react/-/react-11.14.0.tgz#cfaae35ebc67dd9ef4ea2e9acc6cd29e157dd05d" integrity sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA== @@ -1901,7 +2055,7 @@ resolved "https://registry.yarnpkg.com/@emotion/sheet/-/sheet-1.4.0.tgz#c9299c34d248bc26e82563735f78953d2efca83c" integrity sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg== -"@emotion/styled@^11.3.0", "@emotion/styled@^11.6.0": +"@emotion/styled@^11.13.5", "@emotion/styled@^11.3.0", "@emotion/styled@^11.6.0": version "11.14.0" resolved "https://registry.yarnpkg.com/@emotion/styled/-/styled-11.14.0.tgz#f47ca7219b1a295186d7661583376fcea95f0ff3" integrity sha512-XxfOnXFffatap2IyCeJyNov3kiDQWoR08gPUQxvbL7fxKryGBKUZUkG6Hz48DZwVrJSVh9sJboyV1Ds4OW6SgA== @@ -1933,14 +2087,14 @@ resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz#5e13fac887f08c44f76b0ccaf3370eb00fec9bb6" integrity sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg== -"@eslint-community/eslint-utils@^4.2.0": +"@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0": version "4.5.1" resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.5.1.tgz#b0fc7e06d0c94f801537fd4237edc2706d3b8e4c" integrity sha512-soEIOALTfTK6EjmKMMoLugwaP0rzkad90iIWd1hMO9ARkSAyjfMfkRRhLvD5qH7vvM0Cg72pieUfR6yh6XxC4w== dependencies: eslint-visitor-keys "^3.4.3" -"@eslint-community/regexpp@^4.4.0", "@eslint-community/regexpp@^4.6.1": +"@eslint-community/regexpp@^4.10.0", "@eslint-community/regexpp@^4.4.0", "@eslint-community/regexpp@^4.6.1": version "4.12.1" resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.12.1.tgz#cfc6cffe39df390a3841cde2abccf92eaa7ae0e0" integrity sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ== @@ -2328,6 +2482,38 @@ react-stately "^3.32.2" zustand "^4.5.5" +"@interchain-ui/react@^1.26.1": + version "1.26.3" + resolved "https://registry.yarnpkg.com/@interchain-ui/react/-/react-1.26.3.tgz#71c18d95a43eb57b2b3b2ecc256a24e4edc73cd9" + integrity sha512-kvGOZaSDJLNxtxieiyPMHOzxBGkpHR/BCpg/UEMDl2l7nLoPrygB/Oux1QEv/cwkw8GvepOBM7pzfJf8xk1IbQ== + dependencies: + "@floating-ui/core" "^1.6.7" + "@floating-ui/dom" "^1.6.10" + "@floating-ui/react" "^0.26.23" + "@floating-ui/react-dom" "^2.1.1" + "@floating-ui/utils" "^0.2.7" + "@formkit/auto-animate" "^0.8.2" + "@react-aria/listbox" "^3.13.3" + "@react-aria/overlays" "^3.23.2" + "@react-aria/utils" "^3.25.2" + "@tanstack/react-virtual" "^3.10.5" + "@vanilla-extract/css" "^1.15.5" + "@vanilla-extract/css-utils" "^0.1.4" + "@vanilla-extract/dynamic" "^2.1.2" + "@vanilla-extract/private" "^1.0.6" + "@vanilla-extract/recipes" "^0.5.5" + animejs "^3.2.2" + bignumber.js "^9.1.2" + client-only "^0.0.1" + clsx "^2.1.1" + copy-to-clipboard "^3.3.3" + immer "^10.1.1" + lodash "^4.17.21" + rainbow-sprinkles "^0.17.3" + react-aria "^3.34.3" + react-stately "^3.32.2" + zustand "^4.5.5" + "@internationalized/date@^3.7.0": version "3.7.0" resolved "https://registry.yarnpkg.com/@internationalized/date/-/date-3.7.0.tgz#23a4956308ee108e308517a7137c69ab8f5f2ad9" @@ -2932,6 +3118,11 @@ resolved "https://registry.yarnpkg.com/@mui/core-downloads-tracker/-/core-downloads-tracker-5.16.14.tgz#e6536f1b6caa873f7915fbf9703fdc840a5a98d9" integrity sha512-sbjXW+BBSvmzn61XyTMun899E7nGPTXwqD9drm1jBUAvWEhJpPFIRxwQQiATWZnd9rvdxtnhhdsDxEGWI0jxqA== +"@mui/core-downloads-tracker@^6.4.10": + version "6.4.10" + resolved "https://registry.yarnpkg.com/@mui/core-downloads-tracker/-/core-downloads-tracker-6.4.10.tgz#d01f0beda966f7a7d31400274b693d4ceb55747b" + integrity sha512-cblGjlM6+xsptwyaALw8RbRIUoqmKxOqLxlk2LkTDhxqUuql1YSOKKLH3w+Yd2QLz28b7MR65sx1OjsRZUfOSQ== + "@mui/icons-material@^5.0.0", "@mui/icons-material@^5.2.0": version "5.16.14" resolved "https://registry.yarnpkg.com/@mui/icons-material/-/icons-material-5.16.14.tgz#4fdecb05c15b1696f6f668fc29d549875544c892" @@ -2939,6 +3130,13 @@ dependencies: "@babel/runtime" "^7.23.9" +"@mui/icons-material@^5.16.11": + version "5.17.1" + resolved "https://registry.yarnpkg.com/@mui/icons-material/-/icons-material-5.17.1.tgz#2b14832473d4d3738d8194665af359377eb91752" + integrity sha512-CN86LocjkunFGG0yPlO4bgqHkNGgaEOEc3X/jG5Bzm401qYw79/SaLrofA7yAKCCXAGdIGnLoMHohc3+ubs95A== + dependencies: + "@babel/runtime" "^7.23.9" + "@mui/lab@5.0.0-alpha.170": version "5.0.0-alpha.170" resolved "https://registry.yarnpkg.com/@mui/lab/-/lab-5.0.0-alpha.170.tgz#4519dfc8d1c51ca54fb9d8b91b95a3733d07be16" @@ -2952,6 +3150,13 @@ clsx "^2.1.0" prop-types "^15.8.1" +"@mui/material-nextjs@^6.1.9": + version "6.4.3" + resolved "https://registry.yarnpkg.com/@mui/material-nextjs/-/material-nextjs-6.4.3.tgz#516eec4b6ca7d79f196f5c8b1b7f91e69e5c95ab" + integrity sha512-4ZRLrcD1HeWpvY8c7MrKYKuaUSobtvqcLYeEfGh/x5ezzPgKizhl7C0jpVVEgf6g+C9OgOGbhLTVfks7Y2IBAQ== + dependencies: + "@babel/runtime" "^7.26.0" + "@mui/material@^5.0.1", "@mui/material@^5.2.2": version "5.16.14" resolved "https://registry.yarnpkg.com/@mui/material/-/material-5.16.14.tgz#da8a75822f039d8c1b0ab7fb4146d767c2f9248a" @@ -2970,6 +3175,24 @@ react-is "^19.0.0" react-transition-group "^4.4.5" +"@mui/material@^6.1.10": + version "6.4.10" + resolved "https://registry.yarnpkg.com/@mui/material/-/material-6.4.10.tgz#9b9093cfd547f57e41862cdbd92636a22db07664" + integrity sha512-L1B0+Vg9NFjo3NcfODH3bohl6fIkzjyDBHBHb3Al4QI7owaJrFm2sSDyfz++iatzICug6U6q5tHLQrCLO71xkg== + dependencies: + "@babel/runtime" "^7.26.0" + "@mui/core-downloads-tracker" "^6.4.10" + "@mui/system" "^6.4.10" + "@mui/types" "~7.2.24" + "@mui/utils" "^6.4.9" + "@popperjs/core" "^2.11.8" + "@types/react-transition-group" "^4.4.12" + clsx "^2.1.1" + csstype "^3.1.3" + prop-types "^15.8.1" + react-is "^19.0.0" + react-transition-group "^4.4.5" + "@mui/private-theming@^5.16.14": version "5.16.14" resolved "https://registry.yarnpkg.com/@mui/private-theming/-/private-theming-5.16.14.tgz#7ad2b8a8fe0417f9fdfd05011806b9cb33c1a20a" @@ -2979,6 +3202,15 @@ "@mui/utils" "^5.16.14" prop-types "^15.8.1" +"@mui/private-theming@^6.4.9": + version "6.4.9" + resolved "https://registry.yarnpkg.com/@mui/private-theming/-/private-theming-6.4.9.tgz#0c1d65a638a1740aad0eb715d79e76471abe8175" + integrity sha512-LktcVmI5X17/Q5SkwjCcdOLBzt1hXuc14jYa7NPShog0GBDCDvKtcnP0V7a2s6EiVRlv7BzbWEJzH6+l/zaCxw== + dependencies: + "@babel/runtime" "^7.26.0" + "@mui/utils" "^6.4.9" + prop-types "^15.8.1" + "@mui/styled-engine@^5.16.14": version "5.16.14" resolved "https://registry.yarnpkg.com/@mui/styled-engine/-/styled-engine-5.16.14.tgz#f90fef5b4f8ebf11d48e1b1df8854a45bb31a9f5" @@ -2989,6 +3221,18 @@ csstype "^3.1.3" prop-types "^15.8.1" +"@mui/styled-engine@^6.4.9": + version "6.4.9" + resolved "https://registry.yarnpkg.com/@mui/styled-engine/-/styled-engine-6.4.9.tgz#d6d6b5f180466001284f897ff7d7010a8e329497" + integrity sha512-qZRWO0cT407NI4ZRjZcH+1SOu8f3JzLHqdMlg52GyEufM9pkSZFnf7xjpwnlvkixcGjco6wLlMD0VB43KRcBuA== + dependencies: + "@babel/runtime" "^7.26.0" + "@emotion/cache" "^11.13.5" + "@emotion/serialize" "^1.3.3" + "@emotion/sheet" "^1.4.0" + csstype "^3.1.3" + prop-types "^15.8.1" + "@mui/styles@^5.0.1", "@mui/styles@^5.2.2": version "5.16.14" resolved "https://registry.yarnpkg.com/@mui/styles/-/styles-5.16.14.tgz#a9482d0287ae83636c146817ba99f7dde6fcdaca" @@ -3026,6 +3270,20 @@ csstype "^3.1.3" prop-types "^15.8.1" +"@mui/system@^6.4.10": + version "6.4.10" + resolved "https://registry.yarnpkg.com/@mui/system/-/system-6.4.10.tgz#622eb2b73d94137e34abcd105d7708d67e80e70b" + integrity sha512-RyBGQwP3tgo4JEibK+RwVu1a6nQ6y8urMCNsb2aiN/nvTxxumq6P26aoG4GTUf8L4O1sthC4lMXlP4r8ixDkMg== + dependencies: + "@babel/runtime" "^7.26.0" + "@mui/private-theming" "^6.4.9" + "@mui/styled-engine" "^6.4.9" + "@mui/types" "~7.2.24" + "@mui/utils" "^6.4.9" + clsx "^2.1.1" + csstype "^3.1.3" + prop-types "^15.8.1" + "@mui/types@^7.2.14", "@mui/types@^7.2.15", "@mui/types@~7.2.24": version "7.2.24" resolved "https://registry.yarnpkg.com/@mui/types/-/types-7.2.24.tgz#5eff63129d9c29d80bbf2d2e561bd0690314dec2" @@ -3055,6 +3313,18 @@ prop-types "^15.8.1" react-is "^19.0.0" +"@mui/utils@^6.4.9": + version "6.4.9" + resolved "https://registry.yarnpkg.com/@mui/utils/-/utils-6.4.9.tgz#b0df01daa254c7c32a1a30b30a5179e19ef071a7" + integrity sha512-Y12Q9hbK9g+ZY0T3Rxrx9m2m10gaphDuUMgWxyV5kNJevVxXYCLclYUCC9vXaIk1/NdNDTcW2Yfr2OGvNFNmHg== + dependencies: + "@babel/runtime" "^7.26.0" + "@mui/types" "~7.2.24" + "@types/prop-types" "^15.7.14" + clsx "^2.1.1" + prop-types "^15.8.1" + react-is "^19.0.0" + "@mui/x-charts-vendor@7.20.0": version "7.20.0" resolved "https://registry.yarnpkg.com/@mui/x-charts-vendor/-/x-charts-vendor-7.20.0.tgz#b5858b91da0bde4f9c31f5360d05ade0b6eb5e31" @@ -3127,6 +3397,19 @@ prop-types "^15.8.1" react-transition-group "^4.4.5" +"@mui/x-date-pickers@^7.23.2": + version "7.28.3" + resolved "https://registry.yarnpkg.com/@mui/x-date-pickers/-/x-date-pickers-7.28.3.tgz#eaf28b86b50b6fb61d0c671ba4e3d89a1b199a57" + integrity sha512-5umKB/DIMfDN+FAlzcrocix9PpoJDJ+5hMdlby8spTPObP4wCSN+wkEhk0vFC7qE9FAWXr4wjemaKvsNf41cCw== + dependencies: + "@babel/runtime" "^7.25.7" + "@mui/utils" "^5.16.6 || ^6.0.0 || ^7.0.0 || ^7.0.0-beta" + "@mui/x-internals" "7.28.0" + "@types/react-transition-group" "^4.4.11" + clsx "^2.1.1" + prop-types "^15.8.1" + react-transition-group "^4.4.5" + "@mui/x-internals@7.28.0": version "7.28.0" resolved "https://registry.yarnpkg.com/@mui/x-internals/-/x-internals-7.28.0.tgz#b0a04f4c0f53f2f91d13a46f357f731b77c832c5" @@ -3162,6 +3445,11 @@ resolved "https://registry.yarnpkg.com/@next/env/-/env-14.2.21.tgz#09ff0813d29c596397e141205d4f5fd5c236bdd0" integrity sha512-lXcwcJd5oR01tggjWJ6SrNNYFGuOOMB9c251wUNkjCpkoXOPkDeF/15c3mnVlBqrW4JJXb2kVxDFhC4GduJt2A== +"@next/env@15.2.4": + version "15.2.4" + resolved "https://registry.yarnpkg.com/@next/env/-/env-15.2.4.tgz#060f8d8ddb02be5c825eab4ccd9ab619001efffb" + integrity sha512-+SFtMgoiYP3WoSswuNmxJOCwi06TdWE733D+WPjpXIe4LXGULwEaofiiAy6kbS0+XjM5xF5n3lKuBwN2SnqD9g== + "@next/eslint-plugin-next@14.1.4": version "14.1.4" resolved "https://registry.yarnpkg.com/@next/eslint-plugin-next/-/eslint-plugin-next-14.1.4.tgz#d7372b5ffede0e466af8af2ff534386418827fc8" @@ -3169,41 +3457,83 @@ dependencies: glob "10.3.10" +"@next/eslint-plugin-next@15.0.3": + version "15.0.3" + resolved "https://registry.yarnpkg.com/@next/eslint-plugin-next/-/eslint-plugin-next-15.0.3.tgz#ce953098036d462f6901e423cc6445fc165b78c4" + integrity sha512-3Ln/nHq2V+v8uIaxCR6YfYo7ceRgZNXfTd3yW1ukTaFbO+/I8jNakrjYWODvG9BuR2v5kgVtH/C8r0i11quOgw== + dependencies: + fast-glob "3.3.1" + "@next/swc-darwin-arm64@14.2.21": version "14.2.21" resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.21.tgz#32a31992aace1440981df9cf7cb3af7845d94fec" integrity sha512-HwEjcKsXtvszXz5q5Z7wCtrHeTTDSTgAbocz45PHMUjU3fBYInfvhR+ZhavDRUYLonm53aHZbB09QtJVJj8T7g== +"@next/swc-darwin-arm64@15.2.4": + version "15.2.4" + resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.2.4.tgz#3a54f67aa2e0096a9147bd24dff1492e151819ae" + integrity sha512-1AnMfs655ipJEDC/FHkSr0r3lXBgpqKo4K1kiwfUf3iE68rDFXZ1TtHdMvf7D0hMItgDZ7Vuq3JgNMbt/+3bYw== + "@next/swc-darwin-x64@14.2.21": version "14.2.21" resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.21.tgz#5ab4b3f6685b6b52f810d0f5cf6e471480ddffdb" integrity sha512-TSAA2ROgNzm4FhKbTbyJOBrsREOMVdDIltZ6aZiKvCi/v0UwFmwigBGeqXDA97TFMpR3LNNpw52CbVelkoQBxA== +"@next/swc-darwin-x64@15.2.4": + version "15.2.4" + resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-15.2.4.tgz#9b540f24afde1b7878623fdba9695344d26b7d67" + integrity sha512-3qK2zb5EwCwxnO2HeO+TRqCubeI/NgCe+kL5dTJlPldV/uwCnUgC7VbEzgmxbfrkbjehL4H9BPztWOEtsoMwew== + "@next/swc-linux-arm64-gnu@14.2.21": version "14.2.21" resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.21.tgz#8a0e1fa887aef19ca218af2af515d0a5ee67ba3f" integrity sha512-0Dqjn0pEUz3JG+AImpnMMW/m8hRtl1GQCNbO66V1yp6RswSTiKmnHf3pTX6xMdJYSemf3O4Q9ykiL0jymu0TuA== +"@next/swc-linux-arm64-gnu@15.2.4": + version "15.2.4" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.2.4.tgz#417a234c9f4dc5495094a8979859ac528c0f1f58" + integrity sha512-HFN6GKUcrTWvem8AZN7tT95zPb0GUGv9v0d0iyuTb303vbXkkbHDp/DxufB04jNVD+IN9yHy7y/6Mqq0h0YVaQ== + "@next/swc-linux-arm64-musl@14.2.21": version "14.2.21" resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.21.tgz#ddad844406b42fa8965fe11250abc85c1fe0fd05" integrity sha512-Ggfw5qnMXldscVntwnjfaQs5GbBbjioV4B4loP+bjqNEb42fzZlAaK+ldL0jm2CTJga9LynBMhekNfV8W4+HBw== +"@next/swc-linux-arm64-musl@15.2.4": + version "15.2.4" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.2.4.tgz#9bca76375508a175956f2d51f8547d0d6f9ffa64" + integrity sha512-Oioa0SORWLwi35/kVB8aCk5Uq+5/ZIumMK1kJV+jSdazFm2NzPDztsefzdmzzpx5oGCJ6FkUC7vkaUseNTStNA== + "@next/swc-linux-x64-gnu@14.2.21": version "14.2.21" resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.21.tgz#db55fd666f9ba27718f65caa54b622a912cdd16b" integrity sha512-uokj0lubN1WoSa5KKdThVPRffGyiWlm/vCc/cMkWOQHw69Qt0X1o3b2PyLLx8ANqlefILZh1EdfLRz9gVpG6tg== +"@next/swc-linux-x64-gnu@15.2.4": + version "15.2.4" + resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.2.4.tgz#c3d5041d53a5b228bf521ed49649e0f2a7aff947" + integrity sha512-yb5WTRaHdkgOqFOZiu6rHV1fAEK0flVpaIN2HB6kxHVSy/dIajWbThS7qON3W9/SNOH2JWkVCyulgGYekMePuw== + "@next/swc-linux-x64-musl@14.2.21": version "14.2.21" resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.21.tgz#dddb850353624efcd58c4c4e30ad8a1aab379642" integrity sha512-iAEBPzWNbciah4+0yI4s7Pce6BIoxTQ0AGCkxn/UBuzJFkYyJt71MadYQkjPqCQCJAFQ26sYh7MOKdU+VQFgPg== +"@next/swc-linux-x64-musl@15.2.4": + version "15.2.4" + resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.2.4.tgz#b2a51a108b1c412c69a504556cde0517631768c7" + integrity sha512-Dcdv/ix6srhkM25fgXiyOieFUkz+fOYkHlydWCtB0xMST6X9XYI3yPDKBZt1xuhOytONsIFJFB08xXYsxUwJLw== + "@next/swc-win32-arm64-msvc@14.2.21": version "14.2.21" resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.21.tgz#290012ee57b196d3d2d04853e6bf0179cae9fbaf" integrity sha512-plykgB3vL2hB4Z32W3ktsfqyuyGAPxqwiyrAi2Mr8LlEUhNn9VgkiAl5hODSBpzIfWweX3er1f5uNpGDygfQVQ== +"@next/swc-win32-arm64-msvc@15.2.4": + version "15.2.4" + resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.2.4.tgz#7d687b42512abd36f44c2c787d58a1590f174b69" + integrity sha512-dW0i7eukvDxtIhCYkMrZNQfNicPDExt2jPb9AZPpL7cfyUo7QSNl1DjsHjmmKp6qNAqUESyT8YFl/Aw91cNJJg== + "@next/swc-win32-ia32-msvc@14.2.21": version "14.2.21" resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.21.tgz#c959135a78cab18cca588d11d1e33bcf199590d4" @@ -3214,6 +3544,127 @@ resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.21.tgz#21ff892286555b90538a7d1b505ea21a005d6ead" integrity sha512-sT6+llIkzpsexGYZq8cjjthRyRGe5cJVhqh12FmlbxHqna6zsDDK8UNaV7g41T6atFHCJUPeLb3uyAwrBwy0NA== +"@next/swc-win32-x64-msvc@15.2.4": + version "15.2.4" + resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.2.4.tgz#779a0ea272fa4f509387f3b320e2d70803943a95" + integrity sha512-SbnWkJmkS7Xl3kre8SdMF6F/XDh1DTFEhp0jRTj/uB8iPKoU2bb2NDfcu+iifv1+mxQEd1g2vvSxcZbXSKyWiQ== + +"@nivo/annotations@0.88.0": + version "0.88.0" + resolved "https://registry.yarnpkg.com/@nivo/annotations/-/annotations-0.88.0.tgz#96830d735331dea2b60e66ce3971ef43b45cca8c" + integrity sha512-NXE+1oIUn+EGWMQpnpeRMLgi2wyuzhGDoJQY4OUHissCUiNotid2oNQ/PXJwN0toiu+/j9SyhzI32xr70OPi7Q== + dependencies: + "@nivo/colors" "0.88.0" + "@nivo/core" "0.88.0" + "@react-spring/web" "9.4.5 || ^9.7.2" + lodash "^4.17.21" + +"@nivo/axes@0.88.0": + version "0.88.0" + resolved "https://registry.yarnpkg.com/@nivo/axes/-/axes-0.88.0.tgz#b773efb217fc0faedbb5f8bc458f4b78820e2761" + integrity sha512-jF7aIxzTNayV5cI1J/b9Q1FfpMBxTXGk3OwSigXMSfYWlliskDn2u0qGRLiYhuXFdQAWIp4oXsO1GcAQ0eRVdg== + dependencies: + "@nivo/core" "0.88.0" + "@nivo/scales" "0.88.0" + "@react-spring/web" "9.4.5 || ^9.7.2" + "@types/d3-format" "^1.4.1" + "@types/d3-time-format" "^2.3.1" + d3-format "^1.4.4" + d3-time-format "^3.0.0" + +"@nivo/colors@0.88.0": + version "0.88.0" + resolved "https://registry.yarnpkg.com/@nivo/colors/-/colors-0.88.0.tgz#2790ac0607381800270f2902e4d957e65e2cad70" + integrity sha512-IZ+leYIqAlo7dyLHmsQwujanfRgXyoQ5H7PU3RWLEn1PP0zxDKLgEjFEDADpDauuslh2Tx0L81GNkWR6QSP0Mw== + dependencies: + "@nivo/core" "0.88.0" + "@types/d3-color" "^3.0.0" + "@types/d3-scale" "^4.0.8" + "@types/d3-scale-chromatic" "^3.0.0" + "@types/prop-types" "^15.7.2" + d3-color "^3.1.0" + d3-scale "^4.0.2" + d3-scale-chromatic "^3.0.0" + lodash "^4.17.21" + prop-types "^15.7.2" + +"@nivo/core@0.88.0": + version "0.88.0" + resolved "https://registry.yarnpkg.com/@nivo/core/-/core-0.88.0.tgz#ec79c7d63311473f15a463fd78274d9971a52848" + integrity sha512-XjUkA5MmwjLP38bdrJwn36Gj7T5SYMKD55LYQp/1nIJPdxqJ38dUfE4XyBDfIEgfP6yrHOihw3C63cUdnUBoiw== + dependencies: + "@nivo/tooltip" "0.88.0" + "@react-spring/web" "9.4.5 || ^9.7.2" + "@types/d3-shape" "^3.1.6" + d3-color "^3.1.0" + d3-format "^1.4.4" + d3-interpolate "^3.0.1" + d3-scale "^4.0.2" + d3-scale-chromatic "^3.0.0" + d3-shape "^3.2.0" + d3-time-format "^3.0.0" + lodash "^4.17.21" + prop-types "^15.7.2" + +"@nivo/legends@0.88.0": + version "0.88.0" + resolved "https://registry.yarnpkg.com/@nivo/legends/-/legends-0.88.0.tgz#a6007492048358a14bd061472a117ed34e7b0c81" + integrity sha512-d4DF9pHbD8LmGJlp/Gp1cF4e8y2wfQTcw3jVhbZj9zkb7ZWB7JfeF60VHRfbXNux9bjQ9U78/SssQqueVDPEmg== + dependencies: + "@nivo/colors" "0.88.0" + "@nivo/core" "0.88.0" + "@types/d3-scale" "^4.0.8" + d3-scale "^4.0.2" + +"@nivo/line@^0.88.0": + version "0.88.0" + resolved "https://registry.yarnpkg.com/@nivo/line/-/line-0.88.0.tgz#145b194f2c1bff2dd6071ab46fe114f9c4237811" + integrity sha512-hFTyZ3BdAZvq2HwdwMj2SJGUeodjEW+7DLtFMIIoVIxmjZlAs3z533HcJ9cJd3it928fDm8SF/rgHs0TztYf9Q== + dependencies: + "@nivo/annotations" "0.88.0" + "@nivo/axes" "0.88.0" + "@nivo/colors" "0.88.0" + "@nivo/core" "0.88.0" + "@nivo/legends" "0.88.0" + "@nivo/scales" "0.88.0" + "@nivo/tooltip" "0.88.0" + "@nivo/voronoi" "0.88.0" + "@react-spring/web" "9.4.5 || ^9.7.2" + d3-shape "^3.2.0" + +"@nivo/scales@0.88.0": + version "0.88.0" + resolved "https://registry.yarnpkg.com/@nivo/scales/-/scales-0.88.0.tgz#c69110b5ab504debb80a5a01a7824780af241ffb" + integrity sha512-HbpxkQp6tHCltZ1yDGeqdLcaJl5ze54NPjurfGtx/Uq+H5IQoBd4Tln49bUar5CsFAMsXw8yF1HQvASr7I1SIA== + dependencies: + "@types/d3-scale" "^4.0.8" + "@types/d3-time" "^1.1.1" + "@types/d3-time-format" "^3.0.0" + d3-scale "^4.0.2" + d3-time "^1.0.11" + d3-time-format "^3.0.0" + lodash "^4.17.21" + +"@nivo/tooltip@0.88.0": + version "0.88.0" + resolved "https://registry.yarnpkg.com/@nivo/tooltip/-/tooltip-0.88.0.tgz#b98348c0d617fea09c1bbddccc443c12ca697620" + integrity sha512-iEjVfQA8gumAzg/yUinjTwswygCkE5Iwuo8opwnrbpNIqMrleBV+EAKIgB0PrzepIoW8CFG/SJhoiRfbU8jhOw== + dependencies: + "@nivo/core" "0.88.0" + "@react-spring/web" "9.4.5 || ^9.7.2" + +"@nivo/voronoi@0.88.0": + version "0.88.0" + resolved "https://registry.yarnpkg.com/@nivo/voronoi/-/voronoi-0.88.0.tgz#029f984ccb6e72b415f398e1bd74c572960197ae" + integrity sha512-MyiNLvODthFoMjQ7Wjp693nogbTmVEx8Yn/7QkJhyPQbFyyA37TF/D1a/ox4h2OslXtP6K9QFN+42gB/zu7ixw== + dependencies: + "@nivo/core" "0.88.0" + "@nivo/tooltip" "0.88.0" + "@types/d3-delaunay" "^6.0.4" + "@types/d3-scale" "^4.0.8" + d3-delaunay "^6.0.4" + d3-scale "^4.0.2" + "@noble/hashes@^1", "@noble/hashes@^1.0.0", "@noble/hashes@^1.2.0": version "1.7.1" resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.7.1.tgz#5738f6d765710921e7a751e00c20ae091ed8db0f" @@ -3417,6 +3868,11 @@ resolved "https://registry.yarnpkg.com/@nymproject/contract-clients/-/contract-clients-1.2.4-rc.1.tgz#10b385f63d8b33beea6bf47d948fc7cc0c6c2357" integrity sha512-WMiEIHNxc9oTZ8GxAE8278ifpNJ2AIZHEw4JifN1WRhn2XViYaDDJaPCA3o0p+WGRySmqdurHmployr3iUEz1Q== +"@nymproject/contract-clients@^1.4.1": + version "1.4.1" + resolved "https://registry.yarnpkg.com/@nymproject/contract-clients/-/contract-clients-1.4.1.tgz#ae2644387b518eb13e8825fa8021d4c81ffe7852" + integrity sha512-HuJZ4Hv+Rl6ZZEtCHKgurNLJapM+QQRJlGkevFH2a4UdqUqF9omUkUi3AVes4679dPoSFgvA7plyVSDBdbgV6w== + "@nymproject/node-tester@^1.0.0", "@nymproject/node-tester@^1.2.3": version "1.2.3" resolved "https://registry.yarnpkg.com/@nymproject/node-tester/-/node-tester-1.2.3.tgz#79fbde8b69e2d1180eed897557c4610a1aa1038f" @@ -4313,7 +4769,7 @@ resolved "https://registry.yarnpkg.com/@react-spring/types/-/types-9.7.5.tgz#e5dd180f3ed985b44fd2cd2f32aa9203752ef3e8" integrity sha512-HVj7LrZ4ReHWBimBvu2SKND3cDVUPWKLqRTmWe/fNY6o1owGOX0cAHbdPDTMelgBlVbrTKrre6lFkhqGZErK/g== -"@react-spring/web@^9.7.5": +"@react-spring/web@9.4.5 || ^9.7.2", "@react-spring/web@^9.7.5": version "9.7.5" resolved "https://registry.yarnpkg.com/@react-spring/web/-/web-9.7.5.tgz#7d7782560b3a6fb9066b52824690da738605de80" integrity sha512-lmvqGwpe+CSttsWNZVr+Dg62adtKhauGwLyGE/RRyZ8AAMLgb9x3NDMA5RMElXo+IMyTkPp7nxTB8ZQlmhb6JQ== @@ -4860,7 +5316,7 @@ resolved "https://registry.yarnpkg.com/@rtsao/scc/-/scc-1.1.0.tgz#927dd2fae9bc3361403ac2c7a00c32ddce9ad7e8" integrity sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g== -"@rushstack/eslint-patch@^1.3.3": +"@rushstack/eslint-patch@^1.10.3", "@rushstack/eslint-patch@^1.3.3": version "1.11.0" resolved "https://registry.yarnpkg.com/@rushstack/eslint-patch/-/eslint-patch-1.11.0.tgz#75dce8e972f90bba488e2b0cc677fb233aa357ab" integrity sha512-zxnHvoMQVqewTJr/W4pKjF0bMGiKJv1WX7bSrkl46Hg0QjESbzBROWK0Wg4RphzSOS5Jiy7eFimmM3UgMrMZbQ== @@ -6009,11 +6465,18 @@ "@svgr/plugin-jsx" "^6.5.1" "@svgr/plugin-svgo" "^6.5.1" -"@swc/counter@^0.1.3": +"@swc/counter@0.1.3", "@swc/counter@^0.1.3": version "0.1.3" resolved "https://registry.yarnpkg.com/@swc/counter/-/counter-0.1.3.tgz#cc7463bd02949611c6329596fccd2b0ec782b0e9" integrity sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ== +"@swc/helpers@0.5.15", "@swc/helpers@^0.5.0": + version "0.5.15" + resolved "https://registry.yarnpkg.com/@swc/helpers/-/helpers-0.5.15.tgz#79efab344c5819ecf83a43f3f9f811fc84b516d7" + integrity sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g== + dependencies: + tslib "^2.8.0" + "@swc/helpers@0.5.5": version "0.5.5" resolved "https://registry.yarnpkg.com/@swc/helpers/-/helpers-0.5.5.tgz#12689df71bfc9b21c4f4ca00ae55f2f16c8b77c0" @@ -6022,13 +6485,6 @@ "@swc/counter" "^0.1.3" tslib "^2.4.0" -"@swc/helpers@^0.5.0": - version "0.5.15" - resolved "https://registry.yarnpkg.com/@swc/helpers/-/helpers-0.5.15.tgz#79efab344c5819ecf83a43f3f9f811fc84b516d7" - integrity sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g== - dependencies: - tslib "^2.8.0" - "@tanstack/match-sorter-utils@8.19.4": version "8.19.4" resolved "https://registry.yarnpkg.com/@tanstack/match-sorter-utils/-/match-sorter-utils-8.19.4.tgz#dacf772b5d94f4684f10dbeb2518cf72dccab8a5" @@ -6036,6 +6492,35 @@ dependencies: remove-accents "0.5.0" +"@tanstack/query-core@5.71.5": + version "5.71.5" + resolved "https://registry.yarnpkg.com/@tanstack/query-core/-/query-core-5.71.5.tgz#bb373ffca396fa70edb0bc9ef5722add6ba6f0b9" + integrity sha512-XOQ5SyjCdwhxyLksGKWSL5poqyEXYPDnsrZAzJm2LgrMm4Yh6VOrfC+IFosXreDw9HNqC11YAMY3HlfHjNzuaA== + +"@tanstack/query-devtools@5.71.5": + version "5.71.5" + resolved "https://registry.yarnpkg.com/@tanstack/query-devtools/-/query-devtools-5.71.5.tgz#65ba36c1589a88707a4985b8b0e7742171ba72e3" + integrity sha512-Fq1JeAp+I52Md/KnyeFxzG7G0RpdHgeOfDNhSPkZQs/JqqXuAfpUf+wFHDz+vP0GZbSnla2JmcLSQebOkIb1yA== + +"@tanstack/react-query-devtools@^5.64.2": + version "5.71.5" + resolved "https://registry.yarnpkg.com/@tanstack/react-query-devtools/-/react-query-devtools-5.71.5.tgz#9d34b9e7e5622bc5b0aafbe9149031ef87d7977f" + integrity sha512-VErQ29hgwmLrZjMmHp83iv+UdeCbqAyj/JR1mZjDbf+ghyJT+Hmh8jfbh6WYzKh8Fnej7uwl3nHbCsqd3VjoWw== + dependencies: + "@tanstack/query-devtools" "5.71.5" + +"@tanstack/react-query-next-experimental@^5.66.0": + version "5.71.8" + resolved "https://registry.yarnpkg.com/@tanstack/react-query-next-experimental/-/react-query-next-experimental-5.71.8.tgz#d7f390a5edce5bfc586a0badd1019f51a1a2840b" + integrity sha512-9Onh/EfSTk1p2EXpkX+G2dqhsMA89FGO3/5g4aX2EmBstZun582TSbEH2hyje1GTyEKPAnGDNdj5OCALWkTWUg== + +"@tanstack/react-query@^5.64.2": + version "5.71.5" + resolved "https://registry.yarnpkg.com/@tanstack/react-query/-/react-query-5.71.5.tgz#1018f083945ba5e2b91997eec9b9c767efce1e1b" + integrity sha512-WpxZWy4fDASjY+iAaXB+aY+LC95PQ34W6EWVkjJ0hdzWWbczFnr9nHvHkVDpwdR18I1NO8igNGQJFrLrgyzI8Q== + dependencies: + "@tanstack/query-core" "5.71.5" + "@tanstack/react-table@8.20.5": version "8.20.5" resolved "https://registry.yarnpkg.com/@tanstack/react-table/-/react-table-8.20.5.tgz#19987d101e1ea25ef5406dce4352cab3932449d8" @@ -6043,6 +6528,20 @@ dependencies: "@tanstack/table-core" "8.20.5" +"@tanstack/react-table@8.20.6": + version "8.20.6" + resolved "https://registry.yarnpkg.com/@tanstack/react-table/-/react-table-8.20.6.tgz#a1f3103327aa59aa621931f4087a7604a21054d0" + integrity sha512-w0jluT718MrOKthRcr2xsjqzx+oEM7B7s/XXyfs19ll++hlId3fjTm+B2zrR3ijpANpkzBAr15j1XGVOMxpggQ== + dependencies: + "@tanstack/table-core" "8.20.5" + +"@tanstack/react-table@^8.20.6": + version "8.21.2" + resolved "https://registry.yarnpkg.com/@tanstack/react-table/-/react-table-8.21.2.tgz#6a7fce828b64547e33f4606ada8114db496007cc" + integrity sha512-11tNlEDTdIhMJba2RBH+ecJ9l1zgS2kjmexDPAraulc8jeNA4xocSNeyzextT0XJyASil4XsCYlJmf5jEWAtYg== + dependencies: + "@tanstack/table-core" "8.21.2" + "@tanstack/react-virtual@3.10.6": version "3.10.6" resolved "https://registry.yarnpkg.com/@tanstack/react-virtual/-/react-virtual-3.10.6.tgz#f90f97d50a8d83dcd3c3a2d425aadbb55d4837db" @@ -6050,6 +6549,13 @@ dependencies: "@tanstack/virtual-core" "3.10.6" +"@tanstack/react-virtual@3.11.2": + version "3.11.2" + resolved "https://registry.yarnpkg.com/@tanstack/react-virtual/-/react-virtual-3.11.2.tgz#d6b9bd999c181f0a2edce270c87a2febead04322" + integrity sha512-OuFzMXPF4+xZgx8UzJha0AieuMihhhaWG0tCqpp6tDzlFwOmNBPYMuLOtMJ1Tr4pXLHmgjcWhG6RlknY2oNTdQ== + dependencies: + "@tanstack/virtual-core" "3.11.2" + "@tanstack/react-virtual@^3.10.5": version "3.13.4" resolved "https://registry.yarnpkg.com/@tanstack/react-virtual/-/react-virtual-3.13.4.tgz#1188d4e452c8c7569cf42d0f605018ed67941e5c" @@ -6062,11 +6568,21 @@ resolved "https://registry.yarnpkg.com/@tanstack/table-core/-/table-core-8.20.5.tgz#3974f0b090bed11243d4107283824167a395cf1d" integrity sha512-P9dF7XbibHph2PFRz8gfBKEXEY/HJPOhym8CHmjF8y3q5mWpKx9xtZapXQUWCgkqvsK0R46Azuz+VaxD4Xl+Tg== +"@tanstack/table-core@8.21.2": + version "8.21.2" + resolved "https://registry.yarnpkg.com/@tanstack/table-core/-/table-core-8.21.2.tgz#dd57595a1773652bb6fb437e90a5f5386a49fd7e" + integrity sha512-uvXk/U4cBiFMxt+p9/G7yUWI/UbHYbyghLCjlpWZ3mLeIZiUBSKcUnw9UnKkdRz7Z/N4UBuFLWQdJCjUe7HjvA== + "@tanstack/virtual-core@3.10.6": version "3.10.6" resolved "https://registry.yarnpkg.com/@tanstack/virtual-core/-/virtual-core-3.10.6.tgz#babe3989b2344a5f12fc64129f9bbed5d3402999" integrity sha512-1giLc4dzgEKLMx5pgKjL6HlG5fjZMgCjzlKAlpr7yoUtetVPELgER1NtephAI910nMwfPTHNyWKSFmJdHkz2Cw== +"@tanstack/virtual-core@3.11.2": + version "3.11.2" + resolved "https://registry.yarnpkg.com/@tanstack/virtual-core/-/virtual-core-3.11.2.tgz#00409e743ac4eea9afe5b7708594d5fcebb00212" + integrity sha512-vTtpNt7mKCiZ1pwU9hfKPhpdVO2sVzFQsxoVBGtOSHxlrRRzYr8iQ2TlwbAcRYCcEiZ9ECAM8kBzH0v2+VzfKw== + "@tanstack/virtual-core@3.13.4": version "3.13.4" resolved "https://registry.yarnpkg.com/@tanstack/virtual-core/-/virtual-core-3.13.4.tgz#31ac7710d102ed59463f922ab088dde5a9e62010" @@ -6161,10 +6677,10 @@ dependencies: "@tauri-apps/api" "^2.0.0" -"@tauri-apps/plugin-updater@^2.6.1": - version "2.6.1" - resolved "https://registry.yarnpkg.com/@tauri-apps/plugin-updater/-/plugin-updater-2.6.1.tgz#ce6665c1ab77fff96092283702dbe2777c3b295d" - integrity sha512-iiOevw4kc12Ok99J9KthXwUqwPv1sYjG+tNEDZqPmwvOmIq7s58nKMRz6NJPKXT4U16NzMPffFcP/LUOsz6c4A== +"@tauri-apps/plugin-updater@^2.0.0": + version "2.7.0" + resolved "https://registry.yarnpkg.com/@tauri-apps/plugin-updater/-/plugin-updater-2.7.0.tgz#a4390bdfd858106bdc6cc3d05d52efc9e0528ea3" + integrity sha512-oBug5UCH2wOsoYk0LW5LEMAT51mszjg11s8eungRH26x/qOrEjLvnuJJoxVVr9nsWowJ6vnpXKS+lUMfFTlvHQ== dependencies: "@tauri-apps/api" "^2.0.0" @@ -6364,7 +6880,7 @@ resolved "https://registry.yarnpkg.com/@types/d3-array/-/d3-array-3.2.1.tgz#1f6658e3d2006c4fceac53fde464166859f8b8c5" integrity sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg== -"@types/d3-color@*", "@types/d3-color@^3.1.3": +"@types/d3-color@*", "@types/d3-color@^3.0.0", "@types/d3-color@^3.1.3": version "3.1.3" resolved "https://registry.yarnpkg.com/@types/d3-color/-/d3-color-3.1.3.tgz#368c961a18de721da8200e80bf3943fb53136af2" integrity sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A== @@ -6396,6 +6912,11 @@ dependencies: "@types/d3-dsv" "*" +"@types/d3-format@^1.4.1": + version "1.4.5" + resolved "https://registry.yarnpkg.com/@types/d3-format/-/d3-format-1.4.5.tgz#6392303c2ca3c287c3a1a2046455cd0a0bd50bbe" + integrity sha512-mLxrC1MSWupOSncXN/HOlWUAAIffAEBaI4+PKy2uMPsKe4FNZlk7qrbTjmzJXITQQqBHivaks4Td18azgqnotA== + "@types/d3-geo@^1": version "1.12.7" resolved "https://registry.yarnpkg.com/@types/d3-geo/-/d3-geo-1.12.7.tgz#595680bf9b9525031cef956ed80baf38b8f3b4be" @@ -6429,6 +6950,11 @@ resolved "https://registry.yarnpkg.com/@types/d3-path/-/d3-path-3.1.1.tgz#f632b380c3aca1dba8e34aa049bcd6a4af23df8a" integrity sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg== +"@types/d3-scale-chromatic@^3.0.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@types/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz#dc6d4f9a98376f18ea50bad6c39537f1b5463c39" + integrity sha512-iWMJgwkK7yTRmWqRB5plb1kadXyQ5Sj8V/zYlFGMUBbIPKQScw+Dku9cAAMgJG+z5GYDoMjWGLVOvjghDEFnKQ== + "@types/d3-scale@^4.0.1", "@types/d3-scale@^4.0.2", "@types/d3-scale@^4.0.8": version "4.0.9" resolved "https://registry.yarnpkg.com/@types/d3-scale/-/d3-scale-4.0.9.tgz#57a2f707242e6fe1de81ad7bfcccaaf606179afb" @@ -6448,11 +6974,26 @@ dependencies: "@types/d3-path" "*" +"@types/d3-time-format@^2.3.1": + version "2.3.4" + resolved "https://registry.yarnpkg.com/@types/d3-time-format/-/d3-time-format-2.3.4.tgz#544af5184df8b3fc4d9b42b14058789acee2905e" + integrity sha512-xdDXbpVO74EvadI3UDxjxTdR6QIxm1FKzEA/+F8tL4GWWUg/hgvBqf6chql64U5A9ZUGWo7pEu4eNlyLwbKdhg== + +"@types/d3-time-format@^3.0.0": + version "3.0.4" + resolved "https://registry.yarnpkg.com/@types/d3-time-format/-/d3-time-format-3.0.4.tgz#f972bdd7be1048184577cf235a44721a78c6bb4b" + integrity sha512-or9DiDnYI1h38J9hxKEsw513+KVuFbEVhl7qdxcaudoiqWWepapUen+2vAriFGexr6W5+P4l9+HJrB39GG+oRg== + "@types/d3-time@*", "@types/d3-time@^3.0.0", "@types/d3-time@^3.0.3": version "3.0.4" resolved "https://registry.yarnpkg.com/@types/d3-time/-/d3-time-3.0.4.tgz#8472feecd639691450dd8000eb33edd444e1323f" integrity sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g== +"@types/d3-time@^1.1.1": + version "1.1.4" + resolved "https://registry.yarnpkg.com/@types/d3-time/-/d3-time-1.1.4.tgz#20da4b75c537a940e7319b75717c67a2e499515a" + integrity sha512-JIvy2HjRInE+TXOmIGN5LCmeO0hkFZx5f9FZ7kiN+D+YTcc8pptsiLiuHsvwxwC7VVKmJ2ExHUgNlAiV7vQM9g== + "@types/d3-timer@^3.0.0": version "3.0.2" resolved "https://registry.yarnpkg.com/@types/d3-timer/-/d3-timer-3.0.2.tgz#70bbda77dc23aa727413e22e214afa3f0e852f70" @@ -6466,6 +7007,13 @@ "@types/d3-interpolate" "^1" "@types/d3-selection" "^1" +"@types/debug@^4.0.0": + version "4.1.12" + resolved "https://registry.yarnpkg.com/@types/debug/-/debug-4.1.12.tgz#a155f21690871953410df4b6b6f53187f0500917" + integrity sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ== + dependencies: + "@types/ms" "*" + "@types/eslint-scope@^3.7.7": version "3.7.7" resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.7.tgz#3108bd5f18b0cdb277c867b3dd449c9ed7079ac5" @@ -6482,6 +7030,13 @@ "@types/estree" "*" "@types/json-schema" "*" +"@types/estree-jsx@^1.0.0": + version "1.0.5" + resolved "https://registry.yarnpkg.com/@types/estree-jsx/-/estree-jsx-1.0.5.tgz#858a88ea20f34fe65111f005a689fa1ebf70dc18" + integrity sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg== + dependencies: + "@types/estree" "*" + "@types/estree@*", "@types/estree@^1.0.0", "@types/estree@^1.0.6": version "1.0.6" resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.6.tgz#628effeeae2064a1b4e79f78e81d87b7e5fc7b50" @@ -6579,6 +7134,13 @@ dependencies: "@types/unist" "^2" +"@types/hast@^3.0.0": + version "3.0.4" + resolved "https://registry.yarnpkg.com/@types/hast/-/hast-3.0.4.tgz#1d6b39993b82cea6ad783945b0508c25903e15aa" + integrity sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ== + dependencies: + "@types/unist" "*" + "@types/html-minifier-terser@^5.0.0": version "5.1.2" resolved "https://registry.yarnpkg.com/@types/html-minifier-terser/-/html-minifier-terser-5.1.2.tgz#693b316ad323ea97eed6b38ed1a3cc02b1672b57" @@ -6668,6 +7230,13 @@ dependencies: "@types/unist" "^2" +"@types/mdast@^4.0.0": + version "4.0.4" + resolved "https://registry.yarnpkg.com/@types/mdast/-/mdast-4.0.4.tgz#7ccf72edd2f1aa7dd3437e180c64373585804dd6" + integrity sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA== + dependencies: + "@types/unist" "*" + "@types/mime@^1": version "1.3.5" resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.5.tgz#1ef302e01cf7d2b5a0fa526790c9123bf1d06690" @@ -6688,6 +7257,11 @@ resolved "https://registry.yarnpkg.com/@types/minimist/-/minimist-1.2.5.tgz#ec10755e871497bcd83efe927e43ec46e8c0747e" integrity sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag== +"@types/ms@*": + version "2.1.0" + resolved "https://registry.yarnpkg.com/@types/ms/-/ms-2.1.0.tgz#052aa67a48eccc4309d7f0191b7e41434b90bb78" + integrity sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA== + "@types/node-fetch@^2.5.7": version "2.6.12" resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.6.12.tgz#8ab5c3ef8330f13100a7479e2cd56d3386830a03" @@ -6764,7 +7338,7 @@ resolved "https://registry.yarnpkg.com/@types/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz#ee1bd8c9f7a01b3445786aad0ef23aba5f511a44" integrity sha512-nj39q0wAIdhwn7DGUyT9irmsKK1tV0bd5WFEhgpqNTMFZ8cE+jieuTphCW0tfdm47S2zVT5mr09B28b1chmQMA== -"@types/prop-types@*", "@types/prop-types@^15.7.12", "@types/prop-types@^15.7.14": +"@types/prop-types@*", "@types/prop-types@^15.7.12", "@types/prop-types@^15.7.14", "@types/prop-types@^15.7.2": version "15.7.14" resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.14.tgz#1433419d73b2a7ebfc6918dcefd2ec0d5cd698f2" integrity sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ== @@ -6776,7 +7350,7 @@ dependencies: "@types/react" "*" -"@types/qs@*", "@types/qs@^6.9.5": +"@types/qs@*", "@types/qs@^6.9.18", "@types/qs@^6.9.5": version "6.9.18" resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.18.tgz#877292caa91f7c1b213032b34626505b746624c2" integrity sha512-kK7dgTYDyGqS+e2Q4aK9X3D7q234CIZ1Bv0q/7Z5IwRDoADNU81xXJK/YVyLbLTZCoIwUoDoffFeF+p/eIklAA== @@ -6813,7 +7387,7 @@ dependencies: react-tooltip "*" -"@types/react-transition-group@^4.4.10", "@types/react-transition-group@^4.4.11": +"@types/react-transition-group@^4.4.10", "@types/react-transition-group@^4.4.11", "@types/react-transition-group@^4.4.12": version "4.4.12" resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-4.4.12.tgz#b5d76568485b02a307238270bfe96cb51ee2a044" integrity sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w== @@ -6916,6 +7490,11 @@ dependencies: "@types/geojson" "*" +"@types/trusted-types@^2.0.7": + version "2.0.7" + resolved "https://registry.yarnpkg.com/@types/trusted-types/-/trusted-types-2.0.7.tgz#baccb07a970b91707df3a3e8ba6896c57ead2d11" + integrity sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw== + "@types/uglify-js@*": version "3.17.5" resolved "https://registry.yarnpkg.com/@types/uglify-js/-/uglify-js-3.17.5.tgz#905ce03a3cbbf2e31cbefcbc68d15497ee2e17df" @@ -6923,6 +7502,11 @@ dependencies: source-map "^0.6.1" +"@types/unist@*", "@types/unist@^3.0.0": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@types/unist/-/unist-3.0.3.tgz#acaab0f919ce69cce629c2d4ed2eb4adc1b6c20c" + integrity sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q== + "@types/unist@^2", "@types/unist@^2.0.0", "@types/unist@^2.0.2", "@types/unist@^2.0.3": version "2.0.11" resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.11.tgz#11af57b127e32487774841f7a4e54eab166d03c4" @@ -7013,6 +7597,21 @@ semver "^7.3.7" tsutils "^3.21.0" +"@typescript-eslint/eslint-plugin@^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0": + version "8.29.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.29.0.tgz#151c4878700a5ad229ce6713d2674d58b626b3d9" + integrity sha512-PAIpk/U7NIS6H7TEtN45SPGLQaHNgB7wSjsQV/8+KYokAb2T/gloOA/Bee2yd4/yKVhPKe5LlaUGhAZk5zmSaQ== + dependencies: + "@eslint-community/regexpp" "^4.10.0" + "@typescript-eslint/scope-manager" "8.29.0" + "@typescript-eslint/type-utils" "8.29.0" + "@typescript-eslint/utils" "8.29.0" + "@typescript-eslint/visitor-keys" "8.29.0" + graphemer "^1.4.0" + ignore "^5.3.1" + natural-compare "^1.4.0" + ts-api-utils "^2.0.1" + "@typescript-eslint/experimental-utils@^5.3.0": version "5.62.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-5.62.0.tgz#14559bf73383a308026b427a4a6129bae2146741" @@ -7041,6 +7640,17 @@ "@typescript-eslint/visitor-keys" "6.21.0" debug "^4.3.4" +"@typescript-eslint/parser@^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0": + version "8.29.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.29.0.tgz#b98841e0a8099728cb8583da92326fcb7f5be1d2" + integrity sha512-8C0+jlNJOwQso2GapCVWWfW/rzaq7Lbme+vGUFKE31djwNncIpgXD7Cd4weEsDdkoZDjH0lwwr3QDQFuyrMg9g== + dependencies: + "@typescript-eslint/scope-manager" "8.29.0" + "@typescript-eslint/types" "8.29.0" + "@typescript-eslint/typescript-estree" "8.29.0" + "@typescript-eslint/visitor-keys" "8.29.0" + debug "^4.3.4" + "@typescript-eslint/scope-manager@5.62.0": version "5.62.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz#d9457ccc6a0b8d6b37d0eb252a23022478c5460c" @@ -7057,6 +7667,14 @@ "@typescript-eslint/types" "6.21.0" "@typescript-eslint/visitor-keys" "6.21.0" +"@typescript-eslint/scope-manager@8.29.0": + version "8.29.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.29.0.tgz#8fd9872823aef65ff71d3f6d1ec9316ace0b6bf3" + integrity sha512-aO1PVsq7Gm+tcghabUpzEnVSFMCU4/nYIgC2GOatJcllvWfnhrgW0ZEbnTxm36QsikmCN1K/6ZgM7fok2I7xNw== + dependencies: + "@typescript-eslint/types" "8.29.0" + "@typescript-eslint/visitor-keys" "8.29.0" + "@typescript-eslint/type-utils@5.62.0": version "5.62.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.62.0.tgz#286f0389c41681376cdad96b309cedd17d70346a" @@ -7067,6 +7685,16 @@ debug "^4.3.4" tsutils "^3.21.0" +"@typescript-eslint/type-utils@8.29.0": + version "8.29.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.29.0.tgz#98dcfd1193cb4e2b2d0294a8656ce5eb58c443a9" + integrity sha512-ahaWQ42JAOx+NKEf5++WC/ua17q5l+j1GFrbbpVKzFL/tKVc0aYY8rVSYUpUvt2hUP1YBr7mwXzx+E/DfUWI9Q== + dependencies: + "@typescript-eslint/typescript-estree" "8.29.0" + "@typescript-eslint/utils" "8.29.0" + debug "^4.3.4" + ts-api-utils "^2.0.1" + "@typescript-eslint/types@5.62.0": version "5.62.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.62.0.tgz#258607e60effa309f067608931c3df6fed41fd2f" @@ -7077,6 +7705,11 @@ resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-6.21.0.tgz#205724c5123a8fef7ecd195075fa6e85bac3436d" integrity sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg== +"@typescript-eslint/types@8.29.0": + version "8.29.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.29.0.tgz#65add70ab4ef66beaa42a5addf87dab2b05b1f33" + integrity sha512-wcJL/+cOXV+RE3gjCyl/V2G877+2faqvlgtso/ZRbTCnZazh0gXhe+7gbAnfubzN2bNsBtZjDvlh7ero8uIbzg== + "@typescript-eslint/typescript-estree@5.62.0": version "5.62.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz#7d17794b77fabcac615d6a48fb143330d962eb9b" @@ -7104,6 +7737,20 @@ semver "^7.5.4" ts-api-utils "^1.0.1" +"@typescript-eslint/typescript-estree@8.29.0": + version "8.29.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.29.0.tgz#d201a4f115327ec90496307c9958262285065b00" + integrity sha512-yOfen3jE9ISZR/hHpU/bmNvTtBW1NjRbkSFdZOksL1N+ybPEE7UVGMwqvS6CP022Rp00Sb0tdiIkhSCe6NI8ow== + dependencies: + "@typescript-eslint/types" "8.29.0" + "@typescript-eslint/visitor-keys" "8.29.0" + debug "^4.3.4" + fast-glob "^3.3.2" + is-glob "^4.0.3" + minimatch "^9.0.4" + semver "^7.6.0" + ts-api-utils "^2.0.1" + "@typescript-eslint/utils@5.62.0", "@typescript-eslint/utils@^5.10.0": version "5.62.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.62.0.tgz#141e809c71636e4a75daa39faed2fb5f4b10df86" @@ -7118,6 +7765,16 @@ eslint-scope "^5.1.1" semver "^7.3.7" +"@typescript-eslint/utils@8.29.0": + version "8.29.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.29.0.tgz#d6d22b19c8c4812a874f00341f686b45b9fe895f" + integrity sha512-gX/A0Mz9Bskm8avSWFcK0gP7cZpbY4AIo6B0hWYFCaIsz750oaiWR4Jr2CI+PQhfW1CpcQr9OlfPS+kMFegjXA== + dependencies: + "@eslint-community/eslint-utils" "^4.4.0" + "@typescript-eslint/scope-manager" "8.29.0" + "@typescript-eslint/types" "8.29.0" + "@typescript-eslint/typescript-estree" "8.29.0" + "@typescript-eslint/visitor-keys@5.62.0": version "5.62.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz#2174011917ce582875954ffe2f6912d5931e353e" @@ -7134,7 +7791,20 @@ "@typescript-eslint/types" "6.21.0" eslint-visitor-keys "^3.4.1" -"@ungap/structured-clone@^1.2.0": +"@typescript-eslint/visitor-keys@8.29.0": + version "8.29.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.29.0.tgz#2356336c9efdc3597ffcd2aa1ce95432852b743d" + integrity sha512-Sne/pVz8ryR03NFK21VpN88dZ2FdQXOlq3VIklbrTYEt8yXtRFr9tvUhqvCeKjqYk5FSim37sHbooT6vzBTZcg== + dependencies: + "@typescript-eslint/types" "8.29.0" + eslint-visitor-keys "^4.2.0" + +"@uidotdev/usehooks@^2.4.1": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@uidotdev/usehooks/-/usehooks-2.4.1.tgz#4b733eaeae09a7be143c6c9ca158b56cc1ea75bf" + integrity sha512-1I+RwWyS+kdv3Mv0Vmc+p0dPYH0DTRAo04HLyXReYBL9AeseDWUJyi4THuksBJcu9F0Pih69Ak150VDnqbVnXg== + +"@ungap/structured-clone@^1.0.0", "@ungap/structured-clone@^1.2.0": version "1.3.0" resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.3.0.tgz#d06bbb384ebcf6c505fde1c3d0ed4ddffe0aaff8" integrity sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g== @@ -7716,6 +8386,11 @@ agent-base@6, agent-base@^6.0.2: dependencies: debug "4" +agent-base@^7.1.0, agent-base@^7.1.2: + version "7.1.3" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.3.tgz#29435eb821bc4194633a5b89e5bc4703bafc25a1" + integrity sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw== + agentkeepalive@^4.2.1: version "4.6.0" resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.6.0.tgz#35f73e94b3f40bf65f105219c623ad19c136ea6a" @@ -8435,6 +9110,11 @@ bail@^1.0.0: resolved "https://registry.yarnpkg.com/bail/-/bail-1.0.5.tgz#b6fa133404a392cbc1f8c4bf63f5953351e7a776" integrity sha512-xFbRxM1tahm08yHBP16MMjVUAvDaBMD38zsM9EMAUN61omwLmKlOpB/Zku5QkjZ8TZ4vn53pj+t518cH0S03RQ== +bail@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/bail/-/bail-2.0.2.tgz#d26f5cd8fe5d6f832a31517b9f7c356040ba6d5d" + integrity sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw== + balanced-match@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" @@ -9105,6 +9785,11 @@ ccount@^1.0.0: resolved "https://registry.yarnpkg.com/ccount/-/ccount-1.1.0.tgz#246687debb6014735131be8abab2d93898f8d043" integrity sha512-vlNK021QdI7PNeiUh/lKkC/mNHHfV0m/Ad5JoI0TYtlBnJAslM/JIkm/tGC88bkLIwO6OQ5uV6ztS6kVAtCDlg== +ccount@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/ccount/-/ccount-2.0.1.tgz#17a3bf82302e0870d6da43a01311a8bc02a3ecf5" + integrity sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg== + chain-registry@^1.29.1: version "1.69.155" resolved "https://registry.yarnpkg.com/chain-registry/-/chain-registry-1.69.155.tgz#72077fba2a0e39cf241854aca6745f585261d032" @@ -9112,6 +9797,13 @@ chain-registry@^1.29.1: dependencies: "@chain-registry/types" "^0.50.94" +chain-registry@^1.69.64: + version "1.69.173" + resolved "https://registry.yarnpkg.com/chain-registry/-/chain-registry-1.69.173.tgz#21baa0aa8900b3a76c837f09482ebba4765f5c4c" + integrity sha512-OLWL2yPvh/UUZ5U3KSC9eePG5HdTLku2Wv5wUNBowXcgtUk0dAY1Pz/ACnqQ5NH8bhzS9+Sb9RkDpehogOukow== + dependencies: + "@chain-registry/types" "^0.50.107" + chalk@4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.0.tgz#4e14870a618d9e2edd97dd8345fd9d9dc315646a" @@ -9150,21 +9842,41 @@ char-regex@^1.0.2: resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf" integrity sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw== +character-entities-html4@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/character-entities-html4/-/character-entities-html4-2.1.0.tgz#1f1adb940c971a4b22ba39ddca6b618dc6e56b2b" + integrity sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA== + character-entities-legacy@^1.0.0: version "1.1.4" resolved "https://registry.yarnpkg.com/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz#94bc1845dce70a5bb9d2ecc748725661293d8fc1" integrity sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA== +character-entities-legacy@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz#76bc83a90738901d7bc223a9e93759fdd560125b" + integrity sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ== + character-entities@^1.0.0: version "1.2.4" resolved "https://registry.yarnpkg.com/character-entities/-/character-entities-1.2.4.tgz#e12c3939b7eaf4e5b15e7ad4c5e28e1d48c5b16b" integrity sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw== +character-entities@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/character-entities/-/character-entities-2.0.2.tgz#2d09c2e72cd9523076ccb21157dff66ad43fcc22" + integrity sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ== + character-reference-invalid@^1.0.0: version "1.1.4" resolved "https://registry.yarnpkg.com/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz#083329cda0eae272ab3dbbf37e9a382c13af1560" integrity sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg== +character-reference-invalid@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz#85c66b041e43b47210faf401278abf808ac45cb9" + integrity sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw== + chardet@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" @@ -9264,6 +9976,11 @@ classnames@^2.3.0: resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.5.1.tgz#ba774c614be0f016da105c858e7159eae8e7687b" integrity sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow== +cldr-compact-number@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/cldr-compact-number/-/cldr-compact-number-0.4.0.tgz#d74bb8cedab92ca63032423a1bd26aaaf76b9b2c" + integrity sha512-f/Wz46qoQrfhF2Mq4TcsSbIXMaB70LhioR4fJAbYi5nTnczh1LnSsWuBOXuyKTwFsyrMCRnPFr0OHjl0XB6y9A== + clean-css@^4.2.3: version "4.2.4" resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-4.2.4.tgz#733bf46eba4e607c6891ea57c24a989356831178" @@ -9496,6 +10213,11 @@ comma-separated-tokens@^1.0.0: resolved "https://registry.yarnpkg.com/comma-separated-tokens/-/comma-separated-tokens-1.0.8.tgz#632b80b6117867a158f1080ad498b2fbe7e3f5ea" integrity sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw== +comma-separated-tokens@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz#4e89c9458acb61bc8fef19f4529973b2392839ee" + integrity sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg== + commander@2, commander@^2.19.0, commander@^2.20.0: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" @@ -10018,6 +10740,17 @@ css-select@^4.1.3: domutils "^2.8.0" nth-check "^2.0.1" +css-select@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/css-select/-/css-select-5.1.0.tgz#b8ebd6554c3637ccc76688804ad3f6a6fdaea8a6" + integrity sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg== + dependencies: + boolbase "^1.0.0" + css-what "^6.1.0" + domhandler "^5.0.2" + domutils "^3.0.1" + nth-check "^2.0.1" + css-tree@^1.1.2, css-tree@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.1.3.tgz#eb4870fb6fd7707327ec95c2ff2ab09b5e8db91d" @@ -10026,6 +10759,22 @@ css-tree@^1.1.2, css-tree@^1.1.3: mdn-data "2.0.14" source-map "^0.6.1" +css-tree@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-2.3.1.tgz#10264ce1e5442e8572fc82fbe490644ff54b5c20" + integrity sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw== + dependencies: + mdn-data "2.0.30" + source-map-js "^1.0.1" + +css-tree@~2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-2.2.1.tgz#36115d382d60afd271e377f9c5f67d02bd48c032" + integrity sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA== + dependencies: + mdn-data "2.0.28" + source-map-js "^1.0.1" + css-vendor@^2.0.8: version "2.0.8" resolved "https://registry.yarnpkg.com/css-vendor/-/css-vendor-2.0.8.tgz#e47f91d3bd3117d49180a3c935e62e3d9f7f449d" @@ -10105,6 +10854,13 @@ csso@^4.2.0: dependencies: css-tree "^1.1.2" +csso@^5.0.5: + version "5.0.5" + resolved "https://registry.yarnpkg.com/csso/-/csso-5.0.5.tgz#f9b7fe6cc6ac0b7d90781bb16d5e9874303e2ca6" + integrity sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ== + dependencies: + css-tree "~2.2.0" + cssom@^0.4.4: version "0.4.4" resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.4.4.tgz#5a66cf93d2d0b661d80bf6a44fb65f5c2e4e0a10" @@ -10122,6 +10878,14 @@ cssstyle@^2.3.0: dependencies: cssom "~0.3.6" +cssstyle@^4.2.1: + version "4.3.0" + resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-4.3.0.tgz#83db22d1aec8eb7e5ecd812b4d14a17fb3dd243d" + integrity sha512-6r0NiY0xizYqfBvWp1G7WXJ06/bZyrk7Dc6PHql82C/pKGUTKu4yAX4Y8JPamb1ob9nBKuxWzCGTRuGwU3yxJQ== + dependencies: + "@asamuzakjp/css-color" "^3.1.1" + rrweb-cssom "^0.8.0" + csstype@^3.0.2, csstype@^3.0.7, csstype@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.3.tgz#d80ff294d114fb0e6ac500fbf85b60137d7eff81" @@ -10139,6 +10903,13 @@ cyclist@^1.0.1: resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-1.0.2.tgz#673b5f233bf34d8e602b949429f8171d9121bea3" integrity sha512-0sVXIohTfLqVIW3kb/0n6IiWF3Ifj5nm2XaSrLq2DI6fKIGa2fYAZdk917rUneaeLVpYfFcyXE2ft0fe3remsA== +d3-array@2, d3-array@^2.5.0: + version "2.12.1" + resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-2.12.1.tgz#e20b41aafcdffdf5d50928004ececf815a465e81" + integrity sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ== + dependencies: + internmap "^1.0.0" + "d3-array@2 - 3", "d3-array@2.10.0 - 3", d3-array@^3.1.6: version "3.2.4" resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-3.2.4.tgz#15fec33b237f97ac5d7c986dc77da273a8ed0bb5" @@ -10146,13 +10917,6 @@ cyclist@^1.0.1: dependencies: internmap "1 - 2" -d3-array@^2.5.0: - version "2.12.1" - resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-2.12.1.tgz#e20b41aafcdffdf5d50928004ececf815a465e81" - integrity sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ== - dependencies: - internmap "^1.0.0" - "d3-color@1 - 2": version "2.0.0" resolved "https://registry.yarnpkg.com/d3-color/-/d3-color-2.0.0.tgz#8d625cab42ed9b8f601a1760a389f7ea9189d62e" @@ -10198,6 +10962,11 @@ d3-ease@^3.0.1: resolved "https://registry.yarnpkg.com/d3-format/-/d3-format-3.1.0.tgz#9260e23a28ea5cb109e93b21a06e24e2ebd55641" integrity sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA== +d3-format@^1.4.4: + version "1.4.5" + resolved "https://registry.yarnpkg.com/d3-format/-/d3-format-1.4.5.tgz#374f2ba1320e3717eb74a9356c67daee17a7edb4" + integrity sha512-J0piedu6Z8iB6TbIGfZgDzfXxUFN3qQRMofy2oPdXzQibYGqPB/9iMcxr/TGalU+2RsyDO+U4f33id8tbnSRMQ== + d3-geo@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/d3-geo/-/d3-geo-2.0.2.tgz#c065c1b71fe8c5f1be657e5f43d9bdd010383c40" @@ -10212,7 +10981,7 @@ d3-geo@^2.0.1: dependencies: d3-color "1 - 2" -"d3-interpolate@1.2.0 - 3", d3-interpolate@^3.0.1: +"d3-interpolate@1 - 3", "d3-interpolate@1.2.0 - 3", d3-interpolate@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/d3-interpolate/-/d3-interpolate-3.0.1.tgz#3c47aa5b32c5b3dfb56ef3fd4342078a632b400d" integrity sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g== @@ -10224,6 +10993,14 @@ d3-path@^3.1.0: resolved "https://registry.yarnpkg.com/d3-path/-/d3-path-3.1.0.tgz#22df939032fb5a71ae8b1800d61ddb7851c42526" integrity sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ== +d3-scale-chromatic@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz#34c39da298b23c20e02f1a4b239bd0f22e7f1314" + integrity sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ== + dependencies: + d3-color "1 - 3" + d3-interpolate "1 - 3" + d3-scale@^4.0.0, d3-scale@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/d3-scale/-/d3-scale-4.0.2.tgz#82b38e8e8ff7080764f8dcec77bd4be393689396" @@ -10254,6 +11031,20 @@ d3-shape@^3.1.0, d3-shape@^3.2.0: dependencies: d3-time "1 - 3" +d3-time-format@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/d3-time-format/-/d3-time-format-3.0.0.tgz#df8056c83659e01f20ac5da5fdeae7c08d5f1bb6" + integrity sha512-UXJh6EKsHBTjopVqZBhFysQcoXSv/5yLONZvkQ5Kk3qbwiUYkdX17Xa1PT6U1ZWXGGfB1ey5L8dKMlFq2DO0Ag== + dependencies: + d3-time "1 - 2" + +"d3-time@1 - 2": + version "2.1.1" + resolved "https://registry.yarnpkg.com/d3-time/-/d3-time-2.1.1.tgz#e9d8a8a88691f4548e68ca085e5ff956724a6682" + integrity sha512-/eIQe/eR4kCQwq7yxi7z4c6qEXf2IYGcjoWB5OOQy4Tq9Uv39/947qlDcN2TLkiTzQWzvnsuYPB9TrWaNfipKQ== + dependencies: + d3-array "2" + "d3-time@1 - 3", "d3-time@2.1.1 - 3", d3-time@^3.0.0, d3-time@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/d3-time/-/d3-time-3.1.0.tgz#9310db56e992e3c0175e1ef385e545e48a9bb5c7" @@ -10261,6 +11052,11 @@ d3-shape@^3.1.0, d3-shape@^3.2.0: dependencies: d3-array "2 - 3" +d3-time@^1.0.11: + version "1.1.0" + resolved "https://registry.yarnpkg.com/d3-time/-/d3-time-1.1.0.tgz#b1e19d307dae9c900b7e5b25ffc5dcc249a8a0f1" + integrity sha512-Xh0isrZ5rPYYdqhAVk8VLnMEidhz5aP7htAADH6MfzgmmicPkTo8LhkLxci61/lCB7n7UmE3bN0leRt+qvkLxA== + "d3-timer@1 - 2": version "2.0.0" resolved "https://registry.yarnpkg.com/d3-timer/-/d3-timer-2.0.0.tgz#055edb1d170cfe31ab2da8968deee940b56623e6" @@ -10312,6 +11108,14 @@ data-urls@^2.0.0: whatwg-mimetype "^2.3.0" whatwg-url "^8.0.0" +data-urls@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-5.0.0.tgz#2f76906bce1824429ffecb6920f45a0b30f00dde" + integrity sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg== + dependencies: + whatwg-mimetype "^4.0.0" + whatwg-url "^14.0.0" + data-view-buffer@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/data-view-buffer/-/data-view-buffer-1.0.2.tgz#211a03ba95ecaf7798a8c7198d79536211f88570" @@ -10346,6 +11150,11 @@ date-fns@^2.24.0, date-fns@^2.28.0: dependencies: "@babel/runtime" "^7.21.0" +date-fns@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-4.1.0.tgz#64b3d83fff5aa80438f5b1a633c2e83b8a1c2d14" + integrity sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg== + dateformat@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae" @@ -10358,7 +11167,7 @@ debug@2.6.9, debug@^2.2.0, debug@^2.3.3: dependencies: ms "2.0.0" -debug@4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4, debug@^4.3.5, debug@^4.4.0: +debug@4, debug@^4.0.0, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4, debug@^4.3.5, debug@^4.4.0: version "4.4.0" resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.0.tgz#2b3f2aea2ffeb776477460267377dc8710faba8a" integrity sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA== @@ -10395,11 +11204,18 @@ decimal.js-light@^2.4.1: resolved "https://registry.yarnpkg.com/decimal.js-light/-/decimal.js-light-2.5.1.tgz#134fd32508f19e208f4fb2f8dac0d2626a867934" integrity sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg== -decimal.js@10, decimal.js@^10.2.1: +decimal.js@10, decimal.js@^10.2.1, decimal.js@^10.4.3: version "10.5.0" resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.5.0.tgz#0f371c7cf6c4898ce0afb09836db73cd82010f22" integrity sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw== +decode-named-character-reference@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/decode-named-character-reference/-/decode-named-character-reference-1.1.0.tgz#5d6ce68792808901210dac42a8e9853511e2b8bf" + integrity sha512-Wy+JTSbFThEOXQIR2L6mxJvEs+veIzpmqD7ynWxMXGpnk3smkHQOp6forLdHsKpAMW9iJpaBBIxz285t1n1C3w== + dependencies: + character-entities "^2.0.0" + decode-uri-component@^0.2.0: version "0.2.2" resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.2.tgz#e69dbe25d37941171dd540e024c444cd5188e1e9" @@ -10577,6 +11393,11 @@ deprecation@^2.0.0: resolved "https://registry.yarnpkg.com/deprecation/-/deprecation-2.3.1.tgz#6368cbdb40abf3373b525ac87e4a260c3a700919" integrity sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ== +dequal@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/dequal/-/dequal-2.0.3.tgz#2644214f1997d39ed0ee0ece72335490a7ac67be" + integrity sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA== + des.js@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.1.0.tgz#1d37f5766f3bbff4ee9638e871a8768c173b81da" @@ -10637,6 +11458,13 @@ detect-port@^1.3.0: address "^1.0.1" debug "4" +devlop@^1.0.0, devlop@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/devlop/-/devlop-1.1.0.tgz#4db7c2ca4dc6e0e834c30be70c94bbc976dc7018" + integrity sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA== + dependencies: + dequal "^2.0.0" + diacritics@1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/diacritics/-/diacritics-1.3.0.tgz#3efa87323ebb863e6696cebb0082d48ff3d6f7a1" @@ -10745,6 +11573,15 @@ dom-serializer@^1.0.1: domhandler "^4.2.0" entities "^2.0.0" +dom-serializer@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-2.0.0.tgz#e41b802e1eedf9f6cae183ce5e622d789d7d8e53" + integrity sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg== + dependencies: + domelementtype "^2.3.0" + domhandler "^5.0.2" + entities "^4.2.0" + dom-walk@^0.1.0: version "0.1.2" resolved "https://registry.yarnpkg.com/dom-walk/-/dom-walk-0.1.2.tgz#0c548bef048f4d1f2a97249002236060daa3fd84" @@ -10755,7 +11592,7 @@ domain-browser@^1.1.1: resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.2.0.tgz#3d31f50191a6749dd1375a7f522e823d42e54eda" integrity sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA== -domelementtype@^2.0.1, domelementtype@^2.2.0: +domelementtype@^2.0.1, domelementtype@^2.2.0, domelementtype@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.3.0.tgz#5c45e8e869952626331d7aab326d01daf65d589d" integrity sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw== @@ -10774,6 +11611,20 @@ domhandler@^4.0.0, domhandler@^4.2.0, domhandler@^4.3.1: dependencies: domelementtype "^2.2.0" +domhandler@^5.0.2, domhandler@^5.0.3: + version "5.0.3" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-5.0.3.tgz#cc385f7f751f1d1fc650c21374804254538c7d31" + integrity sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w== + dependencies: + domelementtype "^2.3.0" + +dompurify@^3.2.4: + version "3.2.5" + resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-3.2.5.tgz#11b108656a5fb72b24d916df17a1421663d7129c" + integrity sha512-mLPd29uoRe9HpvwP2TxClGQBzGXeEC/we/q+bFlmPPmj2p2Ugl3r6ATu/UU1v77DXNcehiBg9zsr1dREyA/dJQ== + optionalDependencies: + "@types/trusted-types" "^2.0.7" + domutils@^2.5.2, domutils@^2.8.0: version "2.8.0" resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.8.0.tgz#4437def5db6e2d1f5d6ee859bd95ca7d02048135" @@ -10783,6 +11634,15 @@ domutils@^2.5.2, domutils@^2.8.0: domelementtype "^2.2.0" domhandler "^4.2.0" +domutils@^3.0.1: + version "3.2.2" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-3.2.2.tgz#edbfe2b668b0c1d97c24baf0f1062b132221bc78" + integrity sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw== + dependencies: + dom-serializer "^2.0.0" + domelementtype "^2.3.0" + domhandler "^5.0.3" + dot-case@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/dot-case/-/dot-case-3.0.4.tgz#9b2b670d00a431667a8a75ba29cd1b98809ce751" @@ -10996,7 +11856,7 @@ entities@^2.0.0: resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55" integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A== -entities@^4.4.0: +entities@^4.2.0, entities@^4.4.0, entities@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48" integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw== @@ -11276,6 +12136,22 @@ eslint-config-next@14.1.4: eslint-plugin-react "^7.33.2" eslint-plugin-react-hooks "^4.5.0 || 5.0.0-canary-7118f5dd7-20230705" +eslint-config-next@15.0.3: + version "15.0.3" + resolved "https://registry.yarnpkg.com/eslint-config-next/-/eslint-config-next-15.0.3.tgz#b483585260d5e55050d4ab87e053c88089ae12ee" + integrity sha512-IGP2DdQQrgjcr4mwFPve4DrCqo7CVVez1WoYY47XwKSrYO4hC0Dlb+iJA60i0YfICOzgNADIb8r28BpQ5Zs0wg== + dependencies: + "@next/eslint-plugin-next" "15.0.3" + "@rushstack/eslint-patch" "^1.10.3" + "@typescript-eslint/eslint-plugin" "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0" + "@typescript-eslint/parser" "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0" + eslint-import-resolver-node "^0.3.6" + eslint-import-resolver-typescript "^3.5.2" + eslint-plugin-import "^2.31.0" + eslint-plugin-jsx-a11y "^6.10.0" + eslint-plugin-react "^7.35.0" + eslint-plugin-react-hooks "^5.0.0" + eslint-config-prettier@^8.3.0, eslint-config-prettier@^8.5.0: version "8.10.0" resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.10.0.tgz#3a06a662130807e2502fc3ff8b4143d8a0658e11" @@ -11318,7 +12194,7 @@ eslint-module-utils@^2.12.0: dependencies: debug "^3.2.7" -eslint-plugin-import@^2.25.4, eslint-plugin-import@^2.28.1: +eslint-plugin-import@^2.25.4, eslint-plugin-import@^2.28.1, eslint-plugin-import@^2.31.0: version "2.31.0" resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.31.0.tgz#310ce7e720ca1d9c0bb3f69adfd1c6bdd7d9e0e7" integrity sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A== @@ -11350,7 +12226,7 @@ eslint-plugin-jest@^26.1.1: dependencies: "@typescript-eslint/utils" "^5.10.0" -eslint-plugin-jsx-a11y@^6.5.1, eslint-plugin-jsx-a11y@^6.7.1: +eslint-plugin-jsx-a11y@^6.10.0, eslint-plugin-jsx-a11y@^6.5.1, eslint-plugin-jsx-a11y@^6.7.1: version "6.10.2" resolved "https://registry.yarnpkg.com/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.10.2.tgz#d2812bb23bf1ab4665f1718ea442e8372e638483" integrity sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q== @@ -11397,6 +12273,11 @@ eslint-plugin-react-hooks@^4.3.0: resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.0.0-canary-7118f5dd7-20230705.tgz#4d55c50e186f1a2b0636433d2b0b2f592ddbccfd" integrity sha512-AZYbMo/NW9chdL7vk6HQzQhT+PvTAEVqWk9ziruUoW2kAOcN5qNyelv70e0F1VNQAbvutOC9oc+xfWycI9FxDw== +eslint-plugin-react-hooks@^5.0.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.2.0.tgz#1be0080901e6ac31ce7971beed3d3ec0a423d9e3" + integrity sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg== + eslint-plugin-react@^7.29.2, eslint-plugin-react@^7.33.2: version "7.37.4" resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.37.4.tgz#1b6c80b6175b6ae4b26055ae4d55d04c414c7181" @@ -11421,6 +12302,30 @@ eslint-plugin-react@^7.29.2, eslint-plugin-react@^7.33.2: string.prototype.matchall "^4.0.12" string.prototype.repeat "^1.0.0" +eslint-plugin-react@^7.35.0: + version "7.37.5" + resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz#2975511472bdda1b272b34d779335c9b0e877065" + integrity sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA== + dependencies: + array-includes "^3.1.8" + array.prototype.findlast "^1.2.5" + array.prototype.flatmap "^1.3.3" + array.prototype.tosorted "^1.1.4" + doctrine "^2.1.0" + es-iterator-helpers "^1.2.1" + estraverse "^5.3.0" + hasown "^2.0.2" + jsx-ast-utils "^2.4.1 || ^3.0.0" + minimatch "^3.1.2" + object.entries "^1.1.9" + object.fromentries "^2.0.8" + object.values "^1.2.1" + prop-types "^15.8.1" + resolve "^2.0.0-next.5" + semver "^6.3.1" + string.prototype.matchall "^4.0.12" + string.prototype.repeat "^1.0.0" + eslint-plugin-storybook@^0.5.12: version "0.5.13" resolved "https://registry.yarnpkg.com/eslint-plugin-storybook/-/eslint-plugin-storybook-0.5.13.tgz#dee4056eaa59c886a9ae78fb7b2ee0ed5fad4366" @@ -11483,6 +12388,11 @@ eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4 resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== +eslint-visitor-keys@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz#687bacb2af884fcdda8a6e7d65c606f46a14cd45" + integrity sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw== + eslint@^7.18.0: version "7.32.0" resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.32.0.tgz#c6d328a14be3fb08c8d1d21e12c02fdb7a2a812d" @@ -11629,6 +12539,11 @@ estree-to-babel@^3.1.0: "@babel/types" "^7.2.0" c8 "^7.6.0" +estree-util-is-identifier-name@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz#0b5ef4c4ff13508b34dcd01ecfa945f61fce5dbd" + integrity sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg== + estree-walker@^0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-0.6.1.tgz#53049143f40c6eb918b23671d1fe3219f3a1b362" @@ -11865,6 +12780,17 @@ fast-equals@^5.0.1: resolved "https://registry.yarnpkg.com/fast-equals/-/fast-equals-5.2.2.tgz#885d7bfb079fac0ce0e8450374bce29e9b742484" integrity sha512-V7/RktU11J3I36Nwq2JnZEM7tNm17eBJz+u25qdxBZeCKiX6BkVSZQjwWIr+IobgnZy+ag73tTZgZi7tr0LrBw== +fast-glob@3.3.1: + version "3.3.1" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.1.tgz#784b4e897340f3dbbef17413b3f11acf03c874c4" + integrity sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg== + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.2" + merge2 "^1.3.0" + micromatch "^4.0.4" + fast-glob@^2.2.6: version "2.2.7" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-2.2.7.tgz#6953857c3afa475fff92ee6015d52da70a4cd39d" @@ -11877,7 +12803,7 @@ fast-glob@^2.2.6: merge2 "^1.2.3" micromatch "^3.1.10" -fast-glob@^3.2.9: +fast-glob@^3.2.9, fast-glob@^3.3.2: version "3.3.3" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.3.tgz#d06d585ce8dba90a16b0505c543c3ccfb3aeb818" integrity sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg== @@ -12235,7 +13161,7 @@ form-data@^3.0.0: es-set-tostringtag "^2.1.0" mime-types "^2.1.35" -form-data@^4.0.0: +form-data@^4.0.0, form-data@^4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.2.tgz#35cabbdd30c3ce73deb2c42d3c8d3ed9ca51794c" integrity sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w== @@ -12941,6 +13867,27 @@ hast-util-raw@6.0.1: xtend "^4.0.0" zwitch "^1.0.0" +hast-util-to-jsx-runtime@^2.0.0: + version "2.3.6" + resolved "https://registry.yarnpkg.com/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.6.tgz#ff31897aae59f62232e21594eac7ef6b63333e98" + integrity sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg== + dependencies: + "@types/estree" "^1.0.0" + "@types/hast" "^3.0.0" + "@types/unist" "^3.0.0" + comma-separated-tokens "^2.0.0" + devlop "^1.0.0" + estree-util-is-identifier-name "^3.0.0" + hast-util-whitespace "^3.0.0" + mdast-util-mdx-expression "^2.0.0" + mdast-util-mdx-jsx "^3.0.0" + mdast-util-mdxjs-esm "^2.0.0" + property-information "^7.0.0" + space-separated-tokens "^2.0.0" + style-to-js "^1.0.0" + unist-util-position "^5.0.0" + vfile-message "^4.0.0" + hast-util-to-parse5@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/hast-util-to-parse5/-/hast-util-to-parse5-6.0.0.tgz#1ec44650b631d72952066cea9b1445df699f8479" @@ -12952,6 +13899,13 @@ hast-util-to-parse5@^6.0.0: xtend "^4.0.0" zwitch "^1.0.0" +hast-util-whitespace@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz#7778ed9d3c92dd9e8c5c8f648a49c21fc51cb621" + integrity sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw== + dependencies: + "@types/hast" "^3.0.0" + hastscript@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/hastscript/-/hastscript-6.0.0.tgz#e8768d7eac56c3fdeac8a92830d58e811e5bf640" @@ -12978,6 +13932,11 @@ highlight-words@1.2.2: resolved "https://registry.yarnpkg.com/highlight-words/-/highlight-words-1.2.2.tgz#9875b75d11814d7356b24f23feeb7d77761fa867" integrity sha512-Mf4xfPXYm8Ay1wTibCrHpNWeR2nUMynMVFkXCi4mbl+TEgmNOe+I4hV7W3OCZcSvzGL6kupaqpfHOemliMTGxQ== +highlight-words@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/highlight-words/-/highlight-words-2.0.0.tgz#06853d68f1f7c8e59d6ef2dd072fe2f64fc93936" + integrity sha512-If5n+IhSBRXTScE7wl16VPmd+44Vy7kof24EdqhjsZsDuHikpv1OCagVcJFpB4fS4UPUniedlWqrjIO8vWOsIQ== + hmac-drbg@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" @@ -13037,6 +13996,13 @@ html-encoding-sniffer@^2.0.1: dependencies: whatwg-encoding "^1.0.5" +html-encoding-sniffer@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz#696df529a7cfd82446369dc5193e590a3735b448" + integrity sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ== + dependencies: + whatwg-encoding "^3.1.1" + html-entities@^2.1.0, html-entities@^2.3.2: version "2.5.2" resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-2.5.2.tgz#201a3cf95d3a15be7099521620d19dfb4f65359f" @@ -13073,11 +14039,23 @@ html-minifier-terser@^6.0.2: relateurl "^0.2.7" terser "^5.10.0" +html-parse-stringify@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz#dfc1017347ce9f77c8141a507f233040c59c55d2" + integrity sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg== + dependencies: + void-elements "3.1.0" + html-tags@^3.1.0: version "3.3.1" resolved "https://registry.yarnpkg.com/html-tags/-/html-tags-3.3.1.tgz#a04026a18c882e4bba8a01a3d39cfe465d40b5ce" integrity sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ== +html-url-attributes@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/html-url-attributes/-/html-url-attributes-3.0.1.tgz#83b052cd5e437071b756cd74ae70f708870c2d87" + integrity sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ== + html-void-elements@^1.0.0: version "1.0.5" resolved "https://registry.yarnpkg.com/html-void-elements/-/html-void-elements-1.0.5.tgz#ce9159494e86d95e45795b166c2021c2cfca4483" @@ -13173,6 +14151,14 @@ http-proxy-agent@^5.0.0: agent-base "6" debug "4" +http-proxy-agent@^7.0.2: + version "7.0.2" + resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz#9a8b1f246866c028509486585f62b8f2c18c270e" + integrity sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig== + dependencies: + agent-base "^7.1.0" + debug "^4.3.4" + http-proxy-middleware@^2.0.3, http-proxy-middleware@^2.0.6: version "2.0.7" resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-2.0.7.tgz#915f236d92ae98ef48278a95dedf17e991936ec6" @@ -13206,6 +14192,14 @@ https-proxy-agent@^5.0.0: agent-base "6" debug "4" +https-proxy-agent@^7.0.6: + version "7.0.6" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz#da8dfeac7da130b05c2ba4b59c9b6cd66611a6b9" + integrity sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw== + dependencies: + agent-base "^7.1.2" + debug "4" + human-signals@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" @@ -13230,6 +14224,20 @@ i18n-iso-countries@^6.8.0: dependencies: diacritics "1.3.0" +i18next-resources-to-backend@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/i18next-resources-to-backend/-/i18next-resources-to-backend-1.2.1.tgz#fded121e63e3139ce839c9901b9449dbbea7351d" + integrity sha512-okHbVA+HZ7n1/76MsfhPqDou0fptl2dAlhRDu2ideXloRRduzHsqDOznJBef+R3DFZnbvWoBW+KxJ7fnFjd6Yw== + dependencies: + "@babel/runtime" "^7.23.2" + +i18next@^24.2.2: + version "24.2.3" + resolved "https://registry.yarnpkg.com/i18next/-/i18next-24.2.3.tgz#3a05f72615cbd7c00d7e348667e2aabef1df753b" + integrity sha512-lfbf80OzkocvX7nmZtu7nSTNbrTYR52sLWxPtlXX1zAhVw8WEnFk4puUkCR4B1dNQwbSpEHHHemcZu//7EcB7A== + dependencies: + "@babel/runtime" "^7.26.10" + iconv-lite@0.4.24, iconv-lite@^0.4.24: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" @@ -13237,7 +14245,7 @@ iconv-lite@0.4.24, iconv-lite@^0.4.24: dependencies: safer-buffer ">= 2.1.2 < 3" -iconv-lite@^0.6.2: +iconv-lite@0.6.3, iconv-lite@^0.6.2: version "0.6.3" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== @@ -13295,7 +14303,7 @@ ignore@^4.0.3, ignore@^4.0.6: resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== -ignore@^5.0.4, ignore@^5.2.0: +ignore@^5.0.4, ignore@^5.2.0, ignore@^5.3.1: version "5.3.2" resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5" integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g== @@ -13397,6 +14405,11 @@ inline-style-parser@0.1.1: resolved "https://registry.yarnpkg.com/inline-style-parser/-/inline-style-parser-0.1.1.tgz#ec8a3b429274e9c0a1f1c4ffa9453a7fef72cea1" integrity sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q== +inline-style-parser@0.2.4: + version "0.2.4" + resolved "https://registry.yarnpkg.com/inline-style-parser/-/inline-style-parser-0.2.4.tgz#f4af5fe72e612839fcd453d989a586566d695f22" + integrity sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q== + inquirer@^8.2.4: version "8.2.6" resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-8.2.6.tgz#733b74888195d8d400a67ac332011b5fae5ea562" @@ -13497,6 +14510,11 @@ is-alphabetical@1.0.4, is-alphabetical@^1.0.0: resolved "https://registry.yarnpkg.com/is-alphabetical/-/is-alphabetical-1.0.4.tgz#9e7d6b94916be22153745d184c298cbf986a686d" integrity sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg== +is-alphabetical@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-alphabetical/-/is-alphabetical-2.0.1.tgz#01072053ea7c1036df3c7d19a6daaec7f19e789b" + integrity sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ== + is-alphanumerical@^1.0.0: version "1.0.4" resolved "https://registry.yarnpkg.com/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz#7eb9a2431f855f6b1ef1a78e326df515696c4dbf" @@ -13505,6 +14523,14 @@ is-alphanumerical@^1.0.0: is-alphabetical "^1.0.0" is-decimal "^1.0.0" +is-alphanumerical@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz#7c03fbe96e3e931113e57f964b0a368cc2dfd875" + integrity sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw== + dependencies: + is-alphabetical "^2.0.0" + is-decimal "^2.0.0" + is-arguments@^1.1.1: version "1.2.0" resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.2.0.tgz#ad58c6aecf563b78ef2bf04df540da8f5d7d8e1b" @@ -13644,6 +14670,11 @@ is-decimal@^1.0.0: resolved "https://registry.yarnpkg.com/is-decimal/-/is-decimal-1.0.4.tgz#65a3a5958a1c5b63a706e1b333d7cd9f630d3fa5" integrity sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw== +is-decimal@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-decimal/-/is-decimal-2.0.1.tgz#9469d2dc190d0214fd87d78b78caecc0cc14eef7" + integrity sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A== + is-descriptor@^0.1.0: version "0.1.7" resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.7.tgz#2727eb61fd789dcd5bdf0ed4569f551d2fe3be33" @@ -13746,6 +14777,11 @@ is-hexadecimal@^1.0.0: resolved "https://registry.yarnpkg.com/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz#cc35c97588da4bd49a8eedd6bc4082d44dcb23a7" integrity sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw== +is-hexadecimal@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz#86b5bf668fca307498d319dfc03289d781a90027" + integrity sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg== + is-in-browser@^1.0.2, is-in-browser@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/is-in-browser/-/is-in-browser-1.1.3.tgz#56ff4db683a078c6082eb95dad7dc62e1d04f835" @@ -13840,6 +14876,11 @@ is-plain-obj@^3.0.0: resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-3.0.0.tgz#af6f2ea14ac5a646183a5bbdb5baabbc156ad9d7" integrity sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA== +is-plain-obj@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-4.1.0.tgz#d65025edec3657ce032fd7db63c97883eaed71f0" + integrity sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg== + is-plain-object@5.0.0, is-plain-object@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344" @@ -14038,6 +15079,14 @@ isobject@^4.0.0: resolved "https://registry.yarnpkg.com/isobject/-/isobject-4.0.0.tgz#3f1c9155e73b192022a80819bacd0343711697b0" integrity sha512-S/2fF5wH8SJA/kmwr6HYhK/RI/OkhD84k8ntalo0iJjZikgq1XFvR5M8NPT1x5F7fBwCG3qHfnzeP/Vh/ZxCUA== +isomorphic-dompurify@^2.21.0: + version "2.22.0" + resolved "https://registry.yarnpkg.com/isomorphic-dompurify/-/isomorphic-dompurify-2.22.0.tgz#675b751bb80dc1e96a5c6760bbc6396b03f4786d" + integrity sha512-A2xsDNST1yB94rErEnwqlzSvGllCJ4e8lDMe1OWBH2hvpfc/2qzgMEiDshTO1HwO+PIDTiYeOc7ZDB7Ds49BOg== + dependencies: + dompurify "^3.2.4" + jsdom "^26.0.0" + isomorphic-unfetch@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/isomorphic-unfetch/-/isomorphic-unfetch-3.1.0.tgz#87341d5f4f7b63843d468438128cb087b7c3e98f" @@ -14789,6 +15838,33 @@ jsdom@^16.6.0: ws "^7.4.6" xml-name-validator "^3.0.0" +jsdom@^26.0.0: + version "26.0.0" + resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-26.0.0.tgz#446dd1ad8cfc50df7e714e58f1f972c1763b354c" + integrity sha512-BZYDGVAIriBWTpIxYzrXjv3E/4u8+/pSG5bQdIYCbNCGOvsPkDQfTVLAIXAf9ETdCpduCVTkDe2NNZ8NIwUVzw== + dependencies: + cssstyle "^4.2.1" + data-urls "^5.0.0" + decimal.js "^10.4.3" + form-data "^4.0.1" + html-encoding-sniffer "^4.0.0" + http-proxy-agent "^7.0.2" + https-proxy-agent "^7.0.6" + is-potential-custom-element-name "^1.0.1" + nwsapi "^2.2.16" + parse5 "^7.2.1" + rrweb-cssom "^0.8.0" + saxes "^6.0.0" + symbol-tree "^3.2.4" + tough-cookie "^5.0.0" + w3c-xmlserializer "^5.0.0" + webidl-conversions "^7.0.0" + whatwg-encoding "^3.1.1" + whatwg-mimetype "^4.0.0" + whatwg-url "^14.1.0" + ws "^8.18.0" + xml-name-validator "^5.0.0" + jsesc@^3.0.2: version "3.1.0" resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-3.1.0.tgz#74d335a234f67ed19907fdadfac7ccf9d409825d" @@ -15032,6 +16108,72 @@ lazy-universal-dotenv@^3.0.1: dotenv "^8.0.0" dotenv-expand "^5.1.0" +lefthook-darwin-arm64@1.11.6: + version "1.11.6" + resolved "https://registry.yarnpkg.com/lefthook-darwin-arm64/-/lefthook-darwin-arm64-1.11.6.tgz#bffd9f18a8119b07c5a8d6d671ceb012a66d796c" + integrity sha512-gWgdWrKgZgX+bKc6Vs/x7JkO+58lLOpRzpteLx//82D0MKVPlNZwjd4zz4AbIBXtM4Hcj+6gSsOzQ7QDXxjVvQ== + +lefthook-darwin-x64@1.11.6: + version "1.11.6" + resolved "https://registry.yarnpkg.com/lefthook-darwin-x64/-/lefthook-darwin-x64-1.11.6.tgz#52a1d5e2a9aa7dea8175dfbf7b8932f856287605" + integrity sha512-Ia0TjTKuYcSaDTuoCnbWtpPZ2VEoKzgn33OB90VjNaSVs4ooE0PIdpO+w00x1elqIaf1pbrpq6HgeB26Du8KbQ== + +lefthook-freebsd-arm64@1.11.6: + version "1.11.6" + resolved "https://registry.yarnpkg.com/lefthook-freebsd-arm64/-/lefthook-freebsd-arm64-1.11.6.tgz#6aae629dbcaf188b3518388c204c0766685cdda3" + integrity sha512-PxIwj+hmjLahyzEmcIfalIBDhgklAQCavwM4sGCgbzDi4/+VQX+4aEs4pQqtd7v3aohmjtO/4n2emzTI8donww== + +lefthook-freebsd-x64@1.11.6: + version "1.11.6" + resolved "https://registry.yarnpkg.com/lefthook-freebsd-x64/-/lefthook-freebsd-x64-1.11.6.tgz#ffff50541c31c15a44ec923a8d6772e21558249a" + integrity sha512-3o1lMKxz1VtWaP/o117wgUn3ZOpefMoSf+8LuiTzI3/PDprIuzgyw2nXKlBZAMDpNPHMNnJeQNts9XLMRmkldg== + +lefthook-linux-arm64@1.11.6: + version "1.11.6" + resolved "https://registry.yarnpkg.com/lefthook-linux-arm64/-/lefthook-linux-arm64-1.11.6.tgz#01a51d0bc84ee3cc04ba4b4ec20a2ac8fa766b68" + integrity sha512-nKPFZ5cA9f5tVn0ybDVqcXXlpTHZqo05N4KQRhWTj5Nem+JoD2YzJIlvZhdJhUrldERqj6deDMXChH5T3z4Rrw== + +lefthook-linux-x64@1.11.6: + version "1.11.6" + resolved "https://registry.yarnpkg.com/lefthook-linux-x64/-/lefthook-linux-x64-1.11.6.tgz#382da328f510c7ce4c64ddce7cc2b1d5bf5b54e4" + integrity sha512-naN8dllLCOEeP+wznLnq+oXrs1dvt/iMLkcl+pOPWLqFccPfDiHzr8V8GslaTa+rSFsAnvjR7SJIOi5C29xedA== + +lefthook-openbsd-arm64@1.11.6: + version "1.11.6" + resolved "https://registry.yarnpkg.com/lefthook-openbsd-arm64/-/lefthook-openbsd-arm64-1.11.6.tgz#febf8dfec0d49ccc0307f590f06c71dc8344ba52" + integrity sha512-dPxhJfYQ667T+U3pz1+O3mTRNHzXH/BvPlXSH+oy8uiSry4AtVNRXkVvXPUcpLlrAy6HuFYodsrpCIlWFeYwiQ== + +lefthook-openbsd-x64@1.11.6: + version "1.11.6" + resolved "https://registry.yarnpkg.com/lefthook-openbsd-x64/-/lefthook-openbsd-x64-1.11.6.tgz#49fd7242f0ea2cb7f98223de034263ab5fa044e9" + integrity sha512-9D26kcSsjiW4D0AuVDdi+0ZqrsOzRWOpMS/kcUbLfrU99yCvma0rMTqKbbDMkVur/znS7qL53oGahXCXDNA+IQ== + +lefthook-windows-arm64@1.11.6: + version "1.11.6" + resolved "https://registry.yarnpkg.com/lefthook-windows-arm64/-/lefthook-windows-arm64-1.11.6.tgz#705b7c3c3e35a9795496e4251950ff5aaae59e46" + integrity sha512-xdCenr4+BFnfBEhiXj6GJp02EPmcwTAGa7NYm6hVTfDwGXw24tuLv7lpnGjgK3kovN6EukgLH1FYkeyDOBEMnA== + +lefthook-windows-x64@1.11.6: + version "1.11.6" + resolved "https://registry.yarnpkg.com/lefthook-windows-x64/-/lefthook-windows-x64-1.11.6.tgz#71d92d874a7885645eb34c6c7712a91ebfb43dda" + integrity sha512-Fg2GzLhzeDV/GX8+ydrI0wBOytQWpPkNdngx+a8B/feCDbwjAiFklDG5oV4ytuWrtg1JPEEWLJd6nHefj4wtHA== + +lefthook@^1.8.5: + version "1.11.6" + resolved "https://registry.yarnpkg.com/lefthook/-/lefthook-1.11.6.tgz#e13c23a83095c5de258b0702280b097dcba1b029" + integrity sha512-j0VmMM50WlPDassmgvapRum9po29Tv1BXzBNFpzGkk9E91CEG9jKik/OHyH/r/na+q8qNIUUyPL6QQuTN/UhQQ== + optionalDependencies: + lefthook-darwin-arm64 "1.11.6" + lefthook-darwin-x64 "1.11.6" + lefthook-freebsd-arm64 "1.11.6" + lefthook-freebsd-x64 "1.11.6" + lefthook-linux-arm64 "1.11.6" + lefthook-linux-x64 "1.11.6" + lefthook-openbsd-arm64 "1.11.6" + lefthook-openbsd-x64 "1.11.6" + lefthook-windows-arm64 "1.11.6" + lefthook-windows-x64 "1.11.6" + lerna@^7.3.0: version "7.4.2" resolved "https://registry.yarnpkg.com/lerna/-/lerna-7.4.2.tgz#03497125d7b7c8d463eebfe17a701b16bde2ad09" @@ -15358,6 +16500,11 @@ long@^4.0.0: resolved "https://registry.yarnpkg.com/long/-/long-4.0.0.tgz#9a7b71cfb7d361a194ea555241c92f7468d5bf28" integrity sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA== +longest-streak@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/longest-streak/-/longest-streak-3.1.0.tgz#62fa67cd958742a1574af9f39866364102d90cd4" + integrity sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g== + loose-envify@^1.1.0, loose-envify@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" @@ -15576,6 +16723,16 @@ material-react-table@^2.12.1: "@tanstack/react-virtual" "3.10.6" highlight-words "1.2.2" +material-react-table@^3.0.3: + version "3.2.1" + resolved "https://registry.yarnpkg.com/material-react-table/-/material-react-table-3.2.1.tgz#56f595755cab3b669b399999fed9eb305fbb6dd7" + integrity sha512-sQtTf7bETpkPN2Hm5BVtz89wrfXCVQguz6XlwMChSnfKFO5QCKAJJC5aSIKnUc3S0AvTz/k/ILi00FnnY1Gixw== + dependencies: + "@tanstack/match-sorter-utils" "8.19.4" + "@tanstack/react-table" "8.20.6" + "@tanstack/react-virtual" "3.11.2" + highlight-words "2.0.0" + math-intrinsics@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz#a0dd74be81e2aa5c2f27e65ce283605ee4e2b7f9" @@ -15604,6 +16761,74 @@ mdast-util-definitions@^4.0.0: dependencies: unist-util-visit "^2.0.0" +mdast-util-from-markdown@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.2.tgz#4850390ca7cf17413a9b9a0fbefcd1bc0eb4160a" + integrity sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA== + dependencies: + "@types/mdast" "^4.0.0" + "@types/unist" "^3.0.0" + decode-named-character-reference "^1.0.0" + devlop "^1.0.0" + mdast-util-to-string "^4.0.0" + micromark "^4.0.0" + micromark-util-decode-numeric-character-reference "^2.0.0" + micromark-util-decode-string "^2.0.0" + micromark-util-normalize-identifier "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + unist-util-stringify-position "^4.0.0" + +mdast-util-mdx-expression@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.1.tgz#43f0abac9adc756e2086f63822a38c8d3c3a5096" + integrity sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ== + dependencies: + "@types/estree-jsx" "^1.0.0" + "@types/hast" "^3.0.0" + "@types/mdast" "^4.0.0" + devlop "^1.0.0" + mdast-util-from-markdown "^2.0.0" + mdast-util-to-markdown "^2.0.0" + +mdast-util-mdx-jsx@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.2.0.tgz#fd04c67a2a7499efb905a8a5c578dddc9fdada0d" + integrity sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q== + dependencies: + "@types/estree-jsx" "^1.0.0" + "@types/hast" "^3.0.0" + "@types/mdast" "^4.0.0" + "@types/unist" "^3.0.0" + ccount "^2.0.0" + devlop "^1.1.0" + mdast-util-from-markdown "^2.0.0" + mdast-util-to-markdown "^2.0.0" + parse-entities "^4.0.0" + stringify-entities "^4.0.0" + unist-util-stringify-position "^4.0.0" + vfile-message "^4.0.0" + +mdast-util-mdxjs-esm@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-2.0.1.tgz#019cfbe757ad62dd557db35a695e7314bcc9fa97" + integrity sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg== + dependencies: + "@types/estree-jsx" "^1.0.0" + "@types/hast" "^3.0.0" + "@types/mdast" "^4.0.0" + devlop "^1.0.0" + mdast-util-from-markdown "^2.0.0" + mdast-util-to-markdown "^2.0.0" + +mdast-util-phrasing@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz#7cc0a8dec30eaf04b7b1a9661a92adb3382aa6e3" + integrity sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w== + dependencies: + "@types/mdast" "^4.0.0" + unist-util-is "^6.0.0" + mdast-util-to-hast@10.0.1: version "10.0.1" resolved "https://registry.yarnpkg.com/mdast-util-to-hast/-/mdast-util-to-hast-10.0.1.tgz#0cfc82089494c52d46eb0e3edb7a4eb2aea021eb" @@ -15618,16 +16843,63 @@ mdast-util-to-hast@10.0.1: unist-util-position "^3.0.0" unist-util-visit "^2.0.0" +mdast-util-to-hast@^13.0.0: + version "13.2.0" + resolved "https://registry.yarnpkg.com/mdast-util-to-hast/-/mdast-util-to-hast-13.2.0.tgz#5ca58e5b921cc0a3ded1bc02eed79a4fe4fe41f4" + integrity sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA== + dependencies: + "@types/hast" "^3.0.0" + "@types/mdast" "^4.0.0" + "@ungap/structured-clone" "^1.0.0" + devlop "^1.0.0" + micromark-util-sanitize-uri "^2.0.0" + trim-lines "^3.0.0" + unist-util-position "^5.0.0" + unist-util-visit "^5.0.0" + vfile "^6.0.0" + +mdast-util-to-markdown@^2.0.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.2.tgz#f910ffe60897f04bb4b7e7ee434486f76288361b" + integrity sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA== + dependencies: + "@types/mdast" "^4.0.0" + "@types/unist" "^3.0.0" + longest-streak "^3.0.0" + mdast-util-phrasing "^4.0.0" + mdast-util-to-string "^4.0.0" + micromark-util-classify-character "^2.0.0" + micromark-util-decode-string "^2.0.0" + unist-util-visit "^5.0.0" + zwitch "^2.0.0" + mdast-util-to-string@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/mdast-util-to-string/-/mdast-util-to-string-1.1.0.tgz#27055500103f51637bd07d01da01eb1967a43527" integrity sha512-jVU0Nr2B9X3MU4tSK7JP1CMkSvOj7X5l/GboG1tKRw52lLF1x2Ju92Ms9tNetCcbfX3hzlM73zYo2NKkWSfF/A== +mdast-util-to-string@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz#7a5121475556a04e7eddeb67b264aae79d312814" + integrity sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg== + dependencies: + "@types/mdast" "^4.0.0" + mdn-data@2.0.14: version "2.0.14" resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.14.tgz#7113fc4281917d63ce29b43446f701e68c25ba50" integrity sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow== +mdn-data@2.0.28: + version "2.0.28" + resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.28.tgz#5ec48e7bef120654539069e1ae4ddc81ca490eba" + integrity sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g== + +mdn-data@2.0.30: + version "2.0.30" + resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.30.tgz#ce4df6f80af6cfbe218ecd5c552ba13c4dfa08cc" + integrity sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA== + mdurl@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e" @@ -15746,6 +17018,200 @@ microevent.ts@~0.1.1: resolved "https://registry.yarnpkg.com/microevent.ts/-/microevent.ts-0.1.1.tgz#70b09b83f43df5172d0205a63025bce0f7357fa0" integrity sha512-jo1OfR4TaEwd5HOrt5+tAZ9mqT4jmpNAusXtyfNzqVm9uiSYFZlKM1wYL4oU7azZW/PxQW53wM0S6OR1JHNa2g== +micromark-core-commonmark@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz#c691630e485021a68cf28dbc2b2ca27ebf678cd4" + integrity sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg== + dependencies: + decode-named-character-reference "^1.0.0" + devlop "^1.0.0" + micromark-factory-destination "^2.0.0" + micromark-factory-label "^2.0.0" + micromark-factory-space "^2.0.0" + micromark-factory-title "^2.0.0" + micromark-factory-whitespace "^2.0.0" + micromark-util-character "^2.0.0" + micromark-util-chunked "^2.0.0" + micromark-util-classify-character "^2.0.0" + micromark-util-html-tag-name "^2.0.0" + micromark-util-normalize-identifier "^2.0.0" + micromark-util-resolve-all "^2.0.0" + micromark-util-subtokenize "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + +micromark-factory-destination@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz#8fef8e0f7081f0474fbdd92deb50c990a0264639" + integrity sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA== + dependencies: + micromark-util-character "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + +micromark-factory-label@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz#5267efa97f1e5254efc7f20b459a38cb21058ba1" + integrity sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg== + dependencies: + devlop "^1.0.0" + micromark-util-character "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + +micromark-factory-space@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz#36d0212e962b2b3121f8525fc7a3c7c029f334fc" + integrity sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg== + dependencies: + micromark-util-character "^2.0.0" + micromark-util-types "^2.0.0" + +micromark-factory-title@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz#237e4aa5d58a95863f01032d9ee9b090f1de6e94" + integrity sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw== + dependencies: + micromark-factory-space "^2.0.0" + micromark-util-character "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + +micromark-factory-whitespace@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz#06b26b2983c4d27bfcc657b33e25134d4868b0b1" + integrity sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ== + dependencies: + micromark-factory-space "^2.0.0" + micromark-util-character "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + +micromark-util-character@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/micromark-util-character/-/micromark-util-character-2.1.1.tgz#2f987831a40d4c510ac261e89852c4e9703ccda6" + integrity sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q== + dependencies: + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + +micromark-util-chunked@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz#47fbcd93471a3fccab86cff03847fc3552db1051" + integrity sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA== + dependencies: + micromark-util-symbol "^2.0.0" + +micromark-util-classify-character@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz#d399faf9c45ca14c8b4be98b1ea481bced87b629" + integrity sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q== + dependencies: + micromark-util-character "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + +micromark-util-combine-extensions@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz#2a0f490ab08bff5cc2fd5eec6dd0ca04f89b30a9" + integrity sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg== + dependencies: + micromark-util-chunked "^2.0.0" + micromark-util-types "^2.0.0" + +micromark-util-decode-numeric-character-reference@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz#fcf15b660979388e6f118cdb6bf7d79d73d26fe5" + integrity sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw== + dependencies: + micromark-util-symbol "^2.0.0" + +micromark-util-decode-string@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz#6cb99582e5d271e84efca8e61a807994d7161eb2" + integrity sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ== + dependencies: + decode-named-character-reference "^1.0.0" + micromark-util-character "^2.0.0" + micromark-util-decode-numeric-character-reference "^2.0.0" + micromark-util-symbol "^2.0.0" + +micromark-util-encode@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz#0d51d1c095551cfaac368326963cf55f15f540b8" + integrity sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw== + +micromark-util-html-tag-name@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz#e40403096481986b41c106627f98f72d4d10b825" + integrity sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA== + +micromark-util-normalize-identifier@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz#c30d77b2e832acf6526f8bf1aa47bc9c9438c16d" + integrity sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q== + dependencies: + micromark-util-symbol "^2.0.0" + +micromark-util-resolve-all@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz#e1a2d62cdd237230a2ae11839027b19381e31e8b" + integrity sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg== + dependencies: + micromark-util-types "^2.0.0" + +micromark-util-sanitize-uri@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz#ab89789b818a58752b73d6b55238621b7faa8fd7" + integrity sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ== + dependencies: + micromark-util-character "^2.0.0" + micromark-util-encode "^2.0.0" + micromark-util-symbol "^2.0.0" + +micromark-util-subtokenize@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz#d8ade5ba0f3197a1cf6a2999fbbfe6357a1a19ee" + integrity sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA== + dependencies: + devlop "^1.0.0" + micromark-util-chunked "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + +micromark-util-symbol@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz#e5da494e8eb2b071a0d08fb34f6cefec6c0a19b8" + integrity sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q== + +micromark-util-types@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/micromark-util-types/-/micromark-util-types-2.0.2.tgz#f00225f5f5a0ebc3254f96c36b6605c4b393908e" + integrity sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA== + +micromark@^4.0.0: + version "4.0.2" + resolved "https://registry.yarnpkg.com/micromark/-/micromark-4.0.2.tgz#91395a3e1884a198e62116e33c9c568e39936fdb" + integrity sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA== + dependencies: + "@types/debug" "^4.0.0" + debug "^4.0.0" + decode-named-character-reference "^1.0.0" + devlop "^1.0.0" + micromark-core-commonmark "^2.0.0" + micromark-factory-space "^2.0.0" + micromark-util-character "^2.0.0" + micromark-util-chunked "^2.0.0" + micromark-util-combine-extensions "^2.0.0" + micromark-util-decode-numeric-character-reference "^2.0.0" + micromark-util-encode "^2.0.0" + micromark-util-normalize-identifier "^2.0.0" + micromark-util-resolve-all "^2.0.0" + micromark-util-sanitize-uri "^2.0.0" + micromark-util-subtokenize "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + micromatch@^3.1.10, micromatch@^3.1.4: version "3.1.10" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" @@ -16217,6 +17683,29 @@ next@14.2.21: "@next/swc-win32-ia32-msvc" "14.2.21" "@next/swc-win32-x64-msvc" "14.2.21" +next@^15.2.0: + version "15.2.4" + resolved "https://registry.yarnpkg.com/next/-/next-15.2.4.tgz#e05225e9511df98e3b2edc713e17f4c970bff961" + integrity sha512-VwL+LAaPSxEkd3lU2xWbgEOtrM8oedmyhBqaVNmgKB+GvZlCy9rgaEc+y2on0wv+l0oSFqLtYD6dcC1eAedUaQ== + dependencies: + "@next/env" "15.2.4" + "@swc/counter" "0.1.3" + "@swc/helpers" "0.5.15" + busboy "1.6.0" + caniuse-lite "^1.0.30001579" + postcss "8.4.31" + styled-jsx "5.1.6" + optionalDependencies: + "@next/swc-darwin-arm64" "15.2.4" + "@next/swc-darwin-x64" "15.2.4" + "@next/swc-linux-arm64-gnu" "15.2.4" + "@next/swc-linux-arm64-musl" "15.2.4" + "@next/swc-linux-x64-gnu" "15.2.4" + "@next/swc-linux-x64-musl" "15.2.4" + "@next/swc-win32-arm64-msvc" "15.2.4" + "@next/swc-win32-x64-msvc" "15.2.4" + sharp "^0.33.5" + nice-try@^1.0.4: version "1.0.5" resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" @@ -16590,6 +18079,11 @@ nwsapi@^2.2.0: resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.18.tgz#3c4d7927e1ef4d042d319438ecfda6cd81b7ee41" integrity sha512-p1TRH/edngVEHVbwqWnxUViEmq5znDvyB+Sik5cmuLpGOIfDf/39zLiq3swPF8Vakqn+gvNiOQAZu8djYlQILA== +nwsapi@^2.2.16: + version "2.2.20" + resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.20.tgz#22e53253c61e7b0e7e93cef42c891154bcca11ef" + integrity sha512-/ieB+mDe4MrrKMT8z+mQL8klXydZWGR5Dowt4RAGKbJ3kIGEx3X4ljUo+6V73IXtUPWgfOlU5B9MlGxFO5T+cA== + nx@16.10.0, "nx@>=16.5.1 < 17": version "16.10.0" resolved "https://registry.yarnpkg.com/nx/-/nx-16.10.0.tgz#b070461f7de0a3d7988bd78558ea84cda3543ace" @@ -16694,7 +18188,7 @@ object.assign@^4.1.2, object.assign@^4.1.4, object.assign@^4.1.7: has-symbols "^1.1.0" object-keys "^1.1.1" -object.entries@^1.1.0, object.entries@^1.1.5, object.entries@^1.1.8: +object.entries@^1.1.0, object.entries@^1.1.5, object.entries@^1.1.8, object.entries@^1.1.9: version "1.1.9" resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.9.tgz#e4770a6a1444afb61bd39f984018b5bede25f8b3" integrity sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw== @@ -16820,6 +18314,18 @@ open@^8.0.9, open@^8.4.0: is-docker "^2.1.1" is-wsl "^2.2.0" +openapi-fetch@^0.13.4: + version "0.13.5" + resolved "https://registry.yarnpkg.com/openapi-fetch/-/openapi-fetch-0.13.5.tgz#805606860d85b8ba8c2e7cb36ea30b473d8065d9" + integrity sha512-AQK8T9GSKFREFlN1DBXTYsLjs7YV2tZcJ7zUWxbjMoQmj8dDSFRrzhLCbHPZWA1TMV3vACqfCxLEZcwf2wxV6Q== + dependencies: + openapi-typescript-helpers "^0.0.15" + +openapi-typescript-helpers@^0.0.15: + version "0.0.15" + resolved "https://registry.yarnpkg.com/openapi-typescript-helpers/-/openapi-typescript-helpers-0.0.15.tgz#96ffa762a5e01ef66a661b163d5f1109ed1967ed" + integrity sha512-opyTPaunsklCBpTK8JGef6mfPhLSnyy5a0IN9vKtx3+4aExf+KxEqYwIy3hqkedXIB97u357uLMJsOnm3GVjsw== + optionator@^0.9.1, optionator@^0.9.3: version "0.9.4" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.4.tgz#7ea1c1a5d91d764fb282139c88fe11e182a3a734" @@ -17119,6 +18625,19 @@ parse-entities@^2.0.0: is-decimal "^1.0.0" is-hexadecimal "^1.0.0" +parse-entities@^4.0.0: + version "4.0.2" + resolved "https://registry.yarnpkg.com/parse-entities/-/parse-entities-4.0.2.tgz#61d46f5ed28e4ee62e9ddc43d6b010188443f159" + integrity sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw== + dependencies: + "@types/unist" "^2.0.0" + character-entities-legacy "^3.0.0" + character-reference-invalid "^2.0.0" + decode-named-character-reference "^1.0.0" + is-alphanumerical "^2.0.0" + is-decimal "^2.0.0" + is-hexadecimal "^2.0.0" + parse-json@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9" @@ -17163,6 +18682,13 @@ parse5@6.0.1, parse5@^6.0.0, parse5@^6.0.1: resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw== +parse5@^7.2.1: + version "7.2.1" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-7.2.1.tgz#8928f55915e6125f430cc44309765bf17556a33a" + integrity sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ== + dependencies: + entities "^4.5.0" + parseurl@~1.3.2, parseurl@~1.3.3: version "1.3.3" resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" @@ -17916,6 +19442,11 @@ property-information@^5.0.0, property-information@^5.3.0: dependencies: xtend "^4.0.0" +property-information@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/property-information/-/property-information-7.0.0.tgz#3508a6d6b0b8eb3ca6eb2c6623b164d2ed2ab112" + integrity sha512-7D/qOz/+Y4X/rzSB6jKxKUsQnphO046ei8qxG59mtM3RG3DHgTK81HrxrmoDVINJb8NKT5ZsRbwHvQ6B68Iyhg== + protobufjs@^6.11.2, protobufjs@^6.8.8, protobufjs@~6.11.2, protobufjs@~6.11.3: version "6.11.4" resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-6.11.4.tgz#29a412c38bf70d89e537b6d02d904a6f448173aa" @@ -18050,6 +19581,11 @@ qrcode.react@^1.0.1: prop-types "^15.6.0" qr.js "0.0.0" +qrcode.react@^4.1.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/qrcode.react/-/qrcode.react-4.2.0.tgz#1bce8363f348197d145c0da640929a24c83cbca3" + integrity sha512-QpgqWi8rD9DsS9EP3z7BT+5lY5SFhsqGjpgW5DY/i3mK4M9DTBNz3ErMi8BWYEfI3L0d8GIbGmcdFAS1uIRGjA== + qs@6.13.0: version "6.13.0" resolved "https://registry.yarnpkg.com/qs/-/qs-6.13.0.tgz#6ca3bd58439f7e245655798997787b0d88a51906" @@ -18057,7 +19593,7 @@ qs@6.13.0: dependencies: side-channel "^1.0.6" -qs@^6.10.0, qs@^6.12.3: +qs@^6.10.0, qs@^6.12.3, qs@^6.14.0: version "6.14.0" resolved "https://registry.yarnpkg.com/qs/-/qs-6.14.0.tgz#c63fa40680d2c5c941412a0e899c89af60c0a930" integrity sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w== @@ -18221,7 +19757,7 @@ react-docgen@^5.0.0: node-dir "^0.1.10" strip-indent "^3.0.0" -react-dom@^18, react-dom@^18.2.0: +react-dom@^18, react-dom@^18.0.0, react-dom@^18.2.0: version "18.3.1" resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.3.1.tgz#c2265d79511b57d479b3dd3fdfa51536494c5cb4" integrity sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw== @@ -18264,6 +19800,14 @@ react-hook-form@^7.14.2: resolved "https://registry.yarnpkg.com/react-hook-form/-/react-hook-form-7.54.2.tgz#8c26ed54c71628dff57ccd3c074b1dd377cfb211" integrity sha512-eHpAUgUjWbZocoQYUHposymRb4ZP6d0uwUnooL2uOybA9/3tPUvoAKqEWK1WaSiTxxOfTpffNZP7QwlnM3/gEg== +react-i18next@^15.4.0: + version "15.4.1" + resolved "https://registry.yarnpkg.com/react-i18next/-/react-i18next-15.4.1.tgz#33f3e89c2f6c68e2bfcbf9aa59986ad42fe78758" + integrity sha512-ahGab+IaSgZmNPYXdV1n+OYky95TGpFwnKRflX/16dY04DsYYKHtVLjeny7sBSCREEcoMbAgSkFiGLF5g5Oofw== + dependencies: + "@babel/runtime" "^7.25.0" + html-parse-stringify "^3.0.1" + react-identicons@^1.2.5: version "1.2.5" resolved "https://registry.yarnpkg.com/react-identicons/-/react-identicons-1.2.5.tgz#3502249e49d88f4e3500092694410a984bb102fa" @@ -18303,11 +19847,33 @@ react-load-script@^0.0.6: resolved "https://registry.yarnpkg.com/react-load-script/-/react-load-script-0.0.6.tgz#db6851236aaa25bb622677a2eb51dad4f8d2c258" integrity sha512-aRGxDGP9VoLxcsaYvKWIW+LRrMOzz2eEcubTS4NvQPPugjk2VvMhow0wWTkSl7RxookomD1MwcP4l5UStg5ShQ== +react-markdown@^9.0.3: + version "9.1.0" + resolved "https://registry.yarnpkg.com/react-markdown/-/react-markdown-9.1.0.tgz#606bd74c6af131ba382a7c1282ff506708ed2e26" + integrity sha512-xaijuJB0kzGiUdG7nc2MOMDUDBWPyGAjZtUrow9XxUeua8IqeP+VlIfAZ3bphpcLTnSZXz6z9jcVC/TCwbfgdw== + dependencies: + "@types/hast" "^3.0.0" + "@types/mdast" "^4.0.0" + devlop "^1.0.0" + hast-util-to-jsx-runtime "^2.0.0" + html-url-attributes "^3.0.0" + mdast-util-to-hast "^13.0.0" + remark-parse "^11.0.0" + remark-rehype "^11.0.0" + unified "^11.0.0" + unist-util-visit "^5.0.0" + vfile "^6.0.0" + react-merge-refs@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/react-merge-refs/-/react-merge-refs-1.1.0.tgz#73d88b892c6c68cbb7a66e0800faa374f4c38b06" integrity sha512-alTKsjEL0dKH/ru1Iyn7vliS2QRcBp9zZPGoWxUOvRGWPUYgjo+V01is7p04It6KhgrzhJGnIj9GgX8W4bZoCQ== +react-random-avatars@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/react-random-avatars/-/react-random-avatars-1.3.1.tgz#cfea11dd80262939656e4134438fa76eb2d95371" + integrity sha512-HyMGoWniK2D5CRthoA1he4c6LVFIX/yBjp4qarEXN/DsgJ+1R6M5gJwYhTBoATO+3itrGCAGnKZNqimG1sDjAA== + react-refresh-typescript@^2.0.2: version "2.0.10" resolved "https://registry.yarnpkg.com/react-refresh-typescript/-/react-refresh-typescript-2.0.10.tgz#8dfe40d2a51866926d97fb7150d139e70d649eb2" @@ -18415,7 +19981,16 @@ react-transition-group@^4.4.5: loose-envify "^1.4.0" prop-types "^15.6.2" -react@^18, react@^18.2.0: +react-world-flags@^1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/react-world-flags/-/react-world-flags-1.6.0.tgz#7b075aa9be8a1b3789451a9df2cf491294e6fbae" + integrity sha512-eutSeAy5YKoVh14js/JUCSlA6EBk1n4k+bDaV+NkNB50VhnG+f4QDTpYycnTUTsZ5cqw/saPmk0Z4Fa0VVZ1Iw== + dependencies: + svg-country-flags "^1.2.10" + svgo "^3.0.2" + world-countries "^5.0.0" + +react@^18, react@^18.0.0, react@^18.2.0: version "18.3.1" resolved "https://registry.yarnpkg.com/react/-/react-18.3.1.tgz#49ab892009c53933625bd16b2533fc754cab2891" integrity sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ== @@ -18758,6 +20333,27 @@ remark-parse@8.0.3: vfile-location "^3.0.0" xtend "^4.0.1" +remark-parse@^11.0.0: + version "11.0.0" + resolved "https://registry.yarnpkg.com/remark-parse/-/remark-parse-11.0.0.tgz#aa60743fcb37ebf6b069204eb4da304e40db45a1" + integrity sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA== + dependencies: + "@types/mdast" "^4.0.0" + mdast-util-from-markdown "^2.0.0" + micromark-util-types "^2.0.0" + unified "^11.0.0" + +remark-rehype@^11.0.0: + version "11.1.2" + resolved "https://registry.yarnpkg.com/remark-rehype/-/remark-rehype-11.1.2.tgz#2addaadda80ca9bd9aa0da763e74d16327683b37" + integrity sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw== + dependencies: + "@types/hast" "^3.0.0" + "@types/mdast" "^4.0.0" + mdast-util-to-hast "^13.0.0" + unified "^11.0.0" + vfile "^6.0.0" + remark-slug@^6.0.0: version "6.1.0" resolved "https://registry.yarnpkg.com/remark-slug/-/remark-slug-6.1.0.tgz#0503268d5f0c4ecb1f33315c00465ccdd97923ce" @@ -19004,6 +20600,11 @@ rollup@^3.17.2, rollup@^3.2.1: optionalDependencies: fsevents "~2.3.2" +rrweb-cssom@^0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/rrweb-cssom/-/rrweb-cssom-0.8.0.tgz#3021d1b4352fbf3b614aaeed0bc0d5739abe0bc2" + integrity sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw== + rspack-resolver@^1.1.0: version "1.1.2" resolved "https://registry.yarnpkg.com/rspack-resolver/-/rspack-resolver-1.1.2.tgz#33b91858f7ae962d086ab00cbf66b8a4b8e9b570" @@ -19139,6 +20740,13 @@ saxes@^5.0.1: dependencies: xmlchars "^2.2.0" +saxes@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/saxes/-/saxes-6.0.0.tgz#fe5b4a4768df4f14a201b1ba6a65c1f3d9988cc5" + integrity sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA== + dependencies: + xmlchars "^2.2.0" + scheduler@^0.23.2: version "0.23.2" resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.2.tgz#414ba64a3b282892e944cf2108ecc078d115cdc3" @@ -19217,7 +20825,7 @@ semver@7.5.3: dependencies: lru-cache "^6.0.0" -semver@7.x, semver@^7.0.0, semver@^7.1.1, semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8, semver@^7.5.0, semver@^7.5.3, semver@^7.5.4, semver@^7.6.3: +semver@7.x, semver@^7.0.0, semver@^7.1.1, semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8, semver@^7.5.0, semver@^7.5.3, semver@^7.5.4, semver@^7.6.0, semver@^7.6.3: version "7.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.1.tgz#abd5098d82b18c6c81f6074ff2647fd3e7220c9f" integrity sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA== @@ -19382,7 +20990,7 @@ shallow-clone@^3.0.0: dependencies: kind-of "^6.0.2" -sharp@^0.33.1: +sharp@^0.33.1, sharp@^0.33.5: version "0.33.5" resolved "https://registry.yarnpkg.com/sharp/-/sharp-0.33.5.tgz#13e0e4130cc309d6a9497596715240b2ec0c594e" integrity sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw== @@ -19633,7 +21241,7 @@ source-list-map@^2.0.0: resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34" integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw== -source-map-js@^1.0.2, source-map-js@^1.2.1: +source-map-js@^1.0.1, source-map-js@^1.0.2, source-map-js@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46" integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA== @@ -19687,6 +21295,11 @@ space-separated-tokens@^1.0.0: resolved "https://registry.yarnpkg.com/space-separated-tokens/-/space-separated-tokens-1.1.5.tgz#85f32c3d10d9682007e917414ddc5c26d1aa6899" integrity sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA== +space-separated-tokens@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz#1ecd9d2350a3844572c3f4a312bceb018348859f" + integrity sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q== + spdx-correct@^3.0.0: version "3.2.0" resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.2.0.tgz#4f5ab0668f0059e34f9c00dce331784a12de4e9c" @@ -20045,6 +21658,14 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" +stringify-entities@^4.0.0: + version "4.0.4" + resolved "https://registry.yarnpkg.com/stringify-entities/-/stringify-entities-4.0.4.tgz#b3b79ef5f277cc4ac73caeb0236c5ba939b3a4f3" + integrity sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg== + dependencies: + character-entities-html4 "^2.0.0" + character-entities-legacy "^3.0.0" + "strip-ansi-cjs@npm:strip-ansi@^6.0.1": version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" @@ -20149,6 +21770,13 @@ style-loader@^3.3.1: resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-3.3.4.tgz#f30f786c36db03a45cbd55b6a70d930c479090e7" integrity sha512-0WqXzrsMTyb8yjZJHDqwmnwRJvhALK9LfRtRc6B4UTWe8AijYLZYZ9thuJTZc2VfQWINADW/j+LiJnfy2RoC1w== +style-to-js@^1.0.0: + version "1.1.16" + resolved "https://registry.yarnpkg.com/style-to-js/-/style-to-js-1.1.16.tgz#e6bd6cd29e250bcf8fa5e6591d07ced7575dbe7a" + integrity sha512-/Q6ld50hKYPH3d/r6nr117TZkHR0w0kGGIVfpG9N6D8NymRPM9RqCUv4pRpJ62E5DqOYx2AFpbZMyCPnjQCnOw== + dependencies: + style-to-object "1.0.8" + style-to-object@0.3.0, style-to-object@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/style-to-object/-/style-to-object-0.3.0.tgz#b1b790d205991cc783801967214979ee19a76e46" @@ -20156,6 +21784,13 @@ style-to-object@0.3.0, style-to-object@^0.3.0: dependencies: inline-style-parser "0.1.1" +style-to-object@1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/style-to-object/-/style-to-object-1.0.8.tgz#67a29bca47eaa587db18118d68f9d95955e81292" + integrity sha512-xT47I/Eo0rwJmaXC4oilDGDWLohVhR6o/xAQcPQN8q6QBuZVL8qMYL85kLmST5cPjAorwvqIA4qXTRQoYHaL6g== + dependencies: + inline-style-parser "0.2.4" + styled-jsx@5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/styled-jsx/-/styled-jsx-5.1.1.tgz#839a1c3aaacc4e735fed0781b8619ea5d0009d1f" @@ -20163,6 +21798,13 @@ styled-jsx@5.1.1: dependencies: client-only "0.0.1" +styled-jsx@5.1.6: + version "5.1.6" + resolved "https://registry.yarnpkg.com/styled-jsx/-/styled-jsx-5.1.6.tgz#83b90c077e6c6a80f7f5e8781d0f311b2fe41499" + integrity sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA== + dependencies: + client-only "0.0.1" + stylehacks@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/stylehacks/-/stylehacks-5.1.1.tgz#7934a34eb59d7152149fa69d6e9e56f2fc34bcc9" @@ -20210,6 +21852,11 @@ supports-preserve-symlinks-flag@^1.0.0: resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== +svg-country-flags@^1.2.10: + version "1.2.10" + resolved "https://registry.yarnpkg.com/svg-country-flags/-/svg-country-flags-1.2.10.tgz#b1b94e9f08f065d7b3d0ff9ab359898edd4dd3b0" + integrity sha512-xrqwo0TYf/h2cfPvGpjdSuSguUbri4vNNizBnwzoZnX0xGo3O5nGJMlbYEp7NOYcnPGBm6LE2axqDWSB847bLw== + svg-parser@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/svg-parser/-/svg-parser-2.0.4.tgz#fdc2e29e13951736140b76cb122c8ee6630eb6b5" @@ -20228,6 +21875,19 @@ svgo@^2.7.0, svgo@^2.8.0: picocolors "^1.0.0" stable "^0.1.8" +svgo@^3.0.2: + version "3.3.2" + resolved "https://registry.yarnpkg.com/svgo/-/svgo-3.3.2.tgz#ad58002652dffbb5986fc9716afe52d869ecbda8" + integrity sha512-OoohrmuUlBs8B8o6MB2Aevn+pRIH9zDALSR+6hhqVfa6fRwG/Qw9VUMSMW9VNg2CFc/MTIfabtdOVl9ODIJjpw== + dependencies: + "@trysound/sax" "0.2.0" + commander "^7.2.0" + css-select "^5.1.0" + css-tree "^2.3.1" + css-what "^6.1.0" + csso "^5.0.5" + picocolors "^1.0.0" + symbol-observable@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-2.0.3.tgz#5b521d3d07a43c351055fa43b8355b62d33fd16a" @@ -20505,6 +22165,18 @@ tinyrainbow@^1.2.0: resolved "https://registry.yarnpkg.com/tinyrainbow/-/tinyrainbow-1.2.0.tgz#5c57d2fc0fb3d1afd78465c33ca885d04f02abb5" integrity sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ== +tldts-core@^6.1.85: + version "6.1.85" + resolved "https://registry.yarnpkg.com/tldts-core/-/tldts-core-6.1.85.tgz#6f6b795468c0b5f7660a11c7306ff2766ceaea7e" + integrity sha512-DTjUVvxckL1fIoPSb3KE7ISNtkWSawZdpfxGxwiIrZoO6EbHVDXXUIlIuWympPaeS+BLGyggozX/HTMsRAdsoA== + +tldts@^6.1.32: + version "6.1.85" + resolved "https://registry.yarnpkg.com/tldts/-/tldts-6.1.85.tgz#c7636ad5ec2f02591264360da51c6b584ef6f7bb" + integrity sha512-gBdZ1RjCSevRPFix/hpaUWeak2/RNUZB4/8frF1r5uYMHjFptkiT0JXIebWvgI/0ZHXvxaUDDJshiA0j6GdL3w== + dependencies: + tldts-core "^6.1.85" + tmp@^0.0.33: version "0.0.33" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" @@ -20596,6 +22268,13 @@ tough-cookie@^4.0.0: universalify "^0.2.0" url-parse "^1.5.3" +tough-cookie@^5.0.0: + version "5.1.2" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-5.1.2.tgz#66d774b4a1d9e12dc75089725af3ac75ec31bed7" + integrity sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A== + dependencies: + tldts "^6.1.32" + tr46@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/tr46/-/tr46-2.1.0.tgz#fa87aa81ca5d5941da8cbf1f9b749dc969a4e240" @@ -20603,11 +22282,23 @@ tr46@^2.1.0: dependencies: punycode "^2.1.1" +tr46@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-5.1.0.tgz#4a077922360ae807e172075ce5beb79b36e4a101" + integrity sha512-IUWnUK7ADYR5Sl1fZlO1INDUhVhatWl7BtJWsIhwJ0UAK7ilzzIa8uIqOO/aYVWHZPJkKbEL+362wrzoeRF7bw== + dependencies: + punycode "^2.3.1" + tr46@~0.0.3: version "0.0.3" resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== +trim-lines@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/trim-lines/-/trim-lines-3.0.1.tgz#d802e332a07df861c48802c04321017b1bd87338" + integrity sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg== + trim-newlines@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613" @@ -20633,11 +22324,21 @@ trough@^1.0.0: resolved "https://registry.yarnpkg.com/trough/-/trough-1.0.5.tgz#b8b639cefad7d0bb2abd37d433ff8293efa5f406" integrity sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA== +trough@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/trough/-/trough-2.2.0.tgz#94a60bd6bd375c152c1df911a4b11d5b0256f50f" + integrity sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw== + ts-api-utils@^1.0.1: version "1.4.3" resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.4.3.tgz#bfc2215fe6528fecab2b0fba570a2e8a4263b064" integrity sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw== +ts-api-utils@^2.0.1: + version "2.1.0" + resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-2.1.0.tgz#595f7094e46eed364c13fd23e75f9513d29baf91" + integrity sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ== + ts-dedent@^2.0.0, ts-dedent@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/ts-dedent/-/ts-dedent-2.2.0.tgz#39e4bd297cd036292ae2394eb3412be63f563bb5" @@ -21017,6 +22718,19 @@ unified@9.2.0: trough "^1.0.0" vfile "^4.0.0" +unified@^11.0.0: + version "11.0.5" + resolved "https://registry.yarnpkg.com/unified/-/unified-11.0.5.tgz#f66677610a5c0a9ee90cab2b8d4d66037026d9e1" + integrity sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA== + dependencies: + "@types/unist" "^3.0.0" + bail "^2.0.0" + devlop "^1.0.0" + extend "^3.0.0" + is-plain-obj "^4.0.0" + trough "^2.0.0" + vfile "^6.0.0" + union-value@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847" @@ -21084,11 +22798,25 @@ unist-util-is@^4.0.0: resolved "https://registry.yarnpkg.com/unist-util-is/-/unist-util-is-4.1.0.tgz#976e5f462a7a5de73d94b706bac1b90671b57797" integrity sha512-ZOQSsnce92GrxSqlnEEseX0gi7GH9zTJZ0p9dtu87WRb/37mMPO2Ilx1s/t9vBHrFhbgweUwb+t7cIn5dxPhZg== +unist-util-is@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/unist-util-is/-/unist-util-is-6.0.0.tgz#b775956486aff107a9ded971d996c173374be424" + integrity sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw== + dependencies: + "@types/unist" "^3.0.0" + unist-util-position@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/unist-util-position/-/unist-util-position-3.1.0.tgz#1c42ee6301f8d52f47d14f62bbdb796571fa2d47" integrity sha512-w+PkwCbYSFw8vpgWD0v7zRCl1FpY3fjDSQ3/N/wNd9Ffa4gPi8+4keqt99N3XW6F99t/mUzp2xAhNmfKWp95QA== +unist-util-position@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/unist-util-position/-/unist-util-position-5.0.0.tgz#678f20ab5ca1207a97d7ea8a388373c9cf896be4" + integrity sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA== + dependencies: + "@types/unist" "^3.0.0" + unist-util-remove-position@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/unist-util-remove-position/-/unist-util-remove-position-2.0.1.tgz#5d19ca79fdba712301999b2b73553ca8f3b352cc" @@ -21110,6 +22838,13 @@ unist-util-stringify-position@^2.0.0: dependencies: "@types/unist" "^2.0.2" +unist-util-stringify-position@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz#449c6e21a880e0855bf5aabadeb3a740314abac2" + integrity sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ== + dependencies: + "@types/unist" "^3.0.0" + unist-util-visit-parents@^3.0.0: version "3.1.1" resolved "https://registry.yarnpkg.com/unist-util-visit-parents/-/unist-util-visit-parents-3.1.1.tgz#65a6ce698f78a6b0f56aa0e88f13801886cdaef6" @@ -21118,6 +22853,14 @@ unist-util-visit-parents@^3.0.0: "@types/unist" "^2.0.0" unist-util-is "^4.0.0" +unist-util-visit-parents@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz#4d5f85755c3b8f0dc69e21eca5d6d82d22162815" + integrity sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw== + dependencies: + "@types/unist" "^3.0.0" + unist-util-is "^6.0.0" + unist-util-visit@2.0.3, unist-util-visit@^2.0.0: version "2.0.3" resolved "https://registry.yarnpkg.com/unist-util-visit/-/unist-util-visit-2.0.3.tgz#c3703893146df47203bb8a9795af47d7b971208c" @@ -21127,6 +22870,15 @@ unist-util-visit@2.0.3, unist-util-visit@^2.0.0: unist-util-is "^4.0.0" unist-util-visit-parents "^3.0.0" +unist-util-visit@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/unist-util-visit/-/unist-util-visit-5.0.0.tgz#a7de1f31f72ffd3519ea71814cccf5fd6a9217d6" + integrity sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg== + dependencies: + "@types/unist" "^3.0.0" + unist-util-is "^6.0.0" + unist-util-visit-parents "^6.0.0" + universal-user-agent@^6.0.0: version "6.0.1" resolved "https://registry.yarnpkg.com/universal-user-agent/-/universal-user-agent-6.0.1.tgz#15f20f55da3c930c57bddbf1734c6654d5fd35aa" @@ -21393,6 +23145,14 @@ vfile-message@^2.0.0: "@types/unist" "^2.0.0" unist-util-stringify-position "^2.0.0" +vfile-message@^4.0.0: + version "4.0.2" + resolved "https://registry.yarnpkg.com/vfile-message/-/vfile-message-4.0.2.tgz#c883c9f677c72c166362fd635f21fc165a7d1181" + integrity sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw== + dependencies: + "@types/unist" "^3.0.0" + unist-util-stringify-position "^4.0.0" + vfile@^4.0.0: version "4.2.1" resolved "https://registry.yarnpkg.com/vfile/-/vfile-4.2.1.tgz#03f1dce28fc625c625bc6514350fbdb00fa9e624" @@ -21403,6 +23163,14 @@ vfile@^4.0.0: unist-util-stringify-position "^2.0.0" vfile-message "^2.0.0" +vfile@^6.0.0: + version "6.0.3" + resolved "https://registry.yarnpkg.com/vfile/-/vfile-6.0.3.tgz#3652ab1c496531852bf55a6bac57af981ebc38ab" + integrity sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q== + dependencies: + "@types/unist" "^3.0.0" + vfile-message "^4.0.0" + victory-vendor@^36.6.8: version "36.9.2" resolved "https://registry.yarnpkg.com/victory-vendor/-/victory-vendor-36.9.2.tgz#668b02a448fa4ea0f788dbf4228b7e64669ff801" @@ -21428,6 +23196,11 @@ vm-browserify@^1.0.1: resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0" integrity sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ== +void-elements@3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-3.1.0.tgz#614f7fbf8d801f0bb5f0661f5b2f5785750e4f09" + integrity sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w== + vscode-oniguruma@^1.6.1: version "1.7.0" resolved "https://registry.yarnpkg.com/vscode-oniguruma/-/vscode-oniguruma-1.7.0.tgz#439bfad8fe71abd7798338d1cd3dc53a8beea94b" @@ -21452,6 +23225,13 @@ w3c-xmlserializer@^2.0.0: dependencies: xml-name-validator "^3.0.0" +w3c-xmlserializer@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz#f925ba26855158594d907313cedd1476c5967f6c" + integrity sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA== + dependencies: + xml-name-validator "^5.0.0" + walker@^1.0.7, walker@~1.0.5: version "1.0.8" resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.8.tgz#bd498db477afe573dc04185f011d3ab8a8d7653f" @@ -21519,6 +23299,11 @@ webidl-conversions@^6.1.0: resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-6.1.0.tgz#9111b4d7ea80acd40f5270d666621afa78b69514" integrity sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w== +webidl-conversions@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-7.0.0.tgz#256b4e1882be7debbf01d05f0aa2039778ea080a" + integrity sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g== + webpack-cli@^4.8.0: version "4.10.0" resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-4.10.0.tgz#37c1d69c8d85214c5a65e589378f53aec64dab31" @@ -21749,11 +23534,31 @@ whatwg-encoding@^1.0.5: dependencies: iconv-lite "0.4.24" +whatwg-encoding@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz#d0f4ef769905d426e1688f3e34381a99b60b76e5" + integrity sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ== + dependencies: + iconv-lite "0.6.3" + whatwg-mimetype@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf" integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g== +whatwg-mimetype@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz#bc1bf94a985dc50388d54a9258ac405c3ca2fc0a" + integrity sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg== + +whatwg-url@^14.0.0, whatwg-url@^14.1.0: + version "14.2.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-14.2.0.tgz#4ee02d5d725155dae004f6ae95c73e7ef5d95663" + integrity sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw== + dependencies: + tr46 "^5.1.0" + webidl-conversions "^7.0.0" + whatwg-url@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" @@ -21900,6 +23705,11 @@ workerpool@^6.5.1: resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.5.1.tgz#060f73b39d0caf97c6db64da004cd01b4c099544" integrity sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA== +world-countries@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/world-countries/-/world-countries-5.1.0.tgz#ea6b6156d9ef80c9ae6ff1e238d11b7ebd90351f" + integrity sha512-CXR6EBvTbArDlDDIWU3gfKb7Qk0ck2WNZ234b/A0vuecPzIfzzxH+O6Ejnvg1sT8XuiZjVlzOH0h08ZtaO7g0w== + "wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" @@ -21994,7 +23804,7 @@ ws@^7, ws@^7.4.6: resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.10.tgz#58b5c20dc281633f6c19113f39b349bd8bd558d9" integrity sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ== -ws@^8.13.0, ws@^8.2.3: +ws@^8.13.0, ws@^8.18.0, ws@^8.2.3: version "8.18.1" resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.1.tgz#ea131d3784e1dfdff91adb0a4a116b127515e3cb" integrity sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w== @@ -22011,6 +23821,11 @@ xml-name-validator@^3.0.0: resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a" integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw== +xml-name-validator@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-5.0.0.tgz#82be9b957f7afdacf961e5980f1bf227c0bf7673" + integrity sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg== + xml2js@^0.6.1: version "0.6.2" resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.6.2.tgz#dd0b630083aa09c161e25a4d0901e2b2a929b499" @@ -22146,6 +23961,11 @@ yup@^0.32.9: property-expr "^2.0.4" toposort "^2.0.2" +zod@^3.24.1: + version "3.24.2" + resolved "https://registry.yarnpkg.com/zod/-/zod-3.24.2.tgz#8efa74126287c675e92f46871cfc8d15c34372b3" + integrity sha512-lY7CDW43ECgW9u1TcT3IoXHflywfVqDYze4waEz812jR/bZ8FHDsl7pFQoSZTz5N+2NqRXs8GBwnAwo3ZNxqhQ== + zustand@^4.5.5: version "4.5.6" resolved "https://registry.yarnpkg.com/zustand/-/zustand-4.5.6.tgz#6857d52af44874a79fb3408c9473f78367255c96" @@ -22158,6 +23978,11 @@ zwitch@^1.0.0: resolved "https://registry.yarnpkg.com/zwitch/-/zwitch-1.0.5.tgz#d11d7381ffed16b742f6af7b3f223d5cd9fe9920" integrity sha512-V50KMwwzqJV0NpZIZFwfOD5/lyny3WlSzRiXgA0G7VUnRlqttta1L6UQIHzd6EuBY/cHGfwTIck7w1yH6Q5zUw== +zwitch@^2.0.0: + version "2.0.4" + resolved "https://registry.yarnpkg.com/zwitch/-/zwitch-2.0.4.tgz#c827d4b0acb76fc3e685a4c6ec2902d51070e9d7" + integrity sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A== + zxcvbn@^4.4.2: version "4.4.2" resolved "https://registry.yarnpkg.com/zxcvbn/-/zxcvbn-4.4.2.tgz#28ec17cf09743edcab056ddd8b1b06262cc73c30" From f87428485071211cbe1e8691451ee214672af9b7 Mon Sep 17 00:00:00 2001 From: Tommy Verrall Date: Fri, 4 Apr 2025 18:47:35 +0200 Subject: [PATCH 10/48] - Update beyond tauri v2 - use the latest and greatest - fixed links to use the command shell - app version changes, need to be fixed to allow the auto updater too work --- nym-wallet/Cargo.lock | 230 ++---------------- nym-wallet/package.json | 1 + nym-wallet/src-tauri/Capabilities.toml | 2 + nym-wallet/src-tauri/Cargo.toml | 18 +- .../capabilities/main-capability.json | 19 ++ .../src-tauri/capabilities/migrated.json | 11 - .../capabilities/shell-capability.json | 11 + .../src-tauri/gen/schemas/capabilities.json | 2 +- nym-wallet/src-tauri/src/main.rs | 3 + .../src-tauri/src/operations/app/link.rs | 22 ++ .../src-tauri/src/operations/app/mod.rs | 1 + nym-wallet/src-tauri/tauri.conf.json | 7 +- .../Balance/modals/TransferModalSuccess.tsx | 2 +- nym-wallet/src/components/Bonding/Bond.tsx | 2 +- .../src/components/Bonding/BondedGateway.tsx | 2 +- .../src/components/Bonding/BondedMixnode.tsx | 2 +- .../src/components/Bonding/BondedNymNode.tsx | 2 +- .../Bonding/modals/ConfirmationModal.tsx | 2 +- nym-wallet/src/components/CopyToClipboard.tsx | 2 +- .../components/Delegation/DelegationItem.tsx | 2 +- .../components/Delegation/DelegationModal.tsx | 2 +- .../src/components/Delegation/Delegations.tsx | 2 +- .../Delegation/PendingDelegationItem.tsx | 2 +- .../src/components/Send/SendSuccessModal.tsx | 2 +- .../src/components/TauriLinkWrapper.tsx | 21 ++ nym-wallet/src/pages/balance/Balance.tsx | 4 +- nym-wallet/src/pages/delegation/index.tsx | 2 +- .../src/pages/node-settings/node-stats.tsx | 3 +- nym-wallet/src/utils/common.ts | 3 +- nym-wallet/src/utils/shellHelper.ts | 18 ++ yarn.lock | 7 + 31 files changed, 161 insertions(+), 248 deletions(-) create mode 100644 nym-wallet/src-tauri/Capabilities.toml create mode 100644 nym-wallet/src-tauri/capabilities/main-capability.json delete mode 100644 nym-wallet/src-tauri/capabilities/migrated.json create mode 100644 nym-wallet/src-tauri/capabilities/shell-capability.json create mode 100644 nym-wallet/src-tauri/src/operations/app/link.rs create mode 100644 nym-wallet/src/components/TauriLinkWrapper.tsx create mode 100644 nym-wallet/src/utils/shellHelper.ts diff --git a/nym-wallet/Cargo.lock b/nym-wallet/Cargo.lock index bdc7304084..0bd21ea525 100644 --- a/nym-wallet/Cargo.lock +++ b/nym-wallet/Cargo.lock @@ -587,17 +587,6 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e31ea183f6ee62ac8b8a8cf7feddd766317adfb13ff469de57ce033efd6a790" -[[package]] -name = "brotli" -version = "3.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d640d25bc63c50fb1f0b545ffd80207d2e10a4c965530809b40ba3386825c391" -dependencies = [ - "alloc-no-stdlib", - "alloc-stdlib", - "brotli-decompressor 2.5.1", -] - [[package]] name = "brotli" version = "7.0.0" @@ -606,17 +595,7 @@ checksum = "cc97b8f16f944bba54f0433f07e30be199b6dc2bd25937444bbad560bcea29bd" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", - "brotli-decompressor 4.0.2", -] - -[[package]] -name = "brotli-decompressor" -version = "2.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e2e4afe60d7dd600fdd3de8d0f08c2b7ec039712e3b6137ff98b7004e82de4f" -dependencies = [ - "alloc-no-stdlib", - "alloc-stdlib", + "brotli-decompressor", ] [[package]] @@ -1943,15 +1922,6 @@ dependencies = [ "paste", ] -[[package]] -name = "fluent-uri" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17c704e9dbe1ddd863da1e6ff3567795087b1eb201ce80d8fa81162e1516500d" -dependencies = [ - "bitflags 1.3.2", -] - [[package]] name = "fnv" version = "1.0.7" @@ -2917,16 +2887,6 @@ dependencies = [ "cc", ] -[[package]] -name = "ico" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "031530fe562d8c8d71c0635013d6d155bbfe8ba0aa4b4d2d24ce8af6b71047bd" -dependencies = [ - "byteorder", - "png", -] - [[package]] name = "ico" version = "0.4.0" @@ -3123,15 +3083,6 @@ dependencies = [ "serde", ] -[[package]] -name = "infer" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f551f8c3a39f68f986517db0d1759de85881894fdc7db798bd2a9df9cb04b7fc" -dependencies = [ - "cfb", -] - [[package]] name = "infer" version = "0.19.0" @@ -3295,52 +3246,18 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "json-patch" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb3fa5a61630976fc4c353c70297f2e93f1930e3ccee574d59d618ccbd5154ce" -dependencies = [ - "serde", - "serde_json", - "treediff", -] - -[[package]] -name = "json-patch" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b1fb8864823fad91877e6caea0baca82e49e8db50f8e5c9f9a453e27d3330fc" -dependencies = [ - "jsonptr 0.4.7", - "serde", - "serde_json", - "thiserror 1.0.69", -] - [[package]] name = "json-patch" version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "863726d7afb6bc2590eeff7135d923545e5e964f004c2ccf8716c25e70a86f08" dependencies = [ - "jsonptr 0.6.3", + "jsonptr", "serde", "serde_json", "thiserror 1.0.69", ] -[[package]] -name = "jsonptr" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c6e529149475ca0b2820835d3dce8fcc41c6b943ca608d32f35b449255e4627" -dependencies = [ - "fluent-uri", - "serde", - "serde_json", -] - [[package]] name = "jsonptr" version = "0.6.3" @@ -4231,7 +4148,7 @@ dependencies = [ "nym-vesting-contract-common", "nym-wallet-types", "once_cell", - "open 3.2.0", + "open", "pretty_env_logger", "rand_chacha 0.3.1", "reqwest 0.12.15", @@ -4242,8 +4159,8 @@ dependencies = [ "tap", "tauri", "tauri-build", - "tauri-codegen 1.2.1", - "tauri-macros 1.2.1", + "tauri-codegen", + "tauri-macros", "tauri-plugin-clipboard-manager", "tauri-plugin-process", "tauri-plugin-shell", @@ -4503,16 +4420,6 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" -[[package]] -name = "open" -version = "3.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2078c0039e6a54a0c42c28faa984e115fb4c2d5bf2208f77d1961002df8576f8" -dependencies = [ - "pathdiff", - "windows-sys 0.42.0", -] - [[package]] name = "open" version = "5.3.2" @@ -6591,10 +6498,10 @@ dependencies = [ "serialize-to-javascript", "swift-rs", "tauri-build", - "tauri-macros 2.1.1", + "tauri-macros", "tauri-runtime", "tauri-runtime-wry", - "tauri-utils 2.3.1", + "tauri-utils", "thiserror 2.0.12", "tokio", "tray-icon", @@ -6617,42 +6524,17 @@ dependencies = [ "dirs 6.0.0", "glob", "heck 0.5.0", - "json-patch 3.0.1", + "json-patch", "schemars", "semver", "serde", "serde_json", - "tauri-utils 2.3.1", + "tauri-utils", "tauri-winres", "toml 0.8.20", "walkdir", ] -[[package]] -name = "tauri-codegen" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14388d484b6b1b5dc0f6a7d6cc6433b3b230bec85eaa576adcdf3f9fafa49251" -dependencies = [ - "base64 0.13.1", - "brotli 3.5.0", - "ico 0.2.0", - "json-patch 0.2.7", - "plist", - "png", - "proc-macro2", - "quote", - "semver", - "serde", - "serde_json", - "sha2 0.10.8", - "tauri-utils 1.6.2", - "thiserror 1.0.69", - "time", - "uuid", - "walkdir", -] - [[package]] name = "tauri-codegen" version = "2.1.1" @@ -6660,9 +6542,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "458258b19032450ccf975840116ecf013e539eadbb74420bd890e8c56ab2b1a4" dependencies = [ "base64 0.22.1", - "brotli 7.0.0", - "ico 0.4.0", - "json-patch 3.0.1", + "brotli", + "ico", + "json-patch", "plist", "png", "proc-macro2", @@ -6672,7 +6554,7 @@ dependencies = [ "serde_json", "sha2 0.10.8", "syn 2.0.100", - "tauri-utils 2.3.1", + "tauri-utils", "thiserror 2.0.12", "time", "url", @@ -6680,20 +6562,6 @@ dependencies = [ "walkdir", ] -[[package]] -name = "tauri-macros" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "069319e5ecbe653a799b94b0690d9f9bf5d00f7b1d3989aa331c524d4e354075" -dependencies = [ - "heck 0.4.1", - "proc-macro2", - "quote", - "syn 1.0.109", - "tauri-codegen 1.2.1", - "tauri-utils 1.6.2", -] - [[package]] name = "tauri-macros" version = "2.1.1" @@ -6704,8 +6572,8 @@ dependencies = [ "proc-macro2", "quote", "syn 2.0.100", - "tauri-codegen 2.1.1", - "tauri-utils 2.3.1", + "tauri-codegen", + "tauri-utils", ] [[package]] @@ -6720,7 +6588,7 @@ dependencies = [ "schemars", "serde", "serde_json", - "tauri-utils 2.3.1", + "tauri-utils", "toml 0.8.20", "walkdir", ] @@ -6758,7 +6626,7 @@ checksum = "69d5eb3368b959937ad2aeaf6ef9a8f5d11e01ffe03629d3530707bbcb27ff5d" dependencies = [ "encoding_rs", "log", - "open 5.3.2", + "open", "os_pipe", "regex", "schemars", @@ -6782,7 +6650,7 @@ dependencies = [ "flate2", "futures-util", "http 1.3.1", - "infer 0.19.0", + "infer", "log", "minisign-verify", "osakit", @@ -6817,7 +6685,7 @@ dependencies = [ "raw-window-handle", "serde", "serde_json", - "tauri-utils 2.3.1", + "tauri-utils", "thiserror 2.0.12", "url", "windows 0.60.0", @@ -6842,7 +6710,7 @@ dependencies = [ "softbuffer", "tao", "tauri-runtime", - "tauri-utils 2.3.1", + "tauri-utils", "url", "webkit2gtk", "webview2-com", @@ -6850,34 +6718,6 @@ dependencies = [ "wry", ] -[[package]] -name = "tauri-utils" -version = "1.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c357952645e679de02cd35007190fcbce869b93ffc61b029f33fe02648453774" -dependencies = [ - "brotli 7.0.0", - "ctor", - "dunce", - "heck 0.5.0", - "html5ever", - "infer 0.13.0", - "json-patch 2.0.0", - "kuchikiki", - "log", - "memchr", - "phf 0.11.3", - "proc-macro2", - "quote", - "semver", - "serde", - "serde_json", - "serde_with", - "thiserror 1.0.69", - "url", - "windows-version", -] - [[package]] name = "tauri-utils" version = "2.3.1" @@ -6885,15 +6725,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f037e66c7638cc0a2213f61566932b9a06882b8346486579c90e4b019bac447" dependencies = [ "anyhow", - "brotli 7.0.0", + "brotli", "cargo_metadata 0.19.2", "ctor", "dunce", "glob", "html5ever", "http 1.3.1", - "infer 0.19.0", - "json-patch 3.0.1", + "infer", + "json-patch", "kuchikiki", "log", "memchr", @@ -7396,15 +7236,6 @@ dependencies = [ "petgraph", ] -[[package]] -name = "treediff" -version = "3.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "761e8d5ad7ce14bb82b7e61ccc0ca961005a275a060b9644a2431aa11553c2ff" -dependencies = [ - "serde_json", -] - [[package]] name = "try-lock" version = "0.2.5" @@ -8215,21 +8046,6 @@ dependencies = [ "windows-link", ] -[[package]] -name = "windows-sys" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" -dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", -] - [[package]] name = "windows-sys" version = "0.45.0" diff --git a/nym-wallet/package.json b/nym-wallet/package.json index 71dcad14cf..48432944c1 100644 --- a/nym-wallet/package.json +++ b/nym-wallet/package.json @@ -37,6 +37,7 @@ "@tauri-apps/api": "^2.4.0", "@tauri-apps/cli": "^2.4.0", "@tauri-apps/plugin-clipboard-manager": "^2.2.2", + "@tauri-apps/plugin-shell": "^2.2.1", "@tauri-apps/plugin-updater": "^2.0.0", "@tauri-apps/tauri-forage": "^1.0.0-beta.2", "big.js": "^6.2.1", diff --git a/nym-wallet/src-tauri/Capabilities.toml b/nym-wallet/src-tauri/Capabilities.toml new file mode 100644 index 0000000000..fbf2de6a33 --- /dev/null +++ b/nym-wallet/src-tauri/Capabilities.toml @@ -0,0 +1,2 @@ +[capabilities] +shell = { open = true } \ No newline at end of file diff --git a/nym-wallet/src-tauri/Cargo.toml b/nym-wallet/src-tauri/Cargo.toml index d8101b2515..e8a3424f4f 100644 --- a/nym-wallet/src-tauri/Cargo.toml +++ b/nym-wallet/src-tauri/Cargo.toml @@ -8,22 +8,22 @@ repository = "" default-run = "nym_wallet" edition = "2021" build = "src/build.rs" -rust-version = "1.76" +rust-version = "1.85" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [build-dependencies] -tauri-build = { version = "2", features = [] } +tauri-build = { version = "2.1.1", features = [] } -tauri-codegen = "=1.2.1" -tauri-macros = "=1.2.1" +tauri-codegen = "=2.1.1" +tauri-macros = "2.1.1" [dependencies] async-trait = "0.1.68" -tauri-plugin-updater = "2" -tauri-plugin-clipboard-manager = "2" -tauri-plugin-shell = "2" -tauri-plugin-process = "2" +tauri-plugin-updater = "2.7.0" +tauri-plugin-clipboard-manager = "2.2.2" +tauri-plugin-shell = "2.2.0" +tauri-plugin-process = "2.2.1" bip39 = { version = "2.0.0", features = ["zeroize", "rand"] } cfg-if = "1.0.0" colored = "2.0" @@ -35,7 +35,7 @@ futures = "0.3.15" itertools = "0.10" log = { version = "0.4", features = ["serde"] } once_cell = "1.7.2" -open = "3.2" +open = "5.3.2" pretty_env_logger = "0.4" reqwest = { version = "0.12.4", features = ["json"] } serde = { version = "1.0", features = ["derive"] } diff --git a/nym-wallet/src-tauri/capabilities/main-capability.json b/nym-wallet/src-tauri/capabilities/main-capability.json new file mode 100644 index 0000000000..f3f33083fb --- /dev/null +++ b/nym-wallet/src-tauri/capabilities/main-capability.json @@ -0,0 +1,19 @@ +{ + "$schema": "../gen/schemas/desktop-schema.json", + "identifier": "main-capability", + "description": "Default capability for Nym Wallet main window", + "windows": ["main"], + "platforms": ["linux", "macOS", "windows"], + "permissions": [ + "core:default", + "core:path:default", + "core:event:default", + "core:window:default", + "core:app:default", + "core:resources:default", + "core:menu:default", + "core:tray:default", + "shell:allow-open", + "core:window:allow-set-title" + ] + } \ No newline at end of file diff --git a/nym-wallet/src-tauri/capabilities/migrated.json b/nym-wallet/src-tauri/capabilities/migrated.json deleted file mode 100644 index b55bbbbd82..0000000000 --- a/nym-wallet/src-tauri/capabilities/migrated.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "identifier": "migrated", - "description": "permissions that were migrated from v1", - "local": true, - "windows": [ - "main" - ], - "permissions": [ - "core:default" - ] -} \ No newline at end of file diff --git a/nym-wallet/src-tauri/capabilities/shell-capability.json b/nym-wallet/src-tauri/capabilities/shell-capability.json new file mode 100644 index 0000000000..9b5bd6ea49 --- /dev/null +++ b/nym-wallet/src-tauri/capabilities/shell-capability.json @@ -0,0 +1,11 @@ +{ + "$schema": "../gen/schemas/desktop-schema.json", + "identifier": "shell-capability", + "description": "Shell operations capability for Nym Wallet", + "windows": ["main", "nymWalletApp"], + "platforms": ["linux", "macOS", "windows"], + "permissions": [ + "shell:default", + "shell:allow-open" + ] +} \ No newline at end of file diff --git a/nym-wallet/src-tauri/gen/schemas/capabilities.json b/nym-wallet/src-tauri/gen/schemas/capabilities.json index 0ea61d3fbc..f0975ad401 100644 --- a/nym-wallet/src-tauri/gen/schemas/capabilities.json +++ b/nym-wallet/src-tauri/gen/schemas/capabilities.json @@ -1 +1 @@ -{"migrated":{"identifier":"migrated","description":"permissions that were migrated from v1","local":true,"windows":["main"],"permissions":["core:default"]}} \ No newline at end of file +{"main-capability":{"identifier":"main-capability","description":"Default capability for Nym Wallet main window","local":true,"windows":["main"],"permissions":["core:default","core:path:default","core:event:default","core:window:default","core:app:default","core:resources:default","core:menu:default","core:tray:default","shell:allow-open","core:window:allow-set-title"],"platforms":["linux","macOS","windows"]},"shell-capability":{"identifier":"shell-capability","description":"Shell operations capability for Nym Wallet","local":true,"windows":["main","nymWalletApp"],"permissions":["shell:default","shell:allow-open"],"platforms":["linux","macOS","windows"]}} \ No newline at end of file diff --git a/nym-wallet/src-tauri/src/main.rs b/nym-wallet/src-tauri/src/main.rs index 0f9aa2bcdc..81b4b245a1 100644 --- a/nym-wallet/src-tauri/src/main.rs +++ b/nym-wallet/src-tauri/src/main.rs @@ -6,6 +6,7 @@ use nym_mixnet_contract_common::{Gateway, MixNode}; use tauri::menu::{MenuBuilder, MenuItemBuilder, SubmenuBuilder}; use tauri::Manager; +use tauri_plugin_shell::init as init_shell; use crate::menu::SHOW_LOG_WINDOW; use crate::operations::app; @@ -34,8 +35,10 @@ fn main() { let context = tauri::generate_context!(); tauri::Builder::default() + .plugin(init_shell()) .manage(WalletState::default()) .invoke_handler(tauri::generate_handler![ + app::link::open_url, app::version::check_version, mixnet::account::add_account_for_password, mixnet::account::archive_wallet_file, diff --git a/nym-wallet/src-tauri/src/operations/app/link.rs b/nym-wallet/src-tauri/src/operations/app/link.rs new file mode 100644 index 0000000000..87d5318dba --- /dev/null +++ b/nym-wallet/src-tauri/src/operations/app/link.rs @@ -0,0 +1,22 @@ +use std::process::Command; + +#[tauri::command] +pub async fn open_url(url: String) -> Result<(), String> { + println!("Opening URL: {}", url); + + let status = match std::env::consts::OS { + "macos" => Command::new("open").arg(&url).status(), + "windows" => Command::new("cmd").args(["/c", "start", &url]).status(), + "linux" => Command::new("xdg-open").arg(&url).status(), + os => return Err(format!("Unsupported OS: {}", os)), + }; + + match status { + Ok(exit_status) if exit_status.success() => Ok(()), + Ok(exit_status) => Err(format!( + "Command failed with exit code: {:?}", + exit_status.code() + )), + Err(err) => Err(format!("Failed to execute command: {}", err)), + } +} diff --git a/nym-wallet/src-tauri/src/operations/app/mod.rs b/nym-wallet/src-tauri/src/operations/app/mod.rs index 5ceb7502b3..7aa201d014 100644 --- a/nym-wallet/src-tauri/src/operations/app/mod.rs +++ b/nym-wallet/src-tauri/src/operations/app/mod.rs @@ -1,3 +1,4 @@ +pub mod link; pub mod react; pub mod version; pub mod window; diff --git a/nym-wallet/src-tauri/tauri.conf.json b/nym-wallet/src-tauri/tauri.conf.json index 5843d192dd..228864a949 100644 --- a/nym-wallet/src-tauri/tauri.conf.json +++ b/nym-wallet/src-tauri/tauri.conf.json @@ -40,9 +40,9 @@ "beforeDevCommand": "", "devUrl": "http://localhost:9000" }, - "productName": "nym-wallet", - "mainBinaryName": "nym-wallet", - "version": "1.2.15", + "productName": "NymWallet", + "mainBinaryName": "NymWallet", + "version": "1.2.17", "identifier": "net.nymtech.wallet", "plugins": { "updater": { @@ -54,6 +54,7 @@ }, "app": { "security": { + "capabilities": ["main-capability", "shell-capability"], "csp": "default-src blob: data: filesystem: ws: wss: http: https: tauri: 'unsafe-eval' 'unsafe-inline' 'self' img-src: 'self'; connect-src ipc: http://ipc.localhost" }, "windows": [ diff --git a/nym-wallet/src/components/Balance/modals/TransferModalSuccess.tsx b/nym-wallet/src/components/Balance/modals/TransferModalSuccess.tsx index 13b762bcb5..b1fd80bbed 100644 --- a/nym-wallet/src/components/Balance/modals/TransferModalSuccess.tsx +++ b/nym-wallet/src/components/Balance/modals/TransferModalSuccess.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { Stack, Typography } from '@mui/material'; -import { Link } from '@nymproject/react/link/Link'; +import { TauriLink as Link } from 'src/components/TauriLinkWrapper'; import { ConfirmationModal } from 'src/components'; export type TTransactionDetails = { amount: string; url: string }; diff --git a/nym-wallet/src/components/Bonding/Bond.tsx b/nym-wallet/src/components/Bonding/Bond.tsx index 35c9bfd578..8abc7b95b6 100644 --- a/nym-wallet/src/components/Bonding/Bond.tsx +++ b/nym-wallet/src/components/Bonding/Bond.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { Link } from '@nymproject/react/link/Link'; +import { TauriLink as Link } from 'src/components/TauriLinkWrapper'; import { Box, Button, Typography } from '@mui/material'; import { NymCard } from '../NymCard'; diff --git a/nym-wallet/src/components/Bonding/BondedGateway.tsx b/nym-wallet/src/components/Bonding/BondedGateway.tsx index 53d0a06b16..7e5ac1d86e 100644 --- a/nym-wallet/src/components/Bonding/BondedGateway.tsx +++ b/nym-wallet/src/components/Bonding/BondedGateway.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { Box, Button, Stack, Tooltip, Typography } from '@mui/material'; -import { Link } from '@nymproject/react/link/Link'; +import { TauriLink as Link } from 'src/components/TauriLinkWrapper'; import { urls } from 'src/context'; import { NymCard } from 'src/components'; import { Network } from 'src/types'; diff --git a/nym-wallet/src/components/Bonding/BondedMixnode.tsx b/nym-wallet/src/components/Bonding/BondedMixnode.tsx index cb58ac1609..acf395f3a4 100644 --- a/nym-wallet/src/components/Bonding/BondedMixnode.tsx +++ b/nym-wallet/src/components/Bonding/BondedMixnode.tsx @@ -1,7 +1,7 @@ import React, { useEffect, useState } from 'react'; import { useNavigate } from 'react-router-dom'; import { Box, Button, Chip, Stack, Tooltip, Typography } from '@mui/material'; -import { Link } from '@nymproject/react/link/Link'; +import { TauriLink as Link } from 'src/components/TauriLinkWrapper'; import { Network } from 'src/types'; import { urls } from 'src/context'; import { NymCard } from 'src/components'; diff --git a/nym-wallet/src/components/Bonding/BondedNymNode.tsx b/nym-wallet/src/components/Bonding/BondedNymNode.tsx index 67686f230a..c8ee8eb0bb 100644 --- a/nym-wallet/src/components/Bonding/BondedNymNode.tsx +++ b/nym-wallet/src/components/Bonding/BondedNymNode.tsx @@ -1,7 +1,7 @@ import React, { useEffect, useState } from 'react'; import { useNavigate } from 'react-router-dom'; import { Box, Button, Chip, Stack, Tooltip, Typography } from '@mui/material'; -import { Link } from '@nymproject/react/link/Link'; +import { TauriLink as Link } from 'src/components/TauriLinkWrapper'; import { Network } from 'src/types'; import { urls } from 'src/context'; import { NymCard } from 'src/components'; diff --git a/nym-wallet/src/components/Bonding/modals/ConfirmationModal.tsx b/nym-wallet/src/components/Bonding/modals/ConfirmationModal.tsx index 53439a8e8c..fb636b6daa 100644 --- a/nym-wallet/src/components/Bonding/modals/ConfirmationModal.tsx +++ b/nym-wallet/src/components/Bonding/modals/ConfirmationModal.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { Stack, Typography, SxProps } from '@mui/material'; -import { Link } from '@nymproject/react/link/Link'; +import { TauriLink as Link } from 'src/components/TauriLinkWrapper'; import { ConfirmationModal } from 'src/components/Modals/ConfirmationModal'; import { ErrorModal } from 'src/components/Modals/ErrorModal'; diff --git a/nym-wallet/src/components/CopyToClipboard.tsx b/nym-wallet/src/components/CopyToClipboard.tsx index a58f8ff859..140dc75812 100644 --- a/nym-wallet/src/components/CopyToClipboard.tsx +++ b/nym-wallet/src/components/CopyToClipboard.tsx @@ -1,8 +1,8 @@ import React, { useEffect, useState } from 'react'; import { Button, IconButton, Tooltip } from '@mui/material'; import { Check, ContentCopy } from '@mui/icons-material'; +import { writeText } from '@tauri-apps/plugin-clipboard-manager'; import { Console } from '../utils/console'; -import { writeText, readText } from '@tauri-apps/plugin-clipboard-manager'; export const CopyToClipboard = ({ text = '', iconButton }: { text?: string; iconButton?: boolean }) => { const [copied, setCopied] = useState(false); diff --git a/nym-wallet/src/components/Delegation/DelegationItem.tsx b/nym-wallet/src/components/Delegation/DelegationItem.tsx index aa61276161..c92286838a 100644 --- a/nym-wallet/src/components/Delegation/DelegationItem.tsx +++ b/nym-wallet/src/components/Delegation/DelegationItem.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { Box, Chip, IconButton, TableCell, TableRow, Tooltip, Typography } from '@mui/material'; -import { Link } from '@nymproject/react/link/Link'; +import { TauriLink as Link } from 'src/components/TauriLinkWrapper'; import { decimalToPercentage, DelegationWithEverything } from '@nymproject/types'; import { LockOutlined, WarningAmberOutlined } from '@mui/icons-material'; import { isDelegation, useDelegationContext } from 'src/context/delegations'; diff --git a/nym-wallet/src/components/Delegation/DelegationModal.tsx b/nym-wallet/src/components/Delegation/DelegationModal.tsx index 062002e1eb..51a6f63670 100644 --- a/nym-wallet/src/components/Delegation/DelegationModal.tsx +++ b/nym-wallet/src/components/Delegation/DelegationModal.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { Typography, SxProps, Stack } from '@mui/material'; -import { Link } from '@nymproject/react/link/Link'; +import { TauriLink as Link } from 'src/components/TauriLinkWrapper'; import { LoadingModal } from '../Modals/LoadingModal'; import { ConfirmationModal } from '../Modals/ConfirmationModal'; import { ErrorModal } from '../Modals/ErrorModal'; diff --git a/nym-wallet/src/components/Delegation/Delegations.tsx b/nym-wallet/src/components/Delegation/Delegations.tsx index 82c40c9e19..2234dcbc6a 100644 --- a/nym-wallet/src/components/Delegation/Delegations.tsx +++ b/nym-wallet/src/components/Delegation/Delegations.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { Box, Typography } from '@mui/material'; import { DelegationWithEverything } from '@nymproject/types'; -import { Link } from '@nymproject/react/link/Link'; +import { TauriLink as Link } from 'src/components/TauriLinkWrapper'; import { DelegationList } from './DelegationList'; import { DelegationListItemActions } from './DelegationActions'; diff --git a/nym-wallet/src/components/Delegation/PendingDelegationItem.tsx b/nym-wallet/src/components/Delegation/PendingDelegationItem.tsx index aca1d7edc6..1bbbe68808 100644 --- a/nym-wallet/src/components/Delegation/PendingDelegationItem.tsx +++ b/nym-wallet/src/components/Delegation/PendingDelegationItem.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { Chip, TableCell, TableRow, Tooltip } from '@mui/material'; import { WrappedDelegationEvent } from '@nymproject/types'; -import { Link } from '@nymproject/react/link/Link'; +import { TauriLink as Link } from 'src/components/TauriLinkWrapper'; export const PendingDelegationItem = ({ item, explorerUrl }: { item: WrappedDelegationEvent; explorerUrl: string }) => ( diff --git a/nym-wallet/src/components/Send/SendSuccessModal.tsx b/nym-wallet/src/components/Send/SendSuccessModal.tsx index ffee6dd2e2..1361aa1c65 100644 --- a/nym-wallet/src/components/Send/SendSuccessModal.tsx +++ b/nym-wallet/src/components/Send/SendSuccessModal.tsx @@ -1,6 +1,6 @@ import React, { useContext } from 'react'; import { Stack, Typography, SxProps } from '@mui/material'; -import { Link } from '@nymproject/react/link/Link'; +import { TauriLink as Link } from 'src/components/TauriLinkWrapper'; import { AppContext } from 'src/context'; import { TTransactionDetails } from './types'; import { ConfirmationModal } from '../Modals/ConfirmationModal'; diff --git a/nym-wallet/src/components/TauriLinkWrapper.tsx b/nym-wallet/src/components/TauriLinkWrapper.tsx new file mode 100644 index 0000000000..f3cadb4b3f --- /dev/null +++ b/nym-wallet/src/components/TauriLinkWrapper.tsx @@ -0,0 +1,21 @@ +import React from 'react'; +import { Link, LinkProps } from '@nymproject/react/link/Link'; +import { openInBrowser } from '../utils/shellHelper'; + +export const TauriLink: React.FC = (props) => { + const { href, onClick, ...restProps } = props; + + const handleClick = async (event: React.MouseEvent) => { + if (onClick) { + onClick(event); + } + + if (href && (href.startsWith('http://') || href.startsWith('https://'))) { + event.preventDefault(); + console.log('Opening link in browser:', href); + await openInBrowser(href); + } + }; + + return ; +}; diff --git a/nym-wallet/src/pages/balance/Balance.tsx b/nym-wallet/src/pages/balance/Balance.tsx index 9ede7b6902..f73752c9b1 100644 --- a/nym-wallet/src/pages/balance/Balance.tsx +++ b/nym-wallet/src/pages/balance/Balance.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { Alert, Grid, Typography } from '@mui/material'; -import { Link } from '@nymproject/react/link/Link'; +import { TauriLink as Link } from 'src/components/TauriLinkWrapper'; import { ClientAddress } from '@nymproject/react/client-address/ClientAddress'; import { Network } from 'src/types'; import { Balance } from '@nymproject/types'; @@ -49,7 +49,7 @@ export const BalanceCard = ({ {network && ( { diff --git a/nym-wallet/src/utils/common.ts b/nym-wallet/src/utils/common.ts index f2da75f177..0aa835aea0 100644 --- a/nym-wallet/src/utils/common.ts +++ b/nym-wallet/src/utils/common.ts @@ -14,7 +14,8 @@ import { userBalance, } from '../requests'; import { Console } from './console'; -const appWindow = getCurrentWebviewWindow() + +const appWindow = getCurrentWebviewWindow(); export const validateKey = (key: string, bytesLength: number): boolean => { // it must be a valid base58 key diff --git a/nym-wallet/src/utils/shellHelper.ts b/nym-wallet/src/utils/shellHelper.ts new file mode 100644 index 0000000000..d5e63249e2 --- /dev/null +++ b/nym-wallet/src/utils/shellHelper.ts @@ -0,0 +1,18 @@ +import { open } from '@tauri-apps/plugin-shell' + +export const openInBrowser = async (url: string): Promise => { + console.log(`Attempting to open in browser: ${url}`) + + try { + await open(url) + console.log('Browser opened successfully') + } catch (error) { + console.error('Failed to open browser with shell plugin:', error) + + try { + window.open(url, '_blank') + } catch (e) { + console.error('Fallback also failed:', e) + } + } +} diff --git a/yarn.lock b/yarn.lock index ff82392398..0d1624ad0c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6677,6 +6677,13 @@ dependencies: "@tauri-apps/api" "^2.0.0" +"@tauri-apps/plugin-shell@^2.2.1": + version "2.2.1" + resolved "https://registry.yarnpkg.com/@tauri-apps/plugin-shell/-/plugin-shell-2.2.1.tgz#586ab725ef622ba65a946bff1a3e166cee181903" + integrity sha512-G1GFYyWe/KlCsymuLiNImUgC8zGY0tI0Y3p8JgBCWduR5IEXlIJS+JuG1qtveitwYXlfJrsExt3enhv5l2/yhA== + dependencies: + "@tauri-apps/api" "^2.0.0" + "@tauri-apps/plugin-updater@^2.0.0": version "2.7.0" resolved "https://registry.yarnpkg.com/@tauri-apps/plugin-updater/-/plugin-updater-2.7.0.tgz#a4390bdfd858106bdc6cc3d05d52efc9e0528ea3" From 7cc473005b951928611b0748b2dd6fb169a9b7b9 Mon Sep 17 00:00:00 2001 From: Tommy Verrall Date: Mon, 7 Apr 2025 10:09:47 +0200 Subject: [PATCH 11/48] More permissions errors - fix more perm errors - enabled the version in the wallet --- .../capabilities/main-capability.json | 34 ++++++++++++------- .../src-tauri/gen/schemas/capabilities.json | 2 +- nym-wallet/src-tauri/src/main.rs | 2 ++ 3 files changed, 24 insertions(+), 14 deletions(-) diff --git a/nym-wallet/src-tauri/capabilities/main-capability.json b/nym-wallet/src-tauri/capabilities/main-capability.json index f3f33083fb..272f88dd13 100644 --- a/nym-wallet/src-tauri/capabilities/main-capability.json +++ b/nym-wallet/src-tauri/capabilities/main-capability.json @@ -2,18 +2,26 @@ "$schema": "../gen/schemas/desktop-schema.json", "identifier": "main-capability", "description": "Default capability for Nym Wallet main window", - "windows": ["main"], - "platforms": ["linux", "macOS", "windows"], + "windows": [ + "main", + "nymWalletApp" + ], + "platforms": [ + "linux", + "macOS", + "windows" + ], "permissions": [ - "core:default", - "core:path:default", - "core:event:default", - "core:window:default", - "core:app:default", - "core:resources:default", - "core:menu:default", - "core:tray:default", - "shell:allow-open", - "core:window:allow-set-title" + "core:default", + "core:path:default", + "core:event:default", + "core:window:default", + "core:app:default", + "core:resources:default", + "core:menu:default", + "core:tray:default", + "shell:allow-open", + "core:window:allow-set-title", + "core:app:allow-version" ] - } \ No newline at end of file +} \ No newline at end of file diff --git a/nym-wallet/src-tauri/gen/schemas/capabilities.json b/nym-wallet/src-tauri/gen/schemas/capabilities.json index f0975ad401..b1c6a4e67a 100644 --- a/nym-wallet/src-tauri/gen/schemas/capabilities.json +++ b/nym-wallet/src-tauri/gen/schemas/capabilities.json @@ -1 +1 @@ -{"main-capability":{"identifier":"main-capability","description":"Default capability for Nym Wallet main window","local":true,"windows":["main"],"permissions":["core:default","core:path:default","core:event:default","core:window:default","core:app:default","core:resources:default","core:menu:default","core:tray:default","shell:allow-open","core:window:allow-set-title"],"platforms":["linux","macOS","windows"]},"shell-capability":{"identifier":"shell-capability","description":"Shell operations capability for Nym Wallet","local":true,"windows":["main","nymWalletApp"],"permissions":["shell:default","shell:allow-open"],"platforms":["linux","macOS","windows"]}} \ No newline at end of file +{"main-capability":{"identifier":"main-capability","description":"Default capability for Nym Wallet main window","local":true,"windows":["main","nymWalletApp"],"permissions":["core:default","core:path:default","core:event:default","core:window:default","core:app:default","core:resources:default","core:menu:default","core:tray:default","shell:allow-open","core:window:allow-set-title","core:app:allow-version"],"platforms":["linux","macOS","windows"]},"shell-capability":{"identifier":"shell-capability","description":"Shell operations capability for Nym Wallet","local":true,"windows":["main","nymWalletApp"],"permissions":["shell:default","shell:allow-open"],"platforms":["linux","macOS","windows"]}} \ No newline at end of file diff --git a/nym-wallet/src-tauri/src/main.rs b/nym-wallet/src-tauri/src/main.rs index 81b4b245a1..e55afb4383 100644 --- a/nym-wallet/src-tauri/src/main.rs +++ b/nym-wallet/src-tauri/src/main.rs @@ -7,6 +7,7 @@ use nym_mixnet_contract_common::{Gateway, MixNode}; use tauri::menu::{MenuBuilder, MenuItemBuilder, SubmenuBuilder}; use tauri::Manager; use tauri_plugin_shell::init as init_shell; +use tauri_plugin_updater::Builder as UpdaterBuilder; use crate::menu::SHOW_LOG_WINDOW; use crate::operations::app; @@ -36,6 +37,7 @@ fn main() { let context = tauri::generate_context!(); tauri::Builder::default() .plugin(init_shell()) + .plugin(UpdaterBuilder::new().build()) .manage(WalletState::default()) .invoke_handler(tauri::generate_handler![ app::link::open_url, From 3d7728305680d903825d2cfcda9731c975d2795e Mon Sep 17 00:00:00 2001 From: Tommy Verrall Date: Mon, 7 Apr 2025 10:29:03 +0200 Subject: [PATCH 12/48] Add pruning warning errors --- .../src/operations/mixnet/delegate.rs | 18 ++- .../components/Delegation/DelegationList.tsx | 121 +++++++++++++----- 2 files changed, 102 insertions(+), 37 deletions(-) diff --git a/nym-wallet/src-tauri/src/operations/mixnet/delegate.rs b/nym-wallet/src-tauri/src/operations/mixnet/delegate.rs index 744aeb1bb7..8fc47187d0 100644 --- a/nym-wallet/src-tauri/src/operations/mixnet/delegate.rs +++ b/nym-wallet/src-tauri/src/operations/mixnet/delegate.rs @@ -407,14 +407,22 @@ pub async fn get_all_mix_delegations( d.height ); let timestamp = client - .nyxd - .get_block_timestamp(Some(d.height as u32)) - .await - .tap_err(|err| { + .nyxd + .get_block_timestamp(Some(d.height as u32)) + .await + .tap_err(|err| { + let error_message = err.to_string(); + // Check if the error is related to height not being available (pruning) + if error_message.contains("height") && error_message.contains("not available") { + let str_err = "Due to pruning strategies from validators, please navigate to the Settings tab and change your RPC node for your validator to retrieve your delegations."; + log::error!(" <<< {}", str_err); + error_strings.push(str_err.to_string()); + } else { let str_err = format!("Failed to get block timestamp for height = {} for delegation to mix_id = {}. Error: {}", d.height, d.mix_id, err); log::error!(" <<< {}", str_err); error_strings.push(str_err); - }).ok(); + } + }).ok(); let delegated_on_iso_datetime = timestamp.map(|ts| ts.to_rfc3339()); log::trace!( " <<< timestamp = {:?}, delegated_on_iso_datetime = {:?}", diff --git a/nym-wallet/src/components/Delegation/DelegationList.tsx b/nym-wallet/src/components/Delegation/DelegationList.tsx index ebda436025..776a412e6b 100644 --- a/nym-wallet/src/components/Delegation/DelegationList.tsx +++ b/nym-wallet/src/components/Delegation/DelegationList.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { Box, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, TableSortLabel } from '@mui/material'; +import { Alert, AlertTitle, Box, Button, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, TableSortLabel, Typography } from '@mui/material'; import { visuallyHidden } from '@mui/utils'; import ArrowDropDownIcon from '@mui/icons-material/ArrowDropDown'; import { DelegationWithEverything } from '@nymproject/types'; @@ -10,6 +10,7 @@ import { PendingDelegationItem } from './PendingDelegationItem'; import { LoadingModal } from '../Modals/LoadingModal'; import { isDelegation, isPendingDelegation, TDelegations, useDelegationContext } from '../../context/delegations'; import { ErrorModal } from '../Modals/ErrorModal'; +import { useNavigate } from 'react-router-dom'; export type Order = 'asc' | 'desc'; type AdditionalTypes = { profit_margin_percent: number; operating_cost: number }; @@ -83,7 +84,15 @@ const EnhancedTableHead: FCWithChildren = ({ order, orderBy, ); }; -// Pin delegations on unbonded nodes to the top of the list +const hasPruningError = (item: any): boolean => { + if (!isDelegation(item) || !item.errors) return false; + + return ( + item.errors.includes("height") && + item.errors.includes("not available") || + item.errors.includes("Due to pruning strategies") + ); +}; export const DelegationList: FCWithChildren<{ isLoading?: boolean; @@ -94,6 +103,7 @@ export const DelegationList: FCWithChildren<{ }> = ({ isLoading, items, onItemActionClick, explorerUrl }) => { const [order, setOrder] = React.useState('asc'); const [orderBy, setOrderBy] = React.useState('delegated_on_iso_datetime'); + const navigate = useNavigate(); const { delegationItemErrors, setDelegationItemErrors } = useDelegationContext(); @@ -104,36 +114,83 @@ export const DelegationList: FCWithChildren<{ }; const sorted = useSortDelegations(items, order, orderBy); + + // Check if any delegations have pruning errors + const hasPruningErrors = React.useMemo(() => { + return sorted?.some(item => hasPruningError(item)); + }, [sorted]); + + // Navigate to settings page + const navigateToSettings = () => { + navigate('/settings'); + }; + + // Format error message for display + const formatErrorMessage = (message: string) => { + if (message.includes("height") && message.includes("not available")) { + return "Due to pruning strategies from validators, please navigate to the Settings tab and change your RPC node for your validator to retrieve your delegations."; + } + return message; + }; return ( - - {isLoading && } - setDelegationItemErrors(undefined)} - /> - - - - {sorted?.length - ? sorted.map((item: any) => { - if (isPendingDelegation(item)) return ; - if (isDelegation(item)) - return ( - - ); - return null; - }) - : null} - -
-
+ <> + {/* Display pruning error alert at the top if needed */} + {hasPruningErrors && ( + + Go to Settings + + } + > + Data Pruning Detected + + Some delegation details cannot be retrieved because of data pruning on the validator. + Please navigate to the Settings tab and change your RPC node to fix this issue. + + + )} + + + {isLoading && } + setDelegationItemErrors(undefined)} + /> + + + + {sorted?.length + ? sorted.map((item: any, index: number) => { + if (isPendingDelegation(item)) { + // Use index for key instead of mix_id which might not be directly accessible + return ; + } + if (isDelegation(item)) + return ( + + ); + return null; + }) + : null} + +
+
+ ); -}; +}; \ No newline at end of file From 224e63d275e62fea0555e3343e71135ecf721233 Mon Sep 17 00:00:00 2001 From: Tommy Verrall Date: Mon, 7 Apr 2025 11:37:22 +0200 Subject: [PATCH 13/48] Rename and update --- nym-wallet/Cargo.lock | 114 +++++++++--------- nym-wallet/src-tauri/Cargo.toml | 8 +- .../capabilities/main-capability.json | 4 +- .../src-tauri/gen/schemas/capabilities.json | 2 +- nym-wallet/src-tauri/src/main.rs | 2 + .../components/Balance/VestingTimeline.tsx | 68 +++++++---- .../Balance/cards/TokenTransfer.tsx | 17 ++- .../Balance/cards/VestingSchedule.tsx | 31 +++-- nym-wallet/src/pages/balance/Balance.tsx | 34 +++--- nym-wallet/src/pages/balance/Vesting.tsx | 10 +- nym-wallet/src/utils/shellHelper.ts | 16 +-- 11 files changed, 180 insertions(+), 126 deletions(-) diff --git a/nym-wallet/Cargo.lock b/nym-wallet/Cargo.lock index 0bd21ea525..dfa453bb50 100644 --- a/nym-wallet/Cargo.lock +++ b/nym-wallet/Cargo.lock @@ -2,6 +2,63 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "NymWallet" +version = "1.2.16" +dependencies = [ + "async-trait", + "base64 0.13.1", + "bip39", + "cfg-if", + "colored 2.2.0", + "cosmrs", + "cosmwasm-std", + "dirs 4.0.0", + "dotenvy", + "eyre", + "fern", + "futures", + "itertools 0.10.5", + "k256", + "log", + "nym-config", + "nym-contracts-common", + "nym-crypto", + "nym-mixnet-contract-common", + "nym-node-requests", + "nym-store-cipher", + "nym-types", + "nym-validator-client", + "nym-vesting-contract-common", + "nym-wallet-types", + "once_cell", + "open", + "pretty_env_logger", + "rand_chacha 0.3.1", + "reqwest 0.12.15", + "serde", + "serde_json", + "serde_repr", + "strum 0.23.0", + "tap", + "tauri", + "tauri-build", + "tauri-codegen", + "tauri-macros", + "tauri-plugin-clipboard-manager", + "tauri-plugin-process", + "tauri-plugin-shell", + "tauri-plugin-updater", + "tempfile", + "thiserror 1.0.69", + "time", + "tokio", + "toml 0.5.11", + "ts-rs", + "url", + "zeroize", +] + [[package]] name = "addr2line" version = "0.24.2" @@ -4118,63 +4175,6 @@ dependencies = [ "x25519-dalek", ] -[[package]] -name = "nym_wallet" -version = "1.2.16" -dependencies = [ - "async-trait", - "base64 0.13.1", - "bip39", - "cfg-if", - "colored 2.2.0", - "cosmrs", - "cosmwasm-std", - "dirs 4.0.0", - "dotenvy", - "eyre", - "fern", - "futures", - "itertools 0.10.5", - "k256", - "log", - "nym-config", - "nym-contracts-common", - "nym-crypto", - "nym-mixnet-contract-common", - "nym-node-requests", - "nym-store-cipher", - "nym-types", - "nym-validator-client", - "nym-vesting-contract-common", - "nym-wallet-types", - "once_cell", - "open", - "pretty_env_logger", - "rand_chacha 0.3.1", - "reqwest 0.12.15", - "serde", - "serde_json", - "serde_repr", - "strum 0.23.0", - "tap", - "tauri", - "tauri-build", - "tauri-codegen", - "tauri-macros", - "tauri-plugin-clipboard-manager", - "tauri-plugin-process", - "tauri-plugin-shell", - "tauri-plugin-updater", - "tempfile", - "thiserror 1.0.69", - "time", - "tokio", - "toml 0.5.11", - "ts-rs", - "url", - "zeroize", -] - [[package]] name = "objc-sys" version = "0.3.5" diff --git a/nym-wallet/src-tauri/Cargo.toml b/nym-wallet/src-tauri/Cargo.toml index e8a3424f4f..039a3dc928 100644 --- a/nym-wallet/src-tauri/Cargo.toml +++ b/nym-wallet/src-tauri/Cargo.toml @@ -1,11 +1,11 @@ [package] -name = "nym_wallet" +name = "NymWallet" version = "1.2.16" description = "Nym Native Wallet" authors = ["Nym Technologies SA"] license = "" repository = "" -default-run = "nym_wallet" +default-run = "NymWallet" edition = "2021" build = "src/build.rs" rust-version = "1.85" @@ -21,8 +21,8 @@ tauri-macros = "2.1.1" [dependencies] async-trait = "0.1.68" tauri-plugin-updater = "2.7.0" -tauri-plugin-clipboard-manager = "2.2.2" -tauri-plugin-shell = "2.2.0" +tauri-plugin-clipboard-manager = "2" +tauri-plugin-shell = "2.2.1" tauri-plugin-process = "2.2.1" bip39 = { version = "2.0.0", features = ["zeroize", "rand"] } cfg-if = "1.0.0" diff --git a/nym-wallet/src-tauri/capabilities/main-capability.json b/nym-wallet/src-tauri/capabilities/main-capability.json index 272f88dd13..ab0db25d88 100644 --- a/nym-wallet/src-tauri/capabilities/main-capability.json +++ b/nym-wallet/src-tauri/capabilities/main-capability.json @@ -22,6 +22,8 @@ "core:tray:default", "shell:allow-open", "core:window:allow-set-title", - "core:app:allow-version" + "core:app:allow-version", + "clipboard-manager:allow-read-text", + "clipboard-manager:allow-write-text" ] } \ No newline at end of file diff --git a/nym-wallet/src-tauri/gen/schemas/capabilities.json b/nym-wallet/src-tauri/gen/schemas/capabilities.json index b1c6a4e67a..669ad3376c 100644 --- a/nym-wallet/src-tauri/gen/schemas/capabilities.json +++ b/nym-wallet/src-tauri/gen/schemas/capabilities.json @@ -1 +1 @@ -{"main-capability":{"identifier":"main-capability","description":"Default capability for Nym Wallet main window","local":true,"windows":["main","nymWalletApp"],"permissions":["core:default","core:path:default","core:event:default","core:window:default","core:app:default","core:resources:default","core:menu:default","core:tray:default","shell:allow-open","core:window:allow-set-title","core:app:allow-version"],"platforms":["linux","macOS","windows"]},"shell-capability":{"identifier":"shell-capability","description":"Shell operations capability for Nym Wallet","local":true,"windows":["main","nymWalletApp"],"permissions":["shell:default","shell:allow-open"],"platforms":["linux","macOS","windows"]}} \ No newline at end of file +{"main-capability":{"identifier":"main-capability","description":"Default capability for Nym Wallet main window","local":true,"windows":["main","nymWalletApp"],"permissions":["core:default","core:path:default","core:event:default","core:window:default","core:app:default","core:resources:default","core:menu:default","core:tray:default","shell:allow-open","core:window:allow-set-title","core:app:allow-version","clipboard-manager:allow-read-text","clipboard-manager:allow-write-text"],"platforms":["linux","macOS","windows"]},"shell-capability":{"identifier":"shell-capability","description":"Shell operations capability for Nym Wallet","local":true,"windows":["main","nymWalletApp"],"permissions":["shell:default","shell:allow-open"],"platforms":["linux","macOS","windows"]}} \ No newline at end of file diff --git a/nym-wallet/src-tauri/src/main.rs b/nym-wallet/src-tauri/src/main.rs index e55afb4383..b2a1eab5bd 100644 --- a/nym-wallet/src-tauri/src/main.rs +++ b/nym-wallet/src-tauri/src/main.rs @@ -8,6 +8,7 @@ use tauri::menu::{MenuBuilder, MenuItemBuilder, SubmenuBuilder}; use tauri::Manager; use tauri_plugin_shell::init as init_shell; use tauri_plugin_updater::Builder as UpdaterBuilder; +use tauri_plugin_clipboard_manager::ClipboardManagerPlugin; use crate::menu::SHOW_LOG_WINDOW; use crate::operations::app; @@ -38,6 +39,7 @@ fn main() { tauri::Builder::default() .plugin(init_shell()) .plugin(UpdaterBuilder::new().build()) + .plugin(ClipboardManagerPlugin::new()) .manage(WalletState::default()) .invoke_handler(tauri::generate_handler![ app::link::open_url, diff --git a/nym-wallet/src/components/Balance/VestingTimeline.tsx b/nym-wallet/src/components/Balance/VestingTimeline.tsx index cfb489056d..bd6330afcd 100644 --- a/nym-wallet/src/components/Balance/VestingTimeline.tsx +++ b/nym-wallet/src/components/Balance/VestingTimeline.tsx @@ -1,7 +1,6 @@ -/* eslint-disable react/no-array-index-key */ import React, { useContext } from 'react'; import { useTheme } from '@mui/material/styles'; -import { Box, Stack, Tooltip, Typography } from '@mui/material'; +import { Box, Stack, Tooltip, Typography, Skeleton } from '@mui/material'; import { format } from 'date-fns'; import { AppContext } from 'src/context'; @@ -17,7 +16,10 @@ const Marker: FCWithChildren<{ tooltipText: string; color: string; position: str ); -export const VestingTimeline: FCWithChildren<{ percentageComplete: number }> = ({ percentageComplete }) => { +export const VestingTimeline: FCWithChildren<{ percentageComplete: number; isLoading?: boolean }> = ({ + percentageComplete, + isLoading, +}) => { const { userBalance: { currentVestingPeriod, vestingAccountInfo }, } = useContext(AppContext); @@ -32,34 +34,48 @@ export const VestingTimeline: FCWithChildren<{ percentageComplete: number }> = ( return ( - {percentageComplete}% - - - - {vestingAccountInfo?.periods.map((period, i, arr) => ( - = calculateMarkerPosition(arr.length, i) - ? theme.palette.success.main - : '#B9B9B9' - } - tooltipText={format(new Date(Number(period.start_time) * 1000), 'HH:mm do MMM yyyy')} - key={i} - /> - ))} - - + {isLoading ? ( + <> + + + + ) : ( + <> + {percentageComplete}% + + + + {vestingAccountInfo?.periods.map((period, i, arr) => ( + = calculateMarkerPosition(arr.length, i) + ? theme.palette.success.main + : '#B9B9B9' + } + tooltipText={format(new Date(Number(period.start_time) * 1000), 'HH:mm do MMM yyyy')} + key={`period-${period.start_time}`} + /> + ))} + + + + )} - {!!nextPeriod && ( + {!!nextPeriod && !isLoading && ( Next vesting period: {format(new Date(nextPeriod * 1000), 'HH:mm do MMM yyyy')} )} + {isLoading && ( + + + + )} ); }; diff --git a/nym-wallet/src/components/Balance/cards/TokenTransfer.tsx b/nym-wallet/src/components/Balance/cards/TokenTransfer.tsx index 1600a9b54e..b5adbb42cb 100644 --- a/nym-wallet/src/components/Balance/cards/TokenTransfer.tsx +++ b/nym-wallet/src/components/Balance/cards/TokenTransfer.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { Card, Stack, Button } from '@mui/material'; +import { Card, Stack, Button, Skeleton } from '@mui/material'; import { ModalListItem } from 'src/components/Modals/ModalListItem'; export const TokenTransfer = ({ @@ -7,20 +7,27 @@ export const TokenTransfer = ({ unlockedTokens, unlockedRewards, unlockedTransferable, + isLoading, }: { unlockedTokens?: string; unlockedRewards?: string; unlockedTransferable?: string; onTransfer: () => void; + isLoading?: boolean; }) => ( - - - + : unlockedTokens} /> + : unlockedRewards} divider /> + : unlockedTransferable} + fontWeight={600} + /> - diff --git a/nym-wallet/src/components/Balance/cards/VestingSchedule.tsx b/nym-wallet/src/components/Balance/cards/VestingSchedule.tsx index b26b999970..1e544f0012 100644 --- a/nym-wallet/src/components/Balance/cards/VestingSchedule.tsx +++ b/nym-wallet/src/components/Balance/cards/VestingSchedule.tsx @@ -9,6 +9,7 @@ import { Typography, TableCellProps, Card, + Skeleton, } from '@mui/material'; import { Period } from '@nymproject/types'; import { AppContext } from 'src/context'; @@ -28,7 +29,7 @@ const vestingPeriod = (current?: Period, original?: number) => { return 'N/A'; }; -export const VestingSchedule = () => { +export const VestingSchedule = ({ isLoading }: { isLoading?: boolean }) => { const { userBalance, clientDetails } = useContext(AppContext); const [vestedPercentage, setVestedPercentage] = useState(0); @@ -73,8 +74,14 @@ export const VestingSchedule = () => { textTransform: 'uppercase', }} > - {userBalance.tokenAllocation?.vesting || 'n/a'} / {userBalance.originalVesting?.amount.amount}{' '} - {clientDetails?.display_mix_denom.toUpperCase()} + {isLoading ? ( + + ) : ( + <> + {userBalance.tokenAllocation?.vesting || 'n/a'} / {userBalance.originalVesting?.amount.amount}{' '} + {clientDetails?.display_mix_denom.toUpperCase()} + + )} { borderBottom: 'none', }} > - {vestingPeriod(userBalance.currentVestingPeriod, userBalance.originalVesting?.number_of_periods)} + {isLoading ? ( + + ) : ( + vestingPeriod(userBalance.currentVestingPeriod, userBalance.originalVesting?.number_of_periods) + )} { }} align="right" > - {userBalance.tokenAllocation?.vested || 'n/a'} / {userBalance.originalVesting?.amount.amount}{' '} - {clientDetails?.display_mix_denom.toUpperCase()} + {isLoading ? ( + + ) : ( + <> + {userBalance.tokenAllocation?.vested || 'n/a'} / {userBalance.originalVesting?.amount.amount}{' '} + {clientDetails?.display_mix_denom.toUpperCase()} + + )}
@@ -103,7 +120,7 @@ export const VestingSchedule = () => { Percentage - + ); }; diff --git a/nym-wallet/src/pages/balance/Balance.tsx b/nym-wallet/src/pages/balance/Balance.tsx index f73752c9b1..9ce2dcee80 100644 --- a/nym-wallet/src/pages/balance/Balance.tsx +++ b/nym-wallet/src/pages/balance/Balance.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { Alert, Grid, Typography } from '@mui/material'; +import { Alert, Grid, Typography, Skeleton } from '@mui/material'; import { TauriLink as Link } from 'src/components/TauriLinkWrapper'; import { ClientAddress } from '@nymproject/react/client-address/ClientAddress'; import { Network } from 'src/types'; @@ -12,11 +12,13 @@ export const BalanceCard = ({ userBalanceError, network, clientAddress, + isLoading, }: { userBalance?: Balance; userBalanceError?: string; network?: Network; clientAddress?: string; + isLoading?: boolean; }) => ( )} - {!userBalanceError && ( - - {userBalance?.printable_balance} - + {isLoading ? ( + + ) : ( + !userBalanceError && ( + + {userBalance?.printable_balance || '—'} + + ) )} {network && ( diff --git a/nym-wallet/src/pages/balance/Vesting.tsx b/nym-wallet/src/pages/balance/Vesting.tsx index a9eb46a214..a27b70ca16 100644 --- a/nym-wallet/src/pages/balance/Vesting.tsx +++ b/nym-wallet/src/pages/balance/Vesting.tsx @@ -1,6 +1,6 @@ import React, { useEffect } from 'react'; import { Refresh } from '@mui/icons-material'; -import { Grid, IconButton, Typography } from '@mui/material'; +import { Grid, IconButton, Typography, Skeleton } from '@mui/material'; import { useSnackbar } from 'notistack'; import { NymCard } from 'src/components'; import { TokenTransfer } from 'src/components/Balance/cards/TokenTransfer'; @@ -15,6 +15,7 @@ export const VestingCard = ({ onTransfer, fetchBalance, fetchTokenAllocation, + isLoading, }: { unlockedTokens?: string; unlockedRewards?: string; @@ -23,6 +24,7 @@ export const VestingCard = ({ fetchTokenAllocation: () => Promise; fetchBalance: () => Promise; onTransfer: () => Promise; + isLoading?: boolean; }) => { const { enqueueSnackbar, closeSnackbar } = useSnackbar(); @@ -54,14 +56,15 @@ export const VestingCard = ({ await refreshBalances(); enqueueSnackbar('Balances updated', { variant: 'success', preventDuplicate: true }); }} + disabled={isLoading} > - + {isLoading ? : } } > - + diff --git a/nym-wallet/src/utils/shellHelper.ts b/nym-wallet/src/utils/shellHelper.ts index d5e63249e2..4317a6c50c 100644 --- a/nym-wallet/src/utils/shellHelper.ts +++ b/nym-wallet/src/utils/shellHelper.ts @@ -1,18 +1,18 @@ -import { open } from '@tauri-apps/plugin-shell' +import { open } from '@tauri-apps/plugin-shell'; export const openInBrowser = async (url: string): Promise => { - console.log(`Attempting to open in browser: ${url}`) + console.log(`Attempting to open in browser: ${url}`); try { - await open(url) - console.log('Browser opened successfully') + await open(url); + console.log('Browser opened successfully'); } catch (error) { - console.error('Failed to open browser with shell plugin:', error) + console.error('Failed to open browser with shell plugin:', error); try { - window.open(url, '_blank') + window.open(url, '_blank'); } catch (e) { - console.error('Fallback also failed:', e) + console.error('Fallback also failed:', e); } } -} +}; From ca0449e03d98c3fa6a2ab0bd69d953c7ab24ac61 Mon Sep 17 00:00:00 2001 From: Tommy Verrall Date: Mon, 7 Apr 2025 14:22:55 +0200 Subject: [PATCH 14/48] Init clipboard manager --- nym-wallet/src-tauri/src/main.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/nym-wallet/src-tauri/src/main.rs b/nym-wallet/src-tauri/src/main.rs index b2a1eab5bd..207619ce04 100644 --- a/nym-wallet/src-tauri/src/main.rs +++ b/nym-wallet/src-tauri/src/main.rs @@ -8,7 +8,6 @@ use tauri::menu::{MenuBuilder, MenuItemBuilder, SubmenuBuilder}; use tauri::Manager; use tauri_plugin_shell::init as init_shell; use tauri_plugin_updater::Builder as UpdaterBuilder; -use tauri_plugin_clipboard_manager::ClipboardManagerPlugin; use crate::menu::SHOW_LOG_WINDOW; use crate::operations::app; @@ -39,7 +38,7 @@ fn main() { tauri::Builder::default() .plugin(init_shell()) .plugin(UpdaterBuilder::new().build()) - .plugin(ClipboardManagerPlugin::new()) + .plugin(tauri_plugin_clipboard_manager::init()) .manage(WalletState::default()) .invoke_handler(tauri::generate_handler![ app::link::open_url, From 7b75f22a8e18ecee5cafbdd88c84741f7a5cfb19 Mon Sep 17 00:00:00 2001 From: Tommy Verrall Date: Mon, 7 Apr 2025 15:27:54 +0200 Subject: [PATCH 15/48] Remove legacy --- .../forms/legacyForms/BondGatewayForm.tsx | 46 ---- .../forms/legacyForms/GatewayAmountForm.tsx | 84 ------ .../forms/legacyForms/GatewayInitForm.tsx | 124 --------- .../forms/legacyForms/MixnodeAmountForm.tsx | 125 --------- .../forms/legacyForms/MixnodeInitForm.tsx | 117 -------- .../legacyForms/gatewayValidationSchema.ts | 91 ------- .../Bonding/modals/BondGatewayModal.tsx | 130 --------- .../GeneralGatewaySettings.tsx | 254 ------------------ .../GeneralMixnodeSettings.tsx | 245 ----------------- .../settings-pages/general-settings/index.tsx | 4 - 10 files changed, 1220 deletions(-) delete mode 100644 nym-wallet/src/components/Bonding/forms/legacyForms/BondGatewayForm.tsx delete mode 100644 nym-wallet/src/components/Bonding/forms/legacyForms/GatewayAmountForm.tsx delete mode 100644 nym-wallet/src/components/Bonding/forms/legacyForms/GatewayInitForm.tsx delete mode 100644 nym-wallet/src/components/Bonding/forms/legacyForms/MixnodeAmountForm.tsx delete mode 100644 nym-wallet/src/components/Bonding/forms/legacyForms/MixnodeInitForm.tsx delete mode 100644 nym-wallet/src/components/Bonding/forms/legacyForms/gatewayValidationSchema.ts delete mode 100644 nym-wallet/src/components/Bonding/modals/BondGatewayModal.tsx delete mode 100644 nym-wallet/src/pages/bonding/node-settings/settings-pages/general-settings/GeneralGatewaySettings.tsx delete mode 100644 nym-wallet/src/pages/bonding/node-settings/settings-pages/general-settings/GeneralMixnodeSettings.tsx diff --git a/nym-wallet/src/components/Bonding/forms/legacyForms/BondGatewayForm.tsx b/nym-wallet/src/components/Bonding/forms/legacyForms/BondGatewayForm.tsx deleted file mode 100644 index 843d604053..0000000000 --- a/nym-wallet/src/components/Bonding/forms/legacyForms/BondGatewayForm.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import React from 'react'; -import { Box } from '@mui/material'; -import { NodeTypeSelector } from 'src/components'; -import { CurrencyDenom, TNodeType } from '@nymproject/types'; -import { GatewayAmount, GatewayData } from 'src/pages/bonding/types'; -import GatewayInitForm from './GatewayInitForm'; -import GatewayAmountForm from './GatewayAmountForm'; - -export const BondGatewayForm = ({ - step, - denom, - gatewayData, - amountData, - hasVestingTokens, - onSelectNodeType, - onValidateGatewayData, - onValidateAmountData, -}: { - step: 1 | 2 | 3 | 4; - gatewayData: GatewayData; - amountData: GatewayAmount; - denom: CurrencyDenom; - hasVestingTokens: boolean; - onSelectNodeType: (nodeType: TNodeType) => void; - onValidateGatewayData: (data: GatewayData) => void; - onValidateAmountData: (data: GatewayAmount) => Promise; -}) => ( - <> - {step === 1 && ( - <> - - - - - - )} - {step === 2 && ( - - )} - -); diff --git a/nym-wallet/src/components/Bonding/forms/legacyForms/GatewayAmountForm.tsx b/nym-wallet/src/components/Bonding/forms/legacyForms/GatewayAmountForm.tsx deleted file mode 100644 index 7135d2b154..0000000000 --- a/nym-wallet/src/components/Bonding/forms/legacyForms/GatewayAmountForm.tsx +++ /dev/null @@ -1,84 +0,0 @@ -import React, { useEffect } from 'react'; -import { CurrencyDenom } from '@nymproject/types'; -import { useForm } from 'react-hook-form'; -import { yupResolver } from '@hookform/resolvers/yup/dist/yup'; -import { CurrencyFormField } from '@nymproject/react/currency/CurrencyFormField'; -import { Box, Stack } from '@mui/material'; -import { amountSchema } from './gatewayValidationSchema'; -import { checkHasEnoughFunds, checkHasEnoughLockedTokens } from '../../../../utils'; -import { GatewayAmount } from '../../../../pages/bonding/types'; -import { TokenPoolSelector } from '../../../TokenPoolSelector'; - -const GatewayAmountForm = ({ - denom, - amountData, - hasVestingTokens, - onNext, -}: { - denom: CurrencyDenom; - amountData: GatewayAmount; - hasVestingTokens: boolean; - onNext: (data: any) => void; -}) => { - const { - formState: { errors }, - handleSubmit, - setValue, - getValues, - setError, - } = useForm({ resolver: yupResolver(amountSchema), defaultValues: amountData }); - - const handleRequestValidation = async (event: { detail: { step: number } }) => { - let hasSufficientTokens = true; - const values = getValues(); - - if (values.tokenPool === 'balance') { - hasSufficientTokens = await checkHasEnoughFunds(values.amount.amount); - } - - if (values.tokenPool === 'locked') { - hasSufficientTokens = await checkHasEnoughLockedTokens(values.amount.amount); - } - - if (event.detail.step === 2 && hasSufficientTokens) { - handleSubmit(onNext)(); - } else { - setError('amount.amount', { message: 'Not enough tokens' }); - } - }; - - useEffect(() => { - window.addEventListener('validate_bond_gateway_step' as any, handleRequestValidation); - return () => window.removeEventListener('validate_bond_gateway_step' as any, handleRequestValidation); - }, []); - - return ( - - - {hasVestingTokens && setValue('tokenPool', pool)} />} - setValue('amount', newValue, { shouldValidate: true })} - validationError={errors.amount?.amount?.message} - denom={denom} - initialValue={amountData.amount.amount} - /> - setValue('operatorCost', newValue, { shouldValidate: true })} - validationError={errors.operatorCost?.amount?.message} - denom={denom} - initialValue={amountData.operatorCost.amount} - /> - - - ); -}; - -export default GatewayAmountForm; diff --git a/nym-wallet/src/components/Bonding/forms/legacyForms/GatewayInitForm.tsx b/nym-wallet/src/components/Bonding/forms/legacyForms/GatewayInitForm.tsx deleted file mode 100644 index 5a891700c9..0000000000 --- a/nym-wallet/src/components/Bonding/forms/legacyForms/GatewayInitForm.tsx +++ /dev/null @@ -1,124 +0,0 @@ -import React, { useEffect, useState } from 'react'; -import { useForm } from 'react-hook-form'; -import { clean } from 'semver'; -import { Checkbox, FormControlLabel, Stack, TextField } from '@mui/material'; -import { IdentityKeyFormField } from '@nymproject/react/mixnodes/IdentityKeyFormField'; -import { yupResolver } from '@hookform/resolvers/yup/dist/yup'; -import { GatewayData } from '../../../../pages/bonding/types'; -import { gatewayValidationSchema } from './gatewayValidationSchema'; - -const GatewayInitForm = ({ - gatewayData, - onNext, -}: { - gatewayData: GatewayData; - onNext: (data: GatewayData) => void; -}) => { - const [showAdvancedOptions, setShowAdvancedOptions] = useState(false); - - const { - register, - formState: { errors }, - handleSubmit, - setValue, - } = useForm({ resolver: yupResolver(gatewayValidationSchema), defaultValues: gatewayData }); - - const handleRequestValidation = (event: { detail: { step: number } }) => { - if (event.detail.step === 1) { - handleSubmit((data) => { - const validatedData = { - ...data, - version: clean(data.version) as string, - }; - onNext(validatedData); - })(); - } - }; - - useEffect(() => { - window.addEventListener('validate_bond_gateway_step' as any, handleRequestValidation); - return () => window.removeEventListener('validate_bond_gateway_step' as any, handleRequestValidation); - }, []); - - return ( - - setValue('identityKey', value)} - showTickOnValid={false} - /> - - - - - - - setShowAdvancedOptions((show) => !show)} checked={showAdvancedOptions} />} - label="Show advanced options" - /> - {showAdvancedOptions && ( - - - - - )} - - ); -}; - -export default GatewayInitForm; diff --git a/nym-wallet/src/components/Bonding/forms/legacyForms/MixnodeAmountForm.tsx b/nym-wallet/src/components/Bonding/forms/legacyForms/MixnodeAmountForm.tsx deleted file mode 100644 index 13176dadf6..0000000000 --- a/nym-wallet/src/components/Bonding/forms/legacyForms/MixnodeAmountForm.tsx +++ /dev/null @@ -1,125 +0,0 @@ -import React, { useContext, useEffect } from 'react'; -import { useForm } from 'react-hook-form'; -import { Box, FormHelperText, Stack, TextField, Typography } from '@mui/material'; -import { yupResolver } from '@hookform/resolvers/yup/dist/yup'; -import { CurrencyFormField } from '@nymproject/react/currency/CurrencyFormField'; -import { CurrencyDenom } from '@nymproject/types'; -import { amountSchema } from './mixnodeValidationSchema'; -import { MixnodeAmount } from '../../../../pages/bonding/types'; -import { AppContext } from '../../../../context'; -import { checkHasEnoughFunds, checkHasEnoughLockedTokens } from '../../../../utils'; -import { TokenPoolSelector } from '../../../TokenPoolSelector'; -import { ModalListItem } from '../../../Modals/ModalListItem'; - -const MixnodeAmountForm = ({ - amountData, - hasVestingTokens, - denom, - onNext, -}: { - amountData: MixnodeAmount; - hasVestingTokens: boolean; - denom: CurrencyDenom; - onNext: (data: MixnodeAmount) => void; -}) => { - const { mixnetContractParams } = useContext(AppContext); - - const { - register, - formState: { errors }, - handleSubmit, - setValue, - getValues, - setError, - } = useForm({ resolver: yupResolver(amountSchema(mixnetContractParams)), defaultValues: amountData }); - - const { userBalance } = useContext(AppContext); - - const handleRequestValidation = async (event: { detail: { step: number } }) => { - let hasSufficientTokens = true; - const values = getValues(); - - if (values.tokenPool === 'balance') { - hasSufficientTokens = await checkHasEnoughFunds(values.amount.amount); - } - - if (values.tokenPool === 'locked') { - hasSufficientTokens = await checkHasEnoughLockedTokens(values.amount.amount); - } - - if (event.detail.step === 2 && hasSufficientTokens) { - handleSubmit(onNext)(); - } else { - setError('amount.amount', { message: 'Not enough tokens' }); - } - }; - - useEffect(() => { - window.addEventListener('validate_bond_mixnode_step' as any, handleRequestValidation); - return () => window.removeEventListener('validate_bond_mixnode_step' as any, handleRequestValidation); - }, []); - - return ( - - - {hasVestingTokens && setValue('tokenPool', pool)} />} - { - setValue('amount', newValue, { shouldValidate: true }); - }} - validationError={errors.amount?.amount?.message} - denom={denom} - initialValue={amountData.amount.amount} - /> - - - { - setValue('operatorCost', newValue, { shouldValidate: true }); - }} - validationError={errors.operatorCost?.amount?.message} - denom={denom} - initialValue={amountData.operatorCost.amount} - /> - - Monthly operational costs of running your node. If your node is in the active set the amount will be paid back - to you from the rewards. - - - - - - The percentage of node rewards that you as the node operator take before rewards are distributed to operator - and delegators. - - - - {!hasVestingTokens && ( - - )} - Est. fee for this transaction will be calculated in the next page - - - ); -}; - -export default MixnodeAmountForm; diff --git a/nym-wallet/src/components/Bonding/forms/legacyForms/MixnodeInitForm.tsx b/nym-wallet/src/components/Bonding/forms/legacyForms/MixnodeInitForm.tsx deleted file mode 100644 index 0032bb3364..0000000000 --- a/nym-wallet/src/components/Bonding/forms/legacyForms/MixnodeInitForm.tsx +++ /dev/null @@ -1,117 +0,0 @@ -import React, { useEffect, useState } from 'react'; -import { useForm } from 'react-hook-form'; -import { clean } from 'semver'; -import { Checkbox, FormControlLabel, Stack, TextField } from '@mui/material'; -import { IdentityKeyFormField } from '@nymproject/react/mixnodes/IdentityKeyFormField'; -import { yupResolver } from '@hookform/resolvers/yup/dist/yup'; -import { mixnodeValidationSchema } from './mixnodeValidationSchema'; -import { MixnodeData } from '../../../../pages/bonding/types'; - -const MixnodeInitForm = ({ mixnodeData, onNext }: { mixnodeData: MixnodeData; onNext: (data: any) => void }) => { - const [showAdvancedOptions, setShowAdvancedOptions] = useState(false); - - const { - register, - formState: { errors }, - handleSubmit, - setValue, - } = useForm({ resolver: yupResolver(mixnodeValidationSchema), defaultValues: mixnodeData }); - - const handleRequestValidation = (event: { detail: { step: number } }) => { - if (event.detail.step === 1) { - handleSubmit((data) => { - const validatedData = { - ...data, - version: clean(data.version), - }; - onNext(validatedData); - })(); - } - }; - - useEffect(() => { - window.addEventListener('validate_bond_mixnode_step' as any, handleRequestValidation); - return () => window.removeEventListener('validate_bond_mixnode_step' as any, handleRequestValidation); - }, []); - - return ( - - setValue('identityKey', value)} - showTickOnValid={false} - /> - - - - - - setShowAdvancedOptions((show) => !show)} checked={showAdvancedOptions} />} - label="Show advanced options" - /> - {showAdvancedOptions && ( - - - - - - )} - - ); -}; - -export default MixnodeInitForm; diff --git a/nym-wallet/src/components/Bonding/forms/legacyForms/gatewayValidationSchema.ts b/nym-wallet/src/components/Bonding/forms/legacyForms/gatewayValidationSchema.ts deleted file mode 100644 index 9de1e6a1f7..0000000000 --- a/nym-wallet/src/components/Bonding/forms/legacyForms/gatewayValidationSchema.ts +++ /dev/null @@ -1,91 +0,0 @@ -import * as Yup from 'yup'; -import { - isLessThan, - isValidHostname, - validateAmount, - validateKey, - validateLocation, - validateRawPort, - validateVersion, -} from 'src/utils'; - -export const gatewayValidationSchema = Yup.object().shape({ - identityKey: Yup.string() - .required('An identity key is required') - .test('valid-id-key', 'A valid identity key is required', (value) => validateKey(value || '', 32)), - - sphinxKey: Yup.string() - .required('A sphinx key is required') - .test('valid-sphinx-key', 'A valid sphinx key is required', (value) => validateKey(value || '', 32)), - - host: Yup.string() - .required('A host is required') - .test('no-whitespace', 'Host cannot contain whitespace', (value) => !/\s/.test(value || '')) - .test('valid-host', 'A valid host is required', (value) => (value ? isValidHostname(value) : false)), - - version: Yup.string() - .required('A version is required') - .test('no-whitespace', 'A version cannot contain whitespace', (value) => !/\s/.test(value || '')) - .test('valid-version', 'A valid version is required', (value) => (value ? validateVersion(value) : false)), - - location: Yup.string() - .required('A location is required') - .test('valid-location', 'A valid location is required', (locationValueTest) => - locationValueTest ? validateLocation(locationValueTest) : false, - ), - - mixPort: Yup.number() - .required('A mixport is required') - .test('valid-mixport', 'A valid mixport is required', (value) => (value ? validateRawPort(value) : false)), - - clientsPort: Yup.number() - .required('A clients port is required') - .test('valid-clients', 'A valid clients port is required', (value) => (value ? validateRawPort(value) : false)), -}); - -export const amountSchema = Yup.object().shape({ - amount: Yup.object().shape({ - amount: Yup.string() - .required('An amount is required') - .test('valid-amount', 'Pledge error', async function isValidAmount(this, value) { - const isValid = await validateAmount(value || '', '100'); - if (!isValid) { - return this.createError({ message: 'A valid amount is required (min 100)' }); - } - return true; - }), - }), - operatorCost: Yup.object().shape({ - amount: Yup.string() - .required('An operating cost is required') - // eslint-disable-next-line - .test('valid-operating-cost', 'A valid amount is required (min 40)', async function isValidAmount(this, value) { - if (value && (!Number(value) || isLessThan(+value, 40))) { - return false; - } - - return true; - }), - }), -}); - -export const updateGatewayValidationSchema = Yup.object().shape({ - host: Yup.string() - .required('A host is required') - .test('no-whitespace', 'Host cannot contain whitespace', (value) => !/\s/.test(value || '')) - .test('valid-host', 'A valid host is required', (value) => (value ? isValidHostname(value) : false)), - - mixPort: Yup.number() - .required('A mixport is required') - .test('valid-mixport', 'A valid mixport is required', (value) => (value ? validateRawPort(value) : false)), - - httpApiPort: Yup.number() - .required('A clients port is required') - .test('valid-clients', 'A valid clients port is required', (value) => (value ? validateRawPort(value) : false)), - location: Yup.string().test('valid-location', 'A valid location is required', (value) => - value ? validateLocation(value) : false, - ), - version: Yup.string() - .test('no-whitespace', 'A version cannot contain whitespace', (value) => !/\s/.test(value || '')) - .test('valid-version', 'A valid version is required', (value) => (value ? validateVersion(value) : false)), -}); diff --git a/nym-wallet/src/components/Bonding/modals/BondGatewayModal.tsx b/nym-wallet/src/components/Bonding/modals/BondGatewayModal.tsx deleted file mode 100644 index 1060b7c533..0000000000 --- a/nym-wallet/src/components/Bonding/modals/BondGatewayModal.tsx +++ /dev/null @@ -1,130 +0,0 @@ -import React, { useContext, useEffect, useState } from 'react'; -import { Box } from '@mui/material'; -import { CurrencyDenom, TNodeType } from '@nymproject/types'; -import { ConfirmTx } from 'src/components/ConfirmTX'; -import { ModalListItem } from 'src/components/Modals/ModalListItem'; -import { SimpleModal } from 'src/components/Modals/SimpleModal'; -import { useGetFee } from 'src/hooks/useGetFee'; -import { GatewayAmount, GatewayData } from 'src/pages/bonding/types'; -import { BalanceWarning } from 'src/components/FeeWarning'; -import { AppContext } from 'src/context'; -import { BondGatewayForm } from '../forms/legacyForms/BondGatewayForm'; - -const defaultGatewayValues: GatewayData = { - identityKey: '', - sphinxKey: '', - ownerSignature: '', - location: '', - host: '', - version: '', - mixPort: 1789, - clientsPort: 9000, -}; - -const defaultAmountValues = (denom: CurrencyDenom) => ({ - amount: { amount: '100', denom }, - operatorCost: { amount: '40', denom }, - tokenPool: 'balance', -}); - -export const BondGatewayModal = ({ - denom, - hasVestingTokens, - onSelectNodeType, - onClose, - onError, -}: { - denom: CurrencyDenom; - hasVestingTokens: boolean; - onSelectNodeType: (type: TNodeType) => void; - onClose: () => void; - onError: (e: string) => void; -}) => { - const [step, setStep] = useState<1 | 2 | 3>(1); - const [gatewayData, setGatewayData] = useState(defaultGatewayValues); - const [amountData, setAmountData] = useState(defaultAmountValues(denom)); - - const { fee, resetFeeState, feeError } = useGetFee(); - const { userBalance } = useContext(AppContext); - - useEffect(() => { - if (feeError) { - onError(feeError); - } - }, [feeError]); - - const validateStep = async (s: number) => { - const event = new CustomEvent('validate_bond_gateway_step', { detail: { step: s } }); - window.dispatchEvent(event); - }; - - const handleBack = () => { - if (step === 2) { - setStep(1); - } else if (step === 3) { - setStep(2); - } - }; - - const handleUpdateGatwayData = (data: GatewayData) => { - setGatewayData(data); - setStep(2); - }; - - const handleUpdateAmountData = async (data: GatewayAmount) => { - setAmountData(data); - setStep(3); - }; - - const handleConfirm = async () => {}; - - if (fee) { - return ( - - - - {fee.amount?.amount && userBalance.balance && ( - - )} - - ); - } - - return ( - { - await validateStep(step); - }} - onBack={step === 2 ? handleBack : undefined} - onClose={onClose} - header="Bond gateway" - subHeader={`Step ${step}/3`} - okLabel="Next" - > - - - - - ); -}; diff --git a/nym-wallet/src/pages/bonding/node-settings/settings-pages/general-settings/GeneralGatewaySettings.tsx b/nym-wallet/src/pages/bonding/node-settings/settings-pages/general-settings/GeneralGatewaySettings.tsx deleted file mode 100644 index 7f8bdb6d08..0000000000 --- a/nym-wallet/src/pages/bonding/node-settings/settings-pages/general-settings/GeneralGatewaySettings.tsx +++ /dev/null @@ -1,254 +0,0 @@ -import React, { useContext, useState } from 'react'; -import { useForm } from 'react-hook-form'; -import { clean } from 'semver'; -import { yupResolver } from '@hookform/resolvers/yup'; -import { Button, Divider, Typography, TextField, Grid, Box } from '@mui/material'; -import { useTheme } from '@mui/material/styles'; -import { - simulateUpdateGatewayConfig, - simulateVestingUpdateGatewayConfig, - updateGatewayConfig, - vestingUpdateGatewayConfig, -} from 'src/requests'; -import { useBondingContext } from 'src/context/bonding'; -import { SimpleModal } from 'src/components/Modals/SimpleModal'; -import { Console } from 'src/utils/console'; -import { Alert } from 'src/components/Alert'; -import { ConfirmTx } from 'src/components/ConfirmTX'; -import { useGetFee } from 'src/hooks/useGetFee'; -import { LoadingModal } from 'src/components/Modals/LoadingModal'; -import { updateGatewayValidationSchema } from 'src/components/Bonding/forms/legacyForms/gatewayValidationSchema'; -import { BalanceWarning } from 'src/components/FeeWarning'; -import { AppContext } from 'src/context'; -import { TBondedGateway } from 'src/requests/gatewayDetails'; - -export const GeneralGatewaySettings = ({ bondedNode }: { bondedNode: TBondedGateway }) => { - const [openConfirmationModal, setOpenConfirmationModal] = useState(false); - const { getFee, fee, resetFeeState } = useGetFee(); - const { refresh } = useBondingContext(); - const { userBalance } = useContext(AppContext); - - const theme = useTheme(); - - const { - register, - handleSubmit, - formState: { errors, isSubmitting, isDirty, isValid }, - } = useForm({ - resolver: yupResolver(updateGatewayValidationSchema), - mode: 'onChange', - defaultValues: { - host: bondedNode.host, - mixPort: bondedNode.mixPort, - httpApiPort: bondedNode.httpApiPort, - version: bondedNode.version, - location: bondedNode.location, - }, - }); - - const onSubmit = async (data: any) => { - resetFeeState(); - const { host, mixPort, httpApiPort, version, location } = data; - - try { - const GatewayConfigParams = { - host, - mix_port: mixPort, - location, - version: clean(version) as string, - clients_port: httpApiPort, - }; - - if (bondedNode.proxy) { - await vestingUpdateGatewayConfig(GatewayConfigParams, fee?.fee); - } else { - await updateGatewayConfig(GatewayConfigParams, fee?.fee); - } - - setOpenConfirmationModal(true); - } catch (error) { - Console.error(error); - } - }; - - return ( - - {fee && ( - onSubmit(d))} - onPrev={resetFeeState} - onClose={resetFeeState} - > - {fee.amount?.amount && userBalance?.balance?.amount.amount && ( - - - - )} - - )} - {isSubmitting && } - - Changing these values will ONLY change the data about your node on the blockchain. Remember to change your - node’s config file with the same values too - - } - dismissable - /> - - - - - Port - - - - - - - - - - - - - - - - - Host - - - - - - - - - - - - Version - - - - - - - - - - - - Location - - - - - - - - - - - - - - { - setOpenConfirmationModal(false); - await refresh(); - }} - buttonFullWidth - sx={{ - width: '450px', - display: 'flex', - flexDirection: 'column', - alignItems: 'center', - justifyContent: 'center', - }} - headerStyles={{ - width: '100%', - mb: 1, - textAlign: 'center', - color: theme.palette.nym.nymWallet.text.blue, - fontSize: 16, - }} - subHeaderStyles={{ - width: '100%', - mb: 1, - textAlign: 'center', - color: 'main', - fontSize: 14, - }} - /> - - ); -}; diff --git a/nym-wallet/src/pages/bonding/node-settings/settings-pages/general-settings/GeneralMixnodeSettings.tsx b/nym-wallet/src/pages/bonding/node-settings/settings-pages/general-settings/GeneralMixnodeSettings.tsx deleted file mode 100644 index 08304622c7..0000000000 --- a/nym-wallet/src/pages/bonding/node-settings/settings-pages/general-settings/GeneralMixnodeSettings.tsx +++ /dev/null @@ -1,245 +0,0 @@ -import React, { useContext, useState } from 'react'; -import { useForm } from 'react-hook-form'; -import { yupResolver } from '@hookform/resolvers/yup'; -import { clean } from 'semver'; -import { Box, Button, Divider, Grid, Stack, TextField, Typography } from '@mui/material'; -import { useTheme } from '@mui/material/styles'; -import { isMixnode } from 'src/types'; -import { simulateUpdateMixnodeConfig, simulateVestingUpdateMixnodeConfig, updateMixnodeConfig } from 'src/requests'; -import { SimpleModal } from 'src/components/Modals/SimpleModal'; -import { bondedInfoParametersValidationSchema } from 'src/components/Bonding/forms/legacyForms/mixnodeValidationSchema'; -import { Console } from 'src/utils/console'; -import { Alert } from 'src/components/Alert'; -import { vestingUpdateMixnodeConfig } from 'src/requests/vesting'; -import { ConfirmTx } from 'src/components/ConfirmTX'; -import { useGetFee } from 'src/hooks/useGetFee'; -import { LoadingModal } from 'src/components/Modals/LoadingModal'; -import { BalanceWarning } from 'src/components/FeeWarning'; -import { AppContext } from 'src/context'; -import { TBondedMixnode } from 'src/requests/mixnodeDetails'; - -export const GeneralMixnodeSettings = ({ bondedNode }: { bondedNode: TBondedMixnode }) => { - const [openConfirmationModal, setOpenConfirmationModal] = useState(false); - const { getFee, fee, resetFeeState } = useGetFee(); - const { userBalance } = useContext(AppContext); - - const theme = useTheme(); - - const { - register, - handleSubmit, - formState: { errors, isSubmitting, isDirty, isValid }, - } = useForm({ - resolver: yupResolver(bondedInfoParametersValidationSchema), - mode: 'onChange', - defaultValues: isMixnode(bondedNode) ? bondedNode : {}, - }); - - const onSubmit = async (data: { - host?: string; - version?: string; - mixPort?: number; - verlocPort?: number; - httpApiPort?: number; - }) => { - resetFeeState(); - const { host, version, mixPort, verlocPort, httpApiPort } = data; - if (host && version && mixPort && verlocPort && httpApiPort) { - const MixNodeConfigParams = { - host, - mix_port: mixPort, - verloc_port: verlocPort, - http_api_port: httpApiPort, - version: clean(version) as string, - }; - try { - if (bondedNode.proxy) { - await vestingUpdateMixnodeConfig(MixNodeConfigParams); - } else { - await updateMixnodeConfig(MixNodeConfigParams); - } - setOpenConfirmationModal(true); - } catch (error) { - Console.error(error); - } - } - }; - - return ( - - {fee && ( - onSubmit(d))} - onPrev={resetFeeState} - onClose={resetFeeState} - > - {fee.amount?.amount && userBalance?.balance?.amount.amount && ( - - - - )} - - )} - {isSubmitting && } - - - Changing these values will ONLY change the data about your node on the blockchain. - - Remember to change your node’s config file with the same values too. - - } - bgColor={`${theme.palette.nym.nymWallet.text.blue}0D !important`} - dismissable - /> - - - - - Port - - - - - - - - - - - - - - - - - - - Host - - - - - - - - - - - - - Version - - - - - - - - - - - - - - - - - { - await setOpenConfirmationModal(false); - }} - buttonFullWidth - sx={{ - width: '450px', - display: 'flex', - flexDirection: 'column', - alignItems: 'center', - justifyContent: 'center', - }} - headerStyles={{ - width: '100%', - mb: 1, - textAlign: 'center', - color: theme.palette.nym.nymWallet.text.blue, - fontSize: 16, - }} - subHeaderStyles={{ - width: '100%', - mb: 1, - textAlign: 'center', - color: 'main', - fontSize: 14, - }} - /> - - ); -}; diff --git a/nym-wallet/src/pages/bonding/node-settings/settings-pages/general-settings/index.tsx b/nym-wallet/src/pages/bonding/node-settings/settings-pages/general-settings/index.tsx index 7c1f4568b4..5157587737 100644 --- a/nym-wallet/src/pages/bonding/node-settings/settings-pages/general-settings/index.tsx +++ b/nym-wallet/src/pages/bonding/node-settings/settings-pages/general-settings/index.tsx @@ -2,9 +2,7 @@ import React, { useState } from 'react'; import { Box, Button, Divider, Grid } from '@mui/material'; import { isGateway, isMixnode, isNymNode } from 'src/types'; import { TBondedNode } from 'src/context/bonding'; -import { GeneralMixnodeSettings } from './GeneralMixnodeSettings'; import { ParametersSettings } from './ParametersSettings'; -import { GeneralGatewaySettings } from './GeneralGatewaySettings'; import { GeneralNymNodeSettings } from './GeneralNymNodeSettings'; const makeGeneralNav = (bondedNode: TBondedNode) => { @@ -22,8 +20,6 @@ export const NodeGeneralSettings = ({ bondedNode }: { bondedNode: TBondedNode }) const getSettings = () => { switch (navSelection) { case 0: { - if (isMixnode(bondedNode)) return ; - if (isGateway(bondedNode)) return ; if (isNymNode(bondedNode)) return ; break; } From 8ab269fa052d181c987766333c3dab9d9c359604 Mon Sep 17 00:00:00 2001 From: Tommy Verrall Date: Mon, 7 Apr 2025 17:16:22 +0200 Subject: [PATCH 16/48] Jazz up receive modal --- .../src/components/Receive/ReceiveModal.tsx | 151 ++++++++++++++---- .../components/currency/CurrencyFormField.tsx | 6 - 2 files changed, 123 insertions(+), 34 deletions(-) diff --git a/nym-wallet/src/components/Receive/ReceiveModal.tsx b/nym-wallet/src/components/Receive/ReceiveModal.tsx index 7a01cb5980..02e6e6a344 100644 --- a/nym-wallet/src/components/Receive/ReceiveModal.tsx +++ b/nym-wallet/src/components/Receive/ReceiveModal.tsx @@ -1,10 +1,11 @@ import React, { useContext } from 'react'; import { AppContext } from 'src/context'; -import { Box, Stack, SxProps } from '@mui/material'; +import { Box, Stack, SxProps, Typography, alpha, useTheme } from '@mui/material'; import QRCode from 'qrcode.react'; import { ClientAddress } from '@nymproject/react/client-address/ClientAddress'; import { ModalListItem } from '../Modals/ModalListItem'; import { SimpleModal } from '../Modals/SimpleModal'; +import { ArrowDownward } from '@mui/icons-material'; export const ReceiveModal = ({ onClose, @@ -16,51 +17,145 @@ export const ReceiveModal = ({ backdropProps?: object; }) => { const { clientDetails } = useContext(AppContext); + const theme = useTheme(); + + const isLightMode = theme.palette.mode === 'light'; + const highlightColor = theme.palette.nym.highlight; + const darkBgColor = theme.palette.background.default; + return ( - - - ) - } - /> - + + + Your address + + + + {clientDetails?.client_address && ( + + + + )} + + + + (t.palette.mode === 'dark' ? t.palette.background.default : 'rgba(251, 110, 78, 5%)'), - borderRadius: '0px 0px 16px 16px', + width: '100%', + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + pt: 1, + pb: 5, + mt: 2, + bgcolor: isLightMode + ? alpha(highlightColor, 0.06) + : alpha(darkBgColor, 0.7), + borderRadius: '0 0 16px 16px', }} > - t.palette.mode === 'dark' - ? `1px solid ${t.palette.nym.nymWallet.modal.border}` - : `1px solid ${t.palette.nym.highlight}`, - bgcolor: (t) => (t.palette.mode === 'dark' ? 'transparent' : 'white'), - borderRadius: 2, - p: 3, + position: 'relative', + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + width: '200px', + height: '200px', }} > - {clientDetails && } + + + + {clientDetails && ( + + )} + - + + + Scan this QR code with a compatible wallet to receive NYM tokens + + ); -}; +}; \ No newline at end of file diff --git a/sdk/typescript/packages/react-components/src/components/currency/CurrencyFormField.tsx b/sdk/typescript/packages/react-components/src/components/currency/CurrencyFormField.tsx index 72646dc488..3d6331fe91 100644 --- a/sdk/typescript/packages/react-components/src/components/currency/CurrencyFormField.tsx +++ b/sdk/typescript/packages/react-components/src/components/currency/CurrencyFormField.tsx @@ -130,12 +130,6 @@ export const CurrencyFormField: FCWithChildren<{ InputProps={{ readOnly, required, - endAdornment: showCoinMark && ( - - {denom === 'nym' && } - {denom !== 'nym' && NYMT} - - ), ...{ min: MIN_VALUE, max: MAX_VALUE, From 9f23887cc02f6e0b445414e01ddd36e9ea8050e3 Mon Sep 17 00:00:00 2001 From: Tommy Verrall Date: Mon, 7 Apr 2025 20:07:15 +0200 Subject: [PATCH 17/48] Input fields --- nym-wallet/src-tauri/Cargo.toml | 2 +- .../capabilities/main-capability.json | 6 +- .../src-tauri/gen/schemas/capabilities.json | 2 +- .../Bonding/forms/nym-node/NymNodeData.tsx | 36 +- .../forms/nym-node/NymNodeSignature.tsx | 21 +- .../src/components/Buy/SignMessageModal.tsx | 100 +++-- .../components/Clipboard/ClipboardActions.tsx | 253 +++++++++++ .../Clipboard/ClipboardFormFields.tsx | 402 ++++++++++++++++++ nym-wallet/src/components/Clipboard/index.ts | 2 + nym-wallet/src/components/CopyToClipboard.tsx | 60 --- .../components/Delegation/DelegateModal.tsx | 40 +- .../components/Delegation/DelegationList.tsx | 51 ++- nym-wallet/src/components/IdentityKey.tsx | 6 +- .../src/components/Receive/ReceiveModal.tsx | 118 ++--- .../src/components/Send/SendInputModal.tsx | 121 +++++- nym-wallet/src/components/index.ts | 2 +- .../settings-pages/general-settings/index.tsx | 2 +- 17 files changed, 972 insertions(+), 252 deletions(-) create mode 100644 nym-wallet/src/components/Clipboard/ClipboardActions.tsx create mode 100644 nym-wallet/src/components/Clipboard/ClipboardFormFields.tsx create mode 100644 nym-wallet/src/components/Clipboard/index.ts delete mode 100644 nym-wallet/src/components/CopyToClipboard.tsx diff --git a/nym-wallet/src-tauri/Cargo.toml b/nym-wallet/src-tauri/Cargo.toml index 039a3dc928..0f2f5ba6a1 100644 --- a/nym-wallet/src-tauri/Cargo.toml +++ b/nym-wallet/src-tauri/Cargo.toml @@ -21,7 +21,7 @@ tauri-macros = "2.1.1" [dependencies] async-trait = "0.1.68" tauri-plugin-updater = "2.7.0" -tauri-plugin-clipboard-manager = "2" +tauri-plugin-clipboard-manager = "2.0.0" tauri-plugin-shell = "2.2.1" tauri-plugin-process = "2.2.1" bip39 = { version = "2.0.0", features = ["zeroize", "rand"] } diff --git a/nym-wallet/src-tauri/capabilities/main-capability.json b/nym-wallet/src-tauri/capabilities/main-capability.json index ab0db25d88..456cab1ac6 100644 --- a/nym-wallet/src-tauri/capabilities/main-capability.json +++ b/nym-wallet/src-tauri/capabilities/main-capability.json @@ -4,7 +4,8 @@ "description": "Default capability for Nym Wallet main window", "windows": [ "main", - "nymWalletApp" + "nymWalletApp", + "log" ], "platforms": [ "linux", @@ -24,6 +25,7 @@ "core:window:allow-set-title", "core:app:allow-version", "clipboard-manager:allow-read-text", - "clipboard-manager:allow-write-text" + "clipboard-manager:allow-write-text", + "core:event:allow-listen" ] } \ No newline at end of file diff --git a/nym-wallet/src-tauri/gen/schemas/capabilities.json b/nym-wallet/src-tauri/gen/schemas/capabilities.json index 669ad3376c..8fee43048a 100644 --- a/nym-wallet/src-tauri/gen/schemas/capabilities.json +++ b/nym-wallet/src-tauri/gen/schemas/capabilities.json @@ -1 +1 @@ -{"main-capability":{"identifier":"main-capability","description":"Default capability for Nym Wallet main window","local":true,"windows":["main","nymWalletApp"],"permissions":["core:default","core:path:default","core:event:default","core:window:default","core:app:default","core:resources:default","core:menu:default","core:tray:default","shell:allow-open","core:window:allow-set-title","core:app:allow-version","clipboard-manager:allow-read-text","clipboard-manager:allow-write-text"],"platforms":["linux","macOS","windows"]},"shell-capability":{"identifier":"shell-capability","description":"Shell operations capability for Nym Wallet","local":true,"windows":["main","nymWalletApp"],"permissions":["shell:default","shell:allow-open"],"platforms":["linux","macOS","windows"]}} \ No newline at end of file +{"main-capability":{"identifier":"main-capability","description":"Default capability for Nym Wallet main window","local":true,"windows":["main","nymWalletApp","log"],"permissions":["core:default","core:path:default","core:event:default","core:window:default","core:app:default","core:resources:default","core:menu:default","core:tray:default","shell:allow-open","core:window:allow-set-title","core:app:allow-version","clipboard-manager:allow-read-text","clipboard-manager:allow-write-text","core:event:allow-listen"],"platforms":["linux","macOS","windows"]},"shell-capability":{"identifier":"shell-capability","description":"Shell operations capability for Nym Wallet","local":true,"windows":["main","nymWalletApp"],"permissions":["shell:default","shell:allow-open"],"platforms":["linux","macOS","windows"]}} \ No newline at end of file diff --git a/nym-wallet/src/components/Bonding/forms/nym-node/NymNodeData.tsx b/nym-wallet/src/components/Bonding/forms/nym-node/NymNodeData.tsx index 4fc8d7fc98..82c93ba4e2 100644 --- a/nym-wallet/src/components/Bonding/forms/nym-node/NymNodeData.tsx +++ b/nym-wallet/src/components/Bonding/forms/nym-node/NymNodeData.tsx @@ -1,10 +1,10 @@ import React from 'react'; import * as Yup from 'yup'; -import { Stack, TextField, FormControlLabel, Checkbox } from '@mui/material'; +import { Stack, FormControlLabel, Checkbox } from '@mui/material'; import { useForm } from 'react-hook-form'; -import { IdentityKeyFormField } from '@nymproject/react/mixnodes/IdentityKeyFormField'; import { yupResolver } from '@hookform/resolvers/yup'; import { SimpleModal } from 'src/components/Modals/SimpleModal'; +import { HookFormTextFieldWithPaste } from 'src/components/Clipboard/ClipboardFormFields'; import { useFormContext } from './FormContext'; import { settingsValidationSchema } from './settingsValidationSchema'; @@ -53,23 +53,24 @@ const NymNodeData = ({ onClose, onNext, step }: NymNodeDataProps) => { okDisabled={Object.keys(errors).length > 0} > - setValue('identity_key', value, { shouldValidate: true })} - showTickOnValid={false} /> - @@ -78,14 +79,15 @@ const NymNodeData = ({ onClose, onNext, step }: NymNodeDataProps) => { control={ setShowAdvancedOptions((show) => !show)} checked={showAdvancedOptions} />} label="Show advanced options" /> + {showAdvancedOptions && ( - diff --git a/nym-wallet/src/components/Bonding/forms/nym-node/NymNodeSignature.tsx b/nym-wallet/src/components/Bonding/forms/nym-node/NymNodeSignature.tsx index c4c809ca82..e7849bf50f 100644 --- a/nym-wallet/src/components/Bonding/forms/nym-node/NymNodeSignature.tsx +++ b/nym-wallet/src/components/Bonding/forms/nym-node/NymNodeSignature.tsx @@ -1,8 +1,8 @@ import React, { useEffect, useState } from 'react'; import * as yup from 'yup'; -import { Stack, TextField, Typography } from '@mui/material'; +import { Stack, Typography } from '@mui/material'; import { useForm } from 'react-hook-form'; -import { CopyToClipboard } from 'src/components/CopyToClipboard'; +import { CopyToClipboard, TextFieldWithPaste } from 'src/components/Clipboard'; import { ErrorModal } from 'src/components/Modals/ErrorModal'; import { SimpleModal } from 'src/components/Modals/SimpleModal'; import { useBondingContext } from 'src/context'; @@ -41,6 +41,7 @@ const NymNodeSignature = ({ register, formState: { errors }, handleSubmit, + setValue, } = useForm({ defaultValues: { signature, @@ -73,6 +74,11 @@ const NymNodeSignature = ({ handleSubmit(onNext)(); }; + const handleSignatureChange = (value: string) => { + setSignature(value); + setValue('signature', value, { shouldValidate: true }); + }; + if (error) { return {}} />; } @@ -98,14 +104,18 @@ const NymNodeSignature = ({
Then paste the signature in the next field. - + + + Copy Message {message && } - setSignature(e.target.value)} + onPasteValue={handleSignatureChange} + onChange={(e) => handleSignatureChange(e.target.value)} id="outlined-multiline-static" name="signature" rows={3} @@ -115,6 +125,7 @@ const NymNodeSignature = ({ multiline fullWidth required + value={signature} />
diff --git a/nym-wallet/src/components/Buy/SignMessageModal.tsx b/nym-wallet/src/components/Buy/SignMessageModal.tsx index d076971cd7..23d3c9a634 100644 --- a/nym-wallet/src/components/Buy/SignMessageModal.tsx +++ b/nym-wallet/src/components/Buy/SignMessageModal.tsx @@ -1,14 +1,15 @@ import * as React from 'react'; import { useState } from 'react'; -import { Stack, TextField, Typography } from '@mui/material'; +import { Stack, Typography, Box } from '@mui/material'; import { useBuyContext } from 'src/context'; import { SimpleModal } from '../Modals/SimpleModal'; import { ErrorModal } from '../Modals/ErrorModal'; -import { CopyToClipboard } from '../CopyToClipboard'; +import { TextFieldWithPaste } from '../Clipboard/ClipboardFormFields'; +import { CopyToClipboard } from '../Clipboard/ClipboardActions'; export const SignMessageModal = ({ onClose }: { onClose: () => void }) => { - const [message, setMessage] = useState(); - const [signature, setSignature] = useState(); + const [message, setMessage] = useState(''); + const [signature, setSignature] = useState(''); const { signMessage, loading, refresh, error } = useBuyContext(); @@ -16,13 +17,17 @@ export const SignMessageModal = ({ onClose }: { onClose: () => void }) => { if (!message) { return; } - signMessage(message).then((sig) => { - setSignature(sig); - }); + try { + const sig = await signMessage(message); + setSignature(sig || ''); + } catch (err) { + console.error('Signing failed:', err); + setSignature(''); + } }; - const handleChange = (event: React.ChangeEvent) => { - setMessage(event.target.value); + const handleMessageChange = (value: string) => { + setMessage(value); }; if (error) { @@ -38,34 +43,57 @@ export const SignMessageModal = ({ onClose }: { onClose: () => void }) => { } return ( - - - - - - - - Copy signature + + + + + Message to Sign - - + handleMessageChange(e.target.value)} + InputLabelProps={{ shrink: false }} + /> + + + + + Signature + + + + + + ) : undefined, + }} + /> + + ); diff --git a/nym-wallet/src/components/Clipboard/ClipboardActions.tsx b/nym-wallet/src/components/Clipboard/ClipboardActions.tsx new file mode 100644 index 0000000000..065194ad78 --- /dev/null +++ b/nym-wallet/src/components/Clipboard/ClipboardActions.tsx @@ -0,0 +1,253 @@ +import React, { useEffect, useState, useRef } from 'react'; +import { Button, IconButton, Tooltip } from '@mui/material'; +import { Check, ContentCopy, ContentPaste } from '@mui/icons-material'; +import { writeText, readText } from '@tauri-apps/plugin-clipboard-manager'; +import { Console } from '../../utils/console'; + +/** + * ClipboardActions component handles copy and paste operations + * Can be used as standalone buttons or as icons + */ +export const ClipboardActions = ({ + text = '', + iconButton = false, + showCopy = true, + showPaste = true, + onPaste, + pasteTooltip = 'Paste', + copyTooltip = 'Copy', + onCopy, + fieldRef, +}: { + text?: string; + iconButton?: boolean; + showCopy?: boolean; + showPaste?: boolean; + onPaste?: (pastedText: string) => void; + pasteTooltip?: string; + copyTooltip?: string; + onCopy?: () => void; + fieldRef?: React.MutableRefObject; +}) => { + const [copied, setCopied] = useState(false); + const [pasted, setPasted] = useState(false); + + const handleCopy = async (_text: string) => { + try { + await writeText(_text); + setCopied(true); + onCopy?.(); + } catch (e) { + Console.error(`failed to copy: ${e}`); + } + }; + + const handlePaste = async () => { + try { + // Try Tauri clipboard first + const pastedText = await readText(); + + if (pastedText) { + onPaste?.(pastedText); + setPasted(true); + } + } catch (e) { + // Fallback to browser clipboard + try { + const pastedText = await navigator.clipboard.readText(); + if (pastedText) { + onPaste?.(pastedText); + setPasted(true); + } + } catch (err) { + Console.error(`paste failed: ${err}`); + } + } + }; + + const wasSelectAllPressed = useRef(false); + + useEffect(() => { + if (!fieldRef) return; + + const keydownHandler = async (e: KeyboardEvent) => { + // Only handle if the associated field is focused + const { activeElement } = document; + if (fieldRef.current && activeElement === fieldRef.current) { + // Handle Select All (Cmd+A or Ctrl+A) + if ((e.metaKey || e.ctrlKey) && e.key === 'a') { + wasSelectAllPressed.current = true; + return; + } + + // Handle paste (Cmd+V or Ctrl+V) + if ((e.metaKey || e.ctrlKey) && e.key === 'v' && onPaste) { + e.preventDefault(); + await handlePaste(); + return; + } + + if ((e.metaKey || e.ctrlKey) && e.key === 'c' && showCopy) { + const field = fieldRef.current; + + const selectedText = field.value.substring(field.selectionStart || 0, field.selectionEnd || 0); + + if (selectedText || wasSelectAllPressed.current) { + e.preventDefault(); + + if (wasSelectAllPressed.current && !selectedText) { + field.select(); + const textToCopy = field.value; + await writeText(textToCopy); + } else { + await writeText(selectedText); + } + + setCopied(true); + onCopy?.(); + wasSelectAllPressed.current = false; + } else if (text) { + e.preventDefault(); + await writeText(text); + setCopied(true); + onCopy?.(); + } + } + } + }; + + // When field loses focus, reset the Select All tracking + const blurHandler = () => { + wasSelectAllPressed.current = false; + }; + + // Add keydown and blur event listeners + document.addEventListener('keydown', keydownHandler); + if (fieldRef.current) { + fieldRef.current.addEventListener('blur', blurHandler); + } + + return () => { + document.removeEventListener('keydown', keydownHandler); + if (fieldRef.current) { + fieldRef.current.removeEventListener('blur', blurHandler); + } + }; + }, [onPaste, fieldRef, text, showCopy, onCopy]); + + useEffect(() => { + const timer = setTimeout(() => { + setCopied(false); + setPasted(false); + }, 2000); + return () => clearTimeout(timer); + }, [copied, pasted]); + + if (iconButton) { + return ( +
+ {showCopy && ( + + handleCopy(text)} + size="small" + sx={{ + color: 'text.primary', + mr: showPaste ? 0.5 : 0, + }} + disabled={!text} + > + {!copied ? : } + + + )} + + {showPaste && ( + + + {!pasted ? : } + + + )} +
+ ); + } + + return ( +
+ {showCopy && ( + + )} + + {showPaste && ( + + )} +
+ ); +}; + +// For backward compatibility +export const CopyToClipboard = ({ + text = '', + iconButton, + onPaste, + fieldRef, +}: { + text?: string; + iconButton?: boolean; + onPaste?: (pastedText: string) => void; + fieldRef?: React.MutableRefObject; +}) => ( + +); + +// Export a paste-only variant for convenience +export const PasteFromClipboard = ({ + onPaste, + iconButton = true, + tooltip = 'Paste', + fieldRef, +}: { + onPaste: (pastedText: string) => void; + iconButton?: boolean; + tooltip?: string; + fieldRef?: React.MutableRefObject; +}) => ( + +); diff --git a/nym-wallet/src/components/Clipboard/ClipboardFormFields.tsx b/nym-wallet/src/components/Clipboard/ClipboardFormFields.tsx new file mode 100644 index 0000000000..c90c17def1 --- /dev/null +++ b/nym-wallet/src/components/Clipboard/ClipboardFormFields.tsx @@ -0,0 +1,402 @@ +import React, { useRef, useEffect } from 'react'; +import { TextField, InputAdornment, Box, TextFieldProps } from '@mui/material'; +import { CurrencyFormField } from '@nymproject/react/currency/CurrencyFormField'; +import { CurrencyDenom, DecCoin } from '@nymproject/types'; +import { UseFormRegister, UseFormSetValue, FieldValues, Path, FieldErrors } from 'react-hook-form'; +import { writeText } from '@tauri-apps/plugin-clipboard-manager'; +import { PasteFromClipboard } from './ClipboardActions'; + +export const useCopyAllSupport = (inputRef: React.MutableRefObject) => { + useEffect(() => { + if (!inputRef.current) return; + const handleKeyDown = (e: Event) => { + const keyEvent = e as KeyboardEvent; + + if (document.activeElement !== inputRef.current) return; + + // Handle Cmd+A or Ctrl+A (Select All) + if ((keyEvent.metaKey || keyEvent.ctrlKey) && keyEvent.key === 'a') { + setTimeout(() => { + if (document.activeElement === inputRef.current && inputRef.current) { + inputRef.current.select(); + } + }, 0); + } + + // Handle Cmd+C or Ctrl+C (Copy) + if ((keyEvent.metaKey || keyEvent.ctrlKey) && keyEvent.key === 'c') { + if (inputRef.current && inputRef.current.selectionStart !== inputRef.current.selectionEnd) { + const selectedText = inputRef.current.value.substring( + inputRef.current.selectionStart || 0, + inputRef.current.selectionEnd || 0, + ); + + if (selectedText) { + keyEvent.preventDefault(); + writeText(selectedText).catch((err) => console.error('Failed to copy text:', err)); + } + } + } + }; + + const input = inputRef.current; + input.addEventListener('keydown', handleKeyDown); + + return () => { + if (input) { + input.removeEventListener('keydown', handleKeyDown); + } + }; + }, [inputRef.current]); +}; + +export const TextFieldWithPaste = React.forwardRef< + HTMLDivElement, + TextFieldProps & { + onPasteValue?: (value: string) => void; + } +>(({ onPasteValue, ...props }, ref) => { + const inputRef = useRef(null); + + useCopyAllSupport(inputRef); + + const handlePaste = (pastedText: string) => { + onPasteValue?.(pastedText); + + if (inputRef.current) { + inputRef.current.focus(); + } + }; + + return ( + + {onPasteValue && } + {props.InputProps?.endAdornment} + + ), + }} + /> + ); +}); + +export const CurrencyFormFieldWithPaste = ({ + label, + fullWidth, + onChanged, + initialValue, + denom, + required, + autoFocus, + validationError, +}: { + label: string; + fullWidth?: boolean; + onChanged: (value: DecCoin) => void; + initialValue?: string; + denom?: CurrencyDenom; + required?: boolean; + autoFocus?: boolean; + validationError?: string; +}) => { + const fieldRef = useRef(null); + const inputRef = useRef(null); + + // Enable copy-all support for this field + useEffect(() => { + if (!inputRef.current) return; + + const handleKeyDown = (e: Event) => { + const keyEvent = e as KeyboardEvent; + if (document.activeElement !== inputRef.current) return; + + // Handle Cmd+A (Select All) + if ((keyEvent.metaKey || keyEvent.ctrlKey) && keyEvent.key === 'a' && inputRef.current) { + setTimeout(() => { + if (inputRef.current) inputRef.current.select(); + }, 0); + } + + // Handle Cmd+C (Copy) + if ((keyEvent.metaKey || keyEvent.ctrlKey) && keyEvent.key === 'c' && inputRef.current) { + if (inputRef.current.selectionStart !== inputRef.current.selectionEnd) { + const selectedText = inputRef.current.value.substring( + inputRef.current.selectionStart || 0, + inputRef.current.selectionEnd || 0, + ); + + if (selectedText) { + keyEvent.preventDefault(); + writeText(selectedText).catch((err) => console.error('Failed to copy text:', err)); + } + } + } + }; + + const input = inputRef.current; + if (input) { + input.addEventListener('keydown', handleKeyDown); + } + + return () => { + if (input) { + input.removeEventListener('keydown', handleKeyDown); + } + }; + }, [inputRef.current]); + + const handlePaste = (pastedText: string) => { + if (!pastedText) return; + + let cleanedValue = pastedText.trim(); + + cleanedValue = cleanedValue.replace(/[^\d.]/g, ''); + + const parts = cleanedValue.split('.'); + if (parts.length > 2) { + cleanedValue = `${parts[0]}.${parts.slice(1).join('')}`; + } + + const decCoin: DecCoin = { + amount: cleanedValue, + denom: denom as any, + }; + + onChanged(decCoin); + + if (inputRef.current) { + inputRef.current.focus(); + } + }; + + useEffect(() => { + // Function to find the input element + const findInputElement = () => { + if (fieldRef.current) { + inputRef.current = fieldRef.current.querySelector('input'); + } + }; + + // Try immediately after mount + findInputElement(); + + // Also try after a short delay to ensure component has fully rendered + const timeoutId = setTimeout(findInputElement, 100); + + return () => clearTimeout(timeoutId); + }, []); + + return ( + + + + + + + ); +}; + +type CurrencyFieldError = { + amount?: { + message?: string; + }; +}; + +export const HookFormTextFieldWithPaste = ({ + name, + label, + register, + setValue, + errors, + ...props +}: { + name: Path; + label: string; + register: UseFormRegister; + setValue: UseFormSetValue; + errors: FieldErrors; +} & Omit) => { + const inputRef = useRef(null); + + useCopyAllSupport(inputRef); + + const handlePaste = (pastedText: string) => { + setValue(name, pastedText as any, { shouldValidate: true }); + + if (inputRef.current) { + inputRef.current.focus(); + } + }; + + return ( + + + + ), + ...props.InputProps, + }} + {...props} + /> + ); +}; + +export const HookFormCurrencyFieldWithPaste = ({ + name, + label, + setValue, + errors, + denom, + initialValue, + ...props +}: { + name: Path; + label: string; + setValue: UseFormSetValue; + errors: FieldErrors; + denom?: CurrencyDenom; + initialValue?: string; +}) => { + const fieldRef = useRef(null); + const inputRef = useRef(null); + + // Enable copy-all support for this field + useEffect(() => { + if (!inputRef.current) return; + + const handleKeyDown = (e: Event) => { + const keyEvent = e as KeyboardEvent; + if (document.activeElement !== inputRef.current) return; + + // Handle Cmd+A (Select All) + if ((keyEvent.metaKey || keyEvent.ctrlKey) && keyEvent.key === 'a' && inputRef.current) { + setTimeout(() => { + if (inputRef.current) inputRef.current.select(); + }, 0); + } + + // Handle Cmd+C (Copy) + if ((keyEvent.metaKey || keyEvent.ctrlKey) && keyEvent.key === 'c' && inputRef.current) { + if (inputRef.current.selectionStart !== inputRef.current.selectionEnd) { + const selectedText = inputRef.current.value.substring( + inputRef.current.selectionStart || 0, + inputRef.current.selectionEnd || 0, + ); + + if (selectedText) { + keyEvent.preventDefault(); + writeText(selectedText).catch((err) => console.error('Failed to copy text:', err)); + } + } + } + }; + + const input = inputRef.current; + if (input) { + input.addEventListener('keydown', handleKeyDown); + } + + return () => { + if (input) { + input.removeEventListener('keydown', handleKeyDown); + } + }; + }, [inputRef.current]); + + // Handle pasting with number formatting + const handlePaste = (pastedText: string) => { + if (!pastedText) return; + + let cleanedValue = pastedText.trim(); + + cleanedValue = cleanedValue.replace(/[^\d.]/g, ''); + + const parts = cleanedValue.split('.'); + if (parts.length > 2) { + cleanedValue = `${parts[0]}.${parts.slice(1).join('')}`; + } + + setValue(`${String(name)}.amount` as Path, cleanedValue as any, { shouldValidate: true }); + + if (inputRef.current) { + inputRef.current.focus(); + } + }; + + useEffect(() => { + const findInputElement = () => { + if (fieldRef.current) { + inputRef.current = fieldRef.current.querySelector('input'); + } + }; + + findInputElement(); + const timeoutId = setTimeout(findInputElement, 100); + + return () => clearTimeout(timeoutId); + }, []); + + // Safely access error message + const getErrorMessage = (): string | undefined => { + const fieldError = errors[name] as unknown as CurrencyFieldError | undefined; + return fieldError?.amount?.message; + }; + + return ( + + { + setValue(`${String(name)}.amount` as Path, value.amount as any, { shouldValidate: true }); + }} + initialValue={initialValue} + denom={denom} + validationError={getErrorMessage()} + {...props} + /> + + + + + ); +}; diff --git a/nym-wallet/src/components/Clipboard/index.ts b/nym-wallet/src/components/Clipboard/index.ts new file mode 100644 index 0000000000..9c84228cc8 --- /dev/null +++ b/nym-wallet/src/components/Clipboard/index.ts @@ -0,0 +1,2 @@ +export * from './ClipboardActions'; +export * from './ClipboardFormFields'; diff --git a/nym-wallet/src/components/CopyToClipboard.tsx b/nym-wallet/src/components/CopyToClipboard.tsx deleted file mode 100644 index 140dc75812..0000000000 --- a/nym-wallet/src/components/CopyToClipboard.tsx +++ /dev/null @@ -1,60 +0,0 @@ -import React, { useEffect, useState } from 'react'; -import { Button, IconButton, Tooltip } from '@mui/material'; -import { Check, ContentCopy } from '@mui/icons-material'; -import { writeText } from '@tauri-apps/plugin-clipboard-manager'; -import { Console } from '../utils/console'; - -export const CopyToClipboard = ({ text = '', iconButton }: { text?: string; iconButton?: boolean }) => { - const [copied, setCopied] = useState(false); - - const handleCopy = async (_text: string) => { - try { - await writeText(_text); - setCopied(true); - } catch (e) { - Console.error(`failed to copy: ${e}`); - } - }; - - useEffect(() => { - let timer: NodeJS.Timeout; - if (copied) { - timer = setTimeout(() => { - setCopied(false); - }, 2000); - } - return () => clearTimeout(timer); - }, [copied]); - - if (iconButton) - return ( - - handleCopy(text)} - size="small" - sx={{ - color: 'text.primary', - }} - disabled={!text} - > - {!copied ? : } - - - ); - - return ( - - ); -}; diff --git a/nym-wallet/src/components/Delegation/DelegateModal.tsx b/nym-wallet/src/components/Delegation/DelegateModal.tsx index 88b91627c7..b923c20136 100644 --- a/nym-wallet/src/components/Delegation/DelegateModal.tsx +++ b/nym-wallet/src/components/Delegation/DelegateModal.tsx @@ -1,7 +1,5 @@ import React, { useCallback, useContext, useState } from 'react'; -import { Box, Typography, SxProps } from '@mui/material'; -import { IdentityKeyFormField } from '@nymproject/react/mixnodes/IdentityKeyFormField'; -import { CurrencyFormField } from '@nymproject/react/currency/CurrencyFormField'; +import { Box, SxProps } from '@mui/material'; import { CurrencyDenom, FeeDetails, DecCoin, decimalToFloatApproximation } from '@nymproject/types'; import { Console } from 'src/utils/console'; import { useGetFee } from 'src/hooks/useGetFee'; @@ -13,10 +11,10 @@ import { ModalListItem } from '../Modals/ModalListItem'; import { checkTokenBalance, validateAmount, validateKey } from '../../utils'; import { TokenPoolSelector, TPoolOption } from '../TokenPoolSelector'; import { ConfirmTx } from '../ConfirmTX'; - import { getMixnodeStakeSaturation } from '../../requests'; import { ErrorModal } from '../Modals/ErrorModal'; import { BalanceWarning } from '../FeeWarning'; +import { TextFieldWithPaste, CurrencyFormFieldWithPaste } from '../Clipboard/ClipboardFormFields'; const MIN_AMOUNT_TO_DELEGATE = 10; @@ -255,36 +253,24 @@ export const DelegateModal: FCWithChildren<{ backdropProps={backdropProps} > - handleIdentityKeyChanged(e.target.value)} + onPasteValue={handleIdentityKeyChanged} + error={Boolean(errorIdentityKey)} + helperText={errorIdentityKey} + InputLabelProps={{ shrink: true }} /> - - {errorIdentityKey} - {hasVestingContract && setTokenPool(pool)} />} - diff --git a/nym-wallet/src/components/Delegation/DelegationList.tsx b/nym-wallet/src/components/Delegation/DelegationList.tsx index 776a412e6b..0005c52caf 100644 --- a/nym-wallet/src/components/Delegation/DelegationList.tsx +++ b/nym-wallet/src/components/Delegation/DelegationList.tsx @@ -1,16 +1,29 @@ import React from 'react'; -import { Alert, AlertTitle, Box, Button, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, TableSortLabel, Typography } from '@mui/material'; +import { + Alert, + AlertTitle, + Box, + Button, + Table, + TableBody, + TableCell, + TableContainer, + TableHead, + TableRow, + TableSortLabel, + Typography, +} from '@mui/material'; import { visuallyHidden } from '@mui/utils'; import ArrowDropDownIcon from '@mui/icons-material/ArrowDropDown'; import { DelegationWithEverything } from '@nymproject/types'; import { useSortDelegations } from 'src/hooks/useSortDelegations'; +import { useNavigate } from 'react-router-dom'; import { DelegationListItemActions } from './DelegationActions'; import { DelegationItem } from './DelegationItem'; import { PendingDelegationItem } from './PendingDelegationItem'; import { LoadingModal } from '../Modals/LoadingModal'; import { isDelegation, isPendingDelegation, TDelegations, useDelegationContext } from '../../context/delegations'; import { ErrorModal } from '../Modals/ErrorModal'; -import { useNavigate } from 'react-router-dom'; export type Order = 'asc' | 'desc'; type AdditionalTypes = { profit_margin_percent: number; operating_cost: number }; @@ -86,11 +99,10 @@ const EnhancedTableHead: FCWithChildren = ({ order, orderBy, const hasPruningError = (item: any): boolean => { if (!isDelegation(item) || !item.errors) return false; - + return ( - item.errors.includes("height") && - item.errors.includes("not available") || - item.errors.includes("Due to pruning strategies") + (item.errors.includes('height') && item.errors.includes('not available')) || + item.errors.includes('Due to pruning strategies') ); }; @@ -114,11 +126,9 @@ export const DelegationList: FCWithChildren<{ }; const sorted = useSortDelegations(items, order, orderBy); - + // Check if any delegations have pruning errors - const hasPruningErrors = React.useMemo(() => { - return sorted?.some(item => hasPruningError(item)); - }, [sorted]); + const hasPruningErrors = React.useMemo(() => sorted?.some((item) => hasPruningError(item)), [sorted]); // Navigate to settings page const navigateToSettings = () => { @@ -127,8 +137,8 @@ export const DelegationList: FCWithChildren<{ // Format error message for display const formatErrorMessage = (message: string) => { - if (message.includes("height") && message.includes("not available")) { - return "Due to pruning strategies from validators, please navigate to the Settings tab and change your RPC node for your validator to retrieve your delegations."; + if (message.includes('height') && message.includes('not available')) { + return 'Due to pruning strategies from validators, please navigate to the Settings tab and change your RPC node for your validator to retrieve your delegations.'; } return message; }; @@ -137,8 +147,8 @@ export const DelegationList: FCWithChildren<{ <> {/* Display pruning error alert at the top if needed */} {hasPruningErrors && ( - @@ -148,21 +158,19 @@ export const DelegationList: FCWithChildren<{ > Data Pruning Detected - Some delegation details cannot be retrieved because of data pruning on the validator. - Please navigate to the Settings tab and change your RPC node to fix this issue. + Some delegation details cannot be retrieved because of data pruning on the validator. Please navigate to the + Settings tab and change your RPC node to fix this issue. )} - + {isLoading && } setDelegationItemErrors(undefined)} /> @@ -172,7 +180,6 @@ export const DelegationList: FCWithChildren<{ {sorted?.length ? sorted.map((item: any, index: number) => { if (isPendingDelegation(item)) { - // Use index for key instead of mix_id which might not be directly accessible return ; } if (isDelegation(item)) @@ -193,4 +200,4 @@ export const DelegationList: FCWithChildren<{ ); -}; \ No newline at end of file +}; diff --git a/nym-wallet/src/components/IdentityKey.tsx b/nym-wallet/src/components/IdentityKey.tsx index bca08ab68c..283fbfe921 100644 --- a/nym-wallet/src/components/IdentityKey.tsx +++ b/nym-wallet/src/components/IdentityKey.tsx @@ -4,10 +4,10 @@ import { CopyToClipboard } from '@nymproject/react/clipboard/CopyToClipboard'; import { splice } from 'src/utils'; export const IdentityKey = ({ identityKey }: { identityKey: string }) => ( - - + + {splice(6, identityKey)} - + ); diff --git a/nym-wallet/src/components/Receive/ReceiveModal.tsx b/nym-wallet/src/components/Receive/ReceiveModal.tsx index 02e6e6a344..b7373cadc0 100644 --- a/nym-wallet/src/components/Receive/ReceiveModal.tsx +++ b/nym-wallet/src/components/Receive/ReceiveModal.tsx @@ -3,9 +3,7 @@ import { AppContext } from 'src/context'; import { Box, Stack, SxProps, Typography, alpha, useTheme } from '@mui/material'; import QRCode from 'qrcode.react'; import { ClientAddress } from '@nymproject/react/client-address/ClientAddress'; -import { ModalListItem } from '../Modals/ModalListItem'; import { SimpleModal } from '../Modals/SimpleModal'; -import { ArrowDownward } from '@mui/icons-material'; export const ReceiveModal = ({ onClose, @@ -18,11 +16,11 @@ export const ReceiveModal = ({ }) => { const { clientDetails } = useContext(AppContext); const theme = useTheme(); - + const isLightMode = theme.palette.mode === 'light'; const highlightColor = theme.palette.nym.highlight; const darkBgColor = theme.palette.background.default; - + return ( - - - + + + Your address - - {clientDetails?.client_address && ( - - + letterSpacing: '0.5px', + wordBreak: 'break-all', + }} + > + )} - - - + {clientDetails && ( - - - Scan this QR code with a compatible wallet to receive NYM tokens @@ -158,4 +170,4 @@ export const ReceiveModal = ({ ); -}; \ No newline at end of file +}; diff --git a/nym-wallet/src/components/Send/SendInputModal.tsx b/nym-wallet/src/components/Send/SendInputModal.tsx index 842332120d..9f9a72bb5e 100644 --- a/nym-wallet/src/components/Send/SendInputModal.tsx +++ b/nym-wallet/src/components/Send/SendInputModal.tsx @@ -1,14 +1,27 @@ import React, { useEffect, useState } from 'react'; -import { Stack, TextField, Typography, SxProps, FormControlLabel, Checkbox } from '@mui/material'; +import { Stack, Typography, SxProps, FormControlLabel, Checkbox } from '@mui/material'; import Big from 'big.js'; -import { CurrencyFormField } from '@nymproject/react/currency/CurrencyFormField'; import { CurrencyDenom, DecCoin, isValidRawCoin } from '@nymproject/types'; import { validateAmount } from 'src/utils'; import { SimpleModal } from '../Modals/SimpleModal'; import { ModalListItem } from '../Modals/ModalListItem'; +import { TextFieldWithPaste, CurrencyFormFieldWithPaste } from '../Clipboard/ClipboardFormFields'; const maxUserFees = '10.0'; const minUserFees = '0.000001'; // aka 1 unym +const MIN_AMOUNT_TO_SEND = '0.000001'; // Adjust this as needed + +// NYM address validation function +const validateNymAddress = (address: string): boolean => { + if (!address) return false; + + if (!address.startsWith('n1')) return false; + + if (address.length < 40 || address.length > 42) return false; + + const validCharsRegex = /^[a-z0-9]+$/; + return validCharsRegex.test(address); +}; export const SendInputModal = ({ fromAddress, @@ -52,28 +65,66 @@ export const SendInputModal = ({ const [isValid, setIsValid] = useState(false); const [memoIsValid, setMemoIsValid] = useState(true); const [feeAmountIsValid, setFeeAmountIsValid] = useState(true); + const [addressIsValid, setAddressIsValid] = useState(false); + const [errorAmount, setErrorAmount] = useState(); + const [errorFee, setErrorFee] = useState(); - const validate = async (value: DecCoin) => { - const isValidAmount = await validateAmount(value.amount, '0'); - setIsValid(isValidAmount); + const validateSendAmount = async (value: DecCoin) => { + let newValidatedValue = true; + let errorAmountMessage; + + if (!value.amount) { + newValidatedValue = false; + } else { + // Validate amount format + if (!(await validateAmount(value.amount, '0'))) { + newValidatedValue = false; + errorAmountMessage = 'Please enter a valid amount'; + } + + // Check minimum amount + if (Number(value.amount) < Number(MIN_AMOUNT_TO_SEND)) { + newValidatedValue = false; + errorAmountMessage = `Min. send amount: ${MIN_AMOUNT_TO_SEND} ${denom?.toUpperCase()}`; + } + } + + setIsValid(newValidatedValue); + setErrorAmount(errorAmountMessage); + return newValidatedValue; }; const validateUserFees = (fees: DecCoin) => { + let isValid = true; + let errorFeeMessage; + if (!isValidRawCoin(fees.amount) || !Number(fees.amount)) { - setFeeAmountIsValid(false); - return; + isValid = false; + errorFeeMessage = 'Please enter a valid fee amount'; + } else { + const f = Big(fees.amount); + if (f.gt(maxUserFees)) { + isValid = false; + errorFeeMessage = `Max fee: ${maxUserFees} ${denom?.toUpperCase()}`; + } else if (f.lt(minUserFees)) { + isValid = false; + errorFeeMessage = `Min. fee: ${minUserFees} ${denom?.toUpperCase()}`; + } } - const f = Big(fees.amount); - if (f.gt(maxUserFees) || f.lt(minUserFees)) { - setFeeAmountIsValid(false); - return; - } - setFeeAmountIsValid(true); + + setFeeAmountIsValid(isValid); + setErrorFee(errorFeeMessage); + return isValid; }; useEffect(() => { - if (amount) validate(amount); - }, []); + if (amount) validateSendAmount(amount); + }, [amount]); + + // Effect to validate address whenever it changes + useEffect(() => { + setAddressIsValid(validateNymAddress(toAddress)); + }, [toAddress]); useEffect(() => { if (memo && !/^(\w|\s)+$/.test(memo)) { @@ -98,30 +149,44 @@ export const SendInputModal = ({ onClose={onClose} okLabel="Next" onOk={async () => onNext()} - okDisabled={!isValid || !memoIsValid || !feeAmountIsValid} + okDisabled={!isValid || !memoIsValid || !feeAmountIsValid || !addressIsValid} sx={sx} backdropProps={backdropProps} > - onAddressChange(e.target.value)} value={toAddress} + error={toAddress !== '' && !addressIsValid} + helperText={ + toAddress !== '' && !addressIsValid + ? 'Invalid NYM address. Must start with n1 and be 40-42 characters long.' + : undefined + } InputLabelProps={{ shrink: true }} + onPasteValue={onAddressChange} /> - { onAmountChange(value); - validate(value); + validateSendAmount(value); }} initialValue={amount?.amount} denom={denom} + validationError={errorAmount} /> - onMemoChange(e.target.value)} @@ -133,29 +198,39 @@ export const SendInputModal = ({ } InputLabelProps={{ shrink: true }} fullWidth + onPasteValue={onMemoChange} /> + {error} + Est. fee for this transaction will be shown on the next page + setShowMore(!showMore)} checked={showMore} />} label="More options" sx={{ mt: 2 }} /> + {showMore && ( - - + onUserFeesChange(v)} + onChanged={(v) => { + onUserFeesChange(v); + validateUserFees(v); + }} initialValue={userFees?.amount} fullWidth + denom={denom} + validationError={errorFee} /> )} diff --git a/nym-wallet/src/components/index.ts b/nym-wallet/src/components/index.ts index 4174b0c631..904d1a3097 100644 --- a/nym-wallet/src/components/index.ts +++ b/nym-wallet/src/components/index.ts @@ -1,6 +1,6 @@ export * from './AppBar'; export * from './ConfirmPassword'; -export * from './CopyToClipboard'; +export * from './Clipboard'; export * from './ErrorFallback'; export * from './InfoToolTip'; export * from './LoadingPage'; diff --git a/nym-wallet/src/pages/bonding/node-settings/settings-pages/general-settings/index.tsx b/nym-wallet/src/pages/bonding/node-settings/settings-pages/general-settings/index.tsx index 5157587737..c4a5861885 100644 --- a/nym-wallet/src/pages/bonding/node-settings/settings-pages/general-settings/index.tsx +++ b/nym-wallet/src/pages/bonding/node-settings/settings-pages/general-settings/index.tsx @@ -1,6 +1,6 @@ import React, { useState } from 'react'; import { Box, Button, Divider, Grid } from '@mui/material'; -import { isGateway, isMixnode, isNymNode } from 'src/types'; +import { isMixnode, isNymNode } from 'src/types'; import { TBondedNode } from 'src/context/bonding'; import { ParametersSettings } from './ParametersSettings'; import { GeneralNymNodeSettings } from './GeneralNymNodeSettings'; From a4f6426bf90311106f24227f7b07fa9d31d52243 Mon Sep 17 00:00:00 2001 From: Tommy Verrall Date: Tue, 8 Apr 2025 09:32:46 +0200 Subject: [PATCH 18/48] Update account display --- .../src/components/Accounts/AccountItem.tsx | 125 ++- .../Accounts/modals/AccountsModal.tsx | 155 +++- nym-wallet/src/theme/NymWalletTheme.tsx | 27 +- nym-wallet/src/theme/index.tsx | 7 +- nym-wallet/src/theme/mui-theme.d.ts | 15 +- nym-wallet/src/theme/theme.tsx | 734 +++++++++++++++--- 6 files changed, 903 insertions(+), 160 deletions(-) diff --git a/nym-wallet/src/components/Accounts/AccountItem.tsx b/nym-wallet/src/components/Accounts/AccountItem.tsx index 2a0b70d5df..92271a6a4b 100644 --- a/nym-wallet/src/components/Accounts/AccountItem.tsx +++ b/nym-wallet/src/components/Accounts/AccountItem.tsx @@ -1,5 +1,6 @@ import React, { useContext } from 'react'; import EditIcon from '@mui/icons-material/Create'; +import CheckCircleIcon from '@mui/icons-material/CheckCircle'; import { Box, IconButton, @@ -9,6 +10,8 @@ import { ListItemText, Tooltip, Typography, + alpha, + useTheme, } from '@mui/material'; import { useClipboard } from 'use-clipboard-copy'; import { AccountsContext } from 'src/context'; @@ -25,29 +28,98 @@ export const AccountItem = ({ }) => { const { selectedAccount, setDialogToDisplay, setAccountMnemonic, handleAccountToEdit } = useContext(AccountsContext); const { copy, copied } = useClipboard({ copiedTimeout: 1000 }); + const theme = useTheme(); + + const isSelected = selectedAccount?.id === name; + return ( { - handleAccountToEdit(name); - setDialogToDisplay('Edit'); - }} - > - - + + {isSelected && ( + + )} + { + handleAccountToEdit(name); + setDialogToDisplay('Edit'); + }} + > + + + } > - - + + {/* Account avatar with box wrapper to apply styling */} + + {name} + + } secondary={ @@ -58,7 +130,17 @@ export const AccountItem = ({ e.stopPropagation(); copy(address); }} - sx={{ '&:hover': { color: 'grey.900' } }} + sx={{ + fontFamily: 'monospace', + cursor: 'pointer', + color: theme.palette.mode === 'dark' + ? theme.palette.nym.nymWallet.text.muted + : alpha(theme.palette.text.primary, 0.7), + '&:hover': { + color: theme.palette.text.primary, + textDecoration: 'underline', + }, + }} > {address} @@ -67,7 +149,18 @@ export const AccountItem = ({ ) => { e.stopPropagation(); setDialogToDisplay('Mnemonic'); @@ -83,4 +176,4 @@ export const AccountItem = ({ ); -}; +}; \ No newline at end of file diff --git a/nym-wallet/src/components/Accounts/modals/AccountsModal.tsx b/nym-wallet/src/components/Accounts/modals/AccountsModal.tsx index 7c7df2e7ef..964700e410 100644 --- a/nym-wallet/src/components/Accounts/modals/AccountsModal.tsx +++ b/nym-wallet/src/components/Accounts/modals/AccountsModal.tsx @@ -10,9 +10,11 @@ import { IconButton, Typography, Divider, + alpha, + useTheme, + List, } from '@mui/material'; -import { Add, ArrowDownwardSharp, Close } from '@mui/icons-material'; -import { useTheme } from '@mui/material/styles'; +import { Add, ArrowDownwardSharp, Close, SwapHorizOutlined } from '@mui/icons-material'; import { AccountsContext } from 'src/context'; import { AccountItem } from '../AccountItem'; import { ConfirmPasswordModal } from './ConfirmPasswordModal'; @@ -52,23 +54,89 @@ export const AccountsModal = () => { open={dialogToDisplay === 'Accounts'} onClose={handleClose} fullWidth + maxWidth="sm" PaperProps={{ - style: { border: `1px solid ${theme.palette.nym.nymWallet.modal.border}` }, + elevation: 4, + sx: { + borderRadius: 2, + overflow: 'hidden', + border: `1px solid ${theme.palette.nym.nymWallet.modal.border}`, + display: 'flex', + flexDirection: 'column', + maxHeight: '80vh', // Limit maximum height + ...(theme.palette.mode === 'dark' && { + backgroundImage: 'linear-gradient(180deg, rgba(50, 55, 61, 0.8), rgba(36, 43, 45, 0.95))', + }), + }, }} > - - - - Accounts - - - + + + + + + Accounts + - - Switch between accounts - - - + + + + + + Switch between accounts + + + + + {accounts?.map(({ id, address }) => ( { }} /> ))} - - - - - + ); -}; +}; \ No newline at end of file diff --git a/nym-wallet/src/theme/NymWalletTheme.tsx b/nym-wallet/src/theme/NymWalletTheme.tsx index 4cb08eb645..08ea4253d5 100644 --- a/nym-wallet/src/theme/NymWalletTheme.tsx +++ b/nym-wallet/src/theme/NymWalletTheme.tsx @@ -1,18 +1,39 @@ -import React from 'react'; +import React, { useMemo, useEffect } from 'react'; import { CssBaseline, PaletteMode } from '@mui/material'; import { createTheme, ThemeProvider } from '@mui/material/styles'; import { getDesignTokens } from './theme'; import '@assets/fonts/non-variable/fonts.css'; +// Component that applies the Inter font for better typography +const FontLoader = () => { + useEffect(() => { + // Add Inter font for modern typography + const interFontLink = document.createElement('link'); + interFontLink.rel = 'stylesheet'; + interFontLink.href = 'https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap'; + document.head.appendChild(interFontLink); + + return () => { + // Clean up + document.head.removeChild(interFontLink); + }; + }, []); + + return null; +}; + export const NymWalletThemeWithMode: FCWithChildren<{ mode: PaletteMode; children: React.ReactNode }> = ({ mode, children, }) => { - const theme = React.useMemo(() => createTheme(getDesignTokens(mode)), [mode]); + // Create theme with memoization for performance + const theme = useMemo(() => createTheme(getDesignTokens(mode)), [mode]); + return ( + {children} ); -}; +}; \ No newline at end of file diff --git a/nym-wallet/src/theme/index.tsx b/nym-wallet/src/theme/index.tsx index 007e1f4761..79c77757a7 100644 --- a/nym-wallet/src/theme/index.tsx +++ b/nym-wallet/src/theme/index.tsx @@ -8,13 +8,16 @@ import { NymWalletThemeWithMode } from './NymWalletTheme'; /** * Provides the theme for the Network Explorer by reacting to the light/dark mode choice stored in the app context. */ - export const NymWalletTheme: FCWithChildren = ({ children }) => { const { mode } = useContext(AppContext); return {children}; }; +/** + * Themed component specifically for authentication screens + */ export const AuthTheme: FCWithChildren = ({ children }) => { + // Uses dark mode by default for auth screens const theme = createTheme(getDesignTokens('dark')); return ( @@ -22,4 +25,4 @@ export const AuthTheme: FCWithChildren = ({ children }) => { {children} ); -}; +}; \ No newline at end of file diff --git a/nym-wallet/src/theme/mui-theme.d.ts b/nym-wallet/src/theme/mui-theme.d.ts index e1e98ca7c0..77e944a7bf 100644 --- a/nym-wallet/src/theme/mui-theme.d.ts +++ b/nym-wallet/src/theme/mui-theme.d.ts @@ -52,6 +52,8 @@ declare module '@mui/material/styles' { warn: string; grey: string; greyStroke: string; + elevated: string; // New property for more depth options + subtle: string; // New property for subtle backgrounds }; text: { main: string; @@ -60,6 +62,7 @@ declare module '@mui/material/styles' { contrast: string; grey: string; blue: string; + subdued: string; // New property for better text hierarchy }; topNav: { background: string; @@ -76,6 +79,16 @@ declare module '@mui/material/styles' { chart: { grey: string; }; + // New modern properties + gradients: { + primary: string; + subtle: string; + }; + shadows: { + light: string; + medium: string; + strong: string; + }; } /** @@ -106,4 +119,4 @@ declare module '@mui/material/styles' { interface ThemeOptions extends Partial {} interface Palette extends NymPaletteAndNymWalletPalette {} interface PaletteOptions extends NymPaletteAndNymWalletPaletteOptions {} -} +} \ No newline at end of file diff --git a/nym-wallet/src/theme/theme.tsx b/nym-wallet/src/theme/theme.tsx index f57b4dbd9e..4ba9f7793d 100644 --- a/nym-wallet/src/theme/theme.tsx +++ b/nym-wallet/src/theme/theme.tsx @@ -1,25 +1,18 @@ -import { PaletteMode } from '@mui/material'; +import { PaletteMode, alpha, Theme } from '@mui/material'; import { PaletteOptions, - NymPalette, - NymWalletPalette, ThemeOptions, createTheme, - NymPaletteVariant, + Components, } from '@mui/material/styles'; -//----------------------------------------------------------------------------------------------- -// Nym palette type definitions -// - /** * The Nym palette. - * + * * IMPORTANT: do not export this constant, always use the MUI `useTheme` hook to get the correct * colours for dark/light mode. */ - -const nymPalette: NymPalette = { +const nymPalette = { /** emphasises important elements */ highlight: 'rgb(20, 231, 111)', success: 'rgb(20, 231, 111)', @@ -42,14 +35,17 @@ const nymPalette: NymPalette = { }, }; -const darkMode: NymPaletteVariant = { - mode: 'dark', +const darkMode = { + mode: 'dark' as const, background: { main: '#242B2D', paper: '#32373D', warn: '#F97316', grey: '#3A373F', greyStroke: '#49454F', + // New additions for depth and layering + elevated: '#383E42', + subtle: '#2A3134', }, text: { main: '#FFFFFF', @@ -58,6 +54,7 @@ const darkMode: NymPaletteVariant = { contrast: '#242B2D', grey: '#79747E', blue: '#60D7EF', + subdued: '#B8B5BD', }, topNav: { background: '#1C1B1F', @@ -74,16 +71,28 @@ const darkMode: NymPaletteVariant = { chart: { grey: '#49454F', }, + // New additions for modern look + gradients: { + primary: 'linear-gradient(45deg, rgba(20, 231, 111, 0.9), rgba(20, 231, 111, 0.7))', + subtle: 'linear-gradient(180deg, rgba(50, 55, 61, 0.8), rgba(36, 43, 45, 0.8))', + }, + shadows: { + light: '0 2px 8px rgba(0, 0, 0, 0.15)', + medium: '0 4px 12px rgba(0, 0, 0, 0.2)', + strong: '0 8px 24px rgba(0, 0, 0, 0.3)', + }, }; -const lightMode: NymPaletteVariant = { - mode: 'light', +const lightMode = { + mode: 'light' as const, background: { main: '#FFFFFF', paper: '#F4F6F8', warn: '#F97316', grey: '#E2E8EC', greyStroke: '#8DA3B1', + elevated: '#FFFFFF', + subtle: '#F9FAFB', }, text: { main: '#1C1B1F', @@ -92,6 +101,7 @@ const lightMode: NymPaletteVariant = { contrast: '#FFFFFF', grey: '#696571', blue: '#60D7EF', + subdued: '#908E95', }, topNav: { background: '#FFFFFF', @@ -108,37 +118,65 @@ const lightMode: NymPaletteVariant = { chart: { grey: '#79747E', }, + // New modern additions + gradients: { + primary: 'linear-gradient(45deg, rgba(20, 231, 111, 0.9), rgba(20, 231, 111, 0.7))', + subtle: 'linear-gradient(180deg, rgba(255, 255, 255, 1), rgba(244, 246, 248, 0.8))', + }, + shadows: { + light: '0 2px 8px rgba(0, 0, 0, 0.06)', + medium: '0 4px 12px rgba(0, 0, 0, 0.08)', + strong: '0 8px 24px rgba(0, 0, 0, 0.12)', + }, }; -const nymWalletPalette = (variant: NymPaletteVariant): NymWalletPalette => ({ +// Type definition for variant to fix TS errors +type NymVariant = typeof darkMode | typeof lightMode; + +const nymWalletPalette = (variant: NymVariant) => ({ nymWallet: variant, }); -const variantToMUIPalette = (variant: NymPaletteVariant): PaletteOptions => ({ +const variantToMUIPalette = (variant: NymVariant): PaletteOptions => ({ text: { primary: variant.text.main, + secondary: variant.text.subdued, // Using the new subdued color disabled: variant.text.grey, }, primary: { main: nymPalette.highlight, + light: alpha(nymPalette.highlight, 0.8), + dark: alpha(nymPalette.highlight, 1.2), contrastText: variant.text.contrast, }, secondary: { - main: variant.text.main, + main: variant.text.blue, + contrastText: variant.text.contrast, }, success: { main: nymPalette.success, + light: alpha(nymPalette.success, 0.8), + dark: alpha(nymPalette.success, 1.2), }, info: { main: nymPalette.info, + light: alpha(nymPalette.info, 0.8), + dark: alpha(nymPalette.info, 1.2), }, error: { main: nymPalette.red, + light: alpha(nymPalette.red, 0.8), + dark: alpha(nymPalette.red, 1.2), + }, + warning: { + main: variant.background.warn, + contrastText: '#FFFFFF', }, background: { default: variant.background.main, paper: variant.background.paper, }, + divider: variant.background.greyStroke, }); const createLightModePalette = (): PaletteOptions => ({ @@ -156,6 +194,485 @@ const createDarkModePalette = (): PaletteOptions => ({ }, ...variantToMUIPalette(darkMode), }); + +// Define component overrides with proper types +const getComponentOverrides = (mode: PaletteMode): Components => { + return { + MuiCssBaseline: { + styleOverrides: { + body: { + scrollbarColor: `${mode === 'dark' ? darkMode.background.greyStroke : lightMode.background.greyStroke + } transparent`, + '&::-webkit-scrollbar, & *::-webkit-scrollbar': { + width: '8px', + height: '8px', + backgroundColor: 'transparent', + }, + '&::-webkit-scrollbar-thumb, & *::-webkit-scrollbar-thumb': { + borderRadius: 8, + backgroundColor: mode === 'dark' ? darkMode.background.greyStroke : lightMode.background.greyStroke, + minHeight: 24, + }, + '&::-webkit-scrollbar-corner, & *::-webkit-scrollbar-corner': { + backgroundColor: 'transparent', + }, + }, + }, + }, + MuiButton: { + styleOverrides: { + root: { + fontSize: 15, + padding: '8px 20px', + borderRadius: 10, + boxShadow: 'none', + fontWeight: 600, + transition: '0.2s all ease-in-out', + '&:hover': { + transform: 'translateY(-1px)', + boxShadow: mode === 'dark' ? darkMode.shadows.medium : lightMode.shadows.medium, + }, + }, + contained: { + '&:hover': { + boxShadow: mode === 'dark' ? darkMode.shadows.medium : lightMode.shadows.medium, + }, + }, + // Use different approach for overriding MUI styles that have type issues + containedPrimary: ({ theme }) => ({ + background: mode === 'dark' ? darkMode.gradients.primary : lightMode.gradients.primary, + '&:hover': { + background: mode === 'dark' ? darkMode.gradients.primary : lightMode.gradients.primary, + }, + }), + outlined: { + borderWidth: '2px', + '&:hover': { + borderWidth: '2px', + }, + }, + sizeLarge: { + height: 52, + fontSize: 16, + padding: '10px 24px', + }, + sizeSmall: { + height: 36, + fontSize: 14, + padding: '6px 16px', + }, + }, + }, + MuiCard: { + styleOverrides: { + // Use callback format for proper typing + root: ({ theme }) => ({ + borderRadius: 16, + boxShadow: mode === 'dark' ? darkMode.shadows.light : lightMode.shadows.light, + transition: 'transform 0.3s, box-shadow 0.3s', + '&:hover': { + transform: 'translateY(-4px)', + boxShadow: mode === 'dark' ? darkMode.shadows.medium : lightMode.shadows.medium, + }, + }), + }, + }, + MuiCardContent: { + styleOverrides: { + root: { + padding: '24px', + '&:last-child': { + paddingBottom: '24px', + }, + }, + }, + }, + MuiTextField: { + styleOverrides: { + root: { + '& .MuiOutlinedInput-root': { + borderRadius: 10, + '& fieldset': { + borderWidth: '2px', + }, + '&:hover fieldset': { + borderWidth: '2px', + }, + '&.Mui-focused fieldset': { + borderWidth: '2px', + }, + }, + }, + }, + }, + MuiOutlinedInput: { + styleOverrides: { + root: { + borderRadius: 10, + '& fieldset': { + borderWidth: '2px', + transition: 'border-color 0.2s ease-in-out', + }, + '&:hover .MuiOutlinedInput-notchedOutline': { + borderWidth: '2px', + }, + '&.Mui-focused .MuiOutlinedInput-notchedOutline': { + borderWidth: '2px', + }, + }, + input: { + padding: '14px 16px', + }, + }, + }, + MuiSwitch: { + styleOverrides: { + root: ({ theme }) => ({ + width: 62, + height: 34, + padding: 0, + margin: theme.spacing(1), + overflow: 'visible', + '& .MuiSwitch-switchBase': { + padding: 3, + border: '2px solid transparent', + borderRadius: '50%', + transition: theme.transitions.create(['transform', 'background-color'], { + duration: 500, + }), + '&.Mui-checked': { + transform: 'translateX(28px)', + '& + .MuiSwitch-track': { + backgroundColor: nymPalette.highlight, + opacity: 1, + border: 0, + }, + '& .MuiSwitch-thumb': { + backgroundColor: '#fff', + boxShadow: '0px 0px 8px rgba(0, 0, 0, 0.2)', + }, + '&.Mui-disabled + .MuiSwitch-track': { + opacity: 0.5, + }, + }, + '&.Mui-disabled': { + opacity: 0.5, + }, + '&.Mui-disabled .MuiSwitch-thumb': { + opacity: 0.8, + }, + }, + '& .MuiSwitch-thumb': { + boxSizing: 'border-box', + width: 22, + height: 22, + backgroundColor: mode === 'dark' ? '#fff' : '#383838', + borderRadius: '50%', + transition: theme.transitions.create(['width', 'transform', 'background-color'], { + duration: 500, + }), + boxShadow: '0px 2px 4px rgba(0, 0, 0, 0.2)', + }, + '& .MuiSwitch-track': { + borderRadius: 17, + backgroundColor: mode === 'dark' + ? 'rgba(255, 255, 255, 0.1)' + : 'rgba(0, 0, 0, 0.1)', + border: `1px solid ${mode === 'dark' ? 'rgba(255, 255, 255, 0.2)' : 'rgba(0, 0, 0, 0.1)'}`, + opacity: 1, + transition: theme.transitions.create(['background-color', 'border'], { + duration: 500, + }), + '&:before, &:after': { + content: '""', + position: 'absolute', + top: '50%', + transform: 'translateY(-50%)', + width: 16, + height: 16, + }, + '&:before': { + backgroundImage: `url('data:image/svg+xml;utf8,')`, + left: 8, + opacity: mode === 'dark' ? 0 : 0.7, + transition: 'opacity 0.3s', + }, + '&:after': { + backgroundImage: `url('data:image/svg+xml;utf8,')`, + right: 8, + opacity: mode === 'dark' ? 0.7 : 0, + transition: 'opacity 0.3s', + }, + }, + }), + }, + }, + MuiTable: { + styleOverrides: { + root: { + borderCollapse: 'separate', + borderSpacing: '0 8px', + }, + }, + }, + MuiTableRow: { + styleOverrides: { + root: { + borderRadius: 12, + '&.MuiTableRow-hover:hover': { + backgroundColor: mode === 'dark' + ? alpha(darkMode.hover.background, 0.5) + : alpha(lightMode.hover.background, 0.5), + }, + }, + }, + }, + MuiTableCell: { + styleOverrides: { + root: { + padding: '12px 16px', + lineHeight: 1.5, + borderBottom: 'none', + '&:first-of-type': { + borderTopLeftRadius: 12, + borderBottomLeftRadius: 12, + }, + '&:last-of-type': { + borderTopRightRadius: 12, + borderBottomRightRadius: 12, + }, + }, + // Using function format for styleOverrides to avoid type errors + head: ({ theme }) => ({ + fontWeight: 600, + color: mode === 'dark' ? darkMode.text.subdued : lightMode.text.subdued, + backgroundColor: 'transparent', + }), + body: ({ theme }) => ({ + backgroundColor: mode === 'dark' ? darkMode.background.paper : lightMode.background.paper, + }), + }, + }, + MuiToolbar: { + styleOverrides: { + root: { + minWidth: 0, + padding: '8px 16px', + '@media (min-width: 0px)': { + minHeight: 'fit-content', + }, + }, + }, + }, + MuiDialog: { + styleOverrides: { + // Using function format for styleOverrides to avoid type errors + paper: ({ theme }) => ({ + borderRadius: 16, + boxShadow: mode === 'dark' ? darkMode.shadows.strong : lightMode.shadows.strong, + backgroundImage: mode === 'dark' ? darkMode.gradients.subtle : lightMode.gradients.subtle, + backgroundSize: 'cover', + }), + }, + }, + MuiDialogTitle: { + styleOverrides: { + root: { + fontSize: '1.5rem', + fontWeight: 700, + padding: '24px 24px 16px', + }, + }, + }, + MuiDialogContent: { + styleOverrides: { + root: { + padding: '16px 24px 24px', + }, + }, + }, + MuiChip: { + styleOverrides: { + root: { + borderRadius: 8, + fontWeight: 500, + '&.MuiChip-colorPrimary': { + background: mode === 'dark' + ? alpha(nymPalette.highlight, 0.15) + : alpha(nymPalette.highlight, 0.12), + color: nymPalette.highlight, + }, + }, + label: { + padding: '0 12px', + }, + sizeMedium: { + height: 32, + }, + sizeSmall: { + height: 26, + }, + }, + }, + MuiLink: { + defaultProps: { + underline: 'none', + }, + styleOverrides: { + root: { + fontWeight: 500, + transition: 'color 0.2s', + '&:hover': { + color: nymPalette.linkHover, + }, + }, + }, + }, + MuiDivider: { + styleOverrides: { + root: { + opacity: 0.6, + }, + }, + }, + MuiTooltip: { + styleOverrides: { + // Using function format for styleOverrides to avoid type errors + tooltip: ({ theme }) => ({ + borderRadius: 8, + padding: '8px 16px', + fontSize: '0.75rem', + fontWeight: 500, + boxShadow: mode === 'dark' ? darkMode.shadows.medium : lightMode.shadows.medium, + }), + }, + }, + MuiLinearProgress: { + styleOverrides: { + // Using function format for styleOverrides to avoid type errors + root: ({ theme }) => ({ + borderRadius: 6, + height: 8, + backgroundColor: mode === 'dark' + ? alpha(darkMode.background.greyStroke, 0.5) + : alpha(lightMode.background.greyStroke, 0.5), + }), + bar: { + borderRadius: 6, + }, + colorPrimary: { + '& .MuiLinearProgress-bar': { + backgroundImage: mode === 'dark' + ? darkMode.gradients.primary + : lightMode.gradients.primary, + }, + }, + }, + }, + MuiStepIcon: { + styleOverrides: { + root: { + '&.Mui-completed': { + color: nymPalette.success, + }, + '&.Mui-active': { + color: nymPalette.highlight, + }, + }, + }, + }, + MuiSelect: { + defaultProps: { + MenuProps: { + PaperProps: { + sx: { + borderRadius: 2, + boxShadow: mode === 'dark' ? darkMode.shadows.medium : lightMode.shadows.medium, + mt: 1, + '&& .Mui-selected': { + color: nymPalette.highlight, + backgroundColor: mode === 'dark' + ? alpha(darkMode.background.main, 0.9) + : alpha(lightMode.background.main, 0.9), + }, + '&& .Mui-selected:hover': { + backgroundColor: mode === 'dark' + ? alpha(nymPalette.highlight, 0.08) + : alpha(nymPalette.highlight, 0.08), + }, + '& .MuiMenuItem-root': { + borderRadius: 1, + margin: '2px 6px', + padding: '8px 12px', + '&:hover': { + backgroundColor: mode === 'dark' + ? alpha(darkMode.hover.background, 0.7) + : alpha(lightMode.hover.background, 0.7), + }, + }, + }, + }, + }, + }, + }, + MuiMenu: { + styleOverrides: { + list: ({ theme }) => ({ + backgroundColor: mode === 'dark' ? darkMode.background.main : lightMode.background.main, + border: `1px solid ${mode === 'dark' + ? darkMode.background.greyStroke + : lightMode.background.greyStroke}`, + borderRadius: '12px', + padding: '8px 0', + }), + }, + }, + MuiMenuItem: { + styleOverrides: { + root: { + borderRadius: 8, + margin: '2px 8px', + padding: '8px 12px', + '&:hover': { + backgroundColor: mode === 'dark' + ? alpha(darkMode.hover.background, 0.7) + : alpha(lightMode.hover.background, 0.7), + }, + }, + }, + }, + MuiAppBar: { + styleOverrides: { + // Using function format for styleOverrides to avoid type errors + root: ({ theme }) => ({ + boxShadow: mode === 'dark' + ? '0 4px 12px rgba(0, 0, 0, 0.1)' + : '0 4px 12px rgba(0, 0, 0, 0.05)', + backgroundImage: 'none', + }), + }, + }, + MuiPaper: { + styleOverrides: { + root: { + backgroundImage: 'none', + }, + // Using function format for styleOverrides to avoid type errors + elevation1: ({ theme }) => ({ + boxShadow: mode === 'dark' ? darkMode.shadows.light : lightMode.shadows.light, + }), + elevation4: ({ theme }) => ({ + boxShadow: mode === 'dark' ? darkMode.shadows.medium : lightMode.shadows.medium, + }), + }, + }, + }; +}; + export const getDesignTokens = (mode: PaletteMode): ThemeOptions => { const { palette } = createTheme({ palette: { @@ -166,122 +683,107 @@ export const getDesignTokens = (mode: PaletteMode): ThemeOptions => { return { typography: { - fontFamily: ['Lato', 'sans-serif', 'BlinkMacSystemFont', 'Roboto', 'Oxygen', 'Ubuntu', 'Helvetica Neue'].join( + fontFamily: ['Inter', 'Lato', 'sans-serif', 'BlinkMacSystemFont', 'Roboto', 'Oxygen', 'Ubuntu', 'Helvetica Neue'].join( ',', ), fontSize: 14, + fontWeightLight: 300, fontWeightRegular: 400, + fontWeightMedium: 500, + fontWeightBold: 700, + h1: { + fontSize: '2.5rem', + fontWeight: 700, + lineHeight: 1.2, + letterSpacing: '-0.01em', + }, + h2: { + fontSize: '2rem', + fontWeight: 700, + lineHeight: 1.3, + letterSpacing: '-0.005em', + }, + h3: { + fontSize: '1.75rem', + fontWeight: 600, + lineHeight: 1.3, + }, + h4: { + fontSize: '1.5rem', + fontWeight: 600, + lineHeight: 1.4, + }, + h5: { + fontSize: '1.25rem', + fontWeight: 600, + lineHeight: 1.4, + }, + h6: { + fontSize: '1rem', + fontWeight: 600, + lineHeight: 1.5, + }, + subtitle1: { + fontSize: '1rem', + fontWeight: 500, + lineHeight: 1.5, + letterSpacing: '0.005em', + }, + subtitle2: { + fontSize: '0.875rem', + fontWeight: 500, + lineHeight: 1.5, + letterSpacing: '0.005em', + }, + body1: { + fontSize: '1rem', + lineHeight: 1.5, + letterSpacing: '0.001em', + }, + body2: { + fontSize: '0.875rem', + lineHeight: 1.6, + letterSpacing: '0.001em', + }, button: { textTransform: 'none', - fontWeight: '600', + fontWeight: 600, + letterSpacing: '0.01em', + }, + caption: { + fontSize: '0.75rem', + lineHeight: 1.5, + letterSpacing: '0.02em', + }, + overline: { + fontSize: '0.75rem', + fontWeight: 600, + lineHeight: 1.5, + letterSpacing: '0.05em', + textTransform: 'uppercase', }, }, shape: { - borderRadius: 8, + borderRadius: 12, }, transitions: { duration: { - shortest: 150, - shorter: 200, - short: 250, - standard: 300, - complex: 375, + shortest: 120, + shorter: 180, + short: 220, + standard: 280, + complex: 350, enteringScreen: 225, leavingScreen: 195, }, easing: { + easeInOut: 'cubic-bezier(0.4, 0, 0.2, 1)', + easeOut: 'cubic-bezier(0.0, 0, 0.2, 1)', easeIn: 'cubic-bezier(0.4, 0, 1, 1)', + sharp: 'cubic-bezier(0.4, 0, 0.6, 1)', }, }, - components: { - MuiButton: { - styleOverrides: { - root: { - fontSize: 16, - }, - sizeLarge: { - height: 55, - }, - outlined: { - borderWidth: '2px', - }, - }, - }, - MuiStepIcon: { - styleOverrides: { - root: { - '&.Mui-completed': { - color: nymPalette.success, - }, - '&.Mui-active': { - color: nymPalette.background.dark, - }, - }, - }, - }, - MuiTableCell: { - styleOverrides: { - root: { - padding: 0, - paddingTop: '16px', - paddingBottom: '16px', - }, - head: { - fontWeight: '400', - color: nymPalette.text.muted, - }, - }, - }, - MuiLink: { - defaultProps: { - underline: 'none', - }, - }, - MuiDialogTitle: { - styleOverrides: { - root: { - fontWeight: 600, - }, - }, - }, - MuiToolbar: { - styleOverrides: { - root: { - minWidth: 0, - '@media (min-width: 0px)': { - minHeight: 'fit-content', - }, - }, - }, - }, - MuiSelect: { - defaultProps: { - MenuProps: { - PaperProps: { - sx: { - '&& .Mui-selected': { - color: nymPalette.highlight, - backgroundColor: (t) => - t.palette.mode === 'dark' ? `${t.palette.background.default} !important` : '#FFFFFF !important', - }, - '&& .Mui-selected:hover': { - backgroundColor: 'rgba(112, 117, 255, 0.08) !important', - }, - }, - }, - }, - }, - }, - MuiMenu: { - styleOverrides: { - list: ({ _, theme }) => ({ - backgroundColor: theme.palette.mode === 'dark' ? darkMode.background.main : undefined, - border: `1px solid ${theme.palette.nym.border.menu}`, - borderRadius: '8px', - }), - }, - }, - }, + components: getComponentOverrides(mode), palette, }; -}; +}; \ No newline at end of file From 415ef1bf13896544dd50e8b6922726af4a791a37 Mon Sep 17 00:00:00 2001 From: Tommy Verrall Date: Tue, 8 Apr 2025 09:53:35 +0200 Subject: [PATCH 19/48] attempt to push to ci --- .../workflows/publish-nym-wallet-macos.yml | 50 +++++++++++++++---- nym-wallet/Cargo.lock | 2 +- nym-wallet/package.json | 2 + nym-wallet/src-tauri/Cargo.toml | 2 +- nym-wallet/src-tauri/tauri.conf.json | 10 ++-- 5 files changed, 50 insertions(+), 16 deletions(-) diff --git a/.github/workflows/publish-nym-wallet-macos.yml b/.github/workflows/publish-nym-wallet-macos.yml index 40077d2069..0e94b3f9c1 100644 --- a/.github/workflows/publish-nym-wallet-macos.yml +++ b/.github/workflows/publish-nym-wallet-macos.yml @@ -31,10 +31,18 @@ jobs: uses: actions/setup-node@v4 with: node-version: 21 + - name: Install Rust stable - uses: actions-rs/toolchain@v1 + uses: dtolnay/rust-toolchain@stable with: toolchain: stable + + - name: Add Rust target for x86_64-apple-darwin + run: rustup target add x86_64-apple-darwin + + - name: Set Cargo build target to x86_64 + run: echo "CARGO_BUILD_TARGET=x86_64-apple-darwin" >> $GITHUB_ENV + - name: Install the Apple developer certificate for code signing env: APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }} @@ -64,11 +72,19 @@ jobs: fileName: '.env' encodedString: ${{ secrets.WALLET_ADMIN_ADDRESS }} + - name: Yarn cache clean + shell: bash + run: cd .. && yarn cache clean + - name: Install project dependencies shell: bash run: cd .. && yarn --network-timeout 100000 - - name: Install app dependencies and build it + - name: Yarn build + shell: bash + run: cd .. && yarn build + + - name: Install dependencies and build it env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} ENABLE_CODE_SIGNING: ${{ secrets.APPLE_CERTIFICATE }} @@ -78,29 +94,43 @@ jobs: APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_IDENTITY_ID }} APPLE_ID: ${{ secrets.APPLE_ID }} APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }} - TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }} - TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }} - run: yarn && yarn build + # Tauri v2 specific environment variables + TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }} + TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }} + TAURI_NOTARIZATION_USERNAME: ${{ secrets.APPLE_ID }} + TAURI_NOTARIZATION_PASSWORD: ${{ secrets.APPLE_PASSWORD }} + TAURI_NOTARIZATION_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} + run: | + yarn build-macx86 + + - name: Create app tarball + run: | + # Navigate to where the app bundle is and create the tarball + cd target/x86_64-apple-darwin/release/bundle/macos + echo "Creating tarball from app bundle" + tar -czf nym-wallet.app.tar.gz NymWallet.app + cd - - name: Upload Artifact uses: actions/upload-artifact@v4 with: name: nym-wallet.app.tar.gz - path: nym-wallet/target/release/bundle/macos/nym-wallet.app.tar.gz + path: nym-wallet/target/x86_64-apple-darwin/release/bundle/macos/nym-wallet.app.tar.gz retention-days: 5 - name: Clean up keychain if: ${{ always() }} run: | security delete-keychain $RUNNER_TEMP/app-signing.keychain-db + - id: create-release name: Upload to release based on tag name uses: softprops/action-gh-release@v2 if: github.event_name == 'release' with: files: | - nym-wallet/target/release/bundle/dmg/*.dmg - nym-wallet/target/release/bundle/macos/*.app.tar.gz* + nym-wallet/target/x86_64-apple-darwin/release/bundle/dmg/*.dmg + nym-wallet/target/x86_64-apple-darwin/release/bundle/macos/*.app.tar.gz* - name: Deploy artifacts to CI www continue-on-error: true @@ -108,7 +138,7 @@ jobs: env: SSH_PRIVATE_KEY: ${{ secrets.CI_WWW_SSH_PRIVATE_KEY }} ARGS: "-avzr" - SOURCE: "nym-wallet/target/release/bundle/macos/nym-wallet.app.tar.gz" + SOURCE: "nym-wallet/target/x86_64-apple-darwin/release/bundle/macos/nym-wallet.app.tar.gz" REMOTE_HOST: ${{ secrets.CI_WWW_REMOTE_HOST }} REMOTE_USER: ${{ secrets.CI_WWW_REMOTE_USER }} TARGET: ${{ secrets.CI_WWW_REMOTE_TARGET }}/builds/${{ github.ref_name }}/nym-wallet @@ -120,4 +150,4 @@ jobs: needs: publish-tauri with: release_tag: ${{ github.ref_name }} - secrets: inherit + secrets: inherit \ No newline at end of file diff --git a/nym-wallet/Cargo.lock b/nym-wallet/Cargo.lock index dfa453bb50..91bab4cf2c 100644 --- a/nym-wallet/Cargo.lock +++ b/nym-wallet/Cargo.lock @@ -4,7 +4,7 @@ version = 3 [[package]] name = "NymWallet" -version = "1.2.16" +version = "1.2.18" dependencies = [ "async-trait", "base64 0.13.1", diff --git a/nym-wallet/package.json b/nym-wallet/package.json index 48432944c1..77271c8346 100644 --- a/nym-wallet/package.json +++ b/nym-wallet/package.json @@ -5,6 +5,7 @@ "main": "index.js", "scripts": { "build": "run-s webpack:prod tauri:build", + "build-macx86": "run-s webpack:prod tauri:buildx86", "dev": "run-p tauri:dev webpack:dev", "lint": "eslint src", "lint:fix": "eslint src --fix", @@ -15,6 +16,7 @@ "storybook:build": "build-storybook", "tauri:build": "yarn tauri build", "tauri:dev": "yarn tauri dev", + "tauri:buildx86": "yarn tauri build --target x86_64-apple-darwin", "tsc": "tsc --noEmit true", "tsc:watch": "tsc --noEmit true --watch", "webpack:dev": "yarn webpack serve --config webpack.dev.js", diff --git a/nym-wallet/src-tauri/Cargo.toml b/nym-wallet/src-tauri/Cargo.toml index 0f2f5ba6a1..ec1e150804 100644 --- a/nym-wallet/src-tauri/Cargo.toml +++ b/nym-wallet/src-tauri/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "NymWallet" -version = "1.2.16" +version = "1.2.18" description = "Nym Native Wallet" authors = ["Nym Technologies SA"] license = "" diff --git a/nym-wallet/src-tauri/tauri.conf.json b/nym-wallet/src-tauri/tauri.conf.json index 228864a949..0ec9d94f89 100644 --- a/nym-wallet/src-tauri/tauri.conf.json +++ b/nym-wallet/src-tauri/tauri.conf.json @@ -1,7 +1,6 @@ { "bundle": { "active": true, - "targets": "all", "windows": { "certificateThumbprint": "6DB77B1F529A0804FE0E6843A3EB8A8CECFFD408", "digestAlgorithm": "sha256", @@ -22,7 +21,7 @@ "longDescription": "", "macOS": { "frameworks": [], - "minimumSystemVersion": "", + "minimumSystemVersion": "10.13", "exceptionDomain": "", "signingIdentity": "Developer ID Application: Nym Technologies SA (VW5DZLFHM5)", "entitlements": null @@ -42,7 +41,7 @@ }, "productName": "NymWallet", "mainBinaryName": "NymWallet", - "version": "1.2.17", + "version": "1.2.18", "identifier": "net.nymtech.wallet", "plugins": { "updater": { @@ -54,7 +53,10 @@ }, "app": { "security": { - "capabilities": ["main-capability", "shell-capability"], + "capabilities": [ + "main-capability", + "shell-capability" + ], "csp": "default-src blob: data: filesystem: ws: wss: http: https: tauri: 'unsafe-eval' 'unsafe-inline' 'self' img-src: 'self'; connect-src ipc: http://ipc.localhost" }, "windows": [ From 53792cc839f1b639445fd63618d051b2567af0cb Mon Sep 17 00:00:00 2001 From: Tommy Verrall Date: Tue, 8 Apr 2025 10:00:22 +0200 Subject: [PATCH 20/48] Update runner for linux --- .../workflows/publish-nym-wallet-ubuntu.yml | 102 +++++++++++++----- 1 file changed, 78 insertions(+), 24 deletions(-) diff --git a/.github/workflows/publish-nym-wallet-ubuntu.yml b/.github/workflows/publish-nym-wallet-ubuntu.yml index 5f3ecba624..9b2c312834 100644 --- a/.github/workflows/publish-nym-wallet-ubuntu.yml +++ b/.github/workflows/publish-nym-wallet-ubuntu.yml @@ -3,71 +3,111 @@ on: workflow_dispatch: release: types: [created] - defaults: run: working-directory: nym-wallet - jobs: publish-tauri: if: ${{ (startsWith(github.ref, 'refs/tags/nym-wallet-') && github.event_name == 'release') || github.event_name == 'workflow_dispatch' }} strategy: fail-fast: false matrix: - platform: [custom-ubuntu-20.04] + platform: [custom-ubuntu-22.04] runs-on: ${{ matrix.platform }} - outputs: release_id: ${{ steps.create-release.outputs.id }} release_date: ${{ fromJSON(steps.create-release.outputs.assets)[0].created_at }} version: ${{ steps.release-info.outputs.version }} filename: ${{ steps.release-info.outputs.filename }} file_hash: ${{ steps.release-info.outputs.file_hash }} - steps: - uses: actions/checkout@v4 - - - name: Tauri dependencies + + - name: Tauri v2 dependencies run: > sudo apt-get update && - sudo apt-get install -y webkit2gtk-4.0 - continue-on-error: true - + sudo apt-get install -y libwebkit2gtk-4.0-dev libgtk-3-dev \ + libayatana-appindicator3-dev librsvg2-dev patchelf libxdo-dev \ + webkit2gtk-driver xvfb + - name: Node uses: actions/setup-node@v4 with: node-version: 21 - + cache: 'yarn' + - name: Install Rust stable - uses: actions-rs/toolchain@v1 + uses: dtolnay/rust-toolchain@stable with: toolchain: stable - + - name: Install project dependencies shell: bash run: cd .. && yarn --network-timeout 100000 - + - name: Install app dependencies run: yarn + - name: Create env file uses: timheuer/base64-to-file@v1.2 with: fileName: '.env' encodedString: ${{ secrets.WALLET_ADMIN_ADDRESS }} - + - name: Build app run: yarn build env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }} TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }} - + TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }} + TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }} + + - name: Check bundle directory + run: | + echo "Checking bundle directory structure" + ls -la target/release/bundle || echo "Bundle directory not found" + if [ -d "target/release/bundle/appimage" ]; then + echo "AppImage bundle directory exists, checking contents:" + ls -la target/release/bundle/appimage + else + echo "AppImage bundle directory not found, checking alternatives:" + find target/release/bundle -type d -name "*appimage*" -o -name "*AppImage*" || echo "No AppImage directories found" + find target/release/bundle -name "*.AppImage" -o -name "*.appimage" || echo "No AppImage files found" + fi + + - name: Create AppImage tarball if needed + run: | + # Find the AppImage file + APPIMAGE_FILE=$(find target/release/bundle -name "*.AppImage" | head -n 1) + if [ -n "$APPIMAGE_FILE" ]; then + echo "Found AppImage file: $APPIMAGE_FILE" + APPIMAGE_DIR=$(dirname "$APPIMAGE_FILE") + APPIMAGE_NAME=$(basename "$APPIMAGE_FILE") + + # Create tarball if it doesn't exist + if [ ! -f "${APPIMAGE_FILE}.tar.gz" ]; then + echo "Creating tarball for $APPIMAGE_NAME" + cd "$APPIMAGE_DIR" + tar -czf "${APPIMAGE_NAME}.tar.gz" "$APPIMAGE_NAME" + cd - + echo "Created tarball: ${APPIMAGE_FILE}.tar.gz" + else + echo "Tarball already exists: ${APPIMAGE_FILE}.tar.gz" + fi + else + echo "WARNING: No AppImage file found!" + fi + - name: Upload Artifact uses: actions/upload-artifact@v4 with: - name: nym-wallet_1.0.0_amd64.AppImage.tar.gz - path: nym-wallet/target/release/bundle/appimage/nym-wallet*.AppImage.tar.gz + name: nym-wallet-appimage.tar.gz + path: | + nym-wallet/target/release/bundle/appimage/*.AppImage.tar.gz + nym-wallet/target/release/bundle/*/nym-wallet*.AppImage.tar.gz retention-days: 30 - + - id: create-release name: Upload to release based on tag name uses: softprops/action-gh-release@v2 @@ -75,24 +115,38 @@ jobs: with: files: | nym-wallet/target/release/bundle/appimage/*.AppImage - nym-wallet/target/release/bundle/appimage/*.AppImage.tar.gz* - + nym-wallet/target/release/bundle/appimage/*.AppImage.tar.gz + nym-wallet/target/release/bundle/*/nym-wallet*.AppImage + nym-wallet/target/release/bundle/*/nym-wallet*.AppImage.tar.gz + + - name: Find AppImage tarball path for deployment + id: find-appimage + run: | + APPIMAGE_TARBALL=$(find target/release/bundle -name "*.AppImage.tar.gz" | head -n 1) + if [ -n "$APPIMAGE_TARBALL" ]; then + echo "Found AppImage tarball: $APPIMAGE_TARBALL" + echo "appimage_path=$APPIMAGE_TARBALL" >> $GITHUB_OUTPUT + else + echo "WARNING: No AppImage tarball found for deployment!" + echo "appimage_path=target/release/bundle/appimage/nym-wallet*.AppImage.tar.gz" >> $GITHUB_OUTPUT + fi + - name: Deploy artifacts to CI www continue-on-error: true uses: easingthemes/ssh-deploy@main env: SSH_PRIVATE_KEY: ${{ secrets.CI_WWW_SSH_PRIVATE_KEY }} ARGS: "-avzr" - SOURCE: "nym-wallet/target/release/bundle/appimage/nym-wallet*.AppImage.tar.gz" + SOURCE: ${{ steps.find-appimage.outputs.appimage_path }} REMOTE_HOST: ${{ secrets.CI_WWW_REMOTE_HOST }} REMOTE_USER: ${{ secrets.CI_WWW_REMOTE_USER }} TARGET: ${{ secrets.CI_WWW_REMOTE_TARGET }}/builds/${{ github.ref_name }}/nym-wallet EXCLUDE: "/dist/, /node_modules/" - + push-release-data: if: ${{ (startsWith(github.ref, 'refs/tags/nym-wallet-') && github.event_name == 'release') || github.event_name == 'workflow_dispatch' }} uses: ./.github/workflows/release-calculate-hash.yml needs: publish-tauri with: release_tag: ${{ github.ref_name }} - secrets: inherit + secrets: inherit \ No newline at end of file From 873d15a5e10eb8910ef7c240f3b1cc077a47a3e3 Mon Sep 17 00:00:00 2001 From: Tommy Verrall Date: Tue, 8 Apr 2025 10:13:30 +0200 Subject: [PATCH 21/48] update runner platform --- .github/workflows/publish-nym-wallet-ubuntu.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/publish-nym-wallet-ubuntu.yml b/.github/workflows/publish-nym-wallet-ubuntu.yml index 9b2c312834..8d79b65e41 100644 --- a/.github/workflows/publish-nym-wallet-ubuntu.yml +++ b/.github/workflows/publish-nym-wallet-ubuntu.yml @@ -12,7 +12,7 @@ jobs: strategy: fail-fast: false matrix: - platform: [custom-ubuntu-22.04] + platform: [arc-ubuntu-22.04] runs-on: ${{ matrix.platform }} outputs: release_id: ${{ steps.create-release.outputs.id }} From d2ff3cb88d3e95d714d4748fd3b8047497cb900c Mon Sep 17 00:00:00 2001 From: Tommy Verrall Date: Tue, 8 Apr 2025 10:15:27 +0200 Subject: [PATCH 22/48] fix app deps --- .github/workflows/publish-nym-wallet-ubuntu.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/publish-nym-wallet-ubuntu.yml b/.github/workflows/publish-nym-wallet-ubuntu.yml index 8d79b65e41..c69ade8ab3 100644 --- a/.github/workflows/publish-nym-wallet-ubuntu.yml +++ b/.github/workflows/publish-nym-wallet-ubuntu.yml @@ -23,12 +23,12 @@ jobs: steps: - uses: actions/checkout@v4 - - name: Tauri v2 dependencies - run: > - sudo apt-get update && - sudo apt-get install -y libwebkit2gtk-4.0-dev libgtk-3-dev \ - libayatana-appindicator3-dev librsvg2-dev patchelf libxdo-dev \ - webkit2gtk-driver xvfb + - name: Install system dependencies + run: | + sudo apt-get update && sudo apt-get install -y libdbus-1-dev libmnl-dev libnftnl-dev \ + libwebkit2gtk-4.1-dev build-essential curl wget libssl-dev jq \ + libgtk-3-dev squashfs-tools libayatana-appindicator3-dev make libfuse2 unzip librsvg2-dev file \ + libsoup-3.0-dev libjavascriptcoregtk-4.1-dev - name: Node uses: actions/setup-node@v4 From f0ee49788c8110c3a5c4c13032637dc19f844f9c Mon Sep 17 00:00:00 2001 From: Tommy Verrall Date: Tue, 8 Apr 2025 10:18:32 +0200 Subject: [PATCH 23/48] test old runner first --- .github/workflows/publish-nym-wallet-ubuntu.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/publish-nym-wallet-ubuntu.yml b/.github/workflows/publish-nym-wallet-ubuntu.yml index c69ade8ab3..a0f1d24697 100644 --- a/.github/workflows/publish-nym-wallet-ubuntu.yml +++ b/.github/workflows/publish-nym-wallet-ubuntu.yml @@ -12,7 +12,7 @@ jobs: strategy: fail-fast: false matrix: - platform: [arc-ubuntu-22.04] + platform: [custom-ubuntu-20.04] runs-on: ${{ matrix.platform }} outputs: release_id: ${{ steps.create-release.outputs.id }} From ecbe192a8818d64feacff8448837b57c814f96f0 Mon Sep 17 00:00:00 2001 From: Tommy Verrall Date: Tue, 8 Apr 2025 10:20:50 +0200 Subject: [PATCH 24/48] try 22.04 --- .github/workflows/publish-nym-wallet-ubuntu.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/publish-nym-wallet-ubuntu.yml b/.github/workflows/publish-nym-wallet-ubuntu.yml index a0f1d24697..55a5d12c94 100644 --- a/.github/workflows/publish-nym-wallet-ubuntu.yml +++ b/.github/workflows/publish-nym-wallet-ubuntu.yml @@ -12,7 +12,7 @@ jobs: strategy: fail-fast: false matrix: - platform: [custom-ubuntu-20.04] + platform: [ubuntu-22.04] runs-on: ${{ matrix.platform }} outputs: release_id: ${{ steps.create-release.outputs.id }} From 091e98aa740de56e6cf5478a0dc49c043eeda302 Mon Sep 17 00:00:00 2001 From: Tommy Verrall Date: Tue, 8 Apr 2025 11:14:19 +0200 Subject: [PATCH 25/48] attempt windows build --- .../workflows/publish-nym-wallet-win11.yml | 135 ++++++++++++++---- 1 file changed, 110 insertions(+), 25 deletions(-) diff --git a/.github/workflows/publish-nym-wallet-win11.yml b/.github/workflows/publish-nym-wallet-win11.yml index 8f8a631a9a..dd2da982ab 100644 --- a/.github/workflows/publish-nym-wallet-win11.yml +++ b/.github/workflows/publish-nym-wallet-win11.yml @@ -1,6 +1,12 @@ name: publish-nym-wallet-win11 on: workflow_dispatch: + inputs: + sign: + description: "Sign this build using SSL.com. Signing is billed per signature so be careful" + required: false + type: boolean + default: true release: types: [created] @@ -35,35 +41,80 @@ jobs: - uses: actions/checkout@v4 - - name: Import signing certificate - env: - WINDOWS_CERTIFICATE: ${{ secrets.WINDOWS_CERTIFICATE }} - WINDOWS_CERTIFICATE_PASSWORD: ${{ secrets.WINDOWS_CERTIFICATE_PASSWORD }} + - name: Install Rust stable + uses: dtolnay/rust-toolchain@stable + with: + toolchain: stable + + - name: Setup MSBuild.exe + uses: microsoft/setup-msbuild@v2 + + - name: Install yq run: | - New-Item -ItemType directory -Path certificate - Set-Content -Path certificate/tempCert.txt -Value $env:WINDOWS_CERTIFICATE - certutil -decode certificate/tempCert.txt certificate/certificate.pfx - Remove-Item -path certificate -include tempCert.txt - Import-PfxCertificate -FilePath certificate/certificate.pfx -CertStoreLocation Cert:\CurrentUser\My -Password (ConvertTo-SecureString -String $env:WINDOWS_CERTIFICATE_PASSWORD -Force -AsPlainText) + winget list MikeFarah.yq || winget install --disable-interactivity --id MikeFarah.yq - name: Node uses: actions/setup-node@v4 with: node-version: 21 - - name: Install Rust stable - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - - name: Create env file uses: timheuer/base64-to-file@v1.2 with: fileName: '.env' encodedString: ${{ secrets.WALLET_ADMIN_ADDRESS }} - - name: Install Yarn - run: npm install -g yarn + # # Standard certificate import (used when not using SSL.com) + # - name: Import signing certificate + # if: ${{ !inputs.sign }} + # env: + # WINDOWS_CERTIFICATE: ${{ secrets.WINDOWS_CERTIFICATE }} + # WINDOWS_CERTIFICATE_PASSWORD: ${{ secrets.WINDOWS_CERTIFICATE_PASSWORD }} + # run: | + # New-Item -ItemType directory -Path certificate + # Set-Content -Path certificate/tempCert.txt -Value $env:WINDOWS_CERTIFICATE + # certutil -decode certificate/tempCert.txt certificate/certificate.pfx + # Remove-Item -path certificate -include tempCert.txt + # Import-PfxCertificate -FilePath certificate/certificate.pfx -CertStoreLocation Cert:\CurrentUser\My -Password (ConvertTo-SecureString -String $env:WINDOWS_CERTIFICATE_PASSWORD -Force -AsPlainText) + + # SSL.com certificate handling (from VPN workflow) + - name: Download EV CodeSignTool from ssl.com + if: ${{ inputs.sign }} + shell: bash + run: | + curl -L0 https://www.ssl.com/download/codesigntool-for-linux-and-macos/ -o codesigntool.zip + unzip codesigntool.zip + + - name: Get EV certificate credential id + if: ${{ inputs.sign }} + id: get_credential_ids + shell: bash + run: | + echo "SSL_COM_CREDENTIAL_ID=$(./CodeSignTool.sh get_credential_ids -username=${{ secrets.SSL_COM_USERNAME }} -password=${{ secrets.SSL_COM_PASSWORD }} | sed -n '1!p' | sed 's/- //')" >> "$GITHUB_OUTPUT" + + # Add custom sign command to tauri.conf.json for SSL.com signing + - name: Add custom sign command to tauri.conf.json + if: ${{ inputs.sign }} + shell: bash + run: | + yq eval --inplace '.bundle.windows += + { + "signCommand": { + "cmd": "C:\Program Files\Git\bin\bash.EXE", + "args": [ + "/c/actions-runner/_work/nym/nym/nym-wallet/CodeSignTool.sh", + "sign", + "-username ${{ secrets.SSL_COM_USERNAME }}", + "-password ${{ secrets.SSL_COM_PASSWORD }}", + "-credential_id ${{ steps.get_credential_ids.outputs.SSL_COM_CREDENTIAL_ID }}", + "-totp_secret ${{ secrets.SSL_COM_TOTP_SECRET }}", + "-program_name NymWallet", + "-input_file_path", + "%1", + "-override" + ] + } + }' tauri.conf.json - name: Install project dependencies shell: bash @@ -77,18 +128,37 @@ jobs: shell: bash env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - ENABLE_CODE_SIGNING: ${{ secrets.WINDOWS_CERTIFICATE }} - WINDOWS_CERTIFICATE: ${{ secrets.WINDOWS_CERTIFICATE }} - WINDOWS_CERTIFICATE_PASSWORD: ${{ secrets.WINDOWS_CERTIFICATE_PASSWORD }} - TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }} - TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }} + # WINDOWS_CERTIFICATE: ${{ !inputs.sign && secrets.WINDOWS_CERTIFICATE }} + # WINDOWS_CERTIFICATE_PASSWORD: ${{ !inputs.sign && secrets.WINDOWS_CERTIFICATE_PASSWORD }} + # Tauri v2 signing variables + TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }} + TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }} + SSL_COM_USERNAME: ${{ inputs.sign && secrets.SSL_COM_USERNAME }} + SSL_COM_PASSWORD: ${{ inputs.sign && secrets.SSL_COM_PASSWORD }} + SSL_COM_CREDENTIAL_ID: ${{ inputs.sign && steps.get_credential_ids.outputs.SSL_COM_CREDENTIAL_ID }} + SSL_COM_TOTP_SECRET: ${{ inputs.sign && secrets.SSL_COM_TOTP_SECRET }} run: yarn build + - name: Check bundle directory + run: | + echo "Checking bundle directory structure" + ls -la target/release/bundle || echo "Bundle directory not found" + if [ -d "target/release/bundle/msi" ]; then + echo "MSI bundle directory exists, checking contents:" + ls -la target/release/bundle/msi + else + echo "MSI bundle directory not found, checking alternatives:" + find target/release/bundle -type d -name "*msi*" -o -name "*MSI*" || echo "No MSI directories found" + find target/release/bundle -name "*.msi" || echo "No MSI files found" + fi + - name: Upload Artifact uses: actions/upload-artifact@v4 with: - name: nym-wallet_1.0.0_x64_en-US.msi - path: nym-wallet/target/release/bundle/msi/nym-wallet_1.*.msi + name: nym-wallet.msi + path: | + nym-wallet/target/release/bundle/msi/*.msi + nym-wallet/target/release/bundle/*/nym-wallet*.msi retention-days: 30 - id: create-release @@ -99,6 +169,21 @@ jobs: files: | nym-wallet/target/release/bundle/msi/*.msi nym-wallet/target/release/bundle/msi/*.msi.zip* + nym-wallet/target/release/bundle/*/nym-wallet*.msi + nym-wallet/target/release/bundle/*/nym-wallet*.msi.zip* + + - name: Find MSI path for deployment + id: find-msi + shell: bash + run: | + MSI_FILE=$(find target/release/bundle -name "*.msi" | head -n 1) + if [ -n "$MSI_FILE" ]; then + echo "Found MSI file: $MSI_FILE" + echo "msi_path=$MSI_FILE" >> $GITHUB_OUTPUT + else + echo "WARNING: No MSI file found for deployment!" + echo "msi_path=target/release/bundle/msi/nym-wallet_1.*.msi" >> $GITHUB_OUTPUT + fi - name: Deploy artifacts to CI www continue-on-error: true @@ -106,7 +191,7 @@ jobs: env: SSH_PRIVATE_KEY: ${{ secrets.CI_WWW_SSH_PRIVATE_KEY }} ARGS: "-avzr" - SOURCE: "nym-wallet/target/release/bundle/msi/nym-wallet_1.*.msi" + SOURCE: ${{ steps.find-msi.outputs.msi_path }} REMOTE_HOST: ${{ secrets.CI_WWW_REMOTE_HOST }} REMOTE_USER: ${{ secrets.CI_WWW_REMOTE_USER }} TARGET: ${{ secrets.CI_WWW_REMOTE_TARGET }}/builds/${{ github.ref_name }}/nym-wallet @@ -118,4 +203,4 @@ jobs: needs: publish-tauri with: release_tag: ${{ github.ref_name }} - secrets: inherit + secrets: inherit \ No newline at end of file From 1f5ed41bb32a876301a580e3b10a7c1586fb41e1 Mon Sep 17 00:00:00 2001 From: Tommy Verrall Date: Tue, 8 Apr 2025 11:21:53 +0200 Subject: [PATCH 26/48] correct tauri path --- .../workflows/publish-nym-wallet-win11.yml | 143 ++++++++++++------ 1 file changed, 95 insertions(+), 48 deletions(-) diff --git a/.github/workflows/publish-nym-wallet-win11.yml b/.github/workflows/publish-nym-wallet-win11.yml index dd2da982ab..ffc468bdc2 100644 --- a/.github/workflows/publish-nym-wallet-win11.yml +++ b/.github/workflows/publish-nym-wallet-win11.yml @@ -64,57 +64,92 @@ jobs: fileName: '.env' encodedString: ${{ secrets.WALLET_ADMIN_ADDRESS }} - # # Standard certificate import (used when not using SSL.com) - # - name: Import signing certificate - # if: ${{ !inputs.sign }} - # env: - # WINDOWS_CERTIFICATE: ${{ secrets.WINDOWS_CERTIFICATE }} - # WINDOWS_CERTIFICATE_PASSWORD: ${{ secrets.WINDOWS_CERTIFICATE_PASSWORD }} - # run: | - # New-Item -ItemType directory -Path certificate - # Set-Content -Path certificate/tempCert.txt -Value $env:WINDOWS_CERTIFICATE - # certutil -decode certificate/tempCert.txt certificate/certificate.pfx - # Remove-Item -path certificate -include tempCert.txt - # Import-PfxCertificate -FilePath certificate/certificate.pfx -CertStoreLocation Cert:\CurrentUser\My -Password (ConvertTo-SecureString -String $env:WINDOWS_CERTIFICATE_PASSWORD -Force -AsPlainText) + # Find the location of tauri.conf.json for debugging + - name: Locate tauri.conf.json + shell: bash + run: | + echo "Looking for tauri.conf.json file..." + find . -name "tauri.conf.json" -type f + + if [ -f "src-tauri/tauri.conf.json" ]; then + echo "Found in src-tauri directory" + echo "TAURI_CONFIG_DIR=src-tauri" >> $GITHUB_ENV + else + echo "Not found in src-tauri, using root directory" + echo "TAURI_CONFIG_DIR=." >> $GITHUB_ENV + fi - # SSL.com certificate handling (from VPN workflow) + # SSL.com certificate handling - name: Download EV CodeSignTool from ssl.com if: ${{ inputs.sign }} shell: bash run: | + # Navigate to the directory with tauri.conf.json + cd ${{ env.TAURI_CONFIG_DIR }} || true + + # Download and unzip the tool curl -L0 https://www.ssl.com/download/codesigntool-for-linux-and-macos/ -o codesigntool.zip unzip codesigntool.zip + + # Make the tool executable + chmod +x CodeSignTool.sh + + # Show where the tool is located for debugging + pwd + ls -la CodeSignTool.sh || echo "CodeSignTool.sh not found!" - name: Get EV certificate credential id if: ${{ inputs.sign }} id: get_credential_ids shell: bash run: | - echo "SSL_COM_CREDENTIAL_ID=$(./CodeSignTool.sh get_credential_ids -username=${{ secrets.SSL_COM_USERNAME }} -password=${{ secrets.SSL_COM_PASSWORD }} | sed -n '1!p' | sed 's/- //')" >> "$GITHUB_OUTPUT" + # Navigate to the directory with tauri.conf.json + cd ${{ env.TAURI_CONFIG_DIR }} || true + + # Get the credential ID + CREDENTIAL_ID=$(./CodeSignTool.sh get_credential_ids -username=${{ secrets.SSL_COM_USERNAME }} -password=${{ secrets.SSL_COM_PASSWORD }} | sed -n '1!p' | sed 's/- //') + echo "Credential ID: $CREDENTIAL_ID" + echo "SSL_COM_CREDENTIAL_ID=$CREDENTIAL_ID" >> "$GITHUB_OUTPUT" # Add custom sign command to tauri.conf.json for SSL.com signing - name: Add custom sign command to tauri.conf.json if: ${{ inputs.sign }} shell: bash run: | - yq eval --inplace '.bundle.windows += - { - "signCommand": { - "cmd": "C:\Program Files\Git\bin\bash.EXE", - "args": [ - "/c/actions-runner/_work/nym/nym/nym-wallet/CodeSignTool.sh", - "sign", - "-username ${{ secrets.SSL_COM_USERNAME }}", - "-password ${{ secrets.SSL_COM_PASSWORD }}", - "-credential_id ${{ steps.get_credential_ids.outputs.SSL_COM_CREDENTIAL_ID }}", - "-totp_secret ${{ secrets.SSL_COM_TOTP_SECRET }}", - "-program_name NymWallet", - "-input_file_path", - "%1", - "-override" + # Navigate to the directory with tauri.conf.json + cd ${{ env.TAURI_CONFIG_DIR }} || true + CONFIG_PATH="tauri.conf.json" + + # Get the absolute path to the CodeSignTool.sh + TOOL_PATH=$(pwd)/CodeSignTool.sh + + # Convert the tool path to Windows format + # Replace forward slashes with backslashes and add appropriate drive letter + WIN_PATH=$(echo "$TOOL_PATH" | sed 's|/c/|C:\\|g' | sed 's|/|\\|g') + + echo "Windows Tool Path: $WIN_PATH" + + # Update the tauri.conf.json file with the correct path + yq eval --inplace ".bundle.windows += { + \"signCommand\": { + \"cmd\": \"C:\\Program Files\\Git\\bin\\bash.EXE\", + \"args\": [ + \"$WIN_PATH\", + \"sign\", + \"-username ${{ secrets.SSL_COM_USERNAME }}\", + \"-password ${{ secrets.SSL_COM_PASSWORD }}\", + \"-credential_id ${{ steps.get_credential_ids.outputs.SSL_COM_CREDENTIAL_ID }}\", + \"-totp_secret ${{ secrets.SSL_COM_TOTP_SECRET }}\", + \"-program_name NymWallet\", + \"-input_file_path\", + \"%1\", + \"-override\" ] } - }' tauri.conf.json + }" "$CONFIG_PATH" + + # Show the updated configuration (redacted sensitive info) + cat "$CONFIG_PATH" | grep -A 15 "signCommand" | sed 's/\-username.*/\-username REDACTED/g' | sed 's/\-password.*/\-password REDACTED/g' | sed 's/\-totp_secret.*/\-totp_secret REDACTED/g' - name: Install project dependencies shell: bash @@ -128,8 +163,6 @@ jobs: shell: bash env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - # WINDOWS_CERTIFICATE: ${{ !inputs.sign && secrets.WINDOWS_CERTIFICATE }} - # WINDOWS_CERTIFICATE_PASSWORD: ${{ !inputs.sign && secrets.WINDOWS_CERTIFICATE_PASSWORD }} # Tauri v2 signing variables TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }} TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }} @@ -140,25 +173,39 @@ jobs: run: yarn build - name: Check bundle directory + shell: bash run: | echo "Checking bundle directory structure" - ls -la target/release/bundle || echo "Bundle directory not found" - if [ -d "target/release/bundle/msi" ]; then - echo "MSI bundle directory exists, checking contents:" - ls -la target/release/bundle/msi - else - echo "MSI bundle directory not found, checking alternatives:" - find target/release/bundle -type d -name "*msi*" -o -name "*MSI*" || echo "No MSI directories found" - find target/release/bundle -name "*.msi" || echo "No MSI files found" + + # Check standard location + if [ -d "target/release/bundle" ]; then + echo "Found bundle directory at standard location" + ls -la target/release/bundle || echo "Failed to list bundle directory" fi + + # Check src-tauri location + if [ -d "src-tauri/target/release/bundle" ]; then + echo "Found bundle directory in src-tauri" + ls -la src-tauri/target/release/bundle || echo "Failed to list src-tauri bundle directory" + + # Use this path for future steps + echo "BUNDLE_PATH=src-tauri/target/release/bundle" >> $GITHUB_ENV + else + echo "Using standard bundle path" + echo "BUNDLE_PATH=target/release/bundle" >> $GITHUB_ENV + fi + + # Check for MSI files in any location + find . -name "*.msi" -type f - name: Upload Artifact uses: actions/upload-artifact@v4 with: name: nym-wallet.msi path: | - nym-wallet/target/release/bundle/msi/*.msi - nym-wallet/target/release/bundle/*/nym-wallet*.msi + nym-wallet/${{ env.BUNDLE_PATH }}/msi/*.msi + nym-wallet/${{ env.BUNDLE_PATH }}/*/nym-wallet*.msi + nym-wallet/src-tauri/target/release/bundle/msi/*.msi retention-days: 30 - id: create-release @@ -167,22 +214,22 @@ jobs: if: github.event_name == 'release' with: files: | - nym-wallet/target/release/bundle/msi/*.msi - nym-wallet/target/release/bundle/msi/*.msi.zip* - nym-wallet/target/release/bundle/*/nym-wallet*.msi - nym-wallet/target/release/bundle/*/nym-wallet*.msi.zip* + nym-wallet/${{ env.BUNDLE_PATH }}/msi/*.msi + nym-wallet/${{ env.BUNDLE_PATH }}/msi/*.msi.zip* + nym-wallet/${{ env.BUNDLE_PATH }}/*/nym-wallet*.msi + nym-wallet/src-tauri/target/release/bundle/msi/*.msi - name: Find MSI path for deployment id: find-msi shell: bash run: | - MSI_FILE=$(find target/release/bundle -name "*.msi" | head -n 1) + MSI_FILE=$(find . -name "*.msi" -type f | head -n 1) if [ -n "$MSI_FILE" ]; then echo "Found MSI file: $MSI_FILE" echo "msi_path=$MSI_FILE" >> $GITHUB_OUTPUT else echo "WARNING: No MSI file found for deployment!" - echo "msi_path=target/release/bundle/msi/nym-wallet_1.*.msi" >> $GITHUB_OUTPUT + echo "msi_path=${{ env.BUNDLE_PATH }}/msi/nym-wallet*.msi" >> $GITHUB_OUTPUT fi - name: Deploy artifacts to CI www From dcb6de24218529293b6a1a54e9c501f13a3ed340 Mon Sep 17 00:00:00 2001 From: Tommy Verrall Date: Tue, 8 Apr 2025 11:22:38 +0200 Subject: [PATCH 27/48] tauri path --- .github/workflows/publish-nym-wallet-win11.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.github/workflows/publish-nym-wallet-win11.yml b/.github/workflows/publish-nym-wallet-win11.yml index ffc468bdc2..fd1bba58e4 100644 --- a/.github/workflows/publish-nym-wallet-win11.yml +++ b/.github/workflows/publish-nym-wallet-win11.yml @@ -87,14 +87,11 @@ jobs: # Navigate to the directory with tauri.conf.json cd ${{ env.TAURI_CONFIG_DIR }} || true - # Download and unzip the tool curl -L0 https://www.ssl.com/download/codesigntool-for-linux-and-macos/ -o codesigntool.zip unzip codesigntool.zip - # Make the tool executable chmod +x CodeSignTool.sh - # Show where the tool is located for debugging pwd ls -la CodeSignTool.sh || echo "CodeSignTool.sh not found!" @@ -116,11 +113,9 @@ jobs: if: ${{ inputs.sign }} shell: bash run: | - # Navigate to the directory with tauri.conf.json cd ${{ env.TAURI_CONFIG_DIR }} || true CONFIG_PATH="tauri.conf.json" - # Get the absolute path to the CodeSignTool.sh TOOL_PATH=$(pwd)/CodeSignTool.sh # Convert the tool path to Windows format From 4292d8ac03687dd2e9a266db380cbf7ff3786fbb Mon Sep 17 00:00:00 2001 From: Tommy Verrall Date: Tue, 8 Apr 2025 11:40:50 +0200 Subject: [PATCH 28/48] update windows build --- .../workflows/publish-nym-wallet-win11.yml | 194 +++++++++++------- 1 file changed, 121 insertions(+), 73 deletions(-) diff --git a/.github/workflows/publish-nym-wallet-win11.yml b/.github/workflows/publish-nym-wallet-win11.yml index fd1bba58e4..ae18d756ac 100644 --- a/.github/workflows/publish-nym-wallet-win11.yml +++ b/.github/workflows/publish-nym-wallet-win11.yml @@ -64,87 +64,120 @@ jobs: fileName: '.env' encodedString: ${{ secrets.WALLET_ADMIN_ADDRESS }} - # Find the location of tauri.conf.json for debugging - - name: Locate tauri.conf.json + # Find the location of the src-tauri directory + - name: Locate src-tauri directory shell: bash run: | - echo "Looking for tauri.conf.json file..." - find . -name "tauri.conf.json" -type f - - if [ -f "src-tauri/tauri.conf.json" ]; then - echo "Found in src-tauri directory" - echo "TAURI_CONFIG_DIR=src-tauri" >> $GITHUB_ENV + echo "Looking for src-tauri directory..." + if [ -d "src-tauri" ]; then + echo "Found src-tauri directory" + echo "TAURI_SRC_DIR=src-tauri" >> $GITHUB_ENV + echo "TAURI_CONF_PATH=src-tauri/tauri.conf.json" >> $GITHUB_ENV else - echo "Not found in src-tauri, using root directory" - echo "TAURI_CONFIG_DIR=." >> $GITHUB_ENV + echo "src-tauri directory not found, using current directory" + echo "TAURI_SRC_DIR=." >> $GITHUB_ENV + echo "TAURI_CONF_PATH=tauri.conf.json" >> $GITHUB_ENV fi - # SSL.com certificate handling - - name: Download EV CodeSignTool from ssl.com + # SSL.com certificate handling specifically for Windows + - name: Download EV CodeSignTool from ssl.com - Windows if: ${{ inputs.sign }} - shell: bash + shell: powershell run: | - # Navigate to the directory with tauri.conf.json - cd ${{ env.TAURI_CONFIG_DIR }} || true + # Check and navigate to src-tauri directory if it exists + $srcTauriPath = "src-tauri" + if (Test-Path $srcTauriPath) { + Set-Location $srcTauriPath + Write-Output "Changed to src-tauri directory" + } - curl -L0 https://www.ssl.com/download/codesigntool-for-linux-and-macos/ -o codesigntool.zip - unzip codesigntool.zip + Write-Output "Downloading CodeSignTool..." + Invoke-WebRequest -Uri "https://www.ssl.com/download/codesigntool-for-windows/" -OutFile "codesigntool.zip" - chmod +x CodeSignTool.sh + Write-Output "Extracting CodeSignTool..." + Expand-Archive -Path "codesigntool.zip" -DestinationPath "." -Force - pwd - ls -la CodeSignTool.sh || echo "CodeSignTool.sh not found!" + if (Test-Path "CodeSignTool.bat") { + Write-Output "CodeSignTool.bat found!" + Get-ChildItem -Path "CodeSignTool.bat" + } else { + Write-Output "CodeSignTool.bat not found! Listing directory contents:" + Get-ChildItem -Path "." + } + + $toolPath = Join-Path -Path (Get-Location) -ChildPath "CodeSignTool.bat" + Write-Output "Tool path: $toolPath" + $toolPath | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append - name: Get EV certificate credential id if: ${{ inputs.sign }} id: get_credential_ids - shell: bash + shell: powershell run: | - # Navigate to the directory with tauri.conf.json - cd ${{ env.TAURI_CONFIG_DIR }} || true + $srcTauriPath = "src-tauri" + if (Test-Path $srcTauriPath) { + Set-Location $srcTauriPath + Write-Output "Changed to src-tauri directory" + } - # Get the credential ID - CREDENTIAL_ID=$(./CodeSignTool.sh get_credential_ids -username=${{ secrets.SSL_COM_USERNAME }} -password=${{ secrets.SSL_COM_PASSWORD }} | sed -n '1!p' | sed 's/- //') - echo "Credential ID: $CREDENTIAL_ID" - echo "SSL_COM_CREDENTIAL_ID=$CREDENTIAL_ID" >> "$GITHUB_OUTPUT" + Write-Output "Getting credential ID..." + $credentialOutput = & .\CodeSignTool.bat get_credential_ids -username=${{ secrets.SSL_COM_USERNAME }} -password=${{ secrets.SSL_COM_PASSWORD }} + Write-Output "Credential output: $credentialOutput" + + $credentialId = ($credentialOutput | Select-String -Pattern "- (.+)" | ForEach-Object { $_.Matches.Groups[1].Value }) -replace " ", "" + Write-Output "Credential ID: $credentialId" + + "SSL_COM_CREDENTIAL_ID=$credentialId" | Out-File -FilePath $env:GITHUB_OUTPUT -Encoding utf8 -Append + + $toolPath = Join-Path -Path (Get-Location) -ChildPath "CodeSignTool.bat" + "CODESIGNTOOL_PATH=$toolPath" | Out-File -FilePath $env:GITHUB_OUTPUT -Encoding utf8 -Append # Add custom sign command to tauri.conf.json for SSL.com signing - name: Add custom sign command to tauri.conf.json if: ${{ inputs.sign }} - shell: bash + shell: powershell run: | - cd ${{ env.TAURI_CONFIG_DIR }} || true - CONFIG_PATH="tauri.conf.json" + # Find and update tauri.conf.json + $configPath = $env:TAURI_CONF_PATH + Write-Output "Config path: $configPath" - TOOL_PATH=$(pwd)/CodeSignTool.sh + if (Test-Path $configPath) { + Write-Output "Found Tauri config file" + } else { + Write-Output "Tauri config file not found!" + Get-ChildItem -Recurse -Path "." -Filter "tauri.conf.json" | ForEach-Object { Write-Output $_.FullName } + exit 1 + } - # Convert the tool path to Windows format - # Replace forward slashes with backslashes and add appropriate drive letter - WIN_PATH=$(echo "$TOOL_PATH" | sed 's|/c/|C:\\|g' | sed 's|/|\\|g') + $toolPath = ${{ steps.get_credential_ids.outputs.CODESIGNTOOL_PATH }} + Write-Output "CodeSignTool path: $toolPath" - echo "Windows Tool Path: $WIN_PATH" + $escapedToolPath = $toolPath -replace '\\', '\\' + Write-Output "Escaped tool path: $escapedToolPath" - # Update the tauri.conf.json file with the correct path - yq eval --inplace ".bundle.windows += { - \"signCommand\": { - \"cmd\": \"C:\\Program Files\\Git\\bin\\bash.EXE\", - \"args\": [ - \"$WIN_PATH\", - \"sign\", - \"-username ${{ secrets.SSL_COM_USERNAME }}\", - \"-password ${{ secrets.SSL_COM_PASSWORD }}\", - \"-credential_id ${{ steps.get_credential_ids.outputs.SSL_COM_CREDENTIAL_ID }}\", - \"-totp_secret ${{ secrets.SSL_COM_TOTP_SECRET }}\", - \"-program_name NymWallet\", - \"-input_file_path\", - \"%1\", - \"-override\" + yq eval --inplace '.bundle.windows += { + "signCommand": { + "cmd": "' + $escapedToolPath + '", + "args": [ + "sign", + "-username", "${{ secrets.SSL_COM_USERNAME }}", + "-password", "${{ secrets.SSL_COM_PASSWORD }}", + "-credential_id", "${{ steps.get_credential_ids.outputs.SSL_COM_CREDENTIAL_ID }}", + "-totp_secret", "${{ secrets.SSL_COM_TOTP_SECRET }}", + "-program_name", "NymWallet", + "-input_file_path", "%1", + "-override" ] } - }" "$CONFIG_PATH" + }' $configPath - # Show the updated configuration (redacted sensitive info) - cat "$CONFIG_PATH" | grep -A 15 "signCommand" | sed 's/\-username.*/\-username REDACTED/g' | sed 's/\-password.*/\-password REDACTED/g' | sed 's/\-totp_secret.*/\-totp_secret REDACTED/g' + $config = Get-Content $configPath + if ($config -match "signCommand") { + Write-Output "Successfully updated tauri.conf.json with signing command" + } else { + Write-Output "Failed to add signCommand to tauri.conf.json" + exit 1 + } - name: Install project dependencies shell: bash @@ -161,37 +194,50 @@ jobs: # Tauri v2 signing variables TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }} TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }} + # SSL.com signing variables SSL_COM_USERNAME: ${{ inputs.sign && secrets.SSL_COM_USERNAME }} SSL_COM_PASSWORD: ${{ inputs.sign && secrets.SSL_COM_PASSWORD }} SSL_COM_CREDENTIAL_ID: ${{ inputs.sign && steps.get_credential_ids.outputs.SSL_COM_CREDENTIAL_ID }} SSL_COM_TOTP_SECRET: ${{ inputs.sign && secrets.SSL_COM_TOTP_SECRET }} - run: yarn build + run: | + yarn build + + # List build artifacts for debugging + find . -name "*.msi" -type f + find . -name "*.exe" -type f - name: Check bundle directory shell: bash run: | echo "Checking bundle directory structure" - # Check standard location - if [ -d "target/release/bundle" ]; then - echo "Found bundle directory at standard location" - ls -la target/release/bundle || echo "Failed to list bundle directory" - fi - - # Check src-tauri location - if [ -d "src-tauri/target/release/bundle" ]; then - echo "Found bundle directory in src-tauri" - ls -la src-tauri/target/release/bundle || echo "Failed to list src-tauri bundle directory" + find . -path "*/bundle" -type d | while read dir; do + echo "Found bundle directory: $dir" + ls -la "$dir" || echo "Failed to list bundle directory contents" - # Use this path for future steps - echo "BUNDLE_PATH=src-tauri/target/release/bundle" >> $GITHUB_ENV - else - echo "Using standard bundle path" - echo "BUNDLE_PATH=target/release/bundle" >> $GITHUB_ENV - fi + find "$dir" -type d | while read subdir; do + echo "Subdirectory: $subdir" + ls -la "$subdir" || echo "Failed to list subdirectory contents" + done + done - # Check for MSI files in any location + echo "Looking for MSI files:" find . -name "*.msi" -type f + + if [ -d "src-tauri/target/release/bundle" ]; then + echo "BUNDLE_PATH=src-tauri/target/release/bundle" >> $GITHUB_ENV + elif [ -d "target/release/bundle" ]; then + echo "BUNDLE_PATH=target/release/bundle" >> $GITHUB_ENV + else + echo "Using dynamic bundle path detection" + BUNDLE_DIR=$(find . -path "*/bundle" -type d | head -n 1) + if [ -n "$BUNDLE_DIR" ]; then + echo "BUNDLE_PATH=$BUNDLE_DIR" >> $GITHUB_ENV + else + echo "WARNING: Could not find bundle directory!" + echo "BUNDLE_PATH=target/release/bundle" >> $GITHUB_ENV + fi + fi - name: Upload Artifact uses: actions/upload-artifact@v4 @@ -200,7 +246,8 @@ jobs: path: | nym-wallet/${{ env.BUNDLE_PATH }}/msi/*.msi nym-wallet/${{ env.BUNDLE_PATH }}/*/nym-wallet*.msi - nym-wallet/src-tauri/target/release/bundle/msi/*.msi + nym-wallet/**/bundle/**/msi/*.msi + nym-wallet/**/*.msi retention-days: 30 - id: create-release @@ -212,7 +259,8 @@ jobs: nym-wallet/${{ env.BUNDLE_PATH }}/msi/*.msi nym-wallet/${{ env.BUNDLE_PATH }}/msi/*.msi.zip* nym-wallet/${{ env.BUNDLE_PATH }}/*/nym-wallet*.msi - nym-wallet/src-tauri/target/release/bundle/msi/*.msi + nym-wallet/**/bundle/**/msi/*.msi + nym-wallet/**/*.msi - name: Find MSI path for deployment id: find-msi From 4f6d65ab95ff118f333e85e31056e494a101db0d Mon Sep 17 00:00:00 2001 From: Tommy Verrall Date: Tue, 8 Apr 2025 11:50:27 +0200 Subject: [PATCH 29/48] revert previous add more logging --- .../workflows/publish-nym-wallet-win11.yml | 220 ++++++++---------- 1 file changed, 98 insertions(+), 122 deletions(-) diff --git a/.github/workflows/publish-nym-wallet-win11.yml b/.github/workflows/publish-nym-wallet-win11.yml index ae18d756ac..0e5db47229 100644 --- a/.github/workflows/publish-nym-wallet-win11.yml +++ b/.github/workflows/publish-nym-wallet-win11.yml @@ -31,6 +31,20 @@ jobs: file_hash: ${{ steps.release-info.outputs.file_hash }} steps: + - name: Extensive Environment Debug + shell: bash + run: | + echo "Current User: $(whoami)" + echo "Home Directory: $HOME" + echo "Working Directory: $(pwd)" + echo "PATH: $PATH" + echo "Git Bash Path:" + which bash || echo "Bash not found in PATH" + echo "Git Bash Executable Details:" + ls -l "/c/Program Files/Git/bin/bash.exe" || echo "Git Bash executable not found" + echo "Listing Git installation:" + ls -l "/c/Program Files/Git" || echo "Git directory not found" + - name: Clean up first continue-on-error: true working-directory: . @@ -64,120 +78,95 @@ jobs: fileName: '.env' encodedString: ${{ secrets.WALLET_ADMIN_ADDRESS }} - # Find the location of the src-tauri directory - - name: Locate src-tauri directory + # Find the location of tauri.conf.json for debugging + - name: Locate tauri.conf.json shell: bash run: | - echo "Looking for src-tauri directory..." - if [ -d "src-tauri" ]; then - echo "Found src-tauri directory" - echo "TAURI_SRC_DIR=src-tauri" >> $GITHUB_ENV - echo "TAURI_CONF_PATH=src-tauri/tauri.conf.json" >> $GITHUB_ENV + echo "Looking for tauri.conf.json file..." + find . -name "tauri.conf.json" -type f + + if [ -f "src-tauri/tauri.conf.json" ]; then + echo "Found in src-tauri directory" + echo "TAURI_CONFIG_DIR=src-tauri" >> $GITHUB_ENV else - echo "src-tauri directory not found, using current directory" - echo "TAURI_SRC_DIR=." >> $GITHUB_ENV - echo "TAURI_CONF_PATH=tauri.conf.json" >> $GITHUB_ENV + echo "Not found in src-tauri, using root directory" + echo "TAURI_CONFIG_DIR=." >> $GITHUB_ENV fi - # SSL.com certificate handling specifically for Windows - - name: Download EV CodeSignTool from ssl.com - Windows + # SSL.com certificate handling + - name: Download EV CodeSignTool from ssl.com if: ${{ inputs.sign }} - shell: powershell + shell: bash run: | - # Check and navigate to src-tauri directory if it exists - $srcTauriPath = "src-tauri" - if (Test-Path $srcTauriPath) { - Set-Location $srcTauriPath - Write-Output "Changed to src-tauri directory" - } + # Navigate to the directory with tauri.conf.json + cd ${{ env.TAURI_CONFIG_DIR }} || true - Write-Output "Downloading CodeSignTool..." - Invoke-WebRequest -Uri "https://www.ssl.com/download/codesigntool-for-windows/" -OutFile "codesigntool.zip" + # Download with detailed logging + echo "Downloading CodeSignTool..." + curl -v -L0 https://www.ssl.com/download/codesigntool-for-linux-and-macos/ -o codesigntool.zip - Write-Output "Extracting CodeSignTool..." - Expand-Archive -Path "codesigntool.zip" -DestinationPath "." -Force + # Unzip with verbose output + echo "Unzipping CodeSignTool..." + unzip -v codesigntool.zip - if (Test-Path "CodeSignTool.bat") { - Write-Output "CodeSignTool.bat found!" - Get-ChildItem -Path "CodeSignTool.bat" - } else { - Write-Output "CodeSignTool.bat not found! Listing directory contents:" - Get-ChildItem -Path "." - } + # Make the tool executable + chmod +x CodeSignTool.sh - $toolPath = Join-Path -Path (Get-Location) -ChildPath "CodeSignTool.bat" - Write-Output "Tool path: $toolPath" - $toolPath | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append + # Verify tool location and permissions + echo "CodeSignTool Location and Permissions:" + ls -l CodeSignTool.sh + pwd + ls -la - name: Get EV certificate credential id if: ${{ inputs.sign }} id: get_credential_ids - shell: powershell + shell: bash run: | - $srcTauriPath = "src-tauri" - if (Test-Path $srcTauriPath) { - Set-Location $srcTauriPath - Write-Output "Changed to src-tauri directory" - } + # Navigate to the directory with tauri.conf.json + cd ${{ env.TAURI_CONFIG_DIR }} || true - Write-Output "Getting credential ID..." - $credentialOutput = & .\CodeSignTool.bat get_credential_ids -username=${{ secrets.SSL_COM_USERNAME }} -password=${{ secrets.SSL_COM_PASSWORD }} - Write-Output "Credential output: $credentialOutput" - - $credentialId = ($credentialOutput | Select-String -Pattern "- (.+)" | ForEach-Object { $_.Matches.Groups[1].Value }) -replace " ", "" - Write-Output "Credential ID: $credentialId" - - "SSL_COM_CREDENTIAL_ID=$credentialId" | Out-File -FilePath $env:GITHUB_OUTPUT -Encoding utf8 -Append - - $toolPath = Join-Path -Path (Get-Location) -ChildPath "CodeSignTool.bat" - "CODESIGNTOOL_PATH=$toolPath" | Out-File -FilePath $env:GITHUB_OUTPUT -Encoding utf8 -Append + # Get the credential ID with verbose output + echo "Retrieving Credential ID..." + CREDENTIAL_ID=$(./CodeSignTool.sh get_credential_ids -username=${{ secrets.SSL_COM_USERNAME }} -password=${{ secrets.SSL_COM_PASSWORD }} | sed -n '1!p' | sed 's/- //') + echo "Credential ID retrieved" + echo "SSL_COM_CREDENTIAL_ID=$CREDENTIAL_ID" >> "$GITHUB_OUTPUT" # Add custom sign command to tauri.conf.json for SSL.com signing - name: Add custom sign command to tauri.conf.json if: ${{ inputs.sign }} - shell: powershell + shell: bash run: | - # Find and update tauri.conf.json - $configPath = $env:TAURI_CONF_PATH - Write-Output "Config path: $configPath" + # Navigate to the directory with tauri.conf.json + cd ${{ env.TAURI_CONFIG_DIR }} || true + CONFIG_PATH="tauri.conf.json" - if (Test-Path $configPath) { - Write-Output "Found Tauri config file" - } else { - Write-Output "Tauri config file not found!" - Get-ChildItem -Recurse -Path "." -Filter "tauri.conf.json" | ForEach-Object { Write-Output $_.FullName } - exit 1 - } + # Get the absolute path to the CodeSignTool.sh + TOOL_PATH=$(pwd)/CodeSignTool.sh - $toolPath = ${{ steps.get_credential_ids.outputs.CODESIGNTOOL_PATH }} - Write-Output "CodeSignTool path: $toolPath" + # Debug output + echo "CodeSignTool Path: $TOOL_PATH" - $escapedToolPath = $toolPath -replace '\\', '\\' - Write-Output "Escaped tool path: $escapedToolPath" - - yq eval --inplace '.bundle.windows += { - "signCommand": { - "cmd": "' + $escapedToolPath + '", - "args": [ - "sign", - "-username", "${{ secrets.SSL_COM_USERNAME }}", - "-password", "${{ secrets.SSL_COM_PASSWORD }}", - "-credential_id", "${{ steps.get_credential_ids.outputs.SSL_COM_CREDENTIAL_ID }}", - "-totp_secret", "${{ secrets.SSL_COM_TOTP_SECRET }}", - "-program_name", "NymWallet", - "-input_file_path", "%1", - "-override" + # Update the tauri.conf.json file with signing configuration + yq eval --inplace ".bundle.windows += { + \"signCommand\": { + \"cmd\": \"./CodeSignTool.sh\", + \"args\": [ + \"sign\", + \"-username\", \"${{ secrets.SSL_COM_USERNAME }}\", + \"-password\", \"${{ secrets.SSL_COM_PASSWORD }}\", + \"-credential_id\", \"${{ steps.get_credential_ids.outputs.SSL_COM_CREDENTIAL_ID }}\", + \"-totp_secret\", \"${{ secrets.SSL_COM_TOTP_SECRET }}\", + \"-program_name\", \"NymWallet\", + \"-input_file_path\", \"%1\", + \"-override\" ] } - }' $configPath + }" "$CONFIG_PATH" - $config = Get-Content $configPath - if ($config -match "signCommand") { - Write-Output "Successfully updated tauri.conf.json with signing command" - } else { - Write-Output "Failed to add signCommand to tauri.conf.json" - exit 1 - } + # Show the updated configuration (redacted sensitive info) + echo "Updated tauri.conf.json signing configuration:" + cat "$CONFIG_PATH" | grep -A 15 "signCommand" | sed 's/\-username.*/\-username REDACTED/g' | sed 's/\-password.*/\-password REDACTED/g' | sed 's/\-totp_secret.*/\-totp_secret REDACTED/g' - name: Install project dependencies shell: bash @@ -194,50 +183,39 @@ jobs: # Tauri v2 signing variables TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }} TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }} - # SSL.com signing variables SSL_COM_USERNAME: ${{ inputs.sign && secrets.SSL_COM_USERNAME }} SSL_COM_PASSWORD: ${{ inputs.sign && secrets.SSL_COM_PASSWORD }} SSL_COM_CREDENTIAL_ID: ${{ inputs.sign && steps.get_credential_ids.outputs.SSL_COM_CREDENTIAL_ID }} SSL_COM_TOTP_SECRET: ${{ inputs.sign && secrets.SSL_COM_TOTP_SECRET }} run: | - yarn build - - # List build artifacts for debugging - find . -name "*.msi" -type f - find . -name "*.exe" -type f + echo "Starting build process..." + yarn build -v - name: Check bundle directory shell: bash run: | echo "Checking bundle directory structure" - find . -path "*/bundle" -type d | while read dir; do - echo "Found bundle directory: $dir" - ls -la "$dir" || echo "Failed to list bundle directory contents" - - find "$dir" -type d | while read subdir; do - echo "Subdirectory: $subdir" - ls -la "$subdir" || echo "Failed to list subdirectory contents" - done - done - - echo "Looking for MSI files:" - find . -name "*.msi" -type f - - if [ -d "src-tauri/target/release/bundle" ]; then - echo "BUNDLE_PATH=src-tauri/target/release/bundle" >> $GITHUB_ENV - elif [ -d "target/release/bundle" ]; then - echo "BUNDLE_PATH=target/release/bundle" >> $GITHUB_ENV - else - echo "Using dynamic bundle path detection" - BUNDLE_DIR=$(find . -path "*/bundle" -type d | head -n 1) - if [ -n "$BUNDLE_DIR" ]; then - echo "BUNDLE_PATH=$BUNDLE_DIR" >> $GITHUB_ENV - else - echo "WARNING: Could not find bundle directory!" - echo "BUNDLE_PATH=target/release/bundle" >> $GITHUB_ENV - fi + # Check standard location + if [ -d "target/release/bundle" ]; then + echo "Found bundle directory at standard location" + ls -la target/release/bundle || echo "Failed to list bundle directory" fi + + # Check src-tauri location + if [ -d "src-tauri/target/release/bundle" ]; then + echo "Found bundle directory in src-tauri" + ls -la src-tauri/target/release/bundle || echo "Failed to list src-tauri bundle directory" + + # Use this path for future steps + echo "BUNDLE_PATH=src-tauri/target/release/bundle" >> $GITHUB_ENV + else + echo "Using standard bundle path" + echo "BUNDLE_PATH=target/release/bundle" >> $GITHUB_ENV + fi + + # Check for MSI files in any location + find . -name "*.msi" -type f - name: Upload Artifact uses: actions/upload-artifact@v4 @@ -246,8 +224,7 @@ jobs: path: | nym-wallet/${{ env.BUNDLE_PATH }}/msi/*.msi nym-wallet/${{ env.BUNDLE_PATH }}/*/nym-wallet*.msi - nym-wallet/**/bundle/**/msi/*.msi - nym-wallet/**/*.msi + nym-wallet/src-tauri/target/release/bundle/msi/*.msi retention-days: 30 - id: create-release @@ -259,8 +236,7 @@ jobs: nym-wallet/${{ env.BUNDLE_PATH }}/msi/*.msi nym-wallet/${{ env.BUNDLE_PATH }}/msi/*.msi.zip* nym-wallet/${{ env.BUNDLE_PATH }}/*/nym-wallet*.msi - nym-wallet/**/bundle/**/msi/*.msi - nym-wallet/**/*.msi + nym-wallet/src-tauri/target/release/bundle/msi/*.msi - name: Find MSI path for deployment id: find-msi From 1367cad99d07d3f25deefac7f2332eecb17af9f1 Mon Sep 17 00:00:00 2001 From: Tommy Verrall Date: Tue, 8 Apr 2025 11:54:47 +0200 Subject: [PATCH 30/48] another attempt --- .../workflows/publish-nym-wallet-win11.yml | 55 ++++++++----------- 1 file changed, 23 insertions(+), 32 deletions(-) diff --git a/.github/workflows/publish-nym-wallet-win11.yml b/.github/workflows/publish-nym-wallet-win11.yml index 0e5db47229..72838a7da3 100644 --- a/.github/workflows/publish-nym-wallet-win11.yml +++ b/.github/workflows/publish-nym-wallet-win11.yml @@ -31,19 +31,15 @@ jobs: file_hash: ${{ steps.release-info.outputs.file_hash }} steps: - - name: Extensive Environment Debug - shell: bash - run: | - echo "Current User: $(whoami)" - echo "Home Directory: $HOME" - echo "Working Directory: $(pwd)" - echo "PATH: $PATH" - echo "Git Bash Path:" - which bash || echo "Bash not found in PATH" - echo "Git Bash Executable Details:" - ls -l "/c/Program Files/Git/bin/bash.exe" || echo "Git Bash executable not found" - echo "Listing Git installation:" - ls -l "/c/Program Files/Git" || echo "Git directory not found" + - name: Diagnostic Environment Check + shell: bash + run: | + echo "Current Directory: $(pwd)" + echo "Home Directory: $HOME" + echo "PATH: $PATH" + echo "Git Bash Executable:" + which bash || echo "Bash not found" + ls -l "/c/Program Files/Git/bin/bash.exe" || echo "Git Bash executable not found" - name: Clean up first continue-on-error: true @@ -93,31 +89,26 @@ jobs: echo "TAURI_CONFIG_DIR=." >> $GITHUB_ENV fi - # SSL.com certificate handling - - name: Download EV CodeSignTool from ssl.com + - name: Download and Setup SSL.com CodeSignTool if: ${{ inputs.sign }} shell: bash run: | - # Navigate to the directory with tauri.conf.json - cd ${{ env.TAURI_CONFIG_DIR }} || true + mkdir -p codesigntool - # Download with detailed logging - echo "Downloading CodeSignTool..." - curl -v -L0 https://www.ssl.com/download/codesigntool-for-linux-and-macos/ -o codesigntool.zip - - # Unzip with verbose output - echo "Unzipping CodeSignTool..." - unzip -v codesigntool.zip - - # Make the tool executable - chmod +x CodeSignTool.sh - - # Verify tool location and permissions - echo "CodeSignTool Location and Permissions:" - ls -l CodeSignTool.sh - pwd + cd codesigntool + + wget -v https://www.ssl.com/download/codesigntool-for-linux-and-macos/ -O codesigntool.zip + + 7z x codesigntool.zip + ls -la + chmod +x CodeSignTool.sh || true + + cat CodeSignTool.sh || echo "Could not read CodeSignTool.sh" + + mv CodeSignTool.sh /c/codesigntool/CodeSignTool.sh || true + - name: Get EV certificate credential id if: ${{ inputs.sign }} id: get_credential_ids From da9115d51b7b1977ea0d25beec6483abf89fe189 Mon Sep 17 00:00:00 2001 From: Tommy Verrall Date: Tue, 8 Apr 2025 11:58:48 +0200 Subject: [PATCH 31/48] format --- .github/workflows/publish-nym-wallet-win11.yml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/publish-nym-wallet-win11.yml b/.github/workflows/publish-nym-wallet-win11.yml index 72838a7da3..54aabf477d 100644 --- a/.github/workflows/publish-nym-wallet-win11.yml +++ b/.github/workflows/publish-nym-wallet-win11.yml @@ -32,14 +32,14 @@ jobs: steps: - name: Diagnostic Environment Check - shell: bash - run: | - echo "Current Directory: $(pwd)" - echo "Home Directory: $HOME" - echo "PATH: $PATH" - echo "Git Bash Executable:" - which bash || echo "Bash not found" - ls -l "/c/Program Files/Git/bin/bash.exe" || echo "Git Bash executable not found" + shell: bash + run: | + echo "Current Directory: $(pwd)" + echo "Home Directory: $HOME" + echo "PATH: $PATH" + echo "Git Bash Executable:" + which bash || echo "Bash not found" + ls -l "/c/Program Files/Git/bin/bash.exe" || echo "Git Bash executable not found" - name: Clean up first continue-on-error: true @@ -94,7 +94,7 @@ jobs: shell: bash run: | mkdir -p codesigntool - + cd codesigntool wget -v https://www.ssl.com/download/codesigntool-for-linux-and-macos/ -O codesigntool.zip From d9d62195cb472cafb9763c24db12547aba4e8e14 Mon Sep 17 00:00:00 2001 From: Tommy Verrall Date: Tue, 8 Apr 2025 12:05:28 +0200 Subject: [PATCH 32/48] try again --- .../workflows/publish-nym-wallet-win11.yml | 120 ++++-------------- 1 file changed, 23 insertions(+), 97 deletions(-) diff --git a/.github/workflows/publish-nym-wallet-win11.yml b/.github/workflows/publish-nym-wallet-win11.yml index 54aabf477d..9b243a33df 100644 --- a/.github/workflows/publish-nym-wallet-win11.yml +++ b/.github/workflows/publish-nym-wallet-win11.yml @@ -31,24 +31,6 @@ jobs: file_hash: ${{ steps.release-info.outputs.file_hash }} steps: - - name: Diagnostic Environment Check - shell: bash - run: | - echo "Current Directory: $(pwd)" - echo "Home Directory: $HOME" - echo "PATH: $PATH" - echo "Git Bash Executable:" - which bash || echo "Bash not found" - ls -l "/c/Program Files/Git/bin/bash.exe" || echo "Git Bash executable not found" - - - name: Clean up first - continue-on-error: true - working-directory: . - run: | - cd .. - del /s /q /A:H nym - rmdir /s /q nym - - uses: actions/checkout@v4 - name: Install Rust stable @@ -59,105 +41,50 @@ jobs: - name: Setup MSBuild.exe uses: microsoft/setup-msbuild@v2 - - name: Install yq - run: | - winget list MikeFarah.yq || winget install --disable-interactivity --id MikeFarah.yq - - name: Node uses: actions/setup-node@v4 with: node-version: 21 - - name: Create env file - uses: timheuer/base64-to-file@v1.2 - with: - fileName: '.env' - encodedString: ${{ secrets.WALLET_ADMIN_ADDRESS }} - - # Find the location of tauri.conf.json for debugging - - name: Locate tauri.conf.json - shell: bash - run: | - echo "Looking for tauri.conf.json file..." - find . -name "tauri.conf.json" -type f - - if [ -f "src-tauri/tauri.conf.json" ]; then - echo "Found in src-tauri directory" - echo "TAURI_CONFIG_DIR=src-tauri" >> $GITHUB_ENV - else - echo "Not found in src-tauri, using root directory" - echo "TAURI_CONFIG_DIR=." >> $GITHUB_ENV - fi - - - name: Download and Setup SSL.com CodeSignTool + - name: Download EV CodeSignTool from ssl.com + working-directory: nym-wallet/src-tauri if: ${{ inputs.sign }} shell: bash run: | - mkdir -p codesigntool - - cd codesigntool - - wget -v https://www.ssl.com/download/codesigntool-for-linux-and-macos/ -O codesigntool.zip - - 7z x codesigntool.zip - - ls -la - - chmod +x CodeSignTool.sh || true - - cat CodeSignTool.sh || echo "Could not read CodeSignTool.sh" - - mv CodeSignTool.sh /c/codesigntool/CodeSignTool.sh || true + curl -L0 https://www.ssl.com/download/codesigntool-for-linux-and-macos/ -o codesigntool.zip + unzip codesigntool.zip - name: Get EV certificate credential id + working-directory: nym-wallet/src-tauri if: ${{ inputs.sign }} id: get_credential_ids shell: bash run: | - # Navigate to the directory with tauri.conf.json - cd ${{ env.TAURI_CONFIG_DIR }} || true - - # Get the credential ID with verbose output - echo "Retrieving Credential ID..." - CREDENTIAL_ID=$(./CodeSignTool.sh get_credential_ids -username=${{ secrets.SSL_COM_USERNAME }} -password=${{ secrets.SSL_COM_PASSWORD }} | sed -n '1!p' | sed 's/- //') - echo "Credential ID retrieved" - echo "SSL_COM_CREDENTIAL_ID=$CREDENTIAL_ID" >> "$GITHUB_OUTPUT" + echo "SSL_COM_CREDENTIAL_ID=$(./CodeSignTool.sh get_credential_ids -username=${{ secrets.SSL_COM_USERNAME }} -password=${{ secrets.SSL_COM_PASSWORD }} | sed -n '1!p' | sed 's/- //')" >> "$GITHUB_OUTPUT" - # Add custom sign command to tauri.conf.json for SSL.com signing - name: Add custom sign command to tauri.conf.json + working-directory: nym-wallet/src-tauri if: ${{ inputs.sign }} shell: bash run: | - # Navigate to the directory with tauri.conf.json - cd ${{ env.TAURI_CONFIG_DIR }} || true - CONFIG_PATH="tauri.conf.json" - - # Get the absolute path to the CodeSignTool.sh - TOOL_PATH=$(pwd)/CodeSignTool.sh - - # Debug output - echo "CodeSignTool Path: $TOOL_PATH" - - # Update the tauri.conf.json file with signing configuration - yq eval --inplace ".bundle.windows += { - \"signCommand\": { - \"cmd\": \"./CodeSignTool.sh\", - \"args\": [ - \"sign\", - \"-username\", \"${{ secrets.SSL_COM_USERNAME }}\", - \"-password\", \"${{ secrets.SSL_COM_PASSWORD }}\", - \"-credential_id\", \"${{ steps.get_credential_ids.outputs.SSL_COM_CREDENTIAL_ID }}\", - \"-totp_secret\", \"${{ secrets.SSL_COM_TOTP_SECRET }}\", - \"-program_name\", \"NymWallet\", - \"-input_file_path\", \"%1\", - \"-override\" + yq eval --inplace '.bundle.windows += + { + "signCommand": { + "cmd": "C:\Program Files\Git\bin\bash.EXE", + "args": [ + "/c/actions-runner/_work/nym/nym/nym-wallet/src-tauri/CodeSignTool.sh", + "sign", + "-username ${{ secrets.SSL_COM_USERNAME }}", + "-password ${{ secrets.SSL_COM_PASSWORD }}", + "-credential_id ${{ steps.get_credential_ids.outputs.SSL_COM_CREDENTIAL_ID }}", + "-totp_secret ${{ secrets.SSL_COM_TOTP_SECRET }}", + "-program_name NymWallet", + "-input_file_path", + "%1", + "-override" ] } - }" "$CONFIG_PATH" - - # Show the updated configuration (redacted sensitive info) - echo "Updated tauri.conf.json signing configuration:" - cat "$CONFIG_PATH" | grep -A 15 "signCommand" | sed 's/\-username.*/\-username REDACTED/g' | sed 's/\-password.*/\-password REDACTED/g' | sed 's/\-totp_secret.*/\-totp_secret REDACTED/g' + }' tauri.conf.json - name: Install project dependencies shell: bash @@ -171,7 +98,6 @@ jobs: shell: bash env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - # Tauri v2 signing variables TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }} TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }} SSL_COM_USERNAME: ${{ inputs.sign && secrets.SSL_COM_USERNAME }} From c92de832e4723f44c3bef108d39212424744bbf9 Mon Sep 17 00:00:00 2001 From: Tommy Verrall Date: Tue, 8 Apr 2025 12:12:13 +0200 Subject: [PATCH 33/48] remove arg --- .github/workflows/publish-nym-wallet-win11.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/publish-nym-wallet-win11.yml b/.github/workflows/publish-nym-wallet-win11.yml index 9b243a33df..8e854599ec 100644 --- a/.github/workflows/publish-nym-wallet-win11.yml +++ b/.github/workflows/publish-nym-wallet-win11.yml @@ -106,7 +106,7 @@ jobs: SSL_COM_TOTP_SECRET: ${{ inputs.sign && secrets.SSL_COM_TOTP_SECRET }} run: | echo "Starting build process..." - yarn build -v + yarn build - name: Check bundle directory shell: bash From 08c09781c77f1a8cb92fa9140d667c407da0d660 Mon Sep 17 00:00:00 2001 From: Tommy Verrall Date: Tue, 8 Apr 2025 14:36:42 +0200 Subject: [PATCH 34/48] Fixing all yarn lint errors --- .../src/components/Accounts/AccountItem.tsx | 70 +- .../Accounts/modals/AccountsModal.tsx | 66 +- .../Bonding/forms/nym-node/NymNodeData.tsx | 1 + .../forms/nym-node/NymNodeSignature.tsx | 1 - .../components/Clipboard/ClipboardActions.tsx | 1 + .../Clipboard/ClipboardFormFields.tsx | 27 +- .../components/Delegation/DelegationList.tsx | 5 +- .../src/components/Send/SendInputModal.tsx | 110 ++- nym-wallet/src/requests/gatewayDetails.ts | 1 - nym-wallet/src/theme/NymWalletTheme.tsx | 2 +- nym-wallet/src/theme/index.tsx | 2 +- nym-wallet/src/theme/mui-theme.d.ts | 4 +- nym-wallet/src/theme/theme.tsx | 876 +++++++++--------- nym-wallet/src/utils/common.ts | 3 +- nym-wallet/src/utils/shellHelper.ts | 8 +- 15 files changed, 617 insertions(+), 560 deletions(-) diff --git a/nym-wallet/src/components/Accounts/AccountItem.tsx b/nym-wallet/src/components/Accounts/AccountItem.tsx index 92271a6a4b..e19cf7efd5 100644 --- a/nym-wallet/src/components/Accounts/AccountItem.tsx +++ b/nym-wallet/src/components/Accounts/AccountItem.tsx @@ -29,9 +29,9 @@ export const AccountItem = ({ const { selectedAccount, setDialogToDisplay, setAccountMnemonic, handleAccountToEdit } = useContext(AccountsContext); const { copy, copied } = useClipboard({ copiedTimeout: 1000 }); const theme = useTheme(); - + const isSelected = selectedAccount?.id === name; - + return ( {isSelected && ( - )} } > - {/* Account avatar with box wrapper to apply styling */} - ) => { e.stopPropagation(); @@ -176,4 +174,4 @@ export const AccountItem = ({ ); -}; \ No newline at end of file +}; diff --git a/nym-wallet/src/components/Accounts/modals/AccountsModal.tsx b/nym-wallet/src/components/Accounts/modals/AccountsModal.tsx index 964700e410..479335021e 100644 --- a/nym-wallet/src/components/Accounts/modals/AccountsModal.tsx +++ b/nym-wallet/src/components/Accounts/modals/AccountsModal.tsx @@ -2,7 +2,6 @@ import React, { useContext, useState } from 'react'; import { Box, Button, - Paper, Dialog, DialogActions, DialogContent, @@ -74,8 +73,8 @@ export const AccountsModal = () => { { - Switch between accounts - - { ))} - + - - - -