diff --git a/Cargo.lock b/Cargo.lock index 04e954f7..427d5878 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1978,16 +1978,6 @@ dependencies = [ "crossbeam-utils", ] -[[package]] -name = "crossbeam-deque" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" -dependencies = [ - "crossbeam-epoch", - "crossbeam-utils", -] - [[package]] name = "crossbeam-epoch" version = "0.9.18" @@ -6078,6 +6068,7 @@ version = "0.1.0" dependencies = [ "anyhow", "backoff", + "chrono", "cmake", "cxx", "cxx-build", @@ -6347,15 +6338,6 @@ dependencies = [ "instant", ] -[[package]] -name = "ntapi" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4" -dependencies = [ - "winapi", -] - [[package]] name = "nu-ansi-term" version = "0.46.0" @@ -7839,26 +7821,6 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" -[[package]] -name = "rayon" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" -dependencies = [ - "either", - "rayon-core", -] - -[[package]] -name = "rayon-core" -version = "1.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" -dependencies = [ - "crossbeam-deque", - "crossbeam-utils", -] - [[package]] name = "rcgen" version = "0.11.3" @@ -9829,6 +9791,7 @@ dependencies = [ "tempfile", "testcontainers", "thiserror 1.0.69", + "throttle", "time 0.3.41", "tokio", "tokio-tar", @@ -9980,20 +9943,6 @@ dependencies = [ "syn 2.0.104", ] -[[package]] -name = "sysinfo" -version = "0.32.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c33cd241af0f2e9e3b5c32163b873b29956890b5342e6745b917ce9d490f4af" -dependencies = [ - "core-foundation-sys", - "libc", - "memchr", - "ntapi", - "rayon", - "windows 0.57.0", -] - [[package]] name = "system-configuration" version = "0.6.1" @@ -10630,6 +10579,13 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "throttle" +version = "0.1.0" +dependencies = [ + "tracing", +] + [[package]] name = "tiff" version = "0.9.1" @@ -12412,7 +12368,6 @@ dependencies = [ "serde", "serde_json", "swap", - "sysinfo", "tauri", "tauri-build", "tauri-plugin-cli", @@ -12956,8 +12911,8 @@ dependencies = [ "webview2-com-sys", "windows 0.61.3", "windows-core 0.61.2", - "windows-implement 0.60.0", - "windows-interface 0.59.1", + "windows-implement", + "windows-interface", ] [[package]] @@ -13060,16 +13015,6 @@ dependencies = [ "windows-targets 0.52.6", ] -[[package]] -name = "windows" -version = "0.57.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12342cb4d8e3b046f3d80effd474a7a02447231330ef77d71daa6fbc40681143" -dependencies = [ - "windows-core 0.57.0", - "windows-targets 0.52.6", -] - [[package]] name = "windows" version = "0.61.3" @@ -13102,26 +13047,14 @@ dependencies = [ "windows-targets 0.52.6", ] -[[package]] -name = "windows-core" -version = "0.57.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2ed2439a290666cd67ecce2b0ffaad89c2a56b976b736e6ece670297897832d" -dependencies = [ - "windows-implement 0.57.0", - "windows-interface 0.57.0", - "windows-result 0.1.2", - "windows-targets 0.52.6", -] - [[package]] name = "windows-core" version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" dependencies = [ - "windows-implement 0.60.0", - "windows-interface 0.59.1", + "windows-implement", + "windows-interface", "windows-link", "windows-result 0.3.4", "windows-strings", @@ -13138,17 +13071,6 @@ dependencies = [ "windows-threading", ] -[[package]] -name = "windows-implement" -version = "0.57.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.104", -] - [[package]] name = "windows-implement" version = "0.60.0" @@ -13160,17 +13082,6 @@ dependencies = [ "syn 2.0.104", ] -[[package]] -name = "windows-interface" -version = "0.57.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.104", -] - [[package]] name = "windows-interface" version = "0.59.1" diff --git a/Cargo.toml b/Cargo.toml index 048b9310..5fc13258 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [workspace] resolver = "2" -members = [ "electrum-pool", "monero-rpc", "monero-rpc-pool", "monero-sys", "monero-seed", "src-tauri", "swap", "swap-env", "swap-fs", "swap-feed", "swap-serde"] +members = [ "electrum-pool", "monero-rpc", "monero-rpc-pool", "monero-sys", "monero-seed", "src-tauri", "swap", "swap-env", "swap-fs", "swap-feed", "swap-serde", "throttle"] [workspace.dependencies] anyhow = "1" @@ -11,17 +11,17 @@ futures = { version = "0.3", default-features = false, features = ["std"] } tracing = { version = "0.1", features = ["attributes"] } tracing-subscriber = { version = "0.3", default-features = false, features = ["fmt", "ansi", "env-filter", "time", "tracing-log", "json"] } bitcoin = { version = "0.32", features = ["rand", "serde"] } +hex = "0.4" +libp2p = { version = "0.53.2" } monero = { version = "0.12", features = ["serde_support"] } rand = "0.8" -uuid = { version = "1", features = ["v4"] } -typeshare = "1.0" -thiserror = "1" reqwest = { version = "0.12", default-features = false, features = ["json"] } rust_decimal = { version = "1", features = ["serde-float"] } rust_decimal_macros = "1" -libp2p = { version = "0.53.2" } +thiserror = "1" +typeshare = "1.0" url = { version = "2", features = ["serde"] } -hex = "0.4" +uuid = { version = "1", features = ["v4"] } [patch.crates-io] # patch until new release https://github.com/thomaseizinger/rust-jsonrpc-client/pull/51 diff --git a/justfile b/justfile index 7c30e612..19c1c8dc 100644 --- a/justfile +++ b/justfile @@ -26,7 +26,7 @@ test-ffi-address: # Start the Tauri app tauri: - cd src-tauri && cargo tauri dev --no-watch -- -- --testnet + cd src-tauri && cargo tauri dev --no-watch --verbose -- -- --testnet tauri-mainnet: cd src-tauri && cargo tauri dev --no-watch @@ -105,4 +105,4 @@ prepare_mac_os_brew_dependencies: # Takes a crate (e.g monero-rpc-pool) and uses code2prompt to copy to clipboard # E.g code2prompt . --exclude "*.lock" --exclude ".sqlx/*" --exclude "target" code2prompt_single_crate crate: - cd {{crate}} && code2prompt . --exclude "*.lock" --exclude ".sqlx/*" --exclude "target" \ No newline at end of file + cd {{crate}} && code2prompt . --exclude "*.lock" --exclude ".sqlx/*" --exclude "target" diff --git a/monero-harness/src/lib.rs b/monero-harness/src/lib.rs index b6610eec..639a1f84 100644 --- a/monero-harness/src/lib.rs +++ b/monero-harness/src/lib.rs @@ -504,11 +504,7 @@ impl MoneroWallet { /// Sweep multiple addresses with different ratios /// If the address is `None`, the address will be set to the primary address of the /// main wallet. - pub async fn sweep_multi( - &self, - addresses: &[Address], - ratios: &[f64], - ) -> Result { + pub async fn sweep_multi(&self, addresses: &[Address], ratios: &[f64]) -> Result { tracing::info!("`{}` sweeping multi ({:?})", self.name, ratios); self.balance().await?; diff --git a/monero-rpc-pool/src/database.rs b/monero-rpc-pool/src/database.rs index 9ff5ddb4..81a54c56 100644 --- a/monero-rpc-pool/src/database.rs +++ b/monero-rpc-pool/src/database.rs @@ -17,7 +17,7 @@ impl Database { info!("Created application data directory: {}", data_dir.display()); } - let db_path = data_dir.join("nodes_v2.db"); + let db_path = data_dir.join("nodes_v3.db"); info!("Using database at {}", db_path.display()); diff --git a/monero-rpc-pool/src/lib.rs b/monero-rpc-pool/src/lib.rs index e33cf479..b5557f05 100644 --- a/monero-rpc-pool/src/lib.rs +++ b/monero-rpc-pool/src/lib.rs @@ -39,6 +39,7 @@ use proxy::{proxy_handler, stats_handler}; #[derive(Clone)] pub struct AppState { pub node_pool: Arc, + pub http_client: reqwest::Client, } /// Manages background tasks for the RPC pool @@ -59,6 +60,12 @@ pub struct ServerInfo { pub host: String, } +impl Into for ServerInfo { + fn into(self) -> String { + format!("http://{}:{}", self.host, self.port) + } +} + async fn create_app_with_receiver( config: Config, network: Network, @@ -97,7 +104,20 @@ async fn create_app_with_receiver( status_update_handle, }; - let app_state = AppState { node_pool }; + // Create shared HTTP client with connection pooling and keep-alive + let http_client = reqwest::Client::builder() + .timeout(std::time::Duration::from_secs(30)) + .danger_accept_invalid_certs(true) + .pool_max_idle_per_host(10) + .pool_idle_timeout(std::time::Duration::from_secs(90)) + .tcp_keepalive(std::time::Duration::from_secs(60)) + .build() + .expect("Failed to create HTTP client"); + + let app_state = AppState { + node_pool, + http_client, + }; // Build the app let app = Router::new() diff --git a/monero-rpc-pool/src/proxy.rs b/monero-rpc-pool/src/proxy.rs index f3962b63..3103a04a 100644 --- a/monero-rpc-pool/src/proxy.rs +++ b/monero-rpc-pool/src/proxy.rs @@ -11,11 +11,16 @@ use uuid::Uuid; use crate::AppState; +fn display_node(node: &(String, String, i64)) -> String { + format!("{}://{}:{}", node.0, node.1, node.2) +} + #[derive(Debug, Clone)] enum HandlerError { NoNodes, PoolError(String), RequestError(String), + JsonRpcError(String), AllRequestsFailed(Vec<(String, String)>), } @@ -25,6 +30,7 @@ impl std::fmt::Display for HandlerError { HandlerError::NoNodes => write!(f, "No nodes available"), HandlerError::PoolError(msg) => write!(f, "Pool error: {}", msg), HandlerError::RequestError(msg) => write!(f, "Request error: {}", msg), + HandlerError::JsonRpcError(msg) => write!(f, "JSON-RPC error: {}", msg), HandlerError::AllRequestsFailed(errors) => { write!(f, "All requests failed: [")?; for (i, (node, error)) in errors.iter().enumerate() { @@ -60,17 +66,13 @@ fn extract_jsonrpc_method(body: &[u8]) -> Option { } async fn raw_http_request( + client: &reqwest::Client, node_url: (String, String, i64), path: &str, method: &str, headers: &HeaderMap, body: Option<&[u8]>, ) -> Result { - let client = reqwest::Client::builder() - .timeout(std::time::Duration::from_secs(30)) - .build() - .map_err(|e| HandlerError::RequestError(format!("{:#?}", e)))?; - let (scheme, host, port) = &node_url; let url = format!("{}://{}:{}{}", scheme, host, port, path); @@ -172,6 +174,7 @@ async fn record_failure(state: &AppState, scheme: &str, host: &str, port: i64) { } async fn single_raw_request( + client: &reqwest::Client, node_url: (String, String, i64), path: &str, method: &str, @@ -180,7 +183,7 @@ async fn single_raw_request( ) -> Result<(Response, (String, String, i64), f64), HandlerError> { let start_time = Instant::now(); - match raw_http_request(node_url.clone(), path, method, headers, body).await { + match raw_http_request(client, node_url.clone(), path, method, headers, body).await { Ok(response) => { let elapsed = start_time.elapsed(); let latency_ms = elapsed.as_millis() as f64; @@ -195,7 +198,7 @@ async fn single_raw_request( .map_err(|e| HandlerError::RequestError(format!("{:#?}", e)))?; if is_jsonrpc_error(&body_bytes) { - return Err(HandlerError::RequestError("JSON-RPC error".to_string())); + return Err(HandlerError::JsonRpcError("JSON-RPC error".to_string())); } // Reconstruct response with the body we consumed @@ -225,6 +228,7 @@ async fn sequential_requests( body: Option<&[u8]>, ) -> Result { const POOL_SIZE: usize = 20; + const MAX_JSONRPC_ERRORS: usize = 3; // Extract JSON-RPC method for better logging let jsonrpc_method = if path == "/json_rpc" { @@ -238,7 +242,7 @@ async fn sequential_requests( }; let mut tried_nodes = 0; - let mut collected_errors: Vec<(String, String)> = Vec::new(); + let mut collected_errors: Vec<((String, String, i64), HandlerError)> = Vec::new(); // Get the pool of nodes let available_pool = { @@ -283,7 +287,16 @@ async fn sequential_requests( ), } - match single_raw_request(node.clone(), path, method, headers, body).await { + match single_raw_request( + &state.http_client, + node.clone(), + path, + method, + headers, + body, + ) + .await + { Ok((response, winning_node, latency_ms)) => { let (scheme, host, port) = &winning_node; let winning_node_display = format!("{}://{}:{}", scheme, host, port); @@ -304,24 +317,61 @@ async fn sequential_requests( return Ok(response); } Err(e) => { - collected_errors.push((node_display.clone(), e.to_string())); + collected_errors.push((node.clone(), e.clone())); debug!( - "Request failed with node {} with error {} - trying next node...", + "Request failed with node {}: {} - checking if we should fail fast...", node_display, e ); - record_failure(state, &node.0, &node.1, node.2).await; + // Count JSON-RPC errors by checking through all collected errors (type-safe) + let jsonrpc_error_count = collected_errors + .iter() + .filter(|(_, error)| matches!(error, HandlerError::JsonRpcError(_))) + .count(); + + // Fail fast after MAX_JSONRPC_ERRORS JSON-RPC errors + if jsonrpc_error_count >= MAX_JSONRPC_ERRORS { + match &jsonrpc_method { + Some(rpc_method) => error!( + "Failing fast after {} JSON-RPC errors for {} request (JSON-RPC: {}). These are likely request-specific issues that won't resolve on other servers.", + jsonrpc_error_count, method, rpc_method + ), + None => error!( + "Failing fast after {} JSON-RPC errors for {} request. These are likely request-specific issues that won't resolve on other servers.", + jsonrpc_error_count, method + ), + } + + // Record all non-JSON-RPC errors as failures + for (node, error) in collected_errors.iter() { + if !matches!(error, HandlerError::JsonRpcError(_)) { + record_failure(state, &node.0, &node.1, node.2).await; + } + } + + return Err(HandlerError::AllRequestsFailed( + collected_errors + .into_iter() + .map(|(node, error)| (display_node(&node), error.to_string())) + .collect(), + )); + } continue; } } } + // Record failures for all nodes that were tried + for (node, _) in collected_errors.iter() { + record_failure(state, &node.0, &node.1, node.2).await; + } + // Log detailed error information let detailed_errors: Vec = collected_errors .iter() - .map(|(node, error)| format!("{}: {}", node, error)) + .map(|(node, error)| format!("{}: {}", display_node(node), error)) .collect(); match &jsonrpc_method { @@ -340,7 +390,12 @@ async fn sequential_requests( ), } - Err(HandlerError::AllRequestsFailed(collected_errors)) + Err(HandlerError::AllRequestsFailed( + collected_errors + .into_iter() + .map(|(node, error)| (display_node(&node), error.to_string())) + .collect(), + )) } /// Forward a request to the node pool, returning either a successful response or a simple @@ -400,6 +455,15 @@ async fn proxy_request( } }) } + HandlerError::JsonRpcError(msg) => { + json!({ + "error": "JSON-RPC error", + "details": { + "type": "JsonRpcError", + "message": msg + } + }) + } }; Response::builder() diff --git a/monero-sys/.sqlx/query-7e58428584d28a238ab37a83662b88afcef6fc5246f11c85a35869f79da61c34.json b/monero-sys/.sqlx/query-7e58428584d28a238ab37a83662b88afcef6fc5246f11c85a35869f79da61c34.json new file mode 100644 index 00000000..6be7d85e --- /dev/null +++ b/monero-sys/.sqlx/query-7e58428584d28a238ab37a83662b88afcef6fc5246f11c85a35869f79da61c34.json @@ -0,0 +1,23 @@ +{ + "db_name": "SQLite", + "query": "\n SELECT wallet_path, last_opened_at\n FROM recent_wallets \n ORDER BY last_opened_at DESC\n LIMIT ?\n ", + "describe": { + "columns": [ + { + "name": "wallet_path", + "ordinal": 0, + "type_info": "Text" + }, + { + "name": "last_opened_at", + "ordinal": 1, + "type_info": "Text" + } + ], + "parameters": { + "Right": 1 + }, + "nullable": [false, false] + }, + "hash": "7e58428584d28a238ab37a83662b88afcef6fc5246f11c85a35869f79da61c34" +} diff --git a/monero-sys/.sqlx/query-a679f789c90ede34cd840d23d90087520dcf1777fdf4cc3ed7aab0c9d70d060c.json b/monero-sys/.sqlx/query-a679f789c90ede34cd840d23d90087520dcf1777fdf4cc3ed7aab0c9d70d060c.json new file mode 100644 index 00000000..ba272ccc --- /dev/null +++ b/monero-sys/.sqlx/query-a679f789c90ede34cd840d23d90087520dcf1777fdf4cc3ed7aab0c9d70d060c.json @@ -0,0 +1,12 @@ +{ + "db_name": "SQLite", + "query": "\n INSERT INTO recent_wallets (wallet_path, last_opened_at)\n VALUES (?, ?)\n ON CONFLICT(wallet_path) DO UPDATE SET last_opened_at = excluded.last_opened_at\n ", + "describe": { + "columns": [], + "parameters": { + "Right": 2 + }, + "nullable": [] + }, + "hash": "a679f789c90ede34cd840d23d90087520dcf1777fdf4cc3ed7aab0c9d70d060c" +} diff --git a/monero-sys/Cargo.toml b/monero-sys/Cargo.toml index 94d62bf8..213f7a85 100644 --- a/monero-sys/Cargo.toml +++ b/monero-sys/Cargo.toml @@ -6,6 +6,7 @@ edition = "2021" [dependencies] anyhow = { workspace = true } backoff = { version = "0.4.0", features = ["futures", "tokio"] } +chrono = { version = "0.4", features = ["serde"] } cxx = "1.0.137" monero = { workspace = true } serde = { workspace = true } diff --git a/monero-sys/migrations/20250710170522_create_recent_wallets.sql b/monero-sys/migrations/20250710170522_create_recent_wallets.sql new file mode 100644 index 00000000..64f75e9c --- /dev/null +++ b/monero-sys/migrations/20250710170522_create_recent_wallets.sql @@ -0,0 +1,9 @@ +-- Add migration script here + +CREATE TABLE recent_wallets ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + wallet_path TEXT UNIQUE NOT NULL, + last_opened_at TEXT NOT NULL +); + +CREATE INDEX idx_recent_wallets_last_opened ON recent_wallets(last_opened_at DESC); diff --git a/monero-sys/regenerate_sqlx_cache.sh b/monero-sys/regenerate_sqlx_cache.sh new file mode 100755 index 00000000..be4dd69f --- /dev/null +++ b/monero-sys/regenerate_sqlx_cache.sh @@ -0,0 +1,56 @@ +#!/bin/bash + +# regenerate_sqlx_cache.sh +# +# Script to regenerate SQLx query cache for monero-rpc-pool +# +# This script: +# 1. Creates a temporary SQLite database in a temp directory +# 2. Runs all database migrations to set up the schema +# 3. Regenerates the SQLx query cache (.sqlx directory) +# 4. Cleans up temporary files automatically +# +# Usage: +# ./regenerate_sqlx_cache.sh +# +# Requirements: +# - cargo and sqlx-cli must be installed +# - Must be run from the monero-rpc-pool directory +# - migrations/ directory must exist with valid migration files +# +# The generated .sqlx directory should be committed to version control +# to enable offline compilation without requiring DATABASE_URL. + +set -e # Exit on any error + +echo "πŸ”„ Regenerating SQLx query cache..." + +# Create a temporary directory for the database +TEMP_DIR=$(mktemp -d) +TEMP_DB="$TEMP_DIR/temp_sqlx_cache.sqlite" +DATABASE_URL="sqlite:$TEMP_DB" + +echo "πŸ“ Using temporary database: $TEMP_DB" + +# Function to cleanup on exit +cleanup() { + echo "🧹 Cleaning up temporary files..." + rm -rf "$TEMP_DIR" +} +trap cleanup EXIT + +# Export DATABASE_URL for sqlx commands +export DATABASE_URL + +echo "πŸ—„οΈ Creating database..." +cargo sqlx database create + +echo "πŸ”„ Running migrations..." +cargo sqlx migrate run + +echo "⚑ Preparing SQLx query cache..." +cargo sqlx prepare + +echo "βœ… SQLx query cache regenerated successfully!" +echo "πŸ“ The .sqlx directory has been updated with the latest query metadata." +echo "πŸ’‘ Make sure to commit the .sqlx directory to version control." \ No newline at end of file diff --git a/monero-sys/src/bridge.h b/monero-sys/src/bridge.h index 221a24e6..4bc2d739 100644 --- a/monero-sys/src/bridge.h +++ b/monero-sys/src/bridge.h @@ -16,7 +16,9 @@ * - CXX doesn't support static methods as yet, so we define free functions here that * simply call the appropriate static methods. * - CXX also doesn't support returning strings by value from C++ to Rust, so we wrap - * those in a unique_ptr. + * those in a unique_ptr/shared_ptr. + * ATTENTION: unique_ptr will delete the object on drop, + * verify that you actually OWN the object before wrapping it in a unique_ptr. Use shared_ptr otherwise * - CXX doesn't support optional arguments, so we make thin wrapper functions that either * take the argument or not. * @@ -58,6 +60,26 @@ namespace Monero auto addr = wallet.address(account_index, address_index); return std::make_unique(addr); } + + inline void rescanBlockchainAsync(Wallet &wallet) + { + wallet.rescanBlockchainAsync(); + } + + inline void pauseRefresh(Wallet &wallet) + { + wallet.pauseRefresh(); + } + + inline void stop(Wallet &wallet) + { + wallet.stop(); + } + + inline void startRefresh(Wallet &wallet) + { + wallet.startRefresh(); + } /** * Same as for [`address`] @@ -223,6 +245,16 @@ namespace Monero return std::make_unique>(tx.txid()); } + inline uint64_t pendingTransactionFee(const PendingTransaction &tx) + { + return tx.fee(); + } + + inline uint64_t pendingTransactionAmount(const PendingTransaction &tx) + { + return tx.amount(); + } + inline std::unique_ptr walletFilename(const Wallet &wallet) { return std::make_unique(wallet.filename()); @@ -234,6 +266,156 @@ namespace Monero { v.push_back(s); } + + /** + * Get the hash of a transaction from TransactionInfo. + */ + inline std::unique_ptr transactionInfoHash(const TransactionInfo &tx_info) + { + return std::make_unique(tx_info.hash()); + } + + /** + * Get the timestamp of a transaction from TransactionInfo. + */ + inline uint64_t transactionInfoTimestamp(const TransactionInfo &tx_info) + { + return static_cast(tx_info.timestamp()); + } + + // bridge.h +#pragma once +#include +#include +#include "wallet/api/wallet2_api.h" + + +using CB_StringU64 = uintptr_t; +using CB_U64 = uintptr_t; +using CB_Void = uintptr_t; +using CB_Reorg = uintptr_t; +using CB_String = uintptr_t; +using CB_GetPassword = uintptr_t; + +class FunctionBasedListener final : public Monero::WalletListener { +public: + FunctionBasedListener( + CB_StringU64 on_spent, + CB_StringU64 on_received, + CB_StringU64 on_unconfirmed_received, + CB_U64 on_new_block, + CB_Void on_updated, + CB_Void on_refreshed, + CB_Reorg on_reorg, + CB_String on_pool_tx_removed, + CB_GetPassword on_get_password) + : + on_spent_(on_spent), + on_received_(on_received), + on_unconfirmed_received_(on_unconfirmed_received), + on_new_block_(on_new_block), + on_updated_(on_updated), + on_refreshed_(on_refreshed), + on_reorg_(on_reorg), + on_pool_tx_removed_(on_pool_tx_removed), + on_get_password_(on_get_password) {} + + void moneySpent(const std::string& txid, uint64_t amt) override { + if (on_spent_) { + auto* spent = reinterpret_cast(on_spent_); + spent(txid, amt); + } + } + + void moneyReceived(const std::string& txid, uint64_t amt) override + { if (on_received_) { + auto* received = reinterpret_cast(on_received_); + received(txid, amt); + } + } + + void unconfirmedMoneyReceived(const std::string& txid, uint64_t amt) override + { if (on_unconfirmed_received_) { + auto* unconfirmed_received = reinterpret_cast(on_unconfirmed_received_); + unconfirmed_received(txid, amt); + } + } + + void newBlock(uint64_t h) override + { if (on_new_block_) { + auto* new_block = reinterpret_cast(on_new_block_); + new_block(h); + } + } + + void updated() override + { + if (on_updated_) { + auto* updated = reinterpret_cast(on_updated_); + updated(); + } + } + + void refreshed() override + { if (on_refreshed_) { + auto* refreshed = reinterpret_cast(on_refreshed_); + refreshed(); + } + } + + void onReorg(uint64_t h, uint64_t d, size_t t) override + { if (on_reorg_) { + auto* reorg = reinterpret_cast(on_reorg_); + reorg(h, d, t); + } + } + + void onPoolTxRemoved(const std::string& txid) override + { if (on_pool_tx_removed_) { + auto* pool_tx_removed = reinterpret_cast(on_pool_tx_removed_); + pool_tx_removed(txid); + } + } + + optional onGetPassword(const char* reason) override { + if (on_get_password_) { + auto* get_password = reinterpret_cast(on_get_password_); + return std::string(get_password(reason)); + } + return optional(); + } + +private: + CB_StringU64 on_spent_; + CB_StringU64 on_received_; + CB_StringU64 on_unconfirmed_received_; + CB_U64 on_new_block_; + CB_Void on_updated_; + CB_Void on_refreshed_; + CB_Reorg on_reorg_; + CB_String on_pool_tx_removed_; + CB_GetPassword on_get_password_; +}; + +extern "C" { + WalletListener* create_listener( + CB_StringU64 on_spent, + CB_StringU64 on_received, + CB_StringU64 on_unconfirmed_received, + CB_U64 on_new_block, + CB_Void on_updated, + CB_Void on_refreshed, + CB_Reorg on_reorg, + CB_String on_pool_tx_removed, + CB_GetPassword on_get_password) + { + return new FunctionBasedListener( + on_spent,on_received,on_unconfirmed_received,on_new_block, + on_updated,on_refreshed,on_reorg,on_pool_tx_removed,on_get_password); + } + + void destroy_listener(FunctionBasedListener* p) { delete p; } +} } #include "easylogging++.h" @@ -359,3 +541,64 @@ using StringMap = std::map; using StringVec = std::vector; static std::pair _monero_sys_pair_instantiation; + +namespace Monero { + +// Adapter class that forwards Monero::WalletListener callbacks to Rust +class RustListenerAdapter final : public Monero::WalletListener { +public: + explicit RustListenerAdapter(rust::Box listener) + : inner_(std::move(listener)) {} + + // --- Required overrides ------------------------------------------------ + void moneySpent(const std::string &txid, uint64_t amount) override { + wallet_listener::money_spent(*inner_, txid, amount); + } + + void moneyReceived(const std::string &txid, uint64_t amount) override { + wallet_listener::money_received(*inner_, txid, amount); + } + + void unconfirmedMoneyReceived(const std::string &txid, uint64_t amount) override { + wallet_listener::unconfirmed_money_received(*inner_, txid, amount); + } + + void newBlock(uint64_t height) override { + wallet_listener::new_block(*inner_, height); + } + + void updated() override { + wallet_listener::updated(*inner_); + } + + void refreshed() override { + wallet_listener::refreshed(*inner_); + } + + void onReorg(std::uint64_t height, std::uint64_t blocks_detached, std::size_t transfers_detached) override { + wallet_listener::on_reorg(*inner_, height, blocks_detached, transfers_detached); + } + + optional onGetPassword(const char * /*reason*/) override { + return optional(); // Not implemented + } + + void onPoolTxRemoved(const std::string &txid) override { + wallet_listener::pool_tx_removed(*inner_, txid); + } + +private: + rust::Box inner_; +}; + +} // namespace Monero + +namespace wallet_listener { + Monero::WalletListener* create_rust_listener_adapter(rust::Box listener) { + return new Monero::RustListenerAdapter(std::move(listener)); + } + + void destroy_rust_listener_adapter(Monero::WalletListener* ptr) { + delete ptr; + } +} diff --git a/monero-sys/src/bridge.rs b/monero-sys/src/bridge.rs index de136e81..a87c69cf 100644 --- a/monero-sys/src/bridge.rs +++ b/monero-sys/src/bridge.rs @@ -54,10 +54,11 @@ pub mod ffi { /// A pending transaction. type PendingTransaction; - /// A wallet listener. - /// - /// Can be attached to a wallet and will get notified upon specific events. - type WalletListener; + /// A struct containing transaction history. + type TransactionHistory; + + /// A struct containing a single transaction. + type TransactionInfo; /// Get the wallet manager. fn getWalletManager() -> Result<*mut WalletManager>; @@ -100,6 +101,8 @@ pub mod ffi { seed_offset: &CxxString, ) -> Result<*mut Wallet>; + type WalletListener; + ///virtual Wallet * openWallet(const std::string &path, const std::string &password, NetworkType nettype, uint64_t kdf_rounds = 1, WalletListener * listener = nullptr) = 0; unsafe fn openWallet( self: Pin<&mut WalletManager>, @@ -117,9 +120,21 @@ pub mod ffi { store: bool, ) -> Result; + /// Store the wallet state. + fn store(self: Pin<&mut Wallet>, path: &CxxString) -> Result; + /// Check whether a wallet exists at the given path. fn walletExists(self: Pin<&mut WalletManager>, path: &CxxString) -> Result; + /// Verify the password for a wallet at the given path. + fn verifyWalletPassword( + self: &WalletManager, + keys_file_name: &CxxString, + password: &CxxString, + no_spend_key: bool, + kdf_rounds: u64, + ) -> Result; + /// Set the address of the remote node ("daemon"). fn setDaemonAddress(self: Pin<&mut WalletManager>, address: &CxxString) -> Result<()>; @@ -160,6 +175,9 @@ pub mod ffi { /// Get the seed of the wallet. fn walletSeed(wallet: &Wallet, seed_offset: &CxxString) -> Result>; + /// Set the seed language of the wallet. + fn setSeedLanguage(self: Pin<&mut Wallet>, language: &CxxString) -> Result<()>; + /// Get the wallet creation height. fn getRefreshFromBlockHeight(self: &Wallet) -> Result; @@ -181,6 +199,9 @@ pub mod ffi { /// Get the current blockchain height. fn blockChainHeight(self: &Wallet) -> Result; + /// Set a listener to the wallet. + unsafe fn setListener(self: Pin<&mut Wallet>, listener: *mut WalletListener) -> Result<()>; + /// Get the daemon's blockchain height. fn daemonBlockChainTargetHeight(self: &Wallet) -> Result; @@ -199,6 +220,17 @@ pub mod ffi { /// Force a specific restore height. fn setRefreshFromBlockHeight(self: Pin<&mut Wallet>, height: u64) -> Result<()>; + fn getBlockchainHeightByDate(self: &Wallet, year: u16, month: u8, day: u8) -> Result; + + /// Rescan the blockchain asynchronously. + fn rescanBlockchainAsync(self: Pin<&mut Wallet>); + + /// Pause the background refresh. + fn pauseRefresh(self: Pin<&mut Wallet>); + + /// Stop the background refresh once (doesn't stop background refresh thread). + fn stop(self: Pin<&mut Wallet>); + /// Set whether to allow mismatched daemon versions. fn setAllowMismatchedDaemonVersion( self: Pin<&mut Wallet>, @@ -255,6 +287,12 @@ pub mod ffi { tx: &PendingTransaction, ) -> Result>>; + /// Get the fee of a pending transaction. + fn pendingTransactionFee(tx: &PendingTransaction) -> Result; + + /// Get the amount of a pending transaction. + fn pendingTransactionAmount(tx: &PendingTransaction) -> Result; + /// Get the transaction key (r) for a given txid. fn walletGetTxKey(wallet: &Wallet, txid: &CxxString) -> Result>; @@ -271,6 +309,36 @@ pub mod ffi { tx: *mut PendingTransaction, ) -> Result<()>; + /// Get the transaction history. + fn history(self: Pin<&mut Wallet>) -> Result<*mut TransactionHistory>; + + /// Get the transaction history count. + fn count(self: &TransactionHistory) -> i32; + + /// Get a transaction from the history by index. + fn transaction(self: &TransactionHistory, index: i32) -> *mut TransactionInfo; + + /// Refresh the transaction history so it contains the latest transactions. + fn refresh(self: Pin<&mut TransactionHistory>) -> Result<()>; + + /// Get the amount of the transaction. + fn amount(self: &TransactionInfo) -> u64; + + /// Get the fee of the transaction. + fn fee(self: &TransactionInfo) -> u64; + + /// Get the confirmations of the transaction. + fn confirmations(self: &TransactionInfo) -> u64; + + /// Get the direction of the transaction. + fn direction(self: &TransactionInfo) -> i32; + + /// Get the hash of the transaction. + fn transactionInfoHash(tx_info: &TransactionInfo) -> UniquePtr; + + /// Get the timestamp of the transaction. + fn transactionInfoTimestamp(tx_info: &TransactionInfo) -> u64; + /// Sign a message with the wallet's private key. fn signMessage( wallet: Pin<&mut Wallet>, @@ -334,6 +402,214 @@ pub mod log { } } +/// Wallet listener bridge using cxx's virtual table approach +#[cxx::bridge(namespace = "wallet_listener")] +pub mod wallet_listener { + extern "Rust" { + // Opaque Rust type owned by C++ + type WalletListenerBox; + + // Callback methods invoked from C++ + fn money_spent(listener: &mut WalletListenerBox, txid: &CxxString, amount: u64); + fn money_received(listener: &mut WalletListenerBox, txid: &CxxString, amount: u64); + fn unconfirmed_money_received( + listener: &mut WalletListenerBox, + txid: &CxxString, + amount: u64, + ); + fn new_block(listener: &mut WalletListenerBox, height: u64); + fn updated(listener: &mut WalletListenerBox); + fn refreshed(listener: &mut WalletListenerBox); + fn on_reorg( + listener: &mut WalletListenerBox, + height: u64, + blocks_detached: u64, + transfers_detached: usize, + ); + fn pool_tx_removed(listener: &mut WalletListenerBox, txid: &CxxString); + } + + unsafe extern "C++" { + include!("wallet/api/wallet2_api.h"); + include!("bridge.h"); + + // The C++ WalletListener type lives in the Monero namespace. + #[namespace = "Monero"] + #[rust_name = "MoneroWalletListener"] + type WalletListener; + + // Functions implemented in bridge.h that create / destroy the adapter. + #[namespace = "wallet_listener"] + fn create_rust_listener_adapter( + listener: Box, + ) -> *mut MoneroWalletListener; + #[namespace = "wallet_listener"] + unsafe fn destroy_rust_listener_adapter(ptr: *mut MoneroWalletListener); + } +} + +// Callback functions called from C++ - these bridge the C++ callbacks to Rust trait methods +pub fn money_spent(listener: &mut WalletListenerBox, txid: &CxxString, amount: u64) { + listener.on_money_spent(&txid.to_string(), amount); +} + +pub fn money_received(listener: &mut WalletListenerBox, txid: &CxxString, amount: u64) { + listener.on_money_received(&txid.to_string(), amount); +} + +pub fn unconfirmed_money_received(listener: &mut WalletListenerBox, txid: &CxxString, amount: u64) { + listener.on_unconfirmed_money_received(&txid.to_string(), amount); +} + +pub fn new_block(listener: &mut WalletListenerBox, height: u64) { + listener.on_new_block(height); +} + +pub fn updated(listener: &mut WalletListenerBox) { + listener.on_updated(); +} + +pub fn refreshed(listener: &mut WalletListenerBox) { + listener.on_refreshed(); +} + +pub fn on_reorg( + listener: &mut WalletListenerBox, + height: u64, + blocks_detached: u64, + transfers_detached: usize, +) { + listener.on_reorg(height, blocks_detached, transfers_detached); +} + +pub fn pool_tx_removed(listener: &mut WalletListenerBox, txid: &CxxString) { + listener.on_pool_tx_removed(&txid.to_string()); +} + +/// Trait for wallet event listeners - allows custom callback implementations +pub trait WalletEventListener: Send + Sync { + fn on_money_spent(&self, txid: &str, amount: u64); + fn on_money_received(&self, txid: &str, amount: u64); + fn on_unconfirmed_money_received(&self, txid: &str, amount: u64); + fn on_new_block(&self, height: u64); + fn on_updated(&self); + fn on_refreshed(&self); + fn on_reorg(&self, height: u64, blocks_detached: u64, transfers_detached: usize); + fn on_pool_tx_removed(&self, txid: &str); +} + +/// A wrapper around Box because CXX doesn't support trait objects (yet). +pub struct WalletListenerBox { + inner: Box, +} + +impl WalletListenerBox { + /// Create a new wrapper around any WalletEventListener implementation + pub fn new(listener: Box) -> Self { + WalletListenerBox { inner: listener } + } + + /// Create a new boxed wrapper around any WalletEventListener implementation + pub fn new_boxed(listener: Box) -> Box { + Box::new(Self::new(listener)) + } +} + +impl WalletEventListener for WalletListenerBox { + fn on_money_spent(&self, txid: &str, amount: u64) { + self.inner.on_money_spent(txid, amount); + } + + fn on_money_received(&self, txid: &str, amount: u64) { + self.inner.on_money_received(txid, amount); + } + + fn on_unconfirmed_money_received(&self, txid: &str, amount: u64) { + self.inner.on_unconfirmed_money_received(txid, amount); + } + + fn on_new_block(&self, height: u64) { + self.inner.on_new_block(height); + } + + fn on_updated(&self) { + self.inner.on_updated(); + } + + fn on_refreshed(&self) { + self.inner.on_refreshed(); + } + + fn on_reorg(&self, height: u64, blocks_detached: u64, transfers_detached: usize) { + self.inner + .on_reorg(height, blocks_detached, transfers_detached); + } + + fn on_pool_tx_removed(&self, txid: &str) { + self.inner.on_pool_tx_removed(txid); + } +} + +/// Listener implementation that logs all wallet events using tracing with filename context. +pub struct TraceListener { + /// The wallet filename for logging context + pub filename: String, +} + +impl TraceListener { + /// Creates a new TraceListener with a filename for logging context. + pub fn new(filename: String) -> Self { + TraceListener { filename } + } +} + +impl WalletEventListener for TraceListener { + fn on_money_spent(&self, txid: &str, amount: u64) { + tracing::info!( + wallet = self.filename, + "Money spent: {} XMR in transaction {}", + amount as f64 / 1e12, + txid + ); + } + + fn on_money_received(&self, txid: &str, amount: u64) { + tracing::info!( + wallet = self.filename, + "Money received: {} XMR in transaction {}", + amount as f64 / 1e12, + txid + ); + } + + fn on_unconfirmed_money_received(&self, txid: &str, amount: u64) { + tracing::info!( + wallet = self.filename, + "Unconfirmed money received: {} XMR in transaction {}", + amount as f64 / 1e12, + txid + ); + } + + fn on_new_block(&self, _height: u64) {} + + fn on_updated(&self) {} + + fn on_refreshed(&self) {} + + fn on_reorg(&self, height: u64, blocks_detached: u64, transfers_detached: usize) { + tracing::warn!( + wallet = self.filename, + "Blockchain reorganization at height {}: {} blocks detached, {} transfers detached", + height, + blocks_detached, + transfers_detached + ); + } + + fn on_pool_tx_removed(&self, _txid: &str) {} +} + /// This is the actual rust function that forwards the c++ log messages to tracing. /// It is called every time C++ issues a log message. /// diff --git a/monero-sys/src/database.rs b/monero-sys/src/database.rs new file mode 100644 index 00000000..d1089bf0 --- /dev/null +++ b/monero-sys/src/database.rs @@ -0,0 +1,86 @@ +use std::path::PathBuf; + +use anyhow::Result; +use chrono::{DateTime, Utc}; +use serde::{Deserialize, Serialize}; +use sqlx::SqlitePool; +use tracing::info; + +#[derive(Clone)] +pub struct Database { + pub pool: SqlitePool, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct RecentWallet { + pub wallet_path: String, + pub last_opened_at: DateTime, +} + +impl Database { + pub async fn new(data_dir: PathBuf) -> Result { + if !data_dir.exists() { + std::fs::create_dir_all(&data_dir)?; + info!("Created wallet database directory: {}", data_dir.display()); + } + + let db_path = data_dir.join("recent_wallets.db"); + let database_url = format!("sqlite:{}?mode=rwc", db_path.display()); + let pool = SqlitePool::connect(&database_url).await?; + + let db = Self { pool }; + db.migrate().await?; + + Ok(db) + } + + async fn migrate(&self) -> Result<()> { + sqlx::migrate!("./migrations").run(&self.pool).await?; + info!("Recent wallets database migration completed"); + Ok(()) + } + + /// Record that a wallet was accessed + pub async fn record_wallet_access(&self, wallet_path: &str) -> Result<()> { + let now = Utc::now().to_rfc3339(); + + sqlx::query!( + r#" + INSERT INTO recent_wallets (wallet_path, last_opened_at) + VALUES (?, ?) + ON CONFLICT(wallet_path) DO UPDATE SET last_opened_at = excluded.last_opened_at + "#, + wallet_path, + now + ) + .execute(&self.pool) + .await?; + + Ok(()) + } + + /// Get recently opened wallets, most recent first + pub async fn get_recent_wallets(&self, limit: i64) -> Result> { + let rows = sqlx::query!( + r#" + SELECT wallet_path, last_opened_at + FROM recent_wallets + ORDER BY last_opened_at DESC + LIMIT ? + "#, + limit + ) + .fetch_all(&self.pool) + .await?; + + let wallets: Vec = rows + .into_iter() + .map(|row| RecentWallet { + wallet_path: row.wallet_path, + last_opened_at: row.last_opened_at.parse().unwrap_or_else(|_| Utc::now()), + }) + .collect(); + + Ok(wallets) + } +} diff --git a/monero-sys/src/lib.rs b/monero-sys/src/lib.rs index 7e002455..5f00fe0c 100644 --- a/monero-sys/src/lib.rs +++ b/monero-sys/src/lib.rs @@ -11,28 +11,52 @@ //! internally communicate with the wallet thread. mod bridge; +pub mod database; +pub use bridge::wallet_listener; +pub use bridge::{TraceListener, WalletEventListener, WalletListenerBox}; +pub use database::{Database, RecentWallet}; + +use std::sync::{Arc, Mutex}; use std::{ - any::Any, cmp::Ordering, fmt::Display, ops::Deref, path::PathBuf, pin::Pin, str::FromStr, - time::Duration, + any::Any, cmp::Ordering, collections::HashMap, fmt::Display, future::Future, ops::Deref, + path::PathBuf, pin::Pin, str::FromStr, time::Duration, }; use anyhow::{anyhow, bail, Context, Result}; use backoff::{future::retry_notify, retry_notify as blocking_retry_notify}; -use cxx::{let_cxx_string, CxxString, CxxVector, UniquePtr}; +use cxx::{let_cxx_string, CxxString, CxxVector, Exception, UniquePtr}; use monero::Amount; +use serde::{Deserialize, Serialize}; use tokio::sync::{ mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender}, oneshot, }; -use bridge::ffi; +use bridge::ffi::{self, TransactionHistory}; +use typeshare::typeshare; +use uuid::Uuid; + +/// Approval callback for transactions +/// The callback receives (txid, amount, fee) and returns whether to proceed with the transaction +pub type ApprovalCallback = Arc< + dyn Fn(String, monero::Amount, monero::Amount) -> Pin + Send>> + + Send + + Sync, +>; /// A handle which can communicate with the wallet thread via channels. +#[derive(Clone)] pub struct WalletHandle { call_sender: UnboundedSender, } +impl WalletHandle { + fn new(call_sender: UnboundedSender) -> Self { + Self { call_sender } + } +} + impl std::fmt::Display for WalletHandle { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "WalletHandle") @@ -52,11 +76,13 @@ pub struct Wallet { wallet: FfiWallet, manager: WalletManager, call_receiver: UnboundedReceiver, + pending_transactions: HashMap, } /// A function call to be executed on the wallet and a channel to send the result back. struct Call { - function: Box AnyBox + Send>, + function: + Box) -> AnyBox + Send>, sender: oneshot::Sender, } @@ -76,6 +102,7 @@ struct RawWalletManager { /// A single Monero wallet. pub struct FfiWallet { inner: RawWallet, + listeners: Arc>>>, } /// This is our own wrapper around a raw C++ wallet pointer. @@ -123,9 +150,39 @@ pub struct Daemon { pub ssl: bool, } +#[derive(Debug, Clone, Serialize, Deserialize)] +#[typeshare] +pub struct TransactionInfo { + #[serde(with = "monero_serde")] + pub fee: monero::Amount, + #[serde(with = "monero_serde")] + pub amount: monero::Amount, + #[typeshare(serialized_as = "number")] + pub confirmations: u64, + pub tx_hash: String, + pub direction: TransactionDirection, + #[typeshare(serialized_as = "number")] + pub timestamp: u64, +} + +#[typeshare] +#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +pub enum TransactionDirection { + In, + Out, +} + /// A wrapper around a pending transaction. +/// +/// Safety: do _not_ implement copy, send, sync, ... pub struct PendingTransaction(*mut ffi::PendingTransaction); +/// A struct containing transaction history. +pub struct TransactionHistoryHandle(*mut TransactionHistory); + +/// A struct containing a single transaction. +pub struct TransactionInfoHandle(*mut ffi::TransactionInfo); + impl WalletHandle { /// Open an existing wallet or create a new one, with a random seed. pub async fn open_or_create( @@ -134,50 +191,97 @@ impl WalletHandle { network: monero::Network, background_sync: bool, ) -> anyhow::Result { + Self::open_or_create_with_password(path, None, daemon, network, background_sync).await + } + + /// Common implementation used by all `open_*` helpers. + /// Spawns a dedicated wallet thread. Gets the WalletManager. Calls the wallet_op closure with the WalletManager. + /// The wallet_op closure determines which specific WalletManager method is called to create the wallet. + async fn open_with(path: String, daemon: Daemon, wallet_op: F) -> anyhow::Result + where + F: FnOnce(&mut WalletManager) -> anyhow::Result + Send + 'static, + { let (call_sender, call_receiver) = unbounded_channel(); - let wallet_name = path - .split('/') - .last() - .map(ToString::to_string) - .unwrap_or(path.clone()); - - let thread_name = format!("wallet-{}", wallet_name); - - // Capture current dispatcher before spawning + let wallet_name = path.rsplit('/').next().unwrap_or(&path).to_owned(); + let thread_name = format!("wallet-{wallet_name}"); let current_dispatcher = tracing::dispatcher::get_default(|d| d.clone()); + let (tx, rx) = oneshot::channel::>(); std::thread::Builder::new() .name(thread_name) .spawn(move || { - // Set the dispatcher for this thread let _guard = tracing::dispatcher::set_default(¤t_dispatcher); - let mut manager = WalletManager::new(daemon.clone(), &wallet_name) - .expect("wallet manager to be created"); - let wallet = manager - .open_or_create_wallet(&path, None, network, background_sync, daemon.clone()) - .expect("wallet to be created"); + // Get the WalletManager + // If we fail, send the error through the oneshot channel + let mut manager = match WalletManager::new(daemon.clone(), &wallet_name) { + Ok(m) => m, + Err(e) => { + let _ = tx.send(Err(e.context("failed to create wallet manager"))); + return; + } + }; - let mut wrapped_wallet = Wallet::new(wallet, manager, call_receiver); + // Open the wallet via caller-supplied closure + // If we fail, send the error through the oneshot channel + let wallet = match wallet_op(&mut manager) { + Ok(w) => w, + Err(e) => { + let _ = tx.send(Err(e.context("failed to open or create wallet"))); + return; + } + }; - wrapped_wallet.run(); + // Run the wallet thread + let mut wrapped = Wallet::new(wallet, manager, call_receiver); + let _ = tx.send(Ok(())); + wrapped.run(); }) .context("Couldn't start wallet thread")?; - // Ensure the wallet was created successfully by performing a dummy call - let wallet = WalletHandle { call_sender }; - wallet + // Wait for the thread to report success or failure + rx.await + .context("Failed to get result from wallet creation thread through oneshot channel")? + .context("Failed to open or create wallet")?; + + let handle = WalletHandle::new(call_sender); + + handle .check_wallet() .await - .context("failed to create wallet")?; + .context("Failed to open wallet because health check failed")?; - Ok(wallet) + Ok(handle) } - /// Open an existing wallet or create a new one by recovering it from a - /// mnemonic seed. If a wallet already exists at `path` it will be opened, - /// otherwise a new wallet will be recovered using the provided seed. + /// Opens an existing wallet or creates a new one with the specified password. + /// Uses password-based encryption for the wallet file. + /// If no password is provided, the wallet will be unencrypted. + pub async fn open_or_create_with_password( + path: String, + password: impl Into>, + daemon: Daemon, + network: monero::Network, + background_sync: bool, + ) -> anyhow::Result { + let password = password.into(); + + Self::open_with(path.clone(), daemon.clone(), move |manager| { + manager.open_or_create_wallet( + &path, + password.as_ref(), + network, + background_sync, + daemon.clone(), + ) + }) + .await + } + + /// Opens an existing wallet or recovers it from a mnemonic seed. + /// If the wallet exists at the path, it opens the existing wallet. + /// Otherwise, it creates a new wallet by recovering from the provided seed. pub async fn open_or_create_from_seed( path: String, mnemonic: String, @@ -186,78 +290,27 @@ impl WalletHandle { background_sync: bool, daemon: Daemon, ) -> anyhow::Result { - let (call_sender, call_receiver) = unbounded_channel(); - - let wallet_name = path - .split('/') - .last() - .map(ToString::to_string) - .unwrap_or(path.clone()); - - let thread_name = format!("wallet-{}", wallet_name); - - // Capture current dispatcher before spawning - let current_dispatcher = tracing::dispatcher::get_default(|d| d.clone()); - - // Spawn the wallet thread – all interactions with the wallet must - // happen on the same OS thread. - std::thread::Builder::new() - .name(thread_name) - .spawn(move || { - // Set the dispatcher for this thread - let _guard = tracing::dispatcher::set_default(¤t_dispatcher); - - // Create the wallet manager in this thread first. - let mut manager = WalletManager::new(daemon.clone(), &wallet_name) - .expect("wallet manager to be created"); - - // Decide whether we have to open an existing wallet or recover it - // from the mnemonic. - let wallet = if manager.wallet_exists(&path) { - // Existing wallet – open it. - manager - .open_or_create_wallet( - &path, - None, - network, - background_sync, - daemon.clone(), - ) - .expect("wallet to be opened") - } else { - // Wallet does not exist – recover it from the seed. - manager - .recover_wallet( - &path, - None, - &mnemonic, - network, - restore_height, - background_sync, - daemon.clone(), - ) - .expect("wallet to be recovered from seed") - }; - - let mut wrapped_wallet = Wallet::new(wallet, manager, call_receiver); - - wrapped_wallet.run(); - }) - .context("Couldn't start wallet thread")?; - - let wallet = WalletHandle { call_sender }; - // Make a test call to ensure that the wallet is created. - wallet - .check_wallet() - .await - .context("failed to create wallet")?; - - Ok(wallet) + Self::open_with(path.clone(), daemon.clone(), move |manager| { + if manager.wallet_exists(&path) { + manager.open_or_create_wallet(&path, None, network, background_sync, daemon.clone()) + } else { + manager.recover_wallet( + &path, + None, + &mnemonic, + network, + restore_height, + background_sync, + daemon.clone(), + ) + } + }) + .await } - /// Open an existing wallet or create a new one from spend/view keys. If a - /// wallet already exists at `path` it will be opened, otherwise it will be - /// created from the supplied keys. + /// Opens an existing wallet or creates one from spend/view keys. + /// If the wallet exists at the path, it opens the existing wallet. + /// Otherwise, it creates a new wallet from the provided cryptographic keys. #[allow(clippy::too_many_arguments)] pub async fn open_or_create_from_keys( path: String, @@ -270,62 +323,20 @@ impl WalletHandle { background_sync: bool, daemon: Daemon, ) -> anyhow::Result { - let (call_sender, call_receiver) = unbounded_channel(); - - let wallet_name = path - .split('/') - .last() - .map(ToString::to_string) - .unwrap_or(path.clone()); - - let thread_name = format!("wallet-{}", wallet_name); - - // Capture current dispatcher before spawning - let current_dispatcher = tracing::dispatcher::get_default(|d| d.clone()); - - std::thread::Builder::new() - .name(thread_name) - .spawn(move || { - // Set the dispatcher for this thread - let _guard = tracing::dispatcher::set_default(¤t_dispatcher); - - let wallet_name = path - .split('/') - .last() - .map(ToString::to_string) - .unwrap_or(path.clone()); - - let mut manager = WalletManager::new(daemon.clone(), &wallet_name) - .expect("wallet manager to be created"); - - let wallet = manager - .open_or_create_wallet_from_keys( - &path, - password.as_deref(), - network, - &address, - view_key, - spend_key, - restore_height, - background_sync, - daemon.clone(), - ) - .expect("wallet to be opened or created from keys"); - - let mut wrapped_wallet = Wallet::new(wallet, manager, call_receiver); - - wrapped_wallet.run(); - }) - .context("Couldn't start wallet thread")?; - - let wallet = WalletHandle { call_sender }; - // Make a test call to ensure that the wallet is created. - wallet - .check_wallet() - .await - .context("Failed to create wallet")?; - - Ok(wallet) + Self::open_with(path.clone(), daemon.clone(), move |manager| { + manager.open_or_create_wallet_from_keys( + &path, + password.as_deref(), + network, + &address, + view_key, + spend_key, + restore_height, + background_sync, + daemon.clone(), + ) + }) + .await } /// Execute a function on the wallet thread and return the result. @@ -335,6 +346,17 @@ impl WalletHandle { where F: FnOnce(&mut FfiWallet) -> R + Send + 'static, R: Sized + Send + 'static, + { + // Delegate to call_with_pending_txs but ignore the pending_txs parameter + self.call_with_pending_txs(move |wallet, _pending_txs| function(wallet)) + .await + } + + /// Call a function on the wallet with access to pending transactions storage. + pub async fn call_with_pending_txs(&self, function: F) -> R + where + F: FnOnce(&mut FfiWallet, &mut HashMap) -> R + Send + 'static, + R: Sized + Send + 'static, { // Create a oneshot channel for the result let (sender, receiver) = oneshot::channel(); @@ -342,7 +364,9 @@ impl WalletHandle { // Send the function call to the wallet thread (wrapped in a Box) self.call_sender .send(Call { - function: Box::new(move |wallet| Box::new(function(wallet)) as Box), + function: Box::new(move |wallet, pending_txs| { + Box::new(function(wallet, pending_txs)) as Box + }), sender, }) .inspect_err(|e| tracing::error!(error=%e, "failed to send call")) @@ -391,7 +415,7 @@ impl WalletHandle { bail!("Failed to get blockchain height after 5 attempts"); } - /// Transfer funds to an address. + /// Transfer funds to an address without approval. pub async fn transfer( &self, address: &monero::Address, @@ -426,7 +450,7 @@ impl WalletHandle { } /// Get the seed of the wallet. - pub async fn seed(&self) -> String { + pub async fn seed(&self) -> anyhow::Result { self.call(move |wallet| wallet.seed()).await } @@ -452,6 +476,12 @@ impl WalletHandle { .await } + /// Get the transaction history and convert it to a list of serializable transaction infos. + /// This is needed because TransactionHistory and TransactionInfo are not Send. + pub async fn history(&self) -> Vec { + self.call(move |wallet| wallet.history()).await + } + /// Get the unlocked balance of the wallet. pub async fn unlocked_balance(&self) -> monero::Amount { self.call(move |wallet| wallet.unlocked_balance()).await @@ -467,8 +497,75 @@ impl WalletHandle { self.call(move |wallet| wallet.synchronized()).await } + /// Get the restore height of the wallet. + pub async fn set_restore_height(&self, height: u64) -> anyhow::Result<()> { + self.call(move |wallet| wallet.set_restore_height(height)) + .await + .context("Failed to set restore height: FFI call failed with exception") + .expect("Shouldn't panic"); + + Ok(()) + } + + /// Get the restore height of the wallet. + pub async fn get_restore_height(&self) -> anyhow::Result { + self.call(move |wallet| wallet.get_restore_height()) + .await + .map_err(|e| { + anyhow!("Failed to get restore height: FFI call failed with exception: {e}") + }) + } + + pub async fn get_blockchain_height_by_date( + &self, + year: u16, + month: u8, + day: u8, + ) -> Result { + self.call(move |wallet| wallet.get_blockchain_height_by_date(year, month, day)) + .await + .map_err(|e| { + anyhow!( + "Failed to get blockchain height by date: FFI call failed with exception: {e}" + ) + }) + } + + /// Rescan the blockchain asynchronously. + pub async fn rescan_blockchain_async(&self) { + self.call(move |wallet| wallet.rescan_blockchain_async()) + .await + } + + /// Start the refresh. + pub async fn start_refresh(&self) { + self.call(move |wallet| wallet.start_refresh()).await + } + + /// Pause the background refresh. + pub async fn pause_refresh(&self) { + self.call(move |wallet| wallet.pause_refresh()).await + } + + /// Start the background refresh thread. + pub async fn start_refresh_thread(&self) { + self.call(move |wallet| wallet.start_refresh_thread()).await + } + + /// Stop the background refresh once (doesn't stop background refresh thread). + pub async fn stop(&self) { + self.call(move |wallet| wallet.stop()).await + } + + /// Store the wallet state. + /// If `path` is `None`, the wallet will be stored in the location it was opened from. + pub async fn store(&self, path: Option<&str>) { + let path = path.unwrap_or("").to_string(); + self.call(move |wallet| wallet.store(&path)).await; + } + /// Get the sync progress of the wallet. - async fn sync_progress(&self) -> SyncProgress { + pub async fn sync_progress(&self) -> SyncProgress { self.call(move |wallet| wallet.sync_progress()).await } @@ -484,7 +581,7 @@ impl WalletHandle { self.call_sender .send(Call { - function: Box::new(move |wallet| { + function: Box::new(move |wallet, _pending_txs| { Box::new(wallet.check_error()) as Box }), sender, @@ -676,13 +773,133 @@ impl WalletHandle { Ok(()) } + /// Creates pending transaction, gets approval, then publishes or disposes based on approval. + /// Return `None` if the transaction is not published, `Some(receipt)` if it is published. + /// If the amount is `None`, the transaction will be a sweep (whole balance) + /// + /// Returns (TxReceipt, amount, fee) if the transaction is published, `None` otherwise. + pub async fn transfer_with_approval( + &self, + address: &monero::Address, + amount: Option, + approval_callback: ApprovalCallback, + ) -> anyhow::Result> { + let address = *address; + + // Construct and sign the transaction. Do not publish the transaction yet + // Store the pending transaction in the wallet thread inside the [`pending_txs`] map + let (uuid, txid, amount, fee) = self + .call_with_pending_txs(move |wallet, pending_txs| { + let pending_tx = match amount { + Some(amount) => wallet.create_pending_transaction(&address, amount)?, + None => wallet.create_pending_sweep_transaction(&address)?, + }; + + // Get txid + let txid = ffi::pendingTransactionTxId(&pending_tx) + .context("Failed to get txid from pending transaction")? + .to_string(); + + // Get the amount + let amount = ffi::pendingTransactionAmount(&pending_tx) + .context("Failed to get amount from pending transaction")?; + let amount = monero::Amount::from_pico(amount); + + // Get the fee + let fee = ffi::pendingTransactionFee(&pending_tx) + .context("Failed to get fee from pending transaction")?; + let fee = monero::Amount::from_pico(fee); + + // Generate UUID and store it in the pending transactions map + let uuid = Uuid::new_v4(); + pending_txs.insert(uuid, pending_tx); + + Ok::<(Uuid, String, monero::Amount, monero::Amount), anyhow::Error>(( + uuid, txid, amount, fee, + )) + }) + .await?; + + // Get approval asynchronously (no wallet thread blocking) + let approved = approval_callback(txid, amount, fee).await; + + if approved { + // Publish the transaction + let receipt = self + .call_with_pending_txs(move |wallet, pending_txs| { + let mut pending_tx = pending_txs.remove(&uuid).ok_or_else(|| { + anyhow!("Pending transaction not found for UUID: {}", uuid) + })?; + + let result = wallet.publish_pending_transaction(&mut pending_tx); + wallet.dispose_pending_transaction(pending_tx); + result + }) + .await?; + + Ok(Some((receipt, amount, fee))) + } else { + // Dispose the pending transaction without publishing + self.call_with_pending_txs(move |wallet, pending_txs| { + let pending_tx = pending_txs + .remove(&uuid) + .ok_or_else(|| anyhow!("Pending transaction not found for UUID: {}", uuid))?; + + wallet.dispose_pending_transaction(pending_tx); + Ok::<(), anyhow::Error>(()) + }) + .await?; + + Ok(None) + } + } + + /// Verify the password for a wallet at the given path. + /// This function spawns a thread to perform the verification and returns the result via a oneshot channel. + /// Returns `Ok(true)` if the password is correct, `Ok(false)` if incorrect. + pub fn verify_wallet_password(path: String, password: String) -> anyhow::Result { + use std::sync::mpsc; + + // Get the keys file path from the wallet path (simply append .keys to the path) + let keys_file_path = format!("{}.keys", path); + + let (sender, receiver) = mpsc::channel(); + + std::thread::spawn(move || { + let wallet_name = path + .split('/') + .last() + .map(ToString::to_string) + .unwrap_or_else(|| "wallet".to_string()); + + let result = (|| -> anyhow::Result { + let mut manager = WalletManager::new( + // Dummy daemon address + Daemon { + address: "localhost:18081".to_string(), + ssl: false, + }, + &wallet_name, + )?; + + manager.verify_wallet_password(&keys_file_path, &password) + })(); + + let _ = sender.send(result); + }); + + receiver + .recv() + .context("Failed to receive password verification result from thread")? + } + /// Sign a message with the wallet's private key. - /// + /// /// # Arguments /// * `message` - The message to sign (arbitrary byte data) /// * `address` - The address to use for signing (uses main address if None) /// * `sign_with_view_key` - Whether to sign with view key instead of spend key (default: false) - /// + /// /// # Returns /// A proof type prefix + base58 encoded signature pub async fn sign_message( @@ -693,7 +910,7 @@ impl WalletHandle { ) -> anyhow::Result { let message = message.to_string(); let address = address.map(|s| s.to_string()); - + self.call(move |wallet| { wallet.sign_message(&message, address.as_deref(), sign_with_view_key) }) @@ -711,12 +928,15 @@ impl Wallet { wallet, manager, call_receiver, + pending_transactions: HashMap::new(), } } fn run(&mut self) { while let Some(call) = self.call_receiver.blocking_recv() { - let result = (call.function)(&mut self.wallet); + let result = (call.function)(&mut self.wallet, &mut self.pending_transactions); + + // TODO: Do not panic here call.sender .send(result) .expect("failed to send result back to caller"); @@ -775,7 +995,7 @@ impl WalletManager { pub fn open_or_create_wallet( &mut self, path: &str, - password: Option<&str>, + password: Option<&String>, network: monero::Network, background_sync: bool, daemon: Daemon, @@ -787,7 +1007,14 @@ impl WalletManager { tracing::debug!(wallet=%path, "Wallet already exists, opening it"); return self - .open_wallet(path, password, network, background_sync, daemon) + .open_wallet( + path, + password, + network, + background_sync, + daemon, + Box::new(TraceListener::new(path.to_string())), + ) .context(format!("Failed to open wallet `{}`", &path)); } @@ -803,7 +1030,7 @@ impl WalletManager { // Otherwise, create (and open) a new wallet. let kdf_rounds = Self::DEFAULT_KDF_ROUNDS; let_cxx_string!(path = path); - let_cxx_string!(password = password.unwrap_or("")); + let_cxx_string!(password = password.map_or("", |s| s.as_str())); let_cxx_string!(language = "English"); let network_type = network.into(); @@ -844,7 +1071,14 @@ impl WalletManager { tracing::info!(wallet=%path, "Wallet already exists, opening it"); return self - .open_wallet(path, password, network, background_sync, daemon.clone()) + .open_wallet( + path, + password.map(|s| s.to_string()).as_ref(), + network, + background_sync, + daemon.clone(), + Box::new(TraceListener::new(path.to_string())), + ) .context(format!("Failed to open wallet `{}`", &path)); } @@ -946,7 +1180,8 @@ impl WalletManager { fn close_wallet(&mut self, wallet: &mut FfiWallet) -> anyhow::Result<()> { tracing::info!(wallet=%wallet.filename(), "Closing wallet"); - // Safety: we know we have a valid, unique pointer to the wallet + // Safety: we know we have a valid, unique pointer to the wallet and are on the same thread it + // was created on. let success = unsafe { self.inner.pinned().closeWallet(wallet.inner.inner, true) } .context("Failed to close wallet: Ffi call failed with exception")?; @@ -958,23 +1193,23 @@ impl WalletManager { } /// Open a wallet. Only used internally. Use [`WalletManager::open_or_create_wallet`] instead. - /// - /// Todo: add listener support? fn open_wallet( &mut self, path: &str, - password: Option<&str>, + password: Option<&String>, network_type: monero::Network, background_sync: bool, daemon: Daemon, + listener: Box, ) -> anyhow::Result { tracing::debug!(%path, "Opening wallet"); let_cxx_string!(path = path); - let_cxx_string!(password = password.unwrap_or("")); + let_cxx_string!(password = password.map_or("", |s| s.as_str())); let network_type = network_type.into(); let kdf_rounds = Self::DEFAULT_KDF_ROUNDS; + // Safety: we pass a null pointer which is safe because we don't use it. let wallet_pointer = unsafe { self.inner.pinned().openWallet( &path, @@ -995,6 +1230,8 @@ impl WalletManager { let wallet = FfiWallet::new(raw_wallet, background_sync, daemon) .context("Failed to initialize re-opened wallet")?; + wallet.add_listener(listener); + Ok(wallet) } @@ -1022,6 +1259,19 @@ impl WalletManager { .context("Failed to check if wallet exists: FFI call failed with exception") .expect("Wallet check should never fail") } + + /// Verify the password for a wallet at the given path. + /// Returns `Ok(true)` if the password is correct, `Ok(false)` if incorrect. + pub fn verify_wallet_password(&mut self, path: &str, password: &str) -> anyhow::Result { + let_cxx_string!(path = path); + let_cxx_string!(password = password); + + // Safety: we know we have a valid, unique pointer to the wallet manager and are on its original thread. + self.inner + .deref() + .verifyWalletPassword(&path, &password, false, Self::DEFAULT_KDF_ROUNDS) + .context("Failed to verify wallet password: FFI call failed with exception") + } } impl RawWalletManager { @@ -1034,6 +1284,7 @@ impl RawWalletManager { /// the ffi interface mostly takes a Pin<&mut T> but /// we haven't figured out how to hold that in the struct. pub fn pinned(&mut self) -> Pin<&mut ffi::WalletManager> { + // Safety: we know it's a valid pointer on the original thread, we check for null pointers. unsafe { Pin::new_unchecked( self.inner @@ -1044,6 +1295,65 @@ impl RawWalletManager { } } +impl Deref for RawWalletManager { + type Target = ffi::WalletManager; + + fn deref(&self) -> &Self::Target { + // Safety: we know it's a valid pointer on the original thread, we check for null pointers. + unsafe { self.inner.as_ref().expect("wallet manager not to be null") } + } +} + +impl WalletEventListener for Arc>>> { + fn on_money_spent(&self, txid: &str, amount: u64) { + for listener in self.lock().unwrap().iter() { + listener.on_money_spent(txid, amount); + } + } + + fn on_money_received(&self, txid: &str, amount: u64) { + for listener in self.lock().unwrap().iter() { + listener.on_money_received(txid, amount); + } + } + + fn on_unconfirmed_money_received(&self, txid: &str, amount: u64) { + for listener in self.lock().unwrap().iter() { + listener.on_unconfirmed_money_received(txid, amount); + } + } + + fn on_new_block(&self, height: u64) { + for listener in self.lock().unwrap().iter() { + listener.on_new_block(height); + } + } + + fn on_updated(&self) { + for listener in self.lock().unwrap().iter() { + listener.on_updated(); + } + } + + fn on_refreshed(&self) { + for listener in self.lock().unwrap().iter() { + listener.on_refreshed(); + } + } + + fn on_reorg(&self, height: u64, blocks_detached: u64, transfers_detached: usize) { + for listener in self.lock().unwrap().iter() { + listener.on_reorg(height, blocks_detached, transfers_detached); + } + } + + fn on_pool_tx_removed(&self, txid: &str) { + for listener in self.lock().unwrap().iter() { + listener.on_pool_tx_removed(txid); + } + } +} + impl FfiWallet { const MAIN_ACCOUNT_INDEX: u32 = 0; @@ -1053,7 +1363,11 @@ impl FfiWallet { anyhow::bail!("Failed to create wallet: got null pointer"); } - let mut wallet = Self { inner }; + let mut wallet = Self { + inner, + listeners: Arc::new(Mutex::new(vec![])), + }; + wallet .check_error() .context("Something went wrong while creating the wallet (not null pointer, though)")?; @@ -1082,6 +1396,8 @@ impl FfiWallet { wallet.force_background_refresh(); } + wallet.set_single_listener(Box::new(wallet.listeners.clone())); + // Check for errors on general principles wallet.check_error()?; @@ -1170,10 +1486,30 @@ impl FfiWallet { Ok(()) } + /// Set a listener to the wallet. + pub fn set_single_listener(&mut self, listener: Box) { + let cpp_listener = bridge::wallet_listener::create_rust_listener_adapter( + WalletListenerBox::new_boxed(listener), + ) as *mut ffi::WalletListener; + + unsafe { + // Safety: we know that create_rust_listener_adapter returns a valid pointer to a c++ object extending Monero::WalletListener. + self.inner + .pinned() + .setListener(cpp_listener) + .expect("Shouldn't panic"); + } + } + + /// Add a listener to the wallet. + pub fn add_listener(&self, listener: Box) { + self.listeners.lock().unwrap().push(listener); + } + /// Get the sync progress of the wallet as a percentage. /// /// Returns a zeroed sync progress if the daemon is not connected. - fn sync_progress(&self) -> SyncProgress { + pub fn sync_progress(&self) -> SyncProgress { let current_block = self .inner .blockChainHeight() @@ -1245,6 +1581,69 @@ impl FfiWallet { .expect("Shouldn't panic"); } + /// Set the restore height of the wallet. + pub fn set_restore_height(&mut self, height: u64) -> anyhow::Result<()> { + self.inner + .pinned() + .setRefreshFromBlockHeight(height) + .context("Failed to set restore height: FFI call failed with exception") + .expect("Shouldn't panic"); + + Ok(()) + } + + pub fn get_restore_height(&mut self) -> anyhow::Result { + Ok(self + .inner + .pinned() + .getRefreshFromBlockHeight() + .context("Failed to get restore height: FFI call failed with exception") + .expect("Shouldn't panic")) + } + + pub fn get_blockchain_height_by_date( + &mut self, + year: u16, + month: u8, + day: u8, + ) -> Result { + self.inner + .pinned() + .getBlockchainHeightByDate(year, month, day) + } + + /// Rescan the blockchain asynchronously. + fn rescan_blockchain_async(&mut self) { + self.inner.pinned().rescanBlockchainAsync(); + } + + /// Start the refresh. + fn start_refresh(&mut self) { + self.inner + .pinned() + .startRefresh() + .context("Failed to start refresh: FFI call failed with exception") + .expect("Shouldn't panic"); + } + + /// Pause the background refresh. + fn pause_refresh(&mut self) { + self.inner.pinned().pauseRefresh(); + } + + /// Stop the background refresh once (doesn't stop background refresh thread). + fn stop(&mut self) { + self.inner.pinned().stop(); + } + + /// Store the wallet state. + fn store(&mut self, path: &str) { + let_cxx_string!(path = path); + self.inner.pinned().store(&path) + .context("Failed to store wallet: FFI call failed with exception") + .expect("Shouldn't panic"); + } + /// Start the background refresh thread (refreshes every 10 seconds). fn start_refresh_thread(&mut self) { self.inner @@ -1305,8 +1704,6 @@ impl FfiWallet { /// Returns the height of the blockchain, if connected. /// Returns None if not connected. fn daemon_blockchain_height(&self) -> Option { - tracing::trace!(connected=%self.connected(), "Getting daemon blockchain height"); - // Here we actually use the _target_ height -- incase the remote node is // currently catching up we want to work with the height it ends up at. match self @@ -1432,44 +1829,71 @@ impl FfiWallet { address: &monero::Address, amount: monero::Amount, ) -> anyhow::Result { - let_cxx_string!(address = address.to_string()); - let amount = amount.as_pico(); + let mut pending_tx = self.create_pending_transaction(address, amount)?; + let result = self.publish_pending_transaction(&mut pending_tx); + self.dispose_pending_transaction(pending_tx); + result + } - // First we need to create a pending transaction. - let mut pending_tx = PendingTransaction( - ffi::createTransaction(self.inner.pinned(), &address, amount) + /// Create a pending transaction without publishing it. + /// Returns the pending transaction that can be inspected before publishing. + fn create_pending_transaction( + &mut self, + address: &monero::Address, + amount: monero::Amount, + ) -> anyhow::Result { + let_cxx_string!(address_str = address.to_string()); + let amount_pico = amount.as_pico(); + + let pending_tx = PendingTransaction( + ffi::createTransaction(self.inner.pinned(), &address_str, amount_pico) .context("Failed to create transaction: FFI call failed with exception")?, ); - // Get the txid from the pending transaction before we publish, - // otherwise it might be null. - let txid = ffi::pendingTransactionTxId(&pending_tx) + Ok(pending_tx) + } + + /// Create a pending sweep transaction without publishing it. + /// Returns the pending transaction that can be inspected before publishing. + fn create_pending_sweep_transaction( + &mut self, + address: &monero::Address, + ) -> anyhow::Result { + let_cxx_string!(address_str = address.to_string()); + + let pending_tx = PendingTransaction( + ffi::createSweepTransaction(self.inner.pinned(), &address_str) + .context("Failed to create sweep transaction: FFI call failed with exception")?, + ); + + Ok(pending_tx) + } + + /// Publish a pending transaction and return a receipt. + /// Note: Caller is responsible for disposing the pending transaction afterwards. + fn publish_pending_transaction( + &mut self, + pending_tx: &mut PendingTransaction, + ) -> anyhow::Result { + // Get the txid from the pending transaction before we publish + let txid = ffi::pendingTransactionTxId(pending_tx) .context("Failed to get txid from pending transaction: FFI call failed with exception")? .to_string(); // Publish the transaction - let result = pending_tx + pending_tx .publish() - .context("Failed to publish transaction"); + .context("Failed to publish transaction")?; - // Check for errors (make sure to dispose the transaction) - if result.is_err() { - self.dispose_transaction(pending_tx); - return Err(result.expect_err("result is an error as per the check above")); - } - - // Fetch the tx key from the wallet. + // Fetch the tx key from the wallet let_cxx_string!(txid_cxx = txid.clone()); let tx_key = ffi::walletGetTxKey(&self.inner, &txid_cxx) .context("Failed to get tx key from wallet: FFI call failed with exception")? .to_string(); - // Get current blockchain height (wallet height). + // Get current blockchain height let height = self.blockchain_height(); - // Dispose the pending transaction object to avoid memory leak. - self.dispose_transaction(pending_tx); - Ok(TxReceipt { txid, tx_key, @@ -1477,6 +1901,11 @@ impl FfiWallet { }) } + /// Dispose of a pending transaction to free memory. + fn dispose_pending_transaction(&mut self, pending_tx: PendingTransaction) { + self.dispose_transaction(pending_tx); + } + /// Sweep all funds from the wallet to a specified address. /// Returns a list of transaction ids of the created transactions. fn sweep(&mut self, address: &monero::Address) -> anyhow::Result> { @@ -1541,7 +1970,7 @@ impl FfiWallet { ) -> anyhow::Result> { tracing::warn!("STARTED MULTI SWEEP"); - if addresses.len() == 0 { + if addresses.is_empty() { bail!("No addresses to sweep to"); } @@ -1694,10 +2123,54 @@ impl FfiWallet { Ok(amounts) } + /// Get the transaction history. + /// Returns an empty vector if the transaction history is missing. + fn history(&mut self) -> Vec { + let history_ptr = self + .inner + .pinned() + .history() + .context("Failed to get transaction history: FFI call failed with exception"); + + let Ok(history_ptr) = history_ptr else { + tracing::error!(error=%history_ptr.unwrap_err(), "Failed to get transaction history, proceeding with empty history"); + return vec![]; + }; + + let history = unsafe { + // Safety: the pointer is valid as long as the wallet is alive (which is is when we called this a millisecond ago) + Pin::new_unchecked( + history_ptr + // Safety: this pointer isn't exclusive, however this is how we're supposed to do this according to the api + .as_mut() + .expect("history pointer to not be null after we just checked"), + ) + }; + // Ignore result, we'll proceed anyway + let _ = history + .refresh() + .context("Failed to refresh transaction history: FFI call failed with exception") + .inspect_err(|e| tracing::error!(error=%e,"Failed to refresh transaction history")); + + let history_handle = TransactionHistoryHandle(history_ptr); + let count = history_handle.count(); + + let mut transactions = Vec::new(); + for i in 0..count { + if let Some(tx_info_handle) = history_handle.transaction(i) { + if let Some(serialized_tx) = tx_info_handle.serialize() { + transactions.push(serialized_tx); + } + } + } + transactions + } + /// Dispose (deallocate) a pending transaction object. /// Always call this before dropping a pending transaction object, /// otherwise we leak memory. fn dispose_transaction(&mut self, tx: PendingTransaction) { + // Safety: we pass a valid pointer and we verified it's not used again since PendingTransaction is moved into this function unsafe { self.inner .pinned() @@ -1744,21 +2217,33 @@ impl FfiWallet { } /// Get the seed of the wallet. - fn seed(&self) -> String { - let_cxx_string!(seed = ""); - ffi::walletSeed(&self.inner, &seed) + fn seed(&mut self) -> anyhow::Result { + let_cxx_string!(language = "English"); + self.inner + .pinned() + .setSeedLanguage(&language) + .context("Failed to set seed language")?; + + let_cxx_string!(seed_offset = ""); + let seed = ffi::walletSeed(&self.inner, &seed_offset) .context("Failed to get wallet seed: FFI call failed with exception") .expect("Shouldn't panic") - .to_string() + .to_string(); + + if seed.is_empty() { + bail!("Failed to get wallet seed"); + } + + Ok(seed) } /// Sign a message with the wallet's private key. - /// + /// /// # Arguments /// * `message` - The message to sign (arbitrary byte data) /// * `address` - The address to use for signing (uses main address if None) /// * `sign_with_view_key` - Whether to sign with view key instead of spend key (default: false) - /// + /// /// # Returns /// A proof type prefix + base58 encoded signature pub fn sign_message( @@ -1769,23 +2254,25 @@ impl FfiWallet { ) -> anyhow::Result { let_cxx_string!(message_cxx = message); let_cxx_string!(address_cxx = address.unwrap_or("")); - - let signature = ffi::signMessage(self.inner.pinned(), &message_cxx, &address_cxx, sign_with_view_key) - .context("Failed to sign message: FFI call failed with exception")? - .to_string(); - + + let signature = ffi::signMessage( + self.inner.pinned(), + &message_cxx, + &address_cxx, + sign_with_view_key, + ) + .context("Failed to sign message: FFI call failed with exception")? + .to_string(); + if signature.is_empty() { self.check_error().context("Failed to sign message")?; anyhow::bail!("Failed to sign message (no signature returned)"); } - + Ok(signature) } } -/// Safety: We check that it's never accessed outside the homethread at runtime. -unsafe impl Send for RawWalletManager {} - impl PendingTransaction { /// Return `Ok` when the pending transaction is ok, otherwise return the error. /// This is a convenience method we use for retrieving errors after @@ -1894,9 +2381,6 @@ impl PartialEq for SyncProgress { } } -/// Safety: We check that it's never accessed outside the homethread at runtime. -unsafe impl Send for RawWallet {} - impl RawWallet { fn new(inner: *mut ffi::Wallet) -> Self { Self { inner } @@ -1904,6 +2388,7 @@ impl RawWallet { /// Convenience method for getting a pinned reference to the inner (c++) wallet. fn pinned(&mut self) -> Pin<&mut ffi::Wallet> { + // Safety: we know this is a valid pointer in the original thread unsafe { Pin::new_unchecked(self.inner.as_mut().expect("wallet pointer not to be null")) } } } @@ -1914,12 +2399,14 @@ impl Deref for RawWallet { type Target = ffi::Wallet; fn deref(&self) -> &ffi::Wallet { + // Safety: we know this is a valid pointer in the original thread unsafe { self.inner.as_ref().expect("wallet pointer not to be null") } } } impl PendingTransaction { fn pinned(&mut self) -> Pin<&mut ffi::PendingTransaction> { + // Safety: we know this is a valid pointer in the original thread unsafe { Pin::new_unchecked( self.0 @@ -1934,6 +2421,7 @@ impl Deref for PendingTransaction { type Target = ffi::PendingTransaction; fn deref(&self) -> &ffi::PendingTransaction { + // Safety: we know this is a valid pointer in the original thread unsafe { self.0 .as_ref() @@ -1942,6 +2430,173 @@ impl Deref for PendingTransaction { } } +impl TransactionHistoryHandle { + /// Get the number of transactions in the history. + pub fn count(&self) -> i32 { + self.deref().count() + } + + /// Get a transaction from the history by index. + pub fn transaction(&self, index: i32) -> Option { + let tx_info_ptr = self.deref().transaction(index); + + if tx_info_ptr.is_null() { + None + } else { + // We wrap the raw pointer in our safe wrapper struct. + Some(TransactionInfoHandle(tx_info_ptr)) + } + } +} + +impl Deref for TransactionHistoryHandle { + type Target = ffi::TransactionHistory; + + fn deref(&self) -> &Self::Target { + // Safety: we know this is a valid pointer in the original thread + unsafe { + self.0 + .as_ref() + .expect("transaction history pointer not to be null") + } + } +} + +impl TransactionInfoHandle { + /// Get the amount of the transaction. + pub fn amount(&self) -> u64 { + self.deref().amount() + } + + /// Get the fee of the transaction. + pub fn fee(&self) -> u64 { + self.deref().fee() + } + + /// Get the confirmations of the transaction. + pub fn confirmations(&self) -> u64 { + self.deref().confirmations() + } + + /// Get the hash of the transaction. + pub fn hash(&self) -> String { + ffi::transactionInfoHash(self.deref()).to_string() + } + + /// Get the direction of the transaction. + pub fn direction(&self) -> Result { + match self.deref().direction() { + 0 => Ok(TransactionDirection::In), + 1 => Ok(TransactionDirection::Out), + otherwise => bail!( + "Invalid transaction direction received from ffi call: `{}`", + otherwise + ), + } + } + + /// Get the timestamp of the transaction. + pub fn timestamp(&self) -> u64 { + ffi::transactionInfoTimestamp(self.deref()) + } + + pub fn serialize(&self) -> Option { + let fee = self.fee(); + let amount = self.amount(); + let confirmations = self.confirmations(); + let tx_hash = self.hash(); + let timestamp = self.timestamp(); + let direction = self + .direction() + .inspect_err( + |e| tracing::error!(error=%e, %tx_hash, "Failed to get direction of transaction"), + ) + .ok()?; + + Some(TransactionInfo { + fee: monero::Amount::from_pico(fee), + amount: monero::Amount::from_pico(amount), + confirmations, + tx_hash, + direction, + timestamp, + }) + } +} + +impl Deref for TransactionInfoHandle { + type Target = ffi::TransactionInfo; + + fn deref(&self) -> &Self::Target { + // Safety: we know this is a valid pointer in the original thread + unsafe { + self.0 + .as_ref() + .expect("transaction info pointer not to be null") + } + } +} + +pub struct WalletHandleListener { + handle: Arc, + rt_handle: tokio::runtime::Handle, +} + +impl WalletHandleListener { + pub fn new(handle: Arc) -> Self { + let rt_handle = tokio::runtime::Handle::current(); + Self { handle, rt_handle } + } +} + +impl WalletEventListener for WalletHandleListener { + fn on_money_spent(&self, _txid: &str, _amount: u64) {} + + fn on_money_received(&self, _txid: &str, _amount: u64) {} + + fn on_unconfirmed_money_received(&self, _txid: &str, _amount: u64) {} + + fn on_new_block(&self, _height: u64) {} + + fn on_updated(&self) {} + + fn on_refreshed(&self) { + // When the wallet finishes refreshing, we start the refresh thread again. + // The purpose of this is to ensure that if the user does a rescan (restore height changed) + // We start the refresh thread again after the rescan is complete. + let handle = self.handle.clone(); + self.rt_handle.spawn(async move { + handle.start_refresh_thread().await; + }); + } + + fn on_reorg(&self, _height: u64, _blocks_detached: u64, _transfers_detached: usize) {} + + fn on_pool_tx_removed(&self, _txid: &str) {} +} + +pub mod monero_serde { + use monero::Amount; + use serde::{Deserialize, Deserializer, Serializer}; + + pub fn serialize(x: &Amount, s: S) -> Result + where + S: Serializer, + { + s.serialize_u64(x.as_pico()) + } + + pub fn deserialize<'de, D>(deserializer: D) -> Result>::Error> + where + D: Deserializer<'de>, + { + let picos = u64::deserialize(deserializer)?; + let amount = Amount::from_pico(picos); + + Ok(amount) + } +} + /// Create a backoff strategy for retrying a function. /// Default max elapsed time is 5 minutes, default max interval is 30 seconds. fn backoff( @@ -1974,7 +2629,7 @@ mod tests { } // Ensure percentages are valid (non-negative and sum to approximately 1.0) - if percentages.iter().any(|&p| p < 0.0 || p > 1.0) { + if percentages.iter().any(|&p| !(0.0..=1.0).contains(&p)) { return TestResult::discard(); } @@ -2003,7 +2658,7 @@ mod tests { return TestResult::discard(); } - if percentages.iter().any(|&p| p < 0.0 || p > 1.0) { + if percentages.iter().any(|&p| !(0.0..=1.0).contains(&p)) { return TestResult::discard(); } @@ -2029,7 +2684,7 @@ mod tests { return TestResult::discard(); } - if percentages.iter().any(|&p| p < 0.0 || p > 1.0) { + if percentages.iter().any(|&p| !(0.0..=1.0).contains(&p)) { return TestResult::discard(); } diff --git a/monero-sys/tests/sign_message.rs b/monero-sys/tests/sign_message.rs index cdda778a..48c31bf1 100644 --- a/monero-sys/tests/sign_message.rs +++ b/monero-sys/tests/sign_message.rs @@ -33,50 +33,68 @@ async fn test_sign_message() { // Test message to sign let test_message = "Hello, World! This is a test message for signing."; - + tracing::info!("Testing message signing with spend key (default address)"); let signature_spend = wallet .sign_message(test_message, None, false) .await .expect("Failed to sign message with spend key"); - + tracing::info!("Signature with spend key: {}", signature_spend); assert!(!signature_spend.is_empty(), "Signature should not be empty"); - assert!(signature_spend.len() > 10, "Signature should be reasonably long"); + assert!( + signature_spend.len() > 10, + "Signature should be reasonably long" + ); tracing::info!("Testing message signing with view key (default address)"); let signature_view = wallet .sign_message(test_message, None, true) .await .expect("Failed to sign message with view key"); - + tracing::info!("Signature with view key: {}", signature_view); assert!(!signature_view.is_empty(), "Signature should not be empty"); - assert!(signature_view.len() > 10, "Signature should be reasonably long"); + assert!( + signature_view.len() > 10, + "Signature should be reasonably long" + ); // Signatures should be different when using different keys - assert_ne!(signature_spend, signature_view, "Spend key and view key signatures should be different"); + assert_ne!( + signature_spend, signature_view, + "Spend key and view key signatures should be different" + ); tracing::info!("Testing message signing with spend key (explicit address)"); let signature_explicit = wallet .sign_message(test_message, Some(&main_address.to_string()), false) .await .expect("Failed to sign message with explicit address"); - + tracing::info!("Signature with explicit address: {}", signature_explicit); - assert!(!signature_explicit.is_empty(), "Signature should not be empty"); - + assert!( + !signature_explicit.is_empty(), + "Signature should not be empty" + ); + // When using the same key and same address (main address), signatures should be the same - assert_eq!(signature_spend, signature_explicit, "Signatures should be the same when using same key and address"); + assert_eq!( + signature_spend, signature_explicit, + "Signatures should be the same when using same key and address" + ); tracing::info!("Testing empty message signing"); let signature_empty = wallet .sign_message("", None, false) .await .expect("Failed to sign empty message"); - + tracing::info!("Signature for empty message: {}", signature_empty); - assert!(!signature_empty.is_empty(), "Signature should not be empty even for empty message"); + assert!( + !signature_empty.is_empty(), + "Signature should not be empty even for empty message" + ); tracing::info!("All message signing tests passed!"); -} \ No newline at end of file +} diff --git a/src-gui/package-lock.json b/src-gui/package-lock.json index c3fd2f11..2355f33f 100644 --- a/src-gui/package-lock.json +++ b/src-gui/package-lock.json @@ -11,20 +11,21 @@ "@emotion/react": "^11.14.0", "@emotion/styled": "^11.14.0", "@fontsource/roboto": "^5.1.0", - "@mui/icons-material": "^6.1.1", - "@mui/lab": "^6.0.0-beta.13", - "@mui/material": "^6.1.1", - "@mui/styles": "^5.4.8", + "@mui/icons-material": "^7.1.1", + "@mui/lab": "^7.0.0-beta.13", + "@mui/material": "^7.1.1", "@reduxjs/toolkit": "^2.3.0", "@tauri-apps/api": "^2.0.0", "@tauri-apps/plugin-cli": "^2.0.0", "@tauri-apps/plugin-clipboard-manager": "^2.0.0", + "@tauri-apps/plugin-dialog": "^2.0.0", "@tauri-apps/plugin-opener": "^2.0.0", "@tauri-apps/plugin-process": "^2.0.0", "@tauri-apps/plugin-shell": "^2.0.0", "@tauri-apps/plugin-store": "^2.0.0", - "@tauri-apps/plugin-updater": "^2.0.0", + "@tauri-apps/plugin-updater": "2.7.1", "@types/react-redux": "^7.1.34", + "boring-avatars": "^1.11.2", "humanize-duration": "^3.32.1", "jdenticon": "^3.3.0", "lodash": "^4.17.21", @@ -32,11 +33,11 @@ "notistack": "^3.0.1", "pino": "^9.2.0", "pino-pretty": "^11.2.1", - "react": "^18.2.0", - "react-dom": "^18.2.0", + "react": "^19.1.0", + "react-dom": "^19.1.0", "react-qr-code": "^2.0.15", - "react-redux": "^9.1.2", - "react-router-dom": "^6.28.0", + "react-redux": "^9.2.0", + "react-router-dom": "^7.6.1", "redux-persist": "^6.0.0", "semver": "^7.6.2", "virtua": "^0.33.2" @@ -49,9 +50,9 @@ "@testing-library/user-event": "^14.5.2", "@types/humanize-duration": "^3.27.4", "@types/lodash": "^4.17.6", - "@types/node": "^20.14.10", - "@types/react": "^18.2.15", - "@types/react-dom": "^18.2.7", + "@types/node": "^22.15.29", + "@types/react": "^19.1.6", + "@types/react-dom": "^19.1.5", "@types/react-is": "^19.0.0", "@types/semver": "^7.5.8", "@vitejs/plugin-react": "^4.2.1", @@ -464,9 +465,9 @@ } }, "node_modules/@babel/runtime": { - "version": "7.27.4", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.4.tgz", - "integrity": "sha512-t3yaEOuGu9NlIZ+hIeGbBjFtZT7j2cb2tg0fuaJKeGotchRjjLfrBA9Kwf8quhpP1EUuxModQg04q/mBwyg8uA==", + "version": "7.27.6", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.6.tgz", + "integrity": "sha512-vbavdySgbTTrmFE+EsiqUTzlOr5bzlnJtUv9PynGCAKvfQqjIXbvFdumPM/GxMDfyuGMJaJAU6TO4zc1Jf1i8Q==", "license": "MIT", "engines": { "node": ">=6.9.0" @@ -830,44 +831,6 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@floating-ui/core": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.1.tgz", - "integrity": "sha512-azI0DrjMMfIug/ExbBaeDVJXcY0a7EPvPjb2xAJPa4HeimBX+Z18HK8QQR3jb6356SnDDdxx+hinMLcJEDdOjw==", - "license": "MIT", - "dependencies": { - "@floating-ui/utils": "^0.2.9" - } - }, - "node_modules/@floating-ui/dom": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.1.tgz", - "integrity": "sha512-cwsmW/zyw5ltYTUeeYJ60CnQuPqmGwuGVhG9w0PRaRKkAyi38BT5CKrpIbb+jtahSwUl04cWzSx9ZOIxeS6RsQ==", - "license": "MIT", - "dependencies": { - "@floating-ui/core": "^1.7.1", - "@floating-ui/utils": "^0.2.9" - } - }, - "node_modules/@floating-ui/react-dom": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.3.tgz", - "integrity": "sha512-huMBfiU9UnQ2oBwIhgzyIiSpVgvlDstU8CX0AF+wS+KzmYMs0J2a3GwuFHV1Lz+jlrQGeC1fF+Nv0QoumyV0bA==", - "license": "MIT", - "dependencies": { - "@floating-ui/dom": "^1.0.0" - }, - "peerDependencies": { - "react": ">=16.8.0", - "react-dom": ">=16.8.0" - } - }, - "node_modules/@floating-ui/utils": { - "version": "0.2.9", - "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.9.tgz", - "integrity": "sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==", - "license": "MIT" - }, "node_modules/@fontsource/roboto": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/@fontsource/roboto/-/roboto-5.1.0.tgz", @@ -988,59 +951,10 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/@mui/base": { - "version": "5.0.0-beta.42", - "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.42.tgz", - "integrity": "sha512-fWRiUJVCHCPF+mxd5drn08bY2qRw3jj5f1SSQdUXmaJ/yKpk23ys8MgLO2KGVTRtbks/+ctRfgffGPbXifj0Ug==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.24.4", - "@floating-ui/react-dom": "^2.0.8", - "@mui/types": "^7.2.14", - "@mui/utils": "^6.0.0-alpha.1", - "@popperjs/core": "^2.11.8", - "clsx": "^2.1.0", - "prop-types": "^15.8.1" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mui-org" - }, - "peerDependencies": { - "@types/react": "^17.0.0 || ^18.0.0", - "react": "^17.0.0 || ^18.0.0", - "react-dom": "^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@mui/base/node_modules/@mui/types": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.4.3.tgz", - "integrity": "sha512-2UCEiK29vtiZTeLdS2d4GndBKacVyxGvReznGXGr+CzW/YhjIX+OHUdCIczZjzcRAgKBGmE9zCIgoV9FleuyRQ==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.27.1" - }, - "peerDependencies": { - "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, "node_modules/@mui/core-downloads-tracker": { - "version": "6.4.12", - "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-6.4.12.tgz", - "integrity": "sha512-M7IkG4LqSJfkY+thlQQHNkcS5NdmMDwLq/2RKoW40XR0mv/2BYb6X8fRnyaxP4zGdPD2M4MQdbzKihSVormJ7Q==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-7.2.0.tgz", + "integrity": "sha512-d49s7kEgI5iX40xb2YPazANvo7Bx0BLg/MNRwv+7BVpZUzXj1DaVCKlQTDex3gy/0jsCb4w7AY2uH4t4AJvSog==", "license": "MIT", "funding": { "type": "opencollective", @@ -1048,12 +962,12 @@ } }, "node_modules/@mui/icons-material": { - "version": "6.4.12", - "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-6.4.12.tgz", - "integrity": "sha512-ILTe3A2te0+Vb9TG4P1AZVmZFOjDDCV/b2nBmV1rNOmSu3Q/xkHghW+yMhMffwHcXklMlcajMlc4iFSkPbrTKw==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-7.2.0.tgz", + "integrity": "sha512-gRCspp3pfjHQyTmSOmYw7kUQTd9Udpdan4R8EnZvqPeoAtHnPzkvjBrBqzKaoAbbBp5bGF7BcD18zZJh4nwu0A==", "license": "MIT", "dependencies": { - "@babel/runtime": "^7.26.0" + "@babel/runtime": "^7.27.6" }, "engines": { "node": ">=14.0.0" @@ -1063,7 +977,7 @@ "url": "https://opencollective.com/mui-org" }, "peerDependencies": { - "@mui/material": "^6.4.12", + "@mui/material": "^7.2.0", "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react": "^17.0.0 || ^18.0.0 || ^19.0.0" }, @@ -1074,21 +988,20 @@ } }, "node_modules/@mui/lab": { - "version": "6.0.0-dev.240424162023-9968b4889d", - "resolved": "https://registry.npmjs.org/@mui/lab/-/lab-6.0.0-dev.240424162023-9968b4889d.tgz", - "integrity": "sha512-iKFAz7/EeWI4PaFsP4jK2FcYJmUYDBkn3XZwpQSAl5806yYq5J2U2mPQLuZBdhrH50gT2O98p95i3vwL4YBwAg==", + "version": "7.0.0-beta.14", + "resolved": "https://registry.npmjs.org/@mui/lab/-/lab-7.0.0-beta.14.tgz", + "integrity": "sha512-pn+ZvylDcBKQOo17oa/PhtIA/UFQFq8RvpN+r/jHrztz/CjMDju2CWBne0txvQ5JIS8uTIGp2/IsTa7II1g5wg==", "license": "MIT", "dependencies": { - "@babel/runtime": "^7.24.4", - "@mui/base": "5.0.0-beta.42", - "@mui/system": "^6.0.0-dev.240424162023-9968b4889d", - "@mui/types": "^7.2.14", - "@mui/utils": "^6.0.0-alpha.3", - "clsx": "^2.1.0", + "@babel/runtime": "^7.27.1", + "@mui/system": "^7.1.1", + "@mui/types": "^7.4.3", + "@mui/utils": "^7.1.1", + "clsx": "^2.1.1", "prop-types": "^15.8.1" }, "engines": { - "node": ">=12.0.0" + "node": ">=14.0.0" }, "funding": { "type": "opencollective", @@ -1097,10 +1010,11 @@ "peerDependencies": { "@emotion/react": "^11.5.0", "@emotion/styled": "^11.3.0", - "@mui/material": "^6.0.0-dev.240424162023-9968b4889d", - "@types/react": "^17.0.0 || ^18.0.0", - "react": "^17.0.0 || ^18.0.0", - "react-dom": "^17.0.0 || ^18.0.0" + "@mui/material": "^7.1.2", + "@mui/material-pigment-css": "^7.1.1", + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" }, "peerDependenciesMeta": { "@emotion/react": { @@ -1109,6 +1023,9 @@ "@emotion/styled": { "optional": true }, + "@mui/material-pigment-css": { + "optional": true + }, "@types/react": { "optional": true } @@ -1132,22 +1049,22 @@ } }, "node_modules/@mui/material": { - "version": "6.4.12", - "resolved": "https://registry.npmjs.org/@mui/material/-/material-6.4.12.tgz", - "integrity": "sha512-VqoLNS5UaNqoS1FybezZR/PaAvzbTmRe0Mx//afXbolIah43eozpX2FckaFffLvMoiSIyxx1+AMHyENTr2Es0Q==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@mui/material/-/material-7.2.0.tgz", + "integrity": "sha512-NTuyFNen5Z2QY+I242MDZzXnFIVIR6ERxo7vntFi9K1wCgSwvIl0HcAO2OOydKqqKApE6omRiYhpny1ZhGuH7Q==", "license": "MIT", "dependencies": { - "@babel/runtime": "^7.26.0", - "@mui/core-downloads-tracker": "^6.4.12", - "@mui/system": "^6.4.12", - "@mui/types": "~7.2.24", - "@mui/utils": "^6.4.9", + "@babel/runtime": "^7.27.6", + "@mui/core-downloads-tracker": "^7.2.0", + "@mui/system": "^7.2.0", + "@mui/types": "^7.4.4", + "@mui/utils": "^7.2.0", "@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-is": "^19.1.0", "react-transition-group": "^4.4.5" }, "engines": { @@ -1160,7 +1077,7 @@ "peerDependencies": { "@emotion/react": "^11.5.0", "@emotion/styled": "^11.3.0", - "@mui/material-pigment-css": "^6.4.12", + "@mui/material-pigment-css": "^7.2.0", "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" @@ -1180,56 +1097,16 @@ } } }, - "node_modules/@mui/private-theming": { - "version": "5.17.1", - "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.17.1.tgz", - "integrity": "sha512-XMxU0NTYcKqdsG8LRmSoxERPXwMbp16sIXPcLVgLGII/bVNagX0xaheWAwFv8+zDK7tI3ajllkuD3GZZE++ICQ==", + "node_modules/@mui/material/node_modules/@mui/types": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.4.4.tgz", + "integrity": "sha512-p63yhbX52MO/ajXC7hDHJA5yjzJekvWD3q4YDLl1rSg+OXLczMYPvTuSuviPRCgRX8+E42RXz1D/dz9SxPSlWg==", "license": "MIT", "dependencies": { - "@babel/runtime": "^7.23.9", - "@mui/utils": "^5.17.1", - "prop-types": "^15.8.1" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mui-org" + "@babel/runtime": "^7.27.6" }, "peerDependencies": { - "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", - "react": "^17.0.0 || ^18.0.0 || ^19.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@mui/private-theming/node_modules/@mui/utils": { - "version": "5.17.1", - "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.17.1.tgz", - "integrity": "sha512-jEZ8FTqInt2WzxDV8bhImWBqeQRD99c/id/fq83H0ER9tFl+sfZlaAoCdznGvbSQQ9ividMxqSV2c7cC1vBcQg==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.23.9", - "@mui/types": "~7.2.15", - "@types/prop-types": "^15.7.12", - "clsx": "^2.1.1", - "prop-types": "^15.8.1", - "react-is": "^19.0.0" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mui-org" - }, - "peerDependencies": { - "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", - "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0" }, "peerDependenciesMeta": { "@types/react": { @@ -1238,13 +1115,13 @@ } }, "node_modules/@mui/styled-engine": { - "version": "6.4.11", - "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-6.4.11.tgz", - "integrity": "sha512-74AUmlHXaGNbyUqdK/+NwDJOZqgRQw6BcNvhoWYLq3LGbLTkE+khaJ7soz6cIabE4CPYqO2/QAIU1Z/HEjjpcw==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-7.2.0.tgz", + "integrity": "sha512-yq08xynbrNYcB1nBcW9Fn8/h/iniM3ewRguGJXPIAbHvxEF7Pz95kbEEOAAhwzxMX4okhzvHmk0DFuC5ayvgIQ==", "license": "MIT", "dependencies": { - "@babel/runtime": "^7.26.0", - "@emotion/cache": "^11.13.5", + "@babel/runtime": "^7.27.6", + "@emotion/cache": "^11.14.0", "@emotion/serialize": "^1.3.3", "@emotion/sheet": "^1.4.0", "csstype": "^3.1.3", @@ -1271,88 +1148,17 @@ } } }, - "node_modules/@mui/styles": { - "version": "5.17.1", - "resolved": "https://registry.npmjs.org/@mui/styles/-/styles-5.17.1.tgz", - "integrity": "sha512-GxNtcD1jXjj1i81vyuaeNxCpph/ApxSxgJ+G8A2jUY5/bMOxXSmgUdupbB0JLexsDIqmaSqTePVN0jnMZc1iZQ==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.23.9", - "@emotion/hash": "^0.9.1", - "@mui/private-theming": "^5.17.1", - "@mui/types": "~7.2.15", - "@mui/utils": "^5.17.1", - "clsx": "^2.1.0", - "csstype": "^3.1.3", - "hoist-non-react-statics": "^3.3.2", - "jss": "^10.10.0", - "jss-plugin-camel-case": "^10.10.0", - "jss-plugin-default-unit": "^10.10.0", - "jss-plugin-global": "^10.10.0", - "jss-plugin-nested": "^10.10.0", - "jss-plugin-props-sort": "^10.10.0", - "jss-plugin-rule-value-function": "^10.10.0", - "jss-plugin-vendor-prefixer": "^10.10.0", - "prop-types": "^15.8.1" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mui-org" - }, - "peerDependencies": { - "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", - "react": "^17.0.0 || ^18.0.0 || ^19.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@mui/styles/node_modules/@mui/utils": { - "version": "5.17.1", - "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.17.1.tgz", - "integrity": "sha512-jEZ8FTqInt2WzxDV8bhImWBqeQRD99c/id/fq83H0ER9tFl+sfZlaAoCdznGvbSQQ9ividMxqSV2c7cC1vBcQg==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.23.9", - "@mui/types": "~7.2.15", - "@types/prop-types": "^15.7.12", - "clsx": "^2.1.1", - "prop-types": "^15.8.1", - "react-is": "^19.0.0" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mui-org" - }, - "peerDependencies": { - "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", - "react": "^17.0.0 || ^18.0.0 || ^19.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, "node_modules/@mui/system": { - "version": "6.4.12", - "resolved": "https://registry.npmjs.org/@mui/system/-/system-6.4.12.tgz", - "integrity": "sha512-fgEfm1qxpKCztndESeL1L0sLwA2c7josZ2w42D8OM3pbLee4bH2twEjoMo6qf7z2rNw1Uc9EU9haXeMoq0oTdQ==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@mui/system/-/system-7.2.0.tgz", + "integrity": "sha512-PG7cm/WluU6RAs+gNND2R9vDwNh+ERWxPkqTaiXQJGIFAyJ+VxhyKfzpdZNk0z0XdmBxxi9KhFOpgxjehf/O0A==", "license": "MIT", "dependencies": { - "@babel/runtime": "^7.26.0", - "@mui/private-theming": "^6.4.9", - "@mui/styled-engine": "^6.4.11", - "@mui/types": "~7.2.24", - "@mui/utils": "^6.4.9", + "@babel/runtime": "^7.27.6", + "@mui/private-theming": "^7.2.0", + "@mui/styled-engine": "^7.2.0", + "@mui/types": "^7.4.4", + "@mui/utils": "^7.2.0", "clsx": "^2.1.1", "csstype": "^3.1.3", "prop-types": "^15.8.1" @@ -1383,13 +1189,13 @@ } }, "node_modules/@mui/system/node_modules/@mui/private-theming": { - "version": "6.4.9", - "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-6.4.9.tgz", - "integrity": "sha512-LktcVmI5X17/Q5SkwjCcdOLBzt1hXuc14jYa7NPShog0GBDCDvKtcnP0V7a2s6EiVRlv7BzbWEJzH6+l/zaCxw==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-7.2.0.tgz", + "integrity": "sha512-y6N1Yt3T5RMxVFnCh6+zeSWBuQdNDm5/UlM0EAYZzZR/1u+XKJWYQmbpx4e+F+1EpkYi3Nk8KhPiQDi83M3zIw==", "license": "MIT", "dependencies": { - "@babel/runtime": "^7.26.0", - "@mui/utils": "^6.4.9", + "@babel/runtime": "^7.27.6", + "@mui/utils": "^7.2.0", "prop-types": "^15.8.1" }, "engines": { @@ -1409,11 +1215,14 @@ } } }, - "node_modules/@mui/types": { - "version": "7.2.24", - "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.24.tgz", - "integrity": "sha512-3c8tRt/CbWZ+pEg7QpSwbdxOk36EfmhbKf6AGZsD1EcLDLTSZoxxJ86FVtcjxvjuhdyBiWKSTGZFaXCnidO2kw==", + "node_modules/@mui/system/node_modules/@mui/types": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.4.4.tgz", + "integrity": "sha512-p63yhbX52MO/ajXC7hDHJA5yjzJekvWD3q4YDLl1rSg+OXLczMYPvTuSuviPRCgRX8+E42RXz1D/dz9SxPSlWg==", "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.27.6" + }, "peerDependencies": { "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0" }, @@ -1424,17 +1233,17 @@ } }, "node_modules/@mui/utils": { - "version": "6.4.9", - "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-6.4.9.tgz", - "integrity": "sha512-Y12Q9hbK9g+ZY0T3Rxrx9m2m10gaphDuUMgWxyV5kNJevVxXYCLclYUCC9vXaIk1/NdNDTcW2Yfr2OGvNFNmHg==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-7.2.0.tgz", + "integrity": "sha512-O0i1GQL6MDzhKdy9iAu5Yr0Sz1wZjROH1o3aoztuivdCXqEeQYnEjTDiRLGuFxI9zrUbTHBwobMyQH5sNtyacw==", "license": "MIT", "dependencies": { - "@babel/runtime": "^7.26.0", - "@mui/types": "~7.2.24", - "@types/prop-types": "^15.7.14", + "@babel/runtime": "^7.27.6", + "@mui/types": "^7.4.4", + "@types/prop-types": "^15.7.15", "clsx": "^2.1.1", "prop-types": "^15.8.1", - "react-is": "^19.0.0" + "react-is": "^19.1.0" }, "engines": { "node": ">=14.0.0" @@ -1453,6 +1262,23 @@ } } }, + "node_modules/@mui/utils/node_modules/@mui/types": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.4.4.tgz", + "integrity": "sha512-p63yhbX52MO/ajXC7hDHJA5yjzJekvWD3q4YDLl1rSg+OXLczMYPvTuSuviPRCgRX8+E42RXz1D/dz9SxPSlWg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.27.6" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -1517,19 +1343,6 @@ "redux": "^3.5.2 || ^4.0.0 || ^5.0.0" } }, - "node_modules/@redux-devtools/core/node_modules/@babel/runtime": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.0.tgz", - "integrity": "sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw==", - "dev": true, - "license": "MIT", - "dependencies": { - "regenerator-runtime": "^0.14.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@redux-devtools/instrument": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/@redux-devtools/instrument/-/instrument-2.2.0.tgz", @@ -1544,19 +1357,6 @@ "redux": "^3.4.0 || ^4.0.0 || ^5.0.0" } }, - "node_modules/@redux-devtools/instrument/node_modules/@babel/runtime": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.0.tgz", - "integrity": "sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw==", - "dev": true, - "license": "MIT", - "dependencies": { - "regenerator-runtime": "^0.14.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@redux-devtools/remote": { "version": "0.9.5", "resolved": "https://registry.npmjs.org/@redux-devtools/remote/-/remote-0.9.5.tgz", @@ -1575,19 +1375,6 @@ "redux": "^3.5.2 || ^4.0.0 || ^5.0.0" } }, - "node_modules/@redux-devtools/remote/node_modules/@babel/runtime": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.0.tgz", - "integrity": "sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw==", - "dev": true, - "license": "MIT", - "dependencies": { - "regenerator-runtime": "^0.14.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@redux-devtools/serialize": { "version": "0.4.2", "resolved": "https://registry.npmjs.org/@redux-devtools/serialize/-/serialize-0.4.2.tgz", @@ -1602,19 +1389,6 @@ "immutable": "^4.0.0" } }, - "node_modules/@redux-devtools/serialize/node_modules/@babel/runtime": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.0.tgz", - "integrity": "sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw==", - "dev": true, - "license": "MIT", - "dependencies": { - "regenerator-runtime": "^0.14.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@redux-devtools/utils": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/@redux-devtools/utils/-/utils-3.1.1.tgz", @@ -1638,19 +1412,6 @@ "redux": "^4.0.0 || ^5.0.0" } }, - "node_modules/@redux-devtools/utils/node_modules/@babel/runtime": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.0.tgz", - "integrity": "sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw==", - "dev": true, - "license": "MIT", - "dependencies": { - "regenerator-runtime": "^0.14.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@redux-devtools/utils/node_modules/nanoid": { "version": "5.1.5", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.1.5.tgz", @@ -1694,15 +1455,6 @@ } } }, - "node_modules/@remix-run/router": { - "version": "1.21.0", - "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.21.0.tgz", - "integrity": "sha512-xfSkCAchbdG5PnbrKqFWwia4Bi61nH+wm8wLEqfHDyp7Y3dZzgqS2itV8i4gAq9pC2HsTpwyBC6Ds8VHZ96JlA==", - "license": "MIT", - "engines": { - "node": ">=14.0.0" - } - }, "node_modules/@rollup/plugin-virtual": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/@rollup/plugin-virtual/-/plugin-virtual-3.0.2.tgz", @@ -1809,9 +1561,9 @@ } }, "node_modules/@tauri-apps/api": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@tauri-apps/api/-/api-2.1.1.tgz", - "integrity": "sha512-fzUfFFKo4lknXGJq8qrCidkUcKcH2UHhfaaCNt4GzgzGaW2iS26uFOg4tS3H4P8D6ZEeUxtiD5z0nwFF0UN30A==", + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@tauri-apps/api/-/api-2.6.0.tgz", + "integrity": "sha512-hRNcdercfgpzgFrMXWwNDBN0B7vNzOzRepy6ZAmhxi5mDLVPNrTpo9MGg2tN/F7JRugj4d2aF7E1rtPXAHaetg==", "license": "Apache-2.0 OR MIT", "funding": { "type": "opencollective", @@ -1882,6 +1634,15 @@ "@tauri-apps/api": "^2.0.0" } }, + "node_modules/@tauri-apps/plugin-dialog": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@tauri-apps/plugin-dialog/-/plugin-dialog-2.3.0.tgz", + "integrity": "sha512-ylSBvYYShpGlKKh732ZuaHyJ5Ie1JR71QCXewCtsRLqGdc8Is4xWdz6t43rzXyvkItM9syNPMvFVcvjgEy+/GA==", + "license": "MIT OR Apache-2.0", + "dependencies": { + "@tauri-apps/api": "^2.6.0" + } + }, "node_modules/@tauri-apps/plugin-opener": { "version": "2.2.6", "resolved": "https://registry.npmjs.org/@tauri-apps/plugin-opener/-/plugin-opener-2.2.6.tgz", @@ -1919,9 +1680,9 @@ } }, "node_modules/@tauri-apps/plugin-updater": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@tauri-apps/plugin-updater/-/plugin-updater-2.0.0.tgz", - "integrity": "sha512-N0cl71g7RPr7zK2Fe5aoIwzw14NcdLcz7XMGFWZVjprsqgDRWoxbnUkknyCQMZthjhGkppCd/wN2MIsUz+eAhQ==", + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@tauri-apps/plugin-updater/-/plugin-updater-2.7.1.tgz", + "integrity": "sha512-1OPqEY/z7NDVSeTEMIhD2ss/vXWdpfZ5Th2Mk0KtPR/RA6FKuOTDGZQhxoyYBk0pcZJ+nNZUbl/IujDCLBApjA==", "license": "MIT OR Apache-2.0", "dependencies": { "@tauri-apps/api": "^2.0.0" @@ -1976,19 +1737,6 @@ } } }, - "node_modules/@testing-library/react/node_modules/@babel/runtime": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz", - "integrity": "sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==", - "dev": true, - "license": "MIT", - "dependencies": { - "regenerator-runtime": "^0.14.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@testing-library/user-event": { "version": "14.5.2", "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-14.5.2.tgz", @@ -2102,13 +1850,12 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "20.17.9", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.9.tgz", - "integrity": "sha512-0JOXkRyLanfGPE2QRCwgxhzlBAvaRdCNMcvbd7jFfpmD4eEXll7LRwy5ymJmyeZqk7Nh7eD2LeUyQ68BbndmXw==", - "dev": true, + "version": "22.16.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.16.2.tgz", + "integrity": "sha512-Cdqa/eJTvt4fC4wmq1Mcc0CPUjp/Qy2FGqLza3z3pKymsI969TcZ54diNJv8UYUgeWxyb8FSbCkhdR6WqmUFhA==", "license": "MIT", "dependencies": { - "undici-types": "~6.19.2" + "undici-types": "~6.21.0" } }, "node_modules/@types/parse-json": { @@ -2118,29 +1865,28 @@ "license": "MIT" }, "node_modules/@types/prop-types": { - "version": "15.7.14", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.14.tgz", - "integrity": "sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==", + "version": "15.7.15", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz", + "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==", "license": "MIT" }, "node_modules/@types/react": { - "version": "18.3.13", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.13.tgz", - "integrity": "sha512-ii/gswMmOievxAJed4PAHT949bpYjPKXvXo1v6cRB/kqc2ZR4n+SgyCyvyc5Fec5ez8VnUumI1Vk7j6fRyRogg==", + "version": "19.1.8", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.8.tgz", + "integrity": "sha512-AwAfQ2Wa5bCx9WP8nZL2uMZWod7J7/JSplxbTmBQ5ms6QpqNYm672H0Vu9ZVKVngQ+ii4R/byguVEUZQyeg44g==", "license": "MIT", "dependencies": { - "@types/prop-types": "*", "csstype": "^3.0.2" } }, "node_modules/@types/react-dom": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.1.tgz", - "integrity": "sha512-qW1Mfv8taImTthu4KoXgDfLuk4bydU6Q/TkADnDWWHwi4NX4BR+LWfTp2sVmTqRrsHvyDDTelgelxJ+SsejKKQ==", + "version": "19.1.6", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.6.tgz", + "integrity": "sha512-4hOiT/dwO8Ko0gV1m/TJZYk3y0KBnY9vzDh7W+DH17b2HFSOGgdj33dhihPeuy3l0q23+4e+hoXHV6hCC4dCXw==", "dev": true, "license": "MIT", - "dependencies": { - "@types/react": "*" + "peerDependencies": { + "@types/react": "^19.0.0" } }, "node_modules/@types/react-is": { @@ -2165,18 +1911,6 @@ "redux": "^4.0.0" } }, - "node_modules/@types/react-redux/node_modules/@babel/runtime": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz", - "integrity": "sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==", - "license": "MIT", - "dependencies": { - "regenerator-runtime": "^0.14.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@types/react-redux/node_modules/redux": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz", @@ -2195,12 +1929,6 @@ "@types/react": "*" } }, - "node_modules/@types/react/node_modules/@types/prop-types": { - "version": "15.7.13", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.13.tgz", - "integrity": "sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA==", - "license": "MIT" - }, "node_modules/@types/semver": { "version": "7.5.8", "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", @@ -2209,9 +1937,9 @@ "license": "MIT" }, "node_modules/@types/use-sync-external-store": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz", - "integrity": "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA==", + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz", + "integrity": "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==", "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { @@ -2893,18 +2621,6 @@ "npm": ">=6" } }, - "node_modules/babel-plugin-macros/node_modules/@babel/runtime": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz", - "integrity": "sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==", - "license": "MIT", - "dependencies": { - "regenerator-runtime": "^0.14.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/babel-plugin-macros/node_modules/is-core-module": { "version": "2.16.1", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", @@ -2978,6 +2694,12 @@ "safe-buffer": "^5.1.1" } }, + "node_modules/boring-avatars": { + "version": "1.11.2", + "resolved": "https://registry.npmjs.org/boring-avatars/-/boring-avatars-1.11.2.tgz", + "integrity": "sha512-3+wkwPeObwS4R37FGXMYViqc4iTrIRj5yzfX9Qy4mnpZ26sX41dGMhsAgmKks1r/uufY1pl4vpgzMWHYfJRb2A==", + "license": "MIT" + }, "node_modules/brace-expansion": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", @@ -3135,21 +2857,6 @@ "@types/node": "*" } }, - "node_modules/canvas-renderer/node_modules/@types/node": { - "version": "22.10.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.1.tgz", - "integrity": "sha512-qKgsUwfHZV2WCWLAnVP1JqnpE6Im6h3Y0+fYgMTasNQ7V++CBX5OT1as0g0f+OyubbFqhf6XVNIsmN4IIhEgGQ==", - "license": "MIT", - "dependencies": { - "undici-types": "~6.20.0" - } - }, - "node_modules/canvas-renderer/node_modules/undici-types": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", - "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", - "license": "MIT" - }, "node_modules/chai": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/chai/-/chai-5.1.2.tgz", @@ -3264,6 +2971,15 @@ "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", "license": "MIT" }, + "node_modules/cookie": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz", + "integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/core-util-is": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", @@ -3302,28 +3018,6 @@ "node": ">= 8" } }, - "node_modules/css-vendor": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/css-vendor/-/css-vendor-2.0.8.tgz", - "integrity": "sha512-x9Aq0XTInxrkuFeHKbYC7zWY8ai7qJ04Kxd9MnvbC1uO5DagxoHQjm4JvG+vCdXOoFtCjbL2XSZfxmoYa9uQVQ==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.8.3", - "is-in-browser": "^1.0.2" - } - }, - "node_modules/css-vendor/node_modules/@babel/runtime": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz", - "integrity": "sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==", - "license": "MIT", - "dependencies": { - "regenerator-runtime": "^0.14.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/csstype": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", @@ -3539,18 +3233,6 @@ "csstype": "^3.0.2" } }, - "node_modules/dom-helpers/node_modules/@babel/runtime": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz", - "integrity": "sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==", - "license": "MIT", - "dependencies": { - "regenerator-runtime": "^0.14.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/ecdsa-sig-formatter": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", @@ -4611,12 +4293,6 @@ "integrity": "sha512-inh5wue5XdfObhu/IGEMiA1nUXigSGcaKNemcbLRKa7jXYGDZXr3LoT9pTIzq2hPEbld7w/qv9h+ikWGz8fL1g==", "license": "Unlicense" }, - "node_modules/hyphenate-style-name": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.1.0.tgz", - "integrity": "sha512-WDC/ui2VVRrz3jOVi+XtjqkDjiVjTtFaAGiW37k6b+ohyQ5wYDOGkvCZa8+H0nx3gyvv0+BST9xuOgIyGQ00gw==", - "license": "BSD-3-Clause" - }, "node_modules/ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", @@ -4938,12 +4614,6 @@ "node": ">=0.10.0" } }, - "node_modules/is-in-browser": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/is-in-browser/-/is-in-browser-1.1.3.tgz", - "integrity": "sha512-FeXIBgG/CPGd/WUxuEyvgGTEfwiG9Z4EKGxjNMRqviiIIfsmgrpnHLffEDdwUHqNva1VEW91o3xBT/m8Elgl9g==", - "license": "MIT" - }, "node_modules/is-ip": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/is-ip/-/is-ip-3.1.0.tgz", @@ -5356,192 +5026,6 @@ "node": ">=10" } }, - "node_modules/jss": { - "version": "10.10.0", - "resolved": "https://registry.npmjs.org/jss/-/jss-10.10.0.tgz", - "integrity": "sha512-cqsOTS7jqPsPMjtKYDUpdFC0AbhYFLTcuGRqymgmdJIeQ8cH7+AgX7YSgQy79wXloZq2VvATYxUOUQEvS1V/Zw==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.3.1", - "csstype": "^3.0.2", - "is-in-browser": "^1.1.3", - "tiny-warning": "^1.0.2" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/jss" - } - }, - "node_modules/jss-plugin-camel-case": { - "version": "10.10.0", - "resolved": "https://registry.npmjs.org/jss-plugin-camel-case/-/jss-plugin-camel-case-10.10.0.tgz", - "integrity": "sha512-z+HETfj5IYgFxh1wJnUAU8jByI48ED+v0fuTuhKrPR+pRBYS2EDwbusU8aFOpCdYhtRc9zhN+PJ7iNE8pAWyPw==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.3.1", - "hyphenate-style-name": "^1.0.3", - "jss": "10.10.0" - } - }, - "node_modules/jss-plugin-camel-case/node_modules/@babel/runtime": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz", - "integrity": "sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==", - "license": "MIT", - "dependencies": { - "regenerator-runtime": "^0.14.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/jss-plugin-default-unit": { - "version": "10.10.0", - "resolved": "https://registry.npmjs.org/jss-plugin-default-unit/-/jss-plugin-default-unit-10.10.0.tgz", - "integrity": "sha512-SvpajxIECi4JDUbGLefvNckmI+c2VWmP43qnEy/0eiwzRUsafg5DVSIWSzZe4d2vFX1u9nRDP46WCFV/PXVBGQ==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.3.1", - "jss": "10.10.0" - } - }, - "node_modules/jss-plugin-default-unit/node_modules/@babel/runtime": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz", - "integrity": "sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==", - "license": "MIT", - "dependencies": { - "regenerator-runtime": "^0.14.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/jss-plugin-global": { - "version": "10.10.0", - "resolved": "https://registry.npmjs.org/jss-plugin-global/-/jss-plugin-global-10.10.0.tgz", - "integrity": "sha512-icXEYbMufiNuWfuazLeN+BNJO16Ge88OcXU5ZDC2vLqElmMybA31Wi7lZ3lf+vgufRocvPj8443irhYRgWxP+A==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.3.1", - "jss": "10.10.0" - } - }, - "node_modules/jss-plugin-global/node_modules/@babel/runtime": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz", - "integrity": "sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==", - "license": "MIT", - "dependencies": { - "regenerator-runtime": "^0.14.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/jss-plugin-nested": { - "version": "10.10.0", - "resolved": "https://registry.npmjs.org/jss-plugin-nested/-/jss-plugin-nested-10.10.0.tgz", - "integrity": "sha512-9R4JHxxGgiZhurDo3q7LdIiDEgtA1bTGzAbhSPyIOWb7ZubrjQe8acwhEQ6OEKydzpl8XHMtTnEwHXCARLYqYA==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.3.1", - "jss": "10.10.0", - "tiny-warning": "^1.0.2" - } - }, - "node_modules/jss-plugin-nested/node_modules/@babel/runtime": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz", - "integrity": "sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==", - "license": "MIT", - "dependencies": { - "regenerator-runtime": "^0.14.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/jss-plugin-props-sort": { - "version": "10.10.0", - "resolved": "https://registry.npmjs.org/jss-plugin-props-sort/-/jss-plugin-props-sort-10.10.0.tgz", - "integrity": "sha512-5VNJvQJbnq/vRfje6uZLe/FyaOpzP/IH1LP+0fr88QamVrGJa0hpRRyAa0ea4U/3LcorJfBFVyC4yN2QC73lJg==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.3.1", - "jss": "10.10.0" - } - }, - "node_modules/jss-plugin-props-sort/node_modules/@babel/runtime": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz", - "integrity": "sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==", - "license": "MIT", - "dependencies": { - "regenerator-runtime": "^0.14.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/jss-plugin-rule-value-function": { - "version": "10.10.0", - "resolved": "https://registry.npmjs.org/jss-plugin-rule-value-function/-/jss-plugin-rule-value-function-10.10.0.tgz", - "integrity": "sha512-uEFJFgaCtkXeIPgki8ICw3Y7VMkL9GEan6SqmT9tqpwM+/t+hxfMUdU4wQ0MtOiMNWhwnckBV0IebrKcZM9C0g==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.3.1", - "jss": "10.10.0", - "tiny-warning": "^1.0.2" - } - }, - "node_modules/jss-plugin-rule-value-function/node_modules/@babel/runtime": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz", - "integrity": "sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==", - "license": "MIT", - "dependencies": { - "regenerator-runtime": "^0.14.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/jss-plugin-vendor-prefixer": { - "version": "10.10.0", - "resolved": "https://registry.npmjs.org/jss-plugin-vendor-prefixer/-/jss-plugin-vendor-prefixer-10.10.0.tgz", - "integrity": "sha512-UY/41WumgjW8r1qMCO8l1ARg7NHnfRVWRhZ2E2m0DMYsr2DD91qIXLyNhiX83hHswR7Wm4D+oDYNC1zWCJWtqg==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.3.1", - "css-vendor": "^2.0.8", - "jss": "10.10.0" - } - }, - "node_modules/jss-plugin-vendor-prefixer/node_modules/@babel/runtime": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz", - "integrity": "sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==", - "license": "MIT", - "dependencies": { - "regenerator-runtime": "^0.14.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/jss/node_modules/@babel/runtime": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz", - "integrity": "sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==", - "license": "MIT", - "dependencies": { - "regenerator-runtime": "^0.14.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/jsx-ast-utils": { "version": "3.3.5", "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", @@ -6623,28 +6107,24 @@ "license": "MIT" }, "node_modules/react": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", - "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz", + "integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==", "license": "MIT", - "dependencies": { - "loose-envify": "^1.1.0" - }, "engines": { "node": ">=0.10.0" } }, "node_modules/react-dom": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", - "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz", + "integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==", "license": "MIT", "dependencies": { - "loose-envify": "^1.1.0", - "scheduler": "^0.23.2" + "scheduler": "^0.26.0" }, "peerDependencies": { - "react": "^18.3.1" + "react": "^19.1.0" } }, "node_modules/react-is": { @@ -6667,17 +6147,17 @@ } }, "node_modules/react-redux": { - "version": "9.1.2", - "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.1.2.tgz", - "integrity": "sha512-0OA4dhM1W48l3uzmv6B7TXPCGmokUU4p1M44DGN2/D9a1FjVPukVjER1PcPX97jIg6aUeLq1XJo1IpfbgULn0w==", + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.2.0.tgz", + "integrity": "sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g==", "license": "MIT", "dependencies": { - "@types/use-sync-external-store": "^0.0.3", - "use-sync-external-store": "^1.0.0" + "@types/use-sync-external-store": "^0.0.6", + "use-sync-external-store": "^1.4.0" }, "peerDependencies": { - "@types/react": "^18.2.25", - "react": "^18.0", + "@types/react": "^18.2.25 || ^19", + "react": "^18.0 || ^19", "redux": "^5.0.0" }, "peerDependenciesMeta": { @@ -6700,35 +6180,41 @@ } }, "node_modules/react-router": { - "version": "6.28.0", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.28.0.tgz", - "integrity": "sha512-HrYdIFqdrnhDw0PqG/AKjAqEqM7AvxCz0DQ4h2W8k6nqmc5uRBYDag0SBxx9iYz5G8gnuNVLzUe13wl9eAsXXg==", + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.6.3.tgz", + "integrity": "sha512-zf45LZp5skDC6I3jDLXQUu0u26jtuP4lEGbc7BbdyxenBN1vJSTA18czM2D+h5qyMBuMrD+9uB+mU37HIoKGRA==", "license": "MIT", "dependencies": { - "@remix-run/router": "1.21.0" + "cookie": "^1.0.1", + "set-cookie-parser": "^2.6.0" }, "engines": { - "node": ">=14.0.0" + "node": ">=20.0.0" }, "peerDependencies": { - "react": ">=16.8" + "react": ">=18", + "react-dom": ">=18" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } } }, "node_modules/react-router-dom": { - "version": "6.28.0", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.28.0.tgz", - "integrity": "sha512-kQ7Unsl5YdyOltsPGl31zOjLrDv+m2VcIEcIHqYYD3Lp0UppLjrzcfJqDJwXxFw3TH/yvapbnUvPlAj7Kx5nbg==", + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.6.3.tgz", + "integrity": "sha512-DiWJm9qdUAmiJrVWaeJdu4TKu13+iB/8IEi0EW/XgaHCjW/vWGrwzup0GVvaMteuZjKnh5bEvJP/K0MDnzawHw==", "license": "MIT", "dependencies": { - "@remix-run/router": "1.21.0", - "react-router": "6.28.0" + "react-router": "7.6.3" }, "engines": { - "node": ">=14.0.0" + "node": ">=20.0.0" }, "peerDependencies": { - "react": ">=16.8", - "react-dom": ">=16.8" + "react": ">=18", + "react-dom": ">=18" } }, "node_modules/react-transition-group": { @@ -6747,18 +6233,6 @@ "react-dom": ">=16.6.0" } }, - "node_modules/react-transition-group/node_modules/@babel/runtime": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz", - "integrity": "sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==", - "license": "MIT", - "dependencies": { - "regenerator-runtime": "^0.14.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/readable-stream": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", @@ -6853,12 +6327,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/regenerator-runtime": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", - "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", - "license": "MIT" - }, "node_modules/regexp.prototype.flags": { "version": "1.5.3", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.3.tgz", @@ -7072,13 +6540,10 @@ "license": "MIT" }, "node_modules/scheduler": { - "version": "0.23.2", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", - "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", - "license": "MIT", - "dependencies": { - "loose-envify": "^1.1.0" - } + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz", + "integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==", + "license": "MIT" }, "node_modules/secure-json-parse": { "version": "2.7.0", @@ -7098,6 +6563,12 @@ "node": ">=10" } }, + "node_modules/set-cookie-parser": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz", + "integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==", + "license": "MIT" + }, "node_modules/set-function-length": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", @@ -7475,12 +6946,6 @@ "xtend": "~4.0.1" } }, - "node_modules/tiny-warning": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz", - "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==", - "license": "MIT" - }, "node_modules/tinybench": { "version": "2.9.0", "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", @@ -7730,10 +7195,9 @@ } }, "node_modules/undici-types": { - "version": "6.19.8", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", - "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", - "dev": true, + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", "license": "MIT" }, "node_modules/update-browserslist-db": { @@ -7778,12 +7242,12 @@ } }, "node_modules/use-sync-external-store": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.2.tgz", - "integrity": "sha512-PElTlVMwpblvbNqQ82d2n6RjStvdSoNe9FG28kNfz3WiXilJm4DdNkEzRhCZuIDwY8U08WVihhGR5iRqAwfDiw==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.5.0.tgz", + "integrity": "sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==", "license": "MIT", "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "node_modules/util-deprecate": { diff --git a/src-gui/package.json b/src-gui/package.json index fa99b61f..e2a6f048 100644 --- a/src-gui/package.json +++ b/src-gui/package.json @@ -4,9 +4,9 @@ "version": "0.7.0", "type": "module", "scripts": { - "check-bindings": "typeshare --lang=typescript --output-file __temp_bindings.ts ../swap/src ../monero-rpc-pool/src ../electrum-pool/src && dprint fmt __temp_bindings.ts && diff -wbB __temp_bindings.ts ./src/models/tauriModel.ts && rm __temp_bindings.ts", - "gen-bindings-verbose": "RUST_LOG=debug RUST_BACKTRACE=1 typeshare --lang=typescript --output-file ./src/models/tauriModel.ts ../swap/src ../monero-rpc-pool/src ../electrum-pool/src && dprint fmt ./src/models/tauriModel.ts", - "gen-bindings": "typeshare --lang=typescript --output-file ./src/models/tauriModel.ts ../swap/src ../monero-rpc-pool/src ../electrum-pool/src && dprint fmt ./src/models/tauriModel.ts", + "check-bindings": "typeshare --lang=typescript --output-file __temp_bindings.ts ../swap/src ../monero-rpc-pool/src ../electrum-pool/src ../monero-sys/src && dprint fmt __temp_bindings.ts && diff -wbB __temp_bindings.ts ./src/models/tauriModel.ts && rm __temp_bindings.ts", + "gen-bindings-verbose": "RUST_LOG=debug RUST_BACKTRACE=1 typeshare --lang=typescript --output-file ./src/models/tauriModel.ts ../swap/src ../monero-rpc-pool/src ../electrum-pool/src ../monero-sys/src && dprint fmt ./src/models/tauriModel.ts", + "gen-bindings": "typeshare --lang=typescript --output-file ./src/models/tauriModel.ts ../swap/src ../monero-rpc-pool/src ../electrum-pool/src ../monero-sys/src && dprint fmt ./src/models/tauriModel.ts", "test": "vitest", "test:ui": "vitest --ui", "dev": "vite", @@ -22,10 +22,12 @@ "@mui/icons-material": "^7.1.1", "@mui/lab": "^7.0.0-beta.13", "@mui/material": "^7.1.1", + "@mui/x-date-pickers": "^8.8.0", "@reduxjs/toolkit": "^2.3.0", "@tauri-apps/api": "^2.0.0", "@tauri-apps/plugin-cli": "^2.0.0", "@tauri-apps/plugin-clipboard-manager": "^2.0.0", + "@tauri-apps/plugin-dialog": "^2.0.0", "@tauri-apps/plugin-opener": "^2.0.0", "@tauri-apps/plugin-process": "^2.0.0", "@tauri-apps/plugin-shell": "^2.0.0", @@ -33,6 +35,7 @@ "@tauri-apps/plugin-updater": "2.7.1", "@types/react-redux": "^7.1.34", "boring-avatars": "^1.11.2", + "dayjs": "^1.11.13", "humanize-duration": "^3.32.1", "jdenticon": "^3.3.0", "lodash": "^4.17.21", diff --git a/src-gui/src/models/tauriModelExt.ts b/src-gui/src/models/tauriModelExt.ts index d5c5a573..0d058442 100644 --- a/src-gui/src/models/tauriModelExt.ts +++ b/src-gui/src/models/tauriModelExt.ts @@ -8,6 +8,7 @@ import { SelectMakerDetails, TauriBackgroundProgress, TauriSwapProgressEvent, + SendMoneroDetails, } from "./tauriModel"; export type TauriSwapProgressEventType = TauriSwapProgressEvent["type"]; @@ -310,10 +311,13 @@ export type PendingSelectMakerApprovalRequest = PendingApprovalRequest & { request: { type: "SelectMaker"; content: SelectMakerDetails }; }; -export interface SortableQuoteWithAddress extends QuoteWithAddress { - expiration_ts?: number; - request_id?: string; -} +export type PendingSendMoneroApprovalRequest = PendingApprovalRequest & { + request: { type: "SendMonero"; content: SendMoneroDetails }; +}; + +export type PendingPasswordApprovalRequest = PendingApprovalRequest & { + request: { type: "PasswordRequest"; content: { wallet_path: string } }; +}; export function isPendingSelectMakerApprovalEvent( event: ApprovalRequest, @@ -327,6 +331,30 @@ export function isPendingSelectMakerApprovalEvent( return event.request.type === "SelectMaker"; } +export function isPendingSendMoneroApprovalEvent( + event: ApprovalRequest, +): event is PendingSendMoneroApprovalRequest { + // Check if the request is pending + if (event.request_status.state !== "Pending") { + return false; + } + + // Check if the request is a SendMonero request + return event.request.type === "SendMonero"; +} + +export function isPendingPasswordApprovalEvent( + event: ApprovalRequest, +): event is PendingPasswordApprovalRequest { + // Check if the request is pending + if (event.request_status.state !== "Pending") { + return false; + } + + // Check if the request is a PasswordRequest request + return event.request.type === "PasswordRequest"; +} + /** * Checks if any funds have been locked yet based on the swap progress event * Returns true for events where funds have been locked diff --git a/src-gui/src/renderer/background.ts b/src-gui/src/renderer/background.ts index e74a1262..f610ac45 100644 --- a/src-gui/src/renderer/background.ts +++ b/src-gui/src/renderer/background.ts @@ -24,9 +24,16 @@ import { listSellersAtRendezvousPoint, refreshApprovals, updateAllNodeStatuses, + fetchAndUpdateBackgroundItems, + fetchAndUpdateApprovalItems, } from "./rpc"; import { store } from "./store/storeRenderer"; import { exhaustiveGuard } from "utils/typescriptUtils"; +import { + setBalance, + setHistory, + setSyncProgress, +} from "store/features/walletSlice"; const TAURI_UNIFIED_EVENT_CHANNEL_NAME = "tauri-unified-event"; @@ -45,7 +52,7 @@ const UPDATE_RATE_INTERVAL = 5 * 60 * 1_000; // Fetch all conversations every 10 minutes const FETCH_CONVERSATIONS_INTERVAL = 10 * 60 * 1_000; -// Fetch pending approvals every 10 seconds +// Fetch pending approvals every 2 seconds const FETCH_PENDING_APPROVALS_INTERVAL = 2 * 1_000; function setIntervalImmediate(callback: () => void, interval: number): void { @@ -137,6 +144,19 @@ export async function setupBackgroundTasks(): Promise { store.dispatch(poolStatusReceived(eventData)); break; + case "MoneroWalletUpdate": + console.log("MoneroWalletUpdate", eventData); + if (eventData.type === "BalanceChange") { + store.dispatch(setBalance(eventData.content)); + } + if (eventData.type === "HistoryUpdate") { + store.dispatch(setHistory(eventData.content)); + } + if (eventData.type === "SyncProgress") { + store.dispatch(setSyncProgress(eventData.content)); + } + break; + default: exhaustiveGuard(channelName); } diff --git a/src-gui/src/renderer/components/App.tsx b/src-gui/src/renderer/components/App.tsx index 78577fce..dcf62cf3 100644 --- a/src-gui/src/renderer/components/App.tsx +++ b/src-gui/src/renderer/components/App.tsx @@ -20,7 +20,11 @@ import { setupBackgroundTasks } from "renderer/background"; import "@fontsource/roboto"; import FeedbackPage from "./pages/feedback/FeedbackPage"; import IntroductionModal from "./modal/introduction/IntroductionModal"; +import MoneroWalletPage from "./pages/monero/MoneroWalletPage"; import SeedSelectionDialog from "./modal/seed-selection/SeedSelectionDialog"; +import { LocalizationProvider } from "@mui/x-date-pickers/LocalizationProvider"; +import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs"; +import PasswordEntryDialog from "./modal/password-entry/PasswordEntryDialog"; declare module "@mui/material/styles" { interface Theme { @@ -44,16 +48,19 @@ export default function App() { return ( - - - - - - - - - - + + + + + + + + + + + + + ); @@ -70,12 +77,13 @@ function InnerContent() { }} > + } /> + } /> } /> } /> - } /> + } /> } /> } /> - } /> ); diff --git a/src-gui/src/renderer/components/alert/DaemonStatusAlert.tsx b/src-gui/src/renderer/components/alert/DaemonStatusAlert.tsx index a71ae357..e561a5b2 100644 --- a/src-gui/src/renderer/components/alert/DaemonStatusAlert.tsx +++ b/src-gui/src/renderer/components/alert/DaemonStatusAlert.tsx @@ -186,18 +186,11 @@ export default function DaemonStatusAlert() { const contextStatus = useAppSelector((s) => s.rpc.status); const navigate = useNavigate(); - if ( - contextStatus === null || - contextStatus === TauriContextStatusEvent.NotInitialized - ) { - return ( - - Checking for available remote nodes - - ); - } - switch (contextStatus) { + case null: + return null; + case TauriContextStatusEvent.NotInitialized: + return null; case TauriContextStatusEvent.Initializing: return null; case TauriContextStatusEvent.Available: diff --git a/src-gui/src/renderer/components/alert/FundsLeftInWalletAlert.tsx b/src-gui/src/renderer/components/alert/FundsLeftInWalletAlert.tsx index 8db15ff7..c1a571e3 100644 --- a/src-gui/src/renderer/components/alert/FundsLeftInWalletAlert.tsx +++ b/src-gui/src/renderer/components/alert/FundsLeftInWalletAlert.tsx @@ -16,7 +16,7 @@ export default function FundsLeftInWalletAlert() { diff --git a/src-gui/src/renderer/components/alert/SwapStatusAlert/SwapStatusAlert.tsx b/src-gui/src/renderer/components/alert/SwapStatusAlert/SwapStatusAlert.tsx index cec37199..1e4d3cdb 100644 --- a/src-gui/src/renderer/components/alert/SwapStatusAlert/SwapStatusAlert.tsx +++ b/src-gui/src/renderer/components/alert/SwapStatusAlert/SwapStatusAlert.tsx @@ -1,3 +1,4 @@ +import React from "react"; import { Box, Alert, AlertTitle } from "@mui/material"; import { BobStateName, diff --git a/src-gui/src/renderer/components/inputs/MoneroAddressTextField.tsx b/src-gui/src/renderer/components/inputs/MoneroAddressTextField.tsx index e0b570c2..44977a26 100644 --- a/src-gui/src/renderer/components/inputs/MoneroAddressTextField.tsx +++ b/src-gui/src/renderer/components/inputs/MoneroAddressTextField.tsx @@ -23,7 +23,7 @@ type MoneroAddressTextFieldProps = TextFieldProps & { address: string; onAddressChange: (address: string) => void; onAddressValidityChange: (valid: boolean) => void; - helperText: string; + helperText?: string; }; export default function MoneroAddressTextField({ diff --git a/src-gui/src/renderer/components/inputs/NumberInput.tsx b/src-gui/src/renderer/components/inputs/NumberInput.tsx new file mode 100644 index 00000000..4fa2f181 --- /dev/null +++ b/src-gui/src/renderer/components/inputs/NumberInput.tsx @@ -0,0 +1,178 @@ +import { useEffect, useRef, useState } from "react"; +import { darken, useTheme } from "@mui/material"; + +interface NumberInputProps { + value: string; + onChange: (value: string) => void; + placeholder?: string; + fontSize?: string; + fontWeight?: number; + textAlign?: "left" | "center" | "right"; + minWidth?: number; + step?: number; + largeStep?: number; + className?: string; + style?: React.CSSProperties; +} + +export default function NumberInput({ + value, + onChange, + placeholder = "0.00", + fontSize = "2em", + fontWeight = 600, + textAlign = "right", + minWidth = 60, + step = 0.001, + largeStep = 0.1, + className, + style, +}: NumberInputProps) { + const inputRef = useRef(null); + const [inputWidth, setInputWidth] = useState(minWidth); + const [isFocused, setIsFocused] = useState(false); + const measureRef = useRef(null); + + // Calculate precision from step value + const getDecimalPrecision = (num: number): number => { + const str = num.toString(); + if (str.includes(".")) { + return str.split(".")[1].length; + } + return 0; + }; + + const [userPrecision, setUserPrecision] = useState(() => + getDecimalPrecision(step), + ); // Track user's decimal precision + const [minPrecision, setMinPrecision] = useState(3); + const appliedPrecision = + userPrecision > minPrecision ? userPrecision : minPrecision; + + const theme = useTheme(); + + // Initialize with placeholder if no value provided + useEffect(() => { + if ( + (!value || value.trim() === "" || parseFloat(value) === 0) && + !isFocused + ) { + onChange(placeholder); + } + }, [placeholder, isFocused, value, onChange]); + + // Update precision when step changes + useEffect(() => { + setUserPrecision(getDecimalPrecision(step)); + setMinPrecision(getDecimalPrecision(step)); + }, [step]); + + // Measure text width to size input dynamically + useEffect(() => { + if (measureRef.current) { + const text = value; + measureRef.current.textContent = text; + const textWidth = measureRef.current.offsetWidth; + setInputWidth(Math.max(textWidth + 5, minWidth)); // Add padding and minimum width + } + }, [value, placeholder, minWidth]); + + const handleKeyDown = (e: React.KeyboardEvent) => { + if (e.key === "ArrowUp" || e.key === "ArrowDown") { + e.preventDefault(); + const currentValue = parseFloat(value) || 0; + const increment = e.shiftKey ? largeStep : step; + const newValue = + e.key === "ArrowUp" + ? currentValue + increment + : Math.max(0, currentValue - increment); + // Use the user's precision for formatting + onChange(newValue.toFixed(appliedPrecision)); + } + }; + + const handleChange = (e: React.ChangeEvent) => { + const inputValue = e.target.value; + // Allow empty string, numbers, and decimal points + if (inputValue === "" || /^\d*\.?\d*$/.test(inputValue)) { + onChange(inputValue); + + // Track the user's decimal precision + if (inputValue.includes(".")) { + const decimalPart = inputValue.split(".")[1]; + setUserPrecision(decimalPart ? decimalPart.length : 0); + } else if (inputValue && !isNaN(parseFloat(inputValue))) { + // No decimal point, so precision is 0 + setUserPrecision(0); + } + } + }; + + const handleBlur = () => { + setIsFocused(false); + // Reset to placeholder if value is zero or empty + if (!value || value.trim() === "" || parseFloat(value) === 0) { + onChange(placeholder); + } else if (!isNaN(parseFloat(value))) { + // Format valid numbers on blur using user's precision + const formatted = parseFloat(value).toFixed(appliedPrecision); + // Remove trailing zeros if precision allows + onChange(parseFloat(formatted).toString()); + } + }; + + const handleFocus = () => { + setIsFocused(true); + }; + + // Determine if we should show placeholder styling + const isShowingPlaceholder = value === placeholder && !isFocused; + + const defaultStyle: React.CSSProperties = { + fontSize, + fontWeight, + textAlign, + border: "none", + outline: "none", + background: "transparent", + width: `${inputWidth}px`, + minWidth: `${minWidth}px`, + fontFamily: "inherit", + color: isShowingPlaceholder + ? darken(theme.palette.text.primary, 0.5) + : theme.palette.text.primary, + padding: "4px 0", + transition: "color 0.2s ease", + ...style, + }; + + return ( +
+ {/* Hidden span for measuring text width */} + + + +
+ ); +} diff --git a/src-gui/src/renderer/components/modal/password-entry/PasswordEntryDialog.tsx b/src-gui/src/renderer/components/modal/password-entry/PasswordEntryDialog.tsx new file mode 100644 index 00000000..840cdabc --- /dev/null +++ b/src-gui/src/renderer/components/modal/password-entry/PasswordEntryDialog.tsx @@ -0,0 +1,126 @@ +import { + Dialog, + DialogActions, + DialogContent, + DialogTitle, + TextField, + Button, + IconButton, + InputAdornment, +} from "@mui/material"; +import { Visibility, VisibilityOff } from "@mui/icons-material"; +import { useState } from "react"; +import { usePendingPasswordApproval } from "store/hooks"; +import { rejectApproval, resolveApproval } from "renderer/rpc"; +import PromiseInvokeButton from "renderer/components/PromiseInvokeButton"; + +export default function PasswordEntryDialog() { + const pendingApprovals = usePendingPasswordApproval(); + const [password, setPassword] = useState(""); + const [error, setError] = useState(""); + const [showPassword, setShowPassword] = useState(false); + + const approval = pendingApprovals[0]; + + const accept = async () => { + if (!approval) { + throw new Error("No approval request found for password entry"); + } + + try { + await resolveApproval(approval.request_id, password); + setPassword(""); + setError(""); + } catch (err) { + setError("Invalid password. Please try again."); + throw err; + } + }; + + const reject = async () => { + if (!approval) { + throw new Error("No approval request found for password entry"); + } + + try { + await rejectApproval(approval.request_id, ""); + setPassword(""); + setError(""); + } catch (err) { + console.error("Error rejecting password request:", err); + throw err; + } + }; + + const handleTogglePasswordVisibility = () => { + setShowPassword(!showPassword); + }; + + if (!approval) { + return null; + } + + return ( + + Enter Wallet Password + + + { + setPassword(e.target.value); + if (error) setError(""); + }} + error={!!error} + helperText={error} + autoFocus + margin="normal" + onKeyPress={(e) => { + if (e.key === "Enter") { + accept(); + } + }} + InputProps={{ + endAdornment: ( + + + {showPassword ? : } + + + ), + }} + /> + + + + + + Unlock + + + + ); +} diff --git a/src-gui/src/renderer/components/modal/seed-selection/SeedSelectionDialog.tsx b/src-gui/src/renderer/components/modal/seed-selection/SeedSelectionDialog.tsx index e59658e1..2f7287a7 100644 --- a/src-gui/src/renderer/components/modal/seed-selection/SeedSelectionDialog.tsx +++ b/src-gui/src/renderer/components/modal/seed-selection/SeedSelectionDialog.tsx @@ -1,5 +1,4 @@ import { - Button, Dialog, DialogActions, DialogContent, @@ -10,17 +9,43 @@ import { RadioGroup, TextField, Typography, + Button, + Box, + List, + ListItem, + ListItemButton, + ListItemText, + Divider, + Card, + CardContent, } from "@mui/material"; import { useState, useEffect } from "react"; import { usePendingSeedSelectionApproval } from "store/hooks"; import { resolveApproval, checkSeed } from "renderer/rpc"; +import { SeedChoice } from "models/tauriModel"; +import PromiseInvokeButton from "renderer/components/PromiseInvokeButton"; +import { open } from "@tauri-apps/plugin-dialog"; +import AddIcon from "@mui/icons-material/Add"; +import RefreshIcon from "@mui/icons-material/Refresh"; +import FolderOpenIcon from "@mui/icons-material/FolderOpen"; +import SearchIcon from "@mui/icons-material/Search"; export default function SeedSelectionDialog() { const pendingApprovals = usePendingSeedSelectionApproval(); - const [selectedOption, setSelectedOption] = useState("RandomSeed"); + const [selectedOption, setSelectedOption] = useState< + SeedChoice["type"] | undefined + >("RandomSeed"); const [customSeed, setCustomSeed] = useState(""); const [isSeedValid, setIsSeedValid] = useState(false); - const approval = pendingApprovals[0]; // Handle the first pending approval + const [walletPath, setWalletPath] = useState(""); + + const approval = pendingApprovals[0]; + + // Extract recent wallets from the approval request content + const recentWallets = + approval?.request?.type === "SeedSelection" + ? approval.request.content.recent_wallets + : []; useEffect(() => { if (selectedOption === "FromSeed" && customSeed.trim()) { @@ -36,51 +61,193 @@ export default function SeedSelectionDialog() { } }, [customSeed, selectedOption]); - const handleClose = async (accept: boolean) => { - if (!approval) return; - - if (accept) { - const seedChoice = - selectedOption === "RandomSeed" - ? { type: "RandomSeed" } - : { type: "FromSeed", content: { seed: customSeed } }; - - await resolveApproval(approval.request_id, seedChoice); - } else { - // On reject, just close without approval - await resolveApproval(approval.request_id, { type: "RandomSeed" }); + // Auto-select the first recent wallet if available + useEffect(() => { + if (recentWallets.length > 0) { + setSelectedOption("FromWalletPath"); + setWalletPath(recentWallets[0]); } + }, [recentWallets.length]); + + const selectWalletFile = async () => { + const selected = await open({ + multiple: false, + directory: false, + }); + + if (selected) { + setWalletPath(selected); + } + }; + + const Legacy = async () => { + if (!approval) + throw new Error("No approval request found for seed selection"); + + await resolveApproval(approval.request_id, { + type: "Legacy", + }); + }; + + const accept = async () => { + if (!approval) + throw new Error("No approval request found for seed selection"); + + const seedChoice: SeedChoice = + selectedOption === "RandomSeed" + ? { type: "RandomSeed" } + : selectedOption === "FromSeed" + ? { type: "FromSeed", content: { seed: customSeed } } + : { type: "FromWalletPath", content: { wallet_path: walletPath } }; + + await resolveApproval(approval.request_id, seedChoice); }; if (!approval) { return null; } - return ( - - Monero Wallet - - - Choose what seed to use for the wallet. - + // Disable the button if the user is restoring from a seed and the seed is invalid + // or if selecting wallet path and no path is selected + const isDisabled = + selectedOption === "FromSeed" + ? customSeed.trim().length === 0 || !isSeedValid + : selectedOption === "FromWalletPath" + ? !walletPath + : false; - - setSelectedOption(e.target.value)} + return ( + + + + {/* Open existing wallet option */} + setSelectedOption("FromWalletPath")} > - } - label="Create a new wallet" - /> - } - label="Restore wallet from seed" - /> - - + + + + Open wallet file + + + + + {/* Create new wallet option */} + setSelectedOption("RandomSeed")} + > + + + + Create new wallet + + + + + {/* Restore from seed option */} + setSelectedOption("FromSeed")} + > + + + + Restore from seed + + + + + + {selectedOption === "RandomSeed" && ( + + + A new wallet with a random seed phrase will be generated. + + + You will have the option to back it up later. + + + )} {selectedOption === "FromSeed" && ( setCustomSeed(e.target.value)} - sx={{ mt: 2 }} placeholder="Enter your Monero 25 words seed phrase..." error={!isSeedValid && customSeed.length > 0} helperText={ @@ -102,19 +268,115 @@ export default function SeedSelectionDialog() { } /> )} + + {selectedOption === "FromWalletPath" && ( + + + + + + {recentWallets.length > 0 && ( + + + + {recentWallets.map((path, index) => ( + + + setWalletPath(path)} + > + + + + {index < recentWallets.length - 1 && } + + ))} + + + + )} + + )} - - + No wallet (Legacy) + + + Continue + ); diff --git a/src-gui/src/renderer/components/modal/swap/SwapDialogTitle.tsx b/src-gui/src/renderer/components/modal/swap/SwapDialogTitle.tsx deleted file mode 100644 index 5d3a1054..00000000 --- a/src-gui/src/renderer/components/modal/swap/SwapDialogTitle.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import { Box, DialogTitle, Typography } from "@mui/material"; -import DebugPageSwitchBadge from "./pages/DebugPageSwitchBadge"; -import FeedbackSubmitBadge from "./pages/FeedbackSubmitBadge"; - -export default function SwapDialogTitle({ - title, - debug, - setDebug, -}: { - title: string; - debug: boolean; - setDebug: (d: boolean) => void; -}) { - return ( - - {title} - - - - - - ); -} diff --git a/src-gui/src/renderer/components/modal/swap/pages/DebugPage.tsx b/src-gui/src/renderer/components/modal/swap/pages/DebugPage.tsx index 56e536dd..e2f96b72 100644 --- a/src-gui/src/renderer/components/modal/swap/pages/DebugPage.tsx +++ b/src-gui/src/renderer/components/modal/swap/pages/DebugPage.tsx @@ -22,6 +22,7 @@ export default function DebugPage() { }} > diff --git a/src-gui/src/renderer/components/navigation/NavigationHeader.tsx b/src-gui/src/renderer/components/navigation/NavigationHeader.tsx index 5fa2a9fe..7a342945 100644 --- a/src-gui/src/renderer/components/navigation/NavigationHeader.tsx +++ b/src-gui/src/renderer/components/navigation/NavigationHeader.tsx @@ -5,26 +5,35 @@ import SwapHorizOutlinedIcon from "@mui/icons-material/SwapHorizOutlined"; import FeedbackOutlinedIcon from "@mui/icons-material/FeedbackOutlined"; import RouteListItemIconButton from "./RouteListItemIconButton"; import UnfinishedSwapsBadge from "./UnfinishedSwapsCountBadge"; -import { useTotalUnreadMessagesCount } from "store/hooks"; +import { useIsSwapRunning, useTotalUnreadMessagesCount } from "store/hooks"; import SettingsIcon from "@mui/icons-material/Settings"; +import AttachMoneyIcon from "@mui/icons-material/AttachMoney"; +import BitcoinIcon from "../icons/BitcoinIcon"; +import MoneroIcon from "../icons/MoneroIcon"; export default function NavigationHeader() { const totalUnreadCount = useTotalUnreadMessagesCount(); + const isSwapRunning = useIsSwapRunning(); return ( - - + + + + + + + + + + - - - location.pathname === r); return ( - navigate(route)} key={name}> + navigate(firstRoute)} + key={name} + sx={ + isSelected + ? { + backgroundColor: "action.hover", + "&:hover": { + backgroundColor: "action.selected", + }, + } + : undefined + } + > {children} diff --git a/src-gui/src/renderer/components/navigation/UnfinishedSwapsCountBadge.tsx b/src-gui/src/renderer/components/navigation/UnfinishedSwapsCountBadge.tsx index 1f0205ac..ccd3c862 100644 --- a/src-gui/src/renderer/components/navigation/UnfinishedSwapsCountBadge.tsx +++ b/src-gui/src/renderer/components/navigation/UnfinishedSwapsCountBadge.tsx @@ -1,15 +1,18 @@ import React from "react"; import { Badge } from "@mui/material"; -import { useResumeableSwapsCountExcludingPunished } from "store/hooks"; +import { useIsSwapRunning, useResumeableSwapsCountExcludingPunished } from "store/hooks"; export default function UnfinishedSwapsBadge({ children, }: { children: React.ReactNode; }) { + const isSwapRunning = useIsSwapRunning(); const resumableSwapsCount = useResumeableSwapsCountExcludingPunished(); - if (resumableSwapsCount > 0) { + const displayedResumableSwapsCount = isSwapRunning ? resumableSwapsCount - 1 : resumableSwapsCount; + + if (displayedResumableSwapsCount > 0) { return ( {children} diff --git a/src-gui/src/renderer/components/other/ActionableMonospaceTextBox.tsx b/src-gui/src/renderer/components/other/ActionableMonospaceTextBox.tsx index 88efa375..10876897 100644 --- a/src-gui/src/renderer/components/other/ActionableMonospaceTextBox.tsx +++ b/src-gui/src/renderer/components/other/ActionableMonospaceTextBox.tsx @@ -16,6 +16,7 @@ type Props = { content: string; displayCopyIcon?: boolean; enableQrCode?: boolean; + light?: boolean; }; function QRCodeModal({ open, onClose, content }: ModalProps) { @@ -57,6 +58,7 @@ export default function ActionableMonospaceTextBox({ content, displayCopyIcon = true, enableQrCode = true, + light = false, }: Props) { const [copied, setCopied] = useState(false); const [qrCodeOpen, setQrCodeOpen] = useState(false); @@ -88,7 +90,7 @@ export default function ActionableMonospaceTextBox({ }} > - + {content} {displayCopyIcon && ( ({ display: "flex", alignItems: "center", - backgroundColor: theme.palette.grey[900], + backgroundColor: light ? "transparent" : theme.palette.grey[900], borderRadius: 2, + border: light ? `1px solid ${theme.palette.grey[800]}` : "none", padding: theme.spacing(1), })} > diff --git a/src-gui/src/renderer/components/other/RenderedCliLog.tsx b/src-gui/src/renderer/components/other/RenderedCliLog.tsx index 897e5ce2..0db9aab1 100644 --- a/src-gui/src/renderer/components/other/RenderedCliLog.tsx +++ b/src-gui/src/renderer/components/other/RenderedCliLog.tsx @@ -63,11 +63,13 @@ export default function CliLogsBox({ logs, topRightButton = null, autoScroll = false, + minHeight, }: { label: string; logs: (CliLog | string)[]; topRightButton?: ReactNode; autoScroll?: boolean; + minHeight?: string; }) { const [searchQuery, setSearchQuery] = useState(""); @@ -82,6 +84,7 @@ export default function CliLogsBox({ return ( - + {rows} diff --git a/src-gui/src/renderer/components/other/TruncatedText.tsx b/src-gui/src/renderer/components/other/TruncatedText.tsx index 0099f6c9..b3c4c0eb 100644 --- a/src-gui/src/renderer/components/other/TruncatedText.tsx +++ b/src-gui/src/renderer/components/other/TruncatedText.tsx @@ -12,7 +12,7 @@ export default function TruncatedText({ let finalChildren = children ?? ""; const truncatedText = - finalChildren.length > limit + finalChildren.length > limit ? truncateMiddle ? finalChildren.slice(0, Math.floor(limit / 2)) + ellipsis + diff --git a/src-gui/src/renderer/components/other/Units.tsx b/src-gui/src/renderer/components/other/Units.tsx index 40b01ea5..32fb7a00 100644 --- a/src-gui/src/renderer/components/other/Units.tsx +++ b/src-gui/src/renderer/components/other/Units.tsx @@ -32,16 +32,43 @@ export function AmountWithUnit({ return ( - {amount != null - ? Number.parseFloat(amount.toFixed(fixedPrecision)) - : "?"}{" "} - {unit} + {amount != null ? amount.toFixed(fixedPrecision) : "?"} {unit} {parenthesisText != null ? ` (${parenthesisText})` : null} ); } +export function FiatPiconeroAmount({ + amount, + fixedPrecision = 2, +}: { + amount: Amount; + fixedPrecision?: number; +}) { + const xmrPrice = useAppSelector((state) => state.rates.xmrPrice); + const [fetchFiatPrices, fiatCurrency] = useSettings((settings) => [ + settings.fetchFiatPrices, + settings.fiatCurrency, + ]); + + if ( + !fetchFiatPrices || + fiatCurrency == null || + amount == null || + xmrPrice == null + ) { + return null; + } + + return ( + + {(piconerosToXmr(amount) * xmrPrice).toFixed(fixedPrecision)}{" "} + {fiatCurrency} + + ); +} + AmountWithUnit.defaultProps = { exchangeRate: null, }; @@ -59,14 +86,20 @@ export function BitcoinAmount({ amount }: { amount: Amount }) { ); } -export function MoneroAmount({ amount }: { amount: Amount }) { +export function MoneroAmount({ + amount, + fixedPrecision = 4, +}: { + amount: Amount; + fixedPrecision?: number; +}) { const xmrRate = useAppSelector((state) => state.rates.xmrPrice); return ( ); @@ -128,8 +161,17 @@ export function SatsAmount({ amount }: { amount: Amount }) { return ; } -export function PiconeroAmount({ amount }: { amount: Amount }) { +export function PiconeroAmount({ + amount, + fixedPrecision = 8, +}: { + amount: Amount; + fixedPrecision?: number; +}) { return ( - + ); } diff --git a/src-gui/src/renderer/components/pages/history/HistoryPage.tsx b/src-gui/src/renderer/components/pages/history/HistoryPage.tsx index 580dd9ae..c195d0d6 100644 --- a/src-gui/src/renderer/components/pages/history/HistoryPage.tsx +++ b/src-gui/src/renderer/components/pages/history/HistoryPage.tsx @@ -5,7 +5,6 @@ import HistoryTable from "./table/HistoryTable"; export default function HistoryPage() { return ( <> - History diff --git a/src-gui/src/renderer/components/pages/history/table/HistoryTable.tsx b/src-gui/src/renderer/components/pages/history/table/HistoryTable.tsx index e80fd673..f9dca1b8 100644 --- a/src-gui/src/renderer/components/pages/history/table/HistoryTable.tsx +++ b/src-gui/src/renderer/components/pages/history/table/HistoryTable.tsx @@ -7,6 +7,8 @@ import { TableContainer, TableHead, TableRow, + Typography, + Skeleton, } from "@mui/material"; import { useSwapInfosSortedByDate } from "../../../../../store/hooks"; import HistoryRow from "./HistoryRow"; @@ -23,19 +25,75 @@ export default function HistoryTable() { > - - - - ID - Amount - State - - - + {swapSortedByDate.length > 0 && ( + + + + ID + Amount + State + + + + )} - {swapSortedByDate.map((swap) => ( - - ))} + {swapSortedByDate.length === 0 ? ( + <> + + + + Nothing to see here + + + You haven't made any swaps yet + + + + {/* Skeleton rows for visual loading effect */} + {Array.from({ length: 3 }).map((_, index) => ( + + + + + + + + + + + + + + + + + + ))} + + ) : ( + swapSortedByDate.map((swap) => ( + + )) + )}
diff --git a/src-gui/src/renderer/components/pages/history/table/SwapLogFileOpenButton.tsx b/src-gui/src/renderer/components/pages/history/table/SwapLogFileOpenButton.tsx index 48088aab..ab827017 100644 --- a/src-gui/src/renderer/components/pages/history/table/SwapLogFileOpenButton.tsx +++ b/src-gui/src/renderer/components/pages/history/table/SwapLogFileOpenButton.tsx @@ -43,7 +43,11 @@ export default function SwapLogFileOpenButton({ setLogs(null)} fullWidth maxWidth="lg"> Logs of swap {swapId} - + + + Confirm + + + + ); +} diff --git a/src-gui/src/renderer/components/pages/monero/components/SendAmountInput.tsx b/src-gui/src/renderer/components/pages/monero/components/SendAmountInput.tsx new file mode 100644 index 00000000..b428679e --- /dev/null +++ b/src-gui/src/renderer/components/pages/monero/components/SendAmountInput.tsx @@ -0,0 +1,226 @@ +import { Box, Button, Card, Grow, Typography } from "@mui/material"; +import NumberInput from "renderer/components/inputs/NumberInput"; +import SwapVertIcon from "@mui/icons-material/SwapVert"; +import { useTheme } from "@mui/material/styles"; +import { piconerosToXmr } from "../../../../../utils/conversionUtils"; +import { MoneroAmount } from "renderer/components/other/Units"; + +interface SendAmountInputProps { + balance: { + unlocked_balance: string; + }; + amount: string; + onAmountChange: (amount: string) => void; + onMaxClicked?: () => void; + onMaxToggled?: () => void; + currency: string; + onCurrencyChange: (currency: string) => void; + fiatCurrency: string; + xmrPrice: number; + showFiatRate: boolean; + disabled?: boolean; +} + +export default function SendAmountInput({ + balance, + amount, + currency, + onCurrencyChange, + onAmountChange, + onMaxClicked, + onMaxToggled, + fiatCurrency, + xmrPrice, + showFiatRate, + disabled = false, +}: SendAmountInputProps) { + const theme = useTheme(); + + const isMaxSelected = amount === ""; + + // Calculate secondary amount for display + const secondaryAmount = (() => { + if (isMaxSelected) { + return "All available funds"; + } + + if (!amount || amount === "" || isNaN(parseFloat(amount))) { + return "0.00"; + } + + const primaryValue = parseFloat(amount); + if (currency === "XMR") { + // Primary is XMR, secondary is USD + return (primaryValue * xmrPrice).toFixed(2); + } else { + // Primary is USD, secondary is XMR + return (primaryValue / xmrPrice).toFixed(3); + } + })(); + + const handleMaxAmount = () => { + if (disabled) return; + + if (onMaxToggled) { + onMaxToggled(); + } else if (onMaxClicked) { + onMaxClicked(); + } else { + // Fallback to old behavior if no callback provided + if ( + balance?.unlocked_balance !== undefined && + balance?.unlocked_balance !== null + ) { + // TODO: We need to use a real fee here and call sweep(...) instead of just subtracting a fixed amount + const unlocked = parseFloat(balance.unlocked_balance); + const maxAmountXmr = piconerosToXmr(unlocked - 10000000000); // Subtract ~0.01 XMR for fees + + if (currency === "XMR") { + onAmountChange(Math.max(0, maxAmountXmr).toString()); + } else { + // Convert to USD for display + const maxAmountUsd = maxAmountXmr * xmrPrice; + onAmountChange(Math.max(0, maxAmountUsd).toString()); + } + } + } + }; + + const handleMaxTextClick = () => { + if (disabled) return; + if (isMaxSelected && onMaxToggled) { + onMaxToggled(); + } + }; + + const handleCurrencySwap = () => { + if (!isMaxSelected && !disabled) { + onCurrencyChange(currency === "XMR" ? fiatCurrency : "XMR"); + } + }; + + const isAmountTooHigh = + !isMaxSelected && + (currency === "XMR" + ? parseFloat(amount) > + piconerosToXmr(parseFloat(balance.unlocked_balance)) + : parseFloat(amount) / xmrPrice > + piconerosToXmr(parseFloat(balance.unlocked_balance))); + + return ( + + + {isAmountTooHigh && ( + + + You don't have enough +
unlocked balance to send this amount. +
+
+ )} + + {isMaxSelected ? ( + + <MAX> + + ) : ( + <> + {} : onAmountChange} + placeholder={currency === "XMR" ? "0.000" : "0.00"} + fontSize="3em" + fontWeight={600} + minWidth={60} + step={currency === "XMR" ? 0.001 : 0.01} + largeStep={currency === "XMR" ? 0.1 : 10} + /> + + {currency} + + + )} + + {showFiatRate && ( + + + + {secondaryAmount}{" "} + {isMaxSelected ? "" : currency === "XMR" ? fiatCurrency : "XMR"} + + + )} +
+ + + Available + + + + + XMR + + + +
+ ); +} diff --git a/src-gui/src/renderer/components/pages/monero/components/SendApprovalContent.tsx b/src-gui/src/renderer/components/pages/monero/components/SendApprovalContent.tsx new file mode 100644 index 00000000..407ee5fa --- /dev/null +++ b/src-gui/src/renderer/components/pages/monero/components/SendApprovalContent.tsx @@ -0,0 +1,151 @@ +import { useState, useEffect } from "react"; +import { + DialogTitle, + DialogContent, + DialogActions, + Typography, + Box, +} from "@mui/material"; +import CheckIcon from "@mui/icons-material/Check"; +import CloseIcon from "@mui/icons-material/Close"; +import { resolveApproval } from "renderer/rpc"; +import { usePendingSendMoneroApproval } from "store/hooks"; +import { PiconeroAmount } from "renderer/components/other/Units"; +import ActionableMonospaceTextBox from "renderer/components/other/ActionableMonospaceTextBox"; +import PromiseInvokeButton from "renderer/components/PromiseInvokeButton"; + +interface SendApprovalContentProps { + onClose: () => void; +} + +export default function SendApprovalContent({ + onClose, +}: SendApprovalContentProps) { + const pendingApprovals = usePendingSendMoneroApproval(); + const [timeLeft, setTimeLeft] = useState(0); + + const approval = pendingApprovals[0]; // Handle the first approval request + + useEffect(() => { + if ( + !approval?.request_status || + approval.request_status.state !== "Pending" + ) { + return; + } + + const expirationTs = approval.request_status.content.expiration_ts; + const expiresAtMs = expirationTs * 1000; + + const tick = () => { + const remainingMs = Math.max(expiresAtMs - Date.now(), 0); + setTimeLeft(Math.ceil(remainingMs / 1000)); + }; + + tick(); + const id = setInterval(tick, 1000); + return () => clearInterval(id); + }, [approval]); + + const handleApprove = async () => { + if (!approval) throw new Error("No approval request available"); + await resolveApproval(approval.request_id, true); + }; + + const handleReject = async () => { + if (!approval) throw new Error("No approval request available"); + await resolveApproval(approval.request_id, false); + }; + + if (!approval) { + return null; + } + + const { address, amount, fee } = approval.request.content; + + return ( + <> + + + Confirm Monero Transfer + + + + + + {/* Amount */} + + + Amount to Send + + + + + + + {/* Fee */} + + + Network Fee + + + + + + + {/* Destination Address */} + + + Destination Address + + + + + + + {/* Time remaining */} + + {timeLeft > 0 + ? `Request expires in ${timeLeft}s` + : "Request expired"} + + + + + + } + displayErrorSnackbar={true} + requiresContext={false} + > + Reject + + } + displayErrorSnackbar={true} + requiresContext={false} + > + Send + + + + ); +} diff --git a/src-gui/src/renderer/components/pages/monero/components/SendSuccessContent.tsx b/src-gui/src/renderer/components/pages/monero/components/SendSuccessContent.tsx new file mode 100644 index 00000000..c128150f --- /dev/null +++ b/src-gui/src/renderer/components/pages/monero/components/SendSuccessContent.tsx @@ -0,0 +1,66 @@ +import { Box, Button, Typography } from "@mui/material"; +import CheckCircleIcon from "@mui/icons-material/CheckCircle"; +import { FiatPiconeroAmount, PiconeroAmount } from "renderer/components/other/Units"; +import MonospaceTextBox from "renderer/components/other/MonospaceTextBox"; +import ArrowOutwardIcon from "@mui/icons-material/ArrowOutward"; +import { SendMoneroResponse } from "models/tauriModel"; +import { getMoneroTxExplorerUrl } from "../../../../../utils/conversionUtils"; +import { isTestnet } from "store/config"; +import { open } from "@tauri-apps/plugin-shell"; + +export default function SendSuccessContent({ + onClose, + successDetails, +}: { + onClose: () => void; + successDetails: SendMoneroResponse | null; +}) { + + const address = successDetails?.address; + const amount = successDetails?.amount_sent; + const explorerUrl = getMoneroTxExplorerUrl(successDetails?.tx_hash, isTestnet()); + + return ( + + + + Transaction Published + + Sent + + + + () + + + to + + {address.slice(0, 8)} ... {address.slice(-8)} + + + + + + + + + ); +} diff --git a/src-gui/src/renderer/components/pages/monero/components/SendTransactionContent.tsx b/src-gui/src/renderer/components/pages/monero/components/SendTransactionContent.tsx new file mode 100644 index 00000000..1cd4ddbb --- /dev/null +++ b/src-gui/src/renderer/components/pages/monero/components/SendTransactionContent.tsx @@ -0,0 +1,179 @@ +import { + Button, + Box, + DialogActions, + DialogContent, + DialogTitle, + Typography, +} from "@mui/material"; +import { useState } from "react"; +import { xmrToPiconeros } from "../../../../../utils/conversionUtils"; +import SendAmountInput from "./SendAmountInput"; +import MoneroAddressTextField from "renderer/components/inputs/MoneroAddressTextField"; +import PromiseInvokeButton from "renderer/components/PromiseInvokeButton"; +import { sendMoneroTransaction } from "renderer/rpc"; +import { useAppSelector } from "store/hooks"; +import { SendMoneroResponse } from "models/tauriModel"; + +interface SendTransactionContentProps { + balance: { + unlocked_balance: string; + }; + onClose: () => void; + onSuccess: (response: SendMoneroResponse) => void; +} + +export default function SendTransactionContent({ + balance, + onSuccess, + onClose, +}: SendTransactionContentProps) { + const [sendAddress, setSendAddress] = useState(""); + const [sendAmount, setSendAmount] = useState(""); + const [previousAmount, setPreviousAmount] = useState(""); + const [enableSend, setEnableSend] = useState(false); + const [currency, setCurrency] = useState("XMR"); + const [isMaxSelected, setIsMaxSelected] = useState(false); + const [isSending, setIsSending] = useState(false); + + const showFiatRate = useAppSelector( + (state) => state.settings.fetchFiatPrices, + ); + const fiatCurrency = useAppSelector((state) => state.settings.fiatCurrency); + const xmrPrice = useAppSelector((state) => state.rates.xmrPrice); + + const handleCurrencyChange = (newCurrency: string) => { + if (!showFiatRate || !xmrPrice || isMaxSelected || isSending) { + return; + } + + if (sendAmount === "" || parseFloat(sendAmount) === 0) { + setSendAmount(newCurrency === "XMR" ? "0.000" : "0.00"); + } else { + setSendAmount( + newCurrency === "XMR" + ? (parseFloat(sendAmount) / xmrPrice).toFixed(3) + : (parseFloat(sendAmount) * xmrPrice).toFixed(2), + ); + } + setCurrency(newCurrency); + }; + + const handleMaxToggled = () => { + if (isSending) return; + + if (isMaxSelected) { + // Disable MAX mode - restore previous amount + setIsMaxSelected(false); + setSendAmount(previousAmount); + } else { + // Enable MAX mode - save current amount first + setPreviousAmount(sendAmount); + setIsMaxSelected(true); + setSendAmount(""); + } + }; + + const handleAmountChange = (newAmount: string) => { + if (isSending) return; + + if (newAmount !== "") { + setIsMaxSelected(false); + } + setSendAmount(newAmount); + }; + + const handleAddressChange = (newAddress: string) => { + if (isSending) return; + setSendAddress(newAddress); + }; + + const moneroAmount = + currency === "XMR" + ? parseFloat(sendAmount) + : parseFloat(sendAmount) / xmrPrice; + + const handleSend = async () => { + if (!sendAddress) { + throw new Error("Address is required"); + } + + if (isMaxSelected) { + return sendMoneroTransaction({ + address: sendAddress, + amount: { type: "Sweep" }, + }); + } else { + if (!sendAmount || sendAmount === "") { + throw new Error("Amount is required"); + } + + return sendMoneroTransaction({ + address: sendAddress, + amount: { + type: "Specific", + // Floor the amount to avoid rounding decimal amounts + // The amount is in piconeros, so it NEEDS to be a whole number + amount: Math.floor(xmrToPiconeros(moneroAmount)), + }, + }); + } + }; + + const handleSendSuccess = (response: SendMoneroResponse) => { + // Clear form after successful send + handleClear(); + onSuccess(response); + }; + + const handleClear = () => { + setSendAddress(""); + setSendAmount(""); + setPreviousAmount(""); + setIsMaxSelected(false); + }; + + const isSendDisabled = + !enableSend || (!isMaxSelected && (!sendAmount || sendAmount === "")); + + return ( + <> + Send + + + + + + + + + + Send + + + + ); +} diff --git a/src-gui/src/renderer/components/pages/monero/components/StateIndicator.tsx b/src-gui/src/renderer/components/pages/monero/components/StateIndicator.tsx new file mode 100644 index 00000000..96e5e466 --- /dev/null +++ b/src-gui/src/renderer/components/pages/monero/components/StateIndicator.tsx @@ -0,0 +1,58 @@ +import { Box, darken, lighten, useTheme } from "@mui/material"; + +function getColor(colorName: string) { + const theme = useTheme(); + switch (colorName) { + case "primary": + return theme.palette.primary.main; + case "secondary": + return theme.palette.secondary.main; + case "success": + return theme.palette.success.main; + case "warning": + return theme.palette.warning.main; + } +} + +export default function StateIndicator({ + color, + pulsating, +}: { + color: string; + pulsating: boolean; +}) { + const mainShade = getColor(color); + const darkShade = darken(mainShade, 0.4); + const glowShade = lighten(mainShade, 0.4); + + const intensePulsatingStyles = { + animation: "pulse 2s infinite", + "@keyframes pulse": { + "0%": { opacity: 0.5 }, + "50%": { opacity: 1 }, + "100%": { opacity: 0.5 }, + }, + }; + + const softPulsatingStyles = { + animation: "pulse 3.5s infinite", + "@keyframes pulse": { + "0%": { opacity: 0.7 }, + "50%": { opacity: 1 }, + "100%": { opacity: 0.7 }, + }, + }; + + return ( + + ); +} diff --git a/src-gui/src/renderer/components/pages/monero/components/TransactionHistory.tsx b/src-gui/src/renderer/components/pages/monero/components/TransactionHistory.tsx new file mode 100644 index 00000000..4950e077 --- /dev/null +++ b/src-gui/src/renderer/components/pages/monero/components/TransactionHistory.tsx @@ -0,0 +1,102 @@ +import { + Typography, + Card, + CardContent, + Table, + TableBody, + TableCell, + TableContainer, + TableHead, + TableRow, + Paper, + Chip, + IconButton, + Tooltip, + Stack, +} from "@mui/material"; +import { OpenInNew as OpenInNewIcon } from "@mui/icons-material"; +import { open } from "@tauri-apps/plugin-shell"; +import { PiconeroAmount } from "../../../other/Units"; +import { getMoneroTxExplorerUrl } from "../../../../../utils/conversionUtils"; +import { isTestnet } from "store/config"; +import { TransactionInfo } from "models/tauriModel"; + +interface TransactionHistoryProps { + history?: { + transactions: TransactionInfo[]; + }; +} + +// Component for displaying transaction history +export default function TransactionHistory({ + history, +}: TransactionHistoryProps) { + if (!history || !history.transactions || history.transactions.length === 0) { + return Transaction History; + } + + return ( + <> + Transaction History + + + + + + Amount + Fee + Confirmations + Explorer + + + + {[...history.transactions] + .sort((a, b) => a.confirmations - b.confirmations) + .map((tx, index) => ( + + + + + + + + + + + + = 10 ? "success" : "warning"} + size="small" + /> + + + {tx.tx_hash && ( + + { + const url = getMoneroTxExplorerUrl( + tx.tx_hash, + isTestnet(), + ); + open(url); + }} + > + + + + )} + + + ))} + +
+
+ + ); +} diff --git a/src-gui/src/renderer/components/pages/monero/components/WalletActionButtons.tsx b/src-gui/src/renderer/components/pages/monero/components/WalletActionButtons.tsx new file mode 100644 index 00000000..77d0cd63 --- /dev/null +++ b/src-gui/src/renderer/components/pages/monero/components/WalletActionButtons.tsx @@ -0,0 +1,144 @@ +import { + Box, + Button, + Chip, + Dialog, + DialogActions, + DialogContent, + DialogTitle, + IconButton, + ListItemIcon, + Menu, + MenuItem, + TextField, + Typography, +} from "@mui/material"; +import { + Send as SendIcon, + SwapHoriz as SwapIcon, + Restore as RestoreIcon, + MoreHoriz as MoreHorizIcon, +} from "@mui/icons-material"; +import { useState } from "react"; +import { setMoneroRestoreHeight } from "renderer/rpc"; +import SendTransactionModal from "../SendTransactionModal"; +import { useNavigate } from "react-router-dom"; +import PromiseInvokeButton from "renderer/components/PromiseInvokeButton"; +import SetRestoreHeightModal from "../SetRestoreHeightModal"; + +interface WalletActionButtonsProps { + balance: { + unlocked_balance: string; + }; +} + +function RestoreHeightDialog({ + open, + onClose, +}: { + open: boolean; + onClose: () => void; +}) { + const [restoreHeight, setRestoreHeight] = useState(0); + + const handleRestoreHeight = async () => { + await setMoneroRestoreHeight(restoreHeight); + onClose(); + }; + + return ( + + Restore Height + + setRestoreHeight(Number(e.target.value))} + /> + + + + + Restore + + + + ); +} + +export default function WalletActionButtons({ + balance, +}: WalletActionButtonsProps) { + const navigate = useNavigate(); + const [sendDialogOpen, setSendDialogOpen] = useState(false); + const [restoreHeightDialogOpen, setRestoreHeightDialogOpen] = useState(false); + + const [menuAnchorEl, setMenuAnchorEl] = useState(null); + const menuOpen = Boolean(menuAnchorEl); + const handleMenuClick = (event: React.MouseEvent) => { + setMenuAnchorEl(event.currentTarget); + }; + const handleMenuClose = () => { + setMenuAnchorEl(null); + }; + + return ( + <> + setRestoreHeightDialogOpen(false)} + /> + setSendDialogOpen(false)} + /> + + } + label="Send" + variant="button" + clickable + onClick={() => setSendDialogOpen(true)} + /> + navigate("/swap")} + icon={} + label="Swap" + variant="button" + clickable + /> + + + + + + { + setRestoreHeightDialogOpen(true); + handleMenuClose(); + }} + > + + + + Restore Height + + + + + ); +} diff --git a/src-gui/src/renderer/components/pages/monero/components/WalletOverview.tsx b/src-gui/src/renderer/components/pages/monero/components/WalletOverview.tsx new file mode 100644 index 00000000..5a0262d3 --- /dev/null +++ b/src-gui/src/renderer/components/pages/monero/components/WalletOverview.tsx @@ -0,0 +1,148 @@ +import { + Box, + Typography, + CircularProgress, + Button, + Card, + CardContent, + Divider, + CardHeader, + LinearProgress, +} from "@mui/material"; +import { PiconeroAmount } from "../../../other/Units"; +import { FiatPiconeroAmount } from "../../../other/Units"; +import StateIndicator from "./StateIndicator"; + +interface WalletOverviewProps { + balance?: { + unlocked_balance: string; + total_balance: string; + }; + syncProgress?: { + current_block: number; + target_block: number; + progress_percentage: number; + }; +} + +// Component for displaying wallet address and balance +export default function WalletOverview({ + balance, + syncProgress, +}: WalletOverviewProps) { + const pendingBalance = + parseFloat(balance.total_balance) - parseFloat(balance.unlocked_balance); + + const isSyncing = syncProgress && syncProgress.progress_percentage < 100; + const blocksLeft = syncProgress?.target_block - syncProgress?.current_block; + + return ( + + {syncProgress && syncProgress.progress_percentage < 100 && ( + + )} + + {/* Balance */} + + + Available Funds + + + + + + + + {pendingBalance > 0 && ( + <> + + Pending + + + + + + + + + + )} + + + + + {isSyncing ? "syncing" : "synced"} + + + + {isSyncing && ( + + {blocksLeft.toLocaleString()} blocks left + + )} + + + + ); +} diff --git a/src-gui/src/renderer/components/pages/monero/components/WalletPageLoadingState.tsx b/src-gui/src/renderer/components/pages/monero/components/WalletPageLoadingState.tsx new file mode 100644 index 00000000..decdf3d6 --- /dev/null +++ b/src-gui/src/renderer/components/pages/monero/components/WalletPageLoadingState.tsx @@ -0,0 +1,87 @@ +import { Box, Card, Chip, Skeleton, Typography } from "@mui/material"; +import StateIndicator from "./StateIndicator"; +import ActionableMonospaceTextBox from "renderer/components/other/ActionableMonospaceTextBox"; + +const DUMMY_ADDRESS = + "888tNkZrPN6JsEgekjMnABU4TBzc2Dt29EPAvkRxbANsAnjyPbb3iQ1YBRk1UXcdRsiKc9dhwMVgN5S9cQUiyoogDavup3H"; + +export default function WalletPageLoadingState() { + return ( + + + {/* Balance */} + + + Available Funds + + + + + + + + + + + loading + + + + + + + + + + + + {Array.from({ length: 2 }).map((_) => ( + + + + ))} + + + Transaction History + + + ); +} diff --git a/src-gui/src/renderer/components/pages/monero/components/index.ts b/src-gui/src/renderer/components/pages/monero/components/index.ts new file mode 100644 index 00000000..c557d008 --- /dev/null +++ b/src-gui/src/renderer/components/pages/monero/components/index.ts @@ -0,0 +1,5 @@ +export { default as WalletOverview } from "./WalletOverview"; +export { default as TransactionHistory } from "./TransactionHistory"; +export { default as WalletActionButtons } from "./WalletActionButtons"; +export { default as SendTransactionContent } from "./SendTransactionContent"; +export { default as SendApprovalContent } from "./SendApprovalContent"; diff --git a/src-gui/src/renderer/components/pages/swap/swap/components/TransactionInfoBox.tsx b/src-gui/src/renderer/components/pages/swap/swap/components/TransactionInfoBox.tsx index f23f478c..aeccbb6b 100644 --- a/src-gui/src/renderer/components/pages/swap/swap/components/TransactionInfoBox.tsx +++ b/src-gui/src/renderer/components/pages/swap/swap/components/TransactionInfoBox.tsx @@ -1,5 +1,5 @@ +import React, { ReactNode } from "react"; import { Box, Link, Typography } from "@mui/material"; -import { ReactNode } from "react"; import InfoBox from "./InfoBox"; import TruncatedText from "renderer/components/other/TruncatedText"; diff --git a/src-gui/src/renderer/components/pages/swap/swap/init/InitPage.tsx b/src-gui/src/renderer/components/pages/swap/swap/init/InitPage.tsx index ae779187..91d6c6a8 100644 --- a/src-gui/src/renderer/components/pages/swap/swap/init/InitPage.tsx +++ b/src-gui/src/renderer/components/pages/swap/swap/init/InitPage.tsx @@ -14,8 +14,8 @@ export default function InitPage() { useState(false); // We force this to true for now because the internal wallet is not really accessible from the GUI yet - const [useExternalRedeemAddress, _setUseExternalRedeemAddress] = - useState(true); + const [useExternalRedeemAddress, setUseExternalRedeemAddress] = + useState(true); const [redeemAddressValid, setRedeemAddressValid] = useState(false); const [refundAddressValid, setRefundAddressValid] = useState(false); @@ -40,14 +40,35 @@ export default function InitPage() { }} > - + + setUseExternalRedeemAddress(newValue === 1) + } + > + + + + + {useExternalRedeemAddress ? ( + + ) : ( + + The Monero will be sent to the internal Monero wallet of the + GUI. You can then withdraw them from there or use them for + another swap directly. + + )} + diff --git a/src-gui/src/renderer/components/pages/wallet/WalletPage.tsx b/src-gui/src/renderer/components/pages/wallet/WalletPage.tsx index 0d800ecd..d3b4d0b6 100644 --- a/src-gui/src/renderer/components/pages/wallet/WalletPage.tsx +++ b/src-gui/src/renderer/components/pages/wallet/WalletPage.tsx @@ -11,7 +11,6 @@ export default function WalletPage() { gap: "1rem", }} > - Wallet You do not have to deposit money before starting a swap. Instead, you will be greeted with a deposit address after you initiate one. diff --git a/src-gui/src/renderer/components/theme.tsx b/src-gui/src/renderer/components/theme.tsx index 351827d9..07bdef61 100644 --- a/src-gui/src/renderer/components/theme.tsx +++ b/src-gui/src/renderer/components/theme.tsx @@ -1,6 +1,23 @@ import { createTheme, ThemeOptions } from "@mui/material"; import { indigo } from "@mui/material/colors"; +// Extend the theme to include custom chip variants +declare module "@mui/material/Chip" { + interface ChipPropsVariantOverrides { + button: true; + } +} + +// Extend the theme to include custom button variants and sizes +declare module "@mui/material/Button" { + interface ButtonPropsVariantOverrides { + secondary: true; + } + interface ButtonPropsSizeOverrides { + tiny: true; + } +} + export enum Theme { Light = "light", Dark = "dark", @@ -33,7 +50,61 @@ const baseTheme: ThemeOptions = { backgroundColor: "color-mix(in srgb, #bdbdbd 10%, transparent)", }, }, + sizeTiny: { + fontSize: "0.75rem", + fontWeight: 500, + padding: "4px 8px", + minHeight: "24px", + minWidth: "auto", + lineHeight: 1.2, + textTransform: "none", + borderRadius: "4px", + }, }, + variants: [ + { + props: { variant: "secondary" }, + style: ({ theme }) => ({ + backgroundColor: + theme.palette.mode === "dark" + ? "rgba(255, 255, 255, 0.08)" + : "rgba(0, 0, 0, 0.04)", + color: theme.palette.text.secondary, + "&:hover": { + backgroundColor: + theme.palette.mode === "dark" + ? "rgba(255, 255, 255, 0.12)" + : "rgba(0, 0, 0, 0.08)", + borderColor: + theme.palette.mode === "dark" + ? "rgba(255, 255, 255, 0.23)" + : "rgba(0, 0, 0, 0.23)", + }, + "&:disabled": { + backgroundColor: + theme.palette.mode === "dark" + ? "rgba(255, 255, 255, 0.04)" + : "rgba(0, 0, 0, 0.02)", + color: theme.palette.text.disabled, + borderColor: + theme.palette.mode === "dark" + ? "rgba(255, 255, 255, 0.08)" + : "rgba(0, 0, 0, 0.08)", + }, + }), + }, + ], + }, + MuiChip: { + variants: [ + { + props: { variant: "button" }, + style: ({ theme }) => ({ + padding: "12px 16px", + cursor: "pointer", + }), + }, + ], }, MuiDialog: { defaultProps: { diff --git a/src-gui/src/renderer/rpc.ts b/src-gui/src/renderer/rpc.ts index f902d0ea..c7e6747a 100644 --- a/src-gui/src/renderer/rpc.ts +++ b/src-gui/src/renderer/rpc.ts @@ -31,16 +31,31 @@ import { RedactResponse, GetCurrentSwapResponse, LabeledMoneroAddress, - GetPendingApprovalsArgs, + GetMoneroHistoryResponse, + GetMoneroMainAddressResponse, + GetMoneroBalanceResponse, + SendMoneroArgs, + SendMoneroResponse, + GetMoneroSyncProgressResponse, GetPendingApprovalsResponse, + RejectApprovalArgs, + RejectApprovalResponse, + SetRestoreHeightArgs, + SetRestoreHeightResponse, + GetRestoreHeightResponse, } from "models/tauriModel"; import { rpcSetBalance, rpcSetSwapInfo, approvalRequestsReplaced, } from "store/features/rpcSlice"; +import { + setMainAddress, + setBalance, + setSyncProgress, + setHistory, +} from "store/features/walletSlice"; import { store } from "./store/storeRenderer"; -import { Maker } from "models/apiModel"; import { providerToConcatenatedMultiAddr } from "utils/multiAddrUtils"; import { MoneroRecoveryResponse } from "models/rpcModel"; import { ListSellersResponse } from "../models/tauriModel"; @@ -417,6 +432,129 @@ export async function getMoneroAddresses(): Promise return await invokeNoArgs("get_monero_addresses"); } +export async function getRestoreHeight(): Promise { + return await invokeNoArgs("get_restore_height"); +} + +export async function setMoneroRestoreHeight( + height: number | Date, +): Promise { + const args: SetRestoreHeightArgs = + typeof height === "number" + ? { type: "Height", height: height } + : { + type: "Date", + height: { + year: height.getFullYear(), + month: height.getMonth() + 1, // JavaScript months are 0-indexed, but we want 1-indexed + day: height.getDate(), + }, + }; + + return await invoke( + "set_monero_restore_height", + args, + ); +} + +export async function getMoneroHistory(): Promise { + return await invokeNoArgs("get_monero_history"); +} + +export async function getMoneroMainAddress(): Promise { + return await invokeNoArgs( + "get_monero_main_address", + ); +} + +export async function getMoneroBalance(): Promise { + return await invokeNoArgs("get_monero_balance"); +} + +export async function sendMonero( + args: SendMoneroArgs, +): Promise { + return await invoke("send_monero", args); +} + +export async function getMoneroSyncProgress(): Promise { + return await invokeNoArgs( + "get_monero_sync_progress", + ); +} + +// Wallet management functions that handle Redux dispatching +export async function initializeMoneroWallet() { + try { + const [ + addressResponse, + balanceResponse, + syncProgressResponse, + historyResponse, + ] = await Promise.all([ + getMoneroMainAddress(), + getMoneroBalance(), + getMoneroSyncProgress(), + getMoneroHistory(), + ]); + + store.dispatch(setMainAddress(addressResponse.address)); + store.dispatch(setBalance(balanceResponse)); + store.dispatch(setSyncProgress(syncProgressResponse)); + store.dispatch(setHistory(historyResponse)); + } catch (err) { + console.error("Failed to fetch Monero wallet data:", err); + } +} + +export async function sendMoneroTransaction( + args: SendMoneroArgs, +): Promise { + try { + const response = await sendMonero(args); + + // Refresh balance and history after sending - but don't let this block the response + Promise.all([ + getMoneroBalance(), + getMoneroHistory(), + ]).then(([newBalance, newHistory]) => { + store.dispatch(setBalance(newBalance)); + store.dispatch(setHistory(newHistory)); + }).catch(refreshErr => { + console.error("Failed to refresh wallet data after send:", refreshErr); + // Could emit a toast notification here + }); + + return response; + } catch (err) { + console.error("Failed to send Monero:", err); + throw err; // βœ… Re-throw so caller can handle appropriately + } +} + +async function refreshWalletDataAfterTransaction() { + try { + const [newBalance, newHistory] = await Promise.all([ + getMoneroBalance(), + getMoneroHistory(), + ]); + store.dispatch(setBalance(newBalance)); + store.dispatch(setHistory(newHistory)); + } catch (err) { + console.error("Failed to refresh wallet data after transaction:", err); + // Maybe show a non-blocking notification to user + } +} + +export async function updateMoneroSyncProgress() { + try { + const response = await getMoneroSyncProgress(); + store.dispatch(setSyncProgress(response)); + } catch (err) { + console.error("Failed to fetch sync progress:", err); + } +} + export async function getDataDir(): Promise { const testnet = isTestnet(); return await invoke("get_data_dir", { @@ -424,22 +562,37 @@ export async function getDataDir(): Promise { }); } -export async function resolveApproval( +export async function resolveApproval( requestId: string, - accept: object, + accept: T, ): Promise { try { await invoke( "resolve_approval_request", - { request_id: requestId, accept }, + { request_id: requestId, accept: accept as object }, ); - } catch (error) { - // Refresh approval list when resolve fails to keep UI in sync + } finally { + // Always refresh the approval list await refreshApprovals(); - throw error; + + // Refresh the approval list a few miliseconds later to again + // Just to make sure :) + setTimeout(() => { + refreshApprovals(); + }, 200); } } +export async function rejectApproval( + requestId: string, + reject: T, +): Promise { + await invoke( + "reject_approval_request", + { request_id: requestId }, + ); +} + export async function refreshApprovals(): Promise { const response = await invokeNoArgs( "get_pending_approvals", diff --git a/src-gui/src/store/combinedReducer.ts b/src-gui/src/store/combinedReducer.ts index 31bd2ebd..8908ede7 100644 --- a/src-gui/src/store/combinedReducer.ts +++ b/src-gui/src/store/combinedReducer.ts @@ -7,6 +7,7 @@ import settingsSlice from "./features/settingsSlice"; import nodesSlice from "./features/nodesSlice"; import conversationsSlice from "./features/conversationsSlice"; import poolSlice from "./features/poolSlice"; +import walletSlice from "./features/walletSlice"; export const reducers = { swap: swapReducer, @@ -18,4 +19,5 @@ export const reducers = { nodes: nodesSlice, conversations: conversationsSlice, pool: poolSlice, + wallet: walletSlice, }; diff --git a/src-gui/src/store/features/rpcSlice.ts b/src-gui/src/store/features/rpcSlice.ts index 36138b30..856cde97 100644 --- a/src-gui/src/store/features/rpcSlice.ts +++ b/src-gui/src/store/features/rpcSlice.ts @@ -156,6 +156,18 @@ export const rpcSlice = createSlice({ backgroundProgressEventRemoved(slice, action: PayloadAction) { delete slice.state.background[action.payload]; }, + rpcSetBackgroundItems( + slice, + action: PayloadAction<{ [key: string]: TauriBackgroundProgress }>, + ) { + slice.state.background = action.payload; + }, + rpcSetApprovalItems( + slice, + action: PayloadAction<{ [requestId: string]: ApprovalRequest }>, + ) { + slice.state.approvalRequests = action.payload; + }, }, }); @@ -175,6 +187,8 @@ export const { approvalRequestsReplaced, backgroundProgressEventReceived, backgroundProgressEventRemoved, + rpcSetBackgroundItems, + rpcSetApprovalItems, } = rpcSlice.actions; export default rpcSlice.reducer; diff --git a/src-gui/src/store/features/walletSlice.ts b/src-gui/src/store/features/walletSlice.ts new file mode 100644 index 00000000..7f2949b8 --- /dev/null +++ b/src-gui/src/store/features/walletSlice.ts @@ -0,0 +1,65 @@ +import { createSlice, PayloadAction } from "@reduxjs/toolkit"; +import { + GetMoneroBalanceResponse, + GetMoneroHistoryResponse, + GetMoneroSyncProgressResponse, +} from "models/tauriModel"; + +interface WalletState { + // Wallet data + mainAddress: string | null; + balance: GetMoneroBalanceResponse | null; + syncProgress: GetMoneroSyncProgressResponse | null; + history: GetMoneroHistoryResponse | null; +} + +export interface WalletSlice { + state: WalletState; +} + +const initialState: WalletSlice = { + state: { + // Wallet data + mainAddress: null, + balance: null, + syncProgress: null, + history: null, + }, +}; + +export const walletSlice = createSlice({ + name: "wallet", + initialState, + reducers: { + // Wallet data actions + setMainAddress(slice, action: PayloadAction) { + slice.state.mainAddress = action.payload; + }, + setBalance(slice, action: PayloadAction) { + slice.state.balance = action.payload; + }, + setSyncProgress( + slice, + action: PayloadAction, + ) { + slice.state.syncProgress = action.payload; + }, + setHistory(slice, action: PayloadAction) { + slice.state.history = action.payload; + }, + // Reset actions + resetWalletState(slice) { + slice.state = initialState.state; + }, + }, +}); + +export const { + setMainAddress, + setBalance, + setSyncProgress, + setHistory, + resetWalletState, +} = walletSlice.actions; + +export default walletSlice.reducer; diff --git a/src-gui/src/store/hooks.ts b/src-gui/src/store/hooks.ts index 2c71aa05..c987a629 100644 --- a/src-gui/src/store/hooks.ts +++ b/src-gui/src/store/hooks.ts @@ -12,6 +12,10 @@ import { isPendingSelectMakerApprovalEvent, haveFundsBeenLocked, PendingSeedSelectionApprovalRequest, + PendingSendMoneroApprovalRequest, + isPendingSendMoneroApprovalEvent, + PendingPasswordApprovalRequest, + isPendingPasswordApprovalEvent, } from "models/tauriModelExt"; import { TypedUseSelectorHook, useDispatch, useSelector } from "react-redux"; import type { AppDispatch, RootState } from "renderer/store/storeRenderer"; @@ -207,6 +211,11 @@ export function usePendingLockBitcoinApproval(): PendingLockBitcoinApprovalReque return approvals.filter((c) => isPendingLockBitcoinApprovalEvent(c)); } +export function usePendingSendMoneroApproval(): PendingSendMoneroApprovalRequest[] { + const approvals = usePendingApprovals(); + return approvals.filter((c) => isPendingSendMoneroApprovalEvent(c)); +} + export function usePendingSelectMakerApproval(): PendingSelectMakerApprovalRequest[] { const approvals = usePendingApprovals(); return approvals.filter((c) => isPendingSelectMakerApprovalEvent(c)); @@ -217,6 +226,11 @@ export function usePendingSeedSelectionApproval(): PendingSeedSelectionApprovalR return approvals.filter((c) => isPendingSeedSelectionApprovalEvent(c)); } +export function usePendingPasswordApproval(): PendingPasswordApprovalRequest[] { + const approvals = usePendingApprovals(); + return approvals.filter((c) => isPendingPasswordApprovalEvent(c)); +} + /// Returns all the pending background processes /// In the format [id, {componentName, {type: "Pending", content: {consumed, total}}}] export function usePendingBackgroundProcesses(): [ diff --git a/src-gui/src/store/middleware/storeListener.ts b/src-gui/src/store/middleware/storeListener.ts index e9a24f14..9042c5a9 100644 --- a/src-gui/src/store/middleware/storeListener.ts +++ b/src-gui/src/store/middleware/storeListener.ts @@ -6,6 +6,7 @@ import { updateAllNodeStatuses, fetchSellersAtPresetRendezvousPoints, getSwapInfo, + initializeMoneroWallet, } from "renderer/rpc"; import logger from "utils/logger"; import { contextStatusEventReceived } from "store/features/rpcSlice"; @@ -69,6 +70,7 @@ export function createMainListeners() { checkBitcoinBalance(), getAllSwapInfos(), fetchSellersAtPresetRendezvousPoints(), + initializeMoneroWallet(), ]); } }, diff --git a/src-gui/src/utils/conversionUtils.ts b/src-gui/src/utils/conversionUtils.ts index 2e0ce48c..f69db74f 100644 --- a/src-gui/src/utils/conversionUtils.ts +++ b/src-gui/src/utils/conversionUtils.ts @@ -15,6 +15,10 @@ export function piconerosToXmr(piconeros: number): number { return piconeros / 1000000000000; } +export function xmrToPiconeros(xmr: number): number { + return Math.ceil(xmr * 1000000000000); +} + export function isXmrAddressValid(address: string, stagenet: boolean) { const re = stagenet ? "^(?:[57][0-9A-Za-z]{94}|[57][0-9A-Za-z]{105})$" diff --git a/src-gui/yarn.lock b/src-gui/yarn.lock index a077c037..15e9c0e3 100644 --- a/src-gui/yarn.lock +++ b/src-gui/yarn.lock @@ -4,7 +4,7 @@ "@ampproject/remapping@^2.2.0": version "2.3.0" - resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.3.0.tgz#ed441b6fa600072520ce18b43d2c8cc8caecc7f4" + resolved "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz" integrity sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw== dependencies: "@jridgewell/gen-mapping" "^0.3.5" @@ -12,144 +12,211 @@ "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.27.1": version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.27.1.tgz#200f715e66d52a23b221a9435534a91cc13ad5be" + resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz" integrity sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg== dependencies: "@babel/helper-validator-identifier" "^7.27.1" js-tokens "^4.0.0" picocolors "^1.1.1" -"@babel/compat-data@^7.27.2": - version "7.27.5" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.27.5.tgz#7d0658ec1a8420fc866d1df1b03bea0e79934c82" - integrity sha512-KiRAp/VoJaWkkte84TvUd9qjdbZAdiqyvMxrGl1N6vzFogKmaLgoM3L1kgtLicp2HP5fBJS8JrZKLVIZGVJAVg== +"@babel/code-frame@^7.25.9", "@babel/code-frame@^7.26.0", "@babel/code-frame@^7.26.2": + version "7.26.2" + resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz" + integrity sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ== + dependencies: + "@babel/helper-validator-identifier" "^7.25.9" + js-tokens "^4.0.0" + picocolors "^1.0.0" -"@babel/core@^7.27.4": - version "7.27.4" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.27.4.tgz#cc1fc55d0ce140a1828d1dd2a2eba285adbfb3ce" - integrity sha512-bXYxrXFubeYdvB0NhD/NBB3Qi6aZeV20GOWVI47t2dkecCEoneR4NPVcb7abpXDEvejgrUfFtG6vG/zxAKmg+g== +"@babel/compat-data@^7.25.9": + version "7.26.3" + resolved "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.3.tgz" + integrity sha512-nHIxvKPniQXpmQLb0vhY3VaFb3S0YrTAwpOWJZh1wn3oJPjJk9Asva204PsBdmAE8vpzfHudT8DB0scYvy9q0g== + +"@babel/core@^7.26.0": + version "7.26.0" + resolved "https://registry.npmjs.org/@babel/core/-/core-7.26.0.tgz" + integrity sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg== dependencies: "@ampproject/remapping" "^2.2.0" - "@babel/code-frame" "^7.27.1" - "@babel/generator" "^7.27.3" - "@babel/helper-compilation-targets" "^7.27.2" - "@babel/helper-module-transforms" "^7.27.3" - "@babel/helpers" "^7.27.4" - "@babel/parser" "^7.27.4" - "@babel/template" "^7.27.2" - "@babel/traverse" "^7.27.4" - "@babel/types" "^7.27.3" + "@babel/code-frame" "^7.26.0" + "@babel/generator" "^7.26.0" + "@babel/helper-compilation-targets" "^7.25.9" + "@babel/helper-module-transforms" "^7.26.0" + "@babel/helpers" "^7.26.0" + "@babel/parser" "^7.26.0" + "@babel/template" "^7.25.9" + "@babel/traverse" "^7.25.9" + "@babel/types" "^7.26.0" convert-source-map "^2.0.0" debug "^4.1.0" gensync "^1.0.0-beta.2" json5 "^2.2.3" semver "^6.3.1" -"@babel/generator@^7.27.3": - version "7.27.5" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.27.5.tgz#3eb01866b345ba261b04911020cbe22dd4be8c8c" - integrity sha512-ZGhA37l0e/g2s1Cnzdix0O3aLYm66eF8aufiVteOgnwxgnRP8GoyMj7VWsgWnQbVKXyge7hqrFh2K2TQM6t1Hw== +"@babel/generator@^7.26.0", "@babel/generator@^7.26.3": + version "7.26.3" + resolved "https://registry.npmjs.org/@babel/generator/-/generator-7.26.3.tgz" + integrity sha512-6FF/urZvD0sTeO7k6/B15pMLC4CHUv1426lzr3N01aHJTl046uCAh9LXW/fzeXXjPNCJ6iABW5XaWOsIZB93aQ== dependencies: - "@babel/parser" "^7.27.5" + "@babel/parser" "^7.26.3" + "@babel/types" "^7.26.3" + "@jridgewell/gen-mapping" "^0.3.5" + "@jridgewell/trace-mapping" "^0.3.25" + jsesc "^3.0.2" + +"@babel/generator@^7.27.3": + version "7.27.3" + resolved "https://registry.npmjs.org/@babel/generator/-/generator-7.27.3.tgz" + integrity sha512-xnlJYj5zepml8NXtjkG0WquFUv8RskFqyFcVgTBp5k+NaA/8uw/K+OSVf8AMGw5e9HKP2ETd5xpK5MLZQD6b4Q== + dependencies: + "@babel/parser" "^7.27.3" "@babel/types" "^7.27.3" "@jridgewell/gen-mapping" "^0.3.5" "@jridgewell/trace-mapping" "^0.3.25" jsesc "^3.0.2" -"@babel/helper-compilation-targets@^7.27.2": - version "7.27.2" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz#46a0f6efab808d51d29ce96858dd10ce8732733d" - integrity sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ== +"@babel/helper-compilation-targets@^7.25.9": + version "7.25.9" + resolved "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.9.tgz" + integrity sha512-j9Db8Suy6yV/VHa4qzrj9yZfZxhLWQdVnRlXxmKLYlhWUVB1sB2G5sxuWYXk/whHD9iW76PmNzxZ4UCnTQTVEQ== dependencies: - "@babel/compat-data" "^7.27.2" - "@babel/helper-validator-option" "^7.27.1" + "@babel/compat-data" "^7.25.9" + "@babel/helper-validator-option" "^7.25.9" browserslist "^4.24.0" lru-cache "^5.1.1" semver "^6.3.1" -"@babel/helper-module-imports@^7.16.7", "@babel/helper-module-imports@^7.27.1": +"@babel/helper-module-imports@^7.16.7": version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz#7ef769a323e2655e126673bb6d2d6913bbead204" + resolved "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz" integrity sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w== dependencies: "@babel/traverse" "^7.27.1" "@babel/types" "^7.27.1" -"@babel/helper-module-transforms@^7.27.3": - version "7.27.3" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.27.3.tgz#db0bbcfba5802f9ef7870705a7ef8788508ede02" - integrity sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg== +"@babel/helper-module-imports@^7.25.9": + version "7.25.9" + resolved "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz" + integrity sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw== dependencies: - "@babel/helper-module-imports" "^7.27.1" - "@babel/helper-validator-identifier" "^7.27.1" - "@babel/traverse" "^7.27.3" + "@babel/traverse" "^7.25.9" + "@babel/types" "^7.25.9" -"@babel/helper-plugin-utils@^7.27.1": - version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz#ddb2f876534ff8013e6c2b299bf4d39b3c51d44c" - integrity sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw== +"@babel/helper-module-transforms@^7.26.0": + version "7.26.0" + resolved "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz" + integrity sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw== + dependencies: + "@babel/helper-module-imports" "^7.25.9" + "@babel/helper-validator-identifier" "^7.25.9" + "@babel/traverse" "^7.25.9" + +"@babel/helper-plugin-utils@^7.25.9": + version "7.25.9" + resolved "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.25.9.tgz" + integrity sha512-kSMlyUVdWe25rEsRGviIgOWnoT/nfABVWlqt9N19/dIPWViAOW2s9wznP5tURbs/IDuNk4gPy3YdYRgH3uxhBw== + +"@babel/helper-string-parser@^7.25.9": + version "7.25.9" + resolved "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz" + integrity sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA== "@babel/helper-string-parser@^7.27.1": version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz#54da796097ab19ce67ed9f88b47bb2ec49367687" + resolved "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz" integrity sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA== +"@babel/helper-validator-identifier@^7.25.9": + version "7.25.9" + resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz" + integrity sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ== + "@babel/helper-validator-identifier@^7.27.1": version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz#a7054dcc145a967dd4dc8fee845a57c1316c9df8" + resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz" integrity sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow== -"@babel/helper-validator-option@^7.27.1": - version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz#fa52f5b1e7db1ab049445b421c4471303897702f" - integrity sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg== +"@babel/helper-validator-option@^7.25.9": + version "7.25.9" + resolved "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz" + integrity sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw== -"@babel/helpers@^7.27.4": - version "7.27.6" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.27.6.tgz#6456fed15b2cb669d2d1fabe84b66b34991d812c" - integrity sha512-muE8Tt8M22638HU31A3CgfSUciwz1fhATfoVai05aPXGor//CdWDCbnlY1yvBPo07njuVOCNGCSp/GTt12lIug== +"@babel/helpers@^7.26.0": + version "7.26.0" + resolved "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.0.tgz" + integrity sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw== dependencies: - "@babel/template" "^7.27.2" - "@babel/types" "^7.27.6" + "@babel/template" "^7.25.9" + "@babel/types" "^7.26.0" -"@babel/parser@^7.1.0", "@babel/parser@^7.20.7", "@babel/parser@^7.27.2", "@babel/parser@^7.27.4", "@babel/parser@^7.27.5": - version "7.27.5" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.27.5.tgz#ed22f871f110aa285a6fd934a0efed621d118826" - integrity sha512-OsQd175SxWkGlzbny8J3K8TnnDD0N3lrIUtB92xwyRpzaenGZhxDvxN/JgU00U3CDZNj9tPuDJ5H0WS4Nt3vKg== +"@babel/parser@^7.1.0", "@babel/parser@^7.20.7", "@babel/parser@^7.25.9", "@babel/parser@^7.26.0", "@babel/parser@^7.26.3": + version "7.26.3" + resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.26.3.tgz" + integrity sha512-WJ/CvmY8Mea8iDXo6a7RK2wbmJITT5fN3BEkRuFlxVyNx8jOKIIhmC4fSkTcPcf8JyavbBwIe6OpiCOBXt/IcA== + dependencies: + "@babel/types" "^7.26.3" + +"@babel/parser@^7.27.2", "@babel/parser@^7.27.3", "@babel/parser@^7.27.4": + version "7.27.4" + resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.27.4.tgz" + integrity sha512-BRmLHGwpUqLFR2jzx9orBuX/ABDkj2jLKOXrHDTN2aOKL+jFDDKaRNo9nyYsIl9h/UE/7lMKdDjKQQyxKKDZ7g== dependencies: "@babel/types" "^7.27.3" -"@babel/plugin-transform-react-jsx-self@^7.27.1": - version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz#af678d8506acf52c577cac73ff7fe6615c85fc92" - integrity sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw== +"@babel/plugin-transform-react-jsx-self@^7.25.9": + version "7.25.9" + resolved "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.25.9.tgz" + integrity sha512-y8quW6p0WHkEhmErnfe58r7x0A70uKphQm8Sp8cV7tjNQwK56sNVK0M73LK3WuYmsuyrftut4xAkjjgU0twaMg== dependencies: - "@babel/helper-plugin-utils" "^7.27.1" + "@babel/helper-plugin-utils" "^7.25.9" -"@babel/plugin-transform-react-jsx-source@^7.27.1": - version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz#dcfe2c24094bb757bf73960374e7c55e434f19f0" - integrity sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw== +"@babel/plugin-transform-react-jsx-source@^7.25.9": + version "7.25.9" + resolved "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.25.9.tgz" + integrity sha512-+iqjT8xmXhhYv4/uiYd8FNQsraMFZIfxVSqxxVSZP0WbbSAWvBXAul0m/zu+7Vv4O/3WtApy9pmaTMiumEZgfg== dependencies: - "@babel/helper-plugin-utils" "^7.27.1" + "@babel/helper-plugin-utils" "^7.25.9" -"@babel/runtime@^7.12.5", "@babel/runtime@^7.18.3", "@babel/runtime@^7.23.2", "@babel/runtime@^7.26.9", "@babel/runtime@^7.27.1", "@babel/runtime@^7.5.5", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2": +"@babel/runtime@^7.12.5", "@babel/runtime@^7.18.3", "@babel/runtime@^7.23.2", "@babel/runtime@^7.26.9", "@babel/runtime@^7.27.1", "@babel/runtime@^7.27.6", "@babel/runtime@^7.5.5", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2": version "7.27.6" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.27.6.tgz#ec4070a04d76bae8ddbb10770ba55714a417b7c6" + resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.6.tgz" integrity sha512-vbavdySgbTTrmFE+EsiqUTzlOr5bzlnJtUv9PynGCAKvfQqjIXbvFdumPM/GxMDfyuGMJaJAU6TO4zc1Jf1i8Q== +"@babel/template@^7.25.9": + version "7.25.9" + resolved "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz" + integrity sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg== + dependencies: + "@babel/code-frame" "^7.25.9" + "@babel/parser" "^7.25.9" + "@babel/types" "^7.25.9" + "@babel/template@^7.27.2": version "7.27.2" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.27.2.tgz#fa78ceed3c4e7b63ebf6cb39e5852fca45f6809d" + resolved "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz" integrity sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw== dependencies: "@babel/code-frame" "^7.27.1" "@babel/parser" "^7.27.2" "@babel/types" "^7.27.1" -"@babel/traverse@^7.27.1", "@babel/traverse@^7.27.3", "@babel/traverse@^7.27.4": +"@babel/traverse@^7.25.9": + version "7.26.3" + resolved "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.3.tgz" + integrity sha512-yTmc8J+Sj8yLzwr4PD5Xb/WF3bOYu2C2OoSZPzbuqRm4n98XirsbzaX+GloeO376UnSYIYJ4NCanwV5/ugZkwA== + dependencies: + "@babel/code-frame" "^7.26.2" + "@babel/generator" "^7.26.3" + "@babel/parser" "^7.26.3" + "@babel/template" "^7.25.9" + "@babel/types" "^7.26.3" + debug "^4.3.1" + globals "^11.1.0" + +"@babel/traverse@^7.27.1": version "7.27.4" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.27.4.tgz#b0045ac7023c8472c3d35effd7cc9ebd638da6ea" + resolved "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.4.tgz" integrity sha512-oNcu2QbHqts9BtOWJosOVJapWjBDSxGCpFvikNR5TGDYDQf3JwpIoMzIKrvfoti93cLfPJEG4tH9SPVeyCGgdA== dependencies: "@babel/code-frame" "^7.27.1" @@ -160,17 +227,25 @@ debug "^4.3.1" globals "^11.1.0" -"@babel/types@^7.0.0", "@babel/types@^7.20.7", "@babel/types@^7.27.1", "@babel/types@^7.27.3", "@babel/types@^7.27.6": - version "7.27.6" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.27.6.tgz#a434ca7add514d4e646c80f7375c0aa2befc5535" - integrity sha512-ETyHEk2VHHvl9b9jZP5IHPavHYk57EhanlRRuae9XCpb/j5bDCbPPMOBfCWhnl/7EDJz0jEMCi/RhccCE8r1+Q== +"@babel/types@^7.0.0", "@babel/types@^7.20.7", "@babel/types@^7.25.9", "@babel/types@^7.26.0", "@babel/types@^7.26.3": + version "7.26.3" + resolved "https://registry.npmjs.org/@babel/types/-/types-7.26.3.tgz" + integrity sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA== + dependencies: + "@babel/helper-string-parser" "^7.25.9" + "@babel/helper-validator-identifier" "^7.25.9" + +"@babel/types@^7.27.1", "@babel/types@^7.27.3": + version "7.27.3" + resolved "https://registry.npmjs.org/@babel/types/-/types-7.27.3.tgz" + integrity sha512-Y1GkI4ktrtvmawoSq+4FCVHNryea6uR+qUQy0AGxLSsjCX0nVmkYQMBLHDkXZuo5hGx7eYdnIaslsdBFm7zbUw== dependencies: "@babel/helper-string-parser" "^7.27.1" "@babel/helper-validator-identifier" "^7.27.1" "@emotion/babel-plugin@^11.13.5": version "11.13.5" - resolved "https://registry.yarnpkg.com/@emotion/babel-plugin/-/babel-plugin-11.13.5.tgz#eab8d65dbded74e0ecfd28dc218e75607c4e7bc0" + resolved "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.13.5.tgz" integrity sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ== dependencies: "@babel/helper-module-imports" "^7.16.7" @@ -185,9 +260,9 @@ source-map "^0.5.7" stylis "4.2.0" -"@emotion/cache@^11.13.5", "@emotion/cache@^11.14.0": +"@emotion/cache@^11.14.0": version "11.14.0" - resolved "https://registry.yarnpkg.com/@emotion/cache/-/cache-11.14.0.tgz#ee44b26986eeb93c8be82bb92f1f7a9b21b2ed76" + resolved "https://registry.npmjs.org/@emotion/cache/-/cache-11.14.0.tgz" integrity sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA== dependencies: "@emotion/memoize" "^0.9.0" @@ -198,24 +273,24 @@ "@emotion/hash@^0.9.2": version "0.9.2" - resolved "https://registry.yarnpkg.com/@emotion/hash/-/hash-0.9.2.tgz#ff9221b9f58b4dfe61e619a7788734bd63f6898b" + resolved "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz" integrity sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g== "@emotion/is-prop-valid@^1.3.0": version "1.3.1" - resolved "https://registry.yarnpkg.com/@emotion/is-prop-valid/-/is-prop-valid-1.3.1.tgz#8d5cf1132f836d7adbe42cf0b49df7816fc88240" + resolved "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.3.1.tgz" integrity sha512-/ACwoqx7XQi9knQs/G0qKvv5teDMhD7bXYns9N/wM8ah8iNb8jZ2uNO0YOgiq2o2poIvVtJS2YALasQuMSQ7Kw== dependencies: "@emotion/memoize" "^0.9.0" "@emotion/memoize@^0.9.0": version "0.9.0" - resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.9.0.tgz#745969d649977776b43fc7648c556aaa462b4102" + resolved "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz" integrity sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ== "@emotion/react@^11.14.0": version "11.14.0" - resolved "https://registry.yarnpkg.com/@emotion/react/-/react-11.14.0.tgz#cfaae35ebc67dd9ef4ea2e9acc6cd29e157dd05d" + resolved "https://registry.npmjs.org/@emotion/react/-/react-11.14.0.tgz" integrity sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA== dependencies: "@babel/runtime" "^7.18.3" @@ -229,7 +304,7 @@ "@emotion/serialize@^1.3.3": version "1.3.3" - resolved "https://registry.yarnpkg.com/@emotion/serialize/-/serialize-1.3.3.tgz#d291531005f17d704d0463a032fe679f376509e8" + resolved "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.3.tgz" integrity sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA== dependencies: "@emotion/hash" "^0.9.2" @@ -240,12 +315,12 @@ "@emotion/sheet@^1.4.0": version "1.4.0" - resolved "https://registry.yarnpkg.com/@emotion/sheet/-/sheet-1.4.0.tgz#c9299c34d248bc26e82563735f78953d2efca83c" + resolved "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.4.0.tgz" integrity sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg== "@emotion/styled@^11.14.0": version "11.14.0" - resolved "https://registry.yarnpkg.com/@emotion/styled/-/styled-11.14.0.tgz#f47ca7219b1a295186d7661583376fcea95f0ff3" + resolved "https://registry.npmjs.org/@emotion/styled/-/styled-11.14.0.tgz" integrity sha512-XxfOnXFffatap2IyCeJyNov3kiDQWoR08gPUQxvbL7fxKryGBKUZUkG6Hz48DZwVrJSVh9sJboyV1Ds4OW6SgA== dependencies: "@babel/runtime" "^7.18.3" @@ -257,22 +332,22 @@ "@emotion/unitless@^0.10.0": version "0.10.0" - resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.10.0.tgz#2af2f7c7e5150f497bdabd848ce7b218a27cf745" + resolved "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.10.0.tgz" integrity sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg== "@emotion/use-insertion-effect-with-fallbacks@^1.2.0": version "1.2.0" - resolved "https://registry.yarnpkg.com/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.2.0.tgz#8a8cb77b590e09affb960f4ff1e9a89e532738bf" + resolved "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.2.0.tgz" integrity sha512-yJMtVdH59sxi/aVJBpk9FQq+OR8ll5GT8oWd57UpeaKEVGab41JWaCFA7FRLoMLloOZF/c/wsPoe+bfGmRKgDg== "@emotion/utils@^1.4.2": version "1.4.2" - resolved "https://registry.yarnpkg.com/@emotion/utils/-/utils-1.4.2.tgz#6df6c45881fcb1c412d6688a311a98b7f59c1b52" + resolved "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.2.tgz" integrity sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA== "@emotion/weak-memoize@^0.4.0": version "0.4.0" - resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz#5e13fac887f08c44f76b0ccaf3370eb00fec9bb6" + resolved "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz" integrity sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg== "@esbuild/aix-ppc64@0.21.5": @@ -297,7 +372,7 @@ "@esbuild/darwin-arm64@0.21.5": version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz#e495b539660e51690f3928af50a76fb0a6ccff2a" + resolved "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz" integrity sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ== "@esbuild/darwin-x64@0.21.5": @@ -390,50 +465,36 @@ resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz#acad351d582d157bb145535db2a6ff53dd514b5c" integrity sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw== -"@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.7.0": - version "4.7.0" - resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz#607084630c6c033992a082de6e6fbc1a8b52175a" - integrity sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw== +"@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0": + version "4.4.1" + resolved "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz" + integrity sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA== dependencies: eslint-visitor-keys "^3.4.3" "@eslint-community/regexpp@^4.10.0", "@eslint-community/regexpp@^4.12.1": version "4.12.1" - resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.12.1.tgz#cfc6cffe39df390a3841cde2abccf92eaa7ae0e0" + resolved "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz" integrity sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ== -"@eslint/config-array@^0.20.0": - version "0.20.1" - resolved "https://registry.yarnpkg.com/@eslint/config-array/-/config-array-0.20.1.tgz#454f89be82b0e5b1ae872c154c7e2f3dd42c3979" - integrity sha512-OL0RJzC/CBzli0DrrR31qzj6d6i6Mm3HByuhflhl4LOBiWxN+3i6/t/ZQQNii4tjksXi8r2CRW1wMpWA2ULUEw== +"@eslint/config-array@^0.19.0": + version "0.19.0" + resolved "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.19.0.tgz" + integrity sha512-zdHg2FPIFNKPdcHWtiNT+jEFCHYVplAXRDlQDyqy0zGx/q2parwh7brGJSiTxRk/TSMkbM//zt/f5CHgyTyaSQ== dependencies: - "@eslint/object-schema" "^2.1.6" + "@eslint/object-schema" "^2.1.4" debug "^4.3.1" minimatch "^3.1.2" -"@eslint/config-helpers@^0.2.1": - version "0.2.3" - resolved "https://registry.yarnpkg.com/@eslint/config-helpers/-/config-helpers-0.2.3.tgz#39d6da64ed05d7662659aa7035b54cd55a9f3672" - integrity sha512-u180qk2Um1le4yf0ruXH3PYFeEZeYC3p/4wCTKrr2U1CmGdzGi3KtY0nuPDH48UJxlKCC5RDzbcbh4X0XlqgHg== +"@eslint/core@^0.9.0": + version "0.9.0" + resolved "https://registry.npmjs.org/@eslint/core/-/core-0.9.0.tgz" + integrity sha512-7ATR9F0e4W85D/0w7cU0SNj7qkAexMG+bAHEZOjo9akvGuhHE2m7umzWzfnpa0XAg5Kxc1BWmtPMV67jJ+9VUg== -"@eslint/core@^0.14.0": - version "0.14.0" - resolved "https://registry.yarnpkg.com/@eslint/core/-/core-0.14.0.tgz#326289380968eaf7e96f364e1e4cf8f3adf2d003" - integrity sha512-qIbV0/JZr7iSDjqAc60IqbLdsj9GDt16xQtWD+B78d/HAlvysGdZZ6rpJHGAc2T0FQx1X6thsSPdnoiGKdNtdg== - dependencies: - "@types/json-schema" "^7.0.15" - -"@eslint/core@^0.15.0": - version "0.15.0" - resolved "https://registry.yarnpkg.com/@eslint/core/-/core-0.15.0.tgz#8fc04709a7b9a179d9f7d93068fc000cb8c5603d" - integrity sha512-b7ePw78tEWWkpgZCDYkbqDOP8dmM6qe+AOC6iuJqlq1R/0ahMAeH3qynpnqKFGkMltrp44ohV4ubGyvLX28tzw== - dependencies: - "@types/json-schema" "^7.0.15" - -"@eslint/eslintrc@^3.3.1": - version "3.3.1" - resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-3.3.1.tgz#e55f7f1dd400600dd066dbba349c4c0bac916964" - integrity sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ== +"@eslint/eslintrc@^3.2.0": + version "3.2.0" + resolved "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.2.0.tgz" + integrity sha512-grOjVNN8P3hjJn/eIETF1wwd12DdnwFDoyceUJLYYdkpbwq3nLi+4fqrTAONx7XDALqlL220wC/RHSC/QTI/0w== dependencies: ajv "^6.12.4" debug "^4.3.2" @@ -445,37 +506,36 @@ minimatch "^3.1.2" strip-json-comments "^3.1.1" -"@eslint/js@9.28.0", "@eslint/js@^9.9.0": - version "9.28.0" - resolved "https://registry.yarnpkg.com/@eslint/js/-/js-9.28.0.tgz#7822ccc2f8cae7c3cd4f902377d520e9ae03f844" - integrity sha512-fnqSjGWd/CoIp4EXIxWVK/sHA6DOHN4+8Ix2cX5ycOY7LG0UY8nHCU5pIp2eaE1Mc7Qd8kHspYNzYXT2ojPLzg== +"@eslint/js@9.16.0", "@eslint/js@^9.9.0": + version "9.16.0" + resolved "https://registry.npmjs.org/@eslint/js/-/js-9.16.0.tgz" + integrity sha512-tw2HxzQkrbeuvyj1tG2Yqq+0H9wGoI2IMk4EOsQeX+vmd75FtJAzf+gTA69WF+baUKRYQ3x2kbLE08js5OsTVg== -"@eslint/object-schema@^2.1.6": - version "2.1.6" - resolved "https://registry.yarnpkg.com/@eslint/object-schema/-/object-schema-2.1.6.tgz#58369ab5b5b3ca117880c0f6c0b0f32f6950f24f" - integrity sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA== +"@eslint/object-schema@^2.1.4": + version "2.1.4" + resolved "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.4.tgz" + integrity sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ== -"@eslint/plugin-kit@^0.3.1": - version "0.3.2" - resolved "https://registry.yarnpkg.com/@eslint/plugin-kit/-/plugin-kit-0.3.2.tgz#0cad96b134d23a653348e3342f485636b5ef4732" - integrity sha512-4SaFZCNfJqvk/kenHpI8xvN42DMaoycy4PzKc5otHxRswww1kAt82OlBuwRVLofCACCTZEcla2Ydxv8scMXaTg== +"@eslint/plugin-kit@^0.2.3": + version "0.2.3" + resolved "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.3.tgz" + integrity sha512-2b/g5hRmpbb1o4GnTZax9N9m0FXzz9OV42ZzI4rDDMDuHUqigAiQCEWChBWCY4ztAGVRjoWT19v0yMmc5/L5kA== dependencies: - "@eslint/core" "^0.15.0" levn "^0.4.1" "@fontsource/roboto@^5.1.0": - version "5.2.6" - resolved "https://registry.yarnpkg.com/@fontsource/roboto/-/roboto-5.2.6.tgz#93a2a5ef98cc79880355de0aac71def215b873e7" - integrity sha512-hzarG7yAhMoP418smNgfY4fO7UmuUEm5JUtbxCoCcFHT0hOJB+d/qAEyoNjz7YkPU5OjM2LM8rJnW8hfm0JLaA== + version "5.1.0" + resolved "https://registry.npmjs.org/@fontsource/roboto/-/roboto-5.1.0.tgz" + integrity sha512-cFRRC1s6RqPygeZ8Uw/acwVHqih8Czjt6Q0MwoUoDe9U3m4dH1HmNDRBZyqlMSFwgNAUKgFImncKdmDHyKpwdg== "@humanfs/core@^0.19.1": version "0.19.1" - resolved "https://registry.yarnpkg.com/@humanfs/core/-/core-0.19.1.tgz#17c55ca7d426733fe3c561906b8173c336b40a77" + resolved "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz" integrity sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA== "@humanfs/node@^0.16.6": version "0.16.6" - resolved "https://registry.yarnpkg.com/@humanfs/node/-/node-0.16.6.tgz#ee2a10eaabd1131987bf0488fd9b820174cd765e" + resolved "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz" integrity sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw== dependencies: "@humanfs/core" "^0.19.1" @@ -483,23 +543,23 @@ "@humanwhocodes/module-importer@^1.0.1": version "1.0.1" - resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" + resolved "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz" integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== "@humanwhocodes/retry@^0.3.0": version "0.3.1" - resolved "https://registry.yarnpkg.com/@humanwhocodes/retry/-/retry-0.3.1.tgz#c72a5c76a9fbaf3488e231b13dc52c0da7bab42a" + resolved "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz" integrity sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA== -"@humanwhocodes/retry@^0.4.2": - version "0.4.3" - resolved "https://registry.yarnpkg.com/@humanwhocodes/retry/-/retry-0.4.3.tgz#c2b9d2e374ee62c586d3adbea87199b1d7a7a6ba" - integrity sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ== +"@humanwhocodes/retry@^0.4.1": + version "0.4.1" + resolved "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.1.tgz" + integrity sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA== "@jridgewell/gen-mapping@^0.3.5": - version "0.3.8" - resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz#4f0e06362e01362f823d348f1872b08f666d8142" - integrity sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA== + version "0.3.5" + resolved "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz" + integrity sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg== dependencies: "@jridgewell/set-array" "^1.2.1" "@jridgewell/sourcemap-codec" "^1.4.10" @@ -507,43 +567,43 @@ "@jridgewell/resolve-uri@^3.1.0": version "3.1.2" - resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6" + resolved "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz" integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== "@jridgewell/set-array@^1.2.1": version "1.2.1" - resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.2.1.tgz#558fb6472ed16a4c850b889530e6b36438c49280" + resolved "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz" integrity sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A== "@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14", "@jridgewell/sourcemap-codec@^1.5.0": version "1.5.0" - resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz#3188bcb273a414b0d215fd22a58540b989b9409a" + resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz" integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ== "@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25": version "0.3.25" - resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0" + resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz" integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ== dependencies: "@jridgewell/resolve-uri" "^3.1.0" "@jridgewell/sourcemap-codec" "^1.4.14" -"@mui/core-downloads-tracker@^7.1.1": - version "7.1.1" - resolved "https://registry.yarnpkg.com/@mui/core-downloads-tracker/-/core-downloads-tracker-7.1.1.tgz#43532ccf57be19055eb20e7802508520293cf286" - integrity sha512-yBckQs4aQ8mqukLnPC6ivIRv6guhaXi8snVl00VtyojBbm+l6VbVhyTSZ68Abcx7Ah8B+GZhrB7BOli+e+9LkQ== +"@mui/core-downloads-tracker@^7.2.0": + version "7.2.0" + resolved "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-7.2.0.tgz" + integrity sha512-d49s7kEgI5iX40xb2YPazANvo7Bx0BLg/MNRwv+7BVpZUzXj1DaVCKlQTDex3gy/0jsCb4w7AY2uH4t4AJvSog== "@mui/icons-material@^7.1.1": - version "7.1.1" - resolved "https://registry.yarnpkg.com/@mui/icons-material/-/icons-material-7.1.1.tgz#0e0e9640579da5e4096f0449438337c448bc5a5c" - integrity sha512-X37+Yc8QpEnl0sYmz+WcLFy2dWgNRzbswDzLPXG7QU1XDVlP5TPp1HXjdmCupOWLL/I9m1fyhcyZl8/HPpp/Cg== + version "7.2.0" + resolved "https://registry.npmjs.org/@mui/icons-material/-/icons-material-7.2.0.tgz" + integrity sha512-gRCspp3pfjHQyTmSOmYw7kUQTd9Udpdan4R8EnZvqPeoAtHnPzkvjBrBqzKaoAbbBp5bGF7BcD18zZJh4nwu0A== dependencies: - "@babel/runtime" "^7.27.1" + "@babel/runtime" "^7.27.6" "@mui/lab@^7.0.0-beta.13": - version "7.0.0-beta.13" - resolved "https://registry.yarnpkg.com/@mui/lab/-/lab-7.0.0-beta.13.tgz#537991d4ea85052ab4f63bb00439e3587a501c8c" - integrity sha512-wLSeePenug3+/kek4cFMIF3QZVC2fHt2Z3O3HwOFvakgErmT39WltYsNpWNojCnXUqcIExUp9xNW0Wk+tJShgA== + version "7.0.0-beta.14" + resolved "https://registry.npmjs.org/@mui/lab/-/lab-7.0.0-beta.14.tgz" + integrity sha512-pn+ZvylDcBKQOo17oa/PhtIA/UFQFq8RvpN+r/jHrztz/CjMDju2CWBne0txvQ5JIS8uTIGp2/IsTa7II1g5wg== dependencies: "@babel/runtime" "^7.27.1" "@mui/system" "^7.1.1" @@ -553,15 +613,15 @@ prop-types "^15.8.1" "@mui/material@^7.1.1": - version "7.1.1" - resolved "https://registry.yarnpkg.com/@mui/material/-/material-7.1.1.tgz#5f75b25936925be14cb34abe0489cda82a6f8413" - integrity sha512-mTpdmdZCaHCGOH3SrYM41+XKvNL0iQfM9KlYgpSjgadXx/fEKhhvOktxm8++Xw6FFeOHoOiV+lzOI8X1rsv71A== + version "7.2.0" + resolved "https://registry.npmjs.org/@mui/material/-/material-7.2.0.tgz" + integrity sha512-NTuyFNen5Z2QY+I242MDZzXnFIVIR6ERxo7vntFi9K1wCgSwvIl0HcAO2OOydKqqKApE6omRiYhpny1ZhGuH7Q== dependencies: - "@babel/runtime" "^7.27.1" - "@mui/core-downloads-tracker" "^7.1.1" - "@mui/system" "^7.1.1" - "@mui/types" "^7.4.3" - "@mui/utils" "^7.1.1" + "@babel/runtime" "^7.27.6" + "@mui/core-downloads-tracker" "^7.2.0" + "@mui/system" "^7.2.0" + "@mui/types" "^7.4.4" + "@mui/utils" "^7.2.0" "@popperjs/core" "^2.11.8" "@types/react-transition-group" "^4.4.12" clsx "^2.1.1" @@ -570,63 +630,92 @@ react-is "^19.1.0" react-transition-group "^4.4.5" -"@mui/private-theming@^7.1.1": - version "7.1.1" - resolved "https://registry.yarnpkg.com/@mui/private-theming/-/private-theming-7.1.1.tgz#c2ecc57a9b97fbfdd850430de500c42a0f2571fe" - integrity sha512-M8NbLUx+armk2ZuaxBkkMk11ultnWmrPlN0Xe3jUEaBChg/mcxa5HWIWS1EE4DF36WRACaAHVAvyekWlDQf0PQ== +"@mui/private-theming@^7.2.0": + version "7.2.0" + resolved "https://registry.npmjs.org/@mui/private-theming/-/private-theming-7.2.0.tgz" + integrity sha512-y6N1Yt3T5RMxVFnCh6+zeSWBuQdNDm5/UlM0EAYZzZR/1u+XKJWYQmbpx4e+F+1EpkYi3Nk8KhPiQDi83M3zIw== dependencies: - "@babel/runtime" "^7.27.1" - "@mui/utils" "^7.1.1" + "@babel/runtime" "^7.27.6" + "@mui/utils" "^7.2.0" prop-types "^15.8.1" -"@mui/styled-engine@^7.1.1": - version "7.1.1" - resolved "https://registry.yarnpkg.com/@mui/styled-engine/-/styled-engine-7.1.1.tgz#2524e0f4e22782b42ea4c32f36f9e19a50ccf55a" - integrity sha512-R2wpzmSN127j26HrCPYVQ53vvMcT5DaKLoWkrfwUYq3cYytL6TQrCH8JBH3z79B6g4nMZZVoaXrxO757AlShaw== +"@mui/styled-engine@^7.2.0": + version "7.2.0" + resolved "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-7.2.0.tgz" + integrity sha512-yq08xynbrNYcB1nBcW9Fn8/h/iniM3ewRguGJXPIAbHvxEF7Pz95kbEEOAAhwzxMX4okhzvHmk0DFuC5ayvgIQ== dependencies: - "@babel/runtime" "^7.27.1" - "@emotion/cache" "^11.13.5" + "@babel/runtime" "^7.27.6" + "@emotion/cache" "^11.14.0" "@emotion/serialize" "^1.3.3" "@emotion/sheet" "^1.4.0" csstype "^3.1.3" prop-types "^15.8.1" -"@mui/system@^7.1.1": - version "7.1.1" - resolved "https://registry.yarnpkg.com/@mui/system/-/system-7.1.1.tgz#eff52e597b0bfed8ecf2e973f4575ef737430727" - integrity sha512-Kj1uhiqnj4Zo7PDjAOghtXJtNABunWvhcRU0O7RQJ7WOxeynoH6wXPcilphV8QTFtkKaip8EiNJRiCD+B3eROA== +"@mui/system@^7.1.1", "@mui/system@^7.2.0": + version "7.2.0" + resolved "https://registry.npmjs.org/@mui/system/-/system-7.2.0.tgz" + integrity sha512-PG7cm/WluU6RAs+gNND2R9vDwNh+ERWxPkqTaiXQJGIFAyJ+VxhyKfzpdZNk0z0XdmBxxi9KhFOpgxjehf/O0A== dependencies: - "@babel/runtime" "^7.27.1" - "@mui/private-theming" "^7.1.1" - "@mui/styled-engine" "^7.1.1" - "@mui/types" "^7.4.3" - "@mui/utils" "^7.1.1" + "@babel/runtime" "^7.27.6" + "@mui/private-theming" "^7.2.0" + "@mui/styled-engine" "^7.2.0" + "@mui/types" "^7.4.4" + "@mui/utils" "^7.2.0" clsx "^2.1.1" csstype "^3.1.3" prop-types "^15.8.1" "@mui/types@^7.4.3": version "7.4.3" - resolved "https://registry.yarnpkg.com/@mui/types/-/types-7.4.3.tgz#b205ee3404db0478cd93227fc21967e2cb8630fe" + resolved "https://registry.npmjs.org/@mui/types/-/types-7.4.3.tgz" integrity sha512-2UCEiK29vtiZTeLdS2d4GndBKacVyxGvReznGXGr+CzW/YhjIX+OHUdCIczZjzcRAgKBGmE9zCIgoV9FleuyRQ== dependencies: "@babel/runtime" "^7.27.1" -"@mui/utils@^7.1.1": - version "7.1.1" - resolved "https://registry.yarnpkg.com/@mui/utils/-/utils-7.1.1.tgz#de315ec45ac9e16c637dcc2b32cd7912edb4e234" - integrity sha512-BkOt2q7MBYl7pweY2JWwfrlahhp+uGLR8S+EhiyRaofeRYUWL2YKbSGQvN4hgSN1i8poN0PaUiii1kEMrchvzg== +"@mui/types@^7.4.4": + version "7.4.4" + resolved "https://registry.npmjs.org/@mui/types/-/types-7.4.4.tgz" + integrity sha512-p63yhbX52MO/ajXC7hDHJA5yjzJekvWD3q4YDLl1rSg+OXLczMYPvTuSuviPRCgRX8+E42RXz1D/dz9SxPSlWg== dependencies: - "@babel/runtime" "^7.27.1" - "@mui/types" "^7.4.3" - "@types/prop-types" "^15.7.14" + "@babel/runtime" "^7.27.6" + +"@mui/utils@^7.1.1", "@mui/utils@^7.2.0": + version "7.2.0" + resolved "https://registry.npmjs.org/@mui/utils/-/utils-7.2.0.tgz" + integrity sha512-O0i1GQL6MDzhKdy9iAu5Yr0Sz1wZjROH1o3aoztuivdCXqEeQYnEjTDiRLGuFxI9zrUbTHBwobMyQH5sNtyacw== + dependencies: + "@babel/runtime" "^7.27.6" + "@mui/types" "^7.4.4" + "@types/prop-types" "^15.7.15" clsx "^2.1.1" prop-types "^15.8.1" react-is "^19.1.0" +"@mui/x-date-pickers@^8.8.0": + version "8.8.0" + resolved "https://registry.yarnpkg.com/@mui/x-date-pickers/-/x-date-pickers-8.8.0.tgz#360557a60b9bf7a05f8c54724cb91ab128733aec" + integrity sha512-Rlk1wgkNHjMf22Ejv6jB+XueFYZmiwMYlJz3oRw9d8HhnshtMVjJbSNOI9yZ2wtqyEr0CGfryCnryywHpmfzeA== + dependencies: + "@babel/runtime" "^7.27.6" + "@mui/utils" "^7.2.0" + "@mui/x-internals" "8.8.0" + "@types/react-transition-group" "^4.4.12" + clsx "^2.1.1" + prop-types "^15.8.1" + react-transition-group "^4.4.5" + +"@mui/x-internals@8.8.0": + version "8.8.0" + resolved "https://registry.yarnpkg.com/@mui/x-internals/-/x-internals-8.8.0.tgz#ffaf6cd1cfdd7dd7376236637312deac83a94e85" + integrity sha512-qTRK5oINkAjZ7sIHpSnESLNq1xtQUmmfmGscYUSEP0uHoYh6pKkNWH9+7yzggRHuTv+4011VBwN9s+efrk+xZg== + dependencies: + "@babel/runtime" "^7.27.6" + "@mui/utils" "^7.2.0" + reselect "^5.1.1" + "@nodelib/fs.scandir@2.1.5": version "2.1.5" - resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" + resolved "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz" integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== dependencies: "@nodelib/fs.stat" "2.0.5" @@ -634,12 +723,12 @@ "@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": version "2.0.5" - resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" + resolved "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz" integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== "@nodelib/fs.walk@^1.2.3": version "1.2.8" - resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" + resolved "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz" integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== dependencies: "@nodelib/fs.scandir" "2.1.5" @@ -647,12 +736,12 @@ "@popperjs/core@^2.11.8": version "2.11.8" - resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.8.tgz#6b79032e760a0899cd4204710beede972a3a185f" + resolved "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz" integrity sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A== "@redux-devtools/core@^4.1.1": version "4.1.1" - resolved "https://registry.yarnpkg.com/@redux-devtools/core/-/core-4.1.1.tgz#4e0d6fe7d250f10d927872448f0085b6c48cd933" + resolved "https://registry.npmjs.org/@redux-devtools/core/-/core-4.1.1.tgz" integrity sha512-ZyyJwiHX4DFDU0llk45tYSFPoIMekdoKLz0Q7soowpNOtchvTxruQx4Xy//Cohkwsw+DH8W1amdo4C/NYT6ARA== dependencies: "@babel/runtime" "^7.26.9" @@ -660,7 +749,7 @@ "@redux-devtools/instrument@^2.2.0": version "2.2.0" - resolved "https://registry.yarnpkg.com/@redux-devtools/instrument/-/instrument-2.2.0.tgz#bc9d015da693aa9fabdb32f4fd07ee4c1328eb95" + resolved "https://registry.npmjs.org/@redux-devtools/instrument/-/instrument-2.2.0.tgz" integrity sha512-HKaL+ghBQ4ZQkM/kEQIKx8dNwz4E1oeiCDfdQlpPXxEi/BrisyrFFncAXb1y2HIJsLV9zSvQUR2jRtMDWgfi8w== dependencies: "@babel/runtime" "^7.23.2" @@ -668,7 +757,7 @@ "@redux-devtools/remote@^0.9.5": version "0.9.5" - resolved "https://registry.yarnpkg.com/@redux-devtools/remote/-/remote-0.9.5.tgz#e0553026ea2d2f132246991c68dad57ac4d034e1" + resolved "https://registry.npmjs.org/@redux-devtools/remote/-/remote-0.9.5.tgz" integrity sha512-ETOUWgB5n6yopU4xH6wSwwmcVQT6liGBJbrWHkJkXCbCq9j/VqXHQ7spNN398p59vDseFZWOPo8KXNI0Mvo1RQ== dependencies: "@babel/runtime" "^7.26.9" @@ -680,7 +769,7 @@ "@redux-devtools/serialize@^0.4.2": version "0.4.2" - resolved "https://registry.yarnpkg.com/@redux-devtools/serialize/-/serialize-0.4.2.tgz#564c0cf2e5cb119a1884b1994a51f6d2e138b9a5" + resolved "https://registry.npmjs.org/@redux-devtools/serialize/-/serialize-0.4.2.tgz" integrity sha512-YVqZCChJld5l3Ni2psEZ5loe9x5xpf9J4ckz+7OJdzCNsplC7vzjnkQbFxE6+ULZbywRVp+nSBslTXmaXqAw4A== dependencies: "@babel/runtime" "^7.23.2" @@ -688,7 +777,7 @@ "@redux-devtools/utils@^3.1.1": version "3.1.1" - resolved "https://registry.yarnpkg.com/@redux-devtools/utils/-/utils-3.1.1.tgz#a0c0aecf2c2e0f02518d48450dda90b9fe6eeb11" + resolved "https://registry.npmjs.org/@redux-devtools/utils/-/utils-3.1.1.tgz" integrity sha512-l+m3/8a7lcxULInBADIqE/3Tt2DkTJm5MAGVA/4czMCXW0VE+gdjkoRFqgZhTBoDJW1fi1z8pdL+4G/+R1rDJw== dependencies: "@babel/runtime" "^7.26.9" @@ -702,359 +791,333 @@ redux "^5.0.1" "@reduxjs/toolkit@^2.3.0": - version "2.8.2" - resolved "https://registry.yarnpkg.com/@reduxjs/toolkit/-/toolkit-2.8.2.tgz#f4e9f973c6fc930c1e0f3bf462cc95210c28f5f9" - integrity sha512-MYlOhQ0sLdw4ud48FoC5w0dH9VfWQjtCjreKwYTT3l+r427qYC5Y8PihNutepr8XrNaBUDQo9khWUwQxZaqt5A== + version "2.4.0" + resolved "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.4.0.tgz" + integrity sha512-wJZEuSKj14tvNfxiIiJws0tQN77/rDqucBq528ApebMIRHyWpCanJVQRxQ8WWZC19iCDKxDsGlbAir3F1layxA== dependencies: - "@standard-schema/spec" "^1.0.0" - "@standard-schema/utils" "^0.3.0" immer "^10.0.3" redux "^5.0.1" redux-thunk "^3.1.0" reselect "^5.1.0" -"@rolldown/pluginutils@1.0.0-beta.11": - version "1.0.0-beta.11" - resolved "https://registry.yarnpkg.com/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.11.tgz#1e3e8044dd053c3dfa4bbbb3861f6e180ee78343" - integrity sha512-L/gAA/hyCSuzTF1ftlzUSI/IKr2POHsv1Dd78GfqkR83KMNuswWD61JxGV2L7nRwBBBSDr6R1gCkdTmoN7W4ag== - "@rollup/plugin-virtual@^3.0.2": version "3.0.2" - resolved "https://registry.yarnpkg.com/@rollup/plugin-virtual/-/plugin-virtual-3.0.2.tgz#17e17eeecb4c9fa1c0a6e72c9e5f66382fddbb82" + resolved "https://registry.npmjs.org/@rollup/plugin-virtual/-/plugin-virtual-3.0.2.tgz" integrity sha512-10monEYsBp3scM4/ND4LNH5Rxvh3e/cVeL3jWTgZ2SrQ+BmUoQcopVQvnaMcOnykb1VkxUFuDAN+0FnpTFRy2A== -"@rollup/rollup-android-arm-eabi@4.43.0": - version "4.43.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.43.0.tgz#9241b59af721beb7e3587a56c6c245d6c465753d" - integrity sha512-Krjy9awJl6rKbruhQDgivNbD1WuLb8xAclM4IR4cN5pHGAs2oIMMQJEiC3IC/9TZJ+QZkmZhlMO/6MBGxPidpw== +"@rollup/rollup-android-arm-eabi@4.28.0": + version "4.28.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.28.0.tgz#462e7ecdd60968bc9eb95a20d185e74f8243ec1b" + integrity sha512-wLJuPLT6grGZsy34g4N1yRfYeouklTgPhH1gWXCYspenKYD0s3cR99ZevOGw5BexMNywkbV3UkjADisozBmpPQ== -"@rollup/rollup-android-arm64@4.43.0": - version "4.43.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.43.0.tgz#f70ee53ba991fdd65c277b0716c559736d490a58" - integrity sha512-ss4YJwRt5I63454Rpj+mXCXicakdFmKnUNxr1dLK+5rv5FJgAxnN7s31a5VchRYxCFWdmnDWKd0wbAdTr0J5EA== +"@rollup/rollup-android-arm64@4.28.0": + version "4.28.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.28.0.tgz#78a2b8a8a55f71a295eb860a654ae90a2b168f40" + integrity sha512-eiNkznlo0dLmVG/6wf+Ifi/v78G4d4QxRhuUl+s8EWZpDewgk7PX3ZyECUXU0Zq/Ca+8nU8cQpNC4Xgn2gFNDA== -"@rollup/rollup-darwin-arm64@4.43.0": - version "4.43.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.43.0.tgz#9f59000e817cf5760d87515ce899f8b93fe8756a" - integrity sha512-eKoL8ykZ7zz8MjgBenEF2OoTNFAPFz1/lyJ5UmmFSz5jW+7XbH1+MAgCVHy72aG59rbuQLcJeiMrP8qP5d/N0A== +"@rollup/rollup-darwin-arm64@4.28.0": + version "4.28.0" + resolved "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.28.0.tgz" + integrity sha512-lmKx9yHsppblnLQZOGxdO66gT77bvdBtr/0P+TPOseowE7D9AJoBw8ZDULRasXRWf1Z86/gcOdpBrV6VDUY36Q== -"@rollup/rollup-darwin-x64@4.43.0": - version "4.43.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.43.0.tgz#c92aebd02725ae1b88bdce40f08f7823e8055c78" - integrity sha512-SYwXJgaBYW33Wi/q4ubN+ldWC4DzQY62S4Ll2dgfr/dbPoF50dlQwEaEHSKrQdSjC6oIe1WgzosoaNoHCdNuMg== +"@rollup/rollup-darwin-x64@4.28.0": + version "4.28.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.28.0.tgz#f72484e842521a5261978034e18e20f778a2850d" + integrity sha512-8hxgfReVs7k9Js1uAIhS6zq3I+wKQETInnWQtgzt8JfGx51R1N6DRVy3F4o0lQwumbErRz52YqwjfvuwRxGv1w== -"@rollup/rollup-freebsd-arm64@4.43.0": - version "4.43.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.43.0.tgz#b128dbe7b353922ddd729a4fc4e408ddcbf338b5" - integrity sha512-SV+U5sSo0yujrjzBF7/YidieK2iF6E7MdF6EbYxNz94lA+R0wKl3SiixGyG/9Klab6uNBIqsN7j4Y/Fya7wAjQ== +"@rollup/rollup-freebsd-arm64@4.28.0": + version "4.28.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.28.0.tgz#3c919dff72b2fe344811a609c674a8347b033f62" + integrity sha512-lA1zZB3bFx5oxu9fYud4+g1mt+lYXCoch0M0V/xhqLoGatbzVse0wlSQ1UYOWKpuSu3gyN4qEc0Dxf/DII1bhQ== -"@rollup/rollup-freebsd-x64@4.43.0": - version "4.43.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.43.0.tgz#88297a0ddfadddd61d7d9b73eb42b3f227301d30" - integrity sha512-J7uCsiV13L/VOeHJBo5SjasKiGxJ0g+nQTrBkAsmQBIdil3KhPnSE9GnRon4ejX1XDdsmK/l30IYLiAaQEO0Cg== +"@rollup/rollup-freebsd-x64@4.28.0": + version "4.28.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.28.0.tgz#b62a3a8365b363b3fdfa6da11a9188b6ab4dca7c" + integrity sha512-aI2plavbUDjCQB/sRbeUZWX9qp12GfYkYSJOrdYTL/C5D53bsE2/nBPuoiJKoWp5SN78v2Vr8ZPnB+/VbQ2pFA== -"@rollup/rollup-linux-arm-gnueabihf@4.43.0": - version "4.43.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.43.0.tgz#a59afc092523ebe43d3899f33da9cdd2ec01fb87" - integrity sha512-gTJ/JnnjCMc15uwB10TTATBEhK9meBIY+gXP4s0sHD1zHOaIh4Dmy1X9wup18IiY9tTNk5gJc4yx9ctj/fjrIw== +"@rollup/rollup-linux-arm-gnueabihf@4.28.0": + version "4.28.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.28.0.tgz#0d02cc55bd229bd8ca5c54f65f916ba5e0591c94" + integrity sha512-WXveUPKtfqtaNvpf0iOb0M6xC64GzUX/OowbqfiCSXTdi/jLlOmH0Ba94/OkiY2yTGTwteo4/dsHRfh5bDCZ+w== -"@rollup/rollup-linux-arm-musleabihf@4.43.0": - version "4.43.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.43.0.tgz#3095c1327b794bd187d03e372e633717fb69b4c0" - integrity sha512-ZJ3gZynL1LDSIvRfz0qXtTNs56n5DI2Mq+WACWZ7yGHFUEirHBRt7fyIk0NsCKhmRhn7WAcjgSkSVVxKlPNFFw== +"@rollup/rollup-linux-arm-musleabihf@4.28.0": + version "4.28.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.28.0.tgz#c51d379263201e88a60e92bd8e90878f0c044425" + integrity sha512-yLc3O2NtOQR67lI79zsSc7lk31xjwcaocvdD1twL64PK1yNaIqCeWI9L5B4MFPAVGEVjH5k1oWSGuYX1Wutxpg== -"@rollup/rollup-linux-arm64-gnu@4.43.0": - version "4.43.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.43.0.tgz#e43bb77df3a6de85312e991d1e3ad352d1abb00d" - integrity sha512-8FnkipasmOOSSlfucGYEu58U8cxEdhziKjPD2FIa0ONVMxvl/hmONtX/7y4vGjdUhjcTHlKlDhw3H9t98fPvyA== +"@rollup/rollup-linux-arm64-gnu@4.28.0": + version "4.28.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.28.0.tgz#93ce2addc337b5cfa52b84f8e730d2e36eb4339b" + integrity sha512-+P9G9hjEpHucHRXqesY+3X9hD2wh0iNnJXX/QhS/J5vTdG6VhNYMxJ2rJkQOxRUd17u5mbMLHM7yWGZdAASfcg== -"@rollup/rollup-linux-arm64-musl@4.43.0": - version "4.43.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.43.0.tgz#34873a437bcd87618f702dc66f0cbce170aebf9f" - integrity sha512-KPPyAdlcIZ6S9C3S2cndXDkV0Bb1OSMsX0Eelr2Bay4EsF9yi9u9uzc9RniK3mcUGCLhWY9oLr6er80P5DE6XA== +"@rollup/rollup-linux-arm64-musl@4.28.0": + version "4.28.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.28.0.tgz#730af6ddc091a5ba5baac28a3510691725dc808b" + integrity sha512-1xsm2rCKSTpKzi5/ypT5wfc+4bOGa/9yI/eaOLW0oMs7qpC542APWhl4A37AENGZ6St6GBMWhCCMM6tXgTIplw== -"@rollup/rollup-linux-loongarch64-gnu@4.43.0": - version "4.43.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.43.0.tgz#224ff524349e365baa56f1f512822548c2d76910" - integrity sha512-HPGDIH0/ZzAZjvtlXj6g+KDQ9ZMHfSP553za7o2Odegb/BEfwJcR0Sw0RLNpQ9nC6Gy8s+3mSS9xjZ0n3rhcYg== +"@rollup/rollup-linux-powerpc64le-gnu@4.28.0": + version "4.28.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.28.0.tgz#b5565aac20b4de60ca1e557f525e76478b5436af" + integrity sha512-zgWxMq8neVQeXL+ouSf6S7DoNeo6EPgi1eeqHXVKQxqPy1B2NvTbaOUWPn/7CfMKL7xvhV0/+fq/Z/J69g1WAQ== -"@rollup/rollup-linux-powerpc64le-gnu@4.43.0": - version "4.43.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.43.0.tgz#43c3c053b26ace18a1d3dab204596a466c1b0e34" - integrity sha512-gEmwbOws4U4GLAJDhhtSPWPXUzDfMRedT3hFMyRAvM9Mrnj+dJIFIeL7otsv2WF3D7GrV0GIewW0y28dOYWkmw== +"@rollup/rollup-linux-riscv64-gnu@4.28.0": + version "4.28.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.28.0.tgz#d488290bf9338bad4ae9409c4aa8a1728835a20b" + integrity sha512-VEdVYacLniRxbRJLNtzwGt5vwS0ycYshofI7cWAfj7Vg5asqj+pt+Q6x4n+AONSZW/kVm+5nklde0qs2EUwU2g== -"@rollup/rollup-linux-riscv64-gnu@4.43.0": - version "4.43.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.43.0.tgz#e7df825d71daefa7037605015455aa58be43cd7a" - integrity sha512-XXKvo2e+wFtXZF/9xoWohHg+MuRnvO29TI5Hqe9xwN5uN8NKUYy7tXUG3EZAlfchufNCTHNGjEx7uN78KsBo0g== +"@rollup/rollup-linux-s390x-gnu@4.28.0": + version "4.28.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.28.0.tgz#eb2e3f3a06acf448115045c11a5a96868c95a556" + integrity sha512-LQlP5t2hcDJh8HV8RELD9/xlYtEzJkm/aWGsauvdO2ulfl3QYRjqrKW+mGAIWP5kdNCBheqqqYIGElSRCaXfpw== -"@rollup/rollup-linux-riscv64-musl@4.43.0": - version "4.43.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.43.0.tgz#d76ad93a7f4c0b2855a024d8d859196acf38acf5" - integrity sha512-ruf3hPWhjw6uDFsOAzmbNIvlXFXlBQ4nk57Sec8E8rUxs/AI4HD6xmiiasOOx/3QxS2f5eQMKTAwk7KHwpzr/Q== +"@rollup/rollup-linux-x64-gnu@4.28.0": + version "4.28.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.28.0.tgz#065952ef2aea7e837dc7e02aa500feeaff4fc507" + integrity sha512-Nl4KIzteVEKE9BdAvYoTkW19pa7LR/RBrT6F1dJCV/3pbjwDcaOq+edkP0LXuJ9kflW/xOK414X78r+K84+msw== -"@rollup/rollup-linux-s390x-gnu@4.43.0": - version "4.43.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.43.0.tgz#0852608843d05852af3f447bf43bb63d80d62b6a" - integrity sha512-QmNIAqDiEMEvFV15rsSnjoSmO0+eJLoKRD9EAa9rrYNwO/XRCtOGM3A5A0X+wmG+XRrw9Fxdsw+LnyYiZWWcVw== +"@rollup/rollup-linux-x64-musl@4.28.0": + version "4.28.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.28.0.tgz#3435d484d05f5c4d1ffd54541b4facce2887103a" + integrity sha512-eKpJr4vBDOi4goT75MvW+0dXcNUqisK4jvibY9vDdlgLx+yekxSm55StsHbxUsRxSTt3JEQvlr3cGDkzcSP8bw== -"@rollup/rollup-linux-x64-gnu@4.43.0": - version "4.43.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.43.0.tgz#d16a57f86357a4e697142bee244afed59b24e6c5" - integrity sha512-jAHr/S0iiBtFyzjhOkAics/2SrXE092qyqEg96e90L3t9Op8OTzS6+IX0Fy5wCt2+KqeHAkti+eitV0wvblEoQ== +"@rollup/rollup-win32-arm64-msvc@4.28.0": + version "4.28.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.28.0.tgz#69682a2a10d9fedc334f87583cfca83c39c08077" + integrity sha512-Vi+WR62xWGsE/Oj+mD0FNAPY2MEox3cfyG0zLpotZdehPFXwz6lypkGs5y38Jd/NVSbOD02aVad6q6QYF7i8Bg== -"@rollup/rollup-linux-x64-musl@4.43.0": - version "4.43.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.43.0.tgz#51cbc8b1eb46ebc0e284725418b6fbf48686e4e2" - integrity sha512-3yATWgdeXyuHtBhrLt98w+5fKurdqvs8B53LaoKD7P7H7FKOONLsBVMNl9ghPQZQuYcceV5CDyPfyfGpMWD9mQ== +"@rollup/rollup-win32-ia32-msvc@4.28.0": + version "4.28.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.28.0.tgz#b64470f9ac79abb386829c56750b9a4711be3332" + integrity sha512-kN/Vpip8emMLn/eOza+4JwqDZBL6MPNpkdaEsgUtW1NYN3DZvZqSQrbKzJcTL6hd8YNmFTn7XGWMwccOcJBL0A== -"@rollup/rollup-win32-arm64-msvc@4.43.0": - version "4.43.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.43.0.tgz#d6d84aace2b211119bf0ab1c586e29d01e32aa01" - integrity sha512-wVzXp2qDSCOpcBCT5WRWLmpJRIzv23valvcTwMHEobkjippNf+C3ys/+wf07poPkeNix0paTNemB2XrHr2TnGw== +"@rollup/rollup-win32-x64-msvc@4.28.0": + version "4.28.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.28.0.tgz#cb313feef9ac6e3737067fdf34f42804ac65a6f2" + integrity sha512-Bvno2/aZT6usSa7lRDL2+hMjVAGjuqaymF1ApZm31JXzniR/hvr14jpU+/z4X6Gt5BPlzosscyJZGUvguXIqeQ== -"@rollup/rollup-win32-ia32-msvc@4.43.0": - version "4.43.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.43.0.tgz#4af33168de2f65b97a8f36bd1d8d21cea34d3ccb" - integrity sha512-fYCTEyzf8d+7diCw8b+asvWDCLMjsCEA8alvtAutqJOJp/wL5hs1rWSqJ1vkjgW0L2NB4bsYJrpKkiIPRR9dvw== +"@swc/core-darwin-arm64@1.10.0": + version "1.10.0" + resolved "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.10.0.tgz" + integrity sha512-wCeUpanqZyzvgqWRtXIyhcFK3CqukAlYyP+fJpY2gWc/+ekdrenNIfZMwY7tyTFDkXDYEKzvn3BN/zDYNJFowQ== -"@rollup/rollup-win32-x64-msvc@4.43.0": - version "4.43.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.43.0.tgz#42a88207659e404e8ffa655cae763cbad94906ab" - integrity sha512-SnGhLiE5rlK0ofq8kzuDkM0g7FN1s5VYY+YSMTibP7CqShxCQvqtNxTARS4xX4PFJfHjG0ZQYX9iGzI3FQh5Aw== +"@swc/core-darwin-x64@1.10.0": + version "1.10.0" + resolved "https://registry.yarnpkg.com/@swc/core-darwin-x64/-/core-darwin-x64-1.10.0.tgz#d1b95c1db67ac328a96324b800843bc410d17f05" + integrity sha512-0CZPzqTynUBO+SHEl/qKsFSahp2Jv/P2ZRjFG0gwZY5qIcr1+B/v+o74/GyNMBGz9rft+F2WpU31gz2sJwyF4A== -"@standard-schema/spec@^1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@standard-schema/spec/-/spec-1.0.0.tgz#f193b73dc316c4170f2e82a881da0f550d551b9c" - integrity sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA== +"@swc/core-linux-arm-gnueabihf@1.10.0": + version "1.10.0" + resolved "https://registry.yarnpkg.com/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.10.0.tgz#e10510bb028bc3836948cb7345312269cd22295d" + integrity sha512-oq+DdMu5uJOFPtRkeiITc4kxmd+QSmK+v+OBzlhdGkSgoH3yRWZP+H2ao0cBXo93ZgCr2LfjiER0CqSKhjGuNA== -"@standard-schema/utils@^0.3.0": - version "0.3.0" - resolved "https://registry.yarnpkg.com/@standard-schema/utils/-/utils-0.3.0.tgz#3d5e608f16c2390c10528e98e59aef6bf73cae7b" - integrity sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g== +"@swc/core-linux-arm64-gnu@1.10.0": + version "1.10.0" + resolved "https://registry.yarnpkg.com/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.10.0.tgz#a4826c0b44db5b5a02826a0c47307f5969bcc353" + integrity sha512-Y6+PC8knchEViRxiCUj3j8wsGXaIhuvU+WqrFqV834eiItEMEI9+Vh3FovqJMBE3L7d4E4ZQtgImHCXjrHfxbw== -"@swc/core-darwin-arm64@1.12.0": - version "1.12.0" - resolved "https://registry.yarnpkg.com/@swc/core-darwin-arm64/-/core-darwin-arm64-1.12.0.tgz#c502935e9efe124de83c139900e5f06a8f7c9bf2" - integrity sha512-usLr8kC80GDv3pwH2zoEaS279kxtWY0MY3blbMFw7zA8fAjqxa8IDxm3WcgyNLNWckWn4asFfguEwz/Weem3nA== +"@swc/core-linux-arm64-musl@1.10.0": + version "1.10.0" + resolved "https://registry.yarnpkg.com/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.10.0.tgz#d4adab4a646be095e3c64226a0150ebe4b874c1a" + integrity sha512-EbrX9A5U4cECCQQfky7945AW9GYnTXtCUXElWTkTYmmyQK87yCyFfY8hmZ9qMFIwxPOH6I3I2JwMhzdi8Qoz7g== -"@swc/core-darwin-x64@1.12.0": - version "1.12.0" - resolved "https://registry.yarnpkg.com/@swc/core-darwin-x64/-/core-darwin-x64-1.12.0.tgz#c9a3b1cda4b01ac654d15a54261140250db42d97" - integrity sha512-Cvv4sqDcTY7QF2Dh1vn2Xbt/1ENYQcpmrGHzITJrXzxA2aBopsz/n4yQDiyRxTR0t802m4xu0CzMoZIHvVruWQ== +"@swc/core-linux-x64-gnu@1.10.0": + version "1.10.0" + resolved "https://registry.yarnpkg.com/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.10.0.tgz#278655c2b2abcb2e7ada031e75e6853777ebce4c" + integrity sha512-TaxpO6snTjjfLXFYh5EjZ78se69j2gDcqEM8yB9gguPYwkCHi2Ylfmh7iVaNADnDJFtjoAQp0L41bTV/Pfq9Cg== -"@swc/core-linux-arm-gnueabihf@1.12.0": - version "1.12.0" - resolved "https://registry.yarnpkg.com/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.12.0.tgz#a1f72803b65d9ed65109489b15c2079295e26f93" - integrity sha512-seM4/XMJMOupkzfLfHl8sRa3NdhsVZp+XgwA/vVeYZYJE4wuWUxVzhCYzwmNftVY32eF2IiRaWnhG6ho6jusnQ== +"@swc/core-linux-x64-musl@1.10.0": + version "1.10.0" + resolved "https://registry.yarnpkg.com/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.10.0.tgz#7df236de40a685c1723a904d6dead99eea36a30f" + integrity sha512-IEGvDd6aEEKEyZFZ8oCKuik05G5BS7qwG5hO5PEMzdGeh8JyFZXxsfFXbfeAqjue4UaUUrhnoX+Ze3M2jBVMHw== -"@swc/core-linux-arm64-gnu@1.12.0": - version "1.12.0" - resolved "https://registry.yarnpkg.com/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.12.0.tgz#7d182756be2fedfe30147cc8f404533a154d47ea" - integrity sha512-Al0x33gUVxNY5tutEYpSyv7mze6qQS1ONa0HEwoRxcK9WXsX0NHLTiOSGZoCUS1SsXM37ONlbA6/Bsp1MQyP+g== +"@swc/core-win32-arm64-msvc@1.10.0": + version "1.10.0" + resolved "https://registry.yarnpkg.com/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.10.0.tgz#99278f8f02c79e03caeeb6d64941d0487e58d7e1" + integrity sha512-UkQ952GSpY+Z6XONj9GSW8xGSkF53jrCsuLj0nrcuw7Dvr1a816U/9WYZmmcYS8tnG2vHylhpm6csQkyS8lpCw== -"@swc/core-linux-arm64-musl@1.12.0": - version "1.12.0" - resolved "https://registry.yarnpkg.com/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.12.0.tgz#31364690f45303db90198be12c16f1a6a8a91399" - integrity sha512-OeFHz/5Hl9v75J9TYA5jQxNIYAZMqaiPpd9dYSTK2Xyqa/ZGgTtNyPhIwVfxx+9mHBf6+9c1mTlXUtACMtHmaQ== +"@swc/core-win32-ia32-msvc@1.10.0": + version "1.10.0" + resolved "https://registry.yarnpkg.com/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.10.0.tgz#a5cf2cfa3e31e8e01a3692d7e053aaa788d3cf3e" + integrity sha512-a2QpIZmTiT885u/mUInpeN2W9ClCnqrV2LnMqJR1/Fgx1Afw/hAtiDZPtQ0SqS8yDJ2VR5gfNZo3gpxWMrqdVA== -"@swc/core-linux-x64-gnu@1.12.0": - version "1.12.0" - resolved "https://registry.yarnpkg.com/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.12.0.tgz#41d32bb96dd3fc16ccc5dc282c210d576218abb1" - integrity sha512-ltIvqNi7H0c5pRawyqjeYSKEIfZP4vv/datT3mwT6BW7muJtd1+KIDCPFLMIQ4wm/h76YQwPocsin3fzmnFdNA== +"@swc/core-win32-x64-msvc@1.10.0": + version "1.10.0" + resolved "https://registry.yarnpkg.com/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.10.0.tgz#ee1fdf8e6a627de33501b5a404465a7e676c8689" + integrity sha512-tZcCmMwf483nwsEBfUk5w9e046kMa1iSik4bP9Kwi2FGtOfHuDfIcwW4jek3hdcgF5SaBW1ktnK/lgQLDi5AtA== -"@swc/core-linux-x64-musl@1.12.0": - version "1.12.0" - resolved "https://registry.yarnpkg.com/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.12.0.tgz#9d158fd58302a3a842b63690eabac429a4992f18" - integrity sha512-Z/DhpjehaTK0uf+MhNB7mV9SuewpGs3P/q9/8+UsJeYoFr7yuOoPbAvrD6AqZkf6Bh7MRZ5OtG+KQgG5L+goiA== - -"@swc/core-win32-arm64-msvc@1.12.0": - version "1.12.0" - resolved "https://registry.yarnpkg.com/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.12.0.tgz#44076ec055f407c6f8eb336de5d14ae0f2546b99" - integrity sha512-wHnvbfHIh2gfSbvuFT7qP97YCMUDh+fuiso+pcC6ug8IsMxuViNapHET4o0ZdFNWHhXJ7/s0e6w7mkOalsqQiQ== - -"@swc/core-win32-ia32-msvc@1.12.0": - version "1.12.0" - resolved "https://registry.yarnpkg.com/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.12.0.tgz#48a21ed3d6511a7375d208235f4bbd446fd87a85" - integrity sha512-88umlXwK+7J2p4DjfWHXQpmlZgCf1ayt6Ssj+PYlAfMCR0aBiJoAMwHWrvDXEozyOrsyP1j2X6WxbmA861vL5Q== - -"@swc/core-win32-x64-msvc@1.12.0": - version "1.12.0" - resolved "https://registry.yarnpkg.com/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.12.0.tgz#c69cb09e2d8492353659832c55759ba37c5c1ef3" - integrity sha512-KR9TSRp+FEVOhbgTU6c94p/AYpsyBk7dIvlKQiDp8oKScUoyHG5yjmMBFN/BqUyTq4kj6zlgsY2rFE4R8/yqWg== - -"@swc/core@^1.10.16": - version "1.12.0" - resolved "https://registry.yarnpkg.com/@swc/core/-/core-1.12.0.tgz#b7f32a9fd4433b309556eb9e8afae6f78f5c18e4" - integrity sha512-/C0kiMHPY/HnLfqXYGMGxGck3A5Y3mqwxfv+EwHTPHGjAVRfHpWAEEBTSTF5C88vVY6CvwBEkhR2TX7t8Mahcw== +"@swc/core@^1.7.0": + version "1.10.0" + resolved "https://registry.npmjs.org/@swc/core/-/core-1.10.0.tgz" + integrity sha512-+CuuTCmQFfzaNGg1JmcZvdUVITQXJk9sMnl1C2TiDLzOSVOJRwVD4dNo5dljX/qxpMAN+2BIYlwjlSkoGi6grg== dependencies: "@swc/counter" "^0.1.3" - "@swc/types" "^0.1.22" + "@swc/types" "^0.1.17" optionalDependencies: - "@swc/core-darwin-arm64" "1.12.0" - "@swc/core-darwin-x64" "1.12.0" - "@swc/core-linux-arm-gnueabihf" "1.12.0" - "@swc/core-linux-arm64-gnu" "1.12.0" - "@swc/core-linux-arm64-musl" "1.12.0" - "@swc/core-linux-x64-gnu" "1.12.0" - "@swc/core-linux-x64-musl" "1.12.0" - "@swc/core-win32-arm64-msvc" "1.12.0" - "@swc/core-win32-ia32-msvc" "1.12.0" - "@swc/core-win32-x64-msvc" "1.12.0" + "@swc/core-darwin-arm64" "1.10.0" + "@swc/core-darwin-x64" "1.10.0" + "@swc/core-linux-arm-gnueabihf" "1.10.0" + "@swc/core-linux-arm64-gnu" "1.10.0" + "@swc/core-linux-arm64-musl" "1.10.0" + "@swc/core-linux-x64-gnu" "1.10.0" + "@swc/core-linux-x64-musl" "1.10.0" + "@swc/core-win32-arm64-msvc" "1.10.0" + "@swc/core-win32-ia32-msvc" "1.10.0" + "@swc/core-win32-x64-msvc" "1.10.0" "@swc/counter@^0.1.3": version "0.1.3" - resolved "https://registry.yarnpkg.com/@swc/counter/-/counter-0.1.3.tgz#cc7463bd02949611c6329596fccd2b0ec782b0e9" + resolved "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz" integrity sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ== -"@swc/types@^0.1.22": - version "0.1.23" - resolved "https://registry.yarnpkg.com/@swc/types/-/types-0.1.23.tgz#7eabf88b9cfd929253859c562ae95982ee04b4e8" - integrity sha512-u1iIVZV9Q0jxY+yM2vw/hZGDNudsN85bBpTqzAQ9rzkxW9D+e3aEM4Han+ow518gSewkXgjmEK0BD79ZcNVgPw== +"@swc/types@^0.1.17": + version "0.1.17" + resolved "https://registry.npmjs.org/@swc/types/-/types-0.1.17.tgz" + integrity sha512-V5gRru+aD8YVyCOMAjMpWR1Ui577DD5KSJsHP8RAxopAH22jFz6GZd/qxqjO6MJHQhcsjvjOFXyDhyLQUnMveQ== dependencies: "@swc/counter" "^0.1.3" -"@tauri-apps/api@^2.0.0": - version "2.5.0" - resolved "https://registry.yarnpkg.com/@tauri-apps/api/-/api-2.5.0.tgz#f1c67b5505ba689621b549f9d0165389337586dc" - integrity sha512-Ldux4ip+HGAcPUmuLT8EIkk6yafl5vK0P0c0byzAKzxJh7vxelVtdPONjfgTm96PbN24yjZNESY8CKo8qniluA== +"@tauri-apps/api@^2.0.0", "@tauri-apps/api@^2.6.0": + version "2.6.0" + resolved "https://registry.npmjs.org/@tauri-apps/api/-/api-2.6.0.tgz" + integrity sha512-hRNcdercfgpzgFrMXWwNDBN0B7vNzOzRepy6ZAmhxi5mDLVPNrTpo9MGg2tN/F7JRugj4d2aF7E1rtPXAHaetg== -"@tauri-apps/cli-darwin-arm64@2.5.0": - version "2.5.0" - resolved "https://registry.yarnpkg.com/@tauri-apps/cli-darwin-arm64/-/cli-darwin-arm64-2.5.0.tgz#930ce605d5624e6a11ed9f57f17c605a4443a3f5" - integrity sha512-VuVAeTFq86dfpoBDNYAdtQVLbP0+2EKCHIIhkaxjeoPARR0sLpFHz2zs0PcFU76e+KAaxtEtAJAXGNUc8E1PzQ== +"@tauri-apps/cli-darwin-arm64@2.1.0": + version "2.1.0" + resolved "https://registry.npmjs.org/@tauri-apps/cli-darwin-arm64/-/cli-darwin-arm64-2.1.0.tgz" + integrity sha512-ESc6J6CE8hl1yKH2vJ+ALF+thq4Be+DM1mvmTyUCQObvezNCNhzfS6abIUd3ou4x5RGH51ouiANeT3wekU6dCw== -"@tauri-apps/cli-darwin-x64@2.5.0": - version "2.5.0" - resolved "https://registry.yarnpkg.com/@tauri-apps/cli-darwin-x64/-/cli-darwin-x64-2.5.0.tgz#f7272a8b79e66af95da62c7a3ae81a916864b35f" - integrity sha512-hUF01sC06cZVa8+I0/VtsHOk9BbO75rd+YdtHJ48xTdcYaQ5QIwL4yZz9OR1AKBTaUYhBam8UX9Pvd5V2/4Dpw== +"@tauri-apps/cli-darwin-x64@2.1.0": + version "2.1.0" + resolved "https://registry.yarnpkg.com/@tauri-apps/cli-darwin-x64/-/cli-darwin-x64-2.1.0.tgz#08c5f446b65bc351a8e74c0c8019324ae6864351" + integrity sha512-TasHS442DFs8cSH2eUQzuDBXUST4ECjCd0yyP+zZzvAruiB0Bg+c8A+I/EnqCvBQ2G2yvWLYG8q/LI7c87A5UA== -"@tauri-apps/cli-linux-arm-gnueabihf@2.5.0": - version "2.5.0" - resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-arm-gnueabihf/-/cli-linux-arm-gnueabihf-2.5.0.tgz#faca4c693226b95c85662c6cbf55a715f86ddcc7" - integrity sha512-LQKqttsK252LlqYyX8R02MinUsfFcy3+NZiJwHFgi5Y3+ZUIAED9cSxJkyNtuY5KMnR4RlpgWyLv4P6akN1xhg== +"@tauri-apps/cli-linux-arm-gnueabihf@2.1.0": + version "2.1.0" + resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-arm-gnueabihf/-/cli-linux-arm-gnueabihf-2.1.0.tgz#681d0967d0335b93ed8ab4b8bb5d820c72cc8abf" + integrity sha512-aP7ZBGNL4ny07Cbb6kKpUOSrmhcIK2KhjviTzYlh+pPhAptxnC78xQGD3zKQkTi2WliJLPmBYbOHWWQa57lQ9w== -"@tauri-apps/cli-linux-arm64-gnu@2.5.0": - version "2.5.0" - resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-arm64-gnu/-/cli-linux-arm64-gnu-2.5.0.tgz#7c49104baf401baae7286764516f38c950a5d394" - integrity sha512-mTQufsPcpdHg5RW0zypazMo4L55EfeE5snTzrPqbLX4yCK2qalN7+rnP8O8GT06xhp6ElSP/Ku1M2MR297SByQ== +"@tauri-apps/cli-linux-arm64-gnu@2.1.0": + version "2.1.0" + resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-arm64-gnu/-/cli-linux-arm64-gnu-2.1.0.tgz#922ddc50849ae9f2976f30fdb4b79708badb767b" + integrity sha512-ZTdgD5gLeMCzndMT2f358EkoYkZ5T+Qy6zPzU+l5vv5M7dHVN9ZmblNAYYXmoOuw7y+BY4X/rZvHV9pcGrcanQ== -"@tauri-apps/cli-linux-arm64-musl@2.5.0": - version "2.5.0" - resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.5.0.tgz#c1f0943ccb0faea036a2fc854c44dce384d769e1" - integrity sha512-rQO1HhRUQqyEaal5dUVOQruTRda/TD36s9kv1hTxZiFuSq3558lsTjAcUEnMAtBcBkps20sbyTJNMT0AwYIk8Q== +"@tauri-apps/cli-linux-arm64-musl@2.1.0": + version "2.1.0" + resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.1.0.tgz#063020d217faf90b226d93b5054546ae0db14c16" + integrity sha512-NzwqjUCilhnhJzusz3d/0i0F1GFrwCQbkwR6yAHUxItESbsGYkZRJk0yMEWkg3PzFnyK4cWTlQJMEU52TjhEzA== -"@tauri-apps/cli-linux-riscv64-gnu@2.5.0": - version "2.5.0" - resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-riscv64-gnu/-/cli-linux-riscv64-gnu-2.5.0.tgz#1c9f3caf027c1b818253eaa4b24ef6f88bd30f46" - integrity sha512-7oS18FN46yDxyw1zX/AxhLAd7T3GrLj3Ai6s8hZKd9qFVzrAn36ESL7d3G05s8wEtsJf26qjXnVF4qleS3dYsA== +"@tauri-apps/cli-linux-x64-gnu@2.1.0": + version "2.1.0" + resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-x64-gnu/-/cli-linux-x64-gnu-2.1.0.tgz#7d7991a2b956232f96ec614f15189d408421dc03" + integrity sha512-TyiIpMEtZxNOQmuFyfJwaaYbg3movSthpBJLIdPlKxSAB2BW0VWLY3/ZfIxm/G2YGHyREkjJvimzYE0i37PnMA== -"@tauri-apps/cli-linux-x64-gnu@2.5.0": - version "2.5.0" - resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-x64-gnu/-/cli-linux-x64-gnu-2.5.0.tgz#2835a941f1f2c50014a92cff00d2f9e6d1aaec0e" - integrity sha512-SG5sFNL7VMmDBdIg3nO3EzNRT306HsiEQ0N90ILe3ZABYAVoPDO/ttpCO37ApLInTzrq/DLN+gOlC/mgZvLw1w== +"@tauri-apps/cli-linux-x64-musl@2.1.0": + version "2.1.0" + resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-x64-musl/-/cli-linux-x64-musl-2.1.0.tgz#af6293ce56296619d656342f11d83c35c8465979" + integrity sha512-/dQd0TlaxBdJACrR72DhynWftzHDaX32eBtS5WBrNJ+nnNb+znM3gON6nJ9tSE9jgDa6n1v2BkI/oIDtypfUXw== -"@tauri-apps/cli-linux-x64-musl@2.5.0": - version "2.5.0" - resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-x64-musl/-/cli-linux-x64-musl-2.5.0.tgz#c1dbb908d6762928ba65c477d9cd8e9a1a7890cf" - integrity sha512-QXDM8zp/6v05PNWju5ELsVwF0VH1n6b5pk2E6W/jFbbiwz80Vs1lACl9pv5kEHkrxBj+aWU/03JzGuIj2g3SkQ== +"@tauri-apps/cli-win32-arm64-msvc@2.1.0": + version "2.1.0" + resolved "https://registry.yarnpkg.com/@tauri-apps/cli-win32-arm64-msvc/-/cli-win32-arm64-msvc-2.1.0.tgz#adb2b17d9939cdbcb136c5e24bf90d15485265dc" + integrity sha512-NdQJO7SmdYqOcE+JPU7bwg7+odfZMWO6g8xF9SXYCMdUzvM2Gv/AQfikNXz5yS7ralRhNFuW32i5dcHlxh4pDg== -"@tauri-apps/cli-win32-arm64-msvc@2.5.0": - version "2.5.0" - resolved "https://registry.yarnpkg.com/@tauri-apps/cli-win32-arm64-msvc/-/cli-win32-arm64-msvc-2.5.0.tgz#5ff5045afef27314727a657429c71cc07cf2fd31" - integrity sha512-pFSHFK6b+o9y4Un8w0gGLwVyFTZaC3P0kQ7umRt/BLDkzD5RnQ4vBM7CF8BCU5nkwmEBUCZd7Wt3TWZxe41o6Q== +"@tauri-apps/cli-win32-ia32-msvc@2.1.0": + version "2.1.0" + resolved "https://registry.yarnpkg.com/@tauri-apps/cli-win32-ia32-msvc/-/cli-win32-ia32-msvc-2.1.0.tgz#5356a56a30bfc50a0edde3e91c16ccd3fa837d71" + integrity sha512-f5h8gKT/cB8s1ticFRUpNmHqkmaLutT62oFDB7N//2YTXnxst7EpMIn1w+QimxTvTk2gcx6EcW6bEk/y2hZGzg== -"@tauri-apps/cli-win32-ia32-msvc@2.5.0": - version "2.5.0" - resolved "https://registry.yarnpkg.com/@tauri-apps/cli-win32-ia32-msvc/-/cli-win32-ia32-msvc-2.5.0.tgz#288fe9c6c5bb3ff94ac0a6c1d23985bf453eb873" - integrity sha512-EArv1IaRlogdLAQyGlKmEqZqm5RfHCUMhJoedWu7GtdbOMUfSAz6FMX2boE1PtEmNO4An+g188flLeVErrxEKg== - -"@tauri-apps/cli-win32-x64-msvc@2.5.0": - version "2.5.0" - resolved "https://registry.yarnpkg.com/@tauri-apps/cli-win32-x64-msvc/-/cli-win32-x64-msvc-2.5.0.tgz#9e398e07f8a255aaa3400b1dc43fb985bb99cdb0" - integrity sha512-lj43EFYbnAta8pd9JnUq87o+xRUR0odz+4rixBtTUwUgdRdwQ2V9CzFtsMu6FQKpFQ6mujRK6P1IEwhL6ADRsQ== +"@tauri-apps/cli-win32-x64-msvc@2.1.0": + version "2.1.0" + resolved "https://registry.yarnpkg.com/@tauri-apps/cli-win32-x64-msvc/-/cli-win32-x64-msvc-2.1.0.tgz#065f76de7a3aabe6d490b7f15cb09e7f8e911614" + integrity sha512-P/+LrdSSb5Xbho1LRP4haBjFHdyPdjWvGgeopL96OVtrFpYnfC+RctB45z2V2XxqFk3HweDDxk266btjttfjGw== "@tauri-apps/cli@^2.0.0": - version "2.5.0" - resolved "https://registry.yarnpkg.com/@tauri-apps/cli/-/cli-2.5.0.tgz#828cefd5c64be9f96a0379583f9ad25e17c43208" - integrity sha512-rAtHqG0Gh/IWLjN2zTf3nZqYqbo81oMbqop56rGTjrlWk9pTTAjkqOjSL9XQLIMZ3RbeVjveCqqCA0s8RnLdMg== + version "2.1.0" + resolved "https://registry.npmjs.org/@tauri-apps/cli/-/cli-2.1.0.tgz" + integrity sha512-K2VhcKqBhAeS5pNOVdnR/xQRU6jwpgmkSL2ejHXcl0m+kaTggT0WRDQnFtPq6NljA7aE03cvwsbCAoFG7vtkJw== optionalDependencies: - "@tauri-apps/cli-darwin-arm64" "2.5.0" - "@tauri-apps/cli-darwin-x64" "2.5.0" - "@tauri-apps/cli-linux-arm-gnueabihf" "2.5.0" - "@tauri-apps/cli-linux-arm64-gnu" "2.5.0" - "@tauri-apps/cli-linux-arm64-musl" "2.5.0" - "@tauri-apps/cli-linux-riscv64-gnu" "2.5.0" - "@tauri-apps/cli-linux-x64-gnu" "2.5.0" - "@tauri-apps/cli-linux-x64-musl" "2.5.0" - "@tauri-apps/cli-win32-arm64-msvc" "2.5.0" - "@tauri-apps/cli-win32-ia32-msvc" "2.5.0" - "@tauri-apps/cli-win32-x64-msvc" "2.5.0" + "@tauri-apps/cli-darwin-arm64" "2.1.0" + "@tauri-apps/cli-darwin-x64" "2.1.0" + "@tauri-apps/cli-linux-arm-gnueabihf" "2.1.0" + "@tauri-apps/cli-linux-arm64-gnu" "2.1.0" + "@tauri-apps/cli-linux-arm64-musl" "2.1.0" + "@tauri-apps/cli-linux-x64-gnu" "2.1.0" + "@tauri-apps/cli-linux-x64-musl" "2.1.0" + "@tauri-apps/cli-win32-arm64-msvc" "2.1.0" + "@tauri-apps/cli-win32-ia32-msvc" "2.1.0" + "@tauri-apps/cli-win32-x64-msvc" "2.1.0" "@tauri-apps/plugin-cli@^2.0.0": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@tauri-apps/plugin-cli/-/plugin-cli-2.2.0.tgz#59e8c8a8fd313f5d6453c894466df5be122688d8" - integrity sha512-rvNhMog9rHr01Xk+trBFKJ0eZICIvPkm9GX6ogB89/0hROU/lf+a/sb4vC0wtSeR7zrJuCSxwxYuvHCZheaYFA== + version "2.0.0" + resolved "https://registry.npmjs.org/@tauri-apps/plugin-cli/-/plugin-cli-2.0.0.tgz" + integrity sha512-glQmlL1IiCGEa1FHYa/PTPSeYhfu56omLRgHXWlJECDt6DbJyRuJWVgtkQfUxtqnVdYnnU+DGIGeiInoEqtjLw== dependencies: "@tauri-apps/api" "^2.0.0" "@tauri-apps/plugin-clipboard-manager@^2.0.0": - 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== + version "2.0.1" + resolved "https://registry.npmjs.org/@tauri-apps/plugin-clipboard-manager/-/plugin-clipboard-manager-2.0.1.tgz" + integrity sha512-JDzqqhEnIAvt3HAsejgdvPfb74da1CagHfT+71qJL6Jip4Qzu+TzxaYXilIT5p5N8ZFwE7K1nJJ2aGsEUHvJtg== dependencies: "@tauri-apps/api" "^2.0.0" +"@tauri-apps/plugin-dialog@^2.0.0": + version "2.3.0" + resolved "https://registry.npmjs.org/@tauri-apps/plugin-dialog/-/plugin-dialog-2.3.0.tgz" + integrity sha512-ylSBvYYShpGlKKh732ZuaHyJ5Ie1JR71QCXewCtsRLqGdc8Is4xWdz6t43rzXyvkItM9syNPMvFVcvjgEy+/GA== + dependencies: + "@tauri-apps/api" "^2.6.0" + "@tauri-apps/plugin-opener@^2.0.0": - version "2.2.7" - resolved "https://registry.yarnpkg.com/@tauri-apps/plugin-opener/-/plugin-opener-2.2.7.tgz#2fccf081e8bd32b61cfc31640845d81a0bf8ed06" - integrity sha512-uduEyvOdjpPOEeDRrhwlCspG/f9EQalHumWBtLBnp3fRp++fKGLqDOyUhSIn7PzX45b/rKep//ZQSAQoIxobLA== + version "2.2.6" + resolved "https://registry.npmjs.org/@tauri-apps/plugin-opener/-/plugin-opener-2.2.6.tgz" + integrity sha512-bSdkuP71ZQRepPOn8BOEdBKYJQvl6+jb160QtJX/i2H9BF6ZySY/kYljh76N2Ne5fJMQRge7rlKoStYQY5Jq1w== dependencies: "@tauri-apps/api" "^2.0.0" "@tauri-apps/plugin-process@^2.0.0": - version "2.2.1" - resolved "https://registry.yarnpkg.com/@tauri-apps/plugin-process/-/plugin-process-2.2.1.tgz#0d442377e37dac52b45d5a1b55d35a20df5f550c" - integrity sha512-cF/k8J+YjjuowhNG1AboHNTlrGiOwgX5j6NzsX6WFf9FMzyZUchkCgZMxCdSE5NIgFX0vvOgLQhODFJgbMenLg== + version "2.0.0" + resolved "https://registry.npmjs.org/@tauri-apps/plugin-process/-/plugin-process-2.0.0.tgz" + integrity sha512-OYzi0GnkrF4NAnsHZU7U3tjSoP0PbeAlO7T1Z+vJoBUH9sFQ1NSLqWYWQyf8hcb3gVWe7P1JggjiskO+LST1ug== dependencies: "@tauri-apps/api" "^2.0.0" "@tauri-apps/plugin-shell@^2.0.0": - 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== + version "2.0.1" + resolved "https://registry.npmjs.org/@tauri-apps/plugin-shell/-/plugin-shell-2.0.1.tgz" + integrity sha512-akU1b77sw3qHiynrK0s930y8zKmcdrSD60htjH+mFZqv5WaakZA/XxHR3/sF1nNv9Mgmt/Shls37HwnOr00aSw== dependencies: "@tauri-apps/api" "^2.0.0" "@tauri-apps/plugin-store@^2.0.0": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@tauri-apps/plugin-store/-/plugin-store-2.2.0.tgz#e7cddd3692bdb3e8625b77d70dbede76b31d0986" - integrity sha512-hJTRtuJis4w5fW1dkcgftsYxKXK0+DbAqurZ3CURHG5WkAyyZgbxpeYctw12bbzF9ZbZREXZklPq8mocCC3Sgg== + version "2.1.0" + resolved "https://registry.npmjs.org/@tauri-apps/plugin-store/-/plugin-store-2.1.0.tgz" + integrity sha512-GADqrc17opUKYIAKnGHIUgEeTZ2wJGu1ZITKQ1WMuOFdv8fvXRFBAqsqPjE3opgWohbczX6e1NpwmZK1AnuWVw== dependencies: "@tauri-apps/api" "^2.0.0" "@tauri-apps/plugin-updater@2.7.1": version "2.7.1" - resolved "https://registry.yarnpkg.com/@tauri-apps/plugin-updater/-/plugin-updater-2.7.1.tgz#82adcfd06cdd4bc6d64343c8934b700c9174913a" + resolved "https://registry.npmjs.org/@tauri-apps/plugin-updater/-/plugin-updater-2.7.1.tgz" integrity sha512-1OPqEY/z7NDVSeTEMIhD2ss/vXWdpfZ5Th2Mk0KtPR/RA6FKuOTDGZQhxoyYBk0pcZJ+nNZUbl/IujDCLBApjA== dependencies: "@tauri-apps/api" "^2.0.0" "@testing-library/react@^16.0.1": - version "16.3.0" - resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-16.3.0.tgz#3a85bb9bdebf180cd76dba16454e242564d598a6" - integrity sha512-kFSyxiEDwv1WLl2fgsq6pPBbw5aWKrsY2/noi1Id0TK0UParSF62oFQFGHXIyaG4pp2tEub/Zlel+fjjZILDsw== + version "16.0.1" + resolved "https://registry.npmjs.org/@testing-library/react/-/react-16.0.1.tgz" + integrity sha512-dSmwJVtJXmku+iocRhWOUFbrERC76TX2Mnf0ATODz8brzAZrMBbzLwQixlBSanZxR6LddK3eiwpSFZgDET1URg== dependencies: "@babel/runtime" "^7.12.5" "@testing-library/user-event@^14.5.2": - version "14.6.1" - resolved "https://registry.yarnpkg.com/@testing-library/user-event/-/user-event-14.6.1.tgz#13e09a32d7a8b7060fe38304788ebf4197cd2149" - integrity sha512-vq7fv0rnt+QTXgPxr5Hjc210p6YKq2kmdziLgnsZGgLJ9e6VAShx1pACLuRjd/AS/sr7phAR58OIIpf0LlmQNw== + version "14.5.2" + resolved "https://registry.npmjs.org/@testing-library/user-event/-/user-event-14.5.2.tgz" + integrity sha512-YAh82Wh4TIrxYLmfGcixwD18oIjyC1pFQC2Y01F2lzV2HTMiYrI0nze0FD0ocB//CKS/7jIUgae+adPqxK5yCQ== "@types/babel__core@^7.20.5": version "7.20.5" - resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.20.5.tgz#3df15f27ba85319caa07ba08d0721889bb39c017" + resolved "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz" integrity sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA== dependencies: "@babel/parser" "^7.20.7" @@ -1064,104 +1127,92 @@ "@types/babel__traverse" "*" "@types/babel__generator@*": - version "7.27.0" - resolved "https://registry.yarnpkg.com/@types/babel__generator/-/babel__generator-7.27.0.tgz#b5819294c51179957afaec341442f9341e4108a9" - integrity sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg== + version "7.6.8" + resolved "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz" + integrity sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw== dependencies: "@babel/types" "^7.0.0" "@types/babel__template@*": version "7.4.4" - resolved "https://registry.yarnpkg.com/@types/babel__template/-/babel__template-7.4.4.tgz#5672513701c1b2199bc6dad636a9d7491586766f" + resolved "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz" integrity sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A== dependencies: "@babel/parser" "^7.1.0" "@babel/types" "^7.0.0" "@types/babel__traverse@*": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.20.7.tgz#968cdc2366ec3da159f61166428ee40f370e56c2" - integrity sha512-dkO5fhS7+/oos4ciWxyEyjWe48zmG6wbCheo/G2ZnHx4fs3EU6YC6UM8rk56gAjNJ9P3MTH2jo5jb92/K6wbng== + version "7.20.6" + resolved "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.6.tgz" + integrity sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg== dependencies: "@babel/types" "^7.20.7" -"@types/estree@1.0.7": - version "1.0.7" - resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.7.tgz#4158d3105276773d5b7695cd4834b1722e4f37a8" - integrity sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ== - -"@types/estree@^1.0.0", "@types/estree@^1.0.6": - version "1.0.8" - resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.8.tgz#958b91c991b1867ced318bedea0e215ee050726e" - integrity sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w== +"@types/estree@1.0.6", "@types/estree@^1.0.0", "@types/estree@^1.0.6": + version "1.0.6" + resolved "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz" + integrity sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw== "@types/get-params@^0.1.2": version "0.1.2" - resolved "https://registry.yarnpkg.com/@types/get-params/-/get-params-0.1.2.tgz#815f80eceb0f0e2f0bb00a2527c9d2e6e57e2a52" + resolved "https://registry.npmjs.org/@types/get-params/-/get-params-0.1.2.tgz" integrity sha512-ujqPyr1UDsOTDngJPV+WFbR0iHT5AfZKlNPMX6XOCnQcMhEqR+r64dVC/nwYCitqjR3DcpWofnOEAInUQmI/eA== "@types/hoist-non-react-statics@^3.3.0": - version "3.3.6" - resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.6.tgz#6bba74383cdab98e8db4e20ce5b4a6b98caed010" - integrity sha512-lPByRJUer/iN/xa4qpyL0qmL11DqNW81iU/IG1S3uvRUq4oKagz8VCxZjiWkumgt66YT3vOdDgZ0o32sGKtCEw== + version "3.3.5" + resolved "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.5.tgz" + integrity sha512-SbcrWzkKBw2cdwRTwQAswfpB9g9LJWfjtUeW/jvNwbhC8cpmmNYVePa+ncbUe0rGTQ7G3Ff6mYUN2VMfLVr+Sg== dependencies: "@types/react" "*" hoist-non-react-statics "^3.3.0" "@types/humanize-duration@^3.27.4": version "3.27.4" - resolved "https://registry.yarnpkg.com/@types/humanize-duration/-/humanize-duration-3.27.4.tgz#51d6d278213374735440bc3749de920935e9127e" + resolved "https://registry.npmjs.org/@types/humanize-duration/-/humanize-duration-3.27.4.tgz" integrity sha512-yaf7kan2Sq0goxpbcwTQ+8E9RP6HutFBPv74T/IA/ojcHKhuKVlk2YFYyHhWZeLvZPzzLE3aatuQB4h0iqyyUA== "@types/json-schema@^7.0.15": version "7.0.15" - resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" + resolved "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz" integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== "@types/lodash@^4.17.6": - version "4.17.17" - resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.17.17.tgz#fb85a04f47e9e4da888384feead0de05f7070355" - integrity sha512-RRVJ+J3J+WmyOTqnz3PiBLA501eKwXl2noseKOrNo/6+XEHjTAxO4xHvxQB6QuNm+s4WRbn6rSiap8+EA+ykFQ== + version "4.17.13" + resolved "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.13.tgz" + integrity sha512-lfx+dftrEZcdBPczf9d0Qv0x+j/rfNCMuC6OcfXmO8gkfeNAY88PgKUbvG56whcN23gc27yenwF6oJZXGFpYxg== -"@types/node@*": - version "24.0.1" - resolved "https://registry.yarnpkg.com/@types/node/-/node-24.0.1.tgz#e9bfcb1c35547437c294403b7bec497772a88b0a" - integrity sha512-MX4Zioh39chHlDJbKmEgydJDS3tspMP/lnQC67G3SWsTnb9NeYVWOjkxpOSy4oMfPs4StcWHwBrvUb4ybfnuaw== - dependencies: - undici-types "~7.8.0" - -"@types/node@^22.15.29": - version "22.15.31" - resolved "https://registry.yarnpkg.com/@types/node/-/node-22.15.31.tgz#454f11e2061150135c8353d7f3b3b1823fca9f3f" - integrity sha512-jnVe5ULKl6tijxUhvQeNbQG/84fHfg+yMak02cT8QVhBx/F05rAVxCGBYYTh2EKz22D6JF5ktXuNwdx7b9iEGw== +"@types/node@*", "@types/node@^22.15.29": + version "22.16.2" + resolved "https://registry.npmjs.org/@types/node/-/node-22.16.2.tgz" + integrity sha512-Cdqa/eJTvt4fC4wmq1Mcc0CPUjp/Qy2FGqLza3z3pKymsI969TcZ54diNJv8UYUgeWxyb8FSbCkhdR6WqmUFhA== dependencies: undici-types "~6.21.0" "@types/parse-json@^4.0.0": version "4.0.2" - resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.2.tgz#5950e50960793055845e956c427fc2b0d70c5239" + resolved "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz" integrity sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw== -"@types/prop-types@^15.7.14": +"@types/prop-types@^15.7.15": version "15.7.15" - resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.15.tgz#e6e5a86d602beaca71ce5163fadf5f95d70931c7" + resolved "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz" integrity sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw== "@types/react-dom@^19.1.5": version "19.1.6" - resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-19.1.6.tgz#4af629da0e9f9c0f506fc4d1caa610399c595d64" + resolved "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.6.tgz" integrity sha512-4hOiT/dwO8Ko0gV1m/TJZYk3y0KBnY9vzDh7W+DH17b2HFSOGgdj33dhihPeuy3l0q23+4e+hoXHV6hCC4dCXw== "@types/react-is@^19.0.0": version "19.0.0" - resolved "https://registry.yarnpkg.com/@types/react-is/-/react-is-19.0.0.tgz#eccf45556cf1858e9116eed1f9e7b51496501a7a" + resolved "https://registry.npmjs.org/@types/react-is/-/react-is-19.0.0.tgz" integrity sha512-71dSZeeJ0t3aoPyY9x6i+JNSvg5m9EF2i2OlSZI5QoJuI8Ocgor610i+4A10TQmURR+0vLwcVCEYFpXdzM1Biw== dependencies: "@types/react" "*" "@types/react-redux@^7.1.34": version "7.1.34" - resolved "https://registry.yarnpkg.com/@types/react-redux/-/react-redux-7.1.34.tgz#83613e1957c481521e6776beeac4fd506d11bd0e" + resolved "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.34.tgz" integrity sha512-GdFaVjEbYv4Fthm2ZLvj1VSCedV7TqE5y1kNwnjSdBOTXuRSgowux6J8TAct15T3CKBr63UMk+2CO7ilRhyrAQ== dependencies: "@types/hoist-non-react-statics" "^3.3.0" @@ -1171,214 +1222,197 @@ "@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" + resolved "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.12.tgz" integrity sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w== "@types/react@*", "@types/react@^19.1.6": version "19.1.8" - resolved "https://registry.yarnpkg.com/@types/react/-/react-19.1.8.tgz#ff8395f2afb764597265ced15f8dddb0720ae1c3" + resolved "https://registry.npmjs.org/@types/react/-/react-19.1.8.tgz" integrity sha512-AwAfQ2Wa5bCx9WP8nZL2uMZWod7J7/JSplxbTmBQ5ms6QpqNYm672H0Vu9ZVKVngQ+ii4R/byguVEUZQyeg44g== dependencies: csstype "^3.0.2" "@types/semver@^7.5.8": - version "7.7.0" - resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.7.0.tgz#64c441bdae033b378b6eef7d0c3d77c329b9378e" - integrity sha512-k107IF4+Xr7UHjwDc7Cfd6PRQfbdkiRabXGRjo07b4WyPahFBZCZ1sE+BNxYIJPPg73UkfOsVOLwqVc/6ETrIA== + version "7.5.8" + resolved "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz" + integrity sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ== "@types/use-sync-external-store@^0.0.6": version "0.0.6" - resolved "https://registry.yarnpkg.com/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz#60be8d21baab8c305132eb9cb912ed497852aadc" + resolved "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz" integrity sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg== -"@typescript-eslint/eslint-plugin@8.34.0": - version "8.34.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.34.0.tgz#96c9f818782fe24cd5883a5d517ca1826d3fa9c2" - integrity sha512-QXwAlHlbcAwNlEEMKQS2RCgJsgXrTJdjXT08xEgbPFa2yYQgVjBymxP5DrfrE7X7iodSzd9qBUHUycdyVJTW1w== +"@typescript-eslint/eslint-plugin@8.17.0": + version "8.17.0" + resolved "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.17.0.tgz" + integrity sha512-HU1KAdW3Tt8zQkdvNoIijfWDMvdSweFYm4hWh+KwhPstv+sCmWb89hCIP8msFm9N1R/ooh9honpSuvqKWlYy3w== dependencies: "@eslint-community/regexpp" "^4.10.0" - "@typescript-eslint/scope-manager" "8.34.0" - "@typescript-eslint/type-utils" "8.34.0" - "@typescript-eslint/utils" "8.34.0" - "@typescript-eslint/visitor-keys" "8.34.0" + "@typescript-eslint/scope-manager" "8.17.0" + "@typescript-eslint/type-utils" "8.17.0" + "@typescript-eslint/utils" "8.17.0" + "@typescript-eslint/visitor-keys" "8.17.0" graphemer "^1.4.0" - ignore "^7.0.0" + ignore "^5.3.1" natural-compare "^1.4.0" - ts-api-utils "^2.1.0" + ts-api-utils "^1.3.0" -"@typescript-eslint/parser@8.34.0": - version "8.34.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.34.0.tgz#703270426ac529304ae6988482f487c856d9c13f" - integrity sha512-vxXJV1hVFx3IXz/oy2sICsJukaBrtDEQSBiV48/YIV5KWjX1dO+bcIr/kCPrW6weKXvsaGKFNlwH0v2eYdRRbA== +"@typescript-eslint/parser@8.17.0": + version "8.17.0" + resolved "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.17.0.tgz" + integrity sha512-Drp39TXuUlD49F7ilHHCG7TTg8IkA+hxCuULdmzWYICxGXvDXmDmWEjJYZQYgf6l/TFfYNE167m7isnc3xlIEg== dependencies: - "@typescript-eslint/scope-manager" "8.34.0" - "@typescript-eslint/types" "8.34.0" - "@typescript-eslint/typescript-estree" "8.34.0" - "@typescript-eslint/visitor-keys" "8.34.0" + "@typescript-eslint/scope-manager" "8.17.0" + "@typescript-eslint/types" "8.17.0" + "@typescript-eslint/typescript-estree" "8.17.0" + "@typescript-eslint/visitor-keys" "8.17.0" debug "^4.3.4" -"@typescript-eslint/project-service@8.34.0": - version "8.34.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/project-service/-/project-service-8.34.0.tgz#449119b72fe9fae185013a6bdbaf1ffbfee6bcaf" - integrity sha512-iEgDALRf970/B2YExmtPMPF54NenZUf4xpL3wsCRx/lgjz6ul/l13R81ozP/ZNuXfnLCS+oPmG7JIxfdNYKELw== +"@typescript-eslint/scope-manager@8.17.0": + version "8.17.0" + resolved "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.17.0.tgz" + integrity sha512-/ewp4XjvnxaREtqsZjF4Mfn078RD/9GmiEAtTeLQ7yFdKnqwTOgRMSvFz4et9U5RiJQ15WTGXPLj89zGusvxBg== dependencies: - "@typescript-eslint/tsconfig-utils" "^8.34.0" - "@typescript-eslint/types" "^8.34.0" + "@typescript-eslint/types" "8.17.0" + "@typescript-eslint/visitor-keys" "8.17.0" + +"@typescript-eslint/type-utils@8.17.0": + version "8.17.0" + resolved "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.17.0.tgz" + integrity sha512-q38llWJYPd63rRnJ6wY/ZQqIzPrBCkPdpIsaCfkR3Q4t3p6sb422zougfad4TFW9+ElIFLVDzWGiGAfbb/v2qw== + dependencies: + "@typescript-eslint/typescript-estree" "8.17.0" + "@typescript-eslint/utils" "8.17.0" debug "^4.3.4" + ts-api-utils "^1.3.0" -"@typescript-eslint/scope-manager@8.34.0": - version "8.34.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.34.0.tgz#9fedaec02370cf79c018a656ab402eb00dc69e67" - integrity sha512-9Ac0X8WiLykl0aj1oYQNcLZjHgBojT6cW68yAgZ19letYu+Hxd0rE0veI1XznSSst1X5lwnxhPbVdwjDRIomRw== +"@typescript-eslint/types@8.17.0": + version "8.17.0" + resolved "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.17.0.tgz" + integrity sha512-gY2TVzeve3z6crqh2Ic7Cr+CAv6pfb0Egee7J5UAVWCpVvDI/F71wNfolIim4FE6hT15EbpZFVUj9j5i38jYXA== + +"@typescript-eslint/typescript-estree@8.17.0": + version "8.17.0" + resolved "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.17.0.tgz" + integrity sha512-JqkOopc1nRKZpX+opvKqnM3XUlM7LpFMD0lYxTqOTKQfCWAmxw45e3qlOCsEqEB2yuacujivudOFpCnqkBDNMw== dependencies: - "@typescript-eslint/types" "8.34.0" - "@typescript-eslint/visitor-keys" "8.34.0" - -"@typescript-eslint/tsconfig-utils@8.34.0", "@typescript-eslint/tsconfig-utils@^8.34.0": - version "8.34.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.34.0.tgz#97d0a24e89a355e9308cebc8e23f255669bf0979" - integrity sha512-+W9VYHKFIzA5cBeooqQxqNriAP0QeQ7xTiDuIOr71hzgffm3EL2hxwWBIIj4GuofIbKxGNarpKqIq6Q6YrShOA== - -"@typescript-eslint/type-utils@8.34.0": - version "8.34.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.34.0.tgz#03e7eb3776129dfd751ba1cac0c6ea4b0fab5ec6" - integrity sha512-n7zSmOcUVhcRYC75W2pnPpbO1iwhJY3NLoHEtbJwJSNlVAZuwqu05zY3f3s2SDWWDSo9FdN5szqc73DCtDObAg== - dependencies: - "@typescript-eslint/typescript-estree" "8.34.0" - "@typescript-eslint/utils" "8.34.0" - debug "^4.3.4" - ts-api-utils "^2.1.0" - -"@typescript-eslint/types@8.34.0", "@typescript-eslint/types@^8.34.0": - version "8.34.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.34.0.tgz#18000f205c59c9aff7f371fc5426b764cf2890fb" - integrity sha512-9V24k/paICYPniajHfJ4cuAWETnt7Ssy+R0Rbcqo5sSFr3QEZ/8TSoUi9XeXVBGXCaLtwTOKSLGcInCAvyZeMA== - -"@typescript-eslint/typescript-estree@8.34.0": - version "8.34.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.34.0.tgz#c9f3feec511339ef64e9e4884516c3e558f1b048" - integrity sha512-rOi4KZxI7E0+BMqG7emPSK1bB4RICCpF7QD3KCLXn9ZvWoESsOMlHyZPAHyG04ujVplPaHbmEvs34m+wjgtVtg== - dependencies: - "@typescript-eslint/project-service" "8.34.0" - "@typescript-eslint/tsconfig-utils" "8.34.0" - "@typescript-eslint/types" "8.34.0" - "@typescript-eslint/visitor-keys" "8.34.0" + "@typescript-eslint/types" "8.17.0" + "@typescript-eslint/visitor-keys" "8.17.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.1.0" + ts-api-utils "^1.3.0" -"@typescript-eslint/utils@8.34.0": - version "8.34.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.34.0.tgz#7844beebc1153b4d3ec34135c2da53a91e076f8d" - integrity sha512-8L4tWatGchV9A1cKbjaavS6mwYwp39jql8xUmIIKJdm+qiaeHy5KMKlBrf30akXAWBzn2SqKsNOtSENWUwg7XQ== +"@typescript-eslint/utils@8.17.0": + version "8.17.0" + resolved "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.17.0.tgz" + integrity sha512-bQC8BnEkxqG8HBGKwG9wXlZqg37RKSMY7v/X8VEWD8JG2JuTHuNK0VFvMPMUKQcbk6B+tf05k+4AShAEtCtJ/w== dependencies: - "@eslint-community/eslint-utils" "^4.7.0" - "@typescript-eslint/scope-manager" "8.34.0" - "@typescript-eslint/types" "8.34.0" - "@typescript-eslint/typescript-estree" "8.34.0" + "@eslint-community/eslint-utils" "^4.4.0" + "@typescript-eslint/scope-manager" "8.17.0" + "@typescript-eslint/types" "8.17.0" + "@typescript-eslint/typescript-estree" "8.17.0" -"@typescript-eslint/visitor-keys@8.34.0": - version "8.34.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.34.0.tgz#c7a149407be31d755dba71980617d638a40ac099" - integrity sha512-qHV7pW7E85A0x6qyrFn+O+q1k1p3tQCsqIZ1KZ5ESLXY57aTvUd3/a4rdPTeXisvhXn2VQG0VSKUqs8KHF2zcA== +"@typescript-eslint/visitor-keys@8.17.0": + version "8.17.0" + resolved "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.17.0.tgz" + integrity sha512-1Hm7THLpO6ww5QU6H/Qp+AusUUl+z/CAm3cNZZ0jQvon9yicgO7Rwd+/WWRpMKLYV6p2UvdbR27c86rzCPpreg== dependencies: - "@typescript-eslint/types" "8.34.0" + "@typescript-eslint/types" "8.17.0" eslint-visitor-keys "^4.2.0" "@vitejs/plugin-react@^4.2.1": - version "4.5.2" - resolved "https://registry.yarnpkg.com/@vitejs/plugin-react/-/plugin-react-4.5.2.tgz#8b98a8fbcefff4aa4c946966fbec560dc66d2bd9" - integrity sha512-QNVT3/Lxx99nMQWJWF7K4N6apUEuT0KlZA3mx/mVaoGj3smm/8rc8ezz15J1pcbcjDK0V15rpHetVfya08r76Q== + version "4.3.4" + resolved "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.3.4.tgz" + integrity sha512-SCCPBJtYLdE8PX/7ZQAs1QAZ8Jqwih+0VBLum1EGqmCCQal+MIUqLCzj3ZUy8ufbC0cAM4LRlSTm7IQJwWT4ug== dependencies: - "@babel/core" "^7.27.4" - "@babel/plugin-transform-react-jsx-self" "^7.27.1" - "@babel/plugin-transform-react-jsx-source" "^7.27.1" - "@rolldown/pluginutils" "1.0.0-beta.11" + "@babel/core" "^7.26.0" + "@babel/plugin-transform-react-jsx-self" "^7.25.9" + "@babel/plugin-transform-react-jsx-source" "^7.25.9" "@types/babel__core" "^7.20.5" - react-refresh "^0.17.0" + react-refresh "^0.14.2" -"@vitest/expect@2.1.9": - version "2.1.9" - resolved "https://registry.yarnpkg.com/@vitest/expect/-/expect-2.1.9.tgz#b566ea20d58ea6578d8dc37040d6c1a47ebe5ff8" - integrity sha512-UJCIkTBenHeKT1TTlKMJWy1laZewsRIzYighyYiJKZreqtdxSos/S1t+ktRMQWu2CKqaarrkeszJx1cgC5tGZw== +"@vitest/expect@2.1.8": + version "2.1.8" + resolved "https://registry.npmjs.org/@vitest/expect/-/expect-2.1.8.tgz" + integrity sha512-8ytZ/fFHq2g4PJVAtDX57mayemKgDR6X3Oa2Foro+EygiOJHUXhCqBAAKQYYajZpFoIfvBCF1j6R6IYRSIUFuw== dependencies: - "@vitest/spy" "2.1.9" - "@vitest/utils" "2.1.9" + "@vitest/spy" "2.1.8" + "@vitest/utils" "2.1.8" chai "^5.1.2" tinyrainbow "^1.2.0" -"@vitest/mocker@2.1.9": - version "2.1.9" - resolved "https://registry.yarnpkg.com/@vitest/mocker/-/mocker-2.1.9.tgz#36243b27351ca8f4d0bbc4ef91594ffd2dc25ef5" - integrity sha512-tVL6uJgoUdi6icpxmdrn5YNo3g3Dxv+IHJBr0GXHaEdTcw3F+cPKnsXFhli6nO+f/6SDKPHEK1UN+k+TQv0Ehg== +"@vitest/mocker@2.1.8": + version "2.1.8" + resolved "https://registry.npmjs.org/@vitest/mocker/-/mocker-2.1.8.tgz" + integrity sha512-7guJ/47I6uqfttp33mgo6ga5Gr1VnL58rcqYKyShoRK9ebu8T5Rs6HN3s1NABiBeVTdWNrwUMcHH54uXZBN4zA== dependencies: - "@vitest/spy" "2.1.9" + "@vitest/spy" "2.1.8" estree-walker "^3.0.3" magic-string "^0.30.12" -"@vitest/pretty-format@2.1.9", "@vitest/pretty-format@^2.1.9": - version "2.1.9" - resolved "https://registry.yarnpkg.com/@vitest/pretty-format/-/pretty-format-2.1.9.tgz#434ff2f7611689f9ce70cd7d567eceb883653fdf" - integrity sha512-KhRIdGV2U9HOUzxfiHmY8IFHTdqtOhIzCpd8WRdJiE7D/HUcZVD0EgQCVjm+Q9gkUXWgBvMmTtZgIG48wq7sOQ== +"@vitest/pretty-format@2.1.8", "@vitest/pretty-format@^2.1.8": + version "2.1.8" + resolved "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.8.tgz" + integrity sha512-9HiSZ9zpqNLKlbIDRWOnAWqgcA7xu+8YxXSekhr0Ykab7PAYFkhkwoqVArPOtJhPmYeE2YHgKZlj3CP36z2AJQ== dependencies: tinyrainbow "^1.2.0" -"@vitest/runner@2.1.9": - version "2.1.9" - resolved "https://registry.yarnpkg.com/@vitest/runner/-/runner-2.1.9.tgz#cc18148d2d797fd1fd5908d1f1851d01459be2f6" - integrity sha512-ZXSSqTFIrzduD63btIfEyOmNcBmQvgOVsPNPe0jYtESiXkhd8u2erDLnMxmGrDCwHCCHE7hxwRDCT3pt0esT4g== +"@vitest/runner@2.1.8": + version "2.1.8" + resolved "https://registry.npmjs.org/@vitest/runner/-/runner-2.1.8.tgz" + integrity sha512-17ub8vQstRnRlIU5k50bG+QOMLHRhYPAna5tw8tYbj+jzjcspnwnwtPtiOlkuKC4+ixDPTuLZiqiWWQ2PSXHVg== dependencies: - "@vitest/utils" "2.1.9" + "@vitest/utils" "2.1.8" pathe "^1.1.2" -"@vitest/snapshot@2.1.9": - version "2.1.9" - resolved "https://registry.yarnpkg.com/@vitest/snapshot/-/snapshot-2.1.9.tgz#24260b93f798afb102e2dcbd7e61c6dfa118df91" - integrity sha512-oBO82rEjsxLNJincVhLhaxxZdEtV0EFHMK5Kmx5sJ6H9L183dHECjiefOAdnqpIgT5eZwT04PoggUnW88vOBNQ== +"@vitest/snapshot@2.1.8": + version "2.1.8" + resolved "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.1.8.tgz" + integrity sha512-20T7xRFbmnkfcmgVEz+z3AU/3b0cEzZOt/zmnvZEctg64/QZbSDJEVm9fLnnlSi74KibmRsO9/Qabi+t0vCRPg== dependencies: - "@vitest/pretty-format" "2.1.9" + "@vitest/pretty-format" "2.1.8" magic-string "^0.30.12" pathe "^1.1.2" -"@vitest/spy@2.1.9": - version "2.1.9" - resolved "https://registry.yarnpkg.com/@vitest/spy/-/spy-2.1.9.tgz#cb28538c5039d09818b8bfa8edb4043c94727c60" - integrity sha512-E1B35FwzXXTs9FHNK6bDszs7mtydNi5MIfUWpceJ8Xbfb1gBMscAnwLbEu+B44ed6W3XjL9/ehLPHR1fkf1KLQ== +"@vitest/spy@2.1.8": + version "2.1.8" + resolved "https://registry.npmjs.org/@vitest/spy/-/spy-2.1.8.tgz" + integrity sha512-5swjf2q95gXeYPevtW0BLk6H8+bPlMb4Vw/9Em4hFxDcaOxS+e0LOX4yqNxoHzMR2akEB2xfpnWUzkZokmgWDg== dependencies: tinyspy "^3.0.2" -"@vitest/utils@2.1.9": - version "2.1.9" - resolved "https://registry.yarnpkg.com/@vitest/utils/-/utils-2.1.9.tgz#4f2486de8a54acf7ecbf2c5c24ad7994a680a6c1" - integrity sha512-v0psaMSkNJ3A2NMrUEHFRzJtDPFn+/VWZ5WxImB21T9fjucJRmS7xCS3ppEnARb9y11OAzaD+P2Ps+b+BGX5iQ== +"@vitest/utils@2.1.8": + version "2.1.8" + resolved "https://registry.npmjs.org/@vitest/utils/-/utils-2.1.8.tgz" + integrity sha512-dwSoui6djdwbfFmIgbIjX2ZhIoG7Ex/+xpxyiEgIGzjliY8xGkcpITKTlp6B4MgtGkF2ilvm97cPM96XZaAgcA== dependencies: - "@vitest/pretty-format" "2.1.9" + "@vitest/pretty-format" "2.1.8" loupe "^3.1.2" tinyrainbow "^1.2.0" abort-controller@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392" + resolved "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz" integrity sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg== dependencies: event-target-shim "^5.0.0" acorn-jsx@^5.3.2: version "5.3.2" - resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" + resolved "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz" integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== -acorn@^8.15.0: - version "8.15.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.15.0.tgz#a360898bc415edaac46c8241f6383975b930b816" - integrity sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg== +acorn@^8.14.0: + version "8.14.0" + resolved "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz" + integrity sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA== ag-auth@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/ag-auth/-/ag-auth-2.1.0.tgz#e6f9ecabbf23352456bd1e51ada4d6cf2382198d" + resolved "https://registry.npmjs.org/ag-auth/-/ag-auth-2.1.0.tgz" integrity sha512-M4l+IErFmYPk0HAvolaPyvCMyn3oJ4aPHVMeVqlxJIynkHGhyTFiT+LX+jYY34pEdwM03TLkQUMHxpXBMuNmZg== dependencies: jsonwebtoken "^9.0.0" @@ -1386,21 +1420,21 @@ ag-auth@^2.1.0: ag-channel@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/ag-channel/-/ag-channel-5.0.0.tgz#c2c00dfbe372ae43e0466ec89e29aca1bbb2fb3e" + resolved "https://registry.npmjs.org/ag-channel/-/ag-channel-5.0.0.tgz" integrity sha512-bArHkdqQxynim981t8FLZM5TfA0v7p081OlFdOxs6clB79GSGcGlOQMDa31DT9F5VMjzqNiJmhfGwinvfU/3Zg== dependencies: consumable-stream "^2.0.0" ag-request@^1.1.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/ag-request/-/ag-request-1.1.0.tgz#62ef63c572510bbce34993a5d47e467d0040a17f" + resolved "https://registry.npmjs.org/ag-request/-/ag-request-1.1.0.tgz" integrity sha512-d4K7QC1KnIpzcnUNNOeh1ddxmYMLiIdhdc1M8osxiHbZP/uoia4IINhhf2+1CrlnNJEPUoUH0Y58Sx0qeqoIvg== dependencies: sc-errors "^3.0.0" ajv@^6.12.4: version "6.12.6" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" + resolved "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz" integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== dependencies: fast-deep-equal "^3.1.1" @@ -1410,41 +1444,39 @@ ajv@^6.12.4: ansi-styles@^4.1.0: version "4.3.0" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz" integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== dependencies: color-convert "^2.0.1" argparse@^2.0.1: version "2.0.1" - resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" + resolved "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz" integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== -array-buffer-byte-length@^1.0.1, array-buffer-byte-length@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz#384d12a37295aec3769ab022ad323a18a51ccf8b" - integrity sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw== +array-buffer-byte-length@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz" + integrity sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg== dependencies: - call-bound "^1.0.3" - is-array-buffer "^3.0.5" + call-bind "^1.0.5" + is-array-buffer "^3.0.4" array-includes@^3.1.6, array-includes@^3.1.8: - version "3.1.9" - resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.9.tgz#1f0ccaa08e90cdbc3eb433210f903ad0f17c3f3a" - integrity sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ== + version "3.1.8" + resolved "https://registry.npmjs.org/array-includes/-/array-includes-3.1.8.tgz" + integrity sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ== dependencies: - call-bind "^1.0.8" - call-bound "^1.0.4" + call-bind "^1.0.7" define-properties "^1.2.1" - es-abstract "^1.24.0" - es-object-atoms "^1.1.1" - get-intrinsic "^1.3.0" - is-string "^1.1.1" - math-intrinsics "^1.1.0" + es-abstract "^1.23.2" + es-object-atoms "^1.0.0" + get-intrinsic "^1.2.4" + is-string "^1.0.7" array.prototype.findlast@^1.2.5: version "1.2.5" - resolved "https://registry.yarnpkg.com/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz#3e4fbcb30a15a7f5bf64cf2faae22d139c2e4904" + resolved "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz" integrity sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ== dependencies: call-bind "^1.0.7" @@ -1455,28 +1487,28 @@ array.prototype.findlast@^1.2.5: es-shim-unscopables "^1.0.2" array.prototype.flat@^1.3.1: - version "1.3.3" - resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz#534aaf9e6e8dd79fb6b9a9917f839ef1ec63afe5" - integrity sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg== + version "1.3.2" + resolved "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz" + integrity sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA== dependencies: - call-bind "^1.0.8" - define-properties "^1.2.1" - es-abstract "^1.23.5" - es-shim-unscopables "^1.0.2" + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + es-shim-unscopables "^1.0.0" -array.prototype.flatmap@^1.3.3: - version "1.3.3" - resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz#712cc792ae70370ae40586264629e33aab5dd38b" - integrity sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg== +array.prototype.flatmap@^1.3.2: + version "1.3.2" + resolved "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz" + integrity sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ== dependencies: - call-bind "^1.0.8" - define-properties "^1.2.1" - es-abstract "^1.23.5" - es-shim-unscopables "^1.0.2" + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + es-shim-unscopables "^1.0.0" array.prototype.tosorted@^1.1.4: version "1.1.4" - resolved "https://registry.yarnpkg.com/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz#fe954678ff53034e717ea3352a03f0b0b86f7ffc" + resolved "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz" integrity sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA== dependencies: call-bind "^1.0.7" @@ -1485,51 +1517,47 @@ array.prototype.tosorted@^1.1.4: es-errors "^1.3.0" es-shim-unscopables "^1.0.2" -arraybuffer.prototype.slice@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz#9d760d84dbdd06d0cbf92c8849615a1a7ab3183c" - integrity sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ== +arraybuffer.prototype.slice@^1.0.3: + version "1.0.3" + resolved "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz" + integrity sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A== dependencies: array-buffer-byte-length "^1.0.1" - call-bind "^1.0.8" + call-bind "^1.0.5" define-properties "^1.2.1" - es-abstract "^1.23.5" - es-errors "^1.3.0" - get-intrinsic "^1.2.6" + es-abstract "^1.22.3" + es-errors "^1.2.1" + get-intrinsic "^1.2.3" is-array-buffer "^3.0.4" + is-shared-array-buffer "^1.0.2" assertion-error@^2.0.1: version "2.0.1" - resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-2.0.1.tgz#f641a196b335690b1070bf00b6e7593fec190bf7" + resolved "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz" integrity sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA== -async-function@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/async-function/-/async-function-1.0.0.tgz#509c9fca60eaf85034c6829838188e4e4c8ffb2b" - integrity sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA== - async-stream-emitter@^7.0.1: version "7.0.1" - resolved "https://registry.yarnpkg.com/async-stream-emitter/-/async-stream-emitter-7.0.1.tgz#c01832cddcc8f07d8ed528347803ec1517f8886d" + resolved "https://registry.npmjs.org/async-stream-emitter/-/async-stream-emitter-7.0.1.tgz" integrity sha512-1bgA3iZ80rCBX2LocvsyZPy0QB3/xM+CsXBze2HDHLmshOqx2JlAANGq23djaJ48e9fpcKzTzS1QM0hAKKI0UQ== dependencies: stream-demux "^10.0.1" atomic-sleep@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/atomic-sleep/-/atomic-sleep-1.0.0.tgz#eb85b77a601fc932cfe432c5acd364a9e2c9075b" + resolved "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz" integrity sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ== available-typed-arrays@^1.0.7: version "1.0.7" - resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz#a5cc375d6a03c2efc87a553f3e0b1522def14846" + resolved "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz" integrity sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ== dependencies: possible-typed-array-names "^1.0.0" babel-plugin-macros@^3.1.0: version "3.1.0" - resolved "https://registry.yarnpkg.com/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz#9ef6dc74deb934b4db344dc973ee851d148c50c1" + resolved "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz" integrity sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg== dependencies: "@babel/runtime" "^7.12.5" @@ -1538,17 +1566,17 @@ babel-plugin-macros@^3.1.0: balanced-match@^1.0.0: version "1.0.2" - resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== base64-js@^1.3.1: version "1.5.1" - resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" + resolved "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== bl@^1.2.1: version "1.2.3" - resolved "https://registry.yarnpkg.com/bl/-/bl-1.2.3.tgz#1e8dd80142eac80d7158c9dccc047fb620e035e7" + resolved "https://registry.npmjs.org/bl/-/bl-1.2.3.tgz" integrity sha512-pvcNpa0UU69UT341rO6AYy4FVAIkUHuZXRIWbq+zHnsVcRzDDjIAhGuuYoi0d//cwIwtt4pkpKycWEfjdV+vww== dependencies: readable-stream "^2.3.5" @@ -1556,49 +1584,49 @@ bl@^1.2.1: boring-avatars@^1.11.2: version "1.11.2" - resolved "https://registry.yarnpkg.com/boring-avatars/-/boring-avatars-1.11.2.tgz#365e0b765fb0065ca0cb2fd20c200674d0a9ded6" + resolved "https://registry.npmjs.org/boring-avatars/-/boring-avatars-1.11.2.tgz" integrity sha512-3+wkwPeObwS4R37FGXMYViqc4iTrIRj5yzfX9Qy4mnpZ26sX41dGMhsAgmKks1r/uufY1pl4vpgzMWHYfJRb2A== brace-expansion@^1.1.7: - version "1.1.12" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.12.tgz#ab9b454466e5a8cc3a187beaad580412a9c5b843" - integrity sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg== + version "1.1.11" + resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== dependencies: balanced-match "^1.0.0" concat-map "0.0.1" brace-expansion@^2.0.1: - version "2.0.2" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.2.tgz#54fc53237a613d854c7bd37463aad17df87214e7" - integrity sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ== + version "2.0.1" + resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz" + integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== dependencies: balanced-match "^1.0.0" braces@^3.0.3: version "3.0.3" - resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" + resolved "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz" integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== dependencies: fill-range "^7.1.1" browserslist@^4.24.0: - version "4.25.0" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.25.0.tgz#986aa9c6d87916885da2b50d8eb577ac8d133b2c" - integrity sha512-PJ8gYKeS5e/whHBh8xrwYK+dAvEj7JXtz6uTucnMRB8OiGTsKccFekoRrjajPBHV8oOY+2tI4uxeceSimKwMFA== + version "4.24.2" + resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.24.2.tgz" + integrity sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg== dependencies: - caniuse-lite "^1.0.30001718" - electron-to-chromium "^1.5.160" - node-releases "^2.0.19" - update-browserslist-db "^1.1.3" + caniuse-lite "^1.0.30001669" + electron-to-chromium "^1.5.41" + node-releases "^2.0.18" + update-browserslist-db "^1.1.1" -buffer-equal-constant-time@^1.0.1: +buffer-equal-constant-time@1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819" + resolved "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz" integrity sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA== buffer@^5.2.1: version "5.7.1" - resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" + resolved "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz" integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== dependencies: base64-js "^1.3.1" @@ -1606,7 +1634,7 @@ buffer@^5.2.1: buffer@^6.0.3: version "6.0.3" - resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" + resolved "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz" integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== dependencies: base64-js "^1.3.1" @@ -1614,56 +1642,41 @@ buffer@^6.0.3: cac@^6.7.14: version "6.7.14" - resolved "https://registry.yarnpkg.com/cac/-/cac-6.7.14.tgz#804e1e6f506ee363cb0e3ccbb09cad5dd9870959" + resolved "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz" integrity sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ== -call-bind-apply-helpers@^1.0.0, call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz#4b5428c222be985d79c3d82657479dbe0b59b2d6" - integrity sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ== +call-bind@^1.0.2, call-bind@^1.0.5, call-bind@^1.0.6, call-bind@^1.0.7: + version "1.0.7" + resolved "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz" + integrity sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w== dependencies: + es-define-property "^1.0.0" es-errors "^1.3.0" function-bind "^1.1.2" - -call-bind@^1.0.7, call-bind@^1.0.8: - version "1.0.8" - resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.8.tgz#0736a9660f537e3388826f440d5ec45f744eaa4c" - integrity sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww== - dependencies: - call-bind-apply-helpers "^1.0.0" - es-define-property "^1.0.0" get-intrinsic "^1.2.4" - set-function-length "^1.2.2" - -call-bound@^1.0.2, call-bound@^1.0.3, call-bound@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/call-bound/-/call-bound-1.0.4.tgz#238de935d2a2a692928c538c7ccfa91067fd062a" - integrity sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg== - dependencies: - call-bind-apply-helpers "^1.0.2" - get-intrinsic "^1.3.0" + set-function-length "^1.2.1" callsites@^3.0.0: version "3.1.0" - resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" + resolved "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz" integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== -caniuse-lite@^1.0.30001718: - version "1.0.30001722" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001722.tgz#ec25a2b3085b25b9079b623db83c22a70882ce85" - integrity sha512-DCQHBBZtiK6JVkAGw7drvAMK0Q0POD/xZvEmDp6baiMMP6QXXk9HpD6mNYBZWhOPG6LvIDb82ITqtWjhDckHCA== +caniuse-lite@^1.0.30001669: + version "1.0.30001686" + resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001686.tgz" + integrity sha512-Y7deg0Aergpa24M3qLC5xjNklnKnhsmSyR/V89dLZ1n0ucJIFNs7PgR2Yfa/Zf6W79SbBicgtGxZr2juHkEUIA== canvas-renderer@~2.2.0: version "2.2.1" - resolved "https://registry.yarnpkg.com/canvas-renderer/-/canvas-renderer-2.2.1.tgz#c1d131f78a9799aca8af9679ad0a005052b65550" + resolved "https://registry.npmjs.org/canvas-renderer/-/canvas-renderer-2.2.1.tgz" integrity sha512-RrBgVL5qCEDIXpJ6NrzyRNoTnXxYarqm/cS/W6ERhUJts5UQtt/XPEosGN3rqUkZ4fjBArlnCbsISJ+KCFnIAg== dependencies: "@types/node" "*" chai@^5.1.2: - version "5.2.0" - resolved "https://registry.yarnpkg.com/chai/-/chai-5.2.0.tgz#1358ee106763624114addf84ab02697e411c9c05" - integrity sha512-mCuXncKXk5iCLhfhwTc0izo0gtEmpz5CtG2y8GiOINBlMVS6v8TMRc5TaLWKS6692m9+dVVfzgeVxR5UxWHTYw== + version "5.1.2" + resolved "https://registry.npmjs.org/chai/-/chai-5.1.2.tgz" + integrity sha512-aGtmf24DW6MLHHG5gCx4zaI3uBq3KRtxeVs0DjFH6Z0rDNbsvTxFASFvdj79pxjxZ8/5u3PIiN3IwEIQkiiuPw== dependencies: assertion-error "^2.0.1" check-error "^2.1.1" @@ -1673,7 +1686,7 @@ chai@^5.1.2: chalk@^4.0.0: version "4.1.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz" integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== dependencies: ansi-styles "^4.1.0" @@ -1681,12 +1694,12 @@ chalk@^4.0.0: check-error@^2.1.1: version "2.1.1" - resolved "https://registry.yarnpkg.com/check-error/-/check-error-2.1.1.tgz#87eb876ae71ee388fa0471fe423f494be1d96ccc" + resolved "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz" integrity sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw== clone-deep@^4.0.1: version "4.0.1" - resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387" + resolved "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz" integrity sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ== dependencies: is-plain-object "^2.0.4" @@ -1695,69 +1708,69 @@ clone-deep@^4.0.1: clsx@^1.1.0: version "1.2.1" - resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.2.1.tgz#0ddc4a20a549b59c93a4116bb26f5294ca17dc12" + resolved "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz" integrity sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg== clsx@^2.1.1: version "2.1.1" - resolved "https://registry.yarnpkg.com/clsx/-/clsx-2.1.1.tgz#eed397c9fd8bd882bfb18deab7102049a2f32999" + resolved "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz" integrity sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA== color-convert@^2.0.1: version "2.0.1" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + resolved "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz" integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== dependencies: color-name "~1.1.4" color-name@~1.1.4: version "1.1.4" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== colorette@^2.0.7: version "2.0.20" - resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a" + resolved "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz" integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== concat-map@0.0.1: version "0.0.1" - resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + resolved "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== consumable-stream@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/consumable-stream/-/consumable-stream-2.0.0.tgz#11d3c7281b747eb9efd31c199b3a8b1711bec654" + resolved "https://registry.npmjs.org/consumable-stream/-/consumable-stream-2.0.0.tgz" integrity sha512-I6WA2JVYXs/68rEvi1ie3rZjP6qusTVFEQkbzR+WC+fY56TpwiGTIDJETsrnlxv5CsnmK69ps6CkYvIbpEEqBA== consumable-stream@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/consumable-stream/-/consumable-stream-3.0.0.tgz#2bf140e0c5f9b63d6fa116ac6b05e53713d3cb41" + resolved "https://registry.npmjs.org/consumable-stream/-/consumable-stream-3.0.0.tgz" integrity sha512-CnnsJ9OG9ouxAjt3pc63/DaerezRo/WudqU71pc5epaIUi7NHu2T4v+3f0nKbbCY7icS/TfQ1Satr9rwZ7Jwsg== convert-source-map@^1.5.0: version "1.9.0" - resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.9.0.tgz#7faae62353fb4213366d0ca98358d22e8368b05f" + resolved "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz" integrity sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A== convert-source-map@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" + resolved "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz" integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== cookie@^1.0.1: version "1.0.2" - resolved "https://registry.yarnpkg.com/cookie/-/cookie-1.0.2.tgz#27360701532116bd3f1f9416929d176afe1e4610" + resolved "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz" integrity sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA== core-util-is@~1.0.0: version "1.0.3" - resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" + resolved "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz" integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== cosmiconfig@^7.0.0: version "7.1.0" - resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.1.0.tgz#1443b9afa596b670082ea46cbd8f6a62b84635f6" + resolved "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz" integrity sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA== dependencies: "@types/parse-json" "^4.0.0" @@ -1766,9 +1779,9 @@ cosmiconfig@^7.0.0: path-type "^4.0.0" yaml "^1.10.0" -cross-spawn@^7.0.3, cross-spawn@^7.0.6: +cross-spawn@^7.0.3, cross-spawn@^7.0.5: version "7.0.6" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f" + resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz" integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== dependencies: path-key "^3.1.0" @@ -1777,77 +1790,82 @@ cross-spawn@^7.0.3, cross-spawn@^7.0.6: csstype@^3.0.2, csstype@^3.1.3: version "3.1.3" - resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.3.tgz#d80ff294d114fb0e6ac500fbf85b60137d7eff81" + resolved "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz" integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw== -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" - integrity sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ== - dependencies: - call-bound "^1.0.3" - es-errors "^1.3.0" - is-data-view "^1.0.2" - -data-view-byte-length@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz#9e80f7ca52453ce3e93d25a35318767ea7704735" - integrity sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ== - dependencies: - call-bound "^1.0.3" - es-errors "^1.3.0" - is-data-view "^1.0.2" - -data-view-byte-offset@^1.0.1: +data-view-buffer@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz#068307f9b71ab76dbbe10291389e020856606191" - integrity sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ== + resolved "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz" + integrity sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA== dependencies: - call-bound "^1.0.2" + call-bind "^1.0.6" + es-errors "^1.3.0" + is-data-view "^1.0.1" + +data-view-byte-length@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz" + integrity sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ== + dependencies: + call-bind "^1.0.7" + es-errors "^1.3.0" + is-data-view "^1.0.1" + +data-view-byte-offset@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz" + integrity sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA== + dependencies: + call-bind "^1.0.6" es-errors "^1.3.0" is-data-view "^1.0.1" dateformat@^4.6.3: version "4.6.3" - resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-4.6.3.tgz#556fa6497e5217fedb78821424f8a1c22fa3f4b5" + resolved "https://registry.npmjs.org/dateformat/-/dateformat-4.6.3.tgz" integrity sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA== +dayjs@^1.11.13: + version "1.11.13" + resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.13.tgz#92430b0139055c3ebb60150aa13e860a4b5a366c" + integrity sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg== + debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4, debug@^4.3.7: - version "4.4.1" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.1.tgz#e5a8bc6cbc4c6cd3e64308b0693a3d4fa550189b" - integrity sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ== + version "4.3.7" + resolved "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz" + integrity sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ== dependencies: ms "^2.1.3" deep-eql@^5.0.1: version "5.0.2" - resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-5.0.2.tgz#4b756d8d770a9257300825d52a2c2cff99c3a341" + resolved "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz" integrity sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q== deep-is@^0.1.3: version "0.1.4" - resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" + resolved "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz" integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== default-gateway@^6.0.3: version "6.0.3" - resolved "https://registry.yarnpkg.com/default-gateway/-/default-gateway-6.0.3.tgz#819494c888053bdb743edbf343d6cdf7f2943a71" + resolved "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz" integrity sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg== dependencies: execa "^5.0.0" define-data-property@^1.0.1, define-data-property@^1.1.4: version "1.1.4" - resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.4.tgz#894dc141bb7d3060ae4366f6a0107e68fbe48c5e" + resolved "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz" integrity sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A== dependencies: es-define-property "^1.0.0" es-errors "^1.3.0" gopd "^1.0.1" -define-properties@^1.1.3, define-properties@^1.2.1: +define-properties@^1.1.3, define-properties@^1.2.0, define-properties@^1.2.1: version "1.2.1" - resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.1.tgz#10781cc616eb951a80a034bafcaa7377f6af2b6c" + resolved "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz" integrity sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg== dependencies: define-data-property "^1.0.1" @@ -1856,7 +1874,7 @@ define-properties@^1.1.3, define-properties@^1.2.1: dns-over-http-resolver@^1.2.3: version "1.2.3" - resolved "https://registry.yarnpkg.com/dns-over-http-resolver/-/dns-over-http-resolver-1.2.3.tgz#194d5e140a42153f55bb79ac5a64dd2768c36af9" + resolved "https://registry.npmjs.org/dns-over-http-resolver/-/dns-over-http-resolver-1.2.3.tgz" integrity sha512-miDiVSI6KSNbi4SVifzO/reD8rMnxgrlnkrlkugOLQpWQTe2qMdHsZp5DmfKjxNE+/T3VAAYLQUZMv9SMr6+AA== dependencies: debug "^4.3.1" @@ -1865,183 +1883,166 @@ dns-over-http-resolver@^1.2.3: doctrine@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" + resolved "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz" integrity sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw== dependencies: esutils "^2.0.2" dom-helpers@^5.0.1: version "5.2.1" - resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-5.2.1.tgz#d9400536b2bf8225ad98fe052e029451ac40e902" + resolved "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz" integrity sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA== dependencies: "@babel/runtime" "^7.8.7" csstype "^3.0.2" -dunder-proto@^1.0.0, dunder-proto@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/dunder-proto/-/dunder-proto-1.0.1.tgz#d7ae667e1dc83482f8b70fd0f6eefc50da30f58a" - integrity sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A== - dependencies: - call-bind-apply-helpers "^1.0.1" - es-errors "^1.3.0" - gopd "^1.2.0" - ecdsa-sig-formatter@1.0.11: version "1.0.11" - resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz#ae0f0fa2d85045ef14a817daa3ce9acd0489e5bf" + resolved "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz" integrity sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ== dependencies: safe-buffer "^5.0.1" -electron-to-chromium@^1.5.160: - version "1.5.166" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.166.tgz#3fff386ed473cc2169dbe2d3ace9592262601114" - integrity sha512-QPWqHL0BglzPYyJJ1zSSmwFFL6MFXhbACOCcsCdUMCkzPdS9/OIBVxg516X/Ado2qwAq8k0nJJ7phQPCqiaFAw== +electron-to-chromium@^1.5.41: + version "1.5.68" + resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.68.tgz" + integrity sha512-FgMdJlma0OzUYlbrtZ4AeXjKxKPk6KT8WOP8BjcqxWtlg8qyJQjRzPJzUtUn5GBg1oQ26hFs7HOOHJMYiJRnvQ== end-of-stream@^1.1.0: version "1.4.4" - resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" + resolved "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz" integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== dependencies: once "^1.4.0" err-code@^3.0.1: version "3.0.1" - resolved "https://registry.yarnpkg.com/err-code/-/err-code-3.0.1.tgz#a444c7b992705f2b120ee320b09972eef331c920" + resolved "https://registry.npmjs.org/err-code/-/err-code-3.0.1.tgz" integrity sha512-GiaH0KJUewYok+eeY05IIgjtAe4Yltygk9Wqp1V5yVWLdhf0hYZchRjNIT9bb0mSwRcIusT3cx7PJUf3zEIfUA== error-ex@^1.3.1: version "1.3.2" - resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" + resolved "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz" integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== dependencies: is-arrayish "^0.2.1" -es-abstract@^1.17.5, es-abstract@^1.23.2, es-abstract@^1.23.3, es-abstract@^1.23.5, es-abstract@^1.23.6, es-abstract@^1.23.9, es-abstract@^1.24.0: - version "1.24.0" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.24.0.tgz#c44732d2beb0acc1ed60df840869e3106e7af328" - integrity sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg== +es-abstract@^1.17.5, es-abstract@^1.22.1, es-abstract@^1.22.3, es-abstract@^1.23.0, es-abstract@^1.23.2, es-abstract@^1.23.3, es-abstract@^1.23.5: + version "1.23.5" + resolved "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.5.tgz" + integrity sha512-vlmniQ0WNPwXqA0BnmwV3Ng7HxiGlh6r5U6JcTMNx8OilcAGqVJBHJcPjqOMaczU9fRuRK5Px2BdVyPRnKMMVQ== dependencies: - array-buffer-byte-length "^1.0.2" - arraybuffer.prototype.slice "^1.0.4" + array-buffer-byte-length "^1.0.1" + arraybuffer.prototype.slice "^1.0.3" available-typed-arrays "^1.0.7" - call-bind "^1.0.8" - call-bound "^1.0.4" - data-view-buffer "^1.0.2" - data-view-byte-length "^1.0.2" - data-view-byte-offset "^1.0.1" - es-define-property "^1.0.1" + call-bind "^1.0.7" + data-view-buffer "^1.0.1" + data-view-byte-length "^1.0.1" + data-view-byte-offset "^1.0.0" + es-define-property "^1.0.0" es-errors "^1.3.0" - es-object-atoms "^1.1.1" - es-set-tostringtag "^2.1.0" - es-to-primitive "^1.3.0" - function.prototype.name "^1.1.8" - get-intrinsic "^1.3.0" - get-proto "^1.0.1" - get-symbol-description "^1.1.0" + es-object-atoms "^1.0.0" + es-set-tostringtag "^2.0.3" + es-to-primitive "^1.2.1" + function.prototype.name "^1.1.6" + get-intrinsic "^1.2.4" + get-symbol-description "^1.0.2" globalthis "^1.0.4" - gopd "^1.2.0" + gopd "^1.0.1" has-property-descriptors "^1.0.2" - has-proto "^1.2.0" - has-symbols "^1.1.0" + has-proto "^1.0.3" + has-symbols "^1.0.3" hasown "^2.0.2" - internal-slot "^1.1.0" - is-array-buffer "^3.0.5" + internal-slot "^1.0.7" + is-array-buffer "^3.0.4" is-callable "^1.2.7" - is-data-view "^1.0.2" + is-data-view "^1.0.1" is-negative-zero "^2.0.3" - is-regex "^1.2.1" - is-set "^2.0.3" - is-shared-array-buffer "^1.0.4" - is-string "^1.1.1" - is-typed-array "^1.1.15" - is-weakref "^1.1.1" - math-intrinsics "^1.1.0" - object-inspect "^1.13.4" + is-regex "^1.1.4" + is-shared-array-buffer "^1.0.3" + is-string "^1.0.7" + is-typed-array "^1.1.13" + is-weakref "^1.0.2" + object-inspect "^1.13.3" object-keys "^1.1.1" - object.assign "^4.1.7" - own-keys "^1.0.1" - regexp.prototype.flags "^1.5.4" - safe-array-concat "^1.1.3" - safe-push-apply "^1.0.0" - safe-regex-test "^1.1.0" - set-proto "^1.0.0" - stop-iteration-iterator "^1.1.0" - string.prototype.trim "^1.2.10" - string.prototype.trimend "^1.0.9" + object.assign "^4.1.5" + regexp.prototype.flags "^1.5.3" + safe-array-concat "^1.1.2" + safe-regex-test "^1.0.3" + string.prototype.trim "^1.2.9" + string.prototype.trimend "^1.0.8" string.prototype.trimstart "^1.0.8" - typed-array-buffer "^1.0.3" - typed-array-byte-length "^1.0.3" - typed-array-byte-offset "^1.0.4" - typed-array-length "^1.0.7" - unbox-primitive "^1.1.0" - which-typed-array "^1.1.19" + typed-array-buffer "^1.0.2" + typed-array-byte-length "^1.0.1" + typed-array-byte-offset "^1.0.2" + typed-array-length "^1.0.6" + unbox-primitive "^1.0.2" + which-typed-array "^1.1.15" -es-define-property@^1.0.0, es-define-property@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.1.tgz#983eb2f9a6724e9303f61addf011c72e09e0b0fa" - integrity sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g== +es-define-property@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz" + integrity sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ== + dependencies: + get-intrinsic "^1.2.4" -es-errors@^1.3.0: +es-errors@^1.2.1, es-errors@^1.3.0: version "1.3.0" - resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" + resolved "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz" integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== -es-iterator-helpers@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/es-iterator-helpers/-/es-iterator-helpers-1.2.1.tgz#d1dd0f58129054c0ad922e6a9a1e65eef435fe75" - integrity sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w== +es-iterator-helpers@^1.1.0: + version "1.2.0" + resolved "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.2.0.tgz" + integrity sha512-tpxqxncxnpw3c93u8n3VOzACmRFoVmWJqbWXvX/JfKbkhBw1oslgPrUfeSt2psuqyEJFD6N/9lg5i7bsKpoq+Q== dependencies: - call-bind "^1.0.8" - call-bound "^1.0.3" + call-bind "^1.0.7" define-properties "^1.2.1" - es-abstract "^1.23.6" + es-abstract "^1.23.3" es-errors "^1.3.0" es-set-tostringtag "^2.0.3" function-bind "^1.1.2" - get-intrinsic "^1.2.6" + get-intrinsic "^1.2.4" globalthis "^1.0.4" - gopd "^1.2.0" + gopd "^1.0.1" has-property-descriptors "^1.0.2" - has-proto "^1.2.0" - has-symbols "^1.1.0" - internal-slot "^1.1.0" - iterator.prototype "^1.1.4" - safe-array-concat "^1.1.3" + has-proto "^1.0.3" + has-symbols "^1.0.3" + internal-slot "^1.0.7" + iterator.prototype "^1.1.3" + safe-array-concat "^1.1.2" es-module-lexer@^1.5.4: - version "1.7.0" - resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-1.7.0.tgz#9159601561880a85f2734560a9099b2c31e5372a" - integrity sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA== + version "1.5.4" + resolved "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.5.4.tgz" + integrity sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw== -es-object-atoms@^1.0.0, es-object-atoms@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz#1c4f2c4837327597ce69d2ca190a7fdd172338c1" - integrity sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA== +es-object-atoms@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz" + integrity sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw== dependencies: es-errors "^1.3.0" -es-set-tostringtag@^2.0.3, es-set-tostringtag@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz#f31dbbe0c183b00a6d26eb6325c810c0fd18bd4d" - integrity sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA== +es-set-tostringtag@^2.0.3: + version "2.0.3" + resolved "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz" + integrity sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ== dependencies: - es-errors "^1.3.0" - get-intrinsic "^1.2.6" + get-intrinsic "^1.2.4" has-tostringtag "^1.0.2" - hasown "^2.0.2" + hasown "^2.0.1" -es-shim-unscopables@^1.0.2: - version "1.1.0" - resolved "https://registry.yarnpkg.com/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz#438df35520dac5d105f3943d927549ea3b00f4b5" - integrity sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw== +es-shim-unscopables@^1.0.0, es-shim-unscopables@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz" + integrity sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw== dependencies: - hasown "^2.0.2" + hasown "^2.0.0" -es-to-primitive@^1.3.0: +es-to-primitive@^1.2.1: version "1.3.0" - resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.3.0.tgz#96c89c82cc49fd8794a24835ba3e1ff87f214e18" + resolved "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz" integrity sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g== dependencies: is-callable "^1.2.7" @@ -2050,7 +2051,7 @@ es-to-primitive@^1.3.0: esbuild@^0.21.3: version "0.21.5" - resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.21.5.tgz#9ca301b120922959b766360d8ac830da0d02997d" + resolved "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz" integrity sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw== optionalDependencies: "@esbuild/aix-ppc64" "0.21.5" @@ -2079,80 +2080,79 @@ esbuild@^0.21.3: escalade@^3.2.0: version "3.2.0" - resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5" + resolved "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz" integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA== escape-string-regexp@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz" integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== 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== + version "7.37.2" + resolved "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.2.tgz" + integrity sha512-EsTAnj9fLVr/GZleBLFbj/sSuXeWmp1eXIN60ceYnZveqEaUCyW4X+Vh4WTdUhCkW4xutXYqTXCUSyqD4rB75w== dependencies: array-includes "^3.1.8" array.prototype.findlast "^1.2.5" - array.prototype.flatmap "^1.3.3" + array.prototype.flatmap "^1.3.2" array.prototype.tosorted "^1.1.4" doctrine "^2.1.0" - es-iterator-helpers "^1.2.1" + es-iterator-helpers "^1.1.0" 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.entries "^1.1.8" object.fromentries "^2.0.8" - object.values "^1.2.1" + object.values "^1.2.0" prop-types "^15.8.1" resolve "^2.0.0-next.5" semver "^6.3.1" - string.prototype.matchall "^4.0.12" + string.prototype.matchall "^4.0.11" string.prototype.repeat "^1.0.0" -eslint-scope@^8.3.0: - version "8.4.0" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-8.4.0.tgz#88e646a207fad61436ffa39eb505147200655c82" - integrity sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg== +eslint-scope@^8.2.0: + version "8.2.0" + resolved "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.2.0.tgz" + integrity sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A== dependencies: esrecurse "^4.3.0" estraverse "^5.2.0" eslint-visitor-keys@^3.4.3: version "3.4.3" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" + resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz" integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== -eslint-visitor-keys@^4.2.0, eslint-visitor-keys@^4.2.1: - version "4.2.1" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz#4cfea60fe7dd0ad8e816e1ed026c1d5251b512c1" - integrity sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ== +eslint-visitor-keys@^4.2.0: + version "4.2.0" + resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz" + integrity sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw== eslint@^9.9.0: - version "9.28.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-9.28.0.tgz#b0bcbe82a16945a40906924bea75e8b4980ced7d" - integrity sha512-ocgh41VhRlf9+fVpe7QKzwLj9c92fDiqOj8Y3Sd4/ZmVA4Btx4PlUYPq4pp9JDyupkf1upbEXecxL2mwNV7jPQ== + version "9.16.0" + resolved "https://registry.npmjs.org/eslint/-/eslint-9.16.0.tgz" + integrity sha512-whp8mSQI4C8VXd+fLgSM0lh3UlmcFtVwUQjyKCFfsp+2ItAIYhlq/hqGahGqHE6cv9unM41VlqKk2VtKYR2TaA== dependencies: "@eslint-community/eslint-utils" "^4.2.0" "@eslint-community/regexpp" "^4.12.1" - "@eslint/config-array" "^0.20.0" - "@eslint/config-helpers" "^0.2.1" - "@eslint/core" "^0.14.0" - "@eslint/eslintrc" "^3.3.1" - "@eslint/js" "9.28.0" - "@eslint/plugin-kit" "^0.3.1" + "@eslint/config-array" "^0.19.0" + "@eslint/core" "^0.9.0" + "@eslint/eslintrc" "^3.2.0" + "@eslint/js" "9.16.0" + "@eslint/plugin-kit" "^0.2.3" "@humanfs/node" "^0.16.6" "@humanwhocodes/module-importer" "^1.0.1" - "@humanwhocodes/retry" "^0.4.2" + "@humanwhocodes/retry" "^0.4.1" "@types/estree" "^1.0.6" "@types/json-schema" "^7.0.15" ajv "^6.12.4" chalk "^4.0.0" - cross-spawn "^7.0.6" + cross-spawn "^7.0.5" debug "^4.3.2" escape-string-regexp "^4.0.0" - eslint-scope "^8.3.0" + eslint-scope "^8.2.0" eslint-visitor-keys "^4.2.0" espree "^10.3.0" esquery "^1.5.0" @@ -2171,58 +2171,58 @@ eslint@^9.9.0: optionator "^0.9.3" espree@^10.0.1, espree@^10.3.0: - version "10.4.0" - resolved "https://registry.yarnpkg.com/espree/-/espree-10.4.0.tgz#d54f4949d4629005a1fa168d937c3ff1f7e2a837" - integrity sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ== + version "10.3.0" + resolved "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz" + integrity sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg== dependencies: - acorn "^8.15.0" + acorn "^8.14.0" acorn-jsx "^5.3.2" - eslint-visitor-keys "^4.2.1" + eslint-visitor-keys "^4.2.0" esquery@^1.5.0: version "1.6.0" - resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.6.0.tgz#91419234f804d852a82dceec3e16cdc22cf9dae7" + resolved "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz" integrity sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg== dependencies: estraverse "^5.1.0" esrecurse@^4.3.0: version "4.3.0" - resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" + resolved "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz" integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== dependencies: estraverse "^5.2.0" estraverse@^5.1.0, estraverse@^5.2.0, estraverse@^5.3.0: version "5.3.0" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" + resolved "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz" integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== estree-walker@^3.0.3: version "3.0.3" - resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-3.0.3.tgz#67c3e549ec402a487b4fc193d1953a524752340d" + resolved "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz" integrity sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g== dependencies: "@types/estree" "^1.0.0" esutils@^2.0.2: version "2.0.3" - resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" + resolved "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz" integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== event-target-shim@^5.0.0: version "5.0.1" - resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789" + resolved "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz" integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ== events@^3.3.0: version "3.3.0" - resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" + resolved "https://registry.npmjs.org/events/-/events-3.3.0.tgz" integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== execa@^5.0.0: version "5.1.1" - resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" + resolved "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz" integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== dependencies: cross-spawn "^7.0.3" @@ -2236,80 +2236,80 @@ execa@^5.0.0: strip-final-newline "^2.0.0" expect-type@^1.1.0: - version "1.2.1" - resolved "https://registry.yarnpkg.com/expect-type/-/expect-type-1.2.1.tgz#af76d8b357cf5fa76c41c09dafb79c549e75f71f" - integrity sha512-/kP8CAwxzLVEeFrMm4kMmy4CCDlpipyA7MYLVrdJIkV0fYF0UaigQHRsxHiuY/GEea+bh4KSv3TIlgr+2UL6bw== + version "1.1.0" + resolved "https://registry.npmjs.org/expect-type/-/expect-type-1.1.0.tgz" + integrity sha512-bFi65yM+xZgk+u/KRIpekdSYkTB5W1pEf0Lt8Q8Msh7b+eQ7LXVtIB1Bkm4fvclDEL1b2CZkMhv2mOeF8tMdkA== fast-copy@^3.0.2: version "3.0.2" - resolved "https://registry.yarnpkg.com/fast-copy/-/fast-copy-3.0.2.tgz#59c68f59ccbcac82050ba992e0d5c389097c9d35" + resolved "https://registry.npmjs.org/fast-copy/-/fast-copy-3.0.2.tgz" integrity sha512-dl0O9Vhju8IrcLndv2eU4ldt1ftXMqqfgN4H1cpmGV7P6jeB9FwpN9a2c8DPGE1Ys88rNUJVYDHq73CGAGOPfQ== fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" - resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + resolved "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== 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== + version "3.3.2" + resolved "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz" + integrity sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow== dependencies: "@nodelib/fs.stat" "^2.0.2" "@nodelib/fs.walk" "^1.2.3" glob-parent "^5.1.2" merge2 "^1.3.0" - micromatch "^4.0.8" + micromatch "^4.0.4" fast-json-stable-stringify@^2.0.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + resolved "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz" integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== fast-levenshtein@^2.0.6: version "2.0.6" - resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + resolved "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz" integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== fast-redact@^3.1.1: version "3.5.0" - resolved "https://registry.yarnpkg.com/fast-redact/-/fast-redact-3.5.0.tgz#e9ea02f7e57d0cd8438180083e93077e496285e4" + resolved "https://registry.npmjs.org/fast-redact/-/fast-redact-3.5.0.tgz" integrity sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A== fast-safe-stringify@^2.1.1: version "2.1.1" - resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz#c406a83b6e70d9e35ce3b30a81141df30aeba884" + resolved "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz" integrity sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA== fastq@^1.6.0: - version "1.19.1" - resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.19.1.tgz#d50eaba803c8846a883c16492821ebcd2cda55f5" - integrity sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ== + version "1.17.1" + resolved "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz" + integrity sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w== dependencies: reusify "^1.0.4" file-entry-cache@^8.0.0: version "8.0.0" - resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-8.0.0.tgz#7787bddcf1131bffb92636c69457bbc0edd6d81f" + resolved "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz" integrity sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ== dependencies: flat-cache "^4.0.0" fill-range@^7.1.1: version "7.1.1" - resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" + resolved "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz" integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== dependencies: to-regex-range "^5.0.1" find-root@^1.1.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/find-root/-/find-root-1.1.0.tgz#abcfc8ba76f708c42a97b3d685b7e9450bfb9ce4" + resolved "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz" integrity sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng== find-up@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" + resolved "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz" integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== dependencies: locate-path "^6.0.0" @@ -2317,131 +2317,116 @@ find-up@^5.0.0: flat-cache@^4.0.0: version "4.0.1" - resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-4.0.1.tgz#0ece39fcb14ee012f4b0410bd33dd9c1f011127c" + resolved "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz" integrity sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw== dependencies: flatted "^3.2.9" keyv "^4.5.4" flatted@^3.2.9: - version "3.3.3" - resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.3.tgz#67c8fad95454a7c7abebf74bb78ee74a44023358" - integrity sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg== + version "3.3.2" + resolved "https://registry.npmjs.org/flatted/-/flatted-3.3.2.tgz" + integrity sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA== -for-each@^0.3.3, for-each@^0.3.5: - version "0.3.5" - resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.5.tgz#d650688027826920feeb0af747ee7b9421a41d47" - integrity sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg== +for-each@^0.3.3: + version "0.3.3" + resolved "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz" + integrity sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw== dependencies: - is-callable "^1.2.7" + is-callable "^1.1.3" fsevents@~2.3.2, fsevents@~2.3.3: version "2.3.3" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" + resolved "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz" integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== function-bind@^1.1.2: version "1.1.2" - resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" + resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz" integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== -function.prototype.name@^1.1.6, function.prototype.name@^1.1.8: - version "1.1.8" - resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.8.tgz#e68e1df7b259a5c949eeef95cdbde53edffabb78" - integrity sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q== +function.prototype.name@^1.1.6: + version "1.1.6" + resolved "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz" + integrity sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg== dependencies: - call-bind "^1.0.8" - call-bound "^1.0.3" - define-properties "^1.2.1" + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" functions-have-names "^1.2.3" - hasown "^2.0.2" - is-callable "^1.2.7" functions-have-names@^1.2.3: version "1.2.3" - resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" + resolved "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz" integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== gensync@^1.0.0-beta.2: version "1.0.0-beta.2" - resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" + resolved "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz" integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== -get-intrinsic@^1.2.4, get-intrinsic@^1.2.5, get-intrinsic@^1.2.6, get-intrinsic@^1.2.7, get-intrinsic@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz#743f0e3b6964a93a5491ed1bffaae054d7f98d01" - integrity sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ== +get-intrinsic@^1.2.1, get-intrinsic@^1.2.3, get-intrinsic@^1.2.4: + version "1.2.4" + resolved "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz" + integrity sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ== dependencies: - call-bind-apply-helpers "^1.0.2" - es-define-property "^1.0.1" es-errors "^1.3.0" - es-object-atoms "^1.1.1" function-bind "^1.1.2" - get-proto "^1.0.1" - gopd "^1.2.0" - has-symbols "^1.1.0" - hasown "^2.0.2" - math-intrinsics "^1.1.0" + has-proto "^1.0.1" + has-symbols "^1.0.3" + hasown "^2.0.0" get-params@^0.1.2: version "0.1.2" - resolved "https://registry.yarnpkg.com/get-params/-/get-params-0.1.2.tgz#bae0dfaba588a0c60d7834c0d8dc2ff60eeef2fe" + resolved "https://registry.npmjs.org/get-params/-/get-params-0.1.2.tgz" integrity sha512-41eOxtlGgHQRbFyA8KTH+w+32Em3cRdfBud7j67ulzmIfmaHX9doq47s0fa4P5o9H64BZX9nrYI6sJvk46Op+Q== -get-proto@^1.0.0, get-proto@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/get-proto/-/get-proto-1.0.1.tgz#150b3f2743869ef3e851ec0c49d15b1d14d00ee1" - integrity sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g== - dependencies: - dunder-proto "^1.0.1" - es-object-atoms "^1.0.0" - get-stream@^6.0.0: version "6.0.1" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" + resolved "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz" integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== -get-symbol-description@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.1.0.tgz#7bdd54e0befe8ffc9f3b4e203220d9f1e881b6ee" - integrity sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg== +get-symbol-description@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz" + integrity sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg== dependencies: - call-bound "^1.0.3" + call-bind "^1.0.5" es-errors "^1.3.0" - get-intrinsic "^1.2.6" + get-intrinsic "^1.2.4" glob-parent@^5.1.2: version "5.1.2" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz" integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== dependencies: is-glob "^4.0.1" glob-parent@^6.0.2: version "6.0.2" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" + resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz" integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== dependencies: is-glob "^4.0.3" globals@^11.1.0: version "11.12.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" + resolved "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz" integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== globals@^14.0.0: version "14.0.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-14.0.0.tgz#898d7413c29babcf6bafe56fcadded858ada724e" + resolved "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz" integrity sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ== globals@^15.9.0: - version "15.15.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-15.15.0.tgz#7c4761299d41c32b075715a4ce1ede7897ff72a8" - integrity sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg== + version "15.13.0" + resolved "https://registry.npmjs.org/globals/-/globals-15.13.0.tgz" + integrity sha512-49TewVEz0UxZjr1WYYsWpPrhyC/B/pA8Bq0fUmet2n+eR7yn0IvNzNaoBwnK6mdkzcN+se7Ez9zUgULTz2QH4g== globalthis@^1.0.4: version "1.0.4" - resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.4.tgz#7430ed3a975d97bfb59bcce41f5cabbafa651236" + resolved "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz" integrity sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ== dependencies: define-properties "^1.2.1" @@ -2449,135 +2434,130 @@ globalthis@^1.0.4: globrex@^0.1.2: version "0.1.2" - resolved "https://registry.yarnpkg.com/globrex/-/globrex-0.1.2.tgz#dd5d9ec826232730cd6793a5e33a9302985e6098" + resolved "https://registry.npmjs.org/globrex/-/globrex-0.1.2.tgz" integrity sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg== goober@^2.0.33: version "2.1.16" - resolved "https://registry.yarnpkg.com/goober/-/goober-2.1.16.tgz#7d548eb9b83ff0988d102be71f271ca8f9c82a95" + resolved "https://registry.npmjs.org/goober/-/goober-2.1.16.tgz" integrity sha512-erjk19y1U33+XAMe1VTvIONHYoSqE4iS7BYUZfHaqeohLmnC0FdxEh7rQU+6MZ4OajItzjZFSRtVANrQwNq6/g== -gopd@^1.0.1, gopd@^1.2.0: +gopd@^1.0.1, gopd@^1.1.0: version "1.2.0" - resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.2.0.tgz#89f56b8217bdbc8802bd299df6d7f1081d7e51a1" + resolved "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz" integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg== graphemer@^1.4.0: version "1.4.0" - resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6" + resolved "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz" integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== has-bigints@^1.0.2: - version "1.1.0" - resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.1.0.tgz#28607e965ac967e03cd2a2c70a2636a1edad49fe" - integrity sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg== + version "1.0.2" + resolved "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz" + integrity sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ== has-flag@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + resolved "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== has-property-descriptors@^1.0.0, has-property-descriptors@^1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz#963ed7d071dc7bf5f084c5bfbe0d1b6222586854" + resolved "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz" integrity sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg== dependencies: es-define-property "^1.0.0" -has-proto@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.2.0.tgz#5de5a6eabd95fdffd9818b43055e8065e39fe9d5" - integrity sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ== - dependencies: - dunder-proto "^1.0.0" - -has-symbols@^1.0.3, has-symbols@^1.1.0: +has-proto@^1.0.1, has-proto@^1.0.3: version "1.1.0" - resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.1.0.tgz#fc9c6a783a084951d0b971fe1018de813707a338" + resolved "https://registry.npmjs.org/has-proto/-/has-proto-1.1.0.tgz" + integrity sha512-QLdzI9IIO1Jg7f9GT1gXpPpXArAn6cS31R1eEZqz08Gc+uQ8/XiqHWt17Fiw+2p6oTTIq5GXEpQkAlA88YRl/Q== + dependencies: + call-bind "^1.0.7" + +has-symbols@^1.0.3: + version "1.1.0" + resolved "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz" integrity sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ== -has-tostringtag@^1.0.2: +has-tostringtag@^1.0.0, has-tostringtag@^1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz#2cdc42d40bef2e5b4eeab7c01a73c54ce7ab5abc" + resolved "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz" integrity sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw== dependencies: has-symbols "^1.0.3" -hasown@^2.0.2: +hasown@^2.0.0, hasown@^2.0.1, hasown@^2.0.2: version "2.0.2" - resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" + resolved "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz" integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== dependencies: function-bind "^1.1.2" help-me@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/help-me/-/help-me-5.0.0.tgz#b1ebe63b967b74060027c2ac61f9be12d354a6f6" + resolved "https://registry.npmjs.org/help-me/-/help-me-5.0.0.tgz" integrity sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg== hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.1: version "3.3.2" - resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45" + resolved "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz" integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw== dependencies: react-is "^16.7.0" human-signals@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" + resolved "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz" integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== humanize-duration@^3.32.1: - version "3.33.0" - resolved "https://registry.yarnpkg.com/humanize-duration/-/humanize-duration-3.33.0.tgz#29b3276e68443e513fc85223d094faacdbb8454c" - integrity sha512-vYJX7BSzn7EQ4SaP2lPYVy+icHDppB6k7myNeI3wrSRfwMS5+BHyGgzpHR0ptqJ2AQ6UuIKrclSg5ve6Ci4IAQ== + version "3.32.1" + resolved "https://registry.npmjs.org/humanize-duration/-/humanize-duration-3.32.1.tgz" + integrity sha512-inh5wue5XdfObhu/IGEMiA1nUXigSGcaKNemcbLRKa7jXYGDZXr3LoT9pTIzq2hPEbld7w/qv9h+ikWGz8fL1g== ieee754@^1.1.13, ieee754@^1.2.1: version "1.2.1" - resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" + resolved "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== -ignore@^5.2.0: +ignore@^5.2.0, ignore@^5.3.1: version "5.3.2" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5" + resolved "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz" integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g== -ignore@^7.0.0: - version "7.0.5" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-7.0.5.tgz#4cb5f6cd7d4c7ab0365738c7aea888baa6d7efd9" - integrity sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg== - immer@^10.0.3: version "10.1.1" - resolved "https://registry.yarnpkg.com/immer/-/immer-10.1.1.tgz#206f344ea372d8ea176891545ee53ccc062db7bc" + resolved "https://registry.npmjs.org/immer/-/immer-10.1.1.tgz" integrity sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw== immutable@^4.3.7: version "4.3.7" - resolved "https://registry.yarnpkg.com/immutable/-/immutable-4.3.7.tgz#c70145fc90d89fb02021e65c84eb0226e4e5a381" + resolved "https://registry.npmjs.org/immutable/-/immutable-4.3.7.tgz" integrity sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw== import-fresh@^3.2.1: - version "3.3.1" - resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.1.tgz#9cecb56503c0ada1f2741dbbd6546e4b13b57ccf" - integrity sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ== + version "3.3.0" + resolved "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz" + integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== dependencies: parent-module "^1.0.0" resolve-from "^4.0.0" imurmurhash@^0.1.4: version "0.1.4" - resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + resolved "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz" integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== inherits@~2.0.3: version "2.0.4" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== internal-ip@^7.0.0: version "7.0.0" - resolved "https://registry.yarnpkg.com/internal-ip/-/internal-ip-7.0.0.tgz#5b1c6a9d7e188aa73a1b69717daf50c8d8ed774f" + resolved "https://registry.npmjs.org/internal-ip/-/internal-ip-7.0.0.tgz" integrity sha512-qE4TeD4brqC45Vq/+VASeMiS1KRyfBkR6HT2sh9pZVVCzSjPkaCEfKFU+dL0PRv7NHJtvoKN2r82G6wTfzorkw== dependencies: default-gateway "^6.0.3" @@ -2585,325 +2565,320 @@ internal-ip@^7.0.0: is-ip "^3.1.0" p-event "^4.2.0" -internal-slot@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.1.0.tgz#1eac91762947d2f7056bc838d93e13b2e9604961" - integrity sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw== +internal-slot@^1.0.7: + version "1.0.7" + resolved "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz" + integrity sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g== dependencies: es-errors "^1.3.0" - hasown "^2.0.2" - side-channel "^1.1.0" + hasown "^2.0.0" + side-channel "^1.0.4" ip-regex@^4.0.0: version "4.3.0" - resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-4.3.0.tgz#687275ab0f57fa76978ff8f4dddc8a23d5990db5" + resolved "https://registry.npmjs.org/ip-regex/-/ip-regex-4.3.0.tgz" integrity sha512-B9ZWJxHHOHUhUjCPrMpLD4xEq35bUTClHM1S6CBU5ixQnkZmwipwgc96vAd7AAGM9TGHvJR+Uss+/Ak6UphK+Q== ipaddr.js@^2.0.1: version "2.2.0" - resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-2.2.0.tgz#d33fa7bac284f4de7af949638c9d68157c6b92e8" + resolved "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.2.0.tgz" integrity sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA== -is-array-buffer@^3.0.4, is-array-buffer@^3.0.5: - version "3.0.5" - resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.5.tgz#65742e1e687bd2cc666253068fd8707fe4d44280" - integrity sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A== +is-array-buffer@^3.0.4: + version "3.0.4" + resolved "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz" + integrity sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw== dependencies: - call-bind "^1.0.8" - call-bound "^1.0.3" - get-intrinsic "^1.2.6" + call-bind "^1.0.2" + get-intrinsic "^1.2.1" is-arrayish@^0.2.1: version "0.2.1" - resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + resolved "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz" integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg== is-async-function@^2.0.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/is-async-function/-/is-async-function-2.1.1.tgz#3e69018c8e04e73b738793d020bfe884b9fd3523" - integrity sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ== + version "2.0.0" + resolved "https://registry.npmjs.org/is-async-function/-/is-async-function-2.0.0.tgz" + integrity sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA== dependencies: - async-function "^1.0.0" - call-bound "^1.0.3" - get-proto "^1.0.1" - has-tostringtag "^1.0.2" - safe-regex-test "^1.1.0" + has-tostringtag "^1.0.0" is-bigint@^1.1.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.1.0.tgz#dda7a3445df57a42583db4228682eba7c4170672" + resolved "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz" integrity sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ== dependencies: has-bigints "^1.0.2" -is-boolean-object@^1.2.1: - version "1.2.2" - resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.2.2.tgz#7067f47709809a393c71ff5bb3e135d8a9215d9e" - integrity sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A== +is-boolean-object@^1.2.0: + version "1.2.0" + resolved "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.0.tgz" + integrity sha512-kR5g0+dXf/+kXnqI+lu0URKYPKgICtHGGNCDSB10AaUFj3o/HkB3u7WfpRBJGFopxxY0oH3ux7ZsDjLtK7xqvw== dependencies: - call-bound "^1.0.3" + call-bind "^1.0.7" has-tostringtag "^1.0.2" -is-callable@^1.2.7: +is-callable@^1.1.3, is-callable@^1.2.7: version "1.2.7" - resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" + resolved "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz" integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== -is-core-module@^2.13.0, is-core-module@^2.16.0: +is-core-module@^2.13.0: + version "2.15.1" + resolved "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz" + integrity sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ== + dependencies: + hasown "^2.0.2" + +is-core-module@^2.16.0: version "2.16.1" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.16.1.tgz#2a98801a849f43e2add644fbb6bc6229b19a4ef4" + resolved "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz" integrity sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w== dependencies: hasown "^2.0.2" -is-data-view@^1.0.1, is-data-view@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-data-view/-/is-data-view-1.0.2.tgz#bae0a41b9688986c2188dda6657e56b8f9e63b8e" - integrity sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw== +is-data-view@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.1.tgz" + integrity sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w== dependencies: - call-bound "^1.0.2" - get-intrinsic "^1.2.6" is-typed-array "^1.1.13" -is-date-object@^1.0.5, is-date-object@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.1.0.tgz#ad85541996fc7aa8b2729701d27b7319f95d82f7" - integrity sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg== +is-date-object@^1.0.5: + version "1.0.5" + resolved "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz" + integrity sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ== dependencies: - call-bound "^1.0.2" - has-tostringtag "^1.0.2" + has-tostringtag "^1.0.0" is-extglob@^2.1.1: version "2.1.1" - resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + resolved "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz" integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== is-finalizationregistry@^1.1.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz#eefdcdc6c94ddd0674d9c85887bf93f944a97c90" - integrity sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg== + version "1.1.0" + resolved "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.0.tgz" + integrity sha512-qfMdqbAQEwBw78ZyReKnlA8ezmPdb9BemzIIip/JkjaZUhitfXDkkr+3QTboW0JrSXT1QWyYShpvnNHGZ4c4yA== dependencies: - call-bound "^1.0.3" + call-bind "^1.0.7" is-generator-function@^1.0.10: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.1.0.tgz#bf3eeda931201394f57b5dba2800f91a238309ca" - integrity sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ== + version "1.0.10" + resolved "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz" + integrity sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A== dependencies: - call-bound "^1.0.3" - get-proto "^1.0.0" - has-tostringtag "^1.0.2" - safe-regex-test "^1.1.0" + has-tostringtag "^1.0.0" is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3: version "4.0.3" - resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + resolved "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz" integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== dependencies: is-extglob "^2.1.1" is-ip@^3.1.0: version "3.1.0" - resolved "https://registry.yarnpkg.com/is-ip/-/is-ip-3.1.0.tgz#2ae5ddfafaf05cb8008a62093cf29734f657c5d8" + resolved "https://registry.npmjs.org/is-ip/-/is-ip-3.1.0.tgz" integrity sha512-35vd5necO7IitFPjd/YBeqwWnyDWbuLH9ZXQdMfDA8TEo7pv5X8yfrvVO3xbJbLUlERCMvf6X0hTUamQxCYJ9Q== dependencies: ip-regex "^4.0.0" is-map@^2.0.3: version "2.0.3" - resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.3.tgz#ede96b7fe1e270b3c4465e3a465658764926d62e" + resolved "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz" integrity sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw== is-negative-zero@^2.0.3: version "2.0.3" - resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.3.tgz#ced903a027aca6381b777a5743069d7376a49747" + resolved "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz" integrity sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw== -is-number-object@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.1.1.tgz#144b21e95a1bc148205dcc2814a9134ec41b2541" - integrity sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw== +is-number-object@^1.1.0: + version "1.1.0" + resolved "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.0.tgz" + integrity sha512-KVSZV0Dunv9DTPkhXwcZ3Q+tUc9TsaE1ZwX5J2WMvsSGS6Md8TFPun5uwh0yRdrNerI6vf/tbJxqSx4c1ZI1Lw== dependencies: - call-bound "^1.0.3" + call-bind "^1.0.7" has-tostringtag "^1.0.2" is-number@^7.0.0: version "7.0.0" - resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + resolved "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz" integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== is-plain-object@^2.0.4: version "2.0.4" - resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" + resolved "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz" integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== dependencies: isobject "^3.0.1" -is-regex@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.2.1.tgz#76d70a3ed10ef9be48eb577887d74205bf0cad22" - integrity sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g== +is-regex@^1.1.4: + version "1.2.0" + resolved "https://registry.npmjs.org/is-regex/-/is-regex-1.2.0.tgz" + integrity sha512-B6ohK4ZmoftlUe+uvenXSbPJFo6U37BH7oO1B3nQH8f/7h27N56s85MhUtbFJAziz5dcmuR3i8ovUl35zp8pFA== dependencies: - call-bound "^1.0.2" - gopd "^1.2.0" + call-bind "^1.0.7" + gopd "^1.1.0" has-tostringtag "^1.0.2" hasown "^2.0.2" is-set@^2.0.3: version "2.0.3" - resolved "https://registry.yarnpkg.com/is-set/-/is-set-2.0.3.tgz#8ab209ea424608141372ded6e0cb200ef1d9d01d" + resolved "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz" integrity sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg== -is-shared-array-buffer@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz#9b67844bd9b7f246ba0708c3a93e34269c774f6f" - integrity sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A== +is-shared-array-buffer@^1.0.2, is-shared-array-buffer@^1.0.3: + version "1.0.3" + resolved "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz" + integrity sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg== dependencies: - call-bound "^1.0.3" + call-bind "^1.0.7" is-stream@^2.0.0: version "2.0.1" - resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" + resolved "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz" integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== -is-string@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.1.1.tgz#92ea3f3d5c5b6e039ca8677e5ac8d07ea773cbb9" - integrity sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA== +is-string@^1.0.7, is-string@^1.1.0: + version "1.1.0" + resolved "https://registry.npmjs.org/is-string/-/is-string-1.1.0.tgz" + integrity sha512-PlfzajuF9vSo5wErv3MJAKD/nqf9ngAs1NFQYm16nUYFO2IzxJ2hcm+IOCg+EEopdykNNUhVq5cz35cAUxU8+g== dependencies: - call-bound "^1.0.3" + call-bind "^1.0.7" has-tostringtag "^1.0.2" -is-symbol@^1.0.4, is-symbol@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.1.1.tgz#f47761279f532e2b05a7024a7506dbbedacd0634" - integrity sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w== +is-symbol@^1.0.4, is-symbol@^1.1.0: + version "1.1.0" + resolved "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.0.tgz" + integrity sha512-qS8KkNNXUZ/I+nX6QT8ZS1/Yx0A444yhzdTKxCzKkNjQ9sHErBxJnJAgh+f5YhusYECEcjo4XcyH87hn6+ks0A== dependencies: - call-bound "^1.0.2" - has-symbols "^1.1.0" - safe-regex-test "^1.1.0" + call-bind "^1.0.7" + has-symbols "^1.0.3" + safe-regex-test "^1.0.3" -is-typed-array@^1.1.13, is-typed-array@^1.1.14, is-typed-array@^1.1.15: - version "1.1.15" - resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.15.tgz#4bfb4a45b61cee83a5a46fba778e4e8d59c0ce0b" - integrity sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ== +is-typed-array@^1.1.13: + version "1.1.13" + resolved "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz" + integrity sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw== dependencies: - which-typed-array "^1.1.16" + which-typed-array "^1.1.14" is-weakmap@^2.0.2: version "2.0.2" - resolved "https://registry.yarnpkg.com/is-weakmap/-/is-weakmap-2.0.2.tgz#bf72615d649dfe5f699079c54b83e47d1ae19cfd" + resolved "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz" integrity sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w== -is-weakref@^1.0.2, is-weakref@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.1.1.tgz#eea430182be8d64174bd96bffbc46f21bf3f9293" - integrity sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew== +is-weakref@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz" + integrity sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ== dependencies: - call-bound "^1.0.3" + call-bind "^1.0.2" is-weakset@^2.0.3: - version "2.0.4" - resolved "https://registry.yarnpkg.com/is-weakset/-/is-weakset-2.0.4.tgz#c9f5deb0bc1906c6d6f1027f284ddf459249daca" - integrity sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ== + version "2.0.3" + resolved "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.3.tgz" + integrity sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ== dependencies: - call-bound "^1.0.3" - get-intrinsic "^1.2.6" + call-bind "^1.0.7" + get-intrinsic "^1.2.4" isarray@^2.0.5: version "2.0.5" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723" + resolved "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz" integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw== isarray@~1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + resolved "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz" integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ== isexe@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + resolved "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz" integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== isobject@^3.0.1: version "3.0.1" - resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" + resolved "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz" integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg== -iterator.prototype@^1.1.4: - version "1.1.5" - resolved "https://registry.yarnpkg.com/iterator.prototype/-/iterator.prototype-1.1.5.tgz#12c959a29de32de0aa3bbbb801f4d777066dae39" - integrity sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g== +iterator.prototype@^1.1.3: + version "1.1.3" + resolved "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.3.tgz" + integrity sha512-FW5iMbeQ6rBGm/oKgzq2aW4KvAGpxPzYES8N4g4xNXUKpL1mclMvOe+76AcLDTvD+Ze+sOpVhgdAQEKF4L9iGQ== dependencies: - define-data-property "^1.1.4" - es-object-atoms "^1.0.0" - get-intrinsic "^1.2.6" - get-proto "^1.0.0" - has-symbols "^1.1.0" - set-function-name "^2.0.2" + define-properties "^1.2.1" + get-intrinsic "^1.2.1" + has-symbols "^1.0.3" + reflect.getprototypeof "^1.0.4" + set-function-name "^2.0.1" jdenticon@^3.3.0: version "3.3.0" - resolved "https://registry.yarnpkg.com/jdenticon/-/jdenticon-3.3.0.tgz#64bae9f9b3cf5c2a210e183648117afe3a89b367" + resolved "https://registry.npmjs.org/jdenticon/-/jdenticon-3.3.0.tgz" integrity sha512-DhuBRNRIybGPeAjMjdHbkIfiwZCCmf8ggu7C49jhp6aJ7DYsZfudnvnTY5/1vgUhrGA7JaDAx1WevnpjCPvaGg== dependencies: canvas-renderer "~2.2.0" joycon@^3.1.1: version "3.1.1" - resolved "https://registry.yarnpkg.com/joycon/-/joycon-3.1.1.tgz#bce8596d6ae808f8b68168f5fc69280996894f03" + resolved "https://registry.npmjs.org/joycon/-/joycon-3.1.1.tgz" integrity sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw== "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + resolved "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== js-yaml@^4.1.0: version "4.1.0" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" + resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz" integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== dependencies: argparse "^2.0.1" jsan@^3.1.14: version "3.1.14" - resolved "https://registry.yarnpkg.com/jsan/-/jsan-3.1.14.tgz#197fee2d260b85acacb049c1ffa41bd09fb1f213" + resolved "https://registry.npmjs.org/jsan/-/jsan-3.1.14.tgz" integrity sha512-wStfgOJqMv4QKktuH273f5fyi3D3vy2pHOiSDGPvpcS/q+wb/M7AK3vkCcaHbkZxDOlDU/lDJgccygKSG2OhtA== jsesc@^3.0.2: - version "3.1.0" - resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-3.1.0.tgz#74d335a234f67ed19907fdadfac7ccf9d409825d" - integrity sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA== + version "3.0.2" + resolved "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz" + integrity sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g== json-buffer@3.0.1: version "3.0.1" - resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" + resolved "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz" integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== json-parse-even-better-errors@^2.3.0: version "2.3.1" - resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" + resolved "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz" integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== json-schema-traverse@^0.4.1: version "0.4.1" - resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + resolved "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz" integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== json-stable-stringify-without-jsonify@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" + resolved "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz" integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== json5@^2.2.3: version "2.2.3" - resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" + resolved "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz" integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== jsonwebtoken@^9.0.0: version "9.0.2" - resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz#65ff91f4abef1784697d40952bb1998c504caaf3" + resolved "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz" integrity sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ== dependencies: jws "^3.2.2" @@ -2919,7 +2894,7 @@ jsonwebtoken@^9.0.0: "jsx-ast-utils@^2.4.1 || ^3.0.0": version "3.3.5" - resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz#4766bd05a8e2a11af222becd19e15575e52a853a" + resolved "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz" integrity sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ== dependencies: array-includes "^3.1.6" @@ -2928,17 +2903,17 @@ jsonwebtoken@^9.0.0: object.values "^1.1.6" jwa@^1.4.1: - version "1.4.2" - resolved "https://registry.yarnpkg.com/jwa/-/jwa-1.4.2.tgz#16011ac6db48de7b102777e57897901520eec7b9" - integrity sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw== + version "1.4.1" + resolved "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz" + integrity sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA== dependencies: - buffer-equal-constant-time "^1.0.1" + buffer-equal-constant-time "1.0.1" ecdsa-sig-formatter "1.0.11" safe-buffer "^5.0.1" jws@^3.2.2: version "3.2.2" - resolved "https://registry.yarnpkg.com/jws/-/jws-3.2.2.tgz#001099f3639468c9414000e99995fa52fb478304" + resolved "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz" integrity sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA== dependencies: jwa "^1.4.1" @@ -2946,19 +2921,19 @@ jws@^3.2.2: keyv@^4.5.4: version "4.5.4" - resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93" + resolved "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz" integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw== dependencies: json-buffer "3.0.1" kind-of@^6.0.2: version "6.0.3" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" + resolved "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz" integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== levn@^0.4.1: version "0.4.1" - resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" + resolved "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz" integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== dependencies: prelude-ls "^1.2.1" @@ -2966,110 +2941,105 @@ levn@^0.4.1: lines-and-columns@^1.1.6: version "1.2.4" - resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" + resolved "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz" integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== linked-list@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/linked-list/-/linked-list-2.1.0.tgz#fa7b63a6caf4b17862a1eb90d14ead4ee57649f2" + resolved "https://registry.npmjs.org/linked-list/-/linked-list-2.1.0.tgz" integrity sha512-0GK/ylO6e5cv1PCOIdTRHxOaCgQ+0jKwHt+cHzkiCAZlx0KM5Id1bBAPad6g2mkvBNp1pNdmG0cohFGfqjkv9A== locate-path@^6.0.0: version "6.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" + resolved "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz" integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== dependencies: p-locate "^5.0.0" lodash.includes@^4.3.0: version "4.3.0" - resolved "https://registry.yarnpkg.com/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f" + resolved "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz" integrity sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w== lodash.isboolean@^3.0.3: version "3.0.3" - resolved "https://registry.yarnpkg.com/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz#6c2e171db2a257cd96802fd43b01b20d5f5870f6" + resolved "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz" integrity sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg== lodash.isinteger@^4.0.4: version "4.0.4" - resolved "https://registry.yarnpkg.com/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz#619c0af3d03f8b04c31f5882840b77b11cd68343" + resolved "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz" integrity sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA== lodash.isnumber@^3.0.3: version "3.0.3" - resolved "https://registry.yarnpkg.com/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz#3ce76810c5928d03352301ac287317f11c0b1ffc" + resolved "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz" integrity sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw== lodash.isplainobject@^4.0.6: version "4.0.6" - resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" + resolved "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz" integrity sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA== lodash.isstring@^4.0.1: version "4.0.1" - resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451" + resolved "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz" integrity sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw== lodash.merge@^4.6.2: version "4.6.2" - resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" + resolved "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz" integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== lodash.once@^4.0.0: version "4.1.1" - resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" + resolved "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz" integrity sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg== lodash@^4.17.21: version "4.17.21" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== loose-envify@^1.4.0: version "1.4.0" - resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" + resolved "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz" integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== dependencies: js-tokens "^3.0.0 || ^4.0.0" loupe@^3.1.0, loupe@^3.1.2: - version "3.1.3" - resolved "https://registry.yarnpkg.com/loupe/-/loupe-3.1.3.tgz#042a8f7986d77f3d0f98ef7990a2b2fef18b0fd2" - integrity sha512-kkIp7XSkP78ZxJEsSxW3712C6teJVoeHHwgo9zJ380de7IYyJ2ISlxojcH2pC5OFLewESmnRi/+XCDIEEVyoug== + version "3.1.2" + resolved "https://registry.npmjs.org/loupe/-/loupe-3.1.2.tgz" + integrity sha512-23I4pFZHmAemUnz8WZXbYRSKYj801VDaNv9ETuMh7IrMc7VuVVSo+Z9iLE3ni30+U48iDWfi30d3twAXBYmnCg== lru-cache@^5.1.1: version "5.1.1" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" + resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz" integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== dependencies: yallist "^3.0.2" magic-string@^0.30.12: - version "0.30.17" - resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.17.tgz#450a449673d2460e5bbcfba9a61916a1714c7453" - integrity sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA== + version "0.30.14" + resolved "https://registry.npmjs.org/magic-string/-/magic-string-0.30.14.tgz" + integrity sha512-5c99P1WKTed11ZC0HMJOj6CDIue6F8ySu+bJL+85q1zBEIY8IklrJ1eiKC2NDRh3Ct3FcvmJPyQHb9erXMTJNw== dependencies: "@jridgewell/sourcemap-codec" "^1.5.0" -math-intrinsics@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz#a0dd74be81e2aa5c2f27e65ce283605ee4e2b7f9" - integrity sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g== - merge-stream@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" + resolved "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz" integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== merge2@^1.3.0: version "1.4.1" - resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" + resolved "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz" integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== -micromatch@^4.0.8: +micromatch@^4.0.4: version "4.0.8" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202" + resolved "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz" integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA== dependencies: braces "^3.0.3" @@ -3077,43 +3047,43 @@ micromatch@^4.0.8: mimic-fn@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" + resolved "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== minimatch@^3.1.2: version "3.1.2" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz" integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== dependencies: brace-expansion "^1.1.7" minimatch@^5.1.1: version "5.1.6" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96" + resolved "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz" integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g== dependencies: brace-expansion "^2.0.1" minimatch@^9.0.4: version "9.0.5" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.5.tgz#d74f9dd6b57d83d8e98cfb82133b03978bc929e5" + resolved "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz" integrity sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow== dependencies: brace-expansion "^2.0.1" minimist@^1.2.6: version "1.2.8" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" + resolved "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz" integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== ms@^2.1.1, ms@^2.1.3: version "2.1.3" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== multiaddr@^10.0.1: version "10.0.1" - resolved "https://registry.yarnpkg.com/multiaddr/-/multiaddr-10.0.1.tgz#0d15848871370860a4d266bb44d93b3dac5d90ef" + resolved "https://registry.npmjs.org/multiaddr/-/multiaddr-10.0.1.tgz" integrity sha512-G5upNcGzEGuTHkzxezPrrD6CaIHR9uo+7MwqhNVcXTs33IInon4y7nMiGxl2CY5hG7chvYQUQhz5V52/Qe3cbg== dependencies: dns-over-http-resolver "^1.2.3" @@ -3125,89 +3095,86 @@ multiaddr@^10.0.1: multiformats@^9.4.2, multiformats@^9.4.5: version "9.9.0" - resolved "https://registry.yarnpkg.com/multiformats/-/multiformats-9.9.0.tgz#c68354e7d21037a8f1f8833c8ccd68618e8f1d37" + resolved "https://registry.npmjs.org/multiformats/-/multiformats-9.9.0.tgz" integrity sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg== -nanoid@^3.3.11: - version "3.3.11" - resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.11.tgz#4f4f112cefbe303202f2199838128936266d185b" - integrity sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w== +nanoid@^3.3.7: + version "3.3.8" + resolved "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz" + integrity sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w== nanoid@^5.1.2: version "5.1.5" - resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-5.1.5.tgz#f7597f9d9054eb4da9548cdd53ca70f1790e87de" + resolved "https://registry.npmjs.org/nanoid/-/nanoid-5.1.5.tgz" integrity sha512-Ir/+ZpE9fDsNH0hQ3C68uyThDXzYcim2EqcZ8zn8Chtt1iylPT9xXJB0kPCnqzgcEGikO9RxSrh63MsmVCU7Fw== native-fetch@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/native-fetch/-/native-fetch-3.0.0.tgz#06ccdd70e79e171c365c75117959cf4fe14a09bb" + resolved "https://registry.npmjs.org/native-fetch/-/native-fetch-3.0.0.tgz" integrity sha512-G3Z7vx0IFb/FQ4JxvtqGABsOTIqRWvgQz6e+erkB+JJD6LrszQtMozEHI4EkmgZQvnGHrpLVzUWk7t4sJCIkVw== natural-compare@^1.4.0: version "1.4.0" - resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + resolved "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz" integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== -node-releases@^2.0.19: - version "2.0.19" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.19.tgz#9e445a52950951ec4d177d843af370b411caf314" - integrity sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw== +node-releases@^2.0.18: + version "2.0.18" + resolved "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz" + integrity sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g== notistack@^3.0.1: - version "3.0.2" - resolved "https://registry.yarnpkg.com/notistack/-/notistack-3.0.2.tgz#009799c3fccddeffac58565ba1657d27616dfabd" - integrity sha512-0R+/arLYbK5Hh7mEfR2adt0tyXJcCC9KkA2hc56FeWik2QN6Bm/S4uW+BjzDARsJth5u06nTjelSw/VSnB1YEA== + version "3.0.1" + resolved "https://registry.npmjs.org/notistack/-/notistack-3.0.1.tgz" + integrity sha512-ntVZXXgSQH5WYfyU+3HfcXuKaapzAJ8fBLQ/G618rn3yvSzEbnOB8ZSOwhX+dAORy/lw+GC2N061JA0+gYWTVA== dependencies: clsx "^1.1.0" goober "^2.0.33" npm-run-path@^4.0.1: version "4.0.1" - resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" + resolved "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz" integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== dependencies: path-key "^3.0.0" object-assign@^4.1.1: version "4.1.1" - resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + resolved "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz" integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== -object-inspect@^1.13.3, object-inspect@^1.13.4: - version "1.13.4" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.4.tgz#8375265e21bc20d0fa582c22e1b13485d6e00213" - integrity sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew== +object-inspect@^1.13.1, object-inspect@^1.13.3: + version "1.13.3" + resolved "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.3.tgz" + integrity sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA== object-keys@^1.1.1: version "1.1.1" - resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" + resolved "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz" integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== -object.assign@^4.1.4, object.assign@^4.1.7: - version "4.1.7" - resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.7.tgz#8c14ca1a424c6a561b0bb2a22f66f5049a945d3d" - integrity sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw== +object.assign@^4.1.4, object.assign@^4.1.5: + version "4.1.5" + resolved "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz" + integrity sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ== dependencies: - call-bind "^1.0.8" - call-bound "^1.0.3" + call-bind "^1.0.5" define-properties "^1.2.1" - es-object-atoms "^1.0.0" - has-symbols "^1.1.0" + has-symbols "^1.0.3" object-keys "^1.1.1" -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== +object.entries@^1.1.8: + version "1.1.8" + resolved "https://registry.npmjs.org/object.entries/-/object.entries-1.1.8.tgz" + integrity sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ== dependencies: - call-bind "^1.0.8" - call-bound "^1.0.4" + call-bind "^1.0.7" define-properties "^1.2.1" - es-object-atoms "^1.1.1" + es-object-atoms "^1.0.0" object.fromentries@^2.0.8: version "2.0.8" - resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.8.tgz#f7195d8a9b97bd95cbc1999ea939ecd1a2b00c65" + resolved "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz" integrity sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ== dependencies: call-bind "^1.0.7" @@ -3215,38 +3182,37 @@ object.fromentries@^2.0.8: es-abstract "^1.23.2" es-object-atoms "^1.0.0" -object.values@^1.1.6, object.values@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.2.1.tgz#deed520a50809ff7f75a7cfd4bc64c7a038c6216" - integrity sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA== +object.values@^1.1.6, object.values@^1.2.0: + version "1.2.0" + resolved "https://registry.npmjs.org/object.values/-/object.values-1.2.0.tgz" + integrity sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ== dependencies: - call-bind "^1.0.8" - call-bound "^1.0.3" + call-bind "^1.0.7" define-properties "^1.2.1" es-object-atoms "^1.0.0" on-exit-leak-free@^2.1.0: version "2.1.2" - resolved "https://registry.yarnpkg.com/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz#fed195c9ebddb7d9e4c3842f93f281ac8dadd3b8" + resolved "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz" integrity sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA== once@^1.3.1, once@^1.4.0: version "1.4.0" - resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + resolved "https://registry.npmjs.org/once/-/once-1.4.0.tgz" integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== dependencies: wrappy "1" onetime@^5.1.2: version "5.1.2" - resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" + resolved "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz" integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== dependencies: mimic-fn "^2.1.0" optionator@^0.9.3: version "0.9.4" - resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.4.tgz#7ea1c1a5d91d764fb282139c88fe11e182a3a734" + resolved "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz" integrity sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g== dependencies: deep-is "^0.1.3" @@ -3256,58 +3222,49 @@ optionator@^0.9.3: type-check "^0.4.0" word-wrap "^1.2.5" -own-keys@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/own-keys/-/own-keys-1.0.1.tgz#e4006910a2bf913585289676eebd6f390cf51358" - integrity sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg== - dependencies: - get-intrinsic "^1.2.6" - object-keys "^1.1.1" - safe-push-apply "^1.0.0" - p-event@^4.2.0: version "4.2.0" - resolved "https://registry.yarnpkg.com/p-event/-/p-event-4.2.0.tgz#af4b049c8acd91ae81083ebd1e6f5cae2044c1b5" + resolved "https://registry.npmjs.org/p-event/-/p-event-4.2.0.tgz" integrity sha512-KXatOjCRXXkSePPb1Nbi0p0m+gQAwdlbhi4wQKJPI1HsMQS9g+Sqp2o+QHziPr7eYJyOZet836KoHEVM1mwOrQ== dependencies: p-timeout "^3.1.0" p-finally@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" + resolved "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz" integrity sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow== p-limit@^3.0.2: version "3.1.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" + resolved "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz" integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== dependencies: yocto-queue "^0.1.0" p-locate@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" + resolved "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz" integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== dependencies: p-limit "^3.0.2" p-timeout@^3.1.0: version "3.2.0" - resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-3.2.0.tgz#c7e17abc971d2a7962ef83626b35d635acf23dfe" + resolved "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz" integrity sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg== dependencies: p-finally "^1.0.0" parent-module@^1.0.0: version "1.0.1" - resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" + resolved "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz" integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== dependencies: callsites "^3.0.0" parse-json@^5.0.0: version "5.2.0" - resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" + resolved "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz" integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== dependencies: "@babel/code-frame" "^7.0.0" @@ -3317,54 +3274,54 @@ parse-json@^5.0.0: path-exists@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + resolved "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz" integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== path-key@^3.0.0, path-key@^3.1.0: version "3.1.1" - resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + resolved "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz" integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== path-parse@^1.0.7: version "1.0.7" - resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + resolved "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== path-type@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" + resolved "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== pathe@^1.1.2: version "1.1.2" - resolved "https://registry.yarnpkg.com/pathe/-/pathe-1.1.2.tgz#6c4cb47a945692e48a1ddd6e4094d170516437ec" + resolved "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz" integrity sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ== pathval@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/pathval/-/pathval-2.0.0.tgz#7e2550b422601d4f6b8e26f1301bc8f15a741a25" + resolved "https://registry.npmjs.org/pathval/-/pathval-2.0.0.tgz" integrity sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA== -picocolors@^1.1.1: +picocolors@^1.0.0, picocolors@^1.1.0, picocolors@^1.1.1: version "1.1.1" - resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" + resolved "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz" integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== picomatch@^2.3.1: version "2.3.1" - resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + resolved "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== pino-abstract-transport@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/pino-abstract-transport/-/pino-abstract-transport-2.0.0.tgz#de241578406ac7b8a33ce0d77ae6e8a0b3b68a60" + resolved "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-2.0.0.tgz" integrity sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw== dependencies: split2 "^4.0.0" pino-pretty@^11.2.1: version "11.3.0" - resolved "https://registry.yarnpkg.com/pino-pretty/-/pino-pretty-11.3.0.tgz#390b3be044cf3d2e9192c7d19d44f6b690468f2e" + resolved "https://registry.npmjs.org/pino-pretty/-/pino-pretty-11.3.0.tgz" integrity sha512-oXwn7ICywaZPHmu3epHGU2oJX4nPmKvHvB/bwrJHlGcbEWaVcotkpyVHMKLKmiVryWYByNp0jpgAcXpFJDXJzA== dependencies: colorette "^2.0.7" @@ -3384,20 +3341,20 @@ pino-pretty@^11.2.1: pino-std-serializers@^7.0.0: version "7.0.0" - resolved "https://registry.yarnpkg.com/pino-std-serializers/-/pino-std-serializers-7.0.0.tgz#7c625038b13718dbbd84ab446bd673dc52259e3b" + resolved "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-7.0.0.tgz" integrity sha512-e906FRY0+tV27iq4juKzSYPbUj2do2X2JX4EzSca1631EB2QJQUqGbDuERal7LCtOpxl6x3+nvo9NPZcmjkiFA== pino@^9.2.0: - version "9.7.0" - resolved "https://registry.yarnpkg.com/pino/-/pino-9.7.0.tgz#ff7cd86eb3103ee620204dbd5ca6ffda8b53f645" - integrity sha512-vnMCM6xZTb1WDmLvtG2lE/2p+t9hDEIvTWJsu6FejkE62vB7gDhvzrpFR4Cw2to+9JNQxVnkAKVPA1KPB98vWg== + version "9.5.0" + resolved "https://registry.npmjs.org/pino/-/pino-9.5.0.tgz" + integrity sha512-xSEmD4pLnV54t0NOUN16yCl7RIB1c5UUOse5HSyEXtBp+FgFQyPeDutc+Q2ZO7/22vImV7VfEjH/1zV2QuqvYw== dependencies: atomic-sleep "^1.0.0" fast-redact "^3.1.1" on-exit-leak-free "^2.1.0" pino-abstract-transport "^2.0.0" pino-std-serializers "^7.0.0" - process-warning "^5.0.0" + process-warning "^4.0.0" quick-format-unescaped "^4.0.3" real-require "^0.2.0" safe-stable-stringify "^2.3.1" @@ -3405,42 +3362,42 @@ pino@^9.2.0: thread-stream "^3.0.0" possible-typed-array-names@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz#93e3582bc0e5426586d9d07b79ee40fc841de4ae" - integrity sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg== + version "1.0.0" + resolved "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz" + integrity sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q== postcss@^8.4.43: - version "8.5.5" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.5.5.tgz#04de7797f6911fb1c96550e96616d08681537ef3" - integrity sha512-d/jtm+rdNT8tpXuHY5MMtcbJFBkhXE6593XVR9UoGCH8jSFGci7jGvMGH5RYd5PBJW+00NZQt6gf7CbagJCrhg== + version "8.4.49" + resolved "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz" + integrity sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA== dependencies: - nanoid "^3.3.11" + nanoid "^3.3.7" picocolors "^1.1.1" source-map-js "^1.2.1" prelude-ls@^1.2.1: version "1.2.1" - resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" + resolved "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz" integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== process-nextick-args@~2.0.0: version "2.0.1" - resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" + resolved "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz" integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== -process-warning@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/process-warning/-/process-warning-5.0.0.tgz#566e0bf79d1dff30a72d8bbbe9e8ecefe8d378d7" - integrity sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA== +process-warning@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/process-warning/-/process-warning-4.0.0.tgz" + integrity sha512-/MyYDxttz7DfGMMHiysAsFE4qF+pQYAA8ziO/3NcRVrQ5fSk+Mns4QZA/oRPFzvcqNoVJXQNWNAsdwBXLUkQKw== process@^0.11.10: version "0.11.10" - resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" + resolved "https://registry.npmjs.org/process/-/process-0.11.10.tgz" integrity sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A== prop-types@^15.6.2, prop-types@^15.8.1: version "15.8.1" - resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" + resolved "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz" integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== dependencies: loose-envify "^1.4.0" @@ -3449,7 +3406,7 @@ prop-types@^15.6.2, prop-types@^15.8.1: pump@^3.0.0: version "3.0.2" - resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.2.tgz#836f3edd6bc2ee599256c924ffe0d88573ddcbf8" + resolved "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz" integrity sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw== dependencies: end-of-stream "^1.1.0" @@ -3457,44 +3414,44 @@ pump@^3.0.0: punycode@^2.1.0: version "2.3.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" + resolved "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz" integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== qr.js@0.0.0: version "0.0.0" - resolved "https://registry.yarnpkg.com/qr.js/-/qr.js-0.0.0.tgz#cace86386f59a0db8050fa90d9b6b0e88a1e364f" + resolved "https://registry.npmjs.org/qr.js/-/qr.js-0.0.0.tgz" integrity sha512-c4iYnWb+k2E+vYpRimHqSu575b1/wKl4XFeJGpFmrJQz5I88v9aY2czh7s0w36srfCM1sXgC/xpoJz5dJfq+OQ== queue-microtask@^1.2.2: version "1.2.3" - resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" + resolved "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz" integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== quick-format-unescaped@^4.0.3: version "4.0.4" - resolved "https://registry.yarnpkg.com/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz#93ef6dd8d3453cbc7970dd614fad4c5954d6b5a7" + resolved "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz" integrity sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg== react-dom@^19.1.0: version "19.1.0" - resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-19.1.0.tgz#133558deca37fa1d682708df8904b25186793623" + resolved "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz" integrity sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g== dependencies: scheduler "^0.26.0" react-is@^16.13.1, react-is@^16.7.0: version "16.13.1" - resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" + resolved "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== react-is@^19.1.0: version "19.1.0" - resolved "https://registry.yarnpkg.com/react-is/-/react-is-19.1.0.tgz#805bce321546b7e14c084989c77022351bbdd11b" + resolved "https://registry.npmjs.org/react-is/-/react-is-19.1.0.tgz" integrity sha512-Oe56aUPnkHyyDxxkvqtd7KkdQP5uIUfHxd5XTb3wE9d/kRnZLmKbDB0GWk919tdQ+mxxPtG6EAs6RMT6i1qtHg== react-qr-code@^2.0.15: version "2.0.15" - resolved "https://registry.yarnpkg.com/react-qr-code/-/react-qr-code-2.0.15.tgz#fbfc12952c504bcd64275647e9d1ea63251742ce" + resolved "https://registry.npmjs.org/react-qr-code/-/react-qr-code-2.0.15.tgz" integrity sha512-MkZcjEXqVKqXEIMVE0mbcGgDpkfSdd8zhuzXEl9QzYeNcw8Hq2oVIzDLWuZN2PQBwM5PWjc2S31K8Q1UbcFMfw== dependencies: prop-types "^15.8.1" @@ -3502,35 +3459,35 @@ react-qr-code@^2.0.15: react-redux@^9.2.0: version "9.2.0" - resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-9.2.0.tgz#96c3ab23fb9a3af2cb4654be4b51c989e32366f5" + resolved "https://registry.npmjs.org/react-redux/-/react-redux-9.2.0.tgz" integrity sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g== dependencies: "@types/use-sync-external-store" "^0.0.6" use-sync-external-store "^1.4.0" -react-refresh@^0.17.0: - version "0.17.0" - resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.17.0.tgz#b7e579c3657f23d04eccbe4ad2e58a8ed51e7e53" - integrity sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ== +react-refresh@^0.14.2: + version "0.14.2" + resolved "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz" + integrity sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA== react-router-dom@^7.6.1: - version "7.6.2" - resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-7.6.2.tgz#e97e386ab390b6503a2a7968124b7a3237fb10c7" - integrity sha512-Q8zb6VlTbdYKK5JJBLQEN06oTUa/RAbG/oQS1auK1I0TbJOXktqm+QENEVJU6QvWynlXPRBXI3fiOQcSEA78rA== + version "7.6.3" + resolved "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.6.3.tgz" + integrity sha512-DiWJm9qdUAmiJrVWaeJdu4TKu13+iB/8IEi0EW/XgaHCjW/vWGrwzup0GVvaMteuZjKnh5bEvJP/K0MDnzawHw== dependencies: - react-router "7.6.2" + react-router "7.6.3" -react-router@7.6.2: - version "7.6.2" - resolved "https://registry.yarnpkg.com/react-router/-/react-router-7.6.2.tgz#9f48b343bead7d0a94e28342fc4f9ae29131520e" - integrity sha512-U7Nv3y+bMimgWjhlT5CRdzHPu2/KVmqPwKUCChW8en5P3znxUqwlYFlbmyj8Rgp1SF6zs5X4+77kBVknkg6a0w== +react-router@7.6.3: + version "7.6.3" + resolved "https://registry.npmjs.org/react-router/-/react-router-7.6.3.tgz" + integrity sha512-zf45LZp5skDC6I3jDLXQUu0u26jtuP4lEGbc7BbdyxenBN1vJSTA18czM2D+h5qyMBuMrD+9uB+mU37HIoKGRA== dependencies: cookie "^1.0.1" set-cookie-parser "^2.6.0" react-transition-group@^4.4.5: version "4.4.5" - resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.4.5.tgz#e53d4e3f3344da8521489fbef8f2581d42becdd1" + resolved "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz" integrity sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g== dependencies: "@babel/runtime" "^7.5.5" @@ -3540,12 +3497,12 @@ react-transition-group@^4.4.5: react@^19.1.0: version "19.1.0" - resolved "https://registry.yarnpkg.com/react/-/react-19.1.0.tgz#926864b6c48da7627f004795d6cce50e90793b75" + resolved "https://registry.npmjs.org/react/-/react-19.1.0.tgz" integrity sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg== readable-stream@^2.3.5, readable-stream@~2.3.6: version "2.3.8" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b" + resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz" integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA== dependencies: core-util-is "~1.0.0" @@ -3557,9 +3514,9 @@ readable-stream@^2.3.5, readable-stream@~2.3.6: util-deprecate "~1.0.1" readable-stream@^4.0.0: - version "4.7.0" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-4.7.0.tgz#cedbd8a1146c13dfff8dab14068028d58c15ac91" - integrity sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg== + version "4.5.2" + resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.2.tgz" + integrity sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g== dependencies: abort-controller "^3.0.0" buffer "^6.0.3" @@ -3569,77 +3526,74 @@ readable-stream@^4.0.0: real-require@^0.2.0: version "0.2.0" - resolved "https://registry.yarnpkg.com/real-require/-/real-require-0.2.0.tgz#209632dea1810be2ae063a6ac084fee7e33fba78" + resolved "https://registry.npmjs.org/real-require/-/real-require-0.2.0.tgz" integrity sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg== receptacle@^1.3.2: version "1.3.2" - resolved "https://registry.yarnpkg.com/receptacle/-/receptacle-1.3.2.tgz#a7994c7efafc7a01d0e2041839dab6c4951360d2" + resolved "https://registry.npmjs.org/receptacle/-/receptacle-1.3.2.tgz" integrity sha512-HrsFvqZZheusncQRiEE7GatOAETrARKV/lnfYicIm8lbvp/JQOdADOfhjBd2DajvoszEyxSM6RlAAIZgEoeu/A== dependencies: ms "^2.1.1" redux-persist@^6.0.0: version "6.0.0" - resolved "https://registry.yarnpkg.com/redux-persist/-/redux-persist-6.0.0.tgz#b4d2972f9859597c130d40d4b146fecdab51b3a8" + resolved "https://registry.npmjs.org/redux-persist/-/redux-persist-6.0.0.tgz" integrity sha512-71LLMbUq2r02ng2We9S215LtPu3fY0KgaGE0k8WRgl6RkqxtGfl7HUozz1Dftwsb0D/5mZ8dwAaPbtnzfvbEwQ== redux-thunk@^3.1.0: version "3.1.0" - resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-3.1.0.tgz#94aa6e04977c30e14e892eae84978c1af6058ff3" + resolved "https://registry.npmjs.org/redux-thunk/-/redux-thunk-3.1.0.tgz" integrity sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw== redux@^4.0.0: version "4.2.1" - resolved "https://registry.yarnpkg.com/redux/-/redux-4.2.1.tgz#c08f4306826c49b5e9dc901dee0452ea8fce6197" + resolved "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz" integrity sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w== dependencies: "@babel/runtime" "^7.9.2" redux@^5.0.1: version "5.0.1" - resolved "https://registry.yarnpkg.com/redux/-/redux-5.0.1.tgz#97fa26881ce5746500125585d5642c77b6e9447b" + resolved "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz" integrity sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w== -reflect.getprototypeof@^1.0.6, reflect.getprototypeof@^1.0.9: - version "1.0.10" - resolved "https://registry.yarnpkg.com/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz#c629219e78a3316d8b604c765ef68996964e7bf9" - integrity sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw== +reflect.getprototypeof@^1.0.4, reflect.getprototypeof@^1.0.6: + version "1.0.7" + resolved "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.7.tgz" + integrity sha512-bMvFGIUKlc/eSfXNX+aZ+EL95/EgZzuwA0OBPTbZZDEJw/0AkentjMuM1oiRfwHrshqk4RzdgiTg5CcDalXN5g== dependencies: - call-bind "^1.0.8" + call-bind "^1.0.7" define-properties "^1.2.1" - es-abstract "^1.23.9" + es-abstract "^1.23.5" es-errors "^1.3.0" - es-object-atoms "^1.0.0" - get-intrinsic "^1.2.7" - get-proto "^1.0.1" - which-builtin-type "^1.2.1" + get-intrinsic "^1.2.4" + gopd "^1.0.1" + which-builtin-type "^1.1.4" -regexp.prototype.flags@^1.5.3, regexp.prototype.flags@^1.5.4: - version "1.5.4" - resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz#1ad6c62d44a259007e55b3970e00f746efbcaa19" - integrity sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA== +regexp.prototype.flags@^1.5.2, regexp.prototype.flags@^1.5.3: + version "1.5.3" + resolved "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.3.tgz" + integrity sha512-vqlC04+RQoFalODCbCumG2xIOvapzVMHwsyIGM/SIE8fRhFFsXeH8/QQ+s0T0kDAhKc4k30s73/0ydkHQz6HlQ== dependencies: - call-bind "^1.0.8" + call-bind "^1.0.7" define-properties "^1.2.1" es-errors "^1.3.0" - get-proto "^1.0.1" - gopd "^1.2.0" set-function-name "^2.0.2" -reselect@^5.1.0: +reselect@^5.1.0, reselect@^5.1.1: version "5.1.1" - resolved "https://registry.yarnpkg.com/reselect/-/reselect-5.1.1.tgz#c766b1eb5d558291e5e550298adb0becc24bb72e" + resolved "https://registry.npmjs.org/reselect/-/reselect-5.1.1.tgz" integrity sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w== resolve-from@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" + resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz" integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== resolve@^1.19.0: version "1.22.10" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.10.tgz#b663e83ffb09bbf2386944736baae803029b8b39" + resolved "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz" integrity sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w== dependencies: is-core-module "^2.16.0" @@ -3648,7 +3602,7 @@ resolve@^1.19.0: resolve@^2.0.0-next.5: version "2.0.0-next.5" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-2.0.0-next.5.tgz#6b0ec3107e671e52b68cd068ef327173b90dc03c" + resolved "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz" integrity sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA== dependencies: is-core-module "^2.13.0" @@ -3656,132 +3610,126 @@ resolve@^2.0.0-next.5: supports-preserve-symlinks-flag "^1.0.0" reusify@^1.0.4: - version "1.1.0" - resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.1.0.tgz#0fe13b9522e1473f51b558ee796e08f11f9b489f" - integrity sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw== + version "1.0.4" + resolved "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz" + integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== rn-host-detect@^1.2.0: version "1.2.0" - resolved "https://registry.yarnpkg.com/rn-host-detect/-/rn-host-detect-1.2.0.tgz#8b0396fc05631ec60c1cb8789e5070cdb04d0da0" + resolved "https://registry.npmjs.org/rn-host-detect/-/rn-host-detect-1.2.0.tgz" integrity sha512-btNg5kzHcjZZ7t7mvvV/4wNJ9e3MPgrWivkRgWURzXL0JJ0pwWlU4zrbmdlz3HHzHOxhBhHB4D+/dbMFfu4/4A== rollup@^4.20.0: - version "4.43.0" - resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.43.0.tgz#275c09119eb7eaf0c3dea040523b81ef43c57b8c" - integrity sha512-wdN2Kd3Twh8MAEOEJZsuxuLKCsBEo4PVNLK6tQWAn10VhsVewQLzcucMgLolRlhFybGxfclbPeEYBaP6RvUFGg== + version "4.28.0" + resolved "https://registry.npmjs.org/rollup/-/rollup-4.28.0.tgz" + integrity sha512-G9GOrmgWHBma4YfCcX8PjH0qhXSdH8B4HDE2o4/jaxj93S4DPCIDoLcXz99eWMji4hB29UFCEd7B2gwGJDR9cQ== dependencies: - "@types/estree" "1.0.7" + "@types/estree" "1.0.6" optionalDependencies: - "@rollup/rollup-android-arm-eabi" "4.43.0" - "@rollup/rollup-android-arm64" "4.43.0" - "@rollup/rollup-darwin-arm64" "4.43.0" - "@rollup/rollup-darwin-x64" "4.43.0" - "@rollup/rollup-freebsd-arm64" "4.43.0" - "@rollup/rollup-freebsd-x64" "4.43.0" - "@rollup/rollup-linux-arm-gnueabihf" "4.43.0" - "@rollup/rollup-linux-arm-musleabihf" "4.43.0" - "@rollup/rollup-linux-arm64-gnu" "4.43.0" - "@rollup/rollup-linux-arm64-musl" "4.43.0" - "@rollup/rollup-linux-loongarch64-gnu" "4.43.0" - "@rollup/rollup-linux-powerpc64le-gnu" "4.43.0" - "@rollup/rollup-linux-riscv64-gnu" "4.43.0" - "@rollup/rollup-linux-riscv64-musl" "4.43.0" - "@rollup/rollup-linux-s390x-gnu" "4.43.0" - "@rollup/rollup-linux-x64-gnu" "4.43.0" - "@rollup/rollup-linux-x64-musl" "4.43.0" - "@rollup/rollup-win32-arm64-msvc" "4.43.0" - "@rollup/rollup-win32-ia32-msvc" "4.43.0" - "@rollup/rollup-win32-x64-msvc" "4.43.0" + "@rollup/rollup-android-arm-eabi" "4.28.0" + "@rollup/rollup-android-arm64" "4.28.0" + "@rollup/rollup-darwin-arm64" "4.28.0" + "@rollup/rollup-darwin-x64" "4.28.0" + "@rollup/rollup-freebsd-arm64" "4.28.0" + "@rollup/rollup-freebsd-x64" "4.28.0" + "@rollup/rollup-linux-arm-gnueabihf" "4.28.0" + "@rollup/rollup-linux-arm-musleabihf" "4.28.0" + "@rollup/rollup-linux-arm64-gnu" "4.28.0" + "@rollup/rollup-linux-arm64-musl" "4.28.0" + "@rollup/rollup-linux-powerpc64le-gnu" "4.28.0" + "@rollup/rollup-linux-riscv64-gnu" "4.28.0" + "@rollup/rollup-linux-s390x-gnu" "4.28.0" + "@rollup/rollup-linux-x64-gnu" "4.28.0" + "@rollup/rollup-linux-x64-musl" "4.28.0" + "@rollup/rollup-win32-arm64-msvc" "4.28.0" + "@rollup/rollup-win32-ia32-msvc" "4.28.0" + "@rollup/rollup-win32-x64-msvc" "4.28.0" fsevents "~2.3.2" run-parallel@^1.1.9: version "1.2.0" - resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" + resolved "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz" integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== dependencies: queue-microtask "^1.2.2" -safe-array-concat@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/safe-array-concat/-/safe-array-concat-1.1.3.tgz#c9e54ec4f603b0bbb8e7e5007a5ee7aecd1538c3" - integrity sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q== +safe-array-concat@^1.1.2: + version "1.1.2" + resolved "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.2.tgz" + integrity sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q== dependencies: - call-bind "^1.0.8" - call-bound "^1.0.2" - get-intrinsic "^1.2.6" - has-symbols "^1.1.0" + call-bind "^1.0.7" + get-intrinsic "^1.2.4" + has-symbols "^1.0.3" isarray "^2.0.5" safe-buffer@^5.0.1, safe-buffer@^5.1.1, safe-buffer@~5.2.0: version "5.2.1" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.2" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== -safe-push-apply@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/safe-push-apply/-/safe-push-apply-1.0.0.tgz#01850e981c1602d398c85081f360e4e6d03d27f5" - integrity sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA== +safe-regex-test@^1.0.3: + version "1.0.3" + resolved "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz" + integrity sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw== dependencies: + call-bind "^1.0.6" es-errors "^1.3.0" - isarray "^2.0.5" - -safe-regex-test@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.1.0.tgz#7f87dfb67a3150782eaaf18583ff5d1711ac10c1" - integrity sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw== - dependencies: - call-bound "^1.0.2" - es-errors "^1.3.0" - is-regex "^1.2.1" + is-regex "^1.1.4" safe-stable-stringify@^2.3.1: version "2.5.0" - resolved "https://registry.yarnpkg.com/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz#4ca2f8e385f2831c432a719b108a3bf7af42a1dd" + resolved "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz" integrity sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA== sc-errors@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/sc-errors/-/sc-errors-3.0.0.tgz#df2e124f011be5fdd633e92d1de5ce6a6b4c1b85" + resolved "https://registry.npmjs.org/sc-errors/-/sc-errors-3.0.0.tgz" integrity sha512-rIqv2HTPb9DVreZwK/DV0ytRUqyw2DbDcoB9XTKjEQL7oMEQKsfPA8V8dGGr7p8ZYfmvaRIGZ4Wu5qwvs/hGDA== sc-formatter@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/sc-formatter/-/sc-formatter-4.0.0.tgz#2dda494a08e9d4cb069cbc9238a9f670adb3e7a6" + resolved "https://registry.npmjs.org/sc-formatter/-/sc-formatter-4.0.0.tgz" integrity sha512-MgUIvuca+90fBrCWY5LdlU9YUWjlkPFwdpvmomcwQEu3t2id/6YHdG2nhB6o7nhRp4ocfmcXQTh00r/tJtynSg== scheduler@^0.26.0: version "0.26.0" - resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.26.0.tgz#4ce8a8c2a2095f13ea11bf9a445be50c555d6337" + resolved "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz" integrity sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA== secure-json-parse@^2.4.0: version "2.7.0" - resolved "https://registry.yarnpkg.com/secure-json-parse/-/secure-json-parse-2.7.0.tgz#5a5f9cd6ae47df23dba3151edd06855d47e09862" + resolved "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-2.7.0.tgz" integrity sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw== semver@^6.3.1: version "6.3.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" + resolved "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz" integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== -semver@^7.5.4, semver@^7.6.0, semver@^7.6.2: - version "7.7.2" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.2.tgz#67d99fdcd35cec21e6f8b87a7fd515a33f982b58" - integrity sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA== +semver@^7.5.4: + version "7.7.1" + resolved "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz" + integrity sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA== + +semver@^7.6.0, semver@^7.6.2: + version "7.6.3" + resolved "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz" + integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A== set-cookie-parser@^2.6.0: version "2.7.1" - resolved "https://registry.yarnpkg.com/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz#3016f150072202dfbe90fadee053573cc89d2943" + resolved "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz" integrity sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ== -set-function-length@^1.2.2: +set-function-length@^1.2.1: version "1.2.2" - resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.2.tgz#aac72314198eaed975cf77b2c3b6b880695e5449" + resolved "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz" integrity sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg== dependencies: define-data-property "^1.1.4" @@ -3791,9 +3739,9 @@ set-function-length@^1.2.2: gopd "^1.0.1" has-property-descriptors "^1.0.2" -set-function-name@^2.0.2: +set-function-name@^2.0.1, set-function-name@^2.0.2: version "2.0.2" - resolved "https://registry.yarnpkg.com/set-function-name/-/set-function-name-2.0.2.tgz#16a705c5a0dc2f5e638ca96d8a8cd4e1c2b90985" + resolved "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz" integrity sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ== dependencies: define-data-property "^1.1.4" @@ -3801,87 +3749,48 @@ set-function-name@^2.0.2: functions-have-names "^1.2.3" has-property-descriptors "^1.0.2" -set-proto@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/set-proto/-/set-proto-1.0.0.tgz#0760dbcff30b2d7e801fd6e19983e56da337565e" - integrity sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw== - dependencies: - dunder-proto "^1.0.1" - es-errors "^1.3.0" - es-object-atoms "^1.0.0" - shallow-clone@^3.0.0: version "3.0.1" - resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3" + resolved "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz" integrity sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA== dependencies: kind-of "^6.0.2" shebang-command@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + resolved "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz" integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== dependencies: shebang-regex "^3.0.0" shebang-regex@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + resolved "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz" integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== -side-channel-list@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/side-channel-list/-/side-channel-list-1.0.0.tgz#10cb5984263115d3b7a0e336591e290a830af8ad" - integrity sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA== +side-channel@^1.0.4, side-channel@^1.0.6: + version "1.0.6" + resolved "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz" + integrity sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA== dependencies: + call-bind "^1.0.7" es-errors "^1.3.0" - object-inspect "^1.13.3" - -side-channel-map@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/side-channel-map/-/side-channel-map-1.0.1.tgz#d6bb6b37902c6fef5174e5f533fab4c732a26f42" - integrity sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA== - dependencies: - call-bound "^1.0.2" - es-errors "^1.3.0" - get-intrinsic "^1.2.5" - object-inspect "^1.13.3" - -side-channel-weakmap@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz#11dda19d5368e40ce9ec2bdc1fb0ecbc0790ecea" - integrity sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A== - dependencies: - call-bound "^1.0.2" - es-errors "^1.3.0" - get-intrinsic "^1.2.5" - object-inspect "^1.13.3" - side-channel-map "^1.0.1" - -side-channel@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.1.0.tgz#c3fcff9c4da932784873335ec9765fa94ff66bc9" - integrity sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw== - dependencies: - es-errors "^1.3.0" - object-inspect "^1.13.3" - side-channel-list "^1.0.0" - side-channel-map "^1.0.1" - side-channel-weakmap "^1.0.2" + get-intrinsic "^1.2.4" + object-inspect "^1.13.1" siginfo@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/siginfo/-/siginfo-2.0.0.tgz#32e76c70b79724e3bb567cb9d543eb858ccfaf30" + resolved "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz" integrity sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g== signal-exit@^3.0.3: version "3.0.7" - resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" + resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== socketcluster-client@^19.2.3: version "19.2.3" - resolved "https://registry.yarnpkg.com/socketcluster-client/-/socketcluster-client-19.2.3.tgz#89d8f6a215f8b6469ab3d93d1d4d0d9abf30c747" + resolved "https://registry.npmjs.org/socketcluster-client/-/socketcluster-client-19.2.3.tgz" integrity sha512-kYHBTH+P0UXnHQQxTVK9//rSAgETWSaVe8A4wlDpTQPqzpTWn2bq2ARaiLgXx8WouKaS9XcOLDRQc58e2fFscg== dependencies: ag-auth "^2.1.0" @@ -3900,105 +3809,92 @@ socketcluster-client@^19.2.3: sonic-boom@^4.0.1: version "4.2.0" - resolved "https://registry.yarnpkg.com/sonic-boom/-/sonic-boom-4.2.0.tgz#e59a525f831210fa4ef1896428338641ac1c124d" + resolved "https://registry.npmjs.org/sonic-boom/-/sonic-boom-4.2.0.tgz" integrity sha512-INb7TM37/mAcsGmc9hyyI6+QR3rR1zVRu36B0NeGXKnOOLiZOfER5SA+N7X7k3yUYRzLWafduTDvJAfDswwEww== dependencies: atomic-sleep "^1.0.0" 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" + resolved "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz" integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA== source-map@^0.5.7: version "0.5.7" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" + resolved "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz" integrity sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ== split2@^4.0.0: version "4.2.0" - resolved "https://registry.yarnpkg.com/split2/-/split2-4.2.0.tgz#c9c5920904d148bab0b9f67145f245a86aadbfa4" + resolved "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz" integrity sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg== stackback@0.0.2: version "0.0.2" - resolved "https://registry.yarnpkg.com/stackback/-/stackback-0.0.2.tgz#1ac8a0d9483848d1695e418b6d031a3c3ce68e3b" + resolved "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz" integrity sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw== std-env@^3.8.0: - version "3.9.0" - resolved "https://registry.yarnpkg.com/std-env/-/std-env-3.9.0.tgz#1a6f7243b339dca4c9fd55e1c7504c77ef23e8f1" - integrity sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw== - -stop-iteration-iterator@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz#f481ff70a548f6124d0312c3aa14cbfa7aa542ad" - integrity sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ== - dependencies: - es-errors "^1.3.0" - internal-slot "^1.1.0" + version "3.8.0" + resolved "https://registry.npmjs.org/std-env/-/std-env-3.8.0.tgz" + integrity sha512-Bc3YwwCB+OzldMxOXJIIvC6cPRWr/LxOp48CdQTOkPyk/t4JWWJbrilwBd7RJzKV8QW7tJkcgAmeuLLJugl5/w== stream-demux@^10.0.1: version "10.0.1" - resolved "https://registry.yarnpkg.com/stream-demux/-/stream-demux-10.0.1.tgz#204b65fb8973c87cea65119e99622405b3dbcc10" + resolved "https://registry.npmjs.org/stream-demux/-/stream-demux-10.0.1.tgz" integrity sha512-QjTYLJWpZxZ6uL5R1JzgOzjvao8zDx78ec+uOjHNeVc/9TuasYLldoVrYARZeT1xI1hFYuiKf13IM8b4wamhHg== dependencies: consumable-stream "^3.0.0" writable-consumable-stream "^4.1.0" -string.prototype.matchall@^4.0.12: - version "4.0.12" - resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz#6c88740e49ad4956b1332a911e949583a275d4c0" - integrity sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA== +string.prototype.matchall@^4.0.11: + version "4.0.11" + resolved "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.11.tgz" + integrity sha512-NUdh0aDavY2og7IbBPenWqR9exH+E26Sv8e0/eTe1tltDGZL+GtBkDAnnyBtmekfK6/Dq3MkcGtzXFEd1LQrtg== dependencies: - call-bind "^1.0.8" - call-bound "^1.0.3" + call-bind "^1.0.7" define-properties "^1.2.1" - es-abstract "^1.23.6" + es-abstract "^1.23.2" es-errors "^1.3.0" es-object-atoms "^1.0.0" - get-intrinsic "^1.2.6" - gopd "^1.2.0" - has-symbols "^1.1.0" - internal-slot "^1.1.0" - regexp.prototype.flags "^1.5.3" + get-intrinsic "^1.2.4" + gopd "^1.0.1" + has-symbols "^1.0.3" + internal-slot "^1.0.7" + regexp.prototype.flags "^1.5.2" set-function-name "^2.0.2" - side-channel "^1.1.0" + side-channel "^1.0.6" string.prototype.repeat@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz#e90872ee0308b29435aa26275f6e1b762daee01a" + resolved "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz" integrity sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w== dependencies: define-properties "^1.1.3" es-abstract "^1.17.5" -string.prototype.trim@^1.2.10: - version "1.2.10" - resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz#40b2dd5ee94c959b4dcfb1d65ce72e90da480c81" - integrity sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA== +string.prototype.trim@^1.2.9: + version "1.2.9" + resolved "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz" + integrity sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw== dependencies: - call-bind "^1.0.8" - call-bound "^1.0.2" - define-data-property "^1.1.4" + call-bind "^1.0.7" define-properties "^1.2.1" - es-abstract "^1.23.5" + es-abstract "^1.23.0" es-object-atoms "^1.0.0" - has-property-descriptors "^1.0.2" -string.prototype.trimend@^1.0.9: - version "1.0.9" - resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz#62e2731272cd285041b36596054e9f66569b6942" - integrity sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ== +string.prototype.trimend@^1.0.8: + version "1.0.8" + resolved "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz" + integrity sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ== dependencies: - call-bind "^1.0.8" - call-bound "^1.0.2" + call-bind "^1.0.7" define-properties "^1.2.1" es-object-atoms "^1.0.0" string.prototype.trimstart@^1.0.8: version "1.0.8" - resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz#7ee834dda8c7c17eff3118472bb35bfedaa34dde" + resolved "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz" integrity sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg== dependencies: call-bind "^1.0.7" @@ -4007,55 +3903,55 @@ string.prototype.trimstart@^1.0.8: string_decoder@^1.3.0: version "1.3.0" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz" integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== dependencies: safe-buffer "~5.2.0" string_decoder@~1.1.1: version "1.1.1" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz" integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== dependencies: safe-buffer "~5.1.0" strip-final-newline@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" + resolved "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz" integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== strip-json-comments@^3.1.1: version "3.1.1" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + resolved "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== stylis@4.2.0: version "4.2.0" - resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.2.0.tgz#79daee0208964c8fe695a42fcffcac633a211a51" + resolved "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz" integrity sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw== supports-color@^7.1.0: version "7.2.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + resolved "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz" integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== dependencies: has-flag "^4.0.0" supports-preserve-symlinks-flag@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" + resolved "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz" integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== thread-stream@^3.0.0: version "3.1.0" - resolved "https://registry.yarnpkg.com/thread-stream/-/thread-stream-3.1.0.tgz#4b2ef252a7c215064507d4ef70c05a5e2d34c4f1" + resolved "https://registry.npmjs.org/thread-stream/-/thread-stream-3.1.0.tgz" integrity sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A== dependencies: real-require "^0.2.0" through2@^2.0.3: version "2.0.5" - resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd" + resolved "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz" integrity sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ== dependencies: readable-stream "~2.3.6" @@ -4063,89 +3959,89 @@ through2@^2.0.3: tinybench@^2.9.0: version "2.9.0" - resolved "https://registry.yarnpkg.com/tinybench/-/tinybench-2.9.0.tgz#103c9f8ba6d7237a47ab6dd1dcff77251863426b" + resolved "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz" integrity sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg== tinyexec@^0.3.1: - version "0.3.2" - resolved "https://registry.yarnpkg.com/tinyexec/-/tinyexec-0.3.2.tgz#941794e657a85e496577995c6eef66f53f42b3d2" - integrity sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA== + version "0.3.1" + resolved "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.1.tgz" + integrity sha512-WiCJLEECkO18gwqIp6+hJg0//p23HXp4S+gGtAKu3mI2F2/sXC4FvHvXvB0zJVVaTPhx1/tOwdbRsa1sOBIKqQ== tinypool@^1.0.1: - version "1.1.0" - resolved "https://registry.yarnpkg.com/tinypool/-/tinypool-1.1.0.tgz#4252913ec76ef8f728f2524e2118f3bef9cf23f4" - integrity sha512-7CotroY9a8DKsKprEy/a14aCCm8jYVmR7aFy4fpkZM8sdpNJbKkixuNjgM50yCmip2ezc8z4N7k3oe2+rfRJCQ== + version "1.0.2" + resolved "https://registry.npmjs.org/tinypool/-/tinypool-1.0.2.tgz" + integrity sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA== tinyrainbow@^1.2.0: version "1.2.0" - resolved "https://registry.yarnpkg.com/tinyrainbow/-/tinyrainbow-1.2.0.tgz#5c57d2fc0fb3d1afd78465c33ca885d04f02abb5" + resolved "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-1.2.0.tgz" integrity sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ== tinyspy@^3.0.2: version "3.0.2" - resolved "https://registry.yarnpkg.com/tinyspy/-/tinyspy-3.0.2.tgz#86dd3cf3d737b15adcf17d7887c84a75201df20a" + resolved "https://registry.npmjs.org/tinyspy/-/tinyspy-3.0.2.tgz" integrity sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q== to-regex-range@^5.0.1: version "5.0.1" - resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + resolved "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz" integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== dependencies: is-number "^7.0.0" -ts-api-utils@^2.1.0: - 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-api-utils@^1.3.0: + version "1.4.3" + resolved "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.3.tgz" + integrity sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw== tsconfck@^3.0.3: - version "3.1.6" - resolved "https://registry.yarnpkg.com/tsconfck/-/tsconfck-3.1.6.tgz#da1f0b10d82237ac23422374b3fce1edb23c3ead" - integrity sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w== + version "3.1.4" + resolved "https://registry.npmjs.org/tsconfck/-/tsconfck-3.1.4.tgz" + integrity sha512-kdqWFGVJqe+KGYvlSO9NIaWn9jT1Ny4oKVzAJsKii5eoE9snzTJzL4+MMVOMn+fikWGFmKEylcXL710V/kIPJQ== type-check@^0.4.0, type-check@~0.4.0: version "0.4.0" - resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" + resolved "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz" integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== dependencies: prelude-ls "^1.2.1" -typed-array-buffer@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz#a72395450a4869ec033fd549371b47af3a2ee536" - integrity sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw== +typed-array-buffer@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz" + integrity sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ== dependencies: - call-bound "^1.0.3" + call-bind "^1.0.7" es-errors "^1.3.0" - is-typed-array "^1.1.14" + is-typed-array "^1.1.13" -typed-array-byte-length@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz#8407a04f7d78684f3d252aa1a143d2b77b4160ce" - integrity sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg== +typed-array-byte-length@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz" + integrity sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw== dependencies: - call-bind "^1.0.8" + call-bind "^1.0.7" for-each "^0.3.3" - gopd "^1.2.0" - has-proto "^1.2.0" - is-typed-array "^1.1.14" + gopd "^1.0.1" + has-proto "^1.0.3" + is-typed-array "^1.1.13" -typed-array-byte-offset@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz#ae3698b8ec91a8ab945016108aef00d5bff12355" - integrity sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ== +typed-array-byte-offset@^1.0.2: + version "1.0.3" + resolved "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.3.tgz" + integrity sha512-GsvTyUHTriq6o/bHcTd0vM7OQ9JEdlvluu9YISaA7+KzDzPaIzEeDFNkTfhdE3MYcNhNi0vq/LlegYgIs5yPAw== dependencies: available-typed-arrays "^1.0.7" - call-bind "^1.0.8" + call-bind "^1.0.7" for-each "^0.3.3" - gopd "^1.2.0" - has-proto "^1.2.0" - is-typed-array "^1.1.15" - reflect.getprototypeof "^1.0.9" + gopd "^1.0.1" + has-proto "^1.0.3" + is-typed-array "^1.1.13" + reflect.getprototypeof "^1.0.6" -typed-array-length@^1.0.7: +typed-array-length@^1.0.6: version "1.0.7" - resolved "https://registry.yarnpkg.com/typed-array-length/-/typed-array-length-1.0.7.tgz#ee4deff984b64be1e118b0de8c9c877d5ce73d3d" + resolved "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz" integrity sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg== dependencies: call-bind "^1.0.7" @@ -4156,89 +4052,84 @@ typed-array-length@^1.0.7: reflect.getprototypeof "^1.0.6" typescript-eslint@^8.1.0: - version "8.34.0" - resolved "https://registry.yarnpkg.com/typescript-eslint/-/typescript-eslint-8.34.0.tgz#5bc7e405cd0ed5d6f28d86017519700b77ca1298" - integrity sha512-MRpfN7uYjTrTGigFCt8sRyNqJFhjN0WwZecldaqhWm+wy0gaRt8Edb/3cuUy0zdq2opJWT6iXINKAtewnDOltQ== + version "8.17.0" + resolved "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.17.0.tgz" + integrity sha512-409VXvFd/f1br1DCbuKNFqQpXICoTB+V51afcwG1pn1a3Cp92MqAUges3YjwEdQ0cMUoCIodjVDAYzyD8h3SYA== dependencies: - "@typescript-eslint/eslint-plugin" "8.34.0" - "@typescript-eslint/parser" "8.34.0" - "@typescript-eslint/utils" "8.34.0" + "@typescript-eslint/eslint-plugin" "8.17.0" + "@typescript-eslint/parser" "8.17.0" + "@typescript-eslint/utils" "8.17.0" typescript@^5.2.2: - version "5.8.3" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.8.3.tgz#92f8a3e5e3cf497356f4178c34cd65a7f5e8440e" - integrity sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ== + version "5.7.2" + resolved "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz" + integrity sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg== uint8arrays@^3.0.0: version "3.1.1" - resolved "https://registry.yarnpkg.com/uint8arrays/-/uint8arrays-3.1.1.tgz#2d8762acce159ccd9936057572dade9459f65ae0" + resolved "https://registry.npmjs.org/uint8arrays/-/uint8arrays-3.1.1.tgz" integrity sha512-+QJa8QRnbdXVpHYjLoTpJIdCTiw9Ir62nocClWuXIq2JIh4Uta0cQsTSpFL678p2CN8B+XSApwcU+pQEqVpKWg== dependencies: multiformats "^9.4.2" -unbox-primitive@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.1.0.tgz#8d9d2c9edeea8460c7f35033a88867944934d1e2" - integrity sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw== +unbox-primitive@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz" + integrity sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw== dependencies: - call-bound "^1.0.3" + call-bind "^1.0.2" has-bigints "^1.0.2" - has-symbols "^1.1.0" - which-boxed-primitive "^1.1.1" + has-symbols "^1.0.3" + which-boxed-primitive "^1.0.2" undici-types@~6.21.0: version "6.21.0" - resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.21.0.tgz#691d00af3909be93a7faa13be61b3a5b50ef12cb" + resolved "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz" integrity sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ== -undici-types@~7.8.0: - version "7.8.0" - resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-7.8.0.tgz#de00b85b710c54122e44fbfd911f8d70174cd294" - integrity sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw== - -update-browserslist-db@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz#348377dd245216f9e7060ff50b15a1b740b75420" - integrity sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw== +update-browserslist-db@^1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz" + integrity sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A== dependencies: escalade "^3.2.0" - picocolors "^1.1.1" + picocolors "^1.1.0" uri-js@^4.2.2: version "4.4.1" - resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" + resolved "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz" integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== dependencies: punycode "^2.1.0" use-sync-external-store@^1.4.0: version "1.5.0" - resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.5.0.tgz#55122e2a3edd2a6c106174c27485e0fd59bcfca0" + resolved "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.5.0.tgz" integrity sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A== util-deprecate@~1.0.1: version "1.0.2" - resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== uuid@^10.0.0: version "10.0.0" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-10.0.0.tgz#5a95aa454e6e002725c79055fd42aaba30ca6294" + resolved "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz" integrity sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ== uuid@^8.3.2: version "8.3.2" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" + resolved "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== varint@^6.0.0: version "6.0.0" - resolved "https://registry.yarnpkg.com/varint/-/varint-6.0.0.tgz#9881eb0ce8feaea6512439d19ddf84bf551661d0" + resolved "https://registry.npmjs.org/varint/-/varint-6.0.0.tgz" integrity sha512-cXEIW6cfr15lFv563k4GuVuW/fiwjknytD37jIOLSdSWuOI6WnO/oKwmP2FQTU2l01LP8/M5TSAJpzUaGe3uWg== vinyl-buffer@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/vinyl-buffer/-/vinyl-buffer-1.0.1.tgz#96c1a3479b8c5392542c612029013b5b27f88bbf" + resolved "https://registry.npmjs.org/vinyl-buffer/-/vinyl-buffer-1.0.1.tgz" integrity sha512-LRBE2/g3C1hSHL2k/FynSZcVTRhEw8sb08oKGt/0hukZXwrh2m8nfy+r5yLhGEk7eFFuclhyIuPct/Bxlxk6rg== dependencies: bl "^1.2.1" @@ -4246,13 +4137,13 @@ vinyl-buffer@^1.0.1: virtua@^0.33.2: version "0.33.7" - resolved "https://registry.yarnpkg.com/virtua/-/virtua-0.33.7.tgz#bd46d7d31f257886e6245347354fb4e80e27441f" + resolved "https://registry.npmjs.org/virtua/-/virtua-0.33.7.tgz" integrity sha512-IepZaMD/oeEh/ymTqokeQGLrMuRV25+lizPegxVIhOwqX+dEeV9ml1P57Eosok4qiZaeBeQIbIkF9QZrT+EeRQ== -vite-node@2.1.9: - version "2.1.9" - resolved "https://registry.yarnpkg.com/vite-node/-/vite-node-2.1.9.tgz#549710f76a643f1c39ef34bdb5493a944e4f895f" - integrity sha512-AM9aQ/IPrW/6ENLQg3AGY4K1N2TGZdR5e4gu/MmmR2xR3Ll1+dib+nook92g4TV3PXVyeyxdWwtaCAiUL0hMxA== +vite-node@2.1.8: + version "2.1.8" + resolved "https://registry.npmjs.org/vite-node/-/vite-node-2.1.8.tgz" + integrity sha512-uPAwSr57kYjAUux+8E2j0q0Fxpn8M9VoyfGiRI8Kfktz9NcYMCenwY5RnZxnF1WTu3TGiYipirIzacLL3VVGFg== dependencies: cac "^6.7.14" debug "^4.3.7" @@ -4261,24 +4152,24 @@ vite-node@2.1.9: vite "^5.0.0" vite-plugin-top-level-await@^1.4.4: - version "1.5.0" - resolved "https://registry.yarnpkg.com/vite-plugin-top-level-await/-/vite-plugin-top-level-await-1.5.0.tgz#e3f76302921152bf29d1658f169d168f8937e78b" - integrity sha512-r/DtuvHrSqUVk23XpG2cl8gjt1aATMG5cjExXL1BUTcSNab6CzkcPua9BPEc9fuTP5UpwClCxUe3+dNGL0yrgQ== + version "1.4.4" + resolved "https://registry.npmjs.org/vite-plugin-top-level-await/-/vite-plugin-top-level-await-1.4.4.tgz" + integrity sha512-QyxQbvcMkgt+kDb12m2P8Ed35Sp6nXP+l8ptGrnHV9zgYDUpraO0CPdlqLSeBqvY2DToR52nutDG7mIHuysdiw== dependencies: "@rollup/plugin-virtual" "^3.0.2" - "@swc/core" "^1.10.16" + "@swc/core" "^1.7.0" uuid "^10.0.0" vite-plugin-watch@^0.3.1: version "0.3.1" - resolved "https://registry.yarnpkg.com/vite-plugin-watch/-/vite-plugin-watch-0.3.1.tgz#5000f7ded6eb1c42e9483d6ea3d812061ab8188f" + resolved "https://registry.npmjs.org/vite-plugin-watch/-/vite-plugin-watch-0.3.1.tgz" integrity sha512-tmLJ5tqSqXY7wSXoM8+huOgbictUG6SKLh/tZ6LAY51KPSAnPBr0dYwyxPPPQm+JgIIBbKSyNnPHpW11ad+qlw== dependencies: minimatch "^5.1.1" vite-tsconfig-paths@^4.3.2: version "4.3.2" - resolved "https://registry.yarnpkg.com/vite-tsconfig-paths/-/vite-tsconfig-paths-4.3.2.tgz#321f02e4b736a90ff62f9086467faf4e2da857a9" + resolved "https://registry.npmjs.org/vite-tsconfig-paths/-/vite-tsconfig-paths-4.3.2.tgz" integrity sha512-0Vd/a6po6Q+86rPlntHye7F31zA2URZMbH8M3saAZ/xR9QoGN/L21bxEGfXdWmFdNkqPpRdxFT7nmNe12e9/uA== dependencies: debug "^4.1.1" @@ -4286,9 +4177,9 @@ vite-tsconfig-paths@^4.3.2: tsconfck "^3.0.3" vite@^5.0.0, vite@^5.3.1: - version "5.4.19" - resolved "https://registry.yarnpkg.com/vite/-/vite-5.4.19.tgz#20efd060410044b3ed555049418a5e7d1998f959" - integrity sha512-qO3aKv3HoQC8QKiNSTuUM1l9o/XX3+c+VTgLHbJWHZGeTPVAg2XwazI9UWzoxjIJCGCV2zU60uqMzjeLZuULqA== + version "5.4.11" + resolved "https://registry.npmjs.org/vite/-/vite-5.4.11.tgz" + integrity sha512-c7jFQRklXua0mTzneGW9QVyxFjUgwcihC4bXEtujIo2ouWCe1Ajt/amn2PCxYnhYfd5k09JX3SB7OYWFKYqj8Q== dependencies: esbuild "^0.21.3" postcss "^8.4.43" @@ -4297,17 +4188,17 @@ vite@^5.0.0, vite@^5.3.1: fsevents "~2.3.3" vitest@^2.1.1: - version "2.1.9" - resolved "https://registry.yarnpkg.com/vitest/-/vitest-2.1.9.tgz#7d01ffd07a553a51c87170b5e80fea3da7fb41e7" - integrity sha512-MSmPM9REYqDGBI8439mA4mWhV5sKmDlBKWIYbA3lRb2PTHACE0mgKwA8yQ2xq9vxDTuk4iPrECBAEW2aoFXY0Q== + version "2.1.8" + resolved "https://registry.npmjs.org/vitest/-/vitest-2.1.8.tgz" + integrity sha512-1vBKTZskHw/aosXqQUlVWWlGUxSJR8YtiyZDJAFeW2kPAeX6S3Sool0mjspO+kXLuxVWlEDDowBAeqeAQefqLQ== dependencies: - "@vitest/expect" "2.1.9" - "@vitest/mocker" "2.1.9" - "@vitest/pretty-format" "^2.1.9" - "@vitest/runner" "2.1.9" - "@vitest/snapshot" "2.1.9" - "@vitest/spy" "2.1.9" - "@vitest/utils" "2.1.9" + "@vitest/expect" "2.1.8" + "@vitest/mocker" "2.1.8" + "@vitest/pretty-format" "^2.1.8" + "@vitest/runner" "2.1.8" + "@vitest/snapshot" "2.1.8" + "@vitest/spy" "2.1.8" + "@vitest/utils" "2.1.8" chai "^5.1.2" debug "^4.3.7" expect-type "^1.1.0" @@ -4319,42 +4210,42 @@ vitest@^2.1.1: tinypool "^1.0.1" tinyrainbow "^1.2.0" vite "^5.0.0" - vite-node "2.1.9" + vite-node "2.1.8" why-is-node-running "^2.3.0" -which-boxed-primitive@^1.1.0, which-boxed-primitive@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz#d76ec27df7fa165f18d5808374a5fe23c29b176e" - integrity sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA== +which-boxed-primitive@^1.0.2: + version "1.1.0" + resolved "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.0.tgz" + integrity sha512-Ei7Miu/AXe2JJ4iNF5j/UphAgRoma4trE6PtisM09bPygb3egMH3YLW/befsWb1A1AxvNSFidOFTB18XtnIIng== dependencies: is-bigint "^1.1.0" - is-boolean-object "^1.2.1" - is-number-object "^1.1.1" - is-string "^1.1.1" - is-symbol "^1.1.1" + is-boolean-object "^1.2.0" + is-number-object "^1.1.0" + is-string "^1.1.0" + is-symbol "^1.1.0" -which-builtin-type@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/which-builtin-type/-/which-builtin-type-1.2.1.tgz#89183da1b4907ab089a6b02029cc5d8d6574270e" - integrity sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q== +which-builtin-type@^1.1.4: + version "1.2.0" + resolved "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.0.tgz" + integrity sha512-I+qLGQ/vucCby4tf5HsLmGueEla4ZhwTBSqaooS+Y0BuxN4Cp+okmGuV+8mXZ84KDI9BA+oklo+RzKg0ONdSUA== dependencies: - call-bound "^1.0.2" + call-bind "^1.0.7" function.prototype.name "^1.1.6" has-tostringtag "^1.0.2" is-async-function "^2.0.0" - is-date-object "^1.1.0" + is-date-object "^1.0.5" is-finalizationregistry "^1.1.0" is-generator-function "^1.0.10" - is-regex "^1.2.1" + is-regex "^1.1.4" is-weakref "^1.0.2" isarray "^2.0.5" - which-boxed-primitive "^1.1.0" + which-boxed-primitive "^1.0.2" which-collection "^1.0.2" - which-typed-array "^1.1.16" + which-typed-array "^1.1.15" which-collection@^1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/which-collection/-/which-collection-1.0.2.tgz#627ef76243920a107e7ce8e96191debe4b16c2a0" + resolved "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz" integrity sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw== dependencies: is-map "^2.0.3" @@ -4362,29 +4253,27 @@ which-collection@^1.0.2: is-weakmap "^2.0.2" is-weakset "^2.0.3" -which-typed-array@^1.1.16, which-typed-array@^1.1.19: - version "1.1.19" - resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.19.tgz#df03842e870b6b88e117524a4b364b6fc689f956" - integrity sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw== +which-typed-array@^1.1.14, which-typed-array@^1.1.15: + version "1.1.16" + resolved "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.16.tgz" + integrity sha512-g+N+GAWiRj66DngFwHvISJd+ITsyphZvD1vChfVg6cEdnzy53GzB3oy0fUNlvhz7H7+MiqhYr26qxQShCpKTTQ== dependencies: available-typed-arrays "^1.0.7" - call-bind "^1.0.8" - call-bound "^1.0.4" - for-each "^0.3.5" - get-proto "^1.0.1" - gopd "^1.2.0" + call-bind "^1.0.7" + for-each "^0.3.3" + gopd "^1.0.1" has-tostringtag "^1.0.2" which@^2.0.1: version "2.0.2" - resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + resolved "https://registry.npmjs.org/which/-/which-2.0.2.tgz" integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== dependencies: isexe "^2.0.0" why-is-node-running@^2.3.0: version "2.3.0" - resolved "https://registry.yarnpkg.com/why-is-node-running/-/why-is-node-running-2.3.0.tgz#a3f69a97107f494b3cdc3bdddd883a7d65cebf04" + resolved "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz" integrity sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w== dependencies: siginfo "^2.0.0" @@ -4392,42 +4281,42 @@ why-is-node-running@^2.3.0: word-wrap@^1.2.5: version "1.2.5" - resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34" + resolved "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz" integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA== wrappy@1: version "1.0.2" - resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== writable-consumable-stream@^4.1.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/writable-consumable-stream/-/writable-consumable-stream-4.2.0.tgz#731cb8bc7c16d5e120adfaddd7d41c52179934d7" - integrity sha512-A2g0/Xaq/I2DQlYofh7nvKaJYZ0v4UOKuNLePG/G1ylx7p8e904jMTKhS+cdHNO1OulZpP2ModXs37EkG6tqSQ== + version "4.1.0" + resolved "https://registry.npmjs.org/writable-consumable-stream/-/writable-consumable-stream-4.1.0.tgz" + integrity sha512-4cjCPd4Ayfbix0qqPCzMbnPPZKRh/cKeNCj05unybP3/sRkRAOxh7rSwbhxs3YB6G4/Z2p/2FRBEIQcTeB4jyw== dependencies: consumable-stream "^3.0.0" ws@^8.18.0: - version "8.18.2" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.2.tgz#42738b2be57ced85f46154320aabb51ab003705a" - integrity sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ== + version "8.18.1" + resolved "https://registry.npmjs.org/ws/-/ws-8.18.1.tgz" + integrity sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w== xtend@~4.0.1: version "4.0.2" - resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" + resolved "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz" integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== yallist@^3.0.2: version "3.1.1" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" + resolved "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz" integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== yaml@^1.10.0: version "1.10.2" - resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" + resolved "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz" integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== yocto-queue@^0.1.0: version "0.1.0" - resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" + resolved "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz" integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index f526989c..c56785ef 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -21,7 +21,6 @@ rustls = { version = "0.23.26", default-features = false, features = ["ring"] } serde = { workspace = true } serde_json = { workspace = true } swap = { path = "../swap", features = [ "tauri" ] } -sysinfo = "=0.32.1" tauri = { version = "^2.0.0", features = [ "config-json5" ] } tauri-plugin-clipboard-manager = "^2.0.0" tauri-plugin-dialog = "2.2.2" diff --git a/src-tauri/capabilities/default.json b/src-tauri/capabilities/default.json index 637cf252..8181f151 100644 --- a/src-tauri/capabilities/default.json +++ b/src-tauri/capabilities/default.json @@ -13,6 +13,8 @@ "cli:allow-cli-matches", "updater:default", "process:allow-restart", - "opener:default" + "opener:default", + "dialog:allow-open", + "dialog:default" ] } diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 389ac3c3..343fa7da 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -1,4 +1,3 @@ -use anyhow::Context as AnyhowContext; use std::collections::HashMap; use std::io::Write; use std::result::Result; @@ -10,9 +9,12 @@ use swap::cli::{ BalanceArgs, BuyXmrArgs, CancelAndRefundArgs, CheckElectrumNodeArgs, CheckElectrumNodeResponse, CheckMoneroNodeArgs, CheckMoneroNodeResponse, CheckSeedArgs, CheckSeedResponse, ExportBitcoinWalletArgs, GetCurrentSwapArgs, GetDataDirArgs, - GetHistoryArgs, GetLogsArgs, GetMoneroAddressesArgs, GetPendingApprovalsResponse, - GetSwapInfoArgs, GetSwapInfosAllArgs, ListSellersArgs, MoneroRecoveryArgs, RedactArgs, - ResolveApprovalArgs, ResumeSwapArgs, SuspendCurrentSwapArgs, WithdrawBtcArgs, + GetHistoryArgs, GetLogsArgs, GetMoneroAddressesArgs, GetMoneroBalanceArgs, + GetMoneroHistoryArgs, GetMoneroMainAddressArgs, GetMoneroSyncProgressArgs, + GetPendingApprovalsResponse, GetRestoreHeightArgs, GetSwapInfoArgs, + GetSwapInfosAllArgs, ListSellersArgs, MoneroRecoveryArgs, RedactArgs, + RejectApprovalArgs, RejectApprovalResponse, ResolveApprovalArgs, ResumeSwapArgs, + SendMoneroArgs, SetRestoreHeightArgs, SuspendCurrentSwapArgs, WithdrawBtcArgs, }, tauri_bindings::{TauriContextStatusEvent, TauriEmitter, TauriHandle, TauriSettings}, Context, ContextBuilder, @@ -64,11 +66,11 @@ macro_rules! tauri_command { ($fn_name:ident, $request_name:ident) => { #[tauri::command] async fn $fn_name( - context: tauri::State<'_, RwLock>, + state: tauri::State<'_, State>, args: $request_name, ) -> Result<<$request_name as swap::cli::api::request::Request>::Response, String> { // Throw error if context is not available - let context = context.read().await.try_get_context()?; + let context = state.try_get_context()?; <$request_name as swap::cli::api::request::Request>::request(args, context) .await @@ -78,10 +80,10 @@ macro_rules! tauri_command { ($fn_name:ident, $request_name:ident, no_args) => { #[tauri::command] async fn $fn_name( - context: tauri::State<'_, RwLock>, + state: tauri::State<'_, State>, ) -> Result<<$request_name as swap::cli::api::request::Request>::Response, String> { // Throw error if context is not available - let context = context.read().await.try_get_context()?; + let context = state.try_get_context()?; <$request_name as swap::cli::api::request::Request>::request($request_name {}, context) .await @@ -92,7 +94,7 @@ macro_rules! tauri_command { /// Represents the shared Tauri state. It is accessed by Tauri commands struct State { - pub context: Option>, + pub context: RwLock>>, pub handle: TauriHandle, } @@ -100,22 +102,17 @@ impl State { /// Creates a new State instance with no Context fn new(handle: TauriHandle) -> Self { Self { - context: None, + context: RwLock::new(None), handle, } } - /// Sets the context for the application state - /// This is typically called after the Context has been initialized - /// in the setup function - fn set_context(&mut self, context: impl Into>>) { - self.context = context.into(); - } - /// Attempts to retrieve the context /// Returns an error if the context is not available fn try_get_context(&self) -> Result, String> { self.context + .try_read() + .map_err(|_| "Context is being modified".to_string())? .clone() .ok_or("Context not available".to_string()) } @@ -149,8 +146,8 @@ fn setup(app: &mut tauri::App) -> Result<(), Box> { // We need to set a value for the Tauri state right at the start // If we don't do this, Tauri commands will panic at runtime if no value is present let handle = TauriHandle::new(app_handle.clone()); - let state = RwLock::new(State::new(handle)); - app_handle.manage::>(state); + let state = State::new(handle); + app_handle.manage::(state); Ok(()) } @@ -203,8 +200,16 @@ pub fn run() { resolve_approval_request, redact, save_txt_files, + get_monero_history, + get_monero_main_address, + get_monero_balance, + send_monero, + get_monero_sync_progress, check_seed, get_pending_approvals, + set_monero_restore_height, + reject_approval_request, + get_restore_height ]) .setup(setup) .build(tauri::generate_context!()) @@ -215,18 +220,17 @@ pub fn run() { // This is necessary to among other things stop the monero-wallet-rpc process // If the application is forcibly closed, this may not be called. // TODO: fix that - let context = app.state::>().inner().try_read(); + let state = app.state::(); + let context_to_cleanup = if let Ok(context_lock) = state.context.try_read() { + context_lock.clone() + } else { + println!("Failed to acquire lock on context"); + None + }; - match context { - Ok(context) => { - if let Some(context) = context.context.as_ref() { - if let Err(err) = context.cleanup() { - println!("Cleanup failed {}", err); - } - } - } - Err(err) => { - println!("Failed to acquire lock on context: {}", err); + if let Some(context) = context_to_cleanup { + if let Err(err) = context.cleanup() { + println!("Cleanup failed {}", err); } } } @@ -246,6 +250,7 @@ tauri_command!(get_logs, GetLogsArgs); tauri_command!(list_sellers, ListSellersArgs); tauri_command!(cancel_and_refund, CancelAndRefundArgs); tauri_command!(redact, RedactArgs); +tauri_command!(send_monero, SendMoneroArgs); // These commands require no arguments tauri_command!(get_wallet_descriptor, ExportBitcoinWalletArgs, no_args); @@ -254,19 +259,25 @@ tauri_command!(get_swap_info, GetSwapInfoArgs); tauri_command!(get_swap_infos_all, GetSwapInfosAllArgs, no_args); tauri_command!(get_history, GetHistoryArgs, no_args); tauri_command!(get_monero_addresses, GetMoneroAddressesArgs, no_args); +tauri_command!(get_monero_history, GetMoneroHistoryArgs, no_args); tauri_command!(get_current_swap, GetCurrentSwapArgs, no_args); +tauri_command!(set_monero_restore_height, SetRestoreHeightArgs); +tauri_command!(get_restore_height, GetRestoreHeightArgs, no_args); +tauri_command!(get_monero_main_address, GetMoneroMainAddressArgs, no_args); +tauri_command!(get_monero_balance, GetMoneroBalanceArgs, no_args); +tauri_command!(get_monero_sync_progress, GetMoneroSyncProgressArgs, no_args); /// Here we define Tauri commands whose implementation is not delegated to the Request trait #[tauri::command] -async fn is_context_available(context: tauri::State<'_, RwLock>) -> Result { +async fn is_context_available(state: tauri::State<'_, State>) -> Result { // TODO: Here we should return more information about status of the context (e.g. initializing, failed) - Ok(context.read().await.try_get_context().is_ok()) + Ok(state.try_get_context().is_ok()) } #[tauri::command] async fn check_monero_node( args: CheckMoneroNodeArgs, - _: tauri::State<'_, RwLock>, + _: tauri::State<'_, State>, ) -> Result { args.request().await.to_string_result() } @@ -274,7 +285,7 @@ async fn check_monero_node( #[tauri::command] async fn check_electrum_node( args: CheckElectrumNodeArgs, - _: tauri::State<'_, RwLock>, + _: tauri::State<'_, State>, ) -> Result { args.request().await.to_string_result() } @@ -282,7 +293,7 @@ async fn check_electrum_node( #[tauri::command] async fn check_seed( args: CheckSeedArgs, - _: tauri::State<'_, RwLock>, + _: tauri::State<'_, State>, ) -> Result { args.request().await.to_string_result() } @@ -291,10 +302,7 @@ async fn check_seed( // This is independent of the context to ensure the user can open the directory even if the context cannot // be initialized (for troubleshooting purposes) #[tauri::command] -async fn get_data_dir( - args: GetDataDirArgs, - _: tauri::State<'_, RwLock>, -) -> Result { +async fn get_data_dir(args: GetDataDirArgs, _: tauri::State<'_, State>) -> Result { Ok(data::data_dir_from(None, args.is_testnet) .to_string_result()? .to_string_lossy() @@ -349,26 +357,46 @@ async fn save_txt_files( #[tauri::command] async fn resolve_approval_request( args: ResolveApprovalArgs, - state: tauri::State<'_, RwLock>, + state: tauri::State<'_, State>, ) -> Result<(), String> { - println!("Resolving approval request"); - let lock = state.read().await; + let request_id = args + .request_id + .parse() + .map_err(|e| format!("Invalid request ID '{}': {}", args.request_id, e))?; - lock.handle - .resolve_approval(args.request_id.parse().unwrap(), args.accept) + state + .handle + .resolve_approval(request_id, args.accept) .await .to_string_result()?; Ok(()) } +#[tauri::command] +async fn reject_approval_request( + args: RejectApprovalArgs, + state: tauri::State<'_, State>, +) -> Result { + let request_id = args + .request_id + .parse() + .map_err(|e| format!("Invalid request ID '{}': {}", args.request_id, e))?; + + state + .handle + .reject_approval(request_id) + .await + .to_string_result()?; + + Ok(RejectApprovalResponse { success: true }) +} + #[tauri::command] async fn get_pending_approvals( - state: tauri::State<'_, RwLock>, + state: tauri::State<'_, State>, ) -> Result { let approvals = state - .read() - .await .handle .get_pending_approvals() .await @@ -382,41 +410,21 @@ async fn get_pending_approvals( async fn initialize_context( settings: TauriSettings, testnet: bool, - state: tauri::State<'_, RwLock>, + state: tauri::State<'_, State>, ) -> Result<(), String> { - // When the app crashes, the monero-wallet-rpc process may not be killed - // This can lead to issues when the app is restarted - // because the monero-wallet-rpc has a lock on the wallet - // this will prevent the newly spawned instance from opening the wallet - // To fix this, we kill any running monero-wallet-rpc processes - let sys = sysinfo::System::new_with_specifics( - sysinfo::RefreshKind::new().with_processes(sysinfo::ProcessRefreshKind::new()), - ); + // Lock at the beginning - fail immediately if already locked + let mut context_lock = state + .context + .try_write() + .map_err(|_| "Context is already being initialized".to_string())?; - for (pid, process) in sys.processes() { - if process - .name() - .to_string_lossy() - .starts_with("monero-wallet-rpc") - { - #[cfg(not(debug_assertions))] - { - println!("Killing monero-wallet-rpc process with pid: {}", pid); - process.kill(); - } - - #[cfg(debug_assertions)] - println!("Would kill monero-wallet-rpc process with pid: {}", pid); - } + // Fail if the context is already initialized + if context_lock.is_some() { + return Err("Context is already initialized".to_string()); } - // Get app handle and create a Tauri handle - let tauri_handle = state - .try_read() - .context("Context is already being initialized") - .to_string_result()? - .handle - .clone(); + // Get tauri handle from the state + let tauri_handle = state.handle.clone(); // Notify frontend that the context is being initialized tauri_handle.emit_context_init_progress_event(TauriContextStatusEvent::Initializing); @@ -436,11 +444,7 @@ async fn initialize_context( match context_result { Ok(context_instance) => { - state - .try_write() - .context("Context is already being initialized") - .to_string_result()? - .set_context(Arc::new(context_instance)); + *context_lock = Some(Arc::new(context_instance)); tracing::info!("Context initialized"); diff --git a/swap-env/Cargo.toml b/swap-env/Cargo.toml index b29f55a6..edf4ac02 100644 --- a/swap-env/Cargo.toml +++ b/swap-env/Cargo.toml @@ -4,21 +4,21 @@ version = "0.1.0" edition = "2024" [dependencies] -serde = { workspace = true } -time = "0.3" -swap-serde = { path = "../swap-serde" } -bitcoin = { workspace = true } -monero = { workspace = true } anyhow = { workspace = true } -tracing = { workspace = true } -swap-fs = { path = "../swap-fs" } -dialoguer = "0.11" +bitcoin = { workspace = true } config = { version = "0.14", default-features = false, features = ["toml"] } +dialoguer = "0.11" libp2p = { workspace = true, features = ["serde"] } -thiserror = { workspace = true } +monero = { workspace = true } rust_decimal = { workspace = true } -url = { workspace = true } +serde = { workspace = true } +swap-fs = { path = "../swap-fs" } +swap-serde = { path = "../swap-serde" } +thiserror = { workspace = true } +time = "0.3" toml = "0.8" +tracing = { workspace = true } +url = { workspace = true } [lints] workspace = true diff --git a/swap-env/src/config.rs b/swap-env/src/config.rs index 765ac8c7..1759610b 100644 --- a/swap-env/src/config.rs +++ b/swap-env/src/config.rs @@ -1,5 +1,4 @@ use crate::env::{Mainnet, Testnet}; -use swap_fs::{ensure_directory_exists, system_config_dir, system_data_dir}; use anyhow::{bail, Context, Result}; use config::ConfigError; use dialoguer::theme::ColorfulTheme; @@ -12,6 +11,7 @@ use std::ffi::OsStr; use std::fs; use std::path::{Path, PathBuf}; use std::str::FromStr; +use swap_fs::{ensure_directory_exists, system_config_dir, system_data_dir}; use url::Url; pub trait GetDefaults { @@ -130,9 +130,15 @@ pub struct Data { pub struct Network { #[serde(deserialize_with = "swap_serde::libp2p::multiaddresses::deserialize")] pub listen: Vec, - #[serde(default, deserialize_with = "swap_serde::libp2p::multiaddresses::deserialize")] + #[serde( + default, + deserialize_with = "swap_serde::libp2p::multiaddresses::deserialize" + )] pub rendezvous_point: Vec, - #[serde(default, deserialize_with = "swap_serde::libp2p::multiaddresses::deserialize")] + #[serde( + default, + deserialize_with = "swap_serde::libp2p::multiaddresses::deserialize" + )] pub external_addresses: Vec, } @@ -259,7 +265,7 @@ pub fn query_user_for_initial_config(testnet: bool) -> Result { .to_string(), ) .interact_text()?; - let data_dir = data_dir.as_str().parse()?; + let data_dir: PathBuf = data_dir.as_str().parse()?; let target_block = Input::with_theme(&ColorfulTheme::default()) .with_prompt("How fast should your Bitcoin transactions be confirmed? Your transaction fee will be calculated based on this target. Hit return to use default") @@ -373,7 +379,7 @@ pub fn query_user_for_initial_config(testnet: bool) -> Result { println!(); Ok(Config { - data: Data { dir: data_dir }, + data: Data { dir: data_dir}, network: Network { listen: listen_addresses, rendezvous_point: rendezvous_points, // keeping the singular key name for backcompat diff --git a/swap-env/src/env.rs b/swap-env/src/env.rs index f33299fb..6158274c 100644 --- a/swap-env/src/env.rs +++ b/swap-env/src/env.rs @@ -1,8 +1,8 @@ +use crate::config::Config as AsbConfig; use serde::Serialize; use std::cmp::max; use std::time::Duration; use time::ext::NumericalStdDuration; -use crate::config::Config as AsbConfig; #[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize)] pub struct Config { @@ -136,8 +136,6 @@ pub fn new(is_testnet: bool, asb_config: &AsbConfig) -> Config { } } - - #[cfg(test)] mod tests { use super::*; diff --git a/swap-env/src/lib.rs b/swap-env/src/lib.rs index 07a0493a..47cf15af 100644 --- a/swap-env/src/lib.rs +++ b/swap-env/src/lib.rs @@ -1,2 +1,2 @@ +pub mod config; pub mod env; -pub mod config; \ No newline at end of file diff --git a/swap-fs/src/lib.rs b/swap-fs/src/lib.rs index 217d4bf1..d699abcd 100644 --- a/swap-fs/src/lib.rs +++ b/swap-fs/src/lib.rs @@ -20,6 +20,17 @@ pub fn system_data_dir() -> Result { .context("Could not generate default system data-dir dir path") } +pub fn system_data_dir_eigenwallet(testnet: bool) -> Result { + let application_directory = match testnet { + true => "eigenwallet-testnet", + false => "eigenwallet", + }; + + ProjectDirs::from("", "", application_directory) + .map(|proj_dirs| proj_dirs.data_dir().to_path_buf()) + .context("Could not generate default system data-dir dir path") +} + pub fn ensure_directory_exists(file: &Path) -> Result<(), std::io::Error> { if let Some(path) = file.parent() { if !path.exists() { diff --git a/swap-serde/Cargo.toml b/swap-serde/Cargo.toml index a3a54a3d..29e82f3e 100644 --- a/swap-serde/Cargo.toml +++ b/swap-serde/Cargo.toml @@ -4,15 +4,15 @@ version = "0.1.0" edition = "2024" [dependencies] -serde = { workspace = true } -monero = { workspace = true } -bitcoin = { workspace = true } -libp2p = { workspace = true } -serde_json = { workspace = true } -url = { workspace = true } -hex = { workspace = true } anyhow = { workspace = true } +bitcoin = { workspace = true } +hex = { workspace = true } +libp2p = { workspace = true } +monero = { workspace = true } +serde = { workspace = true } +serde_json = { workspace = true } thiserror = { workspace = true } +url = { workspace = true } [lints] workspace = true diff --git a/swap-serde/src/bitcoin.rs b/swap-serde/src/bitcoin.rs index a2d1bbd0..4d94279b 100644 --- a/swap-serde/src/bitcoin.rs +++ b/swap-serde/src/bitcoin.rs @@ -1,5 +1,5 @@ +use bitcoin::Network; use serde::{Deserialize, Serialize}; -use bitcoin::{Network}; #[derive(Serialize, Deserialize)] #[serde(remote = "Network")] @@ -73,4 +73,4 @@ pub mod address_serde { } } } -} \ No newline at end of file +} diff --git a/swap-serde/src/electrum.rs b/swap-serde/src/electrum.rs index ba7d0832..9c36f12a 100644 --- a/swap-serde/src/electrum.rs +++ b/swap-serde/src/electrum.rs @@ -37,4 +37,4 @@ pub mod urls { )), } } -} \ No newline at end of file +} diff --git a/swap-serde/src/lib.rs b/swap-serde/src/lib.rs index 868b9a4f..af5df995 100644 --- a/swap-serde/src/lib.rs +++ b/swap-serde/src/lib.rs @@ -1,4 +1,4 @@ -pub mod monero; pub mod bitcoin; +pub mod electrum; pub mod libp2p; -pub mod electrum; \ No newline at end of file +pub mod monero; diff --git a/swap-serde/src/libp2p.rs b/swap-serde/src/libp2p.rs index e75fc6aa..23f4cf2a 100644 --- a/swap-serde/src/libp2p.rs +++ b/swap-serde/src/libp2p.rs @@ -3,7 +3,7 @@ pub mod multiaddresses { use serde::de::Unexpected; use serde::{de, Deserialize, Deserializer}; use serde_json::Value; - + pub fn deserialize<'de, D>(deserializer: D) -> Result, D::Error> where D: Deserializer<'de>, @@ -24,17 +24,17 @@ pub mod multiaddresses { .map(|v| { if let Value::String(s) = v { s.trim().parse().map_err(de::Error::custom) - } else { - Err(de::Error::custom("expected a string")) - } - }) - .collect(); - Ok(list?) - } - value => Err(de::Error::invalid_type( - Unexpected::Other(&value.to_string()), - &"a string or array", - )), + } else { + Err(de::Error::custom("expected a string")) + } + }) + .collect(); + Ok(list?) } + value => Err(de::Error::invalid_type( + Unexpected::Other(&value.to_string()), + &"a string or array", + )), + } } -} \ No newline at end of file +} diff --git a/swap-serde/src/monero.rs b/swap-serde/src/monero.rs index a95ee015..8849b751 100644 --- a/swap-serde/src/monero.rs +++ b/swap-serde/src/monero.rs @@ -1,4 +1,4 @@ -use monero::{Network, Amount}; +use monero::{Amount, Network}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; #[derive(Serialize, Deserialize)] @@ -11,6 +11,7 @@ pub enum network { } pub mod private_key { + use hex; use monero::consensus::{Decodable, Encodable}; use monero::PrivateKey; use serde::de::Visitor; @@ -18,7 +19,6 @@ pub mod private_key { use serde::{de, Deserializer, Serializer}; use std::fmt; use std::io::Cursor; - use hex; struct BytesVisitor; @@ -143,4 +143,4 @@ pub mod address { }; validate(address, expected_network) } -} \ No newline at end of file +} diff --git a/swap/.sqlx/query-081c729a0f1ad6e4ff3e13d6702c946bc4d37d50f40670b4f51d2efcce595aa6.json b/swap/.sqlx/query-081c729a0f1ad6e4ff3e13d6702c946bc4d37d50f40670b4f51d2efcce595aa6.json index 94b77425..8f1a9699 100644 --- a/swap/.sqlx/query-081c729a0f1ad6e4ff3e13d6702c946bc4d37d50f40670b4f51d2efcce595aa6.json +++ b/swap/.sqlx/query-081c729a0f1ad6e4ff3e13d6702c946bc4d37d50f40670b4f51d2efcce595aa6.json @@ -12,9 +12,7 @@ "parameters": { "Right": 1 }, - "nullable": [ - false - ] + "nullable": [false] }, "hash": "081c729a0f1ad6e4ff3e13d6702c946bc4d37d50f40670b4f51d2efcce595aa6" } diff --git a/swap/.sqlx/query-0d465a17ebbb5761421def759c73cad023c30705d5b41a1399ef79d8d2571d7c.json b/swap/.sqlx/query-0d465a17ebbb5761421def759c73cad023c30705d5b41a1399ef79d8d2571d7c.json index 58fbb3f8..e746a4c0 100644 --- a/swap/.sqlx/query-0d465a17ebbb5761421def759c73cad023c30705d5b41a1399ef79d8d2571d7c.json +++ b/swap/.sqlx/query-0d465a17ebbb5761421def759c73cad023c30705d5b41a1399ef79d8d2571d7c.json @@ -12,9 +12,7 @@ "parameters": { "Right": 1 }, - "nullable": [ - true - ] + "nullable": [true] }, "hash": "0d465a17ebbb5761421def759c73cad023c30705d5b41a1399ef79d8d2571d7c" } diff --git a/swap/.sqlx/query-1f332be08a5426f3fbcadea4e755d82ff1cdc2690eb464ccc607d3a613fa76a1.json b/swap/.sqlx/query-1f332be08a5426f3fbcadea4e755d82ff1cdc2690eb464ccc607d3a613fa76a1.json index 4c1f9abe..52e81ff8 100644 --- a/swap/.sqlx/query-1f332be08a5426f3fbcadea4e755d82ff1cdc2690eb464ccc607d3a613fa76a1.json +++ b/swap/.sqlx/query-1f332be08a5426f3fbcadea4e755d82ff1cdc2690eb464ccc607d3a613fa76a1.json @@ -12,9 +12,7 @@ "parameters": { "Right": 0 }, - "nullable": [ - true - ] + "nullable": [true] }, "hash": "1f332be08a5426f3fbcadea4e755d82ff1cdc2690eb464ccc607d3a613fa76a1" } diff --git a/swap/.sqlx/query-5cc61dd0315571bc198401a354cd9431ee68360941f341386cbacf44ea598de8.json b/swap/.sqlx/query-5cc61dd0315571bc198401a354cd9431ee68360941f341386cbacf44ea598de8.json index 7f58635f..7fc74b31 100644 --- a/swap/.sqlx/query-5cc61dd0315571bc198401a354cd9431ee68360941f341386cbacf44ea598de8.json +++ b/swap/.sqlx/query-5cc61dd0315571bc198401a354cd9431ee68360941f341386cbacf44ea598de8.json @@ -17,10 +17,7 @@ "parameters": { "Right": 0 }, - "nullable": [ - true, - true - ] + "nullable": [true, true] }, "hash": "5cc61dd0315571bc198401a354cd9431ee68360941f341386cbacf44ea598de8" } diff --git a/swap/.sqlx/query-6130b6cdd184181f890964eb460741f5cf23b5237fb676faed009106627a4ca6.json b/swap/.sqlx/query-6130b6cdd184181f890964eb460741f5cf23b5237fb676faed009106627a4ca6.json index 2dfabcfb..a3d9d364 100644 --- a/swap/.sqlx/query-6130b6cdd184181f890964eb460741f5cf23b5237fb676faed009106627a4ca6.json +++ b/swap/.sqlx/query-6130b6cdd184181f890964eb460741f5cf23b5237fb676faed009106627a4ca6.json @@ -17,10 +17,7 @@ "parameters": { "Right": 0 }, - "nullable": [ - false, - false - ] + "nullable": [false, false] }, "hash": "6130b6cdd184181f890964eb460741f5cf23b5237fb676faed009106627a4ca6" } diff --git a/swap/.sqlx/query-88f761a4f7a0429cad1df0b1bebb1c0a27b2a45656549b23076d7542cfa21ecf.json b/swap/.sqlx/query-88f761a4f7a0429cad1df0b1bebb1c0a27b2a45656549b23076d7542cfa21ecf.json index b7429dcd..d13a0de0 100644 --- a/swap/.sqlx/query-88f761a4f7a0429cad1df0b1bebb1c0a27b2a45656549b23076d7542cfa21ecf.json +++ b/swap/.sqlx/query-88f761a4f7a0429cad1df0b1bebb1c0a27b2a45656549b23076d7542cfa21ecf.json @@ -12,9 +12,7 @@ "parameters": { "Right": 1 }, - "nullable": [ - false - ] + "nullable": [false] }, "hash": "88f761a4f7a0429cad1df0b1bebb1c0a27b2a45656549b23076d7542cfa21ecf" } diff --git a/swap/.sqlx/query-d78acba5eb8563826dd190e0886aa665aae3c6f1e312ee444e65df1c95afe8b2.json b/swap/.sqlx/query-d78acba5eb8563826dd190e0886aa665aae3c6f1e312ee444e65df1c95afe8b2.json index cf0105e2..4b61e1a5 100644 --- a/swap/.sqlx/query-d78acba5eb8563826dd190e0886aa665aae3c6f1e312ee444e65df1c95afe8b2.json +++ b/swap/.sqlx/query-d78acba5eb8563826dd190e0886aa665aae3c6f1e312ee444e65df1c95afe8b2.json @@ -12,9 +12,7 @@ "parameters": { "Right": 1 }, - "nullable": [ - false - ] + "nullable": [false] }, "hash": "d78acba5eb8563826dd190e0886aa665aae3c6f1e312ee444e65df1c95afe8b2" } diff --git a/swap/.sqlx/query-dff8b986c3dde27b8121775e48a58564fa346b038866699210a63f8a33b03f0b.json b/swap/.sqlx/query-dff8b986c3dde27b8121775e48a58564fa346b038866699210a63f8a33b03f0b.json index ec1acabc..180b4d0c 100644 --- a/swap/.sqlx/query-dff8b986c3dde27b8121775e48a58564fa346b038866699210a63f8a33b03f0b.json +++ b/swap/.sqlx/query-dff8b986c3dde27b8121775e48a58564fa346b038866699210a63f8a33b03f0b.json @@ -22,11 +22,7 @@ "parameters": { "Right": 1 }, - "nullable": [ - true, - false, - false - ] + "nullable": [true, false, false] }, "hash": "dff8b986c3dde27b8121775e48a58564fa346b038866699210a63f8a33b03f0b" } diff --git a/swap/.sqlx/query-e05620f420f8c1022971eeb66a803323a8cf258cbebb2834e3f7cf8f812fa646.json b/swap/.sqlx/query-e05620f420f8c1022971eeb66a803323a8cf258cbebb2834e3f7cf8f812fa646.json index 5eb6fef3..1835133f 100644 --- a/swap/.sqlx/query-e05620f420f8c1022971eeb66a803323a8cf258cbebb2834e3f7cf8f812fa646.json +++ b/swap/.sqlx/query-e05620f420f8c1022971eeb66a803323a8cf258cbebb2834e3f7cf8f812fa646.json @@ -12,9 +12,7 @@ "parameters": { "Right": 1 }, - "nullable": [ - false - ] + "nullable": [false] }, "hash": "e05620f420f8c1022971eeb66a803323a8cf258cbebb2834e3f7cf8f812fa646" } diff --git a/swap/.sqlx/query-e9d422daf774d099fcbde6c4cda35821da948bd86cc57798b4d8375baf0b51ae.json b/swap/.sqlx/query-e9d422daf774d099fcbde6c4cda35821da948bd86cc57798b4d8375baf0b51ae.json index f375b6b6..952f282c 100644 --- a/swap/.sqlx/query-e9d422daf774d099fcbde6c4cda35821da948bd86cc57798b4d8375baf0b51ae.json +++ b/swap/.sqlx/query-e9d422daf774d099fcbde6c4cda35821da948bd86cc57798b4d8375baf0b51ae.json @@ -12,9 +12,7 @@ "parameters": { "Right": 1 }, - "nullable": [ - false - ] + "nullable": [false] }, "hash": "e9d422daf774d099fcbde6c4cda35821da948bd86cc57798b4d8375baf0b51ae" } diff --git a/swap/Cargo.toml b/swap/Cargo.toml index f4f56498..7ecbb0b3 100644 --- a/swap/Cargo.toml +++ b/swap/Cargo.toml @@ -72,6 +72,7 @@ swap-env = { path = "../swap-env" } swap-feed = { path = "../swap-feed" } swap-fs = { path = "../swap-fs" } swap-serde = { path = "../swap-serde" } +throttle = { path = "../throttle" } tauri = { version = "2.0", features = ["config-json5"], optional = true, default-features = false } thiserror = { workspace = true } time = "0.3" diff --git a/swap/src/asb/command.rs b/swap/src/asb/command.rs index 5e434b51..5797f212 100644 --- a/swap/src/asb/command.rs +++ b/swap/src/asb/command.rs @@ -1,7 +1,4 @@ -use swap_env::config::GetDefaults; use crate::bitcoin::{bitcoin_address, Amount}; -use swap_env::env; -use swap_env::env::GetConfig; use anyhow::Result; use bitcoin::address::NetworkUnchecked; use bitcoin::Address; @@ -9,6 +6,9 @@ use serde::Serialize; use std::ffi::OsString; use std::path::PathBuf; use structopt::StructOpt; +use swap_env::config::GetDefaults; +use swap_env::env; +use swap_env::env::GetConfig; use uuid::Uuid; pub fn parse_args(raw_args: I) -> Result @@ -402,7 +402,9 @@ mod tests { #[test] fn ensure_start_command_mapping_mainnet() { - let default_mainnet_conf_path = env::Mainnet::get_config_file_defaults().unwrap().config_path; + let default_mainnet_conf_path = env::Mainnet::get_config_file_defaults() + .unwrap() + .config_path; let mainnet_env_config = env::Mainnet::get_config(); let raw_ars = vec![BINARY_NAME, "start"]; @@ -420,7 +422,9 @@ mod tests { #[test] fn ensure_history_command_mapping_mainnet() { - let default_mainnet_conf_path = env::Mainnet::get_config_file_defaults().unwrap().config_path; + let default_mainnet_conf_path = env::Mainnet::get_config_file_defaults() + .unwrap() + .config_path; let mainnet_env_config = env::Mainnet::get_config(); let raw_ars = vec![BINARY_NAME, "history"]; @@ -440,7 +444,9 @@ mod tests { #[test] fn ensure_balance_command_mapping_mainnet() { - let default_mainnet_conf_path = env::Mainnet::get_config_file_defaults().unwrap().config_path; + let default_mainnet_conf_path = env::Mainnet::get_config_file_defaults() + .unwrap() + .config_path; let mainnet_env_config = env::Mainnet::get_config(); let raw_ars = vec![BINARY_NAME, "balance"]; @@ -458,7 +464,9 @@ mod tests { #[test] fn ensure_withdraw_command_mapping_mainnet() { - let default_mainnet_conf_path = env::Mainnet::get_config_file_defaults().unwrap().config_path; + let default_mainnet_conf_path = env::Mainnet::get_config_file_defaults() + .unwrap() + .config_path; let mainnet_env_config = env::Mainnet::get_config(); let raw_ars = vec![ BINARY_NAME, @@ -484,7 +492,9 @@ mod tests { #[test] fn ensure_cancel_command_mapping_mainnet() { - let default_mainnet_conf_path = env::Mainnet::get_config_file_defaults().unwrap().config_path; + let default_mainnet_conf_path = env::Mainnet::get_config_file_defaults() + .unwrap() + .config_path; let mainnet_env_config = env::Mainnet::get_config(); let raw_ars = vec![ @@ -510,7 +520,9 @@ mod tests { #[test] fn ensure_refund_command_mappin_mainnet() { - let default_mainnet_conf_path = env::Mainnet::get_config_file_defaults().unwrap().config_path; + let default_mainnet_conf_path = env::Mainnet::get_config_file_defaults() + .unwrap() + .config_path; let mainnet_env_config = env::Mainnet::get_config(); let raw_ars = vec![ @@ -536,7 +548,9 @@ mod tests { #[test] fn ensure_punish_command_mapping_mainnet() { - let default_mainnet_conf_path = env::Mainnet::get_config_file_defaults().unwrap().config_path; + let default_mainnet_conf_path = env::Mainnet::get_config_file_defaults() + .unwrap() + .config_path; let mainnet_env_config = env::Mainnet::get_config(); let raw_ars = vec![ @@ -562,7 +576,9 @@ mod tests { #[test] fn ensure_safely_abort_command_mapping_mainnet() { - let default_mainnet_conf_path = env::Mainnet::get_config_file_defaults().unwrap().config_path; + let default_mainnet_conf_path = env::Mainnet::get_config_file_defaults() + .unwrap() + .config_path; let mainnet_env_config = env::Mainnet::get_config(); let raw_ars = vec![ @@ -588,7 +604,9 @@ mod tests { #[test] fn ensure_start_command_mapping_for_testnet() { - let default_testnet_conf_path = env::Testnet::get_config_file_defaults().unwrap().config_path; + let default_testnet_conf_path = env::Testnet::get_config_file_defaults() + .unwrap() + .config_path; let testnet_env_config = env::Testnet::get_config(); let raw_ars = vec![BINARY_NAME, "--testnet", "start"]; @@ -606,7 +624,9 @@ mod tests { #[test] fn ensure_history_command_mapping_testnet() { - let default_testnet_conf_path = env::Testnet::get_config_file_defaults().unwrap().config_path; + let default_testnet_conf_path = env::Testnet::get_config_file_defaults() + .unwrap() + .config_path; let testnet_env_config = env::Testnet::get_config(); let raw_ars = vec![BINARY_NAME, "--testnet", "history"]; @@ -626,7 +646,9 @@ mod tests { #[test] fn ensure_balance_command_mapping_testnet() { - let default_testnet_conf_path = env::Testnet::get_config_file_defaults().unwrap().config_path; + let default_testnet_conf_path = env::Testnet::get_config_file_defaults() + .unwrap() + .config_path; let testnet_env_config = env::Testnet::get_config(); let raw_ars = vec![BINARY_NAME, "--testnet", "balance"]; @@ -644,7 +666,9 @@ mod tests { #[test] fn ensure_export_monero_command_mapping_testnet() { - let default_testnet_conf_path = env::Testnet::get_config_file_defaults().unwrap().config_path; + let default_testnet_conf_path = env::Testnet::get_config_file_defaults() + .unwrap() + .config_path; let testnet_env_config = env::Testnet::get_config(); let raw_ars = vec![BINARY_NAME, "--testnet", "export-monero-wallet"]; @@ -663,7 +687,9 @@ mod tests { #[test] fn ensure_withdraw_command_mapping_testnet() { - let default_testnet_conf_path = env::Testnet::get_config_file_defaults().unwrap().config_path; + let default_testnet_conf_path = env::Testnet::get_config_file_defaults() + .unwrap() + .config_path; let testnet_env_config = env::Testnet::get_config(); let raw_ars = vec![ @@ -690,7 +716,9 @@ mod tests { } #[test] fn ensure_cancel_command_mapping_testnet() { - let default_testnet_conf_path = env::Testnet::get_config_file_defaults().unwrap().config_path; + let default_testnet_conf_path = env::Testnet::get_config_file_defaults() + .unwrap() + .config_path; let testnet_env_config = env::Testnet::get_config(); let raw_ars = vec![ @@ -717,7 +745,9 @@ mod tests { #[test] fn ensure_refund_command_mapping_testnet() { - let default_testnet_conf_path = env::Testnet::get_config_file_defaults().unwrap().config_path; + let default_testnet_conf_path = env::Testnet::get_config_file_defaults() + .unwrap() + .config_path; let testnet_env_config = env::Testnet::get_config(); let raw_ars = vec![ @@ -744,7 +774,9 @@ mod tests { #[test] fn ensure_punish_command_mapping_testnet() { - let default_testnet_conf_path = env::Testnet::get_config_file_defaults().unwrap().config_path; + let default_testnet_conf_path = env::Testnet::get_config_file_defaults() + .unwrap() + .config_path; let testnet_env_config = env::Testnet::get_config(); let raw_ars = vec![ @@ -771,7 +803,9 @@ mod tests { #[test] fn ensure_safely_abort_command_mapping_testnet() { - let default_testnet_conf_path = env::Testnet::get_config_file_defaults().unwrap().config_path; + let default_testnet_conf_path = env::Testnet::get_config_file_defaults() + .unwrap() + .config_path; let testnet_env_config = env::Testnet::get_config(); let raw_ars = vec![ @@ -798,7 +832,9 @@ mod tests { #[test] fn ensure_disable_timestamp_mapping() { - let default_mainnet_conf_path = env::Mainnet::get_config_file_defaults().unwrap().config_path; + let default_mainnet_conf_path = env::Mainnet::get_config_file_defaults() + .unwrap() + .config_path; let mainnet_env_config = env::Mainnet::get_config(); let raw_ars = vec![BINARY_NAME, "--disable-timestamp", "start"]; @@ -816,7 +852,9 @@ mod tests { #[test] fn ensure_trace_mapping() { - let default_mainnet_conf_path = env::Mainnet::get_config_file_defaults().unwrap().config_path; + let default_mainnet_conf_path = env::Mainnet::get_config_file_defaults() + .unwrap() + .config_path; let mainnet_env_config = env::Mainnet::get_config(); let raw_ars = vec![BINARY_NAME, "--trace", "start"]; diff --git a/swap/src/bin/asb.rs b/swap/src/bin/asb.rs index a362c25c..69343804 100644 --- a/swap/src/bin/asb.rs +++ b/swap/src/bin/asb.rs @@ -24,9 +24,6 @@ use std::sync::Arc; use structopt::clap; use structopt::clap::ErrorKind; use swap::asb::command::{parse_args, Arguments, Command}; -use swap_env::config::{ - initial_setup, query_user_for_initial_config, read_config, Config, ConfigNotInitialized, -}; use swap::asb::{cancel, punish, redeem, refund, safely_abort, EventLoop, Finality, KrakenRate}; use swap::common::tor::init_tor_client; use swap::common::tracing_util::Format; @@ -40,6 +37,9 @@ use swap::protocol::{Database, State}; use swap::seed::Seed; use swap::{bitcoin, monero}; use swap_feed; +use swap_env::config::{ + initial_setup, query_user_for_initial_config, read_config, Config, ConfigNotInitialized, +}; use tracing_subscriber::filter::LevelFilter; use uuid::Uuid; @@ -133,7 +133,7 @@ pub async fn main() -> Result<()> { )); } - let seed = Seed::from_file_or_generate(&config.data.dir, None) + let seed = Seed::from_file_or_generate(&config.data.dir) .await .expect("Could not retrieve/initialize seed"); @@ -431,7 +431,7 @@ pub async fn main() -> Result<()> { let monero_wallet = init_monero_wallet(&config, env_config).await?; let main_wallet = monero_wallet.main_wallet().await; - let seed = main_wallet.seed().await; + let seed = main_wallet.seed().await?; let creation_height = main_wallet.creation_height().await; println!("Seed : {seed}"); @@ -474,7 +474,9 @@ async fn init_bitcoin_wallet( if sync { wallet.sync().await?; } else { - tracing::info!("Skipping Bitcoin wallet sync because we are only using it for receiving funds"); + tracing::info!( + "Skipping Bitcoin wallet sync because we are only using it for receiving funds" + ); } Ok(wallet) @@ -528,6 +530,7 @@ async fn init_monero_wallet( env_config.monero_network, false, None, + None, ) .await .context("Failed to initialize Monero wallets")?; diff --git a/swap/src/bitcoin.rs b/swap/src/bitcoin.rs index b2c5ba38..25ccf7c7 100644 --- a/swap/src/bitcoin.rs +++ b/swap/src/bitcoin.rs @@ -481,7 +481,6 @@ pub struct NotThreeWitnesses(usize); #[cfg(test)] mod tests { use super::*; - use swap_env::env::{GetConfig, Regtest}; use crate::monero::TransferProof; use crate::protocol::{alice, bob}; use bitcoin::secp256k1; @@ -490,6 +489,7 @@ mod tests { use monero::PrivateKey; use rand::rngs::OsRng; use std::matches; + use swap_env::env::{GetConfig, Regtest}; use uuid::Uuid; #[test] diff --git a/swap/src/cli/api.rs b/swap/src/cli/api.rs index 5aa701e3..191486f6 100644 --- a/swap/src/cli/api.rs +++ b/swap/src/cli/api.rs @@ -1,13 +1,11 @@ pub mod request; pub mod tauri_bindings; +use crate::cli::api::tauri_bindings::SeedChoice; use crate::cli::command::{Bitcoin, Monero}; use crate::common::tor::init_tor_client; use crate::common::tracing_util::Format; use crate::database::{open_db, AccessMode}; -use crate::monero::Wallets; -use swap_env::env::{Config as EnvConfig, GetConfig, Mainnet, Testnet}; -use swap_fs::system_data_dir; use crate::network::rendezvous::XmrBtcNamespace; use crate::protocol::Database; use crate::seed::Seed; @@ -19,6 +17,8 @@ use std::fmt; use std::future::Future; use std::path::{Path, PathBuf}; use std::sync::{Arc, Once}; +use swap_env::env::{Config as EnvConfig, GetConfig, Mainnet, Testnet}; +use swap_fs::system_data_dir; use tauri_bindings::{ MoneroNodeConfig, TauriBackgroundProgress, TauriContextStatusEvent, TauriEmitter, TauriHandle, }; @@ -40,6 +40,7 @@ pub struct Config { seed: Option, debug: bool, json: bool, + log_dir: PathBuf, data_dir: PathBuf, is_testnet: bool, } @@ -281,7 +282,12 @@ impl ContextBuilder { /// Takes the builder, initializes the context by initializing the wallets and other components and returns the Context. pub async fn build(self) -> Result { - let data_dir = &data::data_dir_from(self.data, self.is_testnet)?; + // This is the data directory for the eigenwallet (wallet files) + let eigenwallet_data_dir = &eigenwallet_data::new(self.is_testnet)?; + + let base_data_dir = &data::data_dir_from(self.data, self.is_testnet)?; + let log_dir = base_data_dir.join("logs"); + let env_config = env_config_from(self.is_testnet); // Initialize logging let format = if self.json { Format::Json } else { Format::Raw }; @@ -295,7 +301,7 @@ impl ContextBuilder { let _ = common::tracing_util::init( level_filter, format, - data_dir.join("logs"), + log_dir.clone(), self.tauri_handle.clone(), false, ); @@ -308,11 +314,97 @@ impl ContextBuilder { ); }); - // These are needed for everything else, and are blocking calls - let env_config = env_config_from(self.is_testnet); - let seed = &Seed::from_file_or_generate(data_dir.as_path(), self.tauri_handle.clone()) + // Start the rpc pool for the monero wallet + let (server_info, mut status_receiver, pool_handle) = + monero_rpc_pool::start_server_with_random_port( + monero_rpc_pool::config::Config::new_random_port( + "127.0.0.1".to_string(), + base_data_dir.join("monero-rpc-pool"), + ), + match self.is_testnet { + true => crate::monero::Network::Stagenet, + false => crate::monero::Network::Mainnet, + }, + ) + .await?; + + // Listen for pool status updates and forward them to frontend + let pool_tauri_handle = self.tauri_handle.clone(); + tokio::spawn(async move { + while let Ok(status) = status_receiver.recv().await { + pool_tauri_handle.emit_pool_status_update(status); + } + }); + + // Determine the monero node address to use + let (monero_node_address, monero_rpc_pool_handle) = match &self.monero_config { + Some(MoneroNodeConfig::Pool) => { + let rpc_url = server_info.into(); + (rpc_url, Some(Arc::new(pool_handle))) + } + Some(MoneroNodeConfig::SingleNode { url }) => (url.clone(), None), + None => { + // Default to pool if no monero config is provided + let rpc_url = server_info.into(); + (rpc_url, Some(Arc::new(pool_handle))) + } + }; + + // Create a daemon struct for the monero wallet based on the node address + let daemon = monero_sys::Daemon { + address: monero_node_address, + ssl: false, + }; + + // Initialize wallet database for tracking recent wallets + let wallet_database = monero_sys::Database::new(eigenwallet_data_dir.clone()) .await - .context("Failed to read seed in file")?; + .context("Failed to initialize wallet database")?; + + // Prompt the user to open/create a Monero wallet + let (wallet, seed) = request_and_open_monero_wallet( + self.tauri_handle.clone(), + eigenwallet_data_dir, + base_data_dir, + env_config, + &daemon, + &wallet_database, + ) + .await?; + + let primary_address = wallet.main_address().await; + + // Derive data directory from primary address + let data_dir = base_data_dir + .join("identities") + .join(primary_address.to_string()); + + // Ensure the identity directory exists + swap_fs::ensure_directory_exists(&data_dir) + .context("Failed to create identity directory")?; + + tracing::info!( + primary_address = %primary_address, + data_dir = %data_dir.display(), + "Using wallet-specific data directory" + ); + + let wallet_database = Some(Arc::new(wallet_database)); + + // Create the monero wallet manager + let monero_manager = Some(Arc::new( + monero::Wallets::new_with_existing_wallet( + eigenwallet_data_dir.to_path_buf(), + daemon.clone(), + env_config.monero_network, + false, + self.tauri_handle.clone(), + wallet, + wallet_database, + ) + .await + .context("Failed to initialize Monero wallets with existing wallet")?, + )); // Create the data structure we use to manage the swap lock let swap_lock = Arc::new(SwapLock::new()); @@ -350,8 +442,8 @@ impl ContextBuilder { let wallet = init_bitcoin_wallet( urls, - seed, - data_dir, + &seed, + &data_dir, env_config, target_block, self.tauri_handle.clone(), @@ -368,68 +460,6 @@ impl ContextBuilder { } }; - let initialize_monero_wallet = async { - match self.monero_config { - Some(monero_config) => { - let monero_progress_handle = tauri_handle - .new_background_process_with_initial_progress( - TauriBackgroundProgress::OpeningMoneroWallet, - (), - ); - - // If we are instructed to use a pool, we start it and use it - // Otherwise we use the single node address provided by the user - let (monero_node_address, rpc_pool_handle) = match monero_config { - MoneroNodeConfig::Pool => { - // Start RPC pool and use it - let (server_info, mut status_receiver, pool_handle) = - monero_rpc_pool::start_server_with_random_port( - monero_rpc_pool::config::Config::new_random_port( - "127.0.0.1".to_string(), - data_dir.join("monero-rpc-pool"), - ), - match self.is_testnet { - true => crate::monero::Network::Stagenet, - false => crate::monero::Network::Mainnet, - }, - ) - .await?; - - let rpc_url = - format!("http://{}:{}", server_info.host, server_info.port); - tracing::info!("Monero RPC Pool started on {}", rpc_url); - - // Start listening for pool status updates and forward them to frontend - if let Some(ref handle) = self.tauri_handle { - let pool_tauri_handle = handle.clone(); - tokio::spawn(async move { - while let Ok(status) = status_receiver.recv().await { - pool_tauri_handle.emit_pool_status_update(status); - } - }); - } - - (rpc_url, Some(Arc::new(pool_handle))) - } - MoneroNodeConfig::SingleNode { url } => (url, None), - }; - - let wallets = init_monero_wallet( - data_dir.as_path(), - monero_node_address, - env_config, - tauri_handle.clone(), - ) - .await?; - - monero_progress_handle.finish(); - - Ok((Some(wallets), rpc_pool_handle)) - } - None => Ok((None, None)), - } - }; - let initialize_tor_client = async { // Don't init a tor client unless we should use it. if !self.tor { @@ -437,7 +467,7 @@ impl ContextBuilder { return Ok(None); } - let maybe_tor_client = init_tor_client(data_dir, tauri_handle.clone()) + let maybe_tor_client = init_tor_client(&data_dir, tauri_handle.clone()) .await .inspect_err(|err| { tracing::warn!(%err, "Failed to create Tor client. We will continue without Tor"); @@ -447,11 +477,8 @@ impl ContextBuilder { Ok(maybe_tor_client) }; - let (bitcoin_wallet, (monero_manager, monero_rpc_pool_handle), tor) = tokio::try_join!( - initialize_bitcoin_wallet, - initialize_monero_wallet, - initialize_tor_client, - )?; + let (bitcoin_wallet, tor) = + tokio::try_join!(initialize_bitcoin_wallet, initialize_tor_client,)?; // If we have a bitcoin wallet and a tauri handle, we start a background task if let Some(wallet) = bitcoin_wallet.clone() { @@ -466,8 +493,6 @@ impl ContextBuilder { } } - tauri_handle.emit_context_init_progress_event(TauriContextStatusEvent::Available); - let context = Context { db, bitcoin_wallet, @@ -480,14 +505,17 @@ impl ContextBuilder { json: self.json, is_testnet: self.is_testnet, data_dir: data_dir.clone(), + log_dir: log_dir.clone(), }, - swap_lock, + swap_lock, tasks, tauri_handle: self.tauri_handle, tor_client: tor, monero_rpc_pool_handle, }; + tauri_handle.emit_context_init_progress_event(TauriContextStatusEvent::Available); + Ok(context) } } @@ -525,6 +553,15 @@ impl Context { pub fn cleanup(&self) -> Result<()> { // TODO: close all monero wallets + // call store(..) on all wallets + + let monero_manager = self.monero_manager.clone(); + tokio::spawn(async move { + if let Some(monero_manager) = monero_manager { + let wallet = monero_manager.main_wallet().await; + wallet.store(None).await; + } + }); Ok(()) } @@ -575,56 +612,227 @@ async fn init_bitcoin_wallet( Ok(wallet) } -async fn init_monero_wallet( - data_dir: &Path, - monero_daemon_address: String, +async fn request_and_open_monero_wallet_legacy( + data_dir: &PathBuf, env_config: EnvConfig, - tauri_handle: Option, -) -> Result> { - let network = env_config.monero_network; - let wallet_dir = data_dir.join("monero").join("monero-data"); + daemon: &monero_sys::Daemon, +) -> Result { + let wallet_path = data_dir.join("swap-tool-blockchain-monitoring-wallet"); - let daemon = monero_sys::Daemon { - address: monero_daemon_address, - ssl: false, - }; - - // This is the name of a wallet we only use for blockchain monitoring - const DEFAULT_WALLET: &str = "swap-tool-blockchain-monitoring-wallet"; - - // Remove the monitoring wallet if it exists - // It doesn't contain any coins - // Deleting it ensures we never have issues at startup - // And we reset the restore height - let wallet_path = wallet_dir.join(DEFAULT_WALLET); - if wallet_path.exists() { - tracing::debug!( - wallet_path = %wallet_path.display(), - "Removing monitoring wallet" - ); - let _ = tokio::fs::remove_file(&wallet_path).await; - } - let keys_path = wallet_path.with_extension("keys"); - if keys_path.exists() { - tracing::debug!( - keys_path = %keys_path.display(), - "Removing monitoring wallet keys" - ); - let _ = tokio::fs::remove_file(keys_path).await; - } - - let wallets = monero::Wallets::new( - wallet_dir, - DEFAULT_WALLET.to_string(), - daemon, - network, - false, - tauri_handle, + let wallet = monero::Wallet::open_or_create( + wallet_path.display().to_string(), + daemon.clone(), + env_config.monero_network, + true, ) .await - .context("Failed to initialize Monero wallets")?; + .context("Failed to create wallet")?; - Ok(Arc::new(wallets)) + Ok(wallet) +} + +/// Opens or creates a Monero wallet after asking the user via the Tauri UI. +/// +/// The user can: +/// - Create a new wallet with a random seed. +/// - Recover a wallet from a given seed phrase. +/// - Open an existing wallet file (with password verification). +/// +/// Errors if the user aborts, provides an incorrect password, or the wallet +/// fails to open/create. +async fn request_and_open_monero_wallet( + tauri_handle: Option, + eigenwallet_data_dir: &PathBuf, + legacy_data_dir: &PathBuf, + env_config: EnvConfig, + daemon: &monero_sys::Daemon, + wallet_database: &monero_sys::Database, +) -> Result<(monero_sys::WalletHandle, Seed), Error> { + let eigenwallet_wallets_dir = eigenwallet_data_dir.join("wallets"); + + let wallet = match tauri_handle { + Some(tauri_handle) => { + // Get recent wallets from database + let recent_wallets: Vec = wallet_database + .get_recent_wallets(5) + .await + .unwrap_or_default() + .into_iter() + .map(|w| w.wallet_path) + .collect(); + + // This loop continually requests the user to select a wallet file + // It then requests the user to provide a password. + // It repeats until the user provides a valid password or rejects the password request + // When the user rejects the password request, we prompt him to select a wallet again + loop { + let seed_choice = tauri_handle + .request_seed_selection_with_recent_wallets(recent_wallets.clone()) + .await?; + + let _monero_progress_handle = tauri_handle + .new_background_process_with_initial_progress( + TauriBackgroundProgress::OpeningMoneroWallet, + (), + ); + + fn new_wallet_path(eigenwallet_wallets_dir: &PathBuf) -> Result { + let timestamp = std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_secs(); + + let wallet_path = eigenwallet_wallets_dir.join(format!("wallet_{}", timestamp)); + + if let Some(parent) = wallet_path.parent() { + swap_fs::ensure_directory_exists(parent) + .context("Failed to create wallet directory")?; + } + + Ok(wallet_path) + } + + let wallet = match seed_choice { + SeedChoice::RandomSeed => { + // Create wallet with Unix timestamp as name + let wallet_path = new_wallet_path(&eigenwallet_wallets_dir) + .context("Failed to determine path for new wallet")?; + + monero::Wallet::open_or_create( + wallet_path.display().to_string(), + daemon.clone(), + env_config.monero_network, + true, + ) + .await + .context("Failed to create wallet from random seed")? + } + SeedChoice::FromSeed { seed: mnemonic } => { + // Create wallet from provided seed + let wallet_path = new_wallet_path(&eigenwallet_wallets_dir) + .context("Failed to determine path for new wallet")?; + + monero::Wallet::open_or_create_from_seed( + wallet_path.display().to_string(), + mnemonic, + env_config.monero_network, + 0, + true, + daemon.clone(), + ) + .await + .context("Failed to create wallet from provided seed")? + } + SeedChoice::FromWalletPath { wallet_path } => { + // Helper function to verify password + let verify_password = |password: String| -> Result { + monero_sys::WalletHandle::verify_wallet_password( + wallet_path.clone(), + password, + ) + .map_err(|e| anyhow::anyhow!("Failed to verify wallet password: {}", e)) + }; + + // Request and verify password before opening wallet + let wallet_password: Option = { + const WALLET_EMPTY_PASSWORD: &str = ""; + + // First try empty password + if verify_password(WALLET_EMPTY_PASSWORD.to_string())? { + Some(WALLET_EMPTY_PASSWORD.to_string()) + } else { + // If empty password fails, ask user for password + loop { + // Request password from user + let password = tauri_handle + .request_password(wallet_path.clone()) + .await + .inspect_err(|e| { + tracing::error!( + "Failed to get password from user: {}", + e + ); + }) + .ok(); + + // If the user rejects the password request (presses cancel) + // We prompt him to select a wallet again + let password = match password { + Some(password) => password, + None => break None, + }; + + // Verify the password using the helper function + match verify_password(password.clone()) { + Ok(true) => { + break Some(password); + } + Ok(false) => { + // Continue loop to request password again + continue; + } + Err(e) => { + return Err(e); + } + } + } + } + }; + + let password = match wallet_password { + Some(password) => password, + // None means the user rejected the password request + // We prompt him to select a wallet again + None => { + continue; + } + }; + + // Open existing wallet with verified password + monero::Wallet::open_or_create_with_password( + wallet_path.clone(), + password, + daemon.clone(), + env_config.monero_network, + true, + ) + .await + .context("Failed to open wallet from provided path")? + } + + SeedChoice::Legacy => { + let wallet = request_and_open_monero_wallet_legacy(legacy_data_dir, env_config, daemon).await?; + let seed = Seed::from_file_or_generate(legacy_data_dir) + .await + .context("Failed to extract seed from wallet")?; + + break (wallet, seed); + } + }; + + // Extract seed from the wallet + tracing::info!("Extracting seed from wallet directory: {}", legacy_data_dir.display()); + let seed = Seed::from_monero_wallet(&wallet) + .await + .context("Failed to extract seed from wallet")?; + + break (wallet, seed); + } + } + + // If we don't have a tauri handle, we use the seed.pem file + // This is used for the CLI to monitor the blockchain + None => { + let wallet = request_and_open_monero_wallet_legacy(legacy_data_dir, env_config, daemon).await?; + let seed = Seed::from_file_or_generate(legacy_data_dir) + .await + .context("Failed to extract seed from wallet")?; + + (wallet, seed) + } + }; + + Ok(wallet) } pub mod data { @@ -646,6 +854,16 @@ pub mod data { } } +pub mod eigenwallet_data { + use swap_fs::system_data_dir_eigenwallet; + + use super::*; + + pub fn new(testnet: bool) -> Result { + Ok(system_data_dir_eigenwallet(testnet)?) + } +} + fn env_config_from(testnet: bool) -> EnvConfig { if testnet { Testnet::get_config() @@ -657,6 +875,7 @@ fn env_config_from(testnet: bool) -> EnvConfig { impl Config { pub fn for_harness(seed: Seed, env_config: EnvConfig) -> Self { let data_dir = data::data_dir_from(None, false).expect("Could not find data directory"); + let log_dir = data_dir.join("logs"); // not used in production Self { namespace: XmrBtcNamespace::from_is_testnet(false), @@ -666,6 +885,7 @@ impl Config { json: false, is_testnet: false, data_dir, + log_dir, } } } @@ -707,7 +927,8 @@ pub mod api_test { json: bool, ) -> Self { let data_dir = data::data_dir_from(data_dir, is_testnet).unwrap(); - let seed = Seed::from_file_or_generate(data_dir.as_path(), None) + let log_dir = data_dir.clone().join("logs"); + let seed = Seed::from_file_or_generate(data_dir.as_path()) .await .unwrap(); let env_config = env_config_from(is_testnet); @@ -720,6 +941,7 @@ pub mod api_test { json, is_testnet, data_dir, + log_dir, } } } diff --git a/swap/src/cli/api/request.rs b/swap/src/cli/api/request.rs index e1593a61..a12975d1 100644 --- a/swap/src/cli/api/request.rs +++ b/swap/src/cli/api/request.rs @@ -1,6 +1,9 @@ use super::tauri_bindings::TauriHandle; use crate::bitcoin::{wallet, CancelTimelock, ExpiredTimelocks, PunishTimelock}; -use crate::cli::api::tauri_bindings::{SelectMakerDetails, TauriEmitter, TauriSwapProgressEvent}; +use crate::cli::api::tauri_bindings::{ + ApprovalRequestType, SelectMakerDetails, SendMoneroDetails, TauriEmitter, + TauriSwapProgressEvent, +}; use crate::cli::api::Context; use crate::cli::list_sellers::{list_sellers_init, QuoteWithAddress, UnreachableSeller}; use crate::cli::{list_sellers as list_sellers_impl, EventLoop, SellerStatus}; @@ -30,6 +33,7 @@ use serde_json::json; use std::convert::TryInto; use std::future::Future; use std::path::PathBuf; +use std::str::FromStr; use std::sync::Arc; use std::time::Duration; use thiserror::Error; @@ -417,7 +421,7 @@ impl Request for GetLogsArgs { type Response = GetLogsResponse; async fn request(self, ctx: Arc) -> Result { - let dir = self.logs_dir.unwrap_or(ctx.config.data_dir.join("logs")); + let dir = self.logs_dir.unwrap_or(ctx.config.log_dir.clone()); let logs = get_logs(dir, self.swap_id, self.redact).await?; for msg in &logs { @@ -451,6 +455,32 @@ impl Request for RedactArgs { } } +#[typeshare] +#[derive(Debug, Eq, PartialEq, Serialize, Deserialize)] +pub struct GetRestoreHeightArgs; + +#[typeshare] +#[derive(Serialize, Deserialize, Debug)] +pub struct GetRestoreHeightResponse { + #[typeshare(serialized_as = "number")] + pub height: u64, +} + +impl Request for GetRestoreHeightArgs { + type Response = GetRestoreHeightResponse; + + async fn request(self, ctx: Arc) -> Result { + let wallet = ctx + .monero_manager + .as_ref() + .context("Monero wallet manager not available")?; + let wallet = wallet.main_wallet().await; + let height = wallet.get_restore_height().await?; + + Ok(GetRestoreHeightResponse { height }) + } +} + #[typeshare] #[derive(Debug, Eq, PartialEq, Serialize, Deserialize)] pub struct GetMoneroAddressesArgs; @@ -471,6 +501,282 @@ impl Request for GetMoneroAddressesArgs { } } +#[typeshare] +#[derive(Debug, Eq, PartialEq, Serialize, Deserialize)] +pub struct GetMoneroHistoryArgs; + +#[typeshare] +#[derive(Serialize, Clone, Deserialize, Debug)] +pub struct GetMoneroHistoryResponse { + pub transactions: Vec, +} + +impl Request for GetMoneroHistoryArgs { + type Response = GetMoneroHistoryResponse; + + async fn request(self, ctx: Arc) -> Result { + let wallet = ctx + .monero_manager + .as_ref() + .context("Monero wallet manager not available")?; + let wallet = wallet.main_wallet().await; + + let transactions = wallet.history().await; + Ok(GetMoneroHistoryResponse { transactions }) + } +} + +#[typeshare] +#[derive(Debug, Eq, PartialEq, Serialize, Deserialize)] +pub struct GetMoneroMainAddressArgs; + +#[typeshare] +#[derive(Serialize, Deserialize, Debug)] +pub struct GetMoneroMainAddressResponse { + #[typeshare(serialized_as = "String")] + pub address: monero::Address, +} + +impl Request for GetMoneroMainAddressArgs { + type Response = GetMoneroMainAddressResponse; + + async fn request(self, ctx: Arc) -> Result { + let wallet = ctx + .monero_manager + .as_ref() + .context("Monero wallet manager not available")?; + let wallet = wallet.main_wallet().await; + let address = wallet.main_address().await; + Ok(GetMoneroMainAddressResponse { address }) + } +} + +#[typeshare] +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct Date { + #[typeshare(serialized_as = "number")] + pub year: u16, + #[typeshare(serialized_as = "number")] + pub month: u8, + #[typeshare(serialized_as = "number")] + pub day: u8, +} + +#[typeshare] +#[derive(Serialize, Deserialize, Debug)] +#[serde(tag = "type", content = "height")] +pub enum SetRestoreHeightArgs { + #[typeshare(serialized_as = "number")] + Height(u32), + #[typeshare(serialized_as = "object")] + Date(Date), +} + +#[typeshare] +#[derive(Serialize, Deserialize, Debug)] +pub struct SetRestoreHeightResponse { + pub success: bool, +} + +impl Request for SetRestoreHeightArgs { + type Response = SetRestoreHeightResponse; + + async fn request(self, ctx: Arc) -> Result { + let wallet = ctx + .monero_manager + .as_ref() + .context("Monero wallet manager not available")?; + let wallet = wallet.main_wallet().await; + + let height = match self { + SetRestoreHeightArgs::Height(height) => height as u64, + SetRestoreHeightArgs::Date(date) => { + let year: u16 = date.year; + let month: u8 = date.month; + let day: u8 = date.day; + + // Validate ranges + if month < 1 || month > 12 { + bail!("Month must be between 1 and 12"); + } + if day < 1 || day > 31 { + bail!("Day must be between 1 and 31"); + } + + tracing::info!( + "Getting blockchain height for date: {}-{}-{}", + year, + month, + day + ); + + let height = wallet + .get_blockchain_height_by_date(year, month, day) + .await + .with_context(|| { + format!( + "Failed to get blockchain height for date {}-{}-{}", + year, month, day + ) + })?; + tracing::info!( + "Blockchain height for date {}-{}-{}: {}", + year, + month, + day, + height + ); + + height + } + }; + + wallet.set_restore_height(height).await?; + + wallet.pause_refresh().await; + wallet.stop().await; + tracing::debug!("Background refresh stopped"); + + wallet.rescan_blockchain_async().await; + wallet.start_refresh().await; + tracing::info!("Rescanning blockchain from height {} completed", height); + + Ok(SetRestoreHeightResponse { success: true }) + } +} + +// New request type for Monero balance +#[typeshare] +#[derive(Debug, Eq, PartialEq, Serialize, Deserialize)] +pub struct GetMoneroBalanceArgs; + +#[typeshare] +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct GetMoneroBalanceResponse { + #[typeshare(serialized_as = "string")] + pub total_balance: crate::monero::Amount, + #[typeshare(serialized_as = "string")] + pub unlocked_balance: crate::monero::Amount, +} + +impl Request for GetMoneroBalanceArgs { + type Response = GetMoneroBalanceResponse; + + async fn request(self, ctx: Arc) -> Result { + let wallet_manager = ctx + .monero_manager + .as_ref() + .context("Monero wallet manager not available")?; + let wallet = wallet_manager.main_wallet().await; + + let total_balance = wallet.total_balance().await; + let unlocked_balance = wallet.unlocked_balance().await; + + Ok(GetMoneroBalanceResponse { + total_balance: crate::monero::Amount::from_piconero(total_balance.as_pico()), + unlocked_balance: crate::monero::Amount::from_piconero(unlocked_balance.as_pico()), + }) + } +} + +#[typeshare] +#[derive(Debug, Serialize, Deserialize)] +pub struct SendMoneroArgs { + #[typeshare(serialized_as = "String")] + pub address: String, + pub amount: SendMoneroAmount, +} + +#[typeshare] +#[derive(Debug, Serialize, Deserialize)] +#[serde(tag = "type", content = "amount")] +pub enum SendMoneroAmount { + Sweep, + Specific(crate::monero::Amount), +} + +#[typeshare] +#[derive(Serialize, Deserialize, Debug)] +pub struct SendMoneroResponse { + pub tx_hash: String, + pub address: String, + pub amount_sent: crate::monero::Amount, + pub fee: crate::monero::Amount, +} + +impl Request for SendMoneroArgs { + type Response = SendMoneroResponse; + + async fn request(self, ctx: Arc) -> Result { + let wallet_manager = ctx + .monero_manager + .as_ref() + .context("Monero wallet manager not available")?; + let wallet = wallet_manager.main_wallet().await; + + // Parse the address + let address = monero::Address::from_str(&self.address) + .map_err(|e| anyhow::anyhow!("Invalid Monero address: {}", e))?; + + let tauri_handle = ctx + .tauri_handle() + .context("Tauri needs to be available to approve transactions")?; + + // This is a closure that will be called by the monero-sys library to get approval for the transaction + // It sends an approval request to the frontend and returns true if the user approves the transaction + let approval_callback: Arc< + dyn Fn( + String, + ::monero::Amount, + ::monero::Amount, + ) + -> std::pin::Pin + Send>> + + Send + + Sync, + > = std::sync::Arc::new( + move |_txid: String, amount: ::monero::Amount, fee: ::monero::Amount| { + let tauri_handle = tauri_handle.clone(); + + Box::pin(async move { + let details = SendMoneroDetails { + address: address.to_string(), + amount: amount.into(), + fee: fee.into(), + }; + + tauri_handle + .request_approval::( + ApprovalRequestType::SendMonero(details), + Some(60 * 5), + ) + .await + .unwrap_or(false) + }) + }, + ); + + let amount = match self.amount { + SendMoneroAmount::Sweep => None, + SendMoneroAmount::Specific(amount) => Some(amount.into()), + }; + + // This is the actual call to the monero-sys library to send the transaction + // monero-sys will call the approval callback after it has constructed and signed the transaction + // once the user approves, the transaction is published + let (receipt, amount_sent, fee) = wallet + .transfer_with_approval(&address, amount, approval_callback) + .await? + .context("Transaction was not approved by user")?; + + Ok(SendMoneroResponse { + tx_hash: receipt.txid, + address: address.to_string(), + amount_sent: amount_sent.into(), + fee: fee.into(), + }) + } +} + #[tracing::instrument(fields(method = "suspend_current_swap"), skip(context))] pub async fn suspend_current_swap(context: Arc) -> Result { let swap_id = context.swap_lock.get_current_swap_id().await; @@ -1248,23 +1554,6 @@ pub async fn get_current_swap(context: Arc) -> Result, -) -> Result { - let request_id = Uuid::parse_str(&resolve_approval.request_id).context("Invalid request ID")?; - - if let Some(handle) = ctx.tauri_handle.clone() { - handle - .resolve_approval(request_id, resolve_approval.accept) - .await?; - } else { - bail!("Cannot resolve approval without a Tauri handle"); - } - - Ok(ResolveApprovalResponse { success: true }) -} - pub async fn fetch_quotes_task( rendezvous_points: Vec, namespace: XmrBtcNamespace, @@ -1638,6 +1927,18 @@ pub struct ResolveApprovalResponse { pub success: bool, } +#[typeshare] +#[derive(Debug, Serialize, Deserialize)] +pub struct RejectApprovalArgs { + pub request_id: String, +} + +#[typeshare] +#[derive(Serialize, Deserialize, Debug)] +pub struct RejectApprovalResponse { + pub success: bool, +} + #[typeshare] #[derive(Serialize, Deserialize, Debug)] pub struct CheckSeedArgs { @@ -1659,6 +1960,42 @@ impl CheckSeedArgs { } } +// New request type for Monero sync progress +#[typeshare] +#[derive(Debug, Eq, PartialEq, Serialize, Deserialize)] +pub struct GetMoneroSyncProgressArgs; + +#[typeshare] +#[derive(Serialize, Clone, Deserialize, Debug)] +pub struct GetMoneroSyncProgressResponse { + #[typeshare(serialized_as = "number")] + pub current_block: u64, + #[typeshare(serialized_as = "number")] + pub target_block: u64, + #[typeshare(serialized_as = "number")] + pub progress_percentage: f32, +} + +impl Request for GetMoneroSyncProgressArgs { + type Response = GetMoneroSyncProgressResponse; + + async fn request(self, ctx: Arc) -> Result { + let wallet_manager = ctx + .monero_manager + .as_ref() + .context("Monero wallet manager not available")?; + let wallet = wallet_manager.main_wallet().await; + + let sync_progress = wallet.call(|wallet| wallet.sync_progress()).await; + + Ok(GetMoneroSyncProgressResponse { + current_block: sync_progress.current_block, + target_block: sync_progress.target_block, + progress_percentage: sync_progress.percentage(), + }) + } +} + #[typeshare] #[derive(Serialize, Deserialize, Debug)] pub struct GetPendingApprovalsResponse { diff --git a/swap/src/cli/api/tauri_bindings.rs b/swap/src/cli/api/tauri_bindings.rs index c1103f48..62010ea0 100644 --- a/swap/src/cli/api/tauri_bindings.rs +++ b/swap/src/cli/api/tauri_bindings.rs @@ -1,5 +1,8 @@ use super::request::BalanceResponse; use crate::bitcoin; +use crate::cli::api::request::{ + GetMoneroBalanceResponse, GetMoneroHistoryResponse, GetMoneroSyncProgressResponse, +}; use crate::cli::list_sellers::QuoteWithAddress; use crate::monero::MoneroAddressPool; use crate::{bitcoin::ExpiredTimelocks, monero, network::quote::BidQuote}; @@ -31,6 +34,16 @@ pub enum TauriEvent { Approval(ApprovalRequest), BackgroundProgress(TauriBackgroundProgressWrapper), PoolStatusUpdate(PoolStatus), + MoneroWalletUpdate(MoneroWalletUpdate), +} + +#[typeshare] +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(tag = "type", content = "content")] +pub enum MoneroWalletUpdate { + BalanceChange(GetMoneroBalanceResponse), + SyncProgress(GetMoneroSyncProgressResponse), + HistoryUpdate(GetMoneroHistoryResponse), } const TAURI_UNIFIED_EVENT_NAME: &str = "tauri-unified-event"; @@ -62,12 +75,42 @@ pub struct SelectMakerDetails { pub maker: QuoteWithAddress, } +#[typeshare] +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct SendMoneroDetails { + /// Destination address for the Monero transfer + #[typeshare(serialized_as = "string")] + pub address: String, + /// Amount to send + #[typeshare(serialized_as = "number")] + pub amount: monero::Amount, + /// Transaction fee + #[typeshare(serialized_as = "number")] + pub fee: monero::Amount, +} + +#[typeshare] +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct PasswordRequestDetails { + /// The wallet file path that requires a password + pub wallet_path: String, +} + #[typeshare] #[derive(Clone, Debug, Serialize, Deserialize)] #[serde(tag = "type", content = "content")] pub enum SeedChoice { RandomSeed, FromSeed { seed: String }, + FromWalletPath { wallet_path: String }, + Legacy, +} + +#[typeshare] +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct SeedSelectionDetails { + /// List of recently used wallet paths + pub recent_wallets: Vec, } #[typeshare] @@ -90,8 +133,13 @@ pub enum ApprovalRequestType { /// Contains available makers and swap details. SelectMaker(SelectMakerDetails), /// Request seed selection from user. - /// User can choose between random seed or provide their own. - SeedSelection, + /// User can choose between random seed, provide their own, or select wallet file. + SeedSelection(SeedSelectionDetails), + /// Request approval for publishing a Monero transaction. + SendMonero(SendMoneroDetails), + /// Request password for wallet file. + /// User must provide password to unlock the selected wallet. + PasswordRequest(PasswordRequestDetails), } #[typeshare] @@ -297,9 +345,9 @@ impl TauriHandle { ) -> Result<()> { #[cfg(not(feature = "tauri"))] { - return Err(anyhow!( + Err(anyhow!( "Cannot resolve approval: Tauri feature not enabled." - )); + )) } #[cfg(feature = "tauri")] @@ -323,26 +371,38 @@ impl TauriHandle { } } - pub async fn get_pending_approvals(&self) -> Result> { + pub async fn reject_approval(&self, request_id: Uuid) -> Result<()> { #[cfg(not(feature = "tauri"))] { - return Ok(Vec::new()); + Err(anyhow!( + "Cannot reject approval: Tauri feature not enabled." + )) } #[cfg(feature = "tauri")] { - let pending_map = self + let mut pending_map = self .0 .pending_approvals .lock() .map_err(|e| anyhow!("Failed to acquire approval lock: {}", e))?; + if let Some(mut pending) = pending_map.remove(&request_id) { + // Send rejection through oneshot channel + if let Some(responder) = pending.responder.take() { + let _ = responder.send(serde_json::Value::Null); - let approvals: Vec = pending_map - .values() - .map(|pending| pending.request.clone()) - .collect(); + // Emit the rejection event + let mut approval = pending.request.clone(); + approval.request_status = RequestStatus::Rejected; + self.emit_approval(approval); - Ok(approvals) + Ok(()) + } else { + Err(anyhow!("Approval responder was already consumed")) + } + } else { + Err(anyhow!("Approval not found or already handled")) + } } } } @@ -352,7 +412,9 @@ impl Display for ApprovalRequest { match self.request { ApprovalRequestType::LockBitcoin(..) => write!(f, "LockBitcoin()"), ApprovalRequestType::SelectMaker(..) => write!(f, "SelectMaker()"), - ApprovalRequestType::SeedSelection => write!(f, "SeedSelection()"), + ApprovalRequestType::SeedSelection(_) => write!(f, "SeedSelection()"), + ApprovalRequestType::SendMonero(_) => write!(f, "SendMonero()"), + ApprovalRequestType::PasswordRequest(_) => write!(f, "PasswordRequest()"), } } } @@ -373,6 +435,13 @@ pub trait TauriEmitter { async fn request_seed_selection(&self) -> Result; + async fn request_seed_selection_with_recent_wallets( + &self, + recent_wallets: Vec, + ) -> Result; + + async fn request_password(&self, wallet_path: String) -> Result; + fn emit_tauri_event(&self, event: &str, payload: S) -> Result<()>; fn emit_unified_event(&self, event: TauriEvent) { @@ -468,7 +537,22 @@ impl TauriEmitter for TauriHandle { } async fn request_seed_selection(&self) -> Result { - self.request_approval(ApprovalRequestType::SeedSelection, None) + self.request_seed_selection_with_recent_wallets(vec![]) + .await + } + + async fn request_seed_selection_with_recent_wallets( + &self, + recent_wallets: Vec, + ) -> Result { + let details = SeedSelectionDetails { recent_wallets }; + self.request_approval(ApprovalRequestType::SeedSelection(details), None) + .await + } + + async fn request_password(&self, wallet_path: String) -> Result { + let details = PasswordRequestDetails { wallet_path }; + self.request_approval(ApprovalRequestType::PasswordRequest(details), None) .await } @@ -541,6 +625,27 @@ impl TauriEmitter for Option { } } + async fn request_seed_selection_with_recent_wallets( + &self, + recent_wallets: Vec, + ) -> Result { + match self { + Some(tauri) => { + tauri + .request_seed_selection_with_recent_wallets(recent_wallets) + .await + } + None => bail!("No Tauri handle available"), + } + } + + async fn request_password(&self, wallet_path: String) -> Result { + match self { + Some(tauri) => tauri.request_password(wallet_path).await, + None => bail!("No Tauri handle available"), + } + } + fn new_background_process( &self, component: fn(PendingCompleted) -> TauriBackgroundProgress, @@ -566,7 +671,30 @@ impl TauriEmitter for Option { } } -/// A handle for updating a specific background process's progress +impl TauriHandle { + #[cfg(feature = "tauri")] + pub async fn get_pending_approvals(&self) -> Result> { + let pending_map = self + .0 + .pending_approvals + .lock() + .map_err(|e| anyhow!("Failed to acquire approval lock: {}", e))?; + + let approvals: Vec = pending_map + .values() + .map(|pending| pending.request.clone()) + .collect(); + + Ok(approvals) + } + + #[cfg(not(feature = "tauri"))] + pub async fn get_pending_approvals(&self) -> Result> { + Ok(Vec::new()) + } +} + +/// A handle for updating a specific background progress's progress /// /// # Examples /// diff --git a/swap/src/cli/behaviour.rs b/swap/src/cli/behaviour.rs index 03eded8f..c3b535dc 100644 --- a/swap/src/cli/behaviour.rs +++ b/swap/src/cli/behaviour.rs @@ -1,3 +1,4 @@ +use crate::bitcoin; use crate::monero::{Scalar, TransferProof}; use crate::network::cooperative_xmr_redeem_after_punish::CooperativeXmrRedeemRejectReason; use crate::network::quote::BidQuote; @@ -7,8 +8,6 @@ use crate::network::{ cooperative_xmr_redeem_after_punish, encrypted_signature, quote, redial, transfer_proof, }; use crate::protocol::bob::State2; -use crate::bitcoin; -use swap_env::env; use anyhow::{anyhow, Error, Result}; use libp2p::request_response::{ InboundFailure, InboundRequestId, OutboundFailure, OutboundRequestId, ResponseChannel, @@ -17,6 +16,7 @@ use libp2p::swarm::NetworkBehaviour; use libp2p::{identify, identity, ping, PeerId}; use std::sync::Arc; use std::time::Duration; +use swap_env::env; #[derive(Debug)] pub enum OutEvent { diff --git a/swap/src/cli/command.rs b/swap/src/cli/command.rs index 43ad4e11..dff06716 100644 --- a/swap/src/cli/command.rs +++ b/swap/src/cli/command.rs @@ -68,7 +68,11 @@ where tor, } => { let monero_receive_pool: MoneroAddressPool = - swap_serde::monero::address::validate_is_testnet(monero_receive_address, is_testnet)?.into(); + swap_serde::monero::address::validate_is_testnet( + monero_receive_address, + is_testnet, + )? + .into(); let bitcoin_change_address = bitcoin_change_address .map(|address| bitcoin_address::validate(address, is_testnet)) diff --git a/swap/src/common/tracing_util.rs b/swap/src/common/tracing_util.rs index b1292618..489ee0eb 100644 --- a/swap/src/common/tracing_util.rs +++ b/swap/src/common/tracing_util.rs @@ -67,7 +67,16 @@ pub fn init( "libp2p_dcutr", "monero_cpp", ]; - let OUR_CRATES: Vec<&str> = vec!["swap", "asb", "monero_sys", "unstoppableswap-gui-rs"]; + let OUR_CRATES: Vec<&str> = vec![ + "swap", + "asb", + "monero_sys", + "unstoppableswap-gui-rs", + "seed", + "swap_env", + "swap_fs", + "swap_serde", + ]; let INFO_LEVEL_CRATES: Vec<&str> = vec!["monero_rpc_pool"]; diff --git a/swap/src/database.rs b/swap/src/database.rs index f6e5acde..48c60bcb 100644 --- a/swap/src/database.rs +++ b/swap/src/database.rs @@ -3,13 +3,13 @@ pub use bob::Bob; pub use sqlite::SqliteDatabase; use crate::cli::api::tauri_bindings::TauriHandle; -use swap_fs::ensure_directory_exists; use crate::protocol::{Database, State}; use anyhow::{bail, Result}; use serde::{Deserialize, Serialize}; use std::fmt::Display; use std::path::Path; use std::sync::Arc; +use swap_fs::ensure_directory_exists; mod alice; mod bob; diff --git a/swap/src/database/sqlite.rs b/swap/src/database/sqlite.rs index ad224477..ec2aa479 100644 --- a/swap/src/database/sqlite.rs +++ b/swap/src/database/sqlite.rs @@ -163,7 +163,11 @@ impl Database for SqliteDatabase { let addresses = row .iter() .map(|row| -> Result { - let address: Option = row.address.clone().map(|address| address.parse()).transpose()?; + let address: Option = row + .address + .clone() + .map(|address| address.parse()) + .transpose()?; let percentage = Decimal::from_f64(row.percentage).expect("Invalid percentage"); let label = row.label.clone(); @@ -180,9 +184,10 @@ impl Database for SqliteDatabase { } async fn get_monero_addresses(&self) -> Result> { - let rows = sqlx::query!("SELECT DISTINCT address FROM monero_addresses WHERE address IS NOT NULL") - .fetch_all(&self.pool) - .await?; + let rows = + sqlx::query!("SELECT DISTINCT address FROM monero_addresses WHERE address IS NOT NULL") + .fetch_all(&self.pool) + .await?; let addresses = rows .iter() @@ -544,10 +549,18 @@ mod tests { let labeled_addresses = vec![ LabeledMoneroAddress::with_address(address1, Decimal::new(5, 1), "Primary".to_string()) .map_err(|e| anyhow!(e))?, // 0.5 - LabeledMoneroAddress::with_address(address2, Decimal::new(3, 1), "Secondary".to_string()) - .map_err(|e| anyhow!(e))?, // 0.3 - LabeledMoneroAddress::with_address(address3, Decimal::new(2, 1), "Tertiary".to_string()) - .map_err(|e| anyhow!(e))?, // 0.2 + LabeledMoneroAddress::with_address( + address2, + Decimal::new(3, 1), + "Secondary".to_string(), + ) + .map_err(|e| anyhow!(e))?, // 0.3 + LabeledMoneroAddress::with_address( + address3, + Decimal::new(2, 1), + "Tertiary".to_string(), + ) + .map_err(|e| anyhow!(e))?, // 0.2 ]; let address_pool = MoneroAddressPool::new(labeled_addresses); diff --git a/swap/src/monero.rs b/swap/src/monero.rs index 440634cd..60086a9c 100644 --- a/swap/src/monero.rs +++ b/swap/src/monero.rs @@ -20,15 +20,6 @@ use typeshare::typeshare; pub const PICONERO_OFFSET: u64 = 1_000_000_000_000; -#[derive(Serialize, Deserialize)] -#[serde(remote = "Network")] -#[allow(non_camel_case_types)] -pub enum network { - Mainnet, - Stagenet, - Testnet, -} - /// A Monero block height. #[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub struct BlockHeight { @@ -266,7 +257,11 @@ impl LabeledMoneroAddress { }) } - pub fn with_address(address: monero::Address, percentage: Decimal, label: String) -> Result { + pub fn with_address( + address: monero::Address, + percentage: Decimal, + label: String, + ) -> Result { Self::new(address, percentage, label) } @@ -365,18 +360,22 @@ impl MoneroAddressPool { const TOLERANCE: f64 = 1e-6; - if (sum - Decimal::ONE).abs() > Decimal::from_f64(TOLERANCE).unwrap() { + if (sum - Decimal::ONE).abs() + > Decimal::from_f64(TOLERANCE).expect("TOLERANCE constant should be a valid f64") + { bail!("Address pool percentages do not sum to 1"); } Ok(()) } - /// Returns + /// Returns a vector of addresses with the empty addresses filled with the given primary address pub fn fill_empty_addresses(&self, primary_address: monero::Address) -> Vec { - self.0.iter().map(|address| address.address().unwrap_or(primary_address)).collect() + self.0 + .iter() + .map(|address| address.address().unwrap_or(primary_address)) + .collect() } - } impl From<::monero::Address> for MoneroAddressPool { diff --git a/swap/src/monero/wallet.rs b/swap/src/monero/wallet.rs index f621d1ec..071ffaca 100644 --- a/swap/src/monero/wallet.rs +++ b/swap/src/monero/wallet.rs @@ -5,14 +5,19 @@ //! - wait for transactions to be confirmed //! - send money from one wallet to another. -use std::{path::PathBuf, sync::Arc}; +use std::{path::PathBuf, sync::Arc, time::Duration}; +use throttle::{throttle, Throttle}; use anyhow::{Context, Result}; use monero::{Address, Network}; -pub use monero_sys::{Daemon, WalletHandle as Wallet}; +use monero_sys::WalletEventListener; +pub use monero_sys::{Daemon, WalletHandle as Wallet, WalletHandleListener}; use uuid::Uuid; -use crate::cli::api::tauri_bindings::TauriHandle; +use crate::cli::api::{ + request::{GetMoneroBalanceResponse, GetMoneroHistoryResponse, GetMoneroSyncProgressResponse}, + tauri_bindings::{MoneroWalletUpdate, TauriEmitter, TauriEvent, TauriHandle}, +}; use super::{BlockHeight, TransferProof, TxHash}; @@ -34,6 +39,8 @@ pub struct Wallets { /// waiting for a transaction to be confirmed. #[expect(dead_code)] tauri_handle: Option, + /// Database for tracking wallet usage history. + wallet_database: Option>, } /// A request to watch for a transfer. @@ -55,6 +62,174 @@ pub struct TransferRequest { pub amount: monero::Amount, } +struct TauriWalletListener { + // one throttle wrapper per expensive update + balance_throttle: Throttle<()>, + history_throttle: Throttle<()>, + sync_throttle: Throttle<()>, + save_throttle: Throttle<()>, +} + +impl TauriWalletListener { + const BALANCE_UPDATE_THROTTLE: Duration = Duration::from_millis(2 * 1000); + const HISTORY_UPDATE_THROTTLE: Duration = Duration::from_millis(2 * 1000); + const SYNC_UPDATE_THROTTLE: Duration = Duration::from_millis(2 * 1000); + const SAVE_UPDATE_THROTTLE: Duration = Duration::from_millis(60 * 1000); + + pub async fn new(tauri_handle: TauriHandle, wallet: Arc) -> Self { + let rt_handle = tokio::runtime::Handle::current(); + + let balance_job = { + let wallet = wallet.clone(); + let tauri = tauri_handle.clone(); + let rt = rt_handle.clone(); + move |()| { + let wallet = wallet.clone(); + let tauri = tauri.clone(); + let rt = rt.clone(); + rt.spawn(async move { + let response = GetMoneroBalanceResponse { + total_balance: wallet.total_balance().await.into(), + unlocked_balance: wallet.unlocked_balance().await.into(), + }; + tauri.emit_unified_event(TauriEvent::MoneroWalletUpdate( + MoneroWalletUpdate::BalanceChange(response), + )); + }); + } + }; + + let history_job = { + let wallet = wallet.clone(); + let tauri = tauri_handle.clone(); + let rt = rt_handle.clone(); + move |()| { + let wallet = wallet.clone(); + let tauri = tauri.clone(); + let rt = rt.clone(); + rt.spawn(async move { + let transactions = wallet.history().await; + let response = GetMoneroHistoryResponse { transactions }; + + tauri.emit_unified_event(TauriEvent::MoneroWalletUpdate( + MoneroWalletUpdate::HistoryUpdate(response), + )); + }); + } + }; + + let sync_job = { + let wallet = wallet.clone(); + let tauri = tauri_handle.clone(); + let rt = rt_handle.clone(); + move |()| { + let wallet = wallet.clone(); + let tauri = tauri.clone(); + let rt = rt.clone(); + rt.spawn(async move { + let sync_progress = wallet.sync_progress().await; + + let progress_percentage = sync_progress.percentage(); + + let response = GetMoneroSyncProgressResponse { + current_block: sync_progress.current_block, + target_block: sync_progress.target_block, + progress_percentage: progress_percentage, + }; + + tauri.emit_unified_event(TauriEvent::MoneroWalletUpdate( + MoneroWalletUpdate::SyncProgress(response), + )); + }); + } + }; + + let save_job = { + let wallet = wallet.clone(); + let rt = rt_handle.clone(); + move |()| { + let wallet = wallet.clone(); + let rt = rt.clone(); + rt.spawn(async move { + wallet.store(None).await; + }); + } + }; + + Self { + balance_throttle: throttle(balance_job, Self::BALANCE_UPDATE_THROTTLE), + history_throttle: throttle(history_job, Self::HISTORY_UPDATE_THROTTLE), + sync_throttle: throttle(sync_job, Self::SYNC_UPDATE_THROTTLE), + save_throttle: throttle(save_job, Self::SAVE_UPDATE_THROTTLE), + } + } + + fn send_balance_update(&self) { + self.balance_throttle.call(()); + } + + fn send_history_update(&self) { + self.history_throttle.call(()); + } + + fn send_sync_progress(&self) { + self.sync_throttle.call(()); + } + + fn save_wallet(&self) { + self.save_throttle.call(()); + } +} + +impl WalletEventListener for TauriWalletListener { + fn on_money_spent(&self, _txid: &str, _amount: u64) { + self.send_balance_update(); + self.send_history_update(); + self.save_wallet(); + } + + fn on_money_received(&self, _txid: &str, _amount: u64) { + self.send_balance_update(); + self.send_history_update(); + self.save_wallet(); + } + + fn on_unconfirmed_money_received(&self, _txid: &str, _amount: u64) { + self.send_balance_update(); + self.send_history_update(); + self.save_wallet(); + } + + fn on_new_block(&self, _height: u64) { + // We send an update here because a new might mean that funds have been unlocked + // because a UTXO reached 10 confirmations. + self.send_sync_progress(); + } + + fn on_updated(&self) { + self.send_balance_update(); + } + + fn on_refreshed(&self) { + //self.wallet.start_refresh_thread(); + self.send_balance_update(); + self.send_history_update(); + self.save_wallet(); + } + + fn on_reorg(&self, _height: u64, _blocks_detached: u64, _transfers_detached: usize) { + // We send an update here because a reorg might mean that a UTXO has been double spent + // or that a UTXO has been confirmed is now unconfirmed. + self.send_balance_update(); + } + + fn on_pool_tx_removed(&self, _txid: &str) { + // We send an update here because a pool tx removed might mean that our unconfirmed + // balance has gone down because a UTXO has been removed from the pool. + self.send_balance_update(); + } +} + impl Wallets { /// Create a new `Wallets` instance. /// Wallets will be opened on the specified network, connected to the specified daemon @@ -69,6 +244,7 @@ impl Wallets { network: Network, regtest: bool, tauri_handle: Option, + wallet_database: Option>, ) -> Result { let main_wallet = Wallet::open_or_create( wallet_dir.join(&main_wallet_name).display().to_string(), @@ -85,6 +261,20 @@ impl Wallets { let main_wallet = Arc::new(main_wallet); + if let Some(tauri_handle) = tauri_handle.clone() { + let tauri_wallet_listener = + TauriWalletListener::new(tauri_handle, main_wallet.clone()).await; + + let handle_listener = WalletHandleListener::new(main_wallet.clone()); + + main_wallet + .call(move |wallet| { + wallet.add_listener(Box::new(tauri_wallet_listener)); + wallet.add_listener(Box::new(handle_listener)); + }) + .await; + } + let wallets = Self { wallet_dir, network, @@ -92,8 +282,61 @@ impl Wallets { main_wallet, regtest, tauri_handle, + wallet_database, }; + // Record wallet access in database + let wallet_path = wallets.main_wallet.path().await; + let _ = wallets.record_wallet_access(&wallet_path).await; + + Ok(wallets) + } + + /// Create a new `Wallets` instance with an existing wallet as the main wallet. + /// This is used when we want to use a user-selected wallet instead of creating a new one. + pub async fn new_with_existing_wallet( + wallet_dir: PathBuf, + daemon: Daemon, + network: Network, + regtest: bool, + tauri_handle: Option, + existing_wallet: Wallet, + wallet_database: Option>, + ) -> Result { + if regtest { + existing_wallet.unsafe_prepare_for_regtest().await; + } + + let main_wallet = Arc::new(existing_wallet); + + if let Some(tauri_handle) = tauri_handle.clone() { + let tauri_wallet_listener = + TauriWalletListener::new(tauri_handle, main_wallet.clone()).await; + + let handle_listener = WalletHandleListener::new(main_wallet.clone()); + + main_wallet + .call(move |wallet| { + wallet.add_listener(Box::new(tauri_wallet_listener)); + wallet.add_listener(Box::new(handle_listener)); + }) + .await; + } + + let wallets = Self { + wallet_dir, + network, + daemon, + main_wallet, + regtest, + tauri_handle, + wallet_database, + }; + + // Record wallet access in database + let wallet_path = wallets.main_wallet.path().await; + let _ = wallets.record_wallet_access(&wallet_path).await; + Ok(wallets) } @@ -216,6 +459,24 @@ impl Wallets { .context("Failed to get blockchain height")?, }) } + + /// Get the last 5 recently used wallets + pub async fn get_recent_wallets(&self) -> Result> { + if let Some(db) = &self.wallet_database { + let recent_wallets = db.get_recent_wallets(5).await?; + Ok(recent_wallets.into_iter().map(|w| w.wallet_path).collect()) + } else { + Ok(vec![]) + } + } + + /// Record that a wallet was accessed + pub async fn record_wallet_access(&self, wallet_path: &str) -> Result<()> { + if let Some(db) = &self.wallet_database { + db.record_wallet_access(wallet_path).await?; + } + Ok(()) + } } impl TransferRequest { diff --git a/swap/src/network/swap_setup/alice.rs b/swap/src/network/swap_setup/alice.rs index 334972a1..440f9f8d 100644 --- a/swap/src/network/swap_setup/alice.rs +++ b/swap/src/network/swap_setup/alice.rs @@ -6,7 +6,6 @@ use crate::network::swap_setup::{ use crate::protocol::alice::{State0, State3}; use crate::protocol::{Message0, Message2, Message4}; use crate::{asb, bitcoin, monero}; -use swap_env::env; use anyhow::{anyhow, Context, Result}; use futures::future::{BoxFuture, OptionFuture}; use futures::AsyncWriteExt; @@ -20,6 +19,7 @@ use std::collections::VecDeque; use std::fmt::Debug; use std::task::Poll; use std::time::{Duration, Instant}; +use swap_env::env; use uuid::Uuid; #[derive(Debug)] diff --git a/swap/src/network/swap_setup/bob.rs b/swap/src/network/swap_setup/bob.rs index 090cf1fb..84a82ac3 100644 --- a/swap/src/network/swap_setup/bob.rs +++ b/swap/src/network/swap_setup/bob.rs @@ -2,7 +2,6 @@ use crate::network::swap_setup::{protocol, BlockchainNetwork, SpotPriceError, Sp use crate::protocol::bob::{State0, State2}; use crate::protocol::{Message1, Message3}; use crate::{bitcoin, cli, monero}; -use swap_env::env; use anyhow::{Context, Result}; use futures::future::{BoxFuture, OptionFuture}; use futures::AsyncWriteExt; @@ -17,6 +16,7 @@ use std::collections::VecDeque; use std::sync::Arc; use std::task::Poll; use std::time::Duration; +use swap_env::env; use uuid::Uuid; use super::{read_cbor_message, write_cbor_message, SpotPriceRequest}; diff --git a/swap/src/network/swarm.rs b/swap/src/network/swarm.rs index 7899f6b0..2e40668e 100644 --- a/swap/src/network/swarm.rs +++ b/swap/src/network/swarm.rs @@ -3,7 +3,6 @@ use crate::libp2p_ext::MultiAddrExt; use crate::network::rendezvous::XmrBtcNamespace; use crate::seed::Seed; use crate::{asb, bitcoin, cli}; -use swap_env::env; use anyhow::Result; use arti_client::TorClient; use libp2p::swarm::NetworkBehaviour; @@ -12,6 +11,7 @@ use libp2p::{identity, Multiaddr, Swarm}; use std::fmt::Debug; use std::sync::Arc; use std::time::Duration; +use swap_env::env; use tor_rtcompat::tokio::TokioRustlsRuntime; #[allow(clippy::too_many_arguments)] diff --git a/swap/src/protocol/alice.rs b/swap/src/protocol/alice.rs index f0e4cafb..388b2e8c 100644 --- a/swap/src/protocol/alice.rs +++ b/swap/src/protocol/alice.rs @@ -1,9 +1,9 @@ //! Run an XMR/BTC swap in the role of Alice. //! Alice holds XMR and wishes receive BTC. -use swap_env::env::Config; use crate::protocol::Database; use crate::{asb, bitcoin, monero}; use std::sync::Arc; +use swap_env::env::Config; use uuid::Uuid; pub use self::state::*; diff --git a/swap/src/protocol/alice/state.rs b/swap/src/protocol/alice/state.rs index 67aeb5a7..f2befedb 100644 --- a/swap/src/protocol/alice/state.rs +++ b/swap/src/protocol/alice/state.rs @@ -2,7 +2,6 @@ use crate::bitcoin::{ current_epoch, CancelTimelock, ExpiredTimelocks, PunishTimelock, Transaction, TxCancel, TxEarlyRefund, TxPunish, TxRedeem, TxRefund, Txid, }; -use swap_env::env::Config; use crate::monero::wallet::{TransferRequest, WatchRequest}; use crate::monero::BlockHeight; use crate::monero::TransferProof; @@ -15,6 +14,7 @@ use serde::{Deserialize, Serialize}; use sigma_fun::ext::dl_secp256k1_ed25519_eq::CrossCurveDLEQProof; use std::fmt; use std::sync::Arc; +use swap_env::env::Config; use uuid::Uuid; #[derive(Debug, Clone, PartialEq)] diff --git a/swap/src/protocol/alice/swap.rs b/swap/src/protocol/alice/swap.rs index c1cfde99..6ee15a8a 100644 --- a/swap/src/protocol/alice/swap.rs +++ b/swap/src/protocol/alice/swap.rs @@ -7,12 +7,12 @@ use std::time::Duration; use crate::asb::{EventLoopHandle, LatestRate}; use crate::bitcoin::ExpiredTimelocks; use crate::common::retry; -use swap_env::env::Config; use crate::monero::TransferProof; use crate::protocol::alice::{AliceState, Swap}; use crate::{bitcoin, monero}; use ::bitcoin::consensus::encode::serialize_hex; use anyhow::{bail, Context, Result}; +use swap_env::env::Config; use tokio::select; use tokio::time::timeout; use uuid::Uuid; diff --git a/swap/src/protocol/bob/state.rs b/swap/src/protocol/bob/state.rs index 7de17688..8b59f9f2 100644 --- a/swap/src/protocol/bob/state.rs +++ b/swap/src/protocol/bob/state.rs @@ -1,12 +1,11 @@ -use swap_serde::bitcoin::address_serde; use crate::bitcoin::wallet::{EstimateFeeRate, Subscription}; use crate::bitcoin::{ self, current_epoch, CancelTimelock, ExpiredTimelocks, PunishTimelock, Transaction, TxCancel, TxLock, Txid, Wallet, }; use crate::monero::wallet::WatchRequest; +use crate::monero::TransferProof; use crate::monero::{self, MoneroAddressPool, TxHash}; -use crate::monero::{TransferProof}; use crate::monero_ext::ScalarExt; use crate::protocol::{Message0, Message1, Message2, Message3, Message4, CROSS_CURVE_PROOF_SYSTEM}; use anyhow::{anyhow, bail, Context, Result}; @@ -20,6 +19,7 @@ use sha2::Sha256; use sigma_fun::ext::dl_secp256k1_ed25519_eq::CrossCurveDLEQProof; use std::fmt; use std::sync::Arc; +use swap_serde::bitcoin::address_serde; use uuid::Uuid; #[derive(Debug, Clone, PartialEq, Deserialize, Serialize)] diff --git a/swap/src/protocol/bob/swap.rs b/swap/src/protocol/bob/swap.rs index 8b563c08..e6c42c55 100644 --- a/swap/src/protocol/bob/swap.rs +++ b/swap/src/protocol/bob/swap.rs @@ -10,10 +10,10 @@ use crate::network::swap_setup::bob::NewSwap; use crate::protocol::bob::state::*; use crate::protocol::{bob, Database}; use crate::{bitcoin, monero}; -use swap_env::env; use anyhow::{bail, Context as AnyContext, Result}; use std::sync::Arc; use std::time::Duration; +use swap_env::env; use tokio::select; use uuid::Uuid; diff --git a/swap/src/seed.rs b/swap/src/seed.rs index 8f2376db..95fba639 100644 --- a/swap/src/seed.rs +++ b/swap/src/seed.rs @@ -1,7 +1,5 @@ -use crate::cli::api::tauri_bindings::{SeedChoice, TauriEmitter, TauriHandle}; -use swap_fs::ensure_directory_exists; use ::bitcoin::bip32::Xpriv as ExtendedPrivKey; -use anyhow::{Context, Result}; +use anyhow::{Context as AnyContext, Result}; use bitcoin::hashes::{sha256, Hash, HashEngine}; use bitcoin::secp256k1::constants::SECRET_KEY_SIZE; use bitcoin::secp256k1::{self, SecretKey}; @@ -14,6 +12,7 @@ use std::fmt; use std::fs::{self, File}; use std::io::{self, Write}; use std::path::{Path, PathBuf}; +use swap_fs::ensure_directory_exists; use zeroize::Zeroizing; pub const SEED_LENGTH: usize = 32; @@ -32,13 +31,25 @@ impl Seed { Ok(Seed(bytes)) } + /// Extract seed from a Monero wallet + pub async fn from_monero_wallet(wallet: &crate::monero::Wallet) -> Result { + let mnemonic = wallet.seed().await.context("Failed to get wallet seed")?; + + let monero_seed = + MoneroSeed::from_string(Language::English, Zeroizing::new(mnemonic.clone())).map_err( + |e| anyhow::anyhow!("Failed to parse seed from wallet (Error: {:?})", e), + )?; + + Ok(Seed(*monero_seed.entropy())) + } + pub fn derive_extended_private_key( &self, network: bitcoin::Network, ) -> Result { let seed = self.derive(b"BITCOIN_EXTENDED_PRIVATE_KEY").bytes(); let private_key = ExtendedPrivKey::new_master(network, &seed) - .context("Failed to create new master extended private key")?; + .with_context(|| "Failed to create new master extended private key")?; Ok(private_key) } @@ -52,7 +63,7 @@ impl Seed { ) -> Result { let seed = self.derive(b"BITCOIN_EXTENDED_PRIVATE_KEY").bytes(); let private_key = bdk::bitcoin::util::bip32::ExtendedPrivKey::new_master(network, &seed) - .context("Failed to create new master extended private key")?; + .with_context(|| "Failed to create new master extended private key")?; Ok(private_key) } @@ -63,57 +74,27 @@ impl Seed { identity::Keypair::ed25519_from_bytes(bytes).expect("we always pass 32 bytes") } - pub async fn from_file_or_generate( - data_dir: &Path, - tauri_handle: Option, - ) -> Result { + /// Create seed from a Monero wallet mnemonic string + pub fn from_mnemonic(mnemonic: String) -> Result { + let monero_seed = MoneroSeed::from_string(Language::English, Zeroizing::new(mnemonic)) + .with_context(|| "Failed to parse mnemonic")?; + Ok(Seed(*monero_seed.entropy())) + } + + pub async fn from_file_or_generate(data_dir: &Path) -> Result { let file_path_buf = data_dir.join("seed.pem"); let file_path = Path::new(&file_path_buf); if file_path.exists() { - return Self::from_file(file_path).context("Couldn't get seed from file"); + return Self::from_file(file_path).with_context(|| "Couldn't get seed from file"); } tracing::debug!("No seed file found, creating at {}", file_path.display()); - // In debug mode, we allow the user to enter a seed manually. - #[cfg(debug_assertions)] - { - let new_seed = match tauri_handle { - Some(tauri_handle) => { - let seed_choice = tauri_handle.request_seed_selection().await?; - let seed_entered = match seed_choice { - SeedChoice::RandomSeed => Seed::random()?, - SeedChoice::FromSeed { seed } => { - println!("seed: {}", seed); - let monero_seed = - MoneroSeed::from_string(Language::English, Zeroizing::new(seed)) - .unwrap(); - Seed(*monero_seed.entropy()) - } - }; + let random_seed = Seed::random()?; - //TODO: Send error type to front end - - seed_entered - } - None => { - let random_seed = Seed::random()?; - random_seed - } - }; - - new_seed.write_to(file_path.to_path_buf())?; - Ok(new_seed) - } - - // In release mode, we generate a random seed. - #[cfg(not(debug_assertions))] - { - let new_seed = Seed::random()?; - new_seed.write_to(file_path.to_path_buf())?; - Ok(new_seed) - } + random_seed.write_to(file_path.to_path_buf())?; + Ok(random_seed) } /// Derive a new seed using the given scope. @@ -210,6 +191,8 @@ pub enum Error { Rand(#[from] rand::Error), #[error("no default path")] NoDefaultPath, + #[error("Monero wallet error: {0}")] + MoneroWallet(#[from] anyhow::Error), } #[cfg(test)] diff --git a/swap/tests/harness/mod.rs b/swap/tests/harness/mod.rs index fb0b9da9..94d87bba 100644 --- a/swap/tests/harness/mod.rs +++ b/swap/tests/harness/mod.rs @@ -21,8 +21,6 @@ use swap::asb::FixedRate; use swap::bitcoin::{CancelTimelock, PunishTimelock}; use swap::cli::api; use swap::database::{AccessMode, SqliteDatabase}; -use swap_env::env::{Config, GetConfig}; -use swap_fs::ensure_directory_exists; use swap::monero::wallet::no_listener; use swap::monero::Wallets; use swap::network::rendezvous::XmrBtcNamespace; @@ -33,6 +31,8 @@ use swap::protocol::{alice, bob, Database}; use swap::seed::Seed; use swap::{asb, bitcoin, cli, monero}; use swap_env::env; +use swap_env::env::{Config, GetConfig}; +use swap_fs::ensure_directory_exists; use tempfile::{NamedTempFile, TempDir}; use testcontainers::clients::Cli; use testcontainers::{Container, RunnableImage}; diff --git a/throttle/Cargo.toml b/throttle/Cargo.toml new file mode 100644 index 00000000..9a3f6da9 --- /dev/null +++ b/throttle/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "throttle" +version = "0.1.0" +edition = "2024" + +[dependencies] +tracing = { workspace = true } + + +[lib] +path = "src/throttle.rs" + +[lints] +workspace = true diff --git a/throttle/src/throttle.rs b/throttle/src/throttle.rs new file mode 100644 index 00000000..c092d643 --- /dev/null +++ b/throttle/src/throttle.rs @@ -0,0 +1,182 @@ +// copied from: https://github.com/cargo-crates/fns +// MIT License + +use std::pin::Pin; +use std::sync::{mpsc, Arc, Mutex}; +use std::time::{self, /* SystemTime, UNIX_EPOCH, */ Duration}; + +pub fn throttle(closure: F, delay: Duration) -> Throttle +where + F: Fn(T) -> () + Send + Sync + 'static, + T: Send + Sync + 'static, +{ + let (sender, receiver) = mpsc::channel(); + let sender = Arc::new(Mutex::new(sender)); + let throttle_config = Arc::new(Mutex::new(ThrottleConfig { + closure: Box::pin(closure), + delay, + })); + + let dup_throttle_config = throttle_config.clone(); + let throttle = Throttle { + sender: Some(sender), + thread: Some(std::thread::spawn(move || { + let throttle_config = dup_throttle_config; + let mut current_param = None; // ζœ€εŽθ’«δΏε­˜δΈΊζ‰§θ‘Œηš„ε‚ζ•° + let mut closure_time = None; // ι—­εŒ…ζœ€εŽζ‰§θ‘Œζ—Άι—΄ + loop { + if current_param.is_none() { + let message = receiver.recv(); + let now = time::Instant::now(); + match message { + Ok(param) => { + if let Some(param) = param { + let throttle_config = throttle_config.lock().unwrap(); + if closure_time.is_none() + || now.duration_since(closure_time.unwrap()) + >= throttle_config.delay + { + current_param = None; + closure_time = Some(now); + (*throttle_config.closure)(param); + } else { + current_param = Some(param); + } + } else { + current_param = None; + } + } + Err(_) => { + break; + } + } + } else { + let message = receiver.recv_timeout((*throttle_config.lock().unwrap()).delay); + let now = time::Instant::now(); + match message { + Ok(param) => { + if let Some(param) = param { + let throttle_config = throttle_config.lock().unwrap(); + if closure_time.is_none() + || now.duration_since(closure_time.unwrap()) + >= throttle_config.delay + { + (*throttle_config.closure)(param); + current_param = None; + closure_time = Some(now); + } else { + current_param = Some(param); + } + } else { + current_param = None; + } + } + Err(err) => { + match err { + mpsc::RecvTimeoutError::Timeout => { + if let Some(param) = current_param.take() { + (throttle_config.lock().unwrap().closure)(param); + current_param = None; + closure_time = None; // θΆ…ζ—Άζ‰§θ‘ŒδΈΊι’ε€–ηš„ζ‰§θ‘Œ, δΈε½±ε“ηš„δΈ‹δΈ€ζ¬‘ζ‰§θ‘Œ + } + } + mpsc::RecvTimeoutError::Disconnected => { + break; + } + } + } + } + } + } + })), + throttle_config, + }; + throttle +} + +struct ThrottleConfig { + closure: Pin () + Send + Sync + 'static>>, + delay: Duration, +} +impl Drop for ThrottleConfig { + fn drop(&mut self) { + tracing::debug!("drop ThrottleConfig {:?}", format!("{:p}", self)); + } +} + +#[allow(dead_code)] +pub struct Throttle { + sender: Option>>>>, + thread: Option>, + throttle_config: Arc>>, +} +impl Throttle { + pub fn call(&self, param: T) { + self.sender + .as_ref() + .unwrap() + .lock() + .unwrap() + .send(Some(param)) + .unwrap(); + } + pub fn terminate(&self) { + self.sender + .as_ref() + .unwrap() + .lock() + .unwrap() + .send(None) + .unwrap(); + } +} +impl Drop for Throttle { + fn drop(&mut self) { + self.terminate(); + tracing::debug!("drop Throttle {:?}", format!("{:p}", self)); + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() { + let effect_run_times = Arc::new(Mutex::new(0)); + let param = Arc::new(Mutex::new(0)); + let dup_effect_run_times = effect_run_times.clone(); + let dup_param = param.clone(); + let throttle_fn = throttle( + move |param| { + *dup_effect_run_times.lock().unwrap() += 1; + *dup_param.lock().unwrap() = param; + }, + std::time::Duration::from_millis(100), + ); + { + throttle_fn.call(1); + throttle_fn.call(2); + throttle_fn.call(3); + std::thread::sleep(std::time::Duration::from_millis(200)); + assert_eq!(*effect_run_times.lock().unwrap(), 2); // delayεŽζ‰§θ‘Œζœ€ζœ‰δΈ€δΈͺ参数 + assert_eq!(*param.lock().unwrap(), 3); + } + + { + throttle_fn.call(4); + std::thread::sleep(std::time::Duration::from_millis(200)); + assert_eq!(*effect_run_times.lock().unwrap(), 3); + assert_eq!(*param.lock().unwrap(), 4); + } + + { + throttle_fn.call(5); + throttle_fn.call(6); + throttle_fn.terminate(); // η»ˆζ­’ζœ€εŽδΈ€ζ¬‘ζ‰§θ‘Œ + std::thread::sleep(std::time::Duration::from_millis(200)); + assert_eq!(*effect_run_times.lock().unwrap(), 4); + assert_eq!(*param.lock().unwrap(), 5); + } + } +}